WordPress Online Booking and Scheduling Plugin – Bookly - Version 14.5

Version Description

  • Fixed issue with Custom Fields after previous update
Download this release

Release Info

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

Code changes from version 13.2 to 14.5

Files changed (128) hide show
  1. autoload.php +1 -1
  2. backend/Backend.php +19 -14
  3. backend/modules/appearance/Components.php +21 -0
  4. backend/modules/appearance/Controller.php +110 -86
  5. backend/modules/appearance/lib/Helper.php +51 -40
  6. backend/{resources → modules/appearance/resources}/css/bootstrap-editable.css +2 -2
  7. backend/modules/appearance/resources/js/appearance.js +340 -465
  8. backend/modules/appearance/resources/js/bootstrap-editable.bookly.js +112 -0
  9. backend/{resources → modules/appearance/resources}/js/bootstrap-editable.min.js +1 -1
  10. backend/modules/appearance/templates/_1_service.php +88 -78
  11. backend/modules/appearance/templates/_3_time.php +70 -64
  12. backend/modules/appearance/templates/_5_cart.php +59 -50
  13. backend/modules/appearance/templates/_6_details.php +46 -16
  14. backend/modules/appearance/templates/_7_payment.php +28 -37
  15. backend/modules/appearance/templates/_8_complete.php +12 -3
  16. backend/modules/appearance/templates/_card_payment.php +13 -11
  17. backend/modules/appearance/templates/_codes.php +17 -22
  18. backend/modules/appearance/templates/_custom_css.php +40 -0
  19. backend/modules/appearance/templates/_progress_tracker.php +12 -11
  20. backend/modules/appearance/templates/index.php +119 -21
  21. backend/modules/appointments/Controller.php +50 -53
  22. backend/modules/appointments/resources/js/appointments.js +36 -11
  23. backend/modules/appointments/templates/_export_dialog.php +8 -2
  24. backend/modules/appointments/templates/_print_dialog.php +0 -33
  25. backend/modules/appointments/templates/index.php +22 -17
  26. backend/modules/calendar/Components.php +15 -18
  27. backend/modules/calendar/Controller.php +399 -215
  28. backend/modules/calendar/resources/js/calendar-common.js +279 -0
  29. backend/modules/calendar/resources/js/calendar.js +52 -240
  30. backend/modules/calendar/resources/js/ng-appointment_dialog.js +364 -184
  31. backend/modules/calendar/templates/_appointment_dialog.php +137 -59
  32. backend/modules/calendar/templates/_customer_details_dialog.php +19 -70
  33. backend/modules/calendar/templates/calendar.php +9 -8
  34. backend/modules/coupons/Controller.php +0 -66
  35. backend/modules/coupons/forms/Coupon.php +0 -19
  36. backend/modules/coupons/resources/js/coupons.js +0 -63
  37. backend/modules/coupons/templates/_modal.php +0 -66
  38. backend/modules/coupons/templates/index.php +0 -44
  39. backend/modules/custom_fields/Controller.php +0 -90
  40. backend/modules/custom_fields/resources/js/custom_fields.js +0 -233
  41. backend/modules/custom_fields/templates/_services.php +0 -32
  42. backend/modules/custom_fields/templates/index.php +0 -303
  43. backend/modules/customers/Components.php +16 -1
  44. backend/modules/customers/Controller.php +51 -37
  45. backend/modules/customers/forms/Customer.php +5 -2
  46. backend/modules/customers/resources/js/customers.js +25 -19
  47. backend/modules/customers/resources/js/ng-customer_dialog.js +59 -54
  48. backend/modules/customers/templates/_customer_dialog.php +34 -8
  49. backend/modules/customers/templates/_import.php +10 -2
  50. backend/modules/customers/templates/index.php +5 -4
  51. backend/modules/debug/Controller.php +10 -29
  52. backend/modules/debug/templates/index.php +2 -0
  53. backend/modules/message/Controller.php +100 -0
  54. backend/modules/message/resources/js/message.js +71 -0
  55. backend/modules/message/templates/index.php +24 -0
  56. backend/modules/notifications/Components.php +357 -0
  57. backend/modules/notifications/Controller.php +50 -27
  58. backend/modules/notifications/forms/Notifications.php +145 -62
  59. backend/modules/notifications/resources/js/ng-app.js +27 -2
  60. backend/modules/notifications/resources/js/notification.js +7 -1
  61. backend/modules/notifications/templates/_codes.php +0 -34
  62. backend/modules/notifications/templates/_codes_cart.php +0 -16
  63. backend/modules/notifications/templates/_codes_client_new_wp_user.php +0 -15
  64. backend/modules/notifications/templates/_codes_staff_agenda.php +0 -7
  65. backend/modules/{customers/templates/_export.php → notifications/templates/_custom_notification.php} +1 -1
  66. backend/modules/notifications/templates/_test_email_notifications_modal.php +3 -2
  67. backend/modules/notifications/templates/index.php +106 -53
  68. backend/modules/payments/Controller.php +25 -34
  69. backend/modules/payments/resources/js/ng-payment_details_dialog.js +2 -2
  70. backend/modules/payments/resources/js/payments.js +19 -10
  71. backend/modules/payments/templates/details.php +22 -17
  72. backend/modules/payments/templates/index.php +3 -6
  73. backend/modules/services/Controller.php +139 -100
  74. backend/modules/services/forms/Category.php +2 -0
  75. backend/modules/services/forms/Service.php +50 -15
  76. backend/modules/services/resources/js/service.js +228 -226
  77. backend/modules/services/templates/_list.php +138 -144
  78. backend/modules/services/templates/index.php +8 -11
  79. backend/modules/settings/Components.php +30 -0
  80. backend/modules/settings/Controller.php +152 -124
  81. backend/modules/settings/forms/BusinessHours.php +4 -4
  82. backend/modules/settings/forms/Payments.php +11 -1
  83. backend/modules/settings/resources/js/collect-stats-notice.js +13 -0
  84. backend/modules/settings/resources/js/settings.js +71 -61
  85. backend/modules/settings/templates/_calendarForm.php +21 -0
  86. backend/modules/settings/templates/_calendar_codes.php +35 -0
  87. backend/modules/settings/templates/_cartForm.php +5 -3
  88. backend/modules/settings/templates/_collect_stats_notice.php +24 -0
  89. backend/modules/settings/templates/_companyForm.php +1 -0
  90. backend/modules/settings/templates/_customers.php +14 -9
  91. backend/modules/settings/templates/_generalForm.php +22 -28
  92. backend/modules/settings/templates/_googleCalendarForm.php +3 -2
  93. backend/modules/settings/templates/_holidaysForm.php +1 -23
  94. backend/modules/settings/templates/_hoursForm.php +1 -0
  95. backend/modules/settings/templates/_paymentsForm.php +33 -128
  96. backend/modules/settings/templates/_urlForm.php +35 -0
  97. backend/modules/settings/templates/_woocommerce.php +2 -1
  98. backend/modules/settings/templates/_woocommerce_codes.php +1 -1
  99. backend/modules/settings/templates/index.php +16 -5
  100. backend/modules/sms/Components.php +348 -0
  101. backend/modules/sms/Controller.php +45 -21
  102. backend/modules/sms/resources/js/sms.js +107 -21
  103. backend/modules/sms/templates/_codes.php +0 -29
  104. backend/modules/sms/templates/_codes_cart.php +0 -15
  105. backend/modules/sms/templates/_codes_client_new_wp_user.php +0 -14
  106. backend/modules/sms/templates/_codes_staff_agenda.php +0 -7
  107. backend/modules/sms/templates/_custom_notification.php +263 -0
  108. backend/modules/sms/templates/_notifications.php +68 -42
  109. backend/modules/sms/templates/index.php +23 -64
  110. backend/modules/staff/Controller.php +100 -100
  111. backend/modules/staff/forms/StaffSchedule.php +10 -8
  112. backend/modules/staff/forms/StaffServices.php +26 -16
  113. backend/modules/staff/forms/widgets/TimeChoice.php +7 -8
  114. backend/modules/staff/resources/js/staff-days-off.js +52 -0
  115. backend/modules/staff/resources/js/staff-details.js +142 -0
  116. backend/modules/staff/resources/js/staff-schedule.js +322 -0
  117. backend/modules/staff/resources/js/staff-services.js +139 -0
  118. backend/modules/staff/resources/js/staff.js +99 -505
  119. backend/modules/staff/templates/_breaks.php +1 -0
  120. backend/modules/staff/templates/_details.php +110 -0
  121. backend/modules/staff/templates/_list_item.php +3 -3
  122. backend/modules/staff/templates/_new.php +5 -5
  123. backend/modules/staff/templates/edit.php +10 -114
  124. backend/modules/staff/templates/holidays.php +1 -25
  125. backend/modules/staff/templates/index.php +0 -1
  126. backend/modules/staff/templates/schedule.php +70 -67
  127. backend/modules/staff/templates/services.php +110 -78
  128. backend/modules/support/Components.php +20 -5
autoload.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
 
3
  /**
4
- * BooklyLite autoload.
5
  * @param $class
6
  */
7
  function bookly_lite_loader( $class )
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
 
3
  /**
4
+ * Bookly Lite autoload.
5
  * @param $class
6
  */
7
  function bookly_lite_loader( $class )
backend/Backend.php CHANGED
@@ -1,7 +1,6 @@
1
  <?php
2
  namespace BooklyLite\Backend;
3
 
4
- use BooklyLite\Backend\Modules;
5
  use BooklyLite\Frontend;
6
  use BooklyLite\Lib;
7
 
@@ -11,15 +10,16 @@ use BooklyLite\Lib;
11
  */
12
  class Backend
13
  {
 
 
 
14
  public function __construct()
15
  {
16
  // Backend controllers.
17
  $this->apearanceController = Modules\Appearance\Controller::getInstance();
18
  $this->appointmentsController = Modules\Appointments\Controller::getInstance();
19
  $this->calendarController = Modules\Calendar\Controller::getInstance();
20
- $this->couponsController = Modules\Coupons\Controller::getInstance();
21
  $this->customerController = Modules\Customers\Controller::getInstance();
22
- $this->customFieldsController = Modules\CustomFields\Controller::getInstance();
23
  $this->debugController = Modules\Debug\Controller::getInstance();
24
  $this->notificationsController = Modules\Notifications\Controller::getInstance();
25
  $this->paymentController = Modules\Payments\Controller::getInstance();
@@ -28,6 +28,7 @@ class Backend
28
  $this->supportController = Modules\Support\Controller::getInstance();
29
  $this->smsController = Modules\Sms\Controller::getInstance();
30
  $this->staffController = Modules\Staff\Controller::getInstance();
 
31
 
32
  // Frontend controllers that work via admin-ajax.php.
33
  $this->bookingController = Frontend\Modules\Booking\Controller::getInstance();
@@ -38,6 +39,9 @@ class Backend
38
  add_action( 'admin_init', array( $this, 'addTinyMCEPlugin' ) );
39
  }
40
 
 
 
 
41
  public function init()
42
  {
43
  if ( ! session_id() ) {
@@ -50,14 +54,17 @@ class Backend
50
  new Modules\TinyMce\Plugin();
51
  }
52
 
 
 
 
53
  public function addAdminMenu()
54
  {
55
  /** @var \WP_User $current_user */
56
- global $current_user;
57
 
58
  if ( $current_user->has_cap( 'administrator' ) || Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
59
  $dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
60
- add_menu_page( 'Bookly', 'Bookly', 'read', 'bookly-menu', '',
61
  plugins_url( 'resources/images/menu.png', __FILE__ ), $dynamic_position );
62
 
63
  // Translated submenu pages.
@@ -71,14 +78,14 @@ class Backend
71
  $payments = __( 'Payments', 'bookly' );
72
  $appearance = __( 'Appearance', 'bookly' );
73
  $settings = __( 'Settings', 'bookly' );
74
- $coupons = __( 'Coupons', 'bookly' );
75
- $custom_fields = __( 'Custom Fields', 'bookly' );
76
 
77
  add_submenu_page( 'bookly-menu', $calendar, $calendar, 'read',
78
  Modules\Calendar\Controller::page_slug, array( $this->calendarController, 'index' ) );
79
  add_submenu_page( 'bookly-menu', $appointments, $appointments, 'manage_options',
80
  Modules\Appointments\Controller::page_slug, array( $this->appointmentsController, 'index' ) );
81
- do_action( 'bookly_render_menu_after_appointments' );
 
82
  if ( $current_user->has_cap( 'administrator' ) ) {
83
  add_submenu_page( 'bookly-menu', $staff_members, $staff_members, 'manage_options',
84
  Modules\Staff\Controller::page_slug, array( $this->staffController, 'index' ) );
@@ -100,20 +107,18 @@ class Backend
100
  Modules\Payments\Controller::page_slug, array( $this->paymentController, 'index' ) );
101
  add_submenu_page( 'bookly-menu', $appearance, $appearance, 'manage_options',
102
  Modules\Appearance\Controller::page_slug, array( $this->apearanceController, 'index' ) );
103
- add_submenu_page( 'bookly-menu', $custom_fields, $custom_fields, 'manage_options',
104
- Modules\CustomFields\Controller::page_slug, array( $this->customFieldsController, 'index' ) );
105
- add_submenu_page( 'bookly-menu', $coupons, $coupons, 'manage_options',
106
- Modules\Coupons\Controller::page_slug, array( $this->couponsController, 'index' ) );
107
  add_submenu_page( 'bookly-menu', $settings, $settings, 'manage_options',
108
  Modules\Settings\Controller::page_slug, array( $this->settingsController, 'index' ) );
 
 
109
 
110
  if ( isset ( $_GET['page'] ) && $_GET['page'] == 'bookly-debug' ) {
111
  add_submenu_page( 'bookly-menu', 'Debug', 'Debug', 'manage_options',
112
  Modules\Debug\Controller::page_slug, array( $this->debugController, 'index' ) );
113
  }
114
 
115
- global $submenu;
116
- do_action( 'bookly_admin_menu', 'bookly-menu' );
117
  unset ( $submenu['bookly-menu'][0] );
118
  }
119
  }
1
  <?php
2
  namespace BooklyLite\Backend;
3
 
 
4
  use BooklyLite\Frontend;
5
  use BooklyLite\Lib;
6
 
10
  */
11
  class Backend
12
  {
13
+ /**
14
+ * Constructor.
15
+ */
16
  public function __construct()
17
  {
18
  // Backend controllers.
19
  $this->apearanceController = Modules\Appearance\Controller::getInstance();
20
  $this->appointmentsController = Modules\Appointments\Controller::getInstance();
21
  $this->calendarController = Modules\Calendar\Controller::getInstance();
 
22
  $this->customerController = Modules\Customers\Controller::getInstance();
 
23
  $this->debugController = Modules\Debug\Controller::getInstance();
24
  $this->notificationsController = Modules\Notifications\Controller::getInstance();
25
  $this->paymentController = Modules\Payments\Controller::getInstance();
28
  $this->supportController = Modules\Support\Controller::getInstance();
29
  $this->smsController = Modules\Sms\Controller::getInstance();
30
  $this->staffController = Modules\Staff\Controller::getInstance();
31
+ $this->messageController = Modules\Message\Controller::getInstance();
32
 
33
  // Frontend controllers that work via admin-ajax.php.
34
  $this->bookingController = Frontend\Modules\Booking\Controller::getInstance();
39
  add_action( 'admin_init', array( $this, 'addTinyMCEPlugin' ) );
40
  }
41
 
42
+ /**
43
+ * Init.
44
+ */
45
  public function init()
46
  {
47
  if ( ! session_id() ) {
54
  new Modules\TinyMce\Plugin();
55
  }
56
 
57
+ /**
58
+ * Admin menu.
59
+ */
60
  public function addAdminMenu()
61
  {
62
  /** @var \WP_User $current_user */
63
+ global $current_user, $submenu;
64
 
65
  if ( $current_user->has_cap( 'administrator' ) || Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
66
  $dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
67
+ add_menu_page( 'Bookly Lite', 'Bookly Lite', 'read', 'bookly-menu', '',
68
  plugins_url( 'resources/images/menu.png', __FILE__ ), $dynamic_position );
69
 
70
  // Translated submenu pages.
78
  $payments = __( 'Payments', 'bookly' );
79
  $appearance = __( 'Appearance', 'bookly' );
80
  $settings = __( 'Settings', 'bookly' );
81
+ $messages = __( 'Messages', 'bookly' );
 
82
 
83
  add_submenu_page( 'bookly-menu', $calendar, $calendar, 'read',
84
  Modules\Calendar\Controller::page_slug, array( $this->calendarController, 'index' ) );
85
  add_submenu_page( 'bookly-menu', $appointments, $appointments, 'manage_options',
86
  Modules\Appointments\Controller::page_slug, array( $this->appointmentsController, 'index' ) );
87
+ Lib\Proxy\Locations::addBooklyMenuItem();
88
+ Lib\Proxy\Packages::addBooklyMenuItem();
89
  if ( $current_user->has_cap( 'administrator' ) ) {
90
  add_submenu_page( 'bookly-menu', $staff_members, $staff_members, 'manage_options',
91
  Modules\Staff\Controller::page_slug, array( $this->staffController, 'index' ) );
107
  Modules\Payments\Controller::page_slug, array( $this->paymentController, 'index' ) );
108
  add_submenu_page( 'bookly-menu', $appearance, $appearance, 'manage_options',
109
  Modules\Appearance\Controller::page_slug, array( $this->apearanceController, 'index' ) );
110
+ Lib\Proxy\Coupons::addBooklyMenuItem();
111
+ Lib\Proxy\CustomFields::addBooklyMenuItem();
 
 
112
  add_submenu_page( 'bookly-menu', $settings, $settings, 'manage_options',
113
  Modules\Settings\Controller::page_slug, array( $this->settingsController, 'index' ) );
114
+ add_submenu_page( 'bookly-menu', $messages, $messages, 'manage_options',
115
+ Modules\Message\Controller::page_slug, array( $this->messageController, 'index' ) );
116
 
117
  if ( isset ( $_GET['page'] ) && $_GET['page'] == 'bookly-debug' ) {
118
  add_submenu_page( 'bookly-menu', 'Debug', 'Debug', 'manage_options',
119
  Modules\Debug\Controller::page_slug, array( $this->debugController, 'index' ) );
120
  }
121
 
 
 
122
  unset ( $submenu['bookly-menu'][0] );
123
  }
124
  }
backend/modules/appearance/Components.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace BooklyLite\Backend\Modules\Appearance;
3
+
4
+ use BooklyLite\Lib;
5
+
6
+ /**
7
+ * Class Components
8
+ * @package BooklyLite\Backend\Modules\Appearance
9
+ */
10
+ class Components extends Lib\Base\Components
11
+ {
12
+ /**
13
+ * @param array $variables
14
+ * @param bool $echo
15
+ * @return string|void
16
+ */
17
+ public function renderCodes( $variables = array(), $echo = true )
18
+ {
19
+ return $this->render( '_codes', $variables, $echo );
20
+ }
21
+ }
backend/modules/appearance/Controller.php CHANGED
@@ -2,6 +2,7 @@
2
  namespace BooklyLite\Backend\Modules\Appearance;
3
 
4
  use BooklyLite\Lib;
 
5
 
6
  /**
7
  * Class Controller
@@ -31,17 +32,14 @@ class Controller extends Lib\Base\Controller
31
  'css/bookly-main.css',
32
  )
33
  ),
34
- 'backend' => array(
35
- 'bootstrap/css/bootstrap-theme.min.css',
36
- 'css/bootstrap-editable.css',
37
- ),
38
- 'wp' => array( 'wp-color-picker' ),
39
  ) );
40
 
41
  $this->enqueueScripts( array(
42
  'backend' => array(
43
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
44
- 'js/bootstrap-editable.min.js' => array( 'jquery' ),
45
  'js/alert.js' => array( 'jquery' ),
46
  ),
47
  'frontend' => array_merge(
@@ -56,10 +54,15 @@ class Controller extends Lib\Base\Controller
56
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
57
  ),
58
  'wp' => array( 'wp-color-picker' ),
59
- 'module' => array( 'js/appearance.js' => array( 'jquery' ) )
 
 
 
 
60
  ) );
61
 
62
  wp_localize_script( 'bookly-picker.date.js', 'BooklyL10n', array(
 
63
  'today' => __( 'Today', 'bookly' ),
64
  'months' => array_values( $wp_locale->month ),
65
  'days' => array_values( $wp_locale->weekday_abbrev ),
@@ -76,7 +79,7 @@ class Controller extends Lib\Base\Controller
76
  ) );
77
 
78
  // Initialize steps (tabs).
79
- $this->steps = array(
80
  1 => get_option( 'bookly_l10n_step_service' ),
81
  get_option( 'bookly_l10n_step_extras' ),
82
  get_option( 'bookly_l10n_step_time' ),
@@ -87,8 +90,13 @@ class Controller extends Lib\Base\Controller
87
  get_option( 'bookly_l10n_step_done' )
88
  );
89
 
 
 
 
 
 
90
  // Render general layout.
91
- $this->render( 'index' );
92
  }
93
 
94
  /**
@@ -96,76 +104,94 @@ class Controller extends Lib\Base\Controller
96
  */
97
  public function executeUpdateAppearanceOptions()
98
  {
99
- if ( $this->hasParameter( 'options' ) ) {
100
- $get_option = $this->getParameter( 'options' );
101
- $options = array(
102
- // Info text.
103
- 'bookly_l10n_info_cart_step' => $get_option['text_info_cart_step'],
104
- 'bookly_l10n_info_complete_step' => $get_option['text_info_complete_step'],
105
- 'bookly_l10n_info_coupon' => $get_option['text_info_coupon'],
106
- 'bookly_l10n_info_details_step' => $get_option['text_info_details_step'],
107
- 'bookly_l10n_info_details_step_guest' => $get_option['text_info_details_step_guest'],
108
- 'bookly_l10n_info_payment_step' => $get_option['text_info_payment_step'],
109
- 'bookly_l10n_info_service_step' => $get_option['text_info_service_step'],
110
- 'bookly_l10n_info_time_step' => $get_option['text_info_time_step'],
111
- // Step, label and option texts.
112
- 'bookly_l10n_button_apply' => $get_option['text_button_apply'],
113
- 'bookly_l10n_button_back' => $get_option['text_button_back'],
114
- 'bookly_l10n_button_book_more' => $get_option['text_button_book_more'],
115
- 'bookly_l10n_button_next' => $get_option['text_button_next'],
116
- 'bookly_l10n_label_category' => $get_option['text_label_category'],
117
- 'bookly_l10n_label_ccard_code' => $get_option['text_label_ccard_code'],
118
- 'bookly_l10n_label_ccard_expire' => $get_option['text_label_ccard_expire'],
119
- 'bookly_l10n_label_ccard_number' => $get_option['text_label_ccard_number'],
120
- 'bookly_l10n_label_coupon' => $get_option['text_label_coupon'],
121
- 'bookly_l10n_label_email' => $get_option['text_label_email'],
122
- 'bookly_l10n_label_employee' => $get_option['text_label_employee'],
123
- 'bookly_l10n_label_finish_by' => $get_option['text_label_finish_by'],
124
- 'bookly_l10n_label_name' => $get_option['text_label_name'],
125
- 'bookly_l10n_label_number_of_persons' => $get_option['text_label_number_of_persons'],
126
- 'bookly_l10n_label_pay_ccard' => $get_option['text_label_pay_ccard'],
127
- 'bookly_l10n_label_pay_locally' => $get_option['text_label_pay_locally'],
128
- 'bookly_l10n_label_pay_mollie' => $get_option['text_label_pay_mollie'],
129
- 'bookly_l10n_label_pay_paypal' => $get_option['text_label_pay_paypal'],
130
- 'bookly_l10n_label_phone' => $get_option['text_label_phone'],
131
- 'bookly_l10n_label_select_date' => $get_option['text_label_select_date'],
132
- 'bookly_l10n_label_service' => $get_option['text_label_service'],
133
- 'bookly_l10n_label_start_from' => $get_option['text_label_start_from'],
134
- 'bookly_l10n_option_category' => $get_option['text_option_category'],
135
- 'bookly_l10n_option_employee' => $get_option['text_option_employee'],
136
- 'bookly_l10n_option_service' => $get_option['text_option_service'],
137
- 'bookly_l10n_step_cart' => $get_option['text_step_cart'],
138
- 'bookly_l10n_step_details' => $get_option['text_step_details'],
139
- 'bookly_l10n_step_done' => $get_option['text_step_done'],
140
- 'bookly_l10n_step_payment' => $get_option['text_step_payment'],
141
- 'bookly_l10n_step_service' => $get_option['text_step_service'],
142
- 'bookly_l10n_step_time' => $get_option['text_step_time'],
143
- // Validator errors.
144
- 'bookly_l10n_required_email' => $get_option['text_required_email'],
145
- 'bookly_l10n_required_employee' => $get_option['text_required_employee'],
146
- 'bookly_l10n_required_name' => $get_option['text_required_name'],
147
- 'bookly_l10n_required_phone' => $get_option['text_required_phone'],
148
- 'bookly_l10n_required_service' => $get_option['text_required_service'],
149
- // Color.
150
- 'bookly_app_color' => $get_option['color'],
151
- // Checkboxes.
152
- 'bookly_app_required_employee' => $get_option['required_employee'],
153
- 'bookly_app_show_blocked_timeslots' => $get_option['blocked_timeslots'],
154
- 'bookly_app_show_calendar' => $get_option['show_calendar'],
155
- 'bookly_app_show_day_one_column' => $get_option['day_one_column'],
156
- 'bookly_app_show_progress_tracker' => $get_option['progress_tracker'],
157
- 'bookly_app_staff_name_with_price' => $get_option['staff_name_with_price'],
158
- );
159
-
160
- $options = apply_filters( 'bookly_prepare_appearance_settings', $options, $get_option );
161
-
162
- // Save options.
163
- foreach ( $options as $option_name => $option_value ) {
164
- update_option( $option_name, $option_value );
165
- // Register string for translate in WPML.
166
- if ( strpos( $option_name, 'bookly_l10n_' ) === 0 ) {
167
- do_action( 'wpml_register_single_string', 'bookly', $option_name, $option_value );
168
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
  }
171
 
@@ -181,14 +207,12 @@ class Controller extends Lib\Base\Controller
181
  }
182
 
183
  /**
184
- * Override parent method to add 'wp_ajax_bookly_' prefix
185
- * so current 'execute*' methods look nicer.
186
- *
187
- * @param string $prefix
188
  */
189
- protected function registerWpActions( $prefix = '' )
190
  {
191
- parent::registerWpActions( 'wp_ajax_bookly_' );
192
- }
193
 
 
 
194
  }
2
  namespace BooklyLite\Backend\Modules\Appearance;
3
 
4
  use BooklyLite\Lib;
5
+ use BooklyLite\Backend\Modules\Appearance\Lib\Helper;
6
 
7
  /**
8
  * Class Controller
32
  'css/bookly-main.css',
33
  )
34
  ),
35
+ 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
36
+ 'wp' => array( 'wp-color-picker', ),
37
+ 'module' => array( 'css/bootstrap-editable.css', )
 
 
38
  ) );
39
 
40
  $this->enqueueScripts( array(
41
  'backend' => array(
42
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
 
43
  'js/alert.js' => array( 'jquery' ),
44
  ),
45
  'frontend' => array_merge(
54
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
55
  ),
56
  'wp' => array( 'wp-color-picker' ),
57
+ 'module' => array(
58
+ 'js/bootstrap-editable.min.js' => array( 'bookly-bootstrap.min.js' ),
59
+ 'js/bootstrap-editable.bookly.js' => array( 'bookly-bootstrap-editable.min.js' ),
60
+ 'js/appearance.js' => array( 'bookly-bootstrap-editable.bookly.js' )
61
+ )
62
  ) );
63
 
64
  wp_localize_script( 'bookly-picker.date.js', 'BooklyL10n', array(
65
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
66
  'today' => __( 'Today', 'bookly' ),
67
  'months' => array_values( $wp_locale->month ),
68
  'days' => array_values( $wp_locale->weekday_abbrev ),
79
  ) );
80
 
81
  // Initialize steps (tabs).
82
+ $steps = array(
83
  1 => get_option( 'bookly_l10n_step_service' ),
84
  get_option( 'bookly_l10n_step_extras' ),
85
  get_option( 'bookly_l10n_step_time' ),
90
  get_option( 'bookly_l10n_step_done' )
91
  );
92
 
93
+ $custom_css = get_option( 'bookly_app_custom_styles' );
94
+
95
+ // Shortcut to helper class.
96
+ $editable = new Helper();
97
+
98
  // Render general layout.
99
+ $this->render( 'index', compact( 'steps', 'custom_css', 'editable' ) );
100
  }
101
 
102
  /**
104
  */
105
  public function executeUpdateAppearanceOptions()
106
  {
107
+ $options = $this->getParameter( 'options', array() );
108
+
109
+ // Make sure that we save only allowed options.
110
+ $options_to_save = array_intersect_key( $options, array_flip( array(
111
+ // Info text.
112
+ 'bookly_l10n_info_cart_step',
113
+ 'bookly_l10n_info_complete_step',
114
+ 'bookly_l10n_info_complete_step_limit_error',
115
+ 'bookly_l10n_info_complete_step_processing',
116
+ 'bookly_l10n_info_details_step',
117
+ 'bookly_l10n_info_details_step_guest',
118
+ 'bookly_l10n_info_payment_step_single_app',
119
+ 'bookly_l10n_info_payment_step_several_apps',
120
+ 'bookly_l10n_info_service_step',
121
+ 'bookly_l10n_info_time_step',
122
+ // Step, label and option texts.
123
+ 'bookly_l10n_button_apply',
124
+ 'bookly_l10n_button_back',
125
+ 'bookly_l10n_button_book_more',
126
+ 'bookly_l10n_label_category',
127
+ 'bookly_l10n_label_ccard_code',
128
+ 'bookly_l10n_label_ccard_expire',
129
+ 'bookly_l10n_label_ccard_number',
130
+ 'bookly_l10n_label_email',
131
+ 'bookly_l10n_label_employee',
132
+ 'bookly_l10n_label_finish_by',
133
+ 'bookly_l10n_label_name',
134
+ 'bookly_l10n_label_first_name',
135
+ 'bookly_l10n_label_last_name',
136
+ 'bookly_l10n_label_notes',
137
+ 'bookly_l10n_label_number_of_persons',
138
+ 'bookly_l10n_label_pay_ccard',
139
+ 'bookly_l10n_label_pay_locally',
140
+ 'bookly_l10n_label_pay_paypal',
141
+ 'bookly_l10n_label_phone',
142
+ 'bookly_l10n_label_select_date',
143
+ 'bookly_l10n_label_service',
144
+ 'bookly_l10n_label_start_from',
145
+ 'bookly_l10n_option_category',
146
+ 'bookly_l10n_option_employee',
147
+ 'bookly_l10n_option_service',
148
+ 'bookly_l10n_step_service',
149
+ 'bookly_l10n_step_service_mobile_button_next',
150
+ 'bookly_l10n_step_service_button_next',
151
+ 'bookly_l10n_step_time',
152
+ 'bookly_l10n_step_time_slot_not_available',
153
+ 'bookly_l10n_step_cart',
154
+ 'bookly_l10n_step_cart_slot_not_available',
155
+ 'bookly_l10n_step_cart_button_next',
156
+ 'bookly_l10n_step_details',
157
+ 'bookly_l10n_step_details_button_next',
158
+ 'bookly_l10n_step_details_button_login',
159
+ 'bookly_l10n_step_payment',
160
+ 'bookly_l10n_step_payment_button_next',
161
+ 'bookly_l10n_step_done',
162
+ // Validator errors.
163
+ 'bookly_l10n_required_email',
164
+ 'bookly_l10n_required_employee',
165
+ 'bookly_l10n_required_name',
166
+ 'bookly_l10n_required_first_name',
167
+ 'bookly_l10n_required_last_name',
168
+ 'bookly_l10n_required_phone',
169
+ 'bookly_l10n_required_service',
170
+ // Color.
171
+ 'bookly_app_color',
172
+ // Checkboxes.
173
+ 'bookly_app_required_employee',
174
+ 'bookly_app_service_name_with_duration',
175
+ 'bookly_app_show_blocked_timeslots',
176
+ 'bookly_app_show_calendar',
177
+ 'bookly_app_show_day_one_column',
178
+ 'bookly_app_show_login_button',
179
+ 'bookly_app_show_notes',
180
+ 'bookly_app_show_progress_tracker',
181
+ 'bookly_app_staff_name_with_price',
182
+ 'bookly_cst_required_phone',
183
+ 'bookly_cst_first_last_name',
184
+ ) ) );
185
+
186
+ // Allow add-ons to add their options.
187
+ $options_to_save = Lib\Proxy\Shared::prepareAppearanceOptions( $options_to_save, $options );
188
+
189
+ // Save options.
190
+ foreach ( $options_to_save as $option_name => $option_value ) {
191
+ update_option( $option_name, $option_value );
192
+ // Register string for translate in WPML.
193
+ if ( strpos( $option_name, 'bookly_l10n_' ) === 0 ) {
194
+ do_action( 'wpml_register_single_string', 'bookly', $option_name, $option_value );
195
  }
196
  }
197
 
207
  }
208
 
209
  /**
210
+ * Process ajax request to save custom css
 
 
 
211
  */
212
+ public function executeSaveCustomCss()
213
  {
214
+ update_option( 'bookly_app_custom_styles', $this->getParameter( 'custom_css' ) );
 
215
 
216
+ wp_send_json_success( array( 'message' => __( 'Your custom CSS was saved. Please refresh the page to see your changes.', 'bookly') ) );
217
+ }
218
  }
backend/modules/appearance/lib/Helper.php CHANGED
@@ -4,55 +4,66 @@ namespace BooklyLite\Backend\Modules\Appearance\Lib;
4
  class Helper
5
  {
6
  /**
7
- * Render ui for editing frontend messages. <span>
8
  *
9
- * @param array $data l10n option name
10
- * @param null $mirror_class the updated value will be displayed also for the objects with css class
11
  */
12
- public static function renderSpan( array $data, $mirror_class = null )
13
  {
14
- $id = $data[0];
15
- $options = array();
16
- foreach ( $data as $option_name ) {
17
- $options[ $option_name ] = get_option( $option_name );
18
- }
19
- if ( count( $options ) > 1 ) {
20
- printf( '<span id="%s" data-options-default=\'%s\' data-type="multiple"%s%s>%s</span>',
21
- $id, json_encode( $options ), $mirror_class ? ' class="' . $mirror_class . '"' : '',
22
- $mirror_class ? ' data-mirror="' . $mirror_class . '"' : '', esc_html( $options[ $id ] )
23
- );
24
- } else {
25
- printf( '<span id="%s" data-option-default=\'%s\' data-type="text" class="bookly-editable%s"%s>%s</span>',
26
- $id, esc_attr( $options[ $id ] ), $mirror_class ? ' ' . $mirror_class : '',
27
- $mirror_class ? ' data-mirror="' . $mirror_class . '"' : '', esc_html( $options[ $id ] )
28
- );
29
- }
30
  }
31
 
32
  /**
33
- * Render ui for editing frontend messages. <label>
34
  *
35
- * @param array $data l10n option name
36
- * @param null $mirror_class the updated value will be displayed also for the objects with css class
37
  */
38
- public static function renderLabel( array $data, $mirror_class = null )
39
  {
40
- $id = $data[0];
41
- $options = array();
42
- foreach ( $data as $option_name ) {
43
- $options[ $option_name ] = get_option( $option_name );
44
- }
45
- if ( count( $options ) > 1 ) {
46
- printf( '<label id="%s" data-type="multiple" data-options-default=\'%s\'%s%s>%s</label>',
47
- $id, json_encode( $options ), $mirror_class ? ' class="' . $mirror_class . '"' : '',
48
- $mirror_class ? ' data-mirror="' . $mirror_class . '"' : '', esc_html( $options[ $id ] )
49
- );
50
- } else {
51
- printf( '<label id="%s" data-option-default=\'%s\' data-type="text" class="bookly-editable%s"%s>%s</label>',
52
- $id, esc_attr( $options[ $id ] ), $mirror_class ? ' ' . $mirror_class : '',
53
- $mirror_class ? ' data-mirror="' . $mirror_class . '"' : '', esc_html( $options[ $id ] )
54
- );
55
- }
 
 
 
 
 
 
 
56
  }
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  }
4
  class Helper
5
  {
6
  /**
7
+ * Render editable string (single line).
8
  *
9
+ * @param array $options
 
10
  */
11
+ public static function renderString(array $options )
12
  {
13
+ self::_renderEditable( $options, 'span' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
 
16
  /**
17
+ * Render editable label.
18
  *
19
+ * @param array $options
 
20
  */
21
+ public static function renderLabel( array $options )
22
  {
23
+ self::_renderEditable( $options, 'label' );
24
+ }
25
+
26
+ /**
27
+ * Render editable text (multi-line).
28
+ *
29
+ * @param string $option_name
30
+ * @param string $codes
31
+ * @param string $placement
32
+ * @param string $title
33
+ */
34
+ public static function renderText( $option_name, $codes = '', $placement = 'bottom', $title = '' )
35
+ {
36
+ $option_value = get_option( $option_name );
37
+
38
+ printf( '<span class="bookly-js-editable bookly-js-option %s editable-pre-wrapped" data-type="bookly" data-fieldType="textarea" data-values="%s" data-codes="%s" data-title="%s" data-placement="%s">%s</span>',
39
+ $option_name,
40
+ esc_attr( json_encode( array( $option_name => $option_value ) ) ),
41
+ esc_attr( $codes ),
42
+ esc_attr( $title ),
43
+ $placement,
44
+ esc_html( $option_value )
45
+ );
46
  }
47
 
48
+ /**
49
+ * Render editable element.
50
+ *
51
+ * @param array $options
52
+ * @param string $tag
53
+ */
54
+ private static function _renderEditable( array $options, $tag )
55
+ {
56
+ $data = array();
57
+ foreach ( $options as $option_name ) {
58
+ $data[ $option_name ] = get_option( $option_name );
59
+ }
60
+
61
+ printf( '<%s class="bookly-js-editable bookly-js-option %s" data-type="bookly" data-values="%s">%s</%s>',
62
+ $tag,
63
+ $options[0],
64
+ esc_attr( json_encode( $data ) ),
65
+ esc_html( $data[ $options[0] ] ),
66
+ $tag
67
+ );
68
+ }
69
  }
backend/{resources → modules/appearance/resources}/css/bootstrap-editable.css RENAMED
@@ -56,7 +56,7 @@
56
  }
57
 
58
  .editableform-loading {
59
- background: url('../img/loading.gif') center center no-repeat;
60
  height: 25px;
61
  width: auto;
62
  min-width: 25px;
@@ -124,7 +124,7 @@
124
 
125
  /* IOS-style clear button for text inputs */
126
  .editable-clear-x {
127
- background: url('../img/clear.png') center center no-repeat;
128
  display: block;
129
  width: 13px;
130
  height: 13px;
56
  }
57
 
58
  .editableform-loading {
59
+ background: url('../../../../resources/images/loading.gif') center center no-repeat;
60
  height: 25px;
61
  width: auto;
62
  min-width: 25px;
124
 
125
  /* IOS-style clear button for text inputs */
126
  .editable-clear-x {
127
+ background: url('../../../../resources/images/clear.png') center center no-repeat;
128
  display: block;
129
  width: 13px;
130
  height: 13px;
backend/modules/appearance/resources/js/appearance.js CHANGED
@@ -1,180 +1,84 @@
1
  jQuery(function($) {
2
- var // Progress Tracker.
3
- $progress_tracker_option = $('input#ab-progress-tracker-checkbox'),
4
- $staff_name_with_price_option = $('input#ab-staff-name-with-price-checkbox'),
5
- // Time slots setting.
6
- $blocked_timeslots_option = $('input#ab-blocked-timeslots-checkbox'),
7
- $day_one_column_option = $('input#ab-day-one-column-checkbox'),
8
- $show_calendar_option = $('input#ab-show-calendar-checkbox'),
9
- $required_employee_option = $('input#ab-required-employee-checkbox'),
10
- $required_location_option = $('input#ab-required-location-checkbox'),
11
- // Buttons.
12
- $save_button = $('#ajax-send-appearance'),
13
- $reset_button = $('button[type=reset]'),
14
- // Texts.
15
- $text_step_service = $('#bookly_l10n_step_service'),
16
- $text_step_extras = $('#bookly_l10n_step_extras'),
17
- $text_step_time = $('#bookly_l10n_step_time'),
18
- $text_step_cart = $('#bookly_l10n_step_cart'),
19
- $text_step_details = $('#bookly_l10n_step_details'),
20
- $text_step_payment = $('#bookly_l10n_step_payment'),
21
- $text_step_done = $('#bookly_l10n_step_done'),
22
- $text_label_location = $('#bookly_l10n_label_location'),
23
- $text_label_multiply = $('#bookly_l10n_label_multiply'),
24
- $text_label_category = $('#bookly_l10n_label_category'),
25
- $text_option_location = $('#bookly_l10n_option_location'),
26
- $text_option_category = $('#bookly_l10n_option_category'),
27
- $text_option_service = $('#bookly_l10n_option_service'),
28
- $text_option_employee = $('#bookly_l10n_option_employee'),
29
- $text_label_service = $('#bookly_l10n_label_service'),
30
- $text_label_number_of_persons = $('#bookly_l10n_label_number_of_persons'),
31
- $text_label_employee = $('#bookly_l10n_label_employee'),
32
- $text_label_select_date = $('#bookly_l10n_label_select_date'),
33
- $text_label_start_from = $('#bookly_l10n_label_start_from'),
34
- $text_button_next = $('#bookly_l10n_button_next'),
35
- $text_button_back = $('#bookly_l10n_button_back'),
36
- $text_button_book_more = $('#bookly_l10n_button_book_more'),
37
- $text_button_apply = $('#bookly_l10n_button_apply'),
38
- $text_label_finish_by = $('#bookly_l10n_label_finish_by'),
39
- $text_label_name = $('#bookly_l10n_label_name'),
40
- $text_label_phone = $('#bookly_l10n_label_phone'),
41
- $text_label_email = $('#bookly_l10n_label_email'),
42
- $text_label_coupon = $('#bookly_l10n_label_coupon'),
43
- $text_info_service = $('#bookly_l10n_info_service_step'),
44
- $text_info_extras = $('#bookly_l10n_info_extras_step'),
45
- $text_info_time = $('#bookly_l10n_info_time_step'),
46
- $text_info_cart = $('#bookly_l10n_info_cart_step'),
47
- $text_info_details = $('#bookly_l10n_info_details_step'),
48
- $text_info_details_guest = $('#bookly_l10n_info_details_step_guest'),
49
- $text_info_coupon = $('#bookly_l10n_info_coupon'),
50
- $text_info_payment = $('#bookly_l10n_info_payment_step'),
51
- $text_info_complete = $('#bookly_l10n_info_complete_step'),
52
- $text_label_pay_paypal = $('#bookly_l10n_label_pay_paypal'),
53
- $text_label_pay_ccard = $('#bookly_l10n_label_pay_ccard'),
54
- $text_label_ccard_number = $('#bookly_l10n_label_ccard_number'),
55
- $text_label_ccard_expire = $('#bookly_l10n_label_ccard_expire'),
56
- $text_label_ccard_code = $('#bookly_l10n_label_ccard_code'),
57
- $color_picker = $('.bookly-js-color-picker'),
58
- $ab_editable = $('.bookly-editable'),
59
- $text_label_pay_locally = $('#bookly_l10n_label_pay_locally'),
60
- $text_label_pay_mollie = $('#bookly_l10n_label_pay_mollie'),
61
- // Calendars.
62
- $second_step_calendar = $('.ab-selected-date'),
63
- $second_step_calendar_wrap = $('.ab-slot-calendar'),
64
- // Step settings.
65
- $step_settings = $('#bookly-js-step-settings'),
66
  // Step repeat.
67
- $repeat_ui_step_calendar = $('.bookly-repeat-until'),
68
- $repeat_ui_variants = $('[class^="bookly-variant"]'),
69
- $repeat_ui_variant = $('.bookly-repeat-variant'),
70
- $repeat_ui_variant_monthly = $('.variant-monthly'),
71
- $repeat_ui_weekly_week_day = $('.bookly-week-day'),
72
- $repeat_ui_monthly_specific_day = $('.bookly-monthly-specific-day'),
73
- $repeat_ui_monthly_week_day = $('.bookly-monthly-week-day'),
74
- // Step repeat l10n
75
- $text_info_repeat_step = $('#bookly_l10n_info_repeat_step'),
76
- $text_label_repeat = $('#bookly_l10n_label_repeat'),
77
- $text_repeat = $('#bookly_l10n_repeat'),
78
- $text_repeat_another_time = $('#bookly_l10n_repeat_another_time'),
79
- $text_repeat_biweekly = $('#bookly_l10n_repeat_biweekly'),
80
- $text_repeat_daily = $('#bookly_l10n_repeat_daily'),
81
- $text_repeat_day = $('#bookly_l10n_repeat_day'),
82
- $text_repeat_days = $('#bookly_l10n_repeat_days'),
83
- $text_repeat_deleted = $('#bookly_l10n_repeat_deleted'),
84
- $text_repeat_every = $('#bookly_l10n_repeat_every'),
85
- $text_repeat_first = $('#bookly_l10n_repeat_first'),
86
- $text_repeat_first_in_cart_info = $('#bookly_l10n_repeat_first_in_cart_info').first(),
87
- $text_repeat_fourth = $('#bookly_l10n_repeat_fourth'),
88
- $text_repeat_last = $('#bookly_l10n_repeat_last'),
89
- $text_repeat_monthly = $('#bookly_l10n_repeat_monthly'),
90
- $text_repeat_on = $('#bookly_l10n_repeat_on'),
91
- $text_repeat_on_week = $('#bookly_l10n_repeat_on_week'),
92
- $text_repeat_required_week_days = $('#bookly_l10n_repeat_required_week_days'),
93
- $text_repeat_schedule = $('#bookly_l10n_repeat_schedule'),
94
- $text_repeat_schedule_help = $('#bookly_l10n_repeat_schedule_help'),
95
- $text_repeat_schedule_info = $('#bookly_l10n_repeat_schedule_info'),
96
- $text_repeat_second = $('#bookly_l10n_repeat_second'),
97
- $text_repeat_specific = $('#bookly_l10n_repeat_specific'),
98
- $text_repeat_third = $('#bookly_l10n_repeat_third'),
99
- $text_repeat_this_appointment = $('#bookly_l10n_repeat_this_appointment'),
100
- $text_repeat_until = $('#bookly_l10n_repeat_until'),
101
- $text_repeat_weekly = $('#bookly_l10n_repeat_weekly'),
102
- $text_step_repeat = $('#bookly_l10n_step_repeat')
103
  ;
104
 
105
- if (BooklyL10n.intlTelInput.enabled) {
106
- $('.ab-user-phone').intlTelInput({
107
- preferredCountries: [BooklyL10n.intlTelInput.country],
108
- defaultCountry: BooklyL10n.intlTelInput.country,
109
- geoIpLookup: function (callback) {
110
- $.get(ajaxurl, {action: 'bookly_ip_info'}, function () {
111
- }, 'json').always(function (resp) {
112
- var countryCode = (resp && resp.country) ? resp.country : '';
113
- callback(countryCode);
114
- });
115
- },
116
- utilsScript: BooklyL10n.intlTelInput.utils
117
- });
118
- }
119
-
120
- $staff_name_with_price_option.on('change', function () {
121
- var staff = $('.ab-select-employee').val();
122
- if (staff) {
123
- $('.ab-select-employee').val(staff * -1);
124
- }
125
- $('.employee-name-price').toggle($staff_name_with_price_option.prop("checked"));
126
- $('.employee-name').toggle(!$staff_name_with_price_option.prop("checked"));
127
- }).trigger('change');
128
-
129
- // menu fix for WP 3.8.1
130
- $('#toplevel_page_ab-system > ul').css('margin-left', '0px');
131
-
132
- // Tabs.
133
- $('li.bookly-nav-item').on('shown.bs.tab', function (e) {
134
- $step_settings.children().hide();
135
- switch (e.target.getAttribute('data-target')) {
136
- case '#ab-step-1': $step_settings.find('#bookly-js-step-service').show(); break;
137
- case '#ab-step-3': $step_settings.find('#bookly-js-step-time').show(); break;
138
- }
139
  });
 
 
140
 
141
- function getEditableValue(val) {
142
- return $.trim(val == 'Empty' ? '' : val);
143
- }
144
  // Apply color from color picker.
145
  var applyColor = function() {
146
- var color_important = $color_picker.wpColorPicker('color') + '!important';
147
- $('.ab-progress-tracker').find('.active').css('color', $color_picker.wpColorPicker('color')).find('.step').css('background', $color_picker.wpColorPicker('color'));
148
- $('.ab-mobile-step_1 label').css('color', $color_picker.wpColorPicker('color'));
149
- $('.ab-label-error').css('color', $color_picker.wpColorPicker('color'));
150
- $('.bookly-js-actions > a').css('background-color', $color_picker.wpColorPicker('color'));
151
- $('.bookly-next-step, .ab-mobile-next-step').css('background', $color_picker.wpColorPicker('color'));
152
- $('.bookly-week-days label').css('background-color', $color_picker.wpColorPicker('color'));
 
153
  $('.picker__frame').attr('style', 'background: ' + color_important);
154
  $('.picker__header').attr('style', 'border-bottom: ' + '1px solid ' + color_important);
155
- $('.picker__day').mouseenter(function(){
156
  $(this).attr('style', 'color: ' + color_important);
157
- }).mouseleave(function(){ $(this).attr('style', $(this).hasClass('picker__day--selected') ? 'color: ' + color_important : '') });
 
 
158
  $('.picker__day--selected').attr('style', 'color: ' + color_important);
159
  $('.picker__button--clear').attr('style', 'color: ' + color_important);
160
  $('.picker__button--today').attr('style', 'color: ' + color_important);
161
- $('.bookly-extra-step .bookly-extras-thumb.bookly-extras-selected').css('border-color', $color_picker.wpColorPicker('color'));
162
- $('.ab-columnizer .ab-day, .bookly-schedule-date,.bookly-pagination li.active').css({
163
- 'background': $color_picker.wpColorPicker('color'),
164
- 'border-color': $color_picker.wpColorPicker('color')
165
  });
166
- $('.ab-columnizer .ab-hour').off().hover(
167
  function() { // mouse-on
168
  $(this).css({
169
- 'color': $color_picker.wpColorPicker('color'),
170
- 'border': '2px solid ' + $color_picker.wpColorPicker('color')
171
  });
172
- $(this).find('.ab-hour-icon').css({
173
- 'border-color': $color_picker.wpColorPicker('color'),
174
- 'color': $color_picker.wpColorPicker('color')
175
  });
176
- $(this).find('.ab-hour-icon > span').css({
177
- 'background': $color_picker.wpColorPicker('color')
178
  });
179
  },
180
  function() { // mouse-out
@@ -182,28 +86,61 @@ jQuery(function($) {
182
  'color': '#333333',
183
  'border': '1px solid #cccccc'
184
  });
185
- $(this).find('.ab-hour-icon').css({
186
  'border-color': '#333333',
187
  'color': '#cccccc'
188
  });
189
- $(this).find('.ab-hour-icon > span').css({
190
  'background': '#cccccc'
191
  });
192
  }
193
  );
194
- $('.ab-details-step label').css('color', $color_picker.wpColorPicker('color'));
195
- $('.ab-card-form label').css('color', $color_picker.wpColorPicker('color'));
196
- $('.ab-nav-tabs .ladda-button, .bookly-nav-steps .ladda-button, .ab-btn, .bookly-round, .bookly-square').css('background-color', $color_picker.wpColorPicker('color'));
197
- $('.bookly-triangle').css('border-bottom-color', $color_picker.wpColorPicker('color'));
198
- $('.bookly-back-step, .bookly-next-step').css('background', $color_picker.wpColorPicker('color'));
199
- var style_arrow = '.picker__nav--next:before { border-left: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; } .picker__nav--prev:before { border-right: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; }';
200
- $('#ab--style-arrow').html(style_arrow);
201
  };
 
 
202
  $color_picker.wpColorPicker({
203
  change : applyColor
204
  });
205
- // Init calendars.
206
- $('.ab-date-from').pickadate({
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  formatSubmit : 'yyyy-mm-dd',
208
  format : BooklyL10n.date_format,
209
  min : true,
@@ -218,7 +155,38 @@ jQuery(function($) {
218
  firstDay : BooklyL10n.start_of_week == 1
219
  });
220
 
221
- $second_step_calendar.pickadate({
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  formatSubmit : 'yyyy-mm-dd',
223
  format : BooklyL10n.date_format,
224
  min : true,
@@ -239,224 +207,50 @@ jQuery(function($) {
239
  this.open(false);
240
  }
241
  });
242
- $second_step_calendar_wrap.find('.picker__holder').css({ top : '0px', left : '0px' });
243
- $second_step_calendar_wrap.toggle($show_calendar_option.prop('checked'));
244
-
245
- // Update options.
246
- $save_button.on('click', function(e) {
247
- e.preventDefault();
248
- var data = {
249
- action: 'bookly_update_appearance_options',
250
- options: {
251
- // Color.
252
- 'color' : $color_picker.wpColorPicker('color'),
253
- // Info text.
254
- 'text_info_service_step' : getEditableValue($text_info_service.text()),
255
- 'text_info_extras_step' : getEditableValue($text_info_extras.text()),
256
- 'text_info_time_step' : getEditableValue($text_info_time.text()),
257
- 'text_info_cart_step' : getEditableValue($text_info_cart.text()),
258
- 'text_info_details_step' : getEditableValue($text_info_details.text()),
259
- 'text_info_details_step_guest' : getEditableValue($text_info_details_guest.text()),
260
- 'text_info_payment_step' : getEditableValue($text_info_payment.text()),
261
- 'text_info_complete_step' : getEditableValue($text_info_complete.text()),
262
- 'text_info_coupon' : getEditableValue($text_info_coupon.text()),
263
- // Step and label texts.
264
- 'text_step_service' : getEditableValue($text_step_service.text()),
265
- 'text_step_extras' : getEditableValue($text_step_extras.text()),
266
- 'text_step_time' : getEditableValue($text_step_time.text()),
267
- 'text_step_cart' : getEditableValue($text_step_cart.text()),
268
- 'text_step_details' : getEditableValue($text_step_details.text()),
269
- 'text_step_payment' : getEditableValue($text_step_payment.text()),
270
- 'text_step_done' : getEditableValue($text_step_done.text()),
271
- 'text_label_location' : getEditableValue($text_label_location.text()),
272
- 'text_label_category' : getEditableValue($text_label_category.text()),
273
- 'text_label_service' : getEditableValue($text_label_service.text()),
274
- 'text_label_number_of_persons' : getEditableValue($text_label_number_of_persons.text()),
275
- 'text_label_multiply' : getEditableValue($text_label_multiply.text()),
276
- 'text_label_employee' : getEditableValue($text_label_employee.text()),
277
- 'text_label_select_date' : getEditableValue($text_label_select_date.text()),
278
- 'text_label_start_from' : getEditableValue($text_label_start_from.text()),
279
- 'text_button_next' : getEditableValue($text_button_next.text()),
280
- 'text_button_back' : getEditableValue($text_button_back.text()),
281
- 'text_button_apply' : getEditableValue($text_button_apply.text()),
282
- 'text_button_book_more' : getEditableValue($text_button_book_more.text()),
283
- 'text_label_finish_by' : getEditableValue($text_label_finish_by.text()),
284
- 'text_label_name' : getEditableValue($text_label_name.text()),
285
- 'text_label_phone' : getEditableValue($text_label_phone.text()),
286
- 'text_label_email' : getEditableValue($text_label_email.text()),
287
- 'text_label_coupon' : getEditableValue($text_label_coupon.text()),
288
- 'text_option_location' : getEditableValue($text_option_location.text()),
289
- 'text_option_category' : getEditableValue($text_option_category.text()),
290
- 'text_option_service' : getEditableValue($text_option_service.text()),
291
- 'text_option_employee' : getEditableValue($text_option_employee.text()),
292
- 'text_label_pay_locally' : getEditableValue($text_label_pay_locally.text()),
293
- 'text_label_pay_mollie' : getEditableValue($text_label_pay_mollie.text()),
294
- 'text_label_pay_paypal' : getEditableValue($text_label_pay_paypal.text()),
295
- 'text_label_pay_ccard' : getEditableValue($text_label_pay_ccard.text()),
296
- 'text_label_ccard_number' : getEditableValue($text_label_ccard_number.text()),
297
- 'text_label_ccard_expire' : getEditableValue($text_label_ccard_expire.text()),
298
- 'text_label_ccard_code' : getEditableValue($text_label_ccard_code.text()),
299
- // Repeat
300
- 'text_info_repeat_step' : getEditableValue($text_info_repeat_step.text()),
301
- 'text_label_repeat' : getEditableValue($text_label_repeat.text()),
302
- 'text_repeat' : getEditableValue($text_repeat.text()),
303
- 'text_repeat_another_time' : getEditableValue($text_repeat_another_time.text()),
304
- 'text_repeat_biweekly' : getEditableValue($text_repeat_biweekly.text()),
305
- 'text_repeat_daily' : getEditableValue($text_repeat_daily.text()),
306
- 'text_repeat_day' : getEditableValue($text_repeat_day.text()),
307
- 'text_repeat_days' : getEditableValue($text_repeat_days.text()),
308
- 'text_repeat_deleted' : getEditableValue($text_repeat_deleted.text()),
309
- 'text_repeat_every' : getEditableValue($text_repeat_every.text()),
310
- 'text_repeat_first' : getEditableValue($text_repeat_first.text()),
311
- 'text_repeat_first_in_cart_info' : getEditableValue($text_repeat_first_in_cart_info.text()),
312
- 'text_repeat_fourth' : getEditableValue($text_repeat_fourth.text()),
313
- 'text_repeat_last' : getEditableValue($text_repeat_last.text()),
314
- 'text_repeat_monthly' : getEditableValue($text_repeat_monthly.text()),
315
- 'text_repeat_on' : getEditableValue($text_repeat_on.text()),
316
- 'text_repeat_on_week' : getEditableValue($text_repeat_on_week.text()),
317
- 'text_repeat_required_week_days' : getEditableValue($text_repeat_required_week_days.text()),
318
- 'text_repeat_schedule' : getEditableValue($text_repeat_schedule.text()),
319
- 'text_repeat_schedule_help' : getEditableValue($text_repeat_schedule_help.text()),
320
- 'text_repeat_schedule_info' : getEditableValue($text_repeat_schedule_info.text()),
321
- 'text_repeat_second' : getEditableValue($text_repeat_second.text()),
322
- 'text_repeat_specific' : getEditableValue($text_repeat_specific.text()),
323
- 'text_repeat_third' : getEditableValue($text_repeat_third.text()),
324
- 'text_repeat_this_appointment' : getEditableValue($text_repeat_this_appointment.text()),
325
- 'text_repeat_until' : getEditableValue($text_repeat_until.text()),
326
- 'text_repeat_weekly' : getEditableValue($text_repeat_weekly.text()),
327
- 'text_step_repeat' : getEditableValue($text_step_repeat.text()),
328
- // Validator.
329
- 'text_required_location' : getEditableValue($('#bookly_l10n_required_location').html()),
330
- 'text_required_service' : getEditableValue($('#bookly_l10n_required_service').html()),
331
- 'text_required_employee' : getEditableValue($('#bookly_l10n_required_employee').html()),
332
- 'text_required_name' : getEditableValue($('#bookly_l10n_required_name').html()),
333
- 'text_required_phone' : getEditableValue($('#bookly_l10n_required_phone').html()),
334
- 'text_required_email' : getEditableValue($('#bookly_l10n_required_email').html()),
335
- // Checkboxes.
336
- 'progress_tracker' : Number($progress_tracker_option.prop('checked')),
337
- 'staff_name_with_price': Number($staff_name_with_price_option.prop('checked')),
338
- 'blocked_timeslots' : Number($blocked_timeslots_option.prop('checked')),
339
- 'day_one_column' : Number($day_one_column_option.prop('checked')),
340
- 'show_calendar' : Number($show_calendar_option.prop('checked')),
341
- 'required_employee' : Number($required_employee_option.prop('checked')),
342
- 'required_location' : Number($required_location_option.prop('checked'))
343
- } // options
344
- }; // data
345
-
346
- // update data and show spinner while updating
347
- var ladda = Ladda.create(this);
348
- ladda.start();
349
- $.post(ajaxurl, data, function (response) {
350
- ladda.stop();
351
- booklyAlert({success : [BooklyL10n.saved]});
352
- });
353
- });
354
-
355
- // Reset options to defaults.
356
- $reset_button.on('click', function() {
357
- // Reset color.
358
- $color_picker.wpColorPicker('color', $color_picker.data('selected'));
359
-
360
- // Reset texts.
361
- jQuery.each($('.editable:not([data-type=multiple])'), function() {
362
- $(this).text($(this).data('option-default')); //default value for texts
363
- $(this).editable('setValue', $(this).data('option-default')); // default value for editable inputs
364
- });
365
-
366
- // Reset texts.
367
- jQuery.each($('.ab-service-list, .ab-employee-list'), function() {
368
- $(this).html($(this).data('default')); //default value
369
- });
370
-
371
- // default value for multiple inputs
372
- $('[data-type=multiple]').each(function () {
373
- var $elem = $(this),
374
- options = $elem.data('options-default'),
375
- $target;
376
- $elem.data('options',options);
377
- $.each(options, function (name, value) {
378
- $target = $('#' + name);
379
- if ($target.is(':text')) {
380
- $target.val(value);
381
- } else {
382
- $target.text(value);
383
- }
384
- $target = $('[name=' + name + ']:text');
385
- if ($target.is(':text')) {
386
- $target.val(value);
387
- } else {
388
- $target.text(value);
389
- }
390
- });
391
- });
392
- });
393
-
394
- $progress_tracker_option.change(function(){
395
- $('.ab-progress-tracker').toggle($(this).is(':checked'));
396
- }).trigger('change');
397
-
398
- var day_one_column = $('.ab-day-one-column'),
399
- day_columns = $('.ab-day-columns');
400
-
401
- if ($show_calendar_option.prop('checked')) {
402
- $second_step_calendar_wrap.show();
403
- day_columns.find('.col3,.col4,.col5,.col6,.col7').hide();
404
- day_columns.find('.col2 button:gt(0)').attr('style', 'display: none !important');
405
- day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').hide();
406
- }
407
 
408
- // Change show calendar
409
- $show_calendar_option.change(function() {
410
  if (this.checked) {
411
- $second_step_calendar_wrap.show();
412
- day_columns.find('.col3,.col4,.col5,.col6,.col7').hide();
413
- day_columns.find('.col2 button:gt(0)').attr('style', 'display: none !important');
414
- day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').hide();
415
  } else {
416
- $second_step_calendar_wrap.hide();
417
- day_columns.find('.col2 button:gt(0)').attr('style', 'display: block !important');
418
- day_columns.find('.col2 button.ab-first-child').attr('style', 'background: ' + $color_picker.wpColorPicker('color') + '!important;display: block !important');
419
- day_columns.find('.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
420
- day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
421
  }
422
- });
423
 
424
- // Change blocked time slots.
425
- $blocked_timeslots_option.change(function(){
426
  if (this.checked) {
427
- $('.ab-hour.no-booked').removeClass('no-booked').addClass('booked');
428
  } else {
429
- $('.ab-hour.booked').removeClass('booked').addClass('no-booked');
430
  }
431
  });
432
 
433
- // Change day one column.
434
- $day_one_column_option.change(function() {
435
  if (this.checked) {
436
- day_one_column.show();
437
- day_columns.hide();
438
  } else {
439
- day_one_column.hide();
440
- day_columns.show();
441
  }
442
  });
443
 
444
- // Clickable week-days.
445
- $('.bookly-week-day').on('change', function () {
446
- var self = $(this);
447
- if (self.is(':checked') && !self.parent().hasClass('active')) {
448
- self.parent().addClass('active');
449
- } else if (self.parent().hasClass('active')) {
450
- self.parent().removeClass('active')
451
- }
452
- });
453
-
454
- var multiple = function (options) {
455
- this.init('multiple', options, multiple.defaults);
456
- };
457
 
458
- // Step repeat.
459
- $repeat_ui_step_calendar.pickadate({
460
  formatSubmit : 'yyyy-mm-dd',
461
  format : BooklyL10n.date_format,
462
  min : true,
@@ -470,17 +264,17 @@ jQuery(function($) {
470
  onRender : applyColor,
471
  firstDay : BooklyL10n.start_of_week == 1
472
  });
473
- $repeat_ui_variant.on('change', function () {
474
- $repeat_ui_variants.hide();
475
- $('.bookly-variant-' + this.value).show()
476
  }).trigger('change');
477
 
478
- $repeat_ui_variant_monthly.on('change', function () {
479
- $repeat_ui_monthly_week_day.toggle(this.value != 'specific');
480
- $repeat_ui_monthly_specific_day.toggle(this.value == 'specific');
481
  }).trigger('change');
482
 
483
- $repeat_ui_weekly_week_day.on('change', function () {
484
  var $this = $(this);
485
  if ($this.is(':checked')) {
486
  $this.parent().not("[class*='active']").addClass('active');
@@ -489,110 +283,191 @@ jQuery(function($) {
489
  }
490
  });
491
 
492
- // Inherit from Abstract input.
493
- $.fn.editableutils.inherit(multiple, $.fn.editabletypes.abstractinput);
494
 
495
- $.extend(multiple.prototype, {
496
- container: null,
497
- $elem : null,
498
- render : function() {
499
- this.container = jQuery('div.bookly-js-container', this.tpl);
500
- },
501
-
502
- value2html: function (value, element) { },
503
 
504
- activate: function () {
505
- this.container.find(':text:eq(0)').focus();
506
- },
 
 
 
 
 
 
 
 
 
 
 
507
 
508
- value2input: function(value) {
509
- if(!value) {
510
- return;
511
- }
512
- var container = this.container;
513
- this.$elem = value.elem;
514
- if (!this.$elem.data('options')) {
515
- this.$elem.data('options', this.$elem.data('options-default'));
516
- }
517
- container.html('');
518
- $.each(this.$elem.data('options'), function (id, value) {
519
- $('<input/>', {
520
- type : 'text',
521
- class: 'form-control input-sm',
522
- name : id,
523
- value: value || ''
524
- }).appendTo(container);
525
- });
526
- },
527
 
528
- input2value: function() {
529
- var options = {};
530
- $.each(this.container.find(':text'), function () {
531
- var name = $(this).attr('name'),
532
- $target = $('#' + name);
533
- options[name] = this.value;
534
- if($target.is(':text')){
535
- $target.val(this.value);
536
- } else {
537
- $target.text(this.value);
538
- }
539
- });
540
- this.$elem.data('options', options);
541
- return {elem: this.$elem};
542
  }
543
  });
544
- multiple.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
545
- tpl: '<div class="bookly-editable-multiple"><div class="bookly-js-container">',
546
- inputclass: ''
 
 
 
 
 
 
 
 
 
 
 
547
  });
548
 
549
- $.fn.editabletypes.multiple = multiple;
550
- $('[data-type=multiple]').each(function () {
551
- var $elem = $(this);
552
- $elem.editable({ value: { elem: $elem}});
 
 
 
 
 
 
 
 
 
 
553
  });
554
 
555
- $text_info_service.add('#bookly_l10n_info_time_step').add('#bookly_l10n_info_details_step').add('#bookly_l10n_info_payment_step').add('#bookly_l10n_info_complete_step').add('#bookly_l10n_info_coupon').editable({placement: 'right'});
556
- $ab_editable.editable();
557
 
558
- $.fn.editableform.template = '<form class="form-inline editableform"> <div class="control-group"> <div> <div class="editable-input"></div><div class="editable-buttons"></div></div><div class="editable-notes"></div><div class="editable-error-block"></div></div> </form>';
559
- $.fn.editableform.buttons = '<div class="btn-group btn-group-sm"><button type="submit" class="btn btn-success editable-submit"><span class="glyphicon glyphicon-ok"></span></button><button type="button" class="btn btn-default editable-cancel"><span class="glyphicon glyphicon-remove"></span></button></div>';
 
 
 
 
 
 
 
 
 
 
 
560
 
561
- $ab_editable.on('shown', function(e, editable) {
562
- $('.popover').find('.arrow').removeClass().addClass('popover-arrow');
563
- $('.editable-notes').html($(e.target).data('notes'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  });
565
- $('[data-type="multiple"]').on('shown', function(e, editable) {
566
- $('.popover').find('.arrow').removeClass().addClass('popover-arrow');
 
 
 
 
 
 
567
  });
568
 
569
- $("[data-mirror]").on('save', function (e, params) {
570
- $("." + $(e.target).data('mirror')).editable('setValue', params.newValue);
571
- switch ($(e.target).data('mirror')){
572
- case 'text_services':
573
- $(".ab-service-list").html(params.newValue.label);
574
- break;
575
- case 'text_locations':
576
- $(".ab-location-list").html(params.newValue.label);
577
- break;
578
- case 'text_employee':
579
- $(".ab-employee-list").html(params.newValue.label);
580
- break;
 
 
 
581
  }
582
  });
583
 
584
- $('input[type=radio]').change(function () {
585
- if ($('.ab-card-payment').is(':checked')) {
586
- $('form.ab-card-form').show();
587
- } else {
588
- $('form.ab-card-form').hide();
589
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  });
591
 
592
- $('#bookly-js-hint-alert').on('closed.bs.alert', function () {
593
- $.ajax({
594
- url: ajaxurl,
595
- data: { action: 'bookly_dismiss_appearance_notice' }
 
 
 
 
596
  });
597
- })
598
- }); // jQuery
 
 
 
 
 
 
 
1
  jQuery(function($) {
2
+ var
3
+ $color_picker = $('.bookly-js-color-picker'),
4
+ $editableElements = $('.bookly-js-editable'),
5
+ $show_progress_tracker = $('#bookly-show-progress-tracker'),
6
+ $step_settings = $('#bookly-step-settings'),
7
+ // Service step.
8
+ $staff_name_with_price = $('#bookly-staff-name-with-price'),
9
+ $service_name_with_duration = $('#bookly-service-name-with-duration'),
10
+ $required_employee = $('#bookly-required-employee'),
11
+ $required_location = $('#bookly-required-location'),
12
+ // Time step.
13
+ $time_step_calendar = $('.bookly-js-selected-date'),
14
+ $time_step_calendar_wrap = $('.bookly-js-slot-calendar'),
15
+ $show_blocked_timeslots = $('#bookly-show-blocked-timeslots'),
16
+ $show_day_one_column = $('#bookly-show-day-one-column'),
17
+ $show_calendar = $('#bookly-show-calendar'),
18
+ $day_one_column = $('#bookly-day-one-column'),
19
+ $day_multi_columns = $('#bookly-day-multi-columns'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  // Step repeat.
21
+ $repeat_step_calendar = $('.bookly-js-repeat-until'),
22
+ $repeat_variants = $('[class^="bookly-js-variant"]'),
23
+ $repeat_variant = $('.bookly-js-repeat-variant'),
24
+ $repeat_variant_monthly = $('.bookly-js-repeat-variant-monthly'),
25
+ $repeat_weekly_week_day = $('.bookly-js-week-day'),
26
+ $repeat_monthly_specific_day = $('.bookly-js-monthly-specific-day'),
27
+ $repeat_monthly_week_day = $('.bookly-js-monthly-week-day'),
28
+ // Step details.
29
+ $required_phone = $('#bookly-cst-required-phone'),
30
+ $show_login_button = $('#bookly-show-login-button'),
31
+ $first_last_name = $('#bookly-cst-first-last-name'),
32
+ $show_notes_field = $('#bookly-show-notes'),
33
+ // Buttons.
34
+ $save_button = $('#ajax-send-appearance'),
35
+ $reset_button = $('button[type=reset]'),
36
+ $checkboxes = $('#bookly-appearance').find('input[type="checkbox"]')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  ;
38
 
39
+ $checkboxes.each(function () {
40
+ $(this).data('default', $(this).prop('checked'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  });
42
+ // Menu fix for WP 3.8.1
43
+ $('#toplevel_page_ab-system > ul').css('margin-left', '0px');
44
 
 
 
 
45
  // Apply color from color picker.
46
  var applyColor = function() {
47
+ var color = $color_picker.wpColorPicker('color'),
48
+ color_important = color + '!important;';
49
+ $('.bookly-progress-tracker').find('.active').css('color', color).find('.step').css('background', color);
50
+ $('.bookly-js-mobile-step-1 label').css('color', color);
51
+ $('.bookly-label-error').css('color', color);
52
+ $('.bookly-js-actions > a').css('background-color', color);
53
+ $('.bookly-js-mobile-next-step').css('background', color);
54
+ $('.bookly-js-week-days label').css('background-color', color);
55
  $('.picker__frame').attr('style', 'background: ' + color_important);
56
  $('.picker__header').attr('style', 'border-bottom: ' + '1px solid ' + color_important);
57
+ $('.picker__day').off().mouseenter(function() {
58
  $(this).attr('style', 'color: ' + color_important);
59
+ }).mouseleave(function(){
60
+ $(this).attr('style', $(this).hasClass('picker__day--selected') ? 'color: ' + color_important : '')
61
+ });
62
  $('.picker__day--selected').attr('style', 'color: ' + color_important);
63
  $('.picker__button--clear').attr('style', 'color: ' + color_important);
64
  $('.picker__button--today').attr('style', 'color: ' + color_important);
65
+ $('.bookly-extra-step .bookly-extras-thumb.bookly-extras-selected').css('border-color', color);
66
+ $('.bookly-columnizer .bookly-day, .bookly-schedule-date,.bookly-pagination li.active').css({
67
+ 'background': color,
68
+ 'border-color': color
69
  });
70
+ $('.bookly-columnizer .bookly-hour').off().hover(
71
  function() { // mouse-on
72
  $(this).css({
73
+ 'color': color,
74
+ 'border': '2px solid ' + color
75
  });
76
+ $(this).find('.bookly-hour-icon').css({
77
+ 'border-color': color,
78
+ 'color': color
79
  });
80
+ $(this).find('.bookly-hour-icon > span').css({
81
+ 'background': color
82
  });
83
  },
84
  function() { // mouse-out
86
  'color': '#333333',
87
  'border': '1px solid #cccccc'
88
  });
89
+ $(this).find('.bookly-hour-icon').css({
90
  'border-color': '#333333',
91
  'color': '#cccccc'
92
  });
93
+ $(this).find('.bookly-hour-icon > span').css({
94
  'background': '#cccccc'
95
  });
96
  }
97
  );
98
+ $('.bookly-details-step label').css('color', color);
99
+ $('.bookly-card-form label').css('color', color);
100
+ $('.bookly-nav-tabs .ladda-button, .bookly-nav-steps .ladda-button, .bookly-btn, .bookly-round, .bookly-square').css('background-color', color);
101
+ $('.bookly-triangle').css('border-bottom-color', color);
102
+ $('#bookly-pickadate-style').html('.picker__nav--next:before { border-left: 6px solid ' + color_important + ' } .picker__nav--prev:before { border-right: 6px solid ' + color_important + ' }');
 
 
103
  };
104
+
105
+ // Init color picker.
106
  $color_picker.wpColorPicker({
107
  change : applyColor
108
  });
109
+
110
+ // Init editable elements.
111
+ $editableElements.editable();
112
+
113
+ // Show progress tracker.
114
+ $show_progress_tracker.on('change', function() {
115
+ $('.bookly-progress-tracker').toggle(this.checked);
116
+ }).trigger('change');
117
+
118
+ // Show step specific settings.
119
+ $('li.bookly-nav-item').on('shown.bs.tab', function (e) {
120
+ $step_settings.children().hide();
121
+ switch (e.target.getAttribute('data-target')) {
122
+ case '#bookly-step-1': $step_settings.find('.bookly-js-service-settings').show(); break;
123
+ case '#bookly-step-3': $step_settings.find('.bookly-js-time-settings').show(); break;
124
+ case '#bookly-step-6': $step_settings.find('.bookly-js-details-settings').show(); break;
125
+ case '#bookly-step-7': $step_settings.find('.bookly-js-payment-settings').show(); break;
126
+ case '#bookly-step-8': $step_settings.find('.bookly-js-done-settings').show(); break;
127
+ }
128
+ });
129
+
130
+ // Dismiss help notice.
131
+ $('#bookly-js-hint-alert').on('closed.bs.alert', function () {
132
+ $.ajax({
133
+ url: ajaxurl,
134
+ data: { action: 'bookly_dismiss_appearance_notice', csrf_token : BooklyL10n.csrf_token }
135
+ });
136
+ });
137
+
138
+ /**
139
+ * Step Service
140
+ */
141
+
142
+ // Init calendar.
143
+ $('.bookly-js-date-from').pickadate({
144
  formatSubmit : 'yyyy-mm-dd',
145
  format : BooklyL10n.date_format,
146
  min : true,
155
  firstDay : BooklyL10n.start_of_week == 1
156
  });
157
 
158
+ // Show price next to staff member name.
159
+ $staff_name_with_price.on('change', function () {
160
+ var staff = $('.bookly-js-select-employee').val();
161
+ if (staff) {
162
+ $('.bookly-js-select-employee').val(staff * -1);
163
+ }
164
+ $('.employee-name-price').toggle($staff_name_with_price.prop("checked"));
165
+ $('.employee-name').toggle(!$staff_name_with_price.prop("checked"));
166
+ }).trigger('change');
167
+
168
+ // Show duration next to service name.
169
+ $service_name_with_duration.on('change', function () {
170
+ var service = $('.bookly-js-select-service').val();
171
+ if (service) {
172
+ $('.bookly-js-select-service').val(service * -1);
173
+ }
174
+ $('.service-name-duration').toggle($service_name_with_duration.prop("checked"));
175
+ $('.service-name').toggle(!$service_name_with_duration.prop("checked"));
176
+ }).trigger('change');
177
+
178
+ // Clickable week-days.
179
+ $repeat_weekly_week_day.on('change', function () {
180
+ $(this).parent().toggleClass('active', this.checked);
181
+ });
182
+
183
+
184
+ /**
185
+ * Step Time
186
+ */
187
+
188
+ // Init calendar.
189
+ $time_step_calendar.pickadate({
190
  formatSubmit : 'yyyy-mm-dd',
191
  format : BooklyL10n.date_format,
192
  min : true,
207
  this.open(false);
208
  }
209
  });
210
+ $time_step_calendar_wrap.find('.picker__holder').css({ top : '0px', left : '0px' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
+ // Show calendar.
213
+ $show_calendar.on('change', function() {
214
  if (this.checked) {
215
+ $time_step_calendar_wrap.show();
216
+ $day_multi_columns.find('.col3,.col4,.col5,.col6,.col7').hide();
217
+ $day_multi_columns.find('.col2 button:gt(0)').attr('style', 'display: none !important');
218
+ $day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').hide();
219
  } else {
220
+ $time_step_calendar_wrap.hide();
221
+ $day_multi_columns.find('.col2 button:gt(0)').attr('style', 'display: block !important');
222
+ $day_multi_columns.find('.col2 button.bookly-js-first-child').attr('style', 'background: ' + $color_picker.wpColorPicker('color') + '!important;display: block !important');
223
+ $day_multi_columns.find('.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
224
+ $day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
225
  }
226
+ }).trigger('change');
227
 
228
+ // Show blocked time slots.
229
+ $show_blocked_timeslots.on('change', function(){
230
  if (this.checked) {
231
+ $('.bookly-hour.no-booked').removeClass('no-booked').addClass('booked');
232
  } else {
233
+ $('.bookly-hour.booked').removeClass('booked').addClass('no-booked');
234
  }
235
  });
236
 
237
+ // Show day as one column.
238
+ $show_day_one_column.change(function() {
239
  if (this.checked) {
240
+ $day_one_column.show();
241
+ $day_multi_columns.hide();
242
  } else {
243
+ $day_one_column.hide();
244
+ $day_multi_columns.show();
245
  }
246
  });
247
 
248
+ /**
249
+ * Step repeat.
250
+ */
 
 
 
 
 
 
 
 
 
 
251
 
252
+ // Init calendar.
253
+ $repeat_step_calendar.pickadate({
254
  formatSubmit : 'yyyy-mm-dd',
255
  format : BooklyL10n.date_format,
256
  min : true,
264
  onRender : applyColor,
265
  firstDay : BooklyL10n.start_of_week == 1
266
  });
267
+ $repeat_variant.on('change', function () {
268
+ $repeat_variants.hide();
269
+ $('.bookly-js-variant-' + this.value).show()
270
  }).trigger('change');
271
 
272
+ $repeat_variant_monthly.on('change', function () {
273
+ $repeat_monthly_week_day.toggle(this.value != 'specific');
274
+ $repeat_monthly_specific_day.toggle(this.value == 'specific');
275
  }).trigger('change');
276
 
277
+ $repeat_weekly_week_day.on('change', function () {
278
  var $this = $(this);
279
  if ($this.is(':checked')) {
280
  $this.parent().not("[class*='active']").addClass('active');
283
  }
284
  });
285
 
 
 
286
 
287
+ /**
288
+ * Step Details
289
+ */
 
 
 
 
 
290
 
291
+ // Init phone field.
292
+ if (BooklyL10n.intlTelInput.enabled) {
293
+ $('.bookly-user-phone').intlTelInput({
294
+ preferredCountries: [BooklyL10n.intlTelInput.country],
295
+ initialCountry: BooklyL10n.intlTelInput.country,
296
+ geoIpLookup: function (callback) {
297
+ $.get('https://ipinfo.io', function() {}, 'jsonp').always(function(resp) {
298
+ var countryCode = (resp && resp.country) ? resp.country : '';
299
+ callback(countryCode);
300
+ });
301
+ },
302
+ utilsScript: BooklyL10n.intlTelInput.utils
303
+ });
304
+ }
305
 
306
+ // Show login form.
307
+ $show_login_button.change(function () {
308
+ $('#bookly-js-show-login-form').toggle(this.checked);
309
+ }).trigger('change');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
+ // Show first and last name.
312
+ $first_last_name.on('change', function () {
313
+ $first_last_name.popover('toggle');
314
+ if (this.checked) {
315
+ $('.bookly-details-full-name').css('display', 'none');
316
+ $('.bookly-details-first-last-name').css('display', 'table');
317
+ } else {
318
+ $('.bookly-details-full-name').css('display', 'block');
319
+ $('.bookly-details-first-last-name').css('display', 'none');
 
 
 
 
 
320
  }
321
  });
322
+
323
+ // Show notes field.
324
+ $show_notes_field.change(function () {
325
+ $('#bookly-js-notes').toggle(this.checked);
326
+ }).trigger('change');
327
+
328
+ /**
329
+ * Step Payment.
330
+ */
331
+
332
+ // Switch payment step view (single/several services).
333
+ $('#bookly-payment-step-view').on('change', function () {
334
+ $('.bookly-js-payment-single-app').toggle(this.value == 'single-app');
335
+ $('.bookly-js-payment-several-apps').toggle(this.value == 'several-apps');
336
  });
337
 
338
+ // Show credit card form.
339
+ $('.bookly-payment-nav :radio').on('change', function () {
340
+ $('form.bookly-card-form').toggle(this.id == 'bookly-card-payment');
341
+ });
342
+
343
+ /**
344
+ * Step Done.
345
+ */
346
+
347
+ // Switch done step view (success/error).
348
+ $('#bookly-done-step-view').on('change', function () {
349
+ $('.bookly-js-done-success').toggle(this.value == 'booking-success');
350
+ $('.bookly-js-done-limit-error').toggle(this.value == 'booking-limit-error');
351
+ $('.bookly-js-done-processing').toggle(this.value == 'booking-processing');
352
  });
353
 
 
 
354
 
355
+ /**
356
+ * Misc.
357
+ */
358
+
359
+ // Custom CSS.
360
+ $('#bookly-custom-css-save').on('click', function (e) {
361
+ var $custom_css = $('#bookly-custom-css'),
362
+ $modal = $('#bookly-custom-css-dialog');
363
+
364
+ saved_css = $custom_css.val();
365
+
366
+ var ladda = Ladda.create(this);
367
+ ladda.start();
368
 
369
+ $.ajax({
370
+ url : ajaxurl,
371
+ type : 'POST',
372
+ data : {
373
+ action : 'bookly_save_custom_css',
374
+ csrf_token : BooklyL10n.csrf_token,
375
+ custom_css : $custom_css.val()
376
+ },
377
+ dataType : 'json',
378
+ success : function (response) {
379
+ if (response.success) {
380
+ $modal.modal('hide');
381
+ booklyAlert({success : [response.data.message]});
382
+ }
383
+ },
384
+ complete : function () {
385
+ ladda.stop();
386
+ }
387
+ });
388
  });
389
+
390
+ $('#bookly-custom-css-cancel').on('click', function (e) {
391
+ var $custom_css = $('#bookly-custom-css'),
392
+ $modal = $('#bookly-custom-css-dialog');
393
+
394
+ $modal.modal('hide');
395
+
396
+ $custom_css.val(saved_css);
397
  });
398
 
399
+ $('#bookly-custom-css').keydown(function(e) {
400
+ if(e.keyCode === 9) { //tab button
401
+ var start = this.selectionStart;
402
+ var end = this.selectionEnd;
403
+
404
+ var $this = $(this);
405
+ var value = $this.val();
406
+
407
+ $this.val(value.substring(0, start)
408
+ + "\t"
409
+ + value.substring(end));
410
+
411
+ this.selectionStart = this.selectionEnd = start + 1;
412
+
413
+ e.preventDefault();
414
  }
415
  });
416
 
417
+ // Save options.
418
+ $save_button.on('click', function(e) {
419
+ e.preventDefault();
420
+ // Prepare data.
421
+ var data = {
422
+ action: 'bookly_update_appearance_options',
423
+ csrf_token: BooklyL10n.csrf_token,
424
+ options: {
425
+ // Color.
426
+ 'bookly_app_color' : $color_picker.wpColorPicker('color'),
427
+ // Checkboxes.
428
+ 'bookly_app_service_name_with_duration' : Number($service_name_with_duration.prop('checked')),
429
+ 'bookly_app_show_blocked_timeslots' : Number($show_blocked_timeslots.prop('checked')),
430
+ 'bookly_app_show_calendar' : Number($show_calendar.prop('checked')),
431
+ 'bookly_app_show_day_one_column' : Number($show_day_one_column.prop('checked')),
432
+ 'bookly_app_show_login_button' : Number($show_login_button.prop('checked')),
433
+ 'bookly_app_show_notes' : Number($show_notes_field.prop('checked')),
434
+ 'bookly_app_show_progress_tracker' : Number($show_progress_tracker.prop('checked')),
435
+ 'bookly_app_staff_name_with_price' : Number($staff_name_with_price.prop('checked')),
436
+ 'bookly_app_required_employee' : Number($required_employee.prop('checked')),
437
+ 'bookly_app_required_location' : Number($required_location.prop('checked')),
438
+ 'bookly_cst_required_phone' : Number($required_phone.prop('checked')),
439
+ 'bookly_cst_first_last_name' : Number($first_last_name.prop('checked'))
440
+ }
441
+ };
442
+ // Add data from editable elements.
443
+ $editableElements.each(function () {
444
+ $.extend(data.options, $(this).editable('getValue', true));
445
+ });
446
+
447
+ // Update data and show spinner while updating.
448
+ var ladda = Ladda.create(this);
449
+ ladda.start();
450
+ $.post(ajaxurl, data, function (response) {
451
+ ladda.stop();
452
+ booklyAlert({success : [BooklyL10n.saved]});
453
+ });
454
  });
455
 
456
+ // Reset options to defaults.
457
+ $reset_button.on('click', function() {
458
+ // Reset color.
459
+ $color_picker.wpColorPicker('color', $color_picker.data('selected'));
460
+
461
+ // Reset editable texts.
462
+ $editableElements.each(function () {
463
+ $(this).editable('setValue', $.extend({}, $(this).data('values')));
464
  });
465
+
466
+ $checkboxes.each(function () {
467
+ if ($(this).prop('checked') != $(this).data('default')) {
468
+ $(this).prop('checked', $(this).data('default')).trigger('change');
469
+ }
470
+ });
471
+ $first_last_name.popover('hide');
472
+ });
473
+ });
backend/modules/appearance/resources/js/bootstrap-editable.bookly.js ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ var Bookly = function (options) {
3
+ this.init('bookly', options, Bookly.defaults);
4
+ };
5
+
6
+ // Inherit from Abstract input.
7
+ $.fn.editableutils.inherit(Bookly, $.fn.editabletypes.abstractinput);
8
+
9
+ $.extend(Bookly.prototype, {
10
+ html2value: function(html) {
11
+ return $.extend({}, $(this.options.scope).data('values'));
12
+ },
13
+ value2html: function (values, element) {
14
+ $.each(values, function (option_name, option_value) {
15
+ // Find all elements which display option value.
16
+ $('.bookly-js-option.' + option_name).each(function () {
17
+ var $this = $(this);
18
+ if (!$this.hasClass('editable') || $this.is(element)) {
19
+ // Update text.
20
+ $this.text(option_value);
21
+ }
22
+ });
23
+ });
24
+ },
25
+ activate: function () {
26
+ this.$tpl.find(':input:first').focus();
27
+ },
28
+ value2input: function (values) {
29
+ var _this = this;
30
+ this.$tpl.empty();
31
+ $.each(values, function (option_name, option_value) {
32
+ var $row = $('<div/>')
33
+ .css({position: 'relative', 'margin-top': '6px'})
34
+ .appendTo(_this.$tpl);
35
+ if (_this.options.fieldType == 'input') {
36
+ // Create input with "x" button.
37
+ var $clear = $('<span class="editable-clear-x"></span>');
38
+ var $input = $('<input/>', {
39
+ type : 'text',
40
+ class: 'form-control',
41
+ name : option_name,
42
+ value: option_value
43
+ });
44
+ $input.keyup(function(e) {
45
+ // arrows, enter, tab, etc
46
+ if (~$.inArray(e.keyCode, [40,38,9,13,27])) {
47
+ return;
48
+ }
49
+ clearTimeout(this.t);
50
+ this.t = setTimeout(function() {
51
+ var len = $input.val().length,
52
+ visible = $clear.is(':visible');
53
+ if (len && !visible) {
54
+ $clear.show();
55
+ }
56
+ if (!len && visible) {
57
+ $clear.hide();
58
+ }
59
+ }, 100);
60
+ });
61
+ $clear.click(function () {
62
+ $clear.hide();
63
+ $input.val('').focus();
64
+ });
65
+ $row.append($input).append($clear);
66
+ } else {
67
+ // Create textarea.
68
+ $('<textarea/>', {
69
+ class: 'form-control',
70
+ name : option_name,
71
+ rows : 7
72
+ }).val(option_value).appendTo($row);
73
+ }
74
+ });
75
+ // Set codes.
76
+ this.$tpl.closest('form').find('.bookly-js-codes').html($(this.options.scope).data('codes'));
77
+ },
78
+ input2value: function () {
79
+ var _this = this;
80
+ var values = {};
81
+ this.$tpl.find(':input').each(function () {
82
+ values[this.name] = this.value;
83
+ // Find all elements which display option value.
84
+ var option_name = this.name;
85
+ var option_value = this.value;
86
+ $('.bookly-js-option.' + option_name).each(function () {
87
+ var $this = $(this);
88
+ if ($this.hasClass('editable') && !$this.is(_this.$tpl)) {
89
+ // Update editable value.
90
+ var val = $this.editable('getValue', true);
91
+ val[option_name] = option_value;
92
+ $this.editable('setValue', val);
93
+ }
94
+ });
95
+ });
96
+
97
+ return values;
98
+ }
99
+ });
100
+
101
+ Bookly.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
102
+ tpl: '<div/>',
103
+ fieldType: 'input'
104
+ });
105
+
106
+ $.fn.editabletypes.bookly = Bookly;
107
+
108
+ // Set template for popovers and editable form.
109
+ $.fn.popover.Constructor.DEFAULTS.template = '<div class="popover"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>';
110
+ $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group"><div><div class="editable-input"></div><div class="editable-buttons"></div></div><div class="bookly-js-codes"></div><div class="editable-error-block"></div></div></form>';
111
+ $.fn.editableform.buttons = '<div class="btn-group btn-group-sm"><button type="submit" class="btn btn-success editable-submit"><span class="glyphicon glyphicon-ok"></span></button><button type="button" class="btn btn-default editable-cancel"><span class="glyphicon glyphicon-remove"></span></button></div>';
112
+ });
backend/{resources → modules/appearance/resources}/js/bootstrap-editable.min.js RENAMED
@@ -1,4 +1,4 @@
1
- /*! X-editable - v1.5.1
2
  * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
  * http://github.com/vitalets/x-editable
4
  * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
1
+ /*! X-editable - v1.5.1
2
  * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
  * http://github.com/vitalets/x-editable
4
  * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
backend/modules/appearance/templates/_1_service.php CHANGED
@@ -1,28 +1,34 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- /** @var WP_Locale $wp_locale */
3
- global $wp_locale;
 
 
 
 
 
 
4
  ?>
5
  <div class="bookly-form">
6
  <?php include '_progress_tracker.php' ?>
7
 
8
  <div class="bookly-service-step">
9
  <div class="bookly-box">
10
- <span data-option-default="<?php form_option( 'bookly_l10n_info_service_step' ) ?>"
11
- class="bookly-editable ab-bold ab-desc" id="bookly_l10n_info_service_step"
12
- data-rows="7" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_service_step' ) ) ?></span>
13
  </div>
14
- <div class="ab-mobile-step_1 bookly-box">
15
  <div class="bookly-js-chain-item bookly-table bookly-box">
16
- <?php if ( \BooklyLite\Lib\Config::isLocationsEnabled() ) : ?>
17
- <div class="ab-formGroup">
18
- <?php do_action( 'bookly_locations_render_appearance' ) ?>
19
  </div>
20
  <?php endif ?>
21
- <div class="ab-formGroup">
22
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_category', 'bookly_l10n_option_category', ) ) ?>
23
  <div>
24
- <select class="ab-select-mobile ab-select-category">
25
- <option value="" id="bookly_l10n_option_category"><?php echo esc_html( get_option( 'bookly_l10n_option_category' ) ) ?></option>
26
  <option value="1">Cosmetic Dentistry</option>
27
  <option value="2">Invisalign</option>
28
  <option value="3">Orthodontics</option>
@@ -30,126 +36,133 @@
30
  </select>
31
  </div>
32
  </div>
33
- <div class="ab-formGroup">
34
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array(
35
  'bookly_l10n_label_service',
36
  'bookly_l10n_option_service',
37
  'bookly_l10n_required_service',
38
  ) ) ?>
39
  <div>
40
- <select class="ab-select-mobile ab-select-service">
41
- <option id="bookly_l10n_option_service"><?php echo esc_html( get_option( 'bookly_l10n_option_service' ) ) ?></option>
42
- <option>Crown and Bridge</option>
43
- <option>Teeth Whitening</option>
44
- <option>Veneers</option>
45
- <option>Invisalign (invisable braces)</option>
46
- <option>Orthodontics (braces)</option>
47
- <option>Wisdom tooth Removal</option>
48
- <option>Root Canal Treatment</option>
49
- <option>Dentures</option>
 
 
 
 
 
 
 
 
50
  </select>
51
  </div>
52
  </div>
53
- <div class="ab-formGroup">
54
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array(
55
  'bookly_l10n_label_employee',
56
  'bookly_l10n_option_employee',
57
  'bookly_l10n_required_employee',
58
  ) ) ?>
59
  <div>
60
- <select class="ab-select-mobile ab-select-employee">
61
- <option value="0" id="bookly_l10n_option_employee"><?php echo esc_html( get_option( 'bookly_l10n_option_employee' ) ) ?></option>
62
- <option value="1" class="employee-name-price">Nick Knight (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
63
  <option value="-1" class="employee-name">Nick Knight</option>
64
- <option value="2" class="employee-name-price">Jane Howard (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 375 ) ?>)</option>
65
  <option value="-2" class="employee-name">Jane Howard</option>
66
- <option value="3" class="employee-name-price">Ashley Stamp (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 300 ) ?>)</option>
67
  <option value="-3" class="employee-name">Ashley Stamp</option>
68
- <option value="4" class="employee-name-price">Bradley Tannen (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?>)</option>
69
  <option value="-4" class="employee-name">Bradley Tannen</option>
70
- <option value="5" class="employee-name-price">Wayne Turner (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
71
  <option value="-5" class="employee-name">Wayne Turner</option>
72
- <option value="6" class="employee-name-price">Emily Taylor (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
73
  <option value="-6" class="employee-name">Emily Taylor</option>
74
- <option value="7" class="employee-name-price">Hugh Canberg (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 380 ) ?>)</option>
75
  <option value="-7" class="employee-name">Hugh Canberg</option>
76
- <option value="8" class="employee-name-price">Jim Gonzalez (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 390 ) ?>)</option>
77
  <option value="-8" class="employee-name">Jim Gonzalez</option>
78
- <option value="9" class="employee-name-price">Nancy Stinson (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 360 ) ?>)</option>
79
  <option value="-9" class="employee-name">Nancy Stinson</option>
80
- <option value="10" class="employee-name-price">Marry Murphy (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
81
  <option value="-10" class="employee-name">Marry Murphy</option>
82
  </select>
83
  </div>
84
  </div>
85
- <div class="ab-formGroup">
86
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_number_of_persons', ) ) ?>
87
  <div>
88
- <select class="ab-select-mobile ab-select-number-of-persons">
89
  <option>1</option>
90
  <option>2</option>
91
  <option>3</option>
92
  </select>
93
  </div>
94
  </div>
95
- <?php if ( \BooklyLite\Lib\Config::isMultiplyAppointmentsEnabled() ) : ?>
96
- <div class="ab-formGroup">
97
- <?php do_action( 'bookly_multiply_appointments_render_appearance' ) ?>
98
  </div>
99
  <?php endif ?>
100
- <?php if ( \BooklyLite\Lib\Config::isChainAppointmentsEnabled() ) : ?>
101
- <div class="ab-formGroup">
102
  <label></label>
103
  <div>
104
- <button class="bookly-round" ><i class="bookly-icon-sm bookly-icon-plus"></i></button>
105
  </div>
106
  </div>
107
  <?php endif ?>
108
  </div>
109
-
110
- <div class="ab-right ab-mobile-next-step ab-btn ab-none" onclick="return false">
111
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_next' ), 'bookly-js-text-next' ) ?>
112
  </div>
113
  </div>
114
- <div class="ab-mobile-step_2">
115
  <div class="bookly-box">
116
- <div class="ab-left">
117
- <div class="ab-available-date ab-left">
118
- <div class="ab-formGroup">
119
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_select_date', ) ) ?>
120
  <div>
121
- <input class="ab-date-from" style="background-color: #fff;" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
122
  </div>
123
  </div>
124
  </div>
125
- <div class="bookly-week-days bookly-table ab-left">
126
  <?php foreach ( $wp_locale->weekday_abbrev as $weekday_abbrev ) : ?>
127
  <div>
128
  <div class="bookly-font-bold"><?php echo $weekday_abbrev ?></div>
129
  <label class="active">
130
- <input class="bookly-week-day" value="1" checked="checked" type="checkbox">
131
  </label>
132
  </div>
133
  <?php endforeach ?>
134
  </div>
135
  </div>
136
- <div class="ab-time-range ab-left">
137
- <div class="ab-formGroup ab-time-from ab-left">
138
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_start_from', ) ) ?>
139
  <div>
140
- <select class="ab-select-time-from">
141
  <?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
142
- <option><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?></option>
143
  <?php endfor ?>
144
  </select>
145
  </div>
146
  </div>
147
- <div class="ab-formGroup ab-time-to ab-left">
148
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_finish_by', ) ) ?>
149
  <div>
150
- <select class="ab-select-time-to">
151
  <?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
152
- <option<?php selected( $i == 64800 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?></option>
153
  <?php endfor ?>
154
  </select>
155
  </div>
@@ -157,23 +170,20 @@
157
  </div>
158
  </div>
159
  <div class="bookly-box bookly-nav-steps">
160
- <div class="ab-right ab-mobile-prev-step ab-btn ab-none">
161
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_back' ), 'bookly-js-text-back' ) ?>
162
  </div>
163
- <div class="bookly-next-step ab-btn">
164
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_next' ), 'bookly-js-text-next' ) ?>
165
  </div>
166
- <button class="bookly-go-to-cart bookly-round bookly-round-md ladda-button"><span><img src="<?php echo plugins_url( 'bookly-responsive-appointment-booking-tool/frontend/resources/images/cart.png' ) ?>" /></span></button>
167
  </div>
168
  </div>
169
  </div>
170
  </div>
171
  <div style="display: none">
172
  <?php foreach ( array( 'bookly_l10n_required_service', 'bookly_l10n_required_name', 'bookly_l10n_required_phone', 'bookly_l10n_required_email', 'bookly_l10n_required_employee', 'bookly_l10n_required_location' ) as $validator ) : ?>
173
- <div id="<?php echo $validator ?>"><?php echo get_option( $validator ) ?></div>
174
  <?php endforeach ?>
175
  </div>
176
- <style id="ab--style-arrow">
177
- .picker__nav--next:before { border-left: 6px solid <?php echo get_option( 'bookly_app_color' ) ?>!important; }
178
- .picker__nav--prev:before { border-right: 6px solid <?php echo get_option( 'bookly_app_color' ) ?>!important; }
179
- </style>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Price;
3
+ use BooklyLite\Lib\Utils\DateTime;
4
+
5
+ /**
6
+ * @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable
7
+ * @var WP_Locale $wp_locale
8
+ */
9
+ global $wp_locale;
10
  ?>
11
  <div class="bookly-form">
12
  <?php include '_progress_tracker.php' ?>
13
 
14
  <div class="bookly-service-step">
15
  <div class="bookly-box">
16
+ <span class="bookly-bold bookly-desc">
17
+ <?php $editable::renderText( 'bookly_l10n_info_service_step' ) ?>
18
+ </span>
19
  </div>
20
+ <div class="bookly-mobile-step-1 bookly-js-mobile-step-1 bookly-box">
21
  <div class="bookly-js-chain-item bookly-table bookly-box">
22
+ <?php if ( \BooklyLite\Lib\Config::locationsEnabled() ) : ?>
23
+ <div class="bookly-form-group">
24
+ <?php \BooklyLite\Lib\Proxy\Locations::renderAppearance() ?>
25
  </div>
26
  <?php endif ?>
27
+ <div class="bookly-form-group">
28
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_category', 'bookly_l10n_option_category', ) ) ?>
29
  <div>
30
+ <select class="bookly-select-mobile bookly-js-select-category">
31
+ <option value="" class="bookly-js-option bookly_l10n_option_category"><?php echo esc_html( get_option( 'bookly_l10n_option_category' ) ) ?></option>
32
  <option value="1">Cosmetic Dentistry</option>
33
  <option value="2">Invisalign</option>
34
  <option value="3">Orthodontics</option>
36
  </select>
37
  </div>
38
  </div>
39
+ <div class="bookly-form-group">
40
+ <?php $editable::renderLabel( array(
41
  'bookly_l10n_label_service',
42
  'bookly_l10n_option_service',
43
  'bookly_l10n_required_service',
44
  ) ) ?>
45
  <div>
46
+ <select class="bookly-select-mobile bookly-js-select-service">
47
+ <option value="0" class="bookly-js-option bookly_l10n_option_service"><?php echo esc_html( get_option( 'bookly_l10n_option_service' ) ) ?></option>
48
+ <option value="1" class="service-name-duration">Crown and Bridge (<?php echo DateTime::secondsToInterval( 3600 ) ?>)</option>
49
+ <option value="-1" class="service-name">Crown and Bridge</option>
50
+ <option value="2" class="service-name-duration">Teeth Whitening (<?php echo DateTime::secondsToInterval( 3600 * 2 ) ?>)</option>
51
+ <option value="-2" class="service-name">Teeth Whitening</option>
52
+ <option value="3" class="service-name-duration">Veneers (<?php echo DateTime::secondsToInterval( 3600 * 12 ) ?>)</option>
53
+ <option value="-3" class="service-name">Veneers</option>
54
+ <option value="4" class="service-name-duration">Invisalign (invisable braces) (<?php echo DateTime::secondsToInterval( 3600 * 24 ) ?>)</option>
55
+ <option value="-4" class="service-name">Invisalign (invisable braces)</option>
56
+ <option value="5" class="service-name-duration">Orthodontics (braces) (<?php echo DateTime::secondsToInterval( 3600 * 8 ) ?>)</option>
57
+ <option value="-5" class="service-name">Orthodontics (braces)</option>
58
+ <option value="6" class="service-name-duration">Wisdom tooth Removal (<?php echo DateTime::secondsToInterval( 3600 * 6 ) ?>)</option>
59
+ <option value="-6" class="service-name">Wisdom tooth Removal</option>
60
+ <option value="7" class="service-name-duration">Root Canal Treatment (<?php echo DateTime::secondsToInterval( 3600 * 16 ) ?>)</option>
61
+ <option value="-7" class="service-name">Root Canal Treatment</option>
62
+ <option value="8" class="service-name-duration">Dentures (<?php echo DateTime::secondsToInterval( 3600 * 48 ) ?>)</option>
63
+ <option value="-8" class="service-name">Dentures</option>
64
  </select>
65
  </div>
66
  </div>
67
+ <div class="bookly-form-group">
68
+ <?php $editable::renderLabel( array(
69
  'bookly_l10n_label_employee',
70
  'bookly_l10n_option_employee',
71
  'bookly_l10n_required_employee',
72
  ) ) ?>
73
  <div>
74
+ <select class="bookly-select-mobile bookly-js-select-employee">
75
+ <option value="0" class="bookly-js-option bookly_l10n_option_employee"><?php echo esc_html( get_option( 'bookly_l10n_option_employee' ) ) ?></option>
76
+ <option value="1" class="employee-name-price">Nick Knight (<?php echo Price::format( 350 ) ?>)</option>
77
  <option value="-1" class="employee-name">Nick Knight</option>
78
+ <option value="2" class="employee-name-price">Jane Howard (<?php echo Price::format( 375 ) ?>)</option>
79
  <option value="-2" class="employee-name">Jane Howard</option>
80
+ <option value="3" class="employee-name-price">Ashley Stamp (<?php echo Price::format( 300 ) ?>)</option>
81
  <option value="-3" class="employee-name">Ashley Stamp</option>
82
+ <option value="4" class="employee-name-price">Bradley Tannen (<?php echo Price::format( 400 ) ?>)</option>
83
  <option value="-4" class="employee-name">Bradley Tannen</option>
84
+ <option value="5" class="employee-name-price">Wayne Turner (<?php echo Price::format( 350 ) ?>)</option>
85
  <option value="-5" class="employee-name">Wayne Turner</option>
86
+ <option value="6" class="employee-name-price">Emily Taylor (<?php echo Price::format( 350 ) ?>)</option>
87
  <option value="-6" class="employee-name">Emily Taylor</option>
88
+ <option value="7" class="employee-name-price">Hugh Canberg (<?php echo Price::format( 380 ) ?>)</option>
89
  <option value="-7" class="employee-name">Hugh Canberg</option>
90
+ <option value="8" class="employee-name-price">Jim Gonzalez (<?php echo Price::format( 390 ) ?>)</option>
91
  <option value="-8" class="employee-name">Jim Gonzalez</option>
92
+ <option value="9" class="employee-name-price">Nancy Stinson (<?php echo Price::format( 360 ) ?>)</option>
93
  <option value="-9" class="employee-name">Nancy Stinson</option>
94
+ <option value="10" class="employee-name-price">Marry Murphy (<?php echo Price::format( 350 ) ?>)</option>
95
  <option value="-10" class="employee-name">Marry Murphy</option>
96
  </select>
97
  </div>
98
  </div>
99
+ <div class="bookly-form-group" style="display: none">
100
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_number_of_persons', ) ) ?>
101
  <div>
102
+ <select class="bookly-select-mobile bookly-js-select-number-of-persons">
103
  <option>1</option>
104
  <option>2</option>
105
  <option>3</option>
106
  </select>
107
  </div>
108
  </div>
109
+ <?php if ( \BooklyLite\Lib\Config::multiplyAppointmentsEnabled() ) : ?>
110
+ <div class="bookly-form-group">
111
+ <?php \BooklyLite\Lib\Proxy\MultiplyAppointments::renderAppearance() ?>
112
  </div>
113
  <?php endif ?>
114
+ <?php if ( \BooklyLite\Lib\Config::chainAppointmentsEnabled() ) : ?>
115
+ <div class="bookly-form-group">
116
  <label></label>
117
  <div>
118
+ <button class="bookly-round"><i class="bookly-icon-sm bookly-icon-plus"></i></button>
119
  </div>
120
  </div>
121
  <?php endif ?>
122
  </div>
123
+ <div class="bookly-right bookly-mobile-next-step bookly-js-mobile-next-step bookly-btn bookly-none">
124
+ <?php $editable::renderString( array( 'bookly_l10n_step_service_mobile_button_next' ) ) ?>
 
125
  </div>
126
  </div>
127
+ <div class="bookly-mobile-step-2 bookly-js-mobile-step-2">
128
  <div class="bookly-box">
129
+ <div class="bookly-left">
130
+ <div class="bookly-available-date bookly-js-available-date bookly-left">
131
+ <div class="bookly-form-group">
132
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_select_date', ) ) ?>
133
  <div>
134
+ <input class="bookly-date-from bookly-js-date-from" style="background-color: #fff;" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
135
  </div>
136
  </div>
137
  </div>
138
+ <div class="bookly-week-days bookly-js-week-days bookly-table bookly-left">
139
  <?php foreach ( $wp_locale->weekday_abbrev as $weekday_abbrev ) : ?>
140
  <div>
141
  <div class="bookly-font-bold"><?php echo $weekday_abbrev ?></div>
142
  <label class="active">
143
+ <input class="bookly-js-week-day" value="1" checked="checked" type="checkbox">
144
  </label>
145
  </div>
146
  <?php endforeach ?>
147
  </div>
148
  </div>
149
+ <div class="bookly-time-range bookly-js-time-range bookly-left">
150
+ <div class="bookly-form-group bookly-time-from bookly-left">
151
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_start_from', ) ) ?>
152
  <div>
153
+ <select class="bookly-js-select-time-from">
154
  <?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
155
+ <option><?php echo DateTime::formatTime( $i ) ?></option>
156
  <?php endfor ?>
157
  </select>
158
  </div>
159
  </div>
160
+ <div class="bookly-form-group bookly-time-to bookly-left">
161
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_finish_by', ) ) ?>
162
  <div>
163
+ <select class="bookly-js-select-time-to">
164
  <?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
165
+ <option<?php selected( $i == 64800 ) ?>><?php echo DateTime::formatTime( $i ) ?></option>
166
  <?php endfor ?>
167
  </select>
168
  </div>
170
  </div>
171
  </div>
172
  <div class="bookly-box bookly-nav-steps">
173
+ <div class="bookly-right bookly-mobile-prev-step bookly-js-mobile-prev-step bookly-btn bookly-none">
174
+ <?php $editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
175
  </div>
176
+ <div class="bookly-next-step bookly-js-next-step bookly-btn">
177
+ <?php $editable::renderString( array( 'bookly_l10n_step_service_button_next' ) ) ?>
178
  </div>
179
+ <button class="bookly-go-to-cart bookly-js-go-to-cart bookly-round bookly-round-md ladda-button"><span><img src="<?php echo plugins_url( 'bookly-responsive-appointment-booking-tool/frontend/resources/images/cart.png' ) ?>" /></span></button>
180
  </div>
181
  </div>
182
  </div>
183
  </div>
184
  <div style="display: none">
185
  <?php foreach ( array( 'bookly_l10n_required_service', 'bookly_l10n_required_name', 'bookly_l10n_required_phone', 'bookly_l10n_required_email', 'bookly_l10n_required_employee', 'bookly_l10n_required_location' ) as $validator ) : ?>
186
+ <div class="bookly-js-option <?php echo $validator ?>"><?php echo get_option( $validator ) ?></div>
187
  <?php endforeach ?>
188
  </div>
189
+ <style id="bookly-pickadate-style"></style>
 
 
 
backend/modules/appearance/templates/_3_time.php CHANGED
@@ -1,146 +1,152 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <div class="bookly-form">
3
  <?php include '_progress_tracker.php' ?>
4
 
5
  <div class="bookly-box">
6
- <span data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 3 ), false ) ) ?>" data-placement="bottom" data-option-default="<?php form_option( 'bookly_l10n_info_time_step' ) ?>" class="bookly-editable" id="bookly_l10n_info_time_step" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_time_step' ) ) ?></span>
 
 
 
7
  </div>
8
  <!-- timeslots -->
9
- <div class="ab-time-step">
10
- <div class="ab-columnizer-wrap">
11
- <div class="ab-columnizer">
12
- <div class="ab-time-screen ab-day-columns" style="display: <?php echo get_option( 'bookly_app_show_day_one_column' ) == 1 ? ' none' : 'block' ?>">
13
- <div class="ab-input-wrap ab-slot-calendar">
14
- <span class="ab-date-wrap">
15
- <input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
16
  </span>
17
  </div>
18
- <div class="ab-column col1">
19
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', current_time( 'timestamp' ) ) ?></button>
20
  <?php for ( $i = 28800; $i <= 57600; $i += 3600 ) : ?>
21
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
22
  <span class="ladda-label">
23
- <i class="ab-hour-icon"><span></span></i>
24
  <?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
25
  </span>
26
  </button>
27
  <?php endfor ?>
28
  </div>
29
- <div class="ab-column col2">
30
- <button class="ab-hour ladda-button ab-last-child">
31
  <span class="ladda-label">
32
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 61200 ) ?>
33
  </span>
34
  </button>
35
- <button class="ab-day ab-first-child" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'block' ?>"><?php echo date_i18n( 'D, M d', strtotime( '+1 day', current_time( 'timestamp' ) ) ) ?></button>
36
  <?php for ( $i = 28800; $i <= 54000; $i += 3600 ) : ?>
37
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'block' ?>">
38
  <span class="ladda-label">
39
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
40
  </span>
41
  </button>
42
  <?php endfor ?>
43
  </div>
44
- <div class="ab-column col3" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
45
  <?php for ( $i = 57600; $i <= 61200; $i += 3600 ) : ?>
46
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
47
  <span class="ladda-label">
48
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
49
  </span>
50
  </button>
51
  <?php endfor ?>
52
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 days', current_time('timestamp') ) ) ?></button>
53
  <?php for ( $i = 28800; $i <= 50400; $i += 3600 ) : ?>
54
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
55
  <span class="ladda-label">
56
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
57
  </span>
58
  </button>
59
  <?php endfor ?>
60
  </div>
61
- <div class="ab-column col4" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
62
  <?php for ( $i = 54000; $i <= 61200; $i += 3600 ) : ?>
63
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
64
  <span class="ladda-label">
65
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
66
  </span>
67
  </button>
68
  <?php endfor ?>
69
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+3 days', current_time( 'timestamp' ) ) ) ?></button>
70
  <?php for ( $i = 28800; $i <= 46800; $i += 3600 ) : ?>
71
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
72
  <span class="ladda-label">
73
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
74
  </span>
75
  </button>
76
  <?php endfor ?>
77
  </div>
78
- <div class="ab-column col5" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
79
  <?php for ( $i = 50400; $i <= 61200; $i += 3600 ) : ?>
80
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
81
  <span class="ladda-label">
82
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
83
  </span>
84
  </button>
85
  <?php endfor ?>
86
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+4 days', current_time( 'timestamp' ) ) ) ?></button>
87
  <?php for ( $i = 28800; $i <= 43200; $i += 3600 ) : ?>
88
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
89
  <span class="ladda-label">
90
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
91
  </span>
92
  </button>
93
  <?php endfor ?>
94
  </div>
95
- <div class="ab-column col6" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
96
  <?php for ( $i = 46800; $i <= 61200; $i += 3600 ) : ?>
97
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
98
  <span class="ladda-label">
99
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
100
  </span>
101
  </button>
102
  <?php endfor ?>
103
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+5 days', current_time( 'timestamp' ) ) ) ?></button>
104
  <?php for ( $i = 28800; $i <= 39600; $i += 3600 ) : ?>
105
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
106
  <span class="ladda-label">
107
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
108
  </span>
109
  </button>
110
  <?php endfor ?>
111
  </div>
112
- <div class="ab-column col7" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
113
  <?php for ( $i = 43200; $i <= 61200; $i += 3600 ) : ?>
114
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
115
  <span class="ladda-label">
116
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
117
  </span>
118
  </button>
119
  <?php endfor ?>
120
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+6 days', current_time( 'timestamp' ) ) ) ?></button>
121
  <?php for ( $i = 28800; $i <= 36000; $i += 3600 ) : ?>
122
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
123
  <span class="ladda-label">
124
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
125
  </span>
126
  </button>
127
  <?php endfor ?>
128
  </div>
129
  </div>
130
 
131
- <div class="ab-time-screen ab-day-one-column" style="display: <?php echo get_option( 'bookly_app_show_day_one_column' ) == 1 ? ' block' : 'none' ?>">
132
- <div class="ab-input-wrap ab-slot-calendar">
133
- <span class="ab-date-wrap">
134
- <input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
135
  </span>
136
  </div>
137
  <?php for ( $i = 1; $i <= 7; ++ $i ) : ?>
138
- <div class="ab-column col<?php echo $i ?>">
139
- <button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+' . ( $i - 1 ) . ' days', current_time( 'timestamp' ) ) ) ?></button>
140
  <?php for ( $j = 28800; $j <= 61200; $j += 3600 ) : ?>
141
- <button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
142
  <span class="ladda-label">
143
- <i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $j ) ?>
144
  </span>
145
  </button>
146
  <?php endfor ?>
@@ -151,15 +157,15 @@
151
  </div>
152
  </div>
153
  <div class="bookly-box bookly-nav-steps">
154
- <button class="ab-time-next ab-btn ab-right ladda-button">
155
- <span class="ab_label">&gt;</span>
156
  </button>
157
- <button class="ab-time-prev ab-btn ab-right ladda-button">
158
- <span class="ab_label">&lt;</span>
159
  </button>
160
- <div class="bookly-back-step ab-btn">
161
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_back' ), 'bookly-js-text-back' ) ?>
162
  </div>
163
- <button class="bookly-go-to-cart bookly-round bookly-round-md ladda-button" data-style="zoom-in" data-spinner-size="30"><span class="ladda-label"><img src="<?php echo plugins_url( 'bookly-responsive-appointment-booking-tool/frontend/resources/images/cart.png' ) ?>" /></span></button>
164
  </div>
165
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Backend\Modules\Appearance\Components;
3
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
4
+ ?>
5
  <div class="bookly-form">
6
  <?php include '_progress_tracker.php' ?>
7
 
8
  <div class="bookly-box">
9
+ <?php $editable::renderText( 'bookly_l10n_info_time_step', Components::getInstance()->renderCodes( array( 'step' => 3 ), false ) ) ?>
10
+ <div class="bookly-holder bookly-label-error bookly-bold">
11
+ <?php $editable::renderText( 'bookly_l10n_step_time_slot_not_available', null, 'bottom', __( 'Visible when the chosen time slot has been already booked', 'bookly' ) ) ?>
12
+ </div>
13
  </div>
14
  <!-- timeslots -->
15
+ <div class="bookly-time-step">
16
+ <div class="bookly-columnizer-wrap">
17
+ <div class="bookly-columnizer">
18
+ <div id="bookly-day-multi-columns" class="bookly-time-screen" style="display: <?php echo get_option( 'bookly_app_show_day_one_column' ) == 1 ? ' none' : 'block' ?>">
19
+ <div class="bookly-input-wrap bookly-slot-calendar bookly-js-slot-calendar">
20
+ <span class="bookly-date-wrap">
21
+ <input style="display: none" class="bookly-js-selected-date bookly-form-element" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
22
  </span>
23
  </div>
24
+ <div class="bookly-column col1">
25
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', current_time( 'timestamp' ) ) ?></button>
26
  <?php for ( $i = 28800; $i <= 57600; $i += 3600 ) : ?>
27
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
28
  <span class="ladda-label">
29
+ <i class="bookly-hour-icon"><span></span></i>
30
  <?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
31
  </span>
32
  </button>
33
  <?php endfor ?>
34
  </div>
35
+ <div class="bookly-column col2">
36
+ <button class="bookly-hour ladda-button bookly-last-child">
37
  <span class="ladda-label">
38
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 61200 ) ?>
39
  </span>
40
  </button>
41
+ <button class="bookly-day bookly-js-first-child" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'block' ?>"><?php echo date_i18n( 'D, M d', strtotime( '+1 day', current_time( 'timestamp' ) ) ) ?></button>
42
  <?php for ( $i = 28800; $i <= 54000; $i += 3600 ) : ?>
43
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'block' ?>">
44
  <span class="ladda-label">
45
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
46
  </span>
47
  </button>
48
  <?php endfor ?>
49
  </div>
50
+ <div class="bookly-column col3" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
51
  <?php for ( $i = 57600; $i <= 61200; $i += 3600 ) : ?>
52
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
53
  <span class="ladda-label">
54
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
55
  </span>
56
  </button>
57
  <?php endfor ?>
58
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 days', current_time('timestamp') ) ) ?></button>
59
  <?php for ( $i = 28800; $i <= 50400; $i += 3600 ) : ?>
60
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
61
  <span class="ladda-label">
62
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
63
  </span>
64
  </button>
65
  <?php endfor ?>
66
  </div>
67
+ <div class="bookly-column col4" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
68
  <?php for ( $i = 54000; $i <= 61200; $i += 3600 ) : ?>
69
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
70
  <span class="ladda-label">
71
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
72
  </span>
73
  </button>
74
  <?php endfor ?>
75
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+3 days', current_time( 'timestamp' ) ) ) ?></button>
76
  <?php for ( $i = 28800; $i <= 46800; $i += 3600 ) : ?>
77
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
78
  <span class="ladda-label">
79
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
80
  </span>
81
  </button>
82
  <?php endfor ?>
83
  </div>
84
+ <div class="bookly-column col5" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
85
  <?php for ( $i = 50400; $i <= 61200; $i += 3600 ) : ?>
86
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
87
  <span class="ladda-label">
88
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
89
  </span>
90
  </button>
91
  <?php endfor ?>
92
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+4 days', current_time( 'timestamp' ) ) ) ?></button>
93
  <?php for ( $i = 28800; $i <= 43200; $i += 3600 ) : ?>
94
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
95
  <span class="ladda-label">
96
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
97
  </span>
98
  </button>
99
  <?php endfor ?>
100
  </div>
101
+ <div class="bookly-column col6" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
102
  <?php for ( $i = 46800; $i <= 61200; $i += 3600 ) : ?>
103
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
104
  <span class="ladda-label">
105
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
106
  </span>
107
  </button>
108
  <?php endfor ?>
109
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+5 days', current_time( 'timestamp' ) ) ) ?></button>
110
  <?php for ( $i = 28800; $i <= 39600; $i += 3600 ) : ?>
111
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
112
  <span class="ladda-label">
113
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
114
  </span>
115
  </button>
116
  <?php endfor ?>
117
  </div>
118
+ <div class="bookly-column col7" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
119
  <?php for ( $i = 43200; $i <= 61200; $i += 3600 ) : ?>
120
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
121
  <span class="ladda-label">
122
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
123
  </span>
124
  </button>
125
  <?php endfor ?>
126
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+6 days', current_time( 'timestamp' ) ) ) ?></button>
127
  <?php for ( $i = 28800; $i <= 36000; $i += 3600 ) : ?>
128
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
129
  <span class="ladda-label">
130
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
131
  </span>
132
  </button>
133
  <?php endfor ?>
134
  </div>
135
  </div>
136
 
137
+ <div id="bookly-day-one-column" class="bookly-time-screen" style="display: <?php echo get_option( 'bookly_app_show_day_one_column' ) == 1 ? ' block' : 'none' ?>">
138
+ <div class="bookly-input-wrap bookly-slot-calendar bookly-js-slot-calendar">
139
+ <span class="bookly-date-wrap">
140
+ <input style="display: none" class="bookly-js-selected-date bookly-form-element" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
141
  </span>
142
  </div>
143
  <?php for ( $i = 1; $i <= 7; ++ $i ) : ?>
144
+ <div class="bookly-column col<?php echo $i ?>">
145
+ <button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+' . ( $i - 1 ) . ' days', current_time( 'timestamp' ) ) ) ?></button>
146
  <?php for ( $j = 28800; $j <= 61200; $j += 3600 ) : ?>
147
+ <button class="bookly-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
148
  <span class="ladda-label">
149
+ <i class="bookly-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $j ) ?>
150
  </span>
151
  </button>
152
  <?php endfor ?>
157
  </div>
158
  </div>
159
  <div class="bookly-box bookly-nav-steps">
160
+ <button class="bookly-time-next bookly-btn bookly-right ladda-button">
161
+ <span class="bookly-label">&gt;</span>
162
  </button>
163
+ <button class="bookly-time-prev bookly-btn bookly-right ladda-button">
164
+ <span class="bookly-label">&lt;</span>
165
  </button>
166
+ <div class="bookly-back-step bookly-js-back-step bookly-btn">
167
+ <?php $editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
168
  </div>
169
+ <button class="bookly-go-to-cart bookly-js-go-to-cart bookly-round bookly-round-md ladda-button" data-style="zoom-in" data-spinner-size="30"><span class="ladda-label"><img src="<?php echo plugins_url( 'bookly-responsive-appointment-booking-tool/frontend/resources/images/cart.png' ) ?>" /></span></button>
170
  </div>
171
  </div>
backend/modules/appearance/templates/_5_cart.php CHANGED
@@ -1,103 +1,112 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
 
 
 
2
  <div class="bookly-form">
3
  <?php include '_progress_tracker.php' ?>
4
 
5
  <div class="bookly-box">
6
- <span data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 5 ), false ) ) ?>" data-placement="bottom" data-default="<?php form_option( 'bookly_l10n_info_cart_step' ) ?>" class="bookly-editable" id="bookly_l10n_info_cart_step" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_cart_step' ) ) ?></span>
 
 
 
7
  </div>
8
 
9
  <div class="bookly-box">
10
- <div class="ab-btn ab-add-item ab-inline-block">
11
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_book_more', ) ) ?>
12
  </div>
13
  </div>
14
 
15
- <div class="ab-cart-step">
16
- <div class="ab-cart bookly-box">
17
  <table>
18
- <thead class="ab-desktop-version">
19
  <tr>
20
- <th data-default="<?php form_option( 'bookly_l10n_label_service' ) ?>" class="ab-service-list"><?php echo esc_html( get_option( 'bookly_l10n_label_service' ) ) ?></th>
21
  <th><?php _e( 'Date', 'bookly' ) ?></th>
22
  <th><?php _e( 'Time', 'bookly' ) ?></th>
23
- <th data-default="<?php form_option( 'bookly_l10n_label_employee' ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></th>
24
  <th><?php _e( 'Price', 'bookly' ) ?></th>
25
  <th></th>
26
  </tr>
27
  </thead>
28
- <tbody class="ab-desktop-version">
29
- <tr>
30
  <td>Crown and Bridge</td>
31
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( '+2 days' ) ?></td>
32
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 28800 ) ?></td>
33
  <td>Nick Knight</td>
34
- <td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?></td>
35
  <td>
36
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
37
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
38
  </td>
39
  </tr>
40
- <tr>
41
  <td>Teeth Whitening</td>
42
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( '+3 days' ) ?></td>
43
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 57600 ) ?></td>
44
  <td>Wayne Turner</td>
45
- <td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?></td>
46
  <td>
47
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
48
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
49
  </td>
50
  </tr>
51
  </tbody>
52
- <tbody class="ab-mobile-version">
53
- <tr>
54
- <th data-default="<?php form_option( 'bookly_l10n_label_service' ) ?>" class="ab-service-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ?></th>
55
  <td>Crown and Bridge</td>
56
  </tr>
57
- <tr>
58
  <th><?php _e( 'Date', 'bookly' ) ?></th>
59
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( '+2 days' ) ?></td>
60
  </tr>
61
- <tr>
62
  <th><?php _e( 'Time', 'bookly' ) ?></th>
63
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 28800 ) ?></td>
64
  </tr>
65
- <tr>
66
- <th data-default="<?php form_option( 'bookly_l10n_label_employee' ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></th>
67
  <td>Nick Knight</td>
68
  </tr>
69
- <tr>
70
  <th><?php _e( 'Price', 'bookly' ) ?></th>
71
- <td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?></td>
72
  </tr>
73
- <tr>
74
  <th></th>
75
  <td>
76
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
77
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
78
  </td>
79
  </tr>
80
- <tr>
81
- <th data-default="<?php form_option( 'bookly_l10n_label_service' ) ?>" class="ab-service-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ?></th>
82
  <td>Teeth Whitening</td>
83
  </tr>
84
- <tr>
85
  <th><?php _e( 'Date', 'bookly' ) ?></th>
86
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( '+3 days' ) ?></td>
87
  </tr>
88
- <tr>
89
  <th><?php _e( 'Time', 'bookly' ) ?></th>
90
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 57600 ) ?></td>
91
  </tr>
92
- <tr>
93
- <th data-default="<?php form_option( 'bookly_l10n_label_employee' ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></th>
94
  <td>Wayne Turner</td>
95
  </tr>
96
- <tr>
97
  <th><?php _e( 'Price', 'bookly' ) ?></th>
98
- <td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?></td>
99
  </tr>
100
- <tr>
101
  <th></th>
102
  <td>
103
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
@@ -105,31 +114,31 @@
105
  </td>
106
  </tr>
107
  </tbody>
108
- <tfoot class="ab-desktop-version">
109
  <tr>
110
  <td colspan="4"><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></td>
111
- <td><strong class="bookly-js-total-price"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 750 ) ?></strong></td>
112
  <td></td>
113
  </tr>
114
  </tfoot>
115
- <tfoot class="ab-mobile-version">
116
  <tr>
117
  <th><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></th>
118
- <td><strong class="bookly-js-total-price"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 750 ) ?></strong></td>
119
  </tr>
120
  </tfoot>
121
  </table>
122
  </div>
123
  </div>
124
 
125
- <?php do_action( 'bookly_recurring_appointments_render_editable_message_info' ) ?>
126
 
127
  <div class="bookly-box bookly-nav-steps">
128
- <div class="bookly-back-step ab-btn">
129
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_back' ), 'bookly-js-text-back' ) ?>
130
  </div>
131
- <div class="bookly-next-step ab-btn">
132
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_next' ), 'bookly-js-text-next' ) ?>
133
  </div>
134
  </div>
135
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Price;
3
+ use BooklyLite\Lib\Utils\DateTime;
4
+ use BooklyLite\Backend\Modules\Appearance\Components;
5
+
6
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
7
+ ?>
8
  <div class="bookly-form">
9
  <?php include '_progress_tracker.php' ?>
10
 
11
  <div class="bookly-box">
12
+ <?php $editable::renderText( 'bookly_l10n_info_cart_step', Components::getInstance()->renderCodes( array( 'step' => 5 ), false ) ) ?>
13
+ <div class="bookly-holder bookly-label-error bookly-bold">
14
+ <?php $editable::renderText( 'bookly_l10n_step_cart_slot_not_available', null, 'bottom', __( 'Visible when the chosen time slot has been already booked', 'bookly' ) ) ?>
15
+ </div>
16
  </div>
17
 
18
  <div class="bookly-box">
19
+ <div class="bookly-btn bookly-add-item bookly-inline-block">
20
+ <?php $editable::renderString( array( 'bookly_l10n_button_book_more', ) ) ?>
21
  </div>
22
  </div>
23
 
24
+ <div class="bookly-cart-step">
25
+ <div class="bookly-cart bookly-box">
26
  <table>
27
+ <thead class="bookly-desktop-version">
28
  <tr>
29
+ <th class="bookly-js-option bookly_l10n_label_service"><?php echo esc_html( get_option( 'bookly_l10n_label_service' ) ) ?></th>
30
  <th><?php _e( 'Date', 'bookly' ) ?></th>
31
  <th><?php _e( 'Time', 'bookly' ) ?></th>
32
+ <th class="bookly-js-option bookly_l10n_label_employee"><?php echo esc_html( get_option( 'bookly_l10n_label_employee' ) ) ?></th>
33
  <th><?php _e( 'Price', 'bookly' ) ?></th>
34
  <th></th>
35
  </tr>
36
  </thead>
37
+ <tbody class="bookly-desktop-version">
38
+ <tr class="bookly-cart-primary">
39
  <td>Crown and Bridge</td>
40
+ <td><?php echo DateTime::formatDate( '+2 days' ) ?></td>
41
+ <td><?php echo DateTime::formatTime( 28800 ) ?></td>
42
  <td>Nick Knight</td>
43
+ <td><?php echo Price::format( 350 ) ?></td>
44
  <td>
45
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
46
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
47
  </td>
48
  </tr>
49
+ <tr class="bookly-cart-primary">
50
  <td>Teeth Whitening</td>
51
+ <td><?php echo DateTime::formatDate( '+3 days' ) ?></td>
52
+ <td><?php echo DateTime::formatTime( 57600 ) ?></td>
53
  <td>Wayne Turner</td>
54
+ <td><?php echo Price::format( 400 ) ?></td>
55
  <td>
56
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
57
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
58
  </td>
59
  </tr>
60
  </tbody>
61
+ <tbody class="bookly-mobile-version">
62
+ <tr class="bookly-cart-primary">
63
+ <th class="bookly-js-option bookly_l10n_label_service"><?php echo esc_html( get_option( 'bookly_l10n_label_service' ) ) ?></th>
64
  <td>Crown and Bridge</td>
65
  </tr>
66
+ <tr class="bookly-cart-primary">
67
  <th><?php _e( 'Date', 'bookly' ) ?></th>
68
+ <td><?php echo DateTime::formatDate( '+2 days' ) ?></td>
69
  </tr>
70
+ <tr class="bookly-cart-primary">
71
  <th><?php _e( 'Time', 'bookly' ) ?></th>
72
+ <td><?php echo DateTime::formatTime( 28800 ) ?></td>
73
  </tr>
74
+ <tr class="bookly-cart-primary">
75
+ <th class="bookly-js-option bookly_l10n_label_employee"><?php echo esc_html( get_option( 'bookly_l10n_label_employee' ) ) ?></th>
76
  <td>Nick Knight</td>
77
  </tr>
78
+ <tr class="bookly-cart-primary">
79
  <th><?php _e( 'Price', 'bookly' ) ?></th>
80
+ <td><?php echo Price::format( 350 ) ?></td>
81
  </tr>
82
+ <tr class="bookly-cart-primary">
83
  <th></th>
84
  <td>
85
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
86
  <button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></i></button>
87
  </td>
88
  </tr>
89
+ <tr class="bookly-cart-primary">
90
+ <th class="bookly-js-option bookly_l10n_label_service"><?php echo esc_html( get_option( 'bookly_l10n_label_service' ) ) ?></th>
91
  <td>Teeth Whitening</td>
92
  </tr>
93
+ <tr class="bookly-cart-primary">
94
  <th><?php _e( 'Date', 'bookly' ) ?></th>
95
+ <td><?php echo DateTime::formatDate( '+3 days' ) ?></td>
96
  </tr>
97
+ <tr class="bookly-cart-primary">
98
  <th><?php _e( 'Time', 'bookly' ) ?></th>
99
+ <td><?php echo DateTime::formatTime( 57600 ) ?></td>
100
  </tr>
101
+ <tr class="bookly-cart-primary">
102
+ <th class="bookly-js-option bookly_l10n_label_employee"><?php echo esc_html( get_option( 'bookly_l10n_label_employee' ) ) ?></th>
103
  <td>Wayne Turner</td>
104
  </tr>
105
+ <tr class="bookly-cart-primary">
106
  <th><?php _e( 'Price', 'bookly' ) ?></th>
107
+ <td><?php echo Price::format( 400 ) ?></td>
108
  </tr>
109
+ <tr class="bookly-cart-primary">
110
  <th></th>
111
  <td>
112
  <button class="bookly-round" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-edit"></i></button>
114
  </td>
115
  </tr>
116
  </tbody>
117
+ <tfoot class="bookly-desktop-version">
118
  <tr>
119
  <td colspan="4"><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></td>
120
+ <td><strong class="bookly-js-total-price"><?php echo Price::format( 750 ) ?></strong></td>
121
  <td></td>
122
  </tr>
123
  </tfoot>
124
+ <tfoot class="bookly-mobile-version">
125
  <tr>
126
  <th><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></th>
127
+ <td><strong class="bookly-js-total-price"><?php echo Price::format( 750 ) ?></strong></td>
128
  </tr>
129
  </tfoot>
130
  </table>
131
  </div>
132
  </div>
133
 
134
+ <?php \BooklyLite\Lib\Proxy\RecurringAppointments::renderAppearanceEditableInfoMessage() ?>
135
 
136
  <div class="bookly-box bookly-nav-steps">
137
+ <div class="bookly-back-step bookly-js-back-step bookly-btn">
138
+ <?php $editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
139
  </div>
140
+ <div class="bookly-next-step bookly-js-next-step bookly-btn">
141
+ <?php $editable::renderString( array( 'bookly_l10n_step_cart_button_next' ) ) ?>
142
  </div>
143
  </div>
144
  </div>
backend/modules/appearance/templates/_6_details.php CHANGED
@@ -1,44 +1,74 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <div class="bookly-form">
3
  <?php include '_progress_tracker.php' ?>
4
 
5
  <div class="bookly-box">
6
- <span data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 6, 'login' => false ), false ) ) ?>" data-placement="bottom" data-option-default="<?php form_option( 'bookly_l10n_info_details_step' ) ?>" class="bookly-editable" id="bookly_l10n_info_details_step" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_details_step' ) ) ?></span>
7
  </div>
8
  <div class="bookly-box">
9
- <span data-title="<?php _e( 'Visible to non-logged in customers only', 'bookly' ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 6, 'login' => true ), false ) ) ?>" data-placement="bottom" data-option-default="<?php form_option( 'bookly_l10n_info_details_step_guest' ) ?>" class="bookly-editable" id="bookly_l10n_info_details_step_guest" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_details_step_guest' ) ) ?></span>
10
  </div>
11
- <div class="ab-details-step">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  <div class="bookly-box bookly-table">
13
- <div class="ab-formGroup">
14
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_name', 'bookly_l10n_required_name', ) ) ?>
15
  <div>
16
  <input type="text" value="" maxlength="60" />
17
  </div>
18
  </div>
19
- <div class="ab-formGroup">
20
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_phone', 'bookly_l10n_required_phone', ) ) ?>
21
  <div>
22
- <input type="text" class="<?php if ( get_option( 'bookly_cst_phone_default_country' ) != 'disabled' ) : ?>ab-user-phone<?php endif ?>" value="" />
23
  </div>
24
  </div>
25
- <div class="ab-formGroup">
26
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderLabel( array( 'bookly_l10n_label_email', 'bookly_l10n_required_email', ) ) ?>
27
  <div>
28
  <input maxlength="40" type="text" value="" />
29
  </div>
30
  </div>
31
  </div>
 
 
 
 
 
 
 
 
32
  </div>
33
 
34
- <?php do_action( 'bookly_recurring_appointments_render_editable_message_info' ) ?>
35
 
36
  <div class="bookly-box bookly-nav-steps">
37
- <div class="bookly-back-step ab-btn">
38
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_back' ), 'bookly-js-text-back' ) ?>
39
  </div>
40
- <div class="bookly-next-step ab-btn">
41
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_next' ), 'bookly-js-text-next' ) ?>
42
  </div>
43
  </div>
44
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Backend\Modules\Appearance\Components;
3
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
4
+ ?>
5
  <div class="bookly-form">
6
  <?php include '_progress_tracker.php' ?>
7
 
8
  <div class="bookly-box">
9
+ <?php $editable::renderText( 'bookly_l10n_info_details_step', Components::getInstance()->renderCodes( array( 'step' => 6 ), false ) ) ?>
10
  </div>
11
  <div class="bookly-box">
12
+ <?php $editable::renderText( 'bookly_l10n_info_details_step_guest', Components::getInstance()->renderCodes( array( 'step' => 6, 'extra_codes' => 1 ), false ), 'bottom', __( 'Visible to non-logged in customers only', 'bookly' ) ) ?>
13
  </div>
14
+ <div class="bookly-box" id="bookly-js-show-login-form">
15
+ <div class="bookly-btn bookly-inline-block">
16
+ <?php $editable::renderString( array( 'bookly_l10n_step_details_button_login' ) ) ?>
17
+ </div>
18
+ </div>
19
+ <div class="bookly-details-step">
20
+ <div class="bookly-box bookly-table bookly-details-first-last-name" style="display: <?php echo get_option( 'bookly_cst_first_last_name' ) == 0 ? ' none' : 'table' ?>">
21
+ <div class="bookly-form-group">
22
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_first_name', 'bookly_l10n_required_first_name', ) ) ?>
23
+ <div>
24
+ <input type="text" value="" maxlength="60" />
25
+ </div>
26
+ </div>
27
+ <div class="bookly-form-group">
28
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_last_name', 'bookly_l10n_required_last_name', ) ) ?>
29
+ <div>
30
+ <input type="text" value="" maxlength="60" />
31
+ </div>
32
+ </div>
33
+ </div>
34
  <div class="bookly-box bookly-table">
35
+ <div class="bookly-form-group bookly-details-full-name" style="display: <?php echo get_option( 'bookly_cst_first_last_name' ) == 1 ? ' none' : 'block' ?>">
36
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_name', 'bookly_l10n_required_name', ) ) ?>
37
  <div>
38
  <input type="text" value="" maxlength="60" />
39
  </div>
40
  </div>
41
+ <div class="bookly-form-group">
42
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_phone', 'bookly_l10n_required_phone', ) ) ?>
43
  <div>
44
+ <input type="text" class="<?php if ( get_option( 'bookly_cst_phone_default_country' ) != 'disabled' ) : ?>bookly-user-phone<?php endif ?>" value="" />
45
  </div>
46
  </div>
47
+ <div class="bookly-form-group">
48
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_email', 'bookly_l10n_required_email', ) ) ?>
49
  <div>
50
  <input maxlength="40" type="text" value="" />
51
  </div>
52
  </div>
53
  </div>
54
+ <div class="bookly-box" id="bookly-js-notes">
55
+ <div class="bookly-form-group">
56
+ <?php $editable::renderLabel( array( 'bookly_l10n_label_notes' ) ) ?>
57
+ <div>
58
+ <textarea rows="3"></textarea>
59
+ </div>
60
+ </div>
61
+ </div>
62
  </div>
63
 
64
+ <?php \BooklyLite\Lib\Proxy\RecurringAppointments::renderAppearanceEditableInfoMessage() ?>
65
 
66
  <div class="bookly-box bookly-nav-steps">
67
+ <div class="bookly-back-step bookly-js-back-step bookly-btn">
68
+ <?php $editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
69
  </div>
70
+ <div class="bookly-next-step bookly-js-next-step bookly-btn">
71
+ <?php $editable::renderString( array( 'bookly_l10n_step_details_button_next' ) ) ?>
72
  </div>
73
  </div>
74
  </div>
backend/modules/appearance/templates/_7_payment.php CHANGED
@@ -1,69 +1,60 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <div class="bookly-form">
3
  <?php include '_progress_tracker.php' ?>
4
- <div class="bookly-box">
5
- <span data-option-default="<?php form_option( 'bookly_l10n_info_coupon' ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 7 ), false ) ) ?>" class="bookly-editable" id="bookly_l10n_info_coupon" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_coupon' ) ) ?></span>
6
- </div>
7
 
8
- <div class="bookly-box ab-list">
9
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_coupon', ) ) ?>
10
- <div class="ab-inline-block">
11
- <input class="ab-user-coupon" type="text" />
12
- <div class="ab-btn btn-apply-coupon">
13
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_apply', ) ) ?>
14
- </div>
15
  </div>
16
- </div>
17
- <div class="ab-payment-nav">
18
- <div class="bookly-box">
19
- <span data-option-default="<?php form_option( 'bookly_l10n_info_payment_step' ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', compact( 'step' ), false ) ) ?>" class="bookly-editable" id="bookly_l10n_info_payment_step" data-type="textarea"><?php echo esc_html( get_option( 'bookly_l10n_info_payment_step' ) ) ?></span>
20
  </div>
21
 
22
- <div class="bookly-box ab-list">
23
  <label>
24
  <input type="radio" name="payment" checked="checked" />
25
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_pay_locally', ) ) ?>
26
  </label>
27
  </div>
28
 
29
- <div class="bookly-box ab-list">
30
  <label>
31
  <input type="radio" name="payment" />
32
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_pay_paypal', ) ) ?>
33
  <img src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="paypal" />
34
  </label>
35
  </div>
36
 
37
- <div class="bookly-box ab-list">
 
 
 
 
38
  <label>
39
- <input type="radio" name="payment" class="ab-card-payment" />
40
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_pay_ccard', ) ) ?>
41
  <img src="<?php echo plugins_url( 'frontend/resources/images/cards.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="cards" />
42
  </label>
43
- <form class="ab-card-form ab-clearBottom" style="margin-top:15px;display: none;">
44
  <?php include '_card_payment.php' ?>
45
  </form>
46
  </div>
47
 
48
- <div class="bookly-box ab-list">
49
- <label>
50
- <input type="radio" name="payment" />
51
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_pay_mollie', ) ) ?>
52
- <img src="<?php echo plugins_url( 'frontend/resources/images/mollie.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="mollie" />
53
- </label>
54
- </div>
55
-
56
- <?php do_action( 'bookly_render_appearance_payment_gateway_selector' ) ?>
57
  </div>
58
 
59
- <?php do_action( 'bookly_recurring_appointments_render_editable_message_info' ) ?>
60
 
61
  <div class="bookly-box bookly-nav-steps">
62
- <div class="bookly-back-step ab-btn">
63
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_back' ), 'bookly-js-text-back' ) ?>
64
  </div>
65
- <div class="bookly-next-step ab-btn">
66
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_button_next' ), 'bookly-js-text-next' ) ?>
67
  </div>
68
  </div>
69
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Backend\Modules\Appearance\Components;
3
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
4
+ ?>
5
  <div class="bookly-form">
6
  <?php include '_progress_tracker.php' ?>
7
+ <?php \BooklyLite\Lib\Proxy\Coupons::renderAppearance() ?>
 
 
8
 
9
+ <div class="bookly-payment-nav">
10
+ <div class="bookly-box bookly-js-payment-single-app">
11
+ <?php $editable::renderText( 'bookly_l10n_info_payment_step_single_app', Components::getInstance()->renderCodes( array( 'step' => 7 ), false ), 'right' ) ?>
 
 
 
 
12
  </div>
13
+ <div class="bookly-box bookly-js-payment-several-apps" style="display:none">
14
+ <?php $editable::renderText( 'bookly_l10n_info_payment_step_several_apps', Components::getInstance()->renderCodes( array( 'step' => 7, 'extra_codes' => 1 ), false ), 'right' ) ?>
 
 
15
  </div>
16
 
17
+ <div class="bookly-box bookly-list">
18
  <label>
19
  <input type="radio" name="payment" checked="checked" />
20
+ <?php $editable::renderString( array( 'bookly_l10n_label_pay_locally', ) ) ?>
21
  </label>
22
  </div>
23
 
24
+ <div class="bookly-box bookly-list">
25
  <label>
26
  <input type="radio" name="payment" />
27
+ <?php $editable::renderString( array( 'bookly_l10n_label_pay_paypal', ) ) ?>
28
  <img src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="paypal" />
29
  </label>
30
  </div>
31
 
32
+ <div class="bookly-box bookly-list"
33
+ <?php if ( BooklyLite\Lib\Proxy\Shared::appearanceRequiredInterfaceCreditCard( false ) == false ) : ?>
34
+ style="display: none"
35
+ <?php endif ?>
36
+ >
37
  <label>
38
+ <input type="radio" name="payment" id="bookly-card-payment" />
39
+ <?php $editable::renderString( array( 'bookly_l10n_label_pay_ccard', ) ) ?>
40
  <img src="<?php echo plugins_url( 'frontend/resources/images/cards.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="cards" />
41
  </label>
42
+ <form class="bookly-card-form bookly-clear-bottom" style="margin-top:15px;display: none;">
43
  <?php include '_card_payment.php' ?>
44
  </form>
45
  </div>
46
 
47
+ <?php \BooklyLite\Lib\Proxy\Shared::renderAppearancePaymentGatewaySelector() ?>
 
 
 
 
 
 
 
 
48
  </div>
49
 
50
+ <?php \BooklyLite\Lib\Proxy\RecurringAppointments::renderAppearanceEditableInfoMessage() ?>
51
 
52
  <div class="bookly-box bookly-nav-steps">
53
+ <div class="bookly-back-step bookly-js-back-step bookly-btn">
54
+ <?php $editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
55
  </div>
56
+ <div class="bookly-next-step bookly-js-next-step bookly-btn">
57
+ <?php $editable::renderString( array( 'bookly_l10n_step_payment_button_next' ) ) ?>
58
  </div>
59
  </div>
60
  </div>
backend/modules/appearance/templates/_8_complete.php CHANGED
@@ -1,7 +1,16 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <div class="bookly-form">
3
  <?php include '_progress_tracker.php' ?>
4
- <div class="bookly-box">
5
- <span class="bookly-editable" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 8 ), false ) ) ?>" data-placement="bottom" data-option-default="<?php form_option( 'bookly_l10n_info_complete_step' ) ?>" id="bookly_l10n_info_complete_step" data-type="textarea"><?php echo nl2br( esc_html( get_option( 'bookly_l10n_info_complete_step' ) ) ) ?></span>
 
 
 
 
 
 
6
  </div>
7
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Backend\Modules\Appearance\Components;
3
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
4
+ ?>
5
  <div class="bookly-form">
6
  <?php include '_progress_tracker.php' ?>
7
+ <div class="bookly-box bookly-js-done-success">
8
+ <?php $editable::renderText( 'bookly_l10n_info_complete_step', Components::getInstance()->renderCodes( array( 'step' => 8, 'extra_codes' => 1 ), false ) ) ?>
9
+ </div>
10
+ <div class="bookly-box bookly-js-done-limit-error collapse">
11
+ <?php $editable::renderText( 'bookly_l10n_info_complete_step_limit_error', Components::getInstance()->renderCodes( array( 'step' => 8 ), false ) ) ?>
12
+ </div>
13
+ <div class="bookly-box bookly-js-done-processing collapse">
14
+ <?php $editable::renderText( 'bookly_l10n_info_complete_step_processing', Components::getInstance()->renderCodes( array( 'step' => 8, 'extra_codes' => 1 ), false ) ) ?>
15
  </div>
16
  </div>
backend/modules/appearance/templates/_card_payment.php CHANGED
@@ -1,24 +1,26 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
2
  <div class="bookly-box bookly-table">
3
- <div class="ab-formGroup" style="width:200px!important">
4
  <label>
5
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_ccard_number', ) ) ?>
6
  </label>
7
  <div>
8
  <input type="text" />
9
  </div>
10
  </div>
11
- <div class="ab-formGroup">
12
  <label>
13
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_ccard_expire', ) ) ?>
14
  </label>
15
  <div>
16
- <select class="ab-card-exp">
17
  <?php for ( $i = 1; $i <= 12; ++ $i ) : ?>
18
  <option value="<?php echo $i ?>"><?php printf( '%02d', $i ) ?></option>
19
  <?php endfor ?>
20
  </select>
21
- <select class="ab-card-exp">
22
  <?php for ( $i = date( 'Y' ); $i <= date( 'Y' ) + 10; ++ $i ) : ?>
23
  <option value="<?php echo $i ?>"><?php echo $i ?></option>
24
  <?php endfor ?>
@@ -26,13 +28,13 @@
26
  </div>
27
  </div>
28
  </div>
29
- <div class="bookly-box ab-clearBottom">
30
- <div class="ab-formGroup">
31
  <label>
32
- <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_label_ccard_code', ) ) ?>
33
  </label>
34
  <div>
35
- <input class="ab-card-cvc" type="text" />
36
  </div>
37
  </div>
38
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
3
+ ?>
4
  <div class="bookly-box bookly-table">
5
+ <div class="bookly-form-group" style="width:200px!important">
6
  <label>
7
+ <?php $editable::renderString( array( 'bookly_l10n_label_ccard_number', ) ) ?>
8
  </label>
9
  <div>
10
  <input type="text" />
11
  </div>
12
  </div>
13
+ <div class="bookly-form-group">
14
  <label>
15
+ <?php $editable::renderString( array( 'bookly_l10n_label_ccard_expire', ) ) ?>
16
  </label>
17
  <div>
18
+ <select class="bookly-card-exp">
19
  <?php for ( $i = 1; $i <= 12; ++ $i ) : ?>
20
  <option value="<?php echo $i ?>"><?php printf( '%02d', $i ) ?></option>
21
  <?php endfor ?>
22
  </select>
23
+ <select class="bookly-card-exp">
24
  <?php for ( $i = date( 'Y' ); $i <= date( 'Y' ) + 10; ++ $i ) : ?>
25
  <option value="<?php echo $i ?>"><?php echo $i ?></option>
26
  <?php endfor ?>
28
  </div>
29
  </div>
30
  </div>
31
+ <div class="bookly-box bookly-clear-bottom">
32
+ <div class="bookly-form-group">
33
  <label>
34
+ <?php $editable::renderString( array( 'bookly_l10n_label_ccard_code', ) ) ?>
35
  </label>
36
  <div>
37
+ <input class="bookly-card-cvc" type="text" />
38
  </div>
39
  </div>
40
  </div>
backend/modules/appearance/templates/_codes.php CHANGED
@@ -1,25 +1,20 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 
 
 
2
  $codes = array(
3
- array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ), ),
4
- array( 'code' => 'login_form', 'description' => __( 'login form', 'bookly' ),
5
- 'step' => 6,
6
- 'login' => true,
7
- ),
8
- array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ), ),
9
- array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ),
10
- 'step' => 8,
11
- ),
12
- array( 'code' => 'service_date', 'description' => __( 'date of service', 'bookly' ),
13
- 'min_step' => 3,
14
- ),
15
- array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ), ),
16
- array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ), ),
17
- array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ), ),
18
- array( 'code' => 'service_time', 'description' => __( 'time of service', 'bookly' ),
19
- 'min_step' => 3,
20
- ),
21
- array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ), ),
22
- array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ), ),
23
- array( 'code' => 'total_price', 'description' => __( 'total price of booking', 'bookly' ), ),
24
  );
25
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_prepare_appearance_short_codes', $codes ), $step, isset( $login ) ? $login : false );
 
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+
3
+ use BooklyLite\Lib;
4
+
5
  $codes = array(
6
+ array( 'code' => 'appointments_count', 'description' => __( 'total quantity of appointments in cart', 'bookly' ), 'flags' => array( 'step' => 7, 'extra_codes' => true ) ),
7
+ array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ), 'flags' => array( 'step' => 8, 'extra_codes' => true ) ),
8
+ array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
9
+ array( 'code' => 'login_form', 'description' => __( 'login form', 'bookly' ), 'flags' => array( 'step' => 6, 'extra_codes' => true ) ),
10
+ array( 'code' => 'service_date', 'description' => __( 'date of service', 'bookly' ), 'flags' => array( 'step' => '>3' ) ),
11
+ array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
12
+ array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
13
+ array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
14
+ array( 'code' => 'service_time', 'description' => __( 'time of service', 'bookly' ), 'flags' => array( 'step' => '>3' ) ),
15
+ array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
16
+ array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
17
+ array( 'code' => 'total_price', 'description' => __( 'total price of booking', 'bookly' ) ),
 
 
 
 
 
 
 
 
 
18
  );
19
+
20
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareAppearanceCodes( $codes ), array( 'step' => $step, 'extra_codes' => isset ( $extra_codes ) ) );
backend/modules/appearance/templates/_custom_css.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template to work with custom css.
4
+ * Template includes button to show custom css form + form to edit it
5
+ *
6
+ * @var string $custom_css custom css text
7
+ */
8
+ ?>
9
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
10
+
11
+ <div class="form-group">
12
+ <button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-custom-css-dialog">
13
+ <?php _e( 'Edit custom CSS', 'bookly' ); ?>
14
+ </button>
15
+ </div>
16
+
17
+ <div id="bookly-custom-css-dialog" class="modal fade" tabindex=-1 role="dialog">
18
+ <div class="modal-dialog">
19
+ <div class="modal-content">
20
+ <div class="modal-header">
21
+ <?php _e( 'Edit custom CSS', 'bookly' ) ?>
22
+ </div>
23
+ <div class="modal-body">
24
+ <div class="form-group">
25
+ <label for="bookly-custom-css" class="control-label"><?php _e( 'Set up your custom CSS styles', 'bookly' ) ?></label>
26
+ <textarea id="bookly-custom-css" class="form-control" rows="10"><?php echo $custom_css ?></textarea>
27
+ </div>
28
+ </div>
29
+ <div class="modal-footer">
30
+ <div id="bookly-custom-css-error"></div>
31
+ <?php \BooklyLite\Lib\Utils\Common::customButton( 'bookly-custom-css-save', 'btn-success btn-lg', __( 'Save', 'bookly' ) ) ?>
32
+ <?php \BooklyLite\Lib\Utils\Common::customButton( 'bookly-custom-css-cancel', 'btn-default btn-lg', __( 'Cancel', 'bookly' ) ) ?>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </div>
37
+
38
+ <script type="text/javascript">
39
+ var saved_css = <?php echo json_encode( $custom_css ); ?>;
40
+ </script>
backend/modules/appearance/templates/_progress_tracker.php CHANGED
@@ -1,41 +1,42 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 
2
  $i = 1;
3
  ?>
4
- <div class="ab-progress-tracker bookly-table">
5
  <div class="active">
6
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_service' ), 'bookly-js-text-service' ) ?>
7
  <div class="step"></div>
8
  </div>
9
- <?php if ( \BooklyLite\Lib\Config::isServiceExtrasEnabled() ) : ?>
10
  <div <?php if ( $step >= 2 ) : ?>class="active"<?php endif ?>>
11
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_extras' ), 'bookly-js-text-extras' ) ?>
12
  <div class="step"></div>
13
  </div>
14
  <?php endif ?>
15
  <div <?php if ( $step >= 3 ) : ?>class="active"<?php endif ?>>
16
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_time' ), 'bookly-js-text-time' ) ?>
17
  <div class="step"></div>
18
  </div>
19
- <?php if ( \BooklyLite\Lib\Config::isRecurringAppointmentsEnabled() ) : ?>
20
  <div <?php if ( $step >= 4 ) : ?>class="active"<?php endif ?>>
21
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_repeat' ), 'bookly-js-text-repeat' ) ?>
22
  <div class=step></div>
23
  </div>
24
  <?php endif ?>
25
  <div <?php if ( $step >= 5 ) : ?>class="active"<?php endif ?>>
26
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_cart' ), 'bookly-js-text-cart' ) ?>
27
  <div class="step"></div>
28
  </div>
29
  <div <?php if ( $step >= 6 ) : ?>class="active"<?php endif ?>>
30
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_details' ), 'bookly-js-text-details' ) ?>
31
  <div class="step"></div>
32
  </div>
33
  <div <?php if ( $step >= 7 ) : ?>class="active"<?php endif ?>>
34
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_payment' ), 'bookly-js-text-payment' ) ?>
35
  <div class="step"></div>
36
  </div>
37
  <div <?php if ( $step >= 8 ) : ?>class="active"<?php endif ?>>
38
- <?php echo $i ++ ?>. <?php \BooklyLite\Backend\Modules\Appearance\Lib\Helper::renderSpan( array( 'bookly_l10n_step_done' ), 'bookly-js-text-done' ) ?>
39
  <div class="step"></div>
40
  </div>
41
  </div>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var BooklyLite\Backend\Modules\Appearance\Lib\Helper $editable */
3
  $i = 1;
4
  ?>
5
+ <div class="bookly-progress-tracker bookly-table">
6
  <div class="active">
7
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_service' ) ) ?>
8
  <div class="step"></div>
9
  </div>
10
+ <?php if ( \BooklyLite\Lib\Config::serviceExtrasEnabled() ) : ?>
11
  <div <?php if ( $step >= 2 ) : ?>class="active"<?php endif ?>>
12
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_extras' ) ) ?>
13
  <div class="step"></div>
14
  </div>
15
  <?php endif ?>
16
  <div <?php if ( $step >= 3 ) : ?>class="active"<?php endif ?>>
17
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_time' ) ) ?>
18
  <div class="step"></div>
19
  </div>
20
+ <?php if ( \BooklyLite\Lib\Config::recurringAppointmentsEnabled() ) : ?>
21
  <div <?php if ( $step >= 4 ) : ?>class="active"<?php endif ?>>
22
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_repeat' ) ) ?>
23
  <div class=step></div>
24
  </div>
25
  <?php endif ?>
26
  <div <?php if ( $step >= 5 ) : ?>class="active"<?php endif ?>>
27
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_cart' ) ) ?>
28
  <div class="step"></div>
29
  </div>
30
  <div <?php if ( $step >= 6 ) : ?>class="active"<?php endif ?>>
31
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_details' ) ) ?>
32
  <div class="step"></div>
33
  </div>
34
  <div <?php if ( $step >= 7 ) : ?>class="active"<?php endif ?>>
35
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_payment' ) ) ?>
36
  <div class="step"></div>
37
  </div>
38
  <div <?php if ( $step >= 8 ) : ?>class="active"<?php endif ?>>
39
+ <?php echo $i ++ ?>. <?php $editable::renderString( array( 'bookly_l10n_step_done' ) ) ?>
40
  <div class="step"></div>
41
  </div>
42
  </div>
backend/modules/appearance/templates/index.php CHANGED
@@ -1,4 +1,17 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  <div id="bookly-tbs" class="wrap">
3
  <div class="bookly-tbs-body">
4
  <div class="page-header text-right clearfix">
@@ -9,7 +22,7 @@
9
  </div>
10
  <div class="panel panel-default bookly-main">
11
  <div class="panel-body">
12
- <div id="ab-appearance">
13
  <div class="row">
14
  <div class="col-sm-3 col-lg-2 bookly-color-picker-wrapper">
15
  <input type="text" name="color" class="bookly-js-color-picker"
@@ -19,7 +32,7 @@
19
  <div class="col-sm-9 col-lg-10">
20
  <div class="checkbox">
21
  <label>
22
- <input type="checkbox" id=ab-progress-tracker-checkbox <?php checked( get_option( 'bookly_app_show_progress_tracker' ) ) ?>>
23
  <?php _e( 'Show form progress tracker', 'bookly' ) ?>
24
  </label>
25
  </div>
@@ -29,9 +42,9 @@
29
  <ul class="bookly-nav bookly-nav-tabs bookly-margin-top-lg" role="tablist">
30
  <?php $i = 1 ?>
31
  <?php foreach ( $steps as $step => $step_name ) : ?>
32
- <?php if ( ( $step != 2 || \BooklyLite\Lib\Config::isServiceExtrasEnabled() )
33
- && ( $step != 4 || \BooklyLite\Lib\Config::isRecurringAppointmentsEnabled() ) ) : ?>
34
- <li class="bookly-nav-item <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="#ab-step-<?php echo $step ?>" data-toggle="tab">
35
  <?php echo $i++ ?>. <?php echo esc_html( $step_name ) ?>
36
  </li>
37
  <?php endif ?>
@@ -45,31 +58,39 @@
45
  </div>
46
  <?php endif ?>
47
 
48
- <div class="row" id="bookly-js-step-settings">
49
- <div id="bookly-js-step-service" class="bookly-margin-top-lg">
50
- <?php do_action( 'bookly_render_appearance_step_service_settings' ) ?>
51
- <div class="col-md-4">
52
  <div class="checkbox">
53
  <label>
54
- <input type="checkbox" id=ab-required-employee-checkbox <?php checked( get_option( 'bookly_app_required_employee' ) ) ?>>
55
  <?php _e( 'Make selecting employee required', 'bookly' ) ?>
56
  </label>
57
  </div>
58
  </div>
59
- <div class="col-md-4">
60
  <div class="checkbox">
61
  <label>
62
- <input type="checkbox" id=ab-staff-name-with-price-checkbox <?php checked( get_option( 'bookly_app_staff_name_with_price' ) ) ?>>
63
  <?php _e( 'Show service price next to employee name', 'bookly' ) ?>
64
  </label>
65
  </div>
66
  </div>
 
 
 
 
 
 
 
 
67
  </div>
68
- <div id="bookly-js-step-time" class="bookly-margin-top-lg" style="display:none">
69
  <div class="col-md-4">
70
  <div class="checkbox">
71
  <label>
72
- <input type="checkbox" id="ab-show-calendar-checkbox" <?php checked( get_option( 'bookly_app_show_calendar' ) ) ?>>
73
  <?php _e( 'Show calendar', 'bookly' ) ?>
74
  </label>
75
  </div>
@@ -77,7 +98,7 @@
77
  <div class="col-md-4">
78
  <div class="checkbox">
79
  <label>
80
- <input type="checkbox" id="ab-blocked-timeslots-checkbox" <?php checked( get_option( 'bookly_app_show_blocked_timeslots' ) ) ?>>
81
  <?php _e( 'Show blocked timeslots', 'bookly' ) ?>
82
  </label>
83
  </div>
@@ -85,26 +106,101 @@
85
  <div class="col-md-4">
86
  <div class="checkbox">
87
  <label>
88
- <input type="checkbox" id="ab-day-one-column-checkbox" <?php checked( get_option( 'bookly_app_show_day_one_column' ) ) ?>>
89
  <?php _e( 'Show each day in one column', 'bookly' ) ?>
90
  </label>
91
  </div>
92
  </div>
93
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  </div>
95
 
96
  <div class="panel panel-default bookly-margin-top-lg">
97
  <div class="panel-body">
98
  <div class="tab-content">
99
  <?php foreach ( $steps as $step => $step_name ) : ?>
100
- <div id="ab-step-<?php echo $step ?>" class="tab-pane <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="<?php echo $step ?>">
101
  <?php // Render unique data per step
102
  switch ( $step ) :
103
  case 1: include '_1_service.php'; break;
104
- case 2: do_action( 'bookly_service_extras_render_appearance', $this->render( '_progress_tracker', array( 'step' => $step ), false ) );
105
  break;
106
  case 3: include '_3_time.php'; break;
107
- case 4: do_action( 'bookly_recurring_appointments_render_appearance', $this->render( '_progress_tracker', array( 'step' => $step ), false ) );
108
  break;
109
  case 5: include '_5_cart.php'; break;
110
  case 6: include '_6_details.php'; break;
@@ -116,7 +212,9 @@
116
  </div>
117
  </div>
118
  </div>
119
-
 
 
120
  </div>
121
  </div>
122
  <div class="panel-footer">
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /**
3
+ * Template to show appearance page
4
+ * @var array $steps list of steps in booking form, could be string (the name of step) or false if step disabled
5
+ * @var string $custom_css custom css text
6
+ */
7
+ ?>
8
+
9
+ <?php if ( trim( $custom_css ) ) : ?>
10
+ <style type="text/css">
11
+ <?php echo $custom_css ?>
12
+ </style>
13
+ <?php endif ?>
14
+
15
  <div id="bookly-tbs" class="wrap">
16
  <div class="bookly-tbs-body">
17
  <div class="page-header text-right clearfix">
22
  </div>
23
  <div class="panel panel-default bookly-main">
24
  <div class="panel-body">
25
+ <div id="bookly-appearance">
26
  <div class="row">
27
  <div class="col-sm-3 col-lg-2 bookly-color-picker-wrapper">
28
  <input type="text" name="color" class="bookly-js-color-picker"
32
  <div class="col-sm-9 col-lg-10">
33
  <div class="checkbox">
34
  <label>
35
+ <input type="checkbox" id=bookly-show-progress-tracker <?php checked( get_option( 'bookly_app_show_progress_tracker' ) ) ?>>
36
  <?php _e( 'Show form progress tracker', 'bookly' ) ?>
37
  </label>
38
  </div>
42
  <ul class="bookly-nav bookly-nav-tabs bookly-margin-top-lg" role="tablist">
43
  <?php $i = 1 ?>
44
  <?php foreach ( $steps as $step => $step_name ) : ?>
45
+ <?php if ( ( $step != 2 || \BooklyLite\Lib\Config::serviceExtrasEnabled() )
46
+ && ( $step != 4 || \BooklyLite\Lib\Config::recurringAppointmentsEnabled() ) ) : ?>
47
+ <li class="bookly-nav-item <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="#bookly-step-<?php echo $step ?>" data-toggle="tab">
48
  <?php echo $i++ ?>. <?php echo esc_html( $step_name ) ?>
49
  </li>
50
  <?php endif ?>
58
  </div>
59
  <?php endif ?>
60
 
61
+ <div class="row" id="bookly-step-settings">
62
+ <div class="bookly-js-service-settings bookly-margin-top-lg">
63
+ <?php \BooklyLite\Lib\Proxy\Shared::renderAppearanceStepServiceSettings() ?>
64
+ <div class="col-md-3">
65
  <div class="checkbox">
66
  <label>
67
+ <input type="checkbox" id=bookly-required-employee <?php checked( get_option( 'bookly_app_required_employee' ) ) ?>>
68
  <?php _e( 'Make selecting employee required', 'bookly' ) ?>
69
  </label>
70
  </div>
71
  </div>
72
+ <div class="col-md-3">
73
  <div class="checkbox">
74
  <label>
75
+ <input type="checkbox" id=bookly-staff-name-with-price <?php checked( get_option( 'bookly_app_staff_name_with_price' ) ) ?>>
76
  <?php _e( 'Show service price next to employee name', 'bookly' ) ?>
77
  </label>
78
  </div>
79
  </div>
80
+ <div class="col-md-3">
81
+ <div class="checkbox">
82
+ <label>
83
+ <input type="checkbox" id=bookly-service-name-with-duration <?php checked( get_option( 'bookly_app_service_name_with_duration' ) ) ?>>
84
+ <?php _e( 'Show service duration next to service name', 'bookly' ) ?>
85
+ </label>
86
+ </div>
87
+ </div>
88
  </div>
89
+ <div class="bookly-js-time-settings bookly-margin-top-lg" style="display:none">
90
  <div class="col-md-4">
91
  <div class="checkbox">
92
  <label>
93
+ <input type="checkbox" id="bookly-show-calendar" <?php checked( get_option( 'bookly_app_show_calendar' ) ) ?>>
94
  <?php _e( 'Show calendar', 'bookly' ) ?>
95
  </label>
96
  </div>
98
  <div class="col-md-4">
99
  <div class="checkbox">
100
  <label>
101
+ <input type="checkbox" id="bookly-show-blocked-timeslots" <?php checked( get_option( 'bookly_app_show_blocked_timeslots' ) ) ?>>
102
  <?php _e( 'Show blocked timeslots', 'bookly' ) ?>
103
  </label>
104
  </div>
106
  <div class="col-md-4">
107
  <div class="checkbox">
108
  <label>
109
+ <input type="checkbox" id="bookly-show-day-one-column" <?php checked( get_option( 'bookly_app_show_day_one_column' ) ) ?>>
110
  <?php _e( 'Show each day in one column', 'bookly' ) ?>
111
  </label>
112
  </div>
113
  </div>
114
  </div>
115
+ <div class="bookly-js-details-settings bookly-margin-top-lg" style="display:none">
116
+ <div class="col-md-3">
117
+ <div class="checkbox">
118
+ <label>
119
+ <input type="checkbox" id="bookly-cst-required-phone" <?php checked( get_option( 'bookly_cst_required_phone' ) ) ?>>
120
+ <?php _e( 'Make phone field required', 'bookly' ) ?>
121
+ </label>
122
+ </div>
123
+ </div>
124
+ <div class="col-md-3">
125
+ <div class="checkbox">
126
+ <label>
127
+ <input type="checkbox" id="bookly-show-login-button" <?php checked( get_option( 'bookly_app_show_login_button' ) ) ?>>
128
+ <?php _e( 'Show Login button', 'bookly' ) ?>
129
+ </label>
130
+ </div>
131
+ </div>
132
+ <div class="col-md-3">
133
+ <div class="checkbox">
134
+ <label>
135
+ <input type="checkbox" id="bookly-cst-first-last-name" <?php checked( get_option( 'bookly_cst_first_last_name' ) ) ?> data-toggle="popover" data-trigger="focus" data-placement="auto bottom" data-content="<?php _e( 'Do not forget to update your email and SMS codes for customer names', 'bookly' ) ?>">
136
+ <?php _e( 'Use first and last name instead of full name', 'bookly' ) ?>
137
+ </label>
138
+ </div>
139
+ </div>
140
+ <div class="col-md-3">
141
+ <div class="checkbox">
142
+ <label>
143
+ <input type="checkbox" id="bookly-show-notes" <?php checked( get_option( 'bookly_app_show_notes' ) ) ?>>
144
+ <?php _e( 'Show notes field', 'bookly' ) ?>
145
+ </label>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ <div class="bookly-js-payment-settings bookly-margin-top-lg" style="display:none">
150
+ <div class="col-md-12">
151
+ <div class="alert alert-info bookly-margin-top-lg bookly-margin-bottom-remove bookly-flexbox">
152
+ <div class="bookly-flex-row">
153
+ <div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
154
+ <div class="bookly-flex-cell">
155
+ <div>
156
+ <?php _e( 'The booking form on this step may have different set or states of its elements. It depends on various conditions such as installed/activated add-ons, settings configuration or choices made on previous steps. Select option and click on the underlined text to edit.', 'bookly' ) ?>
157
+ </div>
158
+ <div class="bookly-margin-top-lg">
159
+ <select id="bookly-payment-step-view" class="form-control">
160
+ <option value="single-app"><?php _e( 'Form view in case of single booking', 'bookly' ) ?></option>
161
+ <option value="several-apps"><?php _e( 'Form view in case of multiple booking', 'bookly' ) ?></option>
162
+ </select>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ <div class="bookly-js-done-settings bookly-margin-top-lg" style="display:none">
170
+ <div class="col-md-12">
171
+ <div class="alert alert-info bookly-margin-top-lg bookly-margin-bottom-remove bookly-flexbox">
172
+ <div class="bookly-flex-row">
173
+ <div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
174
+ <div class="bookly-flex-cell">
175
+ <div>
176
+ <?php _e( 'The booking form on this step may have different set or states of its elements. It depends on various conditions such as installed/activated add-ons, settings configuration or choices made on previous steps. Select option and click on the underlined text to edit.', 'bookly' ) ?>
177
+ </div>
178
+ <div class="bookly-margin-top-lg">
179
+ <select id="bookly-done-step-view" class="form-control">
180
+ <option value="booking-success"><?php _e( 'Form view in case of successful booking', 'bookly' ) ?></option>
181
+ <option value="booking-limit-error"><?php _e( 'Form view in case the number of bookings exceeds the limit', 'bookly' ) ?></option>
182
+ <option value="booking-processing"><?php _e( 'Form view in case of payment has been accepted for processing', 'bookly' ) ?></option>
183
+ </select>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </div>
189
+ </div>
190
  </div>
191
 
192
  <div class="panel panel-default bookly-margin-top-lg">
193
  <div class="panel-body">
194
  <div class="tab-content">
195
  <?php foreach ( $steps as $step => $step_name ) : ?>
196
+ <div id="bookly-step-<?php echo $step ?>" class="tab-pane <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="<?php echo $step ?>">
197
  <?php // Render unique data per step
198
  switch ( $step ) :
199
  case 1: include '_1_service.php'; break;
200
+ case 2: \BooklyLite\Lib\Proxy\ServiceExtras::renderAppearance( $this->render( '_progress_tracker', compact( 'step', 'editable' ), false ) );
201
  break;
202
  case 3: include '_3_time.php'; break;
203
+ case 4: \BooklyLite\Lib\Proxy\RecurringAppointments::renderAppearance( $this->render( '_progress_tracker', compact( 'step', 'editable' ), false ) );
204
  break;
205
  case 5: include '_5_cart.php'; break;
206
  case 6: include '_6_details.php'; break;
212
  </div>
213
  </div>
214
  </div>
215
+ <div>
216
+ <?php $this->render( '_custom_css', array( 'custom_css' => $custom_css) ); ?>
217
+ </div>
218
  </div>
219
  </div>
220
  <div class="panel-footer">
backend/modules/appointments/Controller.php CHANGED
@@ -2,6 +2,7 @@
2
  namespace BooklyLite\Backend\Modules\Appointments;
3
 
4
  use BooklyLite\Lib;
 
5
 
6
  /**
7
  * Class Controller
@@ -19,6 +20,7 @@ class Controller extends Lib\Base\Controller
19
  $this->enqueueStyles( array(
20
  'frontend' => array( 'css/ladda.min.css', ),
21
  'backend' => array(
 
22
  'bootstrap/css/bootstrap-theme.min.css',
23
  'css/daterangepicker.css',
24
  ),
@@ -27,9 +29,10 @@ class Controller extends Lib\Base\Controller
27
  $this->enqueueScripts( array(
28
  'backend' => array(
29
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
30
- 'js/datatables.min.js' => array( 'jquery' ),
31
  'js/moment.min.js',
32
- 'js/daterangepicker.js' => array( 'jquery' ),
 
33
  ),
34
  'frontend' => array(
35
  'js/spin.min.js' => array( 'jquery' ),
@@ -38,12 +41,11 @@ class Controller extends Lib\Base\Controller
38
  'module' => array( 'js/appointments.js' => array( 'bookly-datatables.min.js' ), ),
39
  ) );
40
 
41
- // Custom fields without captcha field.
42
- $custom_fields = array_filter( json_decode( get_option( 'bookly_custom_fields' ) ), function( $field ) {
43
- return ! in_array( $field->type, array( 'captcha', 'text-content' ) );
44
- } );
45
 
46
  wp_localize_script( 'bookly-appointments.js', 'BooklyL10n', array(
 
47
  'tomorrow' => __( 'Tomorrow', 'bookly' ),
48
  'today' => __( 'Today', 'bookly' ),
49
  'yesterday' => __( 'Yesterday', 'bookly' ),
@@ -68,14 +70,16 @@ class Controller extends Lib\Base\Controller
68
  'zeroRecords' => __( 'No appointments for selected period.', 'bookly' ),
69
  'processing' => __( 'Processing...', 'bookly' ),
70
  'edit' => __( 'Edit', 'bookly' ),
 
71
  'cf_columns' => array_map( function ( $custom_field ) { return $custom_field->id; }, $custom_fields ),
72
  'filter' => (array) get_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', true ),
73
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
 
74
  ) );
75
 
76
  // Filters data
77
- $staff_members = Lib\Entities\Staff::query( 's' )->select( 's.id, s.full_name' )->fetchArray();
78
- $customers = Lib\Entities\Customer::query( 'c' )->select( 'c.id, c.name' )->fetchArray();
79
  $services = Lib\Entities\Service::query( 's' )->select( 's.id, s.title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray();
80
 
81
  $this->render( 'index', compact( 'custom_fields', 'staff_members', 'customers', 'services' ) );
@@ -89,25 +93,27 @@ class Controller extends Lib\Base\Controller
89
  $columns = $this->getParameter( 'columns' );
90
  $order = $this->getParameter( 'order' );
91
  $filter = $this->getParameter( 'filter' );
 
92
 
93
  $query = Lib\Entities\CustomerAppointment::query( 'ca' )
94
  ->select( 'a.id,
95
  ca.payment_id,
96
  ca.status,
97
  ca.id AS ca_id,
 
98
  ca.extras,
99
  a.start_date,
100
- a.extras_duration,
101
- c.name AS customer_name,
102
  c.phone AS customer_phone,
103
  c.email AS customer_email,
104
- s.title AS service_title,
105
- s.duration AS service_duration,
106
  st.full_name AS staff_name,
107
  p.paid AS payment,
108
  p.total AS payment_total,
109
  p.type AS payment_type,
110
- p.status AS payment_status' )
 
 
111
  ->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
112
  ->leftJoin( 'Service', 's', 's.id = a.service_id' )
113
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
@@ -125,19 +131,19 @@ class Controller extends Lib\Base\Controller
125
  $end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
126
  $query->whereBetween( 'a.start_date', $start, $end );
127
 
128
- if ( $filter['staff'] != -1 ) {
129
  $query->where( 'a.staff_id', $filter['staff'] );
130
  }
131
 
132
- if ( $filter['customer'] != -1 ) {
133
  $query->where( 'ca.customer_id', $filter['customer'] );
134
  }
135
 
136
- if ( $filter['service'] != -1 ) {
137
- $query->where( 'a.service_id', $filter['service'] );
138
  }
139
 
140
- if ( $filter['status'] != -1 ) {
141
  $query->where( 'ca.status', $filter['status'] );
142
  }
143
 
@@ -147,9 +153,7 @@ class Controller extends Lib\Base\Controller
147
  }
148
 
149
  $custom_fields = array();
150
- $fields_data = array_filter( json_decode( get_option( 'bookly_custom_fields' ) ), function( $field ) {
151
- return ! in_array( $field->type, array( 'captcha', 'text-content' ) );
152
- } );
153
  foreach ( $fields_data as $field_data ) {
154
  $custom_fields[ $field_data->id ] = '';
155
  }
@@ -158,18 +162,14 @@ class Controller extends Lib\Base\Controller
158
  foreach ( $query->fetchArray() as $row ) {
159
  // Service duration.
160
  $service_duration = Lib\Utils\DateTime::secondsToInterval( $row['service_duration'] );
161
- if ( $row['extras_duration'] > 0 ) {
162
- $service_duration .= ' + ' . Lib\Utils\DateTime::secondsToInterval( $row['extras_duration'] );
163
- }
164
  // Appointment status.
165
  $row['status'] = Lib\Entities\CustomerAppointment::statusToString( $row['status'] );
166
-
167
  // Payment title.
168
  $payment_title = '';
169
  if ( $row['payment'] !== null ) {
170
- $payment_title = Lib\Utils\Common::formatPrice( $row['payment'] );
171
  if ( $row['payment'] != $row['payment_total'] ) {
172
- $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $row['payment_total'] ) );
173
  }
174
  $payment_title .= sprintf(
175
  ' %s <span%s>%s</span>',
@@ -181,7 +181,7 @@ class Controller extends Lib\Base\Controller
181
  // Custom fields
182
  $customer_appointment = new Lib\Entities\CustomerAppointment();
183
  $customer_appointment->load( $row['ca_id'] );
184
- foreach ( $customer_appointment->getCustomFields() as $custom_field ) {
185
  $custom_fields[ $custom_field['id'] ] = $custom_field['value'];
186
  }
187
 
@@ -189,20 +189,21 @@ class Controller extends Lib\Base\Controller
189
  'id' => $row['id'],
190
  'start_date' => Lib\Utils\DateTime::formatDateTime( $row['start_date'] ),
191
  'staff' => array(
192
- 'name' => $row['staff_name'],
193
  ),
194
- 'customer' => array(
195
- 'name' => $row['customer_name'],
196
- 'phone' => $row['customer_phone'],
197
- 'email' => $row['customer_email'],
198
  ),
199
  'service' => array(
200
  'title' => $row['service_title'],
201
  'duration' => $service_duration,
202
- 'extras' => apply_filters( 'bookly_service_extras_get_data_for_appointment', array(), $row['extras'], false ),
203
  ),
204
  'status' => $row['status'],
205
  'payment' => $payment_title,
 
206
  'custom_fields' => $custom_fields,
207
  'ca_id' => $row['ca_id'],
208
  'payment_id' => $row['payment_id'],
@@ -215,7 +216,7 @@ class Controller extends Lib\Base\Controller
215
  update_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', $filter );
216
 
217
  wp_send_json( array(
218
- 'draw' => ( int ) $this->getParameter( 'draw' ),
219
  'recordsTotal' => $total,
220
  'recordsFiltered' => count( $data ),
221
  'data' => $data,
@@ -230,27 +231,23 @@ class Controller extends Lib\Base\Controller
230
  /** @var Lib\Entities\CustomerAppointment $ca */
231
  foreach ( Lib\Entities\CustomerAppointment::query()->whereIn( 'id', $this->getParameter( 'data', array() ) )->find() as $ca ) {
232
  if ( $this->getParameter( 'notify' ) ) {
233
- if ( $ca->get('status') === Lib\Entities\CustomerAppointment::STATUS_PENDING ) {
234
- $ca->set( 'status', Lib\Entities\CustomerAppointment::STATUS_REJECTED );
235
- } else { // STATUS_APPROVED
236
- $ca->set( 'status', Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
 
 
 
 
237
  }
238
- \BooklyLite\Lib\NotificationSender::send( $ca, array( 'cancellation_reason' => $this->getParameter( 'reason' ) ) );
 
 
 
 
239
  }
240
  $ca->deleteCascade();
241
  }
242
  wp_send_json_success();
243
  }
244
-
245
- /**
246
- * Override parent method to add 'wp_ajax_bookly_' prefix
247
- * so current 'execute*' methods look nicer.
248
- *
249
- * @param string $prefix
250
- */
251
- protected function registerWpActions( $prefix = '' )
252
- {
253
- parent::registerWpActions( 'wp_ajax_bookly_' );
254
- }
255
-
256
  }
2
  namespace BooklyLite\Backend\Modules\Appointments;
3
 
4
  use BooklyLite\Lib;
5
+ use BooklyLite\Lib\DataHolders\Booking as DataHolders;
6
 
7
  /**
8
  * Class Controller
20
  $this->enqueueStyles( array(
21
  'frontend' => array( 'css/ladda.min.css', ),
22
  'backend' => array(
23
+ 'css/select2.min.css',
24
  'bootstrap/css/bootstrap-theme.min.css',
25
  'css/daterangepicker.css',
26
  ),
29
  $this->enqueueScripts( array(
30
  'backend' => array(
31
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
32
+ 'js/datatables.min.js' => array( 'jquery' ),
33
  'js/moment.min.js',
34
+ 'js/daterangepicker.js' => array( 'jquery' ),
35
+ 'js/select2.full.min.js' => array( 'jquery' ),
36
  ),
37
  'frontend' => array(
38
  'js/spin.min.js' => array( 'jquery' ),
41
  'module' => array( 'js/appointments.js' => array( 'bookly-datatables.min.js' ), ),
42
  ) );
43
 
44
+ // Custom fields without captcha & text content field.
45
+ $custom_fields = (array) Lib\Proxy\CustomFields::getWhichHaveData();
 
 
46
 
47
  wp_localize_script( 'bookly-appointments.js', 'BooklyL10n', array(
48
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
49
  'tomorrow' => __( 'Tomorrow', 'bookly' ),
50
  'today' => __( 'Today', 'bookly' ),
51
  'yesterday' => __( 'Yesterday', 'bookly' ),
70
  'zeroRecords' => __( 'No appointments for selected period.', 'bookly' ),
71
  'processing' => __( 'Processing...', 'bookly' ),
72
  'edit' => __( 'Edit', 'bookly' ),
73
+ 'show_notes' => Lib\Config::showNotes(),
74
  'cf_columns' => array_map( function ( $custom_field ) { return $custom_field->id; }, $custom_fields ),
75
  'filter' => (array) get_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', true ),
76
+ 'no_result_found' => __( 'No result found', 'bookly' ),
77
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
78
  ) );
79
 
80
  // Filters data
81
+ $staff_members = Lib\Entities\Staff::query( 's' )->select( 's.id, s.full_name' )->where( 'id', 1 )->fetchArray();
82
+ $customers = Lib\Entities\Customer::query( 'c' )->select( 'c.id, c.full_name, c.first_name, c.last_name' )->fetchArray();
83
  $services = Lib\Entities\Service::query( 's' )->select( 's.id, s.title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray();
84
 
85
  $this->render( 'index', compact( 'custom_fields', 'staff_members', 'customers', 'services' ) );
93
  $columns = $this->getParameter( 'columns' );
94
  $order = $this->getParameter( 'order' );
95
  $filter = $this->getParameter( 'filter' );
96
+ $postfix_any = sprintf( ' (%s)', get_option( 'bookly_l10n_option_employee' ) );
97
 
98
  $query = Lib\Entities\CustomerAppointment::query( 'ca' )
99
  ->select( 'a.id,
100
  ca.payment_id,
101
  ca.status,
102
  ca.id AS ca_id,
103
+ ca.notes,
104
  ca.extras,
105
  a.start_date,
106
+ a.staff_any,
107
+ c.full_name AS customer_full_name,
108
  c.phone AS customer_phone,
109
  c.email AS customer_email,
 
 
110
  st.full_name AS staff_name,
111
  p.paid AS payment,
112
  p.total AS payment_total,
113
  p.type AS payment_type,
114
+ p.status AS payment_status,
115
+ COALESCE(s.title, a.custom_service_name) AS service_title,
116
+ TIME_TO_SEC(TIMEDIFF(a.end_date, a.start_date)) + a.extras_duration AS service_duration' )
117
  ->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
118
  ->leftJoin( 'Service', 's', 's.id = a.service_id' )
119
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
131
  $end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
132
  $query->whereBetween( 'a.start_date', $start, $end );
133
 
134
+ if ( $filter['staff'] != '' ) {
135
  $query->where( 'a.staff_id', $filter['staff'] );
136
  }
137
 
138
+ if ( $filter['customer'] != '' ) {
139
  $query->where( 'ca.customer_id', $filter['customer'] );
140
  }
141
 
142
+ if ( $filter['service'] != '' ) {
143
+ $query->where( 'a.service_id', $filter['service'] ?: null );
144
  }
145
 
146
+ if ( $filter['status'] != '' ) {
147
  $query->where( 'ca.status', $filter['status'] );
148
  }
149
 
153
  }
154
 
155
  $custom_fields = array();
156
+ $fields_data = (array) Lib\Proxy\CustomFields::getWhichHaveData();
 
 
157
  foreach ( $fields_data as $field_data ) {
158
  $custom_fields[ $field_data->id ] = '';
159
  }
162
  foreach ( $query->fetchArray() as $row ) {
163
  // Service duration.
164
  $service_duration = Lib\Utils\DateTime::secondsToInterval( $row['service_duration'] );
 
 
 
165
  // Appointment status.
166
  $row['status'] = Lib\Entities\CustomerAppointment::statusToString( $row['status'] );
 
167
  // Payment title.
168
  $payment_title = '';
169
  if ( $row['payment'] !== null ) {
170
+ $payment_title = Lib\Utils\Price::format( $row['payment'] );
171
  if ( $row['payment'] != $row['payment_total'] ) {
172
+ $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $row['payment_total'] ) );
173
  }
174
  $payment_title .= sprintf(
175
  ' %s <span%s>%s</span>',
181
  // Custom fields
182
  $customer_appointment = new Lib\Entities\CustomerAppointment();
183
  $customer_appointment->load( $row['ca_id'] );
184
+ foreach ( (array) Lib\Proxy\CustomFields::getForCustomerAppointment( $customer_appointment ) as $custom_field ) {
185
  $custom_fields[ $custom_field['id'] ] = $custom_field['value'];
186
  }
187
 
189
  'id' => $row['id'],
190
  'start_date' => Lib\Utils\DateTime::formatDateTime( $row['start_date'] ),
191
  'staff' => array(
192
+ 'name' => $row['staff_name'] . ( $row['staff_any'] ? $postfix_any : '' ),
193
  ),
194
+ 'customer' => array(
195
+ 'full_name' => $row['customer_full_name'],
196
+ 'phone' => $row['customer_phone'],
197
+ 'email' => $row['customer_email'],
198
  ),
199
  'service' => array(
200
  'title' => $row['service_title'],
201
  'duration' => $service_duration,
202
+ 'extras' => (array) Lib\Proxy\ServiceExtras::getInfo( json_decode( $row['extras'], true ), false ),
203
  ),
204
  'status' => $row['status'],
205
  'payment' => $payment_title,
206
+ 'notes' => $row['notes'],
207
  'custom_fields' => $custom_fields,
208
  'ca_id' => $row['ca_id'],
209
  'payment_id' => $row['payment_id'],
216
  update_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', $filter );
217
 
218
  wp_send_json( array(
219
+ 'draw' => (int) $this->getParameter( 'draw' ),
220
  'recordsTotal' => $total,
221
  'recordsFiltered' => count( $data ),
222
  'data' => $data,
231
  /** @var Lib\Entities\CustomerAppointment $ca */
232
  foreach ( Lib\Entities\CustomerAppointment::query()->whereIn( 'id', $this->getParameter( 'data', array() ) )->find() as $ca ) {
233
  if ( $this->getParameter( 'notify' ) ) {
234
+ switch ( $ca->getStatus() ) {
235
+ case Lib\Entities\CustomerAppointment::STATUS_PENDING:
236
+ case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
237
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
238
+ break;
239
+ case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
240
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
241
+ break;
242
  }
243
+ Lib\NotificationSender::sendSingle(
244
+ DataHolders\Simple::create( $ca ),
245
+ null,
246
+ array( 'cancellation_reason' => $this->getParameter( 'reason' ) )
247
+ );
248
  }
249
  $ca->deleteCascade();
250
  }
251
  wp_send_json_success();
252
  }
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
backend/modules/appointments/resources/js/appointments.js CHANGED
@@ -23,10 +23,17 @@ jQuery(function($) {
23
 
24
  }
25
 
 
26
  $.each(BooklyL10n.filter, function (field, value) {
27
- if (value != -1) {
28
  $('#bookly-filter-' + field).val(value);
29
  }
 
 
 
 
 
 
30
  });
31
 
32
  /**
@@ -36,7 +43,7 @@ jQuery(function($) {
36
  { data: 'id', responsivePriority: 2 },
37
  { data: 'start_date', responsivePriority: 2 },
38
  { data: 'staff.name', responsivePriority: 2 },
39
- { data: 'customer.name', render: $.fn.dataTable.render.text(), responsivePriority: 2 },
40
  {
41
  data: 'customer.phone',
42
  responsivePriority: 3,
@@ -76,6 +83,13 @@ jQuery(function($) {
76
  }
77
  }
78
  ];
 
 
 
 
 
 
 
79
  $.each(BooklyL10n.cf_columns, function (i, cf_id) {
80
  columns.push({
81
  data: 'custom_fields.' + cf_id,
@@ -96,7 +110,7 @@ jQuery(function($) {
96
  url : ajaxurl,
97
  type: 'POST',
98
  data: function (d) {
99
- return $.extend({action: 'bookly_get_appointments'}, {
100
  filter: {
101
  id : $id_filter.val(),
102
  date : $date_filter.data('date'),
@@ -214,10 +228,11 @@ jQuery(function($) {
214
  url : ajaxurl,
215
  type : 'POST',
216
  data : {
217
- action : 'bookly_delete_customer_appointments',
218
- data : data,
219
- notify : $('#bookly-delete-notify').prop('checked') ? 1 : 0,
220
- reason : $('#bookly-delete-reason').val()
 
221
  },
222
  dataType : 'json',
223
  success : function(response) {
@@ -282,10 +297,20 @@ jQuery(function($) {
282
  /**
283
  * On filters change.
284
  */
285
- $('.bookly-js-chosen-select').chosen({
286
- allow_single_deselect: true,
287
- disable_search_threshold: 10
288
- });
 
 
 
 
 
 
 
 
 
 
289
  $id_filter.on('keyup', function () { dt.ajax.reload(); });
290
  $date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
291
  $staff_filter.on('change', function () { dt.ajax.reload(); });
23
 
24
  }
25
 
26
+ $('.bookly-js-select').val(null);
27
  $.each(BooklyL10n.filter, function (field, value) {
28
+ if (value != '') {
29
  $('#bookly-filter-' + field).val(value);
30
  }
31
+ // check if select has correct values
32
+ if ($('#bookly-filter-' + field).prop('type') == 'select-one') {
33
+ if ($('#bookly-filter-' + field +' option[value="' + value + '"]').length == 0) {
34
+ $('#bookly-filter-' + field).val(null);
35
+ }
36
+ }
37
  });
38
 
39
  /**
43
  { data: 'id', responsivePriority: 2 },
44
  { data: 'start_date', responsivePriority: 2 },
45
  { data: 'staff.name', responsivePriority: 2 },
46
+ { data: 'customer.full_name', render: $.fn.dataTable.render.text(), responsivePriority: 2 },
47
  {
48
  data: 'customer.phone',
49
  responsivePriority: 3,
83
  }
84
  }
85
  ];
86
+ if (BooklyL10n.show_notes) {
87
+ columns.push({
88
+ data: 'notes',
89
+ render: $.fn.dataTable.render.text(),
90
+ responsivePriority: 4
91
+ });
92
+ }
93
  $.each(BooklyL10n.cf_columns, function (i, cf_id) {
94
  columns.push({
95
  data: 'custom_fields.' + cf_id,
110
  url : ajaxurl,
111
  type: 'POST',
112
  data: function (d) {
113
+ return $.extend({action: 'bookly_get_appointments', csrf_token : BooklyL10n.csrf_token}, {
114
  filter: {
115
  id : $id_filter.val(),
116
  date : $date_filter.data('date'),
228
  url : ajaxurl,
229
  type : 'POST',
230
  data : {
231
+ action : 'bookly_delete_customer_appointments',
232
+ csrf_token : BooklyL10n.csrf_token,
233
+ data : data,
234
+ notify : $('#bookly-delete-notify').prop('checked') ? 1 : 0,
235
+ reason : $('#bookly-delete-reason').val()
236
  },
237
  dataType : 'json',
238
  success : function(response) {
297
  /**
298
  * On filters change.
299
  */
300
+ $('.bookly-js-select')
301
+ .on('select2:unselecting', function(e) {
302
+ e.preventDefault();
303
+ $(this).val(null).trigger('change');
304
+ })
305
+ .select2({
306
+ width: '100%',
307
+ theme: 'bootstrap',
308
+ allowClear: true,
309
+ language : {
310
+ noResults: function() { return BooklyL10n.no_result_found; }
311
+ }
312
+ });
313
+
314
  $id_filter.on('keyup', function () { dt.ajax.reload(); });
315
  $date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
316
  $staff_filter.on('change', function () { dt.ajax.reload(); });
backend/modules/appointments/templates/_export_dialog.php CHANGED
@@ -1,4 +1,7 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <div id="bookly-export-dialog" class="modal fade" tabindex=-1 role="dialog">
3
  <div class="modal-dialog">
4
  <div class="modal-content">
@@ -25,7 +28,10 @@
25
  <div class="checkbox"><label><input checked value="7" type="checkbox" /><?php _e( 'Duration', 'bookly' ) ?></label></div>
26
  <div class="checkbox"><label><input checked value="8" type="checkbox" /><?php _e( 'Status', 'bookly' ) ?></label></div>
27
  <div class="checkbox"><label><input checked value="9" type="checkbox" /><?php _e( 'Payment', 'bookly' ) ?></label></div>
28
- <?php $i = 10; foreach ( $custom_fields as $custom_field ) : ?>
 
 
 
29
  <div class="checkbox"><label><input checked value="<?php echo $i ++ ?>" type="checkbox"/><?php echo $custom_field->label ?></label></div>
30
  <?php endforeach ?>
31
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Common;
3
+ use BooklyLite\Lib\Config;
4
+ ?>
5
  <div id="bookly-export-dialog" class="modal fade" tabindex=-1 role="dialog">
6
  <div class="modal-dialog">
7
  <div class="modal-content">
28
  <div class="checkbox"><label><input checked value="7" type="checkbox" /><?php _e( 'Duration', 'bookly' ) ?></label></div>
29
  <div class="checkbox"><label><input checked value="8" type="checkbox" /><?php _e( 'Status', 'bookly' ) ?></label></div>
30
  <div class="checkbox"><label><input checked value="9" type="checkbox" /><?php _e( 'Payment', 'bookly' ) ?></label></div>
31
+ <?php $i = 10; if ( Config::showNotes() ): $i = 11; ?>
32
+ <div class="checkbox"><label><input checked value="10" type="checkbox" /><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_notes' ) ) ?></label></div>
33
+ <?php endif ?>
34
+ <?php foreach ( $custom_fields as $custom_field ) : ?>
35
  <div class="checkbox"><label><input checked value="<?php echo $i ++ ?>" type="checkbox"/><?php echo $custom_field->label ?></label></div>
36
  <?php endforeach ?>
37
  </div>
backend/modules/appointments/templates/_print_dialog.php DELETED
@@ -1,33 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
- <div id="bookly-print-dialog" class="modal fade" tabindex=-1 role="dialog">
3
- <div class="modal-dialog">
4
- <div class="modal-content">
5
- <div class="modal-header">
6
- <button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
7
- <h4 class="modal-title"><?php _e( 'Print', 'bookly' ) ?></h4>
8
- </div>
9
- <div class="modal-body">
10
- <div class="form-group">
11
- <div class="checkbox"><label><input checked value="0" type="checkbox"/><?php _e( 'No.', 'bookly' ) ?></label></div>
12
- <div class="checkbox"><label><input checked value="1" type="checkbox"/><?php _e( 'Appointment Date', 'bookly' ) ?></label></div>
13
- <div class="checkbox"><label><input checked value="2" type="checkbox"/><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></label></div>
14
- <div class="checkbox"><label><input checked value="3" type="checkbox"/><?php _e( 'Customer Name', 'bookly' ) ?></label></div>
15
- <div class="checkbox"><label><input checked value="4" type="checkbox"/><?php _e( 'Customer Phone', 'bookly' ) ?></label></div>
16
- <div class="checkbox"><label><input checked value="5" type="checkbox"/><?php _e( 'Customer Email', 'bookly' ) ?></label></div>
17
- <div class="checkbox"><label><input checked value="6" type="checkbox"/><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ?></label></div>
18
- <div class="checkbox"><label><input checked value="7" type="checkbox"/><?php _e( 'Duration', 'bookly' ) ?></label></div>
19
- <div class="checkbox"><label><input checked value="8" type="checkbox"/><?php _e( 'Status', 'bookly' ) ?></label></div>
20
- <div class="checkbox"><label><input checked value="9" type="checkbox"/><?php _e( 'Payment', 'bookly' ) ?></label></div>
21
- <?php $i = 10; foreach ( $custom_fields as $custom_field ) : ?>
22
- <div class="checkbox"><label><input checked value="<?php echo $i ++ ?>" type="checkbox"/><?php echo $custom_field->label ?></label></div>
23
- <?php endforeach ?>
24
- </div>
25
- </div>
26
- <div class="modal-footer">
27
- <button type="button" class="btn btn-lg btn-success" id="bookly-print" data-dismiss="modal">
28
- <?php _e( 'Print', 'bookly' ) ?>
29
- </button>
30
- </div>
31
- </div>
32
- </div>
33
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/appointments/templates/index.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  use BooklyLite\Lib\Entities\CustomerAppointment;
 
 
3
  ?>
4
  <div id="bookly-tbs" class="wrap">
5
  <div class="bookly-tbs-body">
@@ -23,7 +25,8 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
23
  <button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New appointment', 'bookly' ) ?></button>
24
  </div>
25
  </div>
26
-
 
27
  <div class="col-md-4 col-lg-1">
28
  <div class="form-group">
29
  <input class="form-control" type="text" id="bookly-filter-id" placeholder="<?php esc_attr_e( 'No.', 'bookly' ) ?>" />
@@ -41,10 +44,9 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
41
  </div>
42
  <div class="col-md-4 col-lg-2">
43
  <div class="form-group">
44
- <select class="form-control bookly-js-chosen-select" id="bookly-filter-staff" data-placeholder="<?php echo esc_attr( \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ) ?>">
45
- <option value="-1"></option>
46
  <?php foreach ( $staff_members as $staff ) : ?>
47
- <option value="<?php echo $staff['id'] ?>"><?php esc_html_e( $staff['full_name'] ) ?></option>
48
  <?php endforeach ?>
49
  </select>
50
  </div>
@@ -52,32 +54,33 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
52
  <div class="clearfix visible-md-block"></div>
53
  <div class="col-md-4 col-lg-2">
54
  <div class="form-group">
55
- <select class="form-control bookly-js-chosen-select" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>">
56
- <option value="-1"></option>
57
  <?php foreach ( $customers as $customer ) : ?>
58
- <option value="<?php echo $customer['id'] ?>"><?php esc_html_e( $customer['name'] ) ?></option>
59
  <?php endforeach ?>
60
  </select>
61
  </div>
62
  </div>
63
  <div class="col-md-4 col-lg-2">
64
  <div class="form-group">
65
- <select class="form-control bookly-js-chosen-select" id="bookly-filter-service" data-placeholder="<?php echo esc_attr( \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ) ?>">
66
- <option value="-1"></option>
67
  <?php foreach ( $services as $service ) : ?>
68
- <option value="<?php echo $service['id'] ?>"><?php esc_html_e( $service['title'] ) ?></option>
69
  <?php endforeach ?>
70
  </select>
71
  </div>
72
  </div>
73
  <div class="col-md-4 col-lg-2">
74
  <div class="form-group">
75
- <select class="form-control bookly-js-chosen-select" id="bookly-filter-status" data-placeholder="<?php esc_attr_e( 'Status', 'bookly' ) ?>">
76
- <option value="-1"></option>
77
  <option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ?></option>
78
  <option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ?></option>
79
  <option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ?></option>
80
  <option value="<?php echo CustomerAppointment::STATUS_REJECTED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ?></option>
 
 
 
81
  </select>
82
  </div>
83
  </div>
@@ -88,14 +91,17 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
88
  <tr>
89
  <th><?php _e( 'No.', 'bookly' ) ?></th>
90
  <th><?php _e( 'Appointment Date', 'bookly' ) ?></th>
91
- <th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></th>
92
  <th><?php _e( 'Customer Name', 'bookly' ) ?></th>
93
  <th><?php _e( 'Customer Phone', 'bookly' ) ?></th>
94
  <th><?php _e( 'Customer Email', 'bookly' ) ?></th>
95
- <th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ?></th>
96
  <th><?php _e( 'Duration', 'bookly' ) ?></th>
97
  <th><?php _e( 'Status', 'bookly' ) ?></th>
98
  <th><?php _e( 'Payment', 'bookly' ) ?></th>
 
 
 
99
  <?php foreach ( $custom_fields as $custom_field ) : ?>
100
  <th><?php echo $custom_field->label ?></th>
101
  <?php endforeach ?>
@@ -106,16 +112,15 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
106
  </table>
107
 
108
  <div class="text-right bookly-margin-top-lg">
109
- <?php \BooklyLite\Lib\Utils\Common::deleteButton( '', '', '#bookly-delete-dialog' ) ?>
110
  </div>
111
  </div>
112
  </div>
113
 
114
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderDeleteDialog(); ?>
115
  <?php include '_export_dialog.php' ?>
116
- <?php include '_print_dialog.php' ?>
117
 
118
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
119
- <?php do_action( 'bookly_render_component_appointments' ) ?>
120
  </div>
121
  </div>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  use BooklyLite\Lib\Entities\CustomerAppointment;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ use BooklyLite\Lib\Config;
5
  ?>
6
  <div id="bookly-tbs" class="wrap">
7
  <div class="bookly-tbs-body">
25
  <button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New appointment', 'bookly' ) ?></button>
26
  </div>
27
  </div>
28
+ </div>
29
+ <div class="row">
30
  <div class="col-md-4 col-lg-1">
31
  <div class="form-group">
32
  <input class="form-control" type="text" id="bookly-filter-id" placeholder="<?php esc_attr_e( 'No.', 'bookly' ) ?>" />
44
  </div>
45
  <div class="col-md-4 col-lg-2">
46
  <div class="form-group">
47
+ <select class="form-control bookly-js-select" id="bookly-filter-staff" data-placeholder="<?php echo esc_attr( Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ) ?>">
 
48
  <?php foreach ( $staff_members as $staff ) : ?>
49
+ <option value="<?php echo $staff['id'] ?>"><?php echo esc_html( $staff['full_name'] ) ?></option>
50
  <?php endforeach ?>
51
  </select>
52
  </div>
54
  <div class="clearfix visible-md-block"></div>
55
  <div class="col-md-4 col-lg-2">
56
  <div class="form-group">
57
+ <select class="form-control bookly-js-select" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>">
 
58
  <?php foreach ( $customers as $customer ) : ?>
59
+ <option value="<?php echo $customer['id'] ?>"><?php echo esc_html( $customer['full_name'] ) ?></option>
60
  <?php endforeach ?>
61
  </select>
62
  </div>
63
  </div>
64
  <div class="col-md-4 col-lg-2">
65
  <div class="form-group">
66
+ <select class="form-control bookly-js-select" id="bookly-filter-service" data-placeholder="<?php echo esc_attr( Common::getTranslatedOption( 'bookly_l10n_label_service' ) ) ?>">
67
+ <option value="0"><?php esc_html_e( 'Custom', 'bookly' ) ?></option>
68
  <?php foreach ( $services as $service ) : ?>
69
+ <option value="<?php echo $service['id'] ?>"><?php echo esc_html( $service['title'] ) ?></option>
70
  <?php endforeach ?>
71
  </select>
72
  </div>
73
  </div>
74
  <div class="col-md-4 col-lg-2">
75
  <div class="form-group">
76
+ <select class="form-control bookly-js-select" id="bookly-filter-status" data-placeholder="<?php esc_attr_e( 'Status', 'bookly' ) ?>">
 
77
  <option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ?></option>
78
  <option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ?></option>
79
  <option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ?></option>
80
  <option value="<?php echo CustomerAppointment::STATUS_REJECTED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ?></option>
81
+ <?php if ( Config::waitingListActive() ): ?>
82
+ <option value="<?php echo CustomerAppointment::STATUS_WAITLISTED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ?></option>
83
+ <?php endif ?>
84
  </select>
85
  </div>
86
  </div>
91
  <tr>
92
  <th><?php _e( 'No.', 'bookly' ) ?></th>
93
  <th><?php _e( 'Appointment Date', 'bookly' ) ?></th>
94
+ <th><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ) ?></th>
95
  <th><?php _e( 'Customer Name', 'bookly' ) ?></th>
96
  <th><?php _e( 'Customer Phone', 'bookly' ) ?></th>
97
  <th><?php _e( 'Customer Email', 'bookly' ) ?></th>
98
+ <th><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_service' ) ) ?></th>
99
  <th><?php _e( 'Duration', 'bookly' ) ?></th>
100
  <th><?php _e( 'Status', 'bookly' ) ?></th>
101
  <th><?php _e( 'Payment', 'bookly' ) ?></th>
102
+ <?php if ( Config::showNotes() ): ?>
103
+ <th><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_notes' ) ) ?></th>
104
+ <?php endif ?>
105
  <?php foreach ( $custom_fields as $custom_field ) : ?>
106
  <th><?php echo $custom_field->label ?></th>
107
  <?php endforeach ?>
112
  </table>
113
 
114
  <div class="text-right bookly-margin-top-lg">
115
+ <?php Common::deleteButton( '', '', '#bookly-delete-dialog' ) ?>
116
  </div>
117
  </div>
118
  </div>
119
 
120
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderDeleteDialog(); ?>
121
  <?php include '_export_dialog.php' ?>
 
122
 
123
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
124
+ <?php \BooklyLite\Lib\Proxy\Shared::renderComponentAppointments() ?>
125
  </div>
126
  </div>
backend/modules/calendar/Components.php CHANGED
@@ -18,7 +18,7 @@ class Components extends Lib\Base\Components
18
  global $wp_locale;
19
 
20
  $this->enqueueStyles( array(
21
- 'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', ),
22
  'frontend' => array( 'css/ladda.min.css', ),
23
  ) );
24
 
@@ -27,7 +27,7 @@ class Components extends Lib\Base\Components
27
  'js/angular.min.js' => array( 'jquery' ),
28
  'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js' ),
29
  'js/moment.min.js' => array( 'jquery' ),
30
- 'js/chosen.jquery.min.js' => array( 'jquery' ),
31
  'js/help.js' => array( 'jquery' ),
32
  ),
33
  'frontend' => array(
@@ -40,28 +40,25 @@ class Components extends Lib\Base\Components
40
  ) );
41
 
42
  wp_localize_script( 'bookly-ng-appointment_dialog.js', 'BooklyL10nAppDialog', array(
43
- 'calendar' => array(
44
- 'shortMonths' => array_values( $wp_locale->month_abbrev ),
45
- 'longMonths' => array_values( $wp_locale->month ),
46
- 'shortDays' => array_values( $wp_locale->weekday_abbrev ),
47
- 'longDays' => array_values( $wp_locale->weekday ),
 
 
 
48
  ),
49
- 'dpDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
50
- 'startOfWeek' => (int) get_option( 'start_of_week' ),
51
- 'cf_per_service' => (int) Lib\Config::customFieldsPerService(),
52
- 'title' => array(
53
  'edit_appointment' => __( 'Edit appointment', 'bookly' ),
54
  'new_appointment' => __( 'New appointment', 'bookly' ),
55
  ),
56
  ) );
57
 
58
- // Custom fields without captcha field.
59
- $custom_fields = array_filter(
60
- json_decode( get_option( 'bookly_custom_fields' ) ),
61
- function( $field ) { return ! in_array( $field->type, array( 'captcha', 'text-content' ) ); }
62
- );
63
-
64
- $this->render( '_appointment_dialog', compact( 'custom_fields' ) );
65
  }
66
 
67
  /**
18
  global $wp_locale;
19
 
20
  $this->enqueueStyles( array(
21
+ 'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', 'css/select2.min.css' ),
22
  'frontend' => array( 'css/ladda.min.css', ),
23
  ) );
24
 
27
  'js/angular.min.js' => array( 'jquery' ),
28
  'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js' ),
29
  'js/moment.min.js' => array( 'jquery' ),
30
+ 'js/select2.full.min.js' => array( 'jquery' ),
31
  'js/help.js' => array( 'jquery' ),
32
  ),
33
  'frontend' => array(
40
  ) );
41
 
42
  wp_localize_script( 'bookly-ng-appointment_dialog.js', 'BooklyL10nAppDialog', array(
43
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
44
+ 'dateOptions' => array(
45
+ 'dateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
46
+ 'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
47
+ 'monthNames' => array_values( $wp_locale->month ),
48
+ 'dayNamesMin' => array_values( $wp_locale->weekday_abbrev ),
49
+ 'longDays' => array_values( $wp_locale->weekday ),
50
+ 'firstDay' => (int) get_option( 'start_of_week' ),
51
  ),
52
+ 'cf_per_service' => (int) Lib\Config::customFieldsPerService(),
53
+ 'no_result_found' => __( 'No result found', 'bookly' ),
54
+ 'staff_any' => get_option( 'bookly_l10n_option_employee' ),
55
+ 'title' => array(
56
  'edit_appointment' => __( 'Edit appointment', 'bookly' ),
57
  'new_appointment' => __( 'New appointment', 'bookly' ),
58
  ),
59
  ) );
60
 
61
+ $this->render( '_appointment_dialog' );
 
 
 
 
 
 
62
  }
63
 
64
  /**
backend/modules/calendar/Controller.php CHANGED
@@ -2,6 +2,7 @@
2
  namespace BooklyLite\Backend\Modules\Calendar;
3
 
4
  use BooklyLite\Lib;
 
5
 
6
  /**
7
  * Class Controller
@@ -23,7 +24,7 @@ class Controller extends Lib\Base\Controller
23
 
24
  $this->enqueueStyles( array(
25
  'module' => array( 'css/fullcalendar.min.css', ),
26
- 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
27
  ) );
28
 
29
  $this->enqueueScripts( array(
@@ -31,7 +32,8 @@ class Controller extends Lib\Base\Controller
31
  'module' => array(
32
  'js/fullcalendar.min.js' => array( 'bookly-moment.min.js' ),
33
  'js/fc-multistaff-view.js' => array( 'bookly-fullcalendar.min.js' ),
34
- 'js/calendar.js' => array( 'bookly-fc-multistaff-view.js' ),
 
35
  ),
36
  ) );
37
 
@@ -39,10 +41,11 @@ class Controller extends Lib\Base\Controller
39
  $slot = new \DateInterval( 'PT' . $slot_length_minutes . 'M' );
40
 
41
  $staff_members = Lib\Utils\Common::isCurrentUserAdmin()
42
- ? Lib\Entities\Staff::query()->sortBy( 'position' )->find()
43
  : Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
44
 
45
  wp_localize_script( 'bookly-calendar.js', 'BooklyL10n', array(
 
46
  'slotDuration' => $slot->format( '%H:%I:%S' ),
47
  'calendar' => array(
48
  'shortMonths' => array_values( $wp_locale->month_abbrev ),
@@ -62,7 +65,18 @@ class Controller extends Lib\Base\Controller
62
  'noStaffSelected' => __( 'No staff selected', 'bookly' ),
63
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
64
  'startOfWeek' => (int) get_option( 'start_of_week' ),
65
- 'recurring_appointments_enabled' => (int) Lib\Config::isRecurringAppointmentsEnabled(),
 
 
 
 
 
 
 
 
 
 
 
66
  ) );
67
 
68
  $this->render( 'calendar', compact( 'staff_members' ) );
@@ -83,25 +97,26 @@ class Controller extends Lib\Base\Controller
83
  // FullCalendar sends end date as 1 day further.
84
  $end_date->sub( $one_day );
85
 
 
86
  if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
87
  $staff_members = Lib\Entities\Staff::query()
88
  ->where( 'id', 1 )
89
  ->find();
90
  } else {
91
- $staff_members[] = Lib\Entities\Staff::query()
92
  ->where( 'wp_user_id', get_current_user_id() )
93
  ->findOne();
94
- $staff_ids = array( 1 );
95
  }
96
  // Load special days.
97
  $special_days = array();
98
- foreach ( apply_filters( 'bookly_special_days_get_data_for_available_time', array(), $staff_ids, $start_date, $end_date ) as $day ) {
99
  $special_days[ $day['staff_id'] ][ $day['date'] ][] = $day;
100
  }
101
 
102
  foreach ( $staff_members as $staff ) {
103
  /** @var Lib\Entities\Staff $staff */
104
- $result = array_merge( $result, $this->_getAppointmentsForFC( $staff->get( 'id' ), $start_date, $end_date ) );
105
 
106
  // Schedule.
107
  $items = $staff->getScheduleItems();
@@ -110,7 +125,7 @@ class Controller extends Lib\Base\Controller
110
  $last_end = clone $day;
111
  $last_end->sub( $one_day );
112
  $w = (int) $day->format( 'w' );
113
- $end_time = $items[ $w > 0 ? $w : 7 ]->get( 'end_time' );
114
  if ( $end_time !== null ) {
115
  $end_time = explode( ':', $end_time );
116
  $last_end->setTime( $end_time[0], $end_time[1] );
@@ -121,63 +136,69 @@ class Controller extends Lib\Base\Controller
121
  while ( $day <= $end_date ) {
122
  $start = $last_end->format( 'Y-m-d H:i:s' );
123
  // Check if $day is Special Day for current staff.
124
- if ( isset( $special_days[ $staff->get( 'id' ) ][ $day->format( 'Y-m-d' ) ] ) ) {
125
- $sp_days = $special_days[ $staff->get( 'id' ) ][ $day->format( 'Y-m-d' ) ];
126
- $sp_day = array_shift( $sp_days );
127
- $end = $sp_day['date'] . ' ' . $sp_day['start_time'];
128
  if ( $start < $end ) {
129
  $result[] = array(
130
  'start' => $start,
131
  'end' => $end,
132
  'rendering' => 'background',
133
- 'staffId' => $staff->get( 'id' ),
134
  );
135
- // Check if the first break exists.
136
- if ( isset( $sp_day['break_start'] ) ) {
137
- $result[] = array(
138
- 'start' => $sp_day['date'] . ' ' . $sp_day['break_start'],
139
- 'end' => $sp_day['date'] . ' ' . $sp_day['break_end'],
140
- 'rendering' => 'background',
141
- 'staffId' => $staff->get( 'id' ),
142
- );
143
- }
144
  }
145
  // Breaks.
146
  foreach ( $sp_days as $sp_day ) {
 
 
 
 
 
 
 
 
147
  $result[] = array(
148
- 'start' => $sp_day['date'] . ' ' . $sp_day['break_start'],
149
- 'end' => $sp_day['date'] . ' ' . $sp_day['break_end'],
150
  'rendering' => 'background',
151
- 'staffId' => $staff->get( 'id' ),
152
  );
153
  }
154
- $end_time = explode( ':', $sp_day['end_time'] );
155
  $last_end = clone $day;
156
  $last_end->setTime( $end_time[0], $end_time[1] );
157
  } else {
158
  /** @var Lib\Entities\StaffScheduleItem $item */
159
  $item = $items[ (int) $day->format( 'w' ) + 1 ];
160
- if ( $item->get( 'start_time' ) && ! $staff->isOnHoliday( $day ) ) {
161
- $end = $day->format( 'Y-m-d ' . $item->get( 'start_time' ) );
162
  if ( $start < $end ) {
163
  $result[] = array(
164
  'start' => $start,
165
  'end' => $end,
166
  'rendering' => 'background',
167
- 'staffId' => $staff->get( 'id' ),
168
  );
169
  }
170
  $last_end = clone $day;
171
- $end_time = explode( ':', $item->get( 'end_time' ) );
172
  $last_end->setTime( $end_time[0], $end_time[1] );
173
 
174
  // Breaks.
175
  foreach ( $item->getBreaksList() as $break ) {
 
 
 
 
 
 
 
 
176
  $result[] = array(
177
- 'start' => $day->format( 'Y-m-d ' . $break['start_time'] ),
178
- 'end' => $day->format( 'Y-m-d ' . $break['end_time'] ),
179
  'rendering' => 'background',
180
- 'staffId' => $staff->get( 'id' ),
181
  );
182
  }
183
  } else {
@@ -185,7 +206,7 @@ class Controller extends Lib\Base\Controller
185
  'start' => $last_end->format( 'Y-m-d H:i:s' ),
186
  'end' => $day->format( 'Y-m-d 24:00:00' ),
187
  'rendering' => 'background',
188
- 'staffId' => $staff->get( 'id' ),
189
  );
190
  $last_end = clone $day;
191
  $last_end->setTime( 24, 0 );
@@ -200,7 +221,7 @@ class Controller extends Lib\Base\Controller
200
  'start' => $last_end->format( 'Y-m-d H:i:s' ),
201
  'end' => $last_end->format( 'Y-m-d 24:00:00' ),
202
  'rendering' => 'background',
203
- 'staffId' => $staff->get( 'id' ),
204
  );
205
  }
206
  }
@@ -213,18 +234,21 @@ class Controller extends Lib\Base\Controller
213
  */
214
  public function executeGetDataForAppointmentForm()
215
  {
 
216
  $result = array(
217
  'staff' => array(),
218
  'customers' => array(),
219
  'start_time' => array(),
220
  'end_time' => array(),
 
221
  'time_interval' => Lib\Config::getTimeSlotLength(),
222
  'status' => array(
223
  'items' => array(
224
- 'pending' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_PENDING ),
225
- 'approved' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_APPROVED ),
226
- 'cancelled' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_CANCELLED ),
227
- 'rejected' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_REJECTED ),//@todo I didn't find the place it uses
 
228
  ),
229
  'default' => get_option( 'bookly_gen_default_appointment_status' ),
230
  ),
@@ -238,34 +262,56 @@ class Controller extends Lib\Base\Controller
238
  /** @var Lib\Entities\Staff $staff_member */
239
  foreach ( $staff_members as $staff_member ) {
240
  $services = array();
241
- foreach ( $staff_member->getStaffServices() as $staff_service ) {
242
  $services[] = array(
243
- 'id' => $staff_service->service->get( 'id' ),
244
- 'title' => sprintf(
245
- '%s (%s)',
246
- $staff_service->service->get( 'title' ),
247
- Lib\Utils\DateTime::secondsToInterval( $staff_service->service->get( 'duration' ) )
248
- ),
249
- 'duration' => $staff_service->service->get( 'duration' ),
250
- 'capacity' => $staff_service->get( 'capacity' )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  );
252
  }
253
  $result['staff'][] = array(
254
- 'id' => $staff_member->get( 'id' ),
255
- 'full_name' => $staff_member->get( 'full_name' ),
256
- 'services' => $services
 
257
  );
258
  }
259
 
 
260
  // Customers list.
261
- foreach ( Lib\Entities\Customer::query()->sortBy( 'name' )->find() as $customer ) {
262
- $name = $customer->get( 'name' );
263
- if ( $customer->get( 'email' ) != '' || $customer->get( 'phone' ) != '' ) {
264
- $name .= ' (' . trim( $customer->get( 'email' ) . ', ' . $customer->get( 'phone' ) , ', ' ) . ')';
265
  }
266
 
267
  $result['customers'][] = array(
268
- 'id' => $customer->get( 'id' ),
269
  'name' => $name,
270
  'custom_fields' => array(),
271
  'number_of_persons' => 1,
@@ -281,7 +327,7 @@ class Controller extends Lib\Base\Controller
281
  while ( $time_start <= $time_end ) {
282
  $slot = array(
283
  'value' => Lib\Utils\DateTime::buildTimeString( $time_start, false ),
284
- 'title' => Lib\Utils\DateTime::formatTime( $time_start )
285
  );
286
  if ( $time_start < DAY_IN_SECONDS ) {
287
  $result['start_time'][] = $slot;
@@ -290,6 +336,12 @@ class Controller extends Lib\Base\Controller
290
  $time_start += $ts_length;
291
  }
292
 
 
 
 
 
 
 
293
  wp_send_json( $result );
294
  }
295
 
@@ -305,44 +357,64 @@ class Controller extends Lib\Base\Controller
305
  $response['success'] = true;
306
 
307
  $info = Lib\Entities\Appointment::query( 'a' )
308
- ->select( 'ss.capacity AS max_capacity, SUM( ca.number_of_persons ) AS total_number_of_persons, a.staff_id, a.service_id, a.start_date, a.end_date, a.internal_note' )
 
 
 
 
 
 
 
 
 
 
 
 
309
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
310
  ->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
311
- ->where( 'a.id', $appointment->get( 'id' ) )
312
  ->fetchRow();
313
 
314
  $response['data']['total_number_of_persons'] = $info['total_number_of_persons'];
315
- $response['data']['max_capacity'] = $info['max_capacity'];
316
- $response['data']['start_date'] = $info['start_date'];
317
- $response['data']['end_date'] = $info['end_date'];
318
- $response['data']['staff_id'] = $info['staff_id'];
319
- $response['data']['service_id'] = $info['service_id'];
320
- $response['data']['internal_note'] = $info['internal_note'];
 
 
 
 
 
 
 
321
  $customers = Lib\Entities\CustomerAppointment::query( 'ca' )
322
  ->select( 'ca.id,
323
  ca.customer_id,
 
324
  ca.custom_fields,
325
  ca.extras,
326
  ca.number_of_persons,
 
327
  ca.status,
328
  ca.payment_id,
329
  ca.compound_service_id,
330
  ca.compound_token,
331
- ca.location_id,
332
  p.paid AS payment,
333
  p.total AS payment_total,
334
  p.type AS payment_type,
335
  p.details AS payment_details,
336
  p.status AS payment_status' )
337
  ->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
338
- ->where( 'ca.appointment_id', $appointment->get( 'id' ) )
339
  ->fetchArray();
340
  foreach ( $customers as $customer ) {
341
  $payment_title = '';
342
  if ( $customer['payment'] !== null ) {
343
- $payment_title = Lib\Utils\Common::formatPrice( $customer['payment'] );
344
  if ( $customer['payment'] != $customer['payment_total'] ) {
345
- $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $customer['payment_total'] ) );
346
  }
347
  $payment_title .= sprintf(
348
  ' %s <span%s>%s</span>',
@@ -355,18 +427,19 @@ class Controller extends Lib\Base\Controller
355
  if ( $customer['compound_service_id'] !== null ) {
356
  $service = new Lib\Entities\Service();
357
  if ( $service->load( $customer['compound_service_id'] ) ) {
358
- $compound_service = $service->getTitle();
359
  }
360
  }
361
  $response['data']['customers'][] = array(
362
  'id' => $customer['customer_id'],
363
  'ca_id' => $customer['id'],
 
364
  'compound_service' => $compound_service,
365
  'compound_token' => $customer['compound_token'],
366
  'custom_fields' => (array) json_decode( $customer['custom_fields'], true ),
367
  'extras' => (array) json_decode( $customer['extras'], true ),
368
- 'location_id' => $customer['location_id'],
369
  'number_of_persons' => $customer['number_of_persons'],
 
370
  'payment_id' => $customer['payment_id'],
371
  'payment_type' => $customer['payment'] != $customer['payment_total'] ? 'partial' : 'full',
372
  'payment_title' => $payment_title,
@@ -384,20 +457,34 @@ class Controller extends Lib\Base\Controller
384
  {
385
  $response = array( 'success' => false );
386
 
387
- $internal_note = $this->getParameter( 'internal_note' );
388
- $start_date = $this->getParameter( 'start_date' );
389
- $end_date = $this->getParameter( 'end_date' );
390
- $service_id = $this->getParameter( 'service_id' );
391
- $appointment_id = $this->getParameter( 'id', 0 );
392
- $repeat = json_decode( $this->getParameter( 'repeat', '[]' ), true );
393
- $schedule = $this->getParameter( 'schedule', array() );
394
- $customers = json_decode( $this->getParameter( 'customers', '[]' ), true );
 
 
 
 
 
 
395
 
396
- $staff_service = new Lib\Entities\StaffService();
397
- $staff_service->loadBy( array(
398
- 'staff_id' => 1,
399
- 'service_id' => $service_id
400
- ) );
 
 
 
 
 
 
 
 
401
 
402
  // Check for errors.
403
  if ( ! $start_date ) {
@@ -407,140 +494,193 @@ class Controller extends Lib\Base\Controller
407
  } elseif ( $start_date == $end_date ) {
408
  $response['errors']['time_interval'] = __( 'End time must not be equal to start time', 'bookly' );
409
  }
410
- if ( ! $service_id ) {
411
  $response['errors']['service_required'] = true;
412
- }
413
- if ( empty ( $customers ) ) {
414
- $response['errors']['customers_required'] = true;
415
  }
416
  $total_number_of_persons = 0;
417
  $max_extras_duration = 0;
418
- foreach ( $customers as $customer ) {
419
- if (
420
- ( $customer['status'] != Lib\Entities\CustomerAppointment::STATUS_CANCELLED )
421
- && ( $customer['status'] != Lib\Entities\CustomerAppointment::STATUS_REJECTED )
422
  ) {
423
  $total_number_of_persons += $customer['number_of_persons'];
424
- $extras_duration = apply_filters( 'bookly_service_extras_get_total_duration', 0, $customer['extras'] );
425
  if ( $extras_duration > $max_extras_duration ) {
426
  $max_extras_duration = $extras_duration;
427
  }
428
  }
 
429
  }
430
- if ( $total_number_of_persons > $staff_service->get( 'capacity' ) ) {
431
- $response['errors']['overflow_capacity'] = sprintf(
432
- __( 'The number of customers should not be more than %d', 'bookly' ),
433
- $staff_service->get( 'capacity' )
434
- );
435
- }
436
- $total_end_date = $end_date;
437
- if ( $max_extras_duration > 0 ) {
438
- $total_end_date = date_create( $end_date )->modify( '+' . $max_extras_duration . ' sec' )->format( 'Y-m-d H:i:s' );
439
- }
440
- if ( ! $this->dateIntervalIsAvailableForAppointment( $start_date, $total_end_date, 1, $appointment_id ) ) {
441
- $response['errors']['date_interval_not_available'] = true;
442
  }
443
- $notification = $this->getParameter( 'notification' );
444
 
445
  // If no errors then try to save the appointment.
446
  if ( ! isset ( $response['errors'] ) ) {
447
  if ( $repeat['enabled'] ) {
 
448
  if ( ! empty ( $schedule ) ) {
449
- $recurring_list = array( 'customers' => $customers, 'appointments' => array() );
450
  // Create new series.
451
  $series = new Lib\Entities\Series();
452
  $series
453
- ->set( 'repeat', $this->getParameter( 'repeat' ) )
454
- ->set( 'token', Lib\Utils\Common::generateToken( get_class( $series ), 'token' ) )
455
  ->save();
456
 
457
- $service = new Lib\Entities\Service();
458
- $service->load( $service_id );
 
 
 
 
 
 
 
 
 
 
 
459
 
460
  foreach ( $schedule as $slot ) {
461
  $slot = json_decode( $slot );
462
  $appointment = new Lib\Entities\Appointment();
463
  $appointment
464
- ->set( 'series_id', $series->get( 'id' ) )
465
- ->set( 'staff_id', 1 )
466
- ->set( 'service_id', $service_id )
467
- ->set( 'start_date', date( 'Y-m-d H:i:s', $slot[0][2] ) )
468
- ->set( 'end_date', date( 'Y-m-d H:i:s', $slot[0][2] + $service->get( 'duration' ) ) )
469
- ->set( 'internal_note', $internal_note )
470
- ->set( 'extras_duration', $max_extras_duration )
 
471
  ;
472
 
473
  if ( $appointment->save() !== false ) {
 
 
474
  // Google Calendar.
475
  $appointment->handleGoogleCalendar();
476
- $appointment->saveCustomerAppointments( $customers );
 
477
 
478
  if ( $notification != 'no' ) {
479
- // Collect all appointments for sending recurring notification
480
- $recurring_list['appointments'][] = $appointment ;
 
 
 
 
 
481
  }
482
  }
483
  }
484
- Lib\NotificationSender::sendRecurring( $recurring_list );
 
 
 
 
485
  }
486
  $response['success'] = true;
487
  $response['data'] = array( 'staffId' => 1 ); // make FullCalendar refetch events
488
  } else {
 
489
  $appointment = new Lib\Entities\Appointment();
490
  if ( $appointment_id ) {
491
  // Edit.
492
  $appointment->load( $appointment_id );
 
 
 
493
  }
494
- $appointment->set( 'start_date', $start_date );
495
- $appointment->set( 'end_date', $end_date );
496
- $appointment->set( 'staff_id', 1 );
497
- $appointment->set( 'service_id', $service_id );
498
- $appointment->set( 'internal_note', $internal_note );
499
- $appointment->set( 'extras_duration', $max_extras_duration );
 
 
 
 
 
500
 
501
  if ( $appointment->save() !== false ) {
502
  // Save customer appointments.
503
  $ca_status_changed = $appointment->saveCustomerAppointments( $customers );
504
- $customer_appointments = $appointment->getCustomerAppointments( true );
505
 
506
  // Google Calendar.
507
  $appointment->handleGoogleCalendar();
 
 
508
 
509
  // Send notifications.
510
- if ( $notification != 'no' ) {
511
- foreach ( $customer_appointments as $ca ) {
512
- // Send notification.
513
- if ( $notification == 'all' || in_array( $ca->get( 'id' ), $ca_status_changed ) ) {
514
- Lib\NotificationSender::send( $ca );
515
- }
 
 
516
  }
517
  }
518
 
519
  $response['success'] = true;
520
- $response['data'] = $this->_getAppointmentForFC( 1, $appointment->get( 'id' ) );
521
  } else {
522
  $response['errors'] = array( 'db' => __( 'Could not save appointment in database.', 'bookly' ) );
523
  }
524
  }
525
  }
526
-
527
  wp_send_json( $response );
528
  }
529
 
530
- public function executeCheckAppointmentDateSelection()
531
  {
532
  $start_date = $this->getParameter( 'start_date' );
533
  $end_date = $this->getParameter( 'end_date' );
534
- $service_id = $this->getParameter( 'service_id' );
535
- $appointment_id = $this->getParameter( 'appointment_id' );
 
536
  $timestamp_diff = strtotime( $end_date ) - strtotime( $start_date );
 
537
 
538
  $result = array(
539
- 'date_interval_not_available' => false,
540
- 'date_interval_warning' => false,
 
541
  );
542
 
543
- if ( ! $this->dateIntervalIsAvailableForAppointment( $start_date, $end_date, 1, $appointment_id ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
  $result['date_interval_not_available'] = true;
545
  }
546
 
@@ -548,10 +688,20 @@ class Controller extends Lib\Base\Controller
548
  $service = new Lib\Entities\Service();
549
  $service->load( $service_id );
550
 
551
- $duration = $service->get( 'duration' );
552
 
553
  // Service duration interval is not equal to.
554
  $result['date_interval_warning'] = ( $timestamp_diff != $duration );
 
 
 
 
 
 
 
 
 
 
555
  }
556
 
557
  wp_send_json( $result );
@@ -571,21 +721,24 @@ class Controller extends Lib\Base\Controller
571
  ->find();
572
  /** @var Lib\Entities\CustomerAppointment $ca */
573
  foreach ( $ca_list as $ca ) {
574
- switch ( $ca->get('status') ) {
575
  case Lib\Entities\CustomerAppointment::STATUS_PENDING:
576
- $ca->set( 'status', Lib\Entities\CustomerAppointment::STATUS_REJECTED );
 
577
  break;
578
  case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
579
- $ca->set( 'status', Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
580
  break;
581
  }
582
- Lib\NotificationSender::send( $ca, array( 'cancellation_reason' => $reason ) );
 
 
 
 
583
  }
584
  }
585
 
586
- $appointment = new Lib\Entities\Appointment();
587
- $appointment->load( $appointment_id );
588
- $appointment->delete();
589
 
590
  wp_send_json_success();
591
  }
@@ -600,13 +753,10 @@ class Controller extends Lib\Base\Controller
600
  private function dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id )
601
  {
602
  return Lib\Entities\Appointment::query( 'a' )
603
- ->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
604
  ->whereNot( 'a.id', $appointment_id )
605
  ->where( 'a.staff_id', $staff_id )
606
- ->whereNot( 'ca.status', Lib\Entities\CustomerAppointment::STATUS_CANCELLED )
607
- ->whereNot( 'ca.status', Lib\Entities\CustomerAppointment::STATUS_REJECTED )
608
- ->whereLt( 'start_date', $end_date )
609
- ->whereGt( 'end_date', $start_date )
610
  ->count() == 0;
611
  }
612
 
@@ -653,96 +803,130 @@ class Controller extends Lib\Base\Controller
653
  */
654
  private function _buildAppointmentsForFC( $staff_id, Lib\Query $query )
655
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  $appointments = $query
657
- ->select( 'a.id, a.series_id, a.start_date, DATE_ADD(a.end_date, INTERVAL a.extras_duration SECOND) AS end_date,
658
- s.title AS service_title, s.color AS service_color,
659
- ss.capacity AS max_capacity,
 
660
  (SELECT SUM(ca.number_of_persons) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca WHERE ca.appointment_id = a.id) AS total_number_of_persons,
661
  ca.number_of_persons,
662
  ca.custom_fields,
663
  ca.status AS appointment_status,
664
  ca.extras,
665
- ca.location_id,
666
- c.name AS customer_name, c.phone AS customer_phone, c.email AS customer_email, c.id AS customer_id,
667
- p.total,
668
- p.type AS payment_gateway,
669
- p.status AS payment_status' )
670
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
671
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
672
  ->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
673
  ->leftJoin( 'Service', 's', 's.id = a.service_id' )
 
674
  ->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
675
  ->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
676
- ->where( 'st.id', 1 )
677
  ->groupBy( 'a.id' )
678
  ->fetchArray();
679
 
680
  foreach ( $appointments as $key => $appointment ) {
681
- $desc = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  // Display customer information only if there is 1 customer. Don't confuse with number_of_persons.
683
  if ( $appointment['number_of_persons'] == $appointment['total_number_of_persons'] ) {
684
- // Customer information.
685
- foreach ( array( 'customer_name', 'customer_phone', 'customer_email' ) as $data_entry ) {
 
686
  if ( $appointment[ $data_entry ] ) {
687
- $desc .= '<div>' . esc_html( $appointment[ $data_entry ] ) . '</div>';
688
- }
689
- }
690
- $desc = apply_filters( 'bookly_prepare_calendar_appointment_description', $desc, $appointment );
691
- // Custom fields.
692
- if ( $appointment['custom_fields'] != '[]' ) {
693
- $desc .= '<br/>';
694
- $ca = new Lib\Entities\CustomerAppointment();
695
- $ca->set( 'custom_fields', $appointment['custom_fields'] );
696
- $ca->set( 'appointment_id', $appointment['id'] );
697
- foreach ( $ca->getCustomFields() as $custom_field ) {
698
- $desc .= sprintf( '<div>%s: %s</div>', wp_strip_all_tags( $custom_field['label'] ), nl2br( esc_html( $custom_field['value'] ) ) );
699
  }
700
  }
 
701
  // Payment.
702
  if ( $appointment['total'] ) {
703
- $desc .= sprintf(
704
- '<br/><div>%s: %s %s %s</div>',
705
- __( 'Payment', 'bookly' ),
706
- Lib\Utils\Common::formatPrice( $appointment['total'] ),
707
- Lib\Entities\Payment::typeToString( $appointment['payment_gateway'] ),
708
- Lib\Entities\Payment::statusToString( $appointment['payment_status'] )
709
- );
710
  }
711
  // Status.
712
- $desc .= sprintf( '<br/><div>%s: %s</div>', __( 'Status', 'bookly' ), Lib\Entities\CustomerAppointment::statusToString( $appointment['appointment_status'] ) );
713
- // Signed up & Capacity.
714
- if ( $appointment['max_capacity'] > 1 ) {
715
- $desc .= sprintf( '<br/><div>%s: %d</div>', __( 'Signed up', 'bookly' ), $appointment['total_number_of_persons'] );
716
- $desc .= sprintf( '<div>%s: %d</div>', __( 'Capacity', 'bookly' ), $appointment['max_capacity'] );
717
- }
718
  } else {
719
- $desc .= sprintf( '<div>%s: %d</div>', __( 'Signed up', 'bookly' ), $appointment['total_number_of_persons'] );
720
- $desc .= sprintf( '<div>%s: %d</div>', __( 'Capacity', 'bookly' ), $appointment['max_capacity'] );
721
  }
 
 
 
722
  $appointments[ $key ] = array(
723
- 'id' => $appointment['id'],
724
- 'series_id' => (int) $appointment['series_id'],
725
- 'start' => $appointment['start_date'],
726
- 'end' => $appointment['end_date'],
727
- 'title' => $appointment['service_title'] ? esc_html( $appointment['service_title'] ) : __( 'Untitled', 'bookly' ),
728
- 'desc' => $desc,
729
- 'color' => $appointment['service_color'],
730
- 'staffId' => 1,
 
 
 
731
  );
732
  }
733
 
734
  return $appointments;
735
  }
736
 
737
- /**
738
- * Override parent method to add 'wp_ajax_bookly_' prefix
739
- * so current 'execute*' methods look nicer.
740
- *
741
- * @param string $prefix
742
- */
743
- protected function registerWpActions( $prefix = '' )
744
- {
745
- parent::registerWpActions( 'wp_ajax_bookly_' );
746
- }
747
-
748
  }
2
  namespace BooklyLite\Backend\Modules\Calendar;
3
 
4
  use BooklyLite\Lib;
5
+ use BooklyLite\Lib\DataHolders\Booking as DataHolders;
6
 
7
  /**
8
  * Class Controller
24
 
25
  $this->enqueueStyles( array(
26
  'module' => array( 'css/fullcalendar.min.css', ),
27
+ 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css' ),
28
  ) );
29
 
30
  $this->enqueueScripts( array(
32
  'module' => array(
33
  'js/fullcalendar.min.js' => array( 'bookly-moment.min.js' ),
34
  'js/fc-multistaff-view.js' => array( 'bookly-fullcalendar.min.js' ),
35
+ 'js/calendar-common.js' => array( 'bookly-fc-multistaff-view.js' ),
36
+ 'js/calendar.js' => array( 'bookly-calendar-common.js' ),
37
  ),
38
  ) );
39
 
41
  $slot = new \DateInterval( 'PT' . $slot_length_minutes . 'M' );
42
 
43
  $staff_members = Lib\Utils\Common::isCurrentUserAdmin()
44
+ ? Lib\Entities\Staff::query()->where( 'id', 1 )->find()
45
  : Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
46
 
47
  wp_localize_script( 'bookly-calendar.js', 'BooklyL10n', array(
48
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
49
  'slotDuration' => $slot->format( '%H:%I:%S' ),
50
  'calendar' => array(
51
  'shortMonths' => array_values( $wp_locale->month_abbrev ),
65
  'noStaffSelected' => __( 'No staff selected', 'bookly' ),
66
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
67
  'startOfWeek' => (int) get_option( 'start_of_week' ),
68
+ 'recurring_appointments' => array(
69
+ 'active' => (int) Lib\Config::recurringAppointmentsActive(),
70
+ 'title' => __( 'Recurring appointments', 'bookly' ),
71
+ ),
72
+ 'waiting_list' => array(
73
+ 'active' => (int) Lib\Config::waitingListActive(),
74
+ 'title' => __( 'On waiting list', 'bookly' ),
75
+ ),
76
+ 'packages' => array(
77
+ 'active' => (int) Lib\Config::packagesActive(),
78
+ 'title' => __( 'Package', 'bookly' ),
79
+ ),
80
  ) );
81
 
82
  $this->render( 'calendar', compact( 'staff_members' ) );
97
  // FullCalendar sends end date as 1 day further.
98
  $end_date->sub( $one_day );
99
 
100
+ $staff_ids = array( 1 );
101
  if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
102
  $staff_members = Lib\Entities\Staff::query()
103
  ->where( 'id', 1 )
104
  ->find();
105
  } else {
106
+ $staff_member = Lib\Entities\Staff::query()
107
  ->where( 'wp_user_id', get_current_user_id() )
108
  ->findOne();
109
+ $staff_members[] = $staff_member;
110
  }
111
  // Load special days.
112
  $special_days = array();
113
+ foreach ( (array) Lib\Proxy\SpecialDays::getSchedule( $staff_ids, $start_date, $end_date ) as $day ) {
114
  $special_days[ $day['staff_id'] ][ $day['date'] ][] = $day;
115
  }
116
 
117
  foreach ( $staff_members as $staff ) {
118
  /** @var Lib\Entities\Staff $staff */
119
+ $result = array_merge( $result, $this->_getAppointmentsForFC( $staff->getId(), $start_date, $end_date ) );
120
 
121
  // Schedule.
122
  $items = $staff->getScheduleItems();
125
  $last_end = clone $day;
126
  $last_end->sub( $one_day );
127
  $w = (int) $day->format( 'w' );
128
+ $end_time = $items[ $w > 0 ? $w : 7 ]->getEndTime();
129
  if ( $end_time !== null ) {
130
  $end_time = explode( ':', $end_time );
131
  $last_end->setTime( $end_time[0], $end_time[1] );
136
  while ( $day <= $end_date ) {
137
  $start = $last_end->format( 'Y-m-d H:i:s' );
138
  // Check if $day is Special Day for current staff.
139
+ if ( isset( $special_days[ $staff->getId() ][ $day->format( 'Y-m-d' ) ] ) ) {
140
+ $sp_days = $special_days[ $staff->getId() ][ $day->format( 'Y-m-d' ) ];
141
+ $end = $sp_days[0]['date'] . ' ' . $sp_days[0]['start_time'];
 
142
  if ( $start < $end ) {
143
  $result[] = array(
144
  'start' => $start,
145
  'end' => $end,
146
  'rendering' => 'background',
147
+ 'staffId' => $staff->getId(),
148
  );
 
 
 
 
 
 
 
 
 
149
  }
150
  // Breaks.
151
  foreach ( $sp_days as $sp_day ) {
152
+ $break_start = date(
153
+ 'Y-m-d H:i:s',
154
+ strtotime( $sp_day['date'] ) + Lib\Utils\DateTime::timeToSeconds( $sp_day['break_start'] )
155
+ );
156
+ $break_end = date(
157
+ 'Y-m-d H:i:s',
158
+ strtotime( $sp_day['date'] ) + Lib\Utils\DateTime::timeToSeconds( $sp_day['break_end'] )
159
+ );
160
  $result[] = array(
161
+ 'start' => $break_start,
162
+ 'end' => $break_end,
163
  'rendering' => 'background',
164
+ 'staffId' => $staff->getId(),
165
  );
166
  }
167
+ $end_time = explode( ':', $sp_days[0]['end_time'] );
168
  $last_end = clone $day;
169
  $last_end->setTime( $end_time[0], $end_time[1] );
170
  } else {
171
  /** @var Lib\Entities\StaffScheduleItem $item */
172
  $item = $items[ (int) $day->format( 'w' ) + 1 ];
173
+ if ( $item->getStartTime() && ! $staff->isOnHoliday( $day ) ) {
174
+ $end = $day->format( 'Y-m-d ' . $item->getStartTime() );
175
  if ( $start < $end ) {
176
  $result[] = array(
177
  'start' => $start,
178
  'end' => $end,
179
  'rendering' => 'background',
180
+ 'staffId' => $staff->getId(),
181
  );
182
  }
183
  $last_end = clone $day;
184
+ $end_time = explode( ':', $item->getEndTime() );
185
  $last_end->setTime( $end_time[0], $end_time[1] );
186
 
187
  // Breaks.
188
  foreach ( $item->getBreaksList() as $break ) {
189
+ $break_start = date(
190
+ 'Y-m-d H:i:s',
191
+ $day->getTimestamp() + Lib\Utils\DateTime::timeToSeconds( $break['start_time'] )
192
+ );
193
+ $break_end = date(
194
+ 'Y-m-d H:i:s',
195
+ $day->getTimestamp() + Lib\Utils\DateTime::timeToSeconds( $break['end_time'] )
196
+ );
197
  $result[] = array(
198
+ 'start' => $break_start,
199
+ 'end' => $break_end,
200
  'rendering' => 'background',
201
+ 'staffId' => $staff->getId(),
202
  );
203
  }
204
  } else {
206
  'start' => $last_end->format( 'Y-m-d H:i:s' ),
207
  'end' => $day->format( 'Y-m-d 24:00:00' ),
208
  'rendering' => 'background',
209
+ 'staffId' => $staff->getId(),
210
  );
211
  $last_end = clone $day;
212
  $last_end->setTime( 24, 0 );
221
  'start' => $last_end->format( 'Y-m-d H:i:s' ),
222
  'end' => $last_end->format( 'Y-m-d 24:00:00' ),
223
  'rendering' => 'background',
224
+ 'staffId' => $staff->getId(),
225
  );
226
  }
227
  }
234
  */
235
  public function executeGetDataForAppointmentForm()
236
  {
237
+ $type = $this->getParameter( 'type', false ) == 'package' ? Lib\Entities\Service::TYPE_PACKAGE : Lib\Entities\Service::TYPE_SIMPLE;
238
  $result = array(
239
  'staff' => array(),
240
  'customers' => array(),
241
  'start_time' => array(),
242
  'end_time' => array(),
243
+ 'week_days' => array(),
244
  'time_interval' => Lib\Config::getTimeSlotLength(),
245
  'status' => array(
246
  'items' => array(
247
+ 'pending' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_PENDING ),
248
+ 'approved' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_APPROVED ),
249
+ 'cancelled' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_CANCELLED ),
250
+ 'rejected' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_REJECTED ),
251
+ 'waitlisted' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_WAITLISTED ),
252
  ),
253
  'default' => get_option( 'bookly_gen_default_appointment_status' ),
254
  ),
262
  /** @var Lib\Entities\Staff $staff_member */
263
  foreach ( $staff_members as $staff_member ) {
264
  $services = array();
265
+ if ( $type == Lib\Entities\Service::TYPE_SIMPLE ) {
266
  $services[] = array(
267
+ 'id' => null,
268
+ 'title' => __( 'Custom', 'bookly' ),
269
+ 'duration' => Lib\Config::getTimeSlotLength(),
270
+ 'capacity_min' => 1,
271
+ 'capacity_max' => 1,
272
+ );
273
+ }
274
+ foreach ( $staff_member->getStaffServices( $type ) as $staff_service ) {
275
+ $sub_services = $staff_service->service->getSubServices();
276
+ if ( $type == Lib\Entities\Service::TYPE_SIMPLE || ! empty( $sub_services ) ) {
277
+ $services[] = array(
278
+ 'id' => $staff_service->service->getId(),
279
+ 'title' => sprintf(
280
+ '%s (%s)',
281
+ $staff_service->service->getTitle(),
282
+ Lib\Utils\DateTime::secondsToInterval( $staff_service->service->getDuration() )
283
+ ),
284
+ 'duration' => $staff_service->service->getDuration(),
285
+ 'capacity_min' => 1,
286
+ 'capacity_max' => 1,
287
+ );
288
+ }
289
+ }
290
+ $locations = array();
291
+ foreach ( (array) Lib\Proxy\Locations::findByStaffId( $staff_member->getId() ) as $location ) {
292
+ $locations[] = array(
293
+ 'id' => $location->getId(),
294
+ 'name' => $location->getName(),
295
  );
296
  }
297
  $result['staff'][] = array(
298
+ 'id' => $staff_member->getId(),
299
+ 'full_name' => $staff_member->getFullName(),
300
+ 'services' => $services,
301
+ 'locations' => $locations,
302
  );
303
  }
304
 
305
+ /** @var Lib\Entities\Customer $customer */
306
  // Customers list.
307
+ foreach ( Lib\Entities\Customer::query()->sortBy( 'full_name' )->find() as $customer ) {
308
+ $name = $customer->getFullName();
309
+ if ( $customer->getEmail() != '' || $customer->getPhone() != '' ) {
310
+ $name .= ' (' . trim( $customer->getEmail() . ', ' . $customer->getPhone(), ', ' ) . ')';
311
  }
312
 
313
  $result['customers'][] = array(
314
+ 'id' => $customer->getId(),
315
  'name' => $name,
316
  'custom_fields' => array(),
317
  'number_of_persons' => 1,
327
  while ( $time_start <= $time_end ) {
328
  $slot = array(
329
  'value' => Lib\Utils\DateTime::buildTimeString( $time_start, false ),
330
+ 'title' => Lib\Utils\DateTime::formatTime( $time_start ),
331
  );
332
  if ( $time_start < DAY_IN_SECONDS ) {
333
  $result['start_time'][] = $slot;
336
  $time_start += $ts_length;
337
  }
338
 
339
+ $days_times = Lib\Config::getDaysAndTimes();
340
+ $weekdays = array( 1 => 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', );
341
+ foreach ( $days_times['days'] as $index => $abbrev ) {
342
+ $result['week_days'][] = $weekdays[ $index ];
343
+ }
344
+
345
  wp_send_json( $result );
346
  }
347
 
357
  $response['success'] = true;
358
 
359
  $info = Lib\Entities\Appointment::query( 'a' )
360
+ ->select( 'ss.capacity_min as min_capacity,
361
+ ss.capacity_max AS max_capacity,
362
+ SUM(ca.number_of_persons) AS total_number_of_persons,
363
+ a.staff_id,
364
+ a.staff_any,
365
+ a.service_id,
366
+ a.custom_service_name,
367
+ a.custom_service_price,
368
+ a.start_date,
369
+ a.end_date,
370
+ a.internal_note,
371
+ a.series_id,
372
+ a.location_id' )
373
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
374
  ->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
375
+ ->where( 'a.id', $appointment->getId() )
376
  ->fetchRow();
377
 
378
  $response['data']['total_number_of_persons'] = $info['total_number_of_persons'];
379
+ $response['data']['min_capacity'] = $info['min_capacity'];
380
+ $response['data']['max_capacity'] = $info['max_capacity'];
381
+ $response['data']['start_date'] = $info['start_date'];
382
+ $response['data']['end_date'] = $info['end_date'];
383
+ $response['data']['staff_id'] = $info['staff_id'];
384
+ $response['data']['staff_any'] = (int) $info['staff_any'];
385
+ $response['data']['service_id'] = $info['service_id'];
386
+ $response['data']['custom_service_name'] = $info['custom_service_name'];
387
+ $response['data']['custom_service_price'] = (float) $info['custom_service_price'];
388
+ $response['data']['internal_note'] = $info['internal_note'];
389
+ $response['data']['series_id'] = $info['series_id'];
390
+ $response['data']['location_id'] = $info['location_id'];
391
+
392
  $customers = Lib\Entities\CustomerAppointment::query( 'ca' )
393
  ->select( 'ca.id,
394
  ca.customer_id,
395
+ ca.package_id,
396
  ca.custom_fields,
397
  ca.extras,
398
  ca.number_of_persons,
399
+ ca.notes,
400
  ca.status,
401
  ca.payment_id,
402
  ca.compound_service_id,
403
  ca.compound_token,
 
404
  p.paid AS payment,
405
  p.total AS payment_total,
406
  p.type AS payment_type,
407
  p.details AS payment_details,
408
  p.status AS payment_status' )
409
  ->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
410
+ ->where( 'ca.appointment_id', $appointment->getId() )
411
  ->fetchArray();
412
  foreach ( $customers as $customer ) {
413
  $payment_title = '';
414
  if ( $customer['payment'] !== null ) {
415
+ $payment_title = Lib\Utils\Price::format( $customer['payment'] );
416
  if ( $customer['payment'] != $customer['payment_total'] ) {
417
+ $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $customer['payment_total'] ) );
418
  }
419
  $payment_title .= sprintf(
420
  ' %s <span%s>%s</span>',
427
  if ( $customer['compound_service_id'] !== null ) {
428
  $service = new Lib\Entities\Service();
429
  if ( $service->load( $customer['compound_service_id'] ) ) {
430
+ $compound_service = $service->getTranslatedTitle();
431
  }
432
  }
433
  $response['data']['customers'][] = array(
434
  'id' => $customer['customer_id'],
435
  'ca_id' => $customer['id'],
436
+ 'package_id' => $customer['package_id'],
437
  'compound_service' => $compound_service,
438
  'compound_token' => $customer['compound_token'],
439
  'custom_fields' => (array) json_decode( $customer['custom_fields'], true ),
440
  'extras' => (array) json_decode( $customer['extras'], true ),
 
441
  'number_of_persons' => $customer['number_of_persons'],
442
+ 'notes' => $customer['notes'],
443
  'payment_id' => $customer['payment_id'],
444
  'payment_type' => $customer['payment'] != $customer['payment_total'] ? 'partial' : 'full',
445
  'payment_title' => $payment_title,
457
  {
458
  $response = array( 'success' => false );
459
 
460
+ $appointment_id = (int) $this->getParameter( 'id', 0 );
461
+ $staff_id = (int) $this->getParameter( 'staff_id', 0 );
462
+ $service_id = (int) $this->getParameter( 'service_id', -1 );
463
+ $custom_service_name = trim( $this->getParameter( 'custom_service_name' ) );
464
+ $custom_service_price = trim( $this->getParameter( 'custom_service_price' ) );
465
+ $location_id = (int) $this->getParameter( 'location_id', 0 );
466
+ $start_date = $this->getParameter( 'start_date' );
467
+ $end_date = $this->getParameter( 'end_date' );
468
+ $repeat = json_decode( $this->getParameter( 'repeat', '[]' ), true );
469
+ $schedule = $this->getParameter( 'schedule', array() );
470
+ $customers = json_decode( $this->getParameter( 'customers', '[]' ), true );
471
+ $notification = $this->getParameter( 'notification', 'no' );
472
+ $internal_note = $this->getParameter( 'internal_note' );
473
+ $created_from = $this->getParameter( 'created_from' );
474
 
475
+ if ( ! $service_id ) {
476
+ // Custom service.
477
+ $service_id = null;
478
+ }
479
+ if ( $service_id || $custom_service_name == '' ) {
480
+ $custom_service_name = null;
481
+ }
482
+ if ( $service_id || $custom_service_price == '' ) {
483
+ $custom_service_price = null;
484
+ }
485
+ if ( ! $location_id ) {
486
+ $location_id = null;
487
+ }
488
 
489
  // Check for errors.
490
  if ( ! $start_date ) {
494
  } elseif ( $start_date == $end_date ) {
495
  $response['errors']['time_interval'] = __( 'End time must not be equal to start time', 'bookly' );
496
  }
497
+ if ( $service_id == -1 ) {
498
  $response['errors']['service_required'] = true;
499
+ } else if ( $service_id === null && $custom_service_name === null ) {
500
+ $response['errors']['custom_service_name_required'] = true;
 
501
  }
502
  $total_number_of_persons = 0;
503
  $max_extras_duration = 0;
504
+ foreach ( $customers as $i => $customer ) {
505
+ if ( $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_PENDING ||
506
+ $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_APPROVED
 
507
  ) {
508
  $total_number_of_persons += $customer['number_of_persons'];
509
+ $extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
510
  if ( $extras_duration > $max_extras_duration ) {
511
  $max_extras_duration = $extras_duration;
512
  }
513
  }
514
+ $customers[ $i ]['created_from'] = ( $created_from == 'backend' ) ? 'backend' : 'frontend';
515
  }
516
+ if ( $service_id ) {
517
+ $staff_service = new Lib\Entities\StaffService();
518
+ $staff_service->loadBy( array(
519
+ 'staff_id' => 1,
520
+ 'service_id' => $service_id
521
+ ) );
522
+ if ( $total_number_of_persons > $staff_service->getCapacityMax() ) {
523
+ $response['errors']['overflow_capacity'] = sprintf(
524
+ __( 'The number of customers should not be more than %d', 'bookly' ),
525
+ $staff_service->getCapacityMax()
526
+ );
527
+ }
528
  }
 
529
 
530
  // If no errors then try to save the appointment.
531
  if ( ! isset ( $response['errors'] ) ) {
532
  if ( $repeat['enabled'] ) {
533
+ // Series.
534
  if ( ! empty ( $schedule ) ) {
 
535
  // Create new series.
536
  $series = new Lib\Entities\Series();
537
  $series
538
+ ->setRepeat( $this->getParameter( 'repeat' ) )
539
+ ->setToken( Lib\Utils\Common::generateToken( get_class( $series ), 'token' ) )
540
  ->save();
541
 
542
+ if ( $notification != 'no' ) {
543
+ // Create order per each customer to send notifications.
544
+ /** @var DataHolders\Order[] $orders */
545
+ $orders = array();
546
+ foreach ( $customers as $customer ) {
547
+ $order = DataHolders\Order::create( Lib\Entities\Customer::find( $customer['id'] ) )
548
+ ->addItem( 0, DataHolders\Series::create( $series ) )
549
+ ;
550
+ $orders[ $customer['id'] ] = $order;
551
+ }
552
+ }
553
+
554
+ $service = Lib\Entities\Service::find( $service_id );
555
 
556
  foreach ( $schedule as $slot ) {
557
  $slot = json_decode( $slot );
558
  $appointment = new Lib\Entities\Appointment();
559
  $appointment
560
+ ->setSeries( $series )
561
+ ->setLocationId( $location_id )
562
+ ->setStaffId( $staff_id )
563
+ ->setServiceId( $service_id )
564
+ ->setStartDate( $slot[0][2] )
565
+ ->setEndDate( date( 'Y-m-d H:i:s', strtotime( $slot[0][2] ) + $service->getDuration() ) )
566
+ ->setInternalNote( $internal_note )
567
+ ->setExtrasDuration( $max_extras_duration )
568
  ;
569
 
570
  if ( $appointment->save() !== false ) {
571
+ // Save customer appointments.
572
+ $ca_list = $appointment->saveCustomerAppointments( $customers );
573
  // Google Calendar.
574
  $appointment->handleGoogleCalendar();
575
+ // Waiting list.
576
+ Lib\Proxy\WaitingList::handleParticipantsChange( $appointment );
577
 
578
  if ( $notification != 'no' ) {
579
+ foreach ( $ca_list as $ca ) {
580
+ $item = DataHolders\Simple::create( $ca )
581
+ ->setService( $service )
582
+ ->setAppointment( $appointment )
583
+ ;
584
+ $orders[ $ca->getCustomerId() ]->getItem( 0 )->addItem( $item );
585
+ }
586
  }
587
  }
588
  }
589
+ if ( $notification != 'no' ) {
590
+ foreach ( $orders as $order ) {
591
+ Lib\Proxy\RecurringAppointments::sendRecurring( $order->getItem( 0 ), $order );
592
+ }
593
+ }
594
  }
595
  $response['success'] = true;
596
  $response['data'] = array( 'staffId' => 1 ); // make FullCalendar refetch events
597
  } else {
598
+ // Single appointment.
599
  $appointment = new Lib\Entities\Appointment();
600
  if ( $appointment_id ) {
601
  // Edit.
602
  $appointment->load( $appointment_id );
603
+ if ( $appointment->getStaffId() != $staff_id ) {
604
+ $appointment->setStaffAny( 0 );
605
+ }
606
  }
607
+ $appointment
608
+ ->setLocationId( $location_id )
609
+ ->setStaffId( 1 )
610
+ ->setServiceId( $service_id )
611
+ ->setCustomServiceName( $custom_service_name )
612
+ ->setCustomServicePrice( $custom_service_price )
613
+ ->setStartDate( $start_date )
614
+ ->setEndDate( $end_date )
615
+ ->setInternalNote( $internal_note )
616
+ ->setExtrasDuration( $max_extras_duration )
617
+ ;
618
 
619
  if ( $appointment->save() !== false ) {
620
  // Save customer appointments.
621
  $ca_status_changed = $appointment->saveCustomerAppointments( $customers );
 
622
 
623
  // Google Calendar.
624
  $appointment->handleGoogleCalendar();
625
+ // Waiting list.
626
+ Lib\Proxy\WaitingList::handleParticipantsChange( $appointment );
627
 
628
  // Send notifications.
629
+ if ( $notification == 'changed_status' ) {
630
+ foreach ( $ca_status_changed as $ca ) {
631
+ Lib\NotificationSender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
632
+ }
633
+ } else if ( $notification == 'all' ) {
634
+ $ca_list = $appointment->getCustomerAppointments( true );
635
+ foreach ( $ca_list as $ca ) {
636
+ Lib\NotificationSender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
637
  }
638
  }
639
 
640
  $response['success'] = true;
641
+ $response['data'] = $this->_getAppointmentForFC( $staff_id, $appointment->getId() );
642
  } else {
643
  $response['errors'] = array( 'db' => __( 'Could not save appointment in database.', 'bookly' ) );
644
  }
645
  }
646
  }
647
+ update_user_meta( get_current_user_id(), 'bookly_appointment_form_send_notifications', $notification );
648
  wp_send_json( $response );
649
  }
650
 
651
+ public function executeCheckAppointmentErrors()
652
  {
653
  $start_date = $this->getParameter( 'start_date' );
654
  $end_date = $this->getParameter( 'end_date' );
655
+ $staff_id = (int) $this->getParameter( 'staff_id' );
656
+ $service_id = (int) $this->getParameter( 'service_id' );
657
+ $appointment_id = (int) $this->getParameter( 'appointment_id' );
658
  $timestamp_diff = strtotime( $end_date ) - strtotime( $start_date );
659
+ $customers = json_decode( $this->getParameter( 'customers', '[]' ), true );
660
 
661
  $result = array(
662
+ 'date_interval_not_available' => false,
663
+ 'date_interval_warning' => false,
664
+ 'customers_appointments_limit' => array(),
665
  );
666
 
667
+ $max_extras_duration = 0;
668
+ foreach ( $customers as $customer ) {
669
+ if ( $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_PENDING ||
670
+ $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_APPROVED
671
+ ) {
672
+ $extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
673
+ if ( $extras_duration > $max_extras_duration ) {
674
+ $max_extras_duration = $extras_duration;
675
+ }
676
+ }
677
+ }
678
+
679
+ $total_end_date = $end_date;
680
+ if ( $max_extras_duration > 0 ) {
681
+ $total_end_date = date_create( $end_date )->modify( '+' . $max_extras_duration . ' sec' )->format( 'Y-m-d H:i:s' );
682
+ }
683
+ if ( ! $this->dateIntervalIsAvailableForAppointment( $start_date, $total_end_date, 1, $appointment_id ) ) {
684
  $result['date_interval_not_available'] = true;
685
  }
686
 
688
  $service = new Lib\Entities\Service();
689
  $service->load( $service_id );
690
 
691
+ $duration = $service->getDuration();
692
 
693
  // Service duration interval is not equal to.
694
  $result['date_interval_warning'] = ( $timestamp_diff != $duration );
695
+
696
+ // Check customers for appointments limit
697
+ if ( $start_date ) {
698
+ foreach ( $customers as $index => $customer ) {
699
+ if ( $service->appointmentsLimitReached( $customer['id'], $start_date ) ) {
700
+ $customer_error = Lib\Entities\Customer::find( $customer['id'] );
701
+ $result['customers_appointments_limit'][] = sprintf( __( '%s has reached the limit of bookings for this service', 'bookly' ), $customer_error->getFullName() );
702
+ }
703
+ }
704
+ }
705
  }
706
 
707
  wp_send_json( $result );
721
  ->find();
722
  /** @var Lib\Entities\CustomerAppointment $ca */
723
  foreach ( $ca_list as $ca ) {
724
+ switch ( $ca->getStatus() ) {
725
  case Lib\Entities\CustomerAppointment::STATUS_PENDING:
726
+ case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
727
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
728
  break;
729
  case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
730
+ $ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
731
  break;
732
  }
733
+ Lib\NotificationSender::sendSingle(
734
+ DataHolders\Simple::create( $ca ),
735
+ null,
736
+ array( 'cancellation_reason' => $reason )
737
+ );
738
  }
739
  }
740
 
741
+ Lib\Entities\Appointment::find( $appointment_id )->delete();
 
 
742
 
743
  wp_send_json_success();
744
  }
753
  private function dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id )
754
  {
755
  return Lib\Entities\Appointment::query( 'a' )
 
756
  ->whereNot( 'a.id', $appointment_id )
757
  ->where( 'a.staff_id', $staff_id )
758
+ ->whereLt( 'a.start_date', $end_date )
759
+ ->whereGt( 'a.end_date', $start_date )
 
 
760
  ->count() == 0;
761
  }
762
 
803
  */
804
  private function _buildAppointmentsForFC( $staff_id, Lib\Query $query )
805
  {
806
+ $one_participant = '<div>' . str_replace( "\n", '</div><div>', get_option( 'bookly_cal_one_participant' ) ) . '</div>';
807
+ $many_participants = '<div>' . str_replace( "\n", '</div><div>', get_option( 'bookly_cal_many_participants' ) ) . '</div>';
808
+ $postfix_any = sprintf( ' (%s)', get_option( 'bookly_l10n_option_employee' ) );
809
+ $participants = null;
810
+ $default_codes = array(
811
+ '{amount_due}' => '',
812
+ '{amount_paid}' => '',
813
+ '{appointment_date}' => '',
814
+ '{appointment_time}' => '',
815
+ '{booking_number}' => '',
816
+ '{category_name}' => '',
817
+ '{client_email}' => '',
818
+ '{client_name}' => '',
819
+ '{client_first_name}' => '',
820
+ '{client_last_name}' => '',
821
+ '{client_phone}' => '',
822
+ '{company_address}' => get_option( 'bookly_co_address' ),
823
+ '{company_name}' => get_option( 'bookly_co_name' ),
824
+ '{company_phone}' => get_option( 'bookly_co_phone' ),
825
+ '{company_website}' => get_option( 'bookly_co_website' ),
826
+ '{custom_fields}' => '',
827
+ '{extras}' => '',
828
+ '{extras_total_price}'=> 0,
829
+ '{location_name}' => '',
830
+ '{location_info}' => '',
831
+ '{on_waiting_list}' => '',
832
+ '{payment_status}' => '',
833
+ '{payment_type}' => '',
834
+ '{service_capacity}' => '',
835
+ '{service_info}' => '',
836
+ '{service_name}' => '',
837
+ '{service_price}' => '',
838
+ '{signed_up}' => '',
839
+ '{staff_email}' => '',
840
+ '{staff_info}' => '',
841
+ '{staff_name}' => '',
842
+ '{staff_phone}' => '',
843
+ '{status}' => '',
844
+ '{total_price}' => '',
845
+ );
846
  $appointments = $query
847
+ ->select( 'a.id, a.series_id, a.staff_any, a.location_id, a.start_date, DATE_ADD(a.end_date, INTERVAL a.extras_duration SECOND) AS end_date,
848
+ COALESCE(s.title,a.custom_service_name) AS service_name, COALESCE(s.color,"silver") AS service_color, s.info AS service_info,
849
+ 1 AS service_capacity, COALESCE(ss.price,a.custom_service_price) AS service_price,
850
+ st.full_name AS staff_name, st.email AS staff_email, st.info AS staff_info, st.phone AS staff_phone,
851
  (SELECT SUM(ca.number_of_persons) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca WHERE ca.appointment_id = a.id) AS total_number_of_persons,
852
  ca.number_of_persons,
853
  ca.custom_fields,
854
  ca.status AS appointment_status,
855
  ca.extras,
856
+ ca.package_id,
857
+ ct.name AS category_name,
858
+ c.full_name AS client_name, c.first_name AS client_first_name, c.last_name AS client_last_name, c.phone AS client_phone, c.email AS client_email, c.id AS customer_id,
859
+ p.total, p.type AS payment_gateway, p.status AS payment_status, p.paid,
860
+ (SELECT SUM(ca.number_of_persons) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca WHERE ca.appointment_id = a.id AND ca.status = "waitlisted") AS on_waiting_list' )
861
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
862
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
863
  ->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
864
  ->leftJoin( 'Service', 's', 's.id = a.service_id' )
865
+ ->leftJoin( 'Category', 'ct', 'ct.id = s.category_id' )
866
  ->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
867
  ->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
 
868
  ->groupBy( 'a.id' )
869
  ->fetchArray();
870
 
871
  foreach ( $appointments as $key => $appointment ) {
872
+ $codes = $default_codes;
873
+ $codes['{appointment_date}'] = Lib\Utils\DateTime::formatDate( $appointment['start_date'] );
874
+ $codes['{appointment_time}'] = Lib\Utils\DateTime::formatTime( $appointment['start_date'] );
875
+ $codes['{booking_number}'] = $appointment['id'];
876
+ $codes['{on_waiting_list}'] = $appointment['on_waiting_list'];
877
+ $codes['{service_name}'] = $appointment['service_name'] ? esc_html( $appointment['service_name'] ) : __( 'Untitled', 'bookly' );
878
+ $codes['{service_price}'] = Lib\Utils\Price::format( $appointment['service_price'] );
879
+ $codes['{signed_up}'] = $appointment['total_number_of_persons'];
880
+ foreach ( array( 'staff_name', 'staff_phone', 'staff_info', 'staff_email', 'service_info', 'service_capacity', 'category_name' ) as $field ) {
881
+ $codes[ '{' . $field . '}' ] = esc_html( $appointment[ $field ] );
882
+ }
883
+ if ( $appointment['staff_any'] ) {
884
+ $codes['{staff_name}'] .= $postfix_any;
885
+ }
886
  // Display customer information only if there is 1 customer. Don't confuse with number_of_persons.
887
  if ( $appointment['number_of_persons'] == $appointment['total_number_of_persons'] ) {
888
+ $participants = 'one';
889
+ $template = $one_participant;
890
+ foreach ( array( 'client_name', 'client_first_name', 'client_last_name', 'client_phone', 'client_email' ) as $data_entry ) {
891
  if ( $appointment[ $data_entry ] ) {
892
+ $codes[ '{' . $data_entry . '}' ] = esc_html( $appointment[ $data_entry ] );
 
 
 
 
 
 
 
 
 
 
 
893
  }
894
  }
895
+
896
  // Payment.
897
  if ( $appointment['total'] ) {
898
+ $codes['{total_price}'] = Lib\Utils\Price::format( $appointment['total'] );
899
+ $codes['{amount_paid}'] = Lib\Utils\Price::format( $appointment['paid'] );
900
+ $codes['{amount_due}'] = Lib\Utils\Price::format( $appointment['total'] - $appointment['paid'] );
901
+ $codes['{total_price}'] = Lib\Utils\Price::format( $appointment['total'] );
902
+ $codes['{payment_type}'] = Lib\Entities\Payment::typeToString( $appointment['payment_gateway'] );
903
+ $codes['{payment_status}'] = Lib\Entities\Payment::statusToString( $appointment['payment_status'] );
 
904
  }
905
  // Status.
906
+ $codes['{status}'] = Lib\Entities\CustomerAppointment::statusToString( $appointment['appointment_status'] );
 
 
 
 
 
907
  } else {
908
+ $participants = 'many';
909
+ $template = $many_participants;
910
  }
911
+
912
+ $codes = Lib\Proxy\Shared::prepareCalendarAppointmentCodesData( $codes, $appointment, $participants );
913
+
914
  $appointments[ $key ] = array(
915
+ 'id' => $appointment['id'],
916
+ 'start' => $appointment['start_date'],
917
+ 'end' => $appointment['end_date'],
918
+ 'title' => ' ',
919
+ 'desc' => strtr( $template, $codes ),
920
+ 'color' => $appointment['service_color'],
921
+ 'staffId' => $staff_id,
922
+ 'series_id' => (int) $appointment['series_id'],
923
+ 'package_id' => (int) $appointment['package_id'],
924
+ 'waitlisted' => (int) $appointment['on_waiting_list'],
925
+ 'staff_any' => (int) $appointment['staff_any'],
926
  );
927
  }
928
 
929
  return $appointments;
930
  }
931
 
 
 
 
 
 
 
 
 
 
 
 
932
  }
backend/modules/calendar/resources/js/calendar-common.js ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ var Calendar = function($container, options) {
4
+ var obj = this;
5
+ jQuery.extend(obj.options, options);
6
+
7
+ // settings for fullcalendar.
8
+ var settings = {
9
+ firstDay: obj.options.l10n.startOfWeek,
10
+ allDayText: obj.options.l10n.allDay,
11
+ buttonText: {
12
+ today: obj.options.l10n.today,
13
+ month: obj.options.l10n.month,
14
+ week: obj.options.l10n.week,
15
+ day: obj.options.l10n.day
16
+ },
17
+ axisFormat: obj.options.l10n.mjsTimeFormat,
18
+ slotDuration: obj.options.l10n.slotDuration,
19
+ // Text/Time Customization.
20
+ timeFormat: obj.options.l10n.mjsTimeFormat,
21
+ monthNames: obj.options.l10n.calendar.longMonths,
22
+ monthNamesShort: obj.options.l10n.calendar.shortMonths,
23
+ dayNames: obj.options.l10n.calendar.longDays,
24
+ dayNamesShort: obj.options.l10n.calendar.shortDays,
25
+ allDaySlot: false,
26
+ eventBackgroundColor: 'silver',
27
+ // Agenda Options.
28
+ displayEventEnd: true,
29
+ // Event Dragging & Resizing.
30
+ editable: false,
31
+ // Event Data.
32
+ eventSources: [{
33
+ url: ajaxurl,
34
+ data: {
35
+ action: 'bookly_get_staff_appointments',
36
+ csrf_token : obj.options.l10n.csrf_token,
37
+ staff_ids: function () {
38
+ var ids = [];
39
+ if (obj.options.is_backend && obj.options.getCurrentStaffId() == 0) {
40
+ var staffMembers = obj.options.getStaffMembers();
41
+ for (var i = 0; i < staffMembers.length; ++i) {
42
+ ids.push(staffMembers[i].id);
43
+ }
44
+ } else {
45
+ ids.push(obj.options.getCurrentStaffId());
46
+ }
47
+ return ids;
48
+ }
49
+ }
50
+ }],
51
+ eventAfterRender: function (calEvent, $calEventList, calendar) {
52
+ if (calEvent.rendering !== 'background') {
53
+ $calEventList.each(function () {
54
+ var $calEvent = $(this),
55
+ titleHeight = $calEvent.find('.fc-title').height(),
56
+ origHeight = $calEvent.outerHeight()
57
+ ;
58
+ $calEvent.removeClass('fc-short');
59
+ if (origHeight < titleHeight) {
60
+ var z_index = $calEvent.zIndex();
61
+ // Mouse handlers.
62
+ $calEvent.on('mouseenter', function () {
63
+ $calEvent.css({'z-index': 64, bottom: '', height: ''});
64
+ }).on('mouseleave', function () {
65
+ $calEvent.css({'z-index': z_index, height: origHeight});
66
+ });
67
+ }
68
+ });
69
+ }
70
+ },
71
+ // Clicking & Hovering.
72
+ dayClick: function (date, jsEvent, view) {
73
+ var staff_id, visible_staff_id;
74
+ if (view.type == 'multiStaffDay') {
75
+ var cell = view.coordMap.getCell(jsEvent.pageX, jsEvent.pageY),
76
+ staffMembers = view.opt('staffMembers');
77
+ staff_id = staffMembers[cell.col].id;
78
+ visible_staff_id = 0;
79
+ } else {
80
+ staff_id = visible_staff_id = obj.options.getCurrentStaffId();
81
+ }
82
+ showAppointmentDialog(
83
+ null,
84
+ staff_id,
85
+ date,
86
+ function (event) {
87
+ if (event == 'refresh') {
88
+ $container.fullCalendar('refetchEvents');
89
+ } else {
90
+ if (visible_staff_id == event.staffId || visible_staff_id == 0) {
91
+ if (event.id) {
92
+ // Create event in calendar.
93
+ $container.fullCalendar('renderEvent', event);
94
+ } else {
95
+ $container.fullCalendar('refetchEvents');
96
+ }
97
+ } else {
98
+ // Switch to the event owner tab.
99
+ jQuery('li[data-staff_id=' + event.staffId + ']').click();
100
+ }
101
+ }
102
+ }
103
+ );
104
+ },
105
+ // Event Rendering.
106
+ eventRender: function (calEvent, $event, view) {
107
+ if (calEvent.rendering !== 'background') {
108
+ var $body = $event.find('.fc-title');
109
+ if (calEvent.desc) {
110
+ $body.append(calEvent.desc);
111
+ }
112
+
113
+ var $time = $event.find('.fc-time');
114
+ if (obj.options.l10n.recurring_appointments.active == '1' && calEvent.series_id) {
115
+ $time.prepend(
116
+ $('<a class="bookly-fc-icon dashicons dashicons-admin-links"></a>')
117
+ .attr('title', obj.options.l10n.recurring_appointments.title)
118
+ .on('click', function (e) {
119
+ e.stopPropagation();
120
+ $(document.body).trigger('recurring_appointments.series_dialog', [calEvent.series_id, function (event) {
121
+ // Switch to the event owner tab.
122
+ jQuery('li[data-staff_id=' + event.staffId + ']').click();
123
+ }]);
124
+ })
125
+ );
126
+ }
127
+ if (obj.options.l10n.waiting_list.active == '1' && calEvent.waitlisted > 0) {
128
+ $time.prepend(
129
+ $('<span class="bookly-fc-icon dashicons dashicons-list-view"></span>')
130
+ .attr('title', obj.options.l10n.waiting_list.title)
131
+ );
132
+ }
133
+ if (obj.options.l10n.packages.active == '1' && calEvent.package_id > 0) {
134
+ $time.prepend(
135
+ $('<span class="bookly-fc-icon dashicons dashicons-calendar" style="padding:0 2px;"></span>')
136
+ .attr('title', obj.options.l10n.packages.title)
137
+ .on('click', function (e) {
138
+ e.stopPropagation();
139
+ if (obj.options.l10n.packages.active == '1' && calEvent.package_id) {
140
+ $(document.body).trigger('bookly_packages.schedule_dialog', [calEvent.package_id, function () {
141
+ $container.fullCalendar('refetchEvents');
142
+ }]);
143
+ }
144
+ })
145
+ );
146
+ }
147
+ $time.prepend(
148
+ $('<a class="bookly-fc-icon dashicons dashicons-trash"></a>')
149
+ .attr('title', obj.options.l10n.delete)
150
+ .on('click', function (e) {
151
+ e.stopPropagation();
152
+ // Localize contains only string values
153
+ if (obj.options.l10n.recurring_appointments.active == '1' && calEvent.series_id) {
154
+ $(document.body).trigger('recurring_appointments.delete_dialog', [$container, calEvent]);
155
+ } else {
156
+ obj.$deleteDialog.data('calEvent', calEvent).modal('show');
157
+ }
158
+ })
159
+ );
160
+ }
161
+ },
162
+ eventClick: function (calEvent, jsEvent, view) {
163
+ var visible_staff_id;
164
+ if (view.type == 'multiStaffDay') {
165
+ visible_staff_id = 0;
166
+ } else {
167
+ visible_staff_id = calEvent.staffId;
168
+ }
169
+
170
+ showAppointmentDialog(
171
+ calEvent.id,
172
+ null,
173
+ null,
174
+ function (event) {
175
+ if (event == 'refresh') {
176
+ $container.fullCalendar('refetchEvents');
177
+ } else {
178
+ if (visible_staff_id == event.staffId || visible_staff_id == 0) {
179
+ // Update event in calendar.
180
+ jQuery.extend(calEvent, event);
181
+ $container.fullCalendar('updateEvent', calEvent);
182
+ } else {
183
+ // Switch to the event owner tab.
184
+ jQuery('li[data-staff_id=' + event.staffId + ']').click();
185
+ }
186
+ }
187
+ }
188
+ );
189
+ },
190
+ loading: function (isLoading) {
191
+ if (isLoading) {
192
+ $('.fc-loading-inner').show();
193
+ }
194
+ },
195
+ eventAfterAllRender: function () {
196
+ $('.fc-loading-inner').hide();
197
+ }
198
+ };
199
+
200
+ // Init fullcalendar
201
+ $container.fullCalendar($.extend({}, settings, obj.options.fullcalendar));
202
+
203
+ var $fcDatePicker = $('<input type=hidden />');
204
+
205
+ $('.fc-toolbar .fc-center h2', $container).before($fcDatePicker).on('click', function () {
206
+ $fcDatePicker.datepicker('setDate', $container.fullCalendar('getDate').toDate()).datepicker('show');
207
+ });
208
+
209
+ // Init date picker for fast navigation in FullCalendar.
210
+ $fcDatePicker.datepicker({
211
+ dayNamesMin: settings.dayNamesShort,
212
+ monthNames: settings.monthNames,
213
+ monthNamesShort: settings.monthNamesShort,
214
+ firstDay: settings.firstDay,
215
+ beforeShow: function (input, inst) {
216
+ inst.dpDiv.queue(function () {
217
+ inst.dpDiv.css({marginTop: '35px', 'font-size': '13.5px'});
218
+ inst.dpDiv.dequeue();
219
+ });
220
+ },
221
+ onSelect: function (dateText, inst) {
222
+ var d = new Date(dateText);
223
+ $container.fullCalendar('gotoDate', d);
224
+ if ($container.fullCalendar('getView').type != 'agendaDay' &&
225
+ $container.fullCalendar('getView').type != 'multiStaffDay') {
226
+ $container.find('.fc-day').removeClass('bookly-fc-day-active');
227
+ $container.find('.fc-day[data-date="' + moment(d).format('YYYY-MM-DD') + '"]').addClass('bookly-fc-day-active');
228
+ }
229
+ },
230
+ onClose: function (dateText, inst) {
231
+ inst.dpDiv.queue(function () {
232
+ inst.dpDiv.css({marginTop: '0'});
233
+ inst.dpDiv.dequeue();
234
+ });
235
+ }
236
+ });
237
+
238
+ /**
239
+ * On delete appointment click.
240
+ */
241
+ if (obj.$deleteDialog.data('events') == undefined) {
242
+ obj.$deleteDialog.on('click', '#bookly-delete', function (e) {
243
+ var calEvent = obj.$deleteDialog.data('calEvent'),
244
+ ladda = Ladda.create(this);
245
+ ladda.start();
246
+ $.ajax({
247
+ type : 'POST',
248
+ url : ajaxurl,
249
+ data : {
250
+ 'action': 'bookly_delete_appointment',
251
+ 'csrf_token': obj.options.l10n.csrf_token,
252
+ 'appointment_id': calEvent.id,
253
+ 'notify': $('#bookly-delete-notify').prop('checked') ? 1 : 0,
254
+ 'reason': $('#bookly-delete-reason').val()
255
+ },
256
+ dataType : 'json',
257
+ xhrFields : {withCredentials: true},
258
+ crossDomain: 'withCredentials' in new XMLHttpRequest(),
259
+ success : function (response) {
260
+ ladda.stop();
261
+ $container.fullCalendar('removeEvents', calEvent.id);
262
+ obj.$deleteDialog.modal('hide');
263
+ }
264
+ });
265
+ });
266
+ }
267
+ };
268
+
269
+ Calendar.prototype.$deleteDialog = $('#bookly-delete-dialog');
270
+ Calendar.prototype.options = {
271
+ fullcalendar: {},
272
+ getCurrentStaffId: function () { return -1; },
273
+ getStaffMembers: function () { return []; },
274
+ l10n: {},
275
+ is_backend: true
276
+ };
277
+
278
+ window.BooklyCalendar = Calendar;
279
+ });
backend/modules/calendar/resources/js/calendar.js CHANGED
@@ -5,14 +5,12 @@ jQuery(function ($) {
5
  $staff = $('input.bookly-js-check-entity'),
6
  $showAll = $('input#bookly-check-all-entities'),
7
  firstHour = new Date().getHours(),
8
- $staffButton = $('#ab-staff-button'),
9
  staffMembers = [],
10
  staffIds = getCookie('bookly_cal_st_ids'),
11
  tabId = getCookie('bookly_cal_tab_id'),
12
  lastView = getCookie('bookly_cal_view'),
13
- views = 'month,agendaWeek,agendaDay,multiStaffDay',
14
- $deleteDialog = $('#bookly-delete-dialog'),
15
- $deleteButton = $('#bookly-delete');
16
 
17
  if (views.indexOf(lastView) == -1) {
18
  lastView = 'multiStaffDay';
@@ -39,168 +37,65 @@ jQuery(function ($) {
39
  }
40
  updateStaffButton();
41
 
42
- // Init FullCalendar.
43
- $fullCalendar.fullCalendar({
44
- // General Display.
45
- firstDay: BooklyL10n.startOfWeek,
46
- header: {
47
- left: 'prev,next today',
48
- center: 'title',
49
- right: views
50
- },
51
- height: heightFC(),
52
- // Views.
53
- defaultView: lastView,
54
- scrollTime: firstHour + ':00:00',
55
- views: {
56
- agendaWeek: {
57
- columnFormat: 'ddd, D'
58
- },
59
- multiStaffDay: {
60
- staffMembers: staffMembers
61
- }
62
- },
63
- eventBackgroundColor: 'silver',
64
- // Agenda Options.
65
- allDaySlot: false,
66
- allDayText: BooklyL10n.allDay,
67
- axisFormat: BooklyL10n.mjsTimeFormat,
68
- slotDuration: BooklyL10n.slotDuration,
69
- // Text/Time Customization.
70
- timeFormat: BooklyL10n.mjsTimeFormat,
71
- displayEventEnd: true,
72
- buttonText: {
73
- today: BooklyL10n.today,
74
- month: BooklyL10n.month,
75
- week: BooklyL10n.week,
76
- day: BooklyL10n.day
77
- },
78
- monthNames: BooklyL10n.calendar.longMonths,
79
- monthNamesShort: BooklyL10n.calendar.shortMonths,
80
- dayNames: BooklyL10n.calendar.longDays,
81
- dayNamesShort: BooklyL10n.calendar.shortDays,
82
- // Event Dragging & Resizing.
83
- editable: false,
84
- // Event Data.
85
- eventSources: [{
86
- url: ajaxurl,
87
- data: {
88
- action : 'bookly_get_staff_appointments',
89
- staff_ids : function () {
90
- var ids = [];
91
- if ($tabs.filter('.active').data('staff_id') == 0) {
92
- for (var i = 0; i < staffMembers.length; ++i) {
93
- ids.push(staffMembers[i].id);
94
- }
95
- } else {
96
- ids.push($tabs.filter('.active').data('staff_id'));
97
- }
98
- return ids;
99
- }
100
- }
101
- }],
102
- // Event Rendering.
103
- eventRender : function (calEvent, $event) {
104
- var body = calEvent.title + '<a class="delete-event dashicons dashicons-trash" title="' + BooklyL10n.delete + '"></a>';
105
 
106
- if (calEvent.desc) {
107
- body += calEvent.desc;
108
- }
109
 
110
- $event.find('.fc-title').html(body);
 
 
111
 
112
- var $time = $event.find('.fc-time');
113
- $time.attr('data-start', $time.find('span').text());
114
 
115
- $event.find('.delete-event').on('click', function(e) {
116
- e.stopPropagation();
117
- // Localize contains only string values
118
- if (BooklyL10n.recurring_appointments_enabled == '1' && calEvent.series_id) {
119
- $(document.body).trigger( 'recurring_appointments.delete_dialog', [ $fullCalendar, calEvent ] );
120
- } else {
121
- $deleteDialog.data('calEvent', calEvent).modal('show');
122
- }
123
- });
124
- },
125
- eventAfterRender : function (calEvent, $calEventList, calendar) {
126
- $calEventList.each(function () {
127
- var $calEvent = $(this);
128
- var titleHeight = $calEvent.find('.fc-title').height(),
129
- origHeight = $calEvent.outerHeight();
130
- if (origHeight < titleHeight) {
131
- var z_index = $calEvent.zIndex();
132
- // Mouse handlers.
133
- $calEvent.on('mouseenter', function () {
134
- $calEvent.removeClass('fc-short')
135
- .css({'z-index': 64, bottom: '', height: ''});
136
- }).on('mouseleave', function () {
137
- $calEvent.css({'z-index': z_index, height: origHeight});
138
- });
139
- }
140
- });
141
- },
142
- // Clicking & Hovering.
143
- dayClick: function (date, jsEvent, view) {
144
- var staff_id, visible_staff_id;
145
- if (view.type == 'multiStaffDay') {
146
- var cell = view.coordMap.getCell(jsEvent.pageX, jsEvent.pageY);
147
- var staffMembers = view.opt('staffMembers');
148
- staff_id = staffMembers[cell.col].id;
149
- visible_staff_id = 0;
150
- } else {
151
- staff_id = visible_staff_id = $tabs.filter('.active').data('staff_id');
152
- }
153
 
154
- showAppointmentDialog(
155
- null,
156
- staff_id,
157
- date,
158
- function (event) {
159
- if (visible_staff_id == event.staffId || visible_staff_id == 0) {
160
- if (event.id) {
161
- // Create event in calendar.
162
- $fullCalendar.fullCalendar('renderEvent', event);
163
- } else {
164
- $fullCalendar.fullCalendar('refetchEvents');
165
- }
166
- } else {
167
- // Switch to the event owner tab.
168
- jQuery('li[data-staff_id=' + event.staffId + ']').click();
169
- }
 
 
170
  }
171
- );
172
- },
173
- eventClick: function (calEvent, jsEvent, view) {
174
- var visible_staff_id;
175
- if (view.type == 'multiStaffDay') {
176
- visible_staff_id = 0;
177
- } else {
178
- visible_staff_id = calEvent.staffId;
179
  }
180
-
181
- showAppointmentDialog(
182
- calEvent.id,
183
- null,
184
- null,
185
- function (event) {
186
- if (visible_staff_id == event.staffId || visible_staff_id == 0) {
187
- // Update event in calendar.
188
- jQuery.extend(calEvent, event);
189
- $fullCalendar.fullCalendar('updateEvent', calEvent);
190
- } else {
191
- // Switch to the event owner tab.
192
- jQuery('li[data-staff_id=' + event.staffId + ']').click();
193
- }
194
- }
195
- );
196
  },
197
- loading: function (bool) {
198
- $('.fc-loading-inner').toggle(bool);
199
  },
200
- viewRender: function (view, element) {
201
- setCookie('bookly_cal_view', view.type);
202
- }
203
- });
 
 
 
204
 
205
  $('.fc-agendaDay-button').addClass('fc-corner-right');
206
  if ($tabs.filter('.active').data('staff_id') == 0) {
@@ -209,40 +104,6 @@ jQuery(function ($) {
209
  $('.fc-multiStaffDay-button').hide();
210
  }
211
 
212
- // Init date picker for fast navigation in FullCalendar.
213
- var $fcDatePicker = $('<input type=hidden />');
214
- $('.fc-toolbar .fc-center h2').before($fcDatePicker).on('click', function () {
215
- $fcDatePicker.datepicker('setDate', $fullCalendar.fullCalendar('getDate').toDate()).datepicker('show');
216
- });
217
- $fcDatePicker.datepicker({
218
- dayNamesMin : BooklyL10n.calendar.shortDays,
219
- monthNames : BooklyL10n.calendar.longMonths,
220
- monthNamesShort : BooklyL10n.calendar.shortMonths,
221
- firstDay : BooklyL10n.startOfWeek,
222
- beforeShow: function (input, inst) {
223
- inst.dpDiv.queue(function () {
224
- inst.dpDiv.css({marginTop: '35px'});
225
- inst.dpDiv.dequeue();
226
- });
227
- },
228
- onSelect: function (dateText, inst) {
229
- var d = new Date(dateText);
230
- $fullCalendar.fullCalendar('gotoDate', d);
231
- if ($fullCalendar.fullCalendar('getView').type != 'agendaDay' &&
232
- $fullCalendar.fullCalendar('getView').type != 'multiStaffDay')
233
- {
234
- $fullCalendar.find('.fc-day').removeClass('bookly-fc-day-active');
235
- $fullCalendar.find('.fc-day[data-date="' + moment(d).format('YYYY-MM-DD') + '"]').addClass('bookly-fc-day-active');
236
- }
237
- },
238
- onClose: function (dateText, inst) {
239
- inst.dpDiv.queue(function () {
240
- inst.dpDiv.css({marginTop: '0'});
241
- inst.dpDiv.dequeue();
242
- });
243
- }
244
- });
245
-
246
  $(window).on('resize', function () {
247
  $fullCalendar.fullCalendar('option', 'height', heightFC());
248
  });
@@ -252,7 +113,6 @@ jQuery(function ($) {
252
  e.preventDefault();
253
  $tabs.removeClass('active');
254
  $(this).addClass('active');
255
-
256
  var staff_id = $(this).data('staff_id');
257
  setCookie('bookly_cal_tab_id', staff_id);
258
 
@@ -304,29 +164,6 @@ jQuery(function ($) {
304
  }
305
  });
306
 
307
- /**
308
- * On delete appointment click.
309
- */
310
- $deleteButton.on('click', function (e) {
311
- var calEvent = $deleteDialog.data('calEvent'),
312
- ladda = Ladda.create(this);
313
- ladda.start();
314
- $.post(
315
- ajaxurl,
316
- {
317
- 'action' : 'bookly_delete_appointment',
318
- 'appointment_id' : calEvent.id,
319
- 'notify' : $('#bookly-delete-notify').prop('checked') ? 1 : 0,
320
- 'reason' : $('#bookly-delete-reason').val()
321
- },
322
- function () {
323
- ladda.stop();
324
- $fullCalendar.fullCalendar('removeEvents', calEvent.id);
325
- $deleteDialog.modal('hide');
326
- }
327
- );
328
- });
329
-
330
  function updateStaffButton() {
331
  $showAll.prop('checked', $staff.filter(':not(:checked)').length == 0);
332
 
@@ -373,29 +210,4 @@ jQuery(function ($) {
373
  return keyValue ? keyValue[2] : null;
374
  }
375
 
376
- /**
377
- * Calculate height of FullCalendar.
378
- *
379
- * @return {number}
380
- */
381
- function heightFC() {
382
- var window_height = $(window).height(),
383
- wp_admin_bar_height = $('#wpadminbar').height(),
384
- ab_calendar_tabs_height = $('#bookly-fc-wrapper .tabbable').outerHeight(true),
385
- height_to_reduce = wp_admin_bar_height + ab_calendar_tabs_height,
386
- $wrap = $('#wpbody-content .wrap');
387
-
388
- if ($wrap.css('margin-top')) {
389
- height_to_reduce += parseInt($wrap.css('margin-top').replace('px', ''), 10);
390
- }
391
-
392
- if ($wrap.css('margin-bottom')) {
393
- height_to_reduce += parseInt($wrap.css('margin-bottom').replace('px', ''), 10);
394
- }
395
-
396
- var res = window_height - height_to_reduce - 130;
397
-
398
- return res > 620 ? res : 620;
399
- }
400
-
401
  });
5
  $staff = $('input.bookly-js-check-entity'),
6
  $showAll = $('input#bookly-check-all-entities'),
7
  firstHour = new Date().getHours(),
8
+ $staffButton = $('#bookly-staff-button'),
9
  staffMembers = [],
10
  staffIds = getCookie('bookly_cal_st_ids'),
11
  tabId = getCookie('bookly_cal_tab_id'),
12
  lastView = getCookie('bookly_cal_view'),
13
+ views = 'month,agendaWeek,agendaDay,multiStaffDay';
 
 
14
 
15
  if (views.indexOf(lastView) == -1) {
16
  lastView = 'multiStaffDay';
37
  }
38
  updateStaffButton();
39
 
40
+ /**
41
+ * Calculate height of FullCalendar.
42
+ *
43
+ * @return {number}
44
+ */
45
+ function heightFC() {
46
+ var window_height = $(window).height(),
47
+ wp_admin_bar_height = $('#wpadminbar').height(),
48
+ bookly_calendar_tabs_height = $('#bookly-fc-wrapper .tabbable').outerHeight(true),
49
+ height_to_reduce = wp_admin_bar_height + bookly_calendar_tabs_height,
50
+ $wrap = $('#wpbody-content .wrap');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ if ($wrap.css('margin-top')) {
53
+ height_to_reduce += parseInt($wrap.css('margin-top').replace('px', ''), 10);
54
+ }
55
 
56
+ if ($wrap.css('margin-bottom')) {
57
+ height_to_reduce += parseInt($wrap.css('margin-bottom').replace('px', ''), 10);
58
+ }
59
 
60
+ var res = window_height - height_to_reduce - 130;
 
61
 
62
+ return res > 620 ? res : 620;
63
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ var options = {
66
+ fullcalendar: {
67
+ // General Display.
68
+ header: {
69
+ left: 'prev,next today',
70
+ center: 'title',
71
+ right: views
72
+ },
73
+ height: heightFC(),
74
+ // Views.
75
+ defaultView: lastView,
76
+ scrollTime: firstHour + ':00:00',
77
+ views: {
78
+ agendaWeek: {
79
+ columnFormat: 'ddd, D'
80
+ },
81
+ multiStaffDay: {
82
+ staffMembers: staffMembers
83
  }
84
+ },
85
+ viewRender: function (view, element) {
86
+ setCookie('bookly_cal_view', view.type);
 
 
 
 
 
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  },
89
+ getCurrentStaffId: function () {
90
+ return $tabs.filter('.active').data('staff_id');
91
  },
92
+ getStaffMembers: function () {
93
+ return staffMembers;
94
+ },
95
+ l10n: BooklyL10n
96
+ };
97
+
98
+ var calendar = new BooklyCalendar($fullCalendar, options);
99
 
100
  $('.fc-agendaDay-button').addClass('fc-corner-right');
101
  if ($tabs.filter('.active').data('staff_id') == 0) {
104
  $('.fc-multiStaffDay-button').hide();
105
  }
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  $(window).on('resize', function () {
108
  $fullCalendar.fullCalendar('option', 'height', heightFC());
109
  });
113
  e.preventDefault();
114
  $tabs.removeClass('active');
115
  $(this).addClass('active');
 
116
  var staff_id = $(this).data('staff_id');
117
  setCookie('bookly_cal_tab_id', staff_id);
118
 
164
  }
165
  });
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  function updateStaffButton() {
168
  $showAll.prop('checked', $staff.filter(':not(:checked)').length == 0);
169
 
210
  return keyValue ? keyValue[2] : null;
211
  }
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  });
backend/modules/calendar/resources/js/ng-appointment_dialog.js CHANGED
@@ -20,12 +20,18 @@
20
  }
21
  },
22
  form : {
23
- screen : null,
24
- id : null,
25
- staff : null,
26
- service : null,
27
- date : null,
28
- repeat : {
 
 
 
 
 
 
29
  enabled : null,
30
  repeat : null,
31
  daily : { every : null },
@@ -34,28 +40,29 @@
34
  monthly : { on : null, day : null, weekday : null },
35
  until : null
36
  },
37
- schedule : {
38
  items : [],
39
  edit : null,
40
  page : null,
41
  another_time : []
42
  },
43
- start_time : null,
44
- end_time : null,
45
- customers : [],
46
- notification : null
 
 
 
47
  },
48
  loadData : function() {
49
  var deferred = $q.defer();
50
  if (!ds.loaded) {
51
  jQuery.get(
52
  ajaxurl,
53
- { action : 'bookly_get_data_for_appointment_form' },
54
  function(data) {
55
  ds.loaded = true;
56
  ds.data = data;
57
- // Add empty element to beginning of array for single-select customer form
58
- ds.data.customers.unshift({name: ''});
59
 
60
  if (data.staff.length) {
61
  ds.form.staff = data.staff[0];
@@ -96,6 +103,20 @@
96
  }
97
  return result;
98
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  findTime : function(source, date) {
100
  var result = null,
101
  value_to_find = $filter('date')(date, 'HH:mm'),
@@ -125,11 +146,12 @@
125
  customer.extras = [];
126
  customer.status = ds.data.status.default;
127
  customer.number_of_persons = 1;
 
128
  customer.compound_token = null;
129
- customer.location_id = null;
130
  customer.payment_id = null;
131
  customer.payment_type = null;
132
  customer.payment_title = null;
 
133
  });
134
  },
135
  getDataForEndTime : function() {
@@ -151,10 +173,7 @@
151
  setEndTimeBasedOnService : function() {
152
  var i = jQuery.inArray(ds.form.start_time, ds.data.start_time),
153
  d = ds.form.service ? ds.form.service.duration : ds.data.time_interval;
154
- if (ds.form.service && ds.form.service.duration == 86400) {
155
- ds.form.start_time = ds.data.start_time[0];
156
- ds.form.end_time = ds.data.end_time[ 86400 / ds.data.time_interval ];
157
- } else {
158
  if (i !== -1) {
159
  for (; i < ds.data.end_time.length; ++i) {
160
  d -= ds.data.time_interval;
@@ -167,18 +186,25 @@
167
  }
168
  },
169
  getStartAndEndDates : function() {
170
- var start_date = new Date(ds.form.date.getTime()),
171
- start_time = ds.form.start_time.value.split(':'),
172
- end_date = new Date(ds.form.date.getTime()),
 
 
 
 
 
 
173
  end_time = ds.form.end_time.value.split(':');
174
- start_date.setHours(start_time[0]);
175
- start_date.setMinutes(start_time[1]);
176
- end_date.setHours(end_time[0]);
177
- end_date.setMinutes(end_time[1]);
 
178
 
179
  return {
180
- start_date : $filter('date')(start_date, 'yyyy-MM-dd HH:mm:00'),
181
- end_date : $filter('date')(end_date, 'yyyy-MM-dd HH:mm:00')
182
  };
183
  },
184
  getTotalNumberOfPersons : function () {
@@ -189,10 +215,10 @@
189
 
190
  return result;
191
  },
192
- getTotalNumberOfNotCancelledPersons: function () {
193
  var result = 0;
194
  ds.form.customers.forEach(function (item) {
195
- if (item.status != 'cancelled') {
196
  result += parseInt(item.number_of_persons);
197
  }
198
  });
@@ -202,7 +228,7 @@
202
  getTotalNumberOfCancelledPersons: function () {
203
  var result = 0;
204
  ds.form.customers.forEach(function (item) {
205
- if (item.status == 'cancelled') {
206
  result += parseInt(item.number_of_persons);
207
  }
208
  });
@@ -226,7 +252,7 @@
226
  // Error messages.
227
  $scope.errors = {};
228
  // Callback to be called after editing appointment.
229
- var callback = null;
230
 
231
  /**
232
  * Prepare the form for new event.
@@ -236,16 +262,25 @@
236
  * @param function _callback
237
  */
238
  $scope.configureNewForm = function(staff_id, start_date, _callback) {
239
- var weekday = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][start_date.format('d')];
 
 
 
 
240
  jQuery.extend($scope.form, {
241
- screen : 'main',
242
- id : null,
243
- staff : dataSource.findStaff(staff_id),
244
- service : null,
245
- date : start_date.clone().local().toDate(),
246
- start_time : dataSource.findTime('start', start_date.format('HH:mm')),
247
- end_time : null,
248
- repeat : {
 
 
 
 
 
249
  enabled : 0,
250
  repeat : 'daily',
251
  daily : { every: 1 },
@@ -254,24 +289,24 @@
254
  monthly : { on : 'day', day : start_date.format('D'), weekday : weekday },
255
  until : start_date.clone().add(1, 'month').format('YYYY-MM-DD')
256
  },
257
- schedule : {
258
  items : [],
259
  edit : 0,
260
  page : 0,
261
  another_time : []
262
  },
263
- customers : [],
264
- notification : 'no',
265
- internal_note : null
266
  });
267
  $scope.errors = {};
268
  dataSource.setEndTimeBasedOnService();
269
  callback = _callback;
270
 
271
- $scope.reInitChosen();
272
  $scope.prepareExtras();
273
  $scope.prepareCustomFields();
274
  $scope.dataSource.resetCustomers();
 
275
  };
276
 
277
  /**
@@ -281,23 +316,28 @@
281
  $scope.loading = true;
282
  jQuery.post(
283
  ajaxurl,
284
- {action: 'bookly_get_data_for_appointment', id: appointment_id},
285
  function(response) {
286
  $scope.$apply(function($scope) {
287
  if (response.success) {
288
  var start_date = moment(response.data.start_date),
289
- end_date = moment(response.data.end_date);
 
290
  jQuery.extend($scope.form, {
291
- screen : 'main',
292
- id : appointment_id,
293
- staff : $scope.dataSource.findStaff(response.data.staff_id),
294
- service : $scope.dataSource.findService(response.data.staff_id, response.data.service_id),
295
- date : start_date.clone().local().toDate(),
296
- start_time : $scope.dataSource.findTime('start', start_date.format('HH:mm')),
297
- end_time : start_date.format('YYYY-MM-DD') == end_date.format('YYYY-MM-DD')
 
 
 
 
298
  ? $scope.dataSource.findTime('end', end_date.format('HH:mm'))
299
  : $scope.dataSource.findTime('end', (24 + end_date.hour()) + end_date.format(':mm')),
300
- repeat : {
301
  enabled : 0,
302
  repeat : 'daily',
303
  daily : { every: 1 },
@@ -312,15 +352,16 @@
312
  page : 0,
313
  another_time : []
314
  },
315
- customers : [],
316
- notification : 'no',
317
- internal_note : response.data.internal_note
 
318
  });
319
 
320
- $scope.reInitChosen();
321
  $scope.prepareExtras();
322
  $scope.prepareCustomFields();
323
  $scope.dataSource.resetCustomers();
 
324
 
325
  var customers_ids = [];
326
  response.data.customers.forEach(function (item, i, arr) {
@@ -334,11 +375,12 @@
334
  angular.copy(customer, clone);
335
  }
336
  clone.ca_id = item.ca_id;
 
337
  clone.extras = item.extras;
338
  clone.status = item.status;
339
  clone.custom_fields = item.custom_fields;
340
  clone.number_of_persons = item.number_of_persons;
341
- clone.location_id = item.location_id;
342
  clone.payment_id = item.payment_id;
343
  clone.payment_type = item.payment_type;
344
  clone.payment_title = item.payment_title;
@@ -356,51 +398,108 @@
356
  callback = _callback;
357
  };
358
 
359
- var checkTimeInterval = function() {
360
- var dates = $scope.dataSource.getStartAndEndDates();
361
- jQuery.post(
362
- ajaxurl,
363
- {
364
- action : 'bookly_check_appointment_date_selection',
365
- start_date : dates.start_date,
366
- end_date : dates.end_date,
367
- appointment_id : $scope.form.id,
368
- staff_id : $scope.form.staff ? $scope.form.staff.id : null,
369
- service_id : $scope.form.service ? $scope.form.service.id : null
370
- },
371
- function (response) {
372
- $scope.$apply(function ($scope) {
373
- $scope.errors = response;
 
 
 
 
 
 
 
374
  });
375
- },
376
- 'json'
377
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  };
379
 
380
  $scope.onServiceChange = function() {
381
  $scope.dataSource.setEndTimeBasedOnService();
382
- $scope.reInitChosen();
383
  $scope.prepareExtras();
384
  $scope.prepareCustomFields();
385
- checkTimeInterval();
386
  };
387
 
388
  $scope.onStaffChange = function() {
389
- $scope.form.service = null;
 
 
 
 
 
 
390
  };
391
 
392
  $scope.onStartTimeChange = function() {
393
  $scope.dataSource.setEndTimeBasedOnService();
394
- checkTimeInterval();
395
  };
396
 
397
  $scope.onEndTimeChange = function() {
398
- checkTimeInterval();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  };
400
 
401
  $scope.processForm = function() {
402
  $scope.loading = true;
403
 
 
 
404
  var dates = $scope.dataSource.getStartAndEndDates(),
405
  schedule = [],
406
  customers = []
@@ -427,26 +526,30 @@
427
  ca_id : item.ca_id,
428
  custom_fields : item.custom_fields,
429
  extras : customer_extras,
430
- location_id : item.location_id,
431
  number_of_persons : item.number_of_persons,
 
432
  status : item.status
433
  });
434
  });
435
-
436
  jQuery.post(
437
  ajaxurl,
438
  {
439
- action : 'bookly_save_appointment_form',
440
- id : $scope.form.id,
441
- staff_id : $scope.form.staff ? $scope.form.staff.id : null,
442
- service_id : $scope.form.service ? $scope.form.service.id : null,
443
- start_date : dates.start_date,
444
- end_date : dates.end_date,
445
- repeat : JSON.stringify($scope.form.repeat),
446
- schedule : schedule,
447
- customers : JSON.stringify(customers),
448
- notification : $scope.form.notification,
449
- internal_note : $scope.form.internal_note
 
 
 
 
 
450
  },
451
  function (response) {
452
  $scope.$apply(function($scope) {
@@ -473,16 +576,6 @@
473
  $element.children().modal('hide');
474
  };
475
 
476
- $scope.reInitChosen = function () {
477
- jQuery('#bookly-chosen')
478
- .chosen('destroy')
479
- .chosen({
480
- search_contains : true,
481
- width : '100%',
482
- max_selected_options: dataSource.form.service ? dataSource.form.service.capacity + dataSource.getTotalNumberOfCancelledPersons() : 0
483
- });
484
- };
485
-
486
  $scope.statusToString = function (status) {
487
  return dataSource.data.status.items[status];
488
  };
@@ -497,15 +590,22 @@
497
  */
498
  $scope.createCustomer = function(customer) {
499
  // Add new customer to the list.
 
 
 
 
 
 
 
500
  var new_customer = {
501
  id : customer.id.toString(),
502
- name : customer.name,
503
  custom_fields : customer.custom_fields,
504
  extras : customer.extras,
505
  status : customer.status,
506
- number_of_persons : 1,
 
507
  compound_token : null,
508
- location_id : null,
509
  payment_id : null,
510
  payment_type : null,
511
  payment_title : null
@@ -518,15 +618,19 @@
518
  dataSource.data.customers.push(new_customer);
519
 
520
  // Make it selected.
521
- if (!dataSource.form.service || dataSource.form.customers.length < dataSource.form.service.capacity) {
522
  dataSource.form.customers.push(new_customer);
523
  }
524
-
525
- setTimeout(function() { jQuery('#bookly-chosen').trigger('chosen:updated'); }, 0);
526
  };
527
 
528
  $scope.removeCustomer = function(customer) {
529
  $scope.form.customers.splice($scope.form.customers.indexOf(customer), 1);
 
 
 
 
 
 
530
  };
531
 
532
  /**************************************************************************************************************
@@ -535,27 +639,27 @@
535
 
536
  $scope.editCustomerDetails = function(customer) {
537
  var $dialog = jQuery('#bookly-customer-details-dialog');
538
- $dialog.find('input.ab-custom-field:text, textarea.ab-custom-field, select.ab-custom-field').val('');
539
- $dialog.find('input.ab-custom-field:checkbox, input.ab-custom-field:radio').prop('checked', false);
540
  $dialog.find('#bookly-extras :checkbox').prop('checked', false);
541
 
542
  customer.custom_fields.forEach(function (field) {
543
- var $custom_field = $dialog.find('#ab--custom-fields > *[data-id="' + field.id + '"]');
544
  switch ($custom_field.data('type')) {
545
  case 'checkboxes':
546
  field.value.forEach(function (value) {
547
- $custom_field.find('.ab-custom-field').filter(function () {
548
  return this.value == value;
549
  }).prop('checked', true);
550
  });
551
  break;
552
  case 'radio-buttons':
553
- $custom_field.find('.ab-custom-field').filter(function () {
554
  return this.value == field.value;
555
  }).prop('checked', true);
556
  break;
557
  default:
558
- $custom_field.find('.ab-custom-field').val(field.value);
559
  break;
560
  }
561
  });
@@ -566,10 +670,12 @@
566
  });
567
 
568
  // Prepare select for number of persons.
569
- var $number_of_persons = $dialog.find('#ab-edit-number-of-persons');
570
 
571
  var max = $scope.form.service
572
- ? parseInt($scope.form.service.capacity) - $scope.dataSource.getTotalNumberOfNotCancelledPersons() + ( customer.status != 'cancelled' ? parseInt(customer.number_of_persons) : 0 )
 
 
573
  : 1;
574
  $number_of_persons.empty();
575
  for (var i = 1; i <= max; ++i) {
@@ -579,12 +685,12 @@
579
  $number_of_persons.append('<option value="' + customer.number_of_persons + '">' + customer.number_of_persons + '</option>');
580
  }
581
  $number_of_persons.val(customer.number_of_persons);
582
- $dialog.find('#ab-appointment-status').val(customer.status);
583
- $dialog.find('#ab-appointment-location').val(customer.location_id);
584
- $dialog.find('#ab-deposit-due').val(customer.due);
585
  $scope.edit_customer = customer;
586
 
587
- $dialog.modal({show: true, backdrop: false})
588
  .on('hidden.bs.modal', function () {
589
  jQuery('body').addClass('modal-open');
590
  });
@@ -609,7 +715,7 @@
609
  $scope.prepareCustomFields = function () {
610
  if (BooklyL10nAppDialog.cf_per_service == 1) {
611
  var show = false;
612
- jQuery('#ab--custom-fields div[data-services]').each(function() {
613
  var $this = jQuery(this);
614
  if (dataSource.form.service !== null) {
615
  var services = $this.data('services');
@@ -624,9 +730,9 @@
624
  }
625
  });
626
  if (show) {
627
- jQuery('#ab--custom-fields').show();
628
  } else {
629
- jQuery('#ab--custom-fields').hide();
630
  }
631
  }
632
  };
@@ -634,8 +740,11 @@
634
  $scope.saveCustomFields = function() {
635
  var result = [],
636
  extras = {},
637
- $fields = jQuery('#ab--custom-fields > *'),
638
- $number_of_persons = jQuery('#bookly-customer-details-dialog #ab-edit-number-of-persons')
 
 
 
639
  ;
640
 
641
  $fields.each(function () {
@@ -645,15 +754,15 @@
645
  switch ($this.data('type')) {
646
  case 'checkboxes':
647
  value = [];
648
- $this.find('.ab-custom-field:checked').each(function () {
649
  value.push(this.value);
650
  });
651
  break;
652
  case 'radio-buttons':
653
- value = $this.find('.ab-custom-field:checked').val();
654
  break;
655
  default:
656
- value = $this.find('.ab-custom-field').val();
657
  break;
658
  }
659
  result.push({id: $this.data('id'), value: value});
@@ -661,20 +770,24 @@
661
  });
662
 
663
  if ($scope.form.service) {
664
- jQuery('#bookly-extras .service_' + $scope.form.service.id + ' input.extras-count').each(function () {
665
  if (this.value > 0) {
666
  extras[jQuery(this).data('id')] = this.value;
667
  }
668
  });
669
  }
670
 
671
- $scope.edit_customer.custom_fields = result;
672
  $scope.edit_customer.number_of_persons = $number_of_persons.val();
673
- $scope.edit_customer.location_id = jQuery('#bookly-customer-details-dialog #ab-appointment-location').val();
 
674
  $scope.edit_customer.extras = extras;
675
- $scope.edit_customer.status = jQuery('#bookly-customer-details-dialog #ab-appointment-status').val();
676
 
677
  jQuery('#bookly-customer-details-dialog').modal('hide');
 
 
 
 
678
  };
679
 
680
  /**************************************************************************************************************
@@ -690,6 +803,91 @@
690
  });
691
  };
692
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
  /**************************************************************************************************************
694
  * Schedule of Recurring Appointments *
695
  **************************************************************************************************************/
@@ -713,14 +911,16 @@
713
  jQuery.post(
714
  ajaxurl,
715
  {
716
- action : 'bookly_recurring_appointments_get_schedule',
717
- staff_id : $scope.form.staff.id,
718
- service_id : $scope.form.service.id,
719
- datetime : dates.start_date,
720
- until : $scope.form.repeat.until,
721
- repeat : $scope.form.repeat.repeat,
722
- params : $scope.form.repeat[$scope.form.repeat.repeat],
723
- extras : extras
 
 
724
  },
725
  function (response) {
726
  $scope.$apply(function($scope) {
@@ -749,7 +949,7 @@
749
  month = m.format('M'),
750
  day = m.format('DD');
751
 
752
- return BooklyL10nAppDialog.calendar.shortDays[weekday] + ', ' + BooklyL10nAppDialog.calendar.shortMonths[month-1] + ' ' + day;
753
  };
754
  $scope.schFormatTime = function(slots, options) {
755
  for (var i = 0; i < options.length; ++ i) {
@@ -790,6 +990,7 @@
790
  }
791
  // copy weekly to biweekly
792
  $scope.form.repeat.biweekly.on = $scope.form.repeat.weekly.on.slice();
 
793
  };
794
  $scope.schOnDateChange = function(item) {
795
  var extras = [];
@@ -807,6 +1008,7 @@
807
  ajaxurl,
808
  {
809
  action : 'bookly_recurring_appointments_get_schedule',
 
810
  staff_id : $scope.form.staff.id,
811
  service_id : $scope.form.service.id,
812
  datetime : item.date + ' 00:00',
@@ -844,25 +1046,18 @@
844
  return item.deleted;
845
  });
846
  };
847
- $scope.schDateOptions = {
848
- dateFormat : 'D, M dd, yy',
849
- dayNamesMin : BooklyL10nAppDialog.calendar.shortDays,
850
- dayNamesShort : BooklyL10nAppDialog.calendar.shortDays,
851
- monthNames : BooklyL10nAppDialog.calendar.longMonths,
852
- monthNamesShort : BooklyL10nAppDialog.calendar.shortMonths,
853
- firstDay : BooklyL10nAppDialog.startOfWeek
854
  };
855
 
856
  /**
857
  * Datepicker options.
858
  */
859
- $scope.dateOptions = {
860
- dateFormat : BooklyL10nAppDialog.dpDateFormat,
861
- dayNamesMin : BooklyL10nAppDialog.calendar.shortDays,
862
- monthNames : BooklyL10nAppDialog.calendar.longMonths,
863
- monthNamesShort : BooklyL10nAppDialog.calendar.shortMonths,
864
- firstDay : BooklyL10nAppDialog.startOfWeek
865
- };
866
  });
867
 
868
  /**
@@ -882,30 +1077,6 @@
882
  };
883
  });
884
 
885
- /**
886
- * Directive for chosen.
887
- */
888
- module.directive('chosen',function($timeout) {
889
- var linker = function(scope,element,attrs) {
890
- scope.$watch(attrs['chosen'], function() {
891
- element.trigger('chosen:updated');
892
- });
893
-
894
- scope.$watchCollection(attrs['ngModel'], function() {
895
- $timeout(function() {
896
- element.trigger('chosen:updated');
897
- });
898
- });
899
-
900
- scope.reInitChosen();
901
- };
902
-
903
- return {
904
- restrict:'A',
905
- link: linker
906
- };
907
- });
908
-
909
  /**
910
  * Directive for Popover jQuery plugin.
911
  */
@@ -941,6 +1112,15 @@
941
  return input;
942
  };
943
  });
 
 
 
 
 
 
 
 
 
944
  })();
945
 
946
  /**
20
  }
21
  },
22
  form : {
23
+ screen : null,
24
+ id : null,
25
+ staff : null,
26
+ staff_any : null,
27
+ service : null,
28
+ custom_service_name : null,
29
+ custom_service_price : null,
30
+ location : null,
31
+ date : null,
32
+ start_time : null,
33
+ end_time : null,
34
+ repeat : {
35
  enabled : null,
36
  repeat : null,
37
  daily : { every : null },
40
  monthly : { on : null, day : null, weekday : null },
41
  until : null
42
  },
43
+ schedule : {
44
  items : [],
45
  edit : null,
46
  page : null,
47
  another_time : []
48
  },
49
+ customers : [],
50
+ notification : null,
51
+ series_id : null,
52
+ expand_customers_list : false
53
+ },
54
+ l10n : {
55
+ staff_any: BooklyL10nAppDialog.staff_any
56
  },
57
  loadData : function() {
58
  var deferred = $q.defer();
59
  if (!ds.loaded) {
60
  jQuery.get(
61
  ajaxurl,
62
+ { action : 'bookly_get_data_for_appointment_form', csrf_token : BooklyL10nAppDialog.csrf_token },
63
  function(data) {
64
  ds.loaded = true;
65
  ds.data = data;
 
 
66
 
67
  if (data.staff.length) {
68
  ds.form.staff = data.staff[0];
103
  }
104
  return result;
105
  },
106
+ findLocation : function(staff_id, id) {
107
+ var result = null,
108
+ staff = ds.findStaff(staff_id);
109
+
110
+ if (staff !== null) {
111
+ jQuery.each(staff.locations, function(key, item) {
112
+ if (item.id == id) {
113
+ result = item;
114
+ return false;
115
+ }
116
+ });
117
+ }
118
+ return result;
119
+ },
120
  findTime : function(source, date) {
121
  var result = null,
122
  value_to_find = $filter('date')(date, 'HH:mm'),
146
  customer.extras = [];
147
  customer.status = ds.data.status.default;
148
  customer.number_of_persons = 1;
149
+ customer.notes = null;
150
  customer.compound_token = null;
 
151
  customer.payment_id = null;
152
  customer.payment_type = null;
153
  customer.payment_title = null;
154
+ customer.package_id = null;
155
  });
156
  },
157
  getDataForEndTime : function() {
173
  setEndTimeBasedOnService : function() {
174
  var i = jQuery.inArray(ds.form.start_time, ds.data.start_time),
175
  d = ds.form.service ? ds.form.service.duration : ds.data.time_interval;
176
+ if (d < 86400) {
 
 
 
177
  if (i !== -1) {
178
  for (; i < ds.data.end_time.length; ++i) {
179
  d -= ds.data.time_interval;
186
  }
187
  },
188
  getStartAndEndDates : function() {
189
+ var start_date = moment(ds.form.date.getTime()),
190
+ end_date = moment(ds.form.date.getTime()),
191
+ start_time = [0,0],
192
+ end_time = [0,0]
193
+ ;
194
+ if (ds.form.service && ds.form.service.duration >= 86400) {
195
+ end_date.add(ds.form.service.duration, 'seconds');
196
+ } else {
197
+ start_time = ds.form.start_time.value.split(':');
198
  end_time = ds.form.end_time.value.split(':');
199
+ }
200
+ start_date.hours(start_time[0]);
201
+ start_date.minutes(start_time[1]);
202
+ end_date.hours(end_time[0]);
203
+ end_date.minutes(end_time[1]);
204
 
205
  return {
206
+ start_date : start_date.format('YYYY-MM-DD HH:mm:00'),
207
+ end_date : end_date.format('YYYY-MM-DD HH:mm:00')
208
  };
209
  },
210
  getTotalNumberOfPersons : function () {
215
 
216
  return result;
217
  },
218
+ getTotalNumberOfNotCancelledPersons: function (exceptCustomer) {
219
  var result = 0;
220
  ds.form.customers.forEach(function (item) {
221
+ if ((!exceptCustomer || item.id != exceptCustomer.id) && item.status != 'cancelled' && item.status != 'rejected' && item.status != 'waitlisted') {
222
  result += parseInt(item.number_of_persons);
223
  }
224
  });
228
  getTotalNumberOfCancelledPersons: function () {
229
  var result = 0;
230
  ds.form.customers.forEach(function (item) {
231
+ if (item.status == 'cancelled' || item.status == 'rejected' || item.status == 'waitlisted') {
232
  result += parseInt(item.number_of_persons);
233
  }
234
  });
252
  // Error messages.
253
  $scope.errors = {};
254
  // Callback to be called after editing appointment.
255
+ var callback = null;
256
 
257
  /**
258
  * Prepare the form for new event.
262
  * @param function _callback
263
  */
264
  $scope.configureNewForm = function(staff_id, start_date, _callback) {
265
+ var weekday = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][start_date.format('d')],
266
+ staff = dataSource.findStaff(staff_id),
267
+ service = staff && staff.services.length == 2 ? staff.services[1] : null,
268
+ location = staff && staff.locations.length == 1 ? staff.locations[0] : null
269
+ ;
270
  jQuery.extend($scope.form, {
271
+ screen : 'main',
272
+ id : null,
273
+ staff : staff,
274
+ staff_any : null,
275
+ service : service,
276
+ custom_service_name : null,
277
+ custom_service_price : 0,
278
+ location : location,
279
+ date : start_date.clone().local().toDate(),
280
+ start_time : dataSource.findTime('start', start_date.format('HH:mm')),
281
+ end_time : null,
282
+ series_id : null,
283
+ repeat : {
284
  enabled : 0,
285
  repeat : 'daily',
286
  daily : { every: 1 },
289
  monthly : { on : 'day', day : start_date.format('D'), weekday : weekday },
290
  until : start_date.clone().add(1, 'month').format('YYYY-MM-DD')
291
  },
292
+ schedule : {
293
  items : [],
294
  edit : 0,
295
  page : 0,
296
  another_time : []
297
  },
298
+ customers : [],
299
+ internal_note : null,
300
+ expand_customers_list : false
301
  });
302
  $scope.errors = {};
303
  dataSource.setEndTimeBasedOnService();
304
  callback = _callback;
305
 
 
306
  $scope.prepareExtras();
307
  $scope.prepareCustomFields();
308
  $scope.dataSource.resetCustomers();
309
+ $scope.onRepeatChange();
310
  };
311
 
312
  /**
316
  $scope.loading = true;
317
  jQuery.post(
318
  ajaxurl,
319
+ {action: 'bookly_get_data_for_appointment', id: appointment_id, csrf_token : BooklyL10nAppDialog.csrf_token},
320
  function(response) {
321
  $scope.$apply(function($scope) {
322
  if (response.success) {
323
  var start_date = moment(response.data.start_date),
324
+ end_date = moment(response.data.end_date),
325
+ staff = $scope.dataSource.findStaff(response.data.staff_id);
326
  jQuery.extend($scope.form, {
327
+ screen : 'main',
328
+ id : appointment_id,
329
+ staff : staff,
330
+ staff_any : response.data.staff_any ? staff : null,
331
+ service : $scope.dataSource.findService(response.data.staff_id, response.data.service_id),
332
+ custom_service_name : response.data.custom_service_name,
333
+ custom_service_price : response.data.custom_service_price,
334
+ location : $scope.dataSource.findLocation(response.data.staff_id, response.data.location_id),
335
+ date : start_date.clone().local().toDate(),
336
+ start_time : $scope.dataSource.findTime('start', start_date.format('HH:mm')),
337
+ end_time : start_date.format('YYYY-MM-DD') == end_date.format('YYYY-MM-DD')
338
  ? $scope.dataSource.findTime('end', end_date.format('HH:mm'))
339
  : $scope.dataSource.findTime('end', (24 + end_date.hour()) + end_date.format(':mm')),
340
+ repeat : {
341
  enabled : 0,
342
  repeat : 'daily',
343
  daily : { every: 1 },
352
  page : 0,
353
  another_time : []
354
  },
355
+ customers : [],
356
+ internal_note : response.data.internal_note,
357
+ series_id : response.data.series_id,
358
+ expand_customers_list : false
359
  });
360
 
 
361
  $scope.prepareExtras();
362
  $scope.prepareCustomFields();
363
  $scope.dataSource.resetCustomers();
364
+ $scope.onRepeatChange();
365
 
366
  var customers_ids = [];
367
  response.data.customers.forEach(function (item, i, arr) {
375
  angular.copy(customer, clone);
376
  }
377
  clone.ca_id = item.ca_id;
378
+ clone.package_id = item.package_id;
379
  clone.extras = item.extras;
380
  clone.status = item.status;
381
  clone.custom_fields = item.custom_fields;
382
  clone.number_of_persons = item.number_of_persons;
383
+ clone.notes = item.notes;
384
  clone.payment_id = item.payment_id;
385
  clone.payment_type = item.payment_type;
386
  clone.payment_title = item.payment_title;
398
  callback = _callback;
399
  };
400
 
401
+ var checkAppointmentErrors = function() {
402
+ if ($scope.form.staff) {
403
+ var dates = $scope.dataSource.getStartAndEndDates(),
404
+ customers = [];
405
+
406
+ $scope.form.customers.forEach(function (item, i, arr) {
407
+ var customer_extras = {};
408
+ if ($scope.form.service) {
409
+ jQuery('#bookly-extras .service_' + $scope.form.service.id + ' input.extras-count').each(function () {
410
+ var extra_id = jQuery(this).data('id');
411
+ if (item.extras[extra_id] !== undefined) {
412
+ customer_extras[extra_id] = item.extras[extra_id];
413
+ }
414
+ });
415
+ }
416
+ customers.push({
417
+ id: item.id,
418
+ ca_id: item.ca_id,
419
+ custom_fields: item.custom_fields,
420
+ extras: customer_extras,
421
+ number_of_persons: item.number_of_persons,
422
+ status: item.status
423
  });
424
+ });
425
+
426
+ jQuery.post(
427
+ ajaxurl,
428
+ {
429
+ action : 'bookly_check_appointment_errors',
430
+ csrf_token : BooklyL10nAppDialog.csrf_token,
431
+ start_date : dates.start_date,
432
+ end_date : dates.end_date,
433
+ appointment_id : $scope.form.id,
434
+ customers : JSON.stringify(customers),
435
+ staff_id : $scope.form.staff.id,
436
+ service_id : $scope.form.service ? $scope.form.service.id : null
437
+ },
438
+ function (response) {
439
+ $scope.$apply(function ($scope) {
440
+ angular.forEach(response, function (value, error) {
441
+ $scope.errors[error] = value;
442
+ });
443
+ });
444
+ },
445
+ 'json'
446
+ );
447
+ }
448
  };
449
 
450
  $scope.onServiceChange = function() {
451
  $scope.dataSource.setEndTimeBasedOnService();
 
452
  $scope.prepareExtras();
453
  $scope.prepareCustomFields();
454
+ checkAppointmentErrors();
455
  };
456
 
457
  $scope.onStaffChange = function() {
458
+ if ($scope.form.staff.services.length == 2) {
459
+ $scope.form.service = $scope.form.staff.services[1];
460
+ $scope.onServiceChange();
461
+ } else {
462
+ $scope.form.service = null;
463
+ }
464
+ $scope.form.location = $scope.form.staff.locations.length == 1 ? $scope.form.staff.locations[0] : null;
465
  };
466
 
467
  $scope.onStartTimeChange = function() {
468
  $scope.dataSource.setEndTimeBasedOnService();
469
+ checkAppointmentErrors();
470
  };
471
 
472
  $scope.onEndTimeChange = function() {
473
+ checkAppointmentErrors();
474
+ };
475
+
476
+ $scope.onDateChange = function() {
477
+ checkAppointmentErrors();
478
+ $scope.onRepeatChange();
479
+ };
480
+
481
+ $scope.onCustomersChange = function(old_customers, old_nop) {
482
+ if (dataSource.form.service && dataSource.form.customers.length > old_customers.length) {
483
+ var ids = jQuery.map(old_customers, function(customer) {
484
+ return customer.id;
485
+ });
486
+ var nop = dataSource.form.service.capacity_min - old_nop;
487
+ dataSource.form.customers.some(function (item) {
488
+ if (jQuery.inArray(item.id, ids) == -1) {
489
+ item.number_of_persons = nop > 0 ? nop : 1;
490
+ return true;
491
+ }
492
+ });
493
+ }
494
+ $scope.errors.customers_appointments_limit = [];
495
+ checkAppointmentErrors();
496
  };
497
 
498
  $scope.processForm = function() {
499
  $scope.loading = true;
500
 
501
+ $scope.errors = {};
502
+
503
  var dates = $scope.dataSource.getStartAndEndDates(),
504
  schedule = [],
505
  customers = []
526
  ca_id : item.ca_id,
527
  custom_fields : item.custom_fields,
528
  extras : customer_extras,
 
529
  number_of_persons : item.number_of_persons,
530
+ notes : item.notes,
531
  status : item.status
532
  });
533
  });
 
534
  jQuery.post(
535
  ajaxurl,
536
  {
537
+ action : 'bookly_save_appointment_form',
538
+ csrf_token : BooklyL10nAppDialog.csrf_token,
539
+ id : $scope.form.id || undefined,
540
+ staff_id : $scope.form.staff ? $scope.form.staff.id : undefined,
541
+ service_id : $scope.form.service ? $scope.form.service.id : undefined,
542
+ custom_service_name : $scope.form.custom_service_name,
543
+ custom_service_price : $scope.form.custom_service_price,
544
+ location_id : $scope.form.location ? $scope.form.location.id : undefined,
545
+ start_date : dates.start_date,
546
+ end_date : dates.end_date,
547
+ repeat : JSON.stringify($scope.form.repeat),
548
+ schedule : schedule,
549
+ customers : JSON.stringify(customers),
550
+ notification : $scope.form.notification,
551
+ internal_note : $scope.form.internal_note,
552
+ created_from : typeof BooklySCCalendarL10n !== 'undefined' ? 'staff-cabinet' : 'backend'
553
  },
554
  function (response) {
555
  $scope.$apply(function($scope) {
576
  $element.children().modal('hide');
577
  };
578
 
 
 
 
 
 
 
 
 
 
 
579
  $scope.statusToString = function (status) {
580
  return dataSource.data.status.items[status];
581
  };
590
  */
591
  $scope.createCustomer = function(customer) {
592
  // Add new customer to the list.
593
+ var nop = 1;
594
+ if (dataSource.form.service) {
595
+ nop = dataSource.form.service.capacity_min - dataSource.getTotalNumberOfNotCancelledPersons();
596
+ if (nop < 1) {
597
+ nop = 1;
598
+ }
599
+ }
600
  var new_customer = {
601
  id : customer.id.toString(),
602
+ name : customer.full_name,
603
  custom_fields : customer.custom_fields,
604
  extras : customer.extras,
605
  status : customer.status,
606
+ number_of_persons : nop,
607
+ notes : null,
608
  compound_token : null,
 
609
  payment_id : null,
610
  payment_type : null,
611
  payment_title : null
618
  dataSource.data.customers.push(new_customer);
619
 
620
  // Make it selected.
621
+ if (!dataSource.form.service || dataSource.form.customers.length < dataSource.form.service.capacity_max) {
622
  dataSource.form.customers.push(new_customer);
623
  }
 
 
624
  };
625
 
626
  $scope.removeCustomer = function(customer) {
627
  $scope.form.customers.splice($scope.form.customers.indexOf(customer), 1);
628
+ checkAppointmentErrors();
629
+ };
630
+
631
+ $scope.openNewCustomerDialog = function() {
632
+ var $dialog = jQuery('#bookly-customer-dialog');
633
+ $dialog.modal({show: true});
634
  };
635
 
636
  /**************************************************************************************************************
639
 
640
  $scope.editCustomerDetails = function(customer) {
641
  var $dialog = jQuery('#bookly-customer-details-dialog');
642
+ $dialog.find('input.bookly-custom-field:text, textarea.bookly-custom-field, select.bookly-custom-field').val('');
643
+ $dialog.find('input.bookly-custom-field:checkbox, input.bookly-custom-field:radio').prop('checked', false);
644
  $dialog.find('#bookly-extras :checkbox').prop('checked', false);
645
 
646
  customer.custom_fields.forEach(function (field) {
647
+ var $custom_field = $dialog.find('#bookly-js-custom-fields > *[data-id="' + field.id + '"]');
648
  switch ($custom_field.data('type')) {
649
  case 'checkboxes':
650
  field.value.forEach(function (value) {
651
+ $custom_field.find('.bookly-custom-field').filter(function () {
652
  return this.value == value;
653
  }).prop('checked', true);
654
  });
655
  break;
656
  case 'radio-buttons':
657
+ $custom_field.find('.bookly-custom-field').filter(function () {
658
  return this.value == field.value;
659
  }).prop('checked', true);
660
  break;
661
  default:
662
+ $custom_field.find('.bookly-custom-field').val(field.value);
663
  break;
664
  }
665
  });
670
  });
671
 
672
  // Prepare select for number of persons.
673
+ var $number_of_persons = $dialog.find('#bookly-number-of-persons');
674
 
675
  var max = $scope.form.service
676
+ ? ($scope.form.service.id
677
+ ? parseInt($scope.form.service.capacity_max) - $scope.dataSource.getTotalNumberOfNotCancelledPersons(customer)
678
+ : 1)
679
  : 1;
680
  $number_of_persons.empty();
681
  for (var i = 1; i <= max; ++i) {
685
  $number_of_persons.append('<option value="' + customer.number_of_persons + '">' + customer.number_of_persons + '</option>');
686
  }
687
  $number_of_persons.val(customer.number_of_persons);
688
+ $dialog.find('#bookly-appointment-status').val(customer.status);
689
+ $dialog.find('#bookly-appointment-notes').val(customer.notes);
690
+ $dialog.find('#bookly-deposit-due').val(customer.due);
691
  $scope.edit_customer = customer;
692
 
693
+ $dialog.modal({show: true})
694
  .on('hidden.bs.modal', function () {
695
  jQuery('body').addClass('modal-open');
696
  });
715
  $scope.prepareCustomFields = function () {
716
  if (BooklyL10nAppDialog.cf_per_service == 1) {
717
  var show = false;
718
+ jQuery('#bookly-js-custom-fields div[data-services]').each(function() {
719
  var $this = jQuery(this);
720
  if (dataSource.form.service !== null) {
721
  var services = $this.data('services');
730
  }
731
  });
732
  if (show) {
733
+ jQuery('#bookly-js-custom-fields').show();
734
  } else {
735
+ jQuery('#bookly-js-custom-fields').hide();
736
  }
737
  }
738
  };
740
  $scope.saveCustomFields = function() {
741
  var result = [],
742
  extras = {},
743
+ $fields = jQuery('#bookly-js-custom-fields > *'),
744
+ $status = jQuery('#bookly-appointment-status'),
745
+ $number_of_persons = jQuery('#bookly-number-of-persons'),
746
+ $notes = jQuery('#bookly-appointment-notes'),
747
+ $extras = jQuery('#bookly-extras')
748
  ;
749
 
750
  $fields.each(function () {
754
  switch ($this.data('type')) {
755
  case 'checkboxes':
756
  value = [];
757
+ $this.find('.bookly-custom-field:checked').each(function () {
758
  value.push(this.value);
759
  });
760
  break;
761
  case 'radio-buttons':
762
+ value = $this.find('.bookly-custom-field:checked').val();
763
  break;
764
  default:
765
+ value = $this.find('.bookly-custom-field').val();
766
  break;
767
  }
768
  result.push({id: $this.data('id'), value: value});
770
  });
771
 
772
  if ($scope.form.service) {
773
+ $extras.find(' .service_' + $scope.form.service.id + ' input.extras-count').each(function () {
774
  if (this.value > 0) {
775
  extras[jQuery(this).data('id')] = this.value;
776
  }
777
  });
778
  }
779
 
780
+ $scope.edit_customer.status = $status.val();
781
  $scope.edit_customer.number_of_persons = $number_of_persons.val();
782
+ $scope.edit_customer.notes = $notes.val();
783
+ $scope.edit_customer.custom_fields = result;
784
  $scope.edit_customer.extras = extras;
 
785
 
786
  jQuery('#bookly-customer-details-dialog').modal('hide');
787
+ if ($extras.length > 0) {
788
+ // Check if intersection with another appointment exists.
789
+ checkAppointmentErrors();
790
+ }
791
  };
792
 
793
  /**************************************************************************************************************
803
  });
804
  };
805
 
806
+ /**************************************************************************************************************
807
+ * Package Schedule *
808
+ **************************************************************************************************************/
809
+
810
+ $scope.editPackageSchedule = function(customer) {
811
+ jQuery(document.body).trigger('bookly_packages.schedule_dialog', [customer.package_id, function (deleted) {
812
+ if (jQuery.inArray(Number(customer.ca_id), deleted) != -1) {
813
+ $scope.removeCustomer(customer);
814
+ }
815
+ if (callback) {
816
+ // Call callback.
817
+ callback('refresh');
818
+ }
819
+ }, true]);
820
+ }
821
+
822
+ /**************************************************************************************************************
823
+ * Repeat Times in Recurring Appointments *
824
+ **************************************************************************************************************/
825
+ $scope.isDateMatchesSelections = function (current_date) {
826
+ switch ($scope.form.repeat.repeat) {
827
+ case 'daily':
828
+ if (($scope.form.repeat.daily.every > 6 || jQuery.inArray(current_date.format('ddd').toLowerCase(), $scope.dataSource.data.week_days) != -1) && (current_date.diff(moment($scope.dataSource.form.date.getTime()), 'days') % $scope.form.repeat.daily.every == 0)) {
829
+ return true;
830
+ }
831
+ break;
832
+ case 'weekly':
833
+ case 'biweekly':
834
+ if (($scope.form.repeat.repeat == 'weekly' || current_date.diff(moment($scope.dataSource.form.date.getTime()).startOf('isoWeek'), 'weeks') % 2 == 0) && (jQuery.inArray(current_date.format('ddd').toLowerCase(), $scope.form.repeat.weekly.on) != -1)) {
835
+ return true;
836
+ }
837
+ break;
838
+ case 'monthly':
839
+ switch ($scope.form.repeat.monthly.on) {
840
+ case 'day':
841
+ if (current_date.format('D') == $scope.form.repeat.monthly.day) {
842
+ return true;
843
+ }
844
+ break;
845
+ case 'last':
846
+ if (current_date.format('ddd').toLowerCase() == $scope.form.repeat.monthly.weekday && current_date.clone().endOf('month').diff(current_date, 'days') < 7) {
847
+ return true;
848
+ }
849
+ break;
850
+ default:
851
+ var month_diff = current_date.diff(current_date.clone().startOf('month'), 'days'),
852
+ weeks = ['first', 'second', 'third', 'fourth'],
853
+ week_number = weeks.indexOf($scope.form.repeat.monthly.on);
854
+
855
+ if (current_date.format('ddd').toLowerCase() == $scope.form.repeat.monthly.weekday && month_diff >= week_number * 7 && month_diff < (week_number + 1) * 7) {
856
+ return true;
857
+ }
858
+ }
859
+ break;
860
+ }
861
+
862
+ return false;
863
+ };
864
+ $scope.onRepeatChange = function () {
865
+ if (jQuery('#bookly-repeat-enabled').length) {
866
+ var number_of_times = 0,
867
+ date_until = moment($scope.form.repeat.until).add(1, 'days'),
868
+ current_date = moment($scope.dataSource.form.date.getTime());
869
+ do {
870
+ if ($scope.isDateMatchesSelections(current_date)) {
871
+ number_of_times++
872
+ }
873
+ current_date.add(1, 'days');
874
+ } while (current_date.isBefore(date_until));
875
+ $scope.form.repeat.times = number_of_times;
876
+ }
877
+ };
878
+ $scope.onRepeatChangeTimes = function () {
879
+ var number_of_times = 0,
880
+ date_until = moment($scope.dataSource.form.date.getTime()).add(5, 'years'),
881
+ current_date = moment($scope.dataSource.form.date.getTime());
882
+ do {
883
+ if ($scope.isDateMatchesSelections(current_date)) {
884
+ number_of_times++
885
+ }
886
+ current_date.add(1, 'days');
887
+ } while (number_of_times < $scope.form.repeat.times && current_date.isBefore(date_until));
888
+ $scope.form.repeat.until = current_date.subtract(1, 'days').format('YYYY-MM-DD');
889
+ };
890
+
891
  /**************************************************************************************************************
892
  * Schedule of Recurring Appointments *
893
  **************************************************************************************************************/
911
  jQuery.post(
912
  ajaxurl,
913
  {
914
+ action : 'bookly_recurring_appointments_get_schedule',
915
+ csrf_token : BooklyL10nAppDialog.csrf_token,
916
+ staff_id : $scope.form.staff.id,
917
+ service_id : $scope.form.service.id,
918
+ location_id : $scope.form.location ? $scope.form.location.id : null,
919
+ datetime : dates.start_date,
920
+ until : $scope.form.repeat.until,
921
+ repeat : $scope.form.repeat.repeat,
922
+ params : $scope.form.repeat[$scope.form.repeat.repeat],
923
+ extras : extras
924
  },
925
  function (response) {
926
  $scope.$apply(function($scope) {
949
  month = m.format('M'),
950
  day = m.format('DD');
951
 
952
+ return BooklyL10nAppDialog.dateOptions.dayNamesMin[weekday] + ', ' + BooklyL10nAppDialog.dateOptions.monthNamesShort[month-1] + ' ' + day;
953
  };
954
  $scope.schFormatTime = function(slots, options) {
955
  for (var i = 0; i < options.length; ++ i) {
990
  }
991
  // copy weekly to biweekly
992
  $scope.form.repeat.biweekly.on = $scope.form.repeat.weekly.on.slice();
993
+ $scope.onRepeatChange();
994
  };
995
  $scope.schOnDateChange = function(item) {
996
  var extras = [];
1008
  ajaxurl,
1009
  {
1010
  action : 'bookly_recurring_appointments_get_schedule',
1011
+ csrf_token : BooklyL10nAppDialog.csrf_token,
1012
  staff_id : $scope.form.staff.id,
1013
  service_id : $scope.form.service.id,
1014
  datetime : item.date + ' 00:00',
1046
  return item.deleted;
1047
  });
1048
  };
1049
+ $scope.schDateOptions = jQuery.extend({}, BooklyL10nAppDialog.dateOptions, {dateFormat: 'D, M dd, yy'});
1050
+ $scope.schViewSeries = function () {
1051
+ jQuery(document.body).trigger( 'recurring_appointments.series_dialog', [ $scope.form.series_id, function (event) {
1052
+ // Switch to the event owner tab.
1053
+ jQuery('li[data-staff_id=' + event.staffId + ']').click();
1054
+ } ] );
 
1055
  };
1056
 
1057
  /**
1058
  * Datepicker options.
1059
  */
1060
+ $scope.dateOptions = BooklyL10nAppDialog.dateOptions;
 
 
 
 
 
 
1061
  });
1062
 
1063
  /**
1077
  };
1078
  });
1079
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1080
  /**
1081
  * Directive for Popover jQuery plugin.
1082
  */
1112
  return input;
1113
  };
1114
  });
1115
+
1116
+ jQuery('#bookly-select2').select2({
1117
+ width: '100%',
1118
+ theme: 'bootstrap',
1119
+ allowClear: false,
1120
+ language : {
1121
+ noResults: function() { return BooklyL10nAppDialog.no_result_found; }
1122
+ }
1123
+ });
1124
  })();
1125
 
1126
  /**
backend/modules/calendar/templates/_appointment_dialog.php CHANGED
@@ -1,4 +1,9 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
 
 
2
  <div ng-app="appointmentDialog" ng-controller="appointmentDialogCtrl">
3
  <div id=bookly-appointment-dialog class="modal fade" tabindex=-1 role="dialog">
4
  <div class="modal-dialog">
@@ -13,13 +18,13 @@
13
  </div>
14
  <div ng-hide="loading || form.screen != 'main'" class="modal-body">
15
  <div class=form-group>
16
- <label for="ab_provider"><?php _e( 'Provider', 'bookly' ) ?></label>
17
- <select id="ab_provider" class="field form-control" ng-model="form.staff" ng-options="s.full_name for s in dataSource.data.staff" ng-change="onStaffChange()"></select>
18
  </div>
19
 
20
  <div class=form-group>
21
- <label for="ab_service"><?php _e( 'Service', 'bookly' ) ?></label>
22
- <select id="ab_service" class="field form-control" ng-model="form.service"
23
  ng-options="s.title for s in form.staff.services" ng-change="onServiceChange()">
24
  <option value=""><?php _e( '-- Select a service --', 'bookly' ) ?></option>
25
  </select>
@@ -28,21 +33,43 @@
28
  </p>
29
  </div>
30
 
31
- <div class="row">
32
- <div class="col-sm-4">
33
- <div class=form-group>
34
- <label for="ab_date"><?php _e( 'Date', 'bookly' ) ?></label>
35
- <input id="ab_date" class="form-control" type=text
36
- ng-model=form.date ui-date="dateOptions" autocomplete="off">
37
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </div>
39
- <div class="col-sm-8">
40
- <div class="form-group">
41
- <div ng-hide="form.service.duration == 86400">
42
- <label for="ab_period"><?php _e( 'Period', 'bookly' ) ?></label>
 
 
 
 
 
 
 
 
 
43
  <div class="bookly-flexbox">
44
  <div class="bookly-flex-cell">
45
- <select id="ab_period" class="form-control" ng-model=form.start_time
46
  ng-options="t.title for t in dataSource.data.start_time"
47
  ng-change=onStartTimeChange()></select>
48
  </div>
@@ -59,63 +86,114 @@
59
  <?php _e( 'Selected period doesn\'t match service duration', 'bookly' ) ?>
60
  </p>
61
  <p class="text-success" my-slide-up="errors.time_interval" ng-bind="errors.time_interval"></p>
62
- <p class="text-danger" my-slide-up=errors.date_interval_not_available id=date_interval_not_available_msg>
63
- <?php _e( 'The selected period is occupied by another appointment', 'bookly' ) ?>
64
- </p>
65
  </div>
66
  </div>
 
 
 
67
  </div>
68
  </div>
69
- <?php do_action( 'bookly_recurring_appointments_render_appointment_dialog_repeat' ) ?>
 
 
70
  <div class=form-group>
71
- <label for="bookly-chosen"><?php _e( 'Customers', 'bookly' ) ?></label>
72
- <span ng-show="form.service" title="<?php esc_attr_e( 'Selected / maximum', 'bookly' ) ?>">
73
- ({{dataSource.getTotalNumberOfPersons()}}/{{form.service.capacity}})
 
 
 
74
  </span>
75
- <ul class="ab-customer-list">
76
- <li ng-repeat="customer in form.customers">
77
- {{customer.number_of_persons}}&times;<i class="glyphicon glyphicon-user"></i>
78
- <a ng-click="editCustomerDetails(customer)" title="<?php esc_attr_e( 'Edit booking details', 'bookly' ) ?>" href="#">{{customer.name}}</a>
79
- <span ng-class="{'dashicons': true, 'dashicons-clock': customer.status == 'pending', 'dashicons-yes': customer.status == 'approved', 'dashicons-no': customer.status == 'cancelled', 'dashicons-dismiss': customer.status == 'rejected'}"
80
- popover="<?php esc_attr_e( 'Status', 'bookly' ) ?>: {{statusToString(customer.status)}}"></span>
81
- <span ng-show="customer.payment_id"
82
- popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}">
83
- <a data-toggle="modal"
84
- href="#bookly-payment-details-modal"
85
- data-payment_id="{{customer.payment_id}}"
86
- ng-class="{'bookly-js-toggle-popover dashicons': true, 'dashicons-thumbs-up': customer.payment_type == 'full', 'dashicons-warning': customer.payment_type == 'partial'}"></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  </span>
88
- <a ng-click="removeCustomer(customer)" class="dashicons dashicons-trash text-danger" href="#"
89
- popover="<?php esc_attr_e( 'Remove customer', 'bookly' ) ?>"></a>
90
  </li>
91
  </ul>
92
-
93
- <div ng-show="!form.service || dataSource.getTotalNumberOfNotCancelledPersons() < form.service.capacity">
94
  <div class="form-group">
95
  <div class="input-group">
96
- <select id="bookly-chosen" multiple data-placeholder="<?php esc_attr_e( '-- Search customers --', 'bookly' ) ?>"
97
- class="field chzn-select form-control" chosen="dataSource.data.customers"
98
- ng-model="form.customers" ng-options="c.name for c in dataSource.data.customers">
 
99
  </select>
100
  <span class="input-group-btn">
101
- <a href="#bookly-customer-dialog" class="btn btn-success" data-toggle="modal">
102
  <i class="glyphicon glyphicon-plus"></i>
103
  <?php _e( 'New customer', 'bookly' ) ?>
104
  </a>
105
  </span>
106
  </div>
107
- <p class="text-danger" my-slide-up="errors.customers_required">
108
- <?php _e( 'Please select a customer', 'bookly' ) ?>
109
- </p>
110
- <p class="text-danger" my-slide-up="errors.overflow_capacity" ng-bind="errors.overflow_capacity"></p>
111
  </div>
112
  </div>
 
 
 
 
113
  </div>
114
 
115
  <div class=form-group>
116
- <label for="ab_notification"><?php _e( 'Send notifications', 'bookly' ) ?></label>
117
- <p class="help-block"><?php _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' ) ?></p>
118
- <select class="form-control" style="margin-top: 0" ng-model=form.notification id="ab_notification">
 
 
119
  <option value="no"><?php _e( 'Don\'t send', 'bookly' ) ?></option>
120
  <option value="changed_status"><?php _e( 'If status changed', 'bookly' ) ?></option>
121
  <option value="all"><?php _e( 'To all customers', 'bookly' ) ?></option>
@@ -123,27 +201,27 @@
123
  </div>
124
 
125
  <div class=form-group>
126
- <label for="ab_internal_note"><?php _e( 'Internal note', 'bookly' ) ?></label>
127
- <textarea class="form-control" ng-model=form.internal_note id="ab_internal_note"></textarea>
128
  </div>
129
  </div>
130
- <?php do_action( 'bookly_recurring_appointments_render_appointment_dialog_schedule' ) ?>
131
  <div class="modal-footer">
132
  <div ng-hide=loading>
133
- <?php do_action( 'bookly_recurring_appointments_render_appointment_dialog_next_button' ) ?>
134
- <?php \BooklyLite\Lib\Utils\Common::customButton( 'bookly-save', 'btn-lg btn-success', null, array( 'ng-hide' => 'form.repeat.enabled && form.screen == \'main\'', 'ng-disabled' => 'form.repeat.enabled && schIsScheduleEmpty()' ), 'submit' ) ?>
135
- <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'ng-click' => 'closeDialog()', 'data-dismiss' => 'modal' ) ) ?>
136
  </div>
137
  </div>
138
  </form>
139
  </div><!-- /.modal-content -->
140
  </div><!-- /.modal-dialog -->
141
  </div><!-- /.modal -->
142
- <?php ?>
143
  <div customer-dialog=createCustomer(customer)></div>
144
  <div payment-details-dialog="completePayment(payment_id, payment_title)"></div>
145
 
146
- <?php include '_customer_details_dialog.php' ?>
147
  <?php \BooklyLite\Backend\Modules\Customers\Components::getInstance()->renderCustomerDialog() ?>
148
  <?php \BooklyLite\Backend\Modules\Payments\Components::getInstance()->renderPaymentDetailsDialog() ?>
149
  </div>
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Entities\CustomerAppointment;
3
+ use BooklyLite\Lib\Config;
4
+ use BooklyLite\Lib\Proxy;
5
+ use BooklyLite\Lib\Utils\Common;
6
+ ?>
7
  <div ng-app="appointmentDialog" ng-controller="appointmentDialogCtrl">
8
  <div id=bookly-appointment-dialog class="modal fade" tabindex=-1 role="dialog">
9
  <div class="modal-dialog">
18
  </div>
19
  <div ng-hide="loading || form.screen != 'main'" class="modal-body">
20
  <div class=form-group>
21
+ <label for="bookly-provider"><?php _e( 'Provider', 'bookly' ) ?></label>
22
+ <select id="bookly-provider" class="form-control" ng-model="form.staff" ng-options="s.full_name + (form.staff_any == s ? ' (' + dataSource.l10n.staff_any + ')' : '') for s in dataSource.data.staff" ng-change="onStaffChange()"></select>
23
  </div>
24
 
25
  <div class=form-group>
26
+ <label for="bookly-service"><?php _e( 'Service', 'bookly' ) ?></label>
27
+ <select id="bookly-service" class="form-control" ng-model="form.service"
28
  ng-options="s.title for s in form.staff.services" ng-change="onServiceChange()">
29
  <option value=""><?php _e( '-- Select a service --', 'bookly' ) ?></option>
30
  </select>
33
  </p>
34
  </div>
35
 
36
+ <div class=form-group ng-show="form.service && !form.service.id">
37
+ <label for="bookly-custom-service-name"><?php _e( 'Custom service name', 'bookly' ) ?></label>
38
+ <input type="text" id="bookly-custom-service-name" class="form-control" ng-model="form.custom_service_name" />
39
+ <p class="text-danger" my-slide-up="errors.custom_service_name_required">
40
+ <?php _e( 'Please enter a service name', 'bookly' ) ?>
41
+ </p>
42
+ </div>
43
+
44
+ <div class=form-group ng-show="form.service && !form.service.id">
45
+ <label for="bookly-custom-service-price"><?php _e( 'Custom service price', 'bookly' ) ?></label>
46
+ <input type="number" id="bookly-custom-service-price" class="form-control" ng-model="form.custom_service_price" min="0" step="1" />
47
+ </div>
48
+
49
+ <?php if ( Config::locationsActive() ): ?>
50
+ <div class="form-group">
51
+ <label for="bookly-appointment-location"><?php _e( 'Location', 'bookly' ) ?></label>
52
+ <select id="bookly-appointment-location" class="form-control" ng-model="form.location"
53
+ ng-options="l.name for l in form.staff.locations">
54
+ <option value=""></option>
55
+ </select>
56
  </div>
57
+ <?php endif ?>
58
+
59
+ <div class=form-group>
60
+ <div class="row">
61
+ <div class="col-sm-4">
62
+ <label for="bookly-date"><?php _e( 'Date', 'bookly' ) ?></label>
63
+ <input id="bookly-date" class="form-control" type=text
64
+ ng-model=form.date ui-date="dateOptions" autocomplete="off"
65
+ ng-change=onDateChange()>
66
+ </div>
67
+ <div class="col-sm-8">
68
+ <div ng-hide="form.service.duration >= 86400">
69
+ <label for="bookly-period"><?php _e( 'Period', 'bookly' ) ?></label>
70
  <div class="bookly-flexbox">
71
  <div class="bookly-flex-cell">
72
+ <select id="bookly-period" class="form-control" ng-model=form.start_time
73
  ng-options="t.title for t in dataSource.data.start_time"
74
  ng-change=onStartTimeChange()></select>
75
  </div>
86
  <?php _e( 'Selected period doesn\'t match service duration', 'bookly' ) ?>
87
  </p>
88
  <p class="text-success" my-slide-up="errors.time_interval" ng-bind="errors.time_interval"></p>
 
 
 
89
  </div>
90
  </div>
91
+ <div class="text-success col-sm-12" my-slide-up=errors.date_interval_not_available id=date_interval_not_available_msg>
92
+ <?php _e( 'The selected period is occupied by another appointment', 'bookly' ) ?>
93
+ </div>
94
  </div>
95
  </div>
96
+
97
+ <?php Proxy\RecurringAppointments::renderRecurringSubForm() ?>
98
+
99
  <div class=form-group>
100
+ <label for="bookly-select2"><?php _e( 'Customers', 'bookly' ) ?></label>
101
+ <span ng-show="form.service && form.service.id" title="<?php esc_attr_e( 'Selected / maximum', 'bookly' ) ?>">
102
+ ({{dataSource.getTotalNumberOfPersons()}}/{{form.service.capacity_max}})
103
+ </span>
104
+ <span ng-show="form.customers.length > 5" ng-click="form.expand_customers_list = !form.expand_customers_list" role="button">
105
+ <i class="dashicons" ng-class="{'dashicons-arrow-down-alt2':!form.expand_customers_list, 'dashicons-arrow-up-alt2':form.expand_customers_list}"></i>
106
  </span>
107
+ <p class="text-success" ng-show=form.service my-slide-up="form.service.capacity_min > 1 && form.service.capacity_min > dataSource.getTotalNumberOfPersons()">
108
+ <?php _e( 'Minimum capacity', 'bookly' ) ?>: {{form.service.capacity_min}}
109
+ </p>
110
+ <ul class="bookly-flexbox">
111
+ <li ng-repeat="customer in form.customers" class="bookly-flex-row" ng-hide="$index > 4 && !form.expand_customers_list">
112
+ <a ng-click="editCustomerDetails(customer)" title="<?php esc_attr_e( 'Edit booking details', 'bookly' ) ?>" class="bookly-flex-cell bookly-padding-bottom-sm" href>{{customer.name}}</a>
113
+ <span class="bookly-flex-cell text-right text-nowrap bookly-padding-bottom-sm">
114
+ <?php Proxy\Shared::renderAppointmentDialogCustomerList() ?>
115
+ <span class="dropdown">
116
+ <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)}}">
117
+ <span ng-class="{'dashicons': true, 'dashicons-clock': customer.status == 'pending', 'dashicons-yes': customer.status == 'approved', 'dashicons-no': customer.status == 'cancelled', 'dashicons-dismiss': customer.status == 'rejected', 'dashicons-list-view': customer.status == 'waitlisted'}"></span>
118
+ <span class="caret"></span>
119
+ </button>
120
+ <ul class="dropdown-menu">
121
+ <li>
122
+ <a href ng-click="customer.status = 'pending'">
123
+ <span class="dashicons dashicons-clock"></span>
124
+ <?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?>
125
+ </a>
126
+ </li>
127
+ <li>
128
+ <a href ng-click="customer.status = 'approved'">
129
+ <span class="dashicons dashicons-yes"></span>
130
+ <?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?>
131
+ </a>
132
+ </li>
133
+ <li>
134
+ <a href ng-click="customer.status = 'cancelled'">
135
+ <span class="dashicons dashicons-no"></span>
136
+ <?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?>
137
+ </a>
138
+ </li>
139
+ <li>
140
+ <a href ng-click="customer.status = 'rejected'">
141
+ <span class="dashicons dashicons-dismiss"></span>
142
+ <?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ) ?>
143
+ </a>
144
+ </li>
145
+ <?php if ( Config::waitingListActive() ): ?>
146
+ <li>
147
+ <a href ng-click="customer.status = 'waitlisted'">
148
+ <span class="dashicons dashicons-list-view"></span>
149
+ <?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?>
150
+ </a>
151
+ </li>
152
+ <?php endif ?>
153
+ </ul>
154
+ </span>
155
+ <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" popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}">
156
+ <span ng-class="{'bookly-js-toggle-popover dashicons': true, 'dashicons-thumbs-up': customer.payment_type == 'full', 'dashicons-warning': customer.payment_type == 'partial'}"></span>
157
+ </button>
158
+ <span class="btn btn-sm btn-default disabled bookly-margin-left-xs" style="opacity:1;cursor:default;"><i class="glyphicon glyphicon-user"></i>&times;{{customer.number_of_persons}}</span>
159
+ <button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" ng-click="editPackageSchedule(customer)" ng-show="customer.package_id" popover="<?php esc_attr_e( 'Package schedule', 'bookly' ) ?>">
160
+ <span class="dashicons dashicons-calendar"></span>
161
+ </button>
162
+ <a ng-click="removeCustomer(customer)" class="dashicons dashicons-trash text-danger bookly-vertical-middle" href="#"
163
+ popover="<?php esc_attr_e( 'Remove customer', 'bookly' ) ?>"></a>
164
  </span>
 
 
165
  </li>
166
  </ul>
167
+ <span class="btn btn-default" ng-show="form.customers.length > 5 && !form.expand_customers_list" ng-click="form.expand_customers_list = !form.expand_customers_list" style="width: 100%; line-height: 0; padding-top: 0; padding-bottom: 8px; margin-bottom: 10px;" role="button">...</span>
168
+ <div <?php if ( ! Config::waitingListActive() ): ?>ng-show="!form.service || dataSource.getTotalNumberOfNotCancelledPersons() < form.service.capacity_max"<?php endif ?>>
169
  <div class="form-group">
170
  <div class="input-group">
171
+ <select id="bookly-select2" multiple data-placeholder="<?php esc_attr_e( '-- Search customers --', 'bookly' ) ?>"
172
+ class="form-control"
173
+ ng-model="form.customers" ng-options="c.name for c in dataSource.data.customers"
174
+ ng-change="onCustomersChange({{form.customers}}, {{dataSource.getTotalNumberOfNotCancelledPersons()}})">
175
  </select>
176
  <span class="input-group-btn">
177
+ <a class="btn btn-success" ng-click="openNewCustomerDialog()">
178
  <i class="glyphicon glyphicon-plus"></i>
179
  <?php _e( 'New customer', 'bookly' ) ?>
180
  </a>
181
  </span>
182
  </div>
 
 
 
 
183
  </div>
184
  </div>
185
+ <p class="text-danger" my-slide-up="errors.overflow_capacity" ng-bind="errors.overflow_capacity"></p>
186
+ <p class="text-success" my-slide-up="errors.customers_appointments_limit" ng-repeat="customer_error in errors.customers_appointments_limit">
187
+ {{customer_error}}
188
+ </p>
189
  </div>
190
 
191
  <div class=form-group>
192
+ <label for="bookly-notification"><?php _e( 'Send notifications', 'bookly' ) ?></label>
193
+ <p class="help-block"><?php is_admin() ?
194
+ _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' ) :
195
+ _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>
196
+ <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'" >
197
  <option value="no"><?php _e( 'Don\'t send', 'bookly' ) ?></option>
198
  <option value="changed_status"><?php _e( 'If status changed', 'bookly' ) ?></option>
199
  <option value="all"><?php _e( 'To all customers', 'bookly' ) ?></option>
201
  </div>
202
 
203
  <div class=form-group>
204
+ <label for="bookly-internal-note"><?php _e( 'Internal note', 'bookly' ) ?></label>
205
+ <textarea class="form-control" ng-model=form.internal_note id="bookly-internal-note"></textarea>
206
  </div>
207
  </div>
208
+ <?php Proxy\RecurringAppointments::renderSchedule() ?>
209
  <div class="modal-footer">
210
  <div ng-hide=loading>
211
+ <?php Proxy\Shared::renderAppointmentDialogFooter() ?>
212
+ <?php Common::customButton( 'bookly-save', 'btn-lg btn-success', null, array( 'ng-hide' => 'form.repeat.enabled && form.screen == \'main\'', 'ng-disabled' => 'form.repeat.enabled && schIsScheduleEmpty()', 'formnovalidate' => '' ), 'submit' ) ?>
213
+ <?php Common::customButton( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'ng-click' => 'closeDialog()', 'data-dismiss' => 'modal' ) ) ?>
214
  </div>
215
  </div>
216
  </form>
217
  </div><!-- /.modal-content -->
218
  </div><!-- /.modal-dialog -->
219
  </div><!-- /.modal -->
 
220
  <div customer-dialog=createCustomer(customer)></div>
221
  <div payment-details-dialog="completePayment(payment_id, payment_title)"></div>
222
 
223
+ <?php $this->render( '_customer_details_dialog' ) ?>
224
  <?php \BooklyLite\Backend\Modules\Customers\Components::getInstance()->renderCustomerDialog() ?>
225
  <?php \BooklyLite\Backend\Modules\Payments\Components::getInstance()->renderPaymentDetailsDialog() ?>
226
  </div>
227
+ <?php Proxy\Packages::renderPackageScheduleDialog() ?>
backend/modules/calendar/templates/_customer_details_dialog.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  use BooklyLite\Lib\Entities\CustomerAppointment;
 
 
 
3
  ?>
4
  <div id="bookly-customer-details-dialog" class="modal fade" tabindex=-1 role="dialog">
5
  <div class="modal-dialog">
@@ -11,87 +14,33 @@ use BooklyLite\Lib\Entities\CustomerAppointment;
11
  <form ng-hide=loading style="z-index: 1050">
12
  <div class="modal-body">
13
  <div class="form-group">
14
- <label for="ab-appointment-status"><?php _e( 'Status', 'bookly' ) ?></label>
15
- <select class="ab-custom-field form-control" id="ab-appointment-status">
16
  <option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?></option>
17
  <option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?></option>
18
  <option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?></option>
19
  <option value="<?php echo CustomerAppointment::STATUS_REJECTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ) ?></option>
 
 
 
20
  </select>
21
  </div>
22
- <div class="form-group">
23
- <label for="ab-edit-number-of-persons"><?php _e( 'Number of persons', 'bookly' ) ?></label>
24
- <select class="ab-custom-field form-control" id="ab-edit-number-of-persons"></select>
25
- </div>
26
- <?php do_action( 'bookly_render_customer_details_dialog' ) ?>
27
- <h3 class="bookly-block-head bookly-color-gray">
28
- <?php _e( 'Custom Fields', 'bookly' ) ?>
29
- </h3>
30
- <div id="ab--custom-fields">
31
- <?php foreach ( $custom_fields as $custom_field ) : ?>
32
- <div class="form-group" data-type="<?php echo esc_attr( $custom_field->type )?>" data-id="<?php echo esc_attr( $custom_field->id ) ?>" data-services="<?php echo esc_attr( json_encode( $custom_field->services ) ) ?>">
33
- <label for="custom_field_<?php echo esc_attr( $custom_field->id ) ?>"><?php echo $custom_field->label ?></label>
34
- <div>
35
- <?php if ( $custom_field->type == 'text-field' ) : ?>
36
- <input id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" type="text" class="ab-custom-field form-control" />
37
-
38
- <?php elseif ( $custom_field->type == 'textarea' ) : ?>
39
- <textarea id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" rows="3" class="ab-custom-field form-control"></textarea>
40
-
41
- <?php elseif ( $custom_field->type == 'checkboxes' ) : ?>
42
- <?php foreach ( $custom_field->items as $item ) : ?>
43
- <div class="checkbox">
44
- <label>
45
- <input class="ab-custom-field" type="checkbox" value="<?php echo esc_attr( $item ) ?>" />
46
- <?php echo $item ?>
47
- </label>
48
- </div>
49
- <?php endforeach ?>
50
-
51
- <?php elseif ( $custom_field->type == 'radio-buttons' ) : ?>
52
- <?php foreach ( $custom_field->items as $item ) : ?>
53
- <div class="radio">
54
- <label>
55
- <input type="radio" name="<?php echo $custom_field->id ?>" class="ab-custom-field" value="<?php echo esc_attr( $item ) ?>" />
56
- <?php echo $item ?>
57
- </label>
58
- </div>
59
- <?php endforeach ?>
60
-
61
- <?php elseif ( $custom_field->type == 'drop-down' ) : ?>
62
- <select id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" class="ab-custom-field form-control">
63
- <option value=""></option>
64
- <?php foreach ( $custom_field->items as $item ) : ?>
65
- <option value="<?php echo esc_attr( $item ) ?>"><?php echo $item ?></option>
66
- <?php endforeach ?>
67
- </select>
68
- <?php endif ?>
69
- </div>
70
- </div>
71
- <?php endforeach ?>
72
  </div>
73
-
74
- <?php if ( $extras = apply_filters( 'bookly_service_extras_find_all', array() ) ) : ?>
75
- <h3 class="bookly-block-head bookly-color-gray">
76
- <?php _e( 'Extras', 'bookly' ) ?>
77
- </h3>
78
- <div id="bookly-extras" class="bookly-flexbox">
79
- <?php foreach ( $extras as $extra ) : ?>
80
- <div class="bookly-flex-row service_<?php echo $extra->get( 'service_id' ) ?> bookly-margin-bottom-sm">
81
- <div class="bookly-flex-cell bookly-padding-bottom-sm" style="width:5em">
82
- <input class="extras-count form-control" data-id="<?php echo $extra->get( 'id' ) ?>" type="number" min="0" name="extra[<?php echo $extra->get( 'id' ) ?>]" value="0" />
83
- </div>
84
- <div class="bookly-flex-cell bookly-padding-bottom-sm bookly-vertical-middle">
85
- &nbsp;&times; <b><?php echo $extra->getTitle() ?></b> (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $extra->get( 'price' ) ) ?>)
86
- </div>
87
- </div>
88
- <?php endforeach ?>
89
  </div>
90
  <?php endif ?>
 
 
 
91
  </div>
92
  <div class="modal-footer">
93
- <input type="button" data-customer="" ng-click=saveCustomFields() class="ab-popup-save btn btn-lg btn-success" value="<?php _e( 'Apply', 'bookly' ) ?>">
94
- <input type="button" class="btn btn-lg btn-default" data-dismiss=modal value="<?php _e( 'Cancel', 'bookly' ) ?>" aria-hidden=true>
95
  </div>
96
  </form>
97
  </div><!-- /.modal-content -->
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  use BooklyLite\Lib\Entities\CustomerAppointment;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ use BooklyLite\Lib\Config;
5
+ use BooklyLite\Lib\Proxy;
6
  ?>
7
  <div id="bookly-customer-details-dialog" class="modal fade" tabindex=-1 role="dialog">
8
  <div class="modal-dialog">
14
  <form ng-hide=loading style="z-index: 1050">
15
  <div class="modal-body">
16
  <div class="form-group">
17
+ <label for="bookly-appointment-status"><?php _e( 'Status', 'bookly' ) ?></label>
18
+ <select class="bookly-custom-field form-control" id="bookly-appointment-status">
19
  <option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?></option>
20
  <option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?></option>
21
  <option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?></option>
22
  <option value="<?php echo CustomerAppointment::STATUS_REJECTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ) ?></option>
23
+ <?php if ( Config::waitingListActive() ) : ?>
24
+ <option value="<?php echo CustomerAppointment::STATUS_WAITLISTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?></option>
25
+ <?php endif ?>
26
  </select>
27
  </div>
28
+ <div class="form-group" style="display: none">
29
+ <select class="bookly-custom-field form-control" id="bookly-number-of-persons"></select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  </div>
31
+ <?php if ( Config::showNotes() ): ?>
32
+ <div class="form-group">
33
+ <label for="bookly-appointment-notes"><?php echo Common::getTranslatedOption( 'bookly_l10n_label_notes' ) ?></label>
34
+ <textarea class="bookly-custom-field form-control" id="bookly-appointment-notes"></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
35
  </div>
36
  <?php endif ?>
37
+
38
+ <?php Proxy\CustomFields::renderCustomerDetails() ?>
39
+ <?php Proxy\ServiceExtras::renderCustomerDetails() ?>
40
  </div>
41
  <div class="modal-footer">
42
+ <?php Common::customButton( null, 'btn-lg btn-lg btn-success', __( 'Apply', 'bookly' ), array( 'ng-click' => 'saveCustomFields()' ) ) ?>
43
+ <?php Common::customButton( null, 'btn btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
44
  </div>
45
  </form>
46
  </div><!-- /.modal-content -->
backend/modules/calendar/templates/calendar.php CHANGED
@@ -1,4 +1,6 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
2
  <style>
3
  .fc-slats tr { height: <?php echo max( 21, (int) ( 0.43 * get_option( 'bookly_gen_time_slot_length' ) ) ) ?>px; }
4
  .fc-time-grid-event.fc-short .fc-time::after { content: '' !important; }
@@ -23,15 +25,15 @@
23
  </li>
24
  <?php endif ?>
25
  <?php foreach ( $staff_members as $staff ) : ?>
26
- <li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="<?php echo $staff->id ?>" style="display: none">
27
- <?php echo $staff->full_name ?>
28
  </li>
29
  <?php endforeach ?>
30
  <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
31
  <div class="btn-group pull-right" style="margin-top: 5px;">
32
  <button class="btn btn-default dropdown-toggle bookly-flexbox" data-toggle="dropdown">
33
  <div class="bookly-flex-cell"><i class="dashicons dashicons-admin-users bookly-margin-right-md"></i></div>
34
- <div class="bookly-flex-cell text-left"><span id="ab-staff-button"></span></div>
35
  <div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
36
  </button>
37
  <ul class="dropdown-menu bookly-entity-selector">
@@ -44,8 +46,8 @@
44
  <li>
45
  <a class="checkbox" href="javascript:void(0)">
46
  <label>
47
- <input type="checkbox" id="ab-filter-staff-<?php echo $staff->id ?>" value="<?php echo $staff->id ?>" data-staff_name="<?php echo esc_attr( $staff->full_name ) ?>" class="bookly-js-check-entity">
48
- <?php echo $staff->full_name ?>
49
  </label>
50
  </a>
51
  </li>
@@ -56,7 +58,6 @@
56
  </ul>
57
  <?php endif ?>
58
  <div class="bookly-margin-top-xlg">
59
- <div style="display: none;" class="bookly-loading bookly-js-loading"></div>
60
  <?php if ( $staff_members ) : ?>
61
  <div class="fc-loading-inner" style="display: none">
62
  <div class="fc-loading"></div>
@@ -65,7 +66,7 @@
65
  <div class="bookly-js-calendar-element"></div>
66
  </div>
67
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
68
- <?php do_action( 'bookly_render_component_calendar' ) ?>
69
  <?php else : ?>
70
  <div class="well">
71
  <div class="h1"><?php _e( 'Welcome to Bookly!', 'bookly' ) ?></div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var \BooklyLite\Lib\Entities\Staff[] $staff_members */
3
+ ?>
4
  <style>
5
  .fc-slats tr { height: <?php echo max( 21, (int) ( 0.43 * get_option( 'bookly_gen_time_slot_length' ) ) ) ?>px; }
6
  .fc-time-grid-event.fc-short .fc-time::after { content: '' !important; }
25
  </li>
26
  <?php endif ?>
27
  <?php foreach ( $staff_members as $staff ) : ?>
28
+ <li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="<?php echo $staff->getId() ?>" style="display: none">
29
+ <?php echo $staff->getFullName() ?>
30
  </li>
31
  <?php endforeach ?>
32
  <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
33
  <div class="btn-group pull-right" style="margin-top: 5px;">
34
  <button class="btn btn-default dropdown-toggle bookly-flexbox" data-toggle="dropdown">
35
  <div class="bookly-flex-cell"><i class="dashicons dashicons-admin-users bookly-margin-right-md"></i></div>
36
+ <div class="bookly-flex-cell text-left"><span id="bookly-staff-button"></span></div>
37
  <div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
38
  </button>
39
  <ul class="dropdown-menu bookly-entity-selector">
46
  <li>
47
  <a class="checkbox" href="javascript:void(0)">
48
  <label>
49
+ <input type="checkbox" id="bookly-filter-staff-<?php echo $staff->getId() ?>" value="<?php echo $staff->getId() ?>" data-staff_name="<?php echo esc_attr( $staff->getFullName() ) ?>" class="bookly-js-check-entity">
50
+ <?php echo $staff->getFullName() ?>
51
  </label>
52
  </a>
53
  </li>
58
  </ul>
59
  <?php endif ?>
60
  <div class="bookly-margin-top-xlg">
 
61
  <?php if ( $staff_members ) : ?>
62
  <div class="fc-loading-inner" style="display: none">
63
  <div class="fc-loading"></div>
66
  <div class="bookly-js-calendar-element"></div>
67
  </div>
68
  <?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
69
+ <?php \BooklyLite\Lib\Proxy\Shared::renderComponentCalendar() ?>
70
  <?php else : ?>
71
  <div class="well">
72
  <div class="h1"><?php _e( 'Welcome to Bookly!', 'bookly' ) ?></div>
backend/modules/coupons/Controller.php DELETED
@@ -1,66 +0,0 @@
1
- <?php
2
- namespace BooklyLite\Backend\Modules\Coupons;
3
-
4
- use BooklyLite\Lib;
5
-
6
- /**
7
- * Class Controller
8
- * @package BooklyLite\Backend\Modules\Coupons
9
- */
10
- class Controller extends Lib\Base\Controller
11
- {
12
- const page_slug = 'bookly-coupons';
13
-
14
- /**
15
- * Default action
16
- */
17
- public function index()
18
- {
19
- $this->enqueueStyles( array(
20
- 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
21
- 'frontend' => array( 'css/ladda.min.css', ),
22
- ) );
23
-
24
- $this->enqueueScripts( array(
25
- 'backend' => array(
26
- 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
27
- 'js/datatables.min.js' => array( 'jquery' ),
28
- 'js/alert.js' => array( 'jquery' ),
29
- ),
30
- 'frontend' => array(
31
- 'js/spin.min.js' => array( 'jquery' ),
32
- 'js/ladda.min.js' => array( 'jquery' ),
33
- ),
34
- 'module' => array( 'js/coupons.js' => array( 'jquery' ) )
35
- ) );
36
-
37
- wp_localize_script( 'bookly-coupons.js', 'BooklyL10n', array(
38
- 'edit' => __( 'Edit', 'bookly' ),
39
- 'zeroRecords' => __( 'No coupons found.', 'bookly' ),
40
- 'processing' => __( 'Processing...', 'bookly' ),
41
- 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
42
- 'selector' => array(
43
- 'all_selected' => __( 'All services', 'bookly' ),
44
- 'nothing_selected' => __( 'No service selected', 'bookly' ),
45
- 'collection' => array(),
46
- ),
47
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
48
- ) );
49
-
50
- $this->render( 'index' );
51
- }
52
-
53
- // Protected methods.
54
-
55
- /**
56
- * Override parent method to add 'wp_ajax_bookly_' prefix
57
- * so current 'execute*' methods look nicer.
58
- *
59
- * @param string $prefix
60
- */
61
- protected function registerWpActions( $prefix = '' )
62
- {
63
- parent::registerWpActions( 'wp_ajax_bookly_' );
64
- }
65
-
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/coupons/forms/Coupon.php DELETED
@@ -1,19 +0,0 @@
1
- <?php
2
- namespace BooklyLite\Backend\Modules\Coupons\Forms;
3
-
4
- use BooklyLite\Lib;
5
-
6
- /**
7
- * Class Coupon
8
- * @package BooklyLite\Backend\Modules\Coupons\Forms
9
- */
10
- class Coupon extends Lib\Base\Form
11
- {
12
- protected static $entity_class = 'Coupon';
13
-
14
- public function configure()
15
- {
16
- $this->setFields( array( 'id', 'code', 'discount', 'deduction', 'usage_limit' ) );
17
- }
18
-
19
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/coupons/resources/js/coupons.js DELETED
@@ -1,63 +0,0 @@
1
- jQuery(function($) {
2
-
3
- var
4
- $coupons_list = $('#bookly-coupons-list'),
5
- $coupon_modal = $('#bookly-coupon-modal'),
6
- $save_button = $('#bookly-coupon-save')
7
- ;
8
-
9
- /**
10
- * Init DataTables.
11
- */
12
- var dt = $coupons_list.DataTable({
13
- order: [[ 0, "asc" ]],
14
- paging: false,
15
- info: false,
16
- searching: false,
17
- processing: true,
18
- responsive: true,
19
- data: [],
20
- columns: [
21
- { data: "code" },
22
- { data: "discount" },
23
- { data: "deduction" },
24
- { data: 'service_ids'},
25
- { data: "usage_limit" },
26
- { data: "used" },
27
- {
28
- responsivePriority: 1,
29
- orderable: false,
30
- searchable: false,
31
- render: function ( data, type, row, meta ) {
32
- return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-coupon-modal"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</button>';
33
- }
34
- },
35
- {
36
- responsivePriority: 1,
37
- orderable: false,
38
- searchable: false,
39
- render: function ( data, type, row, meta ) {
40
- return '<input type="checkbox" value="' + row.id + '">';
41
- }
42
- }
43
- ],
44
- language: {
45
- zeroRecords: BooklyL10n.zeroRecords,
46
- processing: BooklyL10n.processing
47
- }
48
- });
49
-
50
- /**
51
- * Save coupon.
52
- */
53
- $save_button.on('click', function (e) {
54
- e.preventDefault();
55
- $coupon_modal.modal('hide');
56
- booklyAlert({error: [BooklyL10n.limitations]});
57
- });
58
- $('#bookly-add,#bookly-delete').on('click', function (e) {
59
- e.preventDefault();
60
- booklyAlert({error: [BooklyL10n.limitations]});
61
- });
62
-
63
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/coupons/templates/_modal.php DELETED
@@ -1,66 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
- <div class="modal fade" id="bookly-coupon-modal" tabindex="-1">
3
- <div class="modal-dialog">
4
- <div class="modal-content">
5
- <form>
6
- <div class="modal-header">
7
- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
8
- <h4 class="modal-title" id="bookly-new-coupon-title"><?php _e( 'New coupon', 'bookly' ) ?></h4>
9
- <h4 class="modal-title" id="bookly-edit-coupon-title"><?php _e( 'Edit coupon', 'bookly' ) ?></h4>
10
- </div>
11
- <div class="modal-body">
12
- <div class="row">
13
- <div class="col-sm-12">
14
- <div class=form-group>
15
- <label for="bookly-coupon-code"><?php _e( 'Code', 'bookly' ) ?></label>
16
- <input type="text" id="bookly-coupon-code" class="form-control" name="code" />
17
- </div>
18
- </div>
19
- <div class="col-sm-6">
20
- <div class=form-group>
21
- <label for="bookly-coupon-discount"><?php _e( 'Discount (%)', 'bookly' ) ?></label>
22
- <input type="number" id="bookly-coupon-discount" class="form-control" name="discount" />
23
- </div>
24
- </div>
25
- <div class="col-sm-6">
26
- <div class=form-group>
27
- <label for="bookly-coupon-deduction"><?php _e( 'Deduction', 'bookly' ) ?></label>
28
- <input type="text" id="bookly-coupon-deduction" class="form-control" name="deduction" />
29
- </div>
30
- </div>
31
- <div class="col-sm-12">
32
- <div class=form-group>
33
- <label for="bookly-coupon-usage-limit"><?php _e( 'Usage limit', 'bookly' ) ?></label>
34
- <input type="number" id="bookly-coupon-usage-limit" class="form-control" name="usage_limit" min="0" step="1" />
35
- </div>
36
- </div>
37
- <div class="col-sm-12">
38
- <div class="btn-group">
39
- <button class="btn btn-default dropdown-toggle bookly-flexbox" data-toggle="dropdown">
40
- <div class="bookly-flex-cell"><i class="glyphicon glyphicon-tag bookly-margin-right-md"></i></div>
41
- <div class="bookly-flex-cell text-left"><span id="bookly-entity-counter"></span></div>
42
- <div class="bookly-flex-cell">
43
- <div class="bookly-margin-left-md"><span class="caret"></span></div>
44
- </div>
45
- </button>
46
- <ul class="dropdown-menu bookly-entity-selector">
47
- <li>
48
- <a class="checkbox" href="javascript:void(0)">
49
- <label><input type="checkbox" id="bookly-check-all-entities"/><?php _e( 'All Services', 'bookly' ) ?></label>
50
- </a>
51
- </li>
52
- </ul>
53
- </div>
54
- </div>
55
- </div>
56
- </div>
57
- <div class="modal-footer">
58
- <?php \BooklyLite\Lib\Utils\Common::submitButton( 'bookly-coupon-save' ) ?>
59
- <button class="btn btn-lg btn-default" data-dismiss="modal">
60
- <?php _e( 'Cancel', 'bookly' ) ?>
61
- </button>
62
- </div>
63
- </form>
64
- </div>
65
- </div>
66
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/coupons/templates/index.php DELETED
@@ -1,44 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
- <div id="bookly-tbs" class="wrap">
3
- <div class="bookly-tbs-body">
4
- <div class="page-header text-right clearfix">
5
- <div class="bookly-page-title">
6
- <?php _e( 'Coupons', 'bookly' ) ?>
7
- </div>
8
- <?php \BooklyLite\Backend\Modules\Support\Components::getInstance()->renderButtons( $this::page_slug ) ?>
9
- </div>
10
- <div class="panel panel-default bookly-main">
11
- <div class="panel-body">
12
- <div class="form-inline bookly-margin-bottom-lg text-right">
13
- <div class="form-group">
14
- <button type="button"
15
- id="bookly-add"
16
- class="btn btn-success">
17
- <i class="glyphicon glyphicon-plus"></i> <?php _e( 'Add Coupon', 'bookly' ) ?>
18
- </button>
19
- </div>
20
- </div>
21
-
22
- <table class="table table-striped" id="bookly-coupons-list" width="100%">
23
- <thead>
24
- <tr>
25
- <th><?php _e( 'Code', 'bookly' ) ?></th>
26
- <th><?php _e( 'Discount (%)', 'bookly' ) ?></th>
27
- <th><?php _e( 'Deduction', 'bookly' ) ?></th>
28
- <th><?php _e( 'Services', 'bookly' ) ?></th>
29
- <th><?php _e( 'Usage limit', 'bookly' ) ?></th>
30
- <th><?php _e( 'Number of times used', 'bookly' ) ?></th>
31
- <th></th>
32
- <th width="16"><input type="checkbox" id="bookly-check-all"></th>
33
- </tr>
34
- </thead>
35
- </table>
36
-
37
- <div class="text-right bookly-margin-top-lg">
38
- <?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
39
- </div>
40
- </div>
41
- </div>
42
- </div>
43
- <?php include '_modal.php' ?>
44
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/custom_fields/Controller.php DELETED
@@ -1,90 +0,0 @@
1
- <?php
2
- namespace BooklyLite\Backend\Modules\CustomFields;
3
-
4
- use BooklyLite\Lib;
5
-
6
- /**
7
- * Class Controller
8
- * @package BooklyLite\Backend\Modules\CustomFields
9
- */
10
- class Controller extends Lib\Base\Controller
11
- {
12
- const page_slug = 'bookly-custom-fields';
13
-
14
- /**
15
- * Default Action
16
- */
17
- public function index()
18
- {
19
- $this->enqueueStyles( array(
20
- 'frontend' => array( 'css/ladda.min.css' ),
21
- 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
22
- ) );
23
-
24
- $this->enqueueScripts( array(
25
- 'module' => array( 'js/custom_fields.js' => array( 'jquery-ui-sortable' ) ),
26
- 'frontend' => array(
27
- 'js/spin.min.js' => array( 'jquery' ),
28
- 'js/ladda.min.js' => array( 'jquery' ),
29
- ),
30
- 'backend' => array(
31
- 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
32
- 'js/alert.js' => array( 'jquery' ),
33
- ),
34
- ) );
35
-
36
- wp_localize_script( 'bookly-custom_fields.js', 'BooklyL10n', array(
37
- 'custom_fields' => get_option( 'bookly_custom_fields' ),
38
- 'saved' => __( 'Settings saved.', 'bookly' ),
39
- 'selector' => array(
40
- 'all_selected' => __( 'All services', 'bookly' ),
41
- 'nothing_selected' => __( 'No service selected', 'bookly' ),
42
- ),
43
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
44
- ) );
45
-
46
- $services = $this->render( '_services', array( 'services' => Lib\Entities\Service::query()->select( 'id, title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray() ), false );
47
- $this->render( 'index', array( 'services_html' => $services ) );
48
- }
49
-
50
- /**
51
- * Save custom fields.
52
- */
53
- public function executeSaveCustomFields()
54
- {
55
- $custom_fields = $this->getParameter( 'fields' );
56
- foreach ( json_decode( $custom_fields ) as $custom_field ) {
57
- switch ( $custom_field->type ) {
58
- case 'textarea':
59
- case 'text-content':
60
- case 'text-field':
61
- case 'captcha':
62
- do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' .sanitize_title( $custom_field->label ), $custom_field->label );
63
- break;
64
- case 'checkboxes':
65
- case 'radio-buttons':
66
- case 'drop-down':
67
- do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' . sanitize_title( $custom_field->label ), $custom_field->label );
68
- foreach ( $custom_field->items as $label ) {
69
- do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' . sanitize_title( $custom_field->label ) . '=' . sanitize_title( $label ), $label );
70
- }
71
- break;
72
- }
73
- }
74
- update_option( 'bookly_custom_fields', $custom_fields );
75
- update_option( 'bookly_custom_fields_per_service', '0' );
76
- wp_send_json_success();
77
- }
78
-
79
- /**
80
- * Override parent method to add 'wp_ajax_bookly_' prefix
81
- * so current 'execute*' methods look nicer.
82
- *
83
- * @param string $prefix
84
- */
85
- protected function registerWpActions( $prefix = '' )
86
- {
87
- parent::registerWpActions( 'wp_ajax_bookly_' );
88
- }
89
-
90
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/custom_fields/resources/js/custom_fields.js DELETED
@@ -1,233 +0,0 @@
1
- jQuery(function($) {
2
-
3
- var $fields = $("#ab-custom-fields"),
4
- $cf_per_service = $('#bookly_custom_fields_per_service');
5
-
6
- $fields.sortable({
7
- axis : 'y',
8
- handle : '.bookly-js-handle'
9
- });
10
-
11
- $cf_per_service.change(function() {
12
- if($(this).val() == 1){
13
- $(this).val('0');
14
- booklyAlert({error: [BooklyL10n.limitations]});
15
- $(this).find('option:gt(0)').prop('disabled', true);
16
- }
17
- });
18
-
19
- /**
20
- * Build initial fields.
21
- */
22
- restoreFields();
23
-
24
- /**
25
- * On "Add new field" button click.
26
- */
27
- $('#ab-add-fields').on('click', 'button', function() {
28
- addField($(this).data('type'));
29
- });
30
-
31
- /**
32
- * On "Add new item" button click.
33
- */
34
- $fields.on('click', 'button', function() {
35
- addItem($(this).prev('ul'), $(this).data('type'));
36
- });
37
-
38
- /**
39
- * Delete field or checkbox/radio button/drop-down option.
40
- */
41
- $fields.on('click', '.ab-delete', function(e) {
42
- e.preventDefault();
43
- $(this).closest('li').fadeOut('fast', function() { $(this).remove(); });
44
- });
45
-
46
- /**
47
- * Submit custom fields form.
48
- */
49
- $('#ajax-send-custom-fields').on('click', function(e) {
50
- e.preventDefault();
51
- var data = [];
52
- $fields.children('li').each(function() {
53
- var $this = $(this),
54
- field = {};
55
- switch ($this.data('type')) {
56
- case 'checkboxes':
57
- case 'radio-buttons':
58
- case 'drop-down':
59
- field.items = [];
60
- $this.find('ul.ab-items li').each(function() {
61
- field.items.push($(this).find('input').val());
62
- });
63
- case 'textarea':
64
- case 'text-field':
65
- case 'text-content':
66
- case 'captcha':
67
- field.type = $this.data('type');
68
- field.label = $this.find('.ab-label').val();
69
- field.required = $this.find('.ab-required').prop('checked');
70
- field.id = $this.data('ab-field-id');
71
- field.services = $this.find('.ab--services-holder input:checked')
72
- .map(function() { return this.value; })
73
- .get();
74
- }
75
- data.push(field);
76
- });
77
-
78
- var ladda = Ladda.create(this);
79
- ladda.start();
80
- $.ajax({
81
- type : 'POST',
82
- url : ajaxurl,
83
- xhrFields : { withCredentials: true },
84
- data : { action: 'bookly_save_custom_fields', fields: JSON.stringify(data), cf_per_service: $cf_per_service.val() },
85
- complete : function() {
86
- ladda.stop();
87
- booklyAlert({success : [BooklyL10n.saved]});
88
- }
89
- });
90
- });
91
-
92
- /**
93
- * On 'Reset' click.
94
- */
95
- $('button[type=reset]').on('click', function() {
96
- $fields.empty();
97
- restoreFields();
98
- });
99
-
100
- /**
101
- * Add new field.
102
- *
103
- * @param type
104
- * @param id
105
- * @param label
106
- * @param required
107
- * @param services
108
- * @returns {*|jQuery}
109
- */
110
- function addField(type, id, label, required, services) {
111
- var $new_field = $('ul#ab-templates > li[data-type=' + type + ']').clone();
112
- // Set id, label and required.
113
- if (typeof id == 'undefined') {
114
- id = Math.floor((Math.random() * 100000) + 1);
115
- }
116
- if (typeof label == 'undefined') {
117
- label = '';
118
- }
119
- if (typeof required == 'undefined') {
120
- required = false;
121
- }
122
- $new_field
123
- .hide()
124
- .data('ab-field-id', id)
125
- .find('.ab-required').prop({
126
- id : 'required-' + id,
127
- checked : required
128
- })
129
- .next('label').attr('for', 'required-' + id)
130
- .end().end()
131
- .find('.ab-label').val(label)
132
- .end()
133
- .find('.ab--services-holder input:checkbox').each(function (index) {
134
- if (services && $.inArray(this.value, services) > -1) {
135
- this.checked = true;
136
- }
137
- this.id = 'check-' + id + '-' + index;
138
- $(this).next().attr('for', 'check-' + id + '-' + index);
139
- });
140
- // Add new field to the list.
141
- $fields.append($new_field);
142
- $new_field.fadeIn('fast');
143
- // Make it sortable.
144
- $new_field.find('ul.ab-items').sortable({
145
- axis : 'y',
146
- handle : '.bookly-js-handle'
147
- });
148
- // Set focus to label field.
149
- $new_field.find('.ab-label').focus();
150
-
151
- return $new_field;
152
- }
153
-
154
- /**
155
- * Add new checkbox/radio button/drop-down option.
156
- *
157
- * @param $ul
158
- * @param type
159
- * @param value
160
- * @return {*|jQuery}
161
- */
162
- function addItem($ul, type, value) {
163
- var $new_item = $('ul#ab-templates > li[data-type=' + type + ']').clone();
164
- if (typeof value != 'undefined') {
165
- $new_item.find('input').val(value);
166
- }
167
- $new_item.hide().appendTo($ul).fadeIn('fast').find('input').focus();
168
-
169
- return $new_item;
170
- }
171
-
172
- /**
173
- * Restore fields from BooklyL10n.custom_fields.
174
- */
175
- function restoreFields() {
176
- if (BooklyL10n.custom_fields) {
177
- var custom_fields = jQuery.parseJSON(BooklyL10n.custom_fields);
178
- $.each(custom_fields, function (i, field) {
179
- var $new_field = addField(field.type, field.id, field.label, field.required, field.services);
180
- // add children
181
- if (field.items) {
182
- $.each(field.items, function (i, value) {
183
- addItem($new_field.find('ul.ab-items'), field.type + '-item', value);
184
- });
185
- }
186
- });
187
- }
188
- $cf_per_service.change();
189
- $('.ab--services-holder').each(function (id, elem) {
190
- updateServiceButton($(elem));
191
- });
192
- $(':focus').blur();
193
- }
194
-
195
- $('.ab-popover').popover({trigger: 'hover'});
196
-
197
- function updateServiceButton($holder) {
198
- var service_checked = $holder.find('.bookly-js-check-entity:checked').length;
199
- if (service_checked == 0) {
200
- $holder.find('.ab-count').text(BooklyL10n.selector.nothing_selected);
201
- $holder.find('.bookly-check-all-entities').prop('checked', false);
202
- } else if (service_checked == 1) {
203
- $holder.find('.ab-count').text($holder.find('.bookly-js-check-entity:checked').data('title'));
204
- $holder.find('.bookly-check-all-entities').prop('checked', (service_checked == $holder.find('.bookly-js-check-entity').length));
205
- } else {
206
- if( service_checked == $holder.find('.bookly-js-check-entity').length) {
207
- $holder.find('.bookly-check-all-entities').prop('checked', true);
208
- $holder.find('.ab-count').text(BooklyL10n.selector.all_selected);
209
- } else {
210
- $holder.find('.bookly-check-all-entities').prop('checked', false);
211
- $holder.find('.ab-count').text(service_checked + '/' + $holder.find('.bookly-js-check-entity').length);
212
- }
213
- }
214
- }
215
-
216
- $(document).on('click', '.bookly-check-all-entities', function () {
217
- var $holder = $(this).parents('.ab--services-holder');
218
- $holder.find('.bookly-js-check-entity').prop('checked', $(this).prop('checked'));
219
- updateServiceButton($holder);
220
- });
221
-
222
- $(document).on('click', '.ab--services-holder ul.dropdown-menu li a[href]', function (e) {
223
- updateServiceButton($(this).parents('.ab--services-holder'));
224
- e.stopPropagation();
225
- });
226
-
227
- $('[data-toggle="popover"]').popover({
228
- html: true,
229
- placement: 'top',
230
- trigger: 'hover',
231
- template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
232
- });
233
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/custom_fields/templates/_services.php DELETED
@@ -1,32 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
- <div class="btn-group ab--services-holder bookly-margin-top-screenxs-sm" style="display: none;">
3
- <button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
4
- <div class="bookly-flex-cell">
5
- <i class="glyphicon glyphicon-tag bookly-margin-right-md"></i>
6
- </div>
7
- <div class="bookly-flex-cell text-left" style="width: 100%">
8
- <span class="ab-count"></span>
9
- </div>
10
- <div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
11
- </button>
12
- <ul class="dropdown-menu bookly-entity-selector">
13
- <li>
14
- <a class="checkbox" href="javascript:void(0)">
15
- <label>
16
- <input type="checkbox" class="bookly-check-all-entities">
17
- <?php _e( 'All services', 'bookly' ) ?>
18
- </label>
19
- </a>
20
- </li>
21
- <?php foreach ( $services as $service ) : ?>
22
- <li>
23
- <a class="checkbox" href="javascript:void(0)">
24
- <label>
25
- <input type="checkbox" class="bookly-js-check-entity" value="<?php echo $service['id'] ?>" data-title="<?php echo esc_attr( $service['title'] ) ?>">
26
- <?php echo $service['title'] ?>
27
- </label>
28
- </a>
29
- </li>
30
- <?php endforeach ?>
31
- </ul>
32
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/custom_fields/templates/index.php DELETED
@@ -1,303 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
- <div id="bookly-tbs" class="wrap">
3
- <div class="bookly-tbs-body">
4
- <div class="page-header text-right clearfix">
5
- <div class="bookly-page-title">
6
- <?php _e( 'Custom Fields', 'bookly' ) ?>
7
- </div>
8
- <?php \BooklyLite\Backend\Modules\Support\Components::getInstance()->renderButtons( $this::page_slug ) ?>
9
- </div>
10
- <div class="panel panel-default bookly-main">
11
- <div class="panel-body">
12
- <div class="row">
13
- <div class="col-md-4">
14
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_custom_fields_per_service', __( 'Bind fields to services', 'bookly' ), __( 'When this setting is enabled you will be able to create service specific custom fields.', 'bookly' ) ) ?>
15
- </div>
16
- </div>
17
-
18
- <hr />
19
-
20
- <ul id="ab-custom-fields"></ul>
21
-
22
- <div id="ab-add-fields">
23
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="text-field"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Field', 'bookly' ) ?></button>
24
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="textarea"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Area', 'bookly' ) ?></button>
25
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="text-content"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Content', 'bookly' ) ?></button>
26
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="checkboxes"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Checkbox Group', 'bookly' ) ?></button>
27
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="radio-buttons"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button Group', 'bookly' ) ?></button>
28
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="drop-down"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Drop Down', 'bookly' ) ?></button>
29
- <button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="captcha"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Captcha', 'bookly' ) ?></button>
30
- </div>
31
- <p class="help-block"><?php _e( 'HTML allowed in all texts and labels.', 'bookly' ) ?></p>
32
-
33
- <ul id="ab-templates" style="display:none">
34
-
35
- <li data-type="textarea">
36
- <div class="bookly-flexbox">
37
- <div class="bookly-flex-cell">
38
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
39
- </div>
40
- <div class="bookly-flex-cell" style="width: 100%">
41
- <p><b><?php _e( 'Text Area', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
42
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
43
- <div class="row">
44
- <div class="col-md-8">
45
- <div class="input-group">
46
- <input class="ab-label form-control" type="text" value="" placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
47
- <label class="input-group-addon">
48
- <input class="ab-required" type="checkbox">
49
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
50
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
51
- </label>
52
- </div>
53
- </div>
54
- <div class="col-md-4">
55
- <?php echo $services_html ?>
56
- </div>
57
- </div>
58
- </div>
59
- </div>
60
- <hr>
61
- </li>
62
-
63
- <li data-type="text-content">
64
- <div class="bookly-flexbox">
65
- <div class="bookly-flex-cell">
66
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
67
- </div>
68
- <div class="bookly-flex-cell" style="width: 100%">
69
- <p><b><?php _e( 'Text Content', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
70
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
71
- <div class="row">
72
- <div class="col-md-8">
73
- <textarea class="ab-label form-control" type="text" rows="3"
74
- placeholder="<?php esc_attr_e( 'Enter a content', 'bookly' ) ?>"></textarea>
75
- <input class="ab-required hidden" type="checkbox" disabled="disabled">
76
- </div>
77
- <div class="col-md-4">
78
- <?php echo $services_html ?>
79
- </div>
80
- </div>
81
- </div>
82
- </div>
83
- <hr>
84
- </li>
85
-
86
- <li data-type="text-field">
87
- <div class="bookly-flexbox">
88
- <div class="bookly-flex-cell">
89
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
90
- </div>
91
- <div class="bookly-flex-cell" style="width: 100%">
92
- <p><b><?php _e( 'Text Field', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
93
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
94
- <div class="row">
95
- <div class="col-md-8">
96
- <div class="input-group">
97
- <input class="ab-label form-control" type="text" value=""
98
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
99
- <label class="input-group-addon">
100
- <input class="ab-required" type="checkbox">
101
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
102
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
103
- </label>
104
- </div>
105
- </div>
106
- <div class="col-md-4">
107
- <?php echo $services_html ?>
108
- </div>
109
- </div>
110
- </div>
111
- </div>
112
- <hr>
113
- </li>
114
-
115
- <li data-type="checkboxes">
116
- <div class="bookly-flexbox">
117
- <div class="bookly-flex-cell">
118
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
119
- </div>
120
- <div class="bookly-flex-cell" style="width: 100%">
121
- <p><b><?php _e( 'Checkbox Group', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
122
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
123
- <div class="row">
124
- <div class="col-md-8">
125
- <div class="input-group">
126
- <input class="ab-label form-control" type="text" value=""
127
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
128
- <label class="input-group-addon">
129
- <input class="ab-required" type="checkbox">
130
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
131
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
132
- </label>
133
- </div>
134
-
135
- <ul class="ab-items bookly-margin-top-sm"></ul>
136
- <button class="btn btn-sm btn-default" data-type="checkboxes-item">
137
- <i class="glyphicon glyphicon-plus"></i> <?php _e( 'Checkbox', 'bookly' ) ?>
138
- </button>
139
- </div>
140
- <div class="col-md-4">
141
- <?php echo $services_html ?>
142
- </div>
143
- </div>
144
- </div>
145
- </div>
146
- <hr>
147
- </li>
148
-
149
- <li data-type="radio-buttons">
150
- <div class="bookly-flexbox">
151
- <div class="bookly-flex-cell">
152
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
153
- </div>
154
- <div class="bookly-flex-cell" style="width: 100%">
155
- <p><b><?php _e( 'Radio Button Group', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
156
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
157
- <div class="row">
158
- <div class="col-md-8">
159
- <div class="input-group">
160
- <input class="ab-label form-control" type="text" value=""
161
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
162
- <label class="input-group-addon">
163
- <input class="ab-required" type="checkbox">
164
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
165
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
166
- </label>
167
- </div>
168
-
169
- <ul class="ab-items bookly-margin-top-sm"></ul>
170
- <button class="btn btn-sm btn-default" data-type="radio-buttons-item">
171
- <i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button', 'bookly' ) ?>
172
- </button>
173
- </div>
174
- <div class="col-md-4">
175
- <?php echo $services_html ?>
176
- </div>
177
- </div>
178
- </div>
179
- </div>
180
- <hr>
181
- </li>
182
-
183
- <li data-type="drop-down">
184
- <div class="bookly-flexbox">
185
- <div class="bookly-flex-cell">
186
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
187
- </div>
188
- <div class="bookly-flex-cell" style="width: 100%">
189
- <p><b><?php _e( 'Drop Down', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
190
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
191
- <div class="row">
192
- <div class="col-md-8">
193
- <div class="input-group">
194
- <input class="ab-label form-control" type="text" value=""
195
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
196
- <label class="input-group-addon">
197
- <input class="ab-required" type="checkbox">
198
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
199
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
200
- </label>
201
- </div>
202
-
203
- <ul class="ab-items bookly-margin-top-sm"></ul>
204
- <button class="btn btn-sm btn-default" data-type="drop-down-item">
205
- <i class="glyphicon glyphicon-plus"></i> <?php _e( 'Option', 'bookly' ) ?>
206
- </button>
207
- </div>
208
- <div class="col-md-4">
209
- <?php echo $services_html ?>
210
- </div>
211
- </div>
212
- </div>
213
- </div>
214
- <hr>
215
- </li>
216
-
217
- <li data-type="captcha">
218
- <div class="bookly-flexbox">
219
- <div class="bookly-flex-cell">
220
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
221
- </div>
222
- <div class="bookly-flex-cell" style="width: 100%">
223
- <p><b><?php _e( 'Captcha', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
224
- title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
225
- <div class="row">
226
- <div class="col-md-8">
227
- <div class="input-group">
228
- <input class="ab-label form-control" type="text" value=""
229
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
230
- <label class="input-group-addon">
231
- <input class="ab-required hidden" type="checkbox">
232
- <input type="checkbox" disabled="disabled" checked="checked">
233
- <span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
234
- <i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
235
- </label>
236
- </div>
237
- </div>
238
- <div class="col-md-4">
239
- <?php echo $services_html ?>
240
- </div>
241
- </div>
242
- </div>
243
- </div>
244
- <hr>
245
- </li>
246
-
247
- <li data-type="checkboxes-item">
248
- <div class="bookly-flexbox">
249
- <div class="bookly-flex-cell bookly-vertical-middle">
250
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
251
- </div>
252
- <div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
253
- <input class="form-control" type="text" value=""
254
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
255
- </div>
256
- <div class="bookly-flex-cell bookly-vertical-middle">
257
- <a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
258
- title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
259
- </div>
260
- </div>
261
- </li>
262
-
263
- <li data-type="radio-buttons-item">
264
- <div class="bookly-flexbox">
265
- <div class="bookly-flex-cell bookly-vertical-middle">
266
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
267
- </div>
268
- <div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
269
- <input class="form-control" type="text" value=""
270
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
271
- </div>
272
- <div class="bookly-flex-cell bookly-vertical-middle">
273
- <a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
274
- title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
275
- </div>
276
- </div>
277
- </li>
278
-
279
- <li data-type="drop-down-item">
280
- <div class="bookly-flexbox">
281
- <div class="bookly-flex-cell bookly-vertical-middle">
282
- <i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
283
- </div>
284
- <div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
285
- <input class="form-control" type="text" value=""
286
- placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
287
- </div>
288
- <div class="bookly-flex-cell bookly-vertical-middle">
289
- <a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
290
- title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
291
- </div>
292
- </div>
293
- </li>
294
- </ul>
295
- </div>
296
-
297
- <div class="panel-footer">
298
- <?php \BooklyLite\Lib\Utils\Common::submitButton( 'ajax-send-custom-fields' ) ?>
299
- <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
300
- </div>
301
- </div>
302
- </div>
303
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/customers/Components.php CHANGED
@@ -15,14 +15,20 @@ class Components extends Lib\Base\Components
15
  */
16
  public function renderCustomerDialog()
17
  {
 
 
18
  $this->enqueueStyles( array(
 
19
  'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
20
  ? array()
21
  : array( 'css/intlTelInput.css' ),
22
  ) );
23
 
24
  $this->enqueueScripts( array(
25
- 'backend' => array( 'js/angular.min.js' => array( 'jquery' ), ),
 
 
 
26
  'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
27
  ? array()
28
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) ),
@@ -30,12 +36,21 @@ class Components extends Lib\Base\Components
30
  ) );
31
 
32
  wp_localize_script( 'bookly-ng-customer_dialog.js', 'BooklyL10nCustDialog', array(
 
33
  'default_status' => get_option( 'bookly_gen_default_appointment_status' ),
34
  'intlTelInput' => array(
35
  'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
36
  'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
37
  'country' => get_option( 'bookly_cst_phone_default_country' ),
38
  ),
 
 
 
 
 
 
 
 
39
  ) );
40
 
41
  $this->render( '_customer_dialog' );
15
  */
16
  public function renderCustomerDialog()
17
  {
18
+ global $wp_locale;
19
+
20
  $this->enqueueStyles( array(
21
+ 'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', ),
22
  'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
23
  ? array()
24
  : array( 'css/intlTelInput.css' ),
25
  ) );
26
 
27
  $this->enqueueScripts( array(
28
+ 'backend' => array(
29
+ 'js/angular.min.js' => array( 'jquery' ),
30
+ 'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js', 'jquery-ui-datepicker' ),
31
+ ),
32
  'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
33
  ? array()
34
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) ),
36
  ) );
37
 
38
  wp_localize_script( 'bookly-ng-customer_dialog.js', 'BooklyL10nCustDialog', array(
39
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
40
  'default_status' => get_option( 'bookly_gen_default_appointment_status' ),
41
  'intlTelInput' => array(
42
  'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
43
  'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
44
  'country' => get_option( 'bookly_cst_phone_default_country' ),
45
  ),
46
+ 'dateOptions' => array(
47
+ 'dateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
48
+ 'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
49
+ 'monthNames' => array_values( $wp_locale->month ),
50
+ 'dayNamesMin' => array_values( $wp_locale->weekday_abbrev ),
51
+ 'longDays' => array_values( $wp_locale->weekday ),
52
+ 'firstDay' => (int) get_option( 'start_of_week' ),
53
+ ),
54
  ) );
55
 
56
  $this->render( '_customer_dialog' );
backend/modules/customers/Controller.php CHANGED
@@ -33,7 +33,6 @@ class Controller extends Lib\Base\Controller
33
  'backend' => array(
34
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
35
  'js/datatables.min.js' => array( 'jquery' ),
36
- 'js/alert.js' => array( 'jquery' ),
37
  ),
38
  'frontend' => array(
39
  'js/spin.min.js' => array( 'jquery' ),
@@ -45,6 +44,8 @@ class Controller extends Lib\Base\Controller
45
  ) );
46
 
47
  wp_localize_script( 'bookly-customers.js', 'BooklyL10n', array(
 
 
48
  'edit' => __( 'Edit', 'bookly' ),
49
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
50
  'wp_users' => get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) ),
@@ -55,7 +56,7 @@ class Controller extends Lib\Base\Controller
55
  'create_customer' => __( 'Create customer', 'bookly' ),
56
  'save' => __( 'Save', 'bookly' ),
57
  'search' => __( 'Quick search customer', 'bookly' ),
58
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
59
  ) );
60
 
61
  $this->render( 'index' );
@@ -101,7 +102,7 @@ class Controller extends Lib\Base\Controller
101
  if ( $filter != '' ) {
102
  $search_value = Lib\Query::escape( $filter );
103
  $query
104
- ->whereLike( 'c.name', "%{$search_value}%" )
105
  ->whereLike( 'c.phone', "%{$search_value}%", 'OR' )
106
  ->whereLike( 'c.email', "%{$search_value}%", 'OR' );
107
  }
@@ -117,15 +118,18 @@ class Controller extends Lib\Base\Controller
117
  foreach ( $query->fetchArray() as $row ) {
118
  $data[] = array(
119
  'id' => $row['id'],
120
- 'name' => $row['name'],
 
 
121
  'wp_user' => $row['wp_user'],
122
  'wp_user_id' => $row['wp_user_id'],
123
  'phone' => $row['phone'],
124
  'email' => $row['email'],
125
  'notes' => $row['notes'],
 
126
  'last_appointment' => $row['last_appointment'] ? Lib\Utils\DateTime::formatDateTime( $row['last_appointment'] ) : '',
127
  'total_appointments' => $row['total_appointments'],
128
- 'payments' => Lib\Utils\Common::formatPrice( $row['payments'] ),
129
  );
130
  }
131
 
@@ -146,35 +150,45 @@ class Controller extends Lib\Base\Controller
146
  $form = new Forms\Customer();
147
 
148
  do {
149
- if ( $this->getParameter( 'name' ) !== '' ) {
150
  $params = $this->getPostParameters();
151
  if ( ! $params['wp_user_id'] ) {
152
  $params['wp_user_id'] = null;
153
  }
 
 
 
154
  $form->bind( $params );
155
  /** @var Lib\Entities\Customer $customer */
156
  $customer = $form->save();
157
  if ( $customer ) {
158
  $response['success'] = true;
159
  $response['customer'] = array(
160
- 'id' => $customer->get( 'id' ),
161
- 'name' => $customer->get( 'name' ),
162
- 'phone' => $customer->get( 'phone' ),
163
- 'email' => $customer->get( 'email' ),
164
- 'notes' => $customer->get( 'notes' ),
165
- 'wp_user_id' => $customer->get( 'wp_user_id' ),
166
- 'jsonString' => json_encode( array(
167
- 'name' => $customer->get( 'name' ),
168
- 'phone' => $customer->get( 'phone' ),
169
- 'email' => $customer->get( 'email' ),
170
- 'notes' => $customer->get( 'notes' )
171
- ) )
172
  );
173
  break;
174
  }
175
  }
176
  $response['success'] = false;
177
- $response['errors'] = array( 'name' => array( 'required' ) );
 
 
 
 
 
 
 
 
 
 
178
  } while ( 0 );
179
 
180
  wp_send_json( $response );
@@ -186,17 +200,28 @@ class Controller extends Lib\Base\Controller
186
  private function importCustomers()
187
  {
188
  @ini_set( 'auto_detect_line_endings', true );
189
-
 
 
 
 
 
190
  $file = fopen( $_FILES['import_customers_file']['tmp_name'], 'r' );
191
  while ( $line = fgetcsv( $file, null, $this->getParameter( 'import_customers_delimiter' ) ) ) {
192
  if ( $line[0] != '' ) {
193
  $customer = new Lib\Entities\Customer();
194
- $customer->set( 'name', $line[0] );
195
- if ( isset( $line[1] ) ) {
196
- $customer->set( 'phone', $line[1] );
197
- }
198
- if ( isset( $line[2] ) ) {
199
- $customer->set( 'email', $line[2] );
 
 
 
 
 
 
200
  }
201
  $customer->save();
202
  }
@@ -221,15 +246,4 @@ class Controller extends Lib\Base\Controller
221
  */
222
  public function executeExportCustomers() { exit; }
223
 
224
- /**
225
- * Override parent method to add 'wp_ajax_bookly_' prefix
226
- * so current 'execute*' methods look nicer.
227
- *
228
- * @param string $prefix
229
- */
230
- protected function registerWpActions( $prefix = '' )
231
- {
232
- parent::registerWpActions( 'wp_ajax_bookly_' );
233
- }
234
-
235
  }
33
  'backend' => array(
34
  'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
35
  'js/datatables.min.js' => array( 'jquery' ),
 
36
  ),
37
  'frontend' => array(
38
  'js/spin.min.js' => array( 'jquery' ),
44
  ) );
45
 
46
  wp_localize_script( 'bookly-customers.js', 'BooklyL10n', array(
47
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
48
+ 'first_last_name' => (int) Lib\Config::showFirstLastName(),
49
  'edit' => __( 'Edit', 'bookly' ),
50
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
51
  'wp_users' => get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) ),
56
  'create_customer' => __( 'Create customer', 'bookly' ),
57
  'save' => __( 'Save', 'bookly' ),
58
  'search' => __( 'Quick search customer', 'bookly' ),
59
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
60
  ) );
61
 
62
  $this->render( 'index' );
102
  if ( $filter != '' ) {
103
  $search_value = Lib\Query::escape( $filter );
104
  $query
105
+ ->whereLike( 'c.full_name', "%{$search_value}%" )
106
  ->whereLike( 'c.phone', "%{$search_value}%", 'OR' )
107
  ->whereLike( 'c.email', "%{$search_value}%", 'OR' );
108
  }
118
  foreach ( $query->fetchArray() as $row ) {
119
  $data[] = array(
120
  'id' => $row['id'],
121
+ 'full_name' => $row['full_name'],
122
+ 'first_name' => $row['first_name'],
123
+ 'last_name' => $row['last_name'],
124
  'wp_user' => $row['wp_user'],
125
  'wp_user_id' => $row['wp_user_id'],
126
  'phone' => $row['phone'],
127
  'email' => $row['email'],
128
  'notes' => $row['notes'],
129
+ 'birthday' => $row['birthday'],
130
  'last_appointment' => $row['last_appointment'] ? Lib\Utils\DateTime::formatDateTime( $row['last_appointment'] ) : '',
131
  'total_appointments' => $row['total_appointments'],
132
+ 'payments' => Lib\Utils\Price::format( $row['payments'] ),
133
  );
134
  }
135
 
150
  $form = new Forms\Customer();
151
 
152
  do {
153
+ if ( ( get_option( 'bookly_cst_first_last_name' ) && $this->getParameter( 'first_name' ) !== '' && $this->getParameter( 'last_name' ) !== '' ) || ( ! get_option( 'bookly_cst_first_last_name' ) && $this->getParameter( 'full_name' ) !== '' ) ) {
154
  $params = $this->getPostParameters();
155
  if ( ! $params['wp_user_id'] ) {
156
  $params['wp_user_id'] = null;
157
  }
158
+ if ( ! $params['birthday'] ) {
159
+ $params['birthday'] = null;
160
+ }
161
  $form->bind( $params );
162
  /** @var Lib\Entities\Customer $customer */
163
  $customer = $form->save();
164
  if ( $customer ) {
165
  $response['success'] = true;
166
  $response['customer'] = array(
167
+ 'id' => $customer->getId(),
168
+ 'wp_user_id' => $customer->getWpUserId(),
169
+ 'full_name' => $customer->getFullName(),
170
+ 'first_name' => $customer->getFirstName(),
171
+ 'last_name' => $customer->getLastName(),
172
+ 'phone' => $customer->getPhone(),
173
+ 'email' => $customer->getEmail(),
174
+ 'notes' => $customer->getNotes(),
175
+ 'birthday' => $customer->getBirthday(),
 
 
 
176
  );
177
  break;
178
  }
179
  }
180
  $response['success'] = false;
181
+ $response['errors'] = array();
182
+ if (get_option( 'bookly_cst_first_last_name' )) {
183
+ if ( $this->getParameter( 'first_name' ) == '' ) {
184
+ $response['errors']['first_name'] = array( 'required' );
185
+ }
186
+ if ( $this->getParameter( 'last_name' ) == '' ) {
187
+ $response['errors']['last_name'] = array( 'required' );
188
+ }
189
+ } else {
190
+ $response['errors'] = array( 'full_name' => array( 'required' ) );
191
+ }
192
  } while ( 0 );
193
 
194
  wp_send_json( $response );
200
  private function importCustomers()
201
  {
202
  @ini_set( 'auto_detect_line_endings', true );
203
+ $fields = array();
204
+ foreach ( array( 'full_name', 'first_name', 'last_name', 'phone', 'email', 'birthday' ) as $field ) {
205
+ if ( $this->getParameter( $field ) ) {
206
+ $fields[] = $field;
207
+ }
208
+ }
209
  $file = fopen( $_FILES['import_customers_file']['tmp_name'], 'r' );
210
  while ( $line = fgetcsv( $file, null, $this->getParameter( 'import_customers_delimiter' ) ) ) {
211
  if ( $line[0] != '' ) {
212
  $customer = new Lib\Entities\Customer();
213
+ foreach ( $line as $number => $value ) {
214
+ if ( $number < count( $fields ) ) {
215
+ if ( $fields[ $number ] == 'birthday' ) {
216
+ $dob = date_create( $value );
217
+ if ( $dob !== false ) {
218
+ $customer->setBirthday( $dob->format( 'Y-m-d' ) );
219
+ }
220
+ } else {
221
+ $method = 'set' . implode( '', array_map( 'ucfirst', explode( '_', $fields[ $number ] ) ) );
222
+ $customer->$method( $value );
223
+ }
224
+ }
225
  }
226
  $customer->save();
227
  }
246
  */
247
  public function executeExportCustomers() { exit; }
248
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
backend/modules/customers/forms/Customer.php CHANGED
@@ -14,11 +14,14 @@ class Customer extends Lib\Base\Form
14
  public function configure()
15
  {
16
  $this->setFields( array(
17
- 'name',
18
  'wp_user_id',
 
 
 
19
  'phone',
20
  'email',
21
- 'notes'
 
22
  ) );
23
  }
24
 
14
  public function configure()
15
  {
16
  $this->setFields( array(
 
17
  'wp_user_id',
18
+ 'full_name',
19
+ 'first_name',
20
+ 'last_name',
21
  'phone',
22
  'email',
23
+ 'notes',
24
+ 'birthday',
25
  ) );
26
  }
27
 
backend/modules/customers/resources/js/customers.js CHANGED
@@ -29,16 +29,20 @@ jQuery(function($) {
29
  responsive: true,
30
  serverSide: true,
31
  ajax: {
32
- url: ajaxurl,
 
33
  data: function (d) {
34
  return $.extend({}, d, {
35
  action: 'bookly_get_customers',
 
36
  filter: $filter.val()
37
  });
38
  }
39
  },
40
  columns: [
41
- { data: 'name', render: $.fn.dataTable.render.text() },
 
 
42
  { data: 'wp_user', render: $.fn.dataTable.render.text() },
43
  { data: 'phone', render: $.fn.dataTable.render.text() },
44
  { data: 'email', render: $.fn.dataTable.render.text() },
@@ -73,14 +77,14 @@ jQuery(function($) {
73
  });
74
 
75
  /**
76
- * Select all coupons.
77
  */
78
  $check_all_button.on('change', function () {
79
  $customers_list.find('tbody input:checkbox').prop('checked', this.checked);
80
  });
81
 
82
  /**
83
- * On coupon select.
84
  */
85
  $customers_list.on('change', 'tbody input:checkbox', function () {
86
  $check_all_button.prop('checked', $customers_list.find('tbody input:not(:checked)').length == 0);
@@ -94,7 +98,7 @@ jQuery(function($) {
94
  });
95
 
96
  /**
97
- * New coupon.
98
  */
99
  $add_button.on('click', function () {
100
  row = null;
@@ -108,30 +112,28 @@ jQuery(function($) {
108
  var $button = $customer_dialog.find('.modal-footer button:first');
109
  var customer;
110
  if (row) {
111
- customer = row.data();
112
  $title.text(BooklyL10n.edit_customer);
113
  $button.text(BooklyL10n.save);
114
  } else {
115
  customer = {
116
  id : '',
117
  wp_user_id : '',
118
- name : '',
 
 
119
  phone : '',
120
  email : '',
121
- notes : ''
 
122
  };
123
  $title.text(BooklyL10n.new_customer);
124
- $button.text(BooklyL10n.create);
125
  }
126
 
127
  var $scope = angular.element(this).scope();
128
  $scope.$apply(function ($scope) {
129
- $scope.customer.id = customer.id;
130
- $scope.customer.wp_user_id = customer.wp_user_id;
131
- $scope.customer.name = customer.name;
132
- $scope.customer.phone = customer.phone;
133
- $scope.customer.email = customer.email;
134
- $scope.customer.notes = customer.notes;
135
  setTimeout(function() {
136
  $customer_dialog.find('#phone').intlTelInput('setNumber', customer.phone);
137
  }, 0);
@@ -142,7 +144,7 @@ jQuery(function($) {
142
  * Delete customers.
143
  */
144
  $delete_button.on('click', function () {
145
- if (!remembered_choice) {
146
  $delete_dialog.modal('show');
147
  } else {
148
  deleteCustomers(this, remembered_choice);
@@ -178,8 +180,9 @@ jQuery(function($) {
178
  type : 'POST',
179
  data : {
180
  action : 'bookly_delete_customers',
 
181
  data : data,
182
- with_wp_user : with_wp_user
183
  },
184
  dataType : 'json',
185
  success : function(response) {
@@ -210,10 +213,13 @@ jQuery(function($) {
210
  $scope.customer = {
211
  id : '',
212
  wp_user_id : '',
213
- name : '',
 
 
214
  phone : '',
215
  email : '',
216
- notes : ''
 
217
  };
218
  $scope.saveCustomer = function(customer) {
219
  jQuery('#bookly-customers-list').DataTable().ajax.reload(null, false);
29
  responsive: true,
30
  serverSide: true,
31
  ajax: {
32
+ url : ajaxurl,
33
+ type: 'POST',
34
  data: function (d) {
35
  return $.extend({}, d, {
36
  action: 'bookly_get_customers',
37
+ csrf_token : BooklyL10n.csrf_token,
38
  filter: $filter.val()
39
  });
40
  }
41
  },
42
  columns: [
43
+ { data: 'full_name', render: $.fn.dataTable.render.text(), visible: BooklyL10n.first_last_name == 0 },
44
+ { data: 'first_name', render: $.fn.dataTable.render.text(), visible: BooklyL10n.first_last_name == 1 },
45
+ { data: 'last_name', render: $.fn.dataTable.render.text(), visible: BooklyL10n.first_last_name == 1 },
46
  { data: 'wp_user', render: $.fn.dataTable.render.text() },
47
  { data: 'phone', render: $.fn.dataTable.render.text() },
48
  { data: 'email', render: $.fn.dataTable.render.text() },
77
  });
78
 
79
  /**
80
+ * Select all customers.
81
  */
82
  $check_all_button.on('change', function () {
83
  $customers_list.find('tbody input:checkbox').prop('checked', this.checked);
84
  });
85
 
86
  /**
87
+ * On customer select.
88
  */
89
  $customers_list.on('change', 'tbody input:checkbox', function () {
90
  $check_all_button.prop('checked', $customers_list.find('tbody input:not(:checked)').length == 0);
98
  });
99
 
100
  /**
101
+ * New customer.
102
  */
103
  $add_button.on('click', function () {
104
  row = null;
112
  var $button = $customer_dialog.find('.modal-footer button:first');
113
  var customer;
114
  if (row) {
115
+ customer = $.extend({}, row.data());
116
  $title.text(BooklyL10n.edit_customer);
117
  $button.text(BooklyL10n.save);
118
  } else {
119
  customer = {
120
  id : '',
121
  wp_user_id : '',
122
+ full_name : '',
123
+ first_name : '',
124
+ last_name : '',
125
  phone : '',
126
  email : '',
127
+ notes : '',
128
+ birthday : ''
129
  };
130
  $title.text(BooklyL10n.new_customer);
131
+ $button.text(BooklyL10n.create_customer);
132
  }
133
 
134
  var $scope = angular.element(this).scope();
135
  $scope.$apply(function ($scope) {
136
+ $scope.customer = customer;
 
 
 
 
 
137
  setTimeout(function() {
138
  $customer_dialog.find('#phone').intlTelInput('setNumber', customer.phone);
139
  }, 0);
144
  * Delete customers.
145
  */
146
  $delete_button.on('click', function () {
147
+ if (remembered_choice === undefined) {
148
  $delete_dialog.modal('show');
149
  } else {
150
  deleteCustomers(this, remembered_choice);
180
  type : 'POST',
181
  data : {
182
  action : 'bookly_delete_customers',
183
+ csrf_token : BooklyL10n.csrf_token,
184
  data : data,
185
+ with_wp_user : with_wp_user ? 1 : 0
186
  },
187
  dataType : 'json',
188
  success : function(response) {
213
  $scope.customer = {
214
  id : '',
215
  wp_user_id : '',
216
+ full_name : '',
217
+ first_name : '',
218
+ last_name : '',
219
  phone : '',
220
  email : '',
221
+ notes : '',
222
+ birthday : ''
223
  };
224
  $scope.saveCustomer = function(customer) {
225
  jQuery('#bookly-customers-list').DataTable().ajax.reload(null, false);
backend/modules/customers/resources/js/ng-customer_dialog.js CHANGED
@@ -1,68 +1,65 @@
1
  ;(function() {
2
 
3
- angular.module('customerDialog', []).directive('customerDialog', function() {
4
  return {
5
  restrict : 'A',
6
  replace : true,
7
  scope : {
8
- callback : '&customerDialog',
9
- form : '=customer'
10
  },
11
  templateUrl : 'bookly-customer-dialog.tpl',
12
  // The linking function will add behavior to the template.
13
  link: function(scope, element, attrs) {
14
  // Init properties.
15
- var init = function() {
16
- // Form fields.
17
- if (!scope.form) {
18
- scope.form = {
19
- id : '',
20
- wp_user_id : '',
21
- name : '',
22
- phone : '',
23
- email : '',
24
- notes : ''
25
- };
26
- }
27
- if (BooklyL10nCustDialog.intlTelInput.enabled) {
28
- element.find('#phone').intlTelInput({
29
- preferredCountries: [BooklyL10nCustDialog.intlTelInput.country],
30
- defaultCountry: BooklyL10nCustDialog.intlTelInput.country,
31
- geoIpLookup: function (callback) {
32
- jQuery.get(ajaxurl, {action: 'bookly_ip_info'}, function () {
33
- }, 'json').always(function (resp) {
34
- var countryCode = (resp && resp.country) ? resp.country : '';
35
- callback(countryCode);
36
- });
37
- },
38
- utilsScript: BooklyL10nCustDialog.intlTelInput.utils
39
- });
40
- }
41
- // Form errors.
42
- scope.errors = {
43
- name: {required: false}
44
  };
45
- // Loading indicator.
46
- scope.loading = false;
47
-
48
- jQuery('#bookly-customer-dialog').modal('hide')
49
- .on('hidden.bs.modal', function () {
50
- if (jQuery('#bookly-appointment-dialog').length) {
51
- jQuery('body').addClass('modal-open');
52
- }
53
- });
54
  };
 
 
 
 
 
 
 
55
 
56
- // Run init.
57
- init();
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- // On 'Cancel' button click.
60
- scope.closeDialog = function () {
61
- // Close the dialog.
62
- jQuery('#bookly-customer-dialog').modal('hide');
63
- // Re-init all properties.
64
- init();
65
- };
66
 
67
  /**
68
  * Send form to server.
@@ -76,7 +73,7 @@
76
  jQuery.ajax({
77
  url : ajaxurl,
78
  type : 'POST',
79
- data : jQuery.extend({ action : 'bookly_save_customer' }, scope.form),
80
  dataType : 'json',
81
  success : function ( response ) {
82
  scope.$apply(function(scope) {
@@ -89,13 +86,16 @@
89
  scope.form = {
90
  id : '',
91
  wp_user_id : '',
92
- name : '',
 
 
93
  phone : '',
94
  email : '',
95
- notes : ''
 
96
  };
97
  // Close the dialog.
98
- scope.closeDialog();
99
  } else {
100
  // Set errors.
101
  jQuery.each(response.errors, function(field, errors) {
@@ -115,6 +115,11 @@
115
  }
116
  });
117
  };
 
 
 
 
 
118
  }
119
  };
120
  });
1
  ;(function() {
2
 
3
+ angular.module('customerDialog', ['ui.date']).directive('customerDialog', function() {
4
  return {
5
  restrict : 'A',
6
  replace : true,
7
  scope : {
8
+ callback : '&customerDialog',
9
+ form : '=customer'
10
  },
11
  templateUrl : 'bookly-customer-dialog.tpl',
12
  // The linking function will add behavior to the template.
13
  link: function(scope, element, attrs) {
14
  // Init properties.
15
+ // Form fields.
16
+ if (!scope.form) {
17
+ scope.form = {
18
+ id : '',
19
+ wp_user_id : '',
20
+ full_name : '',
21
+ first_name : '',
22
+ last_name : '',
23
+ phone : '',
24
+ email : '',
25
+ notes : '',
26
+ birthday : ''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  };
28
+ }
29
+ // Form errors.
30
+ scope.errors = {
31
+ name: {required: false}
 
 
 
 
 
32
  };
33
+ scope.$watch('form', function(newValue, oldValue) {
34
+ if (newValue.name) {
35
+ scope.errors.name.required = false;
36
+ }
37
+ });
38
+ // Loading indicator.
39
+ scope.loading = false;
40
 
41
+ // Init intlTelInput.
42
+ if (BooklyL10nCustDialog.intlTelInput.enabled) {
43
+ element.find('#phone').intlTelInput({
44
+ preferredCountries: [BooklyL10nCustDialog.intlTelInput.country],
45
+ initialCountry: BooklyL10nCustDialog.intlTelInput.country,
46
+ geoIpLookup: function (callback) {
47
+ jQuery.get('https://ipinfo.io', function() {}, 'jsonp').always(function(resp) {
48
+ var countryCode = (resp && resp.country) ? resp.country : '';
49
+ callback(countryCode);
50
+ });
51
+ },
52
+ utilsScript: BooklyL10nCustDialog.intlTelInput.utils
53
+ });
54
+ }
55
 
56
+ // Do stuff on modal hide.
57
+ element.on('hidden.bs.modal', function () {
58
+ // Fix scroll issues when another modal is shown.
59
+ if (jQuery('.modal-backdrop').length) {
60
+ jQuery('body').addClass('modal-open');
61
+ }
62
+ });
63
 
64
  /**
65
  * Send form to server.
73
  jQuery.ajax({
74
  url : ajaxurl,
75
  type : 'POST',
76
+ data : jQuery.extend({ action : 'bookly_save_customer', csrf_token : BooklyL10nCustDialog.csrf_token }, scope.form),
77
  dataType : 'json',
78
  success : function ( response ) {
79
  scope.$apply(function(scope) {
86
  scope.form = {
87
  id : '',
88
  wp_user_id : '',
89
+ full_name : '',
90
+ first_name : '',
91
+ last_name : '',
92
  phone : '',
93
  email : '',
94
+ notes : '',
95
+ birthday : ''
96
  };
97
  // Close the dialog.
98
+ element.modal('hide');
99
  } else {
100
  // Set errors.
101
  jQuery.each(response.errors, function(field, errors) {
115
  }
116
  });
117
  };
118
+
119
+ /**
120
+ * Datepicker options.
121
+ */
122
+ scope.dateOptions = BooklyL10nCustDialog.dateOptions;
123
  }
124
  };
125
  });
backend/modules/customers/templates/_customer_dialog.php CHANGED
@@ -1,4 +1,7 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
2
  <script type="text/ng-template" id="bookly-customer-dialog.tpl">
3
  <div id="bookly-customer-dialog" class="modal fade" tabindex=-1 role="dialog">
4
  <div class="modal-dialog">
@@ -23,31 +26,54 @@
23
  </select>
24
  </div>
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  <div class="form-group">
27
- <label for="username"><?php _e( 'Name', 'bookly' ) ?></label>
28
- <input class="form-control" type="text" ng-model="form.name" id="username">
29
- <span style="font-size: 11px;color: red" ng-show="errors.name.required"><?php _e( 'Required', 'bookly' ) ?></span>
30
  </div>
 
31
 
32
  <div class="form-group">
33
  <label for="phone"><?php _e( 'Phone', 'bookly' ) ?></label>
34
- <input class="form-control" type="text" ng-model=form.phone id="phone">
35
  </div>
36
 
37
  <div class="form-group">
38
  <label for="email"><?php _e( 'Email', 'bookly' ) ?></label>
39
- <input class="form-control" type="text" ng-model=form.email id="email">
40
  </div>
41
 
42
  <div class="form-group">
43
  <label for="notes"><?php _e( 'Notes', 'bookly' ) ?></label>
44
  <textarea class="form-control" ng-model=form.notes id="notes"></textarea>
45
  </div>
 
 
 
 
 
 
46
  </div>
47
  <div class="modal-footer">
48
  <div ng-hide=loading>
49
- <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn-success btn-lg', __( 'Create customer', 'bookly' ), array( 'ng-click' => 'processForm()' ) ) ?>
50
- <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn-default btn-lg', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
51
  </div>
52
  </div>
53
  </div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Common;
3
+ use BooklyLite\Lib\Config;
4
+ ?>
5
  <script type="text/ng-template" id="bookly-customer-dialog.tpl">
6
  <div id="bookly-customer-dialog" class="modal fade" tabindex=-1 role="dialog">
7
  <div class="modal-dialog">
26
  </select>
27
  </div>
28
 
29
+ <?php if ( Config::showFirstLastName() ) : ?>
30
+ <div class="form-group">
31
+ <div class="row">
32
+ <div class="col-sm-6">
33
+ <label for="first_name"><?php _e( 'First name', 'bookly' ) ?></label>
34
+ <input class="form-control" type="text" ng-model="form.first_name" id="first_name" />
35
+ <span style="font-size: 11px;color: red" ng-show="errors.first_name.required"><?php _e( 'Required', 'bookly' ) ?></span>
36
+ </div>
37
+ <div class="col-sm-6">
38
+ <label for="last_name"><?php _e( 'Last name', 'bookly' ) ?></label>
39
+ <input class="form-control" type="text" ng-model="form.last_name" id="last_name" />
40
+ <span style="font-size: 11px;color: red" ng-show="errors.last_name.required"><?php _e( 'Required', 'bookly' ) ?></span>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ <?php else : ?>
45
  <div class="form-group">
46
+ <label for="full_name"><?php _e( 'Name', 'bookly' ) ?></label>
47
+ <input class="form-control" type="text" ng-model="form.full_name" id="full_name" />
48
+ <span style="font-size: 11px;color: red" ng-show="errors.full_name.required"><?php _e( 'Required', 'bookly' ) ?></span>
49
  </div>
50
+ <?php endif ?>
51
 
52
  <div class="form-group">
53
  <label for="phone"><?php _e( 'Phone', 'bookly' ) ?></label>
54
+ <input class="form-control" type="text" ng-model=form.phone id="phone" />
55
  </div>
56
 
57
  <div class="form-group">
58
  <label for="email"><?php _e( 'Email', 'bookly' ) ?></label>
59
+ <input class="form-control" type="text" ng-model=form.email id="email" />
60
  </div>
61
 
62
  <div class="form-group">
63
  <label for="notes"><?php _e( 'Notes', 'bookly' ) ?></label>
64
  <textarea class="form-control" ng-model=form.notes id="notes"></textarea>
65
  </div>
66
+
67
+ <div class="form-group">
68
+ <label for="birthday"><?php _e( 'Date of birth', 'bookly' ) ?></label>
69
+ <input class="form-control" type="text" ng-model=form.birthday id="birthday"
70
+ ui-date="dateOptions" ui-date-format="yy-mm-dd" autocomplete="off" />
71
+ </div>
72
  </div>
73
  <div class="modal-footer">
74
  <div ng-hide=loading>
75
+ <?php Common::customButton( null, 'btn-success btn-lg', '', array( 'ng-click' => 'processForm()' ) ) ?>
76
+ <?php Common::customButton( null, 'btn-default btn-lg', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
77
  </div>
78
  </div>
79
  </div>
backend/modules/customers/templates/_import.php CHANGED
@@ -10,12 +10,20 @@
10
  <div class="modal-body">
11
  <h4><?php _e( 'Note', 'bookly' ) ?></h4>
12
  <p>
13
- <?php _e( 'You may import list of clients in CSV format. The file needs to have three columns: Name, Phone and Email.', 'bookly' ) ?>
14
  </p>
15
  <div class="form-group">
16
  <label for="import_customers_file"><?php _e( 'Select file', 'bookly' ) ?></label>
17
  <input name="import_customers_file" id="import_customers_file" type="file">
18
  </div>
 
 
 
 
 
 
 
 
19
  <div class="form-group">
20
  <label for="import_customers_delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
21
  <select name="import_customers_delimiter" id="import_customers_delimiter" class="form-control">
@@ -26,7 +34,7 @@
26
  <input type="hidden" name="import">
27
  </div>
28
  <div class="modal-footer">
29
- <button type="submit" class="btn btn-lg btn-success ab-popup-save" name="import-customers">
30
  <?php _e( 'Import', 'bookly' ) ?>
31
  </button>
32
  </div>
10
  <div class="modal-body">
11
  <h4><?php _e( 'Note', 'bookly' ) ?></h4>
12
  <p>
13
+ <?php _e( 'You may import list of clients in CSV format. You can choose the columns contained in your file. The sequence of columns should coincide with the specified one.', 'bookly' ) ?>
14
  </p>
15
  <div class="form-group">
16
  <label for="import_customers_file"><?php _e( 'Select file', 'bookly' ) ?></label>
17
  <input name="import_customers_file" id="import_customers_file" type="file">
18
  </div>
19
+ <div class="form-group">
20
+ <div class="checkbox"><label><input checked name="full_name" type="checkbox"> <?php echo esc_html( get_option( 'bookly_l10n_label_name' ) ) ?></label></div>
21
+ <div class="checkbox"><label><input name="first_name" type="checkbox"> <?php echo esc_html( get_option( 'bookly_l10n_label_first_name' ) ) ?></label></div>
22
+ <div class="checkbox"><label><input name="last_name" type="checkbox"> <?php echo esc_html( get_option( 'bookly_l10n_label_last_name' ) ) ?></label></div>
23
+ <div class="checkbox"><label><input checked name="phone" type="checkbox"><?php echo esc_html( get_option( 'bookly_l10n_label_phone' ) ) ?></label></div>
24
+ <div class="checkbox"><label><input checked name="email" type="checkbox"><?php echo esc_html( get_option( 'bookly_l10n_label_email' ) ) ?></label></div>
25
+ <div class="checkbox"><label><input checked name="birthday" type="checkbox"><?php _e( 'Date of birth', 'bookly' ) ?></label></div>
26
+ </div>
27
  <div class="form-group">
28
  <label for="import_customers_delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
29
  <select name="import_customers_delimiter" id="import_customers_delimiter" class="form-control">
34
  <input type="hidden" name="import">
35
  </div>
36
  <div class="modal-footer">
37
+ <button type="submit" class="btn btn-lg btn-success" name="import-customers">
38
  <?php _e( 'Import', 'bookly' ) ?>
39
  </button>
40
  </div>
backend/modules/customers/templates/index.php CHANGED
@@ -31,10 +31,12 @@
31
  <table id="bookly-customers-list" class="table table-striped" width="100%">
32
  <thead>
33
  <tr>
34
- <th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_name' ) ?></th>
 
 
35
  <th><?php _e( 'User', 'bookly' ) ?></th>
36
- <th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_phone' ) ?></th>
37
- <th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_email' ) ?></th>
38
  <th><?php _e( 'Notes', 'bookly' ) ?></th>
39
  <th><?php _e( 'Last appointment', 'bookly' ) ?></th>
40
  <th><?php _e( 'Total appointments', 'bookly' ) ?></th>
@@ -52,7 +54,6 @@
52
  </div>
53
 
54
  <?php include '_import.php' ?>
55
- <?php include '_export.php' ?>
56
 
57
  <div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
58
  <div class="modal-dialog">
31
  <table id="bookly-customers-list" class="table table-striped" width="100%">
32
  <thead>
33
  <tr>
34
+ <th><?php echo esc_html( get_option( 'bookly_l10n_label_name' ) ) ?></th>
35
+ <th><?php echo esc_html( get_option( 'bookly_l10n_label_first_name' ) ) ?></th>
36
+ <th><?php echo esc_html( get_option( 'bookly_l10n_label_last_name' ) ) ?></th>
37
  <th><?php _e( 'User', 'bookly' ) ?></th>
38
+ <th><?php echo esc_html( get_option( 'bookly_l10n_label_phone' ) ) ?></th>
39
+ <th><?php echo esc_html( get_option( 'bookly_l10n_label_email' ) ) ?></th>
40
  <th><?php _e( 'Notes', 'bookly' ) ?></th>
41
  <th><?php _e( 'Last appointment', 'bookly' ) ?></th>
42
  <th><?php _e( 'Total appointments', 'bookly' ) ?></th>
54
  </div>
55
 
56
  <?php include '_import.php' ?>
 
57
 
58
  <div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
59
  <div class="modal-dialog">
backend/modules/debug/Controller.php CHANGED
@@ -86,19 +86,16 @@ class Controller extends Lib\Base\Controller
86
  global $wpdb;
87
 
88
  $result = array();
 
89
 
90
- $installer_class = Lib\Plugin::getRootNamespace() . '\Lib\Installer';
91
- /** @var Lib\Base\Installer $installer */
92
- $installer = new $installer_class();
93
-
94
- foreach ( Lib\Plugin::getEntityClasses() as $entity_class ) {
95
  $table_name = $entity_class::getTableName();
96
  $result['entities'][ $entity_class ] = array(
97
  'fields' => $this->_getTableStructure( $table_name ),
98
  'values' => $wpdb->get_results( 'SELECT * FROM ' . $table_name, ARRAY_N )
99
  );
100
  }
101
- $plugin_prefix = Lib\Plugin::getPrefix();
102
  $options_postfix = array( 'data_loaded', 'grace_start', 'db_version', 'installation_time' );
103
  if ( $plugin_prefix != 'bookly_' ) {
104
  $options_postfix[] = 'enabled';
@@ -108,7 +105,7 @@ class Controller extends Lib\Base\Controller
108
  $result['options'][ $option_name ] = get_option( $option_name );
109
  }
110
 
111
- $result['options'][ Lib\Plugin::getPurchaseCodeOption() ] = Lib\Plugin::getPurchaseCode();
112
  foreach ( $installer->getOptions() as $option_name => $option_value ) {
113
  $result['options'][ $option_name ] = get_option( $option_name );
114
  }
@@ -130,14 +127,12 @@ class Controller extends Lib\Base\Controller
130
 
131
  if ( $file = $_FILES['import']['name'] ) {
132
  $json = file_get_contents( $_FILES['import']['tmp_name'] );
133
- if ( $json !== false ) {
134
  $wpdb->query( 'SET FOREIGN_KEY_CHECKS = 0' );
135
 
136
  $data = json_decode( $json, true );
137
 
138
- $installer_class = Lib\Plugin::getRootNamespace() . '\Lib\Installer';
139
- /** @var Lib\Base\Installer $installer */
140
- $installer = new $installer_class();
141
 
142
  // Drop all data and options.
143
  $installer->removeData();
@@ -147,8 +142,8 @@ class Controller extends Lib\Base\Controller
147
  // Insert tables data.
148
  foreach ( Lib\Plugin::getEntityClasses() as $entity_class ) {
149
  if ( isset ( $data['entities'][ $entity_class ]['values'][0] ) ) {
150
- $table_name = $entity_class::getTableName();
151
- $query = sprintf(
152
  'INSERT INTO `%s` (`%s`) VALUES (%%s)',
153
  $table_name,
154
  implode( '`,`', $data['entities'][ $entity_class ]['fields'] )
@@ -195,9 +190,9 @@ class Controller extends Lib\Base\Controller
195
  $option_name = $plugin_prefix . $option;
196
  add_option( $option_name, $data['options'][ $option_name ] );
197
  }
198
- }
199
 
200
- header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug&status=imported' ) );
 
201
  }
202
 
203
  header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug' ) );
@@ -276,18 +271,4 @@ class Controller extends Lib\Base\Controller
276
 
277
  return $wpdb->query( 'SHOW TABLES LIKE "' . $tableName . '"' );
278
  }
279
-
280
- // Protected methods.
281
-
282
- /**
283
- * Override parent method to add 'wp_ajax_bookly_' prefix
284
- * so current 'execute*' methods look nicer.
285
- *
286
- * @param string $prefix
287
- */
288
- protected function registerWpActions( $prefix = '' )
289
- {
290
- parent::registerWpActions( 'wp_ajax_bookly_' );
291
- }
292
-
293
  }
86
  global $wpdb;
87
 
88
  $result = array();
89
+ $installer = new Lib\Installer();
90
 
91
+ foreach ( Lib\Plugin::getEntityClasses() as $entity_class ) {
 
 
 
 
92
  $table_name = $entity_class::getTableName();
93
  $result['entities'][ $entity_class ] = array(
94
  'fields' => $this->_getTableStructure( $table_name ),
95
  'values' => $wpdb->get_results( 'SELECT * FROM ' . $table_name, ARRAY_N )
96
  );
97
  }
98
+ $plugin_prefix = Lib\Plugin::getPrefix();
99
  $options_postfix = array( 'data_loaded', 'grace_start', 'db_version', 'installation_time' );
100
  if ( $plugin_prefix != 'bookly_' ) {
101
  $options_postfix[] = 'enabled';
105
  $result['options'][ $option_name ] = get_option( $option_name );
106
  }
107
 
108
+ $result['options'][ Lib\Plugin::getPurchaseCodeOption() ] = Lib\Plugin::getPurchaseCode();
109
  foreach ( $installer->getOptions() as $option_name => $option_value ) {
110
  $result['options'][ $option_name ] = get_option( $option_name );
111
  }
127
 
128
  if ( $file = $_FILES['import']['name'] ) {
129
  $json = file_get_contents( $_FILES['import']['tmp_name'] );
130
+ if ( $json !== false) {
131
  $wpdb->query( 'SET FOREIGN_KEY_CHECKS = 0' );
132
 
133
  $data = json_decode( $json, true );
134
 
135
+ $installer = new Lib\Installer();
 
 
136
 
137
  // Drop all data and options.
138
  $installer->removeData();
142
  // Insert tables data.
143
  foreach ( Lib\Plugin::getEntityClasses() as $entity_class ) {
144
  if ( isset ( $data['entities'][ $entity_class ]['values'][0] ) ) {
145
+ $table_name = $entity_class::getTableName();
146
+ $query = sprintf(
147
  'INSERT INTO `%s` (`%s`) VALUES (%%s)',
148
  $table_name,
149
  implode( '`,`', $data['entities'][ $entity_class ]['fields'] )
190
  $option_name = $plugin_prefix . $option;
191
  add_option( $option_name, $data['options'][ $option_name ] );
192
  }
 
193
 
194
+ header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug&status=imported' ) );
195
+ }
196
  }
197
 
198
  header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug' ) );
271
 
272
  return $wpdb->query( 'SHOW TABLES LIKE "' . $tableName . '"' );
273
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  }
backend/modules/debug/templates/index.php CHANGED
@@ -15,6 +15,7 @@
15
  <div class="panel-group" id="data-management">
16
  <div class="bookly-data-button">
17
  <form action="<?php echo admin_url( 'admin-ajax.php?action=bookly_export_data' ) ?>" method="POST">
 
18
  <button id="bookly-export" type="submit" class="btn btn-lg btn-success">
19
  <span class="ladda-label">Export data</span>
20
  </button>
@@ -22,6 +23,7 @@
22
  </div>
23
  <div class="bookly-data-button">
24
  <form id="bookly_import" action="<?php echo admin_url( 'admin-ajax.php?action=bookly_import_data' ) ?>" method="POST" enctype="multipart/form-data">
 
25
  <div id="bookly-import" class="btn btn-lg btn-primary btn-file">
26
  <span class="ladda-label">Import data</span>
27
  <input type="file" id="bookly_import_file" name="import">
15
  <div class="panel-group" id="data-management">
16
  <div class="bookly-data-button">
17
  <form action="<?php echo admin_url( 'admin-ajax.php?action=bookly_export_data' ) ?>" method="POST">
18
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
19
  <button id="bookly-export" type="submit" class="btn btn-lg btn-success">
20
  <span class="ladda-label">Export data</span>
21
  </button>
23
  </div>
24
  <div class="bookly-data-button">
25
  <form id="bookly_import" action="<?php echo admin_url( 'admin-ajax.php?action=bookly_import_data' ) ?>" method="POST" enctype="multipart/form-data">
26
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
27
  <div id="bookly-import" class="btn btn-lg btn-primary btn-file">
28
  <span class="ladda-label">Import data</span>
29
  <input type="file" id="bookly_import_file" name="import">
backend/modules/message/Controller.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace BooklyLite\Backend\Modules\Message;
3
+
4
+ use BooklyLite\Lib;
5
+
6
+ /**
7
+ * Class Controller
8
+ * @package BooklyLite\Backend\Modules\Message
9
+ */
10
+ class Controller extends Lib\Base\Controller
11
+ {
12
+ const page_slug = 'bookly-messages';
13
+
14
+ /**
15
+ * Default action
16
+ */
17
+ public function index()
18
+ {
19
+ $this->enqueueStyles( array(
20
+ 'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
21
+ ) );
22
+
23
+ $this->enqueueScripts( array(
24
+ 'backend' => array(
25
+ 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
26
+ 'js/datatables.min.js' => array( 'jquery' ),
27
+ ),
28
+ 'module' => array( 'js/message.js' => array( 'jquery' ) ),
29
+ ) );
30
+
31
+ wp_localize_script( 'bookly-message.js', 'BooklyL10n', array(
32
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
33
+ 'datatable' => array(
34
+ 'zeroRecords' => __( 'No records.', 'bookly' ),
35
+ 'processing' => __( 'Processing...', 'bookly' ),
36
+ 'per_page' => __( 'messages', 'bookly' ),
37
+ 'paginate' => array(
38
+ 'first' => __( 'First', 'bookly' ),
39
+ 'previous' => __( 'Previous', 'bookly' ),
40
+ 'next' => __( 'Next', 'bookly' ),
41
+ 'last' => __( 'Last', 'bookly' ),
42
+ )
43
+ )
44
+ ) );
45
+ $this->render( 'index' );
46
+ }
47
+
48
+ /**
49
+ * Get messages
50
+ */
51
+ public function executeGetMessages()
52
+ {
53
+ $query = Lib\Entities\Message::query( 'm' );
54
+ $total = $query->count();
55
+
56
+ $query->select( 'm.created, m.subject, m.seen, m.body, m.message_id' )
57
+ ->sortBy( 'm.seen, m.message_id' )->order( 'DESC' );
58
+
59
+ $query->limit( $this->getParameter( 'length' ) )->offset( $this->getParameter( 'start' ) );
60
+
61
+ $data = $query->fetchArray();
62
+ foreach ( $data as &$row ) {
63
+ $row['created'] = Lib\Utils\DateTime::formatDateTime( $row['created'] );
64
+ }
65
+
66
+ wp_send_json( array(
67
+ 'draw' => ( int ) $this->getParameter( 'draw' ),
68
+ 'recordsTotal' => $total,
69
+ 'recordsFiltered' => count( $data ),
70
+ 'data' => $data,
71
+ ) );
72
+ }
73
+
74
+ /**
75
+ * Mark all messages was read
76
+ */
77
+ public function executeMarkReadAllMessages()
78
+ {
79
+ $messages = Lib\Entities\Message::query( 'm' )->select( 'm.message_id' )->whereNot( 'm.seen', 1 )->fetchArray();
80
+ $message_ids = array();
81
+ foreach ( $messages as $message ) {
82
+ $message_ids[] = $message['message_id'];
83
+ }
84
+
85
+ if ( $message_ids ) {
86
+ Lib\API::seenMessages( $message_ids );
87
+ }
88
+ wp_send_json_success();
89
+ }
90
+
91
+ /**
92
+ * Mark some massages was read
93
+ */
94
+ public function executeMarkReadMessages()
95
+ {
96
+ Lib\API::seenMessages( (array) $this->getParameter( 'message_ids' ) );
97
+ wp_send_json_success();
98
+ }
99
+
100
+ }
backend/modules/message/resources/js/message.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+
3
+ var $message_list = $('#bookly-messages-list');
4
+
5
+ /**
6
+ * Init DataTables.
7
+ */
8
+ var dt = $message_list.DataTable({
9
+ paging: true,
10
+ ordering: false,
11
+ info: false,
12
+ searching: false,
13
+ processing: true,
14
+ responsive: true,
15
+ ajax: {
16
+ url: ajaxurl,
17
+ data: { action: 'bookly_get_messages', csrf_token : BooklyL10n.csrf_token }
18
+ },
19
+ fnFooterCallback: function( nFoot, aData, iStart, iEnd, aiDisplay ) {
20
+ var message_ids =[];
21
+ for (var i = iStart; i < iEnd; i++) {
22
+ if (aData[i].seen == 0) {
23
+ // Add new messages
24
+ message_ids.push(aData[i].message_id)
25
+ }
26
+ }
27
+ if (message_ids.length > 0) {
28
+ $.ajax({
29
+ url : ajaxurl,
30
+ type : 'POST',
31
+ data : {
32
+ action : 'bookly_mark_read_messages',
33
+ message_ids: message_ids,
34
+ csrf_token : BooklyL10n.csrf_token
35
+ },
36
+ dataType : 'json',
37
+ success : function (response) {
38
+ if (response.success) {
39
+
40
+ }
41
+ }
42
+ });
43
+ }
44
+ },
45
+ columns: [
46
+ { data: 'created' },
47
+ {
48
+ data: 'subject',
49
+ render: function (data, type, row, meta) {
50
+ if (row.seen != 1) {
51
+ return '<b>' + data + '</b>';
52
+ }
53
+ return data;
54
+ }
55
+ },
56
+ { data: 'body' }
57
+ ],
58
+ language: {
59
+ zeroRecords: BooklyL10n.datatable.zeroRecords,
60
+ processing: BooklyL10n.datatable.processing,
61
+ sLengthMenu: '_MENU_ ' + BooklyL10n.datatable.per_page,
62
+ paginate: {
63
+ first: BooklyL10n.datatable.paginate.first,
64
+ previous: BooklyL10n.datatable.paginate.previous,
65
+ next: BooklyL10n.datatable.paginate.next,
66
+ last: BooklyL10n.datatable.paginate.last
67
+ }
68
+ }
69
+ });
70
+
71
+ });
backend/modules/message/templates/index.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <div id="bookly-tbs" class="wrap">
3
+ <div class="bookly-tbs-body">
4
+ <div class="page-header text-right clearfix">
5
+ <div class="bookly-page-title">
6
+ <?php _e( 'Messages', 'bookly' ) ?>
7
+ </div>
8
+ <?php \BooklyLite\Backend\Modules\Support\Components::getInstance()->renderButtons( $this::page_slug ) ?>
9
+ </div>
10
+ <div class="panel panel-default bookly-main">
11
+ <div class="panel-body">
12
+ <table id="bookly-messages-list" class="table table-striped" width="100%">
13
+ <thead>
14
+ <tr>
15
+ <th><?php _e( 'Date', 'bookly' ) ?></th>
16
+ <th><?php _e( 'Subject', 'bookly' ) ?></th>
17
+ <th><?php _e( 'Message', 'bookly' ) ?></th>
18
+ </tr>
19
+ </thead>
20
+ </table>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
backend/modules/notifications/Components.php ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace BooklyLite\Backend\Modules\Notifications;
3
+
4
+ use BooklyLite\Lib;
5
+ use BooklyLite\Lib\Entities\Notification;
6
+
7
+ /**
8
+ * Class Components
9
+ * @package BooklyLite\Backend\Modules\Notifications
10
+ */
11
+ class Components extends Lib\Base\Components
12
+ {
13
+ protected $css_prefix = 'bookly-js-codes-';
14
+
15
+ /**
16
+ * Codes for all notifications.
17
+ *
18
+ * @return array
19
+ */
20
+ private function getCommonCodes()
21
+ {
22
+ return array(
23
+ array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
24
+ array( 'code' => 'company_logo', 'description' => __( 'company logo', 'bookly' ) ),
25
+ array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
26
+ array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
27
+ array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Render codes for notifications
33
+ *
34
+ * @param string $notification_type
35
+ */
36
+ public function renderCodes( $notification_type )
37
+ {
38
+ switch ( $notification_type ) {
39
+ case Notification::TYPE_APPOINTMENT_START_TIME:
40
+ case Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED:
41
+ case Notification::TYPE_LAST_CUSTOMER_APPOINTMENT:
42
+ case Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED:
43
+ $this->renderCustomerAppointmentCodesForCN( $notification_type );
44
+ break;
45
+ case 'staff_agenda':
46
+ case Notification::TYPE_STAFF_DAY_AGENDA:
47
+ $this->renderStaffDayAgendaCodes();
48
+ break;
49
+ case 'client_birthday_greeting':
50
+ case Notification::TYPE_CUSTOMER_BIRTHDAY:
51
+ $this->renderBirthdayGreetingCodes();
52
+ break;
53
+ case 'client_new_wp_user':
54
+ $this->renderNewWpUserCodes();
55
+ break;
56
+ case 'client_pending_appointment_cart':
57
+ case 'client_approved_appointment_cart':
58
+ $this->renderCompoundCodes();
59
+ break;
60
+ case 'staff_waiting_list':
61
+ $this->renderWaitingListCodes();
62
+ break;
63
+ case 'client_pending_appointment':
64
+ case 'staff_pending_appointment':
65
+ case 'client_approved_appointment':
66
+ case 'staff_approved_appointment':
67
+ case 'client_cancelled_appointment':
68
+ case 'staff_cancelled_appointment':
69
+ case 'client_rejected_appointment':
70
+ case 'staff_rejected_appointment':
71
+ case 'client_waitlisted_appointment':
72
+ case 'staff_waitlisted_appointment':
73
+ case 'client_reminder':
74
+ case 'client_reminder_1st':
75
+ case 'client_reminder_2nd':
76
+ case 'client_reminder_3rd':
77
+ case 'client_follow_up':
78
+ $this->renderBaseCodes();
79
+ break;
80
+ case 'staff_package_purchased':
81
+ case 'client_package_purchased':
82
+ case 'staff_package_deleted':
83
+ case 'client_package_deleted':
84
+ $this->renderPackageCodes( $notification_type );
85
+ break;
86
+ case 'client_pending_recurring_appointment':
87
+ case 'staff_pending_recurring_appointment':
88
+ case 'client_approved_recurring_appointment':
89
+ case 'staff_approved_recurring_appointment':
90
+ case 'client_cancelled_recurring_appointment':
91
+ case 'staff_cancelled_recurring_appointment':
92
+ case 'client_rejected_recurring_appointment':
93
+ case 'staff_rejected_recurring_appointment':
94
+ case 'client_waitlisted_recurring_appointment':
95
+ case 'staff_waitlisted_recurring_appointment':
96
+ $this->renderRecurringCodes();
97
+ break;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Render codes for custom notifications with appointment(s)
103
+ *
104
+ * @param string $notification_type
105
+ */
106
+ private function renderCustomerAppointmentCodesForCN( $notification_type )
107
+ {
108
+ $codes = $this->getCommonCodes();
109
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
110
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
111
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
112
+ $codes[] = array( 'code' => 'appointment_notes', 'description' => __( 'customer notes for appointment', 'bookly' ) );
113
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
114
+ $codes[] = array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) );
115
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
116
+ $codes[] = array( 'code' => 'cancel_appointment', 'description' => __( 'cancel appointment link', 'bookly' ) );
117
+ $codes[] = array( 'code' => 'cancel_appointment_confirm_url', 'description' => esc_html__( 'URL of cancel appointment link with confirmation (to use inside <a> tag)', 'bookly' ) );
118
+ $codes[] = array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) );
119
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
120
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
121
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
122
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
123
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
124
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
125
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
126
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
127
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
128
+ $codes[] = array( 'code' => 'reject_appointment_url', 'description' => esc_html__( 'URL of reject appointment link (to use inside <a> tag)', 'bookly' ) );
129
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
130
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
131
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
132
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
133
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
134
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
135
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
136
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
137
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
138
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
139
+
140
+ $codes = Lib\Proxy\Packages::prepareNotificationCodesList( $codes );
141
+ $codes = Lib\Proxy\RecurringAppointments::prepareNotificationCodesList( $codes );
142
+
143
+ Lib\Utils\Common::codes(
144
+ Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' ),
145
+ array( 'type' => $notification_type )
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Render codes for staff agenda.
151
+ */
152
+ private function renderStaffDayAgendaCodes()
153
+ {
154
+ $codes = $this->getCommonCodes();
155
+ $codes[] = array( 'code' => 'agenda_date', 'description' => __( 'agenda date', 'bookly' ) );
156
+ $codes[] = array( 'code' => 'next_day_agenda', 'description' => __( 'staff agenda for next day', 'bookly' ) );
157
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
158
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
159
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
160
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
161
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
162
+ $codes[] = array( 'code' => 'tomorrow_date', 'description' => __( 'date of next day', 'bookly' ) );
163
+
164
+ Lib\Utils\Common::codes(
165
+ $codes,
166
+ array( 'type' => Notification::TYPE_STAFF_DAY_AGENDA )
167
+ );
168
+ }
169
+
170
+ /**
171
+ * Render codes for Greeting notifications
172
+ */
173
+ private function renderBirthdayGreetingCodes()
174
+ {
175
+ $codes = $this->getCommonCodes();
176
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
177
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
178
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
179
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
180
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
181
+
182
+ Lib\Utils\Common::codes(
183
+ $codes,
184
+ array( 'type' => Notification::TYPE_CUSTOMER_BIRTHDAY )
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Render codes for new WordPress users.
190
+ */
191
+ private function renderNewWpUserCodes()
192
+ {
193
+ $codes = $this->getCommonCodes();
194
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
195
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
196
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
197
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
198
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
199
+ $codes[] = array( 'code' => 'new_password', 'description' => __( 'customer new password', 'bookly' ) );
200
+ $codes[] = array( 'code' => 'new_username', 'description' => __( 'customer new username', 'bookly' ) );
201
+ $codes[] = array( 'code' => 'site_address', 'description' => __( 'site address', 'bookly' ) );
202
+
203
+ Lib\Utils\Common::codes( $codes );
204
+ }
205
+
206
+ /**
207
+ * Render codes for compound (cart) notifications.
208
+ */
209
+ private function renderCompoundCodes()
210
+ {
211
+ $codes = $this->getCommonCodes();
212
+ $codes[] = array( 'code' => 'cart_info', 'description' => __( 'cart information', 'bookly' ) );
213
+ $codes[] = array( 'code' => 'cart_info_c', 'description' => __( 'cart information with cancel', 'bookly' ) );
214
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
215
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
216
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
217
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
218
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
219
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
220
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
221
+
222
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareCartNotificationShortCodes( $codes ) );
223
+ }
224
+
225
+ /**
226
+ * Render base codes
227
+ */
228
+ private function renderBaseCodes()
229
+ {
230
+ $codes = $this->getCommonCodes();
231
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
232
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
233
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
234
+ $codes[] = array( 'code' => 'appointment_notes', 'description' => __( 'customer notes for appointment', 'bookly' ) );
235
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
236
+ $codes[] = array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) );
237
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
238
+ $codes[] = array( 'code' => 'cancel_appointment', 'description' => __( 'cancel appointment link', 'bookly' ) );
239
+ $codes[] = array( 'code' => 'cancel_appointment_confirm_url', 'description' => esc_html__( 'URL of cancel appointment link with confirmation (to use inside <a> tag)', 'bookly' ) );
240
+ $codes[] = array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) );
241
+ $codes[] = array( 'code' => 'cancellation_reason', 'description' => __( 'reason you mentioned while deleting appointment', 'bookly' ) );
242
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
243
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
244
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
245
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
246
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
247
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
248
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
249
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
250
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
251
+ $codes[] = array( 'code' => 'reject_appointment_url', 'description' => esc_html__( 'URL of reject appointment link (to use inside <a> tag)', 'bookly' ) );
252
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
253
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
254
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
255
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
256
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
257
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
258
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
259
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
260
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
261
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
262
+
263
+ Lib\Utils\Common::codes(
264
+ Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' )
265
+ );
266
+ }
267
+
268
+ /**
269
+ * Render codes notifications about package appointments
270
+ *
271
+ * @param string $notification_type
272
+ */
273
+ private function renderPackageCodes( $notification_type )
274
+ {
275
+ $codes = $this->getCommonCodes();
276
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
277
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
278
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
279
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
280
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
281
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
282
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
283
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
284
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
285
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
286
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
287
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
288
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
289
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
290
+
291
+ $codes = Lib\Proxy\Packages::prepareNotificationCodesList( $codes, '', $notification_type );
292
+
293
+ Lib\Utils\Common::codes( $codes );
294
+ }
295
+
296
+ /**
297
+ * Render codes notifications wor appointments in waiting list
298
+ */
299
+ private function renderWaitingListCodes()
300
+ {
301
+ $codes = $this->getCommonCodes();
302
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
303
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
304
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
305
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
306
+ $codes[] = array( 'code' => 'appointment_waiting_list', 'description' => __( 'waiting list of appointment', 'bookly-waiting-list' ) );
307
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
308
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
309
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
310
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
311
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
312
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
313
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
314
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
315
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
316
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
317
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
318
+
319
+ $codes = Lib\Proxy\WaitingList::prepareNotificationCodesList( $codes );
320
+
321
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'appointment' ) );
322
+ }
323
+
324
+ private function renderRecurringCodes()
325
+ {
326
+ $codes = $this->getCommonCodes();
327
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
328
+ $codes[] = array( 'code' => 'appointment_end_date','description' => __( 'end date of appointment', 'bookly' ) );
329
+ $codes[] = array( 'code' => 'appointment_end_time','description' => __( 'end time of appointment', 'bookly' ) );
330
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
331
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
332
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
333
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
334
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
335
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
336
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
337
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
338
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
339
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
340
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
341
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
342
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
343
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
344
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
345
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
346
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
347
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
348
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
349
+ $codes[] = array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) );
350
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
351
+
352
+ $codes = Lib\Proxy\RecurringAppointments::prepareNotificationCodesList( $codes );
353
+
354
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' ) );
355
+ }
356
+
357
+ }
backend/modules/notifications/Controller.php CHANGED
@@ -35,34 +35,46 @@ class Controller extends Lib\Base\Controller
35
  )
36
  ) );
37
  $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
38
- $form = new Forms\Notifications( 'email' );
39
  $alert = array( 'success' => array() );
40
  // Save action.
41
  if ( ! empty ( $_POST ) ) {
42
- $form->bind( $this->getPostParameters() );
43
- $form->save();
44
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
45
- update_option( 'bookly_email_send_as', $this->getParameter( 'bookly_email_send_as' ) );
46
- update_option( 'bookly_email_reply_to_customers', $this->getParameter( 'bookly_email_reply_to_customers' ) );
47
- update_option( 'bookly_email_sender', $this->getParameter( 'bookly_email_sender' ) );
48
- update_option( 'bookly_email_sender_name', $this->getParameter( 'bookly_email_sender_name' ) );
49
- foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder' ) as $type ) {
50
- $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_hour' );
 
 
 
 
 
 
 
51
  }
52
- update_option( 'bookly_cron_reminder_times', $cron_reminder );
53
  }
54
- $cron_path = realpath( Lib\Plugin::getDirectory() . '/lib/utils/send_notifications_cron.php' );
55
  wp_localize_script( 'bookly-alert.js', 'BooklyL10n', array(
56
- 'alert' => $alert,
 
 
57
  'sent_successfully' => __( 'Sent successfully.', 'bookly' ),
58
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
59
  ) );
60
- $this->render( 'index', compact( 'form', 'cron_path', 'cron_reminder' ) );
 
 
 
 
61
  }
62
 
63
  public function executeGetEmailNotificationsData()
64
  {
65
- $form = new Forms\Notifications( 'email' );
66
 
67
  $bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
68
  get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
@@ -72,9 +84,15 @@ class Controller extends Lib\Base\Controller
72
 
73
  $notifications = array();
74
  foreach ( $form->getData() as $notification ) {
 
 
 
 
 
 
75
  $notifications[] = array(
76
  'type' => $notification['type'],
77
- 'name' => $notification['name'],
78
  'active' => $notification['active'],
79
  );
80
  }
@@ -90,21 +108,26 @@ class Controller extends Lib\Base\Controller
90
  wp_send_json_success( $result );
91
  }
92
 
93
- public function executeTestEmailNotifications()
94
- {
95
- }
96
 
97
- // Protected methods.
 
 
 
98
 
99
  /**
100
- * Override parent method to add 'wp_ajax_bookly_' prefix
101
- * so current 'execute*' methods look nicer.
102
- *
103
- * @param string $prefix
104
  */
105
- protected function registerWpActions( $prefix = '' )
106
  {
107
- parent::registerWpActions( 'wp_ajax_bookly_' );
 
 
 
 
 
 
 
108
  }
109
 
110
  }
35
  )
36
  ) );
37
  $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
38
+ $form = new Forms\Notifications( 'email', Components::getInstance() );
39
  $alert = array( 'success' => array() );
40
  // Save action.
41
  if ( ! empty ( $_POST ) ) {
42
+ if ( $this->csrfTokenValid() ) {
43
+ $form->bind( $this->getPostParameters() );
44
+ $form->save();
45
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
46
+ update_option( 'bookly_email_send_as', $this->getParameter( 'bookly_email_send_as' ) );
47
+ update_option( 'bookly_email_reply_to_customers', $this->getParameter( 'bookly_email_reply_to_customers' ) );
48
+ update_option( 'bookly_email_sender', $this->getParameter( 'bookly_email_sender' ) );
49
+ update_option( 'bookly_email_sender_name', $this->getParameter( 'bookly_email_sender_name' ) );
50
+ update_option( 'bookly_ntf_processing_interval', (int) $this->getParameter( 'bookly_ntf_processing_interval' ) );
51
+ foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder', 'client_birthday_greeting' ) as $type ) {
52
+ $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_hour' );
53
+ }
54
+ foreach ( array( 'client_reminder_1st', 'client_reminder_2nd', 'client_reminder_3rd', ) as $type ) {
55
+ $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_before_hour' );
56
+ }
57
+ update_option( 'bookly_cron_reminder_times', $cron_reminder );
58
  }
 
59
  }
60
+ $cron_uri = plugins_url( 'lib/utils/send_notifications_cron.php', Lib\Plugin::getMainFile() );
61
  wp_localize_script( 'bookly-alert.js', 'BooklyL10n', array(
62
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
63
+ 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
64
+ 'alert' => $alert,
65
  'sent_successfully' => __( 'Sent successfully.', 'bookly' ),
66
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
67
  ) );
68
+ $statuses = Lib\Entities\CustomerAppointment::getStatuses();
69
+ foreach ( range( 1, 23 ) as $hours ) {
70
+ $bookly_ntf_processing_interval_values[] = array( $hours, Lib\Utils\DateTime::secondsToInterval( $hours * HOUR_IN_SECONDS ) );
71
+ }
72
+ $this->render( 'index', compact( 'form', 'cron_uri', 'cron_reminder', 'statuses', 'bookly_ntf_processing_interval_values' ) );
73
  }
74
 
75
  public function executeGetEmailNotificationsData()
76
  {
77
+ $form = new Forms\Notifications( 'email', Components::getInstance() );
78
 
79
  $bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
80
  get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
84
 
85
  $notifications = array();
86
  foreach ( $form->getData() as $notification ) {
87
+ $name = Lib\Entities\Notification::getName( $notification['type'] );
88
+ if ( in_array( $notification['type'], Lib\Entities\Notification::getCustomNotificationTypes() ) && $notification['subject'] != '' ) {
89
+ // In window Test Email Notification
90
+ // for custom notification, subject is name.
91
+ $name = $notification['subject'];
92
+ }
93
  $notifications[] = array(
94
  'type' => $notification['type'],
95
+ 'name' => $name,
96
  'active' => $notification['active'],
97
  );
98
  }
108
  wp_send_json_success( $result );
109
  }
110
 
111
+ public function executeTestEmailNotifications(){}
 
 
112
 
113
+ /**
114
+ * Create new custom notification
115
+ */
116
+ public function executeCreateCustomNotification(){}
117
 
118
  /**
119
+ * Delete custom notification
 
 
 
120
  */
121
+ public function executeDeleteCustomNotification()
122
  {
123
+ $id = $this->getParameter( 'id' );
124
+ Lib\Entities\Notification::query()
125
+ ->delete()
126
+ ->where( 'id', $id )
127
+ ->whereIN( 'type', Lib\Entities\Notification::getCustomNotificationTypes() )
128
+ ->execute();
129
+
130
+ wp_send_json_success();
131
  }
132
 
133
  }
backend/modules/notifications/forms/Notifications.php CHANGED
@@ -21,49 +21,52 @@ class Notifications extends Lib\Base\Form
21
  'staff_rejected_appointment',
22
  'client_new_wp_user',
23
  'client_reminder',
 
 
 
24
  'client_follow_up',
 
25
  'staff_agenda',
26
  ),
27
  'combined' => array(
28
  'client_pending_appointment_cart',
29
  'client_approved_appointment_cart',
30
  ),
 
31
  );
32
 
33
  public $gateway;
34
 
 
 
 
35
  /**
36
- * Constructor.
37
  *
38
- * @param string $gateway
 
39
  */
40
- public function __construct( $gateway = 'email' )
41
  {
42
  /*
43
  * make Visual Mode as default (instead of Text Mode)
44
  * allowed: tinymce - Visual Mode, html - Text Mode, test - no one Mode selected
45
  */
46
  add_filter( 'wp_default_editor', create_function( '', 'return \'tinymce\';' ) );
47
- $this->types = apply_filters( 'bookly_prepare_notification_types', $this->types );
48
  $this->gateway = $gateway;
49
- if ( ! Lib\Config::areCombinedNotificationsEnabled() ) {
50
  $this->types['combined'] = array();
51
  }
52
- $this->setFields( array( 'active', 'subject', 'message', 'copy', ) );
 
 
53
  $this->load();
54
  }
55
 
56
  public function bind( array $_post = array(), array $files = array() )
57
  {
58
- foreach ( $this->types as $group ) {
59
- foreach ( $group as $type ) {
60
- foreach ( $this->fields as $field ) {
61
- if ( isset ( $_post[ $type ] [ $field ] ) ) {
62
- $this->data[ $type ][ $field ] = $_post[ $type ][ $field ];
63
- }
64
- }
65
- }
66
- }
67
  }
68
 
69
  /**
@@ -72,62 +75,74 @@ class Notifications extends Lib\Base\Form
72
  public function save()
73
  {
74
  /** @var Lib\Entities\Notification[] $notifications */
75
- $notifications = Lib\Entities\Notification::query( 'n' )
76
  ->where( 'gateway', $this->gateway )
77
- ->indexBy( 'type' )
78
  ->find();
79
- foreach ( $this->types as $group ) {
80
- foreach ( $group as $type ) {
81
- $notifications[ $type ]->setFields( $this->data[ $type ] );
82
- $notifications[ $type ]->save();
83
- }
84
  }
85
  }
86
 
87
  public function load()
88
  {
89
- $notifications = Lib\Entities\Notification::query( 'n' )
90
- ->select( 'active, subject, message, copy, type' )
91
  ->where( 'gateway', $this->gateway )
92
- ->indexBy( 'type' )
93
  ->fetchArray();
94
- foreach ( $this->types as $group ) {
95
- foreach ( $group as $type ) {
96
- $notifications[ $type ]['name'] = Lib\Entities\Notification::getName( $type );
97
- $this->data[ $type ] = $notifications[ $type ];
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
99
  }
 
 
100
  }
101
 
102
  /**
103
  * Render subject.
104
  *
105
- * @param string $type
106
  */
107
- public function renderSubject( $type )
108
  {
109
  printf(
110
  '<div class="form-group">
111
  <label for="%1$s">%2$s</label>
112
- <input type="text" class="form-control" id="%1$s" name="%3$s" value="%4$s" />
113
  </div>',
114
- $type . '_subject',
115
  __( 'Subject', 'bookly' ),
116
- $type . '[subject]',
117
- esc_attr( $this->data[ $type ]['subject'] )
118
  );
119
  }
120
 
121
  /**
122
  * Render message editor.
123
  *
124
- * @param string $type
125
  */
126
- public function renderEditor( $type )
127
  {
128
- $id = $type . '_message';
129
- $name = $type . '[message]';
130
- $value = $this->data[ $type ]['message'];
131
 
132
  if ( $this->gateway == 'sms' ) {
133
  printf(
@@ -135,7 +150,7 @@ class Notifications extends Lib\Base\Form
135
  <label for="%1$s">%2$s</label>
136
  <textarea rows="6" id="%1$s" name="%3$s" class="form-control">%4$s</textarea>
137
  </div>',
138
- $id,
139
  __( 'Message', 'bookly' ),
140
  $name,
141
  esc_textarea( $value )
@@ -154,62 +169,130 @@ class Notifications extends Lib\Base\Form
154
  );
155
 
156
  echo '<div class="form-group"><label>' . __( 'Message', 'bookly' ) . '</label>';
157
- wp_editor( $value, $id, $settings );
158
  echo '</div>';
159
  }
160
  }
161
 
162
  /**
163
- * Render copy.
164
  *
165
- * @param string $type
166
  */
167
- public function renderCopy( $type )
168
  {
169
- if ( in_array( $type, array( 'staff_pending_appointment', 'staff_approved_appointment', 'staff_cancelled_appointment', 'staff_rejected_appointment', 'staff_pending_recurring_appointment', 'staff_approved_recurring_appointment', 'staff_cancelled_recurring_appointment' ) ) ) {
 
 
 
 
170
  printf(
171
  '<div class="form-group">
172
  <input name="%1$s" type="hidden" value="0">
173
  <div class="checkbox"><label for="%2$s"><input id="%2$s" name="%1$s" type="checkbox" value="1" %3$s> %4$s</label></div>
174
  </div>',
175
- $type . '[copy]',
176
- $type . '_copy',
177
- checked( $this->data[ $type ]['copy'], true, false ),
178
  __( 'Send copy to administrators', 'bookly' )
179
  );
180
  }
181
  }
182
 
183
  /**
184
- * Render sending time.
185
  *
186
- * @param string $type
187
  */
188
- public function renderSendingTime( $type )
189
  {
190
- if ( in_array( $type, array( 'staff_agenda', 'client_follow_up', 'client_reminder' ) ) ) {
191
- $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  printf(
193
  '<div class="form-group">
194
- <label for="%1$s">%2$s</label>
195
- <p class="help-block">%3$s</p>
196
- <select class="form-control" name="%1$s" id="%1$s">
197
- %4$s
198
- </select>
199
  </div>',
200
- $type . '_cron_hour',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  __( 'Sending time', 'bookly' ),
202
  __( 'Set the time you want the notification to be sent.', 'bookly' ),
203
- implode( '', array_map( function ( $hour ) use ( $type, $cron_reminder ) {
 
 
 
 
 
 
 
 
 
 
 
204
  return sprintf(
205
  '<option value="%s" %s>%s</option>',
206
  $hour,
207
  selected( $cron_reminder[ $type ], $hour, false ),
208
  Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false )
209
  );
210
- }, range( 0, 23 ) ) )
 
 
 
 
 
 
 
 
 
 
 
211
  );
212
  }
213
  }
214
 
 
 
 
 
 
 
 
 
215
  }
21
  'staff_rejected_appointment',
22
  'client_new_wp_user',
23
  'client_reminder',
24
+ 'client_reminder_1st',
25
+ 'client_reminder_2nd',
26
+ 'client_reminder_3rd',
27
  'client_follow_up',
28
+ 'client_birthday_greeting',
29
  'staff_agenda',
30
  ),
31
  'combined' => array(
32
  'client_pending_appointment_cart',
33
  'client_approved_appointment_cart',
34
  ),
35
+ 'custom' => array(),
36
  );
37
 
38
  public $gateway;
39
 
40
+ /** @var \BooklyLite\Backend\Modules\Notifications\Components|\BooklyLite\Backend\Modules\Sms\Components|Lib\Base\Components */
41
+ protected $codes;
42
+
43
  /**
44
+ * Notifications constructor.
45
  *
46
+ * @param string $gateway
47
+ * @param \BooklyLite\Backend\Modules\Notifications\Components|\BooklyLite\Backend\Modules\Sms\Components $components
48
  */
49
+ public function __construct( $gateway = 'email', Lib\Base\Components $components )
50
  {
51
  /*
52
  * make Visual Mode as default (instead of Text Mode)
53
  * allowed: tinymce - Visual Mode, html - Text Mode, test - no one Mode selected
54
  */
55
  add_filter( 'wp_default_editor', create_function( '', 'return \'tinymce\';' ) );
56
+ $this->types = Lib\Proxy\Shared::prepareNotificationTypes( $this->types );
57
  $this->gateway = $gateway;
58
+ if ( ! Lib\Config::combinedNotificationsEnabled() ) {
59
  $this->types['combined'] = array();
60
  }
61
+ $this->types['custom'] = Lib\Entities\Notification::getCustomNotificationTypes();
62
+ $this->codes = $components;
63
+ $this->setFields( array( 'id', 'active', 'type', 'subject', 'message', 'to_customer', 'to_staff', 'to_admin', 'attach_ics', 'settings' ) );
64
  $this->load();
65
  }
66
 
67
  public function bind( array $_post = array(), array $files = array() )
68
  {
69
+ $this->data = $_post['notification'];
 
 
 
 
 
 
 
 
70
  }
71
 
72
  /**
75
  public function save()
76
  {
77
  /** @var Lib\Entities\Notification[] $notifications */
78
+ $notifications = Lib\Entities\Notification::query()
79
  ->where( 'gateway', $this->gateway )
80
+ ->indexBy( 'id' )
81
  ->find();
82
+ foreach ( $this->data as $id => $fields ) {
83
+ $notifications[ $id ]->setFields( $fields )->save();
84
+ $data = array_merge( $this->data[ $id ], $notifications[ $id ]->getFields() );
85
+ $this->data[ $id ] = $data;
 
86
  }
87
  }
88
 
89
  public function load()
90
  {
91
+ $notifications = Lib\Entities\Notification::query()
 
92
  ->where( 'gateway', $this->gateway )
 
93
  ->fetchArray();
94
+ foreach ( $notifications as $notification ) {
95
+ $this->data[ $notification['id'] ] = $notification;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * @param string $group
101
+ * @return array
102
+ */
103
+ public function getNotifications( $group )
104
+ {
105
+ $notifications = array();
106
+ foreach ( $this->types[ $group ] as $type ) {
107
+ foreach ( $this->data as $notification ) {
108
+ if ( $notification['type'] == $type ) {
109
+ $notifications[] = $notification;
110
+ }
111
  }
112
  }
113
+
114
+ return $notifications;
115
  }
116
 
117
  /**
118
  * Render subject.
119
  *
120
+ * @param int $id
121
  */
122
+ public function renderSubject( $id )
123
  {
124
  printf(
125
  '<div class="form-group">
126
  <label for="%1$s">%2$s</label>
127
+ <input type="text" class="form-control" id="%1$s" name="%3$s" value="%4$s"/>
128
  </div>',
129
+ 'notification_'.$id.'_subject',
130
  __( 'Subject', 'bookly' ),
131
+ 'notification[' . $id . '][subject]',
132
+ esc_attr( $this->data[ $id ]['subject'] )
133
  );
134
  }
135
 
136
  /**
137
  * Render message editor.
138
  *
139
+ * @param int $id
140
  */
141
+ public function renderEditor( $id )
142
  {
143
+ $attr_id = 'notification_' . $id . '_message';
144
+ $name = 'notification[' . $id . '][message]';
145
+ $value = $this->data[ $id ]['message'];
146
 
147
  if ( $this->gateway == 'sms' ) {
148
  printf(
150
  <label for="%1$s">%2$s</label>
151
  <textarea rows="6" id="%1$s" name="%3$s" class="form-control">%4$s</textarea>
152
  </div>',
153
+ $attr_id,
154
  __( 'Message', 'bookly' ),
155
  $name,
156
  esc_textarea( $value )
169
  );
170
 
171
  echo '<div class="form-group"><label>' . __( 'Message', 'bookly' ) . '</label>';
172
+ wp_editor( $value, $attr_id, $settings );
173
  echo '</div>';
174
  }
175
  }
176
 
177
  /**
178
+ * Render to admin.
179
  *
180
+ * @param array $notification
181
  */
182
+ public function renderCopy( array $notification )
183
  {
184
+ if ( strpos( $notification['type'], 'staff' ) === 0
185
+ || strpos( $notification['type'], 'custom_notification' ) === 0
186
+ ) {
187
+ $id = $notification['id'];
188
+ $name = 'notification[' . $notification['id'] . '][to_admin]';
189
  printf(
190
  '<div class="form-group">
191
  <input name="%1$s" type="hidden" value="0">
192
  <div class="checkbox"><label for="%2$s"><input id="%2$s" name="%1$s" type="checkbox" value="1" %3$s> %4$s</label></div>
193
  </div>',
194
+ $name,
195
+ 'notification_' . $id . '_copy',
196
+ checked( $notification['to_admin'], true, false ),
197
  __( 'Send copy to administrators', 'bookly' )
198
  );
199
  }
200
  }
201
 
202
  /**
203
+ * Render attach ICS file.
204
  *
205
+ * @param array $notification
206
  */
207
+ public function renderAttachIcs( array $notification )
208
  {
209
+ if ( in_array( $notification['type'], array(
210
+ 'client_pending_appointment',
211
+ 'staff_pending_appointment',
212
+ 'client_approved_appointment',
213
+ 'staff_approved_appointment',
214
+ 'client_cancelled_appointment',
215
+ 'staff_cancelled_appointment',
216
+ 'client_rejected_appointment',
217
+ 'staff_rejected_appointment',
218
+ 'client_waitlisted_appointment',
219
+ 'staff_waitlisted_appointment',
220
+ 'client_reminder',
221
+ 'client_reminder_1st',
222
+ 'client_reminder_2nd',
223
+ 'client_reminder_3rd',
224
+ 'client_follow_up',
225
+ ) ) ) {
226
+ $id = $notification['id'];
227
+ $name = sprintf( 'notification[%d][attach_ics]', $id );
228
  printf(
229
  '<div class="form-group">
230
+ <input name="%1$s" type="hidden" value="0">
231
+ <div class="checkbox"><label for="%2$s"><input id="%2$s" name="%1$s" type="checkbox" value="1" %3$s> %4$s</label></div>
 
 
 
232
  </div>',
233
+ $name,
234
+ 'notification_' . $id . '_ics',
235
+ checked( $notification['attach_ics'], true, false ),
236
+ __( 'Attach ICS file', 'bookly' )
237
+ );
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Render sending time.
243
+ *
244
+ * @param array $notification
245
+ */
246
+ public function renderSendingTime( array $notification )
247
+ {
248
+ $type = $notification['type'];
249
+ if ( in_array( $type, array( 'staff_agenda', 'client_follow_up', 'client_reminder', 'client_reminder_1st', 'client_reminder_2nd', 'client_reminder_3rd', 'client_birthday_greeting' ) ) ) {
250
+ $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
251
+ $before_hour = strpos( $type, 'client_reminder_' ) !== false;
252
+ $data = array(
253
+ $before_hour ? $type . '_cron_before_hour' : $type . '_cron_hour',
254
  __( 'Sending time', 'bookly' ),
255
  __( 'Set the time you want the notification to be sent.', 'bookly' ),
256
+ );
257
+ if ( $before_hour ) {
258
+ $data[] = implode( '', array_map( function ( $hour ) use ( $type, $cron_reminder ) {
259
+ return sprintf(
260
+ '<option value="%s" %s>%s</option>',
261
+ $hour,
262
+ selected( $cron_reminder[ $type ], $hour, false ),
263
+ sprintf( __( '%s before', 'bookly' ), \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) )
264
+ );
265
+ }, array_merge( range( 1, 24 ), range( 48, 336, 24 ) ) ) );
266
+ } else {
267
+ $data[] = implode( '', array_map( function ( $hour ) use ( $type, $cron_reminder ) {
268
  return sprintf(
269
  '<option value="%s" %s>%s</option>',
270
  $hour,
271
  selected( $cron_reminder[ $type ], $hour, false ),
272
  Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false )
273
  );
274
+ }, range( 0, 23 ) ) );
275
+ }
276
+
277
+ vprintf(
278
+ '<div class="form-group">
279
+ <label for="%1$s">%2$s</label>
280
+ <p class="help-block">%3$s</p>
281
+ <select class="form-control" name="%1$s" id="%1$s">
282
+ %4$s
283
+ </select>
284
+ </div>',
285
+ $data
286
  );
287
  }
288
  }
289
 
290
+ /**
291
+ * @param string $notification_type
292
+ */
293
+ public function renderCodes( $notification_type )
294
+ {
295
+ $this->codes->renderCodes( $notification_type );
296
+ }
297
+
298
  }
backend/modules/notifications/resources/js/ng-app.js CHANGED
@@ -13,7 +13,7 @@
13
  jQuery.ajax({
14
  url : ajaxurl,
15
  type : 'POST',
16
- data : jQuery.extend({ action : 'bookly_get_email_notifications_data' }, params),
17
  dataType : 'json',
18
  success : function(response) {
19
  if (response.success) {
@@ -85,6 +85,31 @@
85
  };
86
 
87
  $scope.testEmailNotifications = function(){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  };
89
 
90
  });
@@ -92,5 +117,5 @@
92
  })();
93
 
94
  var showTestEmailNotificationDialog = function () {
95
- jQuery('#ab_test_email_notifications_dialog').modal('show');
96
  };
13
  jQuery.ajax({
14
  url : ajaxurl,
15
  type : 'POST',
16
+ data : jQuery.extend({ action : 'bookly_get_email_notifications_data', csrf_token : BooklyL10n.csrf_token }, params),
17
  dataType : 'json',
18
  success : function(response) {
19
  if (response.success) {
85
  };
86
 
87
  $scope.testEmailNotifications = function(){
88
+ var data = {
89
+ action: 'bookly_test_email_notifications',
90
+ csrf_token : BooklyL10n.csrf_token,
91
+ notifications: [],
92
+ to_email : $scope.toEmail,
93
+ sender_name : $scope.dataSource.sender_name,
94
+ sender_email : $scope.dataSource.sender_email,
95
+ reply_to_customers : $scope.dataSource.reply_to_customers,
96
+ send_as : $scope.dataSource.send_as
97
+ };
98
+ angular.forEach($scope.dataSource.notifications, function(notification) {
99
+ if (notification.active == '1') {
100
+ data.notifications.push(notification.type);
101
+ }
102
+ });
103
+ jQuery.ajax({
104
+ url: ajaxurl,
105
+ type: 'POST',
106
+ data: data,
107
+ dataType: 'json',
108
+ success: function (response) {
109
+ booklyAlert({success : [BooklyL10n.sent_successfully]});
110
+ Ladda.stopAll();
111
+ }
112
+ });
113
  };
114
 
115
  });
117
  })();
118
 
119
  var showTestEmailNotificationDialog = function () {
120
+ jQuery('#bookly-test-email-notifications-dialog').modal('show');
121
  };
backend/modules/notifications/resources/js/notification.js CHANGED
@@ -19,6 +19,11 @@ jQuery(function($) {
19
  template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
20
  });
21
 
 
 
 
 
 
22
  booklyAlert(BooklyL10n.alert);
23
 
24
  $(':checkbox').on('change', function () {
@@ -28,7 +33,8 @@ jQuery(function($) {
28
  }
29
  });
30
 
31
- $('.ab-test-email-notifications').on('click',function () {
32
  booklyAlert({error: [BooklyL10n.limitations]});
 
33
  });
34
  });
19
  template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
20
  });
21
 
22
+ $("#bookly-js-new-notification").on('click', function () {
23
+ booklyAlert({error: [BooklyL10n.limitations]});
24
+ $(this).prop('disabled', true);
25
+ });
26
+
27
  booklyAlert(BooklyL10n.alert);
28
 
29
  $(':checkbox').on('change', function () {
33
  }
34
  });
35
 
36
+ $('.bookly-test-email-notifications').on('click',function () {
37
  booklyAlert({error: [BooklyL10n.limitations]});
38
+ $(this).prop('disabled', true);
39
  });
40
  });
backend/modules/notifications/templates/_codes.php DELETED
@@ -1,34 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) ),
4
- array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) ),
5
- array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) ),
6
- array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) ),
7
- array( 'code' => 'cancel_appointment', 'description' => __( 'cancel appointment link', 'bookly' ) ),
8
- array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) ),
9
- array( 'code' => 'cancellation_reason', 'description' => __( 'reason you mentioned while deleting appointment', 'bookly' ) ),
10
- array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
11
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
12
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
13
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
14
- array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
15
- array( 'code' => 'company_logo', 'description' => __( 'company logo', 'bookly' ) ),
16
- array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
17
- array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
18
- array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
19
- array( 'code' => 'custom_fields', 'description' => __( 'combined values of all custom fields', 'bookly' ) ),
20
- array( 'code' => 'custom_fields_2c', 'description' => __( 'combined values of all custom fields (formatted in 2 columns)', 'bookly' ) ),
21
- array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) ),
22
- array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) ),
23
- array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
24
- array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
25
- array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
26
- array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
27
- array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) ),
28
- array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
29
- array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
30
- array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) ),
31
- array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) ),
32
- array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
33
- );
34
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_prepare_notification_short_codes', $codes ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/notifications/templates/_codes_cart.php DELETED
@@ -1,16 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'cart_info', 'description' => __( 'cart information', 'bookly' ) ),
4
- array( 'code' => 'cart_info_c', 'description' => __( 'cart information with cancel', 'bookly' ) ),
5
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
6
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
7
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
8
- array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
9
- array( 'code' => 'company_logo', 'description' => __( 'company logo', 'bookly' ) ),
10
- array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
11
- array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
12
- array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
13
- array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
14
- array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
15
- );
16
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_prepare_cart_notification_short_codes', $codes ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/notifications/templates/_codes_client_new_wp_user.php DELETED
@@ -1,15 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
4
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
5
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
6
- array( 'code' => 'company_name', 'description' => __( 'name of your company', 'bookly' ) ),
7
- array( 'code' => 'company_logo', 'description' => __( 'your company logo', 'bookly' ) ),
8
- array( 'code' => 'company_address', 'description' => __( 'address of your company', 'bookly' ) ),
9
- array( 'code' => 'company_phone', 'description' => __( 'your company phone', 'bookly' ) ),
10
- array( 'code' => 'company_website', 'description' => __( 'this web-site address', 'bookly' ) ),
11
- array( 'code' => 'new_username', 'description' => __( 'customer new username', 'bookly' ) ),
12
- array( 'code' => 'new_password', 'description' => __( 'customer new password', 'bookly' ) ),
13
- array( 'code' => 'site_address', 'description' => __( 'site address', 'bookly' ) ),
14
- );
15
- \BooklyLite\Lib\Utils\Common::Codes( $codes );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/notifications/templates/_codes_staff_agenda.php DELETED
@@ -1,7 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'tomorrow_date', 'description' => __( 'date of next day', 'bookly' ) ),
4
- array( 'code' => 'next_day_agenda', 'description' => __( 'staff agenda for next day', 'bookly' ) ),
5
- array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
6
- );
7
- \BooklyLite\Lib\Utils\Common::Codes( $codes );
 
 
 
 
 
 
 
backend/modules/{customers/templates/_export.php → notifications/templates/_custom_notification.php} RENAMED
@@ -1 +1 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
backend/modules/notifications/templates/_test_email_notifications_modal.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <div ng-controller=testEmailNotificationsDialogCtrl>
3
- <div id=ab_test_email_notifications_dialog class="modal fade" tabindex=-1 role="dialog">
4
  <div class="modal-dialog">
5
  <div class="modal-content">
6
  <div ng-show=loading class="bookly-loading"></div>
@@ -83,6 +83,7 @@
83
  </div>
84
  </div>
85
  <div class="modal-footer">
 
86
  <?php \BooklyLite\Lib\Utils\Common::submitButton( '', '', __( 'Send', 'bookly' ) ) ?>
87
  </div>
88
  </form>
@@ -91,7 +92,7 @@
91
  </div><!-- /.modal-dialog -->
92
  </div><!-- /.modal -->
93
 
94
- <div class="modal fade" id="ab--modal" tabindex="-1" role="dialog">
95
  <div class="modal-dialog" role="document">
96
  <div class="modal-content">
97
  <div class="modal-header">
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <div ng-controller=testEmailNotificationsDialogCtrl>
3
+ <div id=bookly-test-email-notifications-dialog class="modal fade" tabindex=-1 role="dialog">
4
  <div class="modal-dialog">
5
  <div class="modal-content">
6
  <div ng-show=loading class="bookly-loading"></div>
83
  </div>
84
  </div>
85
  <div class="modal-footer">
86
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
87
  <?php \BooklyLite\Lib\Utils\Common::submitButton( '', '', __( 'Send', 'bookly' ) ) ?>
88
  </div>
89
  </form>
92
  </div><!-- /.modal-dialog -->
93
  </div><!-- /.modal -->
94
 
95
+ <div class="modal fade" id="bookly-modal" tabindex="-1" role="dialog">
96
  <div class="modal-dialog" role="document">
97
  <div class="modal-content">
98
  <div class="modal-header">
backend/modules/notifications/templates/index.php CHANGED
@@ -1,10 +1,15 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
3
- get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
4
- $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
5
- get_option( 'admin_email' ) : get_option( 'bookly_email_sender' );
6
- $collapse_id = 0;
7
- $form_data = $form->getData();
 
 
 
 
 
8
  ?>
9
  <div id="bookly-tbs" class="wrap">
10
  <div class="bookly-tbs-body" ng-app="notifications">
@@ -12,9 +17,9 @@
12
  <div class="bookly-page-title">
13
  <?php _e( 'Email Notifications', 'bookly' ) ?>
14
  </div>
15
- <?php \BooklyLite\Backend\Modules\Support\Components::getInstance()->renderButtons( $this::page_slug ) ?>
16
  </div>
17
- <form method="post" action="">
18
  <div class="panel panel-default bookly-main" ng-controller="emailNotifications">
19
  <div class="panel-body">
20
  <div class="row">
@@ -27,50 +32,48 @@
27
  <div class="col-md-6">
28
  <div class="form-group">
29
  <label for="sender_email"><?php _e( 'Sender email', 'bookly' ) ?></label>
30
- <input id="sender_email" name="bookly_email_sender" class="form-control ab-sender" type="text" value="<?php echo esc_attr( $bookly_email_sender ) ?>">
31
  </div>
32
  </div>
33
  <div class="col-md-6">
34
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_email_send_as', __( 'Send emails as', 'bookly' ), __( 'HTML allows formatting, colors, fonts, positioning, etc. With Text you must use Text mode of rich-text editors below. On some servers only text emails are sent successfully.', 'bookly' ),
35
  array( array( 'html', __( 'HTML', 'bookly' ) ), array( 'text', __( 'Text', 'bookly' ) ) )
36
  ) ?>
37
  </div>
38
  <div class="col-md-6">
39
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_email_reply_to_customers', __( 'Reply directly to customers', 'bookly' ), __( 'If this option is enabled then the email address of the customer is used as a sender email address for notifications sent to staff members and administrators.', 'bookly' ) ) ?>
40
  </div>
41
  </div>
42
- <?php if ( $form->types['combined'] || \BooklyLite\Lib\Utils\Common::isPluginActive( 'bookly-addon-recurring-appointments/main.php' ) ) : ?>
43
- <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Single', 'bookly' ) ?></h4>
44
- <?php endif ?>
45
- <div class="panel-group bookly-margin-vertical-xlg" id="single">
46
- <?php foreach ( $form->types['single'] as $type ) : ?>
 
 
47
  <div class="panel panel-default bookly-js-collapse">
48
  <div class="panel-heading" role="tab">
49
  <div class="checkbox bookly-margin-remove">
50
  <label>
51
- <input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden">
52
- <input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?>>
53
- <a href="#collapse_<?php echo ++ $collapse_id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#single">
54
- <?php echo $form_data[ $type ]['name'] ?>
55
  </a>
56
  </label>
57
  </div>
58
  </div>
59
- <div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
60
  <div class="panel-body">
61
 
62
- <?php $form->renderSendingTime( $type ) ?>
63
- <?php $form->renderSubject( $type ) ?>
64
- <?php $form->renderEditor( $type ) ?>
65
- <?php $form->renderCopy( $type ) ?>
66
-
67
  <div class="form-group">
68
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
69
- <?php switch ( $type ) :
70
- case 'staff_agenda': include '_codes_staff_agenda.php'; break;
71
- case 'client_new_wp_user': include '_codes_client_new_wp_user.php'; break;
72
- default: include '_codes.php';
73
- endswitch ?>
74
  </div>
75
 
76
  </div>
@@ -80,31 +83,35 @@
80
  </div>
81
 
82
  <?php if ( $form->types['combined'] ) : ?>
 
83
  <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
84
- <div class="panel-group bookly-margin-vertical-xlg" id="combined">
85
- <?php foreach ( $form->types['combined'] as $type ) : ?>
 
 
 
86
  <div class="panel panel-default bookly-js-collapse">
87
  <div class="panel-heading" role="tab">
88
  <div class="checkbox bookly-margin-remove">
89
  <label>
90
- <input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden">
91
- <input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?>>
92
- <a href="#collapse_<?php echo ++ $collapse_id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#combined">
93
- <?php echo $form_data[ $type ]['name'] ?>
94
  </a>
95
  </label>
96
  </div>
97
  </div>
98
- <div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
99
  <div class="panel-body">
100
- <?php $form->renderSubject( $type ) ?>
101
- <?php $form->renderEditor( $type ) ?>
102
- <?php $form->renderCopy( $type ) ?>
 
103
 
104
  <div class="form-group">
105
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
106
- <?php include '_codes_cart.php' ?>
107
-
108
  </div>
109
  </div>
110
  </div>
@@ -113,24 +120,51 @@
113
  </div>
114
  <?php endif ?>
115
 
116
- <?php do_action( 'bookly_render_email_notifications', $form ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  <div class="alert alert-info">
119
- <?php if ( is_multisite() ) : ?>
120
- <p><?php printf( __( 'To send scheduled notifications please refer to <a href="%1$s">Bookly Multisite</a> add-on <a href="%2$s">message</a>.', 'bookly' ), 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
121
- <?php else : ?>
122
- <p><?php _e( 'To send scheduled notifications please execute the following script hourly with your cron:', 'bookly' ) ?></p><br />
123
- <code class="bookly-text-wrap">php -f <?php echo $cron_path ?></code>
124
- <?php endif ?>
 
 
 
 
 
 
 
125
  </div>
126
  </div>
127
 
128
  <div class="panel-footer">
129
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
130
- <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
 
131
 
132
  <div class="pull-left">
133
- <button type="button" class="btn btn-default ab-test-email-notifications btn-lg">
134
  <?php _e( 'Test Email Notifications', 'bookly' ) ?>
135
  </button>
136
  </div>
@@ -139,5 +173,24 @@
139
  </form>
140
 
141
  <?php include '_test_email_notifications_modal.php' ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  </div>
143
  </div>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Entities\Notification;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ use BooklyLite\Lib\Proxy;
5
+ use BooklyLite\Backend\Modules\Support;
6
+
7
+ $bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
8
+ get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
9
+ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
10
+ get_option( 'admin_email' ) : get_option( 'bookly_email_sender' );
11
+
12
+ /** @var BooklyLite\Backend\Modules\Notifications\Forms\Notifications $form */
13
  ?>
14
  <div id="bookly-tbs" class="wrap">
15
  <div class="bookly-tbs-body" ng-app="notifications">
17
  <div class="bookly-page-title">
18
  <?php _e( 'Email Notifications', 'bookly' ) ?>
19
  </div>
20
+ <?php Support\Components::getInstance()->renderButtons( $this::page_slug ) ?>
21
  </div>
22
+ <form method="post" action="<?php echo Common::escAdminUrl( $this::page_slug ) ?>">
23
  <div class="panel panel-default bookly-main" ng-controller="emailNotifications">
24
  <div class="panel-body">
25
  <div class="row">
32
  <div class="col-md-6">
33
  <div class="form-group">
34
  <label for="sender_email"><?php _e( 'Sender email', 'bookly' ) ?></label>
35
+ <input id="sender_email" name="bookly_email_sender" class="form-control bookly-sender" type="text" value="<?php echo esc_attr( $bookly_email_sender ) ?>">
36
  </div>
37
  </div>
38
  <div class="col-md-6">
39
+ <?php Common::optionToggle( 'bookly_email_send_as', __( 'Send emails as', 'bookly' ), __( 'HTML allows formatting, colors, fonts, positioning, etc. With Text you must use Text mode of rich-text editors below. On some servers only text emails are sent successfully.', 'bookly' ),
40
  array( array( 'html', __( 'HTML', 'bookly' ) ), array( 'text', __( 'Text', 'bookly' ) ) )
41
  ) ?>
42
  </div>
43
  <div class="col-md-6">
44
+ <?php Common::optionToggle( 'bookly_email_reply_to_customers', __( 'Reply directly to customers', 'bookly' ), __( 'If this option is enabled then the email address of the customer is used as a sender email address for notifications sent to staff members and administrators.', 'bookly' ) ) ?>
45
  </div>
46
  </div>
47
+
48
+ <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Single', 'bookly' ) ?></h4>
49
+
50
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-single-notifications">
51
+ <?php foreach ( $form->getNotifications( 'single' ) as $notification ) :
52
+ $id = $notification['id'];
53
+ ?>
54
  <div class="panel panel-default bookly-js-collapse">
55
  <div class="panel-heading" role="tab">
56
  <div class="checkbox bookly-margin-remove">
57
  <label>
58
+ <input name="notification[<?php echo $id ?>][active]" value="0" type="checkbox" checked="checked" class="hidden">
59
+ <input id="<?php echo $id ?>_active" name="notification[<?php echo $id ?>][active]" value="1" type="checkbox" <?php checked( $notification['active'] ) ?>>
60
+ <a href="#collapse_<?php echo $id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#bookly-js-single-notifications">
61
+ <?php echo Notification::getName( $notification['type'] ) ?>
62
  </a>
63
  </label>
64
  </div>
65
  </div>
66
+ <div id="collapse_<?php echo $id ?>" class="panel-collapse collapse">
67
  <div class="panel-body">
68
 
69
+ <?php $form->renderSendingTime( $notification ) ?>
70
+ <?php $form->renderSubject( $id ) ?>
71
+ <?php $form->renderEditor( $id ) ?>
72
+ <?php $form->renderAttachIcs( $notification ) ?>
73
+ <?php $form->renderCopy( $notification ) ?>
74
  <div class="form-group">
75
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
76
+ <?php $form->renderCodes( $notification['type'] ) ?>
 
 
 
 
77
  </div>
78
 
79
  </div>
83
  </div>
84
 
85
  <?php if ( $form->types['combined'] ) : ?>
86
+
87
  <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
88
+
89
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-combined-notifications">
90
+ <?php foreach ( $form->getNotifications( 'combined' ) as $notification ) :
91
+ $id = $notification['id'];
92
+ ?>
93
  <div class="panel panel-default bookly-js-collapse">
94
  <div class="panel-heading" role="tab">
95
  <div class="checkbox bookly-margin-remove">
96
  <label>
97
+ <input name="notification[<?php echo $id ?>][active]" value="0" type="checkbox" checked="checked" class="hidden">
98
+ <input id="<?php echo $id ?>_active" name="notification[<?php echo $id ?>][active]" value="1" type="checkbox" <?php checked( $notification['active'] ) ?>>
99
+ <a href="#collapse_<?php echo $id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#bookly-js-combined-notifications">
100
+ <?php echo Notification::getName( $notification['type'] ) ?>
101
  </a>
102
  </label>
103
  </div>
104
  </div>
105
+ <div id="collapse_<?php echo $id ?>" class="panel-collapse collapse">
106
  <div class="panel-body">
107
+
108
+ <?php $form->renderSubject( $id ) ?>
109
+ <?php $form->renderEditor( $id ) ?>
110
+ <?php $form->renderCopy( $notification ) ?>
111
 
112
  <div class="form-group">
113
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
114
+ <?php $form->renderCodes( $notification['type'] ) ?>
 
115
  </div>
116
  </div>
117
  </div>
120
  </div>
121
  <?php endif ?>
122
 
123
+ <?php Proxy\Shared::renderEmailNotifications( $form ) ?>
124
+
125
+ <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Custom', 'bookly' ) ?></h4>
126
+
127
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-custom-notifications">
128
+ <?php foreach ( $form->getNotifications( 'custom' ) as $notification ) :
129
+ $this->render( '_custom_notification', compact( 'form', 'notification', 'statuses' ) );
130
+ endforeach ?>
131
+ </div>
132
+ <div class="row">
133
+ <div class="col-sm-12">
134
+ <div class="form-group">
135
+ <button id="bookly-js-new-notification" type="button" class="btn btn-xlg btn-block btn-success-outline">
136
+ <span class="ladda-label"><i class="dashicons dashicons-plus-alt"></i>
137
+ <?php _e( 'New Notification', 'bookly' ) ?>
138
+ </span>
139
+ </button>
140
+ </div>
141
+ </div>
142
+ </div>
143
 
144
  <div class="alert alert-info">
145
+ <div class="row">
146
+ <div class="col-md-10">
147
+ <?php if ( is_multisite() ) : ?>
148
+ <p><?php printf( __( 'To send scheduled notifications please refer to <a href="%1$s">Bookly Multisite</a> add-on <a href="%2$s">message</a>.', 'bookly' ), 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
149
+ <?php else : ?>
150
+ <p><?php _e( 'To send scheduled notifications please execute the following command hourly with your cron:', 'bookly' ) ?></p><br/>
151
+ <code class="bookly-text-wrap">wget -q -O - <?php echo $cron_uri ?></code>
152
+ <?php endif ?>
153
+ </div>
154
+ <div class="col-md-2">
155
+ <?php Common::optionToggle( 'bookly_ntf_processing_interval', __( 'Notification period', 'bookly' ), __( 'Set period of time when system will attempt to deliver notification to user. Notification will be discarded after period expiration.', 'bookly' ), $bookly_ntf_processing_interval_values ) ?>
156
+ </div>
157
+ </div>
158
  </div>
159
  </div>
160
 
161
  <div class="panel-footer">
162
+ <?php Common::csrf() ?>
163
+ <?php Common::submitButton() ?>
164
+ <?php Common::resetButton() ?>
165
 
166
  <div class="pull-left">
167
+ <button type="button" class="btn btn-default bookly-test-email-notifications btn-lg">
168
  <?php _e( 'Test Email Notifications', 'bookly' ) ?>
169
  </button>
170
  </div>
173
  </form>
174
 
175
  <?php include '_test_email_notifications_modal.php' ?>
176
+
177
+ <div class="modal fade" tabindex="-1" role="dialog" id="bookly-js-continue-confirm">
178
+ <div class="modal-dialog modal-lg" role="document">
179
+ <div class="modal-content">
180
+ <div class="modal-header">
181
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
182
+ <div class="modal-title h4"><?php _e( 'Are you sure?', 'bookly' ) ?></div>
183
+ </div>
184
+ <div class="modal-body">
185
+ <p><?php _e( 'When creating a new notification, the page will be reloaded, and all unsaved changes will be lost.', 'bookly' ) ?></p>
186
+ </div>
187
+ <div class="modal-footer">
188
+ <?php Common::customButton( null, 'btn-lg btn-success bookly-js-save', __( 'Save changes', 'bookly' ) ) ?>
189
+ <?php Common::customButton( null, 'btn-lg btn-danger bookly-js-continue', __( 'Continue without saving', 'bookly' ) ) ?>
190
+ <?php Common::customButton( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
191
+ </div>
192
+ </div>
193
+ </div>
194
+ </div>
195
  </div>
196
  </div>
backend/modules/payments/Controller.php CHANGED
@@ -28,11 +28,12 @@ class Controller extends Lib\Base\Controller
28
  global $wp_locale;
29
 
30
  $this->enqueueStyles( array(
31
- 'backend' => array(
32
- 'bootstrap/css/bootstrap-theme.min.css',
 
 
33
  'css/daterangepicker.css',
34
  ),
35
- 'frontend' => array( 'css/ladda.min.css', ),
36
  ) );
37
 
38
  $this->enqueueScripts( array(
@@ -41,7 +42,7 @@ class Controller extends Lib\Base\Controller
41
  'js/datatables.min.js' => array( 'jquery' ),
42
  'js/moment.min.js',
43
  'js/daterangepicker.js' => array( 'jquery' ),
44
- 'js/chosen.jquery.min.js' => array( 'jquery' ),
45
  ),
46
  'frontend' => array(
47
  'js/spin.min.js' => array( 'jquery' ),
@@ -51,6 +52,7 @@ class Controller extends Lib\Base\Controller
51
  ) );
52
 
53
  wp_localize_script( 'bookly-daterangepicker.js', 'BooklyL10n', array(
 
54
  'today' => __( 'Today', 'bookly' ),
55
  'yesterday' => __( 'Yesterday', 'bookly' ),
56
  'last_7' => __( 'Last 7 Days', 'bookly' ),
@@ -74,6 +76,7 @@ class Controller extends Lib\Base\Controller
74
  'processing' => __( 'Processing...', 'bookly' ),
75
  'details' => __( 'Details', 'bookly' ),
76
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
 
77
  ) );
78
 
79
  $types = array(
@@ -86,7 +89,7 @@ class Controller extends Lib\Base\Controller
86
  Lib\Entities\Payment::TYPE_PAYSON,
87
  Lib\Entities\Payment::TYPE_MOLLIE,
88
  Lib\Entities\Payment::TYPE_COUPON,
89
- Lib\Entities\Payment::WOO_COMMERCE,
90
  );
91
  $providers = Lib\Entities\Staff::query()->select( 'id, full_name' )->sortBy( 'full_name' )->fetchArray();
92
  $services = Lib\Entities\Service::query()->select( 'id, title' )->sortBy( 'title' )->fetchArray();
@@ -104,7 +107,7 @@ class Controller extends Lib\Base\Controller
104
  $filter = $this->getParameter( 'filter' );
105
 
106
  $query = Lib\Entities\Payment::query( 'p' )
107
- ->select( 'p.id, p.created, p.type, p.paid, p.total, p.status, p.details, c.name customer, st.full_name provider, s.title service, a.start_date' )
108
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
109
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
110
  ->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
@@ -118,15 +121,15 @@ class Controller extends Lib\Base\Controller
118
 
119
  $query->whereBetween( 'p.created', $start, $end );
120
 
121
- if ( $filter['type'] != -1 ) {
122
  $query->where( 'p.type', $filter['type'] );
123
  }
124
 
125
- if ( $filter['staff'] != -1 ) {
126
  $query->where( 'st.id', $filter['staff'] );
127
  }
128
 
129
- if ( $filter['service'] != -1 ) {
130
  $query->where( 's.id', $filter['service'] );
131
  }
132
 
@@ -145,9 +148,9 @@ class Controller extends Lib\Base\Controller
145
  ? ' <span class="glyphicon glyphicon-shopping-cart" title="' . esc_attr( __( 'See details for more items', 'bookly' ) ) . '"></span>'
146
  : '' ;
147
 
148
- $paid_title = Lib\Utils\Common::formatPrice( $payment['paid'] );
149
  if ( $payment['paid'] != $payment['total'] ) {
150
- $paid_title = sprintf( __( '%s of %s', 'bookly' ), $paid_title, Lib\Utils\Common::formatPrice( $payment['total'] ) );
151
  }
152
 
153
  $data[] = array(
@@ -173,7 +176,7 @@ class Controller extends Lib\Base\Controller
173
  'recordsTotal' => count( $data ),
174
  'recordsFiltered' => count( $data ),
175
  'data' => $data,
176
- 'total' => Lib\Utils\Common::formatPrice( $total ),
177
  ) );
178
  }
179
 
@@ -192,7 +195,7 @@ class Controller extends Lib\Base\Controller
192
  p.type,
193
  p.details,
194
  p.paid,
195
- c.name AS customer' )
196
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
197
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
198
  ->where( 'p.id', $this->getParameter( 'payment_id' ) )
@@ -210,7 +213,7 @@ class Controller extends Lib\Base\Controller
210
  'paid' => $payment['paid'],
211
  ),
212
  'items' => $details['items'],
213
- 'deposit_enabled' => Lib\Config::isDepositPaymentsEnabled()
214
  );
215
  }
216
 
@@ -234,33 +237,21 @@ class Controller extends Lib\Base\Controller
234
  {
235
  $payment = Lib\Entities\Payment::find( $this->getParameter( 'payment_id' ) );
236
  $payment
237
- ->set( 'paid', $payment->get( 'total' ) )
238
- ->set( 'status', Lib\Entities\Payment::STATUS_COMPLETED )
239
  ->save();
240
 
241
- $payment_title = Lib\Utils\Common::formatPrice( $payment->get( 'paid' ) );
242
- if ( $payment->get( 'paid' ) != $payment->get( 'total' ) ) {
243
- $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $payment->get( 'total' ) ) );
244
  }
245
  $payment_title .= sprintf(
246
  ' %s <span%s>%s</span>',
247
- Lib\Entities\Payment::typeToString( $payment->get( 'type' ) ),
248
- $payment->get( 'status' ) == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
249
- Lib\Entities\Payment::statusToString( $payment->get( 'status' ) )
250
  );
251
 
252
  wp_send_json_success( array( 'payment_title' => $payment_title ) );
253
  }
254
-
255
- /**
256
- * Override parent method to add 'wp_ajax_bookly_' prefix
257
- * so current 'execute*' methods look nicer.
258
- *
259
- * @param string $prefix
260
- */
261
- protected function registerWpActions( $prefix = '' )
262
- {
263
- parent::registerWpActions( 'wp_ajax_bookly_' );
264
- }
265
-
266
  }
28
  global $wp_locale;
29
 
30
  $this->enqueueStyles( array(
31
+ 'frontend' => array( 'css/ladda.min.css', ),
32
+ 'backend' => array(
33
+ 'css/select2.min.css',
34
+ 'bootstrap/css/bootstrap-theme.min.css' => array( 'bookly-select2.min.css' ),
35
  'css/daterangepicker.css',
36
  ),
 
37
  ) );
38
 
39
  $this->enqueueScripts( array(
42
  'js/datatables.min.js' => array( 'jquery' ),
43
  'js/moment.min.js',
44
  'js/daterangepicker.js' => array( 'jquery' ),
45
+ 'js/select2.full.min.js' => array( 'jquery' ),
46
  ),
47
  'frontend' => array(
48
  'js/spin.min.js' => array( 'jquery' ),
52
  ) );
53
 
54
  wp_localize_script( 'bookly-daterangepicker.js', 'BooklyL10n', array(
55
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
56
  'today' => __( 'Today', 'bookly' ),
57
  'yesterday' => __( 'Yesterday', 'bookly' ),
58
  'last_7' => __( 'Last 7 Days', 'bookly' ),
76
  'processing' => __( 'Processing...', 'bookly' ),
77
  'details' => __( 'Details', 'bookly' ),
78
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
79
+ 'no_result_found' => __( 'No result found', 'bookly' )
80
  ) );
81
 
82
  $types = array(
89
  Lib\Entities\Payment::TYPE_PAYSON,
90
  Lib\Entities\Payment::TYPE_MOLLIE,
91
  Lib\Entities\Payment::TYPE_COUPON,
92
+ Lib\Entities\Payment::TYPE_WOOCOMMERCE,
93
  );
94
  $providers = Lib\Entities\Staff::query()->select( 'id, full_name' )->sortBy( 'full_name' )->fetchArray();
95
  $services = Lib\Entities\Service::query()->select( 'id, title' )->sortBy( 'title' )->fetchArray();
107
  $filter = $this->getParameter( 'filter' );
108
 
109
  $query = Lib\Entities\Payment::query( 'p' )
110
+ ->select( 'p.id, p.created, p.type, p.paid, p.total, p.status, p.details, c.full_name customer, st.full_name provider, s.title service, a.start_date' )
111
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
112
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
113
  ->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
121
 
122
  $query->whereBetween( 'p.created', $start, $end );
123
 
124
+ if ( $filter['type'] != '' ) {
125
  $query->where( 'p.type', $filter['type'] );
126
  }
127
 
128
+ if ( $filter['staff'] != '' ) {
129
  $query->where( 'st.id', $filter['staff'] );
130
  }
131
 
132
+ if ( $filter['service'] != '' ) {
133
  $query->where( 's.id', $filter['service'] );
134
  }
135
 
148
  ? ' <span class="glyphicon glyphicon-shopping-cart" title="' . esc_attr( __( 'See details for more items', 'bookly' ) ) . '"></span>'
149
  : '' ;
150
 
151
+ $paid_title = Lib\Utils\Price::format( $payment['paid'] );
152
  if ( $payment['paid'] != $payment['total'] ) {
153
+ $paid_title = sprintf( __( '%s of %s', 'bookly' ), $paid_title, Lib\Utils\Price::format( $payment['total'] ) );
154
  }
155
 
156
  $data[] = array(
176
  'recordsTotal' => count( $data ),
177
  'recordsFiltered' => count( $data ),
178
  'data' => $data,
179
+ 'total' => Lib\Utils\Price::format( $total ),
180
  ) );
181
  }
182
 
195
  p.type,
196
  p.details,
197
  p.paid,
198
+ c.full_name AS customer' )
199
  ->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
200
  ->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
201
  ->where( 'p.id', $this->getParameter( 'payment_id' ) )
213
  'paid' => $payment['paid'],
214
  ),
215
  'items' => $details['items'],
216
+ 'deposit_enabled' => Lib\Config::depositPaymentsEnabled()
217
  );
218
  }
219
 
237
  {
238
  $payment = Lib\Entities\Payment::find( $this->getParameter( 'payment_id' ) );
239
  $payment
240
+ ->setPaid( $payment->getTotal() )
241
+ ->setStatus( Lib\Entities\Payment::STATUS_COMPLETED )
242
  ->save();
243
 
244
+ $payment_title = Lib\Utils\Price::format( $payment->getPaid() );
245
+ if ( $payment->getPaid() != $payment->getTotal() ) {
246
+ $payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $payment->getTotal() ) );
247
  }
248
  $payment_title .= sprintf(
249
  ' %s <span%s>%s</span>',
250
+ Lib\Entities\Payment::typeToString( $payment->getType() ),
251
+ $payment->getStatus() == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
252
+ Lib\Entities\Payment::statusToString( $payment->getStatus() )
253
  );
254
 
255
  wp_send_json_success( array( 'payment_title' => $payment_title ) );
256
  }
 
 
 
 
 
 
 
 
 
 
 
 
257
  }
backend/modules/payments/resources/js/ng-payment_details_dialog.js CHANGED
@@ -20,7 +20,7 @@
20
  }
21
  jQuery.ajax({
22
  url: ajaxurl,
23
- data: {action: 'bookly_get_payment_details', payment_id: payment_id},
24
  dataType: 'json',
25
  success: function (response) {
26
  if (response.success) {
@@ -30,7 +30,7 @@
30
  ladda.start();
31
  jQuery.ajax({
32
  url: ajaxurl,
33
- data: {action: 'bookly_complete_payment', payment_id: payment_id},
34
  dataType: 'json',
35
  type: 'POST',
36
  success: function (response) {
20
  }
21
  jQuery.ajax({
22
  url: ajaxurl,
23
+ data: {action: 'bookly_get_payment_details', payment_id: payment_id, csrf_token : BooklyL10n.csrf_token},
24
  dataType: 'json',
25
  success: function (response) {
26
  if (response.success) {
30
  ladda.start();
31
  jQuery.ajax({
32
  url: ajaxurl,
33
+ data: {action: 'bookly_complete_payment', payment_id: payment_id, csrf_token : BooklyL10n.csrf_token},
34
  dataType: 'json',
35
  type: 'POST',
36
  success: function (response) {
backend/modules/payments/resources/js/payments.js CHANGED
@@ -10,7 +10,19 @@ jQuery(function($) {
10
  $payment_total = $('#bookly-payment-total'),
11
  $delete_button = $('#bookly-delete')
12
  ;
13
-
 
 
 
 
 
 
 
 
 
 
 
 
14
  /**
15
  * Init DataTables.
16
  */
@@ -24,9 +36,11 @@ jQuery(function($) {
24
  serverSide: true,
25
  ajax: {
26
  url: ajaxurl,
 
27
  data: function ( d ) {
28
  return $.extend( {}, d, {
29
  action: 'bookly_get_payments',
 
30
  filter: {
31
  created: $date_filter.data('date'),
32
  type: $type_filter.val(),
@@ -131,13 +145,7 @@ jQuery(function($) {
131
  }
132
  );
133
 
134
- /**
135
- * On filters change.
136
- */
137
- $('.bookly-js-chosen-select').chosen({
138
- allow_single_deselect: true,
139
- disable_search_threshold: 10
140
- });
141
  $date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
142
  $type_filter.on('change', function () { dt.ajax.reload(); });
143
  $staff_filter.on('change', function () { dt.ajax.reload(); });
@@ -162,16 +170,17 @@ jQuery(function($) {
162
  type : 'POST',
163
  data : {
164
  action : 'bookly_delete_payments',
165
- data : data
 
166
  },
167
  dataType : 'json',
168
  success : function(response) {
169
- ladda.stop();
170
  if (response.success) {
171
  dt.rows($checkboxes.closest('td')).remove().draw();
172
  } else {
173
  alert(response.data.message);
174
  }
 
175
  }
176
  });
177
  }
10
  $payment_total = $('#bookly-payment-total'),
11
  $delete_button = $('#bookly-delete')
12
  ;
13
+ $('.bookly-js-select')
14
+ .val(null)
15
+ .on('select2:unselecting', function(e) {
16
+ e.preventDefault();
17
+ $(this).val(null).trigger('change');
18
+ })
19
+ .select2({
20
+ allowClear: true,
21
+ theme: 'bootstrap',
22
+ language: {
23
+ noResults: function() { return BooklyL10n.no_result_found; }
24
+ }
25
+ });
26
  /**
27
  * Init DataTables.
28
  */
36
  serverSide: true,
37
  ajax: {
38
  url: ajaxurl,
39
+ type: 'POST',
40
  data: function ( d ) {
41
  return $.extend( {}, d, {
42
  action: 'bookly_get_payments',
43
+ csrf_token: BooklyL10n.csrf_token,
44
  filter: {
45
  created: $date_filter.data('date'),
46
  type: $type_filter.val(),
145
  }
146
  );
147
 
148
+
 
 
 
 
 
 
149
  $date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
150
  $type_filter.on('change', function () { dt.ajax.reload(); });
151
  $staff_filter.on('change', function () { dt.ajax.reload(); });
170
  type : 'POST',
171
  data : {
172
  action : 'bookly_delete_payments',
173
+ csrf_token : BooklyL10n.csrf_token,
174
+ data : data
175
  },
176
  dataType : 'json',
177
  success : function(response) {
 
178
  if (response.success) {
179
  dt.rows($checkboxes.closest('td')).remove().draw();
180
  } else {
181
  alert(response.data.message);
182
  }
183
+ ladda.stop();
184
  }
185
  });
186
  }
backend/modules/payments/templates/details.php CHANGED
@@ -1,6 +1,11 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $subtotal = 0;
3
- $subtotal_deposit = 0;
 
 
 
 
 
4
  ?>
5
  <?php if ( $payment ) : ?>
6
  <div class="table-responsive">
@@ -15,9 +20,9 @@
15
  <tr>
16
  <td><?php echo $payment['customer'] ?></td>
17
  <td>
18
- <div><?php _e( 'Date', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Utils\DateTime::formatDateTime( $payment['created'] ) ?></div>
19
- <div><?php _e( 'Type', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Entities\Payment::typeToString( $payment['type'] ) ?></div>
20
- <div><?php _e( 'Status', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Entities\Payment::statusToString( $payment['status'] ) ?></div>
21
  </td>
22
  </tr>
23
  </tbody>
@@ -52,14 +57,14 @@
52
  </ul>
53
  <?php endif ?>
54
  </td>
55
- <td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDateTime( $item['appointment_date'] ) ?></td>
56
  <td><?php echo $item['staff_name'] ?></td>
57
- <?php $deposit = apply_filters( 'bookly_deposit_payments_get_deposit_amount', $item['number_of_persons'] * ( $item['service_price'] + $extras_price ), $item['deposit'], $item['number_of_persons'] ) ?>
58
  <?php if ( $deposit_enabled ) : ?>
59
- <td class="text-right"><?php echo apply_filters( 'bookly_deposit_payments_format_deposit', $deposit, $item['deposit'] ) ?></td>
60
  <?php endif ?>
61
  <td class="text-right">
62
- <?php $service_price = \BooklyLite\Lib\Utils\Common::formatPrice( $item['service_price'] ) ?>
63
  <?php if ( $item['number_of_persons'] > 1 ) $service_price = $item['number_of_persons'] . '&nbsp;&times;&nbsp' . $service_price ?>
64
  <?php echo $service_price ?>
65
  <ul class="bookly-list">
@@ -68,7 +73,7 @@
68
  <?php printf( '%s%s%s',
69
  ( $item['number_of_persons'] > 1 ) ? $item['number_of_persons'] . '&nbsp;&times;&nbsp;' : '',
70
  ( $extra['quantity'] > 1 ) ? $extra['quantity'] . '&nbsp;&times;&nbsp;' : '',
71
- \BooklyLite\Lib\Utils\Common::formatPrice( $extra['price'] )
72
  ) ?>
73
  </li>
74
  <?php $subtotal += $item['number_of_persons'] * $extra['price'] * $extra['quantity'] ?>
@@ -85,9 +90,9 @@
85
  <th rowspan="3" style="border-left-color: white; border-bottom-color: white;"></th>
86
  <th colspan="2"><?php _e( 'Subtotal', 'bookly' ) ?></th>
87
  <?php if ( $deposit_enabled ) : ?>
88
- <th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $subtotal_deposit ) ?></th>
89
  <?php endif ?>
90
- <th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $subtotal ) ?></th>
91
  </tr>
92
  <tr>
93
  <th colspan="<?php echo 2 + (int) $deposit_enabled ?>">
@@ -100,26 +105,26 @@
100
  <div>-<?php echo $payment['coupon']['discount'] ?>%</div>
101
  <?php endif ?>
102
  <?php if ( $payment['coupon']['deduction'] ) : ?>
103
- <div><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( - $payment['coupon']['deduction'] ) ?></div>
104
  <?php endif ?>
105
  <?php else : ?>
106
- <?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 0 ) ?>
107
  <?php endif ?>
108
  </th>
109
  </tr>
110
  <tr>
111
  <th colspan="<?php echo 2 + (int) $deposit_enabled ?>"><?php _e( 'Total', 'bookly' ) ?></th>
112
- <th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['total'] ) ?></th>
113
  </tr>
114
  <?php if ( $payment['total'] != $payment['paid'] ) : ?>
115
  <tr>
116
  <td rowspan="2" style="border-left-color:#fff;border-bottom-color:#fff;"></td>
117
  <td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Paid', 'bookly' ) ?></i></td>
118
- <td class="text-right"><i><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['paid'] ) ?></i></td>
119
  </tr>
120
  <tr>
121
  <td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Due', 'bookly' ) ?></i></td>
122
- <td class="text-right"><i><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['total'] - $payment['paid'] ) ?></i></td>
123
  </tr>
124
  <tr>
125
  <td style="border-left-color:#fff;border-bottom-color:#fff;"></td>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Price;
3
+ use BooklyLite\Lib\Utils\DateTime;
4
+ use BooklyLite\Lib\Entities;
5
+ use BooklyLite\Lib\Proxy;
6
+
7
+ $subtotal = 0;
8
+ $subtotal_deposit = 0;
9
  ?>
10
  <?php if ( $payment ) : ?>
11
  <div class="table-responsive">
20
  <tr>
21
  <td><?php echo $payment['customer'] ?></td>
22
  <td>
23
+ <div><?php _e( 'Date', 'bookly' ) ?>: <?php echo DateTime::formatDateTime( $payment['created'] ) ?></div>
24
+ <div><?php _e( 'Type', 'bookly' ) ?>: <?php echo Entities\Payment::typeToString( $payment['type'] ) ?></div>
25
+ <div><?php _e( 'Status', 'bookly' ) ?>: <?php echo Entities\Payment::statusToString( $payment['status'] ) ?></div>
26
  </td>
27
  </tr>
28
  </tbody>
57
  </ul>
58
  <?php endif ?>
59
  </td>
60
+ <td><?php echo DateTime::formatDateTime( $item['appointment_date'] ) ?></td>
61
  <td><?php echo $item['staff_name'] ?></td>
62
+ <?php $deposit = Proxy\DepositPayments::prepareAmount( $item['number_of_persons'] * ( $item['service_price'] + $extras_price ), $item['deposit'], $item['number_of_persons'] ) ?>
63
  <?php if ( $deposit_enabled ) : ?>
64
+ <td class="text-right"><?php echo Proxy\DepositPayments::formatDeposit( $deposit, $item['deposit'] ) ?></td>
65
  <?php endif ?>
66
  <td class="text-right">
67
+ <?php $service_price = Price::format( $item['service_price'] ) ?>
68
  <?php if ( $item['number_of_persons'] > 1 ) $service_price = $item['number_of_persons'] . '&nbsp;&times;&nbsp' . $service_price ?>
69
  <?php echo $service_price ?>
70
  <ul class="bookly-list">
73
  <?php printf( '%s%s%s',
74
  ( $item['number_of_persons'] > 1 ) ? $item['number_of_persons'] . '&nbsp;&times;&nbsp;' : '',
75
  ( $extra['quantity'] > 1 ) ? $extra['quantity'] . '&nbsp;&times;&nbsp;' : '',
76
+ Price::format( $extra['price'] )
77
  ) ?>
78
  </li>
79
  <?php $subtotal += $item['number_of_persons'] * $extra['price'] * $extra['quantity'] ?>
90
  <th rowspan="3" style="border-left-color: white; border-bottom-color: white;"></th>
91
  <th colspan="2"><?php _e( 'Subtotal', 'bookly' ) ?></th>
92
  <?php if ( $deposit_enabled ) : ?>
93
+ <th class="text-right"><?php echo Price::format( $subtotal_deposit ) ?></th>
94
  <?php endif ?>
95
+ <th class="text-right"><?php echo Price::format( $subtotal ) ?></th>
96
  </tr>
97
  <tr>
98
  <th colspan="<?php echo 2 + (int) $deposit_enabled ?>">
105
  <div>-<?php echo $payment['coupon']['discount'] ?>%</div>
106
  <?php endif ?>
107
  <?php if ( $payment['coupon']['deduction'] ) : ?>
108
+ <div><?php echo Price::format( -$payment['coupon']['deduction'] ) ?></div>
109
  <?php endif ?>
110
  <?php else : ?>
111
+ <?php echo Price::format( 0 ) ?>
112
  <?php endif ?>
113
  </th>
114
  </tr>
115
  <tr>
116
  <th colspan="<?php echo 2 + (int) $deposit_enabled ?>"><?php _e( 'Total', 'bookly' ) ?></th>
117
+ <th class="text-right"><?php echo Price::format( $payment['total'] ) ?></th>
118
  </tr>
119
  <?php if ( $payment['total'] != $payment['paid'] ) : ?>
120
  <tr>
121
  <td rowspan="2" style="border-left-color:#fff;border-bottom-color:#fff;"></td>
122
  <td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Paid', 'bookly' ) ?></i></td>
123
+ <td class="text-right"><i><?php echo Price::format( $payment['paid'] ) ?></i></td>
124
  </tr>
125
  <tr>
126
  <td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Due', 'bookly' ) ?></i></td>
127
+ <td class="text-right"><i><?php echo Price::format( $payment['total'] - $payment['paid'] ) ?></i></td>
128
  </tr>
129
  <tr>
130
  <td style="border-left-color:#fff;border-bottom-color:#fff;"></td>
backend/modules/payments/templates/index.php CHANGED
@@ -22,8 +22,7 @@
22
  </div>
23
  <div class="col-md-2 col-lg-2">
24
  <div class="form-group">
25
- <select id="bookly-filter-type" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Type', 'bookly' ) ?>">
26
- <option value="-1"></option>
27
  <?php foreach ( $types as $type ) : ?>
28
  <option value="<?php echo esc_attr( $type ) ?>">
29
  <?php echo \BooklyLite\Lib\Entities\Payment::typeToString( $type ) ?>
@@ -34,8 +33,7 @@
34
  </div>
35
  <div class="col-md-3 col-lg-2">
36
  <div class="form-group">
37
- <select id="bookly-filter-staff" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Provider', 'bookly' ) ?>">
38
- <option value="-1"></option>
39
  <?php foreach ( $providers as $provider ) : ?>
40
  <option value="<?php echo $provider['id'] ?>"><?php echo esc_html( $provider['full_name'] ) ?></option>
41
  <?php endforeach ?>
@@ -44,8 +42,7 @@
44
  </div>
45
  <div class="col-md-3 col-lg-2">
46
  <div class="form-group">
47
- <select id="bookly-filter-service" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Service', 'bookly' ) ?>">
48
- <option value="-1"></option>
49
  <?php foreach ( $services as $service ) : ?>
50
  <option value="<?php echo $service['id'] ?>"><?php echo esc_html( $service['title'] ) ?></option>
51
  <?php endforeach ?>
22
  </div>
23
  <div class="col-md-2 col-lg-2">
24
  <div class="form-group">
25
+ <select id="bookly-filter-type" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Type', 'bookly' ) ?>">
 
26
  <?php foreach ( $types as $type ) : ?>
27
  <option value="<?php echo esc_attr( $type ) ?>">
28
  <?php echo \BooklyLite\Lib\Entities\Payment::typeToString( $type ) ?>
33
  </div>
34
  <div class="col-md-3 col-lg-2">
35
  <div class="form-group">
36
+ <select id="bookly-filter-staff" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Provider', 'bookly' ) ?>">
 
37
  <?php foreach ( $providers as $provider ) : ?>
38
  <option value="<?php echo $provider['id'] ?>"><?php echo esc_html( $provider['full_name'] ) ?></option>
39
  <?php endforeach ?>
42
  </div>
43
  <div class="col-md-3 col-lg-2">
44
  <div class="form-group">
45
+ <select id="bookly-filter-service" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Service', 'bookly' ) ?>">
 
46
  <?php foreach ( $services as $service ) : ?>
47
  <option value="<?php echo $service['id'] ?>"><?php echo esc_html( $service['title'] ) ?></option>
48
  <?php endforeach ?>
backend/modules/services/Controller.php CHANGED
@@ -38,20 +38,26 @@ class Controller extends Lib\Base\Controller
38
  )
39
  ) );
40
 
 
 
 
 
 
 
41
  wp_localize_script( 'bookly-service.js', 'BooklyL10n', array(
42
- 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
43
- 'saved' => __( 'Settings saved.', 'bookly' ),
44
- 'selector' => array( 'nothing_selected' => __( 'No staff selected', 'bookly' ), ),
45
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
 
 
 
46
  ) );
47
 
48
  // Allow add-ons to enqueue their assets.
49
- do_action( 'bookly_enqueue_assets_for_services' );
50
 
51
- $staff_collection = $this->getStaffCollection();
52
- $category_collection = $this->getCategoryCollection();
53
- $service_collection = $this->getServiceCollection();
54
- $this->render( 'index', compact( 'staff_collection', 'category_collection', 'service_collection' ) );
55
  }
56
 
57
  /**
@@ -59,23 +65,25 @@ class Controller extends Lib\Base\Controller
59
  */
60
  public function executeGetCategoryServices()
61
  {
62
- $this->setDataForServiceList();
63
- wp_send_json_success( $this->render( '_list', array(), false ) );
64
  }
65
 
66
  /**
67
  *
68
  */
69
- public function executeCategoryForm()
70
  {
 
71
  if ( ! empty ( $_POST ) ) {
72
- $form = new Forms\Category();
73
- $form->bind( $this->getPostParameters() );
74
- if ( $category = $form->save() ) {
75
- $this->render( '_category_item', array( 'category' => $category->getFields() ) );
 
 
76
  }
77
  }
78
- exit;
79
  }
80
 
81
  /**
@@ -97,7 +105,7 @@ class Controller extends Lib\Base\Controller
97
  foreach ( $category_sorts as $position => $category_id ) {
98
  $category_sort = new Lib\Entities\Category();
99
  $category_sort->load( $category_id );
100
- $category_sort->set( 'position', $position );
101
  $category_sort->save();
102
  }
103
  }
@@ -111,18 +119,46 @@ class Controller extends Lib\Base\Controller
111
  foreach ( $services_sorts as $position => $service_ids ) {
112
  $services_sort = new Lib\Entities\Service();
113
  $services_sort->load( $service_ids );
114
- $services_sort->set( 'position', $position );
115
  $services_sort->save();
116
  }
117
  }
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  /**
120
  * Delete category.
121
  */
122
  public function executeDeleteCategory()
123
  {
124
  $category = new Lib\Entities\Category();
125
- $category->set( 'id', $this->getParameter( 'id', 0 ) );
126
  $category->delete();
127
  }
128
 
@@ -130,19 +166,23 @@ class Controller extends Lib\Base\Controller
130
  {
131
  $form = new Forms\Service();
132
  $form->bind( $this->getPostParameters() );
133
- $form->getObject()->set( 'duration', Lib\Config::getTimeSlotLength() );
134
  $service = $form->save();
135
- $this->setDataForServiceList( $service->get( 'category_id' ) );
136
- do_action( 'bookly_service_created', $service, $this->getPostParameters() );
137
- wp_send_json_success( array( 'html' => $this->render( '_list', array(), false ), 'service_id' => $service->get( 'id' ) ) );
138
  }
139
 
140
  public function executeRemoveServices()
141
  {
142
  $service_ids = $this->getParameter( 'service_ids', array() );
143
  if ( is_array( $service_ids ) && ! empty ( $service_ids ) ) {
 
 
 
144
  Lib\Entities\Service::query( 's' )->delete()->whereIn( 's.id', $service_ids )->execute();
145
  }
 
146
  }
147
 
148
  /**
@@ -158,82 +198,80 @@ class Controller extends Lib\Base\Controller
158
  $service = $form->save();
159
 
160
  $staff_ids = $this->getParameter( 'staff_ids', array() );
161
- if ( empty( $staff_ids ) ) {
162
- Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->get( 'id' ) )->execute();
163
  } else {
164
- Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->get( 'id' ) )->whereNotIn( 'staff_id', $staff_ids )->execute();
165
- if ( $this->getParameter( 'update_staff', false ) ) {
166
- $wpdb->update( Lib\Entities\StaffService::getTableName(), array( 'price' => $this->getParameter( 'price' ) ), array( 'service_id' => $this->getParameter( 'id' ) ) );
167
- $wpdb->update( Lib\Entities\StaffService::getTableName(), array( 'capacity' => 1 ), array( 'service_id' => $this->getParameter( 'id' ) ) );
168
- }
169
- $service_staff_exists = Lib\Entities\StaffService::query()->select( 'staff_id' )->where( 'service_id', $service->get( 'id' ) )->fetchArray();
170
- $service_staff = array();
171
- foreach ( $service_staff_exists as $staff ) {
172
- $service_staff[] = $staff['staff_id'];
173
- }
174
- foreach ( $staff_ids as $staff_id ) {
175
- if ( ! in_array( $staff_id, $service_staff ) ) {
176
- $staff_service = new Lib\Entities\StaffService();
177
- $staff_service->set( 'staff_id', 1 );
178
- $staff_service->set( 'service_id', $service->get( 'id' ) );
179
- $staff_service->set( 'price', $service->get( 'price' ) );
180
- $staff_service->set( 'capacity', 1 );
181
- $staff_service->save();
182
  }
183
- }
184
- }
185
-
186
- do_action( 'bookly_update_service', $service, $this->getPostParameters() );
187
-
188
- /** @var \BooklyServiceExtras\Lib\Entities\ServiceExtra[] $service_extras
189
- * @var \BooklyServiceExtras\Lib\Entities\ServiceExtra[] $db_extras
190
- */
191
- $service_extras = apply_filters( 'bookly_service_extras_find_by_service_id', array(), $service->get( 'id' ) );
192
- $db_extras = array();
193
- foreach ( $service_extras as $extra ) {
194
- $db_extras[ $extra->get( 'id' ) ] = $extra;
195
- }
196
- $new_extras = array();
197
- // Find id for new extras.
198
- foreach ( $this->getParameter( 'extras', array() ) as $_post_id => $_post_extra ) {
199
- if ( isset( $db_extras[ $_post_id ] ) ) {
200
- unset( $db_extras[ $_post_id ] );
201
- } else {
202
- foreach ( $db_extras as $id => $extra ) {
203
- if ( $extra->get( 'title' ) == $_post_extra['title']
204
- && $extra->get( 'price' ) == $_post_extra['price']
205
- && $extra->get( 'duration' ) == $_post_extra['duration']
206
- ) {
207
- $new_extras[ $_post_id ] = $id;
208
- unset( $db_extras[ $id ] );
209
- break;
210
  }
211
  }
212
  }
213
  }
214
 
215
- $price = Lib\Utils\Common::formatPrice( $service->get( 'price' ) );
216
- if ( $service->get( 'type' ) == Lib\Entities\Service::TYPE_SIMPLE ) {
217
- $nice_duration = Lib\Utils\DateTime::secondsToInterval( $service->get( 'duration' ) );
218
- } else {
219
- $nice_duration = sprintf( _n( '%d service', '%d services', count( json_decode( $service->get( 'sub_services' ), true ) ), 'bookly' ), count( json_decode( $service->get( 'sub_services' ), true ) ) );
220
- }
221
 
222
- wp_send_json_success( array( 'title' => $service->get( 'title' ), 'price' => $price, 'color' => $service->get( 'color' ), 'nice_duration' => $nice_duration, 'new_extras' => $new_extras ) );
 
 
 
 
223
  }
224
 
225
  /**
 
 
226
  * @param int $category_id
 
227
  */
228
- private function setDataForServiceList( $category_id = 0 )
229
  {
230
  if ( ! $category_id ) {
231
  $category_id = $this->getParameter( 'category_id', 0 );
232
  }
233
 
234
- $this->service_collection = $this->getServiceCollection( $category_id );
235
- $this->staff_collection = $this->getStaffCollection();
236
- $this->category_collection = $this->getCategoryCollection();
 
 
 
 
 
 
 
 
 
237
  }
238
 
239
  /**
@@ -259,35 +297,36 @@ class Controller extends Lib\Base\Controller
259
  private function getServiceCollection( $id = 0 )
260
  {
261
  $services = Lib\Entities\Service::query( 's' )
262
- ->select( 's.*, COUNT(staff.id) AS total_staff, GROUP_CONCAT(DISTINCT staff.id) AS staff_ids' )
263
  ->leftJoin( 'StaffService', 'ss', 'ss.service_id = s.id' )
 
264
  ->leftJoin( 'Staff', 'staff', 'staff.id = ss.staff_id' )
265
  ->whereRaw( 's.category_id = %d OR !%d', array( $id, $id ) )
266
  ->groupBy( 's.id' )
267
  ->indexBy( 'id' )
268
  ->sortBy( 's.position' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
270
- return $services->fetchArray();
271
  }
272
 
273
  public function executeUpdateExtraPosition()
274
  {
275
- do_action( 'bookly_service_extras_reorder', $this->getParameter( 'position' ) );
276
-
277
  wp_send_json_success();
278
  }
279
-
280
- // Protected methods.
281
-
282
- /**
283
- * Override parent method to add 'wp_ajax_bookly_' prefix
284
- * so current 'execute*' methods look nicer.
285
- *
286
- * @param string $prefix
287
- */
288
- protected function registerWpActions( $prefix = '' )
289
- {
290
- parent::registerWpActions( 'wp_ajax_bookly_' );
291
- }
292
-
293
  }
38
  )
39
  ) );
40
 
41
+ $data = $this->getCaSeStSpCollections();
42
+ $staff = array();
43
+ foreach ( $data['staff_collection'] as $employee ) {
44
+ $staff[ $employee['id'] ] = $employee['full_name'];
45
+ }
46
+
47
  wp_localize_script( 'bookly-service.js', 'BooklyL10n', array(
48
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
49
+ 'capacity_error' => __( 'Min capacity should not be greater than max capacity.', 'bookly' ),
50
+ 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
51
+ 'service_special_day' => Lib\Config::specialDaysEnabled() && Lib\Config::specialDaysEnabled(),
52
+ 'reorder' => esc_attr__( 'Reorder', 'bookly' ),
53
+ 'staff' => $staff,
54
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
55
  ) );
56
 
57
  // Allow add-ons to enqueue their assets.
58
+ Lib\Proxy\Shared::enqueueAssetsForServices();
59
 
60
+ $this->render( 'index', $data );
 
 
 
61
  }
62
 
63
  /**
65
  */
66
  public function executeGetCategoryServices()
67
  {
68
+ wp_send_json_success( $this->render( '_list', $this->getCaSeStSpCollections(), false ) );
 
69
  }
70
 
71
  /**
72
  *
73
  */
74
+ public function executeAddCategory()
75
  {
76
+ $html = '';
77
  if ( ! empty ( $_POST ) ) {
78
+ if ( $this->csrfTokenValid() ) {
79
+ $form = new Forms\Category();
80
+ $form->bind( $this->getPostParameters() );
81
+ if ( $category = $form->save() ) {
82
+ $html = $this->render( '_category_item', array( 'category' => $category->getFields() ), false );
83
+ }
84
  }
85
  }
86
+ wp_send_json_success( compact( 'html' ) );
87
  }
88
 
89
  /**
105
  foreach ( $category_sorts as $position => $category_id ) {
106
  $category_sort = new Lib\Entities\Category();
107
  $category_sort->load( $category_id );
108
+ $category_sort->setPosition( $position );
109
  $category_sort->save();
110
  }
111
  }
119
  foreach ( $services_sorts as $position => $service_ids ) {
120
  $services_sort = new Lib\Entities\Service();
121
  $services_sort->load( $service_ids );
122
+ $services_sort->setPosition( $position );
123
  $services_sort->save();
124
  }
125
  }
126
 
127
+ /**
128
+ * Reorder staff preferences for service
129
+ */
130
+ public function executeUpdateServiceStaffPreferenceOrders()
131
+ {
132
+ $service_id = $this->getParameter( 'service_id' );
133
+ $positions = (array) $this->getParameter( 'positions' );
134
+ /** @var Lib\Entities\StaffPreferenceOrder[] $staff_preferences */
135
+ $staff_preferences = Lib\Entities\StaffPreferenceOrder::query()
136
+ ->where( 'service_id', $service_id )
137
+ ->indexBy( 'staff_id' )
138
+ ->find();
139
+ foreach ( $positions as $position => $staff_id ) {
140
+ if ( array_key_exists( $staff_id, $staff_preferences ) ) {
141
+ $staff_preferences[ $staff_id ]->setPosition( $position )->save();
142
+ } else {
143
+ $preference = new Lib\Entities\StaffPreferenceOrder();
144
+ $preference
145
+ ->setServiceId( $service_id )
146
+ ->setStaffId( $staff_id )
147
+ ->setPosition( $position )
148
+ ->save();
149
+ }
150
+ }
151
+
152
+ wp_send_json_success();
153
+ }
154
+
155
  /**
156
  * Delete category.
157
  */
158
  public function executeDeleteCategory()
159
  {
160
  $category = new Lib\Entities\Category();
161
+ $category->setId( $this->getParameter( 'id', 0 ) );
162
  $category->delete();
163
  }
164
 
166
  {
167
  $form = new Forms\Service();
168
  $form->bind( $this->getPostParameters() );
169
+ $form->getObject()->setDuration( Lib\Config::getTimeSlotLength() );
170
  $service = $form->save();
171
+ $data = $this->getCaSeStSpCollections( $service->getCategoryId() );
172
+ Lib\Proxy\Shared::serviceCreated( $service, $this->getPostParameters() );
173
+ wp_send_json_success( array( 'html' => $this->render( '_list', $data, false ), 'service_id' => $service->getId() ) );
174
  }
175
 
176
  public function executeRemoveServices()
177
  {
178
  $service_ids = $this->getParameter( 'service_ids', array() );
179
  if ( is_array( $service_ids ) && ! empty ( $service_ids ) ) {
180
+ foreach ( $service_ids as $service_id ) {
181
+ Lib\Proxy\Shared::serviceDeleted( $service_id );
182
+ }
183
  Lib\Entities\Service::query( 's' )->delete()->whereIn( 's.id', $service_ids )->execute();
184
  }
185
+ wp_send_json_success();
186
  }
187
 
188
  /**
198
  $service = $form->save();
199
 
200
  $staff_ids = $this->getParameter( 'staff_ids', array() );
201
+ if ( empty ( $staff_ids ) ) {
202
+ Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->getId() )->execute();
203
  } else {
204
+ Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->getId() )->whereNotIn( 'staff_id', $staff_ids )->execute();
205
+ if ( $service->getType() == Lib\Entities\Service::TYPE_SIMPLE ) {
206
+ if ( $this->getParameter( 'update_staff', false ) ) {
207
+ $data = array(
208
+ 'price' => $this->getParameter( 'price' ),
209
+ 'capacity_min' => 1,
210
+ 'capacity_max' => 1,
211
+ );
212
+ $wpdb->update(
213
+ Lib\Entities\StaffService::getTableName(),
214
+ $data,
215
+ array( 'service_id' => $this->getParameter( 'id' ) )
216
+ );
 
 
 
 
 
217
  }
218
+ // Create records for newly linked staff.
219
+ $existing_staff_ids = array();
220
+ $res = Lib\Entities\StaffService::query()
221
+ ->select( 'staff_id' )
222
+ ->where( 'service_id', $service->getId() )
223
+ ->fetchArray();
224
+ foreach ( $res as $staff ) {
225
+ $existing_staff_ids[] = $staff['staff_id'];
226
+ }
227
+ foreach ( $staff_ids as $staff_id ) {
228
+ if ( ! in_array( $staff_id, $existing_staff_ids ) ) {
229
+ $staff_service = new Lib\Entities\StaffService();
230
+ $staff_service->setStaffId( $staff_id )
231
+ ->setServiceId( $service->getId() )
232
+ ->setPrice( $service->getPrice() )
233
+ ->setCapacityMin( 1 )
234
+ ->setCapacityMax( 1 )
235
+ ->save();
 
 
 
 
 
 
 
 
 
236
  }
237
  }
238
  }
239
  }
240
 
241
+ // Update services in addons.
242
+ $alert = Lib\Proxy\Shared::updateService( array( 'success' => array( __( 'Settings saved.', 'bookly' ) ) ), $service, $this->getPostParameters() );
 
 
 
 
243
 
244
+ $price = Lib\Utils\Price::format( $service->getPrice() );
245
+ $nice_duration = Lib\Utils\DateTime::secondsToInterval( $service->getDuration() );
246
+ $title = $service->getTitle();
247
+ $colors = array_fill( 0, 3, $service->getColor() );
248
+ wp_send_json_success( Lib\Proxy\Shared::prepareUpdateServiceResponse( compact( 'title', 'price', 'colors', 'nice_duration', 'alert' ), $service, $this->getPostParameters() ) );
249
  }
250
 
251
  /**
252
+ * Array for rendering service list.
253
+ *
254
  * @param int $category_id
255
+ * @return array
256
  */
257
+ private function getCaSeStSpCollections( $category_id = 0 )
258
  {
259
  if ( ! $category_id ) {
260
  $category_id = $this->getParameter( 'category_id', 0 );
261
  }
262
 
263
+ return array(
264
+ 'service_collection' => $this->getServiceCollection( $category_id ),
265
+ 'staff_collection' => $this->getStaffCollection(),
266
+ 'category_collection' => $this->getCategoryCollection(),
267
+ 'staff_preference' => array(
268
+ Lib\Entities\Service::PREFERRED_ORDER => __( 'Specified order', 'bookly' ),
269
+ Lib\Entities\Service::PREFERRED_LEAST_OCCUPIED => __( 'Least occupied that day', 'bookly' ),
270
+ Lib\Entities\Service::PREFERRED_MOST_OCCUPIED => __( 'Most occupied that day', 'bookly' ),
271
+ Lib\Entities\Service::PREFERRED_LEAST_EXPENSIVE => __( 'Least expensive', 'bookly' ),
272
+ Lib\Entities\Service::PREFERRED_MOST_EXPENSIVE => __( 'Most expensive', 'bookly' ),
273
+ ),
274
+ );
275
  }
276
 
277
  /**
297
  private function getServiceCollection( $id = 0 )
298
  {
299
  $services = Lib\Entities\Service::query( 's' )
300
+ ->select( 's.*, COUNT(staff.id) AS total_staff, GROUP_CONCAT(DISTINCT staff.id) AS staff_ids, GROUP_CONCAT(DISTINCT sp.staff_id ORDER BY sp.position ASC) AS pref_staff_ids' )
301
  ->leftJoin( 'StaffService', 'ss', 'ss.service_id = s.id' )
302
+ ->leftJoin( 'StaffPreferenceOrder', 'sp', 'sp.service_id = s.id' )
303
  ->leftJoin( 'Staff', 'staff', 'staff.id = ss.staff_id' )
304
  ->whereRaw( 's.category_id = %d OR !%d', array( $id, $id ) )
305
  ->groupBy( 's.id' )
306
  ->indexBy( 'id' )
307
  ->sortBy( 's.position' );
308
+ if ( ! Lib\Config::packagesActive() ) {
309
+ $services->whereNot( 's.type', Lib\Entities\Service::TYPE_PACKAGE );
310
+ }
311
+ $result = $services->fetchArray();
312
+ foreach ( $result as &$service ) {
313
+ $service['sub_services'] = Lib\Entities\SubService::query()
314
+ ->where( 'service_id', $service['id'] )
315
+ ->sortBy( 'position' )
316
+ ->fetchArray()
317
+ ;
318
+ $service['sub_services_count'] = array_sum( array_map( function ( $sub_service ) {
319
+ return (int) ( $sub_service['type'] == Lib\Entities\SubService::TYPE_SERVICE );
320
+ }, $service['sub_services'] ) );
321
+ $service['colors'] = Lib\Proxy\Shared::prepareServiceColors( array_fill( 0, 3, $service['color'] ), $service['id'], $service['type'] );
322
+ }
323
 
324
+ return $result;
325
  }
326
 
327
  public function executeUpdateExtraPosition()
328
  {
329
+ Lib\Proxy\ServiceExtras::reorder( $this->getParameter( 'position' ) );
 
330
  wp_send_json_success();
331
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  }
backend/modules/services/forms/Category.php CHANGED
@@ -5,6 +5,8 @@ use BooklyLite\Lib;
5
 
6
  /**
7
  * Class Category
 
 
8
  * @package BooklyLite\Backend\Modules\Services\Forms
9
  */
10
  class Category extends Lib\Base\Form
5
 
6
  /**
7
  * Class Category
8
+ * @method Lib\Entities\Category save()
9
+ *
10
  * @package BooklyLite\Backend\Modules\Services\Forms
11
  */
12
  class Category extends Lib\Base\Form
backend/modules/services/forms/Service.php CHANGED
@@ -5,6 +5,8 @@ use BooklyLite\Lib;
5
 
6
  /**
7
  * Class Service
 
 
8
  * @package BooklyLite\Backend\Modules\Services\Forms
9
  */
10
  class Service extends Lib\Base\Form
@@ -20,18 +22,25 @@ class Service extends Lib\Base\Form
20
  'price',
21
  'category_id',
22
  'color',
23
- 'capacity',
 
24
  'padding_left',
25
  'padding_right',
 
 
 
 
26
  'info',
 
 
27
  'type',
28
  'sub_services',
 
 
 
29
  'visibility',
 
30
  );
31
- if ( Lib\Config::isServiceScheduleEnabled() ) {
32
- $fields[] = 'start_time';
33
- $fields[] = 'end_time';
34
- }
35
 
36
  $this->setFields( $fields );
37
  }
@@ -44,12 +53,11 @@ class Service extends Lib\Base\Form
44
  */
45
  public function bind( array $_post, array $files = array() )
46
  {
47
- // Fields with NULL
48
- foreach ( array( 'category_id', 'start_time', 'end_time' ) as $field_name ) {
49
- if ( array_key_exists( $field_name, $_post ) && ! $_post[ $field_name ] ) {
50
- $_post[ $field_name ] = null;
51
- }
52
  }
 
53
  parent::bind( $_post, $files );
54
  }
55
 
@@ -63,13 +71,40 @@ class Service extends Lib\Base\Form
63
  $this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
64
  }
65
 
66
- if ( $this->data['type'] == Lib\Entities\Service::TYPE_SIMPLE || ! array_key_exists( 'sub_services', $this->data ) || empty( $this->data['sub_services'] ) ) {
67
- $this->data['sub_services'] = '[]';
68
- } elseif ( is_array( $this->data['sub_services'] ) ) {
69
- $this->data['sub_services'] = json_encode( (array) $this->data['sub_services'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
- return parent::save();
73
  }
74
 
75
  }
5
 
6
  /**
7
  * Class Service
8
+ * @method Lib\Entities\Service getObject
9
+ *
10
  * @package BooklyLite\Backend\Modules\Services\Forms
11
  */
12
  class Service extends Lib\Base\Form
22
  'price',
23
  'category_id',
24
  'color',
25
+ 'capacity_min',
26
+ 'capacity_max',
27
  'padding_left',
28
  'padding_right',
29
+ 'package_life_time',
30
+ 'package_size',
31
+ 'appointments_limit',
32
+ 'limit_period',
33
  'info',
34
+ 'start_time_info',
35
+ 'end_time_info',
36
  'type',
37
  'sub_services',
38
+ 'staff_preference',
39
+ 'recurrence_enabled',
40
+ 'recurrence_frequencies',
41
  'visibility',
42
+ 'positions',
43
  );
 
 
 
 
44
 
45
  $this->setFields( $fields );
46
  }
53
  */
54
  public function bind( array $_post, array $files = array() )
55
  {
56
+ // Field with NULL
57
+ if ( array_key_exists( 'category_id', $_post ) && ! $_post['category_id'] ) {
58
+ $_post['category_id'] = null;
 
 
59
  }
60
+
61
  parent::bind( $_post, $files );
62
  }
63
 
71
  $this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
72
  }
73
 
74
+ if ( $this->data['type'] == Lib\Entities\Service::TYPE_SIMPLE ) {
75
+ Lib\Entities\SubService::query()->delete()->where( 'service_id', $this->data['id'] )->execute();
76
+ }
77
+
78
+ if ( $this->data['limit_period'] == 'off' || ! $this->data['appointments_limit'] ) {
79
+ $this->data['appointments_limit'] = null;
80
+ }
81
+
82
+ $this->data = Lib\Proxy\Shared::prepareUpdateService( $this->data );
83
+
84
+ /** @var Lib\Entities\Service $service */
85
+ $service = parent::save();
86
+
87
+ // Saving staff preferences for service
88
+
89
+ /** @var Lib\Entities\StaffPreferenceOrder[] $staff_preferences */
90
+ $staff_preferences = Lib\Entities\StaffPreferenceOrder::query()
91
+ ->where( 'service_id', $service->getId() )
92
+ ->indexBy( 'staff_id' )
93
+ ->find();
94
+ foreach ( (array) $this->data['positions'] as $position => $staff_id ) {
95
+ if ( array_key_exists( $staff_id, $staff_preferences ) ) {
96
+ $staff_preferences[ $staff_id ]->setPosition( $position )->save();
97
+ } else {
98
+ $preference = new Lib\Entities\StaffPreferenceOrder();
99
+ $preference
100
+ ->setServiceId( $service->getId() )
101
+ ->setStaffId( $staff_id )
102
+ ->setPosition( $position )
103
+ ->save();
104
+ }
105
  }
106
 
107
+ return $service;
108
  }
109
 
110
  }
backend/modules/services/resources/js/service.js CHANGED
@@ -17,7 +17,7 @@ jQuery(function($) {
17
  }).on('shown.bs.popover', function () {
18
  // focus input
19
  $new_category_name.focus();
20
- }).on('hidden.bs.popover', function (e) {
21
  //clear input
22
  $new_category_name.val('');
23
  });
@@ -27,12 +27,12 @@ jQuery(function($) {
27
  var data = $(this).serialize();
28
 
29
  $.post(ajaxurl, data, function(response) {
30
- $('#bookly-category-item-list').append(response);
31
  var $new_category = $('.bookly-category-item:last');
32
  // add created category to services
33
  $('select[name="category_id"]').append('<option value="' + $new_category.data('category-id') + '">' + $new_category.find('input').val() + '</option>');
 
34
  });
35
- $new_category_popover.popover('hide');
36
  return false;
37
  });
38
 
@@ -41,21 +41,41 @@ jQuery(function($) {
41
  $new_category_popover.popover('hide');
42
  });
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  // Categories list delegated events.
45
  $('#bookly-categories-list')
46
 
47
  // On category item click.
48
  .on('click', '.bookly-category-item', function(e) {
49
  if ($(e.target).is('.bookly-js-handle')) return;
50
- $('#ab-services-list').html('<div class="bookly-loading"></div>');
51
  var $clicked = $(this);
52
 
53
- $.get(ajaxurl, {action:'bookly_get_category_services', category_id: $clicked.data('category-id')}, function(response) {
54
  if ( response.success ) {
55
  $('.bookly-category-item').not($clicked).removeClass('active');
56
  $clicked.addClass('active');
57
  $('.bookly-category-title').text($clicked.text());
58
- refreshList(response.data, 0);
59
  }
60
  });
61
  })
@@ -72,30 +92,13 @@ jQuery(function($) {
72
  })
73
 
74
  // On blur save changes.
75
- .on('blur', 'input', function() {
76
- var $this = $(this),
77
- $item = $this.closest('.bookly-category-item'),
78
- field = $this.attr('name'),
79
- value = $this.val(),
80
- id = $item.data('category-id'),
81
- data = { action: 'bookly_update_category', id: id };
82
- data[field] = value;
83
- $.post(ajaxurl, data, function(response) {
84
- // Hide input field.
85
- $item.find('input').hide();
86
- $item.find('.displayed-value').show();
87
- // Show modified category name.
88
- $item.find('.displayed-value').text(value);
89
- // update edited category's name for services
90
- $('select[name="category_id"] option[value="' + id + '"]').text(value);
91
- });
92
- })
93
 
94
  // On press Enter save changes.
95
  .on('keypress', 'input', function (e) {
96
  var code = e.keyCode || e.which;
97
  if (code == 13) {
98
- $(this).blur();
99
  }
100
  })
101
 
@@ -108,7 +111,7 @@ jQuery(function($) {
108
  // Ask user if he is sure.
109
  if (confirm(BooklyL10n.are_you_sure)) {
110
  var $item = $(this).closest('.bookly-category-item');
111
- var data = { action: 'bookly_delete_category', id: $item.data('category-id') };
112
  $.post(ajaxurl, data, function(response) {
113
  // Remove category item from Services
114
  $('select[name="category_id"] option[value="' + $item.data('category-id') + '"]').remove();
@@ -132,7 +135,7 @@ jQuery(function($) {
132
  e.preventDefault();
133
  var ladda = rangeTools.ladda(this);
134
  var selected_category_id = $('#bookly-categories-list .active').data('category-id'),
135
- data = { action: 'bookly_add_service' };
136
  if (selected_category_id) {
137
  data['category_id'] = selected_category_id;
138
  }
@@ -147,7 +150,7 @@ jQuery(function($) {
147
  var ladda = rangeTools.ladda(this);
148
 
149
  var $for_delete = $('.service-checker:checked'),
150
- data = { action: 'bookly_remove_services' },
151
  services = [],
152
  $panels = [];
153
 
@@ -155,59 +158,89 @@ jQuery(function($) {
155
  var panel = $(this).parents('.bookly-js-collapse');
156
  $panels.push(panel);
157
  services.push(this.value);
 
 
 
 
 
 
 
158
  });
159
  data['service_ids[]'] = services;
160
- $.post(ajaxurl, data, function() {
161
- ladda.stop();
162
- $.each($panels.reverse(), function (index) {
163
- $(this).delay(500 * index).fadeOut(200, function () {
164
- $(this).remove();
 
 
165
  });
166
- });
 
167
  });
168
  }
169
  })
170
 
171
  .on('change', 'input.bookly-check-all-entities, input.bookly-js-check-entity', function () {
172
- var $panel = $(this).parents('.bookly-js-collapse');
173
  if ($(this).hasClass('bookly-check-all-entities')) {
174
- $panel.find('.bookly-js-check-entity').prop('checked', $(this).prop('checked'));
175
  } else {
176
- $panel.find('.bookly-check-all-entities').prop('checked', $panel.find('.bookly-js-check-entity:not(:checked)').length == 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  }
178
- updateStaffButton($panel);
179
  });
180
 
181
  // Modal window events.
182
- var $modal = $('#ab-staff-update');
183
  $modal
184
- .on('click', '.ab-yes', function() {
185
  $modal.modal('hide');
186
- if ( $('#ab-remember-my-choice').prop('checked') ) {
187
  update_staff_choice = true;
188
  }
189
  submitServiceFrom($modal.data('input'),true);
190
  })
191
- .on('click', '.ab-no', function() {
192
- if ( $('#ab-remember-my-choice').prop('checked') ) {
193
  update_staff_choice = false;
194
  }
195
  submitServiceFrom($modal.data('input'),false);
196
  });
197
 
198
  function refreshList(response,service_id) {
199
- var $list = $('#ab-services-list');
200
  $list.html(response);
201
  if (response.indexOf('panel') >= 0) {
202
  $no_result.hide();
203
- makeServicesSortable();
204
- onCollapseInitChildren();
205
  $list.booklyHelp();
206
  } else {
207
  $no_result.show();
208
  }
209
- $('#service_' + service_id).collapse('show');
210
- $('#service_' + service_id).find('input[name=title]').focus();
 
 
 
211
  }
212
 
213
  function initColorPicker($jquery_collection) {
@@ -219,7 +252,7 @@ jQuery(function($) {
219
  });
220
  }
221
 
222
- $('#ab-services-list').on('change', '[name=capacity]', function(){
223
  if ($(this).val() > 1) {
224
  booklyAlert({error: [BooklyL10n.limitations]});
225
  $(this).val('1').prop('readonly',true);
@@ -234,34 +267,41 @@ jQuery(function($) {
234
 
235
  function submitServiceFrom($form, update_staff) {
236
  $form.find('input[name=update_staff]').val(update_staff ? 1 : 0);
 
237
  var ladda = rangeTools.ladda($form.find('button.ajax-service-send[type=submit]').get(0)),
238
  data = $form.serializeArray();
239
- if ($form.find('input[name=type]:checked').val() == 'compound') {
240
- $form.find('li[data-sub-service-id]').each(function () {
241
- data.push({name: 'sub_services[]', value: $(this).data('sub-service-id')});
242
- });
243
- } else {
244
- data.push({name: 'type', value: 'simple'});
245
- data.push({name: 'sub_services[]', value: false});
246
- }
247
  $.post(ajaxurl, data, function (response) {
248
  if (response.success) {
249
  var $panel = $form.parents('.bookly-js-collapse'),
250
  $price = $form.find('[name=price]'),
251
- $capacity = $form.find('[name=capacity]');
252
- $panel.find('.bookly-js-service-color').css('background-color', response.data.color);
 
 
 
 
253
  $panel.find('.bookly-js-service-title').html(response.data.title);
254
  $panel.find('.bookly-js-service-duration').html(response.data.nice_duration);
255
  $panel.find('.bookly-js-service-price').html(response.data.price);
256
  $price.data('last_value', $price.val());
257
- $capacity.data('last_value', $capacity.val());
258
- booklyAlert({success : [BooklyL10n.saved]});
259
- $.each(response.data.new_extras, function (front_id, real_id) {
 
 
 
 
 
260
  var $li = $('li.extra.new[data-extra-id="' + front_id + '"]', $form);
261
  $('[name^="extras"]', $li).each(function () {
262
- var name = $(this).attr('name');
263
- name = name.replace('[' + front_id + ']', '[' + real_id + ']');
264
- $(this).attr('name', name);
 
 
 
 
265
  });
266
  $li.data('extra-id', real_id).removeClass('new');
267
  $li.append('<input type="hidden" value="' + real_id + '" name="extras[' + real_id + '][id]">');
@@ -274,14 +314,30 @@ jQuery(function($) {
274
  });
275
  }
276
 
277
- function updateStaffButton($panel) {
278
- var staff_checked = $panel.find('.bookly-js-check-entity:checked').length;
279
- if (staff_checked == 0) {
280
- $panel.find('.bookly-entity-counter').text(BooklyL10n.selector.nothing_selected);
281
- } else if (staff_checked == 1) {
282
- $panel.find('.bookly-entity-counter').text($panel.find('.bookly-js-check-entity:checked').data('staff_name'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  } else {
284
- $panel.find('.bookly-entity-counter').text(staff_checked + '/' + $panel.find('.bookly-js-check-entity').length);
 
 
285
  }
286
  }
287
 
@@ -299,12 +355,12 @@ jQuery(function($) {
299
  $.ajax({
300
  type : 'POST',
301
  url : ajaxurl,
302
- data : { action: 'bookly_update_category_position', position: data }
303
  });
304
  }
305
  });
306
 
307
- function makeServicesSortable() {
308
  if ($('.bookly-js-all-services').hasClass('active')) {
309
  var $services = $('#services_list'),
310
  fixHelper = function(e, ui) {
@@ -325,7 +381,7 @@ jQuery(function($) {
325
  $.ajax({
326
  type : 'POST',
327
  url : ajaxurl,
328
- data : { action: 'bookly_update_services_position', position: data }
329
  });
330
  }
331
  });
@@ -334,21 +390,33 @@ jQuery(function($) {
334
  }
335
  }
336
 
337
- function onCollapseInitChildren() {
338
  $('.panel-collapse').on('show.bs.collapse.bookly', function () {
339
- var $panel = $(this);
340
- var $sub_services = $('.ab--service-list', $panel);
341
- initColorPicker($panel.find('.bookly-js-color-picker'));
342
- $('input[name=type]', $panel).on( 'click', function(){
343
- if ($(this).val() == 'simple') {
344
- $('.ab--for-simple', $panel).show();
345
- $('.ab--for-compound', $panel).hide();
346
- } else {
347
- $('.ab--for-simple', $panel).hide();
348
- $('.ab--for-compound', $panel).show();
 
 
 
 
 
 
 
 
 
 
349
  }
350
- });
351
- $('input[name=type]:checked', $panel).trigger('click');
 
 
352
 
353
  $('[data-toggle="popover"]').popover({
354
  html: true,
@@ -357,155 +425,89 @@ jQuery(function($) {
357
  template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
358
  });
359
 
360
- var initSubServicesLi = function ($li) {
361
- $("option[value=" + $li.data('sub-service-id') + "]", $sub_services).prop('disabled', true);
362
- $('.ab--sub-service-remove', $li).click(function () {
363
- $('li.list-group-item[data-sub-service-id="' + $li.data('sub-service-id') + '"]', $panel).remove();
364
- $("option[value=" + $li.data('sub-service-id') + "]", $sub_services).prop('disabled', false);
365
- });
366
- };
367
-
368
- $('.ab--sub-services li.list-group-item[data-sub-service-id]', $panel).each(function () {
369
- initSubServicesLi($(this));
370
  });
371
 
372
- $sub_services.on('change', function () {
373
- if ($(this).val()) {
374
- var $li = $('.ab--templates.services .template_' + $sub_services.val() + ' li').clone();
375
- $li.insertBefore($(this).parents('li'));
376
- initSubServicesLi($li);
377
- $(this).val(0);
378
- }
379
- });
380
 
381
- $('.ab--sub-services', $panel).sortable({axis: 'y', items: "[data-sub-service-id]"});
 
 
 
382
 
383
- updateStaffButton($(this).parents('.bookly-js-collapse'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
 
385
- $panel.find('.ajax-service-send').on('click', function (e) {
386
- e.preventDefault();
387
- var $form = $(this).parents('form'),
388
- show_modal = false;
389
- if(update_staff_choice === null) {
390
- $('.ab-question', $form).each(function () {
391
- if ($(this).data('last_value') != $(this).val()) {
392
- show_modal = true;
393
- }
 
 
 
394
  });
395
- }
396
- if (show_modal) {
397
- $modal.data('input', $form).modal('show');
398
- } else {
399
- submitServiceFrom($form, update_staff_choice);
400
- }
401
- });
402
-
403
- $panel.find('.js-reset').on('click', function () {
404
- $(this).parents('form').trigger('reset');
405
- var $color = $(this).parents('form').find('.wp-color-picker'),
406
- $panel = $(this).parents('.bookly-js-collapse');
407
- $color.val($color.data('last-color')).trigger('change');
408
- $panel.find('.parent-range-start').trigger('change');
409
- updateStaffButton($panel);
410
- });
411
- $panel.find('.ab-question').each(function () {
412
- $(this).data('last_value', $(this).val());
413
- });
414
- $panel.unbind('show.bs.collapse.bookly');
415
- $(document.body).trigger( 'service_list.service_expand', [ $panel, $panel.closest('.panel').data('service-id') ] );
416
- });
417
- }
418
- makeServicesSortable();
419
- onCollapseInitChildren();
420
-
421
- /*<Extras>*/
422
- $('.extras-container').sortable({
423
- axis : 'y',
424
- handle : '.bookly-js-handle',
425
- update : function( event, ui ) {
426
- var data = [];
427
- $(this).find('.extra').each(function() {
428
- data.push($(this).data('extra-id'));
429
- });
430
- $.ajax({
431
- type : 'POST',
432
- url : ajaxurl,
433
- data : { action: 'bookly_update_extra_position', position: data }
434
- });
435
- }
436
- });
437
 
438
- $(document).on('click', '.bookly-js-collapse .extra-new', function (e) {
439
- e.preventDefault();
440
- e.stopPropagation();
441
- var children = $('.extras-container li');
442
 
443
- var id = 1;
444
- children.each(function (i, el) {
445
- var elId = parseInt($(el).data('extra-id'));
446
- id = (elId >= id) ? elId + 1 : id;
447
- });
448
- var template = $('.ab--templates.extras').html();
449
- var $container = $(this).parents('.bookly-js-collapse').find('.extras-container');
450
- id++;
451
- $container.append(
452
- template.replace(/%id%/g, id)
453
- );
454
- $('#title_' + id).focus();
455
- });
456
 
457
- $(document).on('click', '.bookly-js-collapse .extra-attachment', function (e) {
458
- e.preventDefault();
459
- e.stopPropagation();
460
- var extra = $(this).parents('.extra');
461
- var frame = wp.media({
462
- library: {type: 'image'},
463
- multiple: false
464
- });
465
- frame.on('select', function () {
466
- var selection = frame.state().get('selection').toJSON(),
467
- img_src
468
- ;
469
- if (selection.length) {
470
- if (selection[0].sizes['thumbnail'] !== undefined) {
471
- img_src = selection[0].sizes['thumbnail'].url;
472
- } else {
473
- img_src = selection[0].url;
474
  }
475
- extra.find("[name='extras[" + extra.data('extra-id') + "][attachment_id]']").val(selection[0].id);
476
- extra.find('.extra-attachment-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
477
- extra.find('.bookly-js-remove-attachment').show();
478
- $(this).hide();
 
 
 
 
 
479
  }
 
480
  });
 
481
 
482
- frame.open();
483
- });
484
-
485
- $(document).on('click', '.bookly-js-collapse .bookly-js-remove-attachment', function (e) {
486
- e.preventDefault();
487
- e.stopPropagation();
488
- $(this).hide();
489
- var extra = $(this).parents('.extra');
490
- extra.find("[name='extras[" + extra.data('extra-id') + "][attachment_id]']").attr('value', '');
491
- extra.find('.extra-attachment-image').attr('style', '');
492
- extra.find('.extra-attachment').show();
493
- }).on('change', '.popover-range-start, .popover-range-end', function () {
494
- var $popover_content = $(this).closest('.popover-content');
495
- rangeTools.hideInaccessibleBreaks($popover_content.find('.popover-range-start'), $popover_content.find('.popover-range-end'));
496
- });
497
-
498
- $(document).on('click', '.bookly-js-collapse .extra-delete', function (e) {
499
- e.preventDefault();
500
- e.stopPropagation();
501
- if (confirm(BooklyL10n.are_you_sure)) {
502
- var extra = $(this).parents('.extra');
503
- if (!extra.hasClass('new')) {
504
- $.post(ajaxurl, {action: 'bookly_service_extras_delete_service_extra', id: extra.data('extra-id')}, function () {
505
- });
506
- }
507
- extra.remove();
508
- }
509
- });
510
- /*</Extras>*/
511
  });
17
  }).on('shown.bs.popover', function () {
18
  // focus input
19
  $new_category_name.focus();
20
+ }).on('hidden.bs.popover', function () {
21
  //clear input
22
  $new_category_name.val('');
23
  });
27
  var data = $(this).serialize();
28
 
29
  $.post(ajaxurl, data, function(response) {
30
+ $('#bookly-category-item-list').append(response.data.html);
31
  var $new_category = $('.bookly-category-item:last');
32
  // add created category to services
33
  $('select[name="category_id"]').append('<option value="' + $new_category.data('category-id') + '">' + $new_category.find('input').val() + '</option>');
34
+ $new_category_popover.popover('hide');
35
  });
 
36
  return false;
37
  });
38
 
41
  $new_category_popover.popover('hide');
42
  });
43
 
44
+ // Save category.
45
+ function saveCategory() {
46
+ var $this = $(this),
47
+ $item = $this.closest('.bookly-category-item'),
48
+ field = $this.attr('name'),
49
+ value = $this.val(),
50
+ id = $item.data('category-id'),
51
+ data = { action: 'bookly_update_category', id: id, csrf_token : BooklyL10n.csrf_token };
52
+ data[field] = value;
53
+ $.post(ajaxurl, data, function(response) {
54
+ // Hide input field.
55
+ $item.find('input').hide();
56
+ $item.find('.displayed-value').show();
57
+ // Show modified category name.
58
+ $item.find('.displayed-value').text(value);
59
+ // update edited category's name for services
60
+ $('select[name="category_id"] option[value="' + id + '"]').text(value);
61
+ });
62
+ }
63
+
64
  // Categories list delegated events.
65
  $('#bookly-categories-list')
66
 
67
  // On category item click.
68
  .on('click', '.bookly-category-item', function(e) {
69
  if ($(e.target).is('.bookly-js-handle')) return;
70
+ $('#bookly-js-services-list').html('<div class="bookly-loading"></div>');
71
  var $clicked = $(this);
72
 
73
+ $.get(ajaxurl, {action:'bookly_get_category_services', category_id: $clicked.data('category-id'), csrf_token : BooklyL10n.csrf_token}, function(response) {
74
  if ( response.success ) {
75
  $('.bookly-category-item').not($clicked).removeClass('active');
76
  $clicked.addClass('active');
77
  $('.bookly-category-title').text($clicked.text());
78
+ refreshList(response.data);
79
  }
80
  });
81
  })
92
  })
93
 
94
  // On blur save changes.
95
+ .on('blur', 'input', saveCategory)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  // On press Enter save changes.
98
  .on('keypress', 'input', function (e) {
99
  var code = e.keyCode || e.which;
100
  if (code == 13) {
101
+ saveCategory.apply(this);
102
  }
103
  })
104
 
111
  // Ask user if he is sure.
112
  if (confirm(BooklyL10n.are_you_sure)) {
113
  var $item = $(this).closest('.bookly-category-item');
114
+ var data = { action: 'bookly_delete_category', id: $item.data('category-id'), csrf_token : BooklyL10n.csrf_token };
115
  $.post(ajaxurl, data, function(response) {
116
  // Remove category item from Services
117
  $('select[name="category_id"] option[value="' + $item.data('category-id') + '"]').remove();
135
  e.preventDefault();
136
  var ladda = rangeTools.ladda(this);
137
  var selected_category_id = $('#bookly-categories-list .active').data('category-id'),
138
+ data = { action: 'bookly_add_service', csrf_token : BooklyL10n.csrf_token };
139
  if (selected_category_id) {
140
  data['category_id'] = selected_category_id;
141
  }
150
  var ladda = rangeTools.ladda(this);
151
 
152
  var $for_delete = $('.service-checker:checked'),
153
+ data = { action: 'bookly_remove_services', csrf_token : BooklyL10n.csrf_token },
154
  services = [],
155
  $panels = [];
156
 
158
  var panel = $(this).parents('.bookly-js-collapse');
159
  $panels.push(panel);
160
  services.push(this.value);
161
+ if (panel.find('.bookly-js-service-type input[name="type"]:checked').val() == 'simple') {
162
+ $('#services_list .panel.bookly-js-collapse').each(function () {
163
+ if ($(this).find('.bookly-js-service-type input[name="type"]:checked').val() == 'package' && $(this).find('.bookly-js-package-sub-service option:selected').val() == panel.data('service-id')) {
164
+ $panels.push($(this));
165
+ }
166
+ });
167
+ }
168
  });
169
  data['service_ids[]'] = services;
170
+ $.post(ajaxurl, data, function(response) {
171
+ if (response.success) {
172
+ ladda.stop();
173
+ $.each($panels.reverse(), function (index) {
174
+ $(this).delay(500 * index).fadeOut(200, function () {
175
+ $(this).remove();
176
+ });
177
  });
178
+ $(document.body).trigger( 'service.deleted', [ services ] );
179
+ }
180
  });
181
  }
182
  })
183
 
184
  .on('change', 'input.bookly-check-all-entities, input.bookly-js-check-entity', function () {
185
+ var $container = $(this).parents('.form-group');
186
  if ($(this).hasClass('bookly-check-all-entities')) {
187
+ $container.find('.bookly-js-check-entity').prop('checked', $(this).prop('checked'));
188
  } else {
189
+ $container.find('.bookly-check-all-entities').prop('checked', $container.find('.bookly-js-check-entity:not(:checked)').length == 0);
190
+ }
191
+ var $form = $(this).closest('.panel.bookly-js-collapse'),
192
+ service_id = $form.data('service-id'),
193
+ service_type = $form.find('.bookly-js-service-type input[name="type"]:checked').val(),
194
+ staff_index = $(this).closest('li').index() + 1;
195
+ if (service_type == 'simple' && !$(this).is(':checked')) {
196
+ $('#services_list .panel.bookly-js-collapse').each(function () {
197
+ if ($(this).find('.bookly-js-service-type input[name="type"]:checked').val() == 'package' && $(this).find('.bookly-js-package-sub-service option:selected').val() == service_id) {
198
+ $(this).find('.bookly-entity-selector li:nth-child(' + staff_index + ') input').prop('checked', false).trigger('change');
199
+ }
200
+ });
201
+ } else if (service_type == 'package' && $(this).is(':checked')) {
202
+ var sub_service_id = $form.find('.bookly-js-package-sub-service option:selected').val();
203
+ $('#services_list .panel.bookly-js-collapse').each(function () {
204
+ if ($(this).find('.bookly-js-service-type input[name="type"]:checked').val() == 'simple' && $(this).data('service-id') == sub_service_id) {
205
+ $(this).find('.bookly-entity-selector li:nth-child(' + staff_index + ') input').prop('checked', true).trigger('change');
206
+ }
207
+ });
208
  }
209
+ updateSelectorButton($container);
210
  });
211
 
212
  // Modal window events.
213
+ var $modal = $('#bookly-update-service-settings');
214
  $modal
215
+ .on('click', '.bookly-yes', function() {
216
  $modal.modal('hide');
217
+ if ( $('#bookly-remember-my-choice').prop('checked') ) {
218
  update_staff_choice = true;
219
  }
220
  submitServiceFrom($modal.data('input'),true);
221
  })
222
+ .on('click', '.bookly-no', function() {
223
+ if ( $('#bookly-remember-my-choice').prop('checked') ) {
224
  update_staff_choice = false;
225
  }
226
  submitServiceFrom($modal.data('input'),false);
227
  });
228
 
229
  function refreshList(response,service_id) {
230
+ var $list = $('#bookly-js-services-list');
231
  $list.html(response);
232
  if (response.indexOf('panel') >= 0) {
233
  $no_result.hide();
234
+ onCollapseInitServiceForm();
 
235
  $list.booklyHelp();
236
  } else {
237
  $no_result.show();
238
  }
239
+ if (service_id) {
240
+ $('#service_' + service_id).collapse('show');
241
+ $('#service_' + service_id).find('input[name=title]').focus();
242
+ }
243
+ makeSortable();
244
  }
245
 
246
  function initColorPicker($jquery_collection) {
252
  });
253
  }
254
 
255
+ $('#services_list').on('change', '[name=capacity_min],[name=capacity_max]', function(){
256
  if ($(this).val() > 1) {
257
  booklyAlert({error: [BooklyL10n.limitations]});
258
  $(this).val('1').prop('readonly',true);
267
 
268
  function submitServiceFrom($form, update_staff) {
269
  $form.find('input[name=update_staff]').val(update_staff ? 1 : 0);
270
+ $form.find('input[name=package_service_changed]').val($form.find('[name=package_service]').data('last_value') != $form.find('[name=package_service]').val() ? 1 : 0);
271
  var ladda = rangeTools.ladda($form.find('button.ajax-service-send[type=submit]').get(0)),
272
  data = $form.serializeArray();
273
+ $(document.body).trigger( 'service.submitForm', [ $form, data ] );
 
 
 
 
 
 
 
274
  $.post(ajaxurl, data, function (response) {
275
  if (response.success) {
276
  var $panel = $form.parents('.bookly-js-collapse'),
277
  $price = $form.find('[name=price]'),
278
+ $capacity_min = $form.find('[name=capacity_min]'),
279
+ $capacity_max = $form.find('[name=capacity_max]'),
280
+ $package_service = $form.find('[name=package_service]');
281
+ $panel.find('.bookly-js-service-color span:nth-child(1)').css('background-color', response.data.colors[0] == '-1' ? 'grey' : response.data.colors[0]);
282
+ $panel.find('.bookly-js-service-color span:nth-child(2)').css('background-color', response.data.colors[1] == '-1' ? 'grey' : response.data.colors[1]);
283
+ $panel.find('.bookly-js-service-color span:nth-child(3)').css('background-color', response.data.colors[2] == '-1' ? 'grey' : response.data.colors[2]);
284
  $panel.find('.bookly-js-service-title').html(response.data.title);
285
  $panel.find('.bookly-js-service-duration').html(response.data.nice_duration);
286
  $panel.find('.bookly-js-service-price').html(response.data.price);
287
  $price.data('last_value', $price.val());
288
+ $capacity_min.data('last_value', $capacity_min.val());
289
+ $capacity_max.data('last_value', $capacity_max.val());
290
+ $package_service.data('last_value', $package_service.val());
291
+ booklyAlert(response.data.alert);
292
+ if (response.data.new_extras_list) {
293
+ ExtrasL10n.list = response.data.new_extras_list
294
+ }
295
+ $.each(response.data.new_extras_ids, function (front_id, real_id) {
296
  var $li = $('li.extra.new[data-extra-id="' + front_id + '"]', $form);
297
  $('[name^="extras"]', $li).each(function () {
298
+ $(this).attr('name', $(this).attr('name').replace('[' + front_id + ']', '[' + real_id + ']'));
299
+ });
300
+ $('[id*="_extras_"]', $li).each(function () {
301
+ $(this).attr('id', $(this).attr('id').replace(front_id, real_id));
302
+ });
303
+ $('label[for*="_extras_"]', $li).each(function () {
304
+ $(this).attr('for', $(this).attr('for').replace(front_id, real_id));
305
  });
306
  $li.data('extra-id', real_id).removeClass('new');
307
  $li.append('<input type="hidden" value="' + real_id + '" name="extras[' + real_id + '][id]">');
314
  });
315
  }
316
 
317
+ function updateSelectorButton($container) {
318
+ var entity_checked = $container.find('.bookly-js-check-entity:checked').length,
319
+ $check_all = $container.find('.bookly-check-all-entities');
320
+ if (entity_checked == 0) {
321
+ $container.find('.bookly-entity-counter').text($check_all.data('nothing'));
322
+ } else if (entity_checked == 1) {
323
+ $container.find('.bookly-entity-counter').text($container.find('.bookly-js-check-entity:checked').data('title'));
324
+ } else if (entity_checked == $container.find('.bookly-js-check-entity').length) {
325
+ $container.find('.bookly-entity-counter').text($check_all.data('title'));
326
+ $check_all.prop('checked', true);
327
+ } else {
328
+ $container.find('.bookly-entity-counter').text(entity_checked + '/' + $container.find('.bookly-js-check-entity').length);
329
+ }
330
+ }
331
+
332
+ function checkCapacityError($panel) {
333
+ if (parseInt($panel.find('[name="capacity_min"]').val()) > parseInt($panel.find('[name="capacity_max"]').val())) {
334
+ $panel.find('form .bookly-js-services-error').html(BooklyL10n.capacity_error);
335
+ $panel.find('[name="capacity_min"]').closest('.form-group').addClass('has-error');
336
+ $panel.find('form .ajax-service-send').prop('disabled', true);
337
  } else {
338
+ $panel.find('form .bookly-js-services-error').html('');
339
+ $panel.find('[name="capacity_min"]').closest('.form-group').removeClass('has-error');
340
+ $panel.find('form .ajax-service-send').prop('disabled', false);
341
  }
342
  }
343
 
355
  $.ajax({
356
  type : 'POST',
357
  url : ajaxurl,
358
+ data : { action: 'bookly_update_category_position', position: data, csrf_token : BooklyL10n.csrf_token }
359
  });
360
  }
361
  });
362
 
363
+ function makeSortable() {
364
  if ($('.bookly-js-all-services').hasClass('active')) {
365
  var $services = $('#services_list'),
366
  fixHelper = function(e, ui) {
381
  $.ajax({
382
  type : 'POST',
383
  url : ajaxurl,
384
+ data : { action: 'bookly_update_services_position', position: data, csrf_token : BooklyL10n.csrf_token }
385
  });
386
  }
387
  });
390
  }
391
  }
392
 
393
+ function onCollapseInitServiceForm() {
394
  $('.panel-collapse').on('show.bs.collapse.bookly', function () {
395
+ var $panel = $(this),
396
+ $staff_preference = $panel.find('[name=staff_preference]'),
397
+ $staff_list = $panel.find('.bookly-staff-list'),
398
+ $staff_box = $panel.find('.bookly-preference-box');
399
+
400
+ $staff_preference.on('change', function () {
401
+ /** @see Service::PREFERRED_ORDER */
402
+ if ($(this).val() == 'order' && $staff_list.html() == '') {
403
+ var $staff_ids = $staff_preference.data('default'),
404
+ $draggable = $('<div class="bookly-flex-cell"><i class="bookly-js-handle bookly-margin-right-sm bookly-icon bookly-icon-draghandle bookly-cursor-move" title="' + BooklyL10n.reorder + '"></i><input type="hidden" name="positions[]"></div>');
405
+
406
+ $staff_ids.forEach(function (staff_id) {
407
+ $staff_list.append($draggable.clone().find('input').val(staff_id).end().append(BooklyL10n.staff[staff_id]));
408
+ });
409
+ Object.keys(BooklyL10n.staff).forEach(function (staff_id) {
410
+ staff_id = parseInt(staff_id);
411
+ if ($staff_ids.indexOf(staff_id) == -1) {
412
+ $staff_list.append($draggable.clone().find('input').val(staff_id).end().append(BooklyL10n.staff[staff_id]));
413
+ }
414
+ });
415
  }
416
+ $staff_box.toggle($(this).val() == 'order');
417
+ }).trigger('change');
418
+
419
+ initColorPicker($panel.find('.bookly-js-color-picker'));
420
 
421
  $('[data-toggle="popover"]').popover({
422
  html: true,
425
  template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
426
  });
427
 
428
+ $.each($('.bookly-js-entity-selector-container',$(this)), function () {
429
+ updateSelectorButton($(this));
 
 
 
 
 
 
 
 
430
  });
431
 
432
+ $panel
433
+ .find('[name=duration]').on('change', function () {
434
+ $panel.find('[name=start_time_info]').closest('.form-group').toggle($(this).val() >= 86400);
435
+ }).trigger('change');
 
 
 
 
436
 
437
+ $panel
438
+ .find('.bookly-js-capacity').on('keyup change', function () {
439
+ checkCapacityError($(this).parents('.bookly-js-collapse'));
440
+ });
441
 
442
+ $panel
443
+ .find('.ajax-service-send').on('click', function (e) {
444
+ e.preventDefault();
445
+ var $form = $(this).parents('form'),
446
+ show_modal = false;
447
+ if(update_staff_choice === null) {
448
+ $('.bookly-question', $form).each(function () {
449
+ if ($(this).data('last_value') != this.value) {
450
+ show_modal = true;
451
+ }
452
+ });
453
+ }
454
+ if (show_modal) {
455
+ $modal.data('input', $form).modal('show');
456
+ } else {
457
+ submitServiceFrom($form, update_staff_choice);
458
+ }
459
+ });
460
 
461
+ $panel
462
+ .find('.js-reset').on('click', function () {
463
+ $(this).parents('form').trigger('reset');
464
+ var $color = $(this).parents('form').find('.wp-color-picker'),
465
+ $panel = $(this).parents('.bookly-js-collapse');
466
+ $staff_list.html('');
467
+ $staff_preference.trigger('change');
468
+ $color.val($color.data('last-color')).trigger('change');
469
+ $panel.find('.parent-range-start').trigger('change');
470
+ $panel.find('input[name=type]:checked').trigger('change');
471
+ $.each($('.bookly-js-entity-selector-container',$panel), function () {
472
+ updateSelectorButton($(this));
473
  });
474
+ checkCapacityError($panel);
475
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
 
477
+ $panel
478
+ .find('.bookly-question').each(function () {
479
+ $(this).data('last_value', this.value);
480
+ });
481
 
482
+ $staff_list.sortable({
483
+ axis : 'y',
484
+ handle : '.bookly-js-handle',
485
+ update : function() {
486
+ var positions = [];
487
+ $('[name="positions[]"]', $(this)).each(function () {
488
+ positions.push(this.value);
489
+ });
 
 
 
 
 
490
 
491
+ $.ajax({
492
+ type : 'POST',
493
+ url : ajaxurl,
494
+ data : {action: 'bookly_update_service_staff_preference_orders', service_id: $(this).data('service_id'), positions: positions, csrf_token: BooklyL10n.csrf_token}
495
+ });
 
 
 
 
 
 
 
 
 
 
 
 
496
  }
497
+ });
498
+ $panel.unbind('show.bs.collapse.bookly');
499
+ if ($('input[name=type]', $panel).length > 1) {
500
+ $('.bookly-js-service-type', $panel).show();
501
+ $('input[name=type]', $panel).on( 'change', function(){
502
+ $panel.closest('.panel').find('.bookly-js-service').hide();
503
+ $panel.closest('.panel').find('.bookly-js-service-' + this.value).show();
504
+ });
505
+ $('input[name=type]:checked', $panel).trigger('change');
506
  }
507
+ $(document.body).trigger( 'service.initForm', [ $panel, $panel.closest('.panel').data('service-id') ] );
508
  });
509
+ }
510
 
511
+ makeSortable();
512
+ onCollapseInitServiceForm();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  });
backend/modules/services/templates/_list.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $time_interval = get_option( 'bookly_gen_time_slot_length' );
 
 
 
 
 
 
3
  ?>
4
  <?php if ( ! empty( $service_collection ) ) : ?>
5
  <div class="panel-group" id="services_list" role="tablist" aria-multiselectable="true">
@@ -17,9 +23,13 @@
17
  <i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"
18
  title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
19
  </div>
20
- <div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
21
- <span class="bookly-service-color bookly-margin-right-sm bookly-js-service-color"
22
- style="background-color: <?php echo esc_attr( $service['color'] ) ?>">&nbsp;</span>
 
 
 
 
23
  </div>
24
  <div class="bookly-flex-cell bookly-vertical-middle">
25
  <a role="button" class="panel-title collapsed bookly-js-service-title" data-toggle="collapse"
@@ -34,12 +44,20 @@
34
  <div class="bookly-flexbox">
35
  <div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 60%">
36
  <span class="bookly-js-service-duration">
37
- <?php echo( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ? \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['duration'] ) : sprintf( _n( '%d service', '%d services', count( json_decode( $service['sub_services'], true ) ), 'bookly' ), count( json_decode( $service['sub_services'], true ) ) ) ) ?>
 
 
 
 
 
 
 
 
38
  </span>
39
  </div>
40
  <div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 30%">
41
  <span class="bookly-js-service-price">
42
- <?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $service['price'] ) ?>
43
  </span>
44
  </div>
45
  <div class="bookly-flex-cell bookly-vertical-middle text-right" style="width: 10%">
@@ -55,25 +73,35 @@
55
  <div id="service_<?php echo $service_id ?>" class="panel-collapse collapse" role="tabpanel" style="height: 0">
56
  <div class="panel-body">
57
  <form method="post">
58
- <?php do_action( 'bookly_render_service_form_contents', $service ) ?>
 
 
 
 
 
 
 
 
 
59
  <div class="row">
60
- <div class="col-md-9 col-sm-6">
61
  <div class="form-group">
62
  <label for="title_<?php echo $service_id ?>"><?php _e( 'Title', 'bookly' ) ?></label>
63
  <input name="title" value="<?php echo esc_attr( $service['title'] ) ?>" id="title_<?php echo $service_id ?>" class="form-control" type="text">
64
  </div>
65
  </div>
66
- <div class="col-md-3 col-sm-6">
67
  <div class="form-group">
68
  <label><?php _e( 'Color', 'bookly' ) ?></label>
69
  <div class="bookly-color-picker-wrapper">
70
- <input name="color" value="<?php echo esc_attr( $service['color'] ) ?>" class="bookly-js-color-picker" data-last-color="<?php echo esc_attr( $service['color'] ) ?>" type="hidden">
71
  </div>
72
  </div>
73
  </div>
74
  </div>
 
75
  <div class="row">
76
- <div class="col-sm-4">
77
  <div class="form-group">
78
  <label for="visibility_<?php echo $service_id ?>"><?php _e( 'Visibility', 'bookly' ) ?></label>
79
  <p class="help-block"><?php _e( 'To make service invisible to your customers set the visibility to "Private".', 'bookly' ) ?></p>
@@ -83,35 +111,40 @@
83
  </select>
84
  </div>
85
  </div>
86
- <div class="col-sm-4">
87
  <div class="form-group">
88
  <label for="price_<?php echo $service_id ?>"><?php _e( 'Price', 'bookly' ) ?></label>
89
- <input id="price_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="0" step="1" name="price" value="<?php echo esc_attr( $service['price'] ) ?>">
90
  </div>
91
  </div>
92
- <div class="col-sm-4 ab--for-simple">
93
  <div class="form-group">
94
- <label for="capacity_<?php echo $service_id ?>"><?php _e( 'Capacity', 'bookly' ) ?></label>
95
- <p class="help-block"><?php _e( 'The maximum number of customers allowed to book the service for the certain time period.', 'bookly' ) ?></p>
96
- <input id="capacity_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="1" step="1" name="capacity" value="<?php echo esc_attr( $service['capacity'] ) ?>">
 
 
 
 
 
97
  </div>
98
  </div>
99
  </div>
100
 
101
- <div class="ab--for-simple">
102
  <div class="row">
103
- <div class="col-sm-4">
104
  <div class="form-group">
105
  <label for="duration_<?php echo $service_id ?>">
106
  <?php _e( 'Duration', 'bookly' ) ?>
107
  </label>
108
  <select id="duration_<?php echo $service_id ?>" class="form-control" name="duration">
109
- <?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['duration'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['duration'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['duration'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
110
- <option value="86400" <?php selected( $service['duration'], DAY_IN_SECONDS ) ?>><?php _e( 'All day', 'bookly' ) ?></option>
111
  </select>
112
  </div>
113
  </div>
114
- <div class="col-sm-8">
115
  <div class="form-group">
116
  <label for="padding_left_<?php echo $service_id ?>">
117
  <?php _e( 'Padding time (before and after)', 'bookly' ) ?>
@@ -121,23 +154,59 @@
121
  <div class="col-xs-6">
122
  <select id="padding_left_<?php echo $service_id ?>" class="form-control" name="padding_left">
123
  <option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
124
- <?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['padding_left'] > 0 && $service['padding_left'] / 60 > $j - $time_interval && $service['padding_left'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_left'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['padding_left'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_left'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
125
  </select>
126
  </div>
127
  <div class="col-xs-6">
128
  <select id="padding_right_<?php echo $service_id ?>" class="form-control" name="padding_right">
129
  <option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
130
- <?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['padding_right'] > 0 && $service['padding_right'] / 60 > $j - $time_interval && $service['padding_right'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_right'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['padding_right'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_right'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
131
  </select>
132
  </div>
133
  </div>
134
  </div>
135
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  </div>
137
  </div>
138
 
139
  <div class="row">
140
- <div class="col-sm-6">
141
  <div class="form-group">
142
  <label for="category_<?php echo $service_id ?>"><?php _e( 'Category', 'bookly' ) ?></label>
143
  <select id="category_<?php echo $service_id ?>" class="form-control" name="category_id"><option value="0"><?php _e( 'Uncategorized', 'bookly' ) ?></option>
@@ -147,10 +216,10 @@
147
  </select>
148
  </div>
149
  </div>
150
- <div class="col-sm-6 ab--for-simple">
151
  <div class="form-group">
152
  <label><?php _e( 'Providers', 'bookly' ) ?></label><br>
153
- <div class="btn-group">
154
  <button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
155
  <div class="bookly-flex-cell">
156
  <i class="dashicons dashicons-admin-users bookly-margin-right-md"></i>
@@ -164,7 +233,7 @@
164
  <li>
165
  <a class="checkbox" href="javascript:void(0)">
166
  <label>
167
- <input type="checkbox" id="service_<?php echo $service_id ?>_all_bookly-js-check-entity" class="bookly-check-all-entities" <?php checked( $all_staff_selected ) ?>">
168
  <?php _e( 'All staff', 'bookly' ) ?>
169
  </label>
170
  </a>
@@ -173,7 +242,7 @@
173
  <li>
174
  <a class="checkbox" href="javascript:void(0)">
175
  <label>
176
- <input type="checkbox" name="staff_ids[]" class="bookly-js-check-entity" value="<?php echo $staff['id'] ?>" <?php checked( in_array( $staff['id'], $assigned_staff_ids ) ) ?> data-staff_name="<?php echo esc_attr( $staff['full_name'] ) ?>">
177
  <?php echo esc_html( $staff['full_name'] ) ?>
178
  </label>
179
  </a>
@@ -185,7 +254,36 @@
185
  </div>
186
  </div>
187
 
188
- <div class="form-group">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  <label for="info_<?php echo $service_id ?>">
190
  <?php _e( 'Info', 'bookly' ) ?>
191
  </label>
@@ -195,123 +293,16 @@
195
  <textarea class="form-control" id="info_<?php echo $service_id ?>" name="info" rows="3" type="text"><?php echo esc_textarea( $service['info'] ) ?></textarea>
196
  </div>
197
 
198
- <?php do_action( 'bookly_compound_render_sub_services', $service, $service_collection, $service['sub_services'] ) ?>
199
-
200
- <?php if ( has_action( 'bookly_service_schedule_render_service_settings' ) ) : ?>
201
- <div class="ab--for-simple bookly-margin-bottom-xs">
202
- <a class="h4" href="#bookly_service_schedule_container_<?php echo $service_id ?>" data-toggle="collapse" role="button">
203
- <?php _e( 'Schedule', 'bookly' ) ?>
204
- </a>
205
- <div id="bookly_service_schedule_container_<?php echo $service_id ?>" class="bookly-margin-top-lg collapse in">
206
- <?php do_action( 'bookly_service_schedule_render_service_settings', $service ) ?>
207
- </div>
208
- </div>
209
- <?php endif ?>
210
-
211
- <?php if ( has_filter( 'bookly_service_extras_find_by_service_id' ) ) : ?>
212
- <div class="ab--for-simple bookly-margin-bottom-xs">
213
- <a class="h4" href="#bookly_service_extras_container_<?php echo $service_id ?>" data-toggle="collapse" role="button">
214
- <?php echo get_option( 'bookly_l10n_step_extras' ) ?>
215
- </a>
216
- <div id="bookly_service_extras_container_<?php echo $service_id ?>" class="bookly-margin-top-lg collapse in">
217
- <ul class="list-group extras-container" data-service="<?php echo $service_id ?>">
218
- <div class="form-group text-right">
219
- <button type="button" class="btn btn-success extra-new" data-spinner-size="40" data-style="zoom-in">
220
- <span class="ladda-label"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New Item', 'bookly' ) ?></span>
221
- </button>
222
- </div>
223
- <?php foreach ( apply_filters( 'bookly_service_extras_find_by_service_id', array(), $service_id ) as $extra ) : ?>
224
- <li class="list-group-item extra" data-extra-id="<?php echo $extra->get( 'id' ) ?>">
225
- <div class="row">
226
- <div class="col-lg-3">
227
- <div class="bookly-flexbox">
228
- <div class="bookly-flex-cell bookly-vertical-top">
229
- <i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
230
- </div>
231
- <div class="bookly-flex-cell" style="width: 100%">
232
- <div class="form-group">
233
- <input name="extras[<?php echo $extra->get( 'id' ) ?>][id]"
234
- value="<?php echo $extra->get( 'id' ) ?>" type="hidden">
235
- <input name="extras[<?php echo $extra->get( 'id' ) ?>][attachment_id]"
236
- value="<?php echo $extra->get( 'attachment_id' ) ?>" type="hidden">
237
-
238
- <?php $img = wp_get_attachment_image_src( $extra->get( 'attachment_id' ), 'thumbnail' ) ?>
239
-
240
- <div class="extra-attachment-image bookly-thumb bookly-thumb-lg bookly-margin-right-lg"
241
- <?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;"' : '' ?>
242
- >
243
- <a class="bookly-js-remove-attachment dashicons dashicons-trash text-danger bookly-thumb-delete" href="javascript:void(0)" title="<?php _e( 'Delete', 'bookly' ) ?>"
244
- <?php if ( !$img ) : ?>style="display: none;"<?php endif ?>>
245
- </a>
246
- <div class="bookly-thumb-edit extra-attachment" <?php if ( $img ) : ?>style="display: none;"<?php endif ?> >
247
- <div class="bookly-pretty">
248
- <label class="bookly-pretty-indicator bookly-thumb-edit-btn"><?php _e( 'Image', 'bookly' ) ?></label>
249
- </div>
250
- </div>
251
- </div>
252
- </div>
253
- </div>
254
- </div>
255
- </div>
256
-
257
- <div class="col-lg-9">
258
- <div class="form-group">
259
- <label for="title_extras_<?php echo $extra->get( 'id' ) ?>">
260
- <?php _e( 'Title', 'bookly' ) ?>
261
- </label>
262
- <input name="extras[<?php echo $extra->get( 'id' ) ?>][title]" class="form-control" type="text" id="title_extras_<?php echo $extra->get( 'id' ) ?>" value="<?php echo $extra->get( 'title' ) ?>">
263
- </div>
264
-
265
- <div class="row">
266
- <div class="col-sm-4">
267
- <div class="form-group">
268
- <label for="price_extras_<?php echo $extra->get( 'id' ) ?>">
269
- <?php _e( 'Price', 'bookly' ) ?>
270
- </label>
271
- <input name="extras[<?php echo $extra->get( 'id' ) ?>][price]" class="form-control" type="number" step="1" id="price_extras_<?php echo $extra->get( 'id' ) ?>" min="0.00" value="<?php echo $extra->get( 'price' ) ?>">
272
- </div>
273
- </div>
274
-
275
- <div class="col-sm-4">
276
- <div class="form-group">
277
- <label for="duration_extras_<?php echo $extra->get( 'id' ) ?>">
278
- <?php _e( 'Duration', 'bookly' ) ?>
279
- </label>
280
- <select name="extras[<?php echo $extra->get( 'id' ) ?>][duration]" id="duration_extras_<?php echo $extra->get( 'id' ) ?>" class="form-control">
281
- <option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
282
- <?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $extra->get( 'duration' ) > 0 && $extra->get( 'duration' ) / 60 > $j - $time_interval && $extra->get( 'duration' ) / 60 < $j ) : ?><option value="<?php echo esc_attr( $extra->get( 'duration' ) ) ?>" selected><?php echo BooklyLite\Lib\Utils\DateTime::secondsToInterval( $extra->get( 'duration' ) ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $extra->get( 'duration' ), $j * 60 ) ?>><?php echo BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
283
- </select>
284
- </div>
285
- </div>
286
-
287
- <div class="col-sm-4">
288
- <div class="form-group">
289
- <label for="max_quantity_extras_<?php echo $extra->get( 'id' ) ?>">
290
- <?php _e( 'Max quantity', 'bookly' ) ?>
291
- </label>
292
- <input name="extras[<?php echo $extra->get( 'id' ) ?>][max_quantity]" class="form-control" type="number" step="1" id="max_quantity_extras_<?php echo $extra->get( 'id' ) ?>" min="1" value="<?php echo $extra->get( 'max_quantity' ) ?>">
293
- </div>
294
- </div>
295
- </div>
296
-
297
- <div class="form-group text-right">
298
- <?php \BooklyLite\Lib\Utils\Common::deleteButton( null, 'extra-delete' ) ?>
299
- </div>
300
- </div>
301
- </div>
302
- </li>
303
- <?php endforeach ?>
304
- </ul>
305
- </div>
306
- </div>
307
- <?php endif ?>
308
-
309
  <div class="panel-footer">
310
  <input type="hidden" name="action" value="bookly_update_service">
311
  <input type="hidden" name="id" value="<?php echo esc_html( $service_id ) ?>">
312
  <input type="hidden" name="update_staff" value="0">
313
- <?php \BooklyLite\Lib\Utils\Common::submitButton( null, 'ajax-service-send' ) ?>
314
- <?php \BooklyLite\Lib\Utils\Common::resetButton( null, 'js-reset' ) ?>
 
 
315
  </div>
316
  </form>
317
  </div>
@@ -319,4 +310,7 @@
319
  </div>
320
  <?php endforeach ?>
321
  </div>
322
- <?php endif ?>
 
 
 
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Common;
3
+ use BooklyLite\Lib\Utils\DateTime;
4
+ use BooklyLite\Lib\Utils\Price;
5
+ use BooklyLite\Lib\Proxy;
6
+ use BooklyLite\Lib\Entities\Service;
7
+
8
+ $time_interval = get_option( 'bookly_gen_time_slot_length' );
9
  ?>
10
  <?php if ( ! empty( $service_collection ) ) : ?>
11
  <div class="panel-group" id="services_list" role="tablist" aria-multiselectable="true">
23
  <i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"
24
  title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
25
  </div>
26
+ <div class="bookly-flex-cell bookly-vertical-middle bookly-js-service-color" style="width: 55px; padding-left: 25px;">
27
+ <span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package"
28
+ style="background-color: <?php echo esc_attr( $service['colors'][0] == '-1' ? 'grey' : $service['colors'][0] ) ?>">&nbsp;</span>
29
+ <span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-compound bookly-js-service-package"
30
+ style="background-color: <?php echo esc_attr( $service['colors'][1] == '-1' ? 'grey' : $service['colors'][1] ) ?>; <?php if ( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ) : ?>display: none;<?php endif ?>">&nbsp;</span>
31
+ <span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-package"
32
+ style="background-color: <?php echo esc_attr( $service['colors'][2] == '-1' ? 'grey' : $service['colors'][2] ) ?>; <?php if ( $service['type'] != \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ) : ?>display: none;<?php endif ?>">&nbsp;</span>
33
  </div>
34
  <div class="bookly-flex-cell bookly-vertical-middle">
35
  <a role="button" class="panel-title collapsed bookly-js-service-title" data-toggle="collapse"
44
  <div class="bookly-flexbox">
45
  <div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 60%">
46
  <span class="bookly-js-service-duration">
47
+ <?php
48
+ switch ( $service['type'] ) {
49
+ case Service::TYPE_SIMPLE:
50
+ case Service::TYPE_PACKAGE:
51
+ echo DateTime::secondsToInterval( $service['duration'] ); break;
52
+ case Service::TYPE_COMPOUND:
53
+ echo sprintf( _n( '%d service', '%d services', $service['sub_services_count'], 'bookly' ), $service['sub_services_count'] ); break;
54
+ }
55
+ ?>
56
  </span>
57
  </div>
58
  <div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 30%">
59
  <span class="bookly-js-service-price">
60
+ <?php echo Price::format( $service['price'] ) ?>
61
  </span>
62
  </div>
63
  <div class="bookly-flex-cell bookly-vertical-middle text-right" style="width: 10%">
73
  <div id="service_<?php echo $service_id ?>" class="panel-collapse collapse" role="tabpanel" style="height: 0">
74
  <div class="panel-body">
75
  <form method="post">
76
+ <div class="form-inline bookly-margin-bottom-lg bookly-js-service-type collapse">
77
+ <div class="form-group">
78
+ <div class="radio">
79
+ <label class="bookly-margin-right-md">
80
+ <input type="radio" name="type" value="simple" data-panel-class="panel-default" <?php echo checked( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ) ?>><?php _e( 'Simple', 'bookly' ) ?>
81
+ </label>
82
+ </div>
83
+ </div>
84
+ <?php Proxy\Shared::renderServiceFormHead( $service ) ?>
85
+ </div>
86
  <div class="row">
87
+ <div class="col-md-9 col-sm-6 bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
88
  <div class="form-group">
89
  <label for="title_<?php echo $service_id ?>"><?php _e( 'Title', 'bookly' ) ?></label>
90
  <input name="title" value="<?php echo esc_attr( $service['title'] ) ?>" id="title_<?php echo $service_id ?>" class="form-control" type="text">
91
  </div>
92
  </div>
93
+ <div class="col-md-3 col-sm-6 bookly-js-service bookly-js-service-simple">
94
  <div class="form-group">
95
  <label><?php _e( 'Color', 'bookly' ) ?></label>
96
  <div class="bookly-color-picker-wrapper">
97
+ <input name="color" value="<?php echo esc_attr( $service['color'] ) ?>" class="bookly-js-color-picker" data-last-color="<?php echo esc_attr( $service['color'] ) ?>" type="text">
98
  </div>
99
  </div>
100
  </div>
101
  </div>
102
+ <?php Proxy\Packages::renderServicePackage( $service, $service_collection ) ?>
103
  <div class="row">
104
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
105
  <div class="form-group">
106
  <label for="visibility_<?php echo $service_id ?>"><?php _e( 'Visibility', 'bookly' ) ?></label>
107
  <p class="help-block"><?php _e( 'To make service invisible to your customers set the visibility to "Private".', 'bookly' ) ?></p>
111
  </select>
112
  </div>
113
  </div>
114
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
115
  <div class="form-group">
116
  <label for="price_<?php echo $service_id ?>"><?php _e( 'Price', 'bookly' ) ?></label>
117
+ <input id="price_<?php echo $service_id ?>" class="form-control bookly-question" type="number" min="0" step="1" name="price" value="<?php echo esc_attr( $service['price'] ) ?>">
118
  </div>
119
  </div>
120
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple">
121
  <div class="form-group">
122
+ <div class="row">
123
+ <div class="col-xs-6">
124
+ <input id="capacity_min_<?php echo $service_id ?>" class="form-control bookly-question bookly-js-capacity" type="hidden" min="1" step="1" name="capacity_min" value="1">
125
+ </div>
126
+ <div class="col-xs-6">
127
+ <input id="capacity_max_<?php echo $service_id ?>" class="form-control bookly-question bookly-js-capacity" type="hidden" min="1" step="1" name="capacity_max" value="1">
128
+ </div>
129
+ </div>
130
  </div>
131
  </div>
132
  </div>
133
 
134
+ <div class="bookly-js-service bookly-js-service-simple">
135
  <div class="row">
136
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple">
137
  <div class="form-group">
138
  <label for="duration_<?php echo $service_id ?>">
139
  <?php _e( 'Duration', 'bookly' ) ?>
140
  </label>
141
  <select id="duration_<?php echo $service_id ?>" class="form-control" name="duration">
142
+ <?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['duration'] ) ?>" selected><?php echo DateTime::secondsToInterval( $service['duration'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['duration'], $j * 60 ) ?>><?php echo DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
143
+ <?php for ( $j = 86400; $j <= 604800; $j += 86400 ) : ?><option value="<?php echo $j ?>" <?php selected( $service['duration'], $j ) ?>><?php echo DateTime::secondsToInterval( $j ) ?></option><?php endfor ?>
144
  </select>
145
  </div>
146
  </div>
147
+ <div class="col-sm-8 bookly-js-service bookly-js-service-simple">
148
  <div class="form-group">
149
  <label for="padding_left_<?php echo $service_id ?>">
150
  <?php _e( 'Padding time (before and after)', 'bookly' ) ?>
154
  <div class="col-xs-6">
155
  <select id="padding_left_<?php echo $service_id ?>" class="form-control" name="padding_left">
156
  <option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
157
+ <?php for ( $j = $time_interval; $j <= 1440; $j += $time_interval ) : ?><?php if ( $service['padding_left'] > 0 && $service['padding_left'] / 60 > $j - $time_interval && $service['padding_left'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_left'] ) ?>" selected><?php echo DateTime::secondsToInterval( $service['padding_left'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_left'], $j * 60 ) ?>><?php echo DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
158
  </select>
159
  </div>
160
  <div class="col-xs-6">
161
  <select id="padding_right_<?php echo $service_id ?>" class="form-control" name="padding_right">
162
  <option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
163
+ <?php for ( $j = $time_interval; $j <= 1440; $j += $time_interval ) : ?><?php if ( $service['padding_right'] > 0 && $service['padding_right'] / 60 > $j - $time_interval && $service['padding_right'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_right'] ) ?>" selected><?php echo DateTime::secondsToInterval( $service['padding_right'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_right'], $j * 60 ) ?>><?php echo DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
164
  </select>
165
  </div>
166
  </div>
167
  </div>
168
  </div>
169
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple">
170
+ <div class="form-group">
171
+ <label for="start_time_info_<?php echo $service_id ?>"><?php _e( 'Start and end times of the appointment', 'bookly' ) ?></label>
172
+ <p class="help-block"><?php _e( 'Allows to set the start and end times for an appointment for services with the duration of 1 day or longer. This time will be displayed in notifications to customers.', 'bookly' ) ?></p>
173
+ <div class="row">
174
+ <div class="col-xs-6">
175
+ <input id="start_time_info_<?php echo $service_id ?>" class="form-control" type="text" name="start_time_info" value="<?php echo esc_attr( $service['start_time_info'] ) ?>">
176
+ </div>
177
+ <div class="col-xs-6">
178
+ <input class="form-control" type="text" name="end_time_info" value="<?php echo esc_attr( $service['end_time_info'] ) ?>">
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <div class="bookly-js-service bookly-js-service-simple">
187
+ <div class="row">
188
+ <div class="col-sm-4 bookly-js-service bookly-js-service-simple">
189
+ <div class="form-group">
190
+ <label for="staff_preference_<?php echo $service_id ?>">
191
+ <?php _e( 'Providers preference for ANY', 'bookly' ) ?>
192
+ </label>
193
+ <p class="help-block"><?php _e( 'Allows you to define the rule of staff members auto assignment when ANY option is selected', 'bookly' ) ?></p>
194
+ <select id="staff_preference_<?php echo $service_id ?>" class="form-control" name="staff_preference" data-default="[<?php echo $service['pref_staff_ids'] ?>]">
195
+ <?php foreach ( $staff_preference as $rule => $name ) : ?><option value="<?php echo $rule ?>" <?php selected( $rule == $service['staff_preference'] ) ?>><?php echo $name ?></option><?php endforeach ?>
196
+ </select>
197
+ </div>
198
+ </div>
199
+ <div class="col-sm-8 bookly-preference-box">
200
+ <div class="form-group">
201
+ <label for="staff_preferred_<?php echo $service_id ?>"><?php _e( 'Providers', 'bookly' ) ?></label><br/>
202
+ <div class="bookly-staff-list" data-service_id="<?php echo $service_id ?>"></div>
203
+ </div>
204
+ </div>
205
  </div>
206
  </div>
207
 
208
  <div class="row">
209
+ <div class="col-sm-6 bookly-js-service bookly-js-service-simple bookly-js-service-compound">
210
  <div class="form-group">
211
  <label for="category_<?php echo $service_id ?>"><?php _e( 'Category', 'bookly' ) ?></label>
212
  <select id="category_<?php echo $service_id ?>" class="form-control" name="category_id"><option value="0"><?php _e( 'Uncategorized', 'bookly' ) ?></option>
216
  </select>
217
  </div>
218
  </div>
219
+ <div class="col-sm-6 bookly-js-service bookly-js-service-simple bookly-js-service-package">
220
  <div class="form-group">
221
  <label><?php _e( 'Providers', 'bookly' ) ?></label><br>
222
+ <div class="btn-group bookly-js-entity-selector-container">
223
  <button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
224
  <div class="bookly-flex-cell">
225
  <i class="dashicons dashicons-admin-users bookly-margin-right-md"></i>
233
  <li>
234
  <a class="checkbox" href="javascript:void(0)">
235
  <label>
236
+ <input type="checkbox" class="bookly-check-all-entities" <?php checked( $all_staff_selected ) ?> data-title="<?php esc_attr_e( 'All staff', 'bookly' ) ?>" data-nothing="<?php esc_attr_e( 'No staff selected', 'bookly' ) ?>">
237
  <?php _e( 'All staff', 'bookly' ) ?>
238
  </label>
239
  </a>
242
  <li>
243
  <a class="checkbox" href="javascript:void(0)">
244
  <label>
245
+ <input type="checkbox" name="staff_ids[]" class="bookly-js-check-entity" value="<?php echo $staff['id'] ?>" <?php checked( in_array( $staff['id'], $assigned_staff_ids ) ) ?> data-title="<?php echo esc_attr( $staff['full_name'] ) ?>">
246
  <?php echo esc_html( $staff['full_name'] ) ?>
247
  </label>
248
  </a>
254
  </div>
255
  </div>
256
 
257
+ <div class="bookly-js-service bookly-js-service-simple bookly-js-service-compound">
258
+ <div class="row">
259
+ <div class="col-sm-8">
260
+ <label for="appointments_limit_<?php echo $service_id ?>">
261
+ <?php _e( 'Limit appointments per customer', 'bookly' ) ?>
262
+ </label>
263
+ <p class="help-block"><?php _e( 'Allows you to limit the frequency of service bookings per customer.', 'bookly' ) ?></p>
264
+ <div class="row">
265
+ <div class="col-sm-6">
266
+ <div class="form-group">
267
+ <input id="appointments_limit_<?php echo $service_id ?>" class="form-control" type="number" min="0" step="1" name="appointments_limit" value="<?php echo esc_attr( $service['appointments_limit'] ) ?>">
268
+ </div>
269
+ </div>
270
+ <div class="col-sm-6">
271
+ <div class="form-group">
272
+ <select id="limit_period_<?php echo $service_id ?>" class="form-control" name="limit_period">
273
+ <option value="off"><?php _e( 'OFF', 'bookly' ) ?></option>
274
+ <option value="day"<?php selected( 'day', $service['limit_period'] ) ?>><?php _e( 'per day', 'bookly' ) ?></option>
275
+ <option value="week"<?php selected( 'week', $service['limit_period'] ) ?>><?php _e( 'per week', 'bookly' ) ?></option>
276
+ <option value="month"<?php selected( 'month', $service['limit_period'] ) ?>><?php _e( 'per month', 'bookly' ) ?></option>
277
+ <option value="year"<?php selected( 'year', $service['limit_period'] ) ?>><?php _e( 'per year', 'bookly' ) ?></option>
278
+ </select>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ </div>
285
+
286
+ <div class="form-group bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
287
  <label for="info_<?php echo $service_id ?>">
288
  <?php _e( 'Info', 'bookly' ) ?>
289
  </label>
293
  <textarea class="form-control" id="info_<?php echo $service_id ?>" name="info" rows="3" type="text"><?php echo esc_textarea( $service['info'] ) ?></textarea>
294
  </div>
295
 
296
+ <?php Proxy\CompoundServices::renderSubServices( $service, $service_collection ) ?>
297
+ <?php Proxy\Shared::renderServiceForm( $service ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  <div class="panel-footer">
299
  <input type="hidden" name="action" value="bookly_update_service">
300
  <input type="hidden" name="id" value="<?php echo esc_html( $service_id ) ?>">
301
  <input type="hidden" name="update_staff" value="0">
302
+ <span class="bookly-js-services-error text-danger"></span>
303
+ <?php Common::csrf() ?>
304
+ <?php Common::submitButton( null, 'ajax-service-send' ) ?>
305
+ <?php Common::resetButton( null, 'js-reset' ) ?>
306
  </div>
307
  </form>
308
  </div>
310
  </div>
311
  <?php endforeach ?>
312
  </div>
313
+ <?php endif ?>
314
+ <div style="display: none">
315
+ <?php Proxy\Shared::renderAfterServiceList( $service_collection ) ?>
316
+ </div>
backend/modules/services/templates/index.php CHANGED
@@ -14,7 +14,7 @@
14
  <div class="bookly-padding-vertical-xs"><?php _e( 'All Services', 'bookly' ) ?></div>
15
  </div>
16
  <ul id="bookly-category-item-list">
17
- <?php foreach ( $category_collection as $category ) include '_category_item.php'; ?>
18
  </ul>
19
  </div>
20
 
@@ -31,7 +31,8 @@
31
  <div class="form-field form-required">
32
  <label for="bookly-category-name"><?php _e( 'Name', 'bookly' ) ?></label>
33
  <input class="form-control" id="bookly-category-name" type="text" name="name" />
34
- <input type="hidden" name="action" value="bookly_category_form" />
 
35
  </div>
36
  </div>
37
 
@@ -61,7 +62,7 @@
61
  <?php _e( 'No services found. Please add services.', 'bookly' ) ?>
62
  </p>
63
 
64
- <div class="bookly-margin-top-xlg" id="ab-services-list">
65
  <?php include '_list.php' ?>
66
  </div>
67
  <div class="text-right">
@@ -73,7 +74,7 @@
73
  </div>
74
  </div>
75
 
76
- <div id="ab-staff-update" class="modal fade" tabindex=-1 role="dialog">
77
  <div class="modal-dialog">
78
  <div class="modal-content">
79
  <div class="modal-header">
@@ -84,22 +85,18 @@
84
  <p><?php _e( 'You are about to change a service setting which is also configured separately for each staff member. Do you want to update it in staff settings too?', 'bookly' ) ?></p>
85
  <div class="checkbox">
86
  <label>
87
- <input id="ab-remember-my-choice" type="checkbox">
88
  <?php _e( 'Remember my choice', 'bookly' ) ?>
89
  </label>
90
  </div>
91
  </div>
92
  <div class="modal-footer">
93
- <button type="reset" class="btn btn-default ab-no" data-dismiss="modal" aria-hidden="true">
94
  <?php _e( 'No, update just here in services', 'bookly' ) ?>
95
  </button>
96
- <button type="submit" class="btn btn-success ab-yes"><?php _e( 'Yes', 'bookly' ) ?></button>
97
  </div>
98
  </div>
99
  </div>
100
  </div>
101
- </div>
102
-
103
- <div class="hidden">
104
- <?php do_action( 'bookly_render_after_service_list', $service_collection ) ?>
105
  </div>
14
  <div class="bookly-padding-vertical-xs"><?php _e( 'All Services', 'bookly' ) ?></div>
15
  </div>
16
  <ul id="bookly-category-item-list">
17
+ <?php foreach ( $category_collection as $category ) $this->render( '_category_item', compact( 'category' ) ) ?>
18
  </ul>
19
  </div>
20
 
31
  <div class="form-field form-required">
32
  <label for="bookly-category-name"><?php _e( 'Name', 'bookly' ) ?></label>
33
  <input class="form-control" id="bookly-category-name" type="text" name="name" />
34
+ <input type="hidden" name="action" value="bookly_add_category" />
35
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
36
  </div>
37
  </div>
38
 
62
  <?php _e( 'No services found. Please add services.', 'bookly' ) ?>
63
  </p>
64
 
65
+ <div class="bookly-margin-top-xlg" id="bookly-js-services-list">
66
  <?php include '_list.php' ?>
67
  </div>
68
  <div class="text-right">
74
  </div>
75
  </div>
76
 
77
+ <div id="bookly-update-service-settings" class="modal fade" tabindex=-1 role="dialog">
78
  <div class="modal-dialog">
79
  <div class="modal-content">
80
  <div class="modal-header">
85
  <p><?php _e( 'You are about to change a service setting which is also configured separately for each staff member. Do you want to update it in staff settings too?', 'bookly' ) ?></p>
86
  <div class="checkbox">
87
  <label>
88
+ <input id="bookly-remember-my-choice" type="checkbox">
89
  <?php _e( 'Remember my choice', 'bookly' ) ?>
90
  </label>
91
  </div>
92
  </div>
93
  <div class="modal-footer">
94
+ <button type="reset" class="btn btn-default bookly-no" data-dismiss="modal" aria-hidden="true">
95
  <?php _e( 'No, update just here in services', 'bookly' ) ?>
96
  </button>
97
+ <button type="submit" class="btn btn-success bookly-yes"><?php _e( 'Yes', 'bookly' ) ?></button>
98
  </div>
99
  </div>
100
  </div>
101
  </div>
 
 
 
 
102
  </div>
backend/modules/settings/Components.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace BooklyLite\Backend\Modules\Settings;
3
+
4
+ use BooklyLite\Lib;
5
+
6
+ /**
7
+ * Class Components
8
+ * @package BooklyLite\Backend\Modules\Support
9
+ */
10
+ class Components extends Lib\Base\Components
11
+ {
12
+ /**
13
+ * Render collect stats notice and marks it as showed for every user
14
+ */
15
+ public function renderCollectStatsNotice()
16
+ {
17
+ if ( Lib\Utils\Common::isCurrentUserAdmin() &&
18
+ get_option( 'bookly_gen_collect_stats' ) == '0' &&
19
+ ! (int) get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_collect_stats_notice', true )
20
+ ) {
21
+ $this->enqueueStyles( array(
22
+ 'frontend' => array( 'css/ladda.min.css', ),
23
+ ) );
24
+ $this->enqueueScripts( array(
25
+ 'module' => array( 'js/collect-stats-notice.js' => array( 'jquery' ), ),
26
+ ) );
27
+ $this->render( '_collect_stats_notice' );
28
+ }
29
+ }
30
+ }
backend/modules/settings/Controller.php CHANGED
@@ -41,93 +41,98 @@ class Controller extends Lib\Base\Controller
41
 
42
  // Save the settings.
43
  if ( ! empty ( $_POST ) ) {
44
- switch ( $this->getParameter( 'tab' ) ) {
45
- case 'payments': // Payments form.
46
- $form = new Forms\Payments();
47
- update_option( 'bookly_pmt_paypal' ,'disabled' );
48
- update_option( 'bookly_pmt_coupons' ,'0' );
49
- update_option( 'bookly_pmt_authorize_net' ,'disabled' );
50
- update_option( 'bookly_pmt_stripe' ,'disabled' );
51
- update_option( 'bookly_pmt_2checkout' ,'disabled' );
52
- update_option( 'bookly_pmt_payu_latam' ,'disabled' );
53
- update_option( 'bookly_pmt_payson' ,'disabled' );
54
- update_option( 'bookly_pmt_mollie' ,'disabled' );
55
- break;
56
- case 'business_hours': // Business hours form.
57
- $form = new Forms\BusinessHours();
58
- break;
59
- case 'purchase_code': // Purchase Code form.
60
- break;
61
- case 'general': // General form.
62
- $bookly_gen_time_slot_length = $this->getParameter( 'bookly_gen_time_slot_length' );
63
- if ( in_array( $bookly_gen_time_slot_length, array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) ) ) {
64
- update_option( 'bookly_gen_time_slot_length', $bookly_gen_time_slot_length );
65
- }
66
- update_option( 'bookly_lite_uninstall_remove_bookly_data', (int) $this->getParameter( 'bookly_lite_uninstall_remove_bookly_data' ) );
67
- update_option( 'bookly_gen_service_duration_as_slot_length', (int) $this->getParameter( 'bookly_gen_service_duration_as_slot_length' ) );
68
- update_option( 'bookly_gen_allow_staff_edit_profile', (int) $this->getParameter( 'bookly_gen_allow_staff_edit_profile' ) );
69
- update_option( 'bookly_gen_approve_page_url', $this->getParameter( 'bookly_gen_approve_page_url' ) );
70
- update_option( 'bookly_gen_cancel_denied_page_url', $this->getParameter( 'bookly_gen_cancel_denied_page_url' ) );
71
- update_option( 'bookly_gen_cancel_page_url', $this->getParameter( 'bookly_gen_cancel_page_url' ) );
72
- update_option( 'bookly_gen_default_appointment_status', $this->getParameter( 'bookly_gen_default_appointment_status' ) );
73
- update_option( 'bookly_gen_final_step_url', '' );
74
- update_option( 'bookly_gen_link_assets_method', $this->getParameter( 'bookly_gen_link_assets_method' ) );
75
- update_option( 'bookly_gen_max_days_for_booking', (int) $this->getParameter( 'bookly_gen_max_days_for_booking' ) );
76
- update_option( 'bookly_gen_min_time_prior_booking', (int) $this->getParameter( 'bookly_gen_min_time_prior_booking' ) );
77
- update_option( 'bookly_gen_min_time_prior_cancel', $this->getParameter( 'bookly_gen_min_time_prior_cancel' ) );
78
- update_option( 'bookly_gen_use_client_time_zone', (int) $this->getParameter( 'bookly_gen_use_client_time_zone' ) );
79
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
80
- break;
81
- case 'google_calendar': // Google calendar form.
82
- break;
83
- case 'customers': // Customers form.
84
- update_option( 'bookly_cst_cancel_action', $this->getParameter( 'bookly_cst_cancel_action' ) );
85
- update_option( 'bookly_cst_create_account', (int) $this->getParameter( 'bookly_cst_create_account' ) );
86
- update_option( 'bookly_cst_default_country_code', $this->getParameter( 'bookly_cst_default_country_code' ) );
87
- update_option( 'bookly_cst_new_account_role', $this->getParameter( 'bookly_cst_new_account_role' ) );
88
- update_option( 'bookly_cst_combined_notifications', $this->getParameter( 'bookly_cst_combined_notifications' ) );
89
- update_option( 'bookly_cst_phone_default_country', $this->getParameter( 'bookly_cst_phone_default_country' ) );
90
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
91
- break;
92
- case 'woocommerce': // WooCommerce form.
93
- foreach ( array( 'bookly_l10n_wc_cart_info_name', 'bookly_l10n_wc_cart_info_value' ) as $option_name ) {
94
- update_option( $option_name, $this->getParameter( $option_name ) );
95
- do_action( 'wpml_register_single_string', 'bookly', $option_name, $this->getParameter( $option_name ) );
96
- }
97
- update_option( 'bookly_wc_enabled', $this->getParameter( 'bookly_wc_enabled' ) );
98
- update_option( 'bookly_wc_product', $this->getParameter( 'bookly_wc_product' ) );
99
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
100
- break;
101
- case 'cart': // Cart form.
102
- update_option( 'bookly_cart_show_columns', $this->getParameter( 'bookly_cart_show_columns', array() ) );
103
- update_option( 'bookly_cart_enabled', 0 );
104
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
105
- if ( get_option( 'bookly_wc_enabled' ) && $this->getParameter( 'bookly_cart_enabled' ) ) {
106
- $alert['error'][] = sprintf( __( 'To use the cart, disable integration with WooCommerce <a href="%s">here</a>.', 'bookly' ), Lib\Utils\Common::escAdminUrl( self::page_slug, array( 'tab' => 'woocommerce' ) ) );
107
- }
108
- break;
109
- case 'company': // Company form.
110
- update_option( 'bookly_co_address', $this->getParameter( 'bookly_co_address' ) );
111
- update_option( 'bookly_co_logo_attachment_id', $this->getParameter( 'bookly_co_logo_attachment_id' ) );
112
- update_option( 'bookly_co_name', $this->getParameter( 'bookly_co_name' ) );
113
- update_option( 'bookly_co_phone', $this->getParameter( 'bookly_co_phone' ) );
114
- update_option( 'bookly_co_website', $this->getParameter( 'bookly_co_website' ) );
115
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
116
- break;
117
- default:
118
- // Let Add-ons save their settings.
119
- $alert = apply_filters( 'bookly_save_settings', $alert, $this->getParameter( 'tab' ), $this->getPostParameters() );
120
- }
 
 
121
 
122
- if ( in_array( $this->getParameter( 'tab' ), array ( 'payments', 'business_hours' ) ) ) {
123
- $form->bind( $this->getPostParameters(), $_FILES );
124
- $form->save();
125
 
126
- $alert['success'][] = __( 'Settings saved.', 'bookly' );
 
 
 
 
 
127
  }
128
  }
129
 
130
- $holidays = $this->getHolidays();
131
  $candidates = $this->getCandidatesBooklyProduct();
132
 
133
  // Check if WooCommerce cart exists.
@@ -150,18 +155,23 @@ class Controller extends Lib\Base\Controller
150
  );
151
 
152
  wp_localize_script( 'bookly-jCal.js', 'BooklyL10n', array(
153
- 'alert' => $alert,
154
- 'close' => __( 'Close', 'bookly' ),
155
- 'current_tab' => $current_tab,
156
- 'days' => array_values( $wp_locale->weekday_abbrev ),
157
- 'months' => array_values( $wp_locale->month ),
158
- 'repeat' => __( 'Repeat every year', 'bookly' ),
 
 
 
 
 
159
  'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
160
- 'default_country' => get_option( 'bookly_cst_phone_default_country' ),
161
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
162
  ) );
163
  $values = array(
164
- 'bookly_gc_limit_events' => array( array( '0', __( 'Disabled', 'bookly' ) ), array( 25, 25 ), array( 50, 50 ), array( 100, 100 ), array( 250, 250 ), array( 500, 500 ), array( 1000, 1000), array( 2500, 2500 ) ),
165
  'bookly_gen_min_time_prior_booking' => array( array( '0', __( 'Disabled', 'bookly' ) ) ),
166
  'bookly_gen_min_time_prior_cancel' => array( array( '0', __( 'Disabled', 'bookly' ) ) ),
167
  );
@@ -172,14 +182,14 @@ class Controller extends Lib\Base\Controller
172
  foreach ( array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) as $duration ) {
173
  $values['bookly_gen_time_slot_length'][] = array( $duration, Lib\Utils\DateTime::secondsToInterval( $duration * MINUTE_IN_SECONDS ) );
174
  }
175
- foreach ( array_merge( range( 1, 12 ), range( 24, 144, 24 ), range( 168, 672, 168 ) ) as $hour ) {
176
  $values['bookly_gen_min_time_prior_booking'][] = array( $hour, Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) );
177
  }
178
  foreach ( array_merge( array( 1 ), range( 2, 12, 2 ), range( 24, 168, 24 ) ) as $hour ) {
179
  $values['bookly_gen_min_time_prior_cancel'][] = array( $hour, Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) );
180
  }
181
 
182
- $this->render( 'index', compact( 'holidays', 'candidates', 'cart_columns', 'values' ) );
183
  }
184
 
185
  /**
@@ -192,31 +202,59 @@ class Controller extends Lib\Base\Controller
192
  $id = $this->getParameter( 'id', false );
193
  $day = $this->getParameter( 'day', false );
194
  $holiday = $this->getParameter( 'holiday' ) == 'true';
195
- $repeat = $this->getParameter( 'repeat' ) == 'true';
196
 
197
  // update or delete the event
198
  if ( $id ) {
199
  if ( $holiday ) {
200
- $wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => (int) $repeat ), array( 'id' => $id ), array( '%d' ) );
201
- $wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => (int) $repeat ), array( 'parent_id' => $id ), array( '%d' ) );
202
  } else {
203
  Lib\Entities\Holiday::query()->delete()->where( 'id', $id )->where( 'parent_id', $id, 'OR' )->execute();
204
  }
205
  // add the new event
206
  } elseif ( $holiday && $day ) {
207
- $holiday = new Lib\Entities\Holiday( array( 'date' => $day, 'repeat_event' => (int) $repeat ) );
208
- $holiday->save();
 
 
 
209
  foreach ( Lib\Entities\Staff::query()->fetchArray() as $employee ) {
210
- $staff_holiday = new Lib\Entities\Holiday( array( 'date' => $day, 'repeat_event' => (int) $repeat, 'staff_id' => $employee['id'], 'parent_id' => $holiday->get( 'id' ) ) );
211
- $staff_holiday->save();
 
 
 
 
 
212
  }
213
  }
214
 
215
  // and return refreshed events
216
- echo $this->getHolidays();
217
  exit;
218
  }
219
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  /**
221
  * @return string
222
  */
@@ -237,35 +275,25 @@ class Controller extends Lib\Base\Controller
237
  }
238
  }
239
 
240
- return json_encode( $holidays );
241
  }
242
 
 
 
 
243
  protected function getCandidatesBooklyProduct()
244
  {
245
- $goods = array( array( 'id' => 0, 'name' => __( 'Select product', 'bookly' ) ) );
246
- $args = array(
247
- 'numberposts' => -1,
248
- 'post_type' => 'product',
249
- 'suppress_filters' => true
250
- );
251
- $collection = get_posts( $args );
252
- foreach ( $collection as $item ) {
253
- $goods[] = array( 'id' => $item->ID, 'name' => $item->post_title );
254
  }
255
- wp_reset_postdata();
256
 
257
  return $goods;
258
  }
259
-
260
- /**
261
- * Override parent method to add 'wp_ajax_bookly_' prefix
262
- * so current 'execute*' methods look nicer.
263
- *
264
- * @param string $prefix
265
- */
266
- protected function registerWpActions( $prefix = '' )
267
- {
268
- parent::registerWpActions( 'wp_ajax_bookly_' );
269
- }
270
-
271
  }
41
 
42
  // Save the settings.
43
  if ( ! empty ( $_POST ) ) {
44
+ if ( $this->csrfTokenValid() ) {
45
+ switch ( $this->getParameter( 'tab' ) ) {
46
+ case 'calendar': // Calendar form.
47
+ update_option( 'bookly_cal_one_participant', $this->getParameter( 'bookly_cal_one_participant' ) );
48
+ update_option( 'bookly_cal_many_participants', $this->getParameter( 'bookly_cal_many_participants' ) );
49
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
50
+ break;
51
+ case 'payments': // Payments form.
52
+ $form = new Forms\Payments();
53
+ break;
54
+ case 'business_hours': // Business hours form.
55
+ $form = new Forms\BusinessHours();
56
+ break;
57
+ case 'purchase_code': // Purchase Code form.
58
+ break;
59
+ case 'general': // General form.
60
+ $bookly_gen_time_slot_length = $this->getParameter( 'bookly_gen_time_slot_length' );
61
+ if ( in_array( $bookly_gen_time_slot_length, array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) ) ) {
62
+ update_option( 'bookly_gen_time_slot_length', $bookly_gen_time_slot_length );
63
+ }
64
+ update_option( 'bookly_gen_lite_uninstall_remove_bookly_data', (int) $this->getParameter( 'bookly_gen_lite_uninstall_remove_bookly_data' ) );
65
+ update_option( 'bookly_gen_service_duration_as_slot_length', (int) $this->getParameter( 'bookly_gen_service_duration_as_slot_length' ) );
66
+ update_option( 'bookly_gen_allow_staff_edit_profile', (int) $this->getParameter( 'bookly_gen_allow_staff_edit_profile' ) );
67
+ update_option( 'bookly_gen_default_appointment_status', $this->getParameter( 'bookly_gen_default_appointment_status' ) );
68
+ update_option( 'bookly_gen_link_assets_method', $this->getParameter( 'bookly_gen_link_assets_method' ) );
69
+ update_option( 'bookly_gen_max_days_for_booking', (int) $this->getParameter( 'bookly_gen_max_days_for_booking' ) );
70
+ update_option( 'bookly_gen_min_time_prior_booking', $this->getParameter( 'bookly_gen_min_time_prior_booking' ) );
71
+ update_option( 'bookly_gen_min_time_prior_cancel', $this->getParameter( 'bookly_gen_min_time_prior_cancel' ) );
72
+ update_option( 'bookly_gen_use_client_time_zone', (int) $this->getParameter( 'bookly_gen_use_client_time_zone' ) );
73
+ update_option( 'bookly_gen_collect_stats', $this->getParameter( 'bookly_gen_collect_stats' ) );
74
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
75
+ break;
76
+ case 'url': // URL settings form.
77
+ update_option( 'bookly_url_approve_page_url', $this->getParameter( 'bookly_url_approve_page_url' ) );
78
+ update_option( 'bookly_url_approve_denied_page_url', $this->getParameter( 'bookly_url_approve_denied_page_url' ) );
79
+ update_option( 'bookly_url_cancel_page_url', $this->getParameter( 'bookly_url_cancel_page_url' ) );
80
+ update_option( 'bookly_url_cancel_denied_page_url', $this->getParameter( 'bookly_url_cancel_denied_page_url' ) );
81
+ update_option( 'bookly_url_cancel_confirm_page_url', $this->getParameter( 'bookly_url_cancel_confirm_page_url' ) );
82
+ update_option( 'bookly_url_reject_denied_page_url', $this->getParameter( 'bookly_url_reject_denied_page_url' ) );
83
+ update_option( 'bookly_url_reject_page_url', $this->getParameter( 'bookly_url_reject_page_url' ) );
84
+ update_option( 'bookly_url_final_step_url', '' );
85
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
86
+ break;
87
+ case 'google_calendar': // Google calendar form.
88
+ update_option( 'bookly_gc_client_id', '' );
89
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
90
+ break;
91
+ case 'customers': // Customers form.
92
+ update_option( 'bookly_cst_cancel_action', $this->getParameter( 'bookly_cst_cancel_action' ) );
93
+ update_option( 'bookly_cst_combined_notifications', $this->getParameter( 'bookly_cst_combined_notifications' ) );
94
+ update_option( 'bookly_cst_create_account', $this->getParameter( 'bookly_cst_create_account' ) );
95
+ update_option( 'bookly_cst_default_country_code', $this->getParameter( 'bookly_cst_default_country_code' ) );
96
+ update_option( 'bookly_cst_new_account_role', $this->getParameter( 'bookly_cst_new_account_role' ) );
97
+ update_option( 'bookly_cst_phone_default_country', $this->getParameter( 'bookly_cst_phone_default_country' ) );
98
+ update_option( 'bookly_cst_remember_in_cookie', $this->getParameter( 'bookly_cst_remember_in_cookie' ) );
99
+ update_option( 'bookly_cst_show_update_details_dialog', $this->getParameter( 'bookly_cst_show_update_details_dialog' ) );
100
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
101
+ break;
102
+ case 'woo_commerce': // WooCommerce form.
103
+ update_option( 'bookly_wc_enabled', '0' );
104
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
105
+ break;
106
+ case 'cart': // Cart form.
107
+ update_option( 'bookly_cart_show_columns', $this->getParameter( 'bookly_cart_show_columns', array() ) );
108
+ update_option( 'bookly_cart_enabled', '0' );
109
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
110
+ if ( get_option( 'bookly_wc_enabled' ) && $this->getParameter( 'bookly_cart_enabled' ) ) {
111
+ $alert['error'][] = sprintf( __( 'To use the cart, disable integration with WooCommerce <a href="%s">here</a>.', 'bookly' ), Lib\Utils\Common::escAdminUrl( self::page_slug, array( 'tab' => 'woocommerce' ) ) );
112
+ }
113
+ break;
114
+ case 'company': // Company form.
115
+ update_option( 'bookly_co_address', $this->getParameter( 'bookly_co_address' ) );
116
+ update_option( 'bookly_co_logo_attachment_id', $this->getParameter( 'bookly_co_logo_attachment_id' ) );
117
+ update_option( 'bookly_co_name', $this->getParameter( 'bookly_co_name' ) );
118
+ update_option( 'bookly_co_phone', $this->getParameter( 'bookly_co_phone' ) );
119
+ update_option( 'bookly_co_website', $this->getParameter( 'bookly_co_website' ) );
120
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
121
+ break;
122
+ }
123
 
124
+ // Let Add-ons save their settings.
125
+ $alert = Lib\Proxy\Shared::saveSettings( $alert, $this->getParameter( 'tab' ), $this->getPostParameters() );
 
126
 
127
+ if ( in_array( $this->getParameter( 'tab' ), array( 'payments', 'business_hours' ) ) ) {
128
+ $form->bind( $this->getPostParameters(), $_FILES );
129
+ $form->save();
130
+
131
+ $alert['success'][] = __( 'Settings saved.', 'bookly' );
132
+ }
133
  }
134
  }
135
 
 
136
  $candidates = $this->getCandidatesBooklyProduct();
137
 
138
  // Check if WooCommerce cart exists.
155
  );
156
 
157
  wp_localize_script( 'bookly-jCal.js', 'BooklyL10n', array(
158
+ 'alert' => $alert,
159
+ 'current_tab' => $current_tab,
160
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
161
+ 'default_country' => get_option( 'bookly_cst_phone_default_country' ),
162
+ 'holidays' => $this->getHolidays(),
163
+ 'loading_img' => plugins_url( 'bookly-responsive-appointment-booking-tool/backend/resources/images/loading.gif' ),
164
+ 'start_of_week' => get_option( 'start_of_week' ),
165
+ 'days' => array_values( $wp_locale->weekday_abbrev ),
166
+ 'months' => array_values( $wp_locale->month ),
167
+ 'close' => __( 'Close', 'bookly' ),
168
+ 'repeat' => __( 'Repeat every year', 'bookly' ),
169
  'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
170
+ 'sample_price' => number_format_i18n( 10, 3 ),
171
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
172
  ) );
173
  $values = array(
174
+ 'bookly_gc_limit_events' => array( array( '0', __( 'Disabled', 'bookly' ) ), array( 25, 25 ), array( 50, 50 ), array( 100, 100 ), array( 250, 250 ), array( 500, 500 ), array( 1000, 1000 ), array( 2500, 2500 ) ),
175
  'bookly_gen_min_time_prior_booking' => array( array( '0', __( 'Disabled', 'bookly' ) ) ),
176
  'bookly_gen_min_time_prior_cancel' => array( array( '0', __( 'Disabled', 'bookly' ) ) ),
177
  );
182
  foreach ( array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) as $duration ) {
183
  $values['bookly_gen_time_slot_length'][] = array( $duration, Lib\Utils\DateTime::secondsToInterval( $duration * MINUTE_IN_SECONDS ) );
184
  }
185
+ foreach ( array_merge( array( 0.5 ), range( 1, 12 ), range( 24, 144, 24 ), range( 168, 672, 168 ) ) as $hour ) {
186
  $values['bookly_gen_min_time_prior_booking'][] = array( $hour, Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) );
187
  }
188
  foreach ( array_merge( array( 1 ), range( 2, 12, 2 ), range( 24, 168, 24 ) ) as $hour ) {
189
  $values['bookly_gen_min_time_prior_cancel'][] = array( $hour, Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) );
190
  }
191
 
192
+ $this->render( 'index', compact( 'candidates', 'cart_columns', 'values' ) );
193
  }
194
 
195
  /**
202
  $id = $this->getParameter( 'id', false );
203
  $day = $this->getParameter( 'day', false );
204
  $holiday = $this->getParameter( 'holiday' ) == 'true';
205
+ $repeat = (int) ( $this->getParameter( 'repeat' ) == 'true' );
206
 
207
  // update or delete the event
208
  if ( $id ) {
209
  if ( $holiday ) {
210
+ $wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => $repeat ), array( 'id' => $id ), array( '%d' ) );
211
+ $wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => $repeat ), array( 'parent_id' => $id ), array( '%d' ) );
212
  } else {
213
  Lib\Entities\Holiday::query()->delete()->where( 'id', $id )->where( 'parent_id', $id, 'OR' )->execute();
214
  }
215
  // add the new event
216
  } elseif ( $holiday && $day ) {
217
+ $holiday = new Lib\Entities\Holiday( );
218
+ $holiday
219
+ ->setDate( $day )
220
+ ->setRepeatEvent( $repeat )
221
+ ->save();
222
  foreach ( Lib\Entities\Staff::query()->fetchArray() as $employee ) {
223
+ $staff_holiday = new Lib\Entities\Holiday();
224
+ $staff_holiday
225
+ ->setDate( $day)
226
+ ->setRepeatEvent( $repeat )
227
+ ->setStaffId( $employee['id'] )
228
+ ->setParent( $holiday )
229
+ ->save();
230
  }
231
  }
232
 
233
  // and return refreshed events
234
+ echo json_encode( $this->getHolidays() );
235
  exit;
236
  }
237
 
238
+ /**
239
+ * Dismiss collect stats notice.
240
+ */
241
+ public function executeDismissCollectStatsNotice()
242
+ {
243
+ update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_collect_stats_notice', 1 );
244
+ update_option( 'bookly_gen_collect_stats', '0' );
245
+
246
+ wp_send_json_success();
247
+ }
248
+
249
+ public function executeAllowCollectStats()
250
+ {
251
+ update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_collect_stats_notice', 1 );
252
+ update_option( 'bookly_gen_collect_stats', '1' );
253
+
254
+ wp_send_json_success();
255
+ }
256
+
257
+
258
  /**
259
  * @return string
260
  */
275
  }
276
  }
277
 
278
+ return $holidays;
279
  }
280
 
281
+ /**
282
+ * @return array
283
+ */
284
  protected function getCandidatesBooklyProduct()
285
  {
286
+ /** @global \wpdb $wpdb */
287
+ global $wpdb;
288
+
289
+ $goods = array( array( 'id' => 0, 'name' => __( 'Select product', 'bookly' ) ) );
290
+ $query = 'SELECT ID, post_title FROM ' . $wpdb->posts . ' WHERE post_type = \'product\' AND post_status = \'publish\' ORDER BY post_title';
291
+ $products = $wpdb->get_results( $query );
292
+
293
+ foreach ( $products as $product ) {
294
+ $goods[] = array( 'id' => $product->ID, 'name' => $product->post_title );
295
  }
 
296
 
297
  return $goods;
298
  }
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
backend/modules/settings/forms/BusinessHours.php CHANGED
@@ -41,11 +41,11 @@ class BusinessHours extends Lib\Base\Form
41
  * @param bool $is_start
42
  * @return string
43
  */
44
- public function renderField( $field_name = 'ab_settings_monday', $is_start = true )
45
  {
46
  $ts_length = Lib\Config::getTimeSlotLength();
47
- $time_output = Lib\Entities\StaffScheduleItem::WORKING_START_TIME;
48
- $time_end = Lib\Entities\StaffScheduleItem::WORKING_END_TIME;
49
  $option_name = $field_name . ( $is_start ? '_start' : '_end' );
50
  $class_name = $is_start ? 'select_start' : 'select_end bookly-hide-on-off';
51
  $selected_value = get_option( $option_name );
@@ -62,7 +62,7 @@ class BusinessHours extends Lib\Base\Form
62
  if ( $selected_seconds == $time_output ) {
63
  $value_added = true;
64
  } elseif ( $selected_seconds < $time_output ) {
65
- $output .= "<option value='{$selected_value}' selected='selected'>{$selected_value}</option>";
66
  $value_added = true;
67
  }
68
  }
41
  * @param bool $is_start
42
  * @return string
43
  */
44
+ public function renderField( $field_name = 'bookly_bh_monday', $is_start = true )
45
  {
46
  $ts_length = Lib\Config::getTimeSlotLength();
47
+ $time_output = 0;
48
+ $time_end = DAY_IN_SECONDS;
49
  $option_name = $field_name . ( $is_start ? '_start' : '_end' );
50
  $class_name = $is_start ? 'select_start' : 'select_end bookly-hide-on-off';
51
  $selected_value = get_option( $option_name );
62
  if ( $selected_seconds == $time_output ) {
63
  $value_added = true;
64
  } elseif ( $selected_seconds < $time_output ) {
65
+ $output .= sprintf( '<option value="%s" selected="selected">%s</option>', $selected_value, Lib\Utils\DateTime::formatTime( $selected_value ) );
66
  $value_added = true;
67
  }
68
  }
backend/modules/settings/forms/Payments.php CHANGED
@@ -11,10 +11,20 @@ class Payments extends Lib\Base\Form
11
  {
12
  public function __construct()
13
  {
14
- $this->setFields( array(
 
 
 
 
15
  'bookly_pmt_currency',
 
16
  'bookly_pmt_local',
17
  ) );
 
 
 
 
 
18
  }
19
 
20
  public function save()
11
  {
12
  public function __construct()
13
  {
14
+ }
15
+
16
+ public function bind( array $_post, array $files = array() )
17
+ {
18
+ $fields = Lib\Proxy\Shared::preparePaymentOptions( array(
19
  'bookly_pmt_currency',
20
+ 'bookly_pmt_price_format',
21
  'bookly_pmt_local',
22
  ) );
23
+
24
+ $_post = Lib\Proxy\Shared::preparePaymentOptionsData( $_post );
25
+
26
+ $this->setFields( $fields );
27
+ parent::bind( $_post, $files );
28
  }
29
 
30
  public function save()
backend/modules/settings/resources/js/collect-stats-notice.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+ var $alert = $('#bookly-collect-stats-notice');
3
+
4
+ $alert
5
+ .on('click', '.bookly-js-disallow-stats', function () {
6
+ $.post(ajaxurl, {action: 'bookly_dismiss_collect_stats_notice', csrf_token: SupportL10n.csrf_token});
7
+ $alert.alert('close');
8
+ })
9
+ .on('click', '.bookly-js-allow-stats', function () {
10
+ $.post(ajaxurl, {action: 'bookly_allow_collect_stats', csrf_token: SupportL10n.csrf_token});
11
+ $alert.alert('close');
12
+ });
13
+ });
backend/modules/settings/resources/js/settings.js CHANGED
@@ -1,8 +1,9 @@
1
  jQuery(function ($) {
2
  var $form = $('#business-hours'),
3
- $final_step_url = $('input[name=bookly_gen_final_step_url]'),
4
  $final_step_url_mode = $('#bookly_settings_final_step_url_mode'),
5
- $help_btn = $('#bookly-help-btn')
 
6
  ;
7
 
8
  booklyAlert(BooklyL10n.alert);
@@ -25,6 +26,10 @@ jQuery(function ($) {
25
  $(this).prop('disabled',true);
26
  booklyAlert({error: [BooklyL10n.limitations]});
27
  });
 
 
 
 
28
  $('.select_start', $form).on('change', function () {
29
  var $flexbox = $(this).closest('.bookly-flexbox'),
30
  $end_select = $('.select_end', $flexbox),
@@ -78,6 +83,20 @@ jQuery(function ($) {
78
  $default_country_code.val($default_country.find('option:selected').data('code'));
79
  });
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  // Company Tab
82
  $('#bookly-company-reset').on('click', function () {
83
  var $div = $('#bookly-js-logo .bookly-js-image'),
@@ -93,73 +112,38 @@ jQuery(function ($) {
93
  });
94
 
95
  // Payment Tab
96
- $('#bookly_pmt_paypal').change(function () {
97
- if (this.value != 'disabled') {
98
- $(this).val('disabled');
99
- booklyAlert({error: [BooklyL10n.limitations]});
100
- $(this).find('option:gt(0)').prop('disabled', true);
101
- }
102
- $('.bookly-paypal').toggle(this.value != 'disabled');
103
- }).change();
104
-
105
- $('#bookly_pmt_authorize_net').change(function () {
106
- if (this.value != 'disabled') {
107
- $(this).val('disabled');
108
- booklyAlert({error: [BooklyL10n.limitations]});
109
- $(this).find('option:gt(0)').prop('disabled', true);
110
- }
111
- $('.authorize-net').toggle(this.value != 'disabled');
112
- }).change();
113
-
114
- $('#bookly_pmt_stripe').change(function () {
115
- if (this.value != 'disabled') {
116
- $(this).val('disabled');
117
- booklyAlert({error: [BooklyL10n.limitations]});
118
- $(this).find('option:gt(0)').prop('disabled', true);
119
- }
120
- $('.bookly-stripe').toggle(this.value == 1);
121
- }).change();
122
-
123
- $('#bookly_pmt_2checkout').change(function () {
124
- if (this.value != 'disabled') {
125
- $(this).val('disabled');
126
- booklyAlert({error: [BooklyL10n.limitations]});
127
- $(this).find('option:gt(0)').prop('disabled', true);
128
- }
129
- $('.bookly-2checkout').toggle(this.value != 'disabled');
130
- }).change();
131
-
132
- $('#bookly_pmt_payu_latam').change(function () {
133
- if (this.value != 'disabled') {
134
- $(this).val('disabled');
135
- booklyAlert({error: [BooklyL10n.limitations]});
136
- $(this).find('option:gt(0)').prop('disabled', true);
137
- }
138
- $('.bookly-payu_latam').toggle(this.value != 'disabled');
139
- }).change();
140
-
141
- $('#bookly_pmt_payson').change(function () {
142
- if (this.value != 'disabled') {
143
- $(this).val('disabled');
144
- booklyAlert({error: [BooklyL10n.limitations]});
145
- $(this).find('option:gt(0)').prop('disabled', true);
146
- }
147
- $('.bookly-payson').toggle(this.value != 'disabled');
148
- }).change();
149
 
150
- $('#bookly_pmt_mollie').change(function () {
151
- if (this.value != 'disabled') {
152
- $(this).val('disabled');
 
153
  booklyAlert({error: [BooklyL10n.limitations]});
154
  $(this).find('option:gt(0)').prop('disabled', true);
155
  }
156
- $('.bookly-mollie').toggle(this.value != 'disabled');
157
  }).change();
158
 
159
  $('#bookly-payments-reset').on('click', function (event) {
160
  setTimeout(function () {
161
- $('#bookly_pmt_paypal,#bookly_pmt_authorize_net,#bookly_pmt_stripe,#bookly_pmt_2checkout,#bookly_pmt_payu_latam,#bookly_pmt_payson,#bookly_pmt_mollie').change();
162
- }, 50);
163
  });
164
 
165
  $('#bookly-customer-reset').on('click', function (event) {
@@ -179,6 +163,32 @@ jQuery(function ($) {
179
  $('.bookly-nav li[data-toggle="tab"]').on('shown.bs.tab', function(e) {
180
  $help_btn.attr('href', help_link + e.target.getAttribute('data-target').substring(1).replace(/_/g, '-'));
181
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  // Activate tab.
183
  $('li[data-target="#bookly_settings_' + BooklyL10n.current_tab + '"]').tab('show');
184
 
1
  jQuery(function ($) {
2
  var $form = $('#business-hours'),
3
+ $final_step_url = $('input[name=bookly_url_final_step_url]'),
4
  $final_step_url_mode = $('#bookly_settings_final_step_url_mode'),
5
+ $help_btn = $('#bookly-help-btn'),
6
+ $participants = $('#bookly_appointment_participants')
7
  ;
8
 
9
  booklyAlert(BooklyL10n.alert);
26
  $(this).prop('disabled',true);
27
  booklyAlert({error: [BooklyL10n.limitations]});
28
  });
29
+ $('#bookly_wc_product,#bookly_l10n_wc_cart_info_name,[name=bookly_l10n_wc_cart_info_value]').on('focus', function () {
30
+ $(this).prop('disabled',true);
31
+ booklyAlert({error: [BooklyL10n.limitations]});
32
+ });
33
  $('.select_start', $form).on('change', function () {
34
  var $flexbox = $(this).closest('.bookly-flexbox'),
35
  $end_select = $('.select_end', $flexbox),
83
  $default_country_code.val($default_country.find('option:selected').data('code'));
84
  });
85
 
86
+ // Calendar tab
87
+ $participants.on('change', function () {
88
+
89
+ $('#bookly_cal_one_participant').hide();
90
+ $('#bookly_cal_many_participants').hide();
91
+ $('#' + $(this).val() ).show();
92
+ }).trigger('change');
93
+
94
+ $("#bookly_settings_calendar button[type=reset]").on( 'click', function () {
95
+ setTimeout(function () {
96
+ $participants.trigger('change');
97
+ }, 50 );
98
+ });
99
+
100
  // Company Tab
101
  $('#bookly-company-reset').on('click', function () {
102
  var $div = $('#bookly-js-logo .bookly-js-image'),
112
  });
113
 
114
  // Payment Tab
115
+ var $currency = $('#bookly_pmt_currency'),
116
+ $formats = $('#bookly_pmt_price_format')
117
+ ;
118
+ $currency.on('change', function () {
119
+ $formats.find('option').each(function () {
120
+ var decimals = this.value.match(/{price\|(\d)}/)[1],
121
+ price = BooklyL10n.sample_price
122
+ ;
123
+ if (decimals < 3) {
124
+ price = price.slice(0, -(decimals == 0 ? 4 : 3 - decimals));
125
+ }
126
+ this.innerHTML = this.value
127
+ .replace('{symbol}', $currency.find('option:selected').data('symbol'))
128
+ .replace(/{price\|\d}/, price)
129
+ ;
130
+ });
131
+ }).trigger('change');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+ // Payment Tab
134
+ $('#bookly_paypal_enabled').change(function () {
135
+ if (this.value != '0') {
136
+ $(this).val('0');
137
  booklyAlert({error: [BooklyL10n.limitations]});
138
  $(this).find('option:gt(0)').prop('disabled', true);
139
  }
140
+ $('.bookly-paypal').toggle(this.value != '0');
141
  }).change();
142
 
143
  $('#bookly-payments-reset').on('click', function (event) {
144
  setTimeout(function () {
145
+ $('#bookly_pmt_currency,#bookly_paypal_enabled,#bookly_authorize_net_enabled,#bookly_stripe_enabled,#bookly_2checkout_enabled,#bookly_payu_latam_enabled,#bookly_payson_enabled,#bookly_mollie_enabled,#bookly_payu_latam_sandbox').change();
146
+ }, 0);
147
  });
148
 
149
  $('#bookly-customer-reset').on('click', function (event) {
163
  $('.bookly-nav li[data-toggle="tab"]').on('shown.bs.tab', function(e) {
164
  $help_btn.attr('href', help_link + e.target.getAttribute('data-target').substring(1).replace(/_/g, '-'));
165
  });
166
+
167
+ // Holidays
168
+ var d = new Date();
169
+ $('.bookly-js-annual-calendar').jCal({
170
+ day: new Date(d.getFullYear(), 0, 1),
171
+ days: 1,
172
+ showMonths: 12,
173
+ scrollSpeed: 350,
174
+ events: BooklyL10n.holidays,
175
+ action: 'bookly_settings_holiday',
176
+ csrf_token: BooklyL10n.csrf_token,
177
+ dayOffset: parseInt(BooklyL10n.start_of_week),
178
+ loadingImg: BooklyL10n.loading_img,
179
+ dow: BooklyL10n.days,
180
+ ml: BooklyL10n.months,
181
+ we_are_not_working: BooklyL10n.we_are_not_working,
182
+ repeat: BooklyL10n.repeat,
183
+ close: BooklyL10n.close
184
+ });
185
+
186
+ $('.bookly-js-jCalBtn').on('click', function (e) {
187
+ e.preventDefault();
188
+ var trigger = $(this).data('trigger');
189
+ $('.bookly-js-annual-calendar').find($(trigger)).trigger('click');
190
+ });
191
+
192
  // Activate tab.
193
  $('li[data-target="#bookly_settings_' + BooklyL10n.current_tab + '"]').tab('show');
194
 
backend/modules/settings/templates/_calendarForm.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
+ <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'calendar' ) ) ?>">
3
+ <div class="form-group"><label for="bookly_appointment_participants"><?php _e( 'Calendar', 'bookly' ) ?></label>
4
+ <p class="help-block"><?php _e( 'Set order of the fields in calendar for', 'bookly' ) ?></p>
5
+ </div>
6
+ <input id="bookly_appointment_participants" type="hidden" name="bookly_appointment_participants" value="bookly_cal_one_participant">
7
+ <div class="form-group" id="bookly_cal_one_participant">
8
+ <textarea class="form-control" rows="9" name="bookly_cal_one_participant" placeholder="<?php _e( 'Enter a value', 'bookly' ) ?>"><?php echo esc_textarea( get_option( 'bookly_cal_one_participant' ) ) ?></textarea><br/>
9
+ <?php $this->render( '_calendar_codes', array( 'participants' => 'one' ) ) ?>
10
+ </div>
11
+ <div class="form-group" id="bookly_cal_many_participants">
12
+ <textarea class="form-control" rows="9" name="bookly_cal_many_participants" placeholder="<?php _e( 'Enter a value', 'bookly' ) ?>"><?php echo esc_textarea( get_option( 'bookly_cal_many_participants' ) ) ?></textarea><br/>
13
+ <?php $this->render( '_calendar_codes', array( 'participants' => 'many' ) ) ?>
14
+ </div>
15
+
16
+ <div class="panel-footer">
17
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
18
+ <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
19
+ <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
20
+ </div>
21
+ </form>
backend/modules/settings/templates/_calendar_codes.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ $codes = array(
3
+ array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) ),
4
+ array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) ),
5
+ array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) ),
6
+ array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
7
+ array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
8
+ array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
9
+ array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
10
+ array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
11
+ array( 'code' => 'service_capacity', 'description' => __( 'capacity of service', 'bookly' ) ),
12
+ array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
13
+ array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
14
+ array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
15
+ array( 'code' => 'signed_up', 'description' => __( 'number of persons already in the list', 'bookly' ) ),
16
+ array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) ),
17
+ array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
18
+ array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
19
+ array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) ),
20
+ );
21
+ if ( $participants == 'one' ) {
22
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
23
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
24
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
25
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
26
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
27
+ $codes[] = array( 'code' => 'payment_status', 'description' => __( 'status of payment', 'bookly' ) );
28
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
29
+ $codes[] = array( 'code' => 'status', 'description' => __( 'status of appointment', 'bookly' ) );
30
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
31
+ }
32
+
33
+ $codes = BooklyLite\Lib\Proxy\Shared::prepareCalendarAppointmentCodes( $codes, $participants );
34
+
35
+ BooklyLite\Lib\Utils\Common::codes( $codes );
backend/modules/settings/templates/_cartForm.php CHANGED
@@ -1,11 +1,12 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'cart' ) ) ?>">
3
  <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cart_enabled', __( 'Cart', 'bookly' ), __( 'If cart is enabled then your clients will be able to book several appointments at once. Please note that WooCommerce integration must be disabled.', 'bookly' ) ) ?>
 
4
  <div class="form-group">
5
  <label for="bookly_cart_show_columns"><?php _e( 'Columns', 'bookly' ) ?></label><br/>
6
- <div class="ab-flags" id="bookly_cart_show_columns">
7
  <?php foreach ( (array) get_option( 'bookly_cart_show_columns' ) as $column => $attr ) : ?>
8
- <div class="bookly-flexbox"<?php if ( $column == 'deposit' && ! \BooklyLite\Lib\Config::isDepositPaymentsEnabled() ) : ?> style="display:none"<?php endif ?>>
9
  <div class="bookly-flex-cell">
10
  <i class="bookly-js-handle bookly-margin-right-sm bookly-icon bookly-icon-draghandle bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
11
  </div>
@@ -25,7 +26,8 @@
25
  </div>
26
  </div>
27
  <div class="panel-footer">
28
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
 
29
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
30
  </div>
31
  </form>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'cart' ) ) ?>">
3
  <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cart_enabled', __( 'Cart', 'bookly' ), __( 'If cart is enabled then your clients will be able to book several appointments at once. Please note that WooCommerce integration must be disabled.', 'bookly' ) ) ?>
4
+ <?php \BooklyLite\Lib\Proxy\Shared::renderCartSettings() ?>
5
  <div class="form-group">
6
  <label for="bookly_cart_show_columns"><?php _e( 'Columns', 'bookly' ) ?></label><br/>
7
+ <div class="bookly-flags" id="bookly_cart_show_columns">
8
  <?php foreach ( (array) get_option( 'bookly_cart_show_columns' ) as $column => $attr ) : ?>
9
+ <div class="bookly-flexbox"<?php if ( $column == 'deposit' && ! \BooklyLite\Lib\Config::depositPaymentsEnabled() ) : ?> style="display:none"<?php endif ?>>
10
  <div class="bookly-flex-cell">
11
  <i class="bookly-js-handle bookly-margin-right-sm bookly-icon bookly-icon-draghandle bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
12
  </div>
26
  </div>
27
  </div>
28
  <div class="panel-footer">
29
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
30
+ <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn btn-lg btn-success bookly-limitation' ) ?>
31
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
32
  </div>
33
  </form>
backend/modules/settings/templates/_collect_stats_notice.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template to show notice about "we'r starting to collect statistics about usage"
4
+ */
5
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
6
+ use BooklyLite\Lib\Utils\Common;
7
+ ?>
8
+ <div id="bookly-tbs" class="wrap">
9
+ <div id="bookly-collect-stats-notice" class="alert alert-info bookly-tbs-body bookly-flexbox">
10
+ <div class="bookly-flex-row">
11
+ <div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
12
+ <div class="bookly-flex-cell">
13
+ <button type="button" class="close bookly-js-disallow-stats" data-dismiss="alert"></button>
14
+ <?php _e( 'Dear customer,', 'bookly' ) ?>
15
+ <br>
16
+ <?php _e( 'Bookly needs your permission to collect anonymous plugin usage stats so we could constantly improve the plugin. You can always change permissions in Bookly settings.', 'bookly' ); ?>
17
+ <br>
18
+ <br>
19
+ <?php Common::customButton( null, 'btn-success bookly-js-allow-stats', __( 'Allow (OK)', 'bookly' ) ) ?>
20
+ <?php Common::customButton( null, 'btn-default bookly-js-disallow-stats', __( 'Don’t allow', 'bookly' ) ) ?>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
backend/modules/settings/templates/_companyForm.php CHANGED
@@ -47,6 +47,7 @@
47
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_co_website', __( 'Website', 'bookly' ) ) ?>
48
 
49
  <div class="panel-footer">
 
50
  <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
51
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-company-reset' ) ?>
52
  </div>
47
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_co_website', __( 'Website', 'bookly' ) ) ?>
48
 
49
  <div class="panel-footer">
50
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
51
  <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
52
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-company-reset' ) ?>
53
  </div>
backend/modules/settings/templates/_customers.php CHANGED
@@ -1,17 +1,22 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
2
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'customers' ) ) ?>">
3
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cst_create_account', __( 'Create WordPress user account for customers', 'bookly' ), __( 'If this setting is enabled then Bookly will be creating WordPress user accounts for all new customers. If the user is logged in then the new customer will be associated with the existing user account.', 'bookly' ) );
4
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cst_new_account_role', __( 'New user account role', 'bookly' ), __( 'Select what role will be assigned to newly created WordPress user accounts for customers.', 'bookly' ),
5
  $values['bookly_cst_new_account_role'] );
6
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cst_phone_default_country', __( 'Phone field default country', 'bookly' ), __( 'Select default country for the phone field in the \'Details\' step of booking. You can also let Bookly determine the country based on the IP address of the client.', 'bookly' ),
7
  array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( 'auto', __( 'Guess country by user\'s IP address', 'bookly' ) ) ) );
8
- \BooklyLite\Lib\Utils\Common::optionText( 'bookly_cst_default_country_code', __( 'Default country code', 'bookly' ), __( 'Your clients must have their phone numbers in international format in order to receive text messages. However you can specify a default country code that will be used as a prefix for all phone numbers that do not start with "+" or "00". E.g. if you enter "1" as the default country code and a client enters their phone as "(600) 555-2222" the resulting phone number to send the SMS to will be "+1600555222".', 'bookly' ) );
9
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cst_cancel_action', __( 'Cancel appointment action', 'bookly' ), __( 'Select what happens when customer clicks cancel appointment link. With "Delete" the appointment will be deleted from the calendar. With "Cancel" only appointment status will be changed to "Cancelled".', 'bookly' ),
10
  array( array( 'delete', __( 'Delete', 'bookly' ) ), array( 'cancel', __( 'Cancel', 'bookly' ) ) ) );
11
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_cst_combined_notifications', __( 'Combined notifications', 'bookly' ), __( 'If combined notifications are enabled then your clients will receive single notification for entire booking instead of separate notification per each booked appointment (e.g. when cart is enabled). You will need to edit corresponding templates in Email and SMS Notifications.', 'bookly' ) )
 
 
12
  ?>
13
  <div class="panel-footer">
14
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
15
- <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-customer-reset' ) ?>
 
16
  </div>
17
  </form>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Common;
3
+ ?>
4
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'customers' ) ) ?>">
5
+ <?php Common::optionToggle( 'bookly_cst_create_account', __( 'Create WordPress user account for customers', 'bookly' ), __( 'If this setting is enabled then Bookly will be creating WordPress user accounts for all new customers. If the user is logged in then the new customer will be associated with the existing user account.', 'bookly' ) );
6
+ Common::optionToggle( 'bookly_cst_new_account_role', __( 'New user account role', 'bookly' ), __( 'Select what role will be assigned to newly created WordPress user accounts for customers.', 'bookly' ),
7
  $values['bookly_cst_new_account_role'] );
8
+ Common::optionToggle( 'bookly_cst_phone_default_country', __( 'Phone field default country', 'bookly' ), __( 'Select default country for the phone field in the \'Details\' step of booking. You can also let Bookly determine the country based on the IP address of the client.', 'bookly' ),
9
  array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( 'auto', __( 'Guess country by user\'s IP address', 'bookly' ) ) ) );
10
+ Common::optionText( 'bookly_cst_default_country_code', __( 'Default country code', 'bookly' ), __( 'Your clients must have their phone numbers in international format in order to receive text messages. However you can specify a default country code that will be used as a prefix for all phone numbers that do not start with "+" or "00". E.g. if you enter "1" as the default country code and a client enters their phone as "(600) 555-2222" the resulting phone number to send the SMS to will be "+1600555222".', 'bookly' ) );
11
+ Common::optionToggle( 'bookly_cst_cancel_action', __( 'Cancel appointment action', 'bookly' ), __( 'Select what happens when customer clicks cancel appointment link. With "Delete" the appointment will be deleted from the calendar. With "Cancel" only appointment status will be changed to "Cancelled".', 'bookly' ),
12
  array( array( 'delete', __( 'Delete', 'bookly' ) ), array( 'cancel', __( 'Cancel', 'bookly' ) ) ) );
13
+ Common::optionToggle( 'bookly_cst_combined_notifications', __( 'Combined notifications', 'bookly' ), __( 'If combined notifications are enabled then your clients will receive single notification for entire booking instead of separate notification per each booked appointment (e.g. when cart is enabled). You will need to edit corresponding templates in Email and SMS Notifications.', 'bookly' ) );
14
+ Common::optionToggle( 'bookly_cst_remember_in_cookie', __( 'Remember personal information in cookies', 'bookly' ), __( 'If this setting is enabled then returning customers will have their personal information fields filled in at the Details step with the data previously saved in cookies.', 'bookly' ) );
15
+ Common::optionToggle( 'bookly_cst_show_update_details_dialog', __( 'Show confirmation dialog before updating customer\'s data', 'bookly' ), __( 'If this option is enabled and customer enters contact info different from the previous order, a warning message will appear asking to update the data.', 'bookly' ) );
16
  ?>
17
  <div class="panel-footer">
18
+ <?php Common::csrf() ?>
19
+ <?php Common::submitButton() ?>
20
+ <?php Common::resetButton( 'bookly-customer-reset' ) ?>
21
  </div>
22
  </form>
backend/modules/settings/templates/_generalForm.php CHANGED
@@ -1,42 +1,36 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
 
2
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'general' ) ) ?>">
3
  <?php
4
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_lite_uninstall_remove_bookly_data', __( 'Delete all data on uninstall', 'bookly' ), __( 'If you want to replace Bookly Lite with full version of Bookly then disable this setting to prevent data from being deleted when you uninstall Bookly Lite.', 'bookly' ) );
5
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_time_slot_length', __( 'Time slot length', 'bookly' ), __( 'Select a time interval which will be used as a step when building all time slots in the system.', 'bookly' ),
6
  $values['bookly_gen_time_slot_length'] );
7
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_service_duration_as_slot_length', __( 'Service duration as slot length', 'bookly' ), __( 'Enable this option to make slot length equal to service duration at the Time step of booking form.', 'bookly' ) );
8
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_default_appointment_status', __( 'Default appointment status', 'bookly' ), __( 'Select status for newly booked appointments.', 'bookly' ),
9
- array( array( \BooklyLite\Lib\Entities\CustomerAppointment::STATUS_PENDING, __( 'Pending', 'bookly' ) ), array( \BooklyLite\Lib\Entities\CustomerAppointment::STATUS_APPROVED, __( 'Approved', 'bookly' ) ), ) );
10
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_min_time_prior_booking', __( 'Minimum time requirement prior to booking', 'bookly' ), __( 'Set how late appointments can be booked (for example, require customers to book at least 1 hour before the appointment time).', 'bookly' ),
11
  $values['bookly_gen_min_time_prior_booking'] );
12
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_min_time_prior_cancel', __( 'Minimum time requirement prior to canceling', 'bookly' ), __( 'Set how late appointments can be cancelled (for example, require customers to cancel at least 1 hour before the appointment time).', 'bookly' ),
13
  $values['bookly_gen_min_time_prior_cancel'] );
14
- \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gen_approve_page_url', __( 'Set the URL of a page that is shown to staff after they approve their appointment.', 'bookly' ) );
15
- \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gen_cancel_page_url', __( 'Set the URL of a page that is shown to clients after they successfully cancelled their appointment.', 'bookly' ) );
16
- \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gen_cancel_denied_page_url', __( 'Cancel appointment URL (denied)', 'bookly' ), __( 'Set the URL of a page that is shown to clients when the cancellation of appointment is not available anymore.', 'bookly' ) );
17
- \BooklyLite\Lib\Utils\Common::optionNumeric( 'bookly_gen_max_days_for_booking', __( 'Number of days available for booking', 'bookly' ), __( 'Set how far in the future the clients can book appointments.', 'bookly' ), 1, 1 );
18
- \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_use_client_time_zone', __( 'Display available time slots in client\'s time zone', 'bookly' ), __( 'The value is taken from client’s browser.', 'bookly' ) )
19
  ?>
20
  <div class="form-group">
21
- <label for="bookly_settings_final_step_url_mode"><?php _e( 'Final step URL', 'bookly' ) ?></label>
22
- <p class="help-block"><?php _e( 'Set the URL of a page that the user will be forwarded to after successful booking. If disabled then the default Done step is displayed.', 'bookly' ) ?></p>
23
- <select class="form-control" id="bookly_settings_final_step_url_mode">
24
  <?php foreach ( array( __( 'Disabled', 'bookly' ) => 0, __( 'Enabled', 'bookly' ) => 1 ) as $text => $mode ) : ?>
25
- <option value="<?php echo esc_attr( $mode ) ?>" <?php selected( get_option( 'bookly_gen_final_step_url' ), $mode ) ?> ><?php echo $text ?></option>
26
  <?php endforeach ?>
27
  </select>
28
- <input class="form-control"
29
- style="margin-top: 5px; <?php echo get_option( 'bookly_gen_final_step_url' ) == '' ? 'display: none' : '' ?>"
30
- type="text" name="bookly_gen_final_step_url"
31
- value="<?php form_option( 'bookly_gen_final_step_url' ) ?>"
32
- placeholder="<?php esc_attr_e( 'Enter a URL', 'bookly' ) ?>"/>
33
  </div>
34
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_allow_staff_edit_profile', __( 'Allow staff members to edit their profiles', 'bookly' ), __( 'If this option is enabled then all staff members who are associated with WordPress users will be able to edit their own profiles, services, schedule and days off.', 'bookly' ) ) ?>
35
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gen_link_assets_method', __( 'Method to include Bookly JavaScript and CSS files on the page', 'bookly' ), __( 'With "Enqueue" method the JavaScript and CSS files of Bookly will be included on all pages of your website. This method should work with all themes. With "Print" method the files will be included only on the pages which contain Bookly booking form. This method may not work with all themes.', 'bookly' ),
36
- array( array( 'enqueue', 'Enqueue' ), array( 'print', 'Print' ) ) )
37
- ?>
38
  <div class="panel-footer">
39
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
40
- <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
 
41
  </div>
42
  </form>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Plugin;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ use BooklyLite\Lib\Entities\CustomerAppointment;
5
+ ?>
6
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'general' ) ) ?>">
7
  <?php
8
+ Common::optionToggle( 'bookly_gen_lite_uninstall_remove_bookly_data', __( 'Delete all data on uninstall', 'bookly' ), __( 'If you want to replace Bookly Lite with full version of Bookly then disable this setting to prevent data from being deleted when you uninstall Bookly Lite.', 'bookly' ) );
9
+ Common::optionToggle( 'bookly_gen_time_slot_length', __( 'Time slot length', 'bookly' ), __( 'Select a time interval which will be used as a step when building all time slots in the system.', 'bookly' ),
10
  $values['bookly_gen_time_slot_length'] );
11
+ Common::optionToggle( 'bookly_gen_service_duration_as_slot_length', __( 'Service duration as slot length', 'bookly' ), __( 'Enable this option to make slot length equal to service duration at the Time step of booking form.', 'bookly' ) );
12
+ Common::optionToggle( 'bookly_gen_default_appointment_status', __( 'Default appointment status', 'bookly' ), __( 'Select status for newly booked appointments.', 'bookly' ), array( array( CustomerAppointment::STATUS_PENDING, __( 'Pending', 'bookly' ) ), array( CustomerAppointment::STATUS_APPROVED, __( 'Approved', 'bookly' ) ), ) );
13
+ Common::optionToggle( 'bookly_gen_min_time_prior_booking', __( 'Minimum time requirement prior to booking', 'bookly' ), __( 'Set how late appointments can be booked (for example, require customers to book at least 1 hour before the appointment time).', 'bookly' ),
 
14
  $values['bookly_gen_min_time_prior_booking'] );
15
+ Common::optionToggle( 'bookly_gen_min_time_prior_cancel', __( 'Minimum time requirement prior to canceling', 'bookly' ), __( 'Set how late appointments can be cancelled (for example, require customers to cancel at least 1 hour before the appointment time).', 'bookly' ),
16
  $values['bookly_gen_min_time_prior_cancel'] );
17
+ Common::optionNumeric( 'bookly_gen_max_days_for_booking', __( 'Number of days available for booking', 'bookly' ), __( 'Set how far in the future the clients can book appointments.', 'bookly' ), 1, 1 );
18
+ Common::optionToggle( 'bookly_gen_use_client_time_zone', __( 'Display available time slots in client\'s time zone', 'bookly' ), __( 'The value is taken from client’s browser.', 'bookly' ) );
19
+ Common::optionToggle( 'bookly_gen_allow_staff_edit_profile', __( 'Allow staff members to edit their profiles', 'bookly' ), __( 'If this option is enabled then all staff members who are associated with WordPress users will be able to edit their own profiles, services, schedule and days off.', 'bookly' ) );
20
+ Common::optionToggle( 'bookly_gen_link_assets_method', __( 'Method to include Bookly JavaScript and CSS files on the page', 'bookly' ), __( 'With "Enqueue" method the JavaScript and CSS files of Bookly will be included on all pages of your website. This method should work with all themes. With "Print" method the files will be included only on the pages which contain Bookly booking form. This method may not work with all themes.', 'bookly' ),
21
+ array( array( 'enqueue', 'Enqueue' ), array( 'print', 'Print' ) ) )
22
  ?>
23
  <div class="form-group">
24
+ <label for="bookly_gen_collect_stats"><?php _e( 'Help us improve Bookly by sending anonymous usage stats', 'bookly' ); ?></label>
25
+ <select class="form-control" name="bookly_gen_collect_stats" id="bookly_gen_collect_stats">
 
26
  <?php foreach ( array( __( 'Disabled', 'bookly' ) => 0, __( 'Enabled', 'bookly' ) => 1 ) as $text => $mode ) : ?>
27
+ <option value="<?php echo esc_attr( $mode ) ?>" <?php selected( get_option( 'bookly_gen_collect_stats' ), $mode ) ?> ><?php echo $text ?></option>
28
  <?php endforeach ?>
29
  </select>
 
 
 
 
 
30
  </div>
 
 
 
 
31
  <div class="panel-footer">
32
+ <?php Common::csrf() ?>
33
+ <?php Common::submitButton() ?>
34
+ <?php Common::resetButton() ?>
35
  </div>
36
  </form>
backend/modules/settings/templates/_googleCalendarForm.php CHANGED
@@ -18,9 +18,9 @@
18
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_client_id', __( 'Client ID', 'bookly' ), __( 'The client ID obtained from the Developers Console', 'bookly' ) ) ?>
19
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_client_secret', __( 'Client secret', 'bookly' ), __( 'The client secret obtained from the Developers Console', 'bookly' ) ) ?>
20
  <div class="form-group">
21
- <label for="ab_redirect_uri"><?php _e( 'Redirect URI', 'bookly' ) ?></label>
22
  <p class="help-block"><?php _e( 'Enter this URL as a redirect URI in the Developers Console', 'bookly' ) ?></p>
23
- <input id="ab_redirect_uri" class="form-control" type="text" readonly
24
  value="<?php echo \BooklyLite\Lib\Google::generateRedirectURI() ?>" onclick="this.select();"
25
  style="cursor: pointer;"/>
26
  </div>
@@ -29,6 +29,7 @@
29
  $values['bookly_gc_limit_events'] ) ?>
30
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_event_title', __( 'Template for event title', 'bookly' ), __( 'Configure what information should be placed in the title of Google Calendar event. Available codes are {service_name}, {staff_name} and {client_names}.', 'bookly' ) ) ?>
31
  <div class="panel-footer">
 
32
  <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn btn-lg btn-success bookly-limitation' ) ?>
33
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
34
  </div>
18
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_client_id', __( 'Client ID', 'bookly' ), __( 'The client ID obtained from the Developers Console', 'bookly' ) ) ?>
19
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_client_secret', __( 'Client secret', 'bookly' ), __( 'The client secret obtained from the Developers Console', 'bookly' ) ) ?>
20
  <div class="form-group">
21
+ <label for="bookly-redirect-uri"><?php _e( 'Redirect URI', 'bookly' ) ?></label>
22
  <p class="help-block"><?php _e( 'Enter this URL as a redirect URI in the Developers Console', 'bookly' ) ?></p>
23
+ <input id="bookly-redirect-uri" class="form-control" type="text" readonly
24
  value="<?php echo \BooklyLite\Lib\Google::generateRedirectURI() ?>" onclick="this.select();"
25
  style="cursor: pointer;"/>
26
  </div>
29
  $values['bookly_gc_limit_events'] ) ?>
30
  <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_gc_event_title', __( 'Template for event title', 'bookly' ), __( 'Configure what information should be placed in the title of Google Calendar event. Available codes are {service_name}, {staff_name} and {client_names}.', 'bookly' ) ) ?>
31
  <div class="panel-footer">
32
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
33
  <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn btn-lg btn-success bookly-limitation' ) ?>
34
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
35
  </div>
backend/modules/settings/templates/_holidaysForm.php CHANGED
@@ -16,26 +16,4 @@
16
  </div>
17
  </div>
18
 
19
- <div class="bookly-js-annual-calendar bookly-margin-top-lg jCal-wrap"></div>
20
-
21
- <script>
22
- jQuery(function ($) {
23
- var d = new Date();
24
- $('.bookly-js-annual-calendar').jCal({
25
- day : new Date(d.getFullYear(), 0, 1),
26
- days : 1,
27
- showMonths : 12,
28
- scrollSpeed: 350,
29
- events : <?php echo $holidays ?>,
30
- action : 'bookly_settings_holiday',
31
- dayOffset : <?php echo (int)get_option('start_of_week') ?>,
32
- loadingImg : <?php echo json_encode( plugins_url( 'bookly-responsive-appointment-booking-tool/backend/resources/images/loading.gif' ) ) ?>
33
- });
34
-
35
- $('.bookly-js-jCalBtn').on('click', function(e) {
36
- e.preventDefault();
37
- var trigger = $(this).data('trigger');
38
- $('.bookly-js-annual-calendar').find($(trigger)).trigger('click');
39
- })
40
- });
41
- </script>
16
  </div>
17
  </div>
18
 
19
+ <div class="bookly-js-annual-calendar bookly-margin-top-lg jCal-wrap"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/settings/templates/_hoursForm.php CHANGED
@@ -28,6 +28,7 @@
28
  <?php endfor ?>
29
 
30
  <div class="panel-footer">
 
31
  <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
32
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-hours-reset' ) ?>
33
  </div>
28
  <?php endfor ?>
29
 
30
  <div class="panel-footer">
31
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
32
  <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
33
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-hours-reset' ) ?>
34
  </div>
backend/modules/settings/templates/_paymentsForm.php CHANGED
@@ -1,19 +1,31 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
 
 
2
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'payments' ) ) ?>">
3
  <div class="row">
4
- <div class="col-lg-6">
5
  <div class="form-group">
6
  <label for="bookly_pmt_currency"><?php _e( 'Currency', 'bookly' ) ?></label>
7
  <select id="bookly_pmt_currency" class="form-control" name="bookly_pmt_currency">
8
- <?php foreach ( \BooklyLite\Lib\Config::getCurrencyCodes() as $code ) : ?>
9
- <option value="<?php echo $code ?>" <?php selected( get_option( 'bookly_pmt_currency' ), $code ) ?> ><?php echo $code ?></option>
10
  <?php endforeach ?>
11
  </select>
12
  </div>
13
  </div>
14
- <div class="col-lg-6">
15
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_coupons', __( 'Coupons', 'bookly' ) ) ?>
 
 
 
 
 
 
 
16
  </div>
 
17
  </div>
18
 
19
  <div class="panel panel-default">
@@ -21,148 +33,41 @@
21
  <label for="bookly_pmt_local"><?php _e( 'Service paid locally', 'bookly' ) ?></label>
22
  </div>
23
  <div class="panel-body">
24
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_local', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( '1', __( 'Enabled', 'bookly' ) ) ) ) ?>
25
- </div>
26
- </div>
27
-
28
- <div class="panel panel-default">
29
- <div class="panel-heading">
30
- <label for="bookly_pmt_2checkout">2Checkout</label>
31
- <img style="margin-left: 10px; float: right" src="<?php echo plugins_url( 'frontend/resources/images/2Checkout.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>"/>
32
- </div>
33
- <div class="panel-body">
34
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_2checkout', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( 'standard_checkout', __( '2Checkout Standard Checkout', 'bookly' ) ) ) ) ?>
35
- <div class="bookly-2checkout">
36
- <div class="form-group">
37
- <h4><?php _e( 'Instructions', 'bookly' ) ?></h4>
38
- <p>
39
- <?php _e( 'In <b>Checkout Options</b> of your 2Checkout account do the following steps:', 'bookly' ) ?>
40
- </p>
41
- <ol>
42
- <li><?php _e( 'In <b>Direct Return</b> select <b>Header Redirect (Your URL)</b>.', 'bookly' ) ?></li>
43
- <li><?php _e( 'In <b>Approved URL</b> enter the URL of your booking page.', 'bookly' ) ?></li>
44
- </ol>
45
- <p>
46
- <?php _e( 'Finally provide the necessary information in the form below.', 'bookly' ) ?>
47
- </p>
48
- </div>
49
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_2checkout_api_seller_id', __( 'Account Number', 'bookly' ) ) ?>
50
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_2checkout_api_secret_word', __( 'Secret Word', 'bookly' ) ) ?>
51
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_2checkout_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 0, __( 'No', 'bookly' ) ), array( 1, __( 'Yes', 'bookly' ) ) ) ) ?>
52
- </div>
53
  </div>
54
  </div>
55
 
56
  <div class="panel panel-default">
57
  <div class="panel-heading">
58
- <label for="bookly_pmt_paypal">PayPal</label>
59
  <img style="margin-left: 10px; float: right" src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" />
60
  </div>
61
  <div class="panel-body">
62
  <div class="form-group">
63
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_paypal', null, null,
64
  array(
65
- array( 'disabled', __( 'Disabled', 'bookly' ) ),
66
  array( 'ec', 'PayPal Express Checkout' ),
67
- ) ) ?>
 
68
  </div>
69
  <div class="bookly-paypal">
70
  <div class="bookly-paypal-ec">
71
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_paypal_api_username', __( 'API Username', 'bookly' ) ) ?>
72
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_paypal_api_password', __( 'API Password', 'bookly' ) ) ?>
73
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_paypal_api_signature', __( 'API Signature', 'bookly' ) ) ?>
74
- </div>
75
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_paypal_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 1, __( 'Yes', 'bookly' ) ), array( 0, __( 'No', 'bookly' ) ) ) ) ?>
76
- </div>
77
- </div>
78
- </div>
79
-
80
- <div class="panel panel-default">
81
- <div class="panel-heading">
82
- <label for="bookly_pmt_authorize_net">Authorize.Net</label>
83
- <img style="margin-left: 10px; float: right" src="<?php echo plugins_url( 'frontend/resources/images/authorize_net.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>"/>
84
- </div>
85
- <div class="panel-body">
86
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_authorize_net', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( 'aim', 'Authorize.Net AIM' ) ) ) ?>
87
- <div class="authorize-net">
88
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_authorize_net_api_login_id', __( 'API Login ID', 'bookly' ) ) ?>
89
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_authorize_net_transaction_key', __( 'API Transaction Key', 'bookly' ) ) ?>
90
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_authorize_net_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 1, __( 'Yes', 'bookly' ) ), array( 0, __( 'No', 'bookly' ) ) ) ) ?>
91
- </div>
92
- </div>
93
- </div>
94
-
95
- <div class="panel panel-default">
96
- <div class="panel-heading">
97
- <label for="bookly_pmt_stripe">Stripe</label>
98
- <img class="pull-right" src="<?php echo plugins_url( 'frontend/resources/images/stripe.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>">
99
- </div>
100
- <div class="panel-body">
101
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_stripe', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( '1', __( 'Enabled', 'bookly' ) ) ) ) ?>
102
- <div class="bookly-stripe">
103
- <div class="form-group">
104
- <h4><?php _e( 'Instructions', 'bookly' ) ?></h4>
105
- <p>
106
- <?php _e( 'If <b>Publishable Key</b> is provided then Bookly will use <a href="https://stripe.com/docs/stripe.js" target="_blank">Stripe.js</a><br/>for collecting credit card details.', 'bookly' ) ?>
107
- </p>
108
  </div>
109
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_stripe_secret_key', __( 'Secret Key', 'bookly' ) ) ?>
110
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_stripe_publishable_key', __( 'Publishable Key', 'bookly' ) ) ?>
111
- </div>
112
- </div>
113
- </div>
114
-
115
- <div class="panel panel-default">
116
- <div class="panel-heading">
117
- <label for="bookly_pmt_payu_latam">PayU Latam</label>
118
- <img class="pull-right" src="<?php echo plugins_url( 'frontend/resources/images/payu_latam.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>"/>
119
- </div>
120
- <div class="panel-body">
121
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_payu_latam', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( '1', __( 'Enabled', 'bookly' ) ) ) ) ?>
122
- <div class="bookly-payu_latam">
123
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payu_latam_api_key', __( 'API Key', 'bookly' ) ) ?>
124
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payu_latam_api_account_id', __( 'Account ID', 'bookly' ) ) ?>
125
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payu_latam_api_merchant_id', __( 'Merchant ID', 'bookly' ) ) ?>
126
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_payu_latam_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 0, __( 'No', 'bookly' ) ), array( 1, __( 'Yes', 'bookly' ) ) ) ) ?>
127
- </div>
128
- </div>
129
- </div>
130
-
131
- <div class="panel panel-default">
132
- <div class="panel-heading">
133
- <label for="bookly_pmt_payson">Payson</label>
134
- <img class="pull-right" src="<?php echo plugins_url( 'frontend/resources/images/payson.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>"/>
135
- </div>
136
- <div class="panel-body">
137
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_payson', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( '1', __( 'Enabled', 'bookly' ) ) ) ) ?>
138
- <div class="bookly-payson">
139
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payson_api_agent_id', __( 'Agent ID', 'bookly' ) ) ?>
140
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payson_api_key', __( 'API Key', 'bookly' ) ) ?>
141
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_payson_api_receiver_email', __( 'Receiver Email (login)', 'bookly' ) ) ?>
142
- <?php \BooklyLite\Lib\Utils\Common::optionFlags( 'bookly_pmt_payson_funding', array( array( 'CREDITCARD', __( 'Card', 'bookly' ) ), array( 'INVOICE', __( 'Invoice', 'bookly' ) ) ), __( 'Funding', 'bookly' ) ) ?>
143
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_payson_fees_payer', __( 'Fees Payer', 'bookly' ), null, array( array( 'PRIMARYRECEIVER', __( 'I am', 'bookly' ) ), array( 'SENDER', __( 'Client', 'bookly' ) ) ) ) ?>
144
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_payson_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 0, __( 'No', 'bookly' ) ), array( 1, __( 'Yes', 'bookly' ) ) ) ) ?>
145
- </div>
146
- </div>
147
- </div>
148
-
149
- <div class="panel panel-default">
150
- <div class="panel-heading">
151
- <label for="bookly_pmt_mollie">Mollie</label>
152
- <img class="pull-right" src="<?php echo plugins_url( 'frontend/resources/images/mollie.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>"/>
153
- </div>
154
- <div class="panel-body">
155
- <?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_pmt_mollie', null, null, array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( '1', __( 'Enabled', 'bookly' ) ) ) ) ?>
156
- <div class="bookly-mollie">
157
- <?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_pmt_mollie_api_key', __( 'API Key', 'bookly' ) ) ?>
158
  </div>
159
  </div>
160
  </div>
161
 
162
- <?php do_action( 'bookly_render_payment_settings' ) ?>
163
 
164
  <div class="panel-footer">
165
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
166
- <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-payments-reset' ) ?>
 
167
  </div>
168
  </form>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Utils\Common;
3
+ use BooklyLite\Lib\Utils\Price;
4
+ use BooklyLite\Lib\Proxy;
5
+ ?>
6
  <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'payments' ) ) ?>">
7
  <div class="row">
8
+ <div class="col-lg-4">
9
  <div class="form-group">
10
  <label for="bookly_pmt_currency"><?php _e( 'Currency', 'bookly' ) ?></label>
11
  <select id="bookly_pmt_currency" class="form-control" name="bookly_pmt_currency">
12
+ <?php foreach ( Price::getCurrencies() as $code => $currency ) : ?>
13
+ <option value="<?php echo $code ?>" data-symbol="<?php esc_attr_e( $currency['symbol'] ) ?>" <?php selected( get_option( 'bookly_pmt_currency' ), $code ) ?> ><?php echo $code ?> (<?php esc_html_e( $currency['symbol'] ) ?>)</option>
14
  <?php endforeach ?>
15
  </select>
16
  </div>
17
  </div>
18
+ <div class="col-lg-4">
19
+ <div class="form-group">
20
+ <label for="bookly_pmt_price_format"><?php _e( 'Price format', 'bookly' ) ?></label>
21
+ <select id="bookly_pmt_price_format" class="form-control" name="bookly_pmt_price_format">
22
+ <?php foreach ( Price::getFormats() as $format ) : ?>
23
+ <option value="<?php echo $format ?>" <?php selected( get_option( 'bookly_pmt_price_format' ), $format ) ?> ></option>
24
+ <?php endforeach ?>
25
+ </select>
26
+ </div>
27
  </div>
28
+ <?php Proxy\Coupons::renderSettings() ?>
29
  </div>
30
 
31
  <div class="panel panel-default">
33
  <label for="bookly_pmt_local"><?php _e( 'Service paid locally', 'bookly' ) ?></label>
34
  </div>
35
  <div class="panel-body">
36
+ <?php Common::optionToggle( 'bookly_pmt_local' ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  </div>
38
  </div>
39
 
40
  <div class="panel panel-default">
41
  <div class="panel-heading">
42
+ <label for="bookly_paypal_enabled">PayPal</label>
43
  <img style="margin-left: 10px; float: right" src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" />
44
  </div>
45
  <div class="panel-body">
46
  <div class="form-group">
47
+ <?php Common::optionToggle( 'bookly_paypal_enabled', null, null,
48
  array(
49
+ array( '0', __( 'Disabled', 'bookly' ) ),
50
  array( 'ec', 'PayPal Express Checkout' ),
51
+ )
52
+ ) ?>
53
  </div>
54
  <div class="bookly-paypal">
55
  <div class="bookly-paypal-ec">
56
+ <?php Common::optionText( 'bookly_paypal_api_username', __( 'API Username', 'bookly' ) ) ?>
57
+ <?php Common::optionText( 'bookly_paypal_api_password', __( 'API Password', 'bookly' ) ) ?>
58
+ <?php Common::optionText( 'bookly_paypal_api_signature', __( 'API Signature', 'bookly' ) ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  </div>
60
+ <?php Proxy\PaypalPaymentsStandard::renderSetUpOptions() ?>
61
+ <?php Common::optionToggle( 'bookly_paypal_sandbox', __( 'Sandbox Mode', 'bookly' ), null, array( array( 1, __( 'Yes', 'bookly' ) ), array( 0, __( 'No', 'bookly' ) ) ) ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  </div>
63
  </div>
64
  </div>
65
 
66
+ <?php BooklyLite\Lib\Proxy\Shared::renderPaymentGatewaySettings() ?>
67
 
68
  <div class="panel-footer">
69
+ <?php Common::csrf() ?>
70
+ <?php Common::submitButton() ?>
71
+ <?php Common::resetButton( 'bookly-payments-reset' ) ?>
72
  </div>
73
  </form>
backend/modules/settings/templates/_urlForm.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Proxy;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ ?>
5
+ <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'url' ) ) ?>">
6
+ <?php
7
+ Common::optionText( 'bookly_url_approve_page_url', __( 'Approve appointment URL (success)', 'bookly' ), __( 'Set the URL of a page that is shown to staff after they successfully approved the appointment.', 'bookly' ) );
8
+ Common::optionText( 'bookly_url_approve_denied_page_url', __( 'Approve appointment URL (denied)', 'bookly' ), __( 'Set the URL of a page that is shown to staff when the approval of appointment cannot be done (due to capacity, changed status, etc.).', 'bookly' ) );
9
+ Common::optionText( 'bookly_url_cancel_page_url', __( 'Cancel appointment URL (success)', 'bookly' ), __( 'Set the URL of a page that is shown to clients after they successfully cancelled their appointment.', 'bookly' ) );
10
+ Common::optionText( 'bookly_url_cancel_denied_page_url', __( 'Cancel appointment URL (denied)', 'bookly' ), __( 'Set the URL of a page that is shown to clients when the cancellation of appointment is not available anymore.', 'bookly' ) );
11
+ Common::optionText( 'bookly_url_cancel_confirm_page_url', __( 'Appointment cancellation confirmation URL', 'bookly' ), __( 'Set the URL of an appointment cancellation confirmation page that is shown to clients when they press cancellation link.', 'bookly' ) );
12
+ Common::optionText( 'bookly_url_reject_page_url', __( 'Reject appointment URL (success)', 'bookly' ), __( 'Set the URL of a page that is shown to staff after they successfully rejected the appointment.', 'bookly' ) );
13
+ Common::optionText( 'bookly_url_reject_denied_page_url', __( 'Reject appointment URL (denied)', 'bookly' ), __( 'Set the URL of a page that is shown to staff when the rejection of appointment cannot be done (due to changed status, etc.).', 'bookly' ) );
14
+ ?>
15
+ <div class="form-group">
16
+ <label for="bookly_settings_final_step_url_mode"><?php _e( 'Final step URL', 'bookly' ) ?></label>
17
+ <p class="help-block"><?php _e( 'Set the URL of a page that the user will be forwarded to after successful booking. If disabled then the default Done step is displayed.', 'bookly' ) ?></p>
18
+ <select class="form-control" id="bookly_settings_final_step_url_mode">
19
+ <?php foreach ( array( __( 'Disabled', 'bookly' ) => 0, __( 'Enabled', 'bookly' ) => 1 ) as $text => $mode ) : ?>
20
+ <option value="<?php echo esc_attr( $mode ) ?>" <?php selected( get_option( 'bookly_url_final_step_url' ), $mode ) ?> ><?php echo $text ?></option>
21
+ <?php endforeach ?>
22
+ </select>
23
+ <input class="form-control"
24
+ style="margin-top: 5px; <?php echo get_option( 'bookly_url_final_step_url' ) == '' ? 'display: none' : '' ?>"
25
+ type="text" name="bookly_url_final_step_url"
26
+ value="<?php form_option( 'bookly_url_final_step_url' ) ?>"
27
+ placeholder="<?php esc_attr_e( 'Enter a URL', 'bookly' ) ?>"/>
28
+ </div>
29
+ <?php Proxy\Shared::renderUrlSettings() ?>
30
+ <div class="panel-footer">
31
+ <?php Common::csrf() ?>
32
+ <?php Common::submitButton() ?>
33
+ <?php Common::resetButton() ?>
34
+ </div>
35
+ </form>
backend/modules/settings/templates/_woocommerce.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php if (!defined('ABSPATH')) exit; // Exit if accessed directly ?>
2
- <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'woocommerce' ) ) ?>"
3
  id="woocommerce">
4
  <div class="form-group">
5
  <h4><?php _e( 'Instructions', 'bookly' ) ?></h4>
@@ -38,6 +38,7 @@
38
  </div>
39
 
40
  <div class="panel-footer">
 
41
  <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn btn-lg btn-success bookly-limitation' ) ?>
42
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
43
  </div>
1
  <?php if (!defined('ABSPATH')) exit; // Exit if accessed directly ?>
2
+ <form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'woo_commerce' ) ) ?>"
3
  id="woocommerce">
4
  <div class="form-group">
5
  <h4><?php _e( 'Instructions', 'bookly' ) ?></h4>
38
  </div>
39
 
40
  <div class="panel-footer">
41
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
42
  <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'btn btn-lg btn-success bookly-limitation' ) ?>
43
  <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
44
  </div>
backend/modules/settings/templates/_woocommerce_codes.php CHANGED
@@ -10,4 +10,4 @@ $codes = array(
10
  array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ), ),
11
  array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ), ),
12
  );
13
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_woocommerce_short_codes', $codes ) );
10
  array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ), ),
11
  array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ), ),
12
  );
13
+ BooklyLite\Lib\Utils\Common::codes( BooklyLite\Lib\Proxy\Shared::prepareWooCommerceShortCodes( $codes ) );
backend/modules/settings/templates/index.php CHANGED
@@ -10,10 +10,15 @@
10
  <div class="row">
11
  <div id="bookly-sidebar" class="col-sm-4">
12
  <ul class="bookly-nav" role="tablist">
13
- <?php $tab = isset ( $_GET['tab'] ) ? $_GET['tab'] : 'general' ?>
14
  <li class="bookly-nav-item" data-target="#bookly_settings_general" data-toggle="tab">
15
  <?php _e( 'General', 'bookly' ) ?>
16
  </li>
 
 
 
 
 
 
17
  <li class="bookly-nav-item" data-target="#bookly_settings_company" data-toggle="tab">
18
  <?php _e( 'Company', 'bookly' ) ?>
19
  </li>
@@ -23,13 +28,13 @@
23
  <li class="bookly-nav-item" data-target="#bookly_settings_google_calendar" data-toggle="tab">
24
  <?php _e( 'Google Calendar', 'bookly' ) ?>
25
  </li>
26
- <li class="bookly-nav-item" data-target="#bookly_settings_woocommerce" data-toggle="tab">
27
  WooCommerce
28
  </li>
29
  <li class="bookly-nav-item" data-target="#bookly_settings_cart" data-toggle="tab">
30
  <?php _e( 'Cart', 'bookly' ) ?>
31
  </li>
32
- <?php do_action( 'bookly_render_settings_menu', $tab ) ?>
33
  <li class="bookly-nav-item" data-target="#bookly_settings_payments" data-toggle="tab">
34
  <?php _e( 'Payments', 'bookly' ) ?>
35
  </li>
@@ -49,6 +54,12 @@
49
  <div class="tab-pane active" id="bookly_settings_general">
50
  <?php include '_generalForm.php' ?>
51
  </div>
 
 
 
 
 
 
52
  <div class="tab-pane" id="bookly_settings_company">
53
  <?php include '_companyForm.php' ?>
54
  </div>
@@ -58,13 +69,13 @@
58
  <div class="tab-pane" id="bookly_settings_google_calendar">
59
  <?php include '_googleCalendarForm.php' ?>
60
  </div>
61
- <div class="tab-pane" id="bookly_settings_woocommerce">
62
  <?php include '_woocommerce.php' ?>
63
  </div>
64
  <div class="tab-pane" id="bookly_settings_cart">
65
  <?php include '_cartForm.php' ?>
66
  </div>
67
- <?php do_action( 'bookly_render_settings_form' ) ?>
68
  <div class="tab-pane" id="bookly_settings_payments">
69
  <?php include '_paymentsForm.php' ?>
70
  </div>
10
  <div class="row">
11
  <div id="bookly-sidebar" class="col-sm-4">
12
  <ul class="bookly-nav" role="tablist">
 
13
  <li class="bookly-nav-item" data-target="#bookly_settings_general" data-toggle="tab">
14
  <?php _e( 'General', 'bookly' ) ?>
15
  </li>
16
+ <li class="bookly-nav-item" data-target="#bookly_settings_url" data-toggle="tab">
17
+ <?php _e( 'URL Settings', 'bookly' ) ?>
18
+ </li>
19
+ <li class="bookly-nav-item" data-target="#bookly_settings_calendar" data-toggle="tab">
20
+ <?php _e( 'Calendar', 'bookly' ) ?>
21
+ </li>
22
  <li class="bookly-nav-item" data-target="#bookly_settings_company" data-toggle="tab">
23
  <?php _e( 'Company', 'bookly' ) ?>
24
  </li>
28
  <li class="bookly-nav-item" data-target="#bookly_settings_google_calendar" data-toggle="tab">
29
  <?php _e( 'Google Calendar', 'bookly' ) ?>
30
  </li>
31
+ <li class="bookly-nav-item" data-target="#bookly_settings_woo_commerce" data-toggle="tab">
32
  WooCommerce
33
  </li>
34
  <li class="bookly-nav-item" data-target="#bookly_settings_cart" data-toggle="tab">
35
  <?php _e( 'Cart', 'bookly' ) ?>
36
  </li>
37
+ <?php \BooklyLite\Lib\Proxy\Shared::renderSettingsMenu() ?>
38
  <li class="bookly-nav-item" data-target="#bookly_settings_payments" data-toggle="tab">
39
  <?php _e( 'Payments', 'bookly' ) ?>
40
  </li>
54
  <div class="tab-pane active" id="bookly_settings_general">
55
  <?php include '_generalForm.php' ?>
56
  </div>
57
+ <div class="tab-pane" id="bookly_settings_url">
58
+ <?php include '_urlForm.php' ?>
59
+ </div>
60
+ <div class="tab-pane active" id="bookly_settings_calendar">
61
+ <?php include '_calendarForm.php' ?>
62
+ </div>
63
  <div class="tab-pane" id="bookly_settings_company">
64
  <?php include '_companyForm.php' ?>
65
  </div>
69
  <div class="tab-pane" id="bookly_settings_google_calendar">
70
  <?php include '_googleCalendarForm.php' ?>
71
  </div>
72
+ <div class="tab-pane" id="bookly_settings_woo_commerce">
73
  <?php include '_woocommerce.php' ?>
74
  </div>
75
  <div class="tab-pane" id="bookly_settings_cart">
76
  <?php include '_cartForm.php' ?>
77
  </div>
78
+ <?php \BooklyLite\Lib\Proxy\Shared::renderSettingsForm() ?>
79
  <div class="tab-pane" id="bookly_settings_payments">
80
  <?php include '_paymentsForm.php' ?>
81
  </div>
backend/modules/sms/Components.php ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace BooklyLite\Backend\Modules\Sms;
3
+
4
+ use BooklyLite\Lib;
5
+ use BooklyLite\Lib\Entities\Notification;
6
+
7
+ /**
8
+ * Class Components
9
+ * @package BooklyLite\Backend\Modules\Notifications
10
+ */
11
+ class Components extends Lib\Base\Components
12
+ {
13
+ protected $css_prefix = 'bookly-js-codes-';
14
+
15
+ /**
16
+ * Codes for all notifications.
17
+ *
18
+ * @return array
19
+ */
20
+ private function getCommonCodes()
21
+ {
22
+ return array(
23
+ array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
24
+ array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
25
+ array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
26
+ array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
27
+ );
28
+ }
29
+
30
+ /**
31
+ * Render codes for notifications
32
+ *
33
+ * @param string $notification_type
34
+ */
35
+ public function renderCodes( $notification_type )
36
+ {
37
+ switch ( $notification_type ) {
38
+ case Notification::TYPE_APPOINTMENT_START_TIME:
39
+ case Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED:
40
+ case Notification::TYPE_LAST_CUSTOMER_APPOINTMENT:
41
+ case Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED:
42
+ $this->renderCustomerAppointmentCodesForCN( $notification_type );
43
+ break;
44
+ case 'staff_agenda':
45
+ case Notification::TYPE_STAFF_DAY_AGENDA:
46
+ $this->renderStaffDayAgendaCodes();
47
+ break;
48
+ case 'client_birthday_greeting':
49
+ case Notification::TYPE_CUSTOMER_BIRTHDAY:
50
+ $this->renderBirthdayGreetingCodes();
51
+ break;
52
+ case 'client_new_wp_user':
53
+ $this->renderNewWpUserCodes();
54
+ break;
55
+ case 'client_pending_appointment_cart':
56
+ case 'client_approved_appointment_cart':
57
+ $this->renderCompoundCodes();
58
+ break;
59
+ case 'staff_waiting_list':
60
+ $this->renderWaitingListCodes();
61
+ break;
62
+ case 'client_pending_appointment':
63
+ case 'staff_pending_appointment':
64
+ case 'client_approved_appointment':
65
+ case 'staff_approved_appointment':
66
+ case 'client_cancelled_appointment':
67
+ case 'staff_cancelled_appointment':
68
+ case 'client_rejected_appointment':
69
+ case 'staff_rejected_appointment':
70
+ case 'client_waitlisted_appointment':
71
+ case 'staff_waitlisted_appointment':
72
+ case 'client_reminder':
73
+ case 'client_reminder_1st':
74
+ case 'client_reminder_2nd':
75
+ case 'client_reminder_3rd':
76
+ case 'client_follow_up':
77
+ $this->renderBaseCodes();
78
+ break;
79
+ case 'staff_package_purchased':
80
+ case 'client_package_purchased':
81
+ case 'staff_package_deleted':
82
+ case 'client_package_deleted':
83
+ $this->renderPackageCodes( $notification_type );
84
+ break;
85
+ case 'client_pending_recurring_appointment':
86
+ case 'staff_pending_recurring_appointment':
87
+ case 'client_approved_recurring_appointment':
88
+ case 'staff_approved_recurring_appointment':
89
+ case 'client_cancelled_recurring_appointment':
90
+ case 'staff_cancelled_recurring_appointment':
91
+ case 'client_rejected_recurring_appointment':
92
+ case 'staff_rejected_recurring_appointment':
93
+ case 'client_waitlisted_recurring_appointment':
94
+ case 'staff_waitlisted_recurring_appointment':
95
+ $this->renderRecurringCodes();
96
+ break;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Render codes for custom notifications with appointment(s)
102
+ *
103
+ * @param string $notification_type
104
+ */
105
+ private function renderCustomerAppointmentCodesForCN( $notification_type )
106
+ {
107
+ $codes = $this->getCommonCodes();
108
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
109
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
110
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
111
+ $codes[] = array( 'code' => 'appointment_notes', 'description' => __( 'customer notes for appointment', 'bookly' ) );
112
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
113
+ $codes[] = array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) );
114
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
115
+ $codes[] = array( 'code' => 'cancel_appointment_confirm_url', 'description' => esc_html__( 'URL of cancel appointment link with confirmation (to use inside <a> tag)', 'bookly' ) );
116
+ $codes[] = array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) );
117
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
118
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
119
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
120
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
121
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
122
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
123
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
124
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
125
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
126
+ $codes[] = array( 'code' => 'reject_appointment_url', 'description' => esc_html__( 'URL of reject appointment link (to use inside <a> tag)', 'bookly' ) );
127
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
128
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
129
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
130
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
131
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
132
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
133
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
134
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
135
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
136
+
137
+ $codes = Lib\Proxy\Packages::prepareNotificationCodesList( $codes );
138
+ $codes = Lib\Proxy\RecurringAppointments::prepareNotificationCodesList( $codes );
139
+
140
+ Lib\Utils\Common::codes(
141
+ Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' ),
142
+ array( 'type' => $notification_type )
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Render codes for staff agenda.
148
+ */
149
+ private function renderStaffDayAgendaCodes()
150
+ {
151
+ $codes = $this->getCommonCodes();
152
+ $codes[] = array( 'code' => 'agenda_date', 'description' => __( 'agenda date', 'bookly' ) );
153
+ $codes[] = array( 'code' => 'next_day_agenda', 'description' => __( 'staff agenda for next day', 'bookly' ) );
154
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
155
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
156
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
157
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
158
+ $codes[] = array( 'code' => 'tomorrow_date', 'description' => __( 'date of next day', 'bookly' ) );
159
+
160
+ Lib\Utils\Common::codes(
161
+ $codes,
162
+ array( 'type' => Notification::TYPE_STAFF_DAY_AGENDA )
163
+ );
164
+ }
165
+
166
+ /**
167
+ * Render codes for Greeting notifications
168
+ */
169
+ private function renderBirthdayGreetingCodes()
170
+ {
171
+ $codes = $this->getCommonCodes();
172
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
173
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
174
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
175
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
176
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
177
+
178
+ Lib\Utils\Common::codes(
179
+ $codes,
180
+ array( 'type' => Notification::TYPE_CUSTOMER_BIRTHDAY )
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Render codes for new WordPress users.
186
+ */
187
+ private function renderNewWpUserCodes()
188
+ {
189
+ $codes = $this->getCommonCodes();
190
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
191
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
192
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
193
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
194
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
195
+ $codes[] = array( 'code' => 'new_password', 'description' => __( 'customer new password', 'bookly' ) );
196
+ $codes[] = array( 'code' => 'new_username', 'description' => __( 'customer new username', 'bookly' ) );
197
+ $codes[] = array( 'code' => 'site_address', 'description' => __( 'site address', 'bookly' ) );
198
+
199
+ Lib\Utils\Common::codes( $codes );
200
+ }
201
+
202
+ /**
203
+ * Render codes for compound (cart) notifications.
204
+ */
205
+ private function renderCompoundCodes()
206
+ {
207
+ $codes = $this->getCommonCodes();
208
+ $codes[] = array( 'code' => 'cart_info', 'description' => __( 'cart information', 'bookly' ) );
209
+ $codes[] = array( 'code' => 'cart_info_c', 'description' => __( 'cart information with cancel', 'bookly' ) );
210
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
211
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
212
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
213
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
214
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
215
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
216
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
217
+
218
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareCartNotificationShortCodes( $codes ) );
219
+ }
220
+
221
+ /**
222
+ * Render base codes
223
+ */
224
+ private function renderBaseCodes()
225
+ {
226
+ $codes = $this->getCommonCodes();
227
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
228
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
229
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
230
+ $codes[] = array( 'code' => 'appointment_notes', 'description' => __( 'customer notes for appointment', 'bookly' ) );
231
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
232
+ $codes[] = array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) );
233
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
234
+ $codes[] = array( 'code' => 'cancel_appointment_confirm_url', 'description' => esc_html__( 'URL of cancel appointment link with confirmation (to use inside <a> tag)', 'bookly' ) );
235
+ $codes[] = array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) );
236
+ $codes[] = array( 'code' => 'cancellation_reason', 'description' => __( 'reason you mentioned while deleting appointment', 'bookly' ) );
237
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
238
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
239
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
240
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
241
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
242
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
243
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
244
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
245
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
246
+ $codes[] = array( 'code' => 'reject_appointment_url', 'description' => esc_html__( 'URL of reject appointment link (to use inside <a> tag)', 'bookly' ) );
247
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
248
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
249
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
250
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
251
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
252
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
253
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
254
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
255
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
256
+
257
+ Lib\Utils\Common::codes(
258
+ Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' )
259
+ );
260
+ }
261
+
262
+ /**
263
+ * Render codes notifications about package appointments
264
+ *
265
+ * @param string $notification_type
266
+ */
267
+ private function renderPackageCodes( $notification_type )
268
+ {
269
+ $codes = $this->getCommonCodes();
270
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
271
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
272
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
273
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
274
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
275
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
276
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
277
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
278
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
279
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
280
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
281
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
282
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
283
+
284
+ $codes = Lib\Proxy\Packages::prepareNotificationCodesList( $codes, '', $notification_type );
285
+
286
+ Lib\Utils\Common::codes( $codes );
287
+ }
288
+
289
+ /**
290
+ * Render codes notifications wor appointments in waiting list
291
+ */
292
+ private function renderWaitingListCodes()
293
+ {
294
+ $codes = $this->getCommonCodes();
295
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
296
+ $codes[] = array( 'code' => 'appointment_end_date', 'description' => __( 'end date of appointment', 'bookly' ) );
297
+ $codes[] = array( 'code' => 'appointment_end_time', 'description' => __( 'end time of appointment', 'bookly' ) );
298
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
299
+ $codes[] = array( 'code' => 'appointment_waiting_list', 'description' => __( 'waiting list of appointment', 'bookly-waiting-list' ) );
300
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
301
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
302
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
303
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
304
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
305
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
306
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
307
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
308
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
309
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
310
+
311
+ $codes = Lib\Proxy\WaitingList::prepareNotificationCodesList( $codes );
312
+
313
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'appointment' ) );
314
+ }
315
+
316
+ private function renderRecurringCodes()
317
+ {
318
+ $codes = $this->getCommonCodes();
319
+ $codes[] = array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) );
320
+ $codes[] = array( 'code' => 'appointment_end_date','description' => __( 'end date of appointment', 'bookly' ) );
321
+ $codes[] = array( 'code' => 'appointment_end_time','description' => __( 'end time of appointment', 'bookly' ) );
322
+ $codes[] = array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) );
323
+ $codes[] = array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) );
324
+ $codes[] = array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) );
325
+ $codes[] = array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) );
326
+ $codes[] = array( 'code' => 'client_first_name', 'description' => __( 'first name of client', 'bookly' ) );
327
+ $codes[] = array( 'code' => 'client_last_name', 'description' => __( 'last name of client', 'bookly' ) );
328
+ $codes[] = array( 'code' => 'client_name', 'description' => __( 'full name of client', 'bookly' ) );
329
+ $codes[] = array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) );
330
+ $codes[] = array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) );
331
+ $codes[] = array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) );
332
+ $codes[] = array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) );
333
+ $codes[] = array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) );
334
+ $codes[] = array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) );
335
+ $codes[] = array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) );
336
+ $codes[] = array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) );
337
+ $codes[] = array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) );
338
+ $codes[] = array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) );
339
+ $codes[] = array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) );
340
+ $codes[] = array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) );
341
+ $codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
342
+
343
+ $codes = Lib\Proxy\RecurringAppointments::prepareNotificationCodesList( $codes );
344
+
345
+ Lib\Utils\Common::codes( Lib\Proxy\Shared::prepareNotificationCodesList( $codes, 'customer' ) );
346
+ }
347
+
348
+ }
backend/modules/sms/Controller.php CHANGED
@@ -34,6 +34,7 @@ class Controller extends Lib\Base\Controller
34
  'js/datatables.min.js' => array( 'jquery' ),
35
  'js/moment.min.js',
36
  'js/daterangepicker.js' => array( 'jquery' ),
 
37
  'js/alert.js' => array( 'jquery' ),
38
  ),
39
  'frontend' => array_merge(
@@ -50,7 +51,7 @@ class Controller extends Lib\Base\Controller
50
 
51
  $alert = array( 'success' => array(), 'error' => array() );
52
  $prices = array();
53
- $form = new \BooklyLite\Backend\Modules\Notifications\Forms\Notifications( 'sms' );
54
  $sms = new Lib\SMS();
55
  $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
56
 
@@ -92,14 +93,18 @@ class Controller extends Lib\Base\Controller
92
  }
93
  if ( $this->hasParameter( 'form-notifications' ) ) {
94
  update_option( 'bookly_sms_administrator_phone', $this->getParameter( 'bookly_sms_administrator_phone' ) );
 
95
 
96
  $form->bind( $this->getPostParameters() );
97
  $form->save();
98
  $alert['success'][] = __( 'Settings saved.', 'bookly' );
99
 
100
- foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder' ) as $type ) {
101
  $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_hour' );
102
  }
 
 
 
103
  update_option( 'bookly_cron_reminder_times', $cron_reminder );
104
  }
105
  if ( $this->hasParameter( 'tab' ) ) {
@@ -117,6 +122,7 @@ class Controller extends Lib\Base\Controller
117
  $alert['error'] = array_merge( $alert['error'], $sms->getErrors() );
118
  wp_localize_script( 'bookly-daterangepicker.js', 'BooklyL10n',
119
  array(
 
120
  'alert' => $alert,
121
  'apply' => __( 'Apply', 'bookly' ),
122
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
@@ -156,9 +162,12 @@ class Controller extends Lib\Base\Controller
156
  'processing' => __( 'Processing...', 'bookly' ),
157
  )
158
  );
159
- $cron_path = realpath( Lib\Plugin::getDirectory() . '/lib/utils/send_notifications_cron.php' );
160
-
161
- $this->render( 'index', compact( 'form', 'sms', 'is_logged_in', 'prices', 'cron_path', 'cron_reminder' ) );
 
 
 
162
  }
163
 
164
  public function executeGetPurchasesList()
@@ -234,18 +243,20 @@ class Controller extends Lib\Base\Controller
234
  public function executeSendTestSms()
235
  {
236
  $sms = new Lib\SMS();
237
- $phone_number = $this->getParameter( 'phone_number' );
238
- if ( $phone_number != '' ) {
239
- $response = array( 'success' => $sms->sendSms( $phone_number, 'Bookly test SMS.', Lib\Entities\Notification::$type_ids['test_message'] ) );
240
- if ( $response['success'] ) {
241
- $response['message'] = __( 'SMS has been sent successfully.', 'bookly' );
242
- } else {
243
- $response['message'] = __( 'Failed to send SMS.', 'bookly' );
244
- }
245
- wp_send_json( $response );
246
  } else {
247
- wp_send_json( array( 'success' => false, 'message' => __( 'Phone number is empty.', 'bookly' ) ) );
248
  }
 
 
249
  }
250
 
251
  public function executeForgotPassword()
@@ -323,14 +334,27 @@ class Controller extends Lib\Base\Controller
323
  }
324
 
325
  /**
326
- * Override parent method to add 'wp_ajax_bookly_' prefix
327
- * so current 'execute*' methods look nicer.
328
- *
329
- * @param string $prefix
330
  */
331
- protected function registerWpActions( $prefix = '' )
332
  {
333
- parent::registerWpActions( 'wp_ajax_bookly_' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
336
  }
34
  'js/datatables.min.js' => array( 'jquery' ),
35
  'js/moment.min.js',
36
  'js/daterangepicker.js' => array( 'jquery' ),
37
+ 'js/help.js' => array( 'jquery' ),
38
  'js/alert.js' => array( 'jquery' ),
39
  ),
40
  'frontend' => array_merge(
51
 
52
  $alert = array( 'success' => array(), 'error' => array() );
53
  $prices = array();
54
+ $form = new \BooklyLite\Backend\Modules\Notifications\Forms\Notifications( 'sms', Components::getInstance() );
55
  $sms = new Lib\SMS();
56
  $cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
57
 
93
  }
94
  if ( $this->hasParameter( 'form-notifications' ) ) {
95
  update_option( 'bookly_sms_administrator_phone', $this->getParameter( 'bookly_sms_administrator_phone' ) );
96
+ update_option( 'bookly_ntf_processing_interval', (int) $this->getParameter( 'bookly_ntf_processing_interval' ) );
97
 
98
  $form->bind( $this->getPostParameters() );
99
  $form->save();
100
  $alert['success'][] = __( 'Settings saved.', 'bookly' );
101
 
102
+ foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder', 'client_birthday_greeting' ) as $type ) {
103
  $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_hour' );
104
  }
105
+ foreach ( array( 'client_reminder_1st', 'client_reminder_2nd', 'client_reminder_3rd', ) as $type ) {
106
+ $cron_reminder[ $type ] = $this->getParameter( $type . '_cron_before_hour' );
107
+ }
108
  update_option( 'bookly_cron_reminder_times', $cron_reminder );
109
  }
110
  if ( $this->hasParameter( 'tab' ) ) {
122
  $alert['error'] = array_merge( $alert['error'], $sms->getErrors() );
123
  wp_localize_script( 'bookly-daterangepicker.js', 'BooklyL10n',
124
  array(
125
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
126
  'alert' => $alert,
127
  'apply' => __( 'Apply', 'bookly' ),
128
  'are_you_sure' => __( 'Are you sure?', 'bookly' ),
162
  'processing' => __( 'Processing...', 'bookly' ),
163
  )
164
  );
165
+ $cron_uri = plugins_url( 'lib/utils/send_notifications_cron.php', Lib\Plugin::getMainFile() );
166
+ $statuses = Lib\Entities\CustomerAppointment::getStatuses();
167
+ foreach ( range( 1, 23 ) as $hours ) {
168
+ $bookly_ntf_processing_interval_values[] = array( $hours, Lib\Utils\DateTime::secondsToInterval( $hours * HOUR_IN_SECONDS ) );
169
+ }
170
+ $this->render( 'index', compact( 'form', 'sms', 'is_logged_in', 'prices', 'cron_uri', 'cron_reminder', 'statuses', 'bookly_ntf_processing_interval_values' ) );
171
  }
172
 
173
  public function executeGetPurchasesList()
243
  public function executeSendTestSms()
244
  {
245
  $sms = new Lib\SMS();
246
+
247
+ $response = array( 'success' => $sms->sendSms(
248
+ $this->getParameter( 'phone_number' ),
249
+ 'Bookly test SMS.',
250
+ Lib\Entities\Notification::$type_ids['test_message']
251
+ ) );
252
+
253
+ if ( $response['success'] ) {
254
+ $response['message'] = __( 'SMS has been sent successfully.', 'bookly' );
255
  } else {
256
+ $response['message'] = implode( ' ', $sms->getErrors() );
257
  }
258
+
259
+ wp_send_json( $response );
260
  }
261
 
262
  public function executeForgotPassword()
334
  }
335
 
336
  /**
337
+ * Create new custom sms notification
 
 
 
338
  */
339
+ public function executeCreateCustomSms()
340
  {
341
+ $notification = new Lib\Entities\Notification();
342
+ $notification
343
+ ->setType( Lib\Entities\Notification::TYPE_APPOINTMENT_START_TIME )
344
+ ->setToCustomer( 1 )
345
+ ->setToStaff( 1 )
346
+ ->setSettings( json_encode( Lib\DataHolders\Notification\Settings::getDefault() ) )
347
+ ->setGateway( 'sms' )
348
+ ->save();
349
+
350
+ $notification = $notification->getFields();
351
+ $id = $notification['id'];
352
+
353
+ $form = new \BooklyLite\Backend\Modules\Notifications\Forms\Notifications( 'sms', Components::getInstance() );
354
+ $statuses = Lib\Entities\CustomerAppointment::getStatuses();
355
+
356
+ $html = $this->render( '_custom_notification', compact( 'form', 'notification', 'statuses' ), false );
357
+ wp_send_json_success( compact( 'html', 'id' ) );
358
  }
359
 
360
  }
backend/modules/sms/resources/js/sms.js CHANGED
@@ -1,8 +1,9 @@
1
  jQuery(function($) {
2
 
3
- var $form_login = $('.ab-login-form'),
4
- $form_forgot = $('.ab-forgot-form'),
5
- $form_register = $('.ab-register-form');
 
6
 
7
  booklyAlert(BooklyL10n.alert);
8
 
@@ -13,7 +14,7 @@ jQuery(function($) {
13
  $form_forgot.hide();
14
  });
15
 
16
- $('.ab--show-login-form').on('click', function (e) {
17
  e.preventDefault();
18
  $form_login.show();
19
  $form_register.hide();
@@ -35,7 +36,7 @@ jQuery(function($) {
35
  var $pwd = $form.find('input[name="password"]');
36
  var $username = $form.find('input[name="username"]');
37
  var $pwd_repeat = $form.find('input[name="password_repeat"]');
38
- var data = { action: 'bookly_forgot_password', step: $btn.data('step'), 'username': $username.val() };
39
  switch ($(this).data('step')) {
40
  case 0:
41
  forgot_helper( data, function() {
@@ -59,7 +60,7 @@ jQuery(function($) {
59
  data.password_repeat = $pwd_repeat.val();
60
  if (data.password == data.password_repeat && data.password != '') {
61
  forgot_helper(data, function() {
62
- $('.ab--show-login-form').trigger('click');
63
  $btn.data('step', 0);
64
  $username.parent().removeClass('hidden');
65
  $pwd.parent().addClass('hidden');
@@ -98,7 +99,7 @@ jQuery(function($) {
98
  $('.bookly-admin-notify').on('change', function () {
99
  var $checkbox = $(this);
100
  $checkbox.hide().prev('img').show();
101
- $.get(ajaxurl, {action: 'bookly_admin_notify', option_name: $checkbox.attr('name'), value: $checkbox.is(':checked') ? 1 : 0 }, function () {}, 'json').always(function () {
102
  $checkbox.show().prev('img').hide();
103
  });
104
  });
@@ -138,14 +139,70 @@ jQuery(function($) {
138
  /**
139
  * Notifications Tab.
140
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  var $phone_input = $('#admin_phone');
142
  if (BooklyL10n.intlTelInput.enabled) {
143
  $phone_input.intlTelInput({
144
  preferredCountries: [BooklyL10n.intlTelInput.country],
145
- defaultCountry: BooklyL10n.intlTelInput.country,
146
  geoIpLookup: function (callback) {
147
- $.get(ajaxurl, {action: 'bookly_ip_info'}, function () {
148
- }, 'json').always(function (resp) {
149
  var countryCode = (resp && resp.country) ? resp.country : '';
150
  callback(countryCode);
151
  });
@@ -153,7 +210,7 @@ jQuery(function($) {
153
  utilsScript: BooklyL10n.intlTelInput.utils
154
  });
155
  }
156
- $('#js-submit-notifications').on('click', function (e) {
157
  e.preventDefault();
158
  var ladda = Ladda.create(this);
159
  ladda.start();
@@ -165,7 +222,7 @@ jQuery(function($) {
165
  e.preventDefault();
166
  $.ajax({
167
  url : ajaxurl,
168
- data : { action: 'bookly_send_test_sms', phone_number: BooklyL10n.intlTelInput.enabled ? $phone_input.intlTelInput('getNumber') : $phone_input.val() },
169
  dataType : 'json',
170
  xhrFields : { withCredentials: true },
171
  crossDomain : 'withCredentials' in new XMLHttpRequest(),
@@ -189,7 +246,7 @@ jQuery(function($) {
189
  $recharge_init.on('click', function () {
190
  var ladda = Ladda.create(this);
191
  ladda.start();
192
- $.get(ajaxurl, {action: 'bookly_init_auto_recharge', amount: $recharge_amount.val()}, function () {
193
  }, 'json').always(function (response) {
194
  if (response.success) {
195
  window.location.replace(response.data.paypal_preapproval);
@@ -202,7 +259,7 @@ jQuery(function($) {
202
  $recharge_decline.on('click', function () {
203
  var ladda = Ladda.create(this);
204
  ladda.start();
205
- $.get(ajaxurl, {action: 'bookly_decline_auto_recharge'}, function () {}, 'json')
206
  .always(function (response) {
207
  ladda.stop();
208
  if (response.success) {
@@ -277,6 +334,7 @@ jQuery(function($) {
277
  data: function (d) {
278
  return {
279
  action: 'bookly_get_purchases_list',
 
280
  range: $date_range.data('date')
281
  };
282
  },
@@ -331,6 +389,7 @@ jQuery(function($) {
331
  data: function (d) {
332
  return {
333
  action: 'bookly_get_sms_list',
 
334
  range: $date_range.data('date')
335
  };
336
  },
@@ -358,10 +417,10 @@ jQuery(function($) {
358
  /**
359
  * Prices Tab.
360
  */
361
- $("[data-target='#price']").one('click', function() {
362
  fillPriceTable();
363
  });
364
- if ($('form.ab-login-form').length){
365
  fillPriceTable();
366
  }
367
 
@@ -375,7 +434,7 @@ jQuery(function($) {
375
  responsive: true,
376
  ajax: {
377
  url : ajaxurl,
378
- data: { action: 'bookly_get_price_list' },
379
  dataSrc: 'list'
380
  },
381
  columns: [
@@ -427,7 +486,7 @@ jQuery(function($) {
427
  responsive: true,
428
  ajax: {
429
  url: ajaxurl,
430
- data: { action: 'bookly_get_sender_ids_list' },
431
  dataSrc: function (json) {
432
  if (json.pending) {
433
  $sender_id.val(json.pending);
@@ -456,7 +515,7 @@ jQuery(function($) {
456
  ladda.start();
457
  $.ajax({
458
  url : ajaxurl,
459
- data : {action: 'bookly_request_sender_id', 'sender_id': $sender_id.val()},
460
  dataType : 'json',
461
  xhrFields: {withCredentials: true},
462
  success: function (response) {
@@ -480,7 +539,7 @@ jQuery(function($) {
480
  if (confirm(BooklyL10n.are_you_sure)) {
481
  $.ajax({
482
  url: ajaxurl,
483
- data: {action: 'bookly_reset_sender_id'},
484
  dataType: 'json',
485
  xhrFields: {withCredentials: true},
486
  success: function (response) {
@@ -507,7 +566,7 @@ jQuery(function($) {
507
  $.ajax({
508
  method : 'POST',
509
  url : ajaxurl,
510
- data: {action: 'bookly_cancel_sender_id'},
511
  dataType : 'json',
512
  success : function (response) {
513
  if (response.success) {
@@ -529,6 +588,33 @@ jQuery(function($) {
529
  $(this).on('click', function () { dt.ajax.reload(); });
530
  });
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  $('#bookly-open-tab-sender-id').on('click', function (e) {
533
  e.preventDefault();
534
  $('#sms_tabs li[data-target="#sender_id"]').trigger('click');
1
  jQuery(function($) {
2
 
3
+ var $form_login = $('.bookly-login-form'),
4
+ $form_forgot = $('.bookly-forgot-form'),
5
+ $form_register = $('.bookly-register-form'),
6
+ $custom_notifications = $('#bookly-js-custom-notifications');
7
 
8
  booklyAlert(BooklyL10n.alert);
9
 
14
  $form_forgot.hide();
15
  });
16
 
17
+ $('.bookly-show-login-form').on('click', function (e) {
18
  e.preventDefault();
19
  $form_login.show();
20
  $form_register.hide();
36
  var $pwd = $form.find('input[name="password"]');
37
  var $username = $form.find('input[name="username"]');
38
  var $pwd_repeat = $form.find('input[name="password_repeat"]');
39
+ var data = { action: 'bookly_forgot_password', step: $btn.data('step'), 'username': $username.val(), csrf_token : BooklyL10n.csrf_token };
40
  switch ($(this).data('step')) {
41
  case 0:
42
  forgot_helper( data, function() {
60
  data.password_repeat = $pwd_repeat.val();
61
  if (data.password == data.password_repeat && data.password != '') {
62
  forgot_helper(data, function() {
63
+ $('.bookly-show-login-form').trigger('click');
64
  $btn.data('step', 0);
65
  $username.parent().removeClass('hidden');
66
  $pwd.parent().addClass('hidden');
99
  $('.bookly-admin-notify').on('change', function () {
100
  var $checkbox = $(this);
101
  $checkbox.hide().prev('img').show();
102
+ $.get(ajaxurl, {action: 'bookly_admin_notify', csrf_token : BooklyL10n.csrf_token, option_name: $checkbox.attr('name'), value: $checkbox.is(':checked') ? 1 : 0 }, function () {}, 'json').always(function () {
103
  $checkbox.show().prev('img').hide();
104
  });
105
  });
139
  /**
140
  * Notifications Tab.
141
  */
142
+ $custom_notifications
143
+ .on('change', "select[name$='[type]']", function () {
144
+ var $panel = $(this).closest('.panel'),
145
+ $settings = $panel.find('.bookly-js-settings'),
146
+ value = $(this).find(':selected').val(),
147
+ set = $(this).find(':selected').data('set'),
148
+ to = $(this).find(':selected').data('to');
149
+
150
+ $panel.find('table.bookly-codes').each(function () {
151
+ $(this).toggle($(this).hasClass('bookly-js-codes-' + value));
152
+ });
153
+
154
+ $.each(['customer', 'staff', 'admin'], function (index, value) {
155
+ $panel.find("[name$='[to_" + value + "]']").closest('.checkbox-inline').toggle(to.indexOf(value) != -1);
156
+ });
157
+
158
+ $settings.each(function () {
159
+ $(this).toggle($(this).hasClass('bookly-js-' + set));
160
+ });
161
+
162
+ switch (set) {
163
+ case 'after_event':
164
+ var $set = $panel.find('.bookly-js-' + set);
165
+ $set.find('.bookly-js-to').toggle(value == 'ca_status_changed');
166
+ $set.find('.bookly-js-with').toggle(value != 'ca_status_changed');
167
+ break;
168
+ }
169
+ })
170
+ .on('click', '.bookly-js-delete', function () {
171
+ if (confirm(BooklyL10n.are_you_sure)) {
172
+ var $button = $(this),
173
+ id = $button.data('notification_id');
174
+ $.ajax({
175
+ url: ajaxurl,
176
+ type: 'POST',
177
+ data: {
178
+ action: 'bookly_delete_custom_notification',
179
+ id: id,
180
+ csrf_token: BooklyL10n.csrf_token
181
+ },
182
+ dataType: 'json',
183
+ success: function (response) {
184
+ if (response.success) {
185
+ $button.closest('.panel').remove();
186
+ }
187
+ }
188
+ });
189
+ }
190
+ })
191
+ .find("select[name$='[type]']").trigger('change');
192
+
193
+ $('#notifications button[type=reset]').on('click', function () {
194
+ setTimeout(function () {
195
+ $("select[name$='[type]']", $custom_notifications).trigger('change');
196
+ }, 0);
197
+ });
198
+
199
  var $phone_input = $('#admin_phone');
200
  if (BooklyL10n.intlTelInput.enabled) {
201
  $phone_input.intlTelInput({
202
  preferredCountries: [BooklyL10n.intlTelInput.country],
203
+ initialCountry: BooklyL10n.intlTelInput.country,
204
  geoIpLookup: function (callback) {
205
+ $.get('https://ipinfo.io', function() {}, 'jsonp').always(function(resp) {
 
206
  var countryCode = (resp && resp.country) ? resp.country : '';
207
  callback(countryCode);
208
  });
210
  utilsScript: BooklyL10n.intlTelInput.utils
211
  });
212
  }
213
+ $('#bookly-js-submit-notifications').on('click', function (e) {
214
  e.preventDefault();
215
  var ladda = Ladda.create(this);
216
  ladda.start();
222
  e.preventDefault();
223
  $.ajax({
224
  url : ajaxurl,
225
+ data : { action: 'bookly_send_test_sms', csrf_token : BooklyL10n.csrf_token, phone_number: BooklyL10n.intlTelInput.enabled ? $phone_input.intlTelInput('getNumber') : $phone_input.val() },
226
  dataType : 'json',
227
  xhrFields : { withCredentials: true },
228
  crossDomain : 'withCredentials' in new XMLHttpRequest(),
246
  $recharge_init.on('click', function () {
247
  var ladda = Ladda.create(this);
248
  ladda.start();
249
+ $.get(ajaxurl, {action: 'bookly_init_auto_recharge', csrf_token : BooklyL10n.csrf_token, amount: $recharge_amount.val()}, function () {
250
  }, 'json').always(function (response) {
251
  if (response.success) {
252
  window.location.replace(response.data.paypal_preapproval);
259
  $recharge_decline.on('click', function () {
260
  var ladda = Ladda.create(this);
261
  ladda.start();
262
+ $.get(ajaxurl, {action: 'bookly_decline_auto_recharge', csrf_token : BooklyL10n.csrf_token}, function () {}, 'json')
263
  .always(function (response) {
264
  ladda.stop();
265
  if (response.success) {
334
  data: function (d) {
335
  return {
336
  action: 'bookly_get_purchases_list',
337
+ csrf_token: BooklyL10n.csrf_token,
338
  range: $date_range.data('date')
339
  };
340
  },
389
  data: function (d) {
390
  return {
391
  action: 'bookly_get_sms_list',
392
+ csrf_token: BooklyL10n.csrf_token,
393
  range: $date_range.data('date')
394
  };
395
  },
417
  /**
418
  * Prices Tab.
419
  */
420
+ $("[data-target='#price_list']").one('click', function() {
421
  fillPriceTable();
422
  });
423
+ if ($('form.bookly-login-form').length){
424
  fillPriceTable();
425
  }
426
 
434
  responsive: true,
435
  ajax: {
436
  url : ajaxurl,
437
+ data: { action: 'bookly_get_price_list', csrf_token : BooklyL10n.csrf_token },
438
  dataSrc: 'list'
439
  },
440
  columns: [
486
  responsive: true,
487
  ajax: {
488
  url: ajaxurl,
489
+ data: { action: 'bookly_get_sender_ids_list', csrf_token : BooklyL10n.csrf_token },
490
  dataSrc: function (json) {
491
  if (json.pending) {
492
  $sender_id.val(json.pending);
515
  ladda.start();
516
  $.ajax({
517
  url : ajaxurl,
518
+ data : {action: 'bookly_request_sender_id', csrf_token : BooklyL10n.csrf_token, 'sender_id': $sender_id.val()},
519
  dataType : 'json',
520
  xhrFields: {withCredentials: true},
521
  success: function (response) {
539
  if (confirm(BooklyL10n.are_you_sure)) {
540
  $.ajax({
541
  url: ajaxurl,
542
+ data: {action: 'bookly_reset_sender_id', csrf_token : BooklyL10n.csrf_token},
543
  dataType: 'json',
544
  xhrFields: {withCredentials: true},
545
  success: function (response) {
566
  $.ajax({
567
  method : 'POST',
568
  url : ajaxurl,
569
+ data: {action: 'bookly_cancel_sender_id', csrf_token : BooklyL10n.csrf_token},
570
  dataType : 'json',
571
  success : function (response) {
572
  if (response.success) {
588
  $(this).on('click', function () { dt.ajax.reload(); });
589
  });
590
 
591
+ $("#bookly-js-new-notification").on('click', function () {
592
+ var ladda = Ladda.create(this);
593
+ ladda.start();
594
+ $.ajax({
595
+ url : ajaxurl,
596
+ type: 'POST',
597
+ data: {
598
+ action : 'bookly_create_custom_sms',
599
+ csrf_token: BooklyL10n.csrf_token
600
+ },
601
+ dataType: 'json',
602
+ success: function (response) {
603
+ if (response.success) {
604
+ $custom_notifications.append(response.data.html);
605
+ var $subject = $custom_notifications.find('#notification_' + response.data.id + '_subject'),
606
+ $panel = $subject.closest('.panel-collapse');
607
+ $panel.collapse('show');
608
+ $panel.find("select[name$='[type]']").trigger('change');
609
+ $subject.focus();
610
+ }
611
+ },
612
+ complete: function () {
613
+ ladda.stop();
614
+ }
615
+ });
616
+ });
617
+
618
  $('#bookly-open-tab-sender-id').on('click', function (e) {
619
  e.preventDefault();
620
  $('#sms_tabs li[data-target="#sender_id"]').trigger('click');
backend/modules/sms/templates/_codes.php DELETED
@@ -1,29 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) ),
4
- array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) ),
5
- array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) ),
6
- array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) ),
7
- array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) ),
8
- array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
9
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
10
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
11
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
12
- array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
13
- array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
14
- array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
15
- array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
16
- array( 'code' => 'custom_fields', 'description' => __( 'combined values of all custom fields', 'bookly' ) ),
17
- array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) ),
18
- array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) ),
19
- array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
20
- array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
21
- array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
22
- array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
23
- array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) ),
24
- array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
25
- array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) ),
26
- array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
27
- array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
28
- );
29
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_prepare_notification_short_codes', $codes ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/sms/templates/_codes_cart.php DELETED
@@ -1,15 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'cart_info', 'description' => __( 'cart information', 'bookly' ) ),
4
- array( 'code' => 'cart_info_c', 'description' => __( 'cart information with cancel', 'bookly' ) ),
5
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
6
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
7
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
8
- array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
9
- array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
10
- array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
11
- array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
12
- array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
13
- array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
14
- );
15
- \BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_prepare_cart_notification_short_codes', $codes ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/sms/templates/_codes_client_new_wp_user.php DELETED
@@ -1,14 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
4
- array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
5
- array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
6
- array( 'code' => 'company_name', 'description' => __( 'name of your company', 'bookly' ) ),
7
- array( 'code' => 'company_address', 'description' => __( 'address of your company', 'bookly' ) ),
8
- array( 'code' => 'company_phone', 'description' => __( 'your company phone', 'bookly' ) ),
9
- array( 'code' => 'company_website', 'description' => __( 'this web-site address', 'bookly' ) ),
10
- array( 'code' => 'new_username', 'description' => __( 'customer new username', 'bookly' ) ),
11
- array( 'code' => 'new_password', 'description' => __( 'customer new password', 'bookly' ) ),
12
- array( 'code' => 'site_address', 'description' => __( 'site address', 'bookly' ) ),
13
- );
14
- \BooklyLite\Lib\Utils\Common::Codes( $codes );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/sms/templates/_codes_staff_agenda.php DELETED
@@ -1,7 +0,0 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $codes = array(
3
- array( 'code' => 'tomorrow_date', 'description' => __( 'date of next day', 'bookly' ) ),
4
- array( 'code' => 'next_day_agenda', 'description' => __( 'staff agenda for next day', 'bookly' ) ),
5
- array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
6
- );
7
- \BooklyLite\Lib\Utils\Common::Codes( $codes );
 
 
 
 
 
 
 
backend/modules/sms/templates/_custom_notification.php ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var BooklyLite\Backend\Modules\Notifications\Forms\Notifications $form */
3
+ use BooklyLite\Lib\Entities\CustomerAppointment;
4
+ use BooklyLite\Lib\DataHolders\Notification\Settings;
5
+ use BooklyLite\Lib\Entities\Notification;
6
+
7
+ $id = $notification['id'];
8
+ $notification_settings = (array) json_decode( $notification['settings'], true );
9
+ ?>
10
+ <div class="panel panel-default bookly-js-collapse">
11
+ <div class="panel-heading" role="tab">
12
+ <div class="checkbox bookly-margin-remove">
13
+ <label>
14
+ <input name="notification[<?php echo $id ?>][active]" value="0" type="checkbox" checked="checked" class="hidden">
15
+ <input id="<?php echo $id ?>_active" name="notification[<?php echo $id ?>][active]" value="1" type="checkbox" <?php checked( $notification['active'] ) ?>>
16
+ <a href="#collapse_<?php echo $id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#bookly-js-custom-notifications">
17
+ <?php echo $notification['subject'] ?: __( 'Custom notification', 'bookly' ) ?>
18
+ </a>
19
+ </label>
20
+ <button type="button" class="pull-right btn btn-link bookly-js-delete" style="margin-top: -5px" data-notification_id="<?php echo $id ?>" title="<?php esc_attr_e( 'Delete', 'bookly' ) ?>">
21
+ <span class="ladda-label"><i class="glyphicon glyphicon-trash text-danger"></i></span>
22
+ </button>
23
+ </div>
24
+ </div>
25
+ <div id="collapse_<?php echo $id ?>" class="panel-collapse collapse">
26
+ <div class="panel-body">
27
+ <div class="row">
28
+ <div class="col-md-3">
29
+ <div class="form-group">
30
+ <label for="notification_<?php echo $id ?>_type"><?php _e( 'Type', 'bookly' ) ?></label>
31
+ <select class="form-control" name="notification[<?php echo $id ?>][type]" id="notification_<?php echo $id ?>_type">
32
+ <optgroup label="<?php esc_attr_e( 'Event notification', 'bookly' ) ?>">
33
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ?>" data-set="<?php echo Settings::SET_AFTER_EVENT ?>" <?php selected( $notification['type'], Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED ) ?> data-to='["customer","staff","admin"]'><?php _e( 'Status changed', 'bookly' ) ?></option>
34
+ <option value="<?php echo Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED ?>" data-set="<?php echo Settings::SET_AFTER_EVENT ?>" <?php selected( $notification['type'], Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED ) ?> data-to='["customer","staff","admin"]'><?php _e( 'New booking', 'bookly' ) ?></option>
35
+ </optgroup>
36
+ <optgroup label="<?php esc_attr_e( 'Reminder notification', 'bookly' ) ?>">
37
+ <option value="<?php echo Notification::TYPE_APPOINTMENT_START_TIME ?>" data-set="<?php echo Settings::SET_EXISTING_EVENT_WITH_DATE_AND_TIME ?>" <?php selected( $notification['type'], Notification::TYPE_APPOINTMENT_START_TIME ) ?> data-to='["customer","staff","admin"]'><?php _e( 'Appointment date and time', 'bookly' ) ?></option>
38
+ <option value="<?php echo Notification::TYPE_CUSTOMER_BIRTHDAY ?>" data-set="<?php echo Settings::SET_EXISTING_EVENT_WITH_DATE ?>" <?php selected( $notification['type'], Notification::TYPE_CUSTOMER_BIRTHDAY ) ?> data-to='["customer"]'><?php _e( 'Customer\'s birthday', 'bookly' ) ?></option>
39
+ <option value="<?php echo Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ?>" data-set="<?php echo Settings::SET_EXISTING_EVENT_WITH_DATE_AND_TIME ?>" <?php selected( $notification['type'], Notification::TYPE_LAST_CUSTOMER_APPOINTMENT ) ?> data-to='["customer","staff","admin"]'><?php _e( 'Last client\'s appointment', 'bookly' ) ?></option>
40
+ <option value="<?php echo Notification::TYPE_STAFF_DAY_AGENDA ?>" data-set="<?php echo Settings::SET_EXISTING_EVENT_WITH_DATE_BEFORE ?>" <?php selected( $notification['type'], Notification::TYPE_STAFF_DAY_AGENDA ) ?> data-to='["staff","admin"]'><?php _e( 'Full day agenda', 'bookly' ) ?></option>
41
+ </optgroup>
42
+ </select>
43
+ </div>
44
+ </div>
45
+ <div>
46
+ <?php
47
+ $set = Settings::SET_EXISTING_EVENT_WITH_DATE_AND_TIME;
48
+ $settings = @$notification_settings[ $set ];
49
+ ?>
50
+ <div class="bookly-js-settings bookly-js-<?php echo $set ?>">
51
+ <?php $name = 'notification[' . $id . '][settings][' . $set . ']' ?>
52
+ <div class="col-md-3">
53
+ <div class="form-group">
54
+ <label for="notification_<?php echo $id ?>_status_1"><?php _e( 'With status', 'bookly' ) ?></label>
55
+ <select class="form-control" name="<?php echo $name ?>[status]" id="notification_<?php echo $id ?>_status_1">
56
+ <option value="any"><?php _e( 'Any', 'bookly' ) ?></option>
57
+ <?php foreach ( $statuses as $status ) : ?>
58
+ <option value="<?php echo $status ?>" <?php selected( $settings['status'] == $status ) ?>><?php echo CustomerAppointment::statusToString( $status ) ?></option>
59
+ <?php endforeach ?>
60
+ </select>
61
+ </div>
62
+ </div>
63
+ <div class="col-md-6">
64
+ <label for="notification_<?php echo $id ?>_send_1"><?php _e( 'Send', 'bookly' ) ?></label>
65
+ <div class="form-inline bookly-margin-bottom-sm">
66
+ <div class="form-group">
67
+ <label><input type="radio" name="<?php echo $name ?>[option]" value="1" checked id="notification_<?php echo $id ?>_send_1"></label>
68
+ <select class="form-control" name="<?php echo $name ?>[offset_hours]">
69
+ <?php foreach ( array_merge( range( 1, 24 ), range( 48, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
70
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?></option>
71
+ <?php endforeach ?>
72
+ <option value="43200" <?php selected( @$settings['offset_hours'], 43200 ) ?>>30 <?php _e( 'days', 'bookly' ) ?></option>
73
+ </select>
74
+ <select class="form-control" name="<?php echo $name ?>[perform]">
75
+ <option value="before"><?php _e( 'before', 'bookly' ) ?></option>
76
+ <option value="after"<?php selected( @$settings['perform'] == 'after' ) ?>> <?php _e( 'after', 'bookly' ) ?></option>
77
+ </select>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="form-inline">
82
+ <div class="form-group">
83
+ <label><input type="radio" name="<?php echo $name ?>[option]" value="2" <?php checked( @$settings['option'] == 2 ) ?>></label>
84
+ <select class="form-control" name="<?php echo $name ?>[offset_bidirectional_hours]">
85
+ <?php foreach ( array_merge( array( -672, -504 ), range( -336, -24, 24 ) ) as $hour ) : ?>
86
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( abs( $hour ) * HOUR_IN_SECONDS ) ?> <?php _e( 'before', 'bookly' ) ?></option>
87
+ <?php endforeach ?>
88
+ <option value="0" <?php selected( @$settings['offset_bidirectional_hours'], 0 ) ?>><?php _e( 'on the same day', 'bookly' ) ?></option>
89
+ <?php foreach ( array_merge( range( 24, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
90
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?> <?php _e( 'after', 'bookly' ) ?></option>
91
+ <?php endforeach ?>
92
+ </select>
93
+ <?php _e( 'at', 'bookly' ) ?>
94
+ <select class="form-control" name="<?php echo $name ?>[at_hour]">
95
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
96
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['at_hour'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
97
+ <?php endforeach ?>
98
+ </select>
99
+
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+
105
+ <?php
106
+ $set = Settings::SET_EXISTING_EVENT_WITH_DATE;
107
+ $settings = @$notification_settings[ $set ];
108
+ ?>
109
+ <div class="bookly-js-settings bookly-js-<?php echo $set ?>">
110
+ <?php $name = 'notification[' . $id . '][settings][' . $set . ']' ?>
111
+ <div class="col-md-6">
112
+ <label for="notification_<?php echo $id ?>_send_2"><?php _e( 'Send', 'bookly' ) ?></label>
113
+ <div class="form-inline">
114
+ <div class="form-group">
115
+ <select class="form-control" name="<?php echo $name ?>[offset_bidirectional_hours]">
116
+ <?php foreach ( array_merge( array( -672, -504 ), range( -336, -24, 24 ) ) as $hour ) : ?>
117
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( abs( $hour ) * HOUR_IN_SECONDS ) ?> <?php _e( 'before', 'bookly' ) ?></option>
118
+ <?php endforeach ?>
119
+ <option value="0" <?php selected( @$settings['offset_bidirectional_hours'], 0 ) ?>><?php _e( 'on the same day', 'bookly' ) ?></option>
120
+ <?php foreach ( array_merge( range( 24, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
121
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?> <?php _e( 'after', 'bookly' ) ?></option>
122
+ <?php endforeach ?>
123
+ </select>
124
+ <?php _e( 'at', 'bookly' ) ?>
125
+ <select class="form-control" name="<?php echo $name ?>[at_hour]">
126
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
127
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['at_hour'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
128
+ <?php endforeach ?>
129
+ </select>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <?php
136
+ $set = Settings::SET_EXISTING_EVENT_WITH_DATE_BEFORE;
137
+ $settings = @$notification_settings[ $set ];
138
+ ?>
139
+ <div class="bookly-js-settings bookly-js-<?php echo $set ?>">
140
+ <?php $name = 'notification[' . $id . '][settings][' . $set . ']' ?>
141
+ <div class="col-md-6">
142
+ <label for="notification_<?php echo $id ?>_send_2"><?php _e( 'Send', 'bookly' ) ?></label>
143
+ <div class="form-inline">
144
+ <div class="form-group">
145
+ <select class="form-control" name="<?php echo $name ?>[offset_bidirectional_hours]">
146
+ <?php foreach ( array_merge( array( -672, -504 ), range( -336, -24, 24 ) ) as $hour ) : ?>
147
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( abs( $hour ) * HOUR_IN_SECONDS ) ?> <?php _e( 'before', 'bookly' ) ?></option>
148
+ <?php endforeach ?>
149
+ <option value="0" <?php selected( @$settings['offset_bidirectional_hours'], 0 ) ?>><?php _e( 'on the same day', 'bookly' ) ?></option>
150
+ </select>
151
+ <?php _e( 'at', 'bookly' ) ?>
152
+ <select class="form-control" name="<?php echo $name ?>[at_hour]">
153
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
154
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['at_hour'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
155
+ <?php endforeach ?>
156
+ </select>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+
163
+ <?php
164
+ $set = Settings::SET_AFTER_EVENT;
165
+ $settings = @$notification_settings[ $set ];
166
+ ?>
167
+ <div class="bookly-js-settings bookly-js-<?php echo $set ?>">
168
+ <?php $name = 'notification[' . $id . '][settings][' . $set . ']' ?>
169
+ <div class="col-md-3">
170
+ <div class="form-group">
171
+ <label for="notification_<?php echo $id ?>_status_1" class="bookly-js-with"><?php _e( 'With status', 'bookly' ) ?></label>
172
+ <label for="notification_<?php echo $id ?>_status_1" class="bookly-js-to"><?php _e( 'To', 'bookly' ) ?></label>
173
+ <select class="form-control" name="<?php echo $name ?>[status]" id="notification_<?php echo $id ?>_status_1">
174
+ <option value="any"><?php _e( 'Any', 'bookly' ) ?></option>
175
+ <?php foreach ( $statuses as $status ) : ?>
176
+ <option value="<?php echo $status ?>" <?php selected( $settings['status'] == $status ) ?>><?php echo CustomerAppointment::statusToString( $status ) ?></option>
177
+ <?php endforeach ?>
178
+ </select>
179
+ </div>
180
+ </div>
181
+ <div class="col-md-6">
182
+ <label for="notification_<?php echo $id ?>_send_1"><?php _e( 'Send', 'bookly' ) ?></label>
183
+ <div class="form-inline bookly-margin-bottom-sm">
184
+ <div class="form-group">
185
+ <label><input type="radio" name="<?php echo $name ?>[option]" value="1" checked></label> <?php _e( 'Instantly', 'bookly' ) ?>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="form-inline bookly-margin-bottom-sm">
190
+ <div class="form-group">
191
+ <label><input type="radio" name="<?php echo $name ?>[option]" value="2" <?php checked( @$settings['option'] == 2 ) ?>></label>
192
+ <select class="form-control" name="<?php echo $name ?>[offset_hours]">
193
+ <?php foreach ( array_merge( range( 1, 24 ), range( 48, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
194
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?> <?php _e( 'after', 'bookly' ) ?></option>
195
+ <?php endforeach ?>
196
+ </select>
197
+ <input type="hidden" name="<?php echo $name ?>[perform]" value="after">
198
+ </div>
199
+ </div>
200
+
201
+ <div class="form-inline">
202
+ <div class="form-group">
203
+ <label><input type="radio" name="<?php echo $name ?>[option]" value="3" <?php checked( @$settings['option'] == 3 ) ?>></label>
204
+ <select class="form-control" name="<?php echo $name ?>[offset_bidirectional_hours]">
205
+ <option value="0"><?php _e( 'on the same day', 'bookly' ) ?></option>
206
+ <?php foreach ( array_merge( range( 24, 336, 24 ), array( 504, 672 ) ) as $hour ) : ?>
207
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['offset_bidirectional_hours'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) ?> <?php _e( 'after', 'bookly' ) ?></option>
208
+ <?php endforeach ?>
209
+ </select>
210
+ <?php _e( 'at', 'bookly' ) ?>
211
+ <select class="form-control" name="<?php echo $name ?>[at_hour]">
212
+ <?php foreach ( range( 0, 23 ) as $hour ) : ?>
213
+ <option value="<?php echo $hour ?>" <?php selected( @$settings['at_hour'], $hour ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false ) ?></option>
214
+ <?php endforeach ?>
215
+ </select>
216
+
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+
223
+ <div class="row">
224
+ <div class="col-md-6">
225
+ <div class="form-group">
226
+ <label for="notification_<?php echo $id ?>_subject"><?php _e( 'Notification name', 'bookly' ) ?></label>
227
+ <input type="text" class="form-control" id="notification_<?php echo $id ?>_subject" name="notification[<?php echo $id ?>][subject]" value="<?php echo esc_attr( $notification['subject'] ) ?>" />
228
+ </div>
229
+ </div>
230
+ </div>
231
+
232
+ <div class="row">
233
+ <div class="col-md-4">
234
+ <div class="form-group">
235
+ <label><?php _e( 'Recipient', 'bookly' ) ?></label>
236
+ <br>
237
+ <label class="checkbox-inline">
238
+ <input type="hidden" name="notification[<?php echo $id ?>][to_customer]" value="0">
239
+ <input type="checkbox" name="notification[<?php echo $id ?>][to_customer]" value="1" <?php checked( $notification['to_customer'] ) ?>> <?php _e( 'Client', 'bookly' ) ?>
240
+ </label>
241
+ <label class="checkbox-inline">
242
+ <input type="hidden" name="notification[<?php echo $id ?>][to_staff]" value="0">
243
+ <input type="checkbox" name="notification[<?php echo $id ?>][to_staff]" value="1" <?php checked( $notification['to_staff'] ) ?>> <?php _e( 'Staff', 'bookly' ) ?>
244
+ </label>
245
+ <label class="checkbox-inline">
246
+ <input type="hidden" name="notification[<?php echo $id ?>][to_admin]" value="0">
247
+ <input type="checkbox" name="notification[<?php echo $id ?>][to_admin]" value="1" <?php checked( $notification['to_admin'] ) ?>> <?php _e( 'Administrators', 'bookly' ) ?>
248
+ </label>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <?php $form->renderEditor( $id ) ?>
254
+
255
+ <div class="form-group">
256
+ <label><?php _e( 'Codes', 'bookly' ) ?></label>
257
+ <?php foreach ( Notification::getCustomNotificationTypes() as $notification_type ) :
258
+ $form->renderCodes( $notification_type );
259
+ endforeach ?>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </div>
backend/modules/sms/templates/_notifications.php CHANGED
@@ -1,7 +1,8 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- /** @var BooklyLite\Backend\Modules\Notifications\Forms\Notifications $form */
3
- $collapse_id = 0;
4
- $form_data = $form->getData();
 
5
  ?>
6
  <form action="<?php echo esc_url( remove_query_arg( array( 'paypal_result', 'auto-recharge', 'tab' ) ) ) ?>" method="post">
7
  <input type="hidden" name="form-notifications">
@@ -17,40 +18,37 @@
17
  </div>
18
  </div>
19
  </div>
20
- <?php if ( $form->types['combined'] || \BooklyLite\Lib\Utils\Common::isPluginActive( 'bookly-addon-recurring-appointments/main.php' ) ) : ?>
21
- <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Single', 'bookly' ) ?></h4>
22
- <?php endif ?>
23
- <div class="panel-group bookly-margin-vertical-xlg" id="accordion" role="tablist" aria-multiselectable="true">
24
- <?php foreach ( $form->types['single'] as $type ) : ?>
 
 
25
  <div class="panel panel-default bookly-js-collapse">
26
  <div class="panel-heading" role="tab">
27
  <div class="checkbox bookly-margin-remove">
28
  <label>
29
- <input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden"/>
30
- <input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?> />
31
- <a class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse_<?php echo ++ $collapse_id ?>">
32
- <?php echo $form_data[ $type ]['name'] ?>
33
  </a>
34
  </label>
35
  </div>
36
  </div>
37
- <div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
38
  <div class="panel-body">
39
 
40
- <?php $form->renderSendingTime( $type ) ?>
41
- <?php $form->renderEditor( $type ) ?>
42
- <?php $form->renderCopy( $type ) ?>
43
 
44
  <div class="form-group">
45
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
46
- <?php switch ( $type ) :
47
- case 'staff_agenda': include '_codes_staff_agenda.php'; break;
48
- case 'client_new_wp_user': include '_codes_client_new_wp_user.php'; break;
49
- default: include '_codes.php';
50
- endswitch ?>
51
  </div>
52
  </div>
53
-
54
  </div>
55
  </div>
56
  <?php endforeach ?>
@@ -58,29 +56,31 @@
58
 
59
  <?php if ( $form->types['combined'] ) : ?>
60
  <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
61
- <div class="panel-group bookly-margin-vertical-xlg" id="accordion" role="tablist" aria-multiselectable="true">
62
- <?php foreach ( $form->types['combined'] as $type ) : ?>
 
 
63
  <div class="panel panel-default bookly-js-collapse">
64
  <div class="panel-heading" role="tab">
65
  <div class="checkbox bookly-margin-remove">
66
  <label>
67
- <input name="<?php echo $type ?>[active]" value="0" type="checkbox" class="hidden" checked>
68
- <input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?>>
69
- <a class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse_<?php echo ++ $collapse_id ?>">
70
- <?php echo $form_data[ $type ]['name'] ?>
71
  </a>
72
  </label>
73
  </div>
74
  </div>
75
- <div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
76
  <div class="panel-body">
77
- <?php $form->renderSendingTime( $type ) ?>
78
- <?php $form->renderEditor( $type ) ?>
79
- <?php $form->renderCopy( $type ) ?>
80
 
81
  <div class="form-group">
82
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
83
- <?php include '_codes_cart.php' ?>
84
  </div>
85
  </div>
86
  </div>
@@ -89,19 +89,45 @@
89
  </div>
90
  <?php endif ?>
91
 
92
- <?php do_action( 'bookly_render_sms_notifications', $form ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  <div class="alert alert-info">
95
- <?php if ( is_multisite() ) : ?>
96
- <p><?php printf( __( 'To send scheduled notifications please refer to <a href="%1$s">Bookly Multisite</a> add-on <a href="%2$s">message</a>.', 'bookly' ), 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
97
- <?php else : ?>
98
- <p><?php _e( 'To send scheduled notifications please execute the following script hourly with your cron:', 'bookly' ) ?></p><br />
99
- <code class="bookly-text-wrap">php -f <?php echo $cron_path ?></code>
100
- <?php endif ?>
 
 
 
 
 
 
 
101
  </div>
102
 
103
  <div class="panel-footer">
104
- <?php \BooklyLite\Lib\Utils\Common::submitButton( 'js-submit-notifications' ) ?>
105
- <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
106
  </div>
107
  </form>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Lib\Entities\Notification;
3
+ use BooklyLite\Lib\Proxy;
4
+ use BooklyLite\Lib\Utils\Common;
5
+ /** @var BooklyLite\Backend\Modules\Notifications\Forms\Notifications $form */
6
  ?>
7
  <form action="<?php echo esc_url( remove_query_arg( array( 'paypal_result', 'auto-recharge', 'tab' ) ) ) ?>" method="post">
8
  <input type="hidden" name="form-notifications">
18
  </div>
19
  </div>
20
  </div>
21
+
22
+ <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Single', 'bookly' ) ?></h4>
23
+
24
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-single-notifications" role="tablist" aria-multiselectable="true">
25
+ <?php foreach ( $form->getNotifications( 'single' ) as $notification ) :
26
+ $id = $notification['id'];
27
+ ?>
28
  <div class="panel panel-default bookly-js-collapse">
29
  <div class="panel-heading" role="tab">
30
  <div class="checkbox bookly-margin-remove">
31
  <label>
32
+ <input name="notification[<?php echo $id ?>][active]" value="0" type="checkbox" checked="checked" class="hidden">
33
+ <input id="<?php echo $id ?>_active" name="notification[<?php echo $id ?>][active]" value="1" type="checkbox" <?php checked( $notification['active'] ) ?>>
34
+ <a href="#collapse_<?php echo $id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#bookly-js-single-notifications">
35
+ <?php echo Notification::getName( $notification['type'] ) ?>
36
  </a>
37
  </label>
38
  </div>
39
  </div>
40
+ <div id="collapse_<?php echo $id ?>" class="panel-collapse collapse">
41
  <div class="panel-body">
42
 
43
+ <?php $form->renderSendingTime( $notification ) ?>
44
+ <?php $form->renderEditor( $id ) ?>
45
+ <?php $form->renderCopy( $notification ) ?>
46
 
47
  <div class="form-group">
48
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
49
+ <?php $form->renderCodes( $notification['type'] ) ?>
 
 
 
 
50
  </div>
51
  </div>
 
52
  </div>
53
  </div>
54
  <?php endforeach ?>
56
 
57
  <?php if ( $form->types['combined'] ) : ?>
58
  <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
59
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-combined-notifications" role="tablist" aria-multiselectable="true">
60
+ <?php foreach ( $form->getNotifications( 'combined' ) as $notification ) :
61
+ $id = $notification['id'];
62
+ ?>
63
  <div class="panel panel-default bookly-js-collapse">
64
  <div class="panel-heading" role="tab">
65
  <div class="checkbox bookly-margin-remove">
66
  <label>
67
+ <input name="notification[<?php echo $id ?>][active]" value="0" type="checkbox" checked="checked" class="hidden">
68
+ <input id="<?php echo $id ?>_active" name="notification[<?php echo $id ?>][active]" value="1" type="checkbox" <?php checked( $notification['active'] ) ?>>
69
+ <a href="#collapse_<?php echo $id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#bookly-js-combined-notifications">
70
+ <?php echo Notification::getName( $notification['type'] ) ?>
71
  </a>
72
  </label>
73
  </div>
74
  </div>
75
+ <div id="collapse_<?php echo $id ?>" class="panel-collapse collapse">
76
  <div class="panel-body">
77
+
78
+ <?php $form->renderEditor( $id ) ?>
79
+ <?php $form->renderCopy( $notification ) ?>
80
 
81
  <div class="form-group">
82
  <label><?php _e( 'Codes', 'bookly' ) ?></label>
83
+ <?php $form->renderCodes( $notification['type'] ) ?>
84
  </div>
85
  </div>
86
  </div>
89
  </div>
90
  <?php endif ?>
91
 
92
+ <?php Proxy\Shared::renderSmsNotifications( $form ) ?>
93
+
94
+ <h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Custom', 'bookly' ) ?></h4>
95
+ <div class="panel-group bookly-margin-vertical-xlg" id="bookly-js-custom-notifications">
96
+ <?php foreach ( $form->getNotifications( 'custom' ) as $notification ) :
97
+ $this->render( '_custom_notification', compact( 'form', 'notification', 'statuses' ) );
98
+ endforeach ?>
99
+ </div>
100
+
101
+ <div class="row">
102
+ <div class="col-sm-12">
103
+ <div class="form-group">
104
+ <button id="bookly-js-new-notification" type="button" class="btn btn-xlg btn-block btn-success-outline">
105
+ <span class="ladda-label"><i class="dashicons dashicons-plus-alt"></i>
106
+ <?php _e( 'New Notification', 'bookly' ) ?>
107
+ </span>
108
+ </button>
109
+ </div>
110
+ </div>
111
+ </div>
112
 
113
  <div class="alert alert-info">
114
+ <div class="row">
115
+ <div class="col-md-10">
116
+ <?php if ( is_multisite() ) : ?>
117
+ <p><?php printf( __( 'To send scheduled notifications please refer to <a href="%1$s">Bookly Multisite</a> add-on <a href="%2$s">message</a>.', 'bookly' ), 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
118
+ <?php else : ?>
119
+ <p><?php _e( 'To send scheduled notifications please execute the following command hourly with your cron:', 'bookly' ) ?></p><br/>
120
+ <code class="bookly-text-wrap">wget -q -O - <?php echo $cron_uri ?></code>
121
+ <?php endif ?>
122
+ </div>
123
+ <div class="col-md-2">
124
+ <?php Common::optionToggle( 'bookly_ntf_processing_interval', __( 'Notification period', 'bookly' ), __( 'Set period of time when system will attempt to deliver notification to user. Notification will be discarded after period expiration.', 'bookly' ), $bookly_ntf_processing_interval_values ) ?>
125
+ </div>
126
+ </div>
127
  </div>
128
 
129
  <div class="panel-footer">
130
+ <?php Common::submitButton( 'bookly-js-submit-notifications' ) ?>
131
+ <?php Common::resetButton() ?>
132
  </div>
133
  </form>
backend/modules/sms/templates/index.php CHANGED
@@ -55,7 +55,7 @@
55
  <li class="bookly-nav-item" data-toggle="tab" data-target="#auto_recharge"><?php _e( 'Auto-Recharge', 'bookly' ) ?></li>
56
  <li class="bookly-nav-item" data-toggle="tab" data-target="#purchases"><?php _e( 'Purchases', 'bookly' ) ?></li>
57
  <li class="bookly-nav-item" data-toggle="tab" data-target="#sms_details"><?php _e( 'SMS Details', 'bookly' ) ?></li>
58
- <li class="bookly-nav-item" data-toggle="tab" data-target="#price"><?php _e( 'Price list', 'bookly' ) ?></li>
59
  <li class="bookly-nav-item" data-toggle="tab" data-target="#sender_id"><?php _e( 'Sender ID', 'bookly' ) ?></li>
60
  </ul>
61
  <?php endif ?>
@@ -67,7 +67,7 @@
67
  <div class="tab-pane" id="auto_recharge"><?php include '_auto_recharge.php' ?></div>
68
  <div class="tab-pane" id="purchases"><?php include '_purchases.php' ?></div>
69
  <div class="tab-pane" id="sms_details"><?php include '_sms_details.php' ?></div>
70
- <div class="tab-pane" id="price"><?php include '_price.php' ?></div>
71
  <div class="tab-pane" id="sender_id"><?php include '_sender_id.php' ?></div>
72
  </div>
73
  <?php else : ?>
@@ -79,16 +79,16 @@
79
  <div class="row">
80
  <div class="col-md-4">
81
  <div class="well">
82
- <form method="post" class="ab-login-form" action="<?php echo esc_url( remove_query_arg( array( 'paypal_result', 'auto-recharge', 'tab' ) ) ) ?>">
83
  <fieldset>
84
  <legend><?php _e( 'Login', 'bookly' ) ?></legend>
85
  <div class="form-group">
86
- <label for="ab_username"><?php _e( 'Email', 'bookly' ) ?></label>
87
- <input id="ab_username" class="form-control" type="text" required="required" value="" name="username">
88
  </div>
89
  <div class="form-group">
90
- <label for="ab_password"><?php _e( 'Password', 'bookly' ) ?></label>
91
- <input id="ab_password" class="form-control" type="password" required="required" name="password">
92
  </div>
93
  <div class="form-group">
94
  <button type="submit" name="form-login" class="btn btn-success pull-right"><?php _e( 'Log In', 'bookly' ) ?></button>
@@ -98,38 +98,38 @@
98
  </fieldset>
99
  </form>
100
 
101
- <form method="post" class="ab-register-form" style="display: none;">
102
  <fieldset>
103
  <legend><?php _e( 'Registration', 'bookly' ) ?></legend>
104
  <div class="form-group">
105
- <label for="ab_r_username"><?php _e( 'Email', 'bookly' ) ?></label>
106
- <input id="ab_r_username" name="username" class="form-control" required="required" value="" type="text">
107
  </div>
108
  <div class="form-group">
109
- <label for="ab_r_password"><?php _e( 'Password', 'bookly' ) ?></label>
110
- <input id="ab_r_password" name="password" class="form-control" required="required" value="" type="password">
111
  </div>
112
  <div class="form-group">
113
- <label for="ab_r_repeat_password"><?php _e( 'Repeat password', 'bookly' ) ?></label>
114
- <input id="ab_r_repeat_password" name="password_repeat" class="form-control" required="required" value="" type="password">
115
  </div>
116
  <div class="form-group">
117
  <div class="checkbox">
118
- <label for="ab_r_tos">
119
- <input id="ab_r_tos" name="accept_tos" required="required" value="1" type="checkbox">
120
- <?php _e( 'Accept <a href="javascript:void(0)" data-toggle="modal" data-target="#ab-tos">Terms & Conditions</a>', 'bookly' ) ?>
121
  </label>
122
  </div>
123
  </div>
124
 
125
  <div class="form-group">
126
  <button type="submit" name="form-registration" class="btn btn-success pull-right"><?php _e( 'Register', 'bookly' ) ?></button>
127
- <a href="#" class="ab--show-login-form"><?php _e( 'Log In', 'bookly' ) ?></a>
128
  </div>
129
  </fieldset>
130
  </form>
131
 
132
- <form method="post" class="ab-forgot-form" style="display: none;">
133
  <fieldset>
134
  <legend><?php _e( 'Forgot password', 'bookly' ) ?></legend>
135
  <div class="form-group">
@@ -146,7 +146,7 @@
146
  </div>
147
  <div class="form-group">
148
  <button class="btn btn-success pull-right form-forgot-next" data-step="0"><?php _e( 'Next', 'bookly' ) ?></button>
149
- <a href="#" class="ab--show-login-form"><?php _e( 'Log In', 'bookly' ) ?></a>
150
  </div>
151
  </fieldset>
152
  </form>
@@ -184,6 +184,7 @@
184
  </div>
185
  </div>
186
  <div class="modal-footer">
 
187
  <?php \BooklyLite\Lib\Utils\Common::submitButton( 'ajax-send-change-password', 'btn-sm' ) ?>
188
  </div>
189
  <input type="hidden" name="action" value="bookly_change_password">
@@ -193,8 +194,7 @@
193
  </div>
194
 
195
  <?php else : ?>
196
-
197
- <div class="modal fade" id="ab-tos" tabindex=-1 role="dialog">
198
  <div class="modal-dialog">
199
  <div class="modal-content">
200
  <div class="modal-header">
@@ -202,52 +202,11 @@
202
  <div class="modal-title h2"><?php _e( 'Terms & Conditions', 'bookly' ) ?></div>
203
  </div>
204
  <div class="modal-body">
205
- <p>Sivid Software Limited, Belize trading as Bookly SMS undertakes to provide the Customer with value-added SMS services ("the Service"). Bookly SMS will use its reasonable endeavors to provide a prompt and continuing Service but will not be liable for any loss of data resulting from delays, non-deliveries, missed deliveries, or service interruptions caused by events beyond the control of Bookly SMS, or by errors or omissions of the Customer. Bookly SMS specifically excludes any warranty as to the accuracy of information received through the Service.</p>
206
- <p>Save as expressly set out herein, all conditions or warranties which may be implied or incorporated into this contract by law or otherwise are hereby expressly excluded to the extent permitted by law. In no circumstances whatsoever will Bookly SMS be liable for economic, indirect or consequential loss.</p>
207
- <p>Save where the Service is terminated by Bookly SMS without cause, the Customer shall not be entitled to a refund of subscriptions paid.</p>
208
- <h3>Term, Suspension and Termination of Service</h3>
209
- <p>This contract shall be for a term of 3 months from the date of adding money to balance by the Customer or the contract is terminated in accordance with the terms hereof.</p>
210
- <p>Bookly SMS may elect to suspend or terminate the Service immediately and without prior notice, on breach of any of the terms and conditions of this contract, including without limitation late or non-payment of sums due.</p>
211
- <p>From time to time certain mobile gateways, servers, or the whole or part of the Service may be closed down for routine repair, upgrade or maintenance work. Bookly SMS shall give as much notice as in the circumstances is reasonable and shall endeavor to carry out such works during the scheduled maintenance periods as published from time to time.</p>
212
- <h3>Improper Use and Liabilities</h3>
213
- <h4>Use by the Customer</h4>
214
- <p>The Customer acknowledges that it may only use the Service for lawful purposes. The Customer warrants that:</p>
215
- <ul>
216
- <li>it shall not (or authorise or permit any other party to) use the Service to receive or transmit material which is in violation of any law, regulation or the Bookly SMS Acceptable Use Policy, or which is obscene, threatening, menacing, offensive, defamatory, in breach of confidence, in breach of any intellectual property right (including copyright), or otherwise unlawful;</li>
217
- <li>it shall not knowingly or recklessly transmit any electronic material (including viruses) through the Service which shall cause or is likely to cause detriment or harm, in any degree, to computer systems owned by Bookly SMS, other customers of the Service, or any other Service users;</li>
218
- <li>it shall not use any source address that is not allocated for its use by Bookly SMS;</li>
219
- <li>it as the registered user of the account will keep all allocated username(s) and password(s) secure and not let them become public knowledge and that the password(s) will not be stored anywhere on a computer in plain text;</li>
220
- <li>if any password for the Service becomes known to any other unauthorised user it will inform Bookly SMS immediately;</li>
221
- <li>all information they provide, including during service application, will be accurate and correct;</li>
222
- <li>any breach of these obligations shall entitle Bookly SMS to immediately terminate the Service to the Customer without notice.</li>
223
- </ul>
224
- <p>The Customer hereby agrees to fully indemnify and to hold Bookly SMS harmless from and against any claim brought by a third party resulting from the use of the Service by the Customer and in respect of all losses, costs, actions, proceedings, claims, damages, expenses (including reasonable legal costs and expenses), or liabilities, whatsoever suffered or incurred directly by Bookly SMS in consequence of the Customer's breach or non-observance of these terms and conditions.</p>
225
- <p>The Customer shall defend and pay all costs, damages, awards, fees (including any reasonable legal fees) and judgments awarded against Bookly SMS arising from the above claims and shall provide Bookly SMS with notice of such claims, full authority to defend, compromise or settle such claims and reasonable assistance necessary to defend such claims, at the Customer's sole expense.</p>
226
- <p>The Customer shall be liable to pay all and any additional charges in connection with the use of the Service including those levied by its telephone service provider(s).</p>
227
- <h4>Use by others</h4>
228
- <p>The Customer acknowledges that Bookly SMS is unable to exercise control over the content of information passing over the Service, and Bookly SMS hereby excludes all liability of any kind for the transmission or reception of infringing information of whatever nature.</p>
229
- <h3>Prices</h3>
230
- <p>All prices are subject to change without notice. The prices shown in this online price list supersede all previous prices. However, we cannot control price increases by our suppliers. We also reserve the right to correct misprints.</p>
231
- <h3>Taxes</h3>
232
- <p>Fees and all other amounts mentioned in this Agreement do not include any taxes, all of which will be paid by Customer (except for Bookly SMS income taxes). In the event that Bookly SMS is required by applicable law to pay or remit such Taxes, Customer will reimburse MessageMedia for such amounts.</p>
233
- <h3>Password</h3>
234
- <p>Bookly SMS reserves the right to change the Customer's allocated password(s) at any time at its sole discretion.</p>
235
- <h3>Data Protection</h3>
236
- <p>You agree that we may put your name and other information obtained about you from your subscription and the sales process into a computerised directory for internal use only, until we receive specific written instructions from you. Note that no personal details will be passed from us onto other companies, organisations or individuals not connected with servicing your subscription.</p>
237
- <h3>Trial Accounts</h3>
238
- <p>Trial (or evaluation) account Customers acknowledge that access to the Service may be restricted at the sole discretion of Bookly SMS in the interests of fully subscribed customers.</p>
239
- <h3>Refunds</h3>
240
- <p>Refunds will be given at the discretion of the Company Management.</p>
241
- <h3>General</h3>
242
- <p>Bookly SMS reserves the right to vary these terms and conditions from time to time. Such changes shall be notified to the Customer by posting on the Bookly SMS Web site. Changes in this manner shall be deemed to have been accepted if the Customer continues to use the Service after a period of two weeks from the date of posting on the Web site.</p>
243
- <p>Bookly SMS shall not be liable in respect of any breach of this contract due to any cause beyond its reasonable control including but not limited to, inclement weather, hardware failures, network outages, act or omission of Government or public telephone operators or other competent authority or other party for whom Bookly SMS is not responsible.</p>
244
- <p>The Customer acknowledges that it has read and accepts the terms of this contract. Use of the service by the Customer shall be deemed acceptance of the terms of this contract.</p>
245
- <p>This contract shall be governed by and construed in accordance with the laws of Belize and the Customer hereby submits to the exclusive jurisdiction of the courts of Belize.</p>
246
  </div>
247
  </div>
248
  </div>
249
  </div>
250
-
251
  <?php endif ?>
252
  </div>
253
  </div>
55
  <li class="bookly-nav-item" data-toggle="tab" data-target="#auto_recharge"><?php _e( 'Auto-Recharge', 'bookly' ) ?></li>
56
  <li class="bookly-nav-item" data-toggle="tab" data-target="#purchases"><?php _e( 'Purchases', 'bookly' ) ?></li>
57
  <li class="bookly-nav-item" data-toggle="tab" data-target="#sms_details"><?php _e( 'SMS Details', 'bookly' ) ?></li>
58
+ <li class="bookly-nav-item" data-toggle="tab" data-target="#price_list"><?php _e( 'Price list', 'bookly' ) ?></li>
59
  <li class="bookly-nav-item" data-toggle="tab" data-target="#sender_id"><?php _e( 'Sender ID', 'bookly' ) ?></li>
60
  </ul>
61
  <?php endif ?>
67
  <div class="tab-pane" id="auto_recharge"><?php include '_auto_recharge.php' ?></div>
68
  <div class="tab-pane" id="purchases"><?php include '_purchases.php' ?></div>
69
  <div class="tab-pane" id="sms_details"><?php include '_sms_details.php' ?></div>
70
+ <div class="tab-pane" id="price_list"><?php include '_price.php' ?></div>
71
  <div class="tab-pane" id="sender_id"><?php include '_sender_id.php' ?></div>
72
  </div>
73
  <?php else : ?>
79
  <div class="row">
80
  <div class="col-md-4">
81
  <div class="well">
82
+ <form method="post" class="bookly-login-form" action="<?php echo esc_url( remove_query_arg( array( 'paypal_result', 'auto-recharge', 'tab' ) ) ) ?>">
83
  <fieldset>
84
  <legend><?php _e( 'Login', 'bookly' ) ?></legend>
85
  <div class="form-group">
86
+ <label for="bookly-username"><?php _e( 'Email', 'bookly' ) ?></label>
87
+ <input id="bookly-username" class="form-control" type="text" required="required" value="" name="username">
88
  </div>
89
  <div class="form-group">
90
+ <label for="bookly-password"><?php _e( 'Password', 'bookly' ) ?></label>
91
+ <input id="bookly-password" class="form-control" type="password" required="required" name="password">
92
  </div>
93
  <div class="form-group">
94
  <button type="submit" name="form-login" class="btn btn-success pull-right"><?php _e( 'Log In', 'bookly' ) ?></button>
98
  </fieldset>
99
  </form>
100
 
101
+ <form method="post" class="bookly-register-form" style="display: none;">
102
  <fieldset>
103
  <legend><?php _e( 'Registration', 'bookly' ) ?></legend>
104
  <div class="form-group">
105
+ <label for="bookly-r-username"><?php _e( 'Email', 'bookly' ) ?></label>
106
+ <input id="bookly-r-username" name="username" class="form-control" required="required" value="" type="text">
107
  </div>
108
  <div class="form-group">
109
+ <label for="bookly-r-password"><?php _e( 'Password', 'bookly' ) ?></label>
110
+ <input id="bookly-r-password" name="password" class="form-control" required="required" value="" type="password">
111
  </div>
112
  <div class="form-group">
113
+ <label for="bookly-r-repeat-password"><?php _e( 'Repeat password', 'bookly' ) ?></label>
114
+ <input id="bookly-r-repeat-password" name="password_repeat" class="form-control" required="required" value="" type="password">
115
  </div>
116
  <div class="form-group">
117
  <div class="checkbox">
118
+ <label for="bookly-r-tos">
119
+ <input id="bookly-r-tos" name="accept_tos" required="required" value="1" type="checkbox">
120
+ <?php _e( 'Accept <a href="#" data-toggle="modal" data-target="#bookly-tos">Terms & Conditions</a>', 'bookly' ) ?>
121
  </label>
122
  </div>
123
  </div>
124
 
125
  <div class="form-group">
126
  <button type="submit" name="form-registration" class="btn btn-success pull-right"><?php _e( 'Register', 'bookly' ) ?></button>
127
+ <a href="#" class="bookly-show-login-form"><?php _e( 'Log In', 'bookly' ) ?></a>
128
  </div>
129
  </fieldset>
130
  </form>
131
 
132
+ <form method="post" class="bookly-forgot-form" style="display: none;">
133
  <fieldset>
134
  <legend><?php _e( 'Forgot password', 'bookly' ) ?></legend>
135
  <div class="form-group">
146
  </div>
147
  <div class="form-group">
148
  <button class="btn btn-success pull-right form-forgot-next" data-step="0"><?php _e( 'Next', 'bookly' ) ?></button>
149
+ <a href="#" class="bookly-show-login-form"><?php _e( 'Log In', 'bookly' ) ?></a>
150
  </div>
151
  </fieldset>
152
  </form>
184
  </div>
185
  </div>
186
  <div class="modal-footer">
187
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
188
  <?php \BooklyLite\Lib\Utils\Common::submitButton( 'ajax-send-change-password', 'btn-sm' ) ?>
189
  </div>
190
  <input type="hidden" name="action" value="bookly_change_password">
194
  </div>
195
 
196
  <?php else : ?>
197
+ <div class="modal fade" id="bookly-tos" tabindex=-1 role="dialog">
 
198
  <div class="modal-dialog">
199
  <div class="modal-content">
200
  <div class="modal-header">
202
  <div class="modal-title h2"><?php _e( 'Terms & Conditions', 'bookly' ) ?></div>
203
  </div>
204
  <div class="modal-body">
205
+ <iframe src="https://booking-wp-plugin.com/sms-terms.html" width="100%" height="600px"></iframe>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  </div>
207
  </div>
208
  </div>
209
  </div>
 
210
  <?php endif ?>
211
  </div>
212
  </div>
backend/modules/staff/Controller.php CHANGED
@@ -19,9 +19,6 @@ class Controller extends Lib\Base\Controller
19
 
20
  public function index()
21
  {
22
- /** @var \WP_Locale $wp_locale */
23
- global $wp_locale;
24
-
25
  wp_enqueue_media();
26
  $this->enqueueStyles( array(
27
  'frontend' => array_merge(
@@ -49,28 +46,31 @@ class Controller extends Lib\Base\Controller
49
  ? array()
50
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
51
  ),
52
- 'module' => array( 'js/staff.js' => array( 'jquery-ui-sortable', 'jquery', 'jquery-ui-datepicker', 'bookly-range_tools.js' ) ),
 
 
 
 
 
 
53
  ) );
54
 
55
  wp_localize_script( 'bookly-staff.js', 'BooklyL10n', array(
56
- 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
57
- 'close' => __( 'Close', 'bookly' ),
58
- 'days' => array_values( $wp_locale->weekday_abbrev ),
59
- 'months' => array_values( $wp_locale->month ),
60
- 'repeat' => __( 'Repeat every year', 'bookly' ),
61
- 'saved' => __( 'Settings saved.', 'bookly' ),
62
- 'selector' => array( 'all_selected' => __( 'All locations', 'bookly' ), 'nothing_selected' => __( 'No locations selected', 'bookly' ), ),
63
- 'intlTelInput' => array(
64
  'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
65
  'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
66
  'country' => get_option( 'bookly_cst_phone_default_country' ),
67
  ),
68
- 'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
69
- 'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
70
  ) );
71
 
72
  // Allow add-ons to enqueue their assets.
73
- do_action( 'bookly_enqueue_assets_for_staff_profile' );
74
 
75
  $active_staff_id = 1;
76
  $staff_members = Lib\Entities\Staff::query()->where( 'id', $active_staff_id )->fetchArray();
@@ -83,7 +83,7 @@ class Controller extends Lib\Base\Controller
83
  if ( $success_auth ) {
84
  $staff = new Lib\Entities\Staff();
85
  $staff->load( 1 );
86
- $staff->set( 'google_data', null );
87
  $staff->save();
88
 
89
  exit ( '<script>location.href="' . Lib\Google::generateRedirectURI() . '&staff_id=1";</script>' );
@@ -107,9 +107,8 @@ class Controller extends Lib\Base\Controller
107
  {
108
  $staff = new Lib\Entities\Staff();
109
  if ( $staff ) {
110
- $this->render( '_list_item', array( 'staff' => $staff->getFields() ) );
111
  }
112
- exit;
113
  }
114
 
115
  public function executeUpdateStaffPosition()
@@ -118,12 +117,12 @@ class Controller extends Lib\Base\Controller
118
  foreach ( $staff_sorts as $position => $staff_id ) {
119
  $staff_sort = new Lib\Entities\Staff();
120
  $staff_sort->load( $staff_id );
121
- $staff_sort->set( 'position', $position );
122
  $staff_sort->save();
123
  }
124
  }
125
 
126
- public function executeStaffServices()
127
  {
128
  $form = new Forms\StaffServices();
129
  $staff_id = 1;
@@ -132,18 +131,18 @@ class Controller extends Lib\Base\Controller
132
  $services_data = $form->getServicesData();
133
  $uncategorized_services = $form->getUncategorizedServices();
134
 
135
- $this->render( 'services', compact( 'categories', 'services_data', 'uncategorized_services', 'staff_id' ) );
136
- exit;
137
  }
138
 
139
- public function executeStaffSchedule()
140
  {
141
  $staff_id = 1;
142
- $staff = new Lib\Entities\Staff();
143
  $staff->load( $staff_id );
144
  $schedule_items = $staff->getScheduleItems();
145
- $this->render( 'schedule', compact( 'schedule_items', 'staff_id' ) );
146
- exit;
147
  }
148
 
149
  public function executeStaffScheduleUpdate()
@@ -151,7 +150,6 @@ class Controller extends Lib\Base\Controller
151
  $form = new Forms\StaffSchedule();
152
  $form->bind( $this->getPostParameters() );
153
  $form->save();
154
-
155
  wp_send_json_success();
156
  }
157
 
@@ -161,12 +159,12 @@ class Controller extends Lib\Base\Controller
161
  */
162
  public function executeResetBreaks()
163
  {
164
- $breaks = $this->getParameter( 'breaks' );
 
165
 
166
  // Remove all breaks for staff member.
167
  $break = new Lib\Entities\ScheduleItemBreak();
168
  $break->removeBreaksByStaffId( 1 );
169
- $html_breaks = array();
170
 
171
  // Restore previous breaks.
172
  if ( isset( $breaks['breaks'] ) && is_array( $breaks['breaks'] ) ) {
@@ -183,11 +181,11 @@ class Controller extends Lib\Base\Controller
183
  // Make array with breaks (html) for each day.
184
  foreach ( $staff->getScheduleItems() as $item ) {
185
  /** @var Lib\Entities\StaffScheduleItem $item */
186
- $html_breaks[ $item->get( 'id' ) ] = $this->render( '_breaks', array(
187
- 'day_is_not_available' => null === $item->get( 'start_time' ),
188
- 'item' => $item,
189
- 'break_start' => new TimeChoice( array( 'use_empty' => false, 'type' => 'from' ) ),
190
- 'break_end' => new TimeChoice( array( 'use_empty' => false, 'type' => 'to' ) ),
191
  ), false );
192
  }
193
 
@@ -205,15 +203,14 @@ class Controller extends Lib\Base\Controller
205
  wp_send_json_error( array( 'message' => __( 'The start time must be less than the end one', 'bookly' ), ) );
206
  }
207
 
208
- $staffScheduleItem = new Lib\Entities\StaffScheduleItem();
209
- $staffScheduleItem->load( $this->getParameter( 'staff_schedule_item_id' ) );
210
 
211
- $bound = array( $staffScheduleItem->get( 'start_time' ), $staffScheduleItem->get( 'end_time' ) );
212
  $break_id = $this->getParameter( 'break_id', 0 );
213
 
214
  $in_working_time = $working_start <= $start_time && $start_time <= $working_end
215
  && $working_start <= $end_time && $end_time <= $working_end;
216
- if ( !$in_working_time || ! $staffScheduleItem->isBreakIntervalAvailable( $start_time, $end_time, $break_id ) ) {
217
  wp_send_json_error( array( 'message' => __( 'The requested interval is not available', 'bookly' ), ) );
218
  }
219
 
@@ -224,23 +221,23 @@ class Controller extends Lib\Base\Controller
224
  if ( $break_id ) {
225
  $break = new Lib\Entities\ScheduleItemBreak();
226
  $break->load( $break_id );
227
- $break->set( 'start_time', $start_time );
228
- $break->set( 'end_time', $end_time );
229
- $break->save();
230
 
231
  wp_send_json_success( array( 'interval' => $formatted_interval, ) );
232
  } else {
233
  $form = new Forms\StaffScheduleItemBreak();
234
  $form->bind( $this->getPostParameters() );
235
 
236
- $staffScheduleItemBreak = $form->save();
237
- if ( $staffScheduleItemBreak ) {
238
- $breakStart = new TimeChoice( array( 'use_empty' => false, 'type' => 'from', 'bound' => $bound ) );
239
- $breakEnd = new TimeChoice( array( 'use_empty' => false, 'type' => 'bound', 'bound' => $bound ) );
240
  wp_send_json( array(
241
  'success' => true,
242
  'item_content' => $this->render( '_break', array(
243
- 'staff_schedule_item_break_id' => $staffScheduleItemBreak->get( 'id' ),
244
  'formatted_interval' => $formatted_interval,
245
  'break_start_choices' => $breakStart->render( '', $start_time, array( 'class' => 'break-start form-control' ) ),
246
  'break_end_choices' => $breakEnd->render( '', $end_time, array( 'class' => 'break-end form-control' ) ),
@@ -255,7 +252,7 @@ class Controller extends Lib\Base\Controller
255
  public function executeDeleteStaffScheduleBreak()
256
  {
257
  $break = new Lib\Entities\ScheduleItemBreak();
258
- $break->set( 'id', $this->getParameter( 'id', 0 ) );
259
  $break->delete();
260
 
261
  wp_send_json_success();
@@ -266,20 +263,26 @@ class Controller extends Lib\Base\Controller
266
  $form = new Forms\StaffServices();
267
  $form->bind( $this->getPostParameters() );
268
  $form->save();
269
-
270
  wp_send_json_success();
271
  }
272
 
273
  public function executeEditStaff()
274
  {
275
- $calendar_list = array();
276
- $authUrl = null;
277
  $alert = array( 'error' => array() );
278
- $form = new Forms\StaffMemberEdit();
279
  $staff = new Lib\Entities\Staff();
280
  $staff->load( 1 );
281
 
282
- if ( $staff->get( 'google_data' ) == '' ) {
 
 
 
 
 
 
 
 
 
 
283
  if ( get_option( 'bookly_gc_client_id' ) == '' ) {
284
  $authUrl = false;
285
  } else {
@@ -289,7 +292,7 @@ class Controller extends Lib\Base\Controller
289
  } else {
290
  $google = new Lib\Google();
291
  if ( $google->loadByStaff( $staff ) ) {
292
- $calendar_list = $google->getCalendarList();
293
  } else {
294
  foreach ( $google->getErrors() as $error ) {
295
  $alert['error'][] = $error;
@@ -297,21 +300,18 @@ class Controller extends Lib\Base\Controller
297
  }
298
  }
299
 
300
- if ( $gc_errors = Lib\Session::get( 'staff_google_auth_error' ) ) {
301
- foreach ( (array) json_decode( $gc_errors, true ) as $error ) {
302
- $alert['error'][] = $error;
303
- }
304
- Lib\Session::destroy( 'staff_google_auth_error' );
305
- }
306
-
307
- $users_for_staff = Lib\Utils\Common::isCurrentUserAdmin() ? $form->getUsersForStaff( $staff->get( 'id' ) ) : array();
308
 
309
  wp_send_json_success( array(
310
- 'html' => $this->render( 'edit', compact( 'staff', 'users_for_staff', 'authUrl', 'calendar_list' ), false ),
 
 
 
311
  'alert' => $alert,
312
  ) );
313
  }
314
 
 
315
  /**
316
  * Update staff from POST request.
317
  */
@@ -323,7 +323,7 @@ class Controller extends Lib\Base\Controller
323
  if ( get_option( 'bookly_gen_allow_staff_edit_profile' ) ) {
324
  $staff = new Lib\Entities\Staff();
325
  $staff->load( $this->getParameter( 'id' ) );
326
- if ( $staff->get( 'wp_user_id' ) == get_current_user_id() ) {
327
  unset ( $_POST['wp_user_id'] );
328
  break;
329
  }
@@ -332,11 +332,12 @@ class Controller extends Lib\Base\Controller
332
  wp_die( 'Bookly: ' . __( 'You do not have sufficient permissions to access this page.' ) );
333
  } while ( 0 );
334
  }
335
- $form = new Forms\StaffMemberEdit();
 
336
  $form->bind( $this->getPostParameters(), $_FILES );
337
  $employee = $form->save();
338
 
339
- do_action( 'bookly_staff_update', $this->getPostParameters() );
340
 
341
  if ( $employee === false && array_key_exists( 'google_calendar_error', $form->getErrors() ) ) {
342
  $errors = $form->getErrors();
@@ -344,7 +345,7 @@ class Controller extends Lib\Base\Controller
344
  } else {
345
  $wp_users = array();
346
  if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
347
- $form = new Forms\StaffMember();
348
  $wp_users = $form->getUsersForStaff();
349
  }
350
 
@@ -368,17 +369,28 @@ class Controller extends Lib\Base\Controller
368
  {
369
  $staff = new Lib\Entities\Staff();
370
  $staff->load( 1 );
371
- $staff->set( 'attachment_id', null );
372
  $staff->save();
 
373
  wp_send_json_success();
374
  }
375
 
376
  public function executeStaffHolidays()
377
  {
378
- $staff_id = $this->getParameter( 'id', 0 );
379
- $holidays = $this->getHolidays( $staff_id );
380
- $this->render( 'holidays', compact ( 'holidays', 'staff_id' ) );
381
- exit;
 
 
 
 
 
 
 
 
 
 
382
  }
383
 
384
  public function executeStaffHolidaysUpdate()
@@ -400,11 +412,10 @@ class Controller extends Lib\Base\Controller
400
  }
401
  // Add the new event.
402
  } elseif ( $holiday && $day ) {
403
- $wpdb->insert( Lib\Entities\Holiday::getTableName(), array( 'date' => $day, 'repeat_event' => (int) $repeat, 'staff_id' => $staff_id ), array( '%s', '%d', '%d' ) );
404
  }
405
-
406
  // And return refreshed events.
407
- echo $this->getHolidays( $staff_id );
408
  }
409
  exit;
410
  }
@@ -427,7 +438,7 @@ class Controller extends Lib\Base\Controller
427
  }
428
  }
429
 
430
- return json_encode( $holidays );
431
  }
432
 
433
  /**
@@ -439,43 +450,44 @@ class Controller extends Lib\Base\Controller
439
  protected function hasAccess( $action )
440
  {
441
  if ( parent::hasAccess( $action ) ) {
442
-
443
  if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
444
  $staff = new Lib\Entities\Staff();
445
 
446
  switch ( $action ) {
447
  case 'executeEditStaff':
448
  case 'executeDeleteStaffAvatar':
449
- case 'executeStaffServices':
450
  case 'executeStaffSchedule':
451
  case 'executeStaffHolidays':
452
  case 'executeUpdateStaff':
 
453
  $staff->load( $this->getParameter( 'id' ) );
454
  break;
 
 
455
  case 'executeStaffServicesUpdate':
456
  case 'executeStaffHolidaysUpdate':
457
  $staff->load( $this->getParameter( 'staff_id' ) );
458
  break;
459
  case 'executeStaffScheduleHandleBreak':
460
- $staffScheduleItem = new Lib\Entities\StaffScheduleItem();
461
- $staffScheduleItem->load( $this->getParameter( 'staff_schedule_item_id' ) );
462
- $staff->load( $staffScheduleItem->get( 'staff_id' ) );
463
  break;
464
  case 'executeDeleteStaffScheduleBreak':
465
  $break = new Lib\Entities\ScheduleItemBreak();
466
  $break->load( $this->getParameter( 'id' ) );
467
- $staffScheduleItem = new Lib\Entities\StaffScheduleItem();
468
- $staffScheduleItem->load( $break->get( 'staff_schedule_item_id' ) );
469
- $staff->load( $staffScheduleItem->get( 'staff_id' ) );
470
  break;
471
  case 'executeStaffScheduleUpdate':
472
  if ( $this->hasParameter( 'days' ) ) {
473
  foreach ( $this->getParameter( 'days' ) as $id => $day_index ) {
474
- $staffScheduleItem = new Lib\Entities\StaffScheduleItem();
475
- $staffScheduleItem->load( $id );
476
  $staff = new Lib\Entities\Staff();
477
- $staff->load( $staffScheduleItem->get( 'staff_id' ) );
478
- if ( $staff->get( 'wp_user_id' ) != get_current_user_id() ) {
479
  return false;
480
  }
481
  }
@@ -485,7 +497,7 @@ class Controller extends Lib\Base\Controller
485
  return false;
486
  }
487
 
488
- return $staff->get( 'wp_user_id' ) == get_current_user_id();
489
  }
490
 
491
  return true;
@@ -493,16 +505,4 @@ class Controller extends Lib\Base\Controller
493
 
494
  return false;
495
  }
496
-
497
- /**
498
- * Override parent method to add 'wp_ajax_bookly_' prefix
499
- * so current 'execute*' methods look nicer.
500
- *
501
- * @param string $prefix
502
- */
503
- protected function registerWpActions( $prefix = '' )
504
- {
505
- parent::registerWpActions( 'wp_ajax_bookly_' );
506
- }
507
-
508
  }
19
 
20
  public function index()
21
  {
 
 
 
22
  wp_enqueue_media();
23
  $this->enqueueStyles( array(
24
  'frontend' => array_merge(
46
  ? array()
47
  : array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
48
  ),
49
+ 'module' => array(
50
+ 'js/staff-details.js' => array( 'bookly-alert.js', ),
51
+ 'js/staff-services.js' => array( 'bookly-staff-details.js' ),
52
+ 'js/staff-schedule.js' => array( 'bookly-staff-services.js' ),
53
+ 'js/staff-days-off.js' => array( 'bookly-staff-schedule.js' ),
54
+ 'js/staff.js' => array( 'jquery-ui-sortable', 'jquery-ui-datepicker', 'bookly-range_tools.js', 'bookly-staff-days-off.js' ),
55
+ ),
56
  ) );
57
 
58
  wp_localize_script( 'bookly-staff.js', 'BooklyL10n', array(
59
+ 'are_you_sure' => __( 'Are you sure?', 'bookly' ),
60
+ 'saved' => __( 'Settings saved.', 'bookly' ),
61
+ 'capacity_error' => __( 'Min capacity should not be greater than max capacity.', 'bookly' ),
62
+ 'selector' => array( 'all_selected' => __( 'All locations', 'bookly' ), 'nothing_selected' => __( 'No locations selected', 'bookly' ), ),
63
+ 'intlTelInput' => array(
 
 
 
64
  'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
65
  'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
66
  'country' => get_option( 'bookly_cst_phone_default_country' ),
67
  ),
68
+ 'csrf_token' => Lib\Utils\Common::getCsrfToken(),
69
+ 'limitations' => __( '<b class="h4">This function is not available in the Lite version of Bookly.</b><br><br>To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Standard version of Bookly.<br>For more information visit', 'bookly' ) . ' <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
70
  ) );
71
 
72
  // Allow add-ons to enqueue their assets.
73
+ Lib\Proxy\Shared::enqueueAssetsForStaffProfile();
74
 
75
  $active_staff_id = 1;
76
  $staff_members = Lib\Entities\Staff::query()->where( 'id', $active_staff_id )->fetchArray();
83
  if ( $success_auth ) {
84
  $staff = new Lib\Entities\Staff();
85
  $staff->load( 1 );
86
+ $staff->setGoogleData( $google->getAccessToken() );
87
  $staff->save();
88
 
89
  exit ( '<script>location.href="' . Lib\Google::generateRedirectURI() . '&staff_id=1";</script>' );
107
  {
108
  $staff = new Lib\Entities\Staff();
109
  if ( $staff ) {
110
+ wp_send_json_success( array( 'html' => $this->render( '_list_item', array( 'staff' => $staff->getFields() ), false ) ) );
111
  }
 
112
  }
113
 
114
  public function executeUpdateStaffPosition()
117
  foreach ( $staff_sorts as $position => $staff_id ) {
118
  $staff_sort = new Lib\Entities\Staff();
119
  $staff_sort->load( $staff_id );
120
+ $staff_sort->setPosition( $position );
121
  $staff_sort->save();
122
  }
123
  }
124
 
125
+ public function executeGetStaffServices()
126
  {
127
  $form = new Forms\StaffServices();
128
  $staff_id = 1;
131
  $services_data = $form->getServicesData();
132
  $uncategorized_services = $form->getUncategorizedServices();
133
 
134
+ $html = $this->render( 'services', compact( 'categories', 'services_data', 'uncategorized_services', 'staff_id' ), false );
135
+ wp_send_json_success( compact( 'html' ) );
136
  }
137
 
138
+ public function executeGetStaffSchedule()
139
  {
140
  $staff_id = 1;
141
+ $staff = new Lib\Entities\Staff();
142
  $staff->load( $staff_id );
143
  $schedule_items = $staff->getScheduleItems();
144
+ $html = $this->render( 'schedule', compact( 'schedule_items', 'staff_id' ), false );
145
+ wp_send_json_success( compact( 'html' ) );
146
  }
147
 
148
  public function executeStaffScheduleUpdate()
150
  $form = new Forms\StaffSchedule();
151
  $form->bind( $this->getPostParameters() );
152
  $form->save();
 
153
  wp_send_json_success();
154
  }
155
 
159
  */
160
  public function executeResetBreaks()
161
  {
162
+ $breaks = $this->getParameter( 'breaks' );
163
+ $html_breaks = array();
164
 
165
  // Remove all breaks for staff member.
166
  $break = new Lib\Entities\ScheduleItemBreak();
167
  $break->removeBreaksByStaffId( 1 );
 
168
 
169
  // Restore previous breaks.
170
  if ( isset( $breaks['breaks'] ) && is_array( $breaks['breaks'] ) ) {
181
  // Make array with breaks (html) for each day.
182
  foreach ( $staff->getScheduleItems() as $item ) {
183
  /** @var Lib\Entities\StaffScheduleItem $item */
184
+ $html_breaks[ $item->getId() ] = $this->render( '_breaks', array(
185
+ 'day_is_not_available' => null === $item->getStartTime(),
186
+ 'item' => $item,
187
+ 'break_start' => new TimeChoice( array( 'use_empty' => false, 'type' => 'break_from' ) ),
188
+ 'break_end' => new TimeChoice( array( 'use_empty' => false, 'type' => 'to' ) ),
189
  ), false );
190
  }
191
 
203
  wp_send_json_error( array( 'message' => __( 'The start time must be less than the end one', 'bookly' ), ) );
204
  }
205
 
206
+ $res_schedule = new Lib\Entities\StaffScheduleItem();
207
+ $res_schedule->load( $this->getParameter( 'staff_schedule_item_id' ) );
208
 
 
209
  $break_id = $this->getParameter( 'break_id', 0 );
210
 
211
  $in_working_time = $working_start <= $start_time && $start_time <= $working_end
212
  && $working_start <= $end_time && $end_time <= $working_end;
213
+ if ( ! $in_working_time || ! $res_schedule->isBreakIntervalAvailable( $start_time, $end_time, $break_id ) ) {
214
  wp_send_json_error( array( 'message' => __( 'The requested interval is not available', 'bookly' ), ) );
215
  }
216
 
221
  if ( $break_id ) {
222
  $break = new Lib\Entities\ScheduleItemBreak();
223
  $break->load( $break_id );
224
+ $break->setStartTime( $start_time )
225
+ ->setEndTime( $end_time )
226
+ ->save();
227
 
228
  wp_send_json_success( array( 'interval' => $formatted_interval, ) );
229
  } else {
230
  $form = new Forms\StaffScheduleItemBreak();
231
  $form->bind( $this->getPostParameters() );
232
 
233
+ $res_schedule_break = $form->save();
234
+ if ( $res_schedule_break ) {
235
+ $breakStart = new TimeChoice( array( 'use_empty' => false, 'type' => 'break_from' ) );
236
+ $breakEnd = new TimeChoice( array( 'use_empty' => false, 'type' => 'to' ) );
237
  wp_send_json( array(
238
  'success' => true,
239
  'item_content' => $this->render( '_break', array(
240
+ 'staff_schedule_item_break_id' => $res_schedule_break->getId(),
241
  'formatted_interval' => $formatted_interval,
242
  'break_start_choices' => $breakStart->render( '', $start_time, array( 'class' => 'break-start form-control' ) ),
243
  'break_end_choices' => $breakEnd->render( '', $end_time, array( 'class' => 'break-end form-control' ) ),
252
  public function executeDeleteStaffScheduleBreak()
253
  {
254
  $break = new Lib\Entities\ScheduleItemBreak();
255
+ $break->setId( $this->getParameter( 'id', 0 ) );
256
  $break->delete();
257
 
258
  wp_send_json_success();
263
  $form = new Forms\StaffServices();
264
  $form->bind( $this->getPostParameters() );
265
  $form->save();
 
266
  wp_send_json_success();
267
  }
268
 
269
  public function executeEditStaff()
270
  {
 
 
271
  $alert = array( 'error' => array() );
 
272
  $staff = new Lib\Entities\Staff();
273
  $staff->load( 1 );
274
 
275
+ if ( $gc_errors = Lib\Session::get( 'staff_google_auth_error' ) ) {
276
+ foreach ( (array) json_decode( $gc_errors, true ) as $error ) {
277
+ $alert['error'][] = $error;
278
+ }
279
+ Lib\Session::destroy( 'staff_google_auth_error' );
280
+ }
281
+
282
+ $google_calendars = array();
283
+ $authUrl = null;
284
+ $form = new Forms\StaffMemberEdit();
285
+ if ( $staff->getGoogleData() == '' ) {
286
  if ( get_option( 'bookly_gc_client_id' ) == '' ) {
287
  $authUrl = false;
288
  } else {
292
  } else {
293
  $google = new Lib\Google();
294
  if ( $google->loadByStaff( $staff ) ) {
295
+ $google_calendars = $google->getCalendarList();
296
  } else {
297
  foreach ( $google->getErrors() as $error ) {
298
  $alert['error'][] = $error;
300
  }
301
  }
302
 
303
+ $users_for_staff = Lib\Utils\Common::isCurrentUserAdmin() ? $form->getUsersForStaff( $staff->getId() ) : array();
 
 
 
 
 
 
 
304
 
305
  wp_send_json_success( array(
306
+ 'html' => array(
307
+ 'edit' => $this->render( 'edit', compact( 'staff' ), false ),
308
+ 'details' => $this->render( '_details', compact( 'staff', 'authUrl', 'google_calendars', 'users_for_staff' ), false )
309
+ ),
310
  'alert' => $alert,
311
  ) );
312
  }
313
 
314
+
315
  /**
316
  * Update staff from POST request.
317
  */
323
  if ( get_option( 'bookly_gen_allow_staff_edit_profile' ) ) {
324
  $staff = new Lib\Entities\Staff();
325
  $staff->load( $this->getParameter( 'id' ) );
326
+ if ( $staff->getWpUserId() == get_current_user_id() ) {
327
  unset ( $_POST['wp_user_id'] );
328
  break;
329
  }
332
  wp_die( 'Bookly: ' . __( 'You do not have sufficient permissions to access this page.' ) );
333
  } while ( 0 );
334
  }
335
+ $form = new Forms\StaffMemberEdit();
336
+
337
  $form->bind( $this->getPostParameters(), $_FILES );
338
  $employee = $form->save();
339
 
340
+ Lib\Proxy\Shared::updateStaff( $this->getPostParameters() );
341
 
342
  if ( $employee === false && array_key_exists( 'google_calendar_error', $form->getErrors() ) ) {
343
  $errors = $form->getErrors();
345
  } else {
346
  $wp_users = array();
347
  if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
348
+ $form = new Forms\StaffMember();
349
  $wp_users = $form->getUsersForStaff();
350
  }
351
 
369
  {
370
  $staff = new Lib\Entities\Staff();
371
  $staff->load( 1 );
372
+ $staff->setAttachmentId( null );
373
  $staff->save();
374
+
375
  wp_send_json_success();
376
  }
377
 
378
  public function executeStaffHolidays()
379
  {
380
+ /** @var \WP_Locale $wp_locale */
381
+ global $wp_locale;
382
+
383
+ $staff_id = 1;
384
+ $holidays = $this->getHolidays( $staff_id );
385
+ $loading_img = plugins_url( 'bookly-responsive-appointment-booking-tool/backend/resources/images/loading.gif' );
386
+ $start_of_week = (int) get_option( 'start_of_week' );
387
+ $days = array_values( $wp_locale->weekday_abbrev );
388
+ $months = array_values( $wp_locale->month );
389
+ $close = __( 'Close', 'bookly' );
390
+ $repeat = __( 'Repeat every year', 'bookly' );
391
+ $we_are_not_working = __( 'We are not working on this day', 'bookly' );
392
+ $html = $this->render( 'holidays', array(), false );
393
+ wp_send_json_success( compact( 'html', 'holidays', 'days', 'months', 'start_of_week', 'loading_img', 'we_are_not_working', 'repeat', 'close' ) );
394
  }
395
 
396
  public function executeStaffHolidaysUpdate()
412
  }
413
  // Add the new event.
414
  } elseif ( $holiday && $day ) {
415
+ $wpdb->insert( Lib\Entities\Holiday::getTableName(), array( 'date' => $day, 'repeat_event' => (int) $repeat, 'staff_id' => 1 ), array( '%s', '%d', '%d' ) );
416
  }
 
417
  // And return refreshed events.
418
+ echo json_encode( $this->getHolidays( $staff_id ) );
419
  }
420
  exit;
421
  }
438
  }
439
  }
440
 
441
+ return $holidays;
442
  }
443
 
444
  /**
450
  protected function hasAccess( $action )
451
  {
452
  if ( parent::hasAccess( $action ) ) {
 
453
  if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
454
  $staff = new Lib\Entities\Staff();
455
 
456
  switch ( $action ) {
457
  case 'executeEditStaff':
458
  case 'executeDeleteStaffAvatar':
 
459
  case 'executeStaffSchedule':
460
  case 'executeStaffHolidays':
461
  case 'executeUpdateStaff':
462
+ case 'executeGetStaffDetails':
463
  $staff->load( $this->getParameter( 'id' ) );
464
  break;
465
+ case 'executeGetStaffServices':
466
+ case 'executeGetStaffSchedule':
467
  case 'executeStaffServicesUpdate':
468
  case 'executeStaffHolidaysUpdate':
469
  $staff->load( $this->getParameter( 'staff_id' ) );
470
  break;
471
  case 'executeStaffScheduleHandleBreak':
472
+ $res_schedule = new Lib\Entities\StaffScheduleItem();
473
+ $res_schedule->load( $this->getParameter( 'staff_schedule_item_id' ) );
474
+ $staff->load( $res_schedule->getStaffId() );
475
  break;
476
  case 'executeDeleteStaffScheduleBreak':
477
  $break = new Lib\Entities\ScheduleItemBreak();
478
  $break->load( $this->getParameter( 'id' ) );
479
+ $res_schedule = new Lib\Entities\StaffScheduleItem();
480
+ $res_schedule->load( $break->getStaffScheduleItemId() );
481
+ $staff->load( $res_schedule->getStaffId() );
482
  break;
483
  case 'executeStaffScheduleUpdate':
484
  if ( $this->hasParameter( 'days' ) ) {
485
  foreach ( $this->getParameter( 'days' ) as $id => $day_index ) {
486
+ $res_schedule = new Lib\Entities\StaffScheduleItem();
487
+ $res_schedule->load( $id );
488
  $staff = new Lib\Entities\Staff();
489
+ $staff->load( $res_schedule->getStaffId() );
490
+ if ( $staff->getWpUserId() != get_current_user_id() ) {
491
  return false;
492
  }
493
  }
497
  return false;
498
  }
499
 
500
+ return $staff->getWpUserId() == get_current_user_id();
501
  }
502
 
503
  return true;
505
 
506
  return false;
507
  }
 
 
 
 
 
 
 
 
 
 
 
 
508
  }
backend/modules/staff/forms/StaffSchedule.php CHANGED
@@ -20,17 +20,19 @@ class StaffSchedule extends Lib\Base\Form
20
  {
21
  if ( isset( $this->data['days'] ) ) {
22
  foreach ( $this->data['days'] as $id => $day_index ) {
23
- $staffScheduleItem = new Lib\Entities\StaffScheduleItem();
24
- $staffScheduleItem->load( $id );
25
- $staffScheduleItem->set( 'day_index', $day_index );
26
  if ( $this->data['start_time'][ $day_index ] ) {
27
- $staffScheduleItem->set( 'start_time', $this->data['start_time'][ $day_index ] );
28
- $staffScheduleItem->set( 'end_time', $this->data['end_time'][ $day_index ] );
 
29
  } else {
30
- $staffScheduleItem->set( 'start_time', null );
31
- $staffScheduleItem->set( 'end_time', null );
 
32
  }
33
- $staffScheduleItem->save();
34
  }
35
  }
36
  }
20
  {
21
  if ( isset( $this->data['days'] ) ) {
22
  foreach ( $this->data['days'] as $id => $day_index ) {
23
+ $res_schedule = new Lib\Entities\StaffScheduleItem();
24
+ $res_schedule->load( $id );
25
+ $res_schedule->setDayIndex( $day_index );
26
  if ( $this->data['start_time'][ $day_index ] ) {
27
+ $res_schedule
28
+ ->setStartTime( $this->data['start_time'][ $day_index ] )
29
+ ->setEndTime( $this->data['end_time'][ $day_index ] );
30
  } else {
31
+ $res_schedule
32
+ ->setStartTime( null )
33
+ ->setEndTime( null );
34
  }
35
+ $res_schedule->save();
36
  }
37
  }
38
  }
backend/modules/staff/forms/StaffServices.php CHANGED
@@ -28,7 +28,7 @@ class StaffServices extends Lib\Base\Form
28
 
29
  public function configure()
30
  {
31
- $this->setFields( array( 'price', 'deposit', 'service', 'staff_id', 'capacity' ) );
32
  }
33
 
34
  public function load( $staff_id )
@@ -37,21 +37,26 @@ class StaffServices extends Lib\Base\Form
37
  ->select( 'c.name AS category_name, s.*' )
38
  ->innerJoin( 'Service', 's', 's.category_id = c.id' )
39
  ->sortBy( 'c.position, s.position' )
40
- ->where( 's.type', Lib\Entities\Service::TYPE_SIMPLE )
41
  ->fetchArray();
42
  if ( !$data ) {
43
  $data = array();
44
  }
45
 
46
- $this->uncategorized_services = Lib\Entities\Service::query( 's' )->where( 's.category_id', null )->where( 's.type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray();
 
 
 
 
 
47
 
48
  $staff_services = Lib\Entities\StaffService::query( 'ss' )
49
- ->select( 'ss.service_id, ss.price, ss.deposit, 1 as capacity' )
50
  ->where( 'ss.staff_id', 1 )
51
  ->fetchArray();
52
  if ( $staff_services ) {
53
  foreach ( $staff_services as $staff_service ) {
54
- $this->services_data[ $staff_service['service_id'] ] = array( 'price' => $staff_service['price'], 'deposit' => $staff_service['deposit'], 'capacity' => 1 );
55
  }
56
  }
57
 
@@ -70,22 +75,27 @@ class StaffServices extends Lib\Base\Form
70
 
71
  public function save()
72
  {
73
- Lib\Entities\StaffService::query()->delete()->where( 'staff_id', 1 )->execute();
74
- if ( isset( $this->data['service'] ) ) {
75
- foreach ( $this->data['service'] as $service_id ) {
76
- $staff_service = new Lib\Entities\StaffService();
77
- $staff_service->set( 'capacity', 1 )
78
- ->set( 'deposit', empty( $this->data['deposit'] ) ? '100%' : $this->data['deposit'][ $service_id ] )
79
- ->set( 'price', $this->data['price'][ $service_id ] )
80
- ->set( 'service_id', $service_id )
81
- ->set( 'staff_id', 1 )
82
- ->save();
 
 
 
 
 
83
  }
84
  }
85
  }
86
 
87
  /**
88
- * @return Lib\Entities\Category[]|array
89
  */
90
  public function getCategories()
91
  {
28
 
29
  public function configure()
30
  {
31
+ $this->setFields( array( 'price', 'deposit', 'service', 'staff_id', 'capacity_min', 'capacity_max' ) );
32
  }
33
 
34
  public function load( $staff_id )
37
  ->select( 'c.name AS category_name, s.*' )
38
  ->innerJoin( 'Service', 's', 's.category_id = c.id' )
39
  ->sortBy( 'c.position, s.position' )
40
+ ->whereIn( 's.type', array( Lib\Entities\Service::TYPE_SIMPLE, Lib\Entities\Service::TYPE_PACKAGE ) )
41
  ->fetchArray();
42
  if ( !$data ) {
43
  $data = array();
44
  }
45
 
46
+ $this->uncategorized_services = Lib\Entities\Service::query( 's' )
47
+ ->select( 's.*, ss.sub_service_id' )
48
+ ->leftJoin( 'SubService', 'ss', 's.id = ss.service_id' )
49
+ ->where( 's.category_id', null )
50
+ ->whereIn( 's.type', array( Lib\Entities\Service::TYPE_SIMPLE, Lib\Entities\Service::TYPE_PACKAGE ) )
51
+ ->fetchArray();
52
 
53
  $staff_services = Lib\Entities\StaffService::query( 'ss' )
54
+ ->select( 'ss.service_id, ss.price, ss.deposit' )
55
  ->where( 'ss.staff_id', 1 )
56
  ->fetchArray();
57
  if ( $staff_services ) {
58
  foreach ( $staff_services as $staff_service ) {
59
+ $this->services_data[ $staff_service['service_id'] ] = array( 'price' => $staff_service['price'], 'deposit' => $staff_service['deposit'], 'capacity_min' => 1, 'capacity_max' => 1 );
60
  }
61
  }
62
 
75
 
76
  public function save()
77
  {
78
+ $staff_id = $this->data['staff_id'];
79
+ if ( $staff_id ) {
80
+ Lib\Entities\StaffService::query()->delete()->where( 'staff_id', $staff_id )->execute();
81
+ if ( isset ( $this->data['service'] ) ) {
82
+ foreach ( $this->data['service'] as $service_id ) {
83
+ $staff_service = new Lib\Entities\StaffService();
84
+ $staff_service
85
+ ->setCapacityMin( 1 )
86
+ ->setCapacityMax( 1 )
87
+ ->setDeposit( isset ( $this->data['deposit'] ) ? $this->data['deposit'][ $service_id ] : '100%' )
88
+ ->setPrice( $this->data['price'][ $service_id ] )
89
+ ->setServiceId( $service_id )
90
+ ->setStaffId( 1 )
91
+ ->save();
92
+ }
93
  }
94
  }
95
  }
96
 
97
  /**
98
+ * @return Lib\Entities\Category[]
99
  */
100
  public function getCategories()
101
  {
backend/modules/staff/forms/widgets/TimeChoice.php CHANGED
@@ -34,16 +34,15 @@ class TimeChoice
34
  }
35
 
36
  $ts_length = Lib\Config::getTimeSlotLength();
37
- if ( isset( $options['bound'] ) ) {
38
- $time_start = Lib\Utils\DateTime::timeToSeconds( $options['bound'][0] );
39
- $time_end = Lib\Utils\DateTime::timeToSeconds( $options['bound'][1] );
40
- } else {
41
- $time_start = Lib\Entities\StaffScheduleItem::WORKING_START_TIME;
42
- $time_end = Lib\Entities\StaffScheduleItem::WORKING_END_TIME;
43
- }
44
  if ( $options['type'] == 'from' ) {
45
  $time_end -= $ts_length; // Exclude last slot.
46
- } elseif ( $options['type'] == 'to' ) {
 
 
 
47
  $time_end *= 2; // Create slots for 2 days.
48
  }
49
 
34
  }
35
 
36
  $ts_length = Lib\Config::getTimeSlotLength();
37
+ $time_start = 0;
38
+ $time_end = DAY_IN_SECONDS;
39
+
 
 
 
 
40
  if ( $options['type'] == 'from' ) {
41
  $time_end -= $ts_length; // Exclude last slot.
42
+ } else if ( $options['type'] == 'break_from' ) {
43
+ $time_end *= 2; // Create slots for 2 days.
44
+ $time_end -= $ts_length; // Exclude last slot.
45
+ } else if ( $options['type'] == 'to' ) {
46
  $time_end *= 2; // Create slots for 2 days.
47
  }
48
 
backend/modules/staff/resources/js/staff-days-off.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ var DaysOff = function($container, options) {
4
+ var obj = this;
5
+ jQuery.extend(obj.options, options);
6
+
7
+ if (!$container.children().length) {
8
+ $container.html('<div class="bookly-loading"></div>');
9
+ $.ajax({
10
+ url : ajaxurl,
11
+ data : {action: 'bookly_staff_holidays', id: obj.options.staff_id, csrf_token : obj.options.csrf_token},
12
+ xhrFields : { withCredentials: true },
13
+ crossDomain : 'withCredentials' in new XMLHttpRequest(),
14
+ success : function (response) {
15
+ $container.html(response.data.html);
16
+ var d = new Date();
17
+ $('.bookly-js-holidays').jCal({
18
+ day: new Date(d.getFullYear(), 0, 1),
19
+ days: 1,
20
+ showMonths: 12,
21
+ scrollSpeed: 350,
22
+ events: response.data.holidays,
23
+ action: 'bookly_staff_holidays_update',
24
+ csrf_token: obj.options.csrf_token,
25
+ staff_id: obj.options.staff_id,
26
+ dayOffset: response.data.start_of_week,
27
+ loadingImg: response.data.loading_img,
28
+ dow: response.data.days,
29
+ ml: response.data.months,
30
+ we_are_not_working: response.data.we_are_not_working,
31
+ repeat: response.data.repeat,
32
+ close: response.data.close
33
+ });
34
+
35
+ $('.bookly-js-jCalBtn', $container).on('click', function (e) {
36
+ e.preventDefault();
37
+ var trigger = $(this).data('trigger');
38
+ $('.bookly-js-holidays',$container).find($(trigger)).trigger('click');
39
+ })
40
+ }
41
+ });
42
+ }
43
+ };
44
+
45
+ DaysOff.prototype.options = {
46
+ staff_id : -1,
47
+ csrf_token: '',
48
+ l10n: {}
49
+ };
50
+
51
+ window.BooklyStaffDaysOff = DaysOff;
52
+ });
backend/modules/staff/resources/js/staff-details.js ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ var Details = function($container, options) {
4
+ var obj = this;
5
+ jQuery.extend(obj.options, options);
6
+
7
+ if (Object.keys(obj.options.get_details).length === 0) {
8
+ // backend united edit & details in one request.
9
+ initDetails($container);
10
+ } else {
11
+ // get details content.
12
+ $container.html('<div class="bookly-loading"></div>');
13
+ $.ajax({
14
+ url : ajaxurl,
15
+ data : obj.options.get_details,
16
+ dataType : 'json',
17
+ xhrFields : { withCredentials: true },
18
+ crossDomain : 'withCredentials' in new XMLHttpRequest(),
19
+ success : function (response) {
20
+ $container.html(response.data.html);
21
+ initDetails($container);
22
+ }
23
+ });
24
+ }
25
+
26
+ function initDetails($container) {
27
+ var $staff_full_name = $('#bookly-full-name', $container),
28
+ $staff_wp_user = $('#bookly-wp-user', $container),
29
+ $staff_email = $('#bookly-email', $container),
30
+ $staff_phone = $('#bookly-phone', $container),
31
+ $location_row = $('.locations-row', $container)
32
+ ;
33
+
34
+ if (obj.options.intlTelInput.enabled) {
35
+ $staff_phone.intlTelInput({
36
+ preferredCountries: [obj.options.intlTelInput.country],
37
+ initialCountry: obj.options.intlTelInput.country,
38
+ geoIpLookup: function (callback) {
39
+ $.get('https://ipinfo.io', function() {}, 'jsonp').always(function(resp) {
40
+ var countryCode = (resp && resp.country) ? resp.country : '';
41
+ callback(countryCode);
42
+ });
43
+ },
44
+ utilsScript: obj.options.intlTelInput.utils
45
+ });
46
+ }
47
+
48
+ $staff_wp_user.on('change', function () {
49
+ if (this.value) {
50
+ $staff_full_name.val($staff_wp_user.find(':selected').text());
51
+ $staff_email.val($staff_wp_user.find(':selected').data('email'));
52
+ }
53
+ });
54
+
55
+ $('input.bookly-js-all-locations, input.bookly-location', $container).on('change', function () {
56
+ if ($(this).hasClass('bookly-js-all-locations')) {
57
+ $location_row.find('.bookly-location').prop('checked', $(this).prop('checked'));
58
+ } else {
59
+ $location_row.find('.bookly-js-all-locations').prop('checked', $location_row.find('.bookly-location:not(:checked)').length == 0);
60
+ }
61
+ updateLocationsButton();
62
+ });
63
+
64
+ $('button:reset', $container).on('click', function () {
65
+ setTimeout(updateLocationsButton, 0);
66
+ });
67
+
68
+ function updateLocationsButton() {
69
+ var locations_checked = $location_row.find('.bookly-location:checked').length;
70
+ if (locations_checked == 0) {
71
+ $location_row.find('.bookly-locations-count').text(obj.options.l10n.selector.nothing_selected);
72
+ } else if (locations_checked == 1) {
73
+ $location_row.find('.bookly-locations-count').text($location_row.find('.bookly-location:checked').data('location_name'));
74
+ } else {
75
+ if (locations_checked == $location_row.find('.bookly-location').length) {
76
+ $location_row.find('.bookly-locations-count').text(obj.options.l10n.selector.all_selected);
77
+ } else {
78
+ $location_row.find('.bookly-locations-count').text(locations_checked + '/' + $location_row.find('.bookly-location').length);
79
+ }
80
+ }
81
+ }
82
+
83
+ updateLocationsButton();
84
+
85
+ // Save staff member details.
86
+ $('#bookly-details-save', $container).on('click',function(e){
87
+ e.preventDefault();
88
+ var $form = $(this).closest('form'),
89
+ data = $form.serializeArray(),
90
+ ladda = Ladda.create(this),
91
+ $staff_phone = $('#bookly-phone',$form),
92
+ phone;
93
+ try {
94
+ phone = BooklyL10n.intlTelInput.enabled ? $staff_phone.intlTelInput('getNumber') : $staff_phone.val();
95
+ if (phone == '') {
96
+ phone = $staff_phone.val();
97
+ }
98
+ } catch (error) { // In case when intlTelInput can't return phone number.
99
+ phone = $staff_phone.val();
100
+ }
101
+ data.push({name: 'action', value: 'bookly_update_staff'});
102
+ data.push({name: 'phone', value: phone});
103
+ ladda.start();
104
+
105
+ $.ajax({
106
+ type: 'POST',
107
+ url: ajaxurl,
108
+ data: data,
109
+ dataType: 'json',
110
+ xhrFields: {withCredentials: true},
111
+ crossDomain: 'withCredentials' in new XMLHttpRequest(),
112
+ success: function (response) {
113
+ if (response.success) {
114
+ obj.options.booklyAlert({success: [obj.options.l10n.saved]});
115
+ $('[bookly-js-staff-name-' + obj.options.get_details.id + ']').text($('#bookly-full-name', $form).val());
116
+ if (typeof obj.options.renderWpUsers === 'function') {
117
+ obj.options.renderWpUsers(response.data.wp_users);
118
+ }
119
+ } else {
120
+ obj.options.booklyAlert({error: [response.data.error]});
121
+ }
122
+ ladda.stop();
123
+ }
124
+ });
125
+ });
126
+ }
127
+
128
+ };
129
+
130
+ Details.prototype.options = {
131
+ intlTelInput: {},
132
+ get_details : {
133
+ action: 'bookly_get_staff_details',
134
+ id : -1,
135
+ csrf_token: ''
136
+ },
137
+ l10n : {},
138
+ booklyAlert : window.booklyAlert
139
+ };
140
+
141
+ window.BooklyStaffDetails = Details;
142
+ });
backend/modules/staff/resources/js/staff-schedule.js ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+ var Schedule = function ($container, options) {
3
+ var obj = this;
4
+ jQuery.extend(obj.options, options);
5
+
6
+ // Loads schedule list
7
+ if (!$container.children().length) {
8
+ $container.html('<div class="bookly-loading"></div>');
9
+ $.ajax({
10
+ type: 'POST',
11
+ url: ajaxurl,
12
+ data: obj.options.get_staff_schedule,
13
+ dataType: 'json',
14
+ xhrFields: {withCredentials: true},
15
+ crossDomain: 'withCredentials' in new XMLHttpRequest(),
16
+ success: function (response) {
17
+ // fill in the container
18
+ $container.html(response.data.html);
19
+
20
+
21
+ // init 'add break' functionality
22
+ $('.bookly-js-toggle-popover:not(.break-interval)', $container).popover({
23
+ html: true,
24
+ placement: 'bottom',
25
+ template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
26
+ trigger: 'manual',
27
+ content: function () {
28
+ return $($(this).data('popover-content')).html()
29
+ }
30
+ }).on('click', function () {
31
+ $(this).popover('toggle');
32
+
33
+ var $popover = $(this).next('.popover'),
34
+ working_start = $popover.closest('.row').find('.working-schedule-start').val(),
35
+ $break_start = $popover.find('.break-start'),
36
+ $break_end = $popover.find('.break-end'),
37
+ working_start_time = working_start.split(':'),
38
+ working_start_hours = parseInt(working_start_time[0], 10),
39
+ break_start_hours = working_start_hours + 1;
40
+ if (break_start_hours < 10) {
41
+ break_start_hours = '0' + break_start_hours;
42
+ }
43
+ var break_end_hours = working_start_hours + 2;
44
+ if (break_end_hours < 10) {
45
+ break_end_hours = '0' + break_end_hours;
46
+ }
47
+ var break_end_hours_str = break_end_hours + ':' + working_start_time[1] + ':' + working_start_time[2],
48
+ break_start_hours_str = break_start_hours + ':' + working_start_time[1] + ':' + working_start_time[2];
49
+
50
+ $break_start.val(break_start_hours_str);
51
+ $break_end.val(break_end_hours_str);
52
+
53
+ hideInaccessibleBreaks($break_start, $break_end);
54
+
55
+ $popover.find('.bookly-popover-close').on('click', function () {
56
+ $popover.popover('hide');
57
+ });
58
+ });
59
+
60
+ $container
61
+ // Save Schedule
62
+ .on('click', '#bookly-schedule-save', function (e) {
63
+ e.preventDefault();
64
+ var ladda = Ladda.create(this);
65
+ ladda.start();
66
+ var data = {};
67
+ $('select.working-schedule-start, select.working-schedule-end, input:hidden', $container).each(function () {
68
+ data[this.name] = this.value;
69
+ });
70
+ $.post(ajaxurl, $.param(data), function () {
71
+ ladda.stop();
72
+ obj.options.booklyAlert({success: [obj.options.l10n.saved]});
73
+ });
74
+ })
75
+ // Resets initial schedule values
76
+ .on('click', '#bookly-schedule-reset', function (e) {
77
+ e.preventDefault();
78
+ var ladda = Ladda.create(this);
79
+ ladda.start();
80
+
81
+ $('.working-schedule-start', $container).each(function () {
82
+ $(this).val($(this).data('default_value'));
83
+ $(this).trigger('change');
84
+ });
85
+
86
+ $('.working-schedule-end', $container).each(function () {
87
+ $(this).val($(this).data('default_value'));
88
+ });
89
+
90
+ // reset breaks
91
+ $.ajax({
92
+ url: ajaxurl,
93
+ type: 'POST',
94
+ data: {action: 'bookly_reset_breaks', breaks: $(this).data('default-breaks'), csrf_token: obj.options.l10n.csrf_token},
95
+ dataType: 'json',
96
+ success: function (response) {
97
+ for (var k in response) {
98
+ var $content = $(response[k]);
99
+ $('[data-staff_schedule_item_id=' + k + '] .breaks', $container).html($content);
100
+ $content.find('.bookly-intervals-wrapper .delete-break').on('click', function () {
101
+ deleteBreak.call(this);
102
+ });
103
+ }
104
+ },
105
+ complete: function () {
106
+ ladda.stop();
107
+ }
108
+ });
109
+ })
110
+
111
+ .on('click', '.break-interval', function () {
112
+ var $button = $(this);
113
+ $('.popover').popover('hide');
114
+ var break_id = $button.closest('.bookly-intervals-wrapper').data('break_id');
115
+ $(this).popover({
116
+ html: true,
117
+ placement: 'bottom',
118
+ template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
119
+ content: function () {
120
+ return $('.bookly-js-content-break-' + break_id).html();
121
+ },
122
+ trigger: 'manual'
123
+ });
124
+
125
+ $(this).popover('toggle');
126
+
127
+ var $popover = $(this).next('.popover'),
128
+ $break_start = $popover.find('.break-start'),
129
+ $break_end = $popover.find('.break-end');
130
+
131
+ if ($button.hasClass('break-interval')) {
132
+ var interval = $button.html().trim().split(' - ');
133
+ rangeTools.setVal($break_start, interval[0]);
134
+ rangeTools.setVal($break_end, interval[1]);
135
+ }
136
+
137
+ hideInaccessibleBreaks($break_start, $break_end, true);
138
+
139
+ $popover.find('.bookly-popover-close').on('click', function () {
140
+ $popover.popover('hide');
141
+ });
142
+ })
143
+
144
+ .on('click', '.bookly-js-save-break', function (e) {
145
+ var $table = $(this).closest('.bookly-js-schedule-form'),
146
+ $row = $table.parents('.staff-schedule-item-row').first(),
147
+ $data = {
148
+ action: 'bookly_staff_schedule_handle_break',
149
+ staff_schedule_item_id: $row.data('staff_schedule_item_id'),
150
+ start_time: $table.find('.break-start > option:selected').val(),
151
+ end_time: $table.find('.break-end > option:selected').val(),
152
+ working_end: $row.find('.working-schedule-end > option:selected').val(),
153
+ working_start: $row.find('.working-schedule-start > option:selected').val(),
154
+ csrf_token: obj.options.l10n.csrf_token
155
+ },
156
+ $break_interval_wrapper = $table.parents('.bookly-intervals-wrapper').first(),
157
+ ladda = Ladda.create(e.currentTarget);
158
+ ladda.start();
159
+
160
+ if ($break_interval_wrapper.data('break_id')) {
161
+ $data['break_id'] = $break_interval_wrapper.data('break_id');
162
+ }
163
+ $.ajax({
164
+ url: ajaxurl,
165
+ type: 'POST',
166
+ data: $data,
167
+ dataType: 'json',
168
+ success: function (response) {
169
+ if (response.success) {
170
+ if (response['item_content']) {
171
+ var $new_break_interval_item = $(response['item_content']);
172
+ $new_break_interval_item
173
+ .hide()
174
+ .appendTo($row.find('.breaks-list-content'))
175
+ .fadeIn('slow');
176
+ } else if (response.data.interval) {
177
+ $break_interval_wrapper
178
+ .find('.break-interval')
179
+ .text(response.data.interval);
180
+ }
181
+ $('.popover').popover('hide');
182
+ } else {
183
+ obj.options.booklyAlert({error: [response.data.message]});
184
+ }
185
+ },
186
+ complete: function () {
187
+ ladda.stop();
188
+ }
189
+ });
190
+
191
+ return false;
192
+ })
193
+
194
+ .on('click', '.bookly-intervals-wrapper .delete-break', function () {
195
+ deleteBreak.call(this);
196
+ })
197
+
198
+ .on('change', '.break-start', function () {
199
+ var $start = $(this);
200
+ var $end = $start.parents('.bookly-flexbox').find('.break-end');
201
+ hideInaccessibleBreaks($start, $end);
202
+ })
203
+
204
+ .on('change', '.working-schedule-start', function () {
205
+ var $this = $(this),
206
+ $end_select = $this.closest('.bookly-flexbox').find('.working-schedule-end'),
207
+ start_time = $this.val();
208
+
209
+ // Hide end time options to keep them within 24 hours after start time.
210
+ var parts = start_time.split(':');
211
+ parts[0] = parseInt(parts[0]) + 24;
212
+ var end_time = parts.join(':');
213
+ var frag = document.createDocumentFragment();
214
+ var old_value = $end_select.val();
215
+ var new_value = null;
216
+ $('option', $end_select).each(function () {
217
+ if (this.value <= start_time || this.value > end_time) {
218
+ var span = document.createElement('span');
219
+ span.style.display = 'none';
220
+ span.appendChild(this.cloneNode(true));
221
+ frag.appendChild(span);
222
+ } else {
223
+ frag.appendChild(this.cloneNode(true));
224
+ if (new_value === null || old_value == this.value) {
225
+ new_value = this.value;
226
+ }
227
+ }
228
+ });
229
+ $end_select.empty().append(frag).val(new_value);
230
+
231
+ // when the working day is disabled (working start time is set to 'OFF')
232
+ // hide all the elements inside the row
233
+ if (!$this.val()) {
234
+ $this.closest('.row').find('.bookly-hide-on-off').hide();
235
+ } else {
236
+ $this.closest('.row').find('.bookly-hide-on-off').show();
237
+ }
238
+ });
239
+ $('.working-schedule-start', $container).trigger('change');
240
+ $('.break-start', $container).trigger('change');
241
+ }
242
+ });
243
+ }
244
+
245
+ function hideInaccessibleBreaks($start, $end, force_keep_values) {
246
+ var $row = $start.closest('.row'),
247
+ $working_start = $row.find('.working-schedule-start'),
248
+ $working_end = $row.find('.working-schedule-end'),
249
+ frag1 = document.createDocumentFragment(),
250
+ frag2 = document.createDocumentFragment(),
251
+ old_value = $start.val(),
252
+ new_value = null;
253
+
254
+ $('option', $start).each(function () {
255
+ if ((this.value < $working_start.val() || this.value >= $working_end.val()) && (!force_keep_values || this.value != old_value)) {
256
+ var span = document.createElement('span');
257
+ span.style.display = 'none';
258
+ span.appendChild(this.cloneNode(true));
259
+ frag1.appendChild(span);
260
+ } else {
261
+ frag1.appendChild(this.cloneNode(true));
262
+ if (new_value === null || old_value == this.value) {
263
+ new_value = this.value;
264
+ }
265
+ }
266
+ });
267
+ $start.empty().append(frag1).val(new_value);
268
+
269
+ // Hide end time options with value less than in the start time.
270
+ old_value = $end.val();
271
+ new_value = null;
272
+ $('option', $end).each(function () {
273
+ if ((this.value <= $start.val() || this.value > $working_end.val()) && (!force_keep_values || this.value != old_value)) {
274
+ var span = document.createElement('span');
275
+ span.style.display = 'none';
276
+ span.appendChild(this.cloneNode(true));
277
+ frag2.appendChild(span);
278
+ } else {
279
+ frag2.appendChild(this.cloneNode(true));
280
+ if (new_value === null || old_value == this.value) {
281
+ new_value = this.value;
282
+ }
283
+ }
284
+ });
285
+ $end.empty().append(frag2).val(new_value);
286
+ }
287
+
288
+ function deleteBreak() {
289
+ var $break_interval_wrapper = $(this).closest('.bookly-intervals-wrapper');
290
+ if (confirm(obj.options.l10n.are_you_sure)) {
291
+ var ladda = Ladda.create(this);
292
+ ladda.start();
293
+ $.ajax({
294
+ url: ajaxurl,
295
+ type: 'POST',
296
+ data: {action: 'bookly_delete_staff_schedule_break', id: $break_interval_wrapper.data('break_id'), csrf_token: obj.options.l10n.csrf_token},
297
+ success: function (response) {
298
+ if (response.success) {
299
+ $break_interval_wrapper.remove();
300
+ }
301
+ },
302
+ complete: function () {
303
+ ladda.stop();
304
+ }
305
+ });
306
+ }
307
+ }
308
+ };
309
+
310
+ Schedule.prototype.options = {
311
+ get_staff_schedule: {
312
+ action: 'bookly_get_staff_schedule',
313
+ staff_id: -1,
314
+ csrf_token: ''
315
+ },
316
+ booklyAlert: window.booklyAlert,
317
+ l10n: {}
318
+ };
319
+
320
+ window.BooklyStaffSchedule = Schedule;
321
+ });
322
+
backend/modules/staff/resources/js/staff-services.js ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ var Services = function($container, options) {
4
+ var obj = this;
5
+ jQuery.extend(obj.options, options);
6
+
7
+ // Load services form
8
+ if (!$container.children().length) {
9
+ $container.html('<div class="bookly-loading"></div>');
10
+ $.ajax({
11
+ type : 'POST',
12
+ url : ajaxurl,
13
+ data : obj.options.get_staff_services,
14
+ dataType : 'json',
15
+ xhrFields : { withCredentials: true },
16
+ crossDomain : 'withCredentials' in new XMLHttpRequest(),
17
+ success : function (response) {
18
+ $container.html(response.data.html);
19
+ var $services_form = $('form', $container);
20
+ $(document.body).trigger('special_hours.tab_init', [$container, obj.options.get_staff_services.staff_id, obj.options.booklyAlert]);
21
+ var autoTickCheckboxes = function () {
22
+ // Handle 'select category' checkbox.
23
+ $('.bookly-services-category .bookly-category-checkbox').each(function () {
24
+ $(this).prop(
25
+ 'checked',
26
+ $('.bookly-category-services .bookly-service-checkbox.bookly-category-' + $(this).data('category-id') + ':not(:checked)').length == 0
27
+ );
28
+ });
29
+ // Handle 'select all services' checkbox.
30
+ $('#bookly-check-all-entities').prop(
31
+ 'checked',
32
+ $('.bookly-service-checkbox:not(:checked)').length == 0
33
+ );
34
+ };
35
+ var checkCapacityError = function ($form_group) {
36
+ if (parseInt($form_group.find('.bookly-js-capacity-min').val()) > 1) {
37
+ $form_group.find('.bookly-js-capacity-min').val(1).prop('readonly', true);
38
+ booklyAlert({error: [BooklyL10n.limitations]});
39
+ }
40
+ if (parseInt($form_group.find('.bookly-js-capacity-max').val()) > 1) {
41
+ $form_group.find('.bookly-js-capacity-max').val(1).prop('readonly', true);
42
+ booklyAlert({error: [BooklyL10n.limitations]});
43
+ }
44
+ };
45
+
46
+ $services_form
47
+ // Select all services related to chosen category
48
+ .on('click', '.bookly-category-checkbox', function () {
49
+ $('.bookly-category-services .bookly-category-' + $(this).data('category-id')).prop('checked', $(this).is(':checked')).change();
50
+ autoTickCheckboxes();
51
+ })
52
+ // Check and uncheck all services
53
+ .on('click', '#bookly-check-all-entities', function () {
54
+ $('.bookly-service-checkbox', $services_form).prop('checked', $(this).is(':checked')).change();
55
+ $('.bookly-category-checkbox').prop('checked', $(this).is(':checked'));
56
+ })
57
+ // Select service
58
+ .on('click', '.bookly-service-checkbox', function () {
59
+ autoTickCheckboxes();
60
+ })
61
+ // Save services
62
+ .on('click', '#bookly-services-save', function (e) {
63
+ e.preventDefault();
64
+ var ladda = Ladda.create(this);
65
+ ladda.start();
66
+ $.ajax({
67
+ type : 'POST',
68
+ url : ajaxurl,
69
+ data : $services_form.serialize(),
70
+ dataType : 'json',
71
+ xhrFields : {withCredentials: true},
72
+ crossDomain: 'withCredentials' in new XMLHttpRequest(),
73
+ success : function (response) {
74
+ ladda.stop();
75
+ if (response.success) {
76
+ obj.options.booklyAlert({success: [obj.options.l10n.saved]});
77
+ }
78
+ }
79
+ });
80
+ })
81
+ // After reset auto tick group checkboxes.
82
+ .on('click', '#bookly-services-reset', function () {
83
+ setTimeout(function () {
84
+ autoTickCheckboxes();
85
+ $('.bookly-js-capacity-form-group', $services_form).each(function () {
86
+ checkCapacityError($(this));
87
+ });
88
+ $('.bookly-service-checkbox', $services_form).trigger('change');
89
+ }, 0);
90
+ });
91
+
92
+ $('.bookly-service-checkbox').on('change', function () {
93
+ var $this = $(this),
94
+ $service = $this.closest('li'),
95
+ $inputs = $service.find('input:not(:checkbox)');
96
+
97
+ $inputs.attr('disabled', !$this.is(':checked'));
98
+
99
+ // Handle package-service connections
100
+ if ($(this).is(':checked') && $service.data('service-type') == 'package') {
101
+ $('li[data-service-type="simple"][data-service-id="' + $service.data('sub-service') + '"] .bookly-service-checkbox', $services_form).prop('checked', true).trigger('change');
102
+ $('.bookly-js-capacity-min', $service).val($('li[data-service-type="simple"][data-service-id="' + $service.data('sub-service') + '"] .bookly-js-capacity-min', $services_form).val());
103
+ $('.bookly-js-capacity-max', $service).val($('li[data-service-type="simple"][data-service-id="' + $service.data('sub-service') + '"] .bookly-js-capacity-max', $services_form).val());
104
+ }
105
+ if (!$(this).is(':checked') && $service.data('service-type') == 'simple') {
106
+ $('li[data-service-type="package"][data-sub-service="' + $service.data('service-id') + '"] .bookly-service-checkbox', $services_form).prop('checked', false).trigger('change');
107
+ }
108
+ });
109
+
110
+ $('.bookly-js-capacity').on('keyup change', function () {
111
+ var $service = $(this).closest('li');
112
+ if ($service.data('service-type') == 'simple') {
113
+ if ($(this).hasClass('bookly-js-capacity-min')) {
114
+ $('li[data-service-type="package"][data-sub-service="' + $service.data('service-id') + '"] .bookly-js-capacity-min', $services_form).val($(this).val());
115
+ } else {
116
+ $('li[data-service-type="package"][data-sub-service="' + $service.data('service-id') + '"] .bookly-js-capacity-max', $services_form).val($(this).val());
117
+ }
118
+ }
119
+ checkCapacityError($(this).closest('.form-group'));
120
+ });
121
+ autoTickCheckboxes();
122
+ }
123
+ });
124
+ }
125
+
126
+ };
127
+
128
+ Services.prototype.options = {
129
+ get_staff_services: {
130
+ action : 'bookly_get_staff_services',
131
+ staff_id: -1,
132
+ csrf_token: ''
133
+ },
134
+ booklyAlert: window.booklyAlert,
135
+ l10n: {}
136
+ };
137
+
138
+ window.BooklyStaffServices = Services;
139
+ });
backend/modules/staff/resources/js/staff.js CHANGED
@@ -3,39 +3,12 @@ jQuery(function($) {
3
  $new_form = $('#bookly-new-staff'),
4
  $wp_user_select = $('#bookly-new-staff-wpuser'),
5
  $name_input = $('#bookly-new-staff-fullname'),
6
- $edit_form = $('#bookly-container-edit-staff')
7
- ;
8
  function saveNewForm() {
9
  booklyAlert({error: [BooklyL10n.limitations]});
10
  }
11
 
12
- $edit_form.on('click', '.bookly-pretty-indicator', function (e) {
13
- e.preventDefault();
14
- e.stopPropagation();
15
- var frame = wp.media({
16
- library: {type: 'image'},
17
- multiple: false
18
- });
19
- frame.on('select', function () {
20
- var selection = frame.state().get('selection').toJSON(),
21
- img_src
22
- ;
23
- if (selection.length) {
24
- if (selection[0].sizes['thumbnail'] !== undefined) {
25
- img_src = selection[0].sizes['thumbnail'].url;
26
- } else {
27
- img_src = selection[0].url;
28
- }
29
- $edit_form.find('[name=attachment_id]').val(selection[0].id);
30
- $('#bookly-js-staff-avatar').find('.bookly-js-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
31
- $('.bookly-thumb-delete').show();
32
- $(this).hide();
33
- }
34
- });
35
-
36
- frame.open();
37
- });
38
-
39
  // Save new staff on enter press
40
  $name_input.on('keypress', function (e) {
41
  var code = (e.keyCode ? e.keyCode : e.which);
@@ -48,7 +21,7 @@ jQuery(function($) {
48
  $new_form.on('keypress', function (e) {
49
  var code = (e.keyCode ? e.keyCode : e.which);
50
  if (code == 27) {
51
- $('#ab-newstaff-member').popover('hide');
52
  }
53
  });
54
 
@@ -56,6 +29,33 @@ jQuery(function($) {
56
  e.stopPropagation();
57
  });
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  /**
60
  * Load staff profile on click on staff in the list.
61
  */
@@ -65,516 +65,110 @@ jQuery(function($) {
65
  $staff_list.find('.active').removeClass('active');
66
  $this.addClass('active');
67
 
68
- var staff_id = $this.data('staff-id');
69
  var active_tab_id = $('.nav .active a').attr('id');
70
  $edit_form.html('<div class="bookly-loading"></div>');
71
- $.get(ajaxurl, { action: 'bookly_edit_staff', id: staff_id }, function (response) {
72
- $edit_form.html(response.data.html);
73
  booklyAlert(response.data.alert);
74
- var $loading_indicator = $('.bookly-loading'),
75
- $details_container = $('#bookly-details-container'),
76
- $services_container = $('#bookly-services-container'),
77
- $schedule_container = $('#bookly-schedule-container'),
78
- $holidays_container = $('#bookly-holidays-container'),
79
- $delete_staff_button = $('#bookly-delete'),
80
- $save_staff_button = $('#bookly-save'),
81
- $staff_full_name = $('#bookly-full-name'),
82
- $staff_wp_user = $('#bookly-wp-user'),
83
- $staff_email = $('#bookly-email'),
84
- $staff_phone = $('#bookly-phone'),
85
- $schedule_form,
86
- $services_form;
87
-
88
- if (BooklyL10n.intlTelInput.enabled) {
89
- $staff_phone.intlTelInput({
90
- preferredCountries: [BooklyL10n.intlTelInput.country],
91
- defaultCountry: BooklyL10n.intlTelInput.country,
92
- geoIpLookup: function (callback) {
93
- $.get(ajaxurl, {action: 'bookly_ip_info'}, function () {}, 'json').always(function (resp) {
94
- var countryCode = (resp && resp.country) ? resp.country : '';
95
- callback(countryCode);
96
- });
97
- },
98
- utilsScript: BooklyL10n.intlTelInput.utils
99
- });
100
- }
101
-
102
- // Delete staff member.
103
- $delete_staff_button.on('click', function (e) {
104
- e.preventDefault();
105
- if (confirm(BooklyL10n.are_you_sure)) {
106
- booklyAlert({error: [BooklyL10n.limitations]});
107
  }
108
  });
109
 
110
- // Save staff member details.
111
- $save_staff_button.on('click',function(e){
112
- e.preventDefault();
113
- var $form = $(this).closest('form'),
114
- data = $form.serializeArray(),
115
- ladda = Ladda.create(this),
116
- phone;
117
- try {
118
- phone = BooklyL10n.intlTelInput.enabled ? $staff_phone.intlTelInput('getNumber') : $staff_phone.val();
119
- if (phone == '') {
120
- phone = $staff_phone.val();
121
- }
122
- } catch (error) { // In case when intlTelInput can't return phone number.
123
- phone = $staff_phone.val();
124
- }
125
- data.push({name: 'action', value: 'bookly_update_staff'});
126
- data.push({name: 'phone', value: phone});
127
- ladda.start();
128
- $.post(ajaxurl, data, function (response) {
129
- if (response.success) {
130
- booklyAlert({success : [BooklyL10n.saved]});
131
- // Update staff name throughout the page.
132
- $('.bookly-js-staff-name-1').text($staff_full_name.val());
133
- // Update wp users in new staff form.
134
- $wp_user_select.children(':not(:first)').remove();
135
- $.each(response.data.wp_users, function (index, wp_user) {
136
- var $option = $('<option>')
137
- .data('email', wp_user.user_email)
138
- .val(wp_user.ID)
139
- .text(wp_user.display_name);
140
- $wp_user_select.append($option);
141
- });
142
- } else {
143
- booklyAlert({error : [response.data.error]});
144
- }
145
- ladda.stop();
146
- });
147
  });
148
 
149
  // Delete staff avatar
150
- $('.bookly-thumb-delete').on('click', function () {
151
  var $thumb = $(this).parents('.bookly-js-image');
152
- $.post(ajaxurl, {action: 'bookly_delete_staff_avatar', id: 1}, function () {
153
- $thumb.attr('style', '');
154
- $edit_form.find('[name=attachment_id]').val('');
 
 
155
  });
156
  });
157
 
158
- $staff_wp_user.on('change', function () {
159
- if (this.value) {
160
- $staff_full_name.val($staff_wp_user.find(':selected').text());
161
- $staff_email.val($staff_wp_user.find(':selected').data('email'));
162
- }
163
- });
164
-
165
- $('input.all-locations, input.location').on('change', function () {
166
- var $panel = $(this).parents('.locations-row');
167
- if ($(this).hasClass('all-locations')) {
168
- $panel.find('.location').prop('checked', $(this).prop('checked'));
169
- } else {
170
- $panel.find('.all-locations').prop('checked', $panel.find('.location:not(:checked)').length == 0);
171
- }
172
- updateLocationsButton($panel);
173
- });
174
-
175
- function updateLocationsButton($panel) {
176
- var locations_checked = $panel.find('.location:checked').length;
177
- if (locations_checked == 0) {
178
- $panel.find('.locations-count').text(BooklyL10n.selector.nothing_selected);
179
- } else if (locations_checked == 1) {
180
- $panel.find('.locations-count').text($panel.find('.location:checked').data('location_name'));
181
- } else {
182
- if (locations_checked == $panel.find('.location').length) {
183
- $panel.find('.locations-count').text(BooklyL10n.selector.all_selected);
184
- } else {
185
- $panel.find('.locations-count').text(locations_checked + '/' + $panel.find('.location').length);
186
- }
187
- }
188
- }
189
- updateLocationsButton($('.locations-row'));
190
-
191
  // Open details tab
192
- $('#bookly-details-tab').on('click', function () {
193
  $('.tab-pane > div').hide();
194
  $details_container.show();
195
  });
196
 
197
  // Open services tab
198
- $('#bookly-services-tab').on('click', function () {
199
  $('.tab-pane > div').hide();
200
- $services_container.show();
201
-
202
- // Load services form
203
- if (!$services_container.children().length) {
204
- $loading_indicator.show();
205
- $.post(ajaxurl, {action: 'bookly_staff_services', id: 1}, function (response) {
206
- $services_container.html(response);
207
- $services_form = $('form', $services_container);
208
- $(document.body).trigger( 'special_hours.tab_init', [ $services_container ] );
209
- var autoTickCheckboxes = function () {
210
- // Handle 'select category' checkbox.
211
- $('.ab-services-category .bookly-category-checkbox').each(function () {
212
- $(this).prop(
213
- 'checked',
214
- $('.bookly-category-services .ab-service-checkbox.bookly-category-' + $(this).data('category-id') + ':not(:checked)').length == 0
215
- );
216
- });
217
- // Handle 'select all services' checkbox.
218
- $('#bookly-check-all-entities').prop(
219
- 'checked',
220
- $('.ab-service-checkbox:not(:checked)').length == 0
221
- );
222
- };
223
-
224
- $('input[name^="capacity"]', $services_container).on('change', function () {
225
- $(this).val(1);
226
- $(this).prop('readonly',true);
227
- booklyAlert({error: [BooklyL10n.limitations]});
228
- });
229
 
230
- // Select all services related to chosen category
231
- $('.bookly-category-checkbox', $services_form).on('click', function () {
232
- $('.bookly-category-services .bookly-category-' + $(this).data('category-id')).prop('checked', $(this).is(':checked')).change();
233
- autoTickCheckboxes();
234
- });
235
-
236
- // Check and uncheck all services
237
- $('#bookly-check-all-entities').on('click', function () {
238
- $('.ab-service-checkbox', $services_form).prop('checked', $(this).is(':checked')).change();
239
- $('.bookly-category-checkbox').prop('checked', $(this).is(':checked'));
240
- });
241
-
242
- // Select service
243
- $('.ab-service-checkbox', $services_form).on('click', function () {
244
- autoTickCheckboxes();
245
- }).on('change', function () {
246
- var $this = $(this);
247
- var $inputs = $this.closest('li').find('input:not(:checkbox)');
248
- $inputs.attr('disabled', ! $this.is(':checked'));
249
- });
250
-
251
- // Save services
252
- $('#bookly-services-save').on('click', function (e) {
253
- e.preventDefault();
254
- var ladda = Ladda.create(this);
255
- ladda.start();
256
- $.post(ajaxurl, $services_form.serialize(), function (response) {
257
- ladda.stop();
258
- if (response.success) {
259
- booklyAlert({success : [BooklyL10n.saved]});
260
- }
261
- });
262
- });
263
-
264
- // After reset auto tick group checkboxes.
265
- $('#bookly-services-reset').on('click', function () {
266
- setTimeout(function() {
267
- autoTickCheckboxes();
268
- $('.ab-service-checkbox', $services_form).trigger('change');
269
- }, 0);
270
- });
271
 
272
- autoTickCheckboxes();
273
- $loading_indicator.hide();
274
- });
275
- }
276
  });
277
 
278
- // Open 'Special Days' tab
279
- $('#bookly-special-days-tab').on('click', function () {
280
- $(document.body).trigger( 'special_days.tab_show', [ 1, $loading_indicator ] );
 
 
 
 
281
  });
282
 
283
  // Open schedule tab
284
- $('#bookly-schedule-tab').on('click', function () {
285
  $('.tab-pane > div').hide();
286
- $schedule_container.show();
287
-
288
- // Loads schedule list
289
- if (!$schedule_container.children().length) {
290
- $loading_indicator.show();
291
- $.post(ajaxurl, {action: 'bookly_staff_schedule', id: 1}, function (response) {
292
- // fill in the container
293
- $schedule_container.html(response);
294
- $schedule_form = $('form', $schedule_container);
295
-
296
- // Resets initial values
297
- $('#bookly-schedule-reset').on('click', function (e) {
298
- e.preventDefault();
299
- var ladda = Ladda.create(this);
300
- ladda.start();
301
-
302
- $('.working-schedule-start', $schedule_container).each(function () {
303
- $(this).val($(this).data('default_value'));
304
- $(this).trigger('change');
305
- });
306
-
307
- $('.working-schedule-end', $schedule_container).each(function () {
308
- $(this).val($(this).data('default_value'));
309
- });
310
-
311
- // reset breaks
312
- $.ajax({
313
- url : ajaxurl,
314
- type : 'POST',
315
- data : { action : 'bookly_reset_breaks', breaks : $(this).data('default-breaks') },
316
- dataType : 'json',
317
- success : function(response) {
318
- for (var k in response) {
319
- var $content = $(response[k]);
320
- $('[data-staff_schedule_item_id=' + k +'] .breaks', $schedule_container).html($content);
321
- $content.find('.bookly-intervals-wrapper .delete-break').on('click', function(){
322
- deleteBreak.call(this);
323
- });
324
- }
325
- },
326
- complete : function() {
327
- ladda.stop();
328
- }
329
- });
330
- });
331
-
332
- $('#bookly-schedule-save').on('click', function (e) {
333
- e.preventDefault();
334
- var ladda = Ladda.create(this);
335
- ladda.start();
336
- var data = {};
337
- $('select.working-schedule-start, select.working-schedule-end, input:hidden', $('#bookly-schedule-container')).each(function () {
338
- data[this.name] = this.value;
339
- });
340
- $.post(ajaxurl, $.param(data), function (response) {
341
- ladda.stop();
342
- booklyAlert({success : [BooklyL10n.saved]});
343
- });
344
- });
345
-
346
- // init 'add break' functionality
347
- $('.bookly-js-toggle-popover:not(.break-interval)').popover({
348
- html : true,
349
- placement: 'bottom',
350
- template : '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
351
- trigger : 'manual',
352
- content : function () {
353
- return $($(this).data('popover-content')).html()
354
- }
355
- }).on('click', function () {
356
- $(this).popover('toggle');
357
-
358
- var $popover = $(this).next('.popover'),
359
- working_start = $popover.closest('.row').find('.working-schedule-start').val(),
360
- $break_start = $popover.find('.break-start'),
361
- $break_end = $popover.find('.break-end'),
362
- working_start_time = working_start.split(':'),
363
- working_start_hours = parseInt(working_start_time[0], 10),
364
- break_start_hours = working_start_hours + 1;
365
- if (break_start_hours < 10) {
366
- break_start_hours = '0' + break_start_hours;
367
- }
368
- var break_end_hours = working_start_hours + 2;
369
- if (break_end_hours < 10) {
370
- break_end_hours = '0' + break_end_hours;
371
- }
372
- var break_end_hours_str = break_end_hours + ':' + working_start_time[1] + ':' + working_start_time[2],
373
- break_start_hours_str = break_start_hours + ':' + working_start_time[1] + ':' + working_start_time[2];
374
-
375
- $break_start.val(break_start_hours_str);
376
- $break_end.val(break_end_hours_str);
377
-
378
- hideInaccessibleBreaks($break_start, $break_end);
379
-
380
- $popover.find('.bookly-popover-close').on('click', function () {
381
- $popover.popover('hide');
382
- });
383
- });
384
-
385
- $schedule_container.on('click', '.break-interval', function () {
386
- $('.popover').popover('hide');
387
- var break_id = $(this).closest('.bookly-intervals-wrapper').data('break_id');
388
- $(this).popover({
389
- html: true,
390
- placement: 'bottom',
391
- template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
392
- content: function () {
393
- return $('.bookly-js-content-break-' + break_id).html();
394
- },
395
- trigger: 'manual'
396
- });
397
-
398
- $(this).popover('toggle');
399
-
400
- var $popover = $(this).next('.popover'),
401
- $break_start = $popover.find('.break-start'),
402
- $break_end = $popover.find('.break-end');
403
-
404
- hideInaccessibleBreaks($break_start, $break_end, true);
405
-
406
- $popover.find('.bookly-popover-close').on('click', function () {
407
- $popover.popover('hide');
408
- });
409
- });
410
-
411
- $schedule_container.on('click', '.bookly-js-save-break', function (e) {
412
- var $table = $(this).closest('.bookly-js-schedule-form'),
413
- $row = $table.parents('.staff-schedule-item-row').first(),
414
- $data = {
415
- action : 'bookly_staff_schedule_handle_break',
416
- staff_schedule_item_id : $row.data('staff_schedule_item_id'),
417
- start_time : $table.find('.break-start > option:selected').val(),
418
- end_time : $table.find('.break-end > option:selected').val(),
419
- working_end : $row.find('.working-schedule-end > option:selected').val(),
420
- working_start : $row.find('.working-schedule-start > option:selected').val()
421
- },
422
- $break_interval_wrapper = $table.parents('.bookly-intervals-wrapper').first(),
423
- ladda = Ladda.create(e.currentTarget);
424
- ladda.start();
425
-
426
- if ($break_interval_wrapper.data('break_id')) {
427
- $data['break_id'] = $break_interval_wrapper.data('break_id');
428
- }
429
-
430
- $.post(ajaxurl, $data, function (response) {
431
- if (response.success) {
432
- if (response['item_content']) {
433
- var $new_break_interval_item = $(response['item_content']);
434
- $new_break_interval_item
435
- .hide()
436
- .appendTo($row.find('.breaks-list-content'))
437
- .fadeIn('slow');
438
- $new_break_interval_item.find('.delete-break').on('click', function () {
439
- deleteBreak.call(this);
440
- });
441
- } else if (response.data.interval) {
442
- $break_interval_wrapper
443
- .find('.break-interval')
444
- .text(response.data.interval);
445
- }
446
- $('.popover').popover('hide');
447
- } else {
448
- booklyAlert({error : [response.data.message]});
449
- }
450
- },
451
- 'json'
452
- ).always(function () {
453
- ladda.stop()
454
- });
455
-
456
- return false;
457
- });
458
 
459
- $schedule_container.on('change', '.break-start', function() {
460
- var $start = $(this);
461
- var $end = $start.parents('.bookly-flexbox').find('.break-end');
462
- hideInaccessibleBreaks($start, $end);
463
- }).trigger('change');
464
-
465
- $('.working-schedule-start', $schedule_container).on('change', function () {
466
- var $this = $(this),
467
- $end_select = $this.closest('.bookly-flexbox').find('.working-schedule-end'),
468
- start_time = $this.val();
469
-
470
- // Hide end time options to keep them within 24 hours after start time.
471
- var parts = start_time.split(':');
472
- parts[0] = parseInt(parts[0]) + 24;
473
- var end_time = parts.join(':');
474
- var frag = document.createDocumentFragment();
475
- var old_value = $end_select.val();
476
- var new_value = null;
477
- $('option', $end_select).each(function () {
478
- if (this.value <= start_time || this.value > end_time) {
479
- var span = document.createElement('span');
480
- span.style.display = 'none';
481
- span.appendChild(this.cloneNode(true));
482
- frag.appendChild(span);
483
- } else {
484
- frag.appendChild(this.cloneNode(true));
485
- if (new_value === null || old_value == this.value) {
486
- new_value = this.value;
487
- }
488
- }
489
- });
490
- $end_select.empty().append(frag).val(new_value);
491
-
492
- // when the working day is disabled (working start time is set to 'OFF')
493
- // hide all the elements inside the row
494
- if (!$this.val()) {
495
- $this.closest('.row').find('.bookly-hide-on-off').hide();
496
- } else {
497
- $this.closest('.row').find('.bookly-hide-on-off').show();
498
- }
499
- }).trigger('change');
500
-
501
- $schedule_container.find('.bookly-intervals-wrapper .delete-break').on('click', function() {
502
- deleteBreak.call(this);
503
- });
504
 
505
- $loading_indicator.hide();
506
- });
507
- }
508
  });
509
 
510
- // Open 'Days off' tab
511
  $('#bookly-holidays-tab').on('click', function () {
512
  $('.tab-pane > div').hide();
513
- $holidays_container.show();
514
-
515
- if (!$holidays_container.children().length) {
516
- $loading_indicator.show();
517
- $holidays_container.load(ajaxurl, { action: 'bookly_staff_holidays', id: 1 }, function(){ $loading_indicator.hide(); });
518
- }
519
- });
520
-
521
- function hideInaccessibleBreaks( $start, $end, force_keep_values ) {
522
- var $row = $start.closest('.row'),
523
- $working_start = $row.find('.working-schedule-start'),
524
- $working_end = $row.find('.working-schedule-end'),
525
- frag1 = document.createDocumentFragment(),
526
- frag2 = document.createDocumentFragment(),
527
- old_value = $start.val(),
528
- new_value = null;
529
-
530
- $('option', $start).each(function () {
531
- if ((this.value < $working_start.val() || this.value >= $working_end.val()) && (!force_keep_values || this.value != old_value)) {
532
- var span = document.createElement('span');
533
- span.style.display = 'none';
534
- span.appendChild(this.cloneNode(true));
535
- frag1.appendChild(span);
536
- } else {
537
- frag1.appendChild(this.cloneNode(true));
538
- if (new_value === null || old_value == this.value) {
539
- new_value = this.value;
540
- }
541
- }
542
- });
543
- $start.empty().append(frag1).val(new_value);
544
 
545
- // Hide end time options with value less than in the start time.
546
- old_value = $end.val();
547
- new_value = null;
548
- $('option', $end).each(function () {
549
- if ((this.value <= $start.val() || this.value > $working_end.val()) && (!force_keep_values || this.value != old_value)) {
550
- var span = document.createElement('span');
551
- span.style.display = 'none';
552
- span.appendChild(this.cloneNode(true));
553
- frag2.appendChild(span);
554
- } else {
555
- frag2.appendChild(this.cloneNode(true));
556
- if (new_value === null || old_value == this.value) {
557
- new_value = this.value;
558
- }
559
- }
560
  });
561
- $end.empty().append(frag2).val(new_value);
562
- }
563
 
564
- function deleteBreak() {
565
- var $break_interval_wrapper = $(this).closest('.bookly-intervals-wrapper');
566
- if (confirm(BooklyL10n.are_you_sure)) {
567
- var ladda = Ladda.create(this);
568
- ladda.start();
569
- $.post(ajaxurl, { action: 'bookly_delete_staff_schedule_break', id: $break_interval_wrapper.data('break_id') }, function (response) {
570
- if (response.success) {
571
- $break_interval_wrapper.remove();
572
- }
573
- }).always(function () {
574
- ladda.stop()
575
- });
576
- }
577
- }
578
 
579
  $('#' + active_tab_id).click();
580
  });
@@ -599,12 +193,12 @@ jQuery(function($) {
599
  $.ajax({
600
  type : 'POST',
601
  url : ajaxurl,
602
- data : { action: 'bookly_update_staff_position', position: data }
603
  });
604
  }
605
  });
606
 
607
- $('#ab-newstaff-member').popover({
608
  html: true,
609
  placement: 'bottom',
610
  template: '<div class="popover" style="width: calc(100% - 20px)" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
3
  $new_form = $('#bookly-new-staff'),
4
  $wp_user_select = $('#bookly-new-staff-wpuser'),
5
  $name_input = $('#bookly-new-staff-fullname'),
6
+ $edit_form = $('#bookly-container-edit-staff');
7
+
8
  function saveNewForm() {
9
  booklyAlert({error: [BooklyL10n.limitations]});
10
  }
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  // Save new staff on enter press
13
  $name_input.on('keypress', function (e) {
14
  var code = (e.keyCode ? e.keyCode : e.which);
21
  $new_form.on('keypress', function (e) {
22
  var code = (e.keyCode ? e.keyCode : e.which);
23
  if (code == 27) {
24
+ $('#bookly-newstaff-member').popover('hide');
25
  }
26
  });
27
 
29
  e.stopPropagation();
30
  });
31
 
32
+ $edit_form
33
+ .on('click', '.bookly-pretty-indicator', function (e) {
34
+ e.preventDefault();
35
+ e.stopPropagation();
36
+ var frame = wp.media({
37
+ library: {type: 'image'},
38
+ multiple: false
39
+ });
40
+ frame.on('select', function () {
41
+ var selection = frame.state().get('selection').toJSON(),
42
+ img_src;
43
+ if (selection.length) {
44
+ if (selection[0].sizes['thumbnail'] !== undefined) {
45
+ img_src = selection[0].sizes['thumbnail'].url;
46
+ } else {
47
+ img_src = selection[0].url;
48
+ }
49
+ $edit_form.find('[name=attachment_id]').val(selection[0].id);
50
+ $('#bookly-js-staff-avatar').find('.bookly-js-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
51
+ $('.bookly-thumb-delete').show();
52
+ $(this).hide();
53
+ }
54
+ });
55
+
56
+ frame.open();
57
+ });
58
+
59
  /**
60
  * Load staff profile on click on staff in the list.
61
  */
65
  $staff_list.find('.active').removeClass('active');
66
  $this.addClass('active');
67
 
 
68
  var active_tab_id = $('.nav .active a').attr('id');
69
  $edit_form.html('<div class="bookly-loading"></div>');
70
+ $.get(ajaxurl, {action: 'bookly_edit_staff', id: 1, csrf_token: BooklyL10n.csrf_token}, function (response) {
71
+ $edit_form.html(response.data.html.edit);
72
  booklyAlert(response.data.alert);
73
+ var $details_container = $('#bookly-details-container', $edit_form),
74
+ $loading_indicator = $('.bookly-loading', $edit_form),
75
+ $services_container = $('#bookly-services-container', $edit_form),
76
+ $schedule_container = $('#bookly-schedule-container', $edit_form),
77
+ $holidays_container = $('#bookly-holidays-container', $edit_form)
78
+ ;
79
+ $details_container.html(response.data.html.details);
80
+
81
+ new BooklyStaffDetails($details_container, {
82
+ get_details : {},
83
+ intlTelInput : BooklyL10n.intlTelInput,
84
+ l10n : BooklyL10n,
85
+ renderWpUsers : function (wp_users) {
86
+ $wp_user_select.children(':not(:first)').remove();
87
+ $.each(wp_users, function (index, wp_user) {
88
+ var $option = $('<option>')
89
+ .data('email', wp_user.user_email)
90
+ .val(wp_user.ID)
91
+ .text(wp_user.display_name);
92
+ $wp_user_select.append($option);
93
+ });
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
  });
96
 
97
+ // Delete staff member.
98
+ $('#bookly-staff-delete', $edit_form).on('click', function (e) {
99
+ booklyAlert({error: [BooklyL10n.limitations]});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  });
101
 
102
  // Delete staff avatar
103
+ $('.bookly-thumb-delete', $edit_form).on('click', function () {
104
  var $thumb = $(this).parents('.bookly-js-image');
105
+ $.post(ajaxurl, {action: 'bookly_delete_staff_avatar', id: 1, csrf_token: BooklyL10n.csrf_token}, function (response) {
106
+ if (response.success) {
107
+ $thumb.attr('style', '');
108
+ $edit_form.find('[name=attachment_id]').val('');
109
+ }
110
  });
111
  });
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  // Open details tab
114
+ $('#bookly-details-tab', $edit_form).on('click', function () {
115
  $('.tab-pane > div').hide();
116
  $details_container.show();
117
  });
118
 
119
  // Open services tab
120
+ $('#bookly-services-tab', $edit_form).on('click', function () {
121
  $('.tab-pane > div').hide();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ new BooklyStaffServices($services_container, {
124
+ get_staff_services: {
125
+ action : 'bookly_get_staff_services',
126
+ staff_id : 1,
127
+ csrf_token: BooklyL10n.csrf_token
128
+ },
129
+ l10n: BooklyL10n
130
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ $services_container.show();
 
 
 
133
  });
134
 
135
+ // Open special days tab
136
+ $('#bookly-special-days-tab', $edit_form).on('click', function () {
137
+ new BooklyStaffSpecialDays($('.bookly-js-special-days-container'), {
138
+ staff_id : 1,
139
+ csrf_token: BooklyL10n.csrf_token,
140
+ l10n : SpecialDaysL10n
141
+ });
142
  });
143
 
144
  // Open schedule tab
145
+ $('#bookly-schedule-tab', $edit_form).on('click', function () {
146
  $('.tab-pane > div').hide();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
+ new BooklyStaffSchedule($schedule_container, {
149
+ get_staff_schedule: {
150
+ action: 'bookly_get_staff_schedule',
151
+ staff_id: 1,
152
+ csrf_token: BooklyL10n.csrf_token
153
+ },
154
+ l10n: BooklyL10n
155
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ $schedule_container.show();
 
 
158
  });
159
 
160
+ // Open holiday tab
161
  $('#bookly-holidays-tab').on('click', function () {
162
  $('.tab-pane > div').hide();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ new BooklyStaffDaysOff($holidays_container, {
165
+ staff_id : 1,
166
+ csrf_token: BooklyL10n.csrf_token,
167
+ l10n : BooklyL10n
 
 
 
 
 
 
 
 
 
 
 
168
  });
 
 
169
 
170
+ $holidays_container.show();
171
+ });
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  $('#' + active_tab_id).click();
174
  });
193
  $.ajax({
194
  type : 'POST',
195
  url : ajaxurl,
196
+ data : {action: 'bookly_update_staff_position', position: data, csrf_token: BooklyL10n.csrf_token}
197
  });
198
  }
199
  });
200
 
201
+ $('#bookly-newstaff-member').popover({
202
  html: true,
203
  placement: 'bottom',
204
  template: '<div class="popover" style="width: calc(100% - 20px)" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
backend/modules/staff/templates/_breaks.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 
2
  $breaks_list = $item->getBreaksList();
3
  ?>
4
  <div class="breaks-list">
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var \BooklyLite\Lib\Entities\StaffScheduleItem $item */
3
  $breaks_list = $item->getBreaksList();
4
  ?>
5
  <div class="breaks-list">
backend/modules/staff/templates/_details.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var \BooklyLite\Lib\Entities\Staff $staff */
3
+ ?>
4
+ <form>
5
+ <div class="form-group">
6
+ <label for="bookly-full-name"><?php _e( 'Full name', 'bookly' ) ?></label>
7
+ <input type="text" class="form-control" id="bookly-full-name" name="full_name" value="<?php echo esc_attr( $staff->getFullName() ) ?>"/>
8
+ </div>
9
+ <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
10
+ <div class="form-group">
11
+ <label for="bookly-wp-user"><?php _e( 'User', 'bookly' ) ?></label>
12
+
13
+ <p class="help-block">
14
+ <?php _e( 'If this staff member requires separate login to access personal calendar, a regular WP user needs to be created for this purpose.', 'bookly' ) ?>
15
+ <?php _e( 'User with "Administrator" role will have access to calendars and settings of all staff members, user with another role will have access only to personal calendar and settings.', 'bookly' ) ?>
16
+ <?php _e( 'If you leave this field blank, this staff member will not be able to access personal calendar using WP backend.', 'bookly' ) ?>
17
+ </p>
18
+
19
+ <select class="form-control" name="wp_user_id" id="bookly-wp-user">
20
+ <option value=""><?php _e( 'Select from WP users', 'bookly' ) ?></option>
21
+ <?php foreach ( $users_for_staff as $user ) : ?>
22
+ <option value="<?php echo $user->ID ?>" data-email="<?php echo $user->user_email ?>" <?php selected( $user->ID, $staff->getWpUserId() ) ?>><?php echo $user->display_name ?></option>
23
+ <?php endforeach ?>
24
+ </select>
25
+ </div>
26
+ <?php endif ?>
27
+
28
+ <div class="row">
29
+ <div class="col-sm-6">
30
+ <div class="form-group">
31
+ <label for="bookly-email"><?php _e( 'Email', 'bookly' ) ?></label>
32
+ <input class="form-control" id="bookly-email" name="email"
33
+ value="<?php echo esc_attr( $staff->getEmail() ) ?>"
34
+ type="text"/>
35
+ </div>
36
+ </div>
37
+ <div class="col-sm-6">
38
+ <div class="form-group">
39
+ <label for="bookly-phone"><?php _e( 'Phone', 'bookly' ) ?></label>
40
+ <input class="form-control" id="bookly-phone"
41
+ value="<?php echo esc_attr( $staff->getPhone() ) ?>"
42
+ type="text"/>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="form-group">
48
+ <label for="bookly-info"><?php _e( 'Info', 'bookly' ) ?></label>
49
+ <p class="help-block">
50
+ <?php printf( __( 'This text can be inserted into notifications with %s code.', 'bookly' ), '{staff_info}' ) ?>
51
+ </p>
52
+ <textarea id="bookly-info" name="info" rows="3" class="form-control"><?php echo esc_textarea( $staff->getInfo() ) ?></textarea>
53
+ </div>
54
+
55
+ <div class="form-group">
56
+ <label for="bookly-visibility"><?php _e( 'Visibility', 'bookly' ) ?></label>
57
+ <p class="help-block">
58
+ <?php _e( 'To make staff member invisible to your customers set the visibility to "Private".', 'bookly' ) ?>
59
+ </p>
60
+ <select name="visibility" class="form-control" id="bookly-visibility">
61
+ <option value="public" <?php selected( $staff->getVisibility(), 'public' ) ?>><?php _e( 'Public', 'bookly' ) ?></option>
62
+ <option value="private" <?php selected( $staff->getVisibility(), 'private' ) ?>><?php _e( 'Private', 'bookly' ) ?></option>
63
+ </select>
64
+ </div>
65
+ <?php BooklyLite\Lib\Proxy\Shared::renderStaffForm( $staff ) ?>
66
+
67
+ <div class="form-group">
68
+ <h3><?php _e( 'Google Calendar integration', 'bookly' ) ?></h3>
69
+ <p class="help-block">
70
+ <?php _e( 'Synchronize staff member appointments with Google Calendar.', 'bookly' ) ?>
71
+ </p>
72
+ <p>
73
+ <?php if ( isset( $authUrl ) ) : ?>
74
+ <?php if ( $authUrl ) : ?>
75
+ <a href="<?php echo $authUrl ?>"><?php _e( 'Connect', 'bookly' ) ?></a>
76
+ <?php else : ?>
77
+ <?php printf( __( 'Please configure Google Calendar <a href="%s">settings</a> first', 'bookly' ), \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Settings\Controller::page_slug, array( 'tab' => 'google_calendar' ) ) ) ?>
78
+ <?php endif ?>
79
+ <?php else : ?>
80
+ <?php _e( 'Connected', 'bookly' ) ?> (<a href="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Staff\Controller::page_slug, array( 'google_logout' => $staff->getId() ) ) ?>"><?php _e( 'disconnect', 'bookly' ) ?></a>)
81
+ <?php endif ?>
82
+ </p>
83
+ </div>
84
+ <?php if ( ! isset( $authUrl ) ) : ?>
85
+ <div class="form-group">
86
+ <label for="bookly-calendar-id"><?php _e( 'Calendar', 'bookly' ) ?></label>
87
+ <select class="form-control" name="google_calendar_id" id="bookly-calendar-id">
88
+ <?php foreach ( $google_calendars as $id => $calendar ) : ?>
89
+ <option
90
+ <?php selected( $staff->getGoogleCalendarId() == $id || $staff->getGoogleCalendarId() == '' && $calendar['primary'] ) ?>
91
+ value="<?php echo esc_attr( $id ) ?>">
92
+ <?php echo esc_html( $calendar['summary'] ) ?>
93
+ </option>
94
+ <?php endforeach ?>
95
+ </select>
96
+ </div>
97
+ <?php endif ?>
98
+
99
+ <input type="hidden" name="id" value="<?php echo $staff->getId() ?>">
100
+ <input type="hidden" name="attachment_id" value="<?php echo $staff->getAttachmentId() ?>">
101
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
102
+
103
+ <div class="panel-footer">
104
+ <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
105
+ <?php \BooklyLite\Lib\Utils\Common::deleteButton( 'bookly-staff-delete', 'btn-lg pull-left' ) ?>
106
+ <?php endif ?>
107
+ <?php \BooklyLite\Lib\Utils\Common::customButton( 'bookly-details-save', 'btn-lg btn-success', __( 'Save', 'bookly' ) ) ?>
108
+ <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
109
+ </div>
110
+ </form>
backend/modules/staff/templates/_list_item.php CHANGED
@@ -1,6 +1,5 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  $img = wp_get_attachment_image_src( $staff['attachment_id'], 'thumbnail' );
3
- /** @var \BooklyLite\Lib\Entities\Staff $staff */
4
  ?>
5
  <li class="bookly-nav-item<?php if ( $active_staff_id == $staff['id'] ) : ?> active<?php endif ?>" id="bookly-staff-<?php echo $staff['id'] ?>" data-staff-id="<?php echo $staff['id'] ?>">
6
  <div class="bookly-flexbox">
@@ -9,8 +8,9 @@
9
  </div>
10
  <div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
11
  <div class="bookly-thumb bookly-thumb-sm bookly-margin-right-lg"
12
- <?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;background-position:0"' : '' ?>
13
- ></div>
 
14
  </div>
15
  <div class="bookly-flex-cell bookly-vertical-middle">
16
  <?php echo esc_html( $staff['full_name'] ) ?>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
  $img = wp_get_attachment_image_src( $staff['attachment_id'], 'thumbnail' );
 
3
  ?>
4
  <li class="bookly-nav-item<?php if ( $active_staff_id == $staff['id'] ) : ?> active<?php endif ?>" id="bookly-staff-<?php echo $staff['id'] ?>" data-staff-id="<?php echo $staff['id'] ?>">
5
  <div class="bookly-flexbox">
8
  </div>
9
  <div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
10
  <div class="bookly-thumb bookly-thumb-sm bookly-margin-right-lg"
11
+ <?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;background-position:0"' : '' ?>
12
+ >
13
+ </div>
14
  </div>
15
  <div class="bookly-flex-cell bookly-vertical-middle">
16
  <?php echo esc_html( $staff['full_name'] ) ?>
backend/modules/staff/templates/_new.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
3
  <div class="form-group">
4
- <button id="ab-newstaff-member" type="button" class="btn btn-xlg btn-block btn-success-outline">
5
  <i class="dashicons dashicons-plus-alt"></i>
6
  <?php _e( 'New Staff Member', 'bookly' ) ?>
7
  </button>
@@ -13,10 +13,10 @@
13
  <label for="bookly-new-staff-wpuser"><?php _e( 'User', 'bookly' ) ?></label>
14
  <p class="help-block">
15
  <?php _e( 'If this staff member requires separate login to access personal calendar, a regular WP user needs to be created for this purpose.', 'bookly' ) ?>
16
- <?php _e( 'User with "Administrator" role will have access to calendars and settings of all staff members, user with some other role will have access only to personal calendar and settings.', 'bookly' ) ?>
17
- <?php _e( 'If you will leave this field blank, this staff member will not be able to access personal calendar using WP backend.', 'bookly' ) ?>
18
  </p>
19
- <select class="form-control" name="ab_newstaff_wpuser" id="bookly-new-staff-wpuser">
20
  <option value=""><?php _e( 'Select from WP users', 'bookly' ) ?></option>
21
  <?php foreach ( $users_for_staff as $user ) : ?>
22
  <option value="<?php echo $user->ID ?>"><?php echo $user->display_name ?></option>
@@ -26,7 +26,7 @@
26
  <div class="form-group bookly-margin-bottom-md">
27
  <div class="form-field form-required">
28
  <label for="bookly-new-staff-fullname"><?php _e( 'Full name', 'bookly' ) ?></label>
29
- <input class="form-control" id="bookly-new-staff-fullname" name="ab_newstaff_fullname" type="text">
30
  </div>
31
  </div>
32
 
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
2
  <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
3
  <div class="form-group">
4
+ <button id="bookly-newstaff-member" type="button" class="btn btn-xlg btn-block btn-success-outline">
5
  <i class="dashicons dashicons-plus-alt"></i>
6
  <?php _e( 'New Staff Member', 'bookly' ) ?>
7
  </button>
13
  <label for="bookly-new-staff-wpuser"><?php _e( 'User', 'bookly' ) ?></label>
14
  <p class="help-block">
15
  <?php _e( 'If this staff member requires separate login to access personal calendar, a regular WP user needs to be created for this purpose.', 'bookly' ) ?>
16
+ <?php _e( 'User with "Administrator" role will have access to calendars and settings of all staff members, user with another role will have access only to personal calendar and settings.', 'bookly' ) ?>
17
+ <?php _e( 'If you leave this field blank, this staff member will not be able to access personal calendar using WP backend.', 'bookly' ) ?>
18
  </p>
19
+ <select class="form-control" name="bookly-new-staff-wpuser" id="bookly-new-staff-wpuser">
20
  <option value=""><?php _e( 'Select from WP users', 'bookly' ) ?></option>
21
  <?php foreach ( $users_for_staff as $user ) : ?>
22
  <option value="<?php echo $user->ID ?>"><?php echo $user->display_name ?></option>
26
  <div class="form-group bookly-margin-bottom-md">
27
  <div class="form-field form-required">
28
  <label for="bookly-new-staff-fullname"><?php _e( 'Full name', 'bookly' ) ?></label>
29
+ <input class="form-control" id="bookly-new-staff-fullname" name="bookly-new-staff-fullname" type="text">
30
  </div>
31
  </div>
32
 
backend/modules/staff/templates/edit.php CHANGED
@@ -1,5 +1,7 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- /** @var \BooklyLite\Lib\Entities\Staff $staff */
 
 
3
  ?>
4
  <div class="panel panel-default bookly-main">
5
  <div class="panel-body">
@@ -8,15 +10,15 @@
8
  <div id="bookly-js-staff-avatar" class="bookly-thumb bookly-thumb-lg bookly-margin-right-lg">
9
  <div class="bookly-flex-cell" style="width: 100%">
10
  <div class="form-group">
11
- <?php $img = wp_get_attachment_image_src( $staff->get( 'attachment_id' ), 'thumbnail' ) ?>
12
 
13
  <div class="bookly-js-image bookly-thumb bookly-thumb-lg bookly-margin-right-lg"
14
- <?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;"' : '' ?>
15
  >
16
  <a class="dashicons dashicons-trash text-danger bookly-thumb-delete"
17
  href="javascript:void(0)"
18
  title="<?php esc_attr_e( 'Delete', 'bookly' ) ?>"
19
- <?php if ( !$img ) : ?>style="display: none;"<?php endif ?>>
20
  </a>
21
  <div class="bookly-thumb-edit">
22
  <div class="bookly-pretty">
@@ -30,7 +32,7 @@
30
  </div>
31
  </div>
32
  </div>
33
- <div class="bookly-flex-cell bookly-vertical-top"><h1 class="bookly-js-staff-name-<?php echo $staff->get( 'id' ) ?>"><?php echo $staff->get( 'full_name' ) ?></h1></div>
34
  </div>
35
 
36
  <ul class="nav nav-tabs nav-justified bookly-nav-justified">
@@ -52,7 +54,7 @@
52
  <span class="bookly-nav-tabs-title"><?php _e( 'Schedule', 'bookly' ) ?></span>
53
  </a>
54
  </li>
55
- <?php do_action( 'bookly_special_days_render_tab' ) ?>
56
  <li>
57
  <a id="bookly-holidays-tab" href="#daysoff" data-toggle="tab">
58
  <i class="bookly-icon bookly-icon-daysoff"></i>
@@ -65,113 +67,7 @@
65
  <div style="display: none;" class="bookly-loading"></div>
66
 
67
  <div class="tab-pane active" id="details">
68
- <div id="bookly-details-container">
69
- <form>
70
- <div class="form-group">
71
- <label for="bookly-full-name"><?php _e( 'Full name', 'bookly' ) ?></label>
72
- <input type="text" class="form-control" id="bookly-full-name" name="full_name" value="<?php echo esc_attr( $staff->get( 'full_name' ) ) ?>" />
73
- </div>
74
- <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
75
- <div class="form-group">
76
- <label for="bookly-wp-user"><?php _e( 'User', 'bookly' ) ?></label>
77
-
78
- <p class="help-block">
79
- <?php _e( 'If this staff member requires separate login to access personal calendar, a regular WP user needs to be created for this purpose.', 'bookly' ) ?>
80
- <?php _e( 'User with "Administrator" role will have access to calendars and settings of all staff members, user with some other role will have access only to personal calendar and settings.', 'bookly' ) ?>
81
- <?php _e( 'If you will leave this field blank, this staff member will not be able to access personal calendar using WP backend.', 'bookly' ) ?>
82
- </p>
83
-
84
- <select class="form-control" name="wp_user_id" id="bookly-wp-user">
85
- <option value=""><?php _e( 'Select from WP users', 'bookly' ) ?></option>
86
- <?php foreach ( $users_for_staff as $user ) : ?>
87
- <option value="<?php echo $user->ID ?>" data-email="<?php echo $user->user_email ?>" <?php selected( $user->ID, $staff->get( 'wp_user_id' ) ) ?>><?php echo $user->display_name ?></option>
88
- <?php endforeach ?>
89
- </select>
90
- </div>
91
- <?php endif ?>
92
-
93
- <div class="row">
94
- <div class="col-sm-6">
95
- <div class="form-group">
96
- <label for="bookly-email"><?php _e( 'Email', 'bookly' ) ?></label>
97
- <input class="form-control" id="bookly-email" name="email"
98
- value="<?php echo esc_attr( $staff->get( 'email' ) ) ?>"
99
- type="text" />
100
- </div>
101
- </div>
102
- <div class="col-sm-6">
103
- <div class="form-group">
104
- <label for="bookly-phone"><?php _e( 'Phone', 'bookly' ) ?></label>
105
- <input class="form-control" id="bookly-phone"
106
- value="<?php echo esc_attr( $staff->get( 'phone' ) ) ?>"
107
- type="text" />
108
- </div>
109
- </div>
110
- </div>
111
-
112
- <div class="form-group">
113
- <label for="bookly-info"><?php _e( 'Info', 'bookly' ) ?></label>
114
- <p class="help-block">
115
- <?php printf( __( 'This text can be inserted into notifications with %s code.', 'bookly' ), '{staff_info}' ) ?>
116
- </p>
117
- <textarea id="bookly-info" name="info" rows="3" class="form-control"><?php echo esc_textarea( $staff->get( 'info' ) ) ?></textarea>
118
- </div>
119
-
120
- <div class="form-group">
121
- <label for="bookly-visibility"><?php _e( 'Visibility', 'bookly' ) ?></label>
122
- <p class="help-block">
123
- <?php _e( 'To make staff member invisible to your customers set the visibility to "Private".', 'bookly' ) ?>
124
- </p>
125
- <select name="visibility" class="form-control" id="bookly-visibility">
126
- <option value="public" <?php selected( $staff->get( 'visibility' ), 'public' ) ?>><?php _e( 'Public', 'bookly' ) ?></option>
127
- <option value="private" <?php selected( $staff->get( 'visibility' ), 'private' ) ?>><?php _e( 'Private', 'bookly' ) ?></option>
128
- </select>
129
- </div>
130
- <?php do_action( 'bookly_render_staff_form', $staff ) ?>
131
- <div class="form-group">
132
- <h3><?php _e( 'Google Calendar integration', 'bookly' ) ?></h3>
133
- <p class="help-block">
134
- <?php _e( 'Synchronize staff member appointments with Google Calendar.', 'bookly' ) ?>
135
- </p>
136
- <p>
137
- <?php if ( isset( $authUrl ) ) : ?>
138
- <?php if ( $authUrl ) : ?>
139
- <a href="<?php echo $authUrl ?>"><?php _e( 'Connect', 'bookly' ) ?></a>
140
- <?php else : ?>
141
- <?php printf( __( 'Please configure Google Calendar <a href="%s">settings</a> first', 'bookly' ), \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Settings\Controller::page_slug, array( 'tab' => 'google_calendar' ) ) ) ?>
142
- <?php endif ?>
143
- <?php else : ?>
144
- <?php _e( 'Connected', 'bookly' ) ?> (<a href="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Staff\Controller::page_slug, array( 'google_logout' => $staff->get( 'id' ) ) ) ?>" ><?php _e( 'disconnect', 'bookly' ) ?></a>)
145
- <?php endif ?>
146
- </p>
147
- </div>
148
- <?php if ( ! isset( $authUrl ) ) : ?>
149
- <div class="form-group">
150
- <label for="bookly-calendar-id"><?php _e( 'Calendar', 'bookly' ) ?></label>
151
- <select class="form-control" name="google_calendar_id" id="bookly-calendar-id">
152
- <?php foreach ( $calendar_list as $id => $calendar ) : ?>
153
- <option
154
- <?php selected( $staff->get( 'google_calendar_id' ) == $id || $staff->get( 'google_calendar_id' ) == '' && $calendar['primary'] ) ?>
155
- value="<?php echo esc_attr( $id ) ?>">
156
- <?php echo esc_html( $calendar['summary'] ) ?>
157
- </option>
158
- <?php endforeach ?>
159
- </select>
160
- </div>
161
- <?php endif ?>
162
-
163
- <input type="hidden" name="id" value="<?php echo $staff->get( 'id' ) ?>">
164
- <input type="hidden" name="attachment_id" value="<?php echo $staff->get( 'attachment_id' ) ?>">
165
-
166
- <div class="panel-footer">
167
- <?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
168
- <?php \BooklyLite\Lib\Utils\Common::deleteButton( 'bookly-delete', 'btn-lg pull-left' ) ?>
169
- <?php endif ?>
170
- <?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
171
- <?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
172
- </div>
173
- </form>
174
- </div>
175
  </div>
176
  <div class="tab-pane" id="services">
177
  <div id="bookly-services-container" style="display: none"></div>
1
+ <?php if ( ! defined( 'ABSPATH' ) ) {
2
+ exit;
3
+ } // Exit if accessed directly
4
+ /** @var \BooklyLite\Lib\Entities\Staff $staff */
5
  ?>
6
  <div class="panel panel-default bookly-main">
7
  <div class="panel-body">
10
  <div id="bookly-js-staff-avatar" class="bookly-thumb bookly-thumb-lg bookly-margin-right-lg">
11
  <div class="bookly-flex-cell" style="width: 100%">
12
  <div class="form-group">
13
+ <?php $img = wp_get_attachment_image_src( $staff->getAttachmentId(), 'thumbnail' ) ?>
14
 
15
  <div class="bookly-js-image bookly-thumb bookly-thumb-lg bookly-margin-right-lg"
16
+ <?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;"' : '' ?>
17
  >
18
  <a class="dashicons dashicons-trash text-danger bookly-thumb-delete"
19
  href="javascript:void(0)"
20
  title="<?php esc_attr_e( 'Delete', 'bookly' ) ?>"
21
+ <?php if ( ! $img ) : ?>style="display: none;"<?php endif ?>>
22
  </a>
23
  <div class="bookly-thumb-edit">
24
  <div class="bookly-pretty">
32
  </div>
33
  </div>
34
  </div>
35
+ <div class="bookly-flex-cell bookly-vertical-top"><h1 class="bookly-js-staff-name-<?php echo $staff->getId() ?>"><?php echo $staff->getFullName() ?></h1></div>
36
  </div>
37
 
38
  <ul class="nav nav-tabs nav-justified bookly-nav-justified">
54
  <span class="bookly-nav-tabs-title"><?php _e( 'Schedule', 'bookly' ) ?></span>
55
  </a>
56
  </li>
57
+ <?php \BooklyLite\Lib\Proxy\Shared::renderStaffTab( $staff ) ?>
58
  <li>
59
  <a id="bookly-holidays-tab" href="#daysoff" data-toggle="tab">
60
  <i class="bookly-icon bookly-icon-daysoff"></i>
67
  <div style="display: none;" class="bookly-loading"></div>
68
 
69
  <div class="tab-pane active" id="details">
70
+ <div id="bookly-details-container"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  </div>
72
  <div class="tab-pane" id="services">
73
  <div id="bookly-services-container" style="display: none"></div>
backend/modules/staff/templates/holidays.php CHANGED
@@ -15,28 +15,4 @@
15
  </div>
16
  </div>
17
  </div>
18
-
19
- <div class="bookly-js-holidays jCal-wrap bookly-margin-top-lg"></div>
20
-
21
- <script>
22
- jQuery(function ($) {
23
- var d = new Date();
24
- $('.bookly-js-holidays').jCal({
25
- day : new Date(d.getFullYear(), 0, 1),
26
- days : 1,
27
- showMonths : 12,
28
- scrollSpeed : 350,
29
- events : <?php echo $holidays ?>,
30
- action : 'bookly_staff_holidays_update',
31
- staff_id : <?php echo $staff_id ?>,
32
- dayOffset : <?php echo (int)get_option('start_of_week') ?>,
33
- loadingImg : <?php echo json_encode( plugins_url( 'bookly-responsive-appointment-booking-tool/backend/resources/images/loading.gif' ) ) ?>
34
- });
35
-
36
- $('.bookly-js-jCalBtn').on('click', function(e) {
37
- e.preventDefault();
38
- var trigger = $(this).data('trigger');
39
- $('.bookly-js-holidays').find($(trigger)).trigger('click');
40
- })
41
- });
42
- </script>
15
  </div>
16
  </div>
17
  </div>
18
+ <div class="bookly-js-holidays jCal-wrap bookly-margin-top-lg"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/modules/staff/templates/index.php CHANGED
@@ -14,7 +14,6 @@
14
  </div>
15
  <?php endif ?>
16
  </div>
17
-
18
  <div class="row">
19
  <div id="bookly-sidebar" class="col-sm-4"
20
  <?php if ( ! \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
14
  </div>
15
  <?php endif ?>
16
  </div>
 
17
  <div class="row">
18
  <div id="bookly-sidebar" class="col-sm-4"
19
  <?php if ( ! \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
backend/modules/staff/templates/schedule.php CHANGED
@@ -1,21 +1,24 @@
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
- $working_start = new \BooklyLite\Backend\Modules\Staff\Forms\Widgets\TimeChoice( array( 'empty_value' => __( 'OFF', 'bookly' ), 'type' => 'from' ) );
3
- $working_end = new \BooklyLite\Backend\Modules\Staff\Forms\Widgets\TimeChoice( array( 'use_empty' => false, 'type' => 'to' ) );
 
 
 
4
  $default_breaks = array( 'staff_id' => 1 );
5
- $break_start = new \BooklyLite\Backend\Modules\Staff\Forms\Widgets\TimeChoice( array( 'use_empty' => false, 'type' => 'from' ) );
6
  $break_end = clone $working_end;
7
  ?>
8
  <div>
9
  <form>
10
  <?php foreach ( $schedule_items as $item ) : ?>
11
- <div data-id="<?php echo $item->get( 'day_index' ) ?>"
12
- data-staff_schedule_item_id="<?php echo $item->get( 'id' ) ?>"
13
  class="staff-schedule-item-row panel panel-default bookly-panel-unborder">
14
 
15
  <div class="panel-heading bookly-padding-vertical-md">
16
  <div class="row">
17
  <div class="col-sm-7 col-lg-5">
18
- <span class="panel-title"><?php _e( \BooklyLite\Lib\Utils\DateTime::getWeekDayByNumber( $item->get( 'day_index' ) - 1 ) /* take translation from WP catalog */ ) ?></span>
19
  </div>
20
  <div class="col-sm-5 col-lg-7 hidden-xs hidden-sm">
21
  <div class="bookly-font-smaller bookly-color-gray">
@@ -26,78 +29,77 @@
26
  </div>
27
 
28
  <div class="panel-body padding-lr-none">
29
- <div class="row">
30
- <div class="col-sm-7 col-lg-5">
31
- <div class="bookly-flexbox">
32
- <div class="bookly-flex-cell" style="width: 50%">
33
- <?php
34
- $day_is_not_available = null === $item->get( 'start_time' );
35
- echo $working_start->render(
36
- "start_time[{$item->get( 'day_index' )}]",
37
- $item->get( 'start_time' ),
38
- array( 'class' => 'working-schedule-start form-control' )
39
- );
40
- ?>
41
- </div>
42
- <div class="bookly-flex-cell text-center" style="width: 1%">
43
- <div class="bookly-margin-horizontal-lg bookly-hide-on-off">
44
- <?php _e( 'to', 'bookly' ) ?>
45
- </div>
46
- </div>
47
- <div class="bookly-flex-cell" style="width: 50%">
48
- <?php
49
- echo $working_end->render(
50
- "end_time[{$item->get( 'day_index' )}]",
51
- $item->get( 'end_time' ),
52
- array( 'class' => 'working-schedule-end form-control bookly-hide-on-off' )
53
- );
54
- ?>
55
  </div>
56
  </div>
57
-
58
- <input type="hidden"
59
- name="days[<?php echo $item->get( 'id' ) ?>]"
60
- value="<?php echo $item->get( 'day_index' ) ?>"
61
- >
 
 
 
 
62
  </div>
63
 
64
- <div class="col-sm-5 col-lg-7">
65
- <div class="bookly-intervals-wrapper bookly-hide-on-off">
66
- <button type="button"
67
- class="bookly-js-toggle-popover btn btn-link bookly-btn-unborder bookly-margin-vertical-screenxs-sm"
68
- data-popover-content=".bookly-js-content-break-<?php echo $item->get( 'id' ) ?>">
69
- <?php _e( 'add break', 'bookly' ) ?>
70
- </button>
71
 
72
- <div class="bookly-js-content-break-<?php echo $item->get( 'id' ) ?> hidden">
73
- <div class="error" style="display: none"></div>
 
 
 
 
 
74
 
75
- <div class="bookly-js-schedule-form">
76
- <div class="bookly-flexbox" style="width: 260px">
77
- <div class="bookly-flex-cell" style="width: 48%;">
78
- <?php echo $break_start->render( '', $item->get( 'start_time' ), array( 'class' => 'break-start form-control' ) ) ?>
79
- </div>
80
- <div class="bookly-flex-cell" style="width: 4%;">
81
- <div class="bookly-margin-horizontal-lg">
82
- <?php _e( 'to', 'bookly' ) ?>
83
- </div>
84
- </div>
85
- <div class="bookly-flex-cell" style="width: 48%;">
86
- <?php echo $break_end->render( '', $item->get( 'end_time' ), array( 'class' => 'break-end form-control' ) ) ?>
87
  </div>
88
  </div>
89
- <hr>
90
- <div class="text-right">
91
- <?php \BooklyLite\Lib\Utils\Common::submitButton( null, 'bookly-js-save-break' ) ?>
92
- <?php \BooklyLite\Lib\Utils\Common::customButton( null, 'bookly-popover-close btn-lg btn-default', __( 'Close', 'bookly' ) ) ?>
93
  </div>
94
  </div>
 
 
 
 
 
95
  </div>
96
  </div>
 
97
 
98
- <div class="breaks bookly-hide-on-off">
99
- <?php include '_breaks.php' ?>
100
- </div>
101
  </div>
102
  </div>
103
  </div>
@@ -106,10 +108,11 @@
106
  <?php endforeach ?>
107
 
108
  <input type="hidden" name="action" value="bookly_staff_schedule_update">
 
109
 
110
  <div class="panel-footer">
111
- <?php \BooklyLite\Lib\Utils\Common::submitButton( 'bookly-schedule-save' ) ?>
112
- <?php \BooklyLite\Lib\Utils\Common::customButton( 'bookly-schedule-reset', 'btn-lg btn-default', __( 'Reset', 'bookly' ), array( 'data-default-breaks' => esc_attr( json_encode( $default_breaks ) ), 'data-spinner-color' => 'rgb(62, 66, 74)' ) ) ?>
113
  </div>
114
  </form>
115
  </div>
1
  <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ use BooklyLite\Backend\Modules\Staff\Forms\Widgets\TimeChoice;
3
+ use BooklyLite\Lib\Utils\Common;
4
+ /** @var \BooklyLite\Lib\Entities\StaffScheduleItem[] $schedule_items */
5
+ $working_start = new TimeChoice( array( 'empty_value' => __( 'OFF', 'bookly' ), 'type' => 'from' ) );
6
+ $working_end = new TimeChoice( array( 'use_empty' => false, 'type' => 'to' ) );
7
  $default_breaks = array( 'staff_id' => 1 );
8
+ $break_start = new TimeChoice( array( 'use_empty' => false, 'type' => 'break_from' ) );
9
  $break_end = clone $working_end;
10
  ?>
11
  <div>
12
  <form>
13
  <?php foreach ( $schedule_items as $item ) : ?>
14
+ <div data-id="<?php echo $item->getDayIndex() ?>"
15
+ data-staff_schedule_item_id="<?php echo $item->getId() ?>"
16
  class="staff-schedule-item-row panel panel-default bookly-panel-unborder">
17
 
18
  <div class="panel-heading bookly-padding-vertical-md">
19
  <div class="row">
20
  <div class="col-sm-7 col-lg-5">
21
+ <span class="panel-title"><?php _e( \BooklyLite\Lib\Utils\DateTime::getWeekDayByNumber( $item->getDayIndex() - 1 ) /* take translation from WP catalog */ ) ?></span>
22
  </div>
23
  <div class="col-sm-5 col-lg-7 hidden-xs hidden-sm">
24
  <div class="bookly-font-smaller bookly-color-gray">
29
  </div>
30
 
31
  <div class="panel-body padding-lr-none">
32
+ <div class="row">
33
+ <div class="col-sm-7 col-lg-5">
34
+ <div class="bookly-flexbox">
35
+ <div class="bookly-flex-cell" style="width: 50%">
36
+ <?php
37
+ $day_is_not_available = null === $item->getStartTime();
38
+ echo $working_start->render(
39
+ "start_time[{$item->getDayIndex()}]",
40
+ $item->getStartTime(),
41
+ array( 'class' => 'working-schedule-start form-control' )
42
+ );
43
+ ?>
44
+ </div>
45
+ <div class="bookly-flex-cell text-center" style="width: 1%">
46
+ <div class="bookly-margin-horizontal-lg bookly-hide-on-off">
47
+ <?php _e( 'to', 'bookly' ) ?>
 
 
 
 
 
 
 
 
 
 
48
  </div>
49
  </div>
50
+ <div class="bookly-flex-cell" style="width: 50%">
51
+ <?php
52
+ echo $working_end->render(
53
+ "end_time[{$item->getDayIndex()}]",
54
+ $item->getEndTime(),
55
+ array( 'class' => 'working-schedule-end form-control bookly-hide-on-off' )
56
+ );
57
+ ?>
58
+ </div>
59
  </div>
60
 
61
+ <input type="hidden"
62
+ name="days[<?php echo $item->getId() ?>]"
63
+ value="<?php echo $item->getDayIndex() ?>"
64
+ >
65
+ </div>
 
 
66
 
67
+ <div class="col-sm-5 col-lg-7">
68
+ <div class="bookly-intervals-wrapper bookly-hide-on-off">
69
+ <button type="button"
70
+ class="bookly-js-toggle-popover btn btn-link bookly-btn-unborder bookly-margin-vertical-screenxs-sm"
71
+ data-popover-content=".bookly-js-content-break-<?php echo $item->getId() ?>">
72
+ <?php _e( 'add break', 'bookly' ) ?>
73
+ </button>
74
 
75
+ <div class="bookly-js-content-break-<?php echo $item->getId() ?> hidden">
76
+ <div class="error" style="display: none"></div>
77
+
78
+ <div class="bookly-js-schedule-form">
79
+ <div class="bookly-flexbox" style="width: 260px">
80
+ <div class="bookly-flex-cell" style="width: 48%;">
81
+ <?php echo $break_start->render( '', $item->getStartTime(), array( 'class' => 'break-start form-control' ) ) ?>
82
+ </div>
83
+ <div class="bookly-flex-cell" style="width: 4%;">
84
+ <div class="bookly-margin-horizontal-lg">
85
+ <?php _e( 'to', 'bookly' ) ?>
 
86
  </div>
87
  </div>
88
+ <div class="bookly-flex-cell" style="width: 48%;">
89
+ <?php echo $break_end->render( '', $item->getEndTime(), array( 'class' => 'break-end form-control' ) ) ?>
 
 
90
  </div>
91
  </div>
92
+ <hr>
93
+ <div class="text-right">
94
+ <?php Common::customButton( null, 'bookly-js-save-break btn-lg btn-success', __( 'Save', 'bookly' ) ) ?>
95
+ <?php Common::customButton( null, 'bookly-popover-close btn-lg btn-default', __( 'Close', 'bookly' ) ) ?>
96
+ </div>
97
  </div>
98
  </div>
99
+ </div>
100
 
101
+ <div class="breaks bookly-hide-on-off">
102
+ <?php include '_breaks.php' ?>
 
103
  </div>
104
  </div>
105
  </div>
108
  <?php endforeach ?>
109
 
110
  <input type="hidden" name="action" value="bookly_staff_schedule_update">
111
+ <?php Common::csrf() ?>
112
 
113
  <div class="panel-footer">
114
+ <?php Common::submitButton( 'bookly-schedule-save' ) ?>
115
+ <?php Common::customButton( 'bookly-schedule-reset', 'btn-lg btn-default', __( 'Reset', 'bookly' ), array( 'data-default-breaks' => esc_attr( json_encode( $default_breaks ) ), 'data-spinner-color' => 'rgb(62, 66, 74)' ) ) ?>
116
  </div>
117
  </form>
118
  </div>
backend/modules/staff/templates/services.php CHANGED
@@ -1,4 +1,6 @@
1
- <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
 
 
2
  <div>
3
  <?php if ( $categories || $uncategorized_services ) : ?>
4
  <form>
@@ -6,7 +8,7 @@
6
  <div class="panel panel-default bookly-panel-unborder">
7
  <div class="panel-heading">
8
  <div class="row">
9
- <div class="col-md-6">
10
  <div class="checkbox bookly-margin-remove">
11
  <label>
12
  <input id="bookly-check-all-entities" type="checkbox">
@@ -14,18 +16,15 @@
14
  </label>
15
  </div>
16
  </div>
 
 
 
 
 
 
 
17
 
18
- <div class="<?php echo apply_filters( 'bookly_staff_service_label_col_size', 'col-md-3' ) ?> hidden-xs hidden-sm text-right">
19
- <div class="bookly-font-smaller bookly-color-gray">
20
- <?php _e( 'Price', 'bookly' ) ?>
21
- </div>
22
- </div>
23
-
24
- <?php do_action( 'bookly_deposit_payments_staff_service_label' ) ?>
25
-
26
- <div class="<?php echo apply_filters( 'bookly_staff_service_label_col_size', 'col-md-3' ) ?> hidden-xs hidden-sm">
27
- <div class="bookly-font-smaller bookly-color-gray bookly-truncate">
28
- <?php _e( 'Capacity', 'bookly' ) ?>
29
  </div>
30
  </div>
31
  </div>
@@ -33,12 +32,12 @@
33
 
34
  <ul class="bookly-category-services list-group bookly-padding-top-md">
35
  <?php foreach ( $uncategorized_services as $service ) : ?>
36
- <li class="list-group-item">
37
  <div class="row">
38
- <div class="col-md-6">
39
  <div class="checkbox">
40
  <label>
41
- <input class="ab-service-checkbox" <?php checked( array_key_exists( $service['id'], $services_data ) ) ?>
42
  type="checkbox" value="<?php echo $service['id'] ?>"
43
  name="service[<?php echo $service['id'] ?>]"
44
  >
@@ -46,30 +45,44 @@
46
  </label>
47
  </div>
48
  </div>
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- <div class="<?php echo apply_filters( 'bookly_staff_service_input_col_size', 'col-xs-7 col-md-3' ) ?>">
51
- <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm">
52
- <?php _e( 'Price', 'bookly' ) ?>
53
- </div>
54
- <input class="form-control text-right" type="text" <?php disabled( !array_key_exists( $service['id'], $services_data ) ) ?>
55
- name="price[<?php echo $service['id'] ?>]"
56
- value="<?php echo array_key_exists( $service['id'], $services_data ) ? $services_data[ $service['id'] ]['price'] : $service['price'] ?>"
57
- >
58
- </div>
59
-
60
- <?php do_action( 'bookly_staff_render_service', $staff_id, $service['id'], $services_data ) ?>
61
-
62
- <div class="<?php echo apply_filters( 'bookly_staff_service_input_col_size', 'col-xs-5 col-md-3' ) ?>">
63
- <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm">
64
- <?php _e( 'Capacity', 'bookly' ) ?>
 
 
 
 
 
 
 
65
  </div>
66
- <input class="form-control" type="number" min=1 <?php disabled( ! array_key_exists( $service['id'], $services_data ) ) ?>
67
- name="capacity[<?php echo $service['id'] ?>]"
68
- value="<?php echo array_key_exists( $service['id'], $services_data ) ? $services_data[ $service['id'] ]['capacity'] : $service['capacity'] ?>"
69
- >
70
  </div>
71
  </div>
72
- <?php do_action( 'bookly_staff_render_service_tail', $staff_id, $service['id'] ) ?>
73
  </li>
74
  <?php endforeach ?>
75
  </ul>
@@ -79,70 +92,84 @@
79
  <?php if ( ! empty ( $categories ) ) : ?>
80
  <?php foreach ( $categories as $category ) : ?>
81
  <div class="panel panel-default bookly-panel-unborder">
82
- <div class="panel-heading ab-services-category">
83
  <div class="row">
84
- <div class="col-md-6">
85
  <div class="checkbox bookly-margin-remove">
86
  <label>
87
- <input type="checkbox" class="bookly-category-checkbox bookly-category-<?php echo $category->get( 'id' ) ?>"
88
- data-category-id="<?php echo $category->get( 'id' ) ?>">
89
- <b><?php echo esc_html( $category->get( 'name' ) ) ?></b>
90
  </label>
91
  </div>
92
  </div>
 
 
 
 
 
93
 
94
- <div class="<?php echo apply_filters( 'bookly_staff_service_label_col_size', 'col-xs-7 col-md-3')?> hidden-xs hidden-sm text-right">
95
- <div class="bookly-font-smaller bookly-color-gray"><?php _e( 'Price', 'bookly' ) ?></div>
96
- </div>
97
-
98
- <?php do_action( 'bookly_deposit_payments_staff_service_label' ) ?>
99
-
100
- <div class="<?php echo apply_filters( 'bookly_staff_service_label_col_size', 'col-xs-5 col-md-3')?> hidden-xs hidden-sm">
101
- <div class="bookly-font-smaller bookly-color-gray bookly-truncate"><?php _e( 'Capacity', 'bookly' ) ?></div>
102
  </div>
103
  </div>
104
  </div>
105
 
106
  <ul class="bookly-category-services list-group bookly-padding-top-md">
107
  <?php foreach ( $category->getServices() as $service ) : ?>
108
- <li class="list-group-item">
 
109
  <div class="row">
110
- <div class="col-md-6">
111
  <div class="checkbox">
112
  <label>
113
- <input class="ab-service-checkbox bookly-category-<?php echo $category->get( 'id' ) ?>"
114
- data-category-id="<?php echo $category->get( 'id' ) ?>" <?php checked( array_key_exists( $service->get( 'id' ), $services_data ) ) ?>
115
- type="checkbox" value="<?php echo $service->get( 'id' ) ?>"
116
- name="service[<?php echo $service->get( 'id' ) ?>]"
117
  >
118
- <span class="bookly-toggle-label"><?php echo esc_html( $service->get( 'title' ) ) ?></span>
119
  </label>
120
  </div>
121
  </div>
122
-
123
- <div class="<?php echo apply_filters( 'bookly_staff_service_input_col_size', 'col-xs-7 col-sm-3')?>">
124
- <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm">
125
- <?php _e( 'Price', 'bookly' ) ?>
126
- </div>
127
- <input class="form-control text-right" type="text" <?php disabled( ! array_key_exists( $service->get( 'id' ), $services_data ) ) ?>
128
- name="price[<?php echo $service->get( 'id' ) ?>]"
129
- value="<?php echo array_key_exists( $service->get( 'id' ), $services_data ) ? $services_data[ $service->get( 'id' ) ]['price'] : $service->get( 'price' ) ?>"
130
- >
131
- </div>
132
-
133
- <?php do_action( 'bookly_staff_render_service', $staff_id, $service->get( 'id' ), $services_data ) ?>
134
-
135
- <div class="<?php echo apply_filters( 'bookly_staff_service_input_col_size', 'col-xs-5 col-sm-2')?>">
136
- <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm">
137
- <?php _e( 'Capacity', 'bookly' ) ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  </div>
139
- <input class="form-control" type="number" min="1" <?php disabled( ! array_key_exists( $service->get( 'id' ), $services_data ) ) ?>
140
- name="capacity[<?php echo $service->get( 'id' ) ?>]"
141
- value="<?php echo array_key_exists( $service->get( 'id' ), $services_data ) ? $services_data[ $service->get( 'id' ) ]['capacity'] : $service->get( 'capacity' ) ?>"
142
- >
143
  </div>
144
  </div>
145
- <?php do_action( 'bookly_staff_render_service_tail', $staff_id, $service->get( 'id' ) ) ?>
146
  </li>
147
  <?php endforeach ?>
148
  </ul>
@@ -152,8 +179,10 @@
152
 
153
  <input type="hidden" name="action" value="bookly_staff_services_update">
154
  <input type="hidden" name="staff_id" value="<?php echo $staff_id ?>">
 
155
 
156
  <div class="panel-footer">
 
157
  <?php \BooklyLite\Lib\Utils\Common::submitButton( 'bookly-services-save' ) ?>
158
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-services-reset' ) ?>
159
  </div>
@@ -167,4 +196,7 @@
167
  </a>
168
  </p>
169
  <?php endif ?>
170
- </div>
 
 
 
1
+ <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
+ /** @var \BooklyLite\Lib\Entities\Category[] $categories */
3
+ ?>
4
  <div>
5
  <?php if ( $categories || $uncategorized_services ) : ?>
6
  <form>
8
  <div class="panel panel-default bookly-panel-unborder">
9
  <div class="panel-heading">
10
  <div class="row">
11
+ <div class="col-lg-6">
12
  <div class="checkbox bookly-margin-remove">
13
  <label>
14
  <input id="bookly-check-all-entities" type="checkbox">
16
  </label>
17
  </div>
18
  </div>
19
+ <div class="col-lg-6">
20
+ <div class="row">
21
+ <div class="<?php echo \BooklyLite\Lib\Proxy\Shared::prepareStaffServiceLabelClass( 'col-lg-12' ) ?> hidden-xs hidden-sm hidden-md text-right">
22
+ <div class="bookly-font-smaller bookly-color-gray">
23
+ <?php _e( 'Price', 'bookly' ) ?>
24
+ </div>
25
+ </div>
26
 
27
+ <?php \BooklyLite\Lib\Proxy\DepositPayments::renderStaffServiceLabel() ?>
 
 
 
 
 
 
 
 
 
 
28
  </div>
29
  </div>
30
  </div>
32
 
33
  <ul class="bookly-category-services list-group bookly-padding-top-md">
34
  <?php foreach ( $uncategorized_services as $service ) : ?>
35
+ <li class="list-group-item" data-service-id="<?php echo $service['id'] ?>" data-service-type="<?php echo $service['type'] ?>" data-sub-service="<?php echo $service['sub_service_id'] ?>">
36
  <div class="row">
37
+ <div class="col-lg-6">
38
  <div class="checkbox">
39
  <label>
40
+ <input class="bookly-service-checkbox" <?php checked( array_key_exists( $service['id'], $services_data ) ) ?>
41
  type="checkbox" value="<?php echo $service['id'] ?>"
42
  name="service[<?php echo $service['id'] ?>]"
43
  >
45
  </label>
46
  </div>
47
  </div>
48
+ <div class="col-lg-6">
49
+ <div class="row">
50
+ <div class="<?php echo \BooklyLite\Lib\Proxy\Shared::prepareStaffServiceInputClass( 'col-xs-12' ) ?>">
51
+ <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm visible-md">
52
+ <?php _e( 'Price', 'bookly' ) ?>
53
+ </div>
54
+ <input class="form-control text-right" type="text" <?php disabled( !array_key_exists( $service['id'], $services_data ) ) ?>
55
+ name="price[<?php echo $service['id'] ?>]"
56
+ value="<?php echo array_key_exists( $service['id'], $services_data ) ? $services_data[ $service['id'] ]['price'] : $service['price'] ?>"
57
+ >
58
+ </div>
59
 
60
+ <?php \BooklyLite\Lib\Proxy\Shared::renderStaffService( $staff_id, $service['id'], $services_data, $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ? array( 'read-only' => array( 'deposit' => true ) ) : array() ) ?>
61
+
62
+ <div style="display: none">
63
+ <div class="form-group bookly-js-capacity-form-group">
64
+ <div class="row">
65
+ <div class="col-xs-6">
66
+ <input class="form-control bookly-js-capacity bookly-js-capacity-min" type="number" min=1 <?php disabled( ! array_key_exists( $service['id'], $services_data ) ) ?>
67
+ name="capacity_min[<?php echo $service['id'] ?>]"
68
+ value="1"
69
+ <?php if ( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ) : ?>readonly<?php endif ?>
70
+ >
71
+ </div>
72
+ <div class="col-xs-6">
73
+ <input class="form-control bookly-js-capacity bookly-js-capacity-max" type="number" min=1 <?php disabled( ! array_key_exists( $service['id'], $services_data ) ) ?>
74
+ name="capacity_max[<?php echo $service['id'] ?>]"
75
+ value="1"
76
+ <?php if ( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ) : ?>readonly<?php endif ?>
77
+ >
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </div>
82
  </div>
 
 
 
 
83
  </div>
84
  </div>
85
+ <?php if ( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ) { \BooklyLite\Lib\Proxy\Shared::renderStaffServiceTail( $staff_id, $service[ 'id' ] ); } ?>
86
  </li>
87
  <?php endforeach ?>
88
  </ul>
92
  <?php if ( ! empty ( $categories ) ) : ?>
93
  <?php foreach ( $categories as $category ) : ?>
94
  <div class="panel panel-default bookly-panel-unborder">
95
+ <div class="panel-heading bookly-services-category">
96
  <div class="row">
97
+ <div class="col-lg-6">
98
  <div class="checkbox bookly-margin-remove">
99
  <label>
100
+ <input type="checkbox" class="bookly-category-checkbox bookly-category-<?php echo $category->getId() ?>"
101
+ data-category-id="<?php echo $category->getId() ?>">
102
+ <b><?php echo esc_html( $category->getName() ) ?></b>
103
  </label>
104
  </div>
105
  </div>
106
+ <div class="col-lg-6">
107
+ <div class="row">
108
+ <div class="<?php echo \BooklyLite\Lib\Proxy\Shared::prepareStaffServiceLabelClass( 'col-lg-12' )?> hidden-xs hidden-sm hidden-md text-right">
109
+ <div class="bookly-font-smaller bookly-color-gray"><?php _e( 'Price', 'bookly' ) ?></div>
110
+ </div>
111
 
112
+ <?php \BooklyLite\Lib\Proxy\DepositPayments::renderStaffServiceLabel() ?>
113
+ </div>
 
 
 
 
 
 
114
  </div>
115
  </div>
116
  </div>
117
 
118
  <ul class="bookly-category-services list-group bookly-padding-top-md">
119
  <?php foreach ( $category->getServices() as $service ) : ?>
120
+ <?php $sub_service = current( $service->getSubServices() ) ?>
121
+ <li class="list-group-item" data-service-id="<?php echo $service->getId() ?>" data-service-type="<?php echo $service->getType() ?>" data-sub-service="<?php echo empty( $sub_service ) ? null : $sub_service->getId(); ?>">
122
  <div class="row">
123
+ <div class="col-lg-6">
124
  <div class="checkbox">
125
  <label>
126
+ <input class="bookly-service-checkbox bookly-category-<?php echo $category->getId() ?>"
127
+ data-category-id="<?php echo $category->getId() ?>" <?php checked( array_key_exists( $service->getId(), $services_data ) ) ?>
128
+ type="checkbox" value="<?php echo $service->getId() ?>"
129
+ name="service[<?php echo $service->getId() ?>]"
130
  >
131
+ <span class="bookly-toggle-label"><?php echo esc_html( $service->getTitle() ) ?></span>
132
  </label>
133
  </div>
134
  </div>
135
+ <div class="col-lg-6">
136
+ <div class="row">
137
+ <div class="<?php echo \BooklyLite\Lib\Proxy\Shared::prepareStaffServiceInputClass( 'col-xs-12' ) ?>">
138
+ <div class="bookly-font-smaller bookly-margin-bottom-xs bookly-color-gray visible-xs visible-sm visible-md">
139
+ <?php _e( 'Price', 'bookly' ) ?>
140
+ </div>
141
+ <input class="form-control text-right" type="text" <?php disabled( ! array_key_exists( $service->getId(), $services_data ) ) ?>
142
+ name="price[<?php echo $service->getId() ?>]"
143
+ value="<?php echo array_key_exists( $service->getId(), $services_data ) ? $services_data[ $service->getId() ]['price'] : $service->getPrice() ?>"
144
+ >
145
+ </div>
146
+
147
+ <?php \BooklyLite\Lib\Proxy\Shared::renderStaffService( $staff_id, $service->getId(), $services_data, $service->getType() == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ? array( 'read-only' => array( 'deposit' => true ) ) : array() ) ?>
148
+
149
+ <div style="display: none">
150
+ <div class="form-group bookly-js-capacity-form-group">
151
+ <div class="row">
152
+ <div class="col-xs-6">
153
+ <input class="form-control bookly-js-capacity bookly-js-capacity-min" type="number" min="1" <?php disabled( ! array_key_exists( $service->getId(), $services_data ) ) ?>
154
+ name="capacity_min[<?php echo $service->getId() ?>]"
155
+ value="1"
156
+ <?php if ( $service->getType() == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ) : ?>readonly<?php endif ?>
157
+ >
158
+ </div>
159
+ <div class="col-xs-6">
160
+ <input class="form-control bookly-js-capacity bookly-js-capacity-max" type="number" min="1" <?php disabled( ! array_key_exists( $service->getId(), $services_data ) ) ?>
161
+ name="capacity_max[<?php echo $service->getId() ?>]"
162
+ value="1"
163
+ <?php if ( $service->getType() == \BooklyLite\Lib\Entities\Service::TYPE_PACKAGE ) : ?>readonly<?php endif ?>
164
+ >
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
  </div>
 
 
 
 
170
  </div>
171
  </div>
172
+ <?php if ( $service->getType() == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ) { \BooklyLite\Lib\Proxy\Shared::renderStaffServiceTail( $staff_id, $service->getId() ); } ?>
173
  </li>
174
  <?php endforeach ?>
175
  </ul>
179
 
180
  <input type="hidden" name="action" value="bookly_staff_services_update">
181
  <input type="hidden" name="staff_id" value="<?php echo $staff_id ?>">
182
+ <?php \BooklyLite\Lib\Utils\Common::csrf() ?>
183
 
184
  <div class="panel-footer">
185
+ <span class="bookly-js-services-error text-danger"></span>
186
  <?php \BooklyLite\Lib\Utils\Common::submitButton( 'bookly-services-save' ) ?>
187
  <?php \BooklyLite\Lib\Utils\Common::resetButton( 'bookly-services-reset' ) ?>
188
  </div>
196
  </a>
197
  </p>
198
  <?php endif ?>
199
+ <div style="display: none">
200
+ <?php BooklyLite\Lib\Proxy\Shared::renderStaffServices( $staff_id ) ?>
201
+ </div>
202
+ </div>
backend/modules/support/Components.php CHANGED
@@ -10,6 +10,8 @@ use BooklyLite\Backend\Modules;
10
  */
11
  class Components extends Lib\Base\Components
12
  {
 
 
13
  /**
14
  * Render support buttons.
15
  */
@@ -20,7 +22,7 @@ class Components extends Lib\Base\Components
20
  ) );
21
 
22
  $this->enqueueScripts( array(
23
- 'backend' => array( 'js/alert.js' => array( 'jquery' ), ),
24
  'frontend' => array(
25
  'js/spin.min.js' => array( 'jquery' ),
26
  'js/ladda.min.js' => array( 'jquery' ),
@@ -44,7 +46,13 @@ class Components extends Lib\Base\Components
44
 
45
  $current_user = wp_get_current_user();
46
 
47
- $this->render( '_buttons', compact( 'doc_link', 'show_contact_us_notice', 'show_feedback_notice', 'current_user' ) );
 
 
 
 
 
 
48
  }
49
 
50
  /**
@@ -56,9 +64,7 @@ class Components extends Lib\Base\Components
56
  ! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_subscribe_notice', true ) ) {
57
 
58
  // Show notice 1 day after installation time.
59
- $days_in_use = (int) ( ( time() - Lib\Plugin::getInstallationTime() ) / DAY_IN_SECONDS );
60
-
61
- if ( $days_in_use >= 1 ) {
62
  $this->enqueueStyles( array(
63
  'frontend' => array( 'css/ladda.min.css', ),
64
  ) );
@@ -74,4 +80,39 @@ class Components extends Lib\Base\Components
74
  }
75
  }
76
  }
 
 
 
 
 
 
 
 
 
10
  */
11
  class Components extends Lib\Base\Components
12
  {
13
+ const BOOKLY_WORDPRESS_URL = 'https://wordpress.org/support/plugin/bookly-responsive-appointment-booking-tool/reviews/';
14
+
15
  /**
16
  * Render support buttons.
17
  */
22
  ) );
23
 
24
  $this->enqueueScripts( array(
25
+ 'backend' => array( 'js/alert.js' => array( 'jquery' ), ),
26
  'frontend' => array(
27
  'js/spin.min.js' => array( 'jquery' ),
28
  'js/ladda.min.js' => array( 'jquery' ),
46
 
47
  $current_user = wp_get_current_user();
48
 
49
+ wp_localize_script( 'bookly-support.js', 'SupportL10n', array(
50
+ 'csrf_token' => \BooklyLite\Lib\Utils\Common::getCsrfToken()
51
+ ) );
52
+ $messages = Lib\Entities\Message::query( 'm' )->select( 'm.created, m.subject, m.seen' )->sortBy( 'm.seen, m.message_id' )->order( 'DESC' )->limit( 10 )->fetchArray();
53
+ $messages_new = Lib\Entities\Message::query( 'm' )->where( 'm.seen', '0' )->count();
54
+ $messages_link = Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Message\Controller::page_slug );
55
+ $this->render( '_buttons', compact( 'doc_link', 'show_contact_us_notice', 'show_feedback_notice', 'current_user', 'messages', 'messages_new', 'messages_link' ) );
56
  }
57
 
58
  /**
64
  ! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_subscribe_notice', true ) ) {
65
 
66
  // Show notice 1 day after installation time.
67
+ if ( time() - Lib\Plugin::getInstallationTime() >= DAY_IN_SECONDS ) {
 
 
68
  $this->enqueueStyles( array(
69
  'frontend' => array( 'css/ladda.min.css', ),
70
  ) );
80
  }
81
  }
82
  }
83
+
84
+ /**
85
+ * Render Net Promoter Score notice.
86
+ */
87
+ public function renderNpsNotice()
88
+ {
89
+ if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
90
+ $dismiss_value = get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_nps_notice', true );
91
+ //