Version Description
Download this release
Release Info
Developer | Ladela |
Plugin | WordPress Online Booking and Scheduling Plugin – Bookly |
Version | 16.0 |
Comparing to | |
See all releases |
Code changes from version 14.5.1 to 16.0
- autoload.php +4 -4
- backend/Backend.php +86 -94
- backend/components/appearance/Codes.php +41 -0
- backend/components/appearance/Editable.php +109 -0
- backend/components/appearance/proxy/Pro.php +16 -0
- backend/components/appearance/proxy/Shared.php +15 -0
- backend/components/controls/Buttons.php +127 -0
- backend/components/controls/Inputs.php +22 -0
- backend/components/dialogs/appointment/attach_payment/proxy/Pro.php +15 -0
- backend/components/dialogs/appointment/attach_payment/proxy/Taxes.php +15 -0
- backend/components/dialogs/appointment/customer_details/Dialog.php +19 -0
- backend/components/dialogs/appointment/customer_details/proxy/Files.php +15 -0
- backend/components/dialogs/appointment/customer_details/proxy/Pro.php +15 -0
- backend/components/dialogs/appointment/customer_details/proxy/Shared.php +15 -0
- backend/{modules/calendar/templates/_customer_details_dialog.php → components/dialogs/appointment/customer_details/templates/customer_details.php} +18 -12
- backend/components/dialogs/appointment/delete/Ajax.php +56 -0
- backend/components/dialogs/appointment/delete/Dialog.php +33 -0
- backend/{modules/calendar → components/dialogs/appointment/delete}/resources/js/delete_dialog.js +0 -0
- backend/{modules/calendar/templates/_delete_dialog.php → components/dialogs/appointment/delete/templates/delete.php} +4 -8
- backend/components/dialogs/appointment/edit/Ajax.php +808 -0
- backend/{modules/calendar/Components.php → components/dialogs/appointment/edit/Dialog.php} +14 -35
- backend/components/dialogs/appointment/edit/proxy/Pro.php +17 -0
- backend/components/dialogs/appointment/edit/proxy/RecurringAppointments.php +17 -0
- backend/components/dialogs/appointment/edit/proxy/Shared.php +19 -0
- backend/components/dialogs/appointment/edit/proxy/Tasks.php +15 -0
- backend/{modules/calendar/resources/js/ng-appointment_dialog.js → components/dialogs/appointment/edit/resources/js/ng-appointment.js} +289 -109
- backend/{modules/calendar/templates/_appointment_dialog.php → components/dialogs/appointment/edit/templates/edit.php} +88 -74
- backend/components/dialogs/common/CascadeDelete.php +30 -0
- backend/components/dialogs/common/UnsavedChanges.php +30 -0
- backend/components/dialogs/common/templates/delete_cascade.php +21 -0
- backend/components/dialogs/common/templates/unsaved_changes.php +21 -0
- backend/{modules/customers/Components.php → components/dialogs/customer/Edit.php} +20 -18
- backend/components/dialogs/customer/EditAjax.php +103 -0
- backend/{modules/customers → components/dialogs/customer}/forms/Customer.php +12 -4
- backend/components/dialogs/customer/proxy/CustomerGroups.php +16 -0
- backend/components/dialogs/customer/proxy/CustomerInformation.php +16 -0
- backend/components/dialogs/customer/proxy/Pro.php +16 -0
- backend/{modules/customers/resources/js/ng-customer_dialog.js → components/dialogs/customer/resources/js/ng-customer.js} +89 -19
- backend/{modules/customers/templates/_customer_dialog.php → components/dialogs/customer/templates/edit.php} +28 -26
- backend/components/dialogs/payment/Ajax.php +105 -0
- backend/components/dialogs/payment/Dialog.php +32 -0
- backend/components/dialogs/payment/resources/js/ng-payment_details.js +151 -0
- backend/components/dialogs/payment/templates/details.php +217 -0
- backend/{modules/payments/templates/_payment_details_dialog.php → components/dialogs/payment/templates/dialog.php} +4 -2
- backend/components/dialogs/special_price/proxy/SpecialHours.php +14 -0
- backend/components/notices/CollectStats.php +47 -0
- backend/components/notices/CollectStatsAjax.php +41 -0
- backend/components/notices/Limitation.php +19 -0
- backend/components/notices/LiteRebranding.php +27 -0
- backend/components/notices/LiteRebrandingAjax.php +21 -0
- backend/components/notices/Nps.php +48 -0
- backend/components/notices/NpsAjax.php +40 -0
- backend/components/notices/Subscribe.php +38 -0
- backend/components/notices/SubscribeAjax.php +36 -0
- backend/components/notices/proxy/Pro.php +15 -0
- backend/{modules/support → components/notices}/resources/css/bootstrap-stars.css +0 -0
- backend/components/notices/resources/js/collect-stats.js +9 -0
- backend/{modules/support → components/notices}/resources/js/jquery.barrating.min.js +0 -0
- backend/components/notices/resources/js/lite-rebranding.js +6 -0
- backend/{modules/support → components/notices}/resources/js/nps.js +0 -0
- backend/{modules/support → components/notices}/resources/js/subscribe.js +0 -0
- backend/components/notices/templates/collect_stats.php +23 -0
- backend/components/notices/templates/limitation.php +3 -0
- backend/components/notices/templates/lite_rebranding.php +12 -0
- backend/{modules/support/templates/_nps_notice.php → components/notices/templates/nps.php} +8 -4
- backend/{modules/support/templates/_subscribe_notice.php → components/notices/templates/subscribe.php} +1 -1
- backend/components/settings/Image.php +30 -0
- backend/components/settings/Inputs.php +76 -0
- backend/components/settings/Menu.php +23 -0
- backend/components/settings/Payments.php +41 -0
- backend/components/settings/Selects.php +80 -0
- backend/components/settings/proxy/Pro.php +15 -0
- backend/components/settings/proxy/Taxes.php +15 -0
- backend/components/settings/templates/image.php +48 -0
- backend/components/settings/templates/price_correction.php +18 -0
- backend/components/support/Buttons.php +74 -0
- backend/components/support/ButtonsAjax.php +78 -0
- backend/components/support/lib/Urls.php +11 -0
- backend/{modules → components}/support/resources/js/support.js +0 -0
- backend/{modules → components}/support/templates/_email_to_support.php +7 -3
- backend/{modules/support/templates/_buttons.php → components/support/templates/buttons.php} +8 -5
- backend/components/tiny_mce/Tools.php +58 -0
- backend/components/tiny_mce/proxy/DepositPayments.php +15 -0
- backend/components/tiny_mce/proxy/GroupBooking.php +15 -0
- backend/components/tiny_mce/proxy/Shared.php +18 -0
- backend/components/tiny_mce/proxy/SpecialDays.php +15 -0
- backend/components/tiny_mce/proxy/SpecialHours.php +15 -0
- backend/{modules → components}/tiny_mce/resources/images/calendar.png +0 -0
- backend/{modules/tiny_mce/templates/popup.php → components/tiny_mce/templates/bookly_form.php} +55 -26
- backend/modules/appearance/Ajax.php +132 -0
- backend/modules/appearance/Components.php +0 -21
- backend/modules/appearance/Controller.php +0 -218
- backend/modules/appearance/Page.php +103 -0
- backend/modules/appearance/lib/Helper.php +0 -69
- backend/modules/appearance/proxy/Cart.php +17 -0
- backend/modules/appearance/proxy/ChainAppointments.php +14 -0
- backend/modules/appearance/proxy/Coupons.php +16 -0
- backend/modules/appearance/proxy/CustomDuration.php +15 -0
- backend/modules/appearance/proxy/CustomFields.php +15 -0
- backend/modules/appearance/proxy/CustomerInformation.php +15 -0
- backend/modules/appearance/proxy/DepositPayments.php +15 -0
- backend/modules/appearance/proxy/Files.php +16 -0
- backend/modules/appearance/proxy/GoogleMapsAddress.php +15 -0
- backend/modules/appearance/proxy/GroupBooking.php +16 -0
- backend/modules/appearance/proxy/Locations.php +16 -0
- backend/modules/appearance/proxy/MultiplyAppointments.php +16 -0
- backend/modules/appearance/proxy/Pro.php +23 -0
- backend/modules/appearance/proxy/RecurringAppointments.php +18 -0
- backend/modules/appearance/proxy/ServiceExtras.php +18 -0
- backend/modules/appearance/proxy/Shared.php +19 -0
- backend/modules/appearance/proxy/WaitingList.php +15 -0
- backend/modules/appearance/resources/js/appearance.js +319 -10
- backend/modules/appearance/resources/js/bootstrap-editable.bookly.js +50 -37
- backend/modules/appearance/templates/_1_service.php +23 -46
- backend/modules/appearance/templates/_3_time.php +173 -140
- backend/modules/appearance/templates/_5_cart.php +0 -144
- backend/modules/appearance/templates/_6_details.php +31 -16
- backend/modules/appearance/templates/_7_payment.php +17 -27
- backend/modules/appearance/templates/_8_complete.php +5 -5
- backend/modules/appearance/templates/_card_payment.php +4 -4
- backend/modules/appearance/templates/_codes.php +0 -20
- backend/modules/appearance/templates/_custom_css.php +5 -10
- backend/modules/appearance/templates/_progress_tracker.php +21 -17
- backend/modules/appearance/templates/index.php +64 -53
- backend/modules/appointments/Ajax.php +195 -0
- backend/modules/appointments/Controller.php +0 -253
- backend/modules/appointments/Page.php +98 -0
- backend/modules/appointments/proxy/Pro.php +18 -0
- backend/modules/appointments/proxy/Ratings.php +16 -0
- backend/modules/appointments/proxy/Shared.php +15 -0
- backend/modules/appointments/proxy/Tasks.php +15 -0
- backend/modules/appointments/resources/js/appointments.js +130 -36
- backend/modules/appointments/templates/_export_dialog.php +0 -46
- backend/modules/appointments/templates/index.php +27 -16
- backend/modules/calendar/Ajax.php +185 -0
- backend/modules/calendar/Controller.php +0 -932
- backend/modules/calendar/Page.php +229 -0
- backend/modules/calendar/proxy/AdvancedGoogleCalendar.php +15 -0
- backend/modules/calendar/proxy/Locations.php +15 -0
- backend/modules/calendar/proxy/Shared.php +17 -0
- backend/modules/calendar/resources/js/calendar-common.js +52 -14
- backend/modules/calendar/resources/js/calendar.js +135 -17
- backend/modules/calendar/templates/calendar.php +34 -22
- backend/modules/customers/Ajax.php +243 -0
- backend/modules/customers/Controller.php +0 -249
- backend/modules/customers/Page.php +63 -0
- backend/modules/customers/proxy/CustomerGroups.php +20 -0
- backend/modules/customers/proxy/CustomerInformation.php +15 -0
- backend/modules/customers/proxy/Pro.php +20 -0
- backend/modules/customers/proxy/Shared.php +15 -0
- backend/modules/customers/resources/js/customers.js +239 -88
- backend/modules/customers/templates/_import.php +0 -44
- backend/modules/customers/templates/index.php +76 -30
- backend/modules/debug/Ajax.php +151 -0
- backend/modules/debug/Controller.php +0 -274
- backend/modules/debug/Page.php +152 -0
- backend/modules/debug/templates/index.php +5 -3
- backend/modules/message/Controller.php +0 -100
- backend/modules/messages/Ajax.php +63 -0
- backend/modules/messages/Page.php +73 -0
- backend/modules/{message → messages}/resources/js/message.js +0 -0
- backend/modules/{message → messages}/templates/index.php +4 -2
- backend/modules/notifications/Ajax.php +102 -0
- backend/modules/notifications/Components.php +0 -357
- backend/modules/notifications/Controller.php +0 -133
- backend/modules/notifications/Page.php +75 -0
- backend/modules/notifications/forms/Notifications.php +47 -30
- backend/modules/notifications/lib/Codes.php +296 -0
- backend/modules/notifications/proxy/Invoices.php +14 -0
- backend/modules/notifications/proxy/Pro.php +15 -0
- backend/modules/notifications/proxy/Shared.php +19 -0
- backend/modules/notifications/resources/js/ng-app.js +2 -2
- backend/modules/notifications/resources/js/notification.js +142 -13
- backend/modules/notifications/templates/_custom_notification.php +0 -1
- backend/modules/notifications/templates/_test_email_notifications_modal.php +9 -6
- backend/modules/notifications/templates/index.php +41 -52
- backend/modules/payments/Ajax.php +117 -0
- backend/modules/payments/Components.php +0 -34
- backend/modules/payments/Controller.php +0 -257
- backend/modules/payments/Page.php +96 -0
- backend/modules/payments/proxy/Invoices.php +15 -0
- backend/modules/payments/proxy/Pro.php +15 -0
- backend/modules/payments/proxy/Shared.php +15 -0
- backend/modules/payments/resources/js/ng-payment_details_dialog.js +0 -69
- backend/modules/payments/resources/js/payments.js +68 -33
- backend/modules/payments/templates/details.php +0 -137
- backend/modules/payments/templates/index.php +39 -10
- backend/modules/services/Ajax.php +204 -0
- backend/modules/services/Controller.php +0 -332
- backend/modules/services/Page.php +133 -0
- backend/modules/services/forms/Category.php +3 -3
- backend/modules/services/forms/Service.php +24 -41
- backend/modules/services/proxy/CompoundServices.php +15 -0
- backend/modules/services/proxy/CustomDuration.php +18 -0
- backend/modules/services/proxy/CustomerGroups.php +16 -0
- backend/modules/services/proxy/GroupBooking.php +15 -0
- backend/modules/services/proxy/Packages.php +15 -0
- backend/modules/services/proxy/Pro.php +19 -0
- backend/modules/services/proxy/Shared.php +24 -0
- backend/modules/services/proxy/Taxes.php +15 -0
- backend/modules/services/resources/js/service.js +95 -46
- backend/modules/services/templates/_list.php +54 -115
- backend/modules/services/templates/index.php +11 -5
- backend/modules/settings/Ajax.php +60 -0
- backend/modules/settings/Components.php +0 -30
- backend/modules/settings/Controller.php +0 -299
- backend/modules/settings/Page.php +194 -0
- backend/modules/settings/forms/BusinessHours.php +3 -3
- backend/modules/settings/forms/Payments.php +0 -37
- backend/modules/settings/proxy/AdvancedGoogleCalendar.php +16 -0
- backend/modules/settings/proxy/DepositPayments.php +15 -0
- backend/modules/settings/proxy/PaypalPaymentsStandard.php +16 -0
- backend/modules/settings/proxy/Pro.php +27 -0
- backend/modules/settings/proxy/ProSettings.php +46 -0
- backend/modules/settings/proxy/Shared.php +21 -0
- backend/modules/settings/proxy/Taxes.php +15 -0
- backend/modules/settings/resources/js/collect-stats-notice.js +0 -13
- backend/modules/settings/resources/js/settings.js +177 -134
- backend/modules/settings/templates/_calendarForm.php +23 -10
- backend/modules/settings/templates/_calendar_codes.php +4 -2
- backend/modules/settings/templates/_cartForm.php +0 -33
- backend/modules/settings/templates/_collect_stats_notice.php +0 -24
- backend/modules/settings/templates/_companyForm.php +11 -7
- backend/modules/settings/templates/_customers.php +21 -14
- backend/modules/settings/templates/_facebookForm.php +21 -0
- backend/modules/settings/templates/_generalForm.php +19 -28
- backend/modules/settings/templates/_googleCalendarForm.php +0 -36
- backend/modules/settings/templates/_hoursForm.php +9 -6
- backend/modules/settings/templates/_payment_local.php +16 -0
- backend/modules/settings/templates/_paymentsForm.php +16 -46
- backend/modules/settings/templates/_urlForm.php +16 -27
- backend/modules/settings/templates/_woocommerce.php +0 -45
- backend/modules/settings/templates/_woocommerce_codes.php +0 -13
- backend/modules/settings/templates/index.php +13 -22
- backend/modules/shop/Ajax.php +90 -0
- backend/modules/shop/Page.php +70 -0
- backend/modules/shop/resources/css/shop.css +15 -0
- backend/modules/shop/resources/js/shop.js +54 -0
- backend/modules/shop/templates/index.php +69 -0
- backend/modules/sms/Ajax.php +219 -0
- backend/modules/sms/Components.php +0 -348
- backend/modules/sms/Controller.php +0 -23
autoload.php
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
|
3 |
/**
|
4 |
-
* Bookly
|
5 |
* @param $class
|
6 |
*/
|
7 |
-
function
|
8 |
{
|
9 |
-
if ( preg_match( '/^
|
10 |
$file = __DIR__ . DIRECTORY_SEPARATOR
|
11 |
. strtolower( str_replace( '\\', DIRECTORY_SEPARATOR, preg_replace( '/([a-z])([A-Z])/', '$1_$2', $match[1] ) ) )
|
12 |
. $match[2]
|
@@ -16,4 +16,4 @@ function bookly_lite_loader( $class )
|
|
16 |
}
|
17 |
}
|
18 |
}
|
19 |
-
spl_autoload_register( '
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
|
3 |
/**
|
4 |
+
* Bookly autoload.
|
5 |
* @param $class
|
6 |
*/
|
7 |
+
function bookly_loader( $class )
|
8 |
{
|
9 |
+
if ( preg_match( '/^Bookly\\\\(.+)?([^\\\\]+)$/U', ltrim( $class, '\\' ), $match ) ) {
|
10 |
$file = __DIR__ . DIRECTORY_SEPARATOR
|
11 |
. strtolower( str_replace( '\\', DIRECTORY_SEPARATOR, preg_replace( '/([a-z])([A-Z])/', '$1_$2', $match[1] ) ) )
|
12 |
. $match[2]
|
16 |
}
|
17 |
}
|
18 |
}
|
19 |
+
spl_autoload_register( 'bookly_loader', true, true );
|
backend/Backend.php
CHANGED
@@ -1,126 +1,118 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
-
use BooklyLite\Lib;
|
6 |
|
7 |
/**
|
8 |
* Class Backend
|
9 |
-
* @package
|
10 |
*/
|
11 |
-
class Backend
|
12 |
{
|
13 |
/**
|
14 |
-
*
|
15 |
*/
|
16 |
-
public function
|
17 |
{
|
18 |
-
|
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();
|
26 |
-
$this->serviceController = Modules\Services\Controller::getInstance();
|
27 |
-
$this->settingsController = Modules\Settings\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 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
@session_start();
|
49 |
-
}
|
50 |
-
}
|
51 |
-
|
52 |
-
public function addTinyMCEPlugin()
|
53 |
-
{
|
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 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
$calendar = __( 'Calendar', 'bookly' );
|
72 |
-
$appointments = __( 'Appointments', 'bookly' );
|
73 |
-
$staff_members = __( 'Staff Members', 'bookly' );
|
74 |
-
$services = __( 'Services', 'bookly' );
|
75 |
-
$sms = __( 'SMS Notifications', 'bookly' );
|
76 |
-
$notifications = __( 'Email Notifications', 'bookly' );
|
77 |
-
$customers = __( 'Customers', 'bookly' );
|
78 |
-
$payments = __( 'Payments', 'bookly' );
|
79 |
-
$appearance = __( 'Appearance', 'bookly' );
|
80 |
-
$settings = __( 'Settings', 'bookly' );
|
81 |
-
$messages = __( 'Messages', 'bookly' );
|
82 |
|
83 |
-
|
84 |
-
|
85 |
-
|
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' ) );
|
92 |
} else {
|
93 |
-
|
94 |
-
|
95 |
-
Modules\Staff\Controller::page_slug, array( $this->staffController, 'index' ) );
|
96 |
-
}
|
97 |
}
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
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 |
-
|
118 |
-
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
}
|
121 |
|
122 |
unset ( $submenu['bookly-menu'][0] );
|
123 |
}
|
124 |
}
|
125 |
-
|
126 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend;
|
3 |
|
4 |
+
use Bookly\Lib;
|
|
|
5 |
|
6 |
/**
|
7 |
* Class Backend
|
8 |
+
* @package Bookly\Backend
|
9 |
*/
|
10 |
+
abstract class Backend
|
11 |
{
|
12 |
/**
|
13 |
+
* Register hooks.
|
14 |
*/
|
15 |
+
public static function registerHooks()
|
16 |
{
|
17 |
+
add_action( 'admin_menu', array( __CLASS__, 'addAdminMenu' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
+
add_action( 'admin_notices', function () {
|
20 |
+
$bookly_page = isset ( $_REQUEST['page'] ) && strpos( $_REQUEST['page'], 'bookly-' ) === 0;
|
21 |
+
if ( $bookly_page ) {
|
22 |
+
// Subscribe notice.
|
23 |
+
Components\Notices\Subscribe::render();
|
24 |
+
// Subscribe notice.
|
25 |
+
Components\Notices\LiteRebranding::render();
|
26 |
+
// NPS notice.
|
27 |
+
Components\Notices\Nps::render();
|
28 |
+
// Collect stats notice.
|
29 |
+
Components\Notices\CollectStats::render();
|
30 |
+
}
|
31 |
+
// Let add-ons render admin notices.
|
32 |
+
Lib\Proxy\Shared::renderAdminNotices( $bookly_page );
|
33 |
+
}, 10, 0 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
}
|
35 |
|
36 |
/**
|
37 |
* Admin menu.
|
38 |
*/
|
39 |
+
public static function addAdminMenu()
|
40 |
{
|
41 |
/** @var \WP_User $current_user */
|
42 |
global $current_user, $submenu;
|
43 |
|
44 |
if ( $current_user->has_cap( 'administrator' ) || Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
|
45 |
$dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
|
46 |
+
$badge_number = Modules\Messages\Page::getMessagesCount() +
|
47 |
+
Modules\Shop\Page::getNotSeenCount() +
|
48 |
+
Lib\SMS::getUndeliveredSmsCount()
|
49 |
+
;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
|
51 |
+
if ( $badge_number ) {
|
52 |
+
add_menu_page( 'Bookly', sprintf( 'Bookly <span class="update-plugins count-%d"><span class="update-count">%d</span></span>', $badge_number, $badge_number ), 'read', 'bookly-menu', '',
|
53 |
+
plugins_url( 'resources/images/menu.png', __FILE__ ), $dynamic_position );
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
} else {
|
55 |
+
add_menu_page( 'Bookly', 'Bookly', 'read', 'bookly-menu', '',
|
56 |
+
plugins_url( 'resources/images/menu.png', __FILE__ ), $dynamic_position );
|
|
|
|
|
57 |
}
|
58 |
+
if ( Lib\Proxy\Pro::graceExpired() ) {
|
59 |
+
Lib\Proxy\Pro::addLicenseBooklyMenuItem();
|
60 |
+
} else {
|
61 |
+
// Translated submenu pages.
|
62 |
+
$calendar = __( 'Calendar', 'bookly' );
|
63 |
+
$appointments = __( 'Appointments', 'bookly' );
|
64 |
+
$staff_members = __( 'Staff Members', 'bookly' );
|
65 |
+
$services = __( 'Services', 'bookly' );
|
66 |
+
$notifications = __( 'Email Notifications', 'bookly' );
|
67 |
+
$customers = __( 'Customers', 'bookly' );
|
68 |
+
$payments = __( 'Payments', 'bookly' );
|
69 |
+
$appearance = __( 'Appearance', 'bookly' );
|
70 |
+
$settings = __( 'Settings', 'bookly' );
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
+
add_submenu_page( 'bookly-menu', $calendar, $calendar, 'read',
|
73 |
+
Modules\Calendar\Page::pageSlug(), function () { Modules\Calendar\Page::render(); } );
|
74 |
+
add_submenu_page( 'bookly-menu', $appointments, $appointments, 'manage_options',
|
75 |
+
Modules\Appointments\Page::pageSlug(), function () { Modules\Appointments\Page::render(); } );
|
76 |
+
Lib\Proxy\Locations::addBooklyMenuItem();
|
77 |
+
Lib\Proxy\Packages::addBooklyMenuItem();
|
78 |
+
if ( $current_user->has_cap( 'administrator' ) ) {
|
79 |
+
add_submenu_page( 'bookly-menu', $staff_members, $staff_members, 'manage_options',
|
80 |
+
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
|
81 |
+
} else {
|
82 |
+
if ( get_option( 'bookly_gen_allow_staff_edit_profile' ) == 1 ) {
|
83 |
+
add_submenu_page( 'bookly-menu', __( 'Profile', 'bookly' ), __( 'Profile', 'bookly' ), 'read',
|
84 |
+
Modules\Staff\Page::pageSlug(), function () { Modules\Staff\Page::render(); } );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
add_submenu_page( 'bookly-menu', $services, $services, 'manage_options',
|
88 |
+
Modules\Services\Page::pageSlug(), function () { Modules\Services\Page::render(); } );
|
89 |
+
Lib\Proxy\Taxes::addBooklyMenuItem();
|
90 |
+
add_submenu_page( 'bookly-menu', $customers, $customers, 'manage_options',
|
91 |
+
Modules\Customers\Page::pageSlug(), function () { Modules\Customers\Page::render(); } );
|
92 |
+
Lib\Proxy\CustomerInformation::addBooklyMenuItem();
|
93 |
+
Lib\Proxy\CustomerGroups::addBooklyMenuItem();
|
94 |
+
add_submenu_page( 'bookly-menu', $notifications, $notifications, 'manage_options',
|
95 |
+
Modules\Notifications\Page::pageSlug(), function () { Modules\Notifications\Page::render(); } );
|
96 |
+
Modules\Sms\Page::addBooklyMenuItem();
|
97 |
+
add_submenu_page( 'bookly-menu', $payments, $payments, 'manage_options',
|
98 |
+
Modules\Payments\Page::pageSlug(), function () { Modules\Payments\Page::render(); } );
|
99 |
+
add_submenu_page( 'bookly-menu', $appearance, $appearance, 'manage_options',
|
100 |
+
Modules\Appearance\Page::pageSlug(), function () { Modules\Appearance\Page::render(); } );
|
101 |
+
Lib\Proxy\Coupons::addBooklyMenuItem();
|
102 |
+
Lib\Proxy\CustomFields::addBooklyMenuItem();
|
103 |
+
add_submenu_page( 'bookly-menu', $settings, $settings, 'manage_options',
|
104 |
+
Modules\Settings\Page::pageSlug(), function () { Modules\Settings\Page::render(); } );
|
105 |
+
Modules\Messages\Page::addBooklyMenuItem();
|
106 |
+
Modules\Shop\Page::addBooklyMenuItem();
|
107 |
+
Lib\Proxy\Pro::addAnalyticsBooklyMenuItem();
|
108 |
+
|
109 |
+
if ( isset ( $_GET['page'] ) && $_GET['page'] == 'bookly-debug' ) {
|
110 |
+
add_submenu_page( 'bookly-menu', 'Debug', 'Debug', 'manage_options',
|
111 |
+
Modules\Debug\Page::pageSlug(), function () { Modules\Debug\Page::render(); } );
|
112 |
+
}
|
113 |
}
|
114 |
|
115 |
unset ( $submenu['bookly-menu'][0] );
|
116 |
}
|
117 |
}
|
|
|
118 |
}
|
backend/components/appearance/Codes.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Appearance;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Codes
|
8 |
+
* @package Bookly\Backend\Components\Appearance
|
9 |
+
*/
|
10 |
+
class Codes
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Get HTML for appearance codes.
|
14 |
+
*
|
15 |
+
* @param int $step
|
16 |
+
* @param bool $extra_codes
|
17 |
+
* @return string
|
18 |
+
*/
|
19 |
+
public static function getHtml( $step = null, $extra_codes = false )
|
20 |
+
{
|
21 |
+
$codes = array(
|
22 |
+
array( 'code' => 'appointments_count', 'description' => __( 'total quantity of appointments in cart', 'bookly' ), 'flags' => array( 'step' => 7, 'extra_codes' => true ) ),
|
23 |
+
array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ), 'flags' => array( 'step' => 8, 'extra_codes' => true ) ),
|
24 |
+
array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
|
25 |
+
array( 'code' => 'login_form', 'description' => __( 'login form', 'bookly' ), 'flags' => array( 'step' => 6, 'extra_codes' => true ) ),
|
26 |
+
array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) ),
|
27 |
+
array( 'code' => 'service_date', 'description' => __( 'date of service', 'bookly' ), 'flags' => array( 'step' => '>3' ) ),
|
28 |
+
array( 'code' => 'service_duration', 'description' => __( 'duration of service', 'bookly' ) ),
|
29 |
+
array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
|
30 |
+
array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
|
31 |
+
array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
|
32 |
+
array( 'code' => 'service_time', 'description' => __( 'time of service', 'bookly' ), 'flags' => array( 'step' => '>3' ) ),
|
33 |
+
array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
|
34 |
+
array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
|
35 |
+
array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ), 'flags' => array( 'step' => '>1' ) ),
|
36 |
+
array( 'code' => 'total_price', 'description' => __( 'total price of booking', 'bookly' ) ),
|
37 |
+
);
|
38 |
+
|
39 |
+
return Lib\Utils\Common::codes( Proxy\Shared::prepareCodes( $codes ), array( 'step' => $step, 'extra_codes' => isset ( $extra_codes ) ) );
|
40 |
+
}
|
41 |
+
}
|
backend/components/appearance/Editable.php
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Appearance;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Editable
|
6 |
+
* @package Bookly\Backend\Components\Appearance
|
7 |
+
*/
|
8 |
+
class Editable
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Render editable string (single line).
|
12 |
+
*
|
13 |
+
* @param array $options
|
14 |
+
* @param bool $echo
|
15 |
+
* @return string
|
16 |
+
*/
|
17 |
+
public static function renderString( array $options, $echo = true )
|
18 |
+
{
|
19 |
+
return self::_renderEditable( $options, 'span', $echo );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Render editable label.
|
24 |
+
*
|
25 |
+
* @param array $options
|
26 |
+
* @param bool $echo
|
27 |
+
* @return string
|
28 |
+
*/
|
29 |
+
public static function renderLabel( array $options, $echo = true )
|
30 |
+
{
|
31 |
+
return self::_renderEditable( $options, 'label', $echo );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Render editable text (multi-line).
|
36 |
+
*
|
37 |
+
* @param string $option_name
|
38 |
+
* @param string $codes
|
39 |
+
* @param string $placement
|
40 |
+
* @param string $title
|
41 |
+
*/
|
42 |
+
public static function renderText( $option_name, $codes = '', $placement = 'bottom', $title = '' )
|
43 |
+
{
|
44 |
+
$option_value = get_option( $option_name );
|
45 |
+
|
46 |
+
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>',
|
47 |
+
$option_name,
|
48 |
+
esc_attr( json_encode( array( $option_name => $option_value ) ) ),
|
49 |
+
esc_attr( $codes ),
|
50 |
+
esc_attr( $title ),
|
51 |
+
$placement,
|
52 |
+
esc_html( $option_value )
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Render editable number.
|
58 |
+
*
|
59 |
+
* @param string $option_name
|
60 |
+
* @param int $min
|
61 |
+
* @param int $step
|
62 |
+
*/
|
63 |
+
public static function renderNumber( $option_name, $min = 1, $step = 1 )
|
64 |
+
{
|
65 |
+
$option_value = get_option( $option_name );
|
66 |
+
|
67 |
+
printf( '<span class="bookly-js-editable bookly-js-option %s editable-pre-wrapped" data-type="bookly" data-fieldType="number" data-values="%s" data-min="%s" data-step="%s">%s</span>',
|
68 |
+
$option_name,
|
69 |
+
esc_attr( json_encode( array( $option_name => $option_value ) ) ),
|
70 |
+
esc_attr( $min ),
|
71 |
+
esc_attr( $step ),
|
72 |
+
esc_html( $option_value )
|
73 |
+
);
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Render editable element.
|
78 |
+
*
|
79 |
+
* @param array $options
|
80 |
+
* @param string $tag
|
81 |
+
* @param bool $echo
|
82 |
+
* @return string|void
|
83 |
+
*/
|
84 |
+
private static function _renderEditable( array $options, $tag, $echo = true )
|
85 |
+
{
|
86 |
+
$data = array();
|
87 |
+
foreach ( $options as $option_name ) {
|
88 |
+
$data[ $option_name ] = get_option( $option_name );
|
89 |
+
}
|
90 |
+
|
91 |
+
$class = $options[0];
|
92 |
+
$data_values = esc_attr( json_encode( $data ) );
|
93 |
+
$content = esc_html( $data[ $options[0] ] );
|
94 |
+
|
95 |
+
$template = '<{tag} class="bookly-js-editable bookly-js-option {class}" data-type="bookly" data-values="{data-values}">{content}</{tag}>';
|
96 |
+
$html = strtr( $template, array(
|
97 |
+
'{tag}' => $tag,
|
98 |
+
'{class}' => $class,
|
99 |
+
'{data-values}' => $data_values,
|
100 |
+
'{content}' => $content,
|
101 |
+
) );
|
102 |
+
|
103 |
+
if ( ! $echo ) {
|
104 |
+
return $html;
|
105 |
+
}
|
106 |
+
|
107 |
+
echo $html;
|
108 |
+
}
|
109 |
+
}
|
backend/components/appearance/proxy/Pro.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAddress() Render inputs for address fields in appearance.
|
11 |
+
* @method static void renderBirthday() Render inputs for birthday fields in appearance.
|
12 |
+
*/
|
13 |
+
abstract class Pro extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/components/appearance/proxy/Shared.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareCodes( array $codes ) Alter array of codes to be displayed in Bookly Appearance.
|
11 |
+
*/
|
12 |
+
abstract class Shared extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/controls/Buttons.php
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Controls;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Buttons
|
6 |
+
* @package Bookly\Backend\Components\Controls
|
7 |
+
*/
|
8 |
+
class Buttons
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Render custom button.
|
12 |
+
*
|
13 |
+
* @param string $id
|
14 |
+
* @param string $class
|
15 |
+
* @param string $caption
|
16 |
+
* @param array $attributes
|
17 |
+
* @param string $caption_template
|
18 |
+
*/
|
19 |
+
public static function renderCustom( $id = null, $class = 'btn-success', $caption = null, array $attributes = array(), $caption_template = '{caption}' )
|
20 |
+
{
|
21 |
+
if ( $caption === null ) {
|
22 |
+
$caption = __( 'Save', 'bookly' );
|
23 |
+
}
|
24 |
+
|
25 |
+
$caption = strtr( $caption_template, array( '{caption}' => esc_html( $caption ) ) );
|
26 |
+
|
27 |
+
echo self::_createButton( 'button', $id, $class, null, $caption, $attributes );
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Render delete button.
|
32 |
+
*
|
33 |
+
* @param string $id
|
34 |
+
* @param string $extra_class
|
35 |
+
* @param string $caption
|
36 |
+
* @param array $attributes
|
37 |
+
*/
|
38 |
+
public static function renderDelete( $id = 'bookly-delete', $extra_class = null, $caption = null, array $attributes = array() )
|
39 |
+
{
|
40 |
+
if ( $caption === null ) {
|
41 |
+
$caption = __( 'Delete', 'bookly' );
|
42 |
+
}
|
43 |
+
|
44 |
+
echo self::_createButton(
|
45 |
+
'button',
|
46 |
+
$id,
|
47 |
+
'btn-danger',
|
48 |
+
$extra_class,
|
49 |
+
'<i class="glyphicon glyphicon-trash"></i> ' . esc_html( $caption ),
|
50 |
+
$attributes
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* Render reset button.
|
56 |
+
*
|
57 |
+
* @param string $id
|
58 |
+
* @param string $extra_class
|
59 |
+
* @param string $caption
|
60 |
+
* @param array $attributes
|
61 |
+
*/
|
62 |
+
public static function renderReset( $id = null, $extra_class = null, $caption = null, array $attributes = array() )
|
63 |
+
{
|
64 |
+
if ( $caption === null ) {
|
65 |
+
$caption = __( 'Reset', 'bookly' );
|
66 |
+
}
|
67 |
+
|
68 |
+
echo self::_createButton( 'reset', $id, 'btn-lg btn-default', $extra_class, esc_html( $caption ), $attributes );
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Render submit button.
|
73 |
+
*
|
74 |
+
* @param string $id
|
75 |
+
* @param string $extra_class
|
76 |
+
* @param string $caption
|
77 |
+
* @param array $attributes
|
78 |
+
*/
|
79 |
+
public static function renderSubmit( $id = 'bookly-save', $extra_class = null, $caption = null, array $attributes = array() )
|
80 |
+
{
|
81 |
+
if ( $caption === null ) {
|
82 |
+
$caption = __( 'Save', 'bookly' );
|
83 |
+
}
|
84 |
+
|
85 |
+
echo self::_createButton( 'submit', $id, 'btn-lg btn-success', $extra_class, esc_html( $caption ), $attributes );
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Create button.
|
90 |
+
*
|
91 |
+
* @param string $type
|
92 |
+
* @param string $id
|
93 |
+
* @param string $class
|
94 |
+
* @param string $extra_class
|
95 |
+
* @param string $caption_html
|
96 |
+
* @param array $attributes
|
97 |
+
* @return string
|
98 |
+
*/
|
99 |
+
private static function _createButton( $type, $id, $class, $extra_class, $caption_html, array $attributes )
|
100 |
+
{
|
101 |
+
$classes = array( 'btn ladda-button' );
|
102 |
+
if ( $class != '' ) {
|
103 |
+
$classes[] = $class;
|
104 |
+
}
|
105 |
+
if ( $extra_class != '' ) {
|
106 |
+
$classes[] = $extra_class;
|
107 |
+
}
|
108 |
+
|
109 |
+
if ( $id !== null ) {
|
110 |
+
$attributes['id'] = $id;
|
111 |
+
}
|
112 |
+
$attributes_str = '';
|
113 |
+
foreach ( $attributes as $attr => $value ) {
|
114 |
+
$attributes_str .= sprintf( ' %s="%s"', $attr, esc_attr( $value ) );
|
115 |
+
}
|
116 |
+
|
117 |
+
return strtr(
|
118 |
+
'<button type="{type}" class="{class}" data-spinner-size="40" data-style="zoom-in"{attributes}><span class="ladda-label">{caption}</span></button>',
|
119 |
+
array(
|
120 |
+
'{type}' => $type,
|
121 |
+
'{class}' => implode( ' ', $classes ),
|
122 |
+
'{attributes}' => $attributes_str,
|
123 |
+
'{caption}' => $caption_html,
|
124 |
+
)
|
125 |
+
);
|
126 |
+
}
|
127 |
+
}
|
backend/components/controls/Inputs.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Controls;
|
3 |
+
|
4 |
+
use Bookly\Lib\Utils\Common;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Inputs
|
8 |
+
* @package Bookly\Backend\Components\Controls
|
9 |
+
*/
|
10 |
+
class Inputs
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Add hidden input with CSRF token.
|
14 |
+
*/
|
15 |
+
public static function renderCsrf()
|
16 |
+
{
|
17 |
+
printf(
|
18 |
+
'<input type="hidden" name="csrf_token" value="%s" />',
|
19 |
+
esc_attr( Common::getCsrfToken() )
|
20 |
+
);
|
21 |
+
}
|
22 |
+
}
|
backend/components/dialogs/appointment/attach_payment/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAttachPaymentDialog() Render attach payment dialog.
|
11 |
+
*/
|
12 |
+
abstract class Pro extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/dialogs/appointment/attach_payment/proxy/Taxes.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Taxes
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAttachPayment()
|
11 |
+
*/
|
12 |
+
abstract class Taxes extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/dialogs/appointment/customer_details/Dialog.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Dialog
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails
|
9 |
+
*/
|
10 |
+
class Dialog extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render customer details dialog.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
static::renderTemplate( 'customer_details' );
|
18 |
+
}
|
19 |
+
}
|
backend/components/dialogs/appointment/customer_details/proxy/Files.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Files
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment
|
9 |
+
*
|
10 |
+
* @method static void renderCustomField( \stdClass $custom_field ) Render files in Customer Details
|
11 |
+
*/
|
12 |
+
abstract class Files extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/dialogs/appointment/customer_details/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment
|
9 |
+
*
|
10 |
+
* @method static void renderTimeZoneSwitcher() Render time zone switcher in Customer Details
|
11 |
+
*/
|
12 |
+
abstract class Pro extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/dialogs/appointment/customer_details/proxy/Shared.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment
|
9 |
+
*
|
10 |
+
* @method static void renderDetails()
|
11 |
+
*/
|
12 |
+
abstract class Shared extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/{modules/calendar/templates/_customer_details_dialog.php → components/dialogs/appointment/customer_details/templates/customer_details.php}
RENAMED
@@ -1,8 +1,9 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
5 |
-
use
|
|
|
6 |
?>
|
7 |
<div id="bookly-customer-details-dialog" class="modal fade" tabindex=-1 role="dialog">
|
8 |
<div class="modal-dialog">
|
@@ -23,11 +24,16 @@ use BooklyLite\Lib\Proxy;
|
|
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:
|
|
|
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>
|
@@ -35,14 +41,14 @@ use BooklyLite\Lib\Proxy;
|
|
35 |
</div>
|
36 |
<?php endif ?>
|
37 |
|
38 |
-
<?php Proxy\
|
39 |
-
|
40 |
</div>
|
41 |
<div class="modal-footer">
|
42 |
-
<?php
|
43 |
-
<?php
|
44 |
</div>
|
45 |
</form>
|
46 |
-
</div
|
47 |
-
</div
|
48 |
-
</div
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Dialogs\Appointment\CustomerDetails\Proxy;
|
4 |
+
use Bookly\Lib\Entities\CustomerAppointment;
|
5 |
+
use Bookly\Lib\Utils\Common;
|
6 |
+
use Bookly\Lib\Config;
|
7 |
?>
|
8 |
<div id="bookly-customer-details-dialog" class="modal fade" tabindex=-1 role="dialog">
|
9 |
<div class="modal-dialog">
|
24 |
<?php if ( Config::waitingListActive() ) : ?>
|
25 |
<option value="<?php echo CustomerAppointment::STATUS_WAITLISTED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?></option>
|
26 |
<?php endif ?>
|
27 |
+
<?php if ( Config::tasksActive() ) : ?>
|
28 |
+
<option value="<?php echo CustomerAppointment::STATUS_DONE ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_DONE ) ) ?></option>
|
29 |
+
<?php endif ?>
|
30 |
</select>
|
31 |
</div>
|
32 |
+
<div class="form-group" <?php if ( ! Config::groupBookingActive() ) echo ' style="display:none"' ?>>
|
33 |
+
<label for="bookly-number-of-persons"><?php _e( 'Number of persons', 'bookly' ) ?></label>
|
34 |
<select class="bookly-custom-field form-control" id="bookly-number-of-persons"></select>
|
35 |
</div>
|
36 |
+
<?php Proxy\Pro::renderTimeZoneSwitcher() ?>
|
37 |
<?php if ( Config::showNotes() ): ?>
|
38 |
<div class="form-group">
|
39 |
<label for="bookly-appointment-notes"><?php echo Common::getTranslatedOption( 'bookly_l10n_label_notes' ) ?></label>
|
41 |
</div>
|
42 |
<?php endif ?>
|
43 |
|
44 |
+
<?php Proxy\Shared::renderDetails() ?>
|
45 |
+
|
46 |
</div>
|
47 |
<div class="modal-footer">
|
48 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-success', __( 'Apply', 'bookly' ), array( 'ng-click' => 'saveCustomFields()' ) ) ?>
|
49 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
50 |
</div>
|
51 |
</form>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</div>
|
backend/components/dialogs/appointment/delete/Ajax.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Delete;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Ajax
|
9 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Delete
|
10 |
+
*/
|
11 |
+
class Ajax extends Lib\Base\Ajax
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @inheritdoc
|
15 |
+
*/
|
16 |
+
protected static function permissions()
|
17 |
+
{
|
18 |
+
return array( '_default' => 'user' );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Delete single appointment.
|
23 |
+
*/
|
24 |
+
public static function deleteAppointment()
|
25 |
+
{
|
26 |
+
$appointment_id = self::parameter( 'appointment_id' );
|
27 |
+
$reason = self::parameter( 'reason' );
|
28 |
+
|
29 |
+
if ( self::parameter( 'notify' ) ) {
|
30 |
+
$ca_list = Lib\Entities\CustomerAppointment::query()
|
31 |
+
->where( 'appointment_id', $appointment_id )
|
32 |
+
->find();
|
33 |
+
/** @var Lib\Entities\CustomerAppointment $ca */
|
34 |
+
foreach ( $ca_list as $ca ) {
|
35 |
+
switch ( $ca->getStatus() ) {
|
36 |
+
case Lib\Entities\CustomerAppointment::STATUS_PENDING:
|
37 |
+
case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
|
38 |
+
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
|
39 |
+
break;
|
40 |
+
case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
|
41 |
+
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
|
42 |
+
break;
|
43 |
+
}
|
44 |
+
Lib\Notifications\Sender::sendSingle(
|
45 |
+
DataHolders\Simple::create( $ca ),
|
46 |
+
null,
|
47 |
+
array( 'cancellation_reason' => $reason )
|
48 |
+
);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
Lib\Entities\Appointment::find( $appointment_id )->delete();
|
53 |
+
|
54 |
+
wp_send_json_success();
|
55 |
+
}
|
56 |
+
}
|
backend/components/dialogs/appointment/delete/Dialog.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Delete;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Dialog
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment
|
9 |
+
*/
|
10 |
+
class Dialog extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render delete appointment dialog.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
static::enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
19 |
+
) );
|
20 |
+
|
21 |
+
static::enqueueScripts( array(
|
22 |
+
'frontend' => array(
|
23 |
+
'js/spin.min.js' => array( 'jquery' ),
|
24 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
25 |
+
),
|
26 |
+
'module' => array(
|
27 |
+
'js/delete_dialog.js' => array( 'jquery' ),
|
28 |
+
),
|
29 |
+
) );
|
30 |
+
|
31 |
+
static::renderTemplate( 'delete' );
|
32 |
+
}
|
33 |
+
}
|
backend/{modules/calendar → components/dialogs/appointment/delete}/resources/js/delete_dialog.js
RENAMED
File without changes
|
backend/{modules/calendar/templates/_delete_dialog.php → components/dialogs/appointment/delete/templates/delete.php}
RENAMED
@@ -1,10 +1,6 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
* Template for delete appointment dialog
|
4 |
-
*/
|
5 |
-
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
6 |
?>
|
7 |
-
|
8 |
<div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
|
9 |
<div class="modal-dialog">
|
10 |
<div class="modal-content">
|
@@ -24,8 +20,8 @@ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
24 |
</div>
|
25 |
</div>
|
26 |
<div class="modal-footer">
|
27 |
-
<?php
|
28 |
-
<?php
|
29 |
</div>
|
30 |
</div>
|
31 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
|
|
|
|
|
|
3 |
?>
|
|
|
4 |
<div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
|
5 |
<div class="modal-dialog">
|
6 |
<div class="modal-content">
|
20 |
</div>
|
21 |
</div>
|
22 |
<div class="modal-footer">
|
23 |
+
<?php Buttons::renderDelete(); ?>
|
24 |
+
<?php Buttons::renderCustom( null, 'btn-default', __( 'Cancel', 'bookly' ), array( 'ng-click' => 'closeDialog()', 'data-dismiss' => 'modal' ) ) ?>
|
25 |
</div>
|
26 |
</div>
|
27 |
</div>
|
backend/components/dialogs/appointment/edit/Ajax.php
ADDED
@@ -0,0 +1,808 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
6 |
+
use Bookly\Backend\Modules\Calendar;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Ajax
|
10 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit
|
11 |
+
*/
|
12 |
+
class Ajax extends Lib\Base\Ajax
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* @inheritdoc
|
16 |
+
*/
|
17 |
+
protected static function permissions()
|
18 |
+
{
|
19 |
+
return array( '_default' => 'user' );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get data needed for appointment form initialisation.
|
24 |
+
*/
|
25 |
+
public static function getDataForAppointmentForm()
|
26 |
+
{
|
27 |
+
$type = self::parameter( 'type', false ) == 'package'
|
28 |
+
? Lib\Entities\Service::TYPE_PACKAGE
|
29 |
+
: Lib\Entities\Service::TYPE_SIMPLE;
|
30 |
+
|
31 |
+
$result = array(
|
32 |
+
'staff' => array(),
|
33 |
+
'customers' => array(),
|
34 |
+
'start_time' => array(),
|
35 |
+
'end_time' => array(),
|
36 |
+
'app_start_time' => null, // Appointment start time which may not be in the list of start times.
|
37 |
+
'app_end_time' => null, // Appointment end time which may not be in the list of end times.
|
38 |
+
'week_days' => array(),
|
39 |
+
'time_interval' => Lib\Config::getTimeSlotLength(),
|
40 |
+
'status' => array(
|
41 |
+
'items' => array(
|
42 |
+
'pending' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_PENDING ),
|
43 |
+
'approved' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_APPROVED ),
|
44 |
+
'cancelled' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_CANCELLED ),
|
45 |
+
'rejected' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_REJECTED ),
|
46 |
+
'waitlisted' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_WAITLISTED ),
|
47 |
+
'done' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_DONE ),
|
48 |
+
)
|
49 |
+
),
|
50 |
+
);
|
51 |
+
|
52 |
+
// Staff list.
|
53 |
+
$staff = Lib\Entities\Staff::query()->findOne();
|
54 |
+
$staff_members = $staff ? Lib\Proxy\Pro::prepareStaffMembers( array( $staff ) ) : array();
|
55 |
+
$max_duration = 0;
|
56 |
+
|
57 |
+
foreach ( $staff_members as $staff_member ) {
|
58 |
+
$services = array();
|
59 |
+
if ( $type == Lib\Entities\Service::TYPE_SIMPLE ) {
|
60 |
+
$services = Proxy\Pro::addCustomService( $services );
|
61 |
+
}
|
62 |
+
foreach ( $staff_member->getStaffServices( $type ) as $staff_service ) {
|
63 |
+
$sub_services = $staff_service->service->getSubServices();
|
64 |
+
if ( $type == Lib\Entities\Service::TYPE_SIMPLE || ! empty( $sub_services ) ) {
|
65 |
+
if ( $staff_service->getLocationId() === null || Lib\Proxy\Locations::prepareStaffLocationId( $staff_service->getLocationId(), $staff_service->getStaffId() ) == $staff_service->getLocationId() ) {
|
66 |
+
if ( ! in_array( $staff_service->service->getId(), array_map( function ( $service ) { return $service['id']; }, $services ) ) ) {
|
67 |
+
$services[] = array(
|
68 |
+
'id' => $staff_service->service->getId(),
|
69 |
+
'title' => sprintf(
|
70 |
+
'%s (%s)',
|
71 |
+
$staff_service->service->getTitle(),
|
72 |
+
Lib\Utils\DateTime::secondsToInterval( $staff_service->service->getDuration() )
|
73 |
+
),
|
74 |
+
'duration' => $staff_service->service->getDuration(),
|
75 |
+
'units_min' => $staff_service->service->getUnitsMin(),
|
76 |
+
'units_max' => $staff_service->service->getUnitsMax(),
|
77 |
+
'locations' => array(
|
78 |
+
( $staff_service->getLocationId() ?: 0 ) => array(
|
79 |
+
'capacity_min' => Lib\Config::groupBookingActive() ? $staff_service->getCapacityMin() : 1,
|
80 |
+
'capacity_max' => Lib\Config::groupBookingActive() ? $staff_service->getCapacityMax() : 1,
|
81 |
+
),
|
82 |
+
),
|
83 |
+
);
|
84 |
+
$max_duration = max( $max_duration, $staff_service->service->getUnitsMax() * $staff_service->service->getDuration() );
|
85 |
+
} else {
|
86 |
+
array_walk( $services, function ( &$item ) use ( $staff_service ) {
|
87 |
+
if ( $item['id'] == $staff_service->service->getId() ) {
|
88 |
+
$item['locations'][ $staff_service->getLocationId() ?: 0 ] = array(
|
89 |
+
'capacity_min' => Lib\Config::groupBookingActive() ? $staff_service->getCapacityMin() : 1,
|
90 |
+
'capacity_max' => Lib\Config::groupBookingActive() ? $staff_service->getCapacityMax() : 1,
|
91 |
+
);
|
92 |
+
}
|
93 |
+
} );
|
94 |
+
}
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}
|
98 |
+
$locations = array();
|
99 |
+
foreach ( (array) Lib\Proxy\Locations::findByStaffId( $staff_member->getId() ) as $location ) {
|
100 |
+
$locations[] = array(
|
101 |
+
'id' => $location->getId(),
|
102 |
+
'name' => $location->getName(),
|
103 |
+
);
|
104 |
+
}
|
105 |
+
$result['staff'][] = array(
|
106 |
+
'id' => $staff_member->getId(),
|
107 |
+
'full_name' => $staff_member->getFullName(),
|
108 |
+
'services' => $services,
|
109 |
+
'locations' => $locations,
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
/** @var Lib\Entities\Customer $customer */
|
114 |
+
// Customers list.
|
115 |
+
foreach ( Lib\Entities\Customer::query()->sortBy( 'full_name' )->find() as $customer ) {
|
116 |
+
$name = $customer->getFullName();
|
117 |
+
if ( $customer->getEmail() != '' || $customer->getPhone() != '' ) {
|
118 |
+
$name .= ' (' . trim( $customer->getEmail() . ', ' . $customer->getPhone(), ', ' ) . ')';
|
119 |
+
}
|
120 |
+
|
121 |
+
$result['customers'][] = array(
|
122 |
+
'id' => $customer->getId(),
|
123 |
+
'name' => $name,
|
124 |
+
'status' => Lib\Proxy\CustomerGroups::prepareDefaultAppointmentStatus( get_option( 'bookly_gen_default_appointment_status' ), $customer->getGroupId() ),
|
125 |
+
'custom_fields' => array(),
|
126 |
+
'timezone' => Lib\Proxy\Pro::getLastCustomerTimezone( $customer->getId() ),
|
127 |
+
'number_of_persons' => 1,
|
128 |
+
);
|
129 |
+
}
|
130 |
+
|
131 |
+
// Time list.
|
132 |
+
$ts_length = Lib\Config::getTimeSlotLength();
|
133 |
+
$time_start = 0;
|
134 |
+
$time_end = max( $max_duration + DAY_IN_SECONDS, DAY_IN_SECONDS * 2 );
|
135 |
+
|
136 |
+
// Run the loop.
|
137 |
+
while ( $time_start <= $time_end ) {
|
138 |
+
$slot = array(
|
139 |
+
'value' => Lib\Utils\DateTime::buildTimeString( $time_start, false ),
|
140 |
+
'title' => Lib\Utils\DateTime::formatTime( $time_start ),
|
141 |
+
);
|
142 |
+
if ( $time_start < DAY_IN_SECONDS ) {
|
143 |
+
$result['start_time'][] = $slot;
|
144 |
+
}
|
145 |
+
$result['end_time'][] = $slot;
|
146 |
+
$time_start += $ts_length;
|
147 |
+
}
|
148 |
+
|
149 |
+
$days_times = Lib\Config::getDaysAndTimes();
|
150 |
+
$weekdays = array( 1 => 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', );
|
151 |
+
foreach ( $days_times['days'] as $index => $abbrev ) {
|
152 |
+
$result['week_days'][] = $weekdays[ $index ];
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( $type == Lib\Entities\Service::TYPE_PACKAGE ) {
|
156 |
+
$result = Proxy\Shared::prepareDataForPackage( $result );
|
157 |
+
}
|
158 |
+
|
159 |
+
wp_send_json( $result );
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Get appointment data when editing an appointment.
|
164 |
+
*/
|
165 |
+
public static function getDataForAppointment()
|
166 |
+
{
|
167 |
+
$response = array( 'success' => false, 'data' => array( 'customers' => array() ) );
|
168 |
+
|
169 |
+
$appointment = new Lib\Entities\Appointment();
|
170 |
+
if ( $appointment->load( self::parameter( 'id' ) ) ) {
|
171 |
+
$response['success'] = true;
|
172 |
+
|
173 |
+
$query = Lib\Entities\Appointment::query( 'a' )
|
174 |
+
->select( 'SUM(ca.number_of_persons) AS total_number_of_persons,
|
175 |
+
a.staff_id,
|
176 |
+
a.staff_any,
|
177 |
+
a.service_id,
|
178 |
+
a.custom_service_name,
|
179 |
+
a.custom_service_price,
|
180 |
+
a.start_date,
|
181 |
+
a.end_date,
|
182 |
+
a.internal_note,
|
183 |
+
a.series_id,
|
184 |
+
a.location_id' )
|
185 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
|
186 |
+
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id AND ss.location_id = a.location_id' )
|
187 |
+
->where( 'a.id', $appointment->getId() );
|
188 |
+
if ( Lib\Config::groupBookingActive() ) {
|
189 |
+
$query->addSelect( 'ss.capacity_min AS min_capacity, ss.capacity_max AS max_capacity' );
|
190 |
+
} else {
|
191 |
+
$query->addSelect( '1 AS min_capacity, 1 AS max_capacity' );
|
192 |
+
}
|
193 |
+
|
194 |
+
$info = $query->fetchRow();
|
195 |
+
$response['data']['total_number_of_persons'] = $info['total_number_of_persons'];
|
196 |
+
$response['data']['min_capacity'] = $info['min_capacity'];
|
197 |
+
$response['data']['max_capacity'] = $info['max_capacity'];
|
198 |
+
$response['data']['start_date'] = $info['start_date'];
|
199 |
+
$response['data']['end_date'] = $info['end_date'];
|
200 |
+
$response['data']['start_time'] = $info['start_date'] ?
|
201 |
+
array(
|
202 |
+
'value' => date( 'H:i', strtotime( $info['start_date'] ) ),
|
203 |
+
'title' => Lib\Utils\DateTime::formatTime( $info['start_date'] ),
|
204 |
+
) : null;
|
205 |
+
$response['data']['end_time'] = $info['end_date'] ?
|
206 |
+
array(
|
207 |
+
'value' => date( 'H:i', strtotime( $info['end_date'] ) ),
|
208 |
+
'title' => Lib\Utils\DateTime::formatTime( $info['end_date'] ),
|
209 |
+
) : null;
|
210 |
+
$response['data']['staff_id'] = $info['staff_id'];
|
211 |
+
$response['data']['staff_any'] = (int) $info['staff_any'];
|
212 |
+
$response['data']['service_id'] = $info['service_id'];
|
213 |
+
$response['data']['custom_service_name'] = $info['custom_service_name'];
|
214 |
+
$response['data']['custom_service_price'] = (float) $info['custom_service_price'];
|
215 |
+
$response['data']['internal_note'] = $info['internal_note'];
|
216 |
+
$response['data']['series_id'] = $info['series_id'];
|
217 |
+
$response['data']['location_id'] = $info['location_id'];
|
218 |
+
|
219 |
+
$customers = Lib\Entities\CustomerAppointment::query( 'ca' )
|
220 |
+
->select( 'ca.id,
|
221 |
+
ca.customer_id,
|
222 |
+
ca.package_id,
|
223 |
+
ca.custom_fields,
|
224 |
+
ca.extras,
|
225 |
+
ca.number_of_persons,
|
226 |
+
ca.notes,
|
227 |
+
ca.status,
|
228 |
+
ca.payment_id,
|
229 |
+
ca.compound_service_id,
|
230 |
+
ca.compound_token,
|
231 |
+
ca.time_zone,
|
232 |
+
ca.time_zone_offset,
|
233 |
+
p.paid AS payment,
|
234 |
+
p.total AS payment_total,
|
235 |
+
p.type AS payment_type,
|
236 |
+
p.details AS payment_details,
|
237 |
+
p.status AS payment_status' )
|
238 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
239 |
+
->where( 'ca.appointment_id', $appointment->getId() )
|
240 |
+
->fetchArray();
|
241 |
+
foreach ( $customers as $customer ) {
|
242 |
+
$payment_title = '';
|
243 |
+
if ( $customer['payment'] !== null ) {
|
244 |
+
$payment_title = Lib\Utils\Price::format( $customer['payment'] );
|
245 |
+
if ( $customer['payment'] != $customer['payment_total'] ) {
|
246 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $customer['payment_total'] ) );
|
247 |
+
}
|
248 |
+
$payment_title .= sprintf(
|
249 |
+
' %s <span%s>%s</span>',
|
250 |
+
Lib\Entities\Payment::typeToString( $customer['payment_type'] ),
|
251 |
+
$customer['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
252 |
+
Lib\Entities\Payment::statusToString( $customer['payment_status'] )
|
253 |
+
);
|
254 |
+
}
|
255 |
+
$compound_service = '';
|
256 |
+
if ( $customer['compound_service_id'] !== null ) {
|
257 |
+
$service = new Lib\Entities\Service();
|
258 |
+
if ( $service->load( $customer['compound_service_id'] ) ) {
|
259 |
+
$compound_service = $service->getTranslatedTitle();
|
260 |
+
}
|
261 |
+
}
|
262 |
+
$custom_fields = (array) json_decode( $customer['custom_fields'], true );
|
263 |
+
$response['data']['customers'][] = array(
|
264 |
+
'id' => $customer['customer_id'],
|
265 |
+
'ca_id' => $customer['id'],
|
266 |
+
'package_id' => $customer['package_id'],
|
267 |
+
'compound_service' => $compound_service,
|
268 |
+
'compound_token' => $customer['compound_token'],
|
269 |
+
'custom_fields' => $custom_fields,
|
270 |
+
'files' => Lib\Proxy\Files::getFileNamesForCustomFields( $custom_fields ),
|
271 |
+
'extras' => (array) json_decode( $customer['extras'], true ),
|
272 |
+
'number_of_persons' => $customer['number_of_persons'],
|
273 |
+
'notes' => $customer['notes'],
|
274 |
+
'payment_id' => $customer['payment_id'],
|
275 |
+
'payment_type' => $customer['payment'] != $customer['payment_total'] ? 'partial' : 'full',
|
276 |
+
'payment_title' => $payment_title,
|
277 |
+
'status' => $customer['status'],
|
278 |
+
'timezone' => Lib\Proxy\Pro::getCustomerTimezone( $customer['time_zone'], $customer['time_zone_offset'] ),
|
279 |
+
);
|
280 |
+
}
|
281 |
+
}
|
282 |
+
|
283 |
+
wp_send_json( $response );
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Save appointment form (for both create and edit).
|
288 |
+
*/
|
289 |
+
public static function saveAppointmentForm()
|
290 |
+
{
|
291 |
+
$response = array( 'success' => false );
|
292 |
+
|
293 |
+
$appointment_id = (int) self::parameter( 'id', 0 );
|
294 |
+
$staff_id = (int) self::parameter( 'staff_id', 0 );
|
295 |
+
$service_id = (int) self::parameter( 'service_id', -1 );
|
296 |
+
$custom_service_name = trim( self::parameter( 'custom_service_name' ) );
|
297 |
+
$custom_service_price = trim( self::parameter( 'custom_service_price' ) );
|
298 |
+
$location_id = (int) self::parameter( 'location_id', 0 );
|
299 |
+
$skip_date = self::parameter( 'skip_date', 0 );
|
300 |
+
$start_date = self::parameter( 'start_date' );
|
301 |
+
$end_date = self::parameter( 'end_date' );
|
302 |
+
$repeat = json_decode( self::parameter( 'repeat', '[]' ), true );
|
303 |
+
$schedule = self::parameter( 'schedule', array() );
|
304 |
+
$customers = json_decode( self::parameter( 'customers', '[]' ), true );
|
305 |
+
$notification = self::parameter( 'notification', 'no' );
|
306 |
+
$internal_note = self::parameter( 'internal_note' );
|
307 |
+
$created_from = self::parameter( 'created_from' );
|
308 |
+
|
309 |
+
if ( ! $service_id ) {
|
310 |
+
// Custom service.
|
311 |
+
$service_id = null;
|
312 |
+
}
|
313 |
+
if ( $service_id || $custom_service_name == '' ) {
|
314 |
+
$custom_service_name = null;
|
315 |
+
}
|
316 |
+
if ( $service_id || $custom_service_price == '' ) {
|
317 |
+
$custom_service_price = null;
|
318 |
+
}
|
319 |
+
if ( ! $location_id ) {
|
320 |
+
$location_id = null;
|
321 |
+
}
|
322 |
+
|
323 |
+
// Check for errors.
|
324 |
+
if ( ! $skip_date ) {
|
325 |
+
if ( ! $start_date ) {
|
326 |
+
$response['errors']['time_interval'] = __( 'Start time must not be empty', 'bookly' );
|
327 |
+
} elseif ( ! $end_date ) {
|
328 |
+
$response['errors']['time_interval'] = __( 'End time must not be empty', 'bookly' );
|
329 |
+
} elseif ( $start_date == $end_date ) {
|
330 |
+
$response['errors']['time_interval'] = __( 'End time must not be equal to start time', 'bookly' );
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
if ( $service_id == -1 ) {
|
335 |
+
$response['errors']['service_required'] = true;
|
336 |
+
} else if ( $service_id === null && $custom_service_name === null ) {
|
337 |
+
$response['errors']['custom_service_name_required'] = true;
|
338 |
+
}
|
339 |
+
$total_number_of_persons = 0;
|
340 |
+
$max_extras_duration = 0;
|
341 |
+
foreach ( $customers as $i => $customer ) {
|
342 |
+
if ( $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_PENDING ||
|
343 |
+
$customer['status'] == Lib\Entities\CustomerAppointment::STATUS_APPROVED
|
344 |
+
) {
|
345 |
+
$total_number_of_persons += $customer['number_of_persons'];
|
346 |
+
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
|
347 |
+
if ( $extras_duration > $max_extras_duration ) {
|
348 |
+
$max_extras_duration = $extras_duration;
|
349 |
+
}
|
350 |
+
}
|
351 |
+
$customers[ $i ]['created_from'] = ( $created_from == 'backend' ) ? 'backend' : 'frontend';
|
352 |
+
}
|
353 |
+
if ( $service_id ) {
|
354 |
+
$staff_service = new Lib\Entities\StaffService();
|
355 |
+
$staff_service->loadBy( array(
|
356 |
+
'staff_id' => $staff_id,
|
357 |
+
'service_id' => $service_id,
|
358 |
+
'location_id' => $location_id ?: null,
|
359 |
+
) );
|
360 |
+
if ( ! $staff_service->isLoaded() ) {
|
361 |
+
$staff_service->loadBy( array(
|
362 |
+
'staff_id' => $staff_id,
|
363 |
+
'service_id' => $service_id,
|
364 |
+
'location_id' => null,
|
365 |
+
) );
|
366 |
+
}
|
367 |
+
if ( $total_number_of_persons > $staff_service->getCapacityMax() ) {
|
368 |
+
$response['errors']['overflow_capacity'] = sprintf(
|
369 |
+
__( 'The number of customers should not be more than %d', 'bookly' ),
|
370 |
+
$staff_service->getCapacityMax()
|
371 |
+
);
|
372 |
+
}
|
373 |
+
}
|
374 |
+
|
375 |
+
// If no errors then try to save the appointment.
|
376 |
+
if ( ! isset ( $response['errors'] ) ) {
|
377 |
+
$duration = Lib\Slots\DatePoint::fromStr( $end_date )->diff( Lib\Slots\DatePoint::fromStr( $start_date ) );
|
378 |
+
if ( ! $skip_date && $repeat['enabled'] ) {
|
379 |
+
// Series.
|
380 |
+
if ( ! empty ( $schedule ) ) {
|
381 |
+
// Create new series.
|
382 |
+
$series = new Lib\Entities\Series();
|
383 |
+
$series
|
384 |
+
->setRepeat( self::parameter( 'repeat' ) )
|
385 |
+
->setToken( Lib\Utils\Common::generateToken( get_class( $series ), 'token' ) )
|
386 |
+
->save();
|
387 |
+
|
388 |
+
if ( $notification != 'no' ) {
|
389 |
+
// Create order per each customer to send notifications.
|
390 |
+
/** @var DataHolders\Order[] $orders */
|
391 |
+
$orders = array();
|
392 |
+
foreach ( $customers as $customer ) {
|
393 |
+
$order = DataHolders\Order::create( Lib\Entities\Customer::find( $customer['id'] ) )
|
394 |
+
->addItem( 0, DataHolders\Series::create( $series ) )
|
395 |
+
;
|
396 |
+
$orders[ $customer['id'] ] = $order;
|
397 |
+
}
|
398 |
+
}
|
399 |
+
|
400 |
+
if ( $service_id ) {
|
401 |
+
$service = Lib\Entities\Service::find( $service_id );
|
402 |
+
} else {
|
403 |
+
$service = new Lib\Entities\Service();
|
404 |
+
$service
|
405 |
+
->setTitle( $custom_service_name )
|
406 |
+
->setDuration( $duration )
|
407 |
+
->setPrice( $custom_service_price )
|
408 |
+
;
|
409 |
+
}
|
410 |
+
|
411 |
+
foreach ( $schedule as $slot ) {
|
412 |
+
$slot = json_decode( $slot );
|
413 |
+
$appointment = new Lib\Entities\Appointment();
|
414 |
+
$appointment
|
415 |
+
->setSeries( $series )
|
416 |
+
->setLocationId( $location_id )
|
417 |
+
->setStaffId( $staff_id )
|
418 |
+
->setServiceId( $service_id )
|
419 |
+
->setCustomServiceName( $custom_service_name )
|
420 |
+
->setCustomServicePrice( $custom_service_price )
|
421 |
+
->setStartDate( $slot[0][2] )
|
422 |
+
->setEndDate( Lib\Slots\DatePoint::fromStr( $slot[0][2] )->modify( $duration )->format( 'Y-m-d H:i:s' ) )
|
423 |
+
->setInternalNote( $internal_note )
|
424 |
+
->setExtrasDuration( $max_extras_duration )
|
425 |
+
;
|
426 |
+
|
427 |
+
if ( $appointment->save() !== false ) {
|
428 |
+
// Save customer appointments.
|
429 |
+
$ca_list = $appointment->saveCustomerAppointments( $customers );
|
430 |
+
// Google Calendar.
|
431 |
+
Lib\Proxy\Pro::syncGoogleCalendarEvent( $appointment );
|
432 |
+
// Waiting list.
|
433 |
+
Lib\Proxy\WaitingList::handleParticipantsChange( $appointment );
|
434 |
+
|
435 |
+
if ( $notification != 'no' ) {
|
436 |
+
foreach ( $ca_list as $ca ) {
|
437 |
+
$item = DataHolders\Simple::create( $ca )
|
438 |
+
->setService( $service )
|
439 |
+
->setAppointment( $appointment )
|
440 |
+
;
|
441 |
+
$orders[ $ca->getCustomerId() ]->getItem( 0 )->addItem( $item );
|
442 |
+
}
|
443 |
+
}
|
444 |
+
}
|
445 |
+
}
|
446 |
+
foreach ( $customers as $customer ) {
|
447 |
+
if ( $customer['payment_create'] === true ) {
|
448 |
+
Proxy\RecurringAppointments::createBackendPayment( $series, $customer );
|
449 |
+
}
|
450 |
+
}
|
451 |
+
|
452 |
+
if ( $notification != 'no' ) {
|
453 |
+
foreach ( $orders as $order ) {
|
454 |
+
Lib\Proxy\RecurringAppointments::sendRecurring( $order->getItem( 0 ), $order );
|
455 |
+
}
|
456 |
+
}
|
457 |
+
}
|
458 |
+
$response['success'] = true;
|
459 |
+
$response['data'] = array( 'staffId' => $staff_id ); // make FullCalendar refetch events
|
460 |
+
} else {
|
461 |
+
// Single appointment.
|
462 |
+
$appointment = new Lib\Entities\Appointment();
|
463 |
+
if ( $appointment_id ) {
|
464 |
+
// Edit.
|
465 |
+
$appointment->load( $appointment_id );
|
466 |
+
if ( $appointment->getStaffId() != $staff_id ) {
|
467 |
+
$appointment->setStaffAny( 0 );
|
468 |
+
}
|
469 |
+
}
|
470 |
+
$appointment
|
471 |
+
->setLocationId( $location_id )
|
472 |
+
->setStaffId( $staff_id )
|
473 |
+
->setServiceId( $service_id )
|
474 |
+
->setCustomServiceName( $custom_service_name )
|
475 |
+
->setCustomServicePrice( $custom_service_price )
|
476 |
+
->setStartDate( $skip_date ? null : $start_date )
|
477 |
+
->setEndDate( $skip_date ? null : $end_date )
|
478 |
+
->setInternalNote( $internal_note )
|
479 |
+
->setExtrasDuration( $max_extras_duration );
|
480 |
+
|
481 |
+
if ( $appointment->save() !== false ) {
|
482 |
+
// Save customer appointments.
|
483 |
+
$ca_status_changed = $appointment->saveCustomerAppointments( $customers );
|
484 |
+
|
485 |
+
if ( $appointment->getSeriesId() ) {
|
486 |
+
foreach ( $customers as $customer ) {
|
487 |
+
if ( $customer['payment_create'] === true ) {
|
488 |
+
Proxy\RecurringAppointments::createBackendPayment( Lib\Entities\Series::find( $appointment->getSeriesId() ), $customer );
|
489 |
+
}
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
// Google Calendar.
|
494 |
+
Lib\Proxy\Pro::syncGoogleCalendarEvent( $appointment );
|
495 |
+
// Waiting list.
|
496 |
+
Lib\Proxy\WaitingList::handleParticipantsChange( $appointment );
|
497 |
+
|
498 |
+
// Send notifications.
|
499 |
+
if ( $notification == 'changed_status' ) {
|
500 |
+
foreach ( $ca_status_changed as $ca ) {
|
501 |
+
Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
|
502 |
+
}
|
503 |
+
} elseif ( $notification == 'all' ) {
|
504 |
+
$ca_list = $appointment->getCustomerAppointments( true );
|
505 |
+
foreach ( $ca_status_changed as $ca ) {
|
506 |
+
// The value "just_created" was initialized for the objects of this array
|
507 |
+
Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
|
508 |
+
unset( $ca_list[ $ca->getId() ] );
|
509 |
+
}
|
510 |
+
foreach ( $ca_list as $ca ) {
|
511 |
+
Lib\Notifications\Sender::sendSingle( DataHolders\Simple::create( $ca )->setAppointment( $appointment ) );
|
512 |
+
}
|
513 |
+
}
|
514 |
+
|
515 |
+
$response['success'] = true;
|
516 |
+
$response['data'] = self::_getAppointmentForFC( $staff_id, $appointment->getId() );
|
517 |
+
} else {
|
518 |
+
$response['errors'] = array( 'db' => __( 'Could not save appointment in database.', 'bookly' ) );
|
519 |
+
}
|
520 |
+
}
|
521 |
+
}
|
522 |
+
update_user_meta( get_current_user_id(), 'bookly_appointment_form_send_notifications', $notification );
|
523 |
+
|
524 |
+
wp_send_json( $response );
|
525 |
+
}
|
526 |
+
|
527 |
+
/**
|
528 |
+
* Check whether appointment settings produce errors.
|
529 |
+
*/
|
530 |
+
public static function checkAppointmentErrors()
|
531 |
+
{
|
532 |
+
$start_date = self::parameter( 'start_date' );
|
533 |
+
$end_date = self::parameter( 'end_date' );
|
534 |
+
$staff_id = (int) self::parameter( 'staff_id' );
|
535 |
+
$service_id = (int) self::parameter( 'service_id' );
|
536 |
+
$appointment_id = (int) self::parameter( 'appointment_id' );
|
537 |
+
$appointment_duration = strtotime( $end_date ) - strtotime( $start_date );
|
538 |
+
$customers = json_decode( self::parameter( 'customers', '[]' ), true );
|
539 |
+
$service = Lib\Entities\Service::find( $service_id );
|
540 |
+
$service_duration = $service ? $service->getDuration() : 0;
|
541 |
+
|
542 |
+
$result = array(
|
543 |
+
'date_interval_not_available' => false,
|
544 |
+
'date_interval_warning' => false,
|
545 |
+
'interval_not_in_staff_schedule' => false,
|
546 |
+
'interval_not_in_service_schedule' => false,
|
547 |
+
'customers_appointments_limit' => array(),
|
548 |
+
);
|
549 |
+
|
550 |
+
$max_extras_duration = 0;
|
551 |
+
foreach ( $customers as $customer ) {
|
552 |
+
if ( $customer['status'] == Lib\Entities\CustomerAppointment::STATUS_PENDING ||
|
553 |
+
$customer['status'] == Lib\Entities\CustomerAppointment::STATUS_APPROVED
|
554 |
+
) {
|
555 |
+
$extras_duration = Lib\Proxy\ServiceExtras::getTotalDuration( $customer['extras'] );
|
556 |
+
if ( $extras_duration > $max_extras_duration ) {
|
557 |
+
$max_extras_duration = $extras_duration;
|
558 |
+
}
|
559 |
+
}
|
560 |
+
}
|
561 |
+
if ( $start_date && $end_date ) {
|
562 |
+
$total_end_date = $end_date;
|
563 |
+
if ( $max_extras_duration > 0 ) {
|
564 |
+
$total_end_date = date_create( $end_date )->modify( '+' . $max_extras_duration . ' sec' )->format( 'Y-m-d H:i:s' );
|
565 |
+
}
|
566 |
+
if ( ! self::_dateIntervalIsAvailableForAppointment( $start_date, $total_end_date, $staff_id, $appointment_id ) ) {
|
567 |
+
$result['date_interval_not_available'] = true;
|
568 |
+
}
|
569 |
+
|
570 |
+
// Check if selected interval fit into staff schedule.
|
571 |
+
$interval_valid = true;
|
572 |
+
if ( $staff_id && $start_date ) {
|
573 |
+
$staff = Lib\Entities\Staff::find( $staff_id );
|
574 |
+
if ( $service_duration >= DAY_IN_SECONDS ) {
|
575 |
+
// For services with duration 24+ hours check holidays and days off
|
576 |
+
for ( $day = 0; $day < $service_duration / DAY_IN_SECONDS; $day ++ ) {
|
577 |
+
$work_date = date_create( $start_date )->modify( sprintf( '%s days', $day ) );
|
578 |
+
$week_day = $work_date->format( 'w' ) + 1;
|
579 |
+
// Check staff schedule for days off
|
580 |
+
if ( $staff->isOnHoliday( $work_date ) ||
|
581 |
+
! Lib\Entities\StaffScheduleItem::query()
|
582 |
+
->select( 'id' )
|
583 |
+
->where( 'staff_id', $staff_id )
|
584 |
+
->where( 'day_index', $week_day )
|
585 |
+
->whereNot( 'start_time', null )
|
586 |
+
->fetchRow()
|
587 |
+
) {
|
588 |
+
$interval_valid = false;
|
589 |
+
break;
|
590 |
+
}
|
591 |
+
}
|
592 |
+
} else {
|
593 |
+
// Check day before and current day to get night schedule from previous day.
|
594 |
+
$interval_valid = false;
|
595 |
+
for ( $day = 0; $day <= 1; $day ++ ) {
|
596 |
+
$day_start_date = date_create( $start_date )->modify( sprintf( '%s days', $day - 1 ) );
|
597 |
+
$day_end_date = date_create( $end_date )->modify( sprintf( '%s days', $day - 1 ) );
|
598 |
+
if ( ! $staff->isOnHoliday( $day_start_date ) ) {
|
599 |
+
$day_start_hour = ( 1 - $day ) * 24 + $day_start_date->format( 'G' );
|
600 |
+
$day_end_hour = ( 1 - $day ) * 24 + $day_end_date->format( 'G' );
|
601 |
+
$day_start_time = sprintf( '%02d:%02d:00', $day_start_hour, $day_start_date->format( 'i' ) );
|
602 |
+
$day_end_time = sprintf( '%02d:%02d:00', $day_end_hour >= $day_start_hour ? $day_end_hour : $day_end_hour + 24, $day_end_date->format( 'i' ) );
|
603 |
+
|
604 |
+
$special_days = (array) Lib\Proxy\SpecialDays::getSchedule( array( $staff_id ), $day_start_date, $day_start_date );
|
605 |
+
if ( ! empty( $special_days ) ) {
|
606 |
+
// Check if interval fit into special day schedule.
|
607 |
+
$special_day = current( $special_days );
|
608 |
+
if ( ( $special_day['start_time'] <= $day_start_time ) && ( $special_day['end_time'] >= $day_end_time ) ) {
|
609 |
+
if ( ! ( $special_day['break_start'] && ( $special_day['break_start'] < $day_end_time ) && ( $special_day['break_end'] > $day_start_time ) ) ) {
|
610 |
+
$interval_valid = true;
|
611 |
+
break;
|
612 |
+
}
|
613 |
+
}
|
614 |
+
} else {
|
615 |
+
// Check if interval fit into regular staff working schedule.
|
616 |
+
$week_day = $day_start_date->format( 'w' ) + 1;
|
617 |
+
$ssi = Lib\Entities\StaffScheduleItem::query()
|
618 |
+
->select( 'id' )
|
619 |
+
->where( 'staff_id', $staff_id )
|
620 |
+
->where( 'day_index', $week_day )
|
621 |
+
->whereNot( 'start_time', null )
|
622 |
+
->whereLte( 'start_time', $day_start_time )
|
623 |
+
->whereGte( 'end_time', $day_end_time )
|
624 |
+
->fetchRow();
|
625 |
+
if ( $ssi ) {
|
626 |
+
// Check if interval not intercept with breaks.
|
627 |
+
if ( Lib\Entities\ScheduleItemBreak::query()
|
628 |
+
->where( 'staff_schedule_item_id', $ssi['id'] )
|
629 |
+
->whereLt( 'start_time', $day_end_time )
|
630 |
+
->whereGt( 'end_time', $day_start_time )
|
631 |
+
->count() == 0
|
632 |
+
) {
|
633 |
+
$interval_valid = true;
|
634 |
+
break;
|
635 |
+
}
|
636 |
+
}
|
637 |
+
}
|
638 |
+
}
|
639 |
+
}
|
640 |
+
}
|
641 |
+
}
|
642 |
+
if ( ! $interval_valid ) {
|
643 |
+
$result['interval_not_in_staff_schedule'] = true;
|
644 |
+
}
|
645 |
+
if ( $service ) {
|
646 |
+
if ( $service_duration >= DAY_IN_SECONDS ) {
|
647 |
+
// For services with duration 24+ hours check days off
|
648 |
+
$service_schedule = (array) Lib\Proxy\ServiceSchedule::getSchedule( $service_id );
|
649 |
+
$interval_valid = true;
|
650 |
+
|
651 |
+
// Check service schedule and service special days
|
652 |
+
for ( $day = 0; $day < $service_duration / DAY_IN_SECONDS; $day ++ ) {
|
653 |
+
$work_date = date_create( $start_date )->modify( sprintf( '%s days', $day ) );
|
654 |
+
$week_day = $work_date->format( 'w' ) + 1;
|
655 |
+
// Check service schedule for days off
|
656 |
+
$service_schedule_valid = true;
|
657 |
+
if ( Lib\Config::serviceScheduleActive() ) {
|
658 |
+
$service_schedule_valid = false;
|
659 |
+
foreach ( $service_schedule as $day_schedule ) {
|
660 |
+
if ( $day_schedule['day_index'] == $week_day && $day_schedule['start_time'] ) {
|
661 |
+
$service_schedule_valid = true;
|
662 |
+
break;
|
663 |
+
}
|
664 |
+
}
|
665 |
+
}
|
666 |
+
if ( ! $service_schedule_valid ) {
|
667 |
+
$interval_valid = false;
|
668 |
+
break;
|
669 |
+
}
|
670 |
+
// Check service special days for days off
|
671 |
+
$service_special_days_valid = true;
|
672 |
+
if ( Lib\Config::specialDaysActive() ) {
|
673 |
+
$special_days = (array) Lib\Proxy\SpecialDays::getServiceSchedule( $service_id, $work_date, $work_date );
|
674 |
+
if ( ! empty( $special_days ) ) {
|
675 |
+
$service_special_days_valid = false;
|
676 |
+
$schedule = current( $special_days );
|
677 |
+
if ( $schedule['start_time'] ) {
|
678 |
+
$service_special_days_valid = true;
|
679 |
+
}
|
680 |
+
}
|
681 |
+
}
|
682 |
+
if ( ! $service_special_days_valid ) {
|
683 |
+
$interval_valid = false;
|
684 |
+
break;
|
685 |
+
}
|
686 |
+
}
|
687 |
+
if ( ! $interval_valid ) {
|
688 |
+
$result['interval_not_in_service_schedule'] = true;
|
689 |
+
}
|
690 |
+
// Check staff schedule and staff special days
|
691 |
+
$interval_valid = true;
|
692 |
+
for ( $day = 0; $day < $service_duration / DAY_IN_SECONDS; $day ++ ) {
|
693 |
+
$work_date = date_create( $start_date )->modify( sprintf( '%s days', $day ) );
|
694 |
+
$week_day = $work_date->format( 'w' ) + 1;
|
695 |
+
if ( Lib\Entities\StaffScheduleItem::query()
|
696 |
+
->where( 'staff_id', $staff_id )
|
697 |
+
->where( 'day_index', $week_day )
|
698 |
+
->whereNot( 'start_time', null )
|
699 |
+
->count() == 0
|
700 |
+
) {
|
701 |
+
$interval_valid = false;
|
702 |
+
break;
|
703 |
+
}
|
704 |
+
}
|
705 |
+
if ( ! $interval_valid ) {
|
706 |
+
$result['interval_not_in_staff_schedule'] = true;
|
707 |
+
}
|
708 |
+
} else {
|
709 |
+
// Check if selected interval fit into service schedule.
|
710 |
+
$interval_valid = false;
|
711 |
+
// Check day before and current day to get night schedule from previous day.
|
712 |
+
for ( $day = 0; $day <= 1; $day ++ ) {
|
713 |
+
$day_start_date = date_create( $start_date )->modify( sprintf( '%s days', $day - 1 ) );
|
714 |
+
$day_end_date = date_create( $end_date )->modify( sprintf( '%s days', $day - 1 ) );
|
715 |
+
|
716 |
+
$day_start_hour = ( 1 - $day ) * 24 + $day_start_date->format( 'G' );
|
717 |
+
$day_end_hour = ( 1 - $day ) * 24 + $day_end_date->format( 'G' );
|
718 |
+
$day_start_time = sprintf( '%02d:%02d:00', $day_start_hour, $day_start_date->format( 'i' ) );
|
719 |
+
$day_end_time = sprintf( '%02d:%02d:00', $day_end_hour >= $day_start_hour ? $day_end_hour : $day_end_hour + 24, $day_end_date->format( 'i' ) );
|
720 |
+
|
721 |
+
$special_days = (array) Lib\Proxy\SpecialDays::getServiceSchedule( $service_id, $day_start_date, $day_start_date );
|
722 |
+
if ( ! empty( $special_days ) ) {
|
723 |
+
// Check if interval fit into special day schedule.
|
724 |
+
$special_day = current( $special_days );
|
725 |
+
if ( ( $special_day['start_time'] <= $day_start_time ) && ( $special_day['end_time'] >= $day_end_time ) ) {
|
726 |
+
if ( ! ( $special_day['break_start'] && ( $special_day['break_start'] < $day_end_time ) && ( $special_day['break_end'] > $day_start_time ) ) ) {
|
727 |
+
$interval_valid = true;
|
728 |
+
break;
|
729 |
+
}
|
730 |
+
}
|
731 |
+
} else {
|
732 |
+
// Check if interval fit into service working schedule.
|
733 |
+
$schedule = (array) Lib\Proxy\ServiceSchedule::getSchedule( $service_id );
|
734 |
+
if ( ! empty ( $schedule ) ) {
|
735 |
+
$week_day = $day_start_date->format( 'w' ) + 1;
|
736 |
+
foreach ( $schedule as $schedule_day ) {
|
737 |
+
if ( $schedule_day['day_index'] == $week_day ) {
|
738 |
+
if ( ( $schedule_day['start_time'] <= $day_start_time ) && ( $schedule_day['end_time'] >= $day_end_time ) ) {
|
739 |
+
$interval_valid = true;
|
740 |
+
if ( $schedule_day['break_start'] && ( $schedule_day['break_start'] < $day_end_time ) && ( $schedule_day['break_end'] > $day_start_time ) ) {
|
741 |
+
$interval_valid = false;
|
742 |
+
break;
|
743 |
+
}
|
744 |
+
}
|
745 |
+
}
|
746 |
+
}
|
747 |
+
} else {
|
748 |
+
$interval_valid = true;
|
749 |
+
break;
|
750 |
+
}
|
751 |
+
}
|
752 |
+
}
|
753 |
+
if ( ! $interval_valid ) {
|
754 |
+
$result['interval_not_in_service_schedule'] = true;
|
755 |
+
}
|
756 |
+
// Service duration interval is not equal to.
|
757 |
+
$result['date_interval_warning'] = ! ( $appointment_duration >= $service->getMinDuration() && $appointment_duration <= $service->getMaxDuration() && ( $service_duration == 0 || $appointment_duration % $service_duration == 0 ) );
|
758 |
+
}
|
759 |
+
|
760 |
+
// Check customers for appointments limit
|
761 |
+
foreach ( $customers as $index => $customer ) {
|
762 |
+
if ( $service->appointmentsLimitReached( $customer['id'], array( $start_date ) ) ) {
|
763 |
+
$customer_error = Lib\Entities\Customer::find( $customer['id'] );
|
764 |
+
$result['customers_appointments_limit'][] = sprintf( __( '%s has reached the limit of bookings for this service', 'bookly' ), $customer_error->getFullName() );
|
765 |
+
}
|
766 |
+
}
|
767 |
+
}
|
768 |
+
}
|
769 |
+
|
770 |
+
wp_send_json( $result );
|
771 |
+
}
|
772 |
+
|
773 |
+
/**
|
774 |
+
* Get appointment for FullCalendar.
|
775 |
+
*
|
776 |
+
* @param integer $staff_id
|
777 |
+
* @param int $appointment_id
|
778 |
+
* @return array
|
779 |
+
*/
|
780 |
+
private static function _getAppointmentForFC( $staff_id, $appointment_id )
|
781 |
+
{
|
782 |
+
$query = Lib\Entities\Appointment::query( 'a' )
|
783 |
+
->where( 'a.id', $appointment_id );
|
784 |
+
|
785 |
+
$appointments = Calendar\Page::buildAppointmentsForFC( $staff_id, $query );
|
786 |
+
|
787 |
+
return $appointments[0];
|
788 |
+
}
|
789 |
+
|
790 |
+
/**
|
791 |
+
* Check whether interval is available for given appointment.
|
792 |
+
*
|
793 |
+
* @param $start_date
|
794 |
+
* @param $end_date
|
795 |
+
* @param $staff_id
|
796 |
+
* @param $appointment_id
|
797 |
+
* @return bool
|
798 |
+
*/
|
799 |
+
private static function _dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id )
|
800 |
+
{
|
801 |
+
return Lib\Entities\Appointment::query( 'a' )
|
802 |
+
->whereNot( 'a.id', $appointment_id )
|
803 |
+
->where( 'a.staff_id', $staff_id )
|
804 |
+
->whereLt( 'a.start_date', $end_date )
|
805 |
+
->whereGt( 'a.end_date', $start_date )
|
806 |
+
->count() == 0;
|
807 |
+
}
|
808 |
+
}
|
backend/{modules/calendar/Components.php → components/dialogs/appointment/edit/Dialog.php}
RENAMED
@@ -1,28 +1,27 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
|
6 |
/**
|
7 |
-
* Class
|
8 |
-
* @package
|
9 |
*/
|
10 |
-
class
|
11 |
{
|
12 |
/**
|
13 |
-
* Render appointment dialog.
|
14 |
-
* @throws \Exception
|
15 |
*/
|
16 |
-
public function
|
17 |
{
|
18 |
global $wp_locale;
|
19 |
|
20 |
-
|
21 |
-
'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', 'css/select2.min.css' ),
|
22 |
'frontend' => array( 'css/ladda.min.css', ),
|
23 |
) );
|
24 |
|
25 |
-
|
26 |
'backend' => array(
|
27 |
'js/angular.min.js' => array( 'jquery' ),
|
28 |
'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js' ),
|
@@ -35,11 +34,11 @@ class Components extends Lib\Base\Components
|
|
35 |
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
),
|
37 |
'module' => array(
|
38 |
-
'js/ng-
|
39 |
)
|
40 |
) );
|
41 |
|
42 |
-
wp_localize_script( 'bookly-ng-
|
43 |
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
44 |
'dateOptions' => array(
|
45 |
'dateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
@@ -58,28 +57,8 @@ class Components extends Lib\Base\Components
|
|
58 |
),
|
59 |
) );
|
60 |
|
61 |
-
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Render delete appointment dialog
|
66 |
-
*/
|
67 |
-
public function renderDeleteDialog()
|
68 |
-
{
|
69 |
-
$this->enqueueStyles( array(
|
70 |
-
'frontend' => array( 'css/ladda.min.css', ),
|
71 |
-
) );
|
72 |
|
73 |
-
|
74 |
-
'frontend' => array(
|
75 |
-
'js/spin.min.js' => array( 'jquery' ),
|
76 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
77 |
-
),
|
78 |
-
'module' => array(
|
79 |
-
'js/delete_dialog.js' => array( 'jquery' ),
|
80 |
-
),
|
81 |
-
) );
|
82 |
-
$this->render( '_delete_dialog' );
|
83 |
}
|
84 |
-
|
85 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
|
6 |
/**
|
7 |
+
* Class Edit
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit
|
9 |
*/
|
10 |
+
class Dialog extends Lib\Base\Component
|
11 |
{
|
12 |
/**
|
13 |
+
* Render create/edit appointment dialog.
|
|
|
14 |
*/
|
15 |
+
public static function render()
|
16 |
{
|
17 |
global $wp_locale;
|
18 |
|
19 |
+
self::enqueueStyles( array(
|
20 |
+
'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', 'css/select2.min.css', 'css/fontawesome-all.min.css' ),
|
21 |
'frontend' => array( 'css/ladda.min.css', ),
|
22 |
) );
|
23 |
|
24 |
+
self::enqueueScripts( array(
|
25 |
'backend' => array(
|
26 |
'js/angular.min.js' => array( 'jquery' ),
|
27 |
'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js' ),
|
34 |
'js/ladda.min.js' => array( 'jquery' ),
|
35 |
),
|
36 |
'module' => array(
|
37 |
+
'js/ng-appointment.js' => array( 'bookly-angular-ui-date-0.0.8.js', 'jquery-ui-datepicker' ),
|
38 |
)
|
39 |
) );
|
40 |
|
41 |
+
wp_localize_script( 'bookly-ng-appointment.js', 'BooklyL10nAppDialog', array(
|
42 |
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
43 |
'dateOptions' => array(
|
44 |
'dateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
57 |
),
|
58 |
) );
|
59 |
|
60 |
+
Proxy\Shared::enqueueAssets();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
+
self::renderTemplate( 'edit' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
}
|
|
|
64 |
}
|
backend/components/dialogs/appointment/edit/proxy/Pro.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy
|
9 |
+
*
|
10 |
+
* @method static array addCustomService( array $services ) Add custom service to given array of services.
|
11 |
+
* @method static void renderAttachPaymentButton() Render attach payment button.
|
12 |
+
* @method static void renderCustomServiceFields() Render custom service name and price fields.
|
13 |
+
*/
|
14 |
+
abstract class Pro extends Lib\Base\Proxy
|
15 |
+
{
|
16 |
+
|
17 |
+
}
|
backend/components/dialogs/appointment/edit/proxy/RecurringAppointments.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class RecurringAppointments
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy
|
9 |
+
*
|
10 |
+
* @method static void createBackendPayment( Lib\Entities\Series $series, array $customer ) Create payment for series.
|
11 |
+
* @method static void renderSchedule() Render schedule in edit appointment dialog.
|
12 |
+
* @method static void renderSubForm() Add Recurring sub form in edit appointment dialog.
|
13 |
+
*/
|
14 |
+
abstract class RecurringAppointments extends Lib\Base\Proxy
|
15 |
+
{
|
16 |
+
|
17 |
+
}
|
backend/components/dialogs/appointment/edit/proxy/Shared.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareDataForPackage( array $result )
|
11 |
+
* @method static void renderAppointmentDialogCustomersList() Render content in AppointmentForm for customers.
|
12 |
+
* @method static void renderAppointmentDialogFooter() Render buttons in appointments dialog footer.
|
13 |
+
* @method static void renderComponents()
|
14 |
+
* @method static void enqueueAssets()
|
15 |
+
*/
|
16 |
+
abstract class Shared extends Lib\Base\Proxy
|
17 |
+
{
|
18 |
+
|
19 |
+
}
|
backend/components/dialogs/appointment/edit/proxy/Tasks.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class RecurringAppointments
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSkipDate()
|
11 |
+
*/
|
12 |
+
abstract class Tasks extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/{modules/calendar/resources/js/ng-appointment_dialog.js → components/dialogs/appointment/edit/resources/js/ng-appointment.js}
RENAMED
@@ -5,33 +5,35 @@
|
|
5 |
/**
|
6 |
* DataSource service.
|
7 |
*/
|
8 |
-
module.factory('dataSource', function($q
|
9 |
var ds = {
|
10 |
loaded : false,
|
11 |
data : {
|
12 |
-
staff
|
13 |
-
customers
|
14 |
-
start_time
|
15 |
-
end_time
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
20 |
}
|
21 |
},
|
22 |
form : {
|
23 |
-
screen
|
24 |
-
id
|
25 |
-
staff
|
26 |
-
staff_any
|
27 |
-
service
|
28 |
-
custom_service_name
|
29 |
-
custom_service_price
|
30 |
-
location
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
35 |
enabled : null,
|
36 |
repeat : null,
|
37 |
daily : { every : null },
|
@@ -67,6 +69,7 @@
|
|
67 |
if (data.staff.length) {
|
68 |
ds.form.staff = data.staff[0];
|
69 |
}
|
|
|
70 |
ds.form.start_time = data.start_time[0];
|
71 |
ds.form.end_time = data.end_time[1];
|
72 |
deferred.resolve();
|
@@ -117,13 +120,12 @@
|
|
117 |
}
|
118 |
return result;
|
119 |
},
|
120 |
-
findTime : function(source,
|
121 |
var result = null,
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
jQuery.each(time, function(key, item) {
|
126 |
-
if (item.value >=
|
127 |
result = item;
|
128 |
return false;
|
129 |
}
|
@@ -144,55 +146,107 @@
|
|
144 |
ds.data.customers.forEach(function(customer) {
|
145 |
customer.custom_fields = [];
|
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() {
|
158 |
var result = [];
|
159 |
if (ds.form.start_time) {
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
}
|
166 |
-
|
167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
}
|
169 |
-
}
|
170 |
}
|
171 |
return result;
|
172 |
},
|
173 |
setEndTimeBasedOnService : function() {
|
174 |
-
|
175 |
-
|
176 |
if (d < 86400) {
|
177 |
-
|
178 |
-
for (; i < ds.data.end_time.length; ++i) {
|
179 |
-
d -= ds.data.time_interval;
|
180 |
-
if (d < 0) {
|
181 |
-
break;
|
182 |
-
}
|
183 |
-
}
|
184 |
-
ds.form.end_time = ds.data.end_time[i];
|
185 |
-
}
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
} else {
|
197 |
start_time = ds.form.start_time.value.split(':');
|
198 |
end_time = ds.form.end_time.value.split(':');
|
@@ -201,7 +255,6 @@
|
|
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')
|
@@ -234,6 +287,14 @@
|
|
234 |
});
|
235 |
|
236 |
return result;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
}
|
238 |
};
|
239 |
|
@@ -243,7 +304,7 @@
|
|
243 |
/**
|
244 |
* Controller for 'create/edit appointment' dialog form.
|
245 |
*/
|
246 |
-
module.controller('appointmentDialogCtrl', function($scope, $element, dataSource
|
247 |
// Set up initial data.
|
248 |
$scope.$calendar = null;
|
249 |
// Set up data source.
|
@@ -252,7 +313,7 @@
|
|
252 |
// Error messages.
|
253 |
$scope.errors = {};
|
254 |
// Callback to be called after editing appointment.
|
255 |
-
var callback
|
256 |
|
257 |
/**
|
258 |
* Prepare the form for new event.
|
@@ -267,6 +328,8 @@
|
|
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,
|
@@ -277,8 +340,10 @@
|
|
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,
|
@@ -307,6 +372,9 @@
|
|
307 |
$scope.prepareCustomFields();
|
308 |
$scope.dataSource.resetCustomers();
|
309 |
$scope.onRepeatChange();
|
|
|
|
|
|
|
310 |
};
|
311 |
|
312 |
/**
|
@@ -320,47 +388,61 @@
|
|
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
|
328 |
-
id
|
329 |
-
staff
|
330 |
-
staff_any
|
331 |
-
service
|
332 |
-
custom_service_name
|
333 |
-
custom_service_price
|
334 |
-
location
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
:
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
monthly : { on : 'day', day : '1', weekday : 'mon' },
|
347 |
-
until : start_date.clone().add(1, 'month').format('YYYY-MM-DD')
|
348 |
},
|
349 |
-
schedule
|
350 |
-
items
|
351 |
-
edit
|
352 |
-
page
|
353 |
-
another_time
|
354 |
},
|
355 |
-
customers
|
356 |
-
internal_note
|
357 |
-
series_id
|
358 |
-
expand_customers_list
|
359 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
|
361 |
$scope.prepareExtras();
|
362 |
$scope.prepareCustomFields();
|
363 |
$scope.dataSource.resetCustomers();
|
|
|
364 |
$scope.onRepeatChange();
|
365 |
|
366 |
var customers_ids = [];
|
@@ -379,11 +461,16 @@
|
|
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;
|
|
|
|
|
|
|
387 |
clone.compound_token = item.compound_token;
|
388 |
clone.compound_service = item.compound_service;
|
389 |
$scope.form.customers.push(clone);
|
@@ -414,12 +501,13 @@
|
|
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 |
-
|
|
|
423 |
});
|
424 |
});
|
425 |
|
@@ -451,9 +539,28 @@
|
|
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];
|
@@ -462,6 +569,7 @@
|
|
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() {
|
@@ -495,6 +603,10 @@
|
|
495 |
checkAppointmentErrors();
|
496 |
};
|
497 |
|
|
|
|
|
|
|
|
|
498 |
$scope.processForm = function() {
|
499 |
$scope.loading = true;
|
500 |
|
@@ -527,8 +639,13 @@
|
|
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(
|
@@ -542,6 +659,7 @@
|
|
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),
|
@@ -603,12 +721,16 @@
|
|
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
|
|
|
|
|
|
|
612 |
};
|
613 |
|
614 |
if (customer.email || customer.phone){
|
@@ -639,7 +761,7 @@
|
|
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 |
|
@@ -675,7 +797,7 @@
|
|
675 |
var max = $scope.form.service
|
676 |
? ($scope.form.service.id
|
677 |
? parseInt($scope.form.service.capacity_max) - $scope.dataSource.getTotalNumberOfNotCancelledPersons(customer)
|
678 |
-
:
|
679 |
: 1;
|
680 |
$number_of_persons.empty();
|
681 |
for (var i = 1; i <= max; ++i) {
|
@@ -688,12 +810,15 @@
|
|
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 |
});
|
|
|
|
|
697 |
};
|
698 |
|
699 |
$scope.prepareExtras = function () {
|
@@ -742,6 +867,7 @@
|
|
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')
|
@@ -778,6 +904,7 @@
|
|
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;
|
@@ -794,15 +921,59 @@
|
|
794 |
* Payment Details *
|
795 |
**************************************************************************************************************/
|
796 |
|
797 |
-
$scope.
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
|
|
|
|
|
|
|
|
|
|
803 |
});
|
804 |
};
|
805 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
806 |
/**************************************************************************************************************
|
807 |
* Package Schedule *
|
808 |
**************************************************************************************************************/
|
@@ -817,21 +988,22 @@
|
|
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(
|
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(
|
835 |
return true;
|
836 |
}
|
837 |
break;
|
@@ -843,7 +1015,7 @@
|
|
843 |
}
|
844 |
break;
|
845 |
case 'last':
|
846 |
-
if (
|
847 |
return true;
|
848 |
}
|
849 |
break;
|
@@ -852,7 +1024,7 @@
|
|
852 |
weeks = ['first', 'second', 'third', 'fourth'],
|
853 |
week_number = weeks.indexOf($scope.form.repeat.monthly.on);
|
854 |
|
855 |
-
if (
|
856 |
return true;
|
857 |
}
|
858 |
}
|
@@ -862,13 +1034,13 @@
|
|
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));
|
@@ -920,7 +1092,8 @@
|
|
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) {
|
@@ -1011,13 +1184,15 @@
|
|
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',
|
1015 |
until : item.date,
|
1016 |
repeat : 'daily',
|
1017 |
params : {every: 1},
|
1018 |
with_options : 1,
|
1019 |
exclude : exclude,
|
1020 |
-
extras : extras
|
|
|
1021 |
},
|
1022 |
function (response) {
|
1023 |
$scope.$apply(function($scope) {
|
@@ -1025,13 +1200,18 @@
|
|
1025 |
item.options = response.data[0].options;
|
1026 |
var found = false;
|
1027 |
jQuery.each(item.options, function (key, option) {
|
1028 |
-
if (
|
1029 |
found = true;
|
1030 |
return false;
|
1031 |
}
|
1032 |
});
|
1033 |
if (!found) {
|
1034 |
-
item.
|
|
|
|
|
|
|
|
|
|
|
1035 |
}
|
1036 |
} else {
|
1037 |
item.options = [];
|
5 |
/**
|
6 |
* DataSource service.
|
7 |
*/
|
8 |
+
module.factory('dataSource', function($q) {
|
9 |
var ds = {
|
10 |
loaded : false,
|
11 |
data : {
|
12 |
+
staff : [],
|
13 |
+
customers : [],
|
14 |
+
start_time : [],
|
15 |
+
end_time : [],
|
16 |
+
app_start_time : null,
|
17 |
+
app_end_time : null,
|
18 |
+
time_interval : 900,
|
19 |
+
status : {
|
20 |
+
items: []
|
21 |
}
|
22 |
},
|
23 |
form : {
|
24 |
+
screen : null,
|
25 |
+
id : null,
|
26 |
+
staff : null,
|
27 |
+
staff_any : null,
|
28 |
+
service : null,
|
29 |
+
custom_service_name : null,
|
30 |
+
custom_service_price: null,
|
31 |
+
location : null,
|
32 |
+
skip_date : null,
|
33 |
+
date : null,
|
34 |
+
start_time : null,
|
35 |
+
end_time : null,
|
36 |
+
repeat : {
|
37 |
enabled : null,
|
38 |
repeat : null,
|
39 |
daily : { every : null },
|
69 |
if (data.staff.length) {
|
70 |
ds.form.staff = data.staff[0];
|
71 |
}
|
72 |
+
|
73 |
ds.form.start_time = data.start_time[0];
|
74 |
ds.form.end_time = data.end_time[1];
|
75 |
deferred.resolve();
|
120 |
}
|
121 |
return result;
|
122 |
},
|
123 |
+
findTime : function(source, value) {
|
124 |
var result = null,
|
125 |
+
time = source == 'start' ? ds.getDataForStartTime() : ds.form.end_time_data,
|
126 |
+
search_value = parseInt(value);
|
|
|
127 |
jQuery.each(time, function(key, item) {
|
128 |
+
if (parseInt(item.value) >= search_value) {
|
129 |
result = item;
|
130 |
return false;
|
131 |
}
|
146 |
ds.data.customers.forEach(function(customer) {
|
147 |
customer.custom_fields = [];
|
148 |
customer.extras = [];
|
|
|
149 |
customer.number_of_persons = 1;
|
150 |
customer.notes = null;
|
151 |
customer.compound_token = null;
|
152 |
customer.payment_id = null;
|
153 |
customer.payment_type = null;
|
154 |
customer.payment_title = null;
|
155 |
+
customer.payment_create = false;
|
156 |
+
customer.payment_price = null;
|
157 |
+
customer.payment_tax = null;
|
158 |
customer.package_id = null;
|
159 |
+
customer.ca_id = null;
|
160 |
});
|
161 |
},
|
162 |
+
getDataForStartTime : function() {
|
163 |
+
var result = ds.data.start_time.slice();
|
164 |
+
if (
|
165 |
+
ds.data.app_start_time &&
|
166 |
+
result.every(function (item) {return item.value !== ds.data.app_start_time.value;})
|
167 |
+
) {
|
168 |
+
result.push(ds.data.app_start_time);
|
169 |
+
result.sort(function (a, b) {
|
170 |
+
return a.value < b.value ? -1 : (a.value > b.value ? 1 : 0);
|
171 |
+
});
|
172 |
+
}
|
173 |
+
return result;
|
174 |
+
},
|
175 |
getDataForEndTime : function() {
|
176 |
var result = [];
|
177 |
if (ds.form.start_time) {
|
178 |
+
if (ds.form.service && parseInt(ds.form.service.units_max) > 1) {
|
179 |
+
var units_min = parseInt(ds.form.service.units_min),
|
180 |
+
units_max = parseInt(ds.form.service.units_max),
|
181 |
+
start_time = moment(ds.form.start_time.value, 'HH:mm');
|
182 |
+
for (var units = units_min; units <= units_max; units++) {
|
183 |
+
var end_time = moment(start_time).add(units * ds.form.service.duration, 'seconds'),
|
184 |
+
end_hour = parseInt(moment(end_time).format('HH')) + Math.floor((end_time.diff(start_time)) / 3600 / 24000) * 24;
|
185 |
+
jQuery.each(ds.data.end_time, function (key, item) {
|
186 |
+
if (item.value == (end_hour < 10 ? '0' + end_hour : end_hour) + ':' + moment(end_time).format('mm')) {
|
187 |
+
unit_item = jQuery.extend({}, item);
|
188 |
+
unit_item.title = item.title + ' (' + units +')';
|
189 |
+
result.push(unit_item);
|
190 |
+
}
|
191 |
+
});
|
192 |
}
|
193 |
+
} else {
|
194 |
+
var start_time = ds.form.start_time.value.split(':'),
|
195 |
+
end = (24 + parseInt(start_time[0])) + ':' + start_time[1];
|
196 |
+
jQuery.each(ds.data.end_time, function (key, item) {
|
197 |
+
if (item.value > end) {
|
198 |
+
return false;
|
199 |
+
}
|
200 |
+
if (item.value > ds.form.start_time.value) {
|
201 |
+
result.push(item);
|
202 |
+
}
|
203 |
+
});
|
204 |
+
if (
|
205 |
+
ds.data.app_end_time &&
|
206 |
+
ds.data.app_end_time.value > ds.form.start_time.value &&
|
207 |
+
result.every(function (item) {
|
208 |
+
return item.value !== ds.data.app_end_time.value;
|
209 |
+
})
|
210 |
+
) {
|
211 |
+
result.push(ds.data.app_end_time);
|
212 |
+
result.sort(function (a, b) {
|
213 |
+
return a.value < b.value ? -1 : (a.value > b.value ? 1 : 0);
|
214 |
+
});
|
215 |
}
|
216 |
+
}
|
217 |
}
|
218 |
return result;
|
219 |
},
|
220 |
setEndTimeBasedOnService : function() {
|
221 |
+
ds.form.end_time_data = ds.getDataForEndTime();
|
222 |
+
var d = ds.form.service ? ds.form.service.duration * ds.form.service.units_min : ds.data.time_interval;
|
223 |
if (d < 86400) {
|
224 |
+
ds.form.end_time = ds.findTime('end', moment(ds.form.start_time.value, 'HH:mm').add(d, 'seconds').format('HH:mm'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
}
|
226 |
},
|
227 |
getStartAndEndDates : function() {
|
228 |
+
if (ds.form.skip_date) {
|
229 |
+
return {
|
230 |
+
start_date: null,
|
231 |
+
end_date : null
|
232 |
+
}
|
233 |
+
}
|
234 |
var start_date = moment(ds.form.date.getTime()),
|
235 |
end_date = moment(ds.form.date.getTime()),
|
236 |
start_time = [0,0],
|
237 |
end_time = [0,0]
|
238 |
;
|
239 |
if (ds.form.service && ds.form.service.duration >= 86400) {
|
240 |
+
if (ds.form.end_time) {
|
241 |
+
var _start_time = ds.form.start_time.value.split(':');
|
242 |
+
var _end_time = ds.form.end_time.value.split(':');
|
243 |
+
var duration = Math.max(ds.form.service.duration, 60 * (_end_time[0] * 60 + parseInt(_end_time[1]) - _start_time[0] * 60 - parseInt(_start_time[1])));
|
244 |
+
end_date.add(duration, 'seconds');
|
245 |
+
} else if (ds.form.service && ds.form.service.units_max > 1) {
|
246 |
+
end_date.add(ds.form.service.duration * ds.form.service.units_min, 'seconds');
|
247 |
+
} else {
|
248 |
+
end_date.add(ds.form.service.duration, 'seconds');
|
249 |
+
}
|
250 |
} else {
|
251 |
start_time = ds.form.start_time.value.split(':');
|
252 |
end_time = ds.form.end_time.value.split(':');
|
255 |
start_date.minutes(start_time[1]);
|
256 |
end_date.hours(end_time[0]);
|
257 |
end_date.minutes(end_time[1]);
|
|
|
258 |
return {
|
259 |
start_date : start_date.format('YYYY-MM-DD HH:mm:00'),
|
260 |
end_date : end_date.format('YYYY-MM-DD HH:mm:00')
|
287 |
});
|
288 |
|
289 |
return result;
|
290 |
+
},
|
291 |
+
getServiceDuration: function () {
|
292 |
+
var dates = ds.getStartAndEndDates(),
|
293 |
+
start_date = moment(dates.start_date),
|
294 |
+
end_date = moment(dates.end_date)
|
295 |
+
;
|
296 |
+
|
297 |
+
return end_date.diff(start_date, 'seconds');
|
298 |
}
|
299 |
};
|
300 |
|
304 |
/**
|
305 |
* Controller for 'create/edit appointment' dialog form.
|
306 |
*/
|
307 |
+
module.controller('appointmentDialogCtrl', function($scope, $element, dataSource) {
|
308 |
// Set up initial data.
|
309 |
$scope.$calendar = null;
|
310 |
// Set up data source.
|
313 |
// Error messages.
|
314 |
$scope.errors = {};
|
315 |
// Callback to be called after editing appointment.
|
316 |
+
var callback = null;
|
317 |
|
318 |
/**
|
319 |
* Prepare the form for new event.
|
328 |
service = staff && staff.services.length == 2 ? staff.services[1] : null,
|
329 |
location = staff && staff.locations.length == 1 ? staff.locations[0] : null
|
330 |
;
|
331 |
+
$scope.dataSource.data.app_start_time = null;
|
332 |
+
$scope.dataSource.data.app_end_time = null;
|
333 |
jQuery.extend($scope.form, {
|
334 |
screen : 'main',
|
335 |
id : null,
|
340 |
custom_service_price : 0,
|
341 |
location : location,
|
342 |
date : start_date.clone().local().toDate(),
|
343 |
+
skip_date : null,
|
344 |
start_time : dataSource.findTime('start', start_date.format('HH:mm')),
|
345 |
end_time : null,
|
346 |
+
end_time_data : [],
|
347 |
series_id : null,
|
348 |
repeat : {
|
349 |
enabled : 0,
|
372 |
$scope.prepareCustomFields();
|
373 |
$scope.dataSource.resetCustomers();
|
374 |
$scope.onRepeatChange();
|
375 |
+
if (staff) {
|
376 |
+
$scope.onStaffChange();
|
377 |
+
}
|
378 |
};
|
379 |
|
380 |
/**
|
388 |
function(response) {
|
389 |
$scope.$apply(function($scope) {
|
390 |
if (response.success) {
|
391 |
+
var start_date = response.data.start_date === null ? null : moment(response.data.start_date),
|
392 |
+
end_date = response.data.start_date === null ? null : moment(response.data.end_date),
|
393 |
staff = $scope.dataSource.findStaff(response.data.staff_id);
|
394 |
+
$scope.dataSource.data.app_start_time = response.data.start_time;
|
395 |
+
$scope.dataSource.data.app_end_time = response.data.end_time;
|
396 |
jQuery.extend($scope.form, {
|
397 |
+
screen : 'main',
|
398 |
+
id : appointment_id,
|
399 |
+
staff : staff,
|
400 |
+
staff_any : response.data.staff_any ? staff : null,
|
401 |
+
service : $scope.dataSource.findService(response.data.staff_id, response.data.service_id),
|
402 |
+
custom_service_name : response.data.custom_service_name,
|
403 |
+
custom_service_price : response.data.custom_service_price,
|
404 |
+
location : $scope.dataSource.findLocation(response.data.staff_id, response.data.location_id),
|
405 |
+
skip_date : start_date === null ? 1 : 0,
|
406 |
+
end_time : null,
|
407 |
+
end_time_data : [],
|
408 |
+
repeat : {
|
409 |
+
enabled : 0,
|
410 |
+
repeat : 'daily',
|
411 |
+
daily : {every: 1},
|
412 |
+
weekly : {on: []},
|
413 |
+
biweekly: {on: []},
|
414 |
+
monthly : {on: 'day', day: '1', weekday: 'mon'},
|
415 |
+
until : start_date === null ? moment().add(1, 'month').format('YYYY-MM-DD') : start_date.clone().add(1, 'month').format('YYYY-MM-DD')
|
|
|
|
|
416 |
},
|
417 |
+
schedule : {
|
418 |
+
items : [],
|
419 |
+
edit : 0,
|
420 |
+
page : 0,
|
421 |
+
another_time: []
|
422 |
},
|
423 |
+
customers : [],
|
424 |
+
internal_note : response.data.internal_note,
|
425 |
+
series_id : response.data.series_id,
|
426 |
+
expand_customers_list: false
|
427 |
});
|
428 |
+
$scope.form.end_time_data = $scope.dataSource.getDataForEndTime();
|
429 |
+
if (start_date !== null) {
|
430 |
+
$scope.form.date = start_date.clone().local().toDate();
|
431 |
+
$scope.form.start_time = $scope.dataSource.findTime('start', start_date.format('HH:mm'));
|
432 |
+
$scope.dataSource.setEndTimeBasedOnService();
|
433 |
+
$scope.form.end_time = start_date.format('YYYY-MM-DD') == end_date.format('YYYY-MM-DD')
|
434 |
+
? $scope.dataSource.findTime('end', end_date.format('HH:mm'))
|
435 |
+
: $scope.dataSource.findTime('end', (Math.floor((end_date - start_date) / 3600000) + start_date.hour()) + end_date.format(':mm'));
|
436 |
+
} else {
|
437 |
+
$scope.form.date = moment().local().toDate();
|
438 |
+
$scope.form.start_time = $scope.dataSource.findTime('start', moment().format('HH:mm'));
|
439 |
+
$scope.dataSource.setEndTimeBasedOnService();
|
440 |
+
}
|
441 |
|
442 |
$scope.prepareExtras();
|
443 |
$scope.prepareCustomFields();
|
444 |
$scope.dataSource.resetCustomers();
|
445 |
+
$scope.onLocationChange();
|
446 |
$scope.onRepeatChange();
|
447 |
|
448 |
var customers_ids = [];
|
461 |
clone.extras = item.extras;
|
462 |
clone.status = item.status;
|
463 |
clone.custom_fields = item.custom_fields;
|
464 |
+
clone.files = item.files;
|
465 |
clone.number_of_persons = item.number_of_persons;
|
466 |
+
clone.timezone = item.timezone;
|
467 |
clone.notes = item.notes;
|
468 |
clone.payment_id = item.payment_id;
|
469 |
clone.payment_type = item.payment_type;
|
470 |
clone.payment_title = item.payment_title;
|
471 |
+
clone.payment_create = item.payment_create;
|
472 |
+
clone.payment_price = item.payment_price;
|
473 |
+
clone.payment_tax = item.payment_tax;
|
474 |
clone.compound_token = item.compound_token;
|
475 |
clone.compound_service = item.compound_service;
|
476 |
$scope.form.customers.push(clone);
|
501 |
});
|
502 |
}
|
503 |
customers.push({
|
504 |
+
id : item.id,
|
505 |
+
ca_id : item.ca_id,
|
506 |
+
custom_fields : item.custom_fields,
|
507 |
+
extras : customer_extras,
|
508 |
+
number_of_persons : item.number_of_persons,
|
509 |
+
timezone : item.timezone,
|
510 |
+
status : item.status
|
511 |
});
|
512 |
});
|
513 |
|
539 |
$scope.dataSource.setEndTimeBasedOnService();
|
540 |
$scope.prepareExtras();
|
541 |
$scope.prepareCustomFields();
|
542 |
+
$scope.onLocationChange();
|
543 |
checkAppointmentErrors();
|
544 |
};
|
545 |
|
546 |
+
$scope.onLocationChange = function () {
|
547 |
+
if ($scope.form.staff && $scope.form.service) {
|
548 |
+
var current_service = $scope.dataSource.findService($scope.form.staff.id, $scope.form.service.id),
|
549 |
+
location_id = $scope.form.location ? $scope.form.location.id : 0;
|
550 |
+
if (current_service.locations.hasOwnProperty(location_id)) {
|
551 |
+
$scope.form.service.capacity_min = current_service.locations[location_id].capacity_min;
|
552 |
+
$scope.form.service.capacity_max = current_service.locations[location_id].capacity_max;
|
553 |
+
} else if (current_service.locations.hasOwnProperty(0)) {
|
554 |
+
$scope.form.service.capacity_min = current_service.locations[0].capacity_min;
|
555 |
+
$scope.form.service.capacity_max = current_service.locations[0].capacity_max;
|
556 |
+
} else {
|
557 |
+
$scope.form.service.capacity_min = 1;
|
558 |
+
$scope.form.service.capacity_max = 1;
|
559 |
+
}
|
560 |
+
checkAppointmentErrors();
|
561 |
+
}
|
562 |
+
};
|
563 |
+
|
564 |
$scope.onStaffChange = function() {
|
565 |
if ($scope.form.staff.services.length == 2) {
|
566 |
$scope.form.service = $scope.form.staff.services[1];
|
569 |
$scope.form.service = null;
|
570 |
}
|
571 |
$scope.form.location = $scope.form.staff.locations.length == 1 ? $scope.form.staff.locations[0] : null;
|
572 |
+
$scope.onLocationChange();
|
573 |
};
|
574 |
|
575 |
$scope.onStartTimeChange = function() {
|
603 |
checkAppointmentErrors();
|
604 |
};
|
605 |
|
606 |
+
$scope.onSkipDateChange = function() {
|
607 |
+
checkAppointmentErrors();
|
608 |
+
};
|
609 |
+
|
610 |
$scope.processForm = function() {
|
611 |
$scope.loading = true;
|
612 |
|
639 |
custom_fields : item.custom_fields,
|
640 |
extras : customer_extras,
|
641 |
number_of_persons : item.number_of_persons,
|
642 |
+
timezone : item.timezone,
|
643 |
notes : item.notes,
|
644 |
+
status : item.status,
|
645 |
+
payment_id : item.payment_id,
|
646 |
+
payment_create : item.payment_create,
|
647 |
+
payment_price : item.payment_price,
|
648 |
+
payment_tax : item.payment_tax
|
649 |
});
|
650 |
});
|
651 |
jQuery.post(
|
659 |
custom_service_name : $scope.form.custom_service_name,
|
660 |
custom_service_price : $scope.form.custom_service_price,
|
661 |
location_id : $scope.form.location ? $scope.form.location.id : undefined,
|
662 |
+
skip_date : $scope.form.skip_date,
|
663 |
start_date : dates.start_date,
|
664 |
end_date : dates.end_date,
|
665 |
repeat : JSON.stringify($scope.form.repeat),
|
721 |
custom_fields : customer.custom_fields,
|
722 |
extras : customer.extras,
|
723 |
status : customer.status,
|
724 |
+
timezone : customer.timezone,
|
725 |
number_of_persons : nop,
|
726 |
notes : null,
|
727 |
compound_token : null,
|
728 |
payment_id : null,
|
729 |
payment_type : null,
|
730 |
+
payment_title : null,
|
731 |
+
payment_create : false,
|
732 |
+
payment_price : null,
|
733 |
+
payment_tax : null
|
734 |
};
|
735 |
|
736 |
if (customer.email || customer.phone){
|
761 |
|
762 |
$scope.editCustomerDetails = function(customer) {
|
763 |
var $dialog = jQuery('#bookly-customer-details-dialog');
|
764 |
+
$dialog.find('input.bookly-custom-field:text, textarea.bookly-custom-field, select.bookly-custom-field, input.bookly-js-file').val('');
|
765 |
$dialog.find('input.bookly-custom-field:checkbox, input.bookly-custom-field:radio').prop('checked', false);
|
766 |
$dialog.find('#bookly-extras :checkbox').prop('checked', false);
|
767 |
|
797 |
var max = $scope.form.service
|
798 |
? ($scope.form.service.id
|
799 |
? parseInt($scope.form.service.capacity_max) - $scope.dataSource.getTotalNumberOfNotCancelledPersons(customer)
|
800 |
+
: 999)
|
801 |
: 1;
|
802 |
$number_of_persons.empty();
|
803 |
for (var i = 1; i <= max; ++i) {
|
810 |
$dialog.find('#bookly-appointment-status').val(customer.status);
|
811 |
$dialog.find('#bookly-appointment-notes').val(customer.notes);
|
812 |
$dialog.find('#bookly-deposit-due').val(customer.due);
|
813 |
+
$dialog.find('#bookly-customer-time-zone').val(customer.timezone ? customer.timezone : '');
|
814 |
$scope.edit_customer = customer;
|
815 |
|
816 |
$dialog.modal({show: true})
|
817 |
.on('hidden.bs.modal', function () {
|
818 |
jQuery('body').addClass('modal-open');
|
819 |
});
|
820 |
+
|
821 |
+
jQuery(document.body).trigger('bookly.edit.customer_details', [$dialog, $scope.edit_customer]);
|
822 |
};
|
823 |
|
824 |
$scope.prepareExtras = function () {
|
867 |
extras = {},
|
868 |
$fields = jQuery('#bookly-js-custom-fields > *'),
|
869 |
$status = jQuery('#bookly-appointment-status'),
|
870 |
+
$timezone = jQuery('#bookly-customer-time-zone'),
|
871 |
$number_of_persons = jQuery('#bookly-number-of-persons'),
|
872 |
$notes = jQuery('#bookly-appointment-notes'),
|
873 |
$extras = jQuery('#bookly-extras')
|
904 |
}
|
905 |
|
906 |
$scope.edit_customer.status = $status.val();
|
907 |
+
$scope.edit_customer.timezone = $timezone.val();
|
908 |
$scope.edit_customer.number_of_persons = $number_of_persons.val();
|
909 |
$scope.edit_customer.notes = $notes.val();
|
910 |
$scope.edit_customer.custom_fields = result;
|
921 |
* Payment Details *
|
922 |
**************************************************************************************************************/
|
923 |
|
924 |
+
$scope.attachPaymentModal = function (customer) {
|
925 |
+
var $dialog = jQuery('#bookly-payment-attach-modal');
|
926 |
+
$scope.form.attach = {
|
927 |
+
customer_id : customer.id,
|
928 |
+
payment_method: 'create',
|
929 |
+
payment_price : null,
|
930 |
+
payment_tax : null,
|
931 |
+
payment_id : null
|
932 |
+
};
|
933 |
+
$dialog.modal({show: true}).on('hidden.bs.modal', function () {
|
934 |
+
jQuery('body').addClass('modal-open');
|
935 |
});
|
936 |
};
|
937 |
|
938 |
+
$scope.attachPayment = function (attach_method, price, tax, payment_id, customer_id) {
|
939 |
+
var $dialog = jQuery('#bookly-payment-details-modal');
|
940 |
+
if (attach_method == 'search') {
|
941 |
+
$dialog.data('payment_id', payment_id).data('payment_bind', true).data('customer_id', customer_id).modal({show: true}).on('hidden.bs.modal', function () {
|
942 |
+
jQuery('body').addClass('modal-open');
|
943 |
+
});
|
944 |
+
} else {
|
945 |
+
jQuery.each($scope.dataSource.data.customers, function (key, item) {
|
946 |
+
if (item.id == customer_id) {
|
947 |
+
item.payment_create = true;
|
948 |
+
item.payment_price = price;
|
949 |
+
item.payment_tax = tax;
|
950 |
+
item.payment_type = 'partial';
|
951 |
+
}
|
952 |
+
});
|
953 |
+
}
|
954 |
+
};
|
955 |
+
|
956 |
+
$scope.callbackPayment = function (payment_action, payment_id, payment_title, customer_id, payment_type) {
|
957 |
+
if (payment_action == 'bind') {
|
958 |
+
// Bind payment
|
959 |
+
jQuery.each($scope.dataSource.data.customers, function (key, item) {
|
960 |
+
if (item.id == customer_id) {
|
961 |
+
item.payment_id = payment_id;
|
962 |
+
item.payment_type = payment_type;
|
963 |
+
item.payment_title = payment_title;
|
964 |
+
}
|
965 |
+
});
|
966 |
+
} else {
|
967 |
+
// Complete payment
|
968 |
+
jQuery.each($scope.dataSource.data.customers, function (key, item) {
|
969 |
+
if (item.payment_id == payment_id) {
|
970 |
+
item.payment_type = 'full';
|
971 |
+
item.payment_title = payment_title;
|
972 |
+
}
|
973 |
+
});
|
974 |
+
}
|
975 |
+
};
|
976 |
+
|
977 |
/**************************************************************************************************************
|
978 |
* Package Schedule *
|
979 |
**************************************************************************************************************/
|
988 |
callback('refresh');
|
989 |
}
|
990 |
}, true]);
|
991 |
+
};
|
992 |
|
993 |
/**************************************************************************************************************
|
994 |
* Repeat Times in Recurring Appointments *
|
995 |
**************************************************************************************************************/
|
996 |
$scope.isDateMatchesSelections = function (current_date) {
|
997 |
+
var current_day = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'][current_date.format('d')];
|
998 |
switch ($scope.form.repeat.repeat) {
|
999 |
case 'daily':
|
1000 |
+
if (($scope.form.repeat.daily.every > 6 || jQuery.inArray(current_day, $scope.dataSource.data.week_days) != -1) && (current_date.diff(moment($scope.dataSource.form.date.getTime()), 'days') % $scope.form.repeat.daily.every == 0)) {
|
1001 |
return true;
|
1002 |
}
|
1003 |
break;
|
1004 |
case 'weekly':
|
1005 |
case 'biweekly':
|
1006 |
+
if (($scope.form.repeat.repeat == 'weekly' || current_date.diff(moment($scope.dataSource.form.date.getTime()).startOf('isoWeek'), 'weeks') % 2 == 0) && (jQuery.inArray(current_day, $scope.form.repeat.weekly.on) != -1)) {
|
1007 |
return true;
|
1008 |
}
|
1009 |
break;
|
1015 |
}
|
1016 |
break;
|
1017 |
case 'last':
|
1018 |
+
if (current_day == $scope.form.repeat.monthly.weekday && current_date.clone().endOf('month').diff(current_date, 'days') < 7) {
|
1019 |
return true;
|
1020 |
}
|
1021 |
break;
|
1024 |
weeks = ['first', 'second', 'third', 'fourth'],
|
1025 |
week_number = weeks.indexOf($scope.form.repeat.monthly.on);
|
1026 |
|
1027 |
+
if (current_day == $scope.form.repeat.monthly.weekday && month_diff >= week_number * 7 && month_diff < (week_number + 1) * 7) {
|
1028 |
return true;
|
1029 |
}
|
1030 |
}
|
1034 |
return false;
|
1035 |
};
|
1036 |
$scope.onRepeatChange = function () {
|
1037 |
+
if (jQuery('#bookly-repeat-enabled').length && !$scope.form.skip_date) {
|
1038 |
var number_of_times = 0,
|
1039 |
date_until = moment($scope.form.repeat.until).add(1, 'days'),
|
1040 |
current_date = moment($scope.dataSource.form.date.getTime());
|
1041 |
do {
|
1042 |
if ($scope.isDateMatchesSelections(current_date)) {
|
1043 |
+
number_of_times++;
|
1044 |
}
|
1045 |
current_date.add(1, 'days');
|
1046 |
} while (current_date.isBefore(date_until));
|
1092 |
until : $scope.form.repeat.until,
|
1093 |
repeat : $scope.form.repeat.repeat,
|
1094 |
params : $scope.form.repeat[$scope.form.repeat.repeat],
|
1095 |
+
extras : extras,
|
1096 |
+
duration : $scope.form.service.id ? undefined : $scope.dataSource.getServiceDuration()
|
1097 |
},
|
1098 |
function (response) {
|
1099 |
$scope.$apply(function($scope) {
|
1184 |
csrf_token : BooklyL10nAppDialog.csrf_token,
|
1185 |
staff_id : $scope.form.staff.id,
|
1186 |
service_id : $scope.form.service.id,
|
1187 |
+
location_id : $scope.form.location ? $scope.form.location.id : null,
|
1188 |
datetime : item.date + ' 00:00',
|
1189 |
until : item.date,
|
1190 |
repeat : 'daily',
|
1191 |
params : {every: 1},
|
1192 |
with_options : 1,
|
1193 |
exclude : exclude,
|
1194 |
+
extras : extras,
|
1195 |
+
duration : $scope.form.service.id ? undefined : $scope.dataSource.getServiceDuration()
|
1196 |
},
|
1197 |
function (response) {
|
1198 |
$scope.$apply(function($scope) {
|
1200 |
item.options = response.data[0].options;
|
1201 |
var found = false;
|
1202 |
jQuery.each(item.options, function (key, option) {
|
1203 |
+
if (option.value == item.slots) {
|
1204 |
found = true;
|
1205 |
return false;
|
1206 |
}
|
1207 |
});
|
1208 |
if (!found) {
|
1209 |
+
jQuery.each(item.options, function (key, option) {
|
1210 |
+
if (!option.disabled) {
|
1211 |
+
item.slots = option.value;
|
1212 |
+
return false;
|
1213 |
+
}
|
1214 |
+
});
|
1215 |
}
|
1216 |
} else {
|
1217 |
item.options = [];
|
backend/{modules/calendar/templates/_appointment_dialog.php → components/dialogs/appointment/edit/templates/edit.php}
RENAMED
@@ -1,8 +1,10 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
5 |
-
use
|
|
|
|
|
6 |
?>
|
7 |
<div ng-app="appointmentDialog" ng-controller="appointmentDialogCtrl">
|
8 |
<div id=bookly-appointment-dialog class="modal fade" tabindex=-1 role="dialog">
|
@@ -33,68 +35,67 @@ use BooklyLite\Lib\Utils\Common;
|
|
33 |
</p>
|
34 |
</div>
|
35 |
|
36 |
-
|
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 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
|
|
83 |
</div>
|
|
|
|
|
|
|
|
|
84 |
</div>
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
<p class="text-success" my-slide-up="errors.time_interval" ng-bind="errors.time_interval"></p>
|
89 |
</div>
|
90 |
</div>
|
91 |
-
<
|
92 |
-
<?php _e( '
|
93 |
-
</
|
|
|
|
|
|
|
94 |
</div>
|
95 |
-
</div>
|
96 |
|
97 |
-
|
|
|
98 |
|
99 |
<div class=form-group>
|
100 |
<label for="bookly-select2"><?php _e( 'Customers', 'bookly' ) ?></label>
|
@@ -102,7 +103,7 @@ use BooklyLite\Lib\Utils\Common;
|
|
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="
|
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}}
|
@@ -111,55 +112,68 @@ use BooklyLite\Lib\Utils\Common;
|
|
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::
|
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="{'
|
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="
|
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="
|
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="
|
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="
|
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="
|
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
|
157 |
</button>
|
158 |
-
|
|
|
|
|
|
|
|
|
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="
|
161 |
</button>
|
162 |
-
|
|
|
163 |
popover="<?php esc_attr_e( 'Remove customer', 'bookly' ) ?>"></a>
|
164 |
</span>
|
165 |
</li>
|
@@ -175,7 +189,7 @@ use BooklyLite\Lib\Utils\Common;
|
|
175 |
</select>
|
176 |
<span class="input-group-btn">
|
177 |
<a class="btn btn-success" ng-click="openNewCustomerDialog()">
|
178 |
-
<i class="
|
179 |
<?php _e( 'New customer', 'bookly' ) ?>
|
180 |
</a>
|
181 |
</span>
|
@@ -209,19 +223,19 @@ use BooklyLite\Lib\Utils\Common;
|
|
209 |
<div class="modal-footer">
|
210 |
<div ng-hide=loading>
|
211 |
<?php Proxy\Shared::renderAppointmentDialogFooter() ?>
|
212 |
-
<?php
|
213 |
-
<?php
|
214 |
</div>
|
215 |
</div>
|
216 |
</form>
|
217 |
-
</div
|
218 |
-
</div
|
219 |
-
</div
|
220 |
<div customer-dialog=createCustomer(customer)></div>
|
221 |
-
<div payment-details-dialog="
|
222 |
|
223 |
-
<?php
|
224 |
-
<?php \
|
225 |
-
<?php \
|
|
|
226 |
</div>
|
227 |
-
<?php Proxy\Packages::renderPackageScheduleDialog() ?>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Dialogs;
|
4 |
+
use Bookly\Backend\Components\Dialogs\Appointment\Edit\Proxy;
|
5 |
+
use Bookly\Backend\Components\Dialogs\Appointment\AttachPayment\Proxy as AttachPaymentProxy;
|
6 |
+
use Bookly\Lib\Config;
|
7 |
+
use Bookly\Lib\Entities\CustomerAppointment;
|
8 |
?>
|
9 |
<div ng-app="appointmentDialog" ng-controller="appointmentDialogCtrl">
|
10 |
<div id=bookly-appointment-dialog class="modal fade" tabindex=-1 role="dialog">
|
35 |
</p>
|
36 |
</div>
|
37 |
|
38 |
+
<?php Proxy\Pro::renderCustomServiceFields() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
<?php if ( Config::locationsActive() ): ?>
|
41 |
<div class="form-group">
|
42 |
<label for="bookly-appointment-location"><?php _e( 'Location', 'bookly' ) ?></label>
|
43 |
<select id="bookly-appointment-location" class="form-control" ng-model="form.location"
|
44 |
+
ng-options="l.name for l in form.staff.locations" ng-change="onLocationChange()">
|
45 |
<option value=""></option>
|
46 |
</select>
|
47 |
</div>
|
48 |
<?php endif ?>
|
49 |
|
50 |
+
<?php Proxy\Tasks::renderSkipDate() ?>
|
51 |
+
|
52 |
+
<div ng-hide="form.skip_date">
|
53 |
+
<div class=form-group>
|
54 |
+
<div class="row">
|
55 |
+
<div class="col-sm-4">
|
56 |
+
<label for="bookly-date"><?php _e( 'Date', 'bookly' ) ?></label>
|
57 |
+
<input id="bookly-date" class="form-control" type=text
|
58 |
+
ng-model=form.date ui-date="dateOptions" autocomplete="off"
|
59 |
+
ng-change=onDateChange()>
|
60 |
+
</div>
|
61 |
+
<div class="col-sm-8">
|
62 |
+
<div ng-hide="form.service.duration >= 86400">
|
63 |
+
<label for="bookly-period"><?php _e( 'Period', 'bookly' ) ?></label>
|
64 |
+
<div class="bookly-flexbox">
|
65 |
+
<div class="bookly-flex-cell">
|
66 |
+
<select id="bookly-period" class="form-control" ng-model=form.start_time
|
67 |
+
ng-options="t.title for t in dataSource.getDataForStartTime()"
|
68 |
+
ng-change=onStartTimeChange()></select>
|
69 |
+
</div>
|
70 |
+
<div class="bookly-flex-cell" style="width: 4%">
|
71 |
+
<div class="bookly-margin-horizontal-md"><?php _e( 'to', 'bookly' ) ?></div>
|
72 |
+
</div>
|
73 |
+
<div class="bookly-flex-cell" style="width: 48%">
|
74 |
+
<select class="form-control" ng-model=form.end_time
|
75 |
+
ng-options="t.title for t in form.end_time_data"
|
76 |
+
ng-change=onEndTimeChange()></select>
|
77 |
+
</div>
|
78 |
</div>
|
79 |
+
<p class="text-success" my-slide-up=errors.date_interval_warning id=date_interval_warning_msg>
|
80 |
+
<?php _e( 'Selected period doesn\'t match service duration', 'bookly' ) ?>
|
81 |
+
</p>
|
82 |
+
<p class="text-success" my-slide-up="errors.time_interval" ng-bind="errors.time_interval"></p>
|
83 |
</div>
|
84 |
+
</div>
|
85 |
+
<div class="text-success col-sm-12" my-slide-up=errors.date_interval_not_available id=date_interval_not_available_msg>
|
86 |
+
<?php _e( 'The selected period is occupied by another appointment', 'bookly' ) ?>
|
|
|
87 |
</div>
|
88 |
</div>
|
89 |
+
<p class="text-success" my-slide-up=errors.interval_not_in_staff_schedule id=interval_not_in_staff_schedule_msg>
|
90 |
+
<?php _e( 'Selected period doesn\'t match provider\'s schedule', 'bookly' ) ?>
|
91 |
+
</p>
|
92 |
+
<p class="text-success" my-slide-up=errors.interval_not_in_service_schedule id=interval_not_in_service_schedule_msg>
|
93 |
+
<?php _e( 'Selected period doesn\'t match service schedule', 'bookly' ) ?>
|
94 |
+
</p>
|
95 |
</div>
|
|
|
96 |
|
97 |
+
<?php Proxy\RecurringAppointments::renderSubForm() ?>
|
98 |
+
</div>
|
99 |
|
100 |
<div class=form-group>
|
101 |
<label for="bookly-select2"><?php _e( 'Customers', 'bookly' ) ?></label>
|
103 |
({{dataSource.getTotalNumberOfPersons()}}/{{form.service.capacity_max}})
|
104 |
</span>
|
105 |
<span ng-show="form.customers.length > 5" ng-click="form.expand_customers_list = !form.expand_customers_list" role="button">
|
106 |
+
<i class="fa fa-fw" ng-class="{'fa-angle-down':!form.expand_customers_list, 'fa-angle-up':form.expand_customers_list}"></i>
|
107 |
</span>
|
108 |
<p class="text-success" ng-show=form.service my-slide-up="form.service.capacity_min > 1 && form.service.capacity_min > dataSource.getTotalNumberOfPersons()">
|
109 |
<?php _e( 'Minimum capacity', 'bookly' ) ?>: {{form.service.capacity_min}}
|
112 |
<li ng-repeat="customer in form.customers" class="bookly-flex-row" ng-hide="$index > 4 && !form.expand_customers_list">
|
113 |
<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>
|
114 |
<span class="bookly-flex-cell text-right text-nowrap bookly-padding-bottom-sm">
|
115 |
+
<?php Proxy\Shared::renderAppointmentDialogCustomersList() ?>
|
116 |
<span class="dropdown">
|
117 |
<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)}}">
|
118 |
+
<span ng-class="{'fa fa-fw': true, 'fa-clock': customer.status == 'pending', 'fa-check': customer.status == 'approved', 'fa-times': customer.status == 'cancelled', 'fa-times-circle': customer.status == 'rejected', 'fa-list-alt': customer.status == 'waitlisted', 'fa-check-circle': customer.status == 'done'}"></span>
|
119 |
<span class="caret"></span>
|
120 |
</button>
|
121 |
<ul class="dropdown-menu">
|
122 |
<li>
|
123 |
<a href ng-click="customer.status = 'pending'">
|
124 |
+
<span class="fa fa-fw fa-clock"></span>
|
125 |
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?>
|
126 |
</a>
|
127 |
</li>
|
128 |
<li>
|
129 |
<a href ng-click="customer.status = 'approved'">
|
130 |
+
<span class="fa fa-fw fa-check"></span>
|
131 |
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?>
|
132 |
</a>
|
133 |
</li>
|
134 |
<li>
|
135 |
<a href ng-click="customer.status = 'cancelled'">
|
136 |
+
<span class="fa fa-fw fa-times"></span>
|
137 |
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?>
|
138 |
</a>
|
139 |
</li>
|
140 |
<li>
|
141 |
<a href ng-click="customer.status = 'rejected'">
|
142 |
+
<span class="fa fa-fw fa-times-circle"></span>
|
143 |
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_REJECTED ) ) ?>
|
144 |
</a>
|
145 |
</li>
|
146 |
<?php if ( Config::waitingListActive() ): ?>
|
147 |
<li>
|
148 |
<a href ng-click="customer.status = 'waitlisted'">
|
149 |
+
<span class="fa fa-fw fa-list-alt"></span>
|
150 |
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_WAITLISTED ) ) ?>
|
151 |
</a>
|
152 |
</li>
|
153 |
<?php endif ?>
|
154 |
+
<?php if ( Config::tasksActive() ): ?>
|
155 |
+
<li>
|
156 |
+
<a href ng-click="customer.status = 'done'">
|
157 |
+
<span class="fa fa-fw fa-check-circle"></span>
|
158 |
+
<?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_DONE ) ) ?>
|
159 |
+
</a>
|
160 |
+
</li>
|
161 |
+
<?php endif ?>
|
162 |
</ul>
|
163 |
</span>
|
164 |
+
<button type="button" class="btn btn-sm btn-default bookly-margin-left-xs" data-toggle="modal" href="#bookly-payment-details-modal" data-payment_id="{{customer.payment_id}}" ng-show="customer.payment_id || customer.payment_create" popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}" ng-disabled="customer.payment_create">
|
165 |
+
<span ng-class="{'bookly-js-toggle-popover fa fa-fw': true, 'fa-clipboard-check': customer.payment_type == 'full', 'fa-hourglass': customer.payment_type == 'partial'}"></span>
|
166 |
</button>
|
167 |
+
|
168 |
+
<?php Proxy\Pro::renderAttachPaymentButton() ?>
|
169 |
+
|
170 |
+
<span class="btn btn-sm btn-default disabled bookly-margin-left-xs" style="opacity:1;cursor:default;"><i class="fa fa-fw fa-user"></i>×{{customer.number_of_persons}}</span>
|
171 |
+
<?php if ( Config::packagesActive() ) : ?>
|
172 |
<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' ) ?>">
|
173 |
+
<span class="fa fa-fw fa-calendar-alt"></span>
|
174 |
</button>
|
175 |
+
<?php endif ?>
|
176 |
+
<a ng-click="removeCustomer(customer)" class="fa fa-fw fa-trash-alt text-danger bookly-vertical-middle" href="#"
|
177 |
popover="<?php esc_attr_e( 'Remove customer', 'bookly' ) ?>"></a>
|
178 |
</span>
|
179 |
</li>
|
189 |
</select>
|
190 |
<span class="input-group-btn">
|
191 |
<a class="btn btn-success" ng-click="openNewCustomerDialog()">
|
192 |
+
<i class="fa fa-fw fa-plus"></i>
|
193 |
<?php _e( 'New customer', 'bookly' ) ?>
|
194 |
</a>
|
195 |
</span>
|
223 |
<div class="modal-footer">
|
224 |
<div ng-hide=loading>
|
225 |
<?php Proxy\Shared::renderAppointmentDialogFooter() ?>
|
226 |
+
<?php Buttons::renderSubmit( null, null, null, array( 'ng-hide' => 'form.repeat.enabled && !form.skip_date && form.screen == \'main\'', 'ng-disabled' => '!form.skip_date && form.repeat.enabled && schIsScheduleEmpty()', 'formnovalidate' => '' ) ) ?>
|
227 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'ng-click' => 'closeDialog()', 'data-dismiss' => 'modal' ) ) ?>
|
228 |
</div>
|
229 |
</div>
|
230 |
</form>
|
231 |
+
</div>
|
232 |
+
</div>
|
233 |
+
</div>
|
234 |
<div customer-dialog=createCustomer(customer)></div>
|
235 |
+
<div payment-details-dialog="callbackPayment(payment_action, payment_id, payment_title, customer_id, payment_type)"></div>
|
236 |
|
237 |
+
<?php Dialogs\Appointment\CustomerDetails\Dialog::render() ?>
|
238 |
+
<?php AttachPaymentProxy\Pro::renderAttachPaymentDialog() ?>
|
239 |
+
<?php Dialogs\Customer\Edit::render() ?>
|
240 |
+
<?php Dialogs\Payment\Dialog::render() ?>
|
241 |
</div>
|
|
backend/components/dialogs/common/CascadeDelete.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Common;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CascadeDelete
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Common
|
9 |
+
*/
|
10 |
+
class CascadeDelete extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render cascade delete dialog (used in services and staff lists).
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
19 |
+
) );
|
20 |
+
|
21 |
+
self::enqueueScripts( array(
|
22 |
+
'frontend' => array(
|
23 |
+
'js/spin.min.js' => array( 'jquery' ),
|
24 |
+
'js/ladda.min.js' => array( 'bookly-spin.min.js', 'jquery' ),
|
25 |
+
)
|
26 |
+
) );
|
27 |
+
|
28 |
+
self::renderTemplate( 'delete_cascade' );
|
29 |
+
}
|
30 |
+
}
|
backend/components/dialogs/common/UnsavedChanges.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Common;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class UnsavedChanges
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Common
|
9 |
+
*/
|
10 |
+
class UnsavedChanges extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render unsaved data confirm dialog.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
19 |
+
) );
|
20 |
+
|
21 |
+
self::enqueueScripts( array(
|
22 |
+
'frontend' => array(
|
23 |
+
'js/spin.min.js' => array( 'jquery' ),
|
24 |
+
'js/ladda.min.js' => array( 'bookly-spin.min.js', 'jquery' ),
|
25 |
+
)
|
26 |
+
) );
|
27 |
+
|
28 |
+
self::renderTemplate( 'unsaved_changes' );
|
29 |
+
}
|
30 |
+
}
|
backend/components/dialogs/common/templates/delete_cascade.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
?>
|
4 |
+
<div class="modal fade bookly-js-delete-cascade-confirm" tabindex="-1" role="dialog">
|
5 |
+
<div class="modal-dialog modal-lg" role="document">
|
6 |
+
<div class="modal-content">
|
7 |
+
<div class="modal-header">
|
8 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
9 |
+
<div class="modal-title h4"><?php _e( 'Are you sure?', 'bookly' ) ?></div>
|
10 |
+
</div>
|
11 |
+
<div class="modal-body">
|
12 |
+
<p><?php _e( 'You are going to delete item which is involved in upcoming appointments. All related appointments will be deleted. Please double check and edit appointments before this item deletion if needed.', 'bookly' ) ?></p>
|
13 |
+
</div>
|
14 |
+
<div class="modal-footer">
|
15 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-danger bookly-js-delete', __( 'Delete', 'bookly' ) ) ?>
|
16 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-success bookly-js-edit', __( 'Edit appointments', 'bookly' ) ) ?>
|
17 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
</div>
|
21 |
+
</div>
|
backend/components/dialogs/common/templates/unsaved_changes.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
?>
|
4 |
+
<div class="modal fade bookly-js-unsaved-changes" tabindex="-1" role="dialog">
|
5 |
+
<div class="modal-dialog modal-lg" role="document">
|
6 |
+
<div class="modal-content">
|
7 |
+
<div class="modal-header">
|
8 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
9 |
+
<div class="modal-title h4"><?php _e( 'Are you sure?', 'bookly' ) ?></div>
|
10 |
+
</div>
|
11 |
+
<div class="modal-body">
|
12 |
+
<p><?php _e( 'All unsaved changes will be lost.', 'bookly' ) ?></p>
|
13 |
+
</div>
|
14 |
+
<div class="modal-footer">
|
15 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-success bookly-js-save-changes', __( 'Save', 'bookly' ) ) ?>
|
16 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-danger bookly-js-ignore-changes', __( 'Don\'t save', 'bookly' ) ) ?>
|
17 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-default', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
</div>
|
21 |
+
</div>
|
backend/{modules/customers/Components.php → components/dialogs/customer/Edit.php}
RENAMED
@@ -1,30 +1,29 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
|
6 |
/**
|
7 |
-
* Class
|
8 |
-
* @package
|
9 |
*/
|
10 |
-
class
|
11 |
{
|
12 |
/**
|
13 |
* Render customer dialog.
|
14 |
-
* @throws \Exception
|
15 |
*/
|
16 |
-
public function
|
17 |
{
|
18 |
global $wp_locale;
|
19 |
|
20 |
-
|
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 |
-
|
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' ),
|
@@ -32,28 +31,31 @@ class Components extends Lib\Base\Components
|
|
32 |
'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
33 |
? array()
|
34 |
: array( 'js/intlTelInput.min.js' => array( 'jquery' ) ),
|
35 |
-
'module' => array( 'js/ng-
|
36 |
) );
|
37 |
|
38 |
-
wp_localize_script( 'bookly-ng-
|
39 |
-
'csrf_token'
|
40 |
-
'
|
41 |
-
'
|
|
|
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'
|
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 |
-
|
57 |
}
|
58 |
-
|
59 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
|
6 |
/**
|
7 |
+
* Class Edit
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer
|
9 |
*/
|
10 |
+
class Edit extends Lib\Base\Component
|
11 |
{
|
12 |
/**
|
13 |
* Render customer dialog.
|
|
|
14 |
*/
|
15 |
+
public static function render()
|
16 |
{
|
17 |
global $wp_locale;
|
18 |
|
19 |
+
static::enqueueStyles( array(
|
20 |
'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', ),
|
21 |
'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
22 |
? array()
|
23 |
: array( 'css/intlTelInput.css' ),
|
24 |
) );
|
25 |
|
26 |
+
static::enqueueScripts( array(
|
27 |
'backend' => array(
|
28 |
'js/angular.min.js' => array( 'jquery' ),
|
29 |
'js/angular-ui-date-0.0.8.js' => array( 'bookly-angular.min.js', 'jquery-ui-datepicker' ),
|
31 |
'frontend' => get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
32 |
? array()
|
33 |
: array( 'js/intlTelInput.min.js' => array( 'jquery' ) ),
|
34 |
+
'module' => array( 'js/ng-customer.js' => array( 'bookly-angular.min.js' ), )
|
35 |
) );
|
36 |
|
37 |
+
wp_localize_script( 'bookly-ng-customer.js', 'BooklyL10nCustDialog', array(
|
38 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
39 |
+
'first_last_name' => (int) Lib\Config::showFirstLastName(),
|
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' => is_rtl() ? '' : 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 |
+
'yearRange' => sprintf( "%s:%s", date_create()->modify( '-100 years' )->format( 'Y' ), date( 'Y' ) ),
|
54 |
+
'changeYear' => true,
|
55 |
),
|
56 |
+
'infoFields' => (array) Lib\Proxy\CustomerInformation::getFieldsWhichMayHaveData(),
|
57 |
) );
|
58 |
|
59 |
+
static::renderTemplate( 'edit' );
|
60 |
}
|
|
|
61 |
}
|
backend/components/dialogs/customer/EditAjax.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Edit
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer
|
9 |
+
*/
|
10 |
+
class EditAjax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @inheritdoc
|
14 |
+
*/
|
15 |
+
protected static function permissions()
|
16 |
+
{
|
17 |
+
return array( '_default' => 'user' );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Create or edit a customer.
|
22 |
+
*/
|
23 |
+
public static function saveCustomer()
|
24 |
+
{
|
25 |
+
$response = array();
|
26 |
+
|
27 |
+
$params = self::postParameters();
|
28 |
+
$errors = array();
|
29 |
+
|
30 |
+
// Check for errors.
|
31 |
+
if ( get_option( 'bookly_cst_first_last_name' ) ) {
|
32 |
+
if ( $params['first_name'] == '' ) {
|
33 |
+
$errors['first_name'] = array( 'required' );
|
34 |
+
}
|
35 |
+
if ( $params['last_name'] == '' ) {
|
36 |
+
$errors['last_name'] = array( 'required' );
|
37 |
+
}
|
38 |
+
} else if ( $params['full_name'] == '' ) {
|
39 |
+
$errors['full_name'] = array( 'required' );
|
40 |
+
}
|
41 |
+
|
42 |
+
if ( empty ( $errors ) ) {
|
43 |
+
if ( ! $params['wp_user_id'] ) {
|
44 |
+
$params['wp_user_id'] = null;
|
45 |
+
}
|
46 |
+
if ( ! $params['birthday'] ) {
|
47 |
+
$params['birthday'] = null;
|
48 |
+
}
|
49 |
+
if ( ! $params['group_id'] ) {
|
50 |
+
$params['group_id'] = null;
|
51 |
+
}
|
52 |
+
$params = Proxy\CustomerInformation::prepareCustomerFormData( $params );
|
53 |
+
$params['info_fields'] = json_encode( $params['info_fields'] );
|
54 |
+
$form = new Forms\Customer();
|
55 |
+
$form->bind( $params );
|
56 |
+
/** @var Lib\Entities\Customer $customer */
|
57 |
+
$customer = $form->save();
|
58 |
+
$response['success'] = true;
|
59 |
+
$response['customer'] = array(
|
60 |
+
'id' => $customer->getId(),
|
61 |
+
'wp_user_id' => $customer->getWpUserId(),
|
62 |
+
'group_id' => $customer->getGroupId(),
|
63 |
+
'full_name' => $customer->getFullName(),
|
64 |
+
'first_name' => $customer->getFirstName(),
|
65 |
+
'last_name' => $customer->getLastName(),
|
66 |
+
'phone' => $customer->getPhone(),
|
67 |
+
'email' => $customer->getEmail(),
|
68 |
+
'notes' => $customer->getNotes(),
|
69 |
+
'birthday' => $customer->getBirthday(),
|
70 |
+
'info_fields' => json_decode( $customer->getInfoFields() ),
|
71 |
+
);
|
72 |
+
} else {
|
73 |
+
$response['success'] = false;
|
74 |
+
$response['errors'] = $errors;
|
75 |
+
}
|
76 |
+
|
77 |
+
wp_send_json( $response );
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Check if the current user has access to the action.
|
82 |
+
*
|
83 |
+
* @param string $action
|
84 |
+
* @return bool
|
85 |
+
*/
|
86 |
+
protected static function hasAccess( $action )
|
87 |
+
{
|
88 |
+
if ( parent::hasAccess( $action ) ) {
|
89 |
+
if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
|
90 |
+
switch ( $action ) {
|
91 |
+
case 'saveCustomer':
|
92 |
+
return Lib\Entities\Staff::query()
|
93 |
+
->where( 'wp_user_id', get_current_user_id() )
|
94 |
+
->count() > 0;
|
95 |
+
}
|
96 |
+
} else {
|
97 |
+
return true;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
return false;
|
102 |
+
}
|
103 |
+
}
|
backend/{modules/customers → components/dialogs/customer}/forms/Customer.php
RENAMED
@@ -1,11 +1,11 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
|
6 |
/**
|
7 |
* Class Customer
|
8 |
-
* @package
|
9 |
*/
|
10 |
class Customer extends Lib\Base\Form
|
11 |
{
|
@@ -15,14 +15,22 @@ class Customer extends Lib\Base\Form
|
|
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 |
-
|
28 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer\Forms;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
|
6 |
/**
|
7 |
* Class Customer
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer\Forms
|
9 |
*/
|
10 |
class Customer extends Lib\Base\Form
|
11 |
{
|
15 |
{
|
16 |
$this->setFields( array(
|
17 |
'wp_user_id',
|
18 |
+
'group_id',
|
19 |
'full_name',
|
20 |
'first_name',
|
21 |
'last_name',
|
22 |
'phone',
|
23 |
'email',
|
24 |
+
'country',
|
25 |
+
'state',
|
26 |
+
'postcode',
|
27 |
+
'city',
|
28 |
+
'street',
|
29 |
+
'street_number',
|
30 |
+
'additional_address',
|
31 |
'notes',
|
32 |
'birthday',
|
33 |
+
'info_fields',
|
34 |
) );
|
35 |
}
|
|
|
36 |
}
|
backend/components/dialogs/customer/proxy/CustomerGroups.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerGroups
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCustomerDialog() Render 'Customer Group' row in edit customer dialog.
|
11 |
+
* @see \BooklyCustomerGroups\Backend\Components\Dialogs\Customer\ProxyProviders\Local::renderCustomerDialog()
|
12 |
+
*/
|
13 |
+
abstract class CustomerGroups extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/components/dialogs/customer/proxy/CustomerInformation.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerInformation
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCustomerDialog() Render 'Customer Information' row in edit customer dialog.
|
11 |
+
* @method static array prepareCustomerFormData( array $params ) Prepare customer info fields before saving customer form.
|
12 |
+
*/
|
13 |
+
abstract class CustomerInformation extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/components/dialogs/customer/proxy/Pro.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Customer\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Customer\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCustomerDialogAddress() Render address fields in edit customer dialog.
|
11 |
+
* @method static void renderCustomerDialogBirthday() Render birthday fields in edit customer dialog.
|
12 |
+
*/
|
13 |
+
abstract class Pro extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/{modules/customers/resources/js/ng-customer_dialog.js → components/dialogs/customer/resources/js/ng-customer.js}
RENAMED
@@ -15,16 +15,28 @@
|
|
15 |
// Form fields.
|
16 |
if (!scope.form) {
|
17 |
scope.form = {
|
18 |
-
id
|
19 |
-
wp_user_id
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
};
|
|
|
|
|
|
|
28 |
}
|
29 |
// Form errors.
|
30 |
scope.errors = {
|
@@ -60,7 +72,40 @@
|
|
60 |
jQuery('body').addClass('modal-open');
|
61 |
}
|
62 |
});
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
/**
|
65 |
* Send form to server.
|
66 |
*/
|
@@ -84,15 +129,24 @@
|
|
84 |
// Send new customer to the parent scope.
|
85 |
scope.callback({customer : response.customer});
|
86 |
scope.form = {
|
87 |
-
id
|
88 |
-
wp_user_id
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
};
|
97 |
// Close the dialog.
|
98 |
element.modal('hide');
|
@@ -120,6 +174,22 @@
|
|
120 |
* Datepicker options.
|
121 |
*/
|
122 |
scope.dateOptions = BooklyL10nCustDialog.dateOptions;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
}
|
124 |
};
|
125 |
});
|
15 |
// Form fields.
|
16 |
if (!scope.form) {
|
17 |
scope.form = {
|
18 |
+
id : '',
|
19 |
+
wp_user_id : '',
|
20 |
+
group_id : '',
|
21 |
+
full_name : '',
|
22 |
+
first_name : '',
|
23 |
+
last_name : '',
|
24 |
+
phone : '',
|
25 |
+
email : '',
|
26 |
+
country : '',
|
27 |
+
state : '',
|
28 |
+
postcode : '',
|
29 |
+
city : '',
|
30 |
+
street : '',
|
31 |
+
street_number : '',
|
32 |
+
additional_address : '',
|
33 |
+
info_fields : [],
|
34 |
+
notes : '',
|
35 |
+
birthday : ''
|
36 |
};
|
37 |
+
BooklyL10nCustDialog.infoFields.forEach(function (field) {
|
38 |
+
scope.form.info_fields.push({id: field.id, value: field.type === 'checkboxes' ? [] : ''});
|
39 |
+
});
|
40 |
}
|
41 |
// Form errors.
|
42 |
scope.errors = {
|
72 |
jQuery('body').addClass('modal-open');
|
73 |
}
|
74 |
});
|
75 |
+
scope.changeWpUser = function () {
|
76 |
+
var $user = jQuery('#wp_user option:selected'),
|
77 |
+
email = $user.attr('data-email'),
|
78 |
+
first_name = $user.attr('data-first-name'),
|
79 |
+
last_name = $user.attr('data-last-name'),
|
80 |
+
phone = $user.attr('data-phone'),
|
81 |
+
display_name = $user.text().trim();
|
82 |
+
if (BooklyL10nCustDialog.first_last_name == 1) {
|
83 |
+
if (!first_name.length && !last_name.length) {
|
84 |
+
var name_parts = display_name.split(' ');
|
85 |
+
first_name = name_parts[0];
|
86 |
+
name_parts.splice(0, 1);
|
87 |
+
last_name = name_parts.join(' ');
|
88 |
+
}
|
89 |
+
if (first_name.length) {
|
90 |
+
scope.form.first_name = first_name;
|
91 |
+
}
|
92 |
+
if (last_name.length) {
|
93 |
+
scope.form.last_name = last_name;
|
94 |
+
}
|
95 |
+
} else {
|
96 |
+
if (first_name.length || last_name.length) {
|
97 |
+
scope.form.full_name = (first_name + ' ' + last_name).trim();
|
98 |
+
} else {
|
99 |
+
scope.form.full_name = display_name;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
if (email.length) {
|
103 |
+
scope.form.email = email;
|
104 |
+
}
|
105 |
+
if (phone.length) {
|
106 |
+
scope.form.phone = phone;
|
107 |
+
}
|
108 |
+
};
|
109 |
/**
|
110 |
* Send form to server.
|
111 |
*/
|
129 |
// Send new customer to the parent scope.
|
130 |
scope.callback({customer : response.customer});
|
131 |
scope.form = {
|
132 |
+
id : '',
|
133 |
+
wp_user_id : '',
|
134 |
+
group_id : '',
|
135 |
+
full_name : '',
|
136 |
+
first_name : '',
|
137 |
+
last_name : '',
|
138 |
+
phone : '',
|
139 |
+
email : '',
|
140 |
+
country : '',
|
141 |
+
state : '',
|
142 |
+
postcode : '',
|
143 |
+
city : '',
|
144 |
+
street : '',
|
145 |
+
street_number : '',
|
146 |
+
additional_address : '',
|
147 |
+
info_fields : [],
|
148 |
+
notes : '',
|
149 |
+
birthday : ''
|
150 |
};
|
151 |
// Close the dialog.
|
152 |
element.modal('hide');
|
174 |
* Datepicker options.
|
175 |
*/
|
176 |
scope.dateOptions = BooklyL10nCustDialog.dateOptions;
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Toggle checkbox info field.
|
180 |
+
*/
|
181 |
+
scope.toggleCheckbox = function (i, value) {
|
182 |
+
var idx = scope.form.info_fields[i].value.indexOf(value);
|
183 |
+
|
184 |
+
// Is currently selected.
|
185 |
+
if (idx > -1) {
|
186 |
+
scope.form.info_fields[i].value.splice(idx, 1);
|
187 |
+
}
|
188 |
+
// Is newly selected.
|
189 |
+
else {
|
190 |
+
scope.form.info_fields[i].value.push(value);
|
191 |
+
}
|
192 |
+
};
|
193 |
}
|
194 |
};
|
195 |
});
|
backend/{modules/customers/templates/_customer_dialog.php → components/dialogs/customer/templates/edit.php}
RENAMED
@@ -1,6 +1,7 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
|
|
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">
|
@@ -8,18 +9,19 @@ use BooklyLite\Lib\Config;
|
|
8 |
<div class="modal-content">
|
9 |
<div class="modal-header">
|
10 |
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
11 |
-
<div class="modal-title h2"><?php
|
12 |
</div>
|
13 |
<div ng-show=loading class="modal-body">
|
14 |
<div class="bookly-loading"></div>
|
15 |
</div>
|
16 |
<div class="modal-body" ng-hide="loading">
|
17 |
<div class="form-group">
|
18 |
-
<label for="wp_user"><?php
|
19 |
-
<select ng-model="form.wp_user_id" class="form-control" id="wp_user">
|
20 |
<option value=""></option>
|
21 |
-
<?php foreach ( get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) ) as $wp_user ) : ?>
|
22 |
-
|
|
|
23 |
<?php echo $wp_user->display_name ?>
|
24 |
</option>
|
25 |
<?php endforeach ?>
|
@@ -30,50 +32,50 @@ use BooklyLite\Lib\Config;
|
|
30 |
<div class="form-group">
|
31 |
<div class="row">
|
32 |
<div class="col-sm-6">
|
33 |
-
<label for="first_name"><?php
|
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
|
36 |
</div>
|
37 |
<div class="col-sm-6">
|
38 |
-
<label for="last_name"><?php
|
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
|
41 |
</div>
|
42 |
</div>
|
43 |
</div>
|
44 |
<?php else : ?>
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
<?php endif ?>
|
51 |
|
52 |
<div class="form-group">
|
53 |
-
<label for="phone"><?php
|
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
|
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
|
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
|
76 |
-
<?php
|
77 |
</div>
|
78 |
</div>
|
79 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Dialogs\Customer\Proxy;
|
4 |
+
use Bookly\Lib\Config;
|
5 |
?>
|
6 |
<script type="text/ng-template" id="bookly-customer-dialog.tpl">
|
7 |
<div id="bookly-customer-dialog" class="modal fade" tabindex=-1 role="dialog">
|
9 |
<div class="modal-content">
|
10 |
<div class="modal-header">
|
11 |
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
12 |
+
<div class="modal-title h2"><?php esc_html_e( 'New Customer', 'bookly' ) ?></div>
|
13 |
</div>
|
14 |
<div ng-show=loading class="modal-body">
|
15 |
<div class="bookly-loading"></div>
|
16 |
</div>
|
17 |
<div class="modal-body" ng-hide="loading">
|
18 |
<div class="form-group">
|
19 |
+
<label for="wp_user"><?php esc_html_e( 'User', 'bookly' ) ?></label>
|
20 |
+
<select ng-model="form.wp_user_id" class="form-control" id="wp_user" ng-change="changeWpUser()">
|
21 |
<option value=""></option>
|
22 |
+
<?php foreach ( get_users( array( 'fields' => array( 'ID', 'display_name', 'user_email' ), 'orderby' => 'display_name' ) ) as $wp_user ) : ?>
|
23 |
+
<?php $user_data = get_userdata( $wp_user->ID ) ?>
|
24 |
+
<option value="<?php echo $wp_user->ID ?>" data-email="<?php echo esc_html( $wp_user->user_email ) ?>" data-first-name="<?php echo esc_html( $user_data->first_name ) ?>" data-last-name="<?php echo esc_html( $user_data->last_name ) ?>" data-phone="<?php echo esc_html( get_user_meta( $wp_user->ID, 'billing_phone', true ) ) ?>">
|
25 |
<?php echo $wp_user->display_name ?>
|
26 |
</option>
|
27 |
<?php endforeach ?>
|
32 |
<div class="form-group">
|
33 |
<div class="row">
|
34 |
<div class="col-sm-6">
|
35 |
+
<label for="first_name"><?php esc_html_e( 'First name', 'bookly' ) ?></label>
|
36 |
<input class="form-control" type="text" ng-model="form.first_name" id="first_name" />
|
37 |
+
<span style="font-size: 11px;color: red" ng-show="errors.first_name.required"><?php esc_html_e( 'Required', 'bookly' ) ?></span>
|
38 |
</div>
|
39 |
<div class="col-sm-6">
|
40 |
+
<label for="last_name"><?php esc_html_e( 'Last name', 'bookly' ) ?></label>
|
41 |
<input class="form-control" type="text" ng-model="form.last_name" id="last_name" />
|
42 |
+
<span style="font-size: 11px;color: red" ng-show="errors.last_name.required"><?php esc_html_e( 'Required', 'bookly' ) ?></span>
|
43 |
</div>
|
44 |
</div>
|
45 |
</div>
|
46 |
<?php else : ?>
|
47 |
+
<div class="form-group">
|
48 |
+
<label for="full_name"><?php esc_html_e( 'Name', 'bookly' ) ?></label>
|
49 |
+
<input class="form-control" type="text" ng-model="form.full_name" id="full_name" />
|
50 |
+
<span style="font-size: 11px;color: red" ng-show="errors.full_name.required"><?php esc_html_e( 'Required', 'bookly' ) ?></span>
|
51 |
+
</div>
|
52 |
<?php endif ?>
|
53 |
|
54 |
<div class="form-group">
|
55 |
+
<label for="phone"><?php esc_html_e( 'Phone', 'bookly' ) ?></label>
|
56 |
<input class="form-control" type="text" ng-model=form.phone id="phone" />
|
57 |
</div>
|
58 |
|
59 |
<div class="form-group">
|
60 |
+
<label for="email"><?php esc_html_e( 'Email', 'bookly' ) ?></label>
|
61 |
<input class="form-control" type="text" ng-model=form.email id="email" />
|
62 |
</div>
|
63 |
|
64 |
+
<?php Proxy\Pro::renderCustomerDialogBirthday() ?>
|
65 |
+
<?php Proxy\Pro::renderCustomerDialogAddress() ?>
|
66 |
+
<?php Proxy\CustomerInformation::renderCustomerDialog() ?>
|
67 |
+
<?php Proxy\CustomerGroups::renderCustomerDialog() ?>
|
68 |
+
|
69 |
<div class="form-group">
|
70 |
+
<label for="notes"><?php esc_html_e( 'Notes', 'bookly' ) ?></label>
|
71 |
<textarea class="form-control" ng-model=form.notes id="notes"></textarea>
|
72 |
</div>
|
73 |
|
|
|
|
|
|
|
|
|
|
|
74 |
</div>
|
75 |
<div class="modal-footer">
|
76 |
<div ng-hide=loading>
|
77 |
+
<?php Buttons::renderCustom( null, 'btn-success btn-lg', null, array( 'ng-click' => 'processForm()' ) ) ?>
|
78 |
+
<?php Buttons::renderCustom( null, 'btn-default btn-lg', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
79 |
</div>
|
80 |
</div>
|
81 |
</div>
|
backend/components/dialogs/payment/Ajax.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Payment;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Payment
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @inheritdoc
|
14 |
+
*/
|
15 |
+
protected static function permissions()
|
16 |
+
{
|
17 |
+
return array(
|
18 |
+
'completePayment' => 'user',
|
19 |
+
'getPaymentDetails' => 'user',
|
20 |
+
'getPaymentInfo' => 'user',
|
21 |
+
);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Get payment details.
|
26 |
+
*/
|
27 |
+
public static function getPaymentDetails()
|
28 |
+
{
|
29 |
+
$payment = Lib\Entities\Payment::find( self::parameter( 'payment_id' ) );
|
30 |
+
if ( $payment ) {
|
31 |
+
$data = $payment->getPaymentData();
|
32 |
+
$show_deposit = Lib\Config::depositPaymentsActive();
|
33 |
+
if ( ! $show_deposit ) {
|
34 |
+
foreach ( $data['payment']['items'] as $item ) {
|
35 |
+
if ( isset( $item['deposit_format'] ) ) {
|
36 |
+
$show_deposit = true;
|
37 |
+
break;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
$data['show'] = array(
|
43 |
+
'coupons' => Lib\Config::couponsActive(),
|
44 |
+
'customer_groups' => Lib\Config::customerGroupsActive(),
|
45 |
+
'deposit' => (int) $show_deposit,
|
46 |
+
'gateway' => \Bookly\Backend\Modules\Payments\Proxy\Shared::paymentSpecificPriceExists( $data['payment']['type'] ) === true,
|
47 |
+
'taxes' => (int) ( Lib\Config::taxesActive() || $data['payment']['tax_total'] > 0 ),
|
48 |
+
);
|
49 |
+
wp_send_json_success( array( 'html' => self::renderTemplate( 'details', $data, false ) ) );
|
50 |
+
}
|
51 |
+
|
52 |
+
wp_send_json_error( array( 'html' => __( 'Payment is not found.', 'bookly' ) ) );
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Complete payment.
|
57 |
+
*/
|
58 |
+
public static function completePayment()
|
59 |
+
{
|
60 |
+
$payment = Lib\Entities\Payment::find( self::parameter( 'payment_id' ) );
|
61 |
+
$details = json_decode( $payment->getDetails(), true );
|
62 |
+
$details['tax_paid'] = $payment->getTax();
|
63 |
+
$payment
|
64 |
+
->setPaid( $payment->getTotal() )
|
65 |
+
->setStatus( Lib\Entities\Payment::STATUS_COMPLETED )
|
66 |
+
->setDetails( json_encode( $details ) )
|
67 |
+
->save();
|
68 |
+
|
69 |
+
$payment_title = Lib\Utils\Price::format( $payment->getPaid() );
|
70 |
+
if ( $payment->getPaid() != $payment->getTotal() ) {
|
71 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $payment->getTotal() ) );
|
72 |
+
}
|
73 |
+
$payment_title .= sprintf(
|
74 |
+
' %s <span%s>%s</span>',
|
75 |
+
Lib\Entities\Payment::typeToString( $payment->getType() ),
|
76 |
+
$payment->getStatus() == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
77 |
+
Lib\Entities\Payment::statusToString( $payment->getStatus() )
|
78 |
+
);
|
79 |
+
|
80 |
+
wp_send_json_success( array( 'payment_title' => $payment_title ) );
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Get payment info
|
85 |
+
*/
|
86 |
+
public static function getPaymentInfo()
|
87 |
+
{
|
88 |
+
$payment = Lib\Entities\Payment::find( self::parameter( 'payment_id' ) );
|
89 |
+
|
90 |
+
if ( $payment ) {
|
91 |
+
$payment_title = Lib\Utils\Price::format( $payment->getPaid() );
|
92 |
+
if ( $payment->getPaid() != $payment->getTotal() ) {
|
93 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $payment->getTotal() ) );
|
94 |
+
}
|
95 |
+
$payment_title .= sprintf(
|
96 |
+
' %s <span%s>%s</span>',
|
97 |
+
Lib\Entities\Payment::typeToString( $payment->getType() ),
|
98 |
+
$payment->getStatus() == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
99 |
+
Lib\Entities\Payment::statusToString( $payment->getStatus() )
|
100 |
+
);
|
101 |
+
|
102 |
+
wp_send_json_success( array( 'payment_title' => $payment_title, 'payment_type' => $payment->getPaid() == $payment->getTotal() ? 'full' : 'partial' ) );
|
103 |
+
}
|
104 |
+
}
|
105 |
+
}
|
backend/components/dialogs/payment/Dialog.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\Payment;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Details
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\Payment
|
9 |
+
*/
|
10 |
+
class Dialog extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render payment details dialog.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
19 |
+
) );
|
20 |
+
|
21 |
+
self::enqueueScripts( array(
|
22 |
+
'backend' => array( 'js/angular.min.js' => array( 'jquery' ), ),
|
23 |
+
'frontend' => array(
|
24 |
+
'js/spin.min.js' => array( 'jquery' ),
|
25 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
26 |
+
),
|
27 |
+
'module' => array( 'js/ng-payment_details.js' => array( 'bookly-angular.min.js' ), ),
|
28 |
+
) );
|
29 |
+
|
30 |
+
self::renderTemplate( 'dialog' );
|
31 |
+
}
|
32 |
+
}
|
backend/components/dialogs/payment/resources/js/ng-payment_details.js
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
;(function() {
|
2 |
+
|
3 |
+
angular.module('paymentDetailsDialog', []).directive('paymentDetailsDialog', function() {
|
4 |
+
return {
|
5 |
+
restrict: 'A',
|
6 |
+
replace: true,
|
7 |
+
scope: {
|
8 |
+
callback: '&paymentDetailsDialog'
|
9 |
+
},
|
10 |
+
templateUrl: 'bookly-payment-details-dialog.tpl',
|
11 |
+
// The linking function will add behavior to the template.
|
12 |
+
link: function (scope, element, attrs) {
|
13 |
+
var $body = element.find('.modal-body'),
|
14 |
+
spinner = $body.html();
|
15 |
+
|
16 |
+
element
|
17 |
+
.on('show.bs.modal refresh', function (e, payment_id) {
|
18 |
+
if (payment_id === undefined) {
|
19 |
+
if (e.relatedTarget) {
|
20 |
+
payment_id = e.relatedTarget.getAttribute('data-payment_id');
|
21 |
+
var payment_bind = e.relatedTarget.getAttribute('data-payment_bind'),
|
22 |
+
customer_id = e.relatedTarget.getAttribute('data-customer_id');
|
23 |
+
} else if (element.data('payment_id')) {
|
24 |
+
payment_id = element.data('payment_id');
|
25 |
+
var payment_bind = element.data('payment_bind'),
|
26 |
+
customer_id = element.data('customer_id');
|
27 |
+
}
|
28 |
+
}
|
29 |
+
jQuery.ajax({
|
30 |
+
url: ajaxurl,
|
31 |
+
data: {action: 'bookly_get_payment_details', payment_id: payment_id, csrf_token : BooklyL10n.csrf_token},
|
32 |
+
dataType: 'json',
|
33 |
+
success: function (response) {
|
34 |
+
if (response.success) {
|
35 |
+
$body.html(response.data.html);
|
36 |
+
if (payment_bind) {
|
37 |
+
jQuery('.bookly-js-details-main-controls').hide();
|
38 |
+
jQuery('.bookly-js-details-bind-controls').show();
|
39 |
+
}
|
40 |
+
$body.find('#bookly-complete-payment').on('click',function () {
|
41 |
+
var ladda = Ladda.create(this);
|
42 |
+
ladda.start();
|
43 |
+
jQuery.ajax({
|
44 |
+
url: ajaxurl,
|
45 |
+
data: {action: 'bookly_complete_payment', payment_id: payment_id, csrf_token : BooklyL10n.csrf_token},
|
46 |
+
dataType: 'json',
|
47 |
+
type: 'POST',
|
48 |
+
success: function (response) {
|
49 |
+
if (response.success) {
|
50 |
+
element.trigger('refresh', [payment_id]);
|
51 |
+
if (scope.callback) {
|
52 |
+
scope.$apply(function ($scope) {
|
53 |
+
$scope.callback({
|
54 |
+
payment_action: 'complete',
|
55 |
+
payment_id : payment_id,
|
56 |
+
payment_title : response.data.payment_title
|
57 |
+
});
|
58 |
+
});
|
59 |
+
}
|
60 |
+
// Reload DataTable.
|
61 |
+
var $table = jQuery('table#bookly-payments-list.dataTable');
|
62 |
+
if ($table.length) {
|
63 |
+
$table.DataTable().ajax.reload();
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
});
|
68 |
+
});
|
69 |
+
jQuery('#bookly-js-attach-payment', $body).on('click', function () {
|
70 |
+
var ladda = Ladda.create(this);
|
71 |
+
ladda.start();
|
72 |
+
|
73 |
+
jQuery.ajax({
|
74 |
+
url : ajaxurl,
|
75 |
+
data : {action: 'bookly_get_payment_info', payment_id: payment_id, csrf_token: BooklyL10n.csrf_token},
|
76 |
+
dataType: 'json',
|
77 |
+
type : 'POST',
|
78 |
+
success : function (response) {
|
79 |
+
if (response.success) {
|
80 |
+
if (scope.callback) {
|
81 |
+
scope.$apply(function ($scope) {
|
82 |
+
$scope.callback({
|
83 |
+
payment_action: 'bind',
|
84 |
+
payment_id : payment_id,
|
85 |
+
payment_title : response.data.payment_title,
|
86 |
+
payment_type : response.data.payment_type,
|
87 |
+
customer_id : customer_id
|
88 |
+
});
|
89 |
+
});
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
});
|
94 |
+
jQuery(element).modal('hide');
|
95 |
+
});
|
96 |
+
var $adjust_button = jQuery('#bookly-js-adjustment-button', $body),
|
97 |
+
$adjust_field = jQuery('#bookly-js-adjustment-field', $body),
|
98 |
+
$adjust_reason = jQuery('#bookly-js-adjustment-reason', $body),
|
99 |
+
$adjust_amount = jQuery('#bookly-js-adjustment-amount', $body),
|
100 |
+
$adjust_tax = jQuery('#bookly-js-adjustment-tax', $body),
|
101 |
+
$adjust_apply = jQuery('#bookly-js-adjustment-apply', $body),
|
102 |
+
$adjust_cancel = jQuery('#bookly-js-adjustment-cancel', $body);
|
103 |
+
$adjust_button.on('click', function () {
|
104 |
+
$adjust_field.show();
|
105 |
+
$adjust_reason.focus();
|
106 |
+
});
|
107 |
+
$adjust_cancel.on('click', function () {
|
108 |
+
$adjust_field.hide();
|
109 |
+
});
|
110 |
+
$adjust_apply.on('click', function () {
|
111 |
+
$body.html('<div class="bookly-loading"></div>');
|
112 |
+
jQuery.ajax({
|
113 |
+
url : ajaxurl,
|
114 |
+
data : {
|
115 |
+
action : 'bookly_pro_add_payment_adjustment',
|
116 |
+
payment_id: payment_id,
|
117 |
+
reason: $adjust_reason.val(),
|
118 |
+
amount: $adjust_amount.val(),
|
119 |
+
tax : $adjust_tax.val() || 0,
|
120 |
+
csrf_token: BooklyL10n.csrf_token
|
121 |
+
},
|
122 |
+
dataType: 'json',
|
123 |
+
type : 'POST',
|
124 |
+
success : function (response) {
|
125 |
+
if (response.success) {
|
126 |
+
element.trigger('refresh', [payment_id]);
|
127 |
+
// Reload DataTable.
|
128 |
+
var $table = jQuery('table#bookly-payments-list.dataTable');
|
129 |
+
if ($table.length) {
|
130 |
+
$table.DataTable().ajax.reload();
|
131 |
+
}
|
132 |
+
}
|
133 |
+
}
|
134 |
+
});
|
135 |
+
});
|
136 |
+
} else {
|
137 |
+
$body.html(response.data.html);
|
138 |
+
}
|
139 |
+
}
|
140 |
+
});
|
141 |
+
})
|
142 |
+
.on('hidden.bs.modal', function () {
|
143 |
+
$body.html(spinner);
|
144 |
+
if ((jQuery("#bookly-appointment-dialog").data('bs.modal') || {isShown: false}).isShown) {
|
145 |
+
jQuery('body').addClass('modal-open');
|
146 |
+
}
|
147 |
+
});
|
148 |
+
}
|
149 |
+
}
|
150 |
+
});
|
151 |
+
})();
|
backend/components/dialogs/payment/templates/details.php
ADDED
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Modules\Payments\Proxy;
|
4 |
+
use Bookly\Lib\Utils\Price;
|
5 |
+
use Bookly\Lib\Utils\DateTime;
|
6 |
+
use Bookly\Lib\Entities;
|
7 |
+
use Bookly\Lib\Config;
|
8 |
+
/** @var array $show = ['deposit' => int, 'taxes' => int, 'gateway' => bool, 'customer_groups' => bool, 'coupons' => bool] */
|
9 |
+
?>
|
10 |
+
<?php if ( $payment ) : ?>
|
11 |
+
<div class="table-responsive">
|
12 |
+
<table class="table table-bordered">
|
13 |
+
<thead>
|
14 |
+
<tr>
|
15 |
+
<th width="50%"><?php _e( 'Customer', 'bookly' ) ?></th>
|
16 |
+
<th width="50%"><?php _e( 'Payment', 'bookly' ) ?></th>
|
17 |
+
</tr>
|
18 |
+
</thead>
|
19 |
+
<tbody>
|
20 |
+
<tr>
|
21 |
+
<td><?php echo esc_html( $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>
|
29 |
+
</table>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div class="table-responsive">
|
33 |
+
<table class="table table-bordered">
|
34 |
+
<thead>
|
35 |
+
<tr>
|
36 |
+
<th><?php _e( 'Service', 'bookly' ) ?></th>
|
37 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
38 |
+
<th><?php _e( 'Provider', 'bookly' ) ?></th>
|
39 |
+
<?php if ( $show['deposit'] ): ?>
|
40 |
+
<th class="text-right"><?php _e( 'Deposit', 'bookly' ) ?></th>
|
41 |
+
<?php endif ?>
|
42 |
+
<th class="text-right"><?php _e( 'Price', 'bookly' ) ?></th>
|
43 |
+
<?php if ( $show['taxes'] ): ?>
|
44 |
+
<th class="text-right"><?php _e( 'Tax', 'bookly' ) ?></th>
|
45 |
+
<?php endif ?>
|
46 |
+
</tr>
|
47 |
+
</thead>
|
48 |
+
<tbody>
|
49 |
+
<?php foreach ( $payment['items'] as $item ) : ?>
|
50 |
+
<tr>
|
51 |
+
<td>
|
52 |
+
<?php if ( $item['number_of_persons'] > 1 ) echo $item['number_of_persons'] . ' × ' ?><?php echo esc_html( $item['service_name'] ) ?><?php if ( isset( $item['units'], $item['duration'] ) && $item['units'] > 1 ) echo ' (' . DateTime::secondsToInterval( $item['units'] * $item['duration'] ) . ')' ?>
|
53 |
+
<?php if ( ! empty ( $item['extras'] ) ) : ?>
|
54 |
+
<ul class="bookly-list list-dots">
|
55 |
+
<?php foreach ( $item['extras'] as $extra ) : ?>
|
56 |
+
<li><?php if ( $extra['quantity'] > 1 ) echo $extra['quantity'] . ' × ' ?><?php echo esc_html( $extra['title'] ) ?></li>
|
57 |
+
<?php endforeach ?>
|
58 |
+
</ul>
|
59 |
+
<?php endif ?>
|
60 |
+
</td>
|
61 |
+
<td><?php echo $item['appointment_date'] === null ? __( 'N/A', 'bookly' ) : DateTime::formatDateTime( $item['appointment_date'] ) ?></td>
|
62 |
+
<td><?php echo esc_html( $item['staff_name'] ) ?></td>
|
63 |
+
<?php if ( $show['deposit'] ) : ?>
|
64 |
+
<td class="text-right"><?php echo $item['deposit_format'] ?></td>
|
65 |
+
<?php endif ?>
|
66 |
+
<td class="text-right">
|
67 |
+
<?php $service_price = Price::format( $item['service_price'] ) ?>
|
68 |
+
<?php if ( $payment['from_backend'] ) : ?>
|
69 |
+
<?php echo $service_price ?>
|
70 |
+
<?php else : ?>
|
71 |
+
<?php if ( $item['number_of_persons'] > 1 ) $service_price = $item['number_of_persons'] . ' × ' . $service_price ?>
|
72 |
+
<?php echo $service_price ?>
|
73 |
+
<ul class="bookly-list">
|
74 |
+
<?php foreach ( $item['extras'] as $extra ) : ?>
|
75 |
+
<li>
|
76 |
+
<?php printf( '%s%s%s',
|
77 |
+
( $item['number_of_persons'] > 1 && $payment['extras_multiply_nop'] ) ? $item['number_of_persons'] . ' × ' : '',
|
78 |
+
( $extra['quantity'] > 1 ) ? $extra['quantity'] . ' × ' : '',
|
79 |
+
Price::format( $extra['price'] )
|
80 |
+
) ?>
|
81 |
+
</li>
|
82 |
+
<?php endforeach ?>
|
83 |
+
</ul>
|
84 |
+
<?php endif ?>
|
85 |
+
</td>
|
86 |
+
<?php if ( $show['taxes'] ) : ?>
|
87 |
+
<td class="text-right"><?php echo $item['service_tax'] !== null
|
88 |
+
? sprintf( $payment['tax_in_price'] == 'included' ? '(%s)' : '%s', Price::format( $item['service_tax'] ) )
|
89 |
+
: '-' ?></td>
|
90 |
+
<?php endif ?>
|
91 |
+
</tr>
|
92 |
+
<?php endforeach ?>
|
93 |
+
</tbody>
|
94 |
+
<tfoot>
|
95 |
+
<tr>
|
96 |
+
<th style="border-left-color: white; border-bottom-color: white;"></th>
|
97 |
+
<th colspan="2"><?php _e( 'Subtotal', 'bookly' ) ?></th>
|
98 |
+
<?php if ( $show['deposit'] ) : ?>
|
99 |
+
<th class="text-right"><?php echo Price::format( $payment['subtotal']['deposit'] ) ?></th>
|
100 |
+
<?php endif ?>
|
101 |
+
<th class="text-right"><?php echo Price::format( $payment['subtotal']['price'] ) ?></th>
|
102 |
+
<?php if ( $show['taxes'] ) : ?><th></th><?php endif ?>
|
103 |
+
</tr>
|
104 |
+
<?php if ( $show['coupons'] || $payment['coupon'] ) : ?>
|
105 |
+
<tr>
|
106 |
+
<th style="border-left-color: white; border-bottom-color: white;"></th>
|
107 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>">
|
108 |
+
<?php _e( 'Coupon discount', 'bookly' ) ?>
|
109 |
+
<?php if ( $payment['coupon'] ) : ?><div><small>(<?php echo $payment['coupon']['code'] ?>)</small></div><?php endif ?>
|
110 |
+
</th>
|
111 |
+
<th class="text-right">
|
112 |
+
<?php if ( $payment['coupon'] ) : ?>
|
113 |
+
<?php if ( $payment['coupon']['discount'] ) : ?>
|
114 |
+
<div><?php echo $payment['coupon']['discount'] ?>%</div>
|
115 |
+
<?php endif ?>
|
116 |
+
<?php if ( $payment['coupon']['deduction'] ) : ?>
|
117 |
+
<div><?php echo Price::format( $payment['coupon']['deduction'] ) ?></div>
|
118 |
+
<?php endif ?>
|
119 |
+
<?php else : ?>
|
120 |
+
<?php echo Price::format( 0 ) ?>
|
121 |
+
<?php endif ?>
|
122 |
+
</th>
|
123 |
+
<?php if ( $show['taxes'] ) : ?>
|
124 |
+
<th></th>
|
125 |
+
<?php endif ?>
|
126 |
+
</tr>
|
127 |
+
<?php endif ?>
|
128 |
+
<?php if ( $show['customer_groups'] || $payment['group_discount'] ) : ?>
|
129 |
+
<tr>
|
130 |
+
<th style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
131 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>">
|
132 |
+
<?php _e( 'Group discount', 'bookly' ) ?>
|
133 |
+
</th>
|
134 |
+
<th class="text-right">
|
135 |
+
<?php echo $payment['group_discount'] ?: Price::format( 0 ) ?>
|
136 |
+
</th>
|
137 |
+
<?php if ( $show['taxes'] ) : ?><th></th><?php endif ?>
|
138 |
+
</tr>
|
139 |
+
<?php endif ?>
|
140 |
+
<?php foreach ( $adjustments as $adjustment ) : ?>
|
141 |
+
<tr>
|
142 |
+
<th style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
143 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>">
|
144 |
+
<?php echo esc_html( $adjustment['reason'] ) ?>
|
145 |
+
</th>
|
146 |
+
<th class="text-right"><?php echo Price::format( $adjustment['amount'] ) ?></th>
|
147 |
+
<?php if ( $show['taxes'] ) : ?>
|
148 |
+
<th class="text-right"><?php echo Price::format( $adjustment['tax'] ) ?></th>
|
149 |
+
<?php endif ?>
|
150 |
+
</tr>
|
151 |
+
<?php endforeach ?>
|
152 |
+
|
153 |
+
<?php Proxy\Pro::renderManualAdjustmentForm( $show ) ?>
|
154 |
+
|
155 |
+
<?php if ( $show['gateway'] || (float) $payment['price_correction'] ) : ?>
|
156 |
+
<tr>
|
157 |
+
<th style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
158 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>">
|
159 |
+
<?php echo Entities\Payment::typeToString( $payment['type'] ) ?>
|
160 |
+
</th>
|
161 |
+
<th class="text-right">
|
162 |
+
<?php echo Price::format( $payment['price_correction'] ) ?>
|
163 |
+
</th>
|
164 |
+
<?php if ( $show['taxes'] ) : ?>
|
165 |
+
<td class="text-right">-</td>
|
166 |
+
<?php endif ?>
|
167 |
+
</tr>
|
168 |
+
<?php endif ?>
|
169 |
+
<tr>
|
170 |
+
<th style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
171 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>"><?php _e( 'Total', 'bookly' ) ?></th>
|
172 |
+
<th class="text-right"><?php echo Price::format( $payment['total'] ) ?></th>
|
173 |
+
<?php if ( $show['taxes'] ) : ?>
|
174 |
+
<th class="text-right">
|
175 |
+
(<?php echo Price::format( $payment['tax_total'] ) ?>)
|
176 |
+
</th>
|
177 |
+
<?php endif ?>
|
178 |
+
</tr>
|
179 |
+
<?php if ( $payment['total'] != $payment['paid'] ) : ?>
|
180 |
+
<tr>
|
181 |
+
<th rowspan="2" style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
182 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>"><i><?php _e( 'Paid', 'bookly' ) ?></i></th>
|
183 |
+
<th class="text-right"><i><?php echo Price::format( $payment['paid'] ) ?></i></th>
|
184 |
+
<?php if ( $show['taxes'] ) : ?>
|
185 |
+
<th class="text-right"><i>(<?php echo Price::format( $payment['tax_paid'] ) ?>)</i></th>
|
186 |
+
<?php endif ?>
|
187 |
+
</tr>
|
188 |
+
<tr>
|
189 |
+
<th colspan="<?php echo 2 + $show['deposit'] ?>"><i><?php _e( 'Due', 'bookly' ) ?></i></th>
|
190 |
+
<th class="text-right">
|
191 |
+
<i><?php echo Price::format( $payment['total'] - $payment['paid'] ) ?></i>
|
192 |
+
</th>
|
193 |
+
<?php if ( $show['taxes'] ) : ?>
|
194 |
+
<th class="text-right"><i>(<?php echo Price::format( $payment['tax_total'] - $payment['tax_paid'] ) ?>)</i></th>
|
195 |
+
<?php endif ?>
|
196 |
+
</tr>
|
197 |
+
<?php endif ?>
|
198 |
+
<?php if ( Config::proActive() || ( $payment['total'] != $payment['paid'] ) ) : ?>
|
199 |
+
<tr>
|
200 |
+
<th style="border-left-color:#fff;border-bottom-color:#fff;"></th>
|
201 |
+
<th colspan="<?php echo 3 + $show['deposit'] + $show['taxes'] ?>" class="text-right">
|
202 |
+
<div class="bookly-js-details-main-controls">
|
203 |
+
<?php Proxy\Pro::renderManualAdjustmentButton() ?>
|
204 |
+
<?php if ( $payment['total'] != $payment['paid'] ) : ?>
|
205 |
+
<button type="button" class="btn btn-success ladda-button" id="bookly-complete-payment" data-spinner-size="40" data-style="zoom-in"><i><?php _e( 'Complete payment', 'bookly' ) ?></i></button>
|
206 |
+
<?php endif ?>
|
207 |
+
</div>
|
208 |
+
<div class="bookly-js-details-bind-controls collapse">
|
209 |
+
<?php Buttons::renderCustom( 'bookly-js-attach-payment', 'btn-success', __( 'Bind payment', 'bookly' ) ) ?>
|
210 |
+
</div>
|
211 |
+
</th>
|
212 |
+
</tr>
|
213 |
+
<?php endif ?>
|
214 |
+
</tfoot>
|
215 |
+
</table>
|
216 |
+
</div>
|
217 |
+
<?php endif ?>
|
backend/{modules/payments/templates/_payment_details_dialog.php → components/dialogs/payment/templates/dialog.php}
RENAMED
@@ -1,4 +1,6 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
|
2 |
<script type="text/ng-template" id="bookly-payment-details-dialog.tpl">
|
3 |
<div class="modal fade" id="bookly-payment-details-modal" tabindex="-1" role="dialog">
|
4 |
<div class="modal-dialog" role="document">
|
@@ -11,7 +13,7 @@
|
|
11 |
<div class="bookly-loading"></div>
|
12 |
</div>
|
13 |
<div class="modal-footer">
|
14 |
-
<?php
|
15 |
</div>
|
16 |
</div>
|
17 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
?>
|
4 |
<script type="text/ng-template" id="bookly-payment-details-dialog.tpl">
|
5 |
<div class="modal fade" id="bookly-payment-details-modal" tabindex="-1" role="dialog">
|
6 |
<div class="modal-dialog" role="document">
|
13 |
<div class="bookly-loading"></div>
|
14 |
</div>
|
15 |
<div class="modal-footer">
|
16 |
+
<?php Buttons::renderCustom( null, 'btn-lg btn-default', __( 'Close', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
17 |
</div>
|
18 |
</div>
|
19 |
</div>
|
backend/components/dialogs/special_price/proxy/SpecialHours.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Dialogs\SpecialPrice\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib as BooklyLib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class SpecialHours
|
8 |
+
* @package Bookly\Backend\Components\Dialogs\SpecialPrice\ProxyProviders
|
9 |
+
*
|
10 |
+
* @method static void renderSpecialPricePopup( int $staff_id ) Render popup for special price.
|
11 |
+
*/
|
12 |
+
abstract class SpecialHours extends BooklyLib\Base\Proxy
|
13 |
+
{
|
14 |
+
}
|
backend/components/notices/CollectStats.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CollectStats
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class CollectStats extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render collect stats notice.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
if ( self::needShowCollectStatNotice() ) {
|
18 |
+
self::enqueueStyles( array(
|
19 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
20 |
+
) );
|
21 |
+
self::enqueueScripts( array(
|
22 |
+
'module' => array( 'js/collect-stats.js' => array( 'jquery' ), ),
|
23 |
+
) );
|
24 |
+
|
25 |
+
self::renderTemplate( 'collect_stats', array( 'enabled' => get_option( 'bookly_gen_collect_stats' ) == '1' ) );
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @return bool
|
31 |
+
*/
|
32 |
+
public static function needShowCollectStatNotice()
|
33 |
+
{
|
34 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
|
35 |
+
$enabled = get_option( 'bookly_gen_collect_stats' ) == '1';
|
36 |
+
$user_id = get_current_user_id();
|
37 |
+
if (
|
38 |
+
$enabled && get_user_meta( $user_id, 'bookly_show_collecting_stats_notice', true ) ||
|
39 |
+
! $enabled && ! get_user_meta( $user_id, 'bookly_dismiss_collect_stats_notice', true )
|
40 |
+
) {
|
41 |
+
return true;
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
}
|
backend/components/notices/CollectStatsAjax.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CollectStatsAjax
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class CollectStatsAjax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Dismiss 'Collecting stats' notice.
|
14 |
+
*/
|
15 |
+
public static function dismissCollectingStatsNotice()
|
16 |
+
{
|
17 |
+
delete_user_meta( get_current_user_id(), 'bookly_show_collecting_stats_notice' );
|
18 |
+
|
19 |
+
wp_send_json_success();
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Dismiss 'Collect stats' notice.
|
24 |
+
*/
|
25 |
+
public static function dismissCollectStatsNotice()
|
26 |
+
{
|
27 |
+
update_user_meta( get_current_user_id(), 'bookly_dismiss_collect_stats_notice', 1 );
|
28 |
+
|
29 |
+
wp_send_json_success();
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Enable collecting stats.
|
34 |
+
*/
|
35 |
+
public static function enableCollectingStats()
|
36 |
+
{
|
37 |
+
update_option( 'bookly_gen_collect_stats', '1' );
|
38 |
+
|
39 |
+
wp_send_json_success();
|
40 |
+
}
|
41 |
+
}
|
backend/components/notices/Limitation.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Limitation
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class Limitation extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render limitation notice.
|
14 |
+
*/
|
15 |
+
public static function getHtml()
|
16 |
+
{
|
17 |
+
return self::renderTemplate( 'limitation', array(), false );
|
18 |
+
}
|
19 |
+
}
|
backend/components/notices/LiteRebranding.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class LiteRebranding
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class LiteRebranding extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render subscribe notice.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() &&
|
18 |
+
get_user_meta( get_current_user_id(), 'bookly_show_lite_rebranding_notice', true ) ) {
|
19 |
+
|
20 |
+
self::enqueueScripts( array(
|
21 |
+
'module' => array( 'js/lite-rebranding.js' => array( 'jquery' ), ),
|
22 |
+
) );
|
23 |
+
|
24 |
+
self::renderTemplate( 'lite_rebranding' );
|
25 |
+
}
|
26 |
+
}
|
27 |
+
}
|
backend/components/notices/LiteRebrandingAjax.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class LiteRebrandingAjax
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class LiteRebrandingAjax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Dismiss 'Lite Rebranding' notice.
|
14 |
+
*/
|
15 |
+
public static function dismissLiteRebrandingNotice()
|
16 |
+
{
|
17 |
+
delete_user_meta( get_current_user_id(), 'bookly_show_lite_rebranding_notice' );
|
18 |
+
|
19 |
+
wp_send_json_success();
|
20 |
+
}
|
21 |
+
}
|
backend/components/notices/Nps.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Nps
|
9 |
+
* @package Bookly\Backend\Components\Notices
|
10 |
+
*/
|
11 |
+
class Nps extends Lib\Base\Component
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Render Net Promoter Score notice.
|
15 |
+
*/
|
16 |
+
public static function render()
|
17 |
+
{
|
18 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
|
19 |
+
$dismiss_value = get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_nps_notice', true );
|
20 |
+
// Show notice 1 month after it was closed the last time.
|
21 |
+
if ( ! $dismiss_value || $dismiss_value > 1 && time() - $dismiss_value >= 30 * DAY_IN_SECONDS ) {
|
22 |
+
// Show notice 1 month after installation time.
|
23 |
+
if ( time() - Lib\Plugin::getInstallationTime() >= 30 * DAY_IN_SECONDS ) {
|
24 |
+
self::enqueueStyles( array(
|
25 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
26 |
+
'module' => array( 'css/bootstrap-stars.css', ),
|
27 |
+
) );
|
28 |
+
|
29 |
+
self::enqueueScripts( array(
|
30 |
+
'backend' => array(
|
31 |
+
'js/alert.js' => array( 'jquery' ),
|
32 |
+
),
|
33 |
+
'frontend' => array(
|
34 |
+
'js/spin.min.js' => array( 'jquery' ),
|
35 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
+
),
|
37 |
+
'module' => array(
|
38 |
+
'js/jquery.barrating.min.js' => array( 'jquery' ),
|
39 |
+
'js/nps.js' => array( 'bookly-jquery.barrating.min.js', 'bookly-alert.js', 'bookly-ladda.min.js', ),
|
40 |
+
),
|
41 |
+
) );
|
42 |
+
|
43 |
+
self::renderTemplate( 'nps', array( 'current_user' => wp_get_current_user() ) );
|
44 |
+
}
|
45 |
+
}
|
46 |
+
}
|
47 |
+
}
|
48 |
+
}
|
backend/components/notices/NpsAjax.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class NpsAjax
|
9 |
+
* @package Bookly\Backend\Components\Notices
|
10 |
+
*/
|
11 |
+
class NpsAjax extends Lib\Base\Ajax
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Send Net Promoter Score.
|
15 |
+
*/
|
16 |
+
public static function npsSend()
|
17 |
+
{
|
18 |
+
$rate = self::parameter( 'rate' );
|
19 |
+
$msg = self::parameter( 'msg', '' );
|
20 |
+
$email = self::parameter( 'email', '' );
|
21 |
+
|
22 |
+
Lib\API::sendNps( $rate, $msg, $email );
|
23 |
+
|
24 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_nps_notice', 1 );
|
25 |
+
|
26 |
+
wp_send_json_success( array( 'message' => __( 'Sent successfully.', 'bookly' ) ) );
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Dismiss NPS notice.
|
31 |
+
*/
|
32 |
+
public static function dismissNpsNotice()
|
33 |
+
{
|
34 |
+
if ( get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_nps_notice', true ) != 1 ) {
|
35 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_nps_notice', time() );
|
36 |
+
}
|
37 |
+
|
38 |
+
wp_send_json_success();
|
39 |
+
}
|
40 |
+
}
|
backend/components/notices/Subscribe.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Subscribe
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class Subscribe extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render subscribe notice.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() &&
|
18 |
+
! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_subscribe_notice', true ) ) {
|
19 |
+
|
20 |
+
// Show notice 1 day after installation time.
|
21 |
+
if ( time() - Lib\Plugin::getInstallationTime() >= DAY_IN_SECONDS ) {
|
22 |
+
self::enqueueStyles( array(
|
23 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
24 |
+
) );
|
25 |
+
self::enqueueScripts( array(
|
26 |
+
'backend' => array( 'js/alert.js' => array( 'jquery' ), ),
|
27 |
+
'frontend' => array(
|
28 |
+
'js/spin.min.js' => array( 'jquery' ),
|
29 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
30 |
+
),
|
31 |
+
'module' => array( 'js/subscribe.js' => array( 'bookly-alert.js', 'bookly-ladda.min.js', ), ),
|
32 |
+
) );
|
33 |
+
|
34 |
+
self::renderTemplate( 'subscribe' );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
}
|
backend/components/notices/SubscribeAjax.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class SubscribeAjax
|
8 |
+
* @package Bookly\Backend\Components\Notices
|
9 |
+
*/
|
10 |
+
class SubscribeAjax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Subscribe to monthly emails.
|
14 |
+
*/
|
15 |
+
public static function subscribe()
|
16 |
+
{
|
17 |
+
$email = self::parameter( 'email' );
|
18 |
+
if ( is_email( $email ) ) {
|
19 |
+
Lib\API::registerSubscriber( $email );
|
20 |
+
|
21 |
+
wp_send_json_success( array( 'message' => __( 'Sent successfully.', 'bookly' ) ) );
|
22 |
+
} else {
|
23 |
+
wp_send_json_error( array( 'message' => __( 'Invalid email.', 'bookly' ) ) );
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Dismiss subscribe notice.
|
29 |
+
*/
|
30 |
+
public static function dismissSubscribeNotice()
|
31 |
+
{
|
32 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_subscribe_notice', 1 );
|
33 |
+
|
34 |
+
wp_send_json_success();
|
35 |
+
}
|
36 |
+
}
|
backend/components/notices/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Notices\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Notices\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderWelcome()
|
11 |
+
*/
|
12 |
+
abstract class Pro extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/{modules/support → components/notices}/resources/css/bootstrap-stars.css
RENAMED
File without changes
|
backend/components/notices/resources/js/collect-stats.js
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function ($) {
|
2 |
+
var $notice = $('#bookly-collect-stats-notice');
|
3 |
+
$notice.on('close.bs.alert', function () {
|
4 |
+
$.post(ajaxurl, {action: $notice.data('action'), csrf_token : SupportL10n.csrf_token});
|
5 |
+
});
|
6 |
+
$notice.find('#bookly-enable-collecting-stats-btn').on('click', function () {
|
7 |
+
$.post(ajaxurl, {action: 'bookly_enable_collecting_stats', csrf_token : SupportL10n.csrf_token});
|
8 |
+
});
|
9 |
+
});
|
backend/{modules/support → components/notices}/resources/js/jquery.barrating.min.js
RENAMED
File without changes
|
backend/components/notices/resources/js/lite-rebranding.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function ($) {
|
2 |
+
var $notice = $('#bookly-lite-rebranding-notice');
|
3 |
+
$notice.on('close.bs.alert', function () {
|
4 |
+
$.post(ajaxurl, {action: $notice.data('action'), csrf_token : SupportL10n.csrf_token});
|
5 |
+
});
|
6 |
+
});
|
backend/{modules/support → components/notices}/resources/js/nps.js
RENAMED
File without changes
|
backend/{modules/support → components/notices}/resources/js/subscribe.js
RENAMED
File without changes
|
backend/components/notices/templates/collect_stats.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div id="bookly-collect-stats-notice" class="alert alert-info bookly-tbs-body bookly-flexbox" data-action="bookly_dismiss_collect<?php if ( $enabled ): ?>ing<?php endif ?>_stats_notice">
|
4 |
+
<div class="bookly-flex-row">
|
5 |
+
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
6 |
+
<div class="bookly-flex-cell">
|
7 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
8 |
+
<?php if ( $enabled ): ?>
|
9 |
+
<?php esc_html_e( 'To help us improve Bookly, the plugin anonymously collects usage information. You can opt out of sharing the information in Settings > General.', 'bookly' ) ?>
|
10 |
+
<div class="bookly-margin-top-md">
|
11 |
+
<button type="button" class="btn btn-primary" data-dismiss="alert"><?php esc_html_e( 'Close', 'bookly' ) ?></button>
|
12 |
+
</div>
|
13 |
+
<?php else: ?>
|
14 |
+
<?php esc_html_e( 'Let the plugin anonymously collect usage information to help Bookly team improve the product.', 'bookly' ) ?>
|
15 |
+
<div class="bookly-margin-top-md">
|
16 |
+
<button type="button" class="btn btn-default" data-dismiss="alert"><?php esc_html_e( 'Disagree', 'bookly' ) ?></button>
|
17 |
+
<button type="button" class="btn btn-success" id="bookly-enable-collecting-stats-btn" data-dismiss="alert"><?php esc_html_e( 'Agree', 'bookly' ) ?></button>
|
18 |
+
</div>
|
19 |
+
<?php endif ?>
|
20 |
+
</div>
|
21 |
+
</div>
|
22 |
+
</div>
|
23 |
+
</div>
|
backend/components/notices/templates/limitation.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<b class="h4"><?php _e( 'This function is not available in the Bookly.', 'bookly' ) ?></b>
|
3 |
+
<br><br><?php _e( 'To get access to all Bookly features, lifetime free updates and 24/7 support, please upgrade to the Pro version of Bookly.<br>For more information visit', 'bookly' ) ?> <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://www.booking-wp-plugin.com</a>
|
backend/components/notices/templates/lite_rebranding.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div id="bookly-lite-rebranding-notice" class="alert alert-info bookly-tbs-body bookly-flexbox" data-action="bookly_dismiss_lite_rebranding_notice">
|
4 |
+
<div class="bookly-flex-row">
|
5 |
+
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
6 |
+
<div class="bookly-flex-cell">
|
7 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
8 |
+
<?php printf( __( '<b>Bookly Lite rebrands into Bookly with more features available.</b><br/><br/>We have changed the architecture of Bookly Lite and Bookly to optimize the development of both plugin versions and add more features to the new free Bookly. To learn more about the major Bookly update, check our <a href="%s" target="_blank">blog post</a>.', 'bookly' ), 'https://www.booking-wp-plugin.com/bookly-major-update/?utm_source=bookly_admin&utm_medium=pro_not_active&utm_campaign=notification' ) ?>
|
9 |
+
</div>
|
10 |
+
</div>
|
11 |
+
</div>
|
12 |
+
</div>
|
backend/{modules/support/templates/_nps_notice.php → components/notices/templates/nps.php}
RENAMED
@@ -1,10 +1,14 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
|
|
|
|
|
2 |
<div id="bookly-tbs" class="wrap bookly-js-nps-notice">
|
3 |
<div id="bookly-nps-notice" class="alert alert-info bookly-tbs-body bookly-flexbox">
|
4 |
<div class="bookly-flex-row">
|
5 |
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
6 |
<div class="bookly-flex-cell">
|
7 |
-
<button type="button" class="close" data-dismiss="alert"
|
8 |
<div id="bookly-nps-quiz">
|
9 |
<label><?php _e( 'How likely is it that you would recommend Bookly to a friend or colleague?', 'bookly' ) ?></label>
|
10 |
<select id="bookly-nps-stars" class="hidden">
|
@@ -23,12 +27,12 @@
|
|
23 |
<label for="bookly-nps-email" class="control-label"><?php _e( 'Please enter your email (optional)', 'bookly' ) ?></label>
|
24 |
<input type="text" id="bookly-nps-email" class="form-control" value="<?php echo esc_attr( $current_user->user_email ) ?>" />
|
25 |
</div>
|
26 |
-
<?php
|
27 |
</div>
|
28 |
<div id="bookly-nps-thanks" style="display:none;">
|
29 |
<?php printf(
|
30 |
__( 'Please leave your feedback <a href="%s" target="_blank">here</a>.', 'bookly' ),
|
31 |
-
|
32 |
) ?>
|
33 |
</div>
|
34 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Support\Lib\Urls;
|
4 |
+
use Bookly\Lib\Utils\Common;
|
5 |
+
?>
|
6 |
<div id="bookly-tbs" class="wrap bookly-js-nps-notice">
|
7 |
<div id="bookly-nps-notice" class="alert alert-info bookly-tbs-body bookly-flexbox">
|
8 |
<div class="bookly-flex-row">
|
9 |
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
10 |
<div class="bookly-flex-cell">
|
11 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
12 |
<div id="bookly-nps-quiz">
|
13 |
<label><?php _e( 'How likely is it that you would recommend Bookly to a friend or colleague?', 'bookly' ) ?></label>
|
14 |
<select id="bookly-nps-stars" class="hidden">
|
27 |
<label for="bookly-nps-email" class="control-label"><?php _e( 'Please enter your email (optional)', 'bookly' ) ?></label>
|
28 |
<input type="text" id="bookly-nps-email" class="form-control" value="<?php echo esc_attr( $current_user->user_email ) ?>" />
|
29 |
</div>
|
30 |
+
<?php Buttons::renderCustom( 'bookly-nps-btn', 'btn-success', __( 'Send', 'bookly' ) ) ?>
|
31 |
</div>
|
32 |
<div id="bookly-nps-thanks" style="display:none;">
|
33 |
<?php printf(
|
34 |
__( 'Please leave your feedback <a href="%s" target="_blank">here</a>.', 'bookly' ),
|
35 |
+
Common::prepareUrlReferrers( Urls::BOOKLY_CODECANYON_PAGE, 'nps' )
|
36 |
) ?>
|
37 |
</div>
|
38 |
</div>
|
backend/{modules/support/templates/_subscribe_notice.php → components/notices/templates/subscribe.php}
RENAMED
@@ -4,7 +4,7 @@
|
|
4 |
<div class="bookly-flex-row">
|
5 |
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
6 |
<div class="bookly-flex-cell">
|
7 |
-
<button type="button" class="close" data-dismiss="alert"
|
8 |
<label for="bookly-subscribe-email"><?php _e( 'Subscribe to monthly emails about Bookly improvements and new releases.', 'bookly' ) ?></label>
|
9 |
<div class="input-group input-group-sm" style="max-width: 400px">
|
10 |
<span class="input-group-addon"><i class="glyphicon glyphicon-envelope"></i></span>
|
4 |
<div class="bookly-flex-row">
|
5 |
<div class="bookly-flex-cell" style="width:39px"><i class="alert-icon"></i></div>
|
6 |
<div class="bookly-flex-cell">
|
7 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
8 |
<label for="bookly-subscribe-email"><?php _e( 'Subscribe to monthly emails about Bookly improvements and new releases.', 'bookly' ) ?></label>
|
9 |
<div class="input-group input-group-sm" style="max-width: 400px">
|
10 |
<span class="input-group-addon"><i class="glyphicon glyphicon-envelope"></i></span>
|
backend/components/settings/Image.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Image
|
8 |
+
* @package Bookly\Backend\Components\Settings
|
9 |
+
*/
|
10 |
+
class Image extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render media image attachment.
|
14 |
+
*
|
15 |
+
* @param string $option_name
|
16 |
+
* @param string $class
|
17 |
+
*/
|
18 |
+
public static function render( $option_name, $class = 'lg' )
|
19 |
+
{
|
20 |
+
$img = wp_get_attachment_image_src( get_option( $option_name ), 'full' );
|
21 |
+
|
22 |
+
self::renderTemplate( 'image', array(
|
23 |
+
'option_name' => $option_name,
|
24 |
+
'option_value' => get_option( $option_name ),
|
25 |
+
'class' => $class,
|
26 |
+
'img_style' => $img ? 'background-image: url(' . $img[0] . '); background-size: contain;' : '',
|
27 |
+
'delete_style' => $img ? '' : 'display: none;',
|
28 |
+
) );
|
29 |
+
}
|
30 |
+
}
|
backend/components/settings/Inputs.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Inputs
|
6 |
+
* @package Bookly\Backend\Components\Settings
|
7 |
+
*/
|
8 |
+
class Inputs
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Render numeric input.
|
12 |
+
*
|
13 |
+
* @param string $option_name
|
14 |
+
* @param string $label
|
15 |
+
* @param string $help
|
16 |
+
* @param int|null $min
|
17 |
+
* @param int|null $step
|
18 |
+
* @param int|null $max
|
19 |
+
*/
|
20 |
+
public static function renderNumber( $option_name, $label, $help, $min = null, $step = null, $max = null )
|
21 |
+
{
|
22 |
+
$control = strtr(
|
23 |
+
'<input type="number" id="{name}" class="form-control" name="{name}" value="{value}"{min}{max}{step} />',
|
24 |
+
array(
|
25 |
+
'{name}' => esc_attr( $option_name ),
|
26 |
+
'{value}' => esc_attr( get_option( $option_name ) ),
|
27 |
+
'{min}' => $min !== null ? ' min="' . $min . '"' : '',
|
28 |
+
'{max}' => $max !== null ? ' max="' . $max . '"' : '',
|
29 |
+
'{step}' => $step !== null ? ' step="' . $step . '"' : '',
|
30 |
+
)
|
31 |
+
);
|
32 |
+
|
33 |
+
echo self::buildControl( $option_name, $label, $help, $control );
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Render text input.
|
38 |
+
*
|
39 |
+
* @param string $option_name
|
40 |
+
* @param string $label
|
41 |
+
* @param string|null $help
|
42 |
+
*/
|
43 |
+
public static function renderText( $option_name, $label, $help = null )
|
44 |
+
{
|
45 |
+
$control = strtr(
|
46 |
+
'<input type="text" id="{name}" class="form-control" name="{name}" value="{value}" />',
|
47 |
+
array(
|
48 |
+
'{name}' => esc_attr( $option_name ),
|
49 |
+
'{value}' => esc_attr( get_option( $option_name ) ),
|
50 |
+
)
|
51 |
+
);
|
52 |
+
|
53 |
+
echo self::buildControl( $option_name, $label, $help, $control );
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Build setting control.
|
58 |
+
*
|
59 |
+
* @param string $option_name
|
60 |
+
* @param string $label
|
61 |
+
* @param string $help
|
62 |
+
* @param string $control_html
|
63 |
+
* @return string
|
64 |
+
*/
|
65 |
+
public static function buildControl( $option_name, $label, $help, $control_html )
|
66 |
+
{
|
67 |
+
return strtr(
|
68 |
+
'<div class="form-group">{label}{help}{control}</div>',
|
69 |
+
array(
|
70 |
+
'{label}' => $label != '' ? sprintf( '<label for="%s">%s</label>', $option_name, $label ) : '',
|
71 |
+
'{help}' => $help != '' ? sprintf( '<p class="help-block">%s</p>', $help ) : '',
|
72 |
+
'{control}' => $control_html,
|
73 |
+
)
|
74 |
+
);
|
75 |
+
}
|
76 |
+
}
|
backend/components/settings/Menu.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Menu
|
6 |
+
* @package Bookly\Backend\Components\Settings
|
7 |
+
*/
|
8 |
+
class Menu
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Render menu item on settings page.
|
12 |
+
*
|
13 |
+
* @param string $title
|
14 |
+
* @param string $tab
|
15 |
+
*/
|
16 |
+
public static function renderItem( $title, $tab )
|
17 |
+
{
|
18 |
+
printf( '<li class="bookly-nav-item" data-target="#bookly_settings_%s" data-toggle="tab">%s</li>',
|
19 |
+
$tab,
|
20 |
+
$title
|
21 |
+
);
|
22 |
+
}
|
23 |
+
}
|
backend/components/settings/Payments.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Payments
|
8 |
+
* @package Bookly\Backend\Components\Settings
|
9 |
+
*/
|
10 |
+
class Payments extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render discount and deduction for payment gateway.
|
14 |
+
*
|
15 |
+
* @param string $gateway
|
16 |
+
*/
|
17 |
+
public static function renderPriceCorrection( $gateway )
|
18 |
+
{
|
19 |
+
self::renderTemplate( 'price_correction', compact( 'gateway' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Render tax settings for payment gateway.
|
24 |
+
*
|
25 |
+
* @param string $gateway
|
26 |
+
*/
|
27 |
+
public static function renderTax( $gateway )
|
28 |
+
{
|
29 |
+
if ( Lib\Config::taxesActive() ) {
|
30 |
+
Selects::renderSingle(
|
31 |
+
'bookly_' . $gateway . '_send_tax',
|
32 |
+
__( 'Send tax information', 'bookly' ),
|
33 |
+
null,
|
34 |
+
array(
|
35 |
+
array( 0, __( 'No', 'bookly' ) ),
|
36 |
+
array( 1, __( 'Yes', 'bookly' ) ),
|
37 |
+
)
|
38 |
+
);
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
backend/components/settings/Selects.php
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Selects
|
6 |
+
* @package Bookly\Backend\Components\Settings
|
7 |
+
*/
|
8 |
+
class Selects
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Render multiple select (checkbox group).
|
12 |
+
*
|
13 |
+
* @param string $option_name
|
14 |
+
* @param string $label
|
15 |
+
* @param string $help
|
16 |
+
* @param array $options
|
17 |
+
*/
|
18 |
+
public static function renderMultiple( $option_name, $label = null, $help = null, array $options = array() )
|
19 |
+
{
|
20 |
+
$values = (array) get_option( $option_name );
|
21 |
+
$control = '';
|
22 |
+
foreach ( $options as $attr ) {
|
23 |
+
$control .= strtr(
|
24 |
+
'<div class="checkbox"><label><input type="checkbox" name="{name}[]" value="{value}"{checked} />{caption}</label></div>',
|
25 |
+
array(
|
26 |
+
'{name}' => $option_name,
|
27 |
+
'{value}' => esc_attr( $attr[0] ),
|
28 |
+
'{checked}' => checked( in_array( $attr[0], $values ), true, false ),
|
29 |
+
'{caption}' => esc_html( $attr[1] ),
|
30 |
+
)
|
31 |
+
);
|
32 |
+
}
|
33 |
+
$control = "<div class=\"bookly-flags\" id=\"$option_name\">$control</div>";
|
34 |
+
|
35 |
+
echo Inputs::buildControl( $option_name, $label, $help, $control );
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Render drop-down select.
|
40 |
+
*
|
41 |
+
* @param string $option_name
|
42 |
+
* @param string $label
|
43 |
+
* @param array $options
|
44 |
+
* @param string $help
|
45 |
+
*/
|
46 |
+
public static function renderSingle( $option_name, $label = null, $help = null, array $options = array() )
|
47 |
+
{
|
48 |
+
if ( empty ( $options ) ) {
|
49 |
+
$options = array(
|
50 |
+
// value title disabled
|
51 |
+
array( 0, __( 'Disabled', 'bookly' ), 0 ),
|
52 |
+
array( 1, __( 'Enabled', 'bookly' ), 0 ),
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
$options_str = '';
|
57 |
+
foreach ( $options as $attr ) {
|
58 |
+
$options_str .= strtr(
|
59 |
+
'<option value="{value}"{attr}>{caption}</option>',
|
60 |
+
array(
|
61 |
+
'{value}' => esc_attr( $attr[ 0 ] ),
|
62 |
+
'{attr}' => empty ( $attr[ 2 ] )
|
63 |
+
? selected( get_option( $option_name ), $attr[0], false )
|
64 |
+
: disabled( true, true, false ),
|
65 |
+
'{caption}' => esc_html( $attr[1] ),
|
66 |
+
)
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
$control = strtr(
|
71 |
+
'<select id="{name}" class="form-control" name="{name}">{options}</select>',
|
72 |
+
array(
|
73 |
+
'{name}' => $option_name,
|
74 |
+
'{options}' => $options_str,
|
75 |
+
)
|
76 |
+
);
|
77 |
+
|
78 |
+
echo Inputs::buildControl( $option_name, $label, $help, $control );
|
79 |
+
}
|
80 |
+
}
|
backend/components/settings/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Components\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderPurchaseCode( $blog_id = null ) Render purchase code
|
11 |
+
*/
|
12 |
+
abstract class Pro extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/settings/proxy/Taxes.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Taxes
|
8 |
+
* @package Bookly\Backend\Components\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderHelpMessage() Render tax help message.
|
11 |
+
*/
|
12 |
+
abstract class Taxes extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/settings/templates/image.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-js-<?php echo esc_attr( $option_name ) ?>" class="bookly-thumb bookly-thumb-<?php echo $class ?> bookly-margin-right-lg">
|
3 |
+
<input type="hidden" name="<?php echo $option_name ?>" data-default="<?php echo esc_attr( $option_value ) ?>" value="<?php echo esc_attr( $option_value ) ?>">
|
4 |
+
<div class="bookly-flex-cell">
|
5 |
+
<div class="form-group">
|
6 |
+
<div class="bookly-js-image bookly-thumb bookly-thumb-<?php echo esc_attr( $class ) ?> bookly-margin-right-lg" style="<?php echo esc_attr( $img_style ) ?>" data-style="<?php echo esc_attr( $img_style ) ?>">
|
7 |
+
<a class="dashicons dashicons-trash text-danger bookly-thumb-delete" href="javascript:void(0)" style="<?php echo esc_attr( $delete_style ) ?>" title="<?php esc_attr_e( 'Delete', 'bookly' ) ?>"></a>
|
8 |
+
<div class="bookly-thumb-edit">
|
9 |
+
<div class="bookly-pretty"><label class="bookly-pretty-indicator bookly-thumb-edit-btn"><?php esc_html_e( 'Image', 'bookly' ) ?></label></div>
|
10 |
+
</div>
|
11 |
+
</div>
|
12 |
+
</div>
|
13 |
+
</div>
|
14 |
+
</div>
|
15 |
+
<script type="text/javascript">
|
16 |
+
jQuery(function ($) {
|
17 |
+
$('#bookly-js-<?php echo $option_name ?> .bookly-pretty-indicator').on('click', function(){
|
18 |
+
var frame = wp.media({
|
19 |
+
library: {type: 'image'},
|
20 |
+
multiple: false
|
21 |
+
});
|
22 |
+
frame.on('select', function () {
|
23 |
+
var selection = frame.state().get('selection').toJSON(),
|
24 |
+
img_src
|
25 |
+
;
|
26 |
+
if (selection.length) {
|
27 |
+
if (selection[0].sizes['full'] !== undefined) {
|
28 |
+
img_src = selection[0].sizes['full'].url;
|
29 |
+
} else {
|
30 |
+
img_src = selection[0].url;
|
31 |
+
}
|
32 |
+
$('[name=<?php echo $option_name ?>]').val(selection[0].id);
|
33 |
+
$('#bookly-js-<?php echo $option_name ?> .bookly-js-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'contain'});
|
34 |
+
$('#bookly-js-<?php echo $option_name ?> .bookly-thumb-delete').show();
|
35 |
+
$(this).hide();
|
36 |
+
}
|
37 |
+
});
|
38 |
+
frame.open();
|
39 |
+
});
|
40 |
+
|
41 |
+
$('#bookly-js-<?php echo $option_name ?>')
|
42 |
+
.on('click', '.bookly-thumb-delete', function () {
|
43 |
+
var $thumb = $(this).closest('.bookly-js-image');
|
44 |
+
$thumb.attr('style', '');
|
45 |
+
$('[name=<?php echo $option_name ?>]').val('');
|
46 |
+
});
|
47 |
+
});
|
48 |
+
</script>
|
backend/components/settings/templates/price_correction.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Settings;
|
3 |
+
use Bookly\Lib\Entities\Payment;
|
4 |
+
?>
|
5 |
+
<label for="bookly_<?php echo $gateway ?>_discount"><?php _e( 'Price correction', 'bookly' ) ?></label>
|
6 |
+
<?php if ( ! in_array( $gateway, array( Payment::TYPE_MOLLIE, Payment::TYPE_PAYSON, Payment::TYPE_STRIPE, Payment::TYPE_PAYUBIZ ) ) ) :
|
7 |
+
Settings\Proxy\Taxes::renderHelpMessage();
|
8 |
+
endif ?>
|
9 |
+
<div class="form-group">
|
10 |
+
<div class="row">
|
11 |
+
<div class="col-md-6">
|
12 |
+
<?php Settings\Inputs::renderNumber( 'bookly_' . $gateway . '_increase', __( 'Increase/Discount (%)', 'bookly' ), '', -100, 'any', 100 ) ?>
|
13 |
+
</div>
|
14 |
+
<div class="col-md-6">
|
15 |
+
<?php Settings\Inputs::renderNumber( 'bookly_' . $gateway . '_addition', __( 'Addition/Deduction', 'bookly' ), '', null, 'any' ) ?>
|
16 |
+
</div>
|
17 |
+
</div>
|
18 |
+
</div>
|
backend/components/support/Buttons.php
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Support;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules;
|
6 |
+
use Bookly\Backend\Components\Notices;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Buttons
|
10 |
+
* @package Bookly\Backend\Components\Support
|
11 |
+
*/
|
12 |
+
class Buttons extends Lib\Base\Component
|
13 |
+
{
|
14 |
+
/**
|
15 |
+
* Render support buttons.
|
16 |
+
*
|
17 |
+
* @param string $page_slug
|
18 |
+
*/
|
19 |
+
public static function render( $page_slug )
|
20 |
+
{
|
21 |
+
static::enqueueStyles( array(
|
22 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
23 |
+
) );
|
24 |
+
|
25 |
+
static::enqueueScripts( array(
|
26 |
+
'backend' => array( 'js/alert.js' => array( 'jquery' ), ),
|
27 |
+
'frontend' => array(
|
28 |
+
'js/spin.min.js' => array( 'jquery' ),
|
29 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
30 |
+
),
|
31 |
+
'module' => array( 'js/support.js' => array( 'bookly-alert.js', 'bookly-ladda.min.js', ), ),
|
32 |
+
) );
|
33 |
+
|
34 |
+
wp_localize_script( 'bookly-support.js', 'SupportL10n', array(
|
35 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken()
|
36 |
+
) );
|
37 |
+
|
38 |
+
// Documentation link.
|
39 |
+
$doc_link = 'http://api.booking-wp-plugin.com/go/' . $page_slug;
|
40 |
+
|
41 |
+
$days_in_use = (int) ( ( time() - Lib\Plugin::getInstallationTime() ) / DAY_IN_SECONDS );
|
42 |
+
|
43 |
+
// Whether to show contact us notice or not.
|
44 |
+
$show_contact_us_notice = $days_in_use < 7 &&
|
45 |
+
! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_contact_us_notice', true ) &&
|
46 |
+
! Notices\CollectStats::needShowCollectStatNotice();
|
47 |
+
|
48 |
+
// Whether to show feedback notice.
|
49 |
+
$show_feedback_notice = $days_in_use >= 7 &&
|
50 |
+
! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_feedback_notice', true ) &&
|
51 |
+
! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'contact_us_btn_clicked', true );
|
52 |
+
|
53 |
+
$current_user = wp_get_current_user();
|
54 |
+
|
55 |
+
$messages = Lib\Entities\Message::query( 'm' )
|
56 |
+
->select( 'm.created, m.subject, m.seen' )
|
57 |
+
->sortBy( 'm.seen, m.message_id' )
|
58 |
+
->order( 'DESC' )
|
59 |
+
->limit( 10 )
|
60 |
+
->fetchArray();
|
61 |
+
$messages_new = Lib\Entities\Message::query( 'm' )->where( 'm.seen', '0' )->count();
|
62 |
+
$messages_link = Lib\Utils\Common::escAdminUrl( Modules\Messages\Ajax::pageSlug() );
|
63 |
+
|
64 |
+
static::renderTemplate( 'buttons', compact(
|
65 |
+
'doc_link',
|
66 |
+
'show_contact_us_notice',
|
67 |
+
'show_feedback_notice',
|
68 |
+
'current_user',
|
69 |
+
'messages',
|
70 |
+
'messages_new',
|
71 |
+
'messages_link'
|
72 |
+
) );
|
73 |
+
}
|
74 |
+
}
|
backend/components/support/ButtonsAjax.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Support;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class ButtonsAjax
|
9 |
+
* @package Bookly\Backend\Components\Support
|
10 |
+
*/
|
11 |
+
class ButtonsAjax extends Lib\Base\Ajax
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Send support request.
|
15 |
+
*/
|
16 |
+
public static function sendSupportRequest()
|
17 |
+
{
|
18 |
+
$name = trim( self::parameter( 'name' ) );
|
19 |
+
$email = trim( self::parameter( 'email' ) );
|
20 |
+
$msg = trim( self::parameter( 'msg' ) );
|
21 |
+
|
22 |
+
// Validation.
|
23 |
+
if ( $email == '' || $msg == '' ) {
|
24 |
+
wp_send_json_error( array( 'message' => __( 'All fields marked with an asterisk (*) are required.', 'bookly' ) ) );
|
25 |
+
}
|
26 |
+
if ( ! is_email( $email ) ) {
|
27 |
+
wp_send_json_error( array(
|
28 |
+
'invalid_email' => true,
|
29 |
+
'message' => __( 'Invalid email.', 'bookly' ),
|
30 |
+
) );
|
31 |
+
}
|
32 |
+
|
33 |
+
$plugins = apply_filters( 'bookly_plugins', array() );
|
34 |
+
$message = self::renderTemplate( '_email_to_support', compact( 'name', 'email', 'msg', 'plugins' ), false );
|
35 |
+
$headers = array(
|
36 |
+
'Content-Type: text/html; charset=utf-8',
|
37 |
+
'From: ' . get_option( 'bookly_email_sender_name' ) . ' <' . get_option( 'bookly_email_sender' ) . '>',
|
38 |
+
'Reply-To: ' . $name . ' <' . $email . '>'
|
39 |
+
);
|
40 |
+
|
41 |
+
if ( wp_mail( 'support@ladela.com', 'Support Request ' . site_url(), $message, $headers ) ) {
|
42 |
+
wp_send_json_success( array( 'message' => __( 'Sent successfully.', 'bookly' ) ) );
|
43 |
+
} else {
|
44 |
+
wp_send_json_error( array( 'message' => __( 'Error sending support request.', 'bookly' ) ) );
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Dismiss notice for 'Contact Us' button.
|
50 |
+
*/
|
51 |
+
public static function dismissContactUsNotice()
|
52 |
+
{
|
53 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_contact_us_notice', 1 );
|
54 |
+
|
55 |
+
wp_send_json_success();
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Record click on 'Contact Us' button.
|
60 |
+
*/
|
61 |
+
public static function contactUsBtnClicked()
|
62 |
+
{
|
63 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_contact_us_notice', 1 );
|
64 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'contact_us_btn_clicked', 1 );
|
65 |
+
|
66 |
+
wp_send_json_success();
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Dismiss notice for 'Feedback' button.
|
71 |
+
*/
|
72 |
+
public static function dismissFeedbackNotice()
|
73 |
+
{
|
74 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_feedback_notice', 1 );
|
75 |
+
|
76 |
+
wp_send_json_success();
|
77 |
+
}
|
78 |
+
}
|
backend/components/support/lib/Urls.php
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\Support\Lib;
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Class Urls
|
6 |
+
* @package Bookly\Backend\Components\Support\Lib
|
7 |
+
*/
|
8 |
+
abstract class Urls
|
9 |
+
{
|
10 |
+
const BOOKLY_CODECANYON_PAGE = 'https://codecanyon.net/item/bookly-booking-plugin-responsive-appointment-booking-and-scheduling/7226091?ref=ladela';
|
11 |
+
}
|
backend/{modules → components}/support/resources/js/support.js
RENAMED
File without changes
|
backend/{modules → components}/support/templates/_email_to_support.php
RENAMED
@@ -4,12 +4,16 @@
|
|
4 |
<head>
|
5 |
<meta name="viewport" content="width=device-width" />
|
6 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
7 |
-
<title>Support Request <?php echo site_url()
|
8 |
</head>
|
9 |
<body>
|
10 |
-
Bookly Lite
|
11 |
<p><?php echo esc_html( $name ) ?><br /><?php echo esc_html( $email ) ?></p>
|
12 |
<p><?php echo nl2br( esc_html( $msg ) ) ?></p>
|
13 |
-
<
|
|
|
|
|
|
|
|
|
|
|
14 |
</body>
|
15 |
</html>
|
4 |
<head>
|
5 |
<meta name="viewport" content="width=device-width" />
|
6 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
7 |
+
<title>Support Request <?php echo site_url() ?></title>
|
8 |
</head>
|
9 |
<body>
|
|
|
10 |
<p><?php echo esc_html( $name ) ?><br /><?php echo esc_html( $email ) ?></p>
|
11 |
<p><?php echo nl2br( esc_html( $msg ) ) ?></p>
|
12 |
+
<ol>
|
13 |
+
<?php foreach ( $plugins as $plugin ): ?>
|
14 |
+
<li><?php echo $plugin::getTitle() ?> v<?php echo $plugin::getVersion() ?>: <b><?php echo $plugin::getPurchaseCode() ?></b></li>
|
15 |
+
<?php endforeach ?>
|
16 |
+
</ol>
|
17 |
+
<p><?php echo esc_html( $_SERVER['HTTP_REFERER'] ) ?></p>
|
18 |
</body>
|
19 |
</html>
|
backend/{modules/support/templates/_buttons.php → components/support/templates/buttons.php}
RENAMED
@@ -1,5 +1,8 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use \
|
|
|
|
|
|
|
3 |
?>
|
4 |
|
5 |
<style type="text/css">
|
@@ -42,7 +45,7 @@ use \BooklyLite\Lib\Utils;
|
|
42 |
>
|
43 |
<i class="bookly-icon bookly-icon-contact-us"></i><?php _e( 'Contact Us', 'bookly' ) ?>
|
44 |
</a>
|
45 |
-
<a href="<?php echo
|
46 |
data-toggle="modal"
|
47 |
<?php if ( $show_feedback_notice ) : ?>
|
48 |
data-trigger="manual" data-placement="bottom" data-html="1"
|
@@ -74,9 +77,9 @@ use \BooklyLite\Lib\Utils;
|
|
74 |
</div>
|
75 |
</div>
|
76 |
<div class="modal-footer">
|
77 |
-
<?php
|
78 |
-
<?php
|
79 |
-
<?php
|
80 |
</div>
|
81 |
</div>
|
82 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
use Bookly\Backend\Components\Support\Lib\Urls;
|
5 |
+
use Bookly\Lib\Utils;
|
6 |
?>
|
7 |
|
8 |
<style type="text/css">
|
45 |
>
|
46 |
<i class="bookly-icon bookly-icon-contact-us"></i><?php _e( 'Contact Us', 'bookly' ) ?>
|
47 |
</a>
|
48 |
+
<a href="<?php echo Utils\Common::prepareUrlReferrers( Urls::BOOKLY_CODECANYON_PAGE, 'feedback' ) ?>" id="bookly-feedback-btn" target="_blank" class="btn btn-default-outline"
|
49 |
data-toggle="modal"
|
50 |
<?php if ( $show_feedback_notice ) : ?>
|
51 |
data-trigger="manual" data-placement="bottom" data-html="1"
|
77 |
</div>
|
78 |
</div>
|
79 |
<div class="modal-footer">
|
80 |
+
<?php Inputs::renderCsrf() ?>
|
81 |
+
<?php Buttons::renderCustom( 'bookly-support-send', 'btn-success btn-lg', __( 'Send', 'bookly' ) ) ?>
|
82 |
+
<?php Buttons::renderCustom( null, 'btn-default btn-lg', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
83 |
</div>
|
84 |
</div>
|
85 |
</div>
|
backend/components/tiny_mce/Tools.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce;
|
3 |
+
|
4 |
+
use Bookly\Backend\Components\TinyMce\Proxy;
|
5 |
+
use Bookly\Lib;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Tools
|
9 |
+
* @package Bookly\Backend\Modules\TinyMce
|
10 |
+
*/
|
11 |
+
class Tools extends Lib\Base\Component
|
12 |
+
{
|
13 |
+
public static function init()
|
14 |
+
{
|
15 |
+
global $PHP_SELF;
|
16 |
+
if ( // check if we are in admin area and current page is adding/editing the post
|
17 |
+
is_admin() && ( strpos( $PHP_SELF, 'post-new.php' ) !== false || strpos( $PHP_SELF, 'post.php' ) !== false || strpos( $PHP_SELF, 'admin-ajax.php' ) )
|
18 |
+
) {
|
19 |
+
add_action( 'admin_footer', array( '\Bookly\Backend\Components\TinyMce\Tools', 'renderPopup' ), 10, 0 );
|
20 |
+
add_filter( 'media_buttons', array( '\Bookly\Backend\Components\TinyMce\Tools', 'addButton' ), 50, 1 );
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
public static function addButton( $editor_id )
|
25 |
+
{
|
26 |
+
// don't show on dashboard (QuickPress)
|
27 |
+
$current_screen = get_current_screen();
|
28 |
+
if ( $current_screen && 'dashboard' == $current_screen->base ) {
|
29 |
+
return;
|
30 |
+
}
|
31 |
+
|
32 |
+
// don't display button for users who don't have access
|
33 |
+
if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
// do a version check for the new 3.5 UI
|
38 |
+
$version = get_bloginfo( 'version' );
|
39 |
+
|
40 |
+
if ( $version < 3.5 ) {
|
41 |
+
// show button for v 3.4 and below
|
42 |
+
echo '<a href="#TB_inline?width=640&inlineId=bookly-tinymce-popup&height=650" id="add-bookly-form" title="' . esc_attr__( 'Add Bookly booking form', 'bookly' ) . '">' . __( 'Add Bookly booking form', 'bookly' ) . '</a>';
|
43 |
+
} else {
|
44 |
+
// display button matching new UI
|
45 |
+
$img = '<span class="bookly-media-icon"></span> ';
|
46 |
+
echo '<a href="#TB_inline?width=640&inlineId=bookly-tinymce-popup&height=650" id="add-bookly-form" class="thickbox button bookly-media-button" title="' . esc_attr__( 'Add Bookly booking form', 'bookly' ) . '">' . $img . __( 'Add Bookly booking form', 'bookly' ) . '</a>';
|
47 |
+
}
|
48 |
+
Proxy\Shared::renderMediaButtons( $version );
|
49 |
+
}
|
50 |
+
|
51 |
+
public static function renderPopup()
|
52 |
+
{
|
53 |
+
$casest = Lib\Config::getCaSeSt();
|
54 |
+
self::renderTemplate( 'bookly_form', compact( 'casest' ) );
|
55 |
+
|
56 |
+
Proxy\Shared::renderPopup();
|
57 |
+
}
|
58 |
+
}
|
backend/components/tiny_mce/proxy/DepositPayments.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class DepositPayments
|
8 |
+
* @package Bookly\Backend\Components\TinyMce\Proxy\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderStaffCabinetSettings() Render settings in Staff Cabinet shortcode.
|
11 |
+
*/
|
12 |
+
abstract class DepositPayments extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/tiny_mce/proxy/GroupBooking.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class GroupBooking
|
8 |
+
* @package Bookly\Backend\Components\TinyMce\Proxy\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderStaffCabinetSettings() Render settings in Staff Cabinet shortcode.
|
11 |
+
*/
|
12 |
+
abstract class GroupBooking extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/tiny_mce/proxy/Shared.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Components\TinyMce\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderMediaButtons( string $version ) Add buttons to WordPress editor.
|
11 |
+
* @method static void renderBooklyFormFields() Render controls in popup for bookly-form (build shortcode).
|
12 |
+
* @method static void renderBooklyFormHead() Render controls in header of popup for bookly-form (build shortcode).
|
13 |
+
* @method static void renderPopup() Render popup windows for WordPress editor.
|
14 |
+
*/
|
15 |
+
abstract class Shared extends Lib\Base\Proxy
|
16 |
+
{
|
17 |
+
|
18 |
+
}
|
backend/components/tiny_mce/proxy/SpecialDays.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class SpecialDays
|
8 |
+
* @package Bookly\Backend\Components\TinyMce\Proxy\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderStaffCabinetSettings() Render settings in Staff Cabinet shortcode.
|
11 |
+
*/
|
12 |
+
abstract class SpecialDays extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/components/tiny_mce/proxy/SpecialHours.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class SpecialHours
|
8 |
+
* @package Bookly\Backend\Components\TinyMce\Proxy\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderStaffCabinetSettings() Render settings in Staff Cabinet shortcode.
|
11 |
+
*/
|
12 |
+
abstract class SpecialHours extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/{modules → components}/tiny_mce/resources/images/calendar.png
RENAMED
File without changes
|
backend/{modules/tiny_mce/templates/popup.php → components/tiny_mce/templates/bookly_form.php}
RENAMED
@@ -1,8 +1,11 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
|
|
|
2 |
<div id="bookly-tinymce-popup" style="display: none">
|
3 |
<form id="bookly-shortcode-form">
|
4 |
<table>
|
5 |
-
<?php
|
6 |
<tr>
|
7 |
<td>
|
8 |
<label for="bookly-select-category"><?php _e( 'Default value for category select', 'bookly' ) ?></label>
|
@@ -37,7 +40,7 @@
|
|
37 |
<div><label><input type="checkbox" id="bookly-hide-employee" /><?php _e( 'Hide this field', 'bookly' ) ?></label></div>
|
38 |
</td>
|
39 |
</tr>
|
40 |
-
<?php
|
41 |
<tr>
|
42 |
<td>
|
43 |
<label for="bookly-hide-date"><?php _e( 'Date', 'bookly' ) ?></label>
|
@@ -96,6 +99,7 @@
|
|
96 |
$hide_categories = $('#bookly-hide-categories'),
|
97 |
$hide_services = $('#bookly-hide-services'),
|
98 |
$hide_staff = $('#bookly-hide-employee'),
|
|
|
99 |
$hide_number_of_persons = $('#bookly-hide-number-of-persons'),
|
100 |
$hide_quantity = $('#bookly-hide-quantity'),
|
101 |
$hide_date = $('#bookly-hide-date'),
|
@@ -103,6 +107,7 @@
|
|
103 |
$hide_time_range = $('#bookly-hide-time-range'),
|
104 |
$add_button = $('#add-bookly-form'),
|
105 |
$insert = $('#bookly-insert-shortcode'),
|
|
|
106 |
locations = <?php echo json_encode( $casest['locations'] ) ?>,
|
107 |
categories = <?php echo json_encode( $casest['categories'] ) ?>,
|
108 |
services = <?php echo json_encode( $casest['services'] ) ?>,
|
@@ -118,7 +123,7 @@
|
|
118 |
});
|
119 |
},100);
|
120 |
});
|
121 |
-
|
122 |
function setSelect($select, data, value) {
|
123 |
// reset select
|
124 |
$('option:not([value=""])', $select).remove();
|
@@ -152,11 +157,12 @@
|
|
152 |
}
|
153 |
|
154 |
function setSelects(location_id, category_id, service_id, staff_id) {
|
155 |
-
var
|
|
|
156 |
$.each(staff, function(id, staff_member) {
|
157 |
-
if (location_id
|
158 |
-
if (service_id
|
159 |
-
if (category_id
|
160 |
_staff[id] = staff_member;
|
161 |
} else {
|
162 |
$.each(staff_member.services, function(s_id) {
|
@@ -167,32 +173,45 @@
|
|
167 |
});
|
168 |
}
|
169 |
} else if (staff_member.services.hasOwnProperty(service_id)) {
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
}
|
179 |
}
|
180 |
}
|
181 |
});
|
182 |
-
if (location_id
|
183 |
_categories = categories;
|
184 |
$.each(services, function(id, service) {
|
185 |
-
if (category_id
|
186 |
-
if (staff_id
|
187 |
_services[id] = service;
|
188 |
}
|
189 |
}
|
190 |
});
|
191 |
} else {
|
192 |
-
var category_ids = []
|
193 |
-
|
194 |
-
|
195 |
-
|
|
|
|
|
|
|
|
|
196 |
});
|
197 |
});
|
198 |
$.each(categories, function(id, category) {
|
@@ -201,13 +220,16 @@
|
|
201 |
}
|
202 |
});
|
203 |
$.each(services, function(id, service) {
|
204 |
-
if ($.inArray(
|
205 |
-
if (
|
206 |
-
|
|
|
|
|
207 |
}
|
208 |
}
|
209 |
});
|
210 |
}
|
|
|
211 |
setSelect($select_category, _categories, category_id);
|
212 |
setSelect($select_service, _services, service_id);
|
213 |
setSelect($select_employee, _staff, staff_id);
|
@@ -350,9 +372,15 @@
|
|
350 |
if ($hide_services.is(':checked')) {
|
351 |
hide.push('services');
|
352 |
}
|
|
|
|
|
|
|
353 |
if ($select_employee.val()) {
|
354 |
insert += ' staff_member_id="' + $select_employee.val() + '"';
|
355 |
}
|
|
|
|
|
|
|
356 |
if ($hide_quantity.is(':checked')) {
|
357 |
hide.push('quantity');
|
358 |
}
|
@@ -382,6 +410,7 @@
|
|
382 |
$hide_locations.prop('checked', false);
|
383 |
$hide_categories.prop('checked', false);
|
384 |
$hide_services.prop('checked', false);
|
|
|
385 |
$hide_staff.prop('checked', false);
|
386 |
$hide_date.prop('checked', false);
|
387 |
$hide_week_days.prop('checked', false);
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\TinyMce\Proxy;
|
3 |
+
?>
|
4 |
+
|
5 |
<div id="bookly-tinymce-popup" style="display: none">
|
6 |
<form id="bookly-shortcode-form">
|
7 |
<table>
|
8 |
+
<?php Proxy\Shared::renderBooklyFormHead() ?>
|
9 |
<tr>
|
10 |
<td>
|
11 |
<label for="bookly-select-category"><?php _e( 'Default value for category select', 'bookly' ) ?></label>
|
40 |
<div><label><input type="checkbox" id="bookly-hide-employee" /><?php _e( 'Hide this field', 'bookly' ) ?></label></div>
|
41 |
</td>
|
42 |
</tr>
|
43 |
+
<?php Proxy\Shared::renderBooklyFormFields() ?>
|
44 |
<tr>
|
45 |
<td>
|
46 |
<label for="bookly-hide-date"><?php _e( 'Date', 'bookly' ) ?></label>
|
99 |
$hide_categories = $('#bookly-hide-categories'),
|
100 |
$hide_services = $('#bookly-hide-services'),
|
101 |
$hide_staff = $('#bookly-hide-employee'),
|
102 |
+
$hide_service_duration = $('#bookly-hide-service-duration'),
|
103 |
$hide_number_of_persons = $('#bookly-hide-number-of-persons'),
|
104 |
$hide_quantity = $('#bookly-hide-quantity'),
|
105 |
$hide_date = $('#bookly-hide-date'),
|
107 |
$hide_time_range = $('#bookly-hide-time-range'),
|
108 |
$add_button = $('#add-bookly-form'),
|
109 |
$insert = $('#bookly-insert-shortcode'),
|
110 |
+
location_custom = <?php echo (int) Bookly\Lib\Proxy\Locations::servicesPerLocationAllowed() ?>,
|
111 |
locations = <?php echo json_encode( $casest['locations'] ) ?>,
|
112 |
categories = <?php echo json_encode( $casest['categories'] ) ?>,
|
113 |
services = <?php echo json_encode( $casest['services'] ) ?>,
|
123 |
});
|
124 |
},100);
|
125 |
});
|
126 |
+
// insert data into select
|
127 |
function setSelect($select, data, value) {
|
128 |
// reset select
|
129 |
$('option:not([value=""])', $select).remove();
|
157 |
}
|
158 |
|
159 |
function setSelects(location_id, category_id, service_id, staff_id) {
|
160 |
+
var _location_id = (location_custom && location_id) ? location_id : 0
|
161 |
+
var _staff = {}, _services = {}, _categories = {}, _nop = {}, _max_capacity = null, _min_capacity = null;
|
162 |
$.each(staff, function(id, staff_member) {
|
163 |
+
if (!location_id || locations[location_id].staff.hasOwnProperty(id)) {
|
164 |
+
if (!service_id) {
|
165 |
+
if (!category_id) {
|
166 |
_staff[id] = staff_member;
|
167 |
} else {
|
168 |
$.each(staff_member.services, function(s_id) {
|
173 |
});
|
174 |
}
|
175 |
} else if (staff_member.services.hasOwnProperty(service_id)) {
|
176 |
+
// var _location_id = staff_member.services[service_id].locations.hasOwnProperty(location_id) ? location_id : 0;
|
177 |
+
if (staff_member.services[service_id].locations.hasOwnProperty(_location_id)) {
|
178 |
+
if ( staff_member.services[service_id].locations[_location_id].price != null) {
|
179 |
+
_min_capacity = _min_capacity ? Math.min(_min_capacity, staff_member.services[service_id].locations[_location_id].min_capacity) : staff_member.services[service_id].locations[_location_id].min_capacity;
|
180 |
+
_max_capacity = _max_capacity ? Math.max(_max_capacity, staff_member.services[service_id].locations[_location_id].max_capacity) : staff_member.services[service_id].locations[_location_id].max_capacity;
|
181 |
+
_staff[id] = {
|
182 |
+
id : id,
|
183 |
+
name : staff_member.name + ' (' + staff_member.services[service_id].locations[_location_id].price + ')',
|
184 |
+
pos : staff_member.pos
|
185 |
+
};
|
186 |
+
} else {
|
187 |
+
_staff[id] = {
|
188 |
+
id : id,
|
189 |
+
name : staff_member.name,
|
190 |
+
pos : staff_member.pos
|
191 |
+
};
|
192 |
+
}
|
193 |
}
|
194 |
}
|
195 |
}
|
196 |
});
|
197 |
+
if (!location_id) {
|
198 |
_categories = categories;
|
199 |
$.each(services, function(id, service) {
|
200 |
+
if (!category_id || service.category_id == category_id) {
|
201 |
+
if (!staff_id || staff[staff_id].services.hasOwnProperty(id)) {
|
202 |
_services[id] = service;
|
203 |
}
|
204 |
}
|
205 |
});
|
206 |
} else {
|
207 |
+
var category_ids = [],
|
208 |
+
service_ids = [];
|
209 |
+
$.each(staff, function (st_id) {
|
210 |
+
$.each(staff[st_id].services, function (s_id) {
|
211 |
+
if (staff[st_id].services[s_id].locations.hasOwnProperty(_location_id)) {
|
212 |
+
category_ids.push(services[s_id].category_id);
|
213 |
+
service_ids.push(s_id);
|
214 |
+
}
|
215 |
});
|
216 |
});
|
217 |
$.each(categories, function(id, category) {
|
220 |
}
|
221 |
});
|
222 |
$.each(services, function(id, service) {
|
223 |
+
if ($.inArray(id, service_ids) > -1) {
|
224 |
+
if (!category_id || service.category_id == category_id) {
|
225 |
+
if (!staff_id || staff[staff_id].services.hasOwnProperty(id)) {
|
226 |
+
_services[id] = service;
|
227 |
+
}
|
228 |
}
|
229 |
}
|
230 |
});
|
231 |
}
|
232 |
+
|
233 |
setSelect($select_category, _categories, category_id);
|
234 |
setSelect($select_service, _services, service_id);
|
235 |
setSelect($select_employee, _staff, staff_id);
|
372 |
if ($hide_services.is(':checked')) {
|
373 |
hide.push('services');
|
374 |
}
|
375 |
+
if ($hide_service_duration.is(':checked')) {
|
376 |
+
hide.push('service_duration');
|
377 |
+
}
|
378 |
if ($select_employee.val()) {
|
379 |
insert += ' staff_member_id="' + $select_employee.val() + '"';
|
380 |
}
|
381 |
+
if ($hide_number_of_persons.is(':not(:checked)')) {
|
382 |
+
insert += ' show_number_of_persons="1"';
|
383 |
+
}
|
384 |
if ($hide_quantity.is(':checked')) {
|
385 |
hide.push('quantity');
|
386 |
}
|
410 |
$hide_locations.prop('checked', false);
|
411 |
$hide_categories.prop('checked', false);
|
412 |
$hide_services.prop('checked', false);
|
413 |
+
$hide_service_duration.prop('checked', false);
|
414 |
$hide_staff.prop('checked', false);
|
415 |
$hide_date.prop('checked', false);
|
416 |
$hide_week_days.prop('checked', false);
|
backend/modules/appearance/Ajax.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Ajax
|
9 |
+
* @package Bookly\Backend\Modules\Appearance
|
10 |
+
*/
|
11 |
+
class Ajax extends Lib\Base\Ajax
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Update options.
|
15 |
+
*/
|
16 |
+
public static function updateAppearanceOptions()
|
17 |
+
{
|
18 |
+
$options = self::parameter( 'options', array() );
|
19 |
+
|
20 |
+
// Make sure that we save only allowed options.
|
21 |
+
$options_to_save = array_intersect_key( $options, array_flip( array(
|
22 |
+
// Info text.
|
23 |
+
'bookly_l10n_info_complete_step',
|
24 |
+
'bookly_l10n_info_complete_step_limit_error',
|
25 |
+
'bookly_l10n_info_complete_step_processing',
|
26 |
+
'bookly_l10n_info_details_step',
|
27 |
+
'bookly_l10n_info_details_step_guest',
|
28 |
+
'bookly_l10n_info_payment_step_single_app',
|
29 |
+
'bookly_l10n_info_payment_step_several_apps',
|
30 |
+
'bookly_l10n_info_service_step',
|
31 |
+
'bookly_l10n_info_time_step',
|
32 |
+
// Step, label and option texts.
|
33 |
+
'bookly_l10n_button_apply',
|
34 |
+
'bookly_l10n_button_back',
|
35 |
+
'bookly_l10n_label_category',
|
36 |
+
'bookly_l10n_label_ccard_code',
|
37 |
+
'bookly_l10n_label_ccard_expire',
|
38 |
+
'bookly_l10n_label_ccard_number',
|
39 |
+
'bookly_l10n_label_email',
|
40 |
+
'bookly_l10n_label_employee',
|
41 |
+
'bookly_l10n_label_service_duration',
|
42 |
+
'bookly_l10n_label_finish_by',
|
43 |
+
'bookly_l10n_label_name',
|
44 |
+
'bookly_l10n_label_first_name',
|
45 |
+
'bookly_l10n_label_last_name',
|
46 |
+
'bookly_l10n_label_notes',
|
47 |
+
'bookly_l10n_label_number_of_persons',
|
48 |
+
'bookly_l10n_label_pay_ccard',
|
49 |
+
'bookly_l10n_label_pay_locally',
|
50 |
+
'bookly_l10n_label_phone',
|
51 |
+
'bookly_l10n_label_select_date',
|
52 |
+
'bookly_l10n_label_service',
|
53 |
+
'bookly_l10n_label_start_from',
|
54 |
+
'bookly_l10n_option_category',
|
55 |
+
'bookly_l10n_option_employee',
|
56 |
+
'bookly_l10n_option_service',
|
57 |
+
'bookly_l10n_option_day',
|
58 |
+
'bookly_l10n_option_month',
|
59 |
+
'bookly_l10n_option_year',
|
60 |
+
'bookly_l10n_step_service',
|
61 |
+
'bookly_l10n_step_service_mobile_button_next',
|
62 |
+
'bookly_l10n_step_service_button_next',
|
63 |
+
'bookly_l10n_step_time',
|
64 |
+
'bookly_l10n_step_time_slot_not_available',
|
65 |
+
'bookly_l10n_step_details',
|
66 |
+
'bookly_l10n_step_details_button_next',
|
67 |
+
'bookly_l10n_step_details_button_login',
|
68 |
+
'bookly_l10n_step_payment',
|
69 |
+
'bookly_l10n_step_payment_button_next',
|
70 |
+
'bookly_l10n_step_done',
|
71 |
+
// Validator errors.
|
72 |
+
'bookly_l10n_required_email',
|
73 |
+
'bookly_l10n_required_employee',
|
74 |
+
'bookly_l10n_required_name',
|
75 |
+
'bookly_l10n_required_first_name',
|
76 |
+
'bookly_l10n_required_last_name',
|
77 |
+
'bookly_l10n_required_phone',
|
78 |
+
'bookly_l10n_required_service',
|
79 |
+
// Color.
|
80 |
+
'bookly_app_color',
|
81 |
+
// Checkboxes.
|
82 |
+
'bookly_app_required_employee',
|
83 |
+
'bookly_app_service_name_with_duration',
|
84 |
+
'bookly_app_show_blocked_timeslots',
|
85 |
+
'bookly_app_show_calendar',
|
86 |
+
'bookly_app_show_day_one_column',
|
87 |
+
'bookly_app_show_time_zone_switcher',
|
88 |
+
'bookly_app_show_login_button',
|
89 |
+
'bookly_app_show_facebook_login_button',
|
90 |
+
'bookly_app_show_notes',
|
91 |
+
'bookly_app_show_progress_tracker',
|
92 |
+
'bookly_app_staff_name_with_price',
|
93 |
+
'bookly_cst_required_details',
|
94 |
+
'bookly_app_service_duration_with_price',
|
95 |
+
'bookly_cst_first_last_name',
|
96 |
+
// Options.
|
97 |
+
'bookly_multiply_appointments_quantity_max',
|
98 |
+
) ) );
|
99 |
+
|
100 |
+
// Allow add-ons to add their options.
|
101 |
+
$options_to_save = Proxy\Shared::prepareOptions( $options_to_save, $options );
|
102 |
+
|
103 |
+
// Save options.
|
104 |
+
foreach ( $options_to_save as $option_name => $option_value ) {
|
105 |
+
update_option( $option_name, $option_value );
|
106 |
+
// Register string for translate in WPML.
|
107 |
+
if ( strpos( $option_name, 'bookly_l10n_' ) === 0 ) {
|
108 |
+
do_action( 'wpml_register_single_string', 'bookly', $option_name, $option_value );
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
wp_send_json_success();
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Ajax request to dismiss appearance notice for current user.
|
117 |
+
*/
|
118 |
+
public static function dismissAppearanceNotice()
|
119 |
+
{
|
120 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_appearance_notice', 1 );
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Process ajax request to save custom css
|
125 |
+
*/
|
126 |
+
public static function saveCustomCss()
|
127 |
+
{
|
128 |
+
update_option( 'bookly_app_custom_styles', self::parameter( 'custom_css' ) );
|
129 |
+
|
130 |
+
wp_send_json_success( array( 'message' => __( 'Your custom CSS was saved. Please refresh the page to see your changes.', 'bookly') ) );
|
131 |
+
}
|
132 |
+
}
|
backend/modules/appearance/Components.php
DELETED
@@ -1,21 +0,0 @@
|
|
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
DELETED
@@ -1,218 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Appearance;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
use BooklyLite\Backend\Modules\Appearance\Lib\Helper;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Controller
|
9 |
-
* @package BooklyLite\Backend\Modules\Appearance
|
10 |
-
*/
|
11 |
-
class Controller extends Lib\Base\Controller
|
12 |
-
{
|
13 |
-
const page_slug = 'bookly-appearance';
|
14 |
-
|
15 |
-
/**
|
16 |
-
* Default Action
|
17 |
-
*/
|
18 |
-
public function index()
|
19 |
-
{
|
20 |
-
/** @var \WP_Locale $wp_locale */
|
21 |
-
global $wp_locale;
|
22 |
-
|
23 |
-
$this->enqueueStyles( array(
|
24 |
-
'frontend' => array_merge(
|
25 |
-
( get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
26 |
-
? array()
|
27 |
-
: array( 'css/intlTelInput.css' ) ),
|
28 |
-
array(
|
29 |
-
'css/ladda.min.css',
|
30 |
-
'css/picker.classic.css',
|
31 |
-
'css/picker.classic.date.css',
|
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(
|
46 |
-
array(
|
47 |
-
'js/picker.js' => array( 'jquery' ),
|
48 |
-
'js/picker.date.js' => array( 'jquery' ),
|
49 |
-
'js/spin.min.js' => array( 'jquery' ),
|
50 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
51 |
-
),
|
52 |
-
get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
53 |
-
? array()
|
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 ),
|
69 |
-
'nextMonth' => __( 'Next month', 'bookly' ),
|
70 |
-
'prevMonth' => __( 'Previous month', 'bookly' ),
|
71 |
-
'date_format' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_PICKADATE ),
|
72 |
-
'start_of_week' => (int) get_option( 'start_of_week' ),
|
73 |
-
'saved' => __( 'Settings saved.', 'bookly' ),
|
74 |
-
'intlTelInput' => array(
|
75 |
-
'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
|
76 |
-
'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
|
77 |
-
'country' => get_option( 'bookly_cst_phone_default_country' ),
|
78 |
-
)
|
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' ),
|
86 |
-
get_option( 'bookly_l10n_step_repeat' ),
|
87 |
-
get_option( 'bookly_l10n_step_cart' ),
|
88 |
-
get_option( 'bookly_l10n_step_details' ),
|
89 |
-
get_option( 'bookly_l10n_step_payment' ),
|
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 |
-
/**
|
103 |
-
* Update options
|
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 |
-
|
198 |
-
wp_send_json_success();
|
199 |
-
}
|
200 |
-
|
201 |
-
/**
|
202 |
-
* Ajax request to dismiss appearance notice for current user.
|
203 |
-
*/
|
204 |
-
public function executeDismissAppearanceNotice()
|
205 |
-
{
|
206 |
-
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_appearance_notice', 1 );
|
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/Page.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Appearance
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
self::enqueueStyles( array(
|
21 |
+
'frontend' => array_merge(
|
22 |
+
( get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
23 |
+
? array()
|
24 |
+
: array( 'css/intlTelInput.css' ) ),
|
25 |
+
array(
|
26 |
+
'css/ladda.min.css',
|
27 |
+
'css/picker.classic.css',
|
28 |
+
'css/picker.classic.date.css',
|
29 |
+
'css/bookly-main.css',
|
30 |
+
)
|
31 |
+
),
|
32 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
33 |
+
'wp' => array( 'wp-color-picker', ),
|
34 |
+
'module' => array( 'css/bootstrap-editable.css', )
|
35 |
+
) );
|
36 |
+
|
37 |
+
self::enqueueScripts( array(
|
38 |
+
'backend' => array(
|
39 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
40 |
+
'js/alert.js' => array( 'jquery' ),
|
41 |
+
),
|
42 |
+
'frontend' => array_merge(
|
43 |
+
array(
|
44 |
+
'js/picker.js' => array( 'jquery' ),
|
45 |
+
'js/picker.date.js' => array( 'jquery' ),
|
46 |
+
'js/spin.min.js' => array( 'jquery' ),
|
47 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
48 |
+
),
|
49 |
+
get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
50 |
+
? array()
|
51 |
+
: array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
|
52 |
+
),
|
53 |
+
'wp' => array( 'wp-color-picker' ),
|
54 |
+
'module' => array(
|
55 |
+
'js/bootstrap-editable.min.js' => array( 'bookly-bootstrap.min.js' ),
|
56 |
+
'js/bootstrap-editable.bookly.js' => array( 'bookly-bootstrap-editable.min.js' ),
|
57 |
+
'js/appearance.js' => array( 'bookly-bootstrap-editable.bookly.js' )
|
58 |
+
)
|
59 |
+
) );
|
60 |
+
|
61 |
+
wp_localize_script( 'bookly-picker.date.js', 'BooklyL10n', array(
|
62 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
63 |
+
'nop_format' => get_option( 'bookly_group_booking_nop_format' ),
|
64 |
+
'today' => __( 'Today', 'bookly' ),
|
65 |
+
'months' => array_values( $wp_locale->month ),
|
66 |
+
'days' => array_values( $wp_locale->weekday_abbrev ),
|
67 |
+
'nextMonth' => __( 'Next month', 'bookly' ),
|
68 |
+
'prevMonth' => __( 'Previous month', 'bookly' ),
|
69 |
+
'date_format' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_PICKADATE ),
|
70 |
+
'start_of_week' => (int) get_option( 'start_of_week' ),
|
71 |
+
'saved' => __( 'Settings saved.', 'bookly' ),
|
72 |
+
'intlTelInput' => array(
|
73 |
+
'enabled' => get_option( 'bookly_cst_phone_default_country' ) != 'disabled',
|
74 |
+
'utils' => is_rtl() ? '' : plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
|
75 |
+
'country' => get_option( 'bookly_cst_phone_default_country' ),
|
76 |
+
),
|
77 |
+
) );
|
78 |
+
|
79 |
+
// Initialize steps (tabs).
|
80 |
+
$steps = array(
|
81 |
+
1 => array( 'show' => true, 'title' => get_option( 'bookly_l10n_step_service' ) ),
|
82 |
+
3 => array( 'show' => true, 'title' => get_option( 'bookly_l10n_step_time' ) ),
|
83 |
+
6 => array( 'show' => true, 'title' => get_option( 'bookly_l10n_step_details' ) ),
|
84 |
+
7 => array( 'show' => true, 'title' => get_option( 'bookly_l10n_step_payment' ) ),
|
85 |
+
8 => array( 'show' => true, 'title' => get_option( 'bookly_l10n_step_done' ) ),
|
86 |
+
);
|
87 |
+
if ( Lib\Config::serviceExtrasActive() ) {
|
88 |
+
$steps[2] = array( 'show' => get_option( 'bookly_service_extras_enabled' ), 'title' => get_option( 'bookly_l10n_step_extras' ) );
|
89 |
+
}
|
90 |
+
if ( Lib\Config::recurringAppointmentsActive() ) {
|
91 |
+
$steps[4] = array( 'show' => get_option( 'bookly_recurring_appointments_enabled' ), 'title' => get_option( 'bookly_l10n_step_repeat' ) );
|
92 |
+
}
|
93 |
+
if ( Lib\Config::cartActive() ) {
|
94 |
+
$steps[5] = array( 'show' => get_option( 'bookly_cart_enabled' ), 'title' => get_option( 'bookly_l10n_step_cart' ) );
|
95 |
+
}
|
96 |
+
ksort( $steps );
|
97 |
+
|
98 |
+
$custom_css = get_option( 'bookly_app_custom_styles' );
|
99 |
+
|
100 |
+
// Render general layout.
|
101 |
+
self::renderTemplate( 'index', compact( 'steps', 'custom_css' ) );
|
102 |
+
}
|
103 |
+
}
|
backend/modules/appearance/lib/Helper.php
DELETED
@@ -1,69 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Appearance\Lib;
|
3 |
-
|
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/modules/appearance/proxy/Cart.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Cart
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCartStepSettings() Render settings on Cart step.
|
11 |
+
* @method static void renderShowStep() Render "Show Cart step".
|
12 |
+
* @method static void renderStep( string $progress_tracker ) Render Cart step.
|
13 |
+
*/
|
14 |
+
abstract class Cart extends Lib\Base\Proxy
|
15 |
+
{
|
16 |
+
|
17 |
+
}
|
backend/modules/appearance/proxy/ChainAppointments.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class ChainAppointments
|
8 |
+
* @package Bookly\Backend\Modules\Frontend\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderChain() render a 'plus' for chain appointments on service step
|
11 |
+
*/
|
12 |
+
abstract class ChainAppointments extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
}
|
backend/modules/appearance/proxy/Coupons.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Coupons
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCouponBlock() Render coupon block in Payments step.
|
11 |
+
* @method static void renderShowCoupons() Render coupon block in Payments step.
|
12 |
+
*/
|
13 |
+
abstract class Coupons extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appearance/proxy/CustomDuration.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class GoogleMapsAddress
|
8 |
+
* @package Bookly\Backend\Modules\Frontend\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderServiceDuration() render a select with service durations
|
11 |
+
* @method static void renderShowCustomDuration() Render "Show custom duration" checkbox on Service step settings.
|
12 |
+
*/
|
13 |
+
abstract class CustomDuration extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/appearance/proxy/CustomFields.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomFields
|
8 |
+
* @package Bookly\Backend\Modules\Frontend\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCustomFields() render "custom fields" input
|
11 |
+
* @method static void renderShowCustomFields() render a checkbox "Show custom fields"
|
12 |
+
*/
|
13 |
+
abstract class CustomFields extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/appearance/proxy/CustomerInformation.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerInformation
|
8 |
+
* @package Bookly\Backend\Modules\Frontend\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCustomerInformation() render "customer information" input
|
11 |
+
* @method static void renderShowCustomerInformation() render a checkbox "Show customer information"
|
12 |
+
*/
|
13 |
+
abstract class CustomerInformation extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/appearance/proxy/DepositPayments.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class DepositPayments
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAppearance() Render editable selector for deposit/full payment
|
11 |
+
*/
|
12 |
+
abstract class DepositPayments extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/appearance/proxy/Files.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Files
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAppearance() Render button browse in Appearance
|
11 |
+
* @method static void renderShowFiles() Render 'Show files' on details step
|
12 |
+
*/
|
13 |
+
abstract class Files extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appearance/proxy/GoogleMapsAddress.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class GoogleMapsAddress
|
8 |
+
* @package Bookly\Backend\Modules\Frontend\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderGoogleMaps() Render google maps input.
|
11 |
+
* @method static void renderShowGoogleMaps() Render show google maps fields checkbox.
|
12 |
+
*/
|
13 |
+
abstract class GoogleMapsAddress extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/appearance/proxy/GroupBooking.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class GroupBooking
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderNOP() Render number of persons select in Service step.
|
11 |
+
* @method static void renderShowNOP() Render "Show number of persons" checkbox on Service step settings.
|
12 |
+
*/
|
13 |
+
abstract class GroupBooking extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appearance/proxy/Locations.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Locations
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderLocation() Render location select in Service step.
|
11 |
+
* @method static void renderShowLocation() Render "Show location" checkbox on Service step settings.
|
12 |
+
*/
|
13 |
+
abstract class Locations extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appearance/proxy/MultiplyAppointments.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class MultiplyAppointments
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderQuantity() Render Multiply (quantity) control in Service step.
|
11 |
+
* @method static void renderShowQuantity() Render "Show quantity" checkbox on Service step settings.
|
12 |
+
*/
|
13 |
+
abstract class MultiplyAppointments extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appearance/proxy/Pro.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderFacebookButton() Render facebook login button on Time step.
|
11 |
+
* @method static void renderMultipleBookingSelector() Render single/multiple booking selector on Payment step.
|
12 |
+
* @method static void renderMultipleBookingText() Render multiple booking text option on Payment step.
|
13 |
+
* @method static void renderPayPalPaymentOption() Render Cart step.
|
14 |
+
* @method static void renderShowFacebookButton() Render 'Show facebook login button switcher' on Time step.
|
15 |
+
* @method static void renderTimeZoneSwitcher() Render timezone switcher on Time step.
|
16 |
+
* @method static void renderTimeZoneSwitcherCheckbox() Render 'Show time zone switcher' on Time step.
|
17 |
+
* @method static void renderShowAddress() render 'Show Address Fields' on Details Step.
|
18 |
+
* @method static void renderShowBirthday() render 'Show Birthday Fields' on Details Step.
|
19 |
+
*/
|
20 |
+
abstract class Pro extends Lib\Base\Proxy
|
21 |
+
{
|
22 |
+
|
23 |
+
}
|
backend/modules/appearance/proxy/RecurringAppointments.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class RecurringAppointments
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderInfoMessage() Render editable info message in appearance.
|
11 |
+
* @method static void renderShowStep() Render "Show Repeat step".
|
12 |
+
* @method static void renderStep( string $progress_tracker ) Render Repeat step.
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
abstract class RecurringAppointments extends Lib\Base\Proxy
|
16 |
+
{
|
17 |
+
|
18 |
+
}
|
backend/modules/appearance/proxy/ServiceExtras.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class ServiceExtras
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCartExtras() Render extras on Cart step.
|
11 |
+
* @method static void renderShowCartExtras() Render "Show extras" on Cart step.
|
12 |
+
* @method static void renderShowStep() Render "Show Extras step".
|
13 |
+
* @method static void renderStep( string $progress_tracker ) Render Extras step.
|
14 |
+
*/
|
15 |
+
abstract class ServiceExtras extends Lib\Base\Proxy
|
16 |
+
{
|
17 |
+
|
18 |
+
}
|
backend/modules/appearance/proxy/Shared.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareOptions( array $options_to_save, array $options ) Alter array of options to be saved in Bookly Appearance.
|
11 |
+
* @method static void renderPaymentGatewaySelector() Render gateway selector.
|
12 |
+
* @method static int renderServiceStepSettings() Render checkbox settings.
|
13 |
+
* @method static int renderTimeStepSettings() Render checkbox settings.
|
14 |
+
* @method static bool showCreditCard() In case there are payment systems that request credit card information in the Details step, it will return true.
|
15 |
+
*/
|
16 |
+
abstract class Shared extends Lib\Base\Proxy
|
17 |
+
{
|
18 |
+
|
19 |
+
}
|
backend/modules/appearance/proxy/WaitingList.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appearance\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class WaitingList
|
8 |
+
* @package Bookly\Backend\Modules\Appearance\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderInfoText() Render WL info text in Time step.
|
11 |
+
*/
|
12 |
+
abstract class WaitingList extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/appearance/resources/js/appearance.js
CHANGED
@@ -4,19 +4,33 @@ jQuery(function($) {
|
|
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"]'),
|
@@ -25,15 +39,27 @@ jQuery(function($) {
|
|
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 |
-
$
|
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 () {
|
@@ -115,12 +141,41 @@ jQuery(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;
|
@@ -165,6 +220,95 @@ jQuery(function($) {
|
|
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();
|
@@ -175,6 +319,19 @@ jQuery(function($) {
|
|
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);
|
@@ -226,11 +383,22 @@ jQuery(function($) {
|
|
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 |
|
@@ -245,6 +413,46 @@ jQuery(function($) {
|
|
245 |
}
|
246 |
});
|
247 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
/**
|
249 |
* Step repeat.
|
250 |
*/
|
@@ -284,6 +492,20 @@ jQuery(function($) {
|
|
284 |
});
|
285 |
|
286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
/**
|
288 |
* Step Details
|
289 |
*/
|
@@ -303,11 +525,25 @@ jQuery(function($) {
|
|
303 |
});
|
304 |
}
|
305 |
|
306 |
-
// Show login
|
307 |
$show_login_button.change(function () {
|
308 |
-
$('#bookly-
|
309 |
}).trigger('change');
|
310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
// Show first and last name.
|
312 |
$first_last_name.on('change', function () {
|
313 |
$first_last_name.popover('toggle');
|
@@ -325,6 +561,47 @@ jQuery(function($) {
|
|
325 |
$('#bookly-js-notes').toggle(this.checked);
|
326 |
}).trigger('change');
|
327 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
328 |
/**
|
329 |
* Step Payment.
|
330 |
*/
|
@@ -340,6 +617,10 @@ jQuery(function($) {
|
|
340 |
$('form.bookly-card-form').toggle(this.id == 'bookly-card-payment');
|
341 |
});
|
342 |
|
|
|
|
|
|
|
|
|
343 |
/**
|
344 |
* Step Done.
|
345 |
*/
|
@@ -355,6 +636,7 @@ jQuery(function($) {
|
|
355 |
/**
|
356 |
* Misc.
|
357 |
*/
|
|
|
358 |
|
359 |
// Custom CSS.
|
360 |
$('#bookly-custom-css-save').on('click', function (e) {
|
@@ -415,13 +697,13 @@ jQuery(function($) {
|
|
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.
|
@@ -429,14 +711,36 @@ jQuery(function($) {
|
|
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 |
-
'
|
439 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
440 |
}
|
441 |
};
|
442 |
// Add data from editable elements.
|
@@ -468,6 +772,11 @@ jQuery(function($) {
|
|
468 |
$(this).prop('checked', $(this).data('default')).trigger('change');
|
469 |
}
|
470 |
});
|
|
|
|
|
|
|
|
|
|
|
471 |
$first_last_name.popover('hide');
|
472 |
});
|
473 |
});
|
4 |
$editableElements = $('.bookly-js-editable'),
|
5 |
$show_progress_tracker = $('#bookly-show-progress-tracker'),
|
6 |
$step_settings = $('#bookly-step-settings'),
|
7 |
+
$bookly_show_step_extras = $('#bookly-show-step-extras'),
|
8 |
+
$bookly_show_step_repeat = $('#bookly-show-step-repeat'),
|
9 |
+
$bookly_show_step_cart = $('#bookly-show-step-cart'),
|
10 |
// Service step.
|
11 |
$staff_name_with_price = $('#bookly-staff-name-with-price'),
|
12 |
+
$service_duration_with_price = $('#bookly-service-duration-with-price'),
|
13 |
$service_name_with_duration = $('#bookly-service-name-with-duration'),
|
14 |
$required_employee = $('#bookly-required-employee'),
|
15 |
$required_location = $('#bookly-required-location'),
|
16 |
+
$show_ratings = $('#bookly-show-ratings'),
|
17 |
+
$show_chain_appointments = $('#bookly-show-chain-appointments'),
|
18 |
+
$show_location = $('#bookly-show-location'),
|
19 |
+
$show_custom_duration = $('#bookly-show-custom-duration'),
|
20 |
+
$show_nop = $('#bookly-show-nop'),
|
21 |
+
$show_quantity = $('#bookly-show-quantity'),
|
22 |
// Time step.
|
23 |
+
$time_step_nop = $('#bookly-show-nop-on-time-step'),
|
24 |
$time_step_calendar = $('.bookly-js-selected-date'),
|
25 |
$time_step_calendar_wrap = $('.bookly-js-slot-calendar'),
|
26 |
$show_blocked_timeslots = $('#bookly-show-blocked-timeslots'),
|
27 |
+
$show_waiting_list = $('#bookly-show-waiting-list'),
|
28 |
$show_day_one_column = $('#bookly-show-day-one-column'),
|
29 |
+
$show_time_zone_switcher = $('#bookly-show-time-zone-switcher'),
|
30 |
$show_calendar = $('#bookly-show-calendar'),
|
31 |
$day_one_column = $('#bookly-day-one-column'),
|
32 |
$day_multi_columns = $('#bookly-day-multi-columns'),
|
33 |
+
$columnizer = $('.bookly-time-step .bookly-columnizer-wrap'),
|
34 |
// Step repeat.
|
35 |
$repeat_step_calendar = $('.bookly-js-repeat-until'),
|
36 |
$repeat_variants = $('[class^="bookly-js-variant"]'),
|
39 |
$repeat_weekly_week_day = $('.bookly-js-week-day'),
|
40 |
$repeat_monthly_specific_day = $('.bookly-js-monthly-specific-day'),
|
41 |
$repeat_monthly_week_day = $('.bookly-js-monthly-week-day'),
|
42 |
+
// Step Cart.
|
43 |
+
$show_cart_extras = $('#bookly-show-cart-extras'),
|
44 |
// Step details.
|
45 |
+
$required_details = $('#bookly-cst-required-details'),
|
46 |
$show_login_button = $('#bookly-show-login-button'),
|
47 |
+
$show_facebook_login_button = $('#bookly-show-facebook-login-button'),
|
48 |
$first_last_name = $('#bookly-cst-first-last-name'),
|
49 |
$show_notes_field = $('#bookly-show-notes'),
|
50 |
+
$show_birthday_fields = $('#bookly-show-birthday'),
|
51 |
+
$show_address_fields = $('#bookly-show-address'),
|
52 |
+
$show_google_maps = $('#bookly-show-google-maps'),
|
53 |
+
$show_custom_fields = $('#bookly-show-custom-fields'),
|
54 |
+
$show_customer_information = $('#bookly-show-customer-information'),
|
55 |
+
$show_files = $('#bookly-show-files'),
|
56 |
+
// Step payment.
|
57 |
+
$show_coupons = $('#bookly-show-coupons'),
|
58 |
// Buttons.
|
59 |
$save_button = $('#ajax-send-appearance'),
|
60 |
$reset_button = $('button[type=reset]'),
|
61 |
+
$checkboxes = $('#bookly-appearance').find('input[type="checkbox"]'),
|
62 |
+
$selects = $('#bookly-appearance').find('select[data-default]')
|
63 |
;
|
64 |
|
65 |
$checkboxes.each(function () {
|
141 |
$('.bookly-progress-tracker').toggle(this.checked);
|
142 |
}).trigger('change');
|
143 |
|
144 |
+
// Show steps.
|
145 |
+
$('.bookly-js-show-step').on('change', function () {
|
146 |
+
var target = $(this).data('target'),
|
147 |
+
$button = $('li.bookly-nav-item[data-target="#' + target + '"]'),
|
148 |
+
$step = $('div[data-step="' + target + '"]');
|
149 |
+
if ($(this).prop('checked')) {
|
150 |
+
$button.show();
|
151 |
+
$step.show();
|
152 |
+
} else {
|
153 |
+
if ($button.hasClass('active')) {
|
154 |
+
$('li.bookly-nav-item[data-target="#bookly-step-1"]').trigger('click');
|
155 |
+
}
|
156 |
+
$button.hide();
|
157 |
+
$step.hide();
|
158 |
+
}
|
159 |
+
// Hide/show cart buttons
|
160 |
+
if ( target == 'bookly-step-5') {
|
161 |
+
$('.bookly-js-go-to-cart').toggle( $(this).prop('checked') );
|
162 |
+
}
|
163 |
+
|
164 |
+
$('.bookly-progress-tracker > div:visible').each(function (num) {
|
165 |
+
$(this).find('.bookly-js-step-number').html(num + 1);
|
166 |
+
});
|
167 |
+
$('.bookly-js-appearance-steps > li:visible').each(function (num) {
|
168 |
+
$(this).find('.bookly-js-step-number').html(num + 1);
|
169 |
+
});
|
170 |
+
}).trigger('change');
|
171 |
+
|
172 |
// Show step specific settings.
|
173 |
$('li.bookly-nav-item').on('shown.bs.tab', function (e) {
|
174 |
$step_settings.children().hide();
|
175 |
switch (e.target.getAttribute('data-target')) {
|
176 |
case '#bookly-step-1': $step_settings.find('.bookly-js-service-settings').show(); break;
|
177 |
case '#bookly-step-3': $step_settings.find('.bookly-js-time-settings').show(); break;
|
178 |
+
case '#bookly-step-5': $step_settings.find('.bookly-js-cart-settings').show(); break;
|
179 |
case '#bookly-step-6': $step_settings.find('.bookly-js-details-settings').show(); break;
|
180 |
case '#bookly-step-7': $step_settings.find('.bookly-js-payment-settings').show(); break;
|
181 |
case '#bookly-step-8': $step_settings.find('.bookly-js-done-settings').show(); break;
|
220 |
$('.employee-name').toggle(!$staff_name_with_price.prop("checked"));
|
221 |
}).trigger('change');
|
222 |
|
223 |
+
if ($service_duration_with_price.prop("checked")) {
|
224 |
+
$('.bookly-js-select-duration').val(-1);
|
225 |
+
}
|
226 |
+
|
227 |
+
// Show price next to service duration.
|
228 |
+
$service_duration_with_price.on('change', function () {
|
229 |
+
var duration = $('.bookly-js-select-duration').val();
|
230 |
+
if (duration) {
|
231 |
+
$('.bookly-js-select-duration').val(duration * -1);
|
232 |
+
}
|
233 |
+
$('.bookly-js-duration-price').toggle($service_duration_with_price.prop("checked"));
|
234 |
+
$('.bookly-js-duration').toggle(!$service_duration_with_price.prop("checked"));
|
235 |
+
}).trigger('change');
|
236 |
+
|
237 |
+
$show_ratings.on('change', function () {
|
238 |
+
var state = $(this).prop('checked');
|
239 |
+
$('.bookly-js-select-employee option').each(function () {
|
240 |
+
if ($(this).val() != '0') {
|
241 |
+
if (!state) {
|
242 |
+
if ($(this).text().charAt(0) == '★') {
|
243 |
+
$(this).text($(this).text().substring(5));
|
244 |
+
}
|
245 |
+
} else {
|
246 |
+
var rating = Math.round(10 * (Math.random() * 6 + 1)) / 10;
|
247 |
+
if (rating <= 5) {
|
248 |
+
$(this).text('★' + rating.toFixed(1) + ' ' + $(this).text());
|
249 |
+
}
|
250 |
+
}
|
251 |
+
}
|
252 |
+
});
|
253 |
+
}).trigger('change');
|
254 |
+
|
255 |
+
// Show chain appointments
|
256 |
+
$show_chain_appointments.on('change', function () {
|
257 |
+
$('.bookly-js-chain-appointments').toggle( this.checked );
|
258 |
+
});
|
259 |
+
|
260 |
+
// Show location
|
261 |
+
$show_location.on('change', function () {
|
262 |
+
$('.bookly-js-location').toggle( this.checked );
|
263 |
+
if (!this.disabled) {
|
264 |
+
if (this.checked) {
|
265 |
+
$required_location.closest('[data-toggle="popover"]').popover('destroy');
|
266 |
+
$required_location.prop('disabled', false);
|
267 |
+
} else {
|
268 |
+
$required_location.closest('[data-toggle="popover"]').popover();
|
269 |
+
$required_location.prop('checked', false).prop('disabled', true).trigger('change');
|
270 |
+
}
|
271 |
+
}
|
272 |
+
}).trigger('change');
|
273 |
+
|
274 |
+
// Show custom duration
|
275 |
+
$show_custom_duration.on('change', function () {
|
276 |
+
$('.bookly-js-custom-duration').toggle( this.checked );
|
277 |
+
if (this.checked) {
|
278 |
+
$service_duration_with_price.closest('[data-toggle="popover"]').popover('destroy');
|
279 |
+
$service_duration_with_price.prop('disabled', false);
|
280 |
+
} else {
|
281 |
+
$service_duration_with_price.closest('[data-toggle="popover"]').popover();
|
282 |
+
$service_duration_with_price.prop('checked', false).prop('disabled', true).trigger('change');
|
283 |
+
}
|
284 |
+
}).trigger('change');
|
285 |
+
|
286 |
+
// Show number of persons
|
287 |
+
$show_nop.on('change', function () {
|
288 |
+
$('.bookly-js-nop').toggle( this.checked );
|
289 |
+
if (this.checked) {
|
290 |
+
$time_step_nop.closest('[data-toggle="popover"]').popover('destroy');
|
291 |
+
$time_step_nop.prop('disabled', false);
|
292 |
+
} else {
|
293 |
+
$time_step_nop.closest('[data-toggle="popover"]').popover();
|
294 |
+
$time_step_nop.prop('checked', false).prop('disabled', true).trigger('change');
|
295 |
+
}
|
296 |
+
}).trigger('change');
|
297 |
+
|
298 |
+
// Show quantity
|
299 |
+
$show_quantity.on('change', function () {
|
300 |
+
$('.bookly-js-quantity').toggle( this.checked );
|
301 |
+
});
|
302 |
+
|
303 |
+
// Set max quantity
|
304 |
+
$('.bookly_multiply_appointments_quantity_max').on('save', function (e, params) {
|
305 |
+
var $options = '';
|
306 |
+
for (var x = 1; x <= params.newValue['bookly_multiply_appointments_quantity_max']; x++) {
|
307 |
+
$options += "<option>" + x + "</option>";
|
308 |
+
}
|
309 |
+
$('.bookly-js-select-quantity').html($options);
|
310 |
+
});
|
311 |
+
|
312 |
// Show duration next to service name.
|
313 |
$service_name_with_duration.on('change', function () {
|
314 |
var service = $('.bookly-js-select-service').val();
|
319 |
$('.service-name').toggle(!$service_name_with_duration.prop("checked"));
|
320 |
}).trigger('change');
|
321 |
|
322 |
+
// Show price next to service duration.
|
323 |
+
$service_duration_with_price.on('change', function () {
|
324 |
+
if ($(this).prop('checked')) {
|
325 |
+
$('.bookly-js-select-duration option[value="1"]').each(function () {
|
326 |
+
$(this).text($(this).attr('data-text-1'));
|
327 |
+
});
|
328 |
+
} else {
|
329 |
+
$('.bookly-js-select-duration option[value="1"]').each(function () {
|
330 |
+
$(this).text($(this).attr('data-text-0'));
|
331 |
+
});
|
332 |
+
}
|
333 |
+
}).trigger('change');
|
334 |
+
|
335 |
// Clickable week-days.
|
336 |
$repeat_weekly_week_day.on('change', function () {
|
337 |
$(this).parent().toggleClass('active', this.checked);
|
383 |
}).trigger('change');
|
384 |
|
385 |
// Show blocked time slots.
|
386 |
+
$show_blocked_timeslots.on('change', function () {
|
387 |
if (this.checked) {
|
388 |
$('.bookly-hour.no-booked').removeClass('no-booked').addClass('booked');
|
389 |
+
$('.bookly-column .bookly-hour.booked .bookly-time-additional', $columnizer).text('');
|
390 |
} else {
|
391 |
$('.bookly-hour.booked').removeClass('booked').addClass('no-booked');
|
392 |
+
if ($time_step_nop.prop('checked')) {
|
393 |
+
$('.bookly-column .bookly-hour:not(.booked):not(.bookly-slot-in-waiting-list) .bookly-time-additional', $columnizer).each(function () {
|
394 |
+
var nop = Math.ceil(Math.random() * 9);
|
395 |
+
if (BooklyL10n.nop_format == 'busy') {
|
396 |
+
$(this).text('[' + nop + '/10]');
|
397 |
+
} else {
|
398 |
+
$(this).text('[' + nop + ']');
|
399 |
+
}
|
400 |
+
});
|
401 |
+
}
|
402 |
}
|
403 |
});
|
404 |
|
413 |
}
|
414 |
});
|
415 |
|
416 |
+
// Show time zone switcher
|
417 |
+
$show_time_zone_switcher.on('change', function() {
|
418 |
+
$('.bookly-js-time-zone-switcher').toggle(this.checked);
|
419 |
+
}).trigger('change');
|
420 |
+
|
421 |
+
// Show nop/capacity
|
422 |
+
$time_step_nop.on('change', function () {
|
423 |
+
if (this.checked) {
|
424 |
+
$('.bookly-column', $columnizer).addClass('bookly-column-wide');
|
425 |
+
$('.bookly-column .bookly-hour:not(.booked):not(.bookly-slot-in-waiting-list) .bookly-time-additional', $columnizer).each(function () {
|
426 |
+
var nop = Math.ceil(Math.random() * 9);
|
427 |
+
if (BooklyL10n.nop_format == 'busy') {
|
428 |
+
$(this).text('[' + nop + '/10]');
|
429 |
+
} else {
|
430 |
+
$(this).text('[' + nop + ']');
|
431 |
+
}
|
432 |
+
});
|
433 |
+
$('.bookly-column.col5', $columnizer).hide();
|
434 |
+
$('.bookly-column.col6', $columnizer).hide();
|
435 |
+
$('.bookly-column.col7', $columnizer).hide();
|
436 |
+
} else {
|
437 |
+
$('.bookly-column', $columnizer).removeClass('bookly-column-wide');
|
438 |
+
$('.bookly-column .bookly-hour:not(.bookly-slot-in-waiting-list) .bookly-time-additional', $columnizer).text('');
|
439 |
+
if (!$show_calendar.prop('checked')) {
|
440 |
+
$('.bookly-column', $columnizer).removeClass('bookly-column-wide').show();
|
441 |
+
}
|
442 |
+
}
|
443 |
+
}).trigger('change');
|
444 |
+
|
445 |
+
$show_waiting_list.on('change', function () {
|
446 |
+
if (this.checked) {
|
447 |
+
$('.bookly-column .bookly-hour.no-waiting-list, .bookly-column .bookly-hour.bookly-slot-in-waiting-list').each(function () {
|
448 |
+
$(this).removeClass('no-waiting-list').addClass('bookly-slot-in-waiting-list').find('.bookly-time-additional').text('(' + Math.floor(Math.random() * 10) + ')');
|
449 |
+
})
|
450 |
+
} else {
|
451 |
+
$('.bookly-column .bookly-hour.bookly-slot-in-waiting-list').removeClass('bookly-slot-in-waiting-list').addClass('no-waiting-list').find('.bookly-time-additional').text('');
|
452 |
+
$time_step_nop.trigger('change');
|
453 |
+
}
|
454 |
+
}).trigger('change');
|
455 |
+
|
456 |
/**
|
457 |
* Step repeat.
|
458 |
*/
|
492 |
});
|
493 |
|
494 |
|
495 |
+
/**
|
496 |
+
* Step Repeat.
|
497 |
+
*/
|
498 |
+
$bookly_show_step_repeat.change(function () {
|
499 |
+
$('.bookly-js-repeat-enabled').toggle(this.checked);
|
500 |
+
}).trigger('change');
|
501 |
+
|
502 |
+
/**
|
503 |
+
* Step Cart
|
504 |
+
*/
|
505 |
+
$show_cart_extras.change(function () {
|
506 |
+
$('.bookly-js-extras-cart').toggle(this.checked);
|
507 |
+
}).trigger('change');
|
508 |
+
|
509 |
/**
|
510 |
* Step Details
|
511 |
*/
|
525 |
});
|
526 |
}
|
527 |
|
528 |
+
// Show login button.
|
529 |
$show_login_button.change(function () {
|
530 |
+
$('#bookly-login-button').toggle(this.checked);
|
531 |
}).trigger('change');
|
532 |
|
533 |
+
// Show Facebook login button.
|
534 |
+
$show_facebook_login_button.change(function () {
|
535 |
+
if ($(this).data('appid') == '') {
|
536 |
+
$('#bookly-facebook-warning').modal('show');
|
537 |
+
this.checked = false;
|
538 |
+
} else {
|
539 |
+
$('#bookly-facebook-login-button').toggle(this.checked);
|
540 |
+
}
|
541 |
+
});
|
542 |
+
|
543 |
+
if ($show_facebook_login_button.prop('checked')) {
|
544 |
+
$show_facebook_login_button.trigger('change');
|
545 |
+
}
|
546 |
+
|
547 |
// Show first and last name.
|
548 |
$first_last_name.on('change', function () {
|
549 |
$first_last_name.popover('toggle');
|
561 |
$('#bookly-js-notes').toggle(this.checked);
|
562 |
}).trigger('change');
|
563 |
|
564 |
+
// Show birthday fields
|
565 |
+
$show_birthday_fields.change(function () {
|
566 |
+
$('#bookly-js-birthday').toggle(this.checked);
|
567 |
+
}).trigger('change');
|
568 |
+
|
569 |
+
// Show address fields
|
570 |
+
$show_address_fields.change(function () {
|
571 |
+
$('#bookly-js-address').toggle(this.checked);
|
572 |
+
if (this.checked) {
|
573 |
+
$show_google_maps.closest('[data-toggle="popover"]').popover('destroy');
|
574 |
+
$show_google_maps.prop('disabled', false);
|
575 |
+
} else {
|
576 |
+
$show_google_maps.closest('[data-toggle="popover"]').popover();
|
577 |
+
$show_google_maps.prop('checked', false).prop('disabled', true).trigger('change');
|
578 |
+
}
|
579 |
+
}).trigger('change');
|
580 |
+
|
581 |
+
// Show address fields
|
582 |
+
$show_google_maps.change(function () {
|
583 |
+
$('.bookly-js-google-maps').toggle(this.checked);
|
584 |
+
}).trigger('change');
|
585 |
+
|
586 |
+
$show_custom_fields.change(function () {
|
587 |
+
$('.bookly-js-custom-fields').toggle(this.checked);
|
588 |
+
if (this.checked) {
|
589 |
+
$show_files.closest('[data-toggle="popover"]').popover('destroy');
|
590 |
+
$show_files.prop('disabled', false);
|
591 |
+
} else {
|
592 |
+
$show_files.closest('[data-toggle="popover"]').popover();
|
593 |
+
$show_files.prop('checked', false).prop('disabled', true).trigger('change');
|
594 |
+
}
|
595 |
+
}).trigger('change');
|
596 |
+
|
597 |
+
$show_files.change(function () {
|
598 |
+
$('.bookly-js-files').toggle(this.checked);
|
599 |
+
}).trigger('change');
|
600 |
+
|
601 |
+
$show_customer_information.change(function () {
|
602 |
+
$('.bookly-js-customer-information').toggle(this.checked);
|
603 |
+
}).trigger('change');
|
604 |
+
|
605 |
/**
|
606 |
* Step Payment.
|
607 |
*/
|
617 |
$('form.bookly-card-form').toggle(this.id == 'bookly-card-payment');
|
618 |
});
|
619 |
|
620 |
+
$show_coupons.on('change', function () {
|
621 |
+
$('.bookly-js-payment-coupons').toggle( this.checked );
|
622 |
+
});
|
623 |
+
|
624 |
/**
|
625 |
* Step Done.
|
626 |
*/
|
636 |
/**
|
637 |
* Misc.
|
638 |
*/
|
639 |
+
$('.bookly-js-simple-popover').popover();
|
640 |
|
641 |
// Custom CSS.
|
642 |
$('#bookly-custom-css-save').on('click', function (e) {
|
697 |
});
|
698 |
|
699 |
// Save options.
|
700 |
+
$save_button.on('click', function (e) {
|
701 |
e.preventDefault();
|
702 |
// Prepare data.
|
703 |
var data = {
|
704 |
+
action : 'bookly_update_appearance_options',
|
705 |
csrf_token: BooklyL10n.csrf_token,
|
706 |
+
options : {
|
707 |
// Color.
|
708 |
'bookly_app_color' : $color_picker.wpColorPicker('color'),
|
709 |
// Checkboxes.
|
711 |
'bookly_app_show_blocked_timeslots' : Number($show_blocked_timeslots.prop('checked')),
|
712 |
'bookly_app_show_calendar' : Number($show_calendar.prop('checked')),
|
713 |
'bookly_app_show_day_one_column' : Number($show_day_one_column.prop('checked')),
|
714 |
+
'bookly_app_show_time_zone_switcher' : Number($show_time_zone_switcher.prop('checked')),
|
715 |
'bookly_app_show_login_button' : Number($show_login_button.prop('checked')),
|
716 |
+
'bookly_app_show_facebook_login_button' : Number($show_facebook_login_button.prop('checked')),
|
717 |
'bookly_app_show_notes' : Number($show_notes_field.prop('checked')),
|
718 |
+
'bookly_app_show_birthday' : Number($show_birthday_fields.prop('checked')),
|
719 |
+
'bookly_app_show_address' : Number($show_address_fields.prop('checked')),
|
720 |
'bookly_app_show_progress_tracker' : Number($show_progress_tracker.prop('checked')),
|
721 |
'bookly_app_staff_name_with_price' : Number($staff_name_with_price.prop('checked')),
|
722 |
+
'bookly_app_service_duration_with_price': Number($service_duration_with_price.prop('checked')),
|
723 |
'bookly_app_required_employee' : Number($required_employee.prop('checked')),
|
724 |
'bookly_app_required_location' : Number($required_location.prop('checked')),
|
725 |
+
'bookly_group_booking_app_show_nop' : Number($time_step_nop.prop('checked')),
|
726 |
+
'bookly_ratings_app_show_on_frontend' : Number($show_ratings.prop('checked')),
|
727 |
+
'bookly_cst_required_details' : $required_details.val() == 'both' ? ['phone', 'email'] : [$required_details.val()],
|
728 |
+
'bookly_cst_first_last_name' : Number($first_last_name.prop('checked')),
|
729 |
+
'bookly_service_extras_enabled' : Number($bookly_show_step_extras.prop('checked')),
|
730 |
+
'bookly_recurring_appointments_enabled' : Number($bookly_show_step_repeat.prop('checked')),
|
731 |
+
'bookly_cart_enabled' : Number($bookly_show_step_cart.prop('checked')),
|
732 |
+
'bookly_chain_appointments_enabled' : Number($show_chain_appointments.prop('checked')),
|
733 |
+
'bookly_coupons_enabled' : Number($show_coupons.prop('checked')),
|
734 |
+
'bookly_custom_fields_enabled' : Number($show_custom_fields.prop('checked')),
|
735 |
+
'bookly_customer_information_enabled' : Number($show_customer_information.prop('checked')),
|
736 |
+
'bookly_files_enabled' : Number($show_files.prop('checked')),
|
737 |
+
'bookly_waiting_list_enabled' : Number($show_waiting_list.prop('checked')),
|
738 |
+
'bookly_google_maps_address_enabled' : Number($show_google_maps.prop('checked')),
|
739 |
+
'bookly_service_extras_show_in_cart' : Number($show_cart_extras.prop('checked')),
|
740 |
+
'bookly_locations_enabled' : Number($show_location.prop('checked')),
|
741 |
+
'bookly_custom_duration_enabled' : Number($show_custom_duration.prop('checked')),
|
742 |
+
'bookly_group_booking_enabled' : Number($show_nop.prop('checked')),
|
743 |
+
'bookly_multiply_appointments_enabled' : Number($show_quantity.prop('checked'))
|
744 |
}
|
745 |
};
|
746 |
// Add data from editable elements.
|
772 |
$(this).prop('checked', $(this).data('default')).trigger('change');
|
773 |
}
|
774 |
});
|
775 |
+
$selects.each(function () {
|
776 |
+
if ($(this).val() != $(this).data('default')) {
|
777 |
+
$(this).val($(this).data('default')).trigger('change');
|
778 |
+
}
|
779 |
+
});
|
780 |
$first_last_name.popover('hide');
|
781 |
});
|
782 |
});
|
backend/modules/appearance/resources/js/bootstrap-editable.bookly.js
CHANGED
@@ -32,44 +32,57 @@ jQuery(function($) {
|
|
32 |
var $row = $('<div/>')
|
33 |
.css({position: 'relative', 'margin-top': '6px'})
|
34 |
.appendTo(_this.$tpl);
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
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 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
});
|
75 |
// Set codes.
|
32 |
var $row = $('<div/>')
|
33 |
.css({position: 'relative', 'margin-top': '6px'})
|
34 |
.appendTo(_this.$tpl);
|
35 |
+
switch (_this.options.fieldType) {
|
36 |
+
case 'input':
|
37 |
+
// Create input with "x" button.
|
38 |
+
var $clear = $('<span class="editable-clear-x"></span>');
|
39 |
+
var $input = $('<input/>', {
|
40 |
+
type : 'text',
|
41 |
+
class: 'form-control',
|
42 |
+
name : option_name,
|
43 |
+
value: option_value
|
44 |
+
});
|
45 |
+
$input.keyup(function(e) {
|
46 |
+
// arrows, enter, tab, etc
|
47 |
+
if (~$.inArray(e.keyCode, [40,38,9,13,27])) {
|
48 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
+
clearTimeout(this.t);
|
51 |
+
this.t = setTimeout(function() {
|
52 |
+
var len = $input.val().length,
|
53 |
+
visible = $clear.is(':visible');
|
54 |
+
if (len && !visible) {
|
55 |
+
$clear.show();
|
56 |
+
}
|
57 |
+
if (!len && visible) {
|
58 |
+
$clear.hide();
|
59 |
+
}
|
60 |
+
}, 100);
|
61 |
+
});
|
62 |
+
$clear.click(function () {
|
63 |
+
$clear.hide();
|
64 |
+
$input.val('').focus();
|
65 |
+
});
|
66 |
+
$row.append($input).append($clear);
|
67 |
+
break;
|
68 |
+
case 'number':
|
69 |
+
// Create input[type="number"]
|
70 |
+
$('<input/>', {
|
71 |
+
type : 'number',
|
72 |
+
class: 'form-control',
|
73 |
+
name : option_name,
|
74 |
+
min : $(_this.options.scope).data('min'),
|
75 |
+
step : $(_this.options.scope).data('step')
|
76 |
+
}).val(option_value).appendTo($row);
|
77 |
+
break;
|
78 |
+
default :
|
79 |
+
// Create textarea.
|
80 |
+
$('<textarea/>', {
|
81 |
+
class: 'form-control',
|
82 |
+
name : option_name,
|
83 |
+
rows : 7
|
84 |
+
}).val(option_value).appendTo($row);
|
85 |
+
break;
|
86 |
}
|
87 |
});
|
88 |
// Set codes.
|
backend/modules/appearance/templates/_1_service.php
CHANGED
@@ -1,11 +1,10 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
*/
|
9 |
global $wp_locale;
|
10 |
?>
|
11 |
<div class="bookly-form">
|
@@ -14,18 +13,14 @@ global $wp_locale;
|
|
14 |
<div class="bookly-service-step">
|
15 |
<div class="bookly-box">
|
16 |
<span class="bookly-bold bookly-desc">
|
17 |
-
<?php
|
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
|
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
|
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>
|
@@ -37,7 +32,7 @@ global $wp_locale;
|
|
37 |
</div>
|
38 |
</div>
|
39 |
<div class="bookly-form-group">
|
40 |
-
<?php
|
41 |
'bookly_l10n_label_service',
|
42 |
'bookly_l10n_option_service',
|
43 |
'bookly_l10n_required_service',
|
@@ -65,7 +60,7 @@ global $wp_locale;
|
|
65 |
</div>
|
66 |
</div>
|
67 |
<div class="bookly-form-group">
|
68 |
-
<?php
|
69 |
'bookly_l10n_label_employee',
|
70 |
'bookly_l10n_option_employee',
|
71 |
'bookly_l10n_required_employee',
|
@@ -96,32 +91,14 @@ global $wp_locale;
|
|
96 |
</select>
|
97 |
</div>
|
98 |
</div>
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
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
|
125 |
</div>
|
126 |
</div>
|
127 |
<div class="bookly-mobile-step-2 bookly-js-mobile-step-2">
|
@@ -129,7 +106,7 @@ global $wp_locale;
|
|
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
|
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>
|
@@ -148,7 +125,7 @@ global $wp_locale;
|
|
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
|
152 |
<div>
|
153 |
<select class="bookly-js-select-time-from">
|
154 |
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
@@ -158,7 +135,7 @@ global $wp_locale;
|
|
158 |
</div>
|
159 |
</div>
|
160 |
<div class="bookly-form-group bookly-time-to bookly-left">
|
161 |
-
<?php
|
162 |
<div>
|
163 |
<select class="bookly-js-select-time-to">
|
164 |
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
@@ -171,10 +148,10 @@ global $wp_locale;
|
|
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
|
175 |
</div>
|
176 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
177 |
-
<?php
|
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>
|
@@ -186,4 +163,4 @@ global $wp_locale;
|
|
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>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib\Utils\Price;
|
3 |
+
use Bookly\Lib\Utils\DateTime;
|
4 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
5 |
+
use Bookly\Lib\Config;
|
6 |
+
use Bookly\Backend\Components\Appearance\Editable;
|
7 |
+
/** @var WP_Locale $wp_locale */
|
|
|
8 |
global $wp_locale;
|
9 |
?>
|
10 |
<div class="bookly-form">
|
13 |
<div class="bookly-service-step">
|
14 |
<div class="bookly-box">
|
15 |
<span class="bookly-bold bookly-desc">
|
16 |
+
<?php Editable::renderText( 'bookly_l10n_info_service_step' ) ?>
|
17 |
</span>
|
18 |
</div>
|
19 |
<div class="bookly-mobile-step-1 bookly-js-mobile-step-1 bookly-box">
|
20 |
<div class="bookly-js-chain-item bookly-table bookly-box">
|
21 |
+
<?php Proxy\Locations::renderLocation() ?>
|
|
|
|
|
|
|
|
|
22 |
<div class="bookly-form-group">
|
23 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_category', 'bookly_l10n_option_category', ) ) ?>
|
24 |
<div>
|
25 |
<select class="bookly-select-mobile bookly-js-select-category">
|
26 |
<option value="" class="bookly-js-option bookly_l10n_option_category"><?php echo esc_html( get_option( 'bookly_l10n_option_category' ) ) ?></option>
|
32 |
</div>
|
33 |
</div>
|
34 |
<div class="bookly-form-group">
|
35 |
+
<?php Editable::renderLabel( array(
|
36 |
'bookly_l10n_label_service',
|
37 |
'bookly_l10n_option_service',
|
38 |
'bookly_l10n_required_service',
|
60 |
</div>
|
61 |
</div>
|
62 |
<div class="bookly-form-group">
|
63 |
+
<?php Editable::renderLabel( array(
|
64 |
'bookly_l10n_label_employee',
|
65 |
'bookly_l10n_option_employee',
|
66 |
'bookly_l10n_required_employee',
|
91 |
</select>
|
92 |
</div>
|
93 |
</div>
|
94 |
+
<?php Proxy\CustomDuration::renderServiceDuration() ?>
|
95 |
+
<?php Proxy\GroupBooking::renderNOP() ?>
|
96 |
+
<?php Proxy\MultiplyAppointments::renderQuantity() ?>
|
97 |
+
<?php Proxy\ChainAppointments::renderChain() ?>
|
98 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
</div>
|
100 |
<div class="bookly-right bookly-mobile-next-step bookly-js-mobile-next-step bookly-btn bookly-none">
|
101 |
+
<?php Editable::renderString( array( 'bookly_l10n_step_service_mobile_button_next' ) ) ?>
|
102 |
</div>
|
103 |
</div>
|
104 |
<div class="bookly-mobile-step-2 bookly-js-mobile-step-2">
|
106 |
<div class="bookly-left">
|
107 |
<div class="bookly-available-date bookly-js-available-date bookly-left">
|
108 |
<div class="bookly-form-group">
|
109 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_select_date', ) ) ?>
|
110 |
<div>
|
111 |
<input class="bookly-date-from bookly-js-date-from" style="background-color: #fff;" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
112 |
</div>
|
125 |
</div>
|
126 |
<div class="bookly-time-range bookly-js-time-range bookly-left">
|
127 |
<div class="bookly-form-group bookly-time-from bookly-left">
|
128 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_start_from', ) ) ?>
|
129 |
<div>
|
130 |
<select class="bookly-js-select-time-from">
|
131 |
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
135 |
</div>
|
136 |
</div>
|
137 |
<div class="bookly-form-group bookly-time-to bookly-left">
|
138 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_finish_by', ) ) ?>
|
139 |
<div>
|
140 |
<select class="bookly-js-select-time-to">
|
141 |
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
148 |
</div>
|
149 |
<div class="bookly-box bookly-nav-steps">
|
150 |
<div class="bookly-right bookly-mobile-prev-step bookly-js-mobile-prev-step bookly-btn bookly-none">
|
151 |
+
<?php Editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
|
152 |
</div>
|
153 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
154 |
+
<?php Editable::renderString( array( 'bookly_l10n_step_service_button_next' ) ) ?>
|
155 |
</div>
|
156 |
<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>
|
157 |
</div>
|
163 |
<div class="bookly-js-option <?php echo $validator ?>"><?php echo get_option( $validator ) ?></div>
|
164 |
<?php endforeach ?>
|
165 |
</div>
|
166 |
+
<style id="bookly-pickadate-style"></style>
|
backend/modules/appearance/templates/_3_time.php
CHANGED
@@ -1,161 +1,194 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
|
|
|
|
|
|
|
4 |
?>
|
5 |
<div class="bookly-form">
|
6 |
<?php include '_progress_tracker.php' ?>
|
7 |
|
8 |
<div class="bookly-box">
|
9 |
-
<?php
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
13 |
</div>
|
|
|
|
|
14 |
<!-- timeslots -->
|
15 |
<div class="bookly-time-step">
|
16 |
<div class="bookly-columnizer-wrap">
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
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 |
-
</
|
41 |
-
<
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
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 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
<div class="bookly-column
|
145 |
-
|
146 |
-
|
147 |
-
<button class="bookly-hour ladda-button<?php if (
|
148 |
-
|
149 |
-
|
150 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
</button>
|
152 |
<?php endfor ?>
|
153 |
</div>
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
</div>
|
156 |
</div>
|
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">></span>
|
@@ -164,7 +197,7 @@ use BooklyLite\Backend\Modules\Appearance\Components;
|
|
164 |
<span class="bookly-label"><</span>
|
165 |
</button>
|
166 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
167 |
-
<?php
|
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>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib\Utils\DateTime;
|
3 |
+
use Bookly\Lib\Config;
|
4 |
+
use Bookly\Backend\Components\Appearance\Codes;
|
5 |
+
use Bookly\Backend\Components\Appearance\Editable;
|
6 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
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_time_step', Codes::getHtml( 3 ) ) ?>
|
13 |
+
</div>
|
14 |
+
<?php Proxy\WaitingList::renderInfoText() ?>
|
15 |
+
<div class="bookly-box bookly-label-error" style="padding-bottom:2px">
|
16 |
+
<?php Editable::renderText( 'bookly_l10n_step_time_slot_not_available', null, 'bottom', __( 'Visible when the chosen time slot has been already booked', 'bookly' ) ) ?>
|
17 |
</div>
|
18 |
+
<?php Proxy\Pro::renderTimeZoneSwitcher() ?>
|
19 |
+
|
20 |
<!-- timeslots -->
|
21 |
<div class="bookly-time-step">
|
22 |
<div class="bookly-columnizer-wrap">
|
23 |
+
<div class="bookly-columnizer">
|
24 |
+
<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' ?>">
|
25 |
+
<div class="bookly-input-wrap bookly-slot-calendar bookly-js-slot-calendar">
|
26 |
+
<span class="bookly-date-wrap">
|
27 |
+
<input style="display: none" class="bookly-js-selected-date bookly-form-element" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
</span>
|
29 |
+
</div>
|
30 |
+
<div class="bookly-column col1">
|
31 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', current_time( 'timestamp' ) ) ?></button>
|
32 |
+
<?php for ( $i = 28800; $i <= 57600; $i += 3600 ) : ?>
|
33 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
34 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked'; elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list' ?>">
|
35 |
+
<span class="ladda-label bookly-time-main">
|
36 |
+
<i class="bookly-hour-icon"><span></span></i>
|
37 |
+
<?php echo DateTime::formatTime( $i ) ?>
|
38 |
+
</span>
|
39 |
+
<span class="bookly-time-additional"></span>
|
40 |
+
</button>
|
41 |
+
<?php endfor ?>
|
42 |
+
</div>
|
43 |
+
<div class="bookly-column col2">
|
44 |
+
<button class="bookly-hour ladda-button bookly-last-child">
|
45 |
+
<span class="ladda-label bookly-time-main">
|
46 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( 61200 ) ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
</span>
|
48 |
+
<span class="bookly-time-additional"></span>
|
49 |
</button>
|
50 |
+
<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>
|
51 |
+
<?php for ( $i = 28800; $i <= 54000; $i += 3600 ) : ?>
|
52 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
53 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'block' ?>">
|
54 |
+
<span class="ladda-label bookly-time-main">
|
55 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
56 |
+
</span>
|
57 |
+
<span class="bookly-time-additional"></span>
|
58 |
+
</button>
|
59 |
+
<?php endfor ?>
|
60 |
+
</div>
|
61 |
+
<div class="bookly-column col3" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
62 |
+
<?php for ( $i = 57600; $i <= 61200; $i += 3600 ) : ?>
|
63 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
64 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
65 |
+
<span class="ladda-label bookly-time-main">
|
66 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
67 |
+
</span>
|
68 |
+
<span class="bookly-time-additional"></span>
|
69 |
+
</button>
|
70 |
+
<?php endfor ?>
|
71 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 days', current_time('timestamp') ) ) ?></button>
|
72 |
+
<?php for ( $i = 28800; $i <= 50400; $i += 3600 ) : ?>
|
73 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
74 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
75 |
+
<span class="ladda-label bookly-time-main">
|
76 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
77 |
+
</span>
|
78 |
+
<span class="bookly-time-additional"></span>
|
79 |
+
</button>
|
80 |
+
<?php endfor ?>
|
81 |
+
</div>
|
82 |
+
<div class="bookly-column col4" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
83 |
+
<?php for ( $i = 54000; $i <= 61200; $i += 3600 ) : ?>
|
84 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
85 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
86 |
+
<span class="ladda-label bookly-time-main">
|
87 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
88 |
+
</span>
|
89 |
+
<span class="bookly-time-additional"></span>
|
90 |
+
</button>
|
91 |
+
<?php endfor ?>
|
92 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+3 days', current_time( 'timestamp' ) ) ) ?></button>
|
93 |
+
<?php for ( $i = 28800; $i <= 46800; $i += 3600 ) : ?>
|
94 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
95 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
96 |
+
<span class="ladda-label bookly-time-main">
|
97 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
98 |
+
</span>
|
99 |
+
<span class="bookly-time-additional"></span>
|
100 |
+
</button>
|
101 |
+
<?php endfor ?>
|
102 |
+
</div>
|
103 |
+
<div class="bookly-column col5" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
104 |
+
<?php for ( $i = 50400; $i <= 61200; $i += 3600 ) : ?>
|
105 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
106 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
107 |
+
<span class="ladda-label bookly-time-main">
|
108 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
109 |
+
</span>
|
110 |
+
<span class="bookly-time-additional"></span>
|
111 |
+
</button>
|
112 |
+
<?php endfor ?>
|
113 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+4 days', current_time( 'timestamp' ) ) ) ?></button>
|
114 |
+
<?php for ( $i = 28800; $i <= 43200; $i += 3600 ) : ?>
|
115 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
116 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
117 |
+
<span class="ladda-label bookly-time-main">
|
118 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
119 |
+
</span>
|
120 |
+
<span class="bookly-time-additional"></span>
|
121 |
</button>
|
122 |
<?php endfor ?>
|
123 |
</div>
|
124 |
+
<div class="bookly-column col6" style="display: <?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
125 |
+
<?php for ( $i = 46800; $i <= 61200; $i += 3600 ) : ?>
|
126 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
127 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
128 |
+
<span class="ladda-label bookly-time-main">
|
129 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
130 |
+
</span>
|
131 |
+
<span class="bookly-time-additional"></span>
|
132 |
+
</button>
|
133 |
+
<?php endfor ?>
|
134 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+5 days', current_time( 'timestamp' ) ) ) ?></button>
|
135 |
+
<?php for ( $i = 28800; $i <= 39600; $i += 3600 ) : ?>
|
136 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
137 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
138 |
+
<span class="ladda-label bookly-time-main">
|
139 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
140 |
+
</span>
|
141 |
+
<span class="bookly-time-additional"></span>
|
142 |
+
</button>
|
143 |
+
<?php endfor ?>
|
144 |
+
</div>
|
145 |
+
<div class="bookly-column col7" style="display:<?php echo get_option( 'bookly_app_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
146 |
+
<?php for ( $i = 43200; $i <= 61200; $i += 3600 ) : ?>
|
147 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
148 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
149 |
+
<span class="ladda-label bookly-time-main">
|
150 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
151 |
+
</span>
|
152 |
+
<span class="bookly-time-additional"></span>
|
153 |
+
</button>
|
154 |
+
<?php endfor ?>
|
155 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+6 days', current_time( 'timestamp' ) ) ) ?></button>
|
156 |
+
<?php for ( $i = 28800; $i <= 36000; $i += 3600 ) : ?>
|
157 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
158 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
159 |
+
<span class="ladda-label bookly-time-main">
|
160 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $i ) ?>
|
161 |
+
</span>
|
162 |
+
<span class="bookly-time-additional"></span>
|
163 |
+
</button>
|
164 |
+
<?php endfor ?>
|
165 |
+
</div>
|
166 |
+
</div>
|
167 |
+
|
168 |
+
<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' ?>">
|
169 |
+
<div class="bookly-input-wrap bookly-slot-calendar bookly-js-slot-calendar">
|
170 |
+
<span class="bookly-date-wrap">
|
171 |
+
<input style="display: none" class="bookly-js-selected-date bookly-form-element" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
172 |
+
</span>
|
173 |
+
</div>
|
174 |
+
<?php for ( $i = 1; $i <= 7; ++ $i ) : ?>
|
175 |
+
<div class="bookly-column col<?php echo $i ?>">
|
176 |
+
<button class="bookly-day bookly-js-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+' . ( $i - 1 ) . ' days', current_time( 'timestamp' ) ) ) ?></button>
|
177 |
+
<?php for ( $j = 28800; $j <= 61200; $j += 3600 ) : ?>
|
178 |
+
<?php $slot_type = mt_rand( 0, 2 ) ?>
|
179 |
+
<button class="bookly-hour ladda-button<?php if ( $slot_type == 1 ) echo get_option( 'bookly_app_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked';elseif ( $slot_type == 2 && Config::waitingListActive() ) echo ' no-waiting-list'?>">
|
180 |
+
<span class="ladda-label bookly-time-main">
|
181 |
+
<i class="bookly-hour-icon"><span></span></i><?php echo DateTime::formatTime( $j ) ?>
|
182 |
+
</span>
|
183 |
+
<span class="bookly-time-additional"></span>
|
184 |
+
</button>
|
185 |
+
<?php endfor ?>
|
186 |
+
</div>
|
187 |
+
<?php endfor ?>
|
188 |
+
</div>
|
189 |
</div>
|
190 |
</div>
|
191 |
</div>
|
|
|
192 |
<div class="bookly-box bookly-nav-steps">
|
193 |
<button class="bookly-time-next bookly-btn bookly-right ladda-button">
|
194 |
<span class="bookly-label">></span>
|
197 |
<span class="bookly-label"><</span>
|
198 |
</button>
|
199 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
200 |
+
<?php Editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
|
201 |
</div>
|
202 |
<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>
|
203 |
</div>
|
backend/modules/appearance/templates/_5_cart.php
DELETED
@@ -1,144 +0,0 @@
|
|
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>
|
113 |
-
<button class="bookly-round" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><i class="bookly-icon-sm bookly-icon-drop"></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,74 +1,89 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
|
|
|
|
|
|
|
4 |
?>
|
5 |
<div class="bookly-form">
|
6 |
<?php include '_progress_tracker.php' ?>
|
7 |
|
8 |
<div class="bookly-box">
|
9 |
-
<?php
|
10 |
</div>
|
11 |
<div class="bookly-box">
|
12 |
-
<?php
|
13 |
</div>
|
14 |
-
<div class="bookly-box
|
15 |
-
<div class="bookly-btn bookly-
|
16 |
-
<?php
|
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
|
23 |
<div>
|
24 |
<input type="text" value="" maxlength="60" />
|
25 |
</div>
|
26 |
</div>
|
27 |
<div class="bookly-form-group">
|
28 |
-
<?php
|
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
|
37 |
<div>
|
38 |
<input type="text" value="" maxlength="60" />
|
39 |
</div>
|
40 |
</div>
|
41 |
<div class="bookly-form-group">
|
42 |
-
<?php
|
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
|
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
|
57 |
<div>
|
58 |
<textarea rows="3"></textarea>
|
59 |
</div>
|
60 |
</div>
|
61 |
</div>
|
|
|
|
|
62 |
</div>
|
63 |
|
64 |
-
<?php
|
65 |
|
66 |
<div class="bookly-box bookly-nav-steps">
|
67 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
68 |
-
<?php
|
69 |
</div>
|
70 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
71 |
-
<?php
|
72 |
</div>
|
73 |
</div>
|
74 |
</div>
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components;
|
3 |
+
use Bookly\Backend\Components\Appearance\Codes;
|
4 |
+
use Bookly\Backend\Components\Appearance\Editable;
|
5 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
6 |
+
/** @var array $userData */
|
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_details_step', Codes::getHtml( 6 ) ) ?>
|
13 |
</div>
|
14 |
<div class="bookly-box">
|
15 |
+
<?php Editable::renderText( 'bookly_l10n_info_details_step_guest', Codes::getHtml( 6, true ), 'bottom', __( 'Visible to non-logged in customers only', 'bookly' ) ) ?>
|
16 |
</div>
|
17 |
+
<div class="bookly-box bookly-guest">
|
18 |
+
<div class="bookly-btn" id="bookly-login-button">
|
19 |
+
<?php Editable::renderString( array( 'bookly_l10n_step_details_button_login' ) ) ?>
|
20 |
</div>
|
21 |
+
<div class="fb-login-button" id="bookly-facebook-login-button" data-max-rows="1" data-size="large" data-button-type="login_with" data-show-faces="false" data-auto-logout-link="false" data-use-continue-as="false" data-scope="public_profile,email" style="display:none"></div>
|
22 |
</div>
|
23 |
<div class="bookly-details-step">
|
24 |
+
|
25 |
<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' ?>">
|
26 |
<div class="bookly-form-group">
|
27 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_first_name', 'bookly_l10n_required_first_name', ) ) ?>
|
28 |
<div>
|
29 |
<input type="text" value="" maxlength="60" />
|
30 |
</div>
|
31 |
</div>
|
32 |
<div class="bookly-form-group">
|
33 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_last_name', 'bookly_l10n_required_last_name', ) ) ?>
|
34 |
<div>
|
35 |
<input type="text" value="" maxlength="60" />
|
36 |
</div>
|
37 |
</div>
|
38 |
</div>
|
39 |
+
|
40 |
<div class="bookly-box bookly-table">
|
41 |
<div class="bookly-form-group bookly-details-full-name" style="display: <?php echo get_option( 'bookly_cst_first_last_name' ) == 1 ? ' none' : 'block' ?>">
|
42 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_name', 'bookly_l10n_required_name', ) ) ?>
|
43 |
<div>
|
44 |
<input type="text" value="" maxlength="60" />
|
45 |
</div>
|
46 |
</div>
|
47 |
<div class="bookly-form-group">
|
48 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_phone', 'bookly_l10n_required_phone', ) ) ?>
|
49 |
<div>
|
50 |
<input type="text" class="<?php if ( get_option( 'bookly_cst_phone_default_country' ) != 'disabled' ) : ?>bookly-user-phone<?php endif ?>" value="" />
|
51 |
</div>
|
52 |
</div>
|
53 |
<div class="bookly-form-group">
|
54 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_email', 'bookly_l10n_required_email' ) ) ?>
|
55 |
<div>
|
56 |
<input maxlength="40" type="text" value="" />
|
57 |
</div>
|
58 |
</div>
|
59 |
</div>
|
60 |
+
|
61 |
+
<?php Components\Appearance\Proxy\Pro::renderAddress() ?>
|
62 |
+
<?php Components\Appearance\Proxy\Pro::renderBirthday() ?>
|
63 |
+
<?php Proxy\CustomerInformation::renderCustomerInformation() ?>
|
64 |
+
<?php Proxy\CustomFields::renderCustomFields() ?>
|
65 |
+
|
66 |
<div class="bookly-box" id="bookly-js-notes">
|
67 |
<div class="bookly-form-group">
|
68 |
+
<?php Editable::renderLabel( array( 'bookly_l10n_label_notes' ) ) ?>
|
69 |
<div>
|
70 |
<textarea rows="3"></textarea>
|
71 |
</div>
|
72 |
</div>
|
73 |
</div>
|
74 |
+
|
75 |
+
<?php Proxy\Files::renderAppearance() ?>
|
76 |
</div>
|
77 |
|
78 |
+
<?php Proxy\RecurringAppointments::renderInfoMessage() ?>
|
79 |
|
80 |
<div class="bookly-box bookly-nav-steps">
|
81 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
82 |
+
<?php Editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
|
83 |
</div>
|
84 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
85 |
+
<?php Editable::renderString( array( 'bookly_l10n_step_details_button_next' ) ) ?>
|
86 |
</div>
|
87 |
</div>
|
88 |
</div>
|
89 |
+
<?php Proxy\Pro::renderFacebookButton() ?>
|
backend/modules/appearance/templates/_7_payment.php
CHANGED
@@ -1,60 +1,50 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
|
|
|
4 |
?>
|
5 |
<div class="bookly-form">
|
6 |
<?php include '_progress_tracker.php' ?>
|
7 |
-
|
|
|
|
|
8 |
|
9 |
<div class="bookly-payment-nav">
|
10 |
<div class="bookly-box bookly-js-payment-single-app">
|
11 |
-
<?php
|
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
|
21 |
</label>
|
22 |
</div>
|
23 |
|
24 |
-
|
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
|
40 |
-
<img src="<?php echo plugins_url( 'frontend/resources/images/cards.png', \
|
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
|
48 |
</div>
|
49 |
|
50 |
-
<?php
|
51 |
|
52 |
<div class="bookly-box bookly-nav-steps">
|
53 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
54 |
-
<?php
|
55 |
</div>
|
56 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
57 |
-
<?php
|
58 |
</div>
|
59 |
</div>
|
60 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Appearance\Codes;
|
3 |
+
use Bookly\Backend\Components\Appearance\Editable;
|
4 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
5 |
?>
|
6 |
<div class="bookly-form">
|
7 |
<?php include '_progress_tracker.php' ?>
|
8 |
+
|
9 |
+
<?php Proxy\Coupons::renderCouponBlock() ?>
|
10 |
+
<?php Proxy\DepositPayments::renderAppearance() ?>
|
11 |
|
12 |
<div class="bookly-payment-nav">
|
13 |
<div class="bookly-box bookly-js-payment-single-app">
|
14 |
+
<?php Editable::renderText( 'bookly_l10n_info_payment_step_single_app', Codes::getHtml( 7 ), 'right' ) ?>
|
|
|
|
|
|
|
15 |
</div>
|
16 |
+
<?php Proxy\Pro::renderMultipleBookingText() ?>
|
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 |
+
<?php Proxy\Pro::renderPayPalPaymentOption() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
+
<div class="bookly-box bookly-list"<?php if ( Proxy\Shared::showCreditCard() == false ): ?> style="display: none"<?php endif ?>>
|
|
|
|
|
|
|
|
|
27 |
<label>
|
28 |
<input type="radio" name="payment" id="bookly-card-payment" />
|
29 |
+
<?php Editable::renderString( array( 'bookly_l10n_label_pay_ccard', ) ) ?>
|
30 |
+
<img src="<?php echo plugins_url( 'frontend/resources/images/cards.png', \Bookly\Lib\Plugin::getMainFile() ) ?>" alt="cards" />
|
31 |
</label>
|
32 |
<form class="bookly-card-form bookly-clear-bottom" style="margin-top:15px;display: none;">
|
33 |
<?php include '_card_payment.php' ?>
|
34 |
</form>
|
35 |
</div>
|
36 |
|
37 |
+
<?php Proxy\Shared::renderPaymentGatewaySelector() ?>
|
38 |
</div>
|
39 |
|
40 |
+
<?php Proxy\RecurringAppointments::renderInfoMessage() ?>
|
41 |
|
42 |
<div class="bookly-box bookly-nav-steps">
|
43 |
<div class="bookly-back-step bookly-js-back-step bookly-btn">
|
44 |
+
<?php Editable::renderString( array( 'bookly_l10n_button_back' ) ) ?>
|
45 |
</div>
|
46 |
<div class="bookly-next-step bookly-js-next-step bookly-btn">
|
47 |
+
<?php Editable::renderString( array( 'bookly_l10n_step_payment_button_next' ) ) ?>
|
48 |
</div>
|
49 |
</div>
|
50 |
</div>
|
backend/modules/appearance/templates/_8_complete.php
CHANGED
@@ -1,16 +1,16 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
|
4 |
?>
|
5 |
<div class="bookly-form">
|
6 |
<?php include '_progress_tracker.php' ?>
|
7 |
<div class="bookly-box bookly-js-done-success">
|
8 |
-
<?php
|
9 |
</div>
|
10 |
<div class="bookly-box bookly-js-done-limit-error collapse">
|
11 |
-
<?php
|
12 |
</div>
|
13 |
<div class="bookly-box bookly-js-done-processing collapse">
|
14 |
-
<?php
|
15 |
</div>
|
16 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Appearance\Codes;
|
3 |
+
use Bookly\Backend\Components\Appearance\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', Codes::getHtml( 8, true ) ) ?>
|
9 |
</div>
|
10 |
<div class="bookly-box bookly-js-done-limit-error collapse">
|
11 |
+
<?php Editable::renderText( 'bookly_l10n_info_complete_step_limit_error', Codes::getHtml( 8 ) ) ?>
|
12 |
</div>
|
13 |
<div class="bookly-box bookly-js-done-processing collapse">
|
14 |
+
<?php Editable::renderText( 'bookly_l10n_info_complete_step_processing', Codes::getHtml( 8, true ) ) ?>
|
15 |
</div>
|
16 |
</div>
|
backend/modules/appearance/templates/_card_payment.php
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
?>
|
4 |
<div class="bookly-box bookly-table">
|
5 |
<div class="bookly-form-group" style="width:200px!important">
|
6 |
<label>
|
7 |
-
<?php
|
8 |
</label>
|
9 |
<div>
|
10 |
<input type="text" />
|
@@ -12,7 +12,7 @@
|
|
12 |
</div>
|
13 |
<div class="bookly-form-group">
|
14 |
<label>
|
15 |
-
<?php
|
16 |
</label>
|
17 |
<div>
|
18 |
<select class="bookly-card-exp">
|
@@ -31,7 +31,7 @@
|
|
31 |
<div class="bookly-box bookly-clear-bottom">
|
32 |
<div class="bookly-form-group">
|
33 |
<label>
|
34 |
-
<?php
|
35 |
</label>
|
36 |
<div>
|
37 |
<input class="bookly-card-cvc" type="text" />
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Appearance\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" />
|
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">
|
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" />
|
backend/modules/appearance/templates/_codes.php
DELETED
@@ -1,20 +0,0 @@
|
|
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
CHANGED
@@ -1,12 +1,7 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
|
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">
|
@@ -28,8 +23,8 @@
|
|
28 |
</div>
|
29 |
<div class="modal-footer">
|
30 |
<div id="bookly-custom-css-error"></div>
|
31 |
-
<?php
|
32 |
-
<?php
|
33 |
</div>
|
34 |
</div>
|
35 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
/** @var string $custom_css custom css text */
|
|
|
|
|
|
|
|
|
4 |
?>
|
|
|
5 |
|
6 |
<div class="form-group">
|
7 |
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-custom-css-dialog">
|
23 |
</div>
|
24 |
<div class="modal-footer">
|
25 |
<div id="bookly-custom-css-error"></div>
|
26 |
+
<?php Buttons::renderCustom( 'bookly-custom-css-save', 'btn-success btn-lg', __( 'Save', 'bookly' ) ) ?>
|
27 |
+
<?php Buttons::renderCustom( 'bookly-custom-css-cancel', 'btn-default btn-lg', __( 'Cancel', 'bookly' ) ) ?>
|
28 |
</div>
|
29 |
</div>
|
30 |
</div>
|
backend/modules/appearance/templates/_progress_tracker.php
CHANGED
@@ -1,42 +1,46 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
|
|
|
|
4 |
?>
|
5 |
<div class="bookly-progress-tracker bookly-table">
|
6 |
<div class="active">
|
7 |
-
|
8 |
<div class="step"></div>
|
9 |
</div>
|
10 |
-
<?php if (
|
11 |
-
<div <?php if ( $step >= 2 ) : ?>class="active"<?php endif ?>>
|
12 |
-
|
13 |
<div class="step"></div>
|
14 |
</div>
|
15 |
<?php endif ?>
|
16 |
<div <?php if ( $step >= 3 ) : ?>class="active"<?php endif ?>>
|
17 |
-
|
18 |
<div class="step"></div>
|
19 |
</div>
|
20 |
-
<?php if (
|
21 |
-
<div <?php if ( $step >= 4 ) : ?>class="active"<?php endif ?>>
|
22 |
-
|
23 |
<div class=step></div>
|
24 |
</div>
|
25 |
<?php endif ?>
|
26 |
-
|
27 |
-
<?php
|
28 |
-
|
29 |
-
|
|
|
|
|
30 |
<div <?php if ( $step >= 6 ) : ?>class="active"<?php endif ?>>
|
31 |
-
|
32 |
<div class="step"></div>
|
33 |
</div>
|
34 |
<div <?php if ( $step >= 7 ) : ?>class="active"<?php endif ?>>
|
35 |
-
|
36 |
<div class="step"></div>
|
37 |
</div>
|
38 |
<div <?php if ( $step >= 8 ) : ?>class="active"<?php endif ?>>
|
39 |
-
|
40 |
<div class="step"></div>
|
41 |
</div>
|
42 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib\Config;
|
3 |
+
use Bookly\Backend\Components\Appearance\Editable;
|
4 |
+
|
5 |
+
$i = 1;
|
6 |
?>
|
7 |
<div class="bookly-progress-tracker bookly-table">
|
8 |
<div class="active">
|
9 |
+
<span class="bookly-js-step-number"><?php echo $i ++ ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_service' ) ) ?>
|
10 |
<div class="step"></div>
|
11 |
</div>
|
12 |
+
<?php if ( Config::serviceExtrasActive() ) : ?>
|
13 |
+
<div <?php if ( $step >= 2 ) : ?>class="active"<?php endif ?> data-step="bookly-step-2" <?php if ( ! get_option( 'bookly_service_extras_enabled' ) ) : ?>style="display: none;"<?php endif ?>>
|
14 |
+
<span class="bookly-js-step-number"><?php echo get_option( 'bookly_service_extras_enabled' ) ? $i ++ : $i ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_extras' ) ) ?>
|
15 |
<div class="step"></div>
|
16 |
</div>
|
17 |
<?php endif ?>
|
18 |
<div <?php if ( $step >= 3 ) : ?>class="active"<?php endif ?>>
|
19 |
+
<span class="bookly-js-step-number"><?php echo $i ++ ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_time' ) ) ?>
|
20 |
<div class="step"></div>
|
21 |
</div>
|
22 |
+
<?php if ( Config::recurringAppointmentsActive() ) : ?>
|
23 |
+
<div <?php if ( $step >= 4 ) : ?>class="active"<?php endif ?> data-step="bookly-step-4" <?php if ( ! get_option( 'bookly_recurring_appointments_enabled' ) ) : ?>style="display: none;"<?php endif ?>>
|
24 |
+
<span class="bookly-js-step-number"><?php echo get_option( 'bookly_recurring_appointments_enabled' ) ? $i ++ : $i ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_repeat' ) ) ?>
|
25 |
<div class=step></div>
|
26 |
</div>
|
27 |
<?php endif ?>
|
28 |
+
<?php if ( Config::cartActive() ) : ?>
|
29 |
+
<div <?php if ( $step >= 5 ) : ?>class="active"<?php endif ?> data-step="bookly-step-5" <?php if ( ! get_option( 'bookly_cart_enabled' ) ) : ?>style="display: none;"<?php endif ?>>
|
30 |
+
<span class="bookly-js-step-number"><?php echo get_option( 'bookly_cart_enabled' ) ? $i ++ : $i ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_cart' ) ) ?>
|
31 |
+
<div class="step"></div>
|
32 |
+
</div>
|
33 |
+
<?php endif ?>
|
34 |
<div <?php if ( $step >= 6 ) : ?>class="active"<?php endif ?>>
|
35 |
+
<span class="bookly-js-step-number"><?php echo $i ++ ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_details' ) ) ?>
|
36 |
<div class="step"></div>
|
37 |
</div>
|
38 |
<div <?php if ( $step >= 7 ) : ?>class="active"<?php endif ?>>
|
39 |
+
<span class="bookly-js-step-number"><?php echo $i ++ ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_payment' ) ) ?>
|
40 |
<div class="step"></div>
|
41 |
</div>
|
42 |
<div <?php if ( $step >= 8 ) : ?>class="active"<?php endif ?>>
|
43 |
+
<span class="bookly-js-step-number"><?php echo $i ++ ?></span>. <?php Editable::renderString( array( 'bookly_l10n_step_done' ) ) ?>
|
44 |
<div class="step"></div>
|
45 |
</div>
|
46 |
</div>
|
backend/modules/appearance/templates/index.php
CHANGED
@@ -1,11 +1,20 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
*/
|
7 |
?>
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
<?php if ( trim( $custom_css ) ) : ?>
|
10 |
<style type="text/css">
|
11 |
<?php echo $custom_css ?>
|
@@ -18,7 +27,7 @@
|
|
18 |
<div class="bookly-page-title">
|
19 |
<?php _e( 'Appearance', 'bookly' ) ?>
|
20 |
</div>
|
21 |
-
<?php
|
22 |
</div>
|
23 |
<div class="panel panel-default bookly-main">
|
24 |
<div class="panel-body">
|
@@ -29,7 +38,7 @@
|
|
29 |
value="<?php form_option( 'bookly_app_color' ) ?>"
|
30 |
data-selected="<?php form_option( 'bookly_app_color' ) ?>" />
|
31 |
</div>
|
32 |
-
<div class="col-sm-
|
33 |
<div class="checkbox">
|
34 |
<label>
|
35 |
<input type="checkbox" id=bookly-show-progress-tracker <?php checked( get_option( 'bookly_app_show_progress_tracker' ) ) ?>>
|
@@ -37,30 +46,33 @@
|
|
37 |
</label>
|
38 |
</div>
|
39 |
</div>
|
|
|
|
|
|
|
40 |
</div>
|
41 |
|
42 |
-
<ul class="bookly-nav bookly-nav-tabs bookly-margin-top-lg" role="tablist">
|
43 |
<?php $i = 1 ?>
|
44 |
-
<?php foreach ( $steps as $step => $
|
45 |
-
<?php if (
|
46 |
-
|
47 |
-
|
48 |
-
<?php echo $i++ ?>. <?php echo esc_html( $step_name ) ?>
|
49 |
-
</li>
|
50 |
-
<?php endif ?>
|
51 |
<?php endforeach ?>
|
52 |
</ul>
|
53 |
|
54 |
-
<?php if ( ! get_user_meta( get_current_user_id(),
|
55 |
<div class="alert alert-info alert-dismissible fade in bookly-margin-top-lg bookly-margin-bottom-remove" id="bookly-js-hint-alert" role="alert">
|
56 |
-
<button type="button" class="close" data-dismiss="alert"
|
57 |
<?php _e( 'Click on the underlined text to edit.', 'bookly' ) ?>
|
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
|
|
|
|
|
|
|
64 |
<div class="col-md-3">
|
65 |
<div class="checkbox">
|
66 |
<label>
|
@@ -85,9 +97,10 @@
|
|
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-
|
91 |
<div class="checkbox">
|
92 |
<label>
|
93 |
<input type="checkbox" id="bookly-show-calendar" <?php checked( get_option( 'bookly_app_show_calendar' ) ) ?>>
|
@@ -95,7 +108,7 @@
|
|
95 |
</label>
|
96 |
</div>
|
97 |
</div>
|
98 |
-
<div class="col-md-
|
99 |
<div class="checkbox">
|
100 |
<label>
|
101 |
<input type="checkbox" id="bookly-show-blocked-timeslots" <?php checked( get_option( 'bookly_app_show_blocked_timeslots' ) ) ?>>
|
@@ -103,7 +116,7 @@
|
|
103 |
</label>
|
104 |
</div>
|
105 |
</div>
|
106 |
-
<div class="col-md-
|
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' ) ) ?>>
|
@@ -111,15 +124,20 @@
|
|
111 |
</label>
|
112 |
</div>
|
113 |
</div>
|
|
|
|
|
114 |
</div>
|
115 |
-
|
|
|
|
|
|
|
|
|
116 |
<div class="col-md-3">
|
117 |
-
<
|
118 |
-
<
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
</div>
|
123 |
</div>
|
124 |
<div class="col-md-3">
|
125 |
<div class="checkbox">
|
@@ -145,27 +163,19 @@
|
|
145 |
</label>
|
146 |
</div>
|
147 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
</div>
|
149 |
<div class="bookly-js-payment-settings bookly-margin-top-lg" style="display:none">
|
150 |
-
|
151 |
-
|
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">
|
@@ -197,12 +207,13 @@
|
|
197 |
<?php // Render unique data per step
|
198 |
switch ( $step ) :
|
199 |
case 1: include '_1_service.php'; break;
|
200 |
-
case 2:
|
201 |
break;
|
202 |
case 3: include '_3_time.php'; break;
|
203 |
-
case 4:
|
|
|
|
|
204 |
break;
|
205 |
-
case 5: include '_5_cart.php'; break;
|
206 |
case 6: include '_6_details.php'; break;
|
207 |
case 7: include '_7_payment.php'; break;
|
208 |
case 8: include '_8_complete.php'; break;
|
@@ -213,13 +224,13 @@
|
|
213 |
</div>
|
214 |
</div>
|
215 |
<div>
|
216 |
-
<?php $
|
217 |
</div>
|
218 |
</div>
|
219 |
</div>
|
220 |
<div class="panel-footer">
|
221 |
-
<?php
|
222 |
-
<?php
|
223 |
</div>
|
224 |
</div>
|
225 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib;
|
3 |
+
use Bookly\Backend\Components\Support;
|
4 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
5 |
+
use Bookly\Backend\Modules\Appearance\Proxy;
|
|
|
6 |
?>
|
7 |
+
<style type="text/css">
|
8 |
+
.bookly-columnizer .bookly-hour.bookly-slot-in-waiting-list span.bookly-time-additional {
|
9 |
+
color: #f4662f!important;
|
10 |
+
}
|
11 |
+
#bookly-step-settings > div > .col-md-3:nth-child(4n+1) {
|
12 |
+
clear: both;
|
13 |
+
}
|
14 |
+
#bookly-tbs .bookly-cart .bookly-extras-cart-title {
|
15 |
+
padding-left: 25px;
|
16 |
+
}
|
17 |
+
</style>
|
18 |
<?php if ( trim( $custom_css ) ) : ?>
|
19 |
<style type="text/css">
|
20 |
<?php echo $custom_css ?>
|
27 |
<div class="bookly-page-title">
|
28 |
<?php _e( 'Appearance', 'bookly' ) ?>
|
29 |
</div>
|
30 |
+
<?php Support\Buttons::render( $self::pageSlug() ) ?>
|
31 |
</div>
|
32 |
<div class="panel panel-default bookly-main">
|
33 |
<div class="panel-body">
|
38 |
value="<?php form_option( 'bookly_app_color' ) ?>"
|
39 |
data-selected="<?php form_option( 'bookly_app_color' ) ?>" />
|
40 |
</div>
|
41 |
+
<div class="col-sm-3 col-lg-2">
|
42 |
<div class="checkbox">
|
43 |
<label>
|
44 |
<input type="checkbox" id=bookly-show-progress-tracker <?php checked( get_option( 'bookly_app_show_progress_tracker' ) ) ?>>
|
46 |
</label>
|
47 |
</div>
|
48 |
</div>
|
49 |
+
<?php Proxy\ServiceExtras::renderShowStep() ?>
|
50 |
+
<?php Proxy\RecurringAppointments::renderShowStep() ?>
|
51 |
+
<?php Proxy\Cart::renderShowStep() ?>
|
52 |
</div>
|
53 |
|
54 |
+
<ul class="bookly-nav bookly-nav-tabs bookly-margin-top-lg bookly-js-appearance-steps" role="tablist">
|
55 |
<?php $i = 1 ?>
|
56 |
+
<?php foreach ( $steps as $step => $step_data ) : ?>
|
57 |
+
<li class="bookly-nav-item <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="#bookly-step-<?php echo $step ?>" data-toggle="tab" <?php if ( ! $step_data['show'] ) : ?>style="display: none;"<?php endif ?>>
|
58 |
+
<span class="bookly-js-step-number"><?php echo $step_data['show'] ? $i++ : $i ?></span>. <?php echo esc_html( $step_data['title'] ) ?>
|
59 |
+
</li>
|
|
|
|
|
|
|
60 |
<?php endforeach ?>
|
61 |
</ul>
|
62 |
|
63 |
+
<?php if ( ! get_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_appearance_notice', true ) ): ?>
|
64 |
<div class="alert alert-info alert-dismissible fade in bookly-margin-top-lg bookly-margin-bottom-remove" id="bookly-js-hint-alert" role="alert">
|
65 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
66 |
<?php _e( 'Click on the underlined text to edit.', 'bookly' ) ?>
|
67 |
</div>
|
68 |
<?php endif ?>
|
69 |
|
70 |
<div class="row" id="bookly-step-settings">
|
71 |
<div class="bookly-js-service-settings bookly-margin-top-lg">
|
72 |
+
<?php Proxy\Locations::renderShowLocation() ?>
|
73 |
+
<?php Proxy\CustomDuration::renderShowCustomDuration() ?>
|
74 |
+
<?php Proxy\GroupBooking::renderShowNOP() ?>
|
75 |
+
<?php Proxy\MultiplyAppointments::renderShowQuantity() ?>
|
76 |
<div class="col-md-3">
|
77 |
<div class="checkbox">
|
78 |
<label>
|
97 |
</label>
|
98 |
</div>
|
99 |
</div>
|
100 |
+
<?php Proxy\Shared::renderServiceStepSettings() ?>
|
101 |
</div>
|
102 |
<div class="bookly-js-time-settings bookly-margin-top-lg" style="display:none">
|
103 |
+
<div class="col-md-3">
|
104 |
<div class="checkbox">
|
105 |
<label>
|
106 |
<input type="checkbox" id="bookly-show-calendar" <?php checked( get_option( 'bookly_app_show_calendar' ) ) ?>>
|
108 |
</label>
|
109 |
</div>
|
110 |
</div>
|
111 |
+
<div class="col-md-3">
|
112 |
<div class="checkbox">
|
113 |
<label>
|
114 |
<input type="checkbox" id="bookly-show-blocked-timeslots" <?php checked( get_option( 'bookly_app_show_blocked_timeslots' ) ) ?>>
|
116 |
</label>
|
117 |
</div>
|
118 |
</div>
|
119 |
+
<div class="col-md-3">
|
120 |
<div class="checkbox">
|
121 |
<label>
|
122 |
<input type="checkbox" id="bookly-show-day-one-column" <?php checked( get_option( 'bookly_app_show_day_one_column' ) ) ?>>
|
124 |
</label>
|
125 |
</div>
|
126 |
</div>
|
127 |
+
<?php Proxy\Pro::renderTimeZoneSwitcherCheckbox() ?>
|
128 |
+
<?php Proxy\Shared::renderTimeStepSettings() ?>
|
129 |
</div>
|
130 |
+
|
131 |
+
<?php Proxy\Cart::renderCartStepSettings() ?>
|
132 |
+
|
133 |
+
<div class="bookly-js-details-settings bookly-margin-top-lg container-fluid" style="display:none">
|
134 |
+
|
135 |
<div class="col-md-3">
|
136 |
+
<select id="bookly-cst-required-details" class="form-control" data-default="<?php echo ! array_diff( array( 'phone', 'email' ), get_option( 'bookly_cst_required_details', array() ) ) ? 'both' : current( get_option( 'bookly_cst_required_details', array() ) ) ?>">
|
137 |
+
<option value="phone"<?php selected( in_array( 'phone', get_option( 'bookly_cst_required_details', array() ) ) && ! in_array( 'email', get_option( 'bookly_cst_required_details', array() ) ) ) ?><?php disabled( get_option( 'bookly_cst_create_account' ) ) ?>><?php _e( 'Phone field required', 'bookly' ) ?></option>
|
138 |
+
<option value="email"<?php selected( in_array( 'email', get_option( 'bookly_cst_required_details', array() ) ) && ! in_array( 'phone', get_option( 'bookly_cst_required_details', array() ) ) ) ?>><?php _e( 'Email field required', 'bookly' ) ?></option>
|
139 |
+
<option value="both"<?php selected( ! array_diff( array( 'phone', 'email' ), get_option( 'bookly_cst_required_details', array() ) ) ) ?>><?php _e( 'Both email and phone fields required', 'bookly' ) ?></option>
|
140 |
+
</select>
|
|
|
141 |
</div>
|
142 |
<div class="col-md-3">
|
143 |
<div class="checkbox">
|
163 |
</label>
|
164 |
</div>
|
165 |
</div>
|
166 |
+
<?php Proxy\Pro::renderShowBirthday() ?>
|
167 |
+
<?php Proxy\Pro::renderShowAddress() ?>
|
168 |
+
<?php Proxy\GoogleMapsAddress::renderShowGoogleMaps() ?>
|
169 |
+
<?php Proxy\CustomFields::renderShowCustomFields() ?>
|
170 |
+
<?php Proxy\Files::renderShowFiles() ?>
|
171 |
+
<?php Proxy\CustomerInformation::renderShowCustomerInformation() ?>
|
172 |
+
<?php Proxy\Pro::renderShowFacebookButton() ?>
|
173 |
</div>
|
174 |
<div class="bookly-js-payment-settings bookly-margin-top-lg" style="display:none">
|
175 |
+
<?php Proxy\Coupons::renderShowCoupons() ?>
|
176 |
+
<?php Proxy\Pro::renderMultipleBookingSelector() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
</div>
|
178 |
+
|
179 |
<div class="bookly-js-done-settings bookly-margin-top-lg" style="display:none">
|
180 |
<div class="col-md-12">
|
181 |
<div class="alert alert-info bookly-margin-top-lg bookly-margin-bottom-remove bookly-flexbox">
|
207 |
<?php // Render unique data per step
|
208 |
switch ( $step ) :
|
209 |
case 1: include '_1_service.php'; break;
|
210 |
+
case 2: Proxy\ServiceExtras::renderStep( $self::renderTemplate( '_progress_tracker', compact( 'step', 'editable' ), false ) );
|
211 |
break;
|
212 |
case 3: include '_3_time.php'; break;
|
213 |
+
case 4: Proxy\RecurringAppointments::renderStep( $self::renderTemplate( '_progress_tracker', compact( 'step', 'editable' ), false ) );
|
214 |
+
break;
|
215 |
+
case 5: Proxy\Cart::renderStep( $self::renderTemplate( '_progress_tracker', compact( 'step', 'editable' ), false ) );
|
216 |
break;
|
|
|
217 |
case 6: include '_6_details.php'; break;
|
218 |
case 7: include '_7_payment.php'; break;
|
219 |
case 8: include '_8_complete.php'; break;
|
224 |
</div>
|
225 |
</div>
|
226 |
<div>
|
227 |
+
<?php $self::renderTemplate( '_custom_css', array( 'custom_css' => $custom_css ) ) ?>
|
228 |
</div>
|
229 |
</div>
|
230 |
</div>
|
231 |
<div class="panel-footer">
|
232 |
+
<?php Buttons::renderSubmit( 'ajax-send-appearance' ) ?>
|
233 |
+
<?php Buttons::renderReset() ?>
|
234 |
</div>
|
235 |
</div>
|
236 |
</div>
|
backend/modules/appointments/Ajax.php
ADDED
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Ajax
|
9 |
+
* @package Bookly\Backend\Modules\Appointments
|
10 |
+
*/
|
11 |
+
class Ajax extends Lib\Base\Ajax
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Get list of appointments.
|
15 |
+
*/
|
16 |
+
public static function getAppointments()
|
17 |
+
{
|
18 |
+
$columns = self::parameter( 'columns' );
|
19 |
+
$order = self::parameter( 'order' );
|
20 |
+
$filter = self::parameter( 'filter' );
|
21 |
+
$postfix_any = sprintf( ' (%s)', get_option( 'bookly_l10n_option_employee' ) );
|
22 |
+
|
23 |
+
$query = Lib\Entities\CustomerAppointment::query( 'ca' )
|
24 |
+
->select( 'a.id,
|
25 |
+
ca.payment_id,
|
26 |
+
ca.status,
|
27 |
+
ca.id AS ca_id,
|
28 |
+
ca.notes,
|
29 |
+
ca.number_of_persons,
|
30 |
+
ca.extras,
|
31 |
+
ca.rating,
|
32 |
+
ca.rating_comment,
|
33 |
+
a.start_date,
|
34 |
+
a.staff_any,
|
35 |
+
c.full_name AS customer_full_name,
|
36 |
+
c.phone AS customer_phone,
|
37 |
+
c.email AS customer_email,
|
38 |
+
st.full_name AS staff_name,
|
39 |
+
p.paid AS payment,
|
40 |
+
p.total AS payment_total,
|
41 |
+
p.type AS payment_type,
|
42 |
+
p.status AS payment_status,
|
43 |
+
COALESCE(s.title, a.custom_service_name) AS service_title,
|
44 |
+
TIME_TO_SEC(TIMEDIFF(a.end_date, a.start_date)) + a.extras_duration AS service_duration' )
|
45 |
+
->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
|
46 |
+
->leftJoin( 'Service', 's', 's.id = a.service_id' )
|
47 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
48 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
49 |
+
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
50 |
+
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id AND ss.location_id = a.location_id' );
|
51 |
+
|
52 |
+
$total = $query->count();
|
53 |
+
|
54 |
+
$sub_query = Lib\Proxy\Files::getSubQueryAttachmentExists();
|
55 |
+
if ( ! $sub_query ) {
|
56 |
+
$sub_query = '0';
|
57 |
+
}
|
58 |
+
$query->addSelect( '(' . $sub_query . ') AS attachment' );
|
59 |
+
|
60 |
+
if ( $filter['id'] != '' ) {
|
61 |
+
$query->where( 'a.id', $filter['id'] );
|
62 |
+
}
|
63 |
+
|
64 |
+
if ( $filter['date'] ) {
|
65 |
+
list ( $start, $end ) = explode( ' - ', $filter['date'], 2 );
|
66 |
+
$end = date( 'Y-m-d', strtotime( $end ) + DAY_IN_SECONDS );
|
67 |
+
$query->whereBetween( 'a.start_date', $start, $end );
|
68 |
+
} else {
|
69 |
+
$query->where( 'a.start_date', null );
|
70 |
+
}
|
71 |
+
|
72 |
+
if ( $filter['staff'] != '' ) {
|
73 |
+
$query->where( 'a.staff_id', $filter['staff'] );
|
74 |
+
}
|
75 |
+
|
76 |
+
if ( $filter['customer'] != '' ) {
|
77 |
+
$query->where( 'ca.customer_id', $filter['customer'] );
|
78 |
+
}
|
79 |
+
|
80 |
+
if ( $filter['service'] != '' ) {
|
81 |
+
$query->where( 'a.service_id', $filter['service'] ?: null );
|
82 |
+
}
|
83 |
+
|
84 |
+
if ( $filter['status'] != '' ) {
|
85 |
+
$query->where( 'ca.status', $filter['status'] );
|
86 |
+
}
|
87 |
+
|
88 |
+
foreach ( $order as $sort_by ) {
|
89 |
+
$query->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
90 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
91 |
+
}
|
92 |
+
|
93 |
+
$custom_fields = array();
|
94 |
+
$fields_data = (array) Lib\Proxy\CustomFields::getWhichHaveData();
|
95 |
+
foreach ( $fields_data as $field_data ) {
|
96 |
+
$custom_fields[ $field_data->id ] = '';
|
97 |
+
}
|
98 |
+
|
99 |
+
$data = array();
|
100 |
+
foreach ( $query->fetchArray() as $row ) {
|
101 |
+
// Service duration.
|
102 |
+
$service_duration = Lib\Utils\DateTime::secondsToInterval( $row['service_duration'] );
|
103 |
+
// Appointment status.
|
104 |
+
$row['status'] = Lib\Entities\CustomerAppointment::statusToString( $row['status'] );
|
105 |
+
// Payment title.
|
106 |
+
$payment_title = '';
|
107 |
+
if ( $row['payment'] !== null ) {
|
108 |
+
$payment_title = Lib\Utils\Price::format( $row['payment'] );
|
109 |
+
if ( $row['payment'] != $row['payment_total'] ) {
|
110 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Price::format( $row['payment_total'] ) );
|
111 |
+
}
|
112 |
+
$payment_title .= sprintf(
|
113 |
+
' %s <span%s>%s</span>',
|
114 |
+
Lib\Entities\Payment::typeToString( $row['payment_type'] ),
|
115 |
+
$row['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
116 |
+
Lib\Entities\Payment::statusToString( $row['payment_status'] )
|
117 |
+
);
|
118 |
+
}
|
119 |
+
// Custom fields
|
120 |
+
$customer_appointment = new Lib\Entities\CustomerAppointment();
|
121 |
+
$customer_appointment->load( $row['ca_id'] );
|
122 |
+
foreach ( (array) Lib\Proxy\CustomFields::getForCustomerAppointment( $customer_appointment ) as $custom_field ) {
|
123 |
+
$custom_fields[ $custom_field['id'] ] = $custom_field['value'];
|
124 |
+
}
|
125 |
+
|
126 |
+
$data[] = array(
|
127 |
+
'id' => $row['id'],
|
128 |
+
'start_date' => $row['start_date'] === null ? __( 'N/A', 'bookly' ) : Lib\Utils\DateTime::formatDateTime( $row['start_date'] ),
|
129 |
+
'staff' => array(
|
130 |
+
'name' => $row['staff_name'] . ( $row['staff_any'] ? $postfix_any : '' ),
|
131 |
+
),
|
132 |
+
'customer' => array(
|
133 |
+
'full_name' => $row['customer_full_name'],
|
134 |
+
'phone' => $row['customer_phone'],
|
135 |
+
'email' => $row['customer_email'],
|
136 |
+
),
|
137 |
+
'service' => array(
|
138 |
+
'title' => $row['service_title'],
|
139 |
+
'duration' => $service_duration,
|
140 |
+
'extras' => (array) Lib\Proxy\ServiceExtras::getInfo( json_decode( $row['extras'], true ), false ),
|
141 |
+
),
|
142 |
+
'status' => $row['status'],
|
143 |
+
'payment' => $payment_title,
|
144 |
+
'notes' => $row['notes'],
|
145 |
+
'number_of_persons' => $row['number_of_persons'],
|
146 |
+
'rating' => $row['rating'],
|
147 |
+
'rating_comment' => $row['rating_comment'],
|
148 |
+
'custom_fields' => $custom_fields,
|
149 |
+
'ca_id' => $row['ca_id'],
|
150 |
+
'attachment' => $row['attachment'],
|
151 |
+
'payment_id' => $row['payment_id'],
|
152 |
+
);
|
153 |
+
|
154 |
+
$custom_fields = array_map( function () { return ''; }, $custom_fields );
|
155 |
+
}
|
156 |
+
|
157 |
+
unset( $filter['date'] );
|
158 |
+
update_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', $filter );
|
159 |
+
|
160 |
+
wp_send_json( array(
|
161 |
+
'draw' => (int) self::parameter( 'draw' ),
|
162 |
+
'recordsTotal' => $total,
|
163 |
+
'recordsFiltered' => count( $data ),
|
164 |
+
'data' => $data,
|
165 |
+
) );
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Delete customer appointments.
|
170 |
+
*/
|
171 |
+
public static function deleteCustomerAppointments()
|
172 |
+
{
|
173 |
+
/** @var Lib\Entities\CustomerAppointment $ca */
|
174 |
+
foreach ( Lib\Entities\CustomerAppointment::query()->whereIn( 'id', self::parameter( 'data', array() ) )->find() as $ca ) {
|
175 |
+
if ( self::parameter( 'notify' ) ) {
|
176 |
+
switch ( $ca->getStatus() ) {
|
177 |
+
case Lib\Entities\CustomerAppointment::STATUS_PENDING:
|
178 |
+
case Lib\Entities\CustomerAppointment::STATUS_WAITLISTED:
|
179 |
+
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_REJECTED );
|
180 |
+
break;
|
181 |
+
case Lib\Entities\CustomerAppointment::STATUS_APPROVED:
|
182 |
+
$ca->setStatus( Lib\Entities\CustomerAppointment::STATUS_CANCELLED );
|
183 |
+
break;
|
184 |
+
}
|
185 |
+
Lib\Notifications\Sender::sendSingle(
|
186 |
+
DataHolders\Simple::create( $ca ),
|
187 |
+
null,
|
188 |
+
array( 'cancellation_reason' => self::parameter( 'reason' ) )
|
189 |
+
);
|
190 |
+
}
|
191 |
+
$ca->deleteCascade();
|
192 |
+
}
|
193 |
+
wp_send_json_success();
|
194 |
+
}
|
195 |
+
}
|
backend/modules/appointments/Controller.php
DELETED
@@ -1,253 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Appointments;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
use BooklyLite\Lib\DataHolders\Booking as DataHolders;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Controller
|
9 |
-
* @package BooklyLite\Backend\Modules\Appointments
|
10 |
-
*/
|
11 |
-
class Controller extends Lib\Base\Controller
|
12 |
-
{
|
13 |
-
const page_slug = 'bookly-appointments';
|
14 |
-
|
15 |
-
public function index()
|
16 |
-
{
|
17 |
-
/** @var \WP_Locale $wp_locale */
|
18 |
-
global $wp_locale;
|
19 |
-
|
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 |
-
),
|
27 |
-
) );
|
28 |
-
|
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' ),
|
39 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
40 |
-
),
|
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' ),
|
52 |
-
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
53 |
-
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
54 |
-
'this_month' => __( 'This Month', 'bookly' ),
|
55 |
-
'next_month' => __( 'Next Month', 'bookly' ),
|
56 |
-
'custom_range' => __( 'Custom Range', 'bookly' ),
|
57 |
-
'apply' => __( 'Apply', 'bookly' ),
|
58 |
-
'cancel' => __( 'Cancel', 'bookly' ),
|
59 |
-
'to' => __( 'To', 'bookly' ),
|
60 |
-
'from' => __( 'From', 'bookly' ),
|
61 |
-
'calendar' => array(
|
62 |
-
'longMonths' => array_values( $wp_locale->month ),
|
63 |
-
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
64 |
-
'longDays' => array_values( $wp_locale->weekday ),
|
65 |
-
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
66 |
-
),
|
67 |
-
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
68 |
-
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
69 |
-
'are_you_sure' => __( 'Are you sure?', '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' ) );
|
86 |
-
}
|
87 |
-
|
88 |
-
/**
|
89 |
-
* Get list of appointments.
|
90 |
-
*/
|
91 |
-
public function executeGetAppointments()
|
92 |
-
{
|
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' )
|
120 |
-
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
121 |
-
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
122 |
-
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id' );
|
123 |
-
|
124 |
-
$total = $query->count();
|
125 |
-
|
126 |
-
if ( $filter['id'] != '' ) {
|
127 |
-
$query->where( 'a.id', $filter['id'] );
|
128 |
-
}
|
129 |
-
|
130 |
-
list ( $start, $end ) = explode( ' - ', $filter['date'], 2 );
|
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 |
-
|
150 |
-
foreach ( $order as $sort_by ) {
|
151 |
-
$query->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
152 |
-
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
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 |
-
}
|
160 |
-
|
161 |
-
$data = array();
|
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>',
|
176 |
-
Lib\Entities\Payment::typeToString( $row['payment_type'] ),
|
177 |
-
$row['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
178 |
-
Lib\Entities\Payment::statusToString( $row['payment_status'] )
|
179 |
-
);
|
180 |
-
}
|
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 |
-
|
188 |
-
$data[] = array(
|
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'],
|
210 |
-
);
|
211 |
-
|
212 |
-
$custom_fields = array_map( function () { return ''; }, $custom_fields );
|
213 |
-
}
|
214 |
-
|
215 |
-
unset( $filter['date'] );
|
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,
|
223 |
-
) );
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* Delete customer appointments.
|
228 |
-
*/
|
229 |
-
public function executeDeleteCustomerAppointments()
|
230 |
-
{
|
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/Page.php
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Appointments
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
self::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 |
+
),
|
27 |
+
) );
|
28 |
+
|
29 |
+
self::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' ),
|
39 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
40 |
+
),
|
41 |
+
'module' => array( 'js/appointments.js' => array( 'bookly-datatables.min.js' ), ),
|
42 |
+
) );
|
43 |
+
|
44 |
+
// Custom fields without captcha, text content field & file.
|
45 |
+
$custom_fields = $cf_columns = array();
|
46 |
+
foreach ( (array) Lib\Proxy\CustomFields::getWhichHaveData() as $cf ) {
|
47 |
+
if ( $cf->type != 'file' ) {
|
48 |
+
$cf_columns[] = $cf->id;
|
49 |
+
$custom_fields[] = $cf;
|
50 |
+
}
|
51 |
+
}
|
52 |
+
// Show column attachments.
|
53 |
+
$show_attachments = Lib\Config::filesActive() && count( Lib\Proxy\Files::getAllIds() ) > 0;
|
54 |
+
wp_localize_script( 'bookly-appointments.js', 'BooklyL10n', array(
|
55 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
56 |
+
'tomorrow' => __( 'Tomorrow', 'bookly' ),
|
57 |
+
'today' => __( 'Today', 'bookly' ),
|
58 |
+
'yesterday' => __( 'Yesterday', 'bookly' ),
|
59 |
+
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
60 |
+
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
61 |
+
'this_month' => __( 'This Month', 'bookly' ),
|
62 |
+
'next_month' => __( 'Next Month', 'bookly' ),
|
63 |
+
'custom_range' => __( 'Custom Range', 'bookly' ),
|
64 |
+
'apply' => __( 'Apply', 'bookly' ),
|
65 |
+
'cancel' => __( 'Cancel', 'bookly' ),
|
66 |
+
'to' => __( 'To', 'bookly' ),
|
67 |
+
'from' => __( 'From', 'bookly' ),
|
68 |
+
'calendar' => array(
|
69 |
+
'longMonths' => array_values( $wp_locale->month ),
|
70 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
71 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
72 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
73 |
+
),
|
74 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
75 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
76 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
77 |
+
'zeroRecords' => __( 'No appointments for selected period.', 'bookly' ),
|
78 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
79 |
+
'edit' => __( 'Edit', 'bookly' ),
|
80 |
+
'add_columns' => array( 'ratings' => Lib\Config::ratingsActive(), 'number_of_persons' => Lib\Config::groupBookingActive(), 'notes' => Lib\Config::showNotes(), 'attachments' => $show_attachments, ),
|
81 |
+
'cf_columns' => $cf_columns,
|
82 |
+
'filter' => (array) get_user_meta( get_current_user_id(), 'bookly_filter_appointments_list', true ),
|
83 |
+
'no_result_found' => __( 'No result found', 'bookly' ),
|
84 |
+
'attachments' => __( 'Attachments', 'bookly' ),
|
85 |
+
'tasks' => array(
|
86 |
+
'enabled' => Lib\Config::tasksActive(),
|
87 |
+
'title' => Proxy\Tasks::getFilterText(),
|
88 |
+
),
|
89 |
+
) );
|
90 |
+
|
91 |
+
// Filters data
|
92 |
+
$staff_members = Lib\Entities\Staff::query( 's' )->select( 's.id, s.full_name' )->fetchArray();
|
93 |
+
$customers = Lib\Entities\Customer::query( 'c' )->select( 'c.id, c.full_name, c.first_name, c.last_name' )->fetchArray();
|
94 |
+
$services = Lib\Entities\Service::query( 's' )->select( 's.id, s.title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray();
|
95 |
+
|
96 |
+
self::renderTemplate( 'index', compact( 'custom_fields', 'staff_members', 'customers', 'services', 'show_attachments' ) );
|
97 |
+
}
|
98 |
+
}
|
backend/modules/appointments/proxy/Pro.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Appointments\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderExportButton() Render export button.
|
11 |
+
* @method static void renderExportDialog( array $custom_fields ) Render export dialog.
|
12 |
+
* @method static void renderPrintButton() Render print button.
|
13 |
+
* @method static void renderPrintDialog( array $custom_fields ) Render print dialog.
|
14 |
+
*/
|
15 |
+
abstract class Pro extends Lib\Base\Proxy
|
16 |
+
{
|
17 |
+
|
18 |
+
}
|
backend/modules/appointments/proxy/Ratings.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ratings
|
8 |
+
* @package Bookly\Backend\Modules\Appointments\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderExport( int $column ) Render column title for Appointments table
|
11 |
+
* @method static void renderTableHeader() Render 'Ratings' in appointments export popup.
|
12 |
+
*/
|
13 |
+
abstract class Ratings extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/appointments/proxy/Shared.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Appointments\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAddOnsComponents() Render components on appointments list page.
|
11 |
+
*/
|
12 |
+
abstract class Shared extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/appointments/proxy/Tasks.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Appointments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Tasks
|
8 |
+
* @package Bookly\Backend\Modules\Appointments\Proxy
|
9 |
+
*
|
10 |
+
* @method static string getFilterText() get 'Tasks' in appointments date time filter.
|
11 |
+
*/
|
12 |
+
abstract class Tasks extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/appointments/resources/js/appointments.js
CHANGED
@@ -10,10 +10,13 @@ jQuery(function($) {
|
|
10 |
$service_filter = $('#bookly-filter-service'),
|
11 |
$status_filter = $('#bookly-filter-status'),
|
12 |
$add_button = $('#bookly-add'),
|
|
|
|
|
13 |
$export_dialog = $('#bookly-export-dialog'),
|
14 |
$export_button = $('#bookly-export'),
|
15 |
$delete_button = $('#bookly-delete'),
|
16 |
-
isMobile = false
|
|
|
17 |
;
|
18 |
|
19 |
try {
|
@@ -24,17 +27,36 @@ jQuery(function($) {
|
|
24 |
}
|
25 |
|
26 |
$('.bookly-js-select').val(null);
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
}
|
36 |
-
}
|
37 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
/**
|
40 |
* Init DataTables.
|
@@ -49,13 +71,22 @@ jQuery(function($) {
|
|
49 |
responsivePriority: 3,
|
50 |
render: function (data, type, row, meta) {
|
51 |
if (isMobile) {
|
52 |
-
return '<a href="tel:' + data + '">' + $.fn.dataTable.render.text().display(data) + '</a>';
|
53 |
} else {
|
54 |
return $.fn.dataTable.render.text().display(data);
|
55 |
}
|
56 |
}
|
57 |
},
|
58 |
-
{ data: 'customer.email', render: $.fn.dataTable.render.text(), responsivePriority:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
{
|
60 |
data: 'service.title',
|
61 |
responsivePriority: 2,
|
@@ -82,8 +113,23 @@ jQuery(function($) {
|
|
82 |
return '<a href="#bookly-payment-details-modal" data-toggle="modal" data-payment_id="' + row.payment_id + '">' + data + '</a>';
|
83 |
}
|
84 |
}
|
85 |
-
];
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
columns.push({
|
88 |
data: 'notes',
|
89 |
render: $.fn.dataTable.render.text(),
|
@@ -98,6 +144,19 @@ jQuery(function($) {
|
|
98 |
orderable: false
|
99 |
});
|
100 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
var dt = $appointments_list.DataTable({
|
102 |
order: [[ 1, 'desc' ]],
|
103 |
info: false,
|
@@ -106,6 +165,11 @@ jQuery(function($) {
|
|
106 |
processing: true,
|
107 |
responsive: true,
|
108 |
serverSide: true,
|
|
|
|
|
|
|
|
|
|
|
109 |
ajax: {
|
110 |
url : ajaxurl,
|
111 |
type: 'POST',
|
@@ -127,7 +191,7 @@ jQuery(function($) {
|
|
127 |
responsivePriority: 1,
|
128 |
orderable: false,
|
129 |
render: function ( data, type, row, meta ) {
|
130 |
-
return '<button type="button" class="btn btn-default"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</a>';
|
131 |
}
|
132 |
},
|
133 |
{
|
@@ -161,18 +225,19 @@ jQuery(function($) {
|
|
161 |
/**
|
162 |
* Edit appointment.
|
163 |
*/
|
164 |
-
$appointments_list
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
|
|
176 |
|
177 |
/**
|
178 |
* Export.
|
@@ -193,8 +258,26 @@ jQuery(function($) {
|
|
193 |
$.fn.dataTable.ext.buttons.csvHtml5.action(null, dt, null, $.extend({}, $.fn.dataTable.ext.buttons.csvHtml5, config));
|
194 |
});
|
195 |
|
196 |
-
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
});
|
199 |
|
200 |
/**
|
@@ -266,6 +349,9 @@ jQuery(function($) {
|
|
266 |
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
267 |
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
268 |
picker_ranges[BooklyL10n.next_month] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
|
|
|
|
|
|
|
269 |
|
270 |
$date_filter.daterangepicker(
|
271 |
{
|
@@ -273,6 +359,7 @@ jQuery(function($) {
|
|
273 |
startDate: moment().startOf('month'),
|
274 |
endDate: moment().endOf('month'),
|
275 |
ranges: picker_ranges,
|
|
|
276 |
locale: {
|
277 |
applyLabel : BooklyL10n.apply,
|
278 |
cancelLabel: BooklyL10n.cancel,
|
@@ -285,12 +372,19 @@ jQuery(function($) {
|
|
285 |
format : BooklyL10n.mjsDateFormat
|
286 |
}
|
287 |
},
|
288 |
-
function(start, end) {
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
}
|
295 |
);
|
296 |
|
10 |
$service_filter = $('#bookly-filter-service'),
|
11 |
$status_filter = $('#bookly-filter-status'),
|
12 |
$add_button = $('#bookly-add'),
|
13 |
+
$print_dialog = $('#bookly-print-dialog'),
|
14 |
+
$print_button = $('#bookly-print'),
|
15 |
$export_dialog = $('#bookly-export-dialog'),
|
16 |
$export_button = $('#bookly-export'),
|
17 |
$delete_button = $('#bookly-delete'),
|
18 |
+
isMobile = false,
|
19 |
+
urlParts = document.URL.split('#')
|
20 |
;
|
21 |
|
22 |
try {
|
27 |
}
|
28 |
|
29 |
$('.bookly-js-select').val(null);
|
30 |
+
|
31 |
+
// Apply filter from anchor
|
32 |
+
if (urlParts.length > 1) {
|
33 |
+
urlParts[1].split('&').forEach(function (part) {
|
34 |
+
var params = part.split('=');
|
35 |
+
if (params[0] == 'range') {
|
36 |
+
var format = 'YYYY-MM-DD',
|
37 |
+
start = moment(params['1'].substring(0, 10)),
|
38 |
+
end = moment(params['1'].substring(11));
|
39 |
+
$date_filter
|
40 |
+
.data('date', start.format(format) + ' - ' + end.format(format))
|
41 |
+
.find('span')
|
42 |
+
.html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
43 |
+
} else {
|
44 |
+
$('#bookly-filter-' + params[0]).val(params[1]);
|
45 |
}
|
46 |
+
});
|
47 |
+
} else {
|
48 |
+
$.each(BooklyL10n.filter, function (field, value) {
|
49 |
+
if (value != '') {
|
50 |
+
$('#bookly-filter-' + field).val(value);
|
51 |
+
}
|
52 |
+
// check if select has correct values
|
53 |
+
if ($('#bookly-filter-' + field).prop('type') == 'select-one') {
|
54 |
+
if ($('#bookly-filter-' + field + ' option[value="' + value + '"]').length == 0) {
|
55 |
+
$('#bookly-filter-' + field).val(null);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
});
|
59 |
+
}
|
60 |
|
61 |
/**
|
62 |
* Init DataTables.
|
71 |
responsivePriority: 3,
|
72 |
render: function (data, type, row, meta) {
|
73 |
if (isMobile) {
|
74 |
+
return '<a href="tel:' + $.fn.dataTable.render.text().display(data) + '">' + $.fn.dataTable.render.text().display(data) + '</a>';
|
75 |
} else {
|
76 |
return $.fn.dataTable.render.text().display(data);
|
77 |
}
|
78 |
}
|
79 |
},
|
80 |
+
{ data: 'customer.email', render: $.fn.dataTable.render.text(), responsivePriority: 5 }
|
81 |
+
];
|
82 |
+
if (BooklyL10n.add_columns.number_of_persons) {
|
83 |
+
columns.push({
|
84 |
+
data: 'number_of_persons',
|
85 |
+
render: $.fn.dataTable.render.text(),
|
86 |
+
responsivePriority: 4
|
87 |
+
});
|
88 |
+
}
|
89 |
+
columns = columns.concat([
|
90 |
{
|
91 |
data: 'service.title',
|
92 |
responsivePriority: 2,
|
113 |
return '<a href="#bookly-payment-details-modal" data-toggle="modal" data-payment_id="' + row.payment_id + '">' + data + '</a>';
|
114 |
}
|
115 |
}
|
116 |
+
]);
|
117 |
+
|
118 |
+
if (BooklyL10n.add_columns.ratings) {
|
119 |
+
columns.push({
|
120 |
+
data: 'rating',
|
121 |
+
render: function ( data, type, row, meta ) {
|
122 |
+
if (row.rating_comment == null) {
|
123 |
+
return row.rating;
|
124 |
+
} else {
|
125 |
+
return '<a href="#" data-toggle="popover" data-trigger="focus" data-placement="bottom" data-content="' + $.fn.dataTable.render.text().display(row.rating_comment) + '">' + $.fn.dataTable.render.text().display(row.rating) + '</a>';
|
126 |
+
}
|
127 |
+
},
|
128 |
+
responsivePriority: 2
|
129 |
+
});
|
130 |
+
}
|
131 |
+
|
132 |
+
if (BooklyL10n.add_columns.notes) {
|
133 |
columns.push({
|
134 |
data: 'notes',
|
135 |
render: $.fn.dataTable.render.text(),
|
144 |
orderable: false
|
145 |
});
|
146 |
});
|
147 |
+
if (BooklyL10n.add_columns.attachments) {
|
148 |
+
columns.push({
|
149 |
+
data: 'attachment',
|
150 |
+
render: function (data, type, row, meta) {
|
151 |
+
if (data == '1') {
|
152 |
+
return '<button type="button" class="btn btn-link bookly-js-attachment" title="' + BooklyL10n.attachments + '"><span class="dashicons dashicons-paperclip"></span></button>';
|
153 |
+
}
|
154 |
+
return '';
|
155 |
+
},
|
156 |
+
responsivePriority: 1
|
157 |
+
});
|
158 |
+
}
|
159 |
+
|
160 |
var dt = $appointments_list.DataTable({
|
161 |
order: [[ 1, 'desc' ]],
|
162 |
info: false,
|
165 |
processing: true,
|
166 |
responsive: true,
|
167 |
serverSide: true,
|
168 |
+
drawCallback: function( settings ) {
|
169 |
+
$('[data-toggle="popover"]').on('click', function (e) {
|
170 |
+
e.preventDefault();
|
171 |
+
}).popover();
|
172 |
+
},
|
173 |
ajax: {
|
174 |
url : ajaxurl,
|
175 |
type: 'POST',
|
191 |
responsivePriority: 1,
|
192 |
orderable: false,
|
193 |
render: function ( data, type, row, meta ) {
|
194 |
+
return '<button type="button" class="btn btn-default bookly-js-edit"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</a>';
|
195 |
}
|
196 |
},
|
197 |
{
|
225 |
/**
|
226 |
* Edit appointment.
|
227 |
*/
|
228 |
+
$appointments_list
|
229 |
+
.on('click', 'button.bookly-js-edit', function (e) {
|
230 |
+
e.preventDefault();
|
231 |
+
var data = dt.row($(this).closest('td')).data();
|
232 |
+
showAppointmentDialog(
|
233 |
+
data.id,
|
234 |
+
null,
|
235 |
+
null,
|
236 |
+
function (event) {
|
237 |
+
dt.ajax.reload();
|
238 |
+
}
|
239 |
+
)
|
240 |
+
});
|
241 |
|
242 |
/**
|
243 |
* Export.
|
258 |
$.fn.dataTable.ext.buttons.csvHtml5.action(null, dt, null, $.extend({}, $.fn.dataTable.ext.buttons.csvHtml5, config));
|
259 |
});
|
260 |
|
261 |
+
/**
|
262 |
+
* Print.
|
263 |
+
*/
|
264 |
+
$print_button.on('click', function () {
|
265 |
+
var columns = [];
|
266 |
+
$print_dialog.find('input:checked').each(function () {
|
267 |
+
columns.push(this.value);
|
268 |
+
});
|
269 |
+
var config = {
|
270 |
+
title: '',
|
271 |
+
exportOptions: {
|
272 |
+
columns: columns
|
273 |
+
},
|
274 |
+
customize: function (win) {
|
275 |
+
win.document.firstChild.style.backgroundColor = '#fff';
|
276 |
+
win.document.body.id = 'bookly-tbs';
|
277 |
+
$(win.document.body).find('table').removeClass('collapsed');
|
278 |
+
}
|
279 |
+
};
|
280 |
+
$.fn.dataTable.ext.buttons.print.action(null, dt, null, $.extend({}, $.fn.dataTable.ext.buttons.print, config));
|
281 |
});
|
282 |
|
283 |
/**
|
349 |
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
350 |
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
351 |
picker_ranges[BooklyL10n.next_month] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
|
352 |
+
if (BooklyL10n.tasks.enabled) {
|
353 |
+
picker_ranges[BooklyL10n.tasks.title] = [moment(), moment().add(1, 'days')];
|
354 |
+
}
|
355 |
|
356 |
$date_filter.daterangepicker(
|
357 |
{
|
359 |
startDate: moment().startOf('month'),
|
360 |
endDate: moment().endOf('month'),
|
361 |
ranges: picker_ranges,
|
362 |
+
autoUpdateInput: false,
|
363 |
locale: {
|
364 |
applyLabel : BooklyL10n.apply,
|
365 |
cancelLabel: BooklyL10n.cancel,
|
372 |
format : BooklyL10n.mjsDateFormat
|
373 |
}
|
374 |
},
|
375 |
+
function(start, end, label) {
|
376 |
+
if (label != BooklyL10n.tasks.title) {
|
377 |
+
var format = 'YYYY-MM-DD';
|
378 |
+
$date_filter
|
379 |
+
.data('date', start.format(format) + ' - ' + end.format(format))
|
380 |
+
.find('span')
|
381 |
+
.html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
382 |
+
} else {
|
383 |
+
$date_filter
|
384 |
+
.data('date', null)
|
385 |
+
.find('span')
|
386 |
+
.html(BooklyL10n.tasks.title);
|
387 |
+
}
|
388 |
}
|
389 |
);
|
390 |
|
backend/modules/appointments/templates/_export_dialog.php
DELETED
@@ -1,46 +0,0 @@
|
|
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">
|
8 |
-
<div class="modal-header">
|
9 |
-
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
10 |
-
<div class="modal-title h2"><?php _e( 'Export to CSV', 'bookly' ) ?></div>
|
11 |
-
</div>
|
12 |
-
<div class="modal-body">
|
13 |
-
<div class="form-group">
|
14 |
-
<label for="bookly-csv-delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
|
15 |
-
<select id="bookly-csv-delimiter" class="form-control">
|
16 |
-
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
17 |
-
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
18 |
-
</select>
|
19 |
-
</div>
|
20 |
-
<div class="form-group">
|
21 |
-
<div class="checkbox"><label><input checked value="0" type="checkbox" /><?php _e( 'No.', 'bookly' ) ?></label></div>
|
22 |
-
<div class="checkbox"><label><input checked value="1" type="checkbox" /><?php _e( 'Booking Time', 'bookly' ) ?></label></div>
|
23 |
-
<div class="checkbox"><label><input checked value="2" type="checkbox" /><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ) ?></label></div>
|
24 |
-
<div class="checkbox"><label><input checked value="3" type="checkbox" /><?php _e( 'Customer Name', 'bookly' ) ?></label></div>
|
25 |
-
<div class="checkbox"><label><input checked value="4" type="checkbox" /><?php _e( 'Customer Phone', 'bookly' ) ?></label></div>
|
26 |
-
<div class="checkbox"><label><input checked value="5" type="checkbox" /><?php _e( 'Customer Email', 'bookly' ) ?></label></div>
|
27 |
-
<div class="checkbox"><label><input checked value="6" type="checkbox" /><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ) ?></label></div>
|
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>
|
38 |
-
</div>
|
39 |
-
<div class="modal-footer">
|
40 |
-
<button type="submit" class="btn btn-lg btn-success" id="bookly-export" data-dismiss="modal">
|
41 |
-
<?php _e( 'Export to CSV', 'bookly' ) ?>
|
42 |
-
</button>
|
43 |
-
</div>
|
44 |
-
</div>
|
45 |
-
</div>
|
46 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appointments/templates/index.php
CHANGED
@@ -1,7 +1,11 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
|
|
|
|
|
|
|
|
5 |
?>
|
6 |
<div id="bookly-tbs" class="wrap">
|
7 |
<div class="bookly-tbs-body">
|
@@ -9,18 +13,14 @@ use BooklyLite\Lib\Config;
|
|
9 |
<div class="bookly-page-title">
|
10 |
<?php _e( 'Appointments', 'bookly' ) ?>
|
11 |
</div>
|
12 |
-
<?php
|
13 |
</div>
|
14 |
<div class="panel panel-default bookly-main">
|
15 |
<div class="panel-body">
|
16 |
<div class="row">
|
17 |
<div class="form-inline bookly-margin-bottom-lg text-right">
|
18 |
-
|
19 |
-
|
20 |
-
</div>
|
21 |
-
<div class="form-group">
|
22 |
-
<button type="button" class="btn btn-default bookly-btn-block-xs bookly-limitation"><i class="glyphicon glyphicon-print"></i> <?php _e( 'Print', 'bookly' ) ?></button>
|
23 |
-
</div>
|
24 |
<div class="form-group">
|
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>
|
@@ -37,7 +37,7 @@ use BooklyLite\Lib\Config;
|
|
37 |
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( 'first day of' ) ) ?> - <?php echo date( 'Y-m-d', strtotime( 'last day of' ) ) ?>">
|
38 |
<i class="dashicons dashicons-calendar-alt"></i>
|
39 |
<span>
|
40 |
-
<?php echo \
|
41 |
</span>
|
42 |
</button>
|
43 |
</div>
|
@@ -81,6 +81,9 @@ use BooklyLite\Lib\Config;
|
|
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>
|
@@ -95,16 +98,23 @@ use BooklyLite\Lib\Config;
|
|
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 ?>
|
|
|
|
|
|
|
108 |
<th></th>
|
109 |
<th width="16"><input type="checkbox" id="bookly-check-all" /></th>
|
110 |
</tr>
|
@@ -112,15 +122,16 @@ use BooklyLite\Lib\Config;
|
|
112 |
</table>
|
113 |
|
114 |
<div class="text-right bookly-margin-top-lg">
|
115 |
-
<?php
|
116 |
</div>
|
117 |
</div>
|
118 |
</div>
|
119 |
|
120 |
-
<?php \
|
121 |
-
<?php
|
122 |
|
123 |
-
<?php \
|
124 |
-
<?php \
|
|
|
125 |
</div>
|
126 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls;
|
3 |
+
use Bookly\Backend\Components\Dialogs;
|
4 |
+
use Bookly\Backend\Components\Support;
|
5 |
+
use Bookly\Backend\Modules\Appointments\Proxy;
|
6 |
+
use Bookly\Lib\Config;
|
7 |
+
use Bookly\Lib\Entities\CustomerAppointment;
|
8 |
+
use Bookly\Lib\Utils\Common;
|
9 |
?>
|
10 |
<div id="bookly-tbs" class="wrap">
|
11 |
<div class="bookly-tbs-body">
|
13 |
<div class="bookly-page-title">
|
14 |
<?php _e( 'Appointments', 'bookly' ) ?>
|
15 |
</div>
|
16 |
+
<?php Support\Buttons::render( $self::pageSlug() ) ?>
|
17 |
</div>
|
18 |
<div class="panel panel-default bookly-main">
|
19 |
<div class="panel-body">
|
20 |
<div class="row">
|
21 |
<div class="form-inline bookly-margin-bottom-lg text-right">
|
22 |
+
<?php Proxy\Pro::renderExportButton() ?>
|
23 |
+
<?php Proxy\Pro::renderPrintButton() ?>
|
|
|
|
|
|
|
|
|
24 |
<div class="form-group">
|
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>
|
37 |
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( 'first day of' ) ) ?> - <?php echo date( 'Y-m-d', strtotime( 'last day of' ) ) ?>">
|
38 |
<i class="dashicons dashicons-calendar-alt"></i>
|
39 |
<span>
|
40 |
+
<?php echo \Bookly\Lib\Utils\DateTime::formatDate( 'first day of this month' ) ?> - <?php echo \Bookly\Lib\Utils\DateTime::formatDate( 'last day of this month' ) ?>
|
41 |
</span>
|
42 |
</button>
|
43 |
</div>
|
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 |
+
<?php if ( Config::tasksActive() ): ?>
|
85 |
+
<option value="<?php echo CustomerAppointment::STATUS_DONE ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_DONE ) ?></option>
|
86 |
+
<?php endif ?>
|
87 |
</select>
|
88 |
</div>
|
89 |
</div>
|
98 |
<th><?php _e( 'Customer Name', 'bookly' ) ?></th>
|
99 |
<th><?php _e( 'Customer Phone', 'bookly' ) ?></th>
|
100 |
<th><?php _e( 'Customer Email', 'bookly' ) ?></th>
|
101 |
+
<?php if ( Config::groupBookingActive() ) : ?>
|
102 |
+
<th><?php _e( 'Number of persons', 'bookly' ) ?></th>
|
103 |
+
<?php endif ?>
|
104 |
<th><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_service' ) ) ?></th>
|
105 |
<th><?php _e( 'Duration', 'bookly' ) ?></th>
|
106 |
<th><?php _e( 'Status', 'bookly' ) ?></th>
|
107 |
<th><?php _e( 'Payment', 'bookly' ) ?></th>
|
108 |
+
<?php Proxy\Ratings::renderTableHeader() ?>
|
109 |
<?php if ( Config::showNotes() ): ?>
|
110 |
<th><?php echo esc_html( Common::getTranslatedOption( 'bookly_l10n_label_notes' ) ) ?></th>
|
111 |
<?php endif ?>
|
112 |
<?php foreach ( $custom_fields as $custom_field ) : ?>
|
113 |
<th><?php echo $custom_field->label ?></th>
|
114 |
<?php endforeach ?>
|
115 |
+
<?php if ( $show_attachments ) : ?>
|
116 |
+
<th><?php _e( 'Attachments', 'bookly' ) ?></th>
|
117 |
+
<?php endif ?>
|
118 |
<th></th>
|
119 |
<th width="16"><input type="checkbox" id="bookly-check-all" /></th>
|
120 |
</tr>
|
122 |
</table>
|
123 |
|
124 |
<div class="text-right bookly-margin-top-lg">
|
125 |
+
<?php Controls\Buttons::renderDelete( null, null, null, array( 'data-toggle' => 'modal', 'data-target'=> '#bookly-delete-dialog' ) ) ?>
|
126 |
</div>
|
127 |
</div>
|
128 |
</div>
|
129 |
|
130 |
+
<?php Proxy\Pro::renderExportDialog( $custom_fields ) ?>
|
131 |
+
<?php Proxy\Pro::renderPrintDialog( $custom_fields ) ?>
|
132 |
|
133 |
+
<?php Dialogs\Appointment\Delete\Dialog::render() ?>
|
134 |
+
<?php Dialogs\Appointment\Edit\Dialog::render() ?>
|
135 |
+
<?php Proxy\Shared::renderAddOnsComponents() ?>
|
136 |
</div>
|
137 |
</div>
|
backend/modules/calendar/Ajax.php
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Calendar;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Calendar
|
9 |
+
*/
|
10 |
+
class Ajax extends Page
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @inheritdoc
|
14 |
+
*/
|
15 |
+
protected static function permissions()
|
16 |
+
{
|
17 |
+
return array( '_default' => 'user' );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Get data for FullCalendar.
|
22 |
+
*/
|
23 |
+
public static function getStaffAppointments()
|
24 |
+
{
|
25 |
+
$result = array();
|
26 |
+
$staff = Lib\Entities\Staff::query()->findOne();
|
27 |
+
$staff_members = $staff ? Lib\Proxy\Pro::prepareStaffMembers( array( $staff ) ) : array();
|
28 |
+
$one_day = new \DateInterval( 'P1D' );
|
29 |
+
$start_date = new \DateTime( self::parameter( 'start' ) );
|
30 |
+
$end_date = new \DateTime( self::parameter( 'end' ) );
|
31 |
+
// FullCalendar sends end date as 1 day further.
|
32 |
+
$end_date->sub( $one_day );
|
33 |
+
|
34 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
|
35 |
+
$staff_ids = explode( ',', self::parameter( 'staff_ids' ) );
|
36 |
+
foreach ( $staff_members as $id => $staff ) {
|
37 |
+
if ( ! in_array( $staff->getId(), $staff_ids ) ) {
|
38 |
+
unset( $staff_members[ $id ] );
|
39 |
+
}
|
40 |
+
}
|
41 |
+
} else {
|
42 |
+
$staff_ids = array( $staff_members[0]->getId() );
|
43 |
+
}
|
44 |
+
// Load special days.
|
45 |
+
$special_days = array();
|
46 |
+
foreach ( (array) Lib\Proxy\SpecialDays::getSchedule( $staff_ids, $start_date, $end_date ) as $day ) {
|
47 |
+
$special_days[ $day['staff_id'] ][ $day['date'] ][] = $day;
|
48 |
+
}
|
49 |
+
|
50 |
+
if ( ! Lib\Config::locationsActive() || self::parameter( 'location_ids' ) ) {
|
51 |
+
foreach ( $staff_members as $staff ) {
|
52 |
+
/** @var Lib\Entities\Staff $staff */
|
53 |
+
$result = array_merge( $result, self::_getAppointmentsForFC( $staff->getId(), $start_date, $end_date ) );
|
54 |
+
|
55 |
+
// Schedule.
|
56 |
+
$items = $staff->getScheduleItems();
|
57 |
+
$day = clone $start_date;
|
58 |
+
// Find previous day end time.
|
59 |
+
$last_end = clone $day;
|
60 |
+
$last_end->sub( $one_day );
|
61 |
+
$w = (int) $day->format( 'w' );
|
62 |
+
$end_time = $items[ $w > 0 ? $w : 7 ]->getEndTime();
|
63 |
+
if ( $end_time !== null ) {
|
64 |
+
$end_time = explode( ':', $end_time );
|
65 |
+
$last_end->setTime( $end_time[0], $end_time[1] );
|
66 |
+
} else {
|
67 |
+
$last_end->setTime( 24, 0 );
|
68 |
+
}
|
69 |
+
// Do the loop.
|
70 |
+
while ( $day <= $end_date ) {
|
71 |
+
$start = $last_end->format( 'Y-m-d H:i:s' );
|
72 |
+
// Check if $day is Special Day for current staff.
|
73 |
+
if ( isset( $special_days[ $staff->getId() ][ $day->format( 'Y-m-d' ) ] ) ) {
|
74 |
+
$sp_days = $special_days[ $staff->getId() ][ $day->format( 'Y-m-d' ) ];
|
75 |
+
$end = $sp_days[0]['date'] . ' ' . $sp_days[0]['start_time'];
|
76 |
+
if ( $start < $end ) {
|
77 |
+
$result[] = array(
|
78 |
+
'start' => $start,
|
79 |
+
'end' => $end,
|
80 |
+
'rendering' => 'background',
|
81 |
+
'staffId' => $staff->getId(),
|
82 |
+
);
|
83 |
+
}
|
84 |
+
// Breaks.
|
85 |
+
foreach ( $sp_days as $sp_day ) {
|
86 |
+
$break_start = date(
|
87 |
+
'Y-m-d H:i:s',
|
88 |
+
strtotime( $sp_day['date'] ) + Lib\Utils\DateTime::timeToSeconds( $sp_day['break_start'] )
|
89 |
+
);
|
90 |
+
$break_end = date(
|
91 |
+
'Y-m-d H:i:s',
|
92 |
+
strtotime( $sp_day['date'] ) + Lib\Utils\DateTime::timeToSeconds( $sp_day['break_end'] )
|
93 |
+
);
|
94 |
+
$result[] = array(
|
95 |
+
'start' => $break_start,
|
96 |
+
'end' => $break_end,
|
97 |
+
'rendering' => 'background',
|
98 |
+
'staffId' => $staff->getId(),
|
99 |
+
);
|
100 |
+
}
|
101 |
+
$end_time = explode( ':', $sp_days[0]['end_time'] );
|
102 |
+
$last_end = clone $day;
|
103 |
+
$last_end->setTime( $end_time[0], $end_time[1] );
|
104 |
+
} else {
|
105 |
+
/** @var Lib\Entities\StaffScheduleItem $item */
|
106 |
+
$item = $items[ (int) $day->format( 'w' ) + 1 ];
|
107 |
+
if ( $item->getStartTime() && ! $staff->isOnHoliday( $day ) ) {
|
108 |
+
$end = $day->format( 'Y-m-d ' . $item->getStartTime() );
|
109 |
+
if ( $start < $end ) {
|
110 |
+
$result[] = array(
|
111 |
+
'start' => $start,
|
112 |
+
'end' => $end,
|
113 |
+
'rendering' => 'background',
|
114 |
+
'staffId' => $staff->getId(),
|
115 |
+
);
|
116 |
+
}
|
117 |
+
$last_end = clone $day;
|
118 |
+
$end_time = explode( ':', $item->getEndTime() );
|
119 |
+
$last_end->setTime( $end_time[0], $end_time[1] );
|
120 |
+
|
121 |
+
// Breaks.
|
122 |
+
foreach ( $item->getBreaksList() as $break ) {
|
123 |
+
$break_start = date(
|
124 |
+
'Y-m-d H:i:s',
|
125 |
+
$day->getTimestamp() + Lib\Utils\DateTime::timeToSeconds( $break['start_time'] )
|
126 |
+
);
|
127 |
+
$break_end = date(
|
128 |
+
'Y-m-d H:i:s',
|
129 |
+
$day->getTimestamp() + Lib\Utils\DateTime::timeToSeconds( $break['end_time'] )
|
130 |
+
);
|
131 |
+
$result[] = array(
|
132 |
+
'start' => $break_start,
|
133 |
+
'end' => $break_end,
|
134 |
+
'rendering' => 'background',
|
135 |
+
'staffId' => $staff->getId(),
|
136 |
+
);
|
137 |
+
}
|
138 |
+
} else {
|
139 |
+
$result[] = array(
|
140 |
+
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
141 |
+
'end' => $day->format( 'Y-m-d 24:00:00' ),
|
142 |
+
'rendering' => 'background',
|
143 |
+
'staffId' => $staff->getId(),
|
144 |
+
);
|
145 |
+
$last_end = clone $day;
|
146 |
+
$last_end->setTime( 24, 0 );
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
$day->add( $one_day );
|
151 |
+
}
|
152 |
+
|
153 |
+
if ( $last_end->format( 'H' ) != 24 ) {
|
154 |
+
$result[] = array(
|
155 |
+
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
156 |
+
'end' => $last_end->format( 'Y-m-d 24:00:00' ),
|
157 |
+
'rendering' => 'background',
|
158 |
+
'staffId' => $staff->getId(),
|
159 |
+
);
|
160 |
+
}
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
wp_send_json( $result );
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Get appointments for FullCalendar.
|
169 |
+
*
|
170 |
+
* @param integer $staff_id
|
171 |
+
* @param \DateTime $start_date
|
172 |
+
* @param \DateTime $end_date
|
173 |
+
* @return array
|
174 |
+
*/
|
175 |
+
private static function _getAppointmentsForFC( $staff_id, \DateTime $start_date, \DateTime $end_date )
|
176 |
+
{
|
177 |
+
$query = Lib\Entities\Appointment::query( 'a' )
|
178 |
+
->where( 'st.id', $staff_id )
|
179 |
+
->whereBetween( 'DATE(a.start_date)', $start_date->format( 'Y-m-d' ), $end_date->format( 'Y-m-d' ) );
|
180 |
+
|
181 |
+
Proxy\Shared::prepareAppointmentsQueryForFC( $query, $staff_id, $start_date, $end_date );
|
182 |
+
|
183 |
+
return self::buildAppointmentsForFC( $staff_id, $query );
|
184 |
+
}
|
185 |
+
}
|
backend/modules/calendar/Controller.php
DELETED
@@ -1,932 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Calendar;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
use BooklyLite\Lib\DataHolders\Booking as DataHolders;
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class Controller
|
9 |
-
* @package BooklyLite\Backend\Modules\Calendar
|
10 |
-
*/
|
11 |
-
class Controller extends Lib\Base\Controller
|
12 |
-
{
|
13 |
-
const page_slug = 'bookly-calendar';
|
14 |
-
|
15 |
-
protected function getPermissions()
|
16 |
-
{
|
17 |
-
return array( '_this' => 'user' );
|
18 |
-
}
|
19 |
-
|
20 |
-
public function index()
|
21 |
-
{
|
22 |
-
/** @var \WP_Locale $wp_locale */
|
23 |
-
global $wp_locale;
|
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(
|
31 |
-
'backend' => array( 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ), ),
|
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 |
-
|
40 |
-
$slot_length_minutes = get_option( 'bookly_gen_time_slot_length', '15' );
|
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 ),
|
52 |
-
'longMonths' => array_values( $wp_locale->month ),
|
53 |
-
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
54 |
-
'longDays' => array_values( $wp_locale->weekday ),
|
55 |
-
),
|
56 |
-
'dpDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
57 |
-
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
58 |
-
'mjsTimeFormat' => Lib\Utils\DateTime::convertFormat( 'time', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
59 |
-
'today' => __( 'Today', 'bookly' ),
|
60 |
-
'week' => __( 'Week', 'bookly' ),
|
61 |
-
'day' => __( 'Day', 'bookly' ),
|
62 |
-
'month' => __( 'Month', 'bookly' ),
|
63 |
-
'allDay' => __( 'All Day', 'bookly' ),
|
64 |
-
'delete' => __( 'Delete', 'bookly' ),
|
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' ) );
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Get data for FullCalendar.
|
87 |
-
*
|
88 |
-
* return string json
|
89 |
-
*/
|
90 |
-
public function executeGetStaffAppointments()
|
91 |
-
{
|
92 |
-
$result = array();
|
93 |
-
$staff_members = array();
|
94 |
-
$one_day = new \DateInterval( 'P1D' );
|
95 |
-
$start_date = new \DateTime( $this->getParameter( 'start' ) );
|
96 |
-
$end_date = new \DateTime( $this->getParameter( 'end' ) );
|
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();
|
123 |
-
$day = clone $start_date;
|
124 |
-
// Find previous day end time.
|
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] );
|
132 |
-
} else {
|
133 |
-
$last_end->setTime( 24, 0 );
|
134 |
-
}
|
135 |
-
// Do the loop.
|
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 {
|
205 |
-
$result[] = array(
|
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 );
|
213 |
-
}
|
214 |
-
}
|
215 |
-
|
216 |
-
$day->add( $one_day );
|
217 |
-
}
|
218 |
-
|
219 |
-
if ( $last_end->format( 'H' ) != 24 ) {
|
220 |
-
$result[] = array(
|
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 |
-
}
|
228 |
-
|
229 |
-
wp_send_json( $result );
|
230 |
-
}
|
231 |
-
|
232 |
-
/**
|
233 |
-
* Get data needed for appointment form initialisation.
|
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 |
-
),
|
255 |
-
);
|
256 |
-
|
257 |
-
// Staff list.
|
258 |
-
$staff_members = Lib\Utils\Common::isCurrentUserAdmin()
|
259 |
-
? Lib\Entities\Staff::query()->sortBy( 'position' )->find()
|
260 |
-
: Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
|
261 |
-
|
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,
|
318 |
-
);
|
319 |
-
}
|
320 |
-
|
321 |
-
// Time list.
|
322 |
-
$ts_length = Lib\Config::getTimeSlotLength();
|
323 |
-
$time_start = 0;
|
324 |
-
$time_end = DAY_IN_SECONDS * 2;
|
325 |
-
|
326 |
-
// Run the loop.
|
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;
|
334 |
-
}
|
335 |
-
$result['end_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 |
-
|
348 |
-
/**
|
349 |
-
* Get appointment data when editing an appointment.
|
350 |
-
*/
|
351 |
-
public function executeGetDataForAppointment()
|
352 |
-
{
|
353 |
-
$response = array( 'success' => false, 'data' => array( 'customers' => array() ) );
|
354 |
-
|
355 |
-
$appointment = new Lib\Entities\Appointment();
|
356 |
-
if ( $appointment->load( $this->getParameter( 'id' ) ) ) {
|
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>',
|
421 |
-
Lib\Entities\Payment::typeToString( $customer['payment_type'] ),
|
422 |
-
$customer['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
423 |
-
Lib\Entities\Payment::statusToString( $customer['payment_status'] )
|
424 |
-
);
|
425 |
-
}
|
426 |
-
$compound_service = '';
|
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,
|
446 |
-
'status' => $customer['status'],
|
447 |
-
);
|
448 |
-
}
|
449 |
-
}
|
450 |
-
wp_send_json( $response );
|
451 |
-
}
|
452 |
-
|
453 |
-
/**
|
454 |
-
* Save appointment form (for both create and edit).
|
455 |
-
*/
|
456 |
-
public function executeSaveAppointmentForm()
|
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 ) {
|
491 |
-
$response['errors']['time_interval'] = __( 'Start time must not be empty', 'bookly' );
|
492 |
-
} elseif ( ! $end_date ) {
|
493 |
-
$response['errors']['time_interval'] = __( 'End time must not be empty', 'bookly' );
|
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 |
-
|
687 |
-
if ( $service_id ) {
|
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 );
|
708 |
-
}
|
709 |
-
|
710 |
-
/**
|
711 |
-
* Delete single appointment.
|
712 |
-
*/
|
713 |
-
public function executeDeleteAppointment()
|
714 |
-
{
|
715 |
-
$appointment_id = $this->getParameter( 'appointment_id' );
|
716 |
-
$reason = $this->getParameter( 'reason' );
|
717 |
-
|
718 |
-
if ( $this->getParameter( 'notify' ) ) {
|
719 |
-
$ca_list = Lib\Entities\CustomerAppointment::query()
|
720 |
-
->where( 'appointment_id', $appointment_id )
|
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 |
-
}
|
745 |
-
|
746 |
-
/**
|
747 |
-
* @param $start_date
|
748 |
-
* @param $end_date
|
749 |
-
* @param $staff_id
|
750 |
-
* @param $appointment_id
|
751 |
-
* @return bool
|
752 |
-
*/
|
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 |
-
|
763 |
-
/**
|
764 |
-
* Get appointments for FullCalendar.
|
765 |
-
*
|
766 |
-
* @param integer $staff_id
|
767 |
-
* @param \DateTime $start_date
|
768 |
-
* @param \DateTime $end_date
|
769 |
-
* @return array
|
770 |
-
*/
|
771 |
-
private function _getAppointmentsForFC( $staff_id, \DateTime $start_date, \DateTime $end_date )
|
772 |
-
{
|
773 |
-
$query = Lib\Entities\Appointment::query( 'a' )
|
774 |
-
->where( 'st.id', $staff_id )
|
775 |
-
->whereBetween( 'DATE(a.start_date)', $start_date->format( 'Y-m-d' ), $end_date->format( 'Y-m-d' ) );
|
776 |
-
|
777 |
-
return $this->_buildAppointmentsForFC( $staff_id, $query );
|
778 |
-
}
|
779 |
-
|
780 |
-
/**
|
781 |
-
* Get appointment for FullCalendar.
|
782 |
-
*
|
783 |
-
* @param integer $staff_id
|
784 |
-
* @param int $appointment_id
|
785 |
-
* @return array
|
786 |
-
*/
|
787 |
-
private function _getAppointmentForFC( $staff_id, $appointment_id )
|
788 |
-
{
|
789 |
-
$query = Lib\Entities\Appointment::query( 'a' )
|
790 |
-
->where( 'a.id', $appointment_id );
|
791 |
-
|
792 |
-
$appointments = $this->_buildAppointmentsForFC( $staff_id, $query );
|
793 |
-
|
794 |
-
return $appointments[0];
|
795 |
-
}
|
796 |
-
|
797 |
-
/**
|
798 |
-
* Build appointments for FullCalendar.
|
799 |
-
*
|
800 |
-
* @param integer $staff_id
|
801 |
-
* @param Lib\Query $query
|
802 |
-
* @return mixed
|
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/Page.php
ADDED
@@ -0,0 +1,229 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Calendar;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Calendar
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
self::enqueueStyles( array(
|
21 |
+
'module' => array( 'css/fullcalendar.min.css', ),
|
22 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css' ),
|
23 |
+
) );
|
24 |
+
|
25 |
+
self::enqueueScripts( array(
|
26 |
+
'backend' => array(
|
27 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
+
'js/alert.js' => array( 'jquery' ),
|
29 |
+
),
|
30 |
+
'module' => array(
|
31 |
+
'js/fullcalendar.min.js' => array( 'bookly-moment.min.js' ),
|
32 |
+
'js/fc-multistaff-view.js' => array( 'bookly-fullcalendar.min.js' ),
|
33 |
+
'js/calendar-common.js' => array( 'bookly-fc-multistaff-view.js' ),
|
34 |
+
'js/calendar.js' => array( 'bookly-calendar-common.js' ),
|
35 |
+
),
|
36 |
+
) );
|
37 |
+
|
38 |
+
$slot_length_minutes = get_option( 'bookly_gen_time_slot_length', '15' );
|
39 |
+
$slot = new \DateInterval( 'PT' . $slot_length_minutes . 'M' );
|
40 |
+
|
41 |
+
wp_localize_script( 'bookly-calendar.js', 'BooklyL10n', array(
|
42 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
43 |
+
'slotDuration' => $slot->format( '%H:%I:%S' ),
|
44 |
+
'calendar' => array(
|
45 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
46 |
+
'longMonths' => array_values( $wp_locale->month ),
|
47 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
48 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
49 |
+
),
|
50 |
+
'dpDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
51 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
52 |
+
'mjsTimeFormat' => Lib\Utils\DateTime::convertFormat( 'time', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
53 |
+
'today' => __( 'Today', 'bookly' ),
|
54 |
+
'week' => __( 'Week', 'bookly' ),
|
55 |
+
'day' => __( 'Day', 'bookly' ),
|
56 |
+
'month' => __( 'Month', 'bookly' ),
|
57 |
+
'allDay' => __( 'All Day', 'bookly' ),
|
58 |
+
'delete' => __( 'Delete', 'bookly' ),
|
59 |
+
'noStaffSelected' => __( 'No staff selected', 'bookly' ),
|
60 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
61 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
62 |
+
'recurring_appointments' => array(
|
63 |
+
'active' => (int) Lib\Config::recurringAppointmentsActive(),
|
64 |
+
'title' => __( 'Recurring appointments', 'bookly' ),
|
65 |
+
),
|
66 |
+
'waiting_list' => array(
|
67 |
+
'active' => (int) Lib\Config::waitingListActive(),
|
68 |
+
'title' => __( 'On waiting list', 'bookly' ),
|
69 |
+
),
|
70 |
+
'packages' => array(
|
71 |
+
'active' => (int) Lib\Config::packagesActive(),
|
72 |
+
'title' => __( 'Package', 'bookly' ),
|
73 |
+
),
|
74 |
+
) );
|
75 |
+
$staff = Lib\Entities\Staff::query()->findOne();
|
76 |
+
$staff_members = $staff ? Lib\Proxy\Pro::prepareStaffMembers( array( $staff ) ) : array();
|
77 |
+
|
78 |
+
self::renderTemplate( 'calendar', compact( 'staff_members' ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Build appointments for FullCalendar.
|
83 |
+
*
|
84 |
+
* @param integer $staff_id
|
85 |
+
* @param Lib\Query $query
|
86 |
+
* @return mixed
|
87 |
+
*/
|
88 |
+
public static function buildAppointmentsForFC( $staff_id, Lib\Query $query )
|
89 |
+
{
|
90 |
+
$one_participant = '<div>' . str_replace( "\n", '</div><div>', get_option( 'bookly_cal_one_participant' ) ) . '</div>';
|
91 |
+
$many_participants = '<div>' . str_replace( "\n", '</div><div>', get_option( 'bookly_cal_many_participants' ) ) . '</div>';
|
92 |
+
$postfix_any = sprintf( ' (%s)', get_option( 'bookly_l10n_option_employee' ) );
|
93 |
+
$participants = null;
|
94 |
+
$default_codes = array(
|
95 |
+
'{amount_due}' => '',
|
96 |
+
'{amount_paid}' => '',
|
97 |
+
'{appointment_date}' => '',
|
98 |
+
'{appointment_time}' => '',
|
99 |
+
'{booking_number}' => '',
|
100 |
+
'{category_name}' => '',
|
101 |
+
'{client_email}' => '',
|
102 |
+
'{client_name}' => '',
|
103 |
+
'{client_first_name}' => '',
|
104 |
+
'{client_last_name}' => '',
|
105 |
+
'{client_phone}' => '',
|
106 |
+
'{company_address}' => get_option( 'bookly_co_address' ),
|
107 |
+
'{company_name}' => get_option( 'bookly_co_name' ),
|
108 |
+
'{company_phone}' => get_option( 'bookly_co_phone' ),
|
109 |
+
'{company_website}' => get_option( 'bookly_co_website' ),
|
110 |
+
'{custom_fields}' => '',
|
111 |
+
'{extras}' => '',
|
112 |
+
'{extras_total_price}'=> 0,
|
113 |
+
'{location_name}' => '',
|
114 |
+
'{location_info}' => '',
|
115 |
+
'{on_waiting_list}' => '',
|
116 |
+
'{payment_status}' => '',
|
117 |
+
'{payment_type}' => '',
|
118 |
+
'{service_capacity}' => '',
|
119 |
+
'{service_info}' => '',
|
120 |
+
'{service_name}' => '',
|
121 |
+
'{service_price}' => '',
|
122 |
+
'{signed_up}' => '',
|
123 |
+
'{staff_email}' => '',
|
124 |
+
'{staff_info}' => '',
|
125 |
+
'{staff_name}' => '',
|
126 |
+
'{staff_phone}' => '',
|
127 |
+
'{status}' => '',
|
128 |
+
'{total_price}' => '',
|
129 |
+
);
|
130 |
+
$query
|
131 |
+
->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,
|
132 |
+
COALESCE(s.title,a.custom_service_name) AS service_name, COALESCE(s.color,"silver") AS service_color, s.info AS service_info,
|
133 |
+
COALESCE(ss.price,a.custom_service_price) AS service_price,
|
134 |
+
st.full_name AS staff_name, st.email AS staff_email, st.info AS staff_info, st.phone AS staff_phone,
|
135 |
+
(SELECT SUM(ca.number_of_persons) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca WHERE ca.appointment_id = a.id) AS total_number_of_persons,
|
136 |
+
s.duration,
|
137 |
+
s.start_time_info,
|
138 |
+
s.end_time_info,
|
139 |
+
ca.number_of_persons,
|
140 |
+
ca.units,
|
141 |
+
ca.custom_fields,
|
142 |
+
ca.status AS appointment_status,
|
143 |
+
ca.extras,
|
144 |
+
ca.package_id,
|
145 |
+
ct.name AS category_name,
|
146 |
+
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,
|
147 |
+
p.total, p.type AS payment_gateway, p.status AS payment_status, p.paid,
|
148 |
+
(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' )
|
149 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
|
150 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
151 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
152 |
+
->leftJoin( 'Service', 's', 's.id = a.service_id' )
|
153 |
+
->leftJoin( 'Category', 'ct', 'ct.id = s.category_id' )
|
154 |
+
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
155 |
+
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
|
156 |
+
->groupBy( 'a.id' );
|
157 |
+
|
158 |
+
if ( Lib\Config::groupBookingActive() ) {
|
159 |
+
$query->addSelect( 'COALESCE(ss.capacity_max,9999) AS service_capacity' );
|
160 |
+
} else {
|
161 |
+
$query->addSelect( '1 AS service_capacity' );
|
162 |
+
}
|
163 |
+
|
164 |
+
$appointments = $query->fetchArray();
|
165 |
+
|
166 |
+
foreach ( $appointments as $key => $appointment ) {
|
167 |
+
$codes = $default_codes;
|
168 |
+
$codes['{appointment_date}'] = Lib\Utils\DateTime::formatDate( $appointment['start_date'] );
|
169 |
+
$codes['{appointment_time}'] = $appointment['duration'] >= DAY_IN_SECONDS ? $appointment['start_time_info'] : Lib\Utils\DateTime::formatTime( $appointment['start_date'] );
|
170 |
+
$codes['{booking_number}'] = $appointment['id'];
|
171 |
+
$codes['{on_waiting_list}'] = $appointment['on_waiting_list'];
|
172 |
+
$codes['{service_name}'] = $appointment['service_name'] ? esc_html( $appointment['service_name'] ) : __( 'Untitled', 'bookly' );
|
173 |
+
$codes['{service_price}'] = Lib\Utils\Price::format( $appointment['service_price'] * $appointment['units'] );
|
174 |
+
$codes['{signed_up}'] = $appointment['total_number_of_persons'];
|
175 |
+
foreach ( array( 'staff_name', 'staff_phone', 'staff_info', 'staff_email', 'service_info', 'service_capacity', 'category_name' ) as $field ) {
|
176 |
+
$codes[ '{' . $field . '}' ] = esc_html( $appointment[ $field ] );
|
177 |
+
}
|
178 |
+
if ( $appointment['staff_any'] ) {
|
179 |
+
$codes['{staff_name}'] .= $postfix_any;
|
180 |
+
}
|
181 |
+
// Display customer information only if there is 1 customer. Don't confuse with number_of_persons.
|
182 |
+
if ( $appointment['number_of_persons'] == $appointment['total_number_of_persons'] ) {
|
183 |
+
$participants = 'one';
|
184 |
+
$template = $one_participant;
|
185 |
+
foreach ( array( 'client_name', 'client_first_name', 'client_last_name', 'client_phone', 'client_email' ) as $data_entry ) {
|
186 |
+
if ( $appointment[ $data_entry ] ) {
|
187 |
+
$codes[ '{' . $data_entry . '}' ] = esc_html( $appointment[ $data_entry ] );
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
+
// Payment.
|
192 |
+
if ( $appointment['total'] ) {
|
193 |
+
$codes['{total_price}'] = Lib\Utils\Price::format( $appointment['total'] );
|
194 |
+
$codes['{amount_paid}'] = Lib\Utils\Price::format( $appointment['paid'] );
|
195 |
+
$codes['{amount_due}'] = Lib\Utils\Price::format( $appointment['total'] - $appointment['paid'] );
|
196 |
+
$codes['{total_price}'] = Lib\Utils\Price::format( $appointment['total'] );
|
197 |
+
$codes['{payment_type}'] = Lib\Entities\Payment::typeToString( $appointment['payment_gateway'] );
|
198 |
+
$codes['{payment_status}'] = Lib\Entities\Payment::statusToString( $appointment['payment_status'] );
|
199 |
+
}
|
200 |
+
// Status.
|
201 |
+
$codes['{status}'] = Lib\Entities\CustomerAppointment::statusToString( $appointment['appointment_status'] );
|
202 |
+
} else {
|
203 |
+
$participants = 'many';
|
204 |
+
$template = $many_participants;
|
205 |
+
}
|
206 |
+
|
207 |
+
$codes = Proxy\Shared::prepareAppointmentCodesData( $codes, $appointment, $participants );
|
208 |
+
|
209 |
+
$appointments[ $key ] = array(
|
210 |
+
'id' => $appointment['id'],
|
211 |
+
'start' => $appointment['start_date'],
|
212 |
+
'end' => $appointment['end_date'],
|
213 |
+
'title' => ' ',
|
214 |
+
'desc' => strtr( $template, $codes ),
|
215 |
+
'color' => $appointment['service_color'],
|
216 |
+
'staffId' => $staff_id,
|
217 |
+
'series_id' => (int) $appointment['series_id'],
|
218 |
+
'package_id' => (int) $appointment['package_id'],
|
219 |
+
'waitlisted' => (int) $appointment['on_waiting_list'],
|
220 |
+
'staff_any' => (int) $appointment['staff_any'],
|
221 |
+
);
|
222 |
+
if ( $appointment['duration'] * $appointment['units'] >= DAY_IN_SECONDS && $appointment['start_time_info'] ) {
|
223 |
+
$appointments[ $key ]['header_text'] = sprintf( '%s - %s', $appointment['start_time_info'], $appointment['end_time_info'] );
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
return $appointments;
|
228 |
+
}
|
229 |
+
}
|
backend/modules/calendar/proxy/AdvancedGoogleCalendar.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Calendar\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class AdvancedGoogleCalendar
|
8 |
+
* @package Bookly\Backend\Modules\Calendar\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSyncButton( array $staff_members ) Render Google Calendar sync button.
|
11 |
+
*/
|
12 |
+
abstract class AdvancedGoogleCalendar extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/calendar/proxy/Locations.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Calendar\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Locations
|
8 |
+
* @package Bookly\Backend\Modules\Calendar\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCalendarLocationFilter()
|
11 |
+
*/
|
12 |
+
abstract class Locations extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/calendar/proxy/Shared.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Calendar\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Calendar\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareAppointmentCodesData( array $codes, array $appointment_data, string $participants ) Prepare codes data for appointment description displayed in calendar.
|
11 |
+
* @method static void prepareAppointmentsQueryForFC( Lib\Query $query, int $staff_id, \DateTime $start_date, \DateTime $end_date ) Prepare appointments query for full calendar
|
12 |
+
* @method static void renderAddOnsComponents() Render components on calendar page.
|
13 |
+
*/
|
14 |
+
abstract class Shared extends Lib\Base\Proxy
|
15 |
+
{
|
16 |
+
|
17 |
+
}
|
backend/modules/calendar/resources/js/calendar-common.js
CHANGED
@@ -33,7 +33,7 @@ jQuery(function ($) {
|
|
33 |
url: ajaxurl,
|
34 |
data: {
|
35 |
action: 'bookly_get_staff_appointments',
|
36 |
-
csrf_token
|
37 |
staff_ids: function () {
|
38 |
var ids = [];
|
39 |
if (obj.options.is_backend && obj.options.getCurrentStaffId() == 0) {
|
@@ -45,6 +45,20 @@ jQuery(function ($) {
|
|
45 |
ids.push(obj.options.getCurrentStaffId());
|
46 |
}
|
47 |
return ids;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
}
|
49 |
}
|
50 |
}],
|
@@ -88,17 +102,24 @@ jQuery(function ($) {
|
|
88 |
$container.fullCalendar('refetchEvents');
|
89 |
} else {
|
90 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
91 |
-
if (event.
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
|
|
|
|
96 |
}
|
97 |
} else {
|
98 |
// Switch to the event owner tab.
|
99 |
jQuery('li[data-staff_id=' + event.staffId + ']').click();
|
100 |
}
|
101 |
}
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
);
|
104 |
},
|
@@ -111,6 +132,9 @@ jQuery(function ($) {
|
|
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>')
|
@@ -175,15 +199,22 @@ jQuery(function ($) {
|
|
175 |
if (event == 'refresh') {
|
176 |
$container.fullCalendar('refetchEvents');
|
177 |
} else {
|
178 |
-
if (
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
|
|
|
|
185 |
}
|
186 |
}
|
|
|
|
|
|
|
|
|
|
|
187 |
}
|
188 |
);
|
189 |
},
|
@@ -238,7 +269,9 @@ jQuery(function ($) {
|
|
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);
|
@@ -266,6 +299,11 @@ jQuery(function ($) {
|
|
266 |
}
|
267 |
};
|
268 |
|
|
|
|
|
|
|
|
|
|
|
269 |
Calendar.prototype.$deleteDialog = $('#bookly-delete-dialog');
|
270 |
Calendar.prototype.options = {
|
271 |
fullcalendar: {},
|
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) {
|
45 |
ids.push(obj.options.getCurrentStaffId());
|
46 |
}
|
47 |
return ids;
|
48 |
+
},
|
49 |
+
location_ids: function() {
|
50 |
+
if (obj.options.is_backend) {
|
51 |
+
var ids = [];
|
52 |
+
$('.bookly-js-locations-filter input').each(function () {
|
53 |
+
var input = $(this);
|
54 |
+
if (input.prop('checked')) {
|
55 |
+
ids.push(input.val());
|
56 |
+
}
|
57 |
+
});
|
58 |
+
return ids;
|
59 |
+
} else {
|
60 |
+
return ['all'];
|
61 |
+
}
|
62 |
}
|
63 |
}
|
64 |
}],
|
102 |
$container.fullCalendar('refetchEvents');
|
103 |
} else {
|
104 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
105 |
+
if (event.start !== null) {
|
106 |
+
if (event.id) {
|
107 |
+
// Create event in calendar.
|
108 |
+
$container.fullCalendar('renderEvent', event);
|
109 |
+
} else {
|
110 |
+
$container.fullCalendar('refetchEvents');
|
111 |
+
}
|
112 |
}
|
113 |
} else {
|
114 |
// Switch to the event owner tab.
|
115 |
jQuery('li[data-staff_id=' + event.staffId + ']').click();
|
116 |
}
|
117 |
}
|
118 |
+
|
119 |
+
if (locationChanged) {
|
120 |
+
$container.fullCalendar('refetchEvents');
|
121 |
+
locationChanged = false;
|
122 |
+
}
|
123 |
}
|
124 |
);
|
125 |
},
|
132 |
}
|
133 |
|
134 |
var $time = $event.find('.fc-time');
|
135 |
+
if (calEvent.header_text !== undefined) {
|
136 |
+
$time.html(calEvent.header_text);
|
137 |
+
}
|
138 |
if (obj.options.l10n.recurring_appointments.active == '1' && calEvent.series_id) {
|
139 |
$time.prepend(
|
140 |
$('<a class="bookly-fc-icon dashicons dashicons-admin-links"></a>')
|
199 |
if (event == 'refresh') {
|
200 |
$container.fullCalendar('refetchEvents');
|
201 |
} else {
|
202 |
+
if (event.start !== null) {
|
203 |
+
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
204 |
+
// Update event in calendar.
|
205 |
+
jQuery.extend(calEvent, event);
|
206 |
+
$container.fullCalendar('updateEvent', calEvent);
|
207 |
+
} else {
|
208 |
+
// Switch to the event owner tab.
|
209 |
+
jQuery('li[data-staff_id=' + event.staffId + ']').click();
|
210 |
+
}
|
211 |
}
|
212 |
}
|
213 |
+
|
214 |
+
if (locationChanged) {
|
215 |
+
$container.fullCalendar('refetchEvents');
|
216 |
+
locationChanged = false;
|
217 |
+
}
|
218 |
}
|
219 |
);
|
220 |
},
|
269 |
/**
|
270 |
* On delete appointment click.
|
271 |
*/
|
272 |
+
if (obj.$deleteDialog.data('events') == undefined
|
273 |
+
|| obj.$deleteDialog.data('events').click == undefined)
|
274 |
+
{
|
275 |
obj.$deleteDialog.on('click', '#bookly-delete', function (e) {
|
276 |
var calEvent = obj.$deleteDialog.data('calEvent'),
|
277 |
ladda = Ladda.create(this);
|
299 |
}
|
300 |
};
|
301 |
|
302 |
+
var locationChanged = false;
|
303 |
+
$('body').on('change', '#bookly-appointment-location', function() {
|
304 |
+
locationChanged = true;
|
305 |
+
});
|
306 |
+
|
307 |
Calendar.prototype.$deleteDialog = $('#bookly-delete-dialog');
|
308 |
Calendar.prototype.options = {
|
309 |
fullcalendar: {},
|
backend/modules/calendar/resources/js/calendar.js
CHANGED
@@ -5,6 +5,7 @@ jQuery(function ($) {
|
|
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'),
|
@@ -27,7 +28,7 @@ jQuery(function ($) {
|
|
27 |
$tabs.filter('[data-staff_id=' + value + ']').show();
|
28 |
});
|
29 |
} else {
|
30 |
-
$('.dropdown-toggle').dropdown('toggle');
|
31 |
}
|
32 |
|
33 |
$tabs.filter('[data-staff_id=' + tabId + ']').addClass('active');
|
@@ -43,23 +44,9 @@ jQuery(function ($) {
|
|
43 |
* @return {number}
|
44 |
*/
|
45 |
function heightFC() {
|
46 |
-
var
|
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 |
-
|
61 |
-
|
62 |
-
return res > 620 ? res : 620;
|
63 |
}
|
64 |
|
65 |
var options = {
|
@@ -95,6 +82,117 @@ jQuery(function ($) {
|
|
95 |
l10n: BooklyL10n
|
96 |
};
|
97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
var calendar = new BooklyCalendar($fullCalendar, options);
|
99 |
|
100 |
$('.fc-agendaDay-button').addClass('fc-corner-right');
|
@@ -210,4 +308,24 @@ jQuery(function ($) {
|
|
210 |
return keyValue ? keyValue[2] : null;
|
211 |
}
|
212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
});
|
5 |
$staff = $('input.bookly-js-check-entity'),
|
6 |
$showAll = $('input#bookly-check-all-entities'),
|
7 |
firstHour = new Date().getHours(),
|
8 |
+
$syncButton = $('#bookly-google-calendar-sync'),
|
9 |
$staffButton = $('#bookly-staff-button'),
|
10 |
staffMembers = [],
|
11 |
staffIds = getCookie('bookly_cal_st_ids'),
|
28 |
$tabs.filter('[data-staff_id=' + value + ']').show();
|
29 |
});
|
30 |
} else {
|
31 |
+
$('.bookly-js-staff-filter.dropdown-toggle').dropdown('toggle');
|
32 |
}
|
33 |
|
34 |
$tabs.filter('[data-staff_id=' + tabId + ']').addClass('active');
|
44 |
* @return {number}
|
45 |
*/
|
46 |
function heightFC() {
|
47 |
+
var height = $(window).height() - $('#bookly-fc-wrapper').offset().top - 20;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
+
return height > 620 ? height : 620;
|
|
|
|
|
50 |
}
|
51 |
|
52 |
var options = {
|
82 |
l10n: BooklyL10n
|
83 |
};
|
84 |
|
85 |
+
/**
|
86 |
+
* @param {jQuery} $container
|
87 |
+
* @param {Object} callbacks
|
88 |
+
*/
|
89 |
+
function initLocationFilter($container, callbacks)
|
90 |
+
{
|
91 |
+
var elements = {};
|
92 |
+
|
93 |
+
var initElements = function() {
|
94 |
+
elements.toggleButton = $container.find('.bookly-js-toggle-button');
|
95 |
+
elements.title = $container.find('.bookly-js-locations-title');
|
96 |
+
|
97 |
+
elements.items = $container.find('input[type=checkbox]');
|
98 |
+
elements.itemAllLocations = elements.items.filter('.bookly-js-all-locations');
|
99 |
+
elements.itemNoLocations = elements.items.filter('.bookly-js-no-locations');
|
100 |
+
elements.locationItems = elements.items.filter('.bookly-js-locations-item');
|
101 |
+
};
|
102 |
+
|
103 |
+
var restoreItemsFromCookie = function () {
|
104 |
+
var selectedValues = getCookie('bookly_cal_location_ids');
|
105 |
+
|
106 |
+
if (!selectedValues) {
|
107 |
+
return;
|
108 |
+
}
|
109 |
+
|
110 |
+
selectedValues = selectedValues.split(',');
|
111 |
+
|
112 |
+
if (selectedValues.indexOf('all') !== -1) {
|
113 |
+
elements.locationItems.prop('checked', true);
|
114 |
+
} else {
|
115 |
+
selectedValues.forEach(function (value) {
|
116 |
+
elements.locationItems.filter('[value=' + value + ']').prop('checked', true);
|
117 |
+
});
|
118 |
+
}
|
119 |
+
};
|
120 |
+
|
121 |
+
var getSelectedItems = function () {
|
122 |
+
return elements.locationItems.filter(':checked');
|
123 |
+
};
|
124 |
+
|
125 |
+
var updateTitle = function() {
|
126 |
+
var selectedItems = getSelectedItems(),
|
127 |
+
selectedCount = selectedItems.length,
|
128 |
+
locationCount = elements.locationItems.length;
|
129 |
+
|
130 |
+
if (selectedCount === 0) {
|
131 |
+
elements.title.text($container.data('nothing_selected'));
|
132 |
+
} else if (selectedCount === locationCount) {
|
133 |
+
elements.title.text($container.data('all_selected'));
|
134 |
+
} else if (selectedCount === 1) {
|
135 |
+
elements.title.text(selectedItems.data('name'));
|
136 |
+
} else {
|
137 |
+
elements.title.text(selectedCount + '/' + locationCount);
|
138 |
+
}
|
139 |
+
|
140 |
+
elements.itemAllLocations.prop('checked', selectedCount === locationCount);
|
141 |
+
};
|
142 |
+
|
143 |
+
var saveCookie = function () {
|
144 |
+
var selectedItems = getSelectedItems(),
|
145 |
+
selectedValues = [];
|
146 |
+
|
147 |
+
selectedItems.each(function () {
|
148 |
+
selectedValues.push(this.value);
|
149 |
+
});
|
150 |
+
|
151 |
+
setCookie('bookly_cal_location_ids', selectedValues);
|
152 |
+
};
|
153 |
+
|
154 |
+
var initEvents = function () {
|
155 |
+
elements.itemAllLocations.on('change', function () {
|
156 |
+
elements.locationItems.prop('checked', elements.itemAllLocations.prop('checked'));
|
157 |
+
});
|
158 |
+
|
159 |
+
elements.items.on('change', function() {
|
160 |
+
updateTitle();
|
161 |
+
saveCookie();
|
162 |
+
|
163 |
+
if (callbacks.change instanceof Function) {
|
164 |
+
callbacks.change();
|
165 |
+
}
|
166 |
+
});
|
167 |
+
};
|
168 |
+
|
169 |
+
function setDefaultLocations()
|
170 |
+
{
|
171 |
+
var locationIds = getCookie('bookly_cal_location_ids');
|
172 |
+
|
173 |
+
if (!locationIds) {
|
174 |
+
$container.find('input[type="checkbox"]').prop('checked', true);
|
175 |
+
setCookie('bookly_cal_location_ids', ['all']);
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
initElements();
|
180 |
+
setDefaultLocations();
|
181 |
+
restoreItemsFromCookie();
|
182 |
+
initEvents();
|
183 |
+
updateTitle();
|
184 |
+
}
|
185 |
+
|
186 |
+
initLocationFilter($('.bookly-js-locations-filter'), {
|
187 |
+
change: function() {
|
188 |
+
var view = $fullCalendar.fullCalendar('getView');
|
189 |
+
if (view.type === 'multiStaffDay') {
|
190 |
+
view.displayView($fullCalendar.fullCalendar('getDate'));
|
191 |
+
}
|
192 |
+
$fullCalendar.fullCalendar('refetchEvents');
|
193 |
+
}
|
194 |
+
});
|
195 |
+
|
196 |
var calendar = new BooklyCalendar($fullCalendar, options);
|
197 |
|
198 |
$('.fc-agendaDay-button').addClass('fc-corner-right');
|
308 |
return keyValue ? keyValue[2] : null;
|
309 |
}
|
310 |
|
311 |
+
/**
|
312 |
+
* Sync with Google Calendar.
|
313 |
+
*/
|
314 |
+
$syncButton.on('click', function () {
|
315 |
+
var ladda = Ladda.create(this);
|
316 |
+
ladda.start();
|
317 |
+
$.post(
|
318 |
+
ajaxurl,
|
319 |
+
{action: 'bookly_advanced_google_calendar_sync', csrf_token: BooklyL10n.csrf_token},
|
320 |
+
function (response) {
|
321 |
+
if (response.success) {
|
322 |
+
$fullCalendar.fullCalendar('refetchEvents');
|
323 |
+
}
|
324 |
+
booklyAlert(response.data.alert);
|
325 |
+
ladda.stop();
|
326 |
+
},
|
327 |
+
'json'
|
328 |
+
);
|
329 |
+
});
|
330 |
+
|
331 |
});
|
backend/modules/calendar/templates/calendar.php
CHANGED
@@ -1,5 +1,9 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
|
|
|
|
|
|
|
|
3 |
?>
|
4 |
<style>
|
5 |
.fc-slats tr { height: <?php echo max( 21, (int) ( 0.43 * get_option( 'bookly_gen_time_slot_length' ) ) ) ?>px; }
|
@@ -11,27 +15,27 @@
|
|
11 |
<div class="bookly-page-title">
|
12 |
<?php _e( 'Calendar', 'bookly' ) ?>
|
13 |
</div>
|
14 |
-
<?php if (
|
15 |
-
<?php \
|
16 |
<?php endif ?>
|
17 |
</div>
|
18 |
<div class="panel panel-default bookly-main bookly-fc-inner">
|
19 |
<div class="panel-body">
|
20 |
<?php if ( $staff_members ) : ?>
|
21 |
<ul class="bookly-nav bookly-nav-tabs">
|
22 |
-
<?php if (
|
23 |
<li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="0">
|
24 |
<?php _e( 'All', 'bookly' ) ?>
|
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 (
|
33 |
-
<div class="btn-group pull-right
|
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>
|
@@ -47,7 +51,7 @@
|
|
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>
|
@@ -55,6 +59,8 @@
|
|
55 |
</ul>
|
56 |
</div>
|
57 |
<?php endif ?>
|
|
|
|
|
58 |
</ul>
|
59 |
<?php endif ?>
|
60 |
<div class="bookly-margin-top-xlg">
|
@@ -65,24 +71,30 @@
|
|
65 |
<div id="bookly-fc-wrapper" class="bookly-calendar">
|
66 |
<div class="bookly-js-calendar-element"></div>
|
67 |
</div>
|
68 |
-
<?php \
|
69 |
-
<?php
|
|
|
|
|
70 |
<?php else : ?>
|
71 |
<div class="well">
|
72 |
-
<div class="h1"><?php
|
73 |
-
<h3><?php
|
74 |
-
<
|
75 |
-
<p><?php _e( 'To start using Bookly, you need to follow these steps which are the minimum requirements to get it running!', 'bookly' ) ?></p>
|
76 |
<ol>
|
77 |
-
<li><?php
|
78 |
-
<li><?php
|
|
|
79 |
</ol>
|
|
|
80 |
<hr>
|
81 |
-
<a class="btn btn-success" href="<?php echo
|
82 |
-
<?php
|
83 |
</a>
|
84 |
-
<a class="btn btn-success" href="<?php echo
|
85 |
-
<?php
|
|
|
|
|
|
|
86 |
</a>
|
87 |
</div>
|
88 |
<?php endif ?>
|
@@ -90,6 +102,6 @@
|
|
90 |
</div>
|
91 |
</div>
|
92 |
|
93 |
-
<?php \
|
94 |
</div>
|
95 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib\Utils\Common;
|
3 |
+
use Bookly\Backend\Components;
|
4 |
+
use Bookly\Backend\Modules as Backend;
|
5 |
+
use Bookly\Backend\Modules\Calendar\Proxy;
|
6 |
+
/** @var Bookly\Lib\Entities\Staff[] $staff_members */
|
7 |
?>
|
8 |
<style>
|
9 |
.fc-slats tr { height: <?php echo max( 21, (int) ( 0.43 * get_option( 'bookly_gen_time_slot_length' ) ) ) ?>px; }
|
15 |
<div class="bookly-page-title">
|
16 |
<?php _e( 'Calendar', 'bookly' ) ?>
|
17 |
</div>
|
18 |
+
<?php if ( Common::isCurrentUserAdmin() ) : ?>
|
19 |
+
<?php Components\Support\Buttons::render( $self::pageSlug() ) ?>
|
20 |
<?php endif ?>
|
21 |
</div>
|
22 |
<div class="panel panel-default bookly-main bookly-fc-inner">
|
23 |
<div class="panel-body">
|
24 |
<?php if ( $staff_members ) : ?>
|
25 |
<ul class="bookly-nav bookly-nav-tabs">
|
26 |
+
<?php if ( Common::isCurrentUserAdmin() ) : ?>
|
27 |
<li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="0">
|
28 |
<?php _e( 'All', 'bookly' ) ?>
|
29 |
</li>
|
30 |
<?php endif ?>
|
31 |
<?php foreach ( $staff_members as $staff ) : ?>
|
32 |
<li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="<?php echo $staff->getId() ?>" style="display: none">
|
33 |
+
<?php echo esc_html( $staff->getFullName() ) ?>
|
34 |
</li>
|
35 |
<?php endforeach ?>
|
36 |
+
<?php if ( Common::isCurrentUserAdmin() ) : ?>
|
37 |
+
<div class="btn-group pull-right bookly-margin-top-xs">
|
38 |
+
<button class="btn btn-default dropdown-toggle bookly-js-staff-filter bookly-flexbox" data-toggle="dropdown">
|
39 |
<div class="bookly-flex-cell"><i class="dashicons dashicons-admin-users bookly-margin-right-md"></i></div>
|
40 |
<div class="bookly-flex-cell text-left"><span id="bookly-staff-button"></span></div>
|
41 |
<div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
|
51 |
<a class="checkbox" href="javascript:void(0)">
|
52 |
<label>
|
53 |
<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">
|
54 |
+
<?php echo esc_html( $staff->getFullName() ) ?>
|
55 |
</label>
|
56 |
</a>
|
57 |
</li>
|
59 |
</ul>
|
60 |
</div>
|
61 |
<?php endif ?>
|
62 |
+
<?php Proxy\Locations::renderCalendarLocationFilter() ?>
|
63 |
+
<?php Proxy\AdvancedGoogleCalendar::renderSyncButton( $staff_members ) ?>
|
64 |
</ul>
|
65 |
<?php endif ?>
|
66 |
<div class="bookly-margin-top-xlg">
|
71 |
<div id="bookly-fc-wrapper" class="bookly-calendar">
|
72 |
<div class="bookly-js-calendar-element"></div>
|
73 |
</div>
|
74 |
+
<?php Components\Dialogs\Appointment\Edit\Dialog::render() ?>
|
75 |
+
<?php Proxy\Shared::renderAddOnsComponents() ?>
|
76 |
+
<?php elseif( Bookly\Lib\Config::proActive() ) : ?>
|
77 |
+
<?php Components\Notices\Proxy\Pro::renderWelcome() ?>
|
78 |
<?php else : ?>
|
79 |
<div class="well">
|
80 |
+
<div class="h1"><?php esc_html_e( 'Welcome to Bookly and thank you for your choice!', 'bookly' ) ?></div>
|
81 |
+
<h3><?php esc_html_e( 'Bookly will simplify the booking process for your customers. This plugin creates another touchpoint to convert your visitors into customers. With Bookly your clients can see your availability, pick the services you provide, book them online and much more.', 'bookly' ) ?></h3>
|
82 |
+
<p><?php esc_html_e( 'To start using Bookly, you need to set up the services you provide and specify the staff members who will provide those services.', 'bookly' ) ?></p>
|
|
|
83 |
<ol>
|
84 |
+
<li><?php esc_html_e( 'Add a staff member (you can add only one service provider with a free version of Bookly).', 'bookly' ) ?></li>
|
85 |
+
<li><?php esc_html_e( 'Add services you provide (up to five with a free version of Bookly) and assign them to a staff member.', 'bookly' ) ?></li>
|
86 |
+
<li><?php esc_html_e( 'Go to Posts/Pages and click on the “Add Bookly booking form” button in the page editor to publish the booking form on your website.', 'bookly' ) ?></li>
|
87 |
</ol>
|
88 |
+
<p><?php printf( __( 'Bookly can boost your sales and scale together with your business. Get more features and remove the limits by upgrading to the paid version with the <a href="%s" target="_blank">Bookly Pro add-on</a>, which allows you to use a vast number of additional features and settings for booking services, install other add-ons for Bookly, and includes six months of customer support.', 'bookly' ), Common::prepareUrlReferrers( 'https://codecanyon.net/item/bookly/7226091?ref=ladela', 'welcome' ) ) ?></p>
|
89 |
<hr>
|
90 |
+
<a class="btn btn-success" href="<?php echo Common::escAdminUrl( Backend\Staff\Ajax::pageSlug() ) ?>">
|
91 |
+
<?php esc_html_e( 'Add Staff Members', 'bookly' ) ?>
|
92 |
</a>
|
93 |
+
<a class="btn btn-success" href="<?php echo Common::escAdminUrl( Backend\Services\Ajax::pageSlug() ) ?>">
|
94 |
+
<?php esc_html_e( 'Add Services', 'bookly' ) ?>
|
95 |
+
</a>
|
96 |
+
<a class="btn btn-success" href="<?php echo Common::prepareUrlReferrers( 'https://codecanyon.net/item/bookly/7226091?ref=ladela', 'welcome' ) ?>" target="_blank">
|
97 |
+
<?php esc_html_e( 'Try Bookly Pro add-on', 'bookly' ) ?>
|
98 |
</a>
|
99 |
</div>
|
100 |
<?php endif ?>
|
102 |
</div>
|
103 |
</div>
|
104 |
|
105 |
+
<?php Components\Dialogs\Appointment\Delete\Dialog::render() ?>
|
106 |
</div>
|
107 |
</div>
|
backend/modules/customers/Ajax.php
ADDED
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Customers
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @inheritdoc
|
14 |
+
*/
|
15 |
+
protected static function permissions()
|
16 |
+
{
|
17 |
+
return array( '_default' => 'user' );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Get list of customers.
|
22 |
+
*/
|
23 |
+
public static function getCustomers()
|
24 |
+
{
|
25 |
+
global $wpdb;
|
26 |
+
|
27 |
+
$columns = self::parameter( 'columns' );
|
28 |
+
$order = self::parameter( 'order' );
|
29 |
+
$filter = self::parameter( 'filter' );
|
30 |
+
|
31 |
+
$query = Lib\Entities\Customer::query( 'c' );
|
32 |
+
|
33 |
+
$total = $query->count();
|
34 |
+
|
35 |
+
$select = 'SQL_CALC_FOUND_ROWS c.*,
|
36 |
+
(
|
37 |
+
SELECT MAX(a.start_date) FROM ' . Lib\Entities\Appointment::getTableName() . ' a
|
38 |
+
LEFT JOIN ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca ON ca.appointment_id = a.id
|
39 |
+
WHERE ca.customer_id = c.id
|
40 |
+
) AS last_appointment,
|
41 |
+
(
|
42 |
+
SELECT COUNT(DISTINCT ca.appointment_id) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca
|
43 |
+
WHERE ca.customer_id = c.id
|
44 |
+
) AS total_appointments,
|
45 |
+
(
|
46 |
+
SELECT SUM(p.total) FROM ' . Lib\Entities\Payment::getTableName() . ' p
|
47 |
+
WHERE p.id IN (
|
48 |
+
SELECT DISTINCT ca.payment_id FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca
|
49 |
+
WHERE ca.customer_id = c.id
|
50 |
+
)
|
51 |
+
) AS payments,
|
52 |
+
wpu.display_name AS wp_user';
|
53 |
+
|
54 |
+
$select = Proxy\CustomerGroups::prepareCustomerSelect( $select );
|
55 |
+
|
56 |
+
$query
|
57 |
+
->select( $select )
|
58 |
+
->tableJoin( $wpdb->users, 'wpu', 'wpu.ID = c.wp_user_id' )
|
59 |
+
->groupBy( 'c.id' );
|
60 |
+
|
61 |
+
$query = Proxy\CustomerGroups::prepareCustomerQuery( $query );
|
62 |
+
|
63 |
+
if ( $filter != '' ) {
|
64 |
+
$search_value = Lib\Query::escape( $filter );
|
65 |
+
$query
|
66 |
+
->whereLike( 'c.full_name', "%{$search_value}%" )
|
67 |
+
->whereLike( 'c.phone', "%{$search_value}%", 'OR' )
|
68 |
+
->whereLike( 'c.email', "%{$search_value}%", 'OR' )
|
69 |
+
->whereLike( 'c.info_fields', "%{$search_value}%", 'OR' )
|
70 |
+
;
|
71 |
+
}
|
72 |
+
|
73 |
+
foreach ( $order as $sort_by ) {
|
74 |
+
$query
|
75 |
+
->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
76 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
77 |
+
}
|
78 |
+
|
79 |
+
$query->limit( self::parameter( 'length' ) )->offset( self::parameter( 'start' ) );
|
80 |
+
|
81 |
+
$data = array();
|
82 |
+
foreach ( $query->fetchArray() as $row ) {
|
83 |
+
|
84 |
+
$address = Lib\Proxy\Pro::getFullAddressByCustomerData( $row );
|
85 |
+
|
86 |
+
$customer_data = array(
|
87 |
+
'id' => $row['id'],
|
88 |
+
'wp_user_id' => $row['wp_user_id'],
|
89 |
+
'wp_user' => $row['wp_user'],
|
90 |
+
'facebook_id' => $row['facebook_id'],
|
91 |
+
'group_id' => $row['group_id'],
|
92 |
+
'full_name' => $row['full_name'],
|
93 |
+
'first_name' => $row['first_name'],
|
94 |
+
'last_name' => $row['last_name'],
|
95 |
+
'phone' => $row['phone'],
|
96 |
+
'email' => $row['email'],
|
97 |
+
'country' => $row['country'],
|
98 |
+
'state' => $row['state'],
|
99 |
+
'postcode' => $row['postcode'],
|
100 |
+
'city' => $row['city'],
|
101 |
+
'street' => $row['street'],
|
102 |
+
'street_number' => $row['street_number'],
|
103 |
+
'additional_address' => $row['additional_address'],
|
104 |
+
'address' => $address,
|
105 |
+
'notes' => $row['notes'],
|
106 |
+
'birthday' => $row['birthday'] !== null && date_create( $row['birthday'] )->format( 'Y' ) == '0000' ? date_create( $row['birthday'] )->modify( '+ 1900 years' )->format( 'Y-m-d' ) : $row['birthday'],
|
107 |
+
'last_appointment' => $row['last_appointment'] ? Lib\Utils\DateTime::formatDateTime( $row['last_appointment'] ) : '',
|
108 |
+
'total_appointments' => $row['total_appointments'],
|
109 |
+
'payments' => Lib\Utils\Price::format( $row['payments'] ),
|
110 |
+
);
|
111 |
+
|
112 |
+
$customer_data = Proxy\CustomerGroups::prepareCustomerListData( $customer_data, $row );
|
113 |
+
$customer_data = Proxy\CustomerInformation::prepareCustomerListData( $customer_data, $row );
|
114 |
+
|
115 |
+
$data[] = $customer_data;
|
116 |
+
}
|
117 |
+
|
118 |
+
wp_send_json( array(
|
119 |
+
'draw' => ( int ) self::parameter( 'draw' ),
|
120 |
+
'recordsTotal' => $total,
|
121 |
+
'recordsFiltered' => ( int ) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
|
122 |
+
'data' => $data,
|
123 |
+
) );
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Delete customers.
|
128 |
+
*/
|
129 |
+
public static function deleteCustomers()
|
130 |
+
{
|
131 |
+
foreach ( self::parameter( 'data', array() ) as $id ) {
|
132 |
+
$customer = new Lib\Entities\Customer();
|
133 |
+
$customer->load( $id );
|
134 |
+
$customer->deleteWithWPUser( (bool) self::parameter( 'with_wp_user' ) );
|
135 |
+
}
|
136 |
+
wp_send_json_success();
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Merge customers.
|
141 |
+
*/
|
142 |
+
public static function mergeCustomers()
|
143 |
+
{
|
144 |
+
$target_id = self::parameter( 'target_id' );
|
145 |
+
$ids = self::parameter( 'ids', array() );
|
146 |
+
|
147 |
+
// Move appointments.
|
148 |
+
Lib\Entities\CustomerAppointment::query()
|
149 |
+
->update()
|
150 |
+
->set( 'customer_id', $target_id )
|
151 |
+
->whereIn( 'customer_id', $ids )
|
152 |
+
->execute();
|
153 |
+
|
154 |
+
// Let add-ons do their stuff.
|
155 |
+
Proxy\Shared::mergeCustomers( $target_id, $ids );
|
156 |
+
|
157 |
+
// Merge customer data.
|
158 |
+
$target_customer = Lib\Entities\Customer::find( $target_id );
|
159 |
+
foreach ( $ids as $id ) {
|
160 |
+
if ( $id != $target_id ) {
|
161 |
+
$customer = Lib\Entities\Customer::find( $id );
|
162 |
+
if ( ! $target_customer->getWpUserId() && $customer->getWpUserId() ) {
|
163 |
+
$target_customer->setWpUserId( $customer->getWpUserId() );
|
164 |
+
}
|
165 |
+
if ( ! $target_customer->getGroupId() ) {
|
166 |
+
$target_customer->setGroupId( $customer->getGroupId() );
|
167 |
+
}
|
168 |
+
if ( ! $target_customer->getFacebookId() ) {
|
169 |
+
$target_customer->setFacebookId( $customer->getFacebookId() );
|
170 |
+
}
|
171 |
+
if ( $target_customer->getFullName() == '' ) {
|
172 |
+
$target_customer->setFullName( $customer->getFullName() );
|
173 |
+
}
|
174 |
+
if ( $target_customer->getFirstName() == '' ) {
|
175 |
+
$target_customer->setFirstName( $customer->getFirstName() );
|
176 |
+
}
|
177 |
+
if ( $target_customer->getLastName() == '' ) {
|
178 |
+
$target_customer->setLastName( $customer->getLastName() );
|
179 |
+
}
|
180 |
+
if ( $target_customer->getPhone() == '' ) {
|
181 |
+
$target_customer->setPhone( $customer->getPhone() );
|
182 |
+
}
|
183 |
+
if ( $target_customer->getEmail() == '' ) {
|
184 |
+
$target_customer->setEmail( $customer->getEmail() );
|
185 |
+
}
|
186 |
+
if ( $target_customer->getBirthday() == '' ) {
|
187 |
+
$target_customer->setBirthday( $customer->getBirthday() );
|
188 |
+
}
|
189 |
+
if ( $target_customer->getCountry() == '' ) {
|
190 |
+
$target_customer->setCountry( $customer->getCountry() );
|
191 |
+
}
|
192 |
+
if ( $target_customer->getState() == '' ) {
|
193 |
+
$target_customer->setState( $customer->getState() );
|
194 |
+
}
|
195 |
+
if ( $target_customer->getPostcode() == '' ) {
|
196 |
+
$target_customer->setPostcode( $customer->getPostcode() );
|
197 |
+
}
|
198 |
+
if ( $target_customer->getCity() == '' ) {
|
199 |
+
$target_customer->setCity( $customer->getCity() );
|
200 |
+
}
|
201 |
+
if ( $target_customer->getStreet() == '' ) {
|
202 |
+
$target_customer->setStreet( $customer->getStreet() );
|
203 |
+
}
|
204 |
+
if ( $target_customer->getAdditionalAddress() == '' ) {
|
205 |
+
$target_customer->setAdditionalAddress( $customer->getAdditionalAddress() );
|
206 |
+
}
|
207 |
+
if ( $target_customer->getNotes() == '' ) {
|
208 |
+
$target_customer->setNotes( $customer->getNotes() );
|
209 |
+
}
|
210 |
+
// Delete merged customer.
|
211 |
+
$customer->delete();
|
212 |
+
}
|
213 |
+
$target_customer->save();
|
214 |
+
}
|
215 |
+
|
216 |
+
wp_send_json_success();
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Check if the current user has access to the action.
|
221 |
+
*
|
222 |
+
* @param string $action
|
223 |
+
* @return bool
|
224 |
+
*/
|
225 |
+
protected static function hasAccess( $action )
|
226 |
+
{
|
227 |
+
if ( parent::hasAccess( $action ) ) {
|
228 |
+
if ( ! Lib\Utils\Common::isCurrentUserAdmin() ) {
|
229 |
+
switch ( $action ) {
|
230 |
+
case 'getCustomers':
|
231 |
+
case 'deleteCustomers':
|
232 |
+
return Lib\Entities\Staff::query()
|
233 |
+
->where( 'wp_user_id', get_current_user_id() )
|
234 |
+
->count() > 0;
|
235 |
+
}
|
236 |
+
} else {
|
237 |
+
return true;
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
return false;
|
242 |
+
}
|
243 |
+
}
|
backend/modules/customers/Controller.php
DELETED
@@ -1,249 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Customers;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Customers
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-customers';
|
13 |
-
|
14 |
-
protected function getPermissions()
|
15 |
-
{
|
16 |
-
return array(
|
17 |
-
'executeSaveCustomer' => 'user',
|
18 |
-
);
|
19 |
-
}
|
20 |
-
|
21 |
-
public function index()
|
22 |
-
{
|
23 |
-
if ( $this->hasParameter( 'import-customers' ) ) {
|
24 |
-
$this->importCustomers();
|
25 |
-
}
|
26 |
-
|
27 |
-
$this->enqueueStyles( array(
|
28 |
-
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
29 |
-
'frontend' => array( 'css/ladda.min.css', ),
|
30 |
-
) );
|
31 |
-
|
32 |
-
$this->enqueueScripts( array(
|
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' ),
|
39 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
40 |
-
),
|
41 |
-
'module' => array(
|
42 |
-
'js/customers.js' => array( 'bookly-datatables.min.js', 'bookly-ng-customer_dialog.js' ),
|
43 |
-
),
|
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' ) ),
|
52 |
-
'zeroRecords' => __( 'No customers found.', 'bookly' ),
|
53 |
-
'processing' => __( 'Processing...', 'bookly' ),
|
54 |
-
'edit_customer' => __( 'Edit customer', 'bookly' ),
|
55 |
-
'new_customer' => __( 'New customer', 'bookly' ),
|
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' );
|
63 |
-
}
|
64 |
-
|
65 |
-
/**
|
66 |
-
* Get list of customers.
|
67 |
-
*/
|
68 |
-
public function executeGetCustomers()
|
69 |
-
{
|
70 |
-
global $wpdb;
|
71 |
-
|
72 |
-
$columns = $this->getParameter( 'columns' );
|
73 |
-
$order = $this->getParameter( 'order' );
|
74 |
-
$filter = $this->getParameter( 'filter' );
|
75 |
-
|
76 |
-
$query = Lib\Entities\Customer::query( 'c' );
|
77 |
-
|
78 |
-
$total = $query->count();
|
79 |
-
|
80 |
-
$query
|
81 |
-
->select( 'SQL_CALC_FOUND_ROWS c.*,
|
82 |
-
(
|
83 |
-
SELECT MAX(a.start_date) FROM ' . Lib\Entities\Appointment::getTableName() . ' a
|
84 |
-
LEFT JOIN ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca ON ca.appointment_id = a.id
|
85 |
-
WHERE ca.customer_id = c.id
|
86 |
-
) AS last_appointment,
|
87 |
-
(
|
88 |
-
SELECT COUNT(DISTINCT ca.appointment_id) FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca
|
89 |
-
WHERE ca.customer_id = c.id
|
90 |
-
) AS total_appointments,
|
91 |
-
(
|
92 |
-
SELECT SUM(p.total) FROM ' . Lib\Entities\Payment::getTableName() . ' p
|
93 |
-
WHERE p.id IN (
|
94 |
-
SELECT DISTINCT ca.payment_id FROM ' . Lib\Entities\CustomerAppointment::getTableName() . ' ca
|
95 |
-
WHERE ca.customer_id = c.id
|
96 |
-
)
|
97 |
-
) AS payments,
|
98 |
-
wpu.display_name AS wp_user' )
|
99 |
-
->tableJoin( $wpdb->users, 'wpu', 'wpu.ID = c.wp_user_id' )
|
100 |
-
->groupBy( 'c.id' );
|
101 |
-
|
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 |
-
}
|
109 |
-
|
110 |
-
foreach ( $order as $sort_by ) {
|
111 |
-
$query->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
112 |
-
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
113 |
-
}
|
114 |
-
|
115 |
-
$query->limit( $this->getParameter( 'length' ) )->offset( $this->getParameter( 'start' ) );
|
116 |
-
|
117 |
-
$data = array();
|
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 |
-
|
136 |
-
wp_send_json( array(
|
137 |
-
'draw' => ( int ) $this->getParameter( 'draw' ),
|
138 |
-
'recordsTotal' => $total,
|
139 |
-
'recordsFiltered' => ( int ) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
|
140 |
-
'data' => $data,
|
141 |
-
) );
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Create or edit a customer.
|
146 |
-
*/
|
147 |
-
public function executeSaveCustomer()
|
148 |
-
{
|
149 |
-
$response = array();
|
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 );
|
195 |
-
}
|
196 |
-
|
197 |
-
/**
|
198 |
-
* Import customers from CSV.
|
199 |
-
*/
|
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 |
-
}
|
228 |
-
}
|
229 |
-
}
|
230 |
-
|
231 |
-
/**
|
232 |
-
* Delete customers.
|
233 |
-
*/
|
234 |
-
public function executeDeleteCustomers()
|
235 |
-
{
|
236 |
-
foreach ( $this->getParameter( 'data', array() ) as $id ) {
|
237 |
-
$customer = new Lib\Entities\Customer();
|
238 |
-
$customer->load( $id );
|
239 |
-
$customer->deleteWithWPUser( (bool) $this->getParameter( 'with_wp_user' ) );
|
240 |
-
}
|
241 |
-
wp_send_json_success();
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* Export Customers to CSV
|
246 |
-
*/
|
247 |
-
public function executeExportCustomers() { exit; }
|
248 |
-
|
249 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customers/Page.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Customers
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
if ( self::hasParameter( 'import-customers' ) ) {
|
18 |
+
Proxy\Pro::importCustomers();
|
19 |
+
}
|
20 |
+
|
21 |
+
self::enqueueStyles( array(
|
22 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
23 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
24 |
+
) );
|
25 |
+
|
26 |
+
self::enqueueScripts( array(
|
27 |
+
'backend' => array(
|
28 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
29 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
30 |
+
),
|
31 |
+
'frontend' => array(
|
32 |
+
'js/spin.min.js' => array( 'jquery' ),
|
33 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
34 |
+
),
|
35 |
+
'module' => array(
|
36 |
+
'js/customers.js' => array( 'bookly-datatables.min.js', 'bookly-ng-customer.js' ),
|
37 |
+
),
|
38 |
+
) );
|
39 |
+
|
40 |
+
// Customer information fields.
|
41 |
+
$info_fields = (array) Lib\Proxy\CustomerInformation::getFieldsWhichMayHaveData();
|
42 |
+
|
43 |
+
wp_localize_script( 'bookly-customers.js', 'BooklyL10n', array(
|
44 |
+
'csrfToken' => Lib\Utils\Common::getCsrfToken(),
|
45 |
+
'first_last_name' => (int) Lib\Config::showFirstLastName(),
|
46 |
+
'groupsActive' => (int) Lib\Config::customerGroupsActive(),
|
47 |
+
'infoFields' => $info_fields,
|
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' ) ),
|
51 |
+
'zeroRecords' => __( 'No customers found.', 'bookly' ),
|
52 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
53 |
+
'edit_customer' => __( 'Edit customer', 'bookly' ),
|
54 |
+
'new_customer' => __( 'New customer', 'bookly' ),
|
55 |
+
'create_customer' => __( 'Create customer', 'bookly' ),
|
56 |
+
'save' => __( 'Save', 'bookly' ),
|
57 |
+
'search' => __( 'Quick search customer', 'bookly' ),
|
58 |
+
'proEnabled' => (int) Lib\Config::proActive(),
|
59 |
+
) );
|
60 |
+
|
61 |
+
self::renderTemplate( 'index', compact( 'info_fields' ) );
|
62 |
+
}
|
63 |
+
}
|
backend/modules/customers/proxy/CustomerGroups.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerGroups
|
8 |
+
* @package Bookly\Backend\Modules\Customers\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareCustomerListData( array $data, array $row ) Prepare 'Customer Groups' data in customers table.
|
11 |
+
* @method static Lib\Query prepareCustomerQuery( Lib\Query $query ) Prepare 'Customer Groups' query in customers table.
|
12 |
+
* @method static string prepareCustomerSelect( string $select ) Prepare 'Customer Groups' select in customers table.
|
13 |
+
* @method static array prepareExportTitles( array $titles ) Prepare 'Customer Groups' titles in customers export.
|
14 |
+
* @method static void renderExportDialogRow() Render 'Customer Group' row in export customer dialog.
|
15 |
+
* @method static void renderCustomerTableHeader() Render 'Customer Group' in customers table.
|
16 |
+
*/
|
17 |
+
abstract class CustomerGroups extends Lib\Base\Proxy
|
18 |
+
{
|
19 |
+
|
20 |
+
}
|
backend/modules/customers/proxy/CustomerInformation.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerInformation
|
8 |
+
* @package Bookly\Backend\Modules\Customers\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareCustomerListData( array $customer_data, array $row ) Prepare customer info fields for customers list.
|
11 |
+
*/
|
12 |
+
abstract class CustomerInformation extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/customers/proxy/Pro.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Customers\Proxy
|
9 |
+
*
|
10 |
+
* @method static void importCustomers() Import customers from CSV.
|
11 |
+
* @method static void renderCustomerAddressTableHeader() Render 'address' column header.
|
12 |
+
* @method static void renderImportButton() Render import button.
|
13 |
+
* @method static void renderExportButton() Render export button.
|
14 |
+
* @method static void renderImportDialog() Render import dialog.
|
15 |
+
* @method static void renderExportDialog( array $info_fields ) Render export dialog.
|
16 |
+
*/
|
17 |
+
abstract class Pro extends Lib\Base\Proxy
|
18 |
+
{
|
19 |
+
|
20 |
+
}
|
backend/modules/customers/proxy/Shared.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Customers\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Customers\Proxy
|
9 |
+
*
|
10 |
+
* @method static void mergeCustomers( int $target_id, array $ids ) Merge customers.
|
11 |
+
*/
|
12 |
+
abstract class Shared extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/customers/resources/js/customers.js
CHANGED
@@ -1,72 +1,109 @@
|
|
1 |
jQuery(function($) {
|
2 |
|
3 |
var
|
4 |
-
$
|
5 |
-
$
|
6 |
-
$
|
7 |
-
$
|
8 |
-
$
|
9 |
-
$
|
10 |
-
$
|
11 |
-
$
|
12 |
-
$
|
13 |
-
$
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
row
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
/**
|
19 |
* Init DataTables.
|
20 |
*/
|
21 |
-
var dt = $
|
22 |
-
order: [[
|
23 |
-
info: false,
|
24 |
-
searching: false,
|
25 |
lengthChange: false,
|
26 |
-
pageLength: 25,
|
27 |
-
pagingType: 'numbers',
|
28 |
-
processing: true,
|
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
|
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() },
|
49 |
-
{ data: 'notes', render: $.fn.dataTable.render.text() },
|
50 |
-
{ data: 'last_appointment' },
|
51 |
-
{ data: 'total_appointments' },
|
52 |
-
{ data: 'payments' },
|
53 |
{
|
54 |
responsivePriority: 1,
|
55 |
-
orderable: false,
|
56 |
-
searchable: false,
|
57 |
-
render: function (
|
58 |
return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</button>';
|
59 |
}
|
60 |
},
|
61 |
{
|
62 |
responsivePriority: 1,
|
63 |
-
orderable: false,
|
64 |
-
searchable: false,
|
65 |
-
render: function (
|
66 |
-
return '<input type="checkbox" value="' + row.id + '"
|
67 |
}
|
68 |
}
|
69 |
-
],
|
70 |
dom: "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
71 |
"<'row'<'col-sm-12'tr>>" +
|
72 |
"<'row pull-left'<'col-sm-12 bookly-margin-top-lg'p>>",
|
@@ -79,54 +116,66 @@ jQuery(function($) {
|
|
79 |
/**
|
80 |
* Select all customers.
|
81 |
*/
|
82 |
-
$
|
83 |
-
$
|
84 |
});
|
85 |
|
86 |
/**
|
87 |
* On customer select.
|
88 |
*/
|
89 |
-
$
|
90 |
-
$
|
|
|
91 |
});
|
92 |
|
93 |
/**
|
94 |
* Edit customer.
|
95 |
*/
|
96 |
-
$
|
97 |
row = dt.row($(this).closest('td'));
|
98 |
});
|
99 |
|
100 |
/**
|
101 |
* New customer.
|
102 |
*/
|
103 |
-
$
|
104 |
row = null;
|
105 |
});
|
106 |
|
107 |
/**
|
108 |
* On show modal.
|
109 |
*/
|
110 |
-
$
|
111 |
-
var $title = $
|
112 |
-
var $button = $
|
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 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
};
|
|
|
|
|
|
|
130 |
$title.text(BooklyL10n.new_customer);
|
131 |
$button.text(BooklyL10n.create_customer);
|
132 |
}
|
@@ -135,7 +184,11 @@ jQuery(function($) {
|
|
135 |
$scope.$apply(function ($scope) {
|
136 |
$scope.customer = customer;
|
137 |
setTimeout(function() {
|
138 |
-
|
|
|
|
|
|
|
|
|
139 |
}, 0);
|
140 |
});
|
141 |
});
|
@@ -143,24 +196,24 @@ jQuery(function($) {
|
|
143 |
/**
|
144 |
* Delete customers.
|
145 |
*/
|
146 |
-
$
|
147 |
-
if (
|
148 |
-
$
|
149 |
} else {
|
150 |
-
deleteCustomers(this,
|
151 |
}}
|
152 |
);
|
153 |
|
154 |
-
$
|
155 |
-
if ($
|
156 |
-
|
157 |
}
|
158 |
deleteCustomers(this, false);
|
159 |
});
|
160 |
|
161 |
-
$
|
162 |
-
if ($
|
163 |
-
|
164 |
}
|
165 |
deleteCustomers(this, true);
|
166 |
});
|
@@ -170,7 +223,7 @@ jQuery(function($) {
|
|
170 |
ladda.start();
|
171 |
|
172 |
var data = [];
|
173 |
-
var $checkboxes = $
|
174 |
$checkboxes.each(function () {
|
175 |
data.push(this.value);
|
176 |
});
|
@@ -180,14 +233,14 @@ jQuery(function($) {
|
|
180 |
type : 'POST',
|
181 |
data : {
|
182 |
action : 'bookly_delete_customers',
|
183 |
-
csrf_token : BooklyL10n.
|
184 |
data : data,
|
185 |
with_wp_user : with_wp_user ? 1 : 0
|
186 |
},
|
187 |
dataType : 'json',
|
188 |
success : function(response) {
|
189 |
ladda.stop();
|
190 |
-
$
|
191 |
if (response.success) {
|
192 |
dt.ajax.reload(null, false);
|
193 |
} else {
|
@@ -202,24 +255,122 @@ jQuery(function($) {
|
|
202 |
*/
|
203 |
$filter.on('keyup', function () { dt.ajax.reload(); });
|
204 |
|
205 |
-
|
206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
208 |
});
|
209 |
|
210 |
(function() {
|
211 |
var module = angular.module('customer', ['customerDialog']);
|
212 |
module.controller('customerCtrl', function($scope) {
|
213 |
$scope.customer = {
|
214 |
-
id
|
215 |
-
wp_user_id
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
};
|
224 |
$scope.saveCustomer = function(customer) {
|
225 |
jQuery('#bookly-customers-list').DataTable().ajax.reload(null, false);
|
1 |
jQuery(function($) {
|
2 |
|
3 |
var
|
4 |
+
$customersList = $('#bookly-customers-list'),
|
5 |
+
$mergeListContainer = $('#bookly-merge-list'),
|
6 |
+
$mergeList = $customersList.clone().prop('id', '').find('th:last').remove().end().appendTo($mergeListContainer),
|
7 |
+
$filter = $('#bookly-filter'),
|
8 |
+
$checkAllButton = $('#bookly-check-all'),
|
9 |
+
$customerDialog = $('#bookly-customer-dialog'),
|
10 |
+
$addButton = $('#bookly-add'),
|
11 |
+
$deleteButton = $('#bookly-delete'),
|
12 |
+
$deleteDialog = $('#bookly-delete-dialog'),
|
13 |
+
$deleteButtonNo = $('#bookly-delete-no'),
|
14 |
+
$deleteButtonYes = $('#bookly-delete-yes'),
|
15 |
+
$selectForMergeButton = $('#bookly-select-for-merge'),
|
16 |
+
$mergeWithButton = $('#bookly-merge-with'),
|
17 |
+
$mergeDialog = $('#bookly-merge-dialog'),
|
18 |
+
$mergeButton = $('#bookly-merge'),
|
19 |
+
$rememberChoice = $('#bookly-delete-remember-choice'),
|
20 |
+
rememberedChoice,
|
21 |
row
|
22 |
+
;
|
23 |
+
|
24 |
+
var columns = [
|
25 |
+
{data: 'full_name', render: $.fn.dataTable.render.text(), responsivePriority: 2, visible: BooklyL10n.first_last_name == 0},
|
26 |
+
{data: 'first_name', render: $.fn.dataTable.render.text(), responsivePriority: 2, visible: BooklyL10n.first_last_name == 1},
|
27 |
+
{data: 'last_name', render: $.fn.dataTable.render.text(), responsivePriority: 2, visible: BooklyL10n.first_last_name == 1},
|
28 |
+
{data: 'wp_user', render: $.fn.dataTable.render.text(), responsivePriority: 2}
|
29 |
+
];
|
30 |
+
if (BooklyL10n.groupsActive == 1) {
|
31 |
+
columns.push({data: 'group_name', render: $.fn.dataTable.render.text(), responsivePriority: 2});
|
32 |
+
}
|
33 |
+
columns = columns.concat([
|
34 |
+
{data: 'phone', render: $.fn.dataTable.render.text(), responsivePriority: 2},
|
35 |
+
{data: 'email', render: $.fn.dataTable.render.text(), responsivePriority: 2}
|
36 |
+
]);
|
37 |
+
BooklyL10n.infoFields.forEach(function (field, i) {
|
38 |
+
columns.push({
|
39 |
+
data: 'info_fields.' + i + '.value' + (field.type === 'checkboxes' ? '[, ]' : ''),
|
40 |
+
render: $.fn.dataTable.render.text(),
|
41 |
+
responsivePriority: 3,
|
42 |
+
orderable: false
|
43 |
+
});
|
44 |
+
});
|
45 |
+
columns = columns.concat([
|
46 |
+
{data: 'notes', render: $.fn.dataTable.render.text(), responsivePriority: 2},
|
47 |
+
{data: 'last_appointment', responsivePriority: 2},
|
48 |
+
{data: 'total_appointments', responsivePriority: 2},
|
49 |
+
{data: 'payments', responsivePriority: 2}
|
50 |
+
]);
|
51 |
+
|
52 |
+
if (BooklyL10n.proEnabled == 1){
|
53 |
+
columns = columns.concat([
|
54 |
+
{data: 'address', responsivePriority: 3, orderable: false},
|
55 |
+
{
|
56 |
+
data: 'facebook_id',
|
57 |
+
responsivePriority: 2,
|
58 |
+
render: function (data, type, row, meta) {
|
59 |
+
return data ? '<a href="https://www.facebook.com/app_scoped_user_id/' + data + '/" target="_blank"><span class="dashicons dashicons-facebook"></span></a>' : '';
|
60 |
+
}
|
61 |
+
}
|
62 |
+
]);
|
63 |
+
}
|
64 |
|
65 |
/**
|
66 |
* Init DataTables.
|
67 |
*/
|
68 |
+
var dt = $customersList.DataTable({
|
69 |
+
order : [[0, 'asc']],
|
70 |
+
info : false,
|
71 |
+
searching : false,
|
72 |
lengthChange: false,
|
73 |
+
pageLength : 25,
|
74 |
+
pagingType : 'numbers',
|
75 |
+
processing : true,
|
76 |
+
responsive : true,
|
77 |
+
serverSide : true,
|
78 |
+
ajax : {
|
79 |
url : ajaxurl,
|
80 |
type: 'POST',
|
81 |
data: function (d) {
|
82 |
return $.extend({}, d, {
|
83 |
+
action : 'bookly_get_customers',
|
84 |
+
csrf_token: BooklyL10n.csrfToken,
|
85 |
+
filter : $filter.val()
|
86 |
});
|
87 |
}
|
88 |
},
|
89 |
+
columns: columns.concat([
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
{
|
91 |
responsivePriority: 1,
|
92 |
+
orderable : false,
|
93 |
+
searchable : false,
|
94 |
+
render : function (data, type, row, meta) {
|
95 |
return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</button>';
|
96 |
}
|
97 |
},
|
98 |
{
|
99 |
responsivePriority: 1,
|
100 |
+
orderable : false,
|
101 |
+
searchable : false,
|
102 |
+
render : function (data, type, row, meta) {
|
103 |
+
return '<input type="checkbox" value="' + row.id + '" />';
|
104 |
}
|
105 |
}
|
106 |
+
]),
|
107 |
dom: "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
108 |
"<'row'<'col-sm-12'tr>>" +
|
109 |
"<'row pull-left'<'col-sm-12 bookly-margin-top-lg'p>>",
|
116 |
/**
|
117 |
* Select all customers.
|
118 |
*/
|
119 |
+
$checkAllButton.on('change', function () {
|
120 |
+
$customersList.find('tbody input:checkbox').prop('checked', this.checked);
|
121 |
});
|
122 |
|
123 |
/**
|
124 |
* On customer select.
|
125 |
*/
|
126 |
+
$customersList.on('change', 'tbody input:checkbox', function () {
|
127 |
+
$checkAllButton.prop('checked', $customersList.find('tbody input:not(:checked)').length == 0);
|
128 |
+
$mergeWithButton.prop('disabled', $customersList.find('tbody input:checked').length != 1);
|
129 |
});
|
130 |
|
131 |
/**
|
132 |
* Edit customer.
|
133 |
*/
|
134 |
+
$customersList.on('click', 'button', function () {
|
135 |
row = dt.row($(this).closest('td'));
|
136 |
});
|
137 |
|
138 |
/**
|
139 |
* New customer.
|
140 |
*/
|
141 |
+
$addButton.on('click', function () {
|
142 |
row = null;
|
143 |
});
|
144 |
|
145 |
/**
|
146 |
* On show modal.
|
147 |
*/
|
148 |
+
$customerDialog.on('show.bs.modal', function () {
|
149 |
+
var $title = $customerDialog.find('.modal-title');
|
150 |
+
var $button = $customerDialog.find('.modal-footer button:first');
|
151 |
var customer;
|
152 |
if (row) {
|
153 |
+
customer = $.extend(true, {}, row.data());
|
154 |
$title.text(BooklyL10n.edit_customer);
|
155 |
$button.text(BooklyL10n.save);
|
156 |
} else {
|
157 |
customer = {
|
158 |
+
id : '',
|
159 |
+
wp_user_id : '',
|
160 |
+
group_id : '',
|
161 |
+
full_name : '',
|
162 |
+
first_name : '',
|
163 |
+
last_name : '',
|
164 |
+
phone : '',
|
165 |
+
email : '',
|
166 |
+
country : '',
|
167 |
+
state : '',
|
168 |
+
postcode : '',
|
169 |
+
city : '',
|
170 |
+
street : '',
|
171 |
+
address : '',
|
172 |
+
info_fields : [],
|
173 |
+
notes : '',
|
174 |
+
birthday : ''
|
175 |
};
|
176 |
+
BooklyL10n.infoFields.forEach(function (field) {
|
177 |
+
customer.info_fields.push({id: field.id, value: field.type === 'checkboxes' ? [] : ''});
|
178 |
+
});
|
179 |
$title.text(BooklyL10n.new_customer);
|
180 |
$button.text(BooklyL10n.create_customer);
|
181 |
}
|
184 |
$scope.$apply(function ($scope) {
|
185 |
$scope.customer = customer;
|
186 |
setTimeout(function() {
|
187 |
+
if (BooklyL10nCustDialog.intlTelInput.enabled) {
|
188 |
+
$customerDialog.find('#phone').intlTelInput('setNumber', customer.phone);
|
189 |
+
} else {
|
190 |
+
$customerDialog.find('#phone').val(customer.phone);
|
191 |
+
}
|
192 |
}, 0);
|
193 |
});
|
194 |
});
|
196 |
/**
|
197 |
* Delete customers.
|
198 |
*/
|
199 |
+
$deleteButton.on('click', function () {
|
200 |
+
if (rememberedChoice === undefined) {
|
201 |
+
$deleteDialog.modal('show');
|
202 |
} else {
|
203 |
+
deleteCustomers(this, rememberedChoice);
|
204 |
}}
|
205 |
);
|
206 |
|
207 |
+
$deleteButtonNo.on('click', function () {
|
208 |
+
if ($rememberChoice.prop('checked')) {
|
209 |
+
rememberedChoice = false;
|
210 |
}
|
211 |
deleteCustomers(this, false);
|
212 |
});
|
213 |
|
214 |
+
$deleteButtonYes.on('click', function () {
|
215 |
+
if ($rememberChoice.prop('checked')) {
|
216 |
+
rememberedChoice = true;
|
217 |
}
|
218 |
deleteCustomers(this, true);
|
219 |
});
|
223 |
ladda.start();
|
224 |
|
225 |
var data = [];
|
226 |
+
var $checkboxes = $customersList.find('tbody input:checked');
|
227 |
$checkboxes.each(function () {
|
228 |
data.push(this.value);
|
229 |
});
|
233 |
type : 'POST',
|
234 |
data : {
|
235 |
action : 'bookly_delete_customers',
|
236 |
+
csrf_token : BooklyL10n.csrfToken,
|
237 |
data : data,
|
238 |
with_wp_user : with_wp_user ? 1 : 0
|
239 |
},
|
240 |
dataType : 'json',
|
241 |
success : function(response) {
|
242 |
ladda.stop();
|
243 |
+
$deleteDialog.modal('hide');
|
244 |
if (response.success) {
|
245 |
dt.ajax.reload(null, false);
|
246 |
} else {
|
255 |
*/
|
256 |
$filter.on('keyup', function () { dt.ajax.reload(); });
|
257 |
|
258 |
+
/**
|
259 |
+
* Merge list.
|
260 |
+
*/
|
261 |
+
var mdt = $mergeList.DataTable({
|
262 |
+
order : [[0, 'asc']],
|
263 |
+
info : false,
|
264 |
+
searching : false,
|
265 |
+
paging : false,
|
266 |
+
responsive : true,
|
267 |
+
columns: columns.concat([
|
268 |
+
{
|
269 |
+
responsivePriority: 1,
|
270 |
+
orderable : false,
|
271 |
+
searchable : false,
|
272 |
+
render : function (data, type, row, meta) {
|
273 |
+
return '<button type="button" class="btn btn-default"><i class="glyphicon glyphicon-remove"></i></button>';
|
274 |
+
}
|
275 |
+
}
|
276 |
+
]),
|
277 |
+
language: {
|
278 |
+
zeroRecords: BooklyL10n.zeroRecords
|
279 |
+
}
|
280 |
+
});
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Select for merge.
|
284 |
+
*/
|
285 |
+
$selectForMergeButton.on('click', function () {
|
286 |
+
var $checkboxes = $customersList.find('tbody input:checked');
|
287 |
+
|
288 |
+
if ($checkboxes.length) {
|
289 |
+
$checkboxes.each(function () {
|
290 |
+
var data = dt.row($(this).closest('td')).data();
|
291 |
+
if (mdt.rows().data().indexOf(data) < 0) {
|
292 |
+
mdt.row.add(data).draw();
|
293 |
+
}
|
294 |
+
this.checked = false;
|
295 |
+
}).trigger('change');
|
296 |
+
$mergeWithButton.show();
|
297 |
+
$mergeListContainer.show();
|
298 |
+
}
|
299 |
+
});
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Merge customers.
|
303 |
+
*/
|
304 |
+
$mergeButton.on('click', function () {
|
305 |
+
var ladda = Ladda.create(this);
|
306 |
+
ladda.start();
|
307 |
+
var ids = [];
|
308 |
+
mdt.rows().every(function () {
|
309 |
+
ids.push(this.data().id);
|
310 |
+
});
|
311 |
+
$.ajax({
|
312 |
+
url : ajaxurl,
|
313 |
+
type : 'POST',
|
314 |
+
data : {
|
315 |
+
action : 'bookly_merge_customers',
|
316 |
+
csrf_token : BooklyL10n.csrfToken,
|
317 |
+
target_id : $customersList.find('tbody input:checked').val(),
|
318 |
+
ids : ids
|
319 |
+
},
|
320 |
+
dataType : 'json',
|
321 |
+
success : function(response) {
|
322 |
+
ladda.stop();
|
323 |
+
$mergeDialog.modal('hide');
|
324 |
+
if (response.success) {
|
325 |
+
dt.ajax.reload(null, false);
|
326 |
+
mdt.clear();
|
327 |
+
$mergeListContainer.hide();
|
328 |
+
$mergeWithButton.hide();
|
329 |
+
} else {
|
330 |
+
alert(response.data.message);
|
331 |
+
}
|
332 |
+
}
|
333 |
+
});
|
334 |
});
|
335 |
+
|
336 |
+
/**
|
337 |
+
* Remove customer from merge list.
|
338 |
+
*/
|
339 |
+
$mergeList.on('click', 'button', function () {
|
340 |
+
mdt.row($(this).closest('td')).remove().draw();
|
341 |
+
var any = mdt.rows().any();
|
342 |
+
$mergeWithButton.toggle(any);
|
343 |
+
$mergeListContainer.toggle(any);
|
344 |
+
});
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Import & export customers.
|
348 |
+
*/
|
349 |
+
Ladda.bind('#bookly-import-customers-dialog button[type=submit]');
|
350 |
+
Ladda.bind('#bookly-export-customers-dialog button[type=submit]', {timeout: 2000});
|
351 |
});
|
352 |
|
353 |
(function() {
|
354 |
var module = angular.module('customer', ['customerDialog']);
|
355 |
module.controller('customerCtrl', function($scope) {
|
356 |
$scope.customer = {
|
357 |
+
id : '',
|
358 |
+
wp_user_id : '',
|
359 |
+
group_id : '',
|
360 |
+
full_name : '',
|
361 |
+
first_name : '',
|
362 |
+
last_name : '',
|
363 |
+
phone : '',
|
364 |
+
email : '',
|
365 |
+
country : '',
|
366 |
+
state : '',
|
367 |
+
postcode : '',
|
368 |
+
city : '',
|
369 |
+
street : '',
|
370 |
+
address : '',
|
371 |
+
info_fields : [],
|
372 |
+
notes : '',
|
373 |
+
birthday : ''
|
374 |
};
|
375 |
$scope.saveCustomer = function(customer) {
|
376 |
jQuery('#bookly-customers-list').DataTable().ajax.reload(null, false);
|
backend/modules/customers/templates/_import.php
DELETED
@@ -1,44 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div id="bookly-import-customers-dialog" class="modal fade" tabindex=-1 role="dialog">
|
3 |
-
<div class="modal-dialog">
|
4 |
-
<form enctype="multipart/form-data" action="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Customers\Controller::page_slug ) ?>" method="POST">
|
5 |
-
<div class="modal-content">
|
6 |
-
<div class="modal-header">
|
7 |
-
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
8 |
-
<div class="modal-title h2"><?php _e( 'Import', 'bookly' ) ?></div>
|
9 |
-
</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">
|
30 |
-
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
31 |
-
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
32 |
-
</select>
|
33 |
-
</div>
|
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>
|
41 |
-
</div>
|
42 |
-
</form>
|
43 |
-
</div>
|
44 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customers/templates/index.php
CHANGED
@@ -1,11 +1,16 @@
|
|
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
|
7 |
</div>
|
8 |
-
<?php
|
9 |
</div>
|
10 |
<div class="panel panel-default bookly-main">
|
11 |
<div class="panel-body">
|
@@ -15,15 +20,13 @@
|
|
15 |
<input class="form-control" type="text" id="bookly-filter" placeholder="<?php esc_attr_e( 'Quick search customer', 'bookly' ) ?>" />
|
16 |
</div>
|
17 |
</div>
|
18 |
-
<div class="col-md-8 form-inline bookly-margin-bottom-lg
|
|
|
|
|
|
|
|
|
19 |
<div class="form-group">
|
20 |
-
<button type="button" class="btn btn-
|
21 |
-
</div>
|
22 |
-
<div class="form-group">
|
23 |
-
<button type="button" class="btn btn-default bookly-btn-block-xs" data-toggle="modal" data-target="#bookly-import-customers-dialog"><i class="glyphicon glyphicon-import"></i> <?php _e( 'Import', 'bookly' ) ?></button>
|
24 |
-
</div>
|
25 |
-
<div class="form-group">
|
26 |
-
<button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New customer', 'bookly' ) ?></button>
|
27 |
</div>
|
28 |
</div>
|
29 |
</div>
|
@@ -31,29 +34,52 @@
|
|
31 |
<table id="bookly-customers-list" class="table table-striped" width="100%">
|
32 |
<thead>
|
33 |
<tr>
|
34 |
-
<th><?php
|
35 |
-
<th><?php
|
36 |
-
<th><?php
|
37 |
-
<th><?php
|
38 |
-
|
39 |
-
<th><?php
|
40 |
-
<th><?php
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
<th></th>
|
45 |
-
<th width="16"><input type="checkbox" id="bookly-check-all"
|
46 |
</tr>
|
47 |
</thead>
|
48 |
</table>
|
49 |
|
50 |
-
<div class="text-right bookly-margin-top-lg">
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
</div>
|
53 |
</div>
|
54 |
</div>
|
55 |
|
56 |
-
<?php
|
|
|
|
|
|
|
57 |
|
58 |
<div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
|
59 |
<div class="modal-dialog">
|
@@ -63,20 +89,40 @@
|
|
63 |
<div class="modal-title h2"><?php _e( 'Delete customers', 'bookly' ) ?></div>
|
64 |
</div>
|
65 |
<div class="modal-body">
|
66 |
-
<?php
|
67 |
<div class="checkbox">
|
68 |
<label>
|
69 |
-
<input id="bookly-delete-remember-choice" type="checkbox" /><?php
|
70 |
</label>
|
71 |
</div>
|
72 |
</div>
|
73 |
<div class="modal-footer">
|
74 |
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-no" data-spinner-size="40" data-style="zoom-in">
|
75 |
-
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php
|
76 |
</button>
|
77 |
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-yes" data-spinner-size="40" data-style="zoom-in">
|
78 |
-
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
</button>
|
|
|
80 |
</div>
|
81 |
</div>
|
82 |
</div>
|
@@ -84,7 +130,7 @@
|
|
84 |
|
85 |
<div ng-app="customer" ng-controller="customerCtrl">
|
86 |
<div customer-dialog=saveCustomer(customer) customer="customer"></div>
|
87 |
-
<?php \
|
88 |
</div>
|
89 |
</div>
|
90 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Dialogs;
|
4 |
+
use Bookly\Backend\Components\Support;
|
5 |
+
use Bookly\Backend\Modules\Customers\Proxy;
|
6 |
+
?>
|
7 |
<div id="bookly-tbs" class="wrap">
|
8 |
<div class="bookly-tbs-body">
|
9 |
<div class="page-header text-right clearfix">
|
10 |
<div class="bookly-page-title">
|
11 |
+
<?php esc_html_e( 'Customers', 'bookly' ) ?>
|
12 |
</div>
|
13 |
+
<?php Support\Buttons::render( $self::pageSlug() ) ?>
|
14 |
</div>
|
15 |
<div class="panel panel-default bookly-main">
|
16 |
<div class="panel-body">
|
20 |
<input class="form-control" type="text" id="bookly-filter" placeholder="<?php esc_attr_e( 'Quick search customer', 'bookly' ) ?>" />
|
21 |
</div>
|
22 |
</div>
|
23 |
+
<div class="col-md-8 form-inline bookly-margin-bottom-lg text-right">
|
24 |
+
<?php
|
25 |
+
Proxy\Pro::renderExportButton();
|
26 |
+
Proxy\Pro::renderImportButton();
|
27 |
+
?>
|
28 |
<div class="form-group">
|
29 |
+
<button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-plus"></i> <?php esc_html_e( 'New customer', 'bookly' ) ?></button>
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
</div>
|
31 |
</div>
|
32 |
</div>
|
34 |
<table id="bookly-customers-list" class="table table-striped" width="100%">
|
35 |
<thead>
|
36 |
<tr>
|
37 |
+
<th><?php esc_html_e( get_option( 'bookly_l10n_label_name' ) ) ?></th>
|
38 |
+
<th><?php esc_html_e( get_option( 'bookly_l10n_label_first_name' ) ) ?></th>
|
39 |
+
<th><?php esc_html_e( get_option( 'bookly_l10n_label_last_name' ) ) ?></th>
|
40 |
+
<th><?php esc_html_e( 'User', 'bookly' ) ?></th>
|
41 |
+
<?php Proxy\CustomerGroups::renderCustomerTableHeader() ?>
|
42 |
+
<th><?php esc_html_e( get_option( 'bookly_l10n_label_phone' ) ) ?></th>
|
43 |
+
<th><?php esc_html_e( get_option( 'bookly_l10n_label_email' ) ) ?></th>
|
44 |
+
<?php foreach ( $info_fields as $field ) : ?>
|
45 |
+
<th><?php echo $field->label ?></th>
|
46 |
+
<?php endforeach ?>
|
47 |
+
<th><?php esc_html_e( 'Notes', 'bookly' ) ?></th>
|
48 |
+
<th><?php esc_html_e( 'Last appointment', 'bookly' ) ?></th>
|
49 |
+
<th><?php esc_html_e( 'Total appointments', 'bookly' ) ?></th>
|
50 |
+
<th><?php esc_html_e( 'Payments', 'bookly' ) ?></th>
|
51 |
+
<?php Proxy\Pro::renderCustomerAddressTableHeader() ?>
|
52 |
+
<?php if ( \Bookly\Lib\Config::proActive() ) : ?>
|
53 |
+
<th>Facebook</th>
|
54 |
+
<?php endif ?>
|
55 |
<th></th>
|
56 |
+
<th width="16"><input type="checkbox" id="bookly-check-all" /></th>
|
57 |
</tr>
|
58 |
</thead>
|
59 |
</table>
|
60 |
|
61 |
+
<div class="text-right form-inline bookly-margin-top-lg">
|
62 |
+
<div class="form-group">
|
63 |
+
<button type="button" id="bookly-merge-with" class="btn btn-default" data-toggle="modal" data-target="#bookly-merge-dialog" disabled="disabled" style="display:none"><i class="glyphicon glyphicon-road"></i> <?php esc_html_e( 'Merge with', 'bookly' ) ?></button>
|
64 |
+
</div>
|
65 |
+
<div class="form-group">
|
66 |
+
<button type="button" id="bookly-select-for-merge" class="btn btn-default"><i class="glyphicon glyphicon-plus"></i> <?php esc_html_e( 'Select for merge', 'bookly' ) ?></button>
|
67 |
+
</div>
|
68 |
+
<div class="form-group">
|
69 |
+
<?php Buttons::renderDelete() ?>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
|
73 |
+
<div id="bookly-merge-list" class="bookly-margin-top-xlg" style="display:none">
|
74 |
+
<h4><?php esc_html_e( 'Merge list', 'bookly' ) ?></h4>
|
75 |
</div>
|
76 |
</div>
|
77 |
</div>
|
78 |
|
79 |
+
<?php
|
80 |
+
Proxy\Pro::renderImportDialog();
|
81 |
+
Proxy\Pro::renderExportDialog( $info_fields );
|
82 |
+
?>
|
83 |
|
84 |
<div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
|
85 |
<div class="modal-dialog">
|
89 |
<div class="modal-title h2"><?php _e( 'Delete customers', 'bookly' ) ?></div>
|
90 |
</div>
|
91 |
<div class="modal-body">
|
92 |
+
<?php esc_html_e( 'You are about to delete customers which may have WordPress accounts associated to them. Do you want to delete those accounts too (if there are any)?', 'bookly' ) ?>
|
93 |
<div class="checkbox">
|
94 |
<label>
|
95 |
+
<input id="bookly-delete-remember-choice" type="checkbox" /><?php esc_html_e( 'Remember my choice', 'bookly' ) ?>
|
96 |
</label>
|
97 |
</div>
|
98 |
</div>
|
99 |
<div class="modal-footer">
|
100 |
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-no" data-spinner-size="40" data-style="zoom-in">
|
101 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php esc_html_e( 'No, delete just customers', 'bookly' ) ?></span>
|
102 |
</button>
|
103 |
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-yes" data-spinner-size="40" data-style="zoom-in">
|
104 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php esc_html_e( 'Yes', 'bookly' ) ?></span>
|
105 |
+
</button>
|
106 |
+
</div>
|
107 |
+
</div>
|
108 |
+
</div>
|
109 |
+
</div>
|
110 |
+
|
111 |
+
<div id="bookly-merge-dialog" class="modal fade" tabindex=-1 role="dialog">
|
112 |
+
<div class="modal-dialog">
|
113 |
+
<div class="modal-content">
|
114 |
+
<div class="modal-header">
|
115 |
+
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
116 |
+
<div class="modal-title h2"><?php _e( 'Merge customers', 'bookly' ) ?></div>
|
117 |
+
</div>
|
118 |
+
<div class="modal-body">
|
119 |
+
<?php esc_html_e( 'You are about to merge customers from the merge list with the selected one. This will result in losing the merged customers and moving all their appointments to the selected customer. Are you sure you want to continue?', 'bookly' ) ?>
|
120 |
+
</div>
|
121 |
+
<div class="modal-footer">
|
122 |
+
<button type="button" class="btn btn-danger ladda-button" id="bookly-merge" data-spinner-size="40" data-style="zoom-in">
|
123 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-road"></i> <?php esc_html_e( 'Merge', 'bookly' ) ?></span>
|
124 |
</button>
|
125 |
+
<?php Buttons::renderCustom( null, 'btn-default btn-lg', __( 'Cancel', 'bookly' ), array( 'data-dismiss' => 'modal' ) ) ?>
|
126 |
</div>
|
127 |
</div>
|
128 |
</div>
|
130 |
|
131 |
<div ng-app="customer" ng-controller="customerCtrl">
|
132 |
<div customer-dialog=saveCustomer(customer) customer="customer"></div>
|
133 |
+
<?php Dialogs\Customer\Edit::render() ?>
|
134 |
</div>
|
135 |
</div>
|
136 |
</div>
|
backend/modules/debug/Ajax.php
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Debug;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Debug
|
9 |
+
*/
|
10 |
+
class Ajax extends Page
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Export database data.
|
14 |
+
*/
|
15 |
+
public static function exportData()
|
16 |
+
{
|
17 |
+
/** @var \wpdb $wpdb */
|
18 |
+
global $wpdb;
|
19 |
+
|
20 |
+
$result = array();
|
21 |
+
|
22 |
+
foreach ( apply_filters( 'bookly_plugins', array() ) as $plugin ) {
|
23 |
+
/** @var Lib\Base\Plugin $plugin */
|
24 |
+
$installer_class = $plugin::getRootNamespace() . '\Lib\Installer';
|
25 |
+
/** @var Lib\Base\Installer $installer */
|
26 |
+
$installer = new $installer_class();
|
27 |
+
|
28 |
+
foreach ( $plugin::getEntityClasses() as $entity_class ) {
|
29 |
+
$table_name = $entity_class::getTableName();
|
30 |
+
$result['entities'][ $entity_class ] = array(
|
31 |
+
'fields' => self::_getTableStructure( $table_name ),
|
32 |
+
'values' => $wpdb->get_results( 'SELECT * FROM ' . $table_name, ARRAY_N )
|
33 |
+
);
|
34 |
+
}
|
35 |
+
$plugin_prefix = $plugin::getPrefix();
|
36 |
+
$options_postfix = array( 'data_loaded', 'grace_start', 'db_version', 'installation_time' );
|
37 |
+
if ( $plugin_prefix != 'bookly_' ) {
|
38 |
+
$options_postfix[] = 'enabled';
|
39 |
+
}
|
40 |
+
foreach ( $options_postfix as $option ) {
|
41 |
+
$option_name = $plugin_prefix . $option;
|
42 |
+
$result['options'][ $option_name ] = get_option( $option_name );
|
43 |
+
}
|
44 |
+
|
45 |
+
$result['options'][ $plugin::getPurchaseCodeOption() ] = $plugin::getPurchaseCode();
|
46 |
+
foreach ( $installer->getOptions() as $option_name => $option_value ) {
|
47 |
+
$result['options'][ $option_name ] = get_option( $option_name );
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
header( 'Content-type: application/json' );
|
52 |
+
header( 'Content-Disposition: attachment; filename=bookly_db_export_' . date( 'YmdHis' ) . '.json' );
|
53 |
+
echo json_encode( $result );
|
54 |
+
|
55 |
+
exit ( 0 );
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Import database data.
|
60 |
+
*/
|
61 |
+
public static function importData()
|
62 |
+
{
|
63 |
+
/** @var \wpdb $wpdb */
|
64 |
+
global $wpdb;
|
65 |
+
|
66 |
+
if ( $file = $_FILES['import']['name'] ) {
|
67 |
+
$json = file_get_contents( $_FILES['import']['tmp_name'] );
|
68 |
+
if ( $json !== false) {
|
69 |
+
$wpdb->query( 'SET FOREIGN_KEY_CHECKS = 0' );
|
70 |
+
|
71 |
+
$data = json_decode( $json, true );
|
72 |
+
/** @var Lib\Base\Plugin[] $bookly_plugins */
|
73 |
+
$bookly_plugins = apply_filters( 'bookly_plugins', array() );
|
74 |
+
foreach ( array_merge( array( 'bookly-responsive-appointment-booking-tool', 'bookly-addon-pro' ), array_keys( $bookly_plugins ) ) as $slug ) {
|
75 |
+
if ( ! array_key_exists( $slug, $bookly_plugins ) ) {
|
76 |
+
continue;
|
77 |
+
}
|
78 |
+
/** @var Lib\Base\Plugin $plugin */
|
79 |
+
$plugin = $bookly_plugins[ $slug ];
|
80 |
+
unset( $bookly_plugins[ $slug ] );
|
81 |
+
$installer_class = $plugin::getRootNamespace() . '\Lib\Installer';
|
82 |
+
/** @var Lib\Base\Installer $installer */
|
83 |
+
$installer = new $installer_class();
|
84 |
+
|
85 |
+
// Drop all data and options.
|
86 |
+
$installer->removeData();
|
87 |
+
$installer->dropTables();
|
88 |
+
$installer->createTables();
|
89 |
+
|
90 |
+
// Insert tables data.
|
91 |
+
foreach ( $plugin::getEntityClasses() as $entity_class ) {
|
92 |
+
if ( isset ( $data['entities'][ $entity_class ]['values'][0] ) ) {
|
93 |
+
$table_name = $entity_class::getTableName();
|
94 |
+
$query = sprintf(
|
95 |
+
'INSERT INTO `%s` (`%s`) VALUES (%%s)',
|
96 |
+
$table_name,
|
97 |
+
implode( '`,`', $data['entities'][ $entity_class ]['fields'] )
|
98 |
+
);
|
99 |
+
$placeholders = array();
|
100 |
+
$values = array();
|
101 |
+
$counter = 0;
|
102 |
+
foreach ( $data['entities'][ $entity_class ]['values'] as $row ) {
|
103 |
+
$params = array();
|
104 |
+
foreach ( $row as $value ) {
|
105 |
+
if ( $value === null ) {
|
106 |
+
$params[] = 'NULL';
|
107 |
+
} else {
|
108 |
+
$params[] = '%s';
|
109 |
+
$values[] = $value;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
$placeholders[] = implode( ',', $params );
|
113 |
+
if ( ++ $counter > 50 ) {
|
114 |
+
// Flush.
|
115 |
+
$wpdb->query( $wpdb->prepare( sprintf( $query, implode( '),(', $placeholders ) ), $values ) );
|
116 |
+
$placeholders = array();
|
117 |
+
$values = array();
|
118 |
+
$counter = 0;
|
119 |
+
}
|
120 |
+
}
|
121 |
+
if ( ! empty ( $placeholders ) ) {
|
122 |
+
$wpdb->query( $wpdb->prepare( sprintf( $query, implode( '),(', $placeholders ) ), $values ) );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
// Insert options data.
|
128 |
+
foreach ( $installer->getOptions() as $option_name => $option_value ) {
|
129 |
+
add_option( $option_name, $data['options'][ $option_name ] );
|
130 |
+
}
|
131 |
+
|
132 |
+
$plugin_prefix = $plugin::getPrefix();
|
133 |
+
$options_postfix = array( 'data_loaded', 'grace_start', 'db_version' );
|
134 |
+
if ( $plugin_prefix != 'bookly_' ) {
|
135 |
+
$options_postfix[] = 'enabled';
|
136 |
+
}
|
137 |
+
foreach ( $options_postfix as $option ) {
|
138 |
+
$option_name = $plugin_prefix . $option;
|
139 |
+
add_option( $option_name, $data['options'][ $option_name ] );
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug&status=imported' ) );
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
header( 'Location: ' . admin_url( 'admin.php?page=bookly-debug' ) );
|
148 |
+
|
149 |
+
exit ( 0 );
|
150 |
+
}
|
151 |
+
}
|
backend/modules/debug/Controller.php
DELETED
@@ -1,274 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Debug;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Debug
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-debug';
|
13 |
-
|
14 |
-
const TABLE_STATUS_OK = 1;
|
15 |
-
const TABLE_STATUS_ERROR = 0;
|
16 |
-
const TABLE_STATUS_WARNING = 2;
|
17 |
-
|
18 |
-
/**
|
19 |
-
* Default action
|
20 |
-
*/
|
21 |
-
public function index()
|
22 |
-
{
|
23 |
-
$this->enqueueStyles( array(
|
24 |
-
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
25 |
-
'module' => array( 'css/style.css' ),
|
26 |
-
) );
|
27 |
-
|
28 |
-
$this->enqueueScripts( array(
|
29 |
-
'backend' => array( 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ) ),
|
30 |
-
'module' => array( 'js/debug.js' => array( 'jquery' ) ),
|
31 |
-
) );
|
32 |
-
|
33 |
-
$debug = array();
|
34 |
-
foreach ( Lib\Plugin::getEntityClasses() as $entity_class ) {
|
35 |
-
$tableName = $entity_class::getTableName();
|
36 |
-
$debug[ $tableName ] = array(
|
37 |
-
'fields' => null,
|
38 |
-
'constraints' => null,
|
39 |
-
'status' => null,
|
40 |
-
);
|
41 |
-
if ( $this->_tableExists( $tableName ) ) {
|
42 |
-
$tableStructure = $this->_getTableStructure( $tableName );
|
43 |
-
$tableConstraints = $this->_getTableConstraints( $tableName );
|
44 |
-
$entitySchema = $entity_class::getSchema();
|
45 |
-
$entityConstraints = $entity_class::getConstraints();
|
46 |
-
$debug[ $tableName ]['status'] = self::TABLE_STATUS_OK;
|
47 |
-
$debug[ $tableName ]['fields'] = array();
|
48 |
-
|
49 |
-
// Comparing model schema with real DB schema
|
50 |
-
foreach ( $entitySchema as $field => $data ) {
|
51 |
-
if ( in_array( $field, $tableStructure ) ) {
|
52 |
-
$debug[ $tableName ]['fields'][ $field ] = 1;
|
53 |
-
} else {
|
54 |
-
$debug[ $tableName ]['fields'][ $field ] = 0;
|
55 |
-
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
56 |
-
}
|
57 |
-
}
|
58 |
-
|
59 |
-
// Comparing model constraints with real DB constraints
|
60 |
-
foreach ( $entityConstraints as $constraint ) {
|
61 |
-
$key = $constraint['column_name'] . $constraint['referenced_table_name'] . $constraint['referenced_column_name'];
|
62 |
-
$debug[ $tableName ]['constraints'][ $key ] = $constraint;
|
63 |
-
if ( array_key_exists ( $key, $tableConstraints ) ) {
|
64 |
-
$debug[ $tableName ]['constraints'][ $key ]['status'] = 1;
|
65 |
-
} else {
|
66 |
-
$debug[ $tableName ]['constraints'][ $key ]['status'] = 0;
|
67 |
-
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
68 |
-
}
|
69 |
-
}
|
70 |
-
|
71 |
-
} else {
|
72 |
-
$debug[ $tableName ]['status'] = self::TABLE_STATUS_ERROR;
|
73 |
-
}
|
74 |
-
}
|
75 |
-
|
76 |
-
$import_status = $this->getParameter( 'status' );
|
77 |
-
$this->render( 'index', compact( 'debug', 'import_status' ) );
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Export database data.
|
82 |
-
*/
|
83 |
-
public function executeExportData()
|
84 |
-
{
|
85 |
-
/** @var \wpdb $wpdb */
|
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';
|
102 |
-
}
|
103 |
-
foreach ( $options_postfix as $option ) {
|
104 |
-
$option_name = $plugin_prefix . $option;
|
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 |
-
}
|
112 |
-
|
113 |
-
header( 'Content-type: application/json' );
|
114 |
-
header( 'Content-Disposition: attachment; filename=bookly_db_export_' . date( 'YmdHis' ) . '.json' );
|
115 |
-
echo json_encode( $result );
|
116 |
-
|
117 |
-
exit ( 0 );
|
118 |
-
}
|
119 |
-
|
120 |
-
/**
|
121 |
-
* Import database data.
|
122 |
-
*/
|
123 |
-
public function executeImportData()
|
124 |
-
{
|
125 |
-
/** @var \wpdb $wpdb */
|
126 |
-
global $wpdb;
|
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();
|
139 |
-
$installer->dropTables();
|
140 |
-
$installer->createTables();
|
141 |
-
|
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'] )
|
150 |
-
);
|
151 |
-
$placeholders = array();
|
152 |
-
$values = array();
|
153 |
-
$counter = 0;
|
154 |
-
foreach ( $data['entities'][ $entity_class ]['values'] as $row ) {
|
155 |
-
$params = array();
|
156 |
-
foreach ( $row as $value ) {
|
157 |
-
if ( $value === null ) {
|
158 |
-
$params[] = 'NULL';
|
159 |
-
} else {
|
160 |
-
$params[] = '%s';
|
161 |
-
$values[] = $value;
|
162 |
-
}
|
163 |
-
}
|
164 |
-
$placeholders[] = implode( ',', $params );
|
165 |
-
if ( ++ $counter > 50 ) {
|
166 |
-
// Flush.
|
167 |
-
$wpdb->query( $wpdb->prepare( sprintf( $query, implode( '),(', $placeholders ) ), $values ) );
|
168 |
-
$placeholders = array();
|
169 |
-
$values = array();
|
170 |
-
$counter = 0;
|
171 |
-
}
|
172 |
-
}
|
173 |
-
if ( ! empty ( $placeholders ) ) {
|
174 |
-
$wpdb->query( $wpdb->prepare( sprintf( $query, implode( '),(', $placeholders ) ), $values ) );
|
175 |
-
}
|
176 |
-
}
|
177 |
-
}
|
178 |
-
|
179 |
-
// Insert options data.
|
180 |
-
foreach ( $installer->getOptions() as $option_name => $option_value ) {
|
181 |
-
add_option( $option_name, $data['options'][ $option_name ] );
|
182 |
-
}
|
183 |
-
|
184 |
-
$plugin_prefix = Lib\Plugin::getPrefix();
|
185 |
-
$options_postfix = array( 'data_loaded', 'grace_start', 'db_version' );
|
186 |
-
if ( $plugin_prefix != 'bookly_' ) {
|
187 |
-
$options_postfix[] = 'enabled';
|
188 |
-
}
|
189 |
-
foreach ( $options_postfix as $option ) {
|
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' ) );
|
199 |
-
|
200 |
-
exit ( 0 );
|
201 |
-
}
|
202 |
-
|
203 |
-
/**
|
204 |
-
* Get table structure
|
205 |
-
*
|
206 |
-
* @param string $tableName
|
207 |
-
* @return array
|
208 |
-
*/
|
209 |
-
private function _getTableStructure( $tableName )
|
210 |
-
{
|
211 |
-
global $wpdb;
|
212 |
-
|
213 |
-
$tableStructure = array();
|
214 |
-
$results = $wpdb->get_results( 'DESCRIBE `' . $tableName . '`;' );
|
215 |
-
if ( $results ) {
|
216 |
-
foreach ( $results as $row ) {
|
217 |
-
$tableStructure[] = $row->Field;
|
218 |
-
}
|
219 |
-
}
|
220 |
-
|
221 |
-
return $tableStructure;
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* Get table constraints
|
226 |
-
*
|
227 |
-
* @param string $tableName
|
228 |
-
* @return array
|
229 |
-
*/
|
230 |
-
private function _getTableConstraints( $tableName )
|
231 |
-
{
|
232 |
-
global $wpdb;
|
233 |
-
|
234 |
-
$tableConstraints = array();
|
235 |
-
$results = $wpdb->get_results(
|
236 |
-
'SELECT
|
237 |
-
COLUMN_NAME,
|
238 |
-
CONSTRAINT_NAME,
|
239 |
-
REFERENCED_COLUMN_NAME,
|
240 |
-
REFERENCED_TABLE_NAME
|
241 |
-
FROM information_schema.KEY_COLUMN_USAGE
|
242 |
-
WHERE
|
243 |
-
TABLE_NAME = "' . $tableName . '"
|
244 |
-
AND CONSTRAINT_SCHEMA = SCHEMA()
|
245 |
-
AND CONSTRAINT_NAME <> "PRIMARY";'
|
246 |
-
);
|
247 |
-
if ( $results ) {
|
248 |
-
foreach ( $results as $row ) {
|
249 |
-
$constraint = array(
|
250 |
-
'column_name' => $row->COLUMN_NAME,
|
251 |
-
'referenced_table_name' => $row->REFERENCED_COLUMN_NAME,
|
252 |
-
'referenced_column_name' => $row->REFERENCED_TABLE_NAME,
|
253 |
-
);
|
254 |
-
$key = $row->COLUMN_NAME . $row->REFERENCED_TABLE_NAME . $row->REFERENCED_COLUMN_NAME;
|
255 |
-
$tableConstraints[ $key ] = $constraint;
|
256 |
-
}
|
257 |
-
}
|
258 |
-
|
259 |
-
return $tableConstraints;
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* Verifying if table exists
|
264 |
-
*
|
265 |
-
* @param string $tableName
|
266 |
-
* @return int
|
267 |
-
*/
|
268 |
-
private function _tableExists( $tableName )
|
269 |
-
{
|
270 |
-
global $wpdb;
|
271 |
-
|
272 |
-
return $wpdb->query( 'SHOW TABLES LIKE "' . $tableName . '"' );
|
273 |
-
}
|
274 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/debug/Page.php
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Debug;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Debug
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
const TABLE_STATUS_OK = 1;
|
13 |
+
const TABLE_STATUS_ERROR = 0;
|
14 |
+
const TABLE_STATUS_WARNING = 2;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Render page.
|
18 |
+
*/
|
19 |
+
public static function render()
|
20 |
+
{
|
21 |
+
self::enqueueStyles( array(
|
22 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
23 |
+
'module' => array( 'css/style.css' ),
|
24 |
+
) );
|
25 |
+
|
26 |
+
self::enqueueScripts( array(
|
27 |
+
'backend' => array( 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ) ),
|
28 |
+
'module' => array( 'js/debug.js' => array( 'jquery' ) ),
|
29 |
+
) );
|
30 |
+
|
31 |
+
$debug = array();
|
32 |
+
/** @var Lib\Base\Plugin $plugin */
|
33 |
+
foreach ( apply_filters( 'bookly_plugins', array() ) as $plugin ) {
|
34 |
+
foreach ( $plugin::getEntityClasses() as $entity_class ) {
|
35 |
+
$tableName = $entity_class::getTableName();
|
36 |
+
$debug[ $tableName ] = array(
|
37 |
+
'fields' => null,
|
38 |
+
'constraints' => null,
|
39 |
+
'status' => null,
|
40 |
+
);
|
41 |
+
if ( self::_tableExists( $tableName ) ) {
|
42 |
+
$tableStructure = self::_getTableStructure( $tableName );
|
43 |
+
$tableConstraints = self::_getTableConstraints( $tableName );
|
44 |
+
$entitySchema = $entity_class::getSchema();
|
45 |
+
$entityConstraints = $entity_class::getConstraints();
|
46 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_OK;
|
47 |
+
$debug[ $tableName ]['fields'] = array();
|
48 |
+
|
49 |
+
// Comparing model schema with real DB schema
|
50 |
+
foreach ( $entitySchema as $field => $data ) {
|
51 |
+
if ( in_array( $field, $tableStructure ) ) {
|
52 |
+
$debug[ $tableName ]['fields'][ $field ] = 1;
|
53 |
+
} else {
|
54 |
+
$debug[ $tableName ]['fields'][ $field ] = 0;
|
55 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
// Comparing model constraints with real DB constraints
|
60 |
+
foreach ( $entityConstraints as $constraint ) {
|
61 |
+
$key = $constraint['column_name'] . $constraint['referenced_table_name'] . $constraint['referenced_column_name'];
|
62 |
+
$debug[ $tableName ]['constraints'][ $key ] = $constraint;
|
63 |
+
if ( array_key_exists ( $key, $tableConstraints ) ) {
|
64 |
+
$debug[ $tableName ]['constraints'][ $key ]['status'] = 1;
|
65 |
+
} else {
|
66 |
+
$debug[ $tableName ]['constraints'][ $key ]['status'] = 0;
|
67 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
} else {
|
72 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_ERROR;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
$import_status = self::parameter( 'status' );
|
78 |
+
self::renderTemplate( 'index', compact( 'debug', 'import_status' ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Get table structure
|
83 |
+
*
|
84 |
+
* @param string $tableName
|
85 |
+
* @return array
|
86 |
+
*/
|
87 |
+
protected static function _getTableStructure( $tableName )
|
88 |
+
{
|
89 |
+
global $wpdb;
|
90 |
+
|
91 |
+
$tableStructure = array();
|
92 |
+
$results = $wpdb->get_results( 'DESCRIBE `' . $tableName . '`;' );
|
93 |
+
if ( $results ) {
|
94 |
+
foreach ( $results as $row ) {
|
95 |
+
$tableStructure[] = $row->Field;
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
return $tableStructure;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Get table constraints
|
104 |
+
*
|
105 |
+
* @param string $tableName
|
106 |
+
* @return array
|
107 |
+
*/
|
108 |
+
protected static function _getTableConstraints( $tableName )
|
109 |
+
{
|
110 |
+
global $wpdb;
|
111 |
+
|
112 |
+
$tableConstraints = array();
|
113 |
+
$results = $wpdb->get_results(
|
114 |
+
'SELECT
|
115 |
+
COLUMN_NAME,
|
116 |
+
CONSTRAINT_NAME,
|
117 |
+
REFERENCED_COLUMN_NAME,
|
118 |
+
REFERENCED_TABLE_NAME
|
119 |
+
FROM information_schema.KEY_COLUMN_USAGE
|
120 |
+
WHERE
|
121 |
+
TABLE_NAME = "' . $tableName . '"
|
122 |
+
AND CONSTRAINT_SCHEMA = SCHEMA()
|
123 |
+
AND CONSTRAINT_NAME <> "PRIMARY";'
|
124 |
+
);
|
125 |
+
if ( $results ) {
|
126 |
+
foreach ( $results as $row ) {
|
127 |
+
$constraint = array(
|
128 |
+
'column_name' => $row->COLUMN_NAME,
|
129 |
+
'referenced_table_name' => $row->REFERENCED_COLUMN_NAME,
|
130 |
+
'referenced_column_name' => $row->REFERENCED_TABLE_NAME,
|
131 |
+
);
|
132 |
+
$key = $row->COLUMN_NAME . $row->REFERENCED_TABLE_NAME . $row->REFERENCED_COLUMN_NAME;
|
133 |
+
$tableConstraints[ $key ] = $constraint;
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
return $tableConstraints;
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Verifying if table exists
|
142 |
+
*
|
143 |
+
* @param string $tableName
|
144 |
+
* @return int
|
145 |
+
*/
|
146 |
+
protected static function _tableExists( $tableName )
|
147 |
+
{
|
148 |
+
global $wpdb;
|
149 |
+
|
150 |
+
return $wpdb->query( 'SHOW TABLES LIKE "' . $tableName . '"' );
|
151 |
+
}
|
152 |
+
}
|
backend/modules/debug/templates/index.php
CHANGED
@@ -1,4 +1,6 @@
|
|
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">
|
@@ -15,7 +17,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 |
-
<?php
|
19 |
<button id="bookly-export" type="submit" class="btn btn-lg btn-success">
|
20 |
<span class="ladda-label">Export data</span>
|
21 |
</button>
|
@@ -23,7 +25,7 @@
|
|
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
|
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">
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
3 |
+
?>
|
4 |
<div id="bookly-tbs" class="wrap">
|
5 |
<div class="bookly-tbs-body">
|
6 |
<div class="page-header text-right clearfix">
|
17 |
<div class="panel-group" id="data-management">
|
18 |
<div class="bookly-data-button">
|
19 |
<form action="<?php echo admin_url( 'admin-ajax.php?action=bookly_export_data' ) ?>" method="POST">
|
20 |
+
<?php Inputs::renderCsrf() ?>
|
21 |
<button id="bookly-export" type="submit" class="btn btn-lg btn-success">
|
22 |
<span class="ladda-label">Export data</span>
|
23 |
</button>
|
25 |
</div>
|
26 |
<div class="bookly-data-button">
|
27 |
<form id="bookly_import" action="<?php echo admin_url( 'admin-ajax.php?action=bookly_import_data' ) ?>" method="POST" enctype="multipart/form-data">
|
28 |
+
<?php Inputs::renderCsrf() ?>
|
29 |
<div id="bookly-import" class="btn btn-lg btn-primary btn-file">
|
30 |
<span class="ladda-label">Import data</span>
|
31 |
<input type="file" id="bookly_import_file" name="import">
|
backend/modules/message/Controller.php
DELETED
@@ -1,100 +0,0 @@
|
|
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/messages/Ajax.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Messages;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Messages
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Get messages
|
14 |
+
*/
|
15 |
+
public static function getMessages()
|
16 |
+
{
|
17 |
+
$query = Lib\Entities\Message::query( 'm' );
|
18 |
+
$total = $query->count();
|
19 |
+
|
20 |
+
$query->select( 'm.created, m.subject, m.seen, m.body, m.message_id' )
|
21 |
+
->sortBy( 'm.seen, m.message_id' )->order( 'DESC' );
|
22 |
+
|
23 |
+
$query->limit( self::parameter( 'length' ) )->offset( self::parameter( 'start' ) );
|
24 |
+
|
25 |
+
$data = $query->fetchArray();
|
26 |
+
foreach ( $data as &$row ) {
|
27 |
+
$row['created'] = Lib\Utils\DateTime::formatDateTime( $row['created'] );
|
28 |
+
}
|
29 |
+
|
30 |
+
wp_send_json( array(
|
31 |
+
'draw' => ( int ) self::parameter( 'draw' ),
|
32 |
+
'recordsTotal' => $total,
|
33 |
+
'recordsFiltered' => count( $data ),
|
34 |
+
'data' => $data,
|
35 |
+
) );
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Mark all messages was read
|
40 |
+
*/
|
41 |
+
public static function markReadAllMessages()
|
42 |
+
{
|
43 |
+
$messages = Lib\Entities\Message::query( 'm' )->select( 'm.message_id' )->whereNot( 'm.seen', 1 )->fetchArray();
|
44 |
+
$message_ids = array();
|
45 |
+
foreach ( $messages as $message ) {
|
46 |
+
$message_ids[] = $message['message_id'];
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( $message_ids ) {
|
50 |
+
Lib\API::seenMessages( $message_ids );
|
51 |
+
}
|
52 |
+
wp_send_json_success();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Mark some massages was read
|
57 |
+
*/
|
58 |
+
public static function markReadMessages()
|
59 |
+
{
|
60 |
+
Lib\API::seenMessages( (array) self::parameter( 'message_ids' ) );
|
61 |
+
wp_send_json_success();
|
62 |
+
}
|
63 |
+
}
|
backend/modules/messages/Page.php
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Messages;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Messages
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
19 |
+
) );
|
20 |
+
|
21 |
+
self::enqueueScripts( array(
|
22 |
+
'backend' => array(
|
23 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
24 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
25 |
+
),
|
26 |
+
'module' => array( 'js/message.js' => array( 'jquery' ) ),
|
27 |
+
) );
|
28 |
+
|
29 |
+
wp_localize_script( 'bookly-message.js', 'BooklyL10n', array(
|
30 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
31 |
+
'datatable' => array(
|
32 |
+
'zeroRecords' => __( 'No records.', 'bookly' ),
|
33 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
34 |
+
'per_page' => __( 'messages', 'bookly' ),
|
35 |
+
'paginate' => array(
|
36 |
+
'first' => __( 'First', 'bookly' ),
|
37 |
+
'previous' => __( 'Previous', 'bookly' ),
|
38 |
+
'next' => __( 'Next', 'bookly' ),
|
39 |
+
'last' => __( 'Last', 'bookly' ),
|
40 |
+
)
|
41 |
+
)
|
42 |
+
) );
|
43 |
+
|
44 |
+
self::renderTemplate( 'index' );
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* @return int
|
49 |
+
*/
|
50 |
+
public static function getMessagesCount()
|
51 |
+
{
|
52 |
+
return Lib\Entities\Message::query()
|
53 |
+
->where( 'seen', 0 )
|
54 |
+
->count();
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Show 'Messages' submenu with counter inside Bookly main menu
|
59 |
+
*/
|
60 |
+
public static function addBooklyMenuItem()
|
61 |
+
{
|
62 |
+
$messages = __( 'Messages', 'bookly' );
|
63 |
+
$count = self::getMessagesCount();
|
64 |
+
if ( $count ) {
|
65 |
+
add_submenu_page( 'bookly-menu', $messages, sprintf( '%s <span class="update-plugins count-%d"><span class="update-count">%d</span></span>', $messages, $count, $count ), 'manage_options',
|
66 |
+
self::pageSlug(), function () { Page::render(); } );
|
67 |
+
} else {
|
68 |
+
add_submenu_page( 'bookly-menu', $messages, $messages, 'manage_options',
|
69 |
+
self::pageSlug(), function () { Page::render(); } );
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
}
|
backend/modules/{message → messages}/resources/js/message.js
RENAMED
File without changes
|
backend/modules/{message → messages}/templates/index.php
RENAMED
@@ -1,11 +1,13 @@
|
|
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 \
|
9 |
</div>
|
10 |
<div class="panel panel-default bookly-main">
|
11 |
<div class="panel-body">
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components;
|
3 |
+
?>
|
4 |
<div id="bookly-tbs" class="wrap">
|
5 |
<div class="bookly-tbs-body">
|
6 |
<div class="page-header text-right clearfix">
|
7 |
<div class="bookly-page-title">
|
8 |
<?php _e( 'Messages', 'bookly' ) ?>
|
9 |
</div>
|
10 |
+
<?php Components\Support\Buttons::render( $self::pageSlug() ) ?>
|
11 |
</div>
|
12 |
<div class="panel panel-default bookly-main">
|
13 |
<div class="panel-body">
|
backend/modules/notifications/Ajax.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Notifications
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Get notifications data.
|
14 |
+
*/
|
15 |
+
public static function getEmailNotificationsData()
|
16 |
+
{
|
17 |
+
$form = new Forms\Notifications( 'email' );
|
18 |
+
|
19 |
+
$bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
|
20 |
+
get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
|
21 |
+
|
22 |
+
$bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
23 |
+
get_option( 'admin_email' ) : get_option( 'bookly_email_sender' );
|
24 |
+
|
25 |
+
$notifications = array();
|
26 |
+
foreach ( $form->getData() as $notification ) {
|
27 |
+
if ( Lib\Config::proActive() || in_array( $notification['type'], Lib\Entities\Notification::$bookly_notifications['email'] ) ) {
|
28 |
+
$name = Lib\Entities\Notification::getNameIfExists( $notification['type'] );
|
29 |
+
if ( $name !== false ) {
|
30 |
+
if ( in_array( $notification['type'], Lib\Entities\Notification::getCustomNotificationTypes() ) && $notification['subject'] != '' ) {
|
31 |
+
// In window Test Email Notification
|
32 |
+
// for custom notification, subject is name.
|
33 |
+
$name = $notification['subject'];
|
34 |
+
}
|
35 |
+
$notifications[] = array(
|
36 |
+
'id' => $notification['id'],
|
37 |
+
'name' => $name,
|
38 |
+
'active' => $notification['active'],
|
39 |
+
);
|
40 |
+
}
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
$result = array(
|
45 |
+
'notifications' => $notifications,
|
46 |
+
'sender_email' => $bookly_email_sender,
|
47 |
+
'sender_name' => $bookly_email_sender_name,
|
48 |
+
'send_as' => get_option( 'bookly_email_send_as' ),
|
49 |
+
'reply_to_customers' => get_option( 'bookly_email_reply_to_customers' ),
|
50 |
+
);
|
51 |
+
|
52 |
+
wp_send_json_success( $result );
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Test email notifications.
|
57 |
+
*/
|
58 |
+
public static function testEmailNotifications()
|
59 |
+
{
|
60 |
+
$to_email = self::parameter( 'to_email' );
|
61 |
+
$sender_name = self::parameter( 'sender_name' );
|
62 |
+
$sender_email = self::parameter( 'sender_email' );
|
63 |
+
$send_as = self::parameter( 'send_as' );
|
64 |
+
$notifications = self::parameter( 'notifications' );
|
65 |
+
$reply_to_customers = self::parameter( 'reply_to_customers' );
|
66 |
+
|
67 |
+
// Change 'Content-Type' and 'Reply-To' for test email notification.
|
68 |
+
add_filter( 'bookly_email_headers', function ( $headers ) use ( $sender_name, $sender_email, $send_as, $reply_to_customers ) {
|
69 |
+
$headers = array();
|
70 |
+
if ( $send_as == 'html' ) {
|
71 |
+
$headers[] = 'Content-Type: text/html; charset=utf-8';
|
72 |
+
} else {
|
73 |
+
$headers[] = 'Content-Type: text/plain; charset=utf-8';
|
74 |
+
}
|
75 |
+
$headers[] = 'From: ' . $sender_name . ' <' . $sender_email . '>';
|
76 |
+
if ( $reply_to_customers ) {
|
77 |
+
$headers[] = 'Reply-To: ' . $sender_name . ' <' . $sender_email . '>';
|
78 |
+
}
|
79 |
+
|
80 |
+
return $headers;
|
81 |
+
}, 10, 1 );
|
82 |
+
|
83 |
+
Lib\Notifications\Sender::sendTestEmailNotifications( $to_email, $notifications, $send_as );
|
84 |
+
|
85 |
+
wp_send_json_success();
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Delete custom notification
|
90 |
+
*/
|
91 |
+
public static function deleteCustomNotification()
|
92 |
+
{
|
93 |
+
$id = self::parameter( 'id' );
|
94 |
+
Lib\Entities\Notification::query()
|
95 |
+
->delete()
|
96 |
+
->where( 'id', $id )
|
97 |
+
->whereIn( 'type', Lib\Entities\Notification::getCustomNotificationTypes() )
|
98 |
+
->execute();
|
99 |
+
|
100 |
+
wp_send_json_success();
|
101 |
+
}
|
102 |
+
}
|
backend/modules/notifications/Components.php
DELETED
@@ -1,357 +0,0 @@
|
|
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
DELETED
@@ -1,133 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Notifications;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Notifications
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-notifications';
|
13 |
-
|
14 |
-
public function index()
|
15 |
-
{
|
16 |
-
$this->enqueueStyles( array(
|
17 |
-
'frontend' => array( 'css/ladda.min.css' ),
|
18 |
-
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
19 |
-
) );
|
20 |
-
|
21 |
-
$this->enqueueScripts( array(
|
22 |
-
'backend' => array(
|
23 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
24 |
-
'js/angular.min.js',
|
25 |
-
'js/help.js' => array( 'jquery' ),
|
26 |
-
'js/alert.js' => array( 'jquery' ),
|
27 |
-
),
|
28 |
-
'module' => array(
|
29 |
-
'js/notification.js' => array( 'jquery' ),
|
30 |
-
'js/ng-app.js' => array( 'jquery', 'bookly-angular.min.js' ),
|
31 |
-
),
|
32 |
-
'frontend' => array(
|
33 |
-
'js/spin.min.js' => array( 'jquery' ),
|
34 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
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' );
|
81 |
-
|
82 |
-
$bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
83 |
-
get_option( 'admin_email' ) : get_option( 'bookly_email_sender' );
|
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 |
-
}
|
99 |
-
|
100 |
-
$result = array(
|
101 |
-
'notifications' => $notifications,
|
102 |
-
'sender_email' => $bookly_email_sender,
|
103 |
-
'sender_name' => $bookly_email_sender_name,
|
104 |
-
'send_as' => get_option( 'bookly_email_send_as' ),
|
105 |
-
'reply_to_customers' => get_option( 'bookly_email_reply_to_customers' ),
|
106 |
-
);
|
107 |
-
|
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/Page.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Notifications
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
19 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
20 |
+
) );
|
21 |
+
|
22 |
+
self::enqueueScripts( array(
|
23 |
+
'backend' => array(
|
24 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
25 |
+
'js/angular.min.js',
|
26 |
+
'js/help.js' => array( 'jquery' ),
|
27 |
+
'js/alert.js' => array( 'jquery' ),
|
28 |
+
),
|
29 |
+
'module' => array(
|
30 |
+
'js/notification.js' => array( 'jquery' ),
|
31 |
+
'js/ng-app.js' => array( 'jquery', 'bookly-angular.min.js' ),
|
32 |
+
),
|
33 |
+
'frontend' => array(
|
34 |
+
'js/spin.min.js' => array( 'jquery' ),
|
35 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
+
)
|
37 |
+
) );
|
38 |
+
$cron_reminder = (array) get_option( 'bookly_cron_reminder_times' );
|
39 |
+
$form = new Forms\Notifications( 'email' );
|
40 |
+
$alert = array( 'success' => array() );
|
41 |
+
$current_notification_id = null;
|
42 |
+
// Save action.
|
43 |
+
if ( ! empty ( $_POST ) ) {
|
44 |
+
if ( self::csrfTokenValid() ) {
|
45 |
+
$form->bind( self::postParameters() );
|
46 |
+
$form->save();
|
47 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
48 |
+
update_option( 'bookly_email_send_as', self::parameter( 'bookly_email_send_as' ) );
|
49 |
+
update_option( 'bookly_email_reply_to_customers', self::parameter( 'bookly_email_reply_to_customers' ) );
|
50 |
+
update_option( 'bookly_email_sender', self::parameter( 'bookly_email_sender' ) );
|
51 |
+
update_option( 'bookly_email_sender_name', self::parameter( 'bookly_email_sender_name' ) );
|
52 |
+
update_option( 'bookly_ntf_processing_interval', (int) self::parameter( 'bookly_ntf_processing_interval' ) );
|
53 |
+
foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder', 'client_birthday_greeting' ) as $type ) {
|
54 |
+
$cron_reminder[ $type ] = self::parameter( $type . '_cron_hour' );
|
55 |
+
}
|
56 |
+
foreach ( array( 'client_reminder_1st', 'client_reminder_2nd', 'client_reminder_3rd', ) as $type ) {
|
57 |
+
$cron_reminder[ $type ] = self::parameter( $type . '_cron_before_hour' );
|
58 |
+
}
|
59 |
+
update_option( 'bookly_cron_reminder_times', $cron_reminder );
|
60 |
+
$current_notification_id = self::parameter( 'new_notification_id' );
|
61 |
+
}
|
62 |
+
}
|
63 |
+
wp_localize_script( 'bookly-alert.js', 'BooklyL10n', array(
|
64 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
65 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
66 |
+
'alert' => $alert,
|
67 |
+
'current_notification_id' => $current_notification_id,
|
68 |
+
'sent_successfully' => __( 'Sent successfully.', 'bookly' ),
|
69 |
+
) );
|
70 |
+
foreach ( range( 1, 23 ) as $hours ) {
|
71 |
+
$bookly_ntf_processing_interval_values[] = array( $hours, Lib\Utils\DateTime::secondsToInterval( $hours * HOUR_IN_SECONDS ) );
|
72 |
+
}
|
73 |
+
self::renderTemplate( 'index', compact( 'form', 'cron_reminder', 'bookly_ntf_processing_interval_values' ) );
|
74 |
+
}
|
75 |
+
}
|
backend/modules/notifications/forms/Notifications.php
CHANGED
@@ -1,11 +1,13 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
|
|
|
|
5 |
|
6 |
/**
|
7 |
* Class Notifications
|
8 |
-
* @package
|
9 |
*/
|
10 |
class Notifications extends Lib\Base\Form
|
11 |
{
|
@@ -37,36 +39,35 @@ class Notifications extends Lib\Base\Form
|
|
37 |
|
38 |
public $gateway;
|
39 |
|
40 |
-
/** @var
|
41 |
protected $codes;
|
42 |
|
43 |
/**
|
44 |
* Notifications constructor.
|
45 |
*
|
46 |
-
* @param string
|
47 |
-
* @param \BooklyLite\Backend\Modules\Notifications\Components|\BooklyLite\Backend\Modules\Sms\Components $components
|
48 |
*/
|
49 |
-
public function __construct( $gateway = 'email'
|
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',
|
56 |
-
$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 = $
|
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 $
|
68 |
{
|
69 |
-
$this->data = $
|
70 |
}
|
71 |
|
72 |
/**
|
@@ -105,7 +106,7 @@ class Notifications extends Lib\Base\Form
|
|
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 |
}
|
@@ -156,21 +157,14 @@ class Notifications extends Lib\Base\Form
|
|
156 |
esc_textarea( $value )
|
157 |
);
|
158 |
} else {
|
159 |
-
|
160 |
-
'
|
161 |
-
|
162 |
-
|
163 |
-
'
|
164 |
-
|
165 |
-
|
166 |
-
',justifyright,justifyfull,|,link,unlink,|'.
|
167 |
-
',spellchecker,wp_fullscreen,wp_adv'
|
168 |
-
)
|
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 |
|
@@ -222,6 +216,17 @@ class Notifications extends Lib\Base\Form
|
|
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 );
|
@@ -238,6 +243,18 @@ class Notifications extends Lib\Base\Form
|
|
238 |
}
|
239 |
}
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
/**
|
242 |
* Render sending time.
|
243 |
*
|
@@ -260,7 +277,7 @@ class Notifications extends Lib\Base\Form
|
|
260 |
'<option value="%s" %s>%s</option>',
|
261 |
$hour,
|
262 |
selected( $cron_reminder[ $type ], $hour, false ),
|
263 |
-
sprintf( __( '%s before', 'bookly' ),
|
264 |
);
|
265 |
}, array_merge( range( 1, 24 ), range( 48, 336, 24 ) ) ) );
|
266 |
} else {
|
@@ -292,7 +309,7 @@ class Notifications extends Lib\Base\Form
|
|
292 |
*/
|
293 |
public function renderCodes( $notification_type )
|
294 |
{
|
295 |
-
$this->codes->
|
296 |
}
|
297 |
|
298 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications\Forms;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules\Notifications\Proxy;
|
6 |
+
use Bookly\Backend\Modules\Notifications\Lib\Codes;
|
7 |
|
8 |
/**
|
9 |
* Class Notifications
|
10 |
+
* @package Bookly\Backend\Modules\Notifications\Forms
|
11 |
*/
|
12 |
class Notifications extends Lib\Base\Form
|
13 |
{
|
39 |
|
40 |
public $gateway;
|
41 |
|
42 |
+
/** @var Codes */
|
43 |
protected $codes;
|
44 |
|
45 |
/**
|
46 |
* Notifications constructor.
|
47 |
*
|
48 |
+
* @param string $gateway
|
|
|
49 |
*/
|
50 |
+
public function __construct( $gateway = 'email' )
|
51 |
{
|
52 |
/*
|
53 |
* make Visual Mode as default (instead of Text Mode)
|
54 |
* allowed: tinymce - Visual Mode, html - Text Mode, test - no one Mode selected
|
55 |
*/
|
56 |
+
add_filter( 'wp_default_editor', function() { return 'tinymce'; } );
|
57 |
+
$this->types = Proxy\Shared::prepareNotificationTypes( $this->types );
|
58 |
$this->gateway = $gateway;
|
59 |
if ( ! Lib\Config::combinedNotificationsEnabled() ) {
|
60 |
$this->types['combined'] = array();
|
61 |
}
|
62 |
$this->types['custom'] = Lib\Entities\Notification::getCustomNotificationTypes();
|
63 |
+
$this->codes = new Codes( $gateway );
|
64 |
+
$this->setFields( array( 'id', 'active', 'type', 'subject', 'message', 'to_customer', 'to_staff', 'to_admin', 'attach_ics', 'attach_invoice', 'settings' ) );
|
65 |
$this->load();
|
66 |
}
|
67 |
|
68 |
+
public function bind( array $params = array(), array $files = array() )
|
69 |
{
|
70 |
+
$this->data = $params['notification'];
|
71 |
}
|
72 |
|
73 |
/**
|
106 |
$notifications = array();
|
107 |
foreach ( $this->types[ $group ] as $type ) {
|
108 |
foreach ( $this->data as $notification ) {
|
109 |
+
if ( $notification['type'] == $type && ( Lib\Config::proActive() || in_array( $notification['type'], Lib\Entities\Notification::$bookly_notifications[ $notification['gateway'] ] ) ) ) {
|
110 |
$notifications[] = $notification;
|
111 |
}
|
112 |
}
|
157 |
esc_textarea( $value )
|
158 |
);
|
159 |
} else {
|
160 |
+
printf(
|
161 |
+
'<div class="form-group">
|
162 |
+
<input type="hidden" name="%1$s" value="%2$s" class="bookly-js-message-input"/>
|
163 |
+
<div class="bookly-js-tinymce-message"></div>
|
164 |
+
</div>',
|
165 |
+
$name,
|
166 |
+
esc_attr( $value )
|
|
|
|
|
|
|
167 |
);
|
|
|
|
|
|
|
|
|
168 |
}
|
169 |
}
|
170 |
|
216 |
'client_reminder_2nd',
|
217 |
'client_reminder_3rd',
|
218 |
'client_follow_up',
|
219 |
+
// Recurring.
|
220 |
+
'client_pending_recurring_appointment',
|
221 |
+
'staff_pending_recurring_appointment',
|
222 |
+
'client_approved_recurring_appointment',
|
223 |
+
'staff_approved_recurring_appointment',
|
224 |
+
'client_cancelled_recurring_appointment',
|
225 |
+
'staff_cancelled_recurring_appointment',
|
226 |
+
'client_rejected_recurring_appointment',
|
227 |
+
'staff_rejected_recurring_appointment',
|
228 |
+
'client_waitlisted_recurring_appointment',
|
229 |
+
'staff_waitlisted_recurring_appointment',
|
230 |
) ) ) {
|
231 |
$id = $notification['id'];
|
232 |
$name = sprintf( 'notification[%d][attach_ics]', $id );
|
243 |
}
|
244 |
}
|
245 |
|
246 |
+
/**
|
247 |
+
* Render attach invoice file.
|
248 |
+
*
|
249 |
+
* @param array $notification
|
250 |
+
*/
|
251 |
+
public function renderAttachInvoice( array $notification )
|
252 |
+
{
|
253 |
+
if ( in_array( $notification['type'], array( 'client_pending_appointment', 'client_approved_appointment', 'client_follow_up', ) ) ) {
|
254 |
+
Proxy\Invoices::renderAttach( $notification );
|
255 |
+
}
|
256 |
+
}
|
257 |
+
|
258 |
/**
|
259 |
* Render sending time.
|
260 |
*
|
277 |
'<option value="%s" %s>%s</option>',
|
278 |
$hour,
|
279 |
selected( $cron_reminder[ $type ], $hour, false ),
|
280 |
+
sprintf( __( '%s before', 'bookly' ), Lib\Utils\DateTime::secondsToInterval( $hour * HOUR_IN_SECONDS ) )
|
281 |
);
|
282 |
}, array_merge( range( 1, 24 ), range( 48, 336, 24 ) ) ) );
|
283 |
} else {
|
309 |
*/
|
310 |
public function renderCodes( $notification_type )
|
311 |
{
|
312 |
+
$this->codes->render( $notification_type );
|
313 |
}
|
314 |
|
315 |
}
|
backend/modules/notifications/lib/Codes.php
ADDED
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications\Lib;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Lib\Entities\Notification;
|
6 |
+
use Bookly\Backend\Modules\Notifications\Proxy;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Codes
|
10 |
+
* @package Bookly\Backend\Modules\Notifications\Lib
|
11 |
+
*/
|
12 |
+
class Codes
|
13 |
+
{
|
14 |
+
/** @var string */
|
15 |
+
protected $type;
|
16 |
+
|
17 |
+
/** @var array */
|
18 |
+
protected $codes;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Constructor.
|
22 |
+
*
|
23 |
+
* @param string $type
|
24 |
+
*/
|
25 |
+
public function __construct( $type = 'email' )
|
26 |
+
{
|
27 |
+
$this->type = $type;
|
28 |
+
$this->codes = array(
|
29 |
+
'appointment' => array(
|
30 |
+
'appointment_date' => __( 'date of appointment', 'bookly' ),
|
31 |
+
'appointment_end_date' => __( 'end date of appointment', 'bookly' ),
|
32 |
+
'appointment_end_time' => __( 'end time of appointment', 'bookly' ),
|
33 |
+
'appointment_notes' => __( 'customer notes for appointment', 'bookly' ),
|
34 |
+
'appointment_time' => __( 'time of appointment', 'bookly' ),
|
35 |
+
'booking_number' => __( 'booking number', 'bookly' ),
|
36 |
+
),
|
37 |
+
'cart' => array(
|
38 |
+
'cart_info' => __( 'cart information', 'bookly' ),
|
39 |
+
'cart_info_c' => __( 'cart information with cancel', 'bookly' ),
|
40 |
+
),
|
41 |
+
'category' => array(
|
42 |
+
'category_name' => __( 'name of category', 'bookly' ),
|
43 |
+
),
|
44 |
+
'company' => array(
|
45 |
+
'company_address' => __( 'address of company', 'bookly' ),
|
46 |
+
'company_name' => __( 'name of company', 'bookly' ),
|
47 |
+
'company_phone' => __( 'company phone', 'bookly' ),
|
48 |
+
'company_website' => __( 'company web-site address', 'bookly' ),
|
49 |
+
),
|
50 |
+
'customer' => array(
|
51 |
+
'client_address' => __( 'address of client', 'bookly' ),
|
52 |
+
'client_email' => __( 'email of client', 'bookly' ),
|
53 |
+
'client_first_name' => __( 'first name of client', 'bookly' ),
|
54 |
+
'client_last_name' => __( 'last name of client', 'bookly' ),
|
55 |
+
'client_name' => __( 'full name of client', 'bookly' ),
|
56 |
+
'client_phone' => __( 'phone of client', 'bookly' ),
|
57 |
+
),
|
58 |
+
'customer_timezone' => array(
|
59 |
+
'client_timezone' => __( 'time zone of client', 'bookly' ),
|
60 |
+
),
|
61 |
+
'customer_appointment' => array(
|
62 |
+
'approve_appointment_url' => __( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ),
|
63 |
+
'cancel_appointment_confirm_url' => __( 'URL of cancel appointment link with confirmation (to use inside <a> tag)', 'bookly' ),
|
64 |
+
'cancel_appointment_url' => __( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ),
|
65 |
+
'cancellation_reason' => __( 'reason you mentioned while deleting appointment', 'bookly' ),
|
66 |
+
'google_calendar_url' => __( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ),
|
67 |
+
'number_of_persons' => __( 'number of persons', 'bookly' ),
|
68 |
+
'reject_appointment_url' => __( 'URL of reject appointment link (to use inside <a> tag)', 'bookly' ),
|
69 |
+
),
|
70 |
+
'payment' => array(
|
71 |
+
'payment_type' => __( 'payment type', 'bookly' ),
|
72 |
+
'total_price' => __( 'total price of booking (sum of all cart items after applying coupon)' ),
|
73 |
+
),
|
74 |
+
'service' => array(
|
75 |
+
'service_duration' => __( 'duration of service', 'bookly' ),
|
76 |
+
'service_info' => __( 'info of service', 'bookly' ),
|
77 |
+
'service_name' => __( 'name of service', 'bookly' ),
|
78 |
+
'service_price' => __( 'price of service', 'bookly' ),
|
79 |
+
),
|
80 |
+
'staff' => array(
|
81 |
+
'staff_email' => __( 'email of staff', 'bookly' ),
|
82 |
+
'staff_info' => __( 'info of staff', 'bookly' ),
|
83 |
+
'staff_name' => __( 'name of staff', 'bookly' ),
|
84 |
+
'staff_phone' => __( 'phone of staff', 'bookly' ),
|
85 |
+
),
|
86 |
+
'staff_agenda' => array(
|
87 |
+
'agenda_date' => __( 'agenda date', 'bookly' ),
|
88 |
+
'next_day_agenda' => __( 'staff agenda for next day', 'bookly' ),
|
89 |
+
'tomorrow_date' => __( 'date of next day', 'bookly' ),
|
90 |
+
),
|
91 |
+
'user_credentials' => array(
|
92 |
+
'new_password' => __( 'customer new password', 'bookly' ),
|
93 |
+
'new_username' => __( 'customer new username', 'bookly' ),
|
94 |
+
'site_address' => __( 'site address', 'bookly' ),
|
95 |
+
),
|
96 |
+
'rating' => array(),
|
97 |
+
);
|
98 |
+
|
99 |
+
if ( $type == 'email' ) {
|
100 |
+
// Only email.
|
101 |
+
$this->codes['company']['company_logo'] = __( 'company logo', 'bookly' );
|
102 |
+
$this->codes['customer_appointment']['cancel_appointment'] = __( 'cancel appointment link', 'bookly' );
|
103 |
+
$this->codes['staff']['staff_photo'] = __( 'photo of staff', 'bookly' );
|
104 |
+
}
|
105 |
+
|
106 |
+
// Add codes from add-ons.
|
107 |
+
$this->codes = Proxy\Shared::prepareNotificationCodes( $this->codes, $type );
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Render codes for given notification type.
|
112 |
+
*
|
113 |
+
* @param $notification_type
|
114 |
+
*/
|
115 |
+
public function render( $notification_type )
|
116 |
+
{
|
117 |
+
$codes = $this->_build( $notification_type );
|
118 |
+
|
119 |
+
ksort( $codes );
|
120 |
+
|
121 |
+
$tbody = '';
|
122 |
+
foreach ( $codes as $code => $description ) {
|
123 |
+
$tbody .= sprintf(
|
124 |
+
'<tr><td><input value="{%s}" readonly="readonly" onclick="this.select()" /> - %s</td></tr>',
|
125 |
+
$code,
|
126 |
+
esc_html( $description )
|
127 |
+
);
|
128 |
+
}
|
129 |
+
|
130 |
+
printf(
|
131 |
+
'<table class="bookly-codes bookly-js-codes-%s"><tbody>%s</tbody></table>',
|
132 |
+
$notification_type,
|
133 |
+
$tbody
|
134 |
+
);
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Build array of codes for given notification type.
|
139 |
+
*
|
140 |
+
* @param $notification_type
|
141 |
+
* @return array
|
142 |
+
*/
|
143 |
+
private function _build( $notification_type )
|
144 |
+
{
|
145 |
+
$codes = array();
|
146 |
+
|
147 |
+
switch ( $notification_type ) {
|
148 |
+
case Notification::TYPE_APPOINTMENT_START_TIME:
|
149 |
+
case Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED:
|
150 |
+
case Notification::TYPE_LAST_CUSTOMER_APPOINTMENT:
|
151 |
+
case Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED:
|
152 |
+
$codes = array_merge(
|
153 |
+
$this->codes['appointment'],
|
154 |
+
$this->codes['category'],
|
155 |
+
$this->codes['service'],
|
156 |
+
$this->codes['customer_appointment'],
|
157 |
+
$this->codes['customer'],
|
158 |
+
$this->codes['customer_timezone'],
|
159 |
+
$this->codes['staff'],
|
160 |
+
$this->codes['payment'],
|
161 |
+
$this->codes['company']
|
162 |
+
);
|
163 |
+
if ( Lib\Config::invoicesActive() &&
|
164 |
+
in_array( $notification_type, array(
|
165 |
+
Notification::TYPE_CUSTOMER_APPOINTMENT_CREATED,
|
166 |
+
Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED,
|
167 |
+
) )
|
168 |
+
) {
|
169 |
+
$codes = array_merge( $codes, $this->codes['invoice'] );
|
170 |
+
}
|
171 |
+
break;
|
172 |
+
case 'staff_agenda':
|
173 |
+
case Notification::TYPE_STAFF_DAY_AGENDA:
|
174 |
+
$codes = array_merge(
|
175 |
+
$this->codes['staff'],
|
176 |
+
$this->codes['staff_agenda'],
|
177 |
+
$this->codes['company']
|
178 |
+
);
|
179 |
+
break;
|
180 |
+
case 'client_birthday_greeting':
|
181 |
+
case Notification::TYPE_CUSTOMER_BIRTHDAY:
|
182 |
+
$codes = array_merge(
|
183 |
+
$this->codes['customer'],
|
184 |
+
$this->codes['company']
|
185 |
+
);
|
186 |
+
break;
|
187 |
+
case 'client_new_wp_user':
|
188 |
+
$codes = array_merge(
|
189 |
+
$this->codes['customer'],
|
190 |
+
$this->codes['user_credentials'],
|
191 |
+
$this->codes['company']
|
192 |
+
);
|
193 |
+
break;
|
194 |
+
case 'client_pending_appointment_cart':
|
195 |
+
case 'client_approved_appointment_cart':
|
196 |
+
$codes = array_merge(
|
197 |
+
$this->codes['cart'],
|
198 |
+
$this->codes['customer'],
|
199 |
+
$this->codes['customer_timezone'],
|
200 |
+
$this->codes['payment'],
|
201 |
+
$this->codes['company']
|
202 |
+
);
|
203 |
+
break;
|
204 |
+
case 'client_pending_appointment':
|
205 |
+
case 'staff_pending_appointment':
|
206 |
+
case 'client_approved_appointment':
|
207 |
+
case 'staff_approved_appointment':
|
208 |
+
case 'client_cancelled_appointment':
|
209 |
+
case 'staff_cancelled_appointment':
|
210 |
+
case 'client_rejected_appointment':
|
211 |
+
case 'staff_rejected_appointment':
|
212 |
+
case 'client_waitlisted_appointment':
|
213 |
+
case 'staff_waitlisted_appointment':
|
214 |
+
case 'client_reminder':
|
215 |
+
case 'client_reminder_1st':
|
216 |
+
case 'client_reminder_2nd':
|
217 |
+
case 'client_reminder_3rd':
|
218 |
+
$codes = array_merge(
|
219 |
+
$this->codes['appointment'],
|
220 |
+
$this->codes['category'],
|
221 |
+
$this->codes['service'],
|
222 |
+
$this->codes['customer_appointment'],
|
223 |
+
$this->codes['staff'],
|
224 |
+
$this->codes['customer'],
|
225 |
+
$this->codes['customer_timezone'],
|
226 |
+
$this->codes['payment'],
|
227 |
+
$this->codes['company']
|
228 |
+
);
|
229 |
+
if ( Lib\Config::invoicesActive() &&
|
230 |
+
in_array( $notification_type, array(
|
231 |
+
'client_pending_appointment',
|
232 |
+
'client_approved_appointment'
|
233 |
+
) )
|
234 |
+
) {
|
235 |
+
$codes = array_merge( $codes, $this->codes['invoice'] );
|
236 |
+
}
|
237 |
+
break;
|
238 |
+
case 'client_follow_up':
|
239 |
+
$codes = array_merge(
|
240 |
+
$this->codes['appointment'],
|
241 |
+
$this->codes['category'],
|
242 |
+
$this->codes['service'],
|
243 |
+
$this->codes['customer_appointment'],
|
244 |
+
$this->codes['staff'],
|
245 |
+
$this->codes['customer'],
|
246 |
+
$this->codes['customer_timezone'],
|
247 |
+
$this->codes['payment'],
|
248 |
+
$this->codes['company'],
|
249 |
+
$this->codes['rating'],
|
250 |
+
isset( $this->codes['invoice'] ) ? $this->codes['invoice'] : array()
|
251 |
+
);
|
252 |
+
break;
|
253 |
+
default:
|
254 |
+
$codes = Proxy\Shared::buildNotificationCodesList( $codes, $notification_type, $this->codes );
|
255 |
+
}
|
256 |
+
|
257 |
+
return $codes;
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* @param array $groups
|
262 |
+
* @param bool $echo
|
263 |
+
* @return string
|
264 |
+
*/
|
265 |
+
public function renderGroups( array $groups, $echo = true )
|
266 |
+
{
|
267 |
+
$codes = array();
|
268 |
+
foreach ( $groups as $group ) {
|
269 |
+
if ( array_key_exists( $group, $this->codes ) ) {
|
270 |
+
$codes = array_merge( $codes, $this->codes[ $group ] );
|
271 |
+
}
|
272 |
+
}
|
273 |
+
|
274 |
+
ksort( $codes );
|
275 |
+
|
276 |
+
$tbody = '';
|
277 |
+
foreach ( $codes as $code => $description ) {
|
278 |
+
$tbody .= sprintf(
|
279 |
+
'<tr><td><input value="{%s}" readonly="readonly" onclick="this.select()" /> - %s</td></tr>',
|
280 |
+
$code,
|
281 |
+
esc_html( $description )
|
282 |
+
);
|
283 |
+
}
|
284 |
+
|
285 |
+
$result = sprintf(
|
286 |
+
'<table class="bookly-codes"><tbody>%s</tbody></table>',
|
287 |
+
$tbody
|
288 |
+
);
|
289 |
+
|
290 |
+
if ( $echo ) {
|
291 |
+
echo $result;
|
292 |
+
}
|
293 |
+
|
294 |
+
return $result;
|
295 |
+
}
|
296 |
+
}
|
backend/modules/notifications/proxy/Invoices.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Notifications\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderAttach( array $notification ) Render checkbox for attaching invoice to notifications.
|
11 |
+
*/
|
12 |
+
abstract class Invoices extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
}
|
backend/modules/notifications/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules\Notifications\Forms;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Pro
|
9 |
+
* @package Bookly\Backend\Modules\Notifications\Proxy
|
10 |
+
*
|
11 |
+
* @method static void renderCustomEmailNotifications( Forms\Notifications $form ) Render custom email notifications.
|
12 |
+
*/
|
13 |
+
abstract class Pro extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/notifications/proxy/Shared.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Notifications\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules\Notifications\Forms;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Shared
|
9 |
+
* @package Bookly\Backend\Modules\Notifications\Proxy
|
10 |
+
*
|
11 |
+
* @method static array buildNotificationCodesList( array $codes, string $notification_type, array $codes_data ) Build array of codes to be displayed in notification template.
|
12 |
+
* @method static array prepareNotificationCodes( array $codes, string $type ) Alter codes for displaying in notification templates.
|
13 |
+
* @method static array prepareNotificationTypes( array $types ) Prepare notification types.
|
14 |
+
* @method static void renderEmailNotifications( Forms\Notifications $form ) Render email notification(s).
|
15 |
+
*/
|
16 |
+
abstract class Shared extends Lib\Base\Proxy
|
17 |
+
{
|
18 |
+
|
19 |
+
}
|
backend/modules/notifications/resources/js/ng-app.js
CHANGED
@@ -17,7 +17,7 @@
|
|
17 |
dataType : 'json',
|
18 |
success : function(response) {
|
19 |
if (response.success) {
|
20 |
-
ds.sender_name
|
21 |
ds.sender_email = response.data.sender_email;
|
22 |
ds.reply_to_customers = response.data.reply_to_customers;
|
23 |
ds.send_as = response.data.send_as;
|
@@ -97,7 +97,7 @@
|
|
97 |
};
|
98 |
angular.forEach($scope.dataSource.notifications, function(notification) {
|
99 |
if (notification.active == '1') {
|
100 |
-
data.notifications.push(notification.
|
101 |
}
|
102 |
});
|
103 |
jQuery.ajax({
|
17 |
dataType : 'json',
|
18 |
success : function(response) {
|
19 |
if (response.success) {
|
20 |
+
ds.sender_name = response.data.sender_name;
|
21 |
ds.sender_email = response.data.sender_email;
|
22 |
ds.reply_to_customers = response.data.reply_to_customers;
|
23 |
ds.send_as = response.data.send_as;
|
97 |
};
|
98 |
angular.forEach($scope.dataSource.notifications, function(notification) {
|
99 |
if (notification.active == '1') {
|
100 |
+
data.notifications.push(notification.id);
|
101 |
}
|
102 |
});
|
103 |
jQuery.ajax({
|
backend/modules/notifications/resources/js/notification.js
CHANGED
@@ -1,5 +1,9 @@
|
|
1 |
jQuery(function($) {
|
2 |
|
|
|
|
|
|
|
|
|
3 |
Ladda.bind( 'button[type=submit]' );
|
4 |
|
5 |
// menu fix for WP 3.8.1
|
@@ -12,6 +16,43 @@ jQuery(function($) {
|
|
12 |
$(this).parents('.panel-heading').next().collapse(this.checked ? 'show' : 'hide');
|
13 |
});
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
$('[data-toggle="popover"]').popover({
|
16 |
html: true,
|
17 |
placement: 'top',
|
@@ -19,22 +60,110 @@ 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 |
-
$
|
23 |
-
|
24 |
-
$(this).
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
-
|
|
|
|
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
});
|
35 |
|
36 |
-
$(
|
37 |
-
|
38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
});
|
|
|
|
|
40 |
});
|
1 |
jQuery(function($) {
|
2 |
|
3 |
+
var $custom_notifications = $('#bookly-js-custom-notifications'),
|
4 |
+
$tiny_mce_container = $('#bookly-js-tinymce-container'),
|
5 |
+
$container = $('.bookly-main');
|
6 |
+
|
7 |
Ladda.bind( 'button[type=submit]' );
|
8 |
|
9 |
// menu fix for WP 3.8.1
|
16 |
$(this).parents('.panel-heading').next().collapse(this.checked ? 'show' : 'hide');
|
17 |
});
|
18 |
|
19 |
+
$container.on('show.bs.collapse', '.panel', function () {
|
20 |
+
var $panel = $(this),
|
21 |
+
$old_panel = $('#bookly-js-tinymce-container').closest('.panel'),
|
22 |
+
message_id = $panel.find('.panel-collapse').attr('id');
|
23 |
+
|
24 |
+
$container.find('.panel .collapse.in').collapse('hide');
|
25 |
+
if ($old_panel.hasClass('bookly-js-collapse')) {
|
26 |
+
$old_panel.find('.bookly-js-message-input').val(tinymce.get('bookly-js-tinymce-area').getContent());
|
27 |
+
$old_panel.find('#bookly-js-tinymce-area-tmce').click();
|
28 |
+
}
|
29 |
+
tinymce.remove("#bookly-js-tinymce-area");
|
30 |
+
$tiny_mce_container.detach().appendTo('#' + message_id + ' .bookly-js-tinymce-message');
|
31 |
+
tinymce.init(tinyMCEPreInit.mceInit['bookly-js-tinymce-area']);
|
32 |
+
tinymce.get('bookly-js-tinymce-area').setContent($panel.find('.bookly-js-message-input').val());
|
33 |
+
});
|
34 |
+
|
35 |
+
$container.on('shown.bs.collapse', '.panel', function () {
|
36 |
+
var $panel = $(this),
|
37 |
+
$subject = $panel.find('input[id$="subject"]');
|
38 |
+
|
39 |
+
$subject.focus();
|
40 |
+
$('html, body').animate({
|
41 |
+
scrollTop: Math.max($panel.offset().top - 40, 0)
|
42 |
+
}, 1000);
|
43 |
+
});
|
44 |
+
|
45 |
+
$('#bookly-save').on('click', function (e) {
|
46 |
+
e.preventDefault();
|
47 |
+
var $panel = $('#bookly-js-tinymce-container').closest('.panel');
|
48 |
+
|
49 |
+
if ($panel.hasClass('bookly-js-collapse')) {
|
50 |
+
$panel.find('.bookly-js-message-input').val(tinymce.get('bookly-js-tinymce-area').getContent());
|
51 |
+
}
|
52 |
+
|
53 |
+
$('#bookly-save').closest('form').submit();
|
54 |
+
});
|
55 |
+
|
56 |
$('[data-toggle="popover"]').popover({
|
57 |
html: true,
|
58 |
placement: 'top',
|
60 |
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>'
|
61 |
});
|
62 |
|
63 |
+
$custom_notifications
|
64 |
+
.on('change', "select[name$='[type]']", function () {
|
65 |
+
var $panel = $(this).closest('.panel'),
|
66 |
+
$settings = $panel.find('.bookly-js-settings'),
|
67 |
+
$attach = $panel.find('.bookly-js-attach'),
|
68 |
+
value = $(this).find(':selected').val(),
|
69 |
+
set = $(this).find(':selected').data('set'),
|
70 |
+
to = $(this).find(':selected').data('to'),
|
71 |
+
showAttach = $(this).find(':selected').data('attach-show')||[]
|
72 |
+
;
|
73 |
|
74 |
+
$panel.find('table.bookly-codes').each(function () {
|
75 |
+
$(this).toggle($(this).hasClass('bookly-js-codes-' + value));
|
76 |
+
});
|
77 |
|
78 |
+
$.each(['customer', 'staff', 'admin'], function (index, value) {
|
79 |
+
$panel.find("[name$='[to_" + value + "]']").closest('.checkbox-inline').toggle(to.indexOf(value) != -1);
|
80 |
+
});
|
81 |
+
|
82 |
+
$attach.hide();
|
83 |
+
$.each(showAttach, function (index, value) {
|
84 |
+
$('.bookly-js-' + value, $panel).show();
|
85 |
+
});
|
86 |
+
|
87 |
+
$settings.each(function () {
|
88 |
+
$(this).toggle($(this).hasClass('bookly-js-' + set));
|
89 |
+
});
|
90 |
+
|
91 |
+
switch (set) {
|
92 |
+
case 'after_event':
|
93 |
+
var $set = $panel.find('.bookly-js-' + set);
|
94 |
+
$set.find('.bookly-js-to').toggle(value == 'ca_status_changed');
|
95 |
+
$set.find('.bookly-js-with').toggle(value != 'ca_status_changed');
|
96 |
+
break;
|
97 |
+
}
|
98 |
+
})
|
99 |
+
.on('click', '.bookly-js-delete', function () {
|
100 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
101 |
+
var $button = $(this),
|
102 |
+
id = $button.data('notification_id'),
|
103 |
+
ladda = Ladda.create(this);
|
104 |
+
ladda.start();
|
105 |
+
$.ajax({
|
106 |
+
url: ajaxurl,
|
107 |
+
type: 'POST',
|
108 |
+
data: {
|
109 |
+
action: 'bookly_delete_custom_notification',
|
110 |
+
id: id,
|
111 |
+
csrf_token: BooklyL10n.csrf_token
|
112 |
+
},
|
113 |
+
dataType: 'json',
|
114 |
+
success: function (response) {
|
115 |
+
if (response.success) {
|
116 |
+
var $panel = $button.closest('.panel');
|
117 |
+
if ($panel.find('#bookly-js-tinymce-container').length != 0) {
|
118 |
+
$panel.find('#bookly-js-tinymce-area-tmce').click();
|
119 |
+
tinymce.remove("#bookly-js-tinymce-area");
|
120 |
+
$tiny_mce_container.detach().appendTo('#bookly-js-tinymce-wrap');
|
121 |
+
tinymce.init(tinyMCEPreInit.mceInit['bookly-js-tinymce-area']);
|
122 |
+
tinymce.get('bookly-js-tinymce-area').setContent($panel.find('.bookly-js-message-input').val());
|
123 |
+
}
|
124 |
+
$panel.remove();
|
125 |
+
ladda.stop();
|
126 |
+
}
|
127 |
+
}
|
128 |
+
});
|
129 |
+
}
|
130 |
+
})
|
131 |
+
.find("select[name$='[type]']").trigger('change');
|
132 |
+
|
133 |
+
$('button[type=reset]').on('click', function () {
|
134 |
+
setTimeout(function () {
|
135 |
+
$("select[name$='[type]']", $custom_notifications).trigger('change');
|
136 |
+
}, 0);
|
137 |
});
|
138 |
|
139 |
+
$("#bookly-js-new-notification").on('click', function () {
|
140 |
+
var ladda = Ladda.create(this);
|
141 |
+
ladda.start();
|
142 |
+
$.ajax({
|
143 |
+
url : ajaxurl,
|
144 |
+
type: 'POST',
|
145 |
+
data: {
|
146 |
+
action : 'bookly_pro_create_custom_notification',
|
147 |
+
render : true,
|
148 |
+
csrf_token: BooklyL10n.csrf_token
|
149 |
+
},
|
150 |
+
dataType: 'json',
|
151 |
+
success: function (response) {
|
152 |
+
if (response.success) {
|
153 |
+
$custom_notifications.append(response.data.html);
|
154 |
+
var $subject = $custom_notifications.find('[name="notification[' + response.data.id + '][subject]"]'),
|
155 |
+
$panel = $subject.closest('.panel-collapse');
|
156 |
+
|
157 |
+
$panel.collapse('show');
|
158 |
+
$panel.find("select[name$='[type]']").trigger('change');
|
159 |
+
$subject.focus();
|
160 |
+
}
|
161 |
+
},
|
162 |
+
complete: function () {
|
163 |
+
ladda.stop();
|
164 |
+
}
|
165 |
+
});
|
166 |
});
|
167 |
+
|
168 |
+
booklyAlert(BooklyL10n.alert);
|
169 |
});
|
backend/modules/notifications/templates/_custom_notification.php
DELETED
@@ -1 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
backend/modules/notifications/templates/_test_email_notifications_modal.php
CHANGED
@@ -1,4 +1,7 @@
|
|
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">
|
@@ -83,14 +86,14 @@
|
|
83 |
</div>
|
84 |
</div>
|
85 |
<div class="modal-footer">
|
86 |
-
<?php
|
87 |
-
<?php
|
88 |
</div>
|
89 |
</form>
|
90 |
</div>
|
91 |
-
</div
|
92 |
-
</div
|
93 |
-
</div
|
94 |
|
95 |
<div class="modal fade" id="bookly-modal" tabindex="-1" role="dialog">
|
96 |
<div class="modal-dialog" role="document">
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
?>
|
5 |
<div ng-controller=testEmailNotificationsDialogCtrl>
|
6 |
<div id=bookly-test-email-notifications-dialog class="modal fade" tabindex=-1 role="dialog">
|
7 |
<div class="modal-dialog">
|
86 |
</div>
|
87 |
</div>
|
88 |
<div class="modal-footer">
|
89 |
+
<?php Inputs::renderCsrf() ?>
|
90 |
+
<?php Buttons::renderSubmit( null, null, __( 'Send', 'bookly' ) ) ?>
|
91 |
</div>
|
92 |
</form>
|
93 |
</div>
|
94 |
+
</div>
|
95 |
+
</div>
|
96 |
+
</div>
|
97 |
|
98 |
<div class="modal fade" id="bookly-modal" tabindex="-1" role="dialog">
|
99 |
<div class="modal-dialog" role="document">
|
backend/modules/notifications/templates/index.php
CHANGED
@@ -1,15 +1,19 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
5 |
-
use
|
|
|
|
|
|
|
|
|
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
|
13 |
?>
|
14 |
<div id="bookly-tbs" class="wrap">
|
15 |
<div class="bookly-tbs-body" ng-app="notifications">
|
@@ -17,9 +21,9 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
17 |
<div class="bookly-page-title">
|
18 |
<?php _e( 'Email Notifications', 'bookly' ) ?>
|
19 |
</div>
|
20 |
-
<?php Support\
|
21 |
</div>
|
22 |
-
<form method="post" action="<?php echo Common::escAdminUrl( $
|
23 |
<div class="panel panel-default bookly-main" ng-controller="emailNotifications">
|
24 |
<div class="panel-body">
|
25 |
<div class="row">
|
@@ -36,12 +40,12 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
36 |
</div>
|
37 |
</div>
|
38 |
<div class="col-md-6">
|
39 |
-
<?php
|
40 |
array( array( 'html', __( 'HTML', 'bookly' ) ), array( 'text', __( 'Text', 'bookly' ) ) )
|
41 |
) ?>
|
42 |
</div>
|
43 |
<div class="col-md-6">
|
44 |
-
<?php
|
45 |
</div>
|
46 |
</div>
|
47 |
|
@@ -70,6 +74,7 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
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>
|
@@ -82,7 +87,7 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
82 |
<?php endforeach ?>
|
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 |
|
@@ -122,49 +127,52 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
122 |
|
123 |
<?php Proxy\Shared::renderEmailNotifications( $form ) ?>
|
124 |
|
125 |
-
|
126 |
-
|
127 |
-
<div
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
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
|
152 |
<?php endif ?>
|
153 |
</div>
|
154 |
<div class="col-md-2">
|
155 |
-
<?php
|
156 |
</div>
|
157 |
</div>
|
158 |
</div>
|
|
|
159 |
</div>
|
160 |
|
161 |
<div class="panel-footer">
|
162 |
-
<?php
|
163 |
-
<?php
|
164 |
-
<?php
|
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,24 +181,5 @@ $bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
|
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">×</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>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
use Bookly\Backend\Components\Settings\Selects;
|
5 |
+
use Bookly\Backend\Components\Support;
|
6 |
+
use Bookly\Backend\Modules\Notifications\Proxy;
|
7 |
+
use Bookly\Lib\Entities\Notification;
|
8 |
+
use Bookly\Lib\Utils\Common;
|
9 |
+
use Bookly\Lib\Config;
|
10 |
|
11 |
$bookly_email_sender_name = get_option( 'bookly_email_sender_name' ) == '' ?
|
12 |
get_option( 'blogname' ) : get_option( 'bookly_email_sender_name' );
|
13 |
$bookly_email_sender = get_option( 'bookly_email_sender' ) == '' ?
|
14 |
get_option( 'admin_email' ) : get_option( 'bookly_email_sender' );
|
15 |
|
16 |
+
/** @var Bookly\Backend\Modules\Notifications\Forms\Notifications $form */
|
17 |
?>
|
18 |
<div id="bookly-tbs" class="wrap">
|
19 |
<div class="bookly-tbs-body" ng-app="notifications">
|
21 |
<div class="bookly-page-title">
|
22 |
<?php _e( 'Email Notifications', 'bookly' ) ?>
|
23 |
</div>
|
24 |
+
<?php Support\Buttons::render( $self::pageSlug() ) ?>
|
25 |
</div>
|
26 |
+
<form method="post" action="<?php echo Common::escAdminUrl( $self::pageSlug() ) ?>">
|
27 |
<div class="panel panel-default bookly-main" ng-controller="emailNotifications">
|
28 |
<div class="panel-body">
|
29 |
<div class="row">
|
40 |
</div>
|
41 |
</div>
|
42 |
<div class="col-md-6">
|
43 |
+
<?php Selects::renderSingle( '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' ),
|
44 |
array( array( 'html', __( 'HTML', 'bookly' ) ), array( 'text', __( 'Text', 'bookly' ) ) )
|
45 |
) ?>
|
46 |
</div>
|
47 |
<div class="col-md-6">
|
48 |
+
<?php Selects::renderSingle( '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' ) ) ?>
|
49 |
</div>
|
50 |
</div>
|
51 |
|
74 |
<?php $form->renderSubject( $id ) ?>
|
75 |
<?php $form->renderEditor( $id ) ?>
|
76 |
<?php $form->renderAttachIcs( $notification ) ?>
|
77 |
+
<?php $form->renderAttachInvoice( $notification ) ?>
|
78 |
<?php $form->renderCopy( $notification ) ?>
|
79 |
<div class="form-group">
|
80 |
<label><?php _e( 'Codes', 'bookly' ) ?></label>
|
87 |
<?php endforeach ?>
|
88 |
</div>
|
89 |
|
90 |
+
<?php if ( Config::proActive() && $form->types['combined'] ) : ?>
|
91 |
|
92 |
<h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
|
93 |
|
127 |
|
128 |
<?php Proxy\Shared::renderEmailNotifications( $form ) ?>
|
129 |
|
130 |
+
<?php Proxy\Pro::renderCustomEmailNotifications( $form ) ?>
|
131 |
+
|
132 |
+
<div id="bookly-js-tinymce-wrap" class="collapse">
|
133 |
+
<div id="bookly-js-tinymce-container">
|
134 |
+
<?php
|
135 |
+
$settings = array(
|
136 |
+
'media_buttons' => false,
|
137 |
+
'editor_height' => 384,
|
138 |
+
'tinymce' => array(
|
139 |
+
'theme_advanced_buttons1' => 'formatselect,|,bold,italic,underline,|,'.
|
140 |
+
'bullist,blockquote,|,justifyleft,justifycenter'.
|
141 |
+
',justifyright,justifyfull,|,link,unlink,|'.
|
142 |
+
',spellchecker,wp_fullscreen,wp_adv'
|
143 |
+
)
|
144 |
+
);
|
145 |
+
wp_editor( '', 'bookly-js-tinymce-area', $settings );
|
146 |
+
?>
|
147 |
</div>
|
148 |
</div>
|
149 |
|
150 |
+
<?php if ( Config::proActive() ) : ?>
|
151 |
<div class="alert alert-info">
|
152 |
<div class="row">
|
153 |
<div class="col-md-10">
|
154 |
<?php if ( is_multisite() ) : ?>
|
155 |
+
<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' ), Common::prepareUrlReferrers( 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', 'cron_setup' ), network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
|
156 |
<?php else : ?>
|
157 |
<p><?php _e( 'To send scheduled notifications please execute the following command hourly with your cron:', 'bookly' ) ?></p><br/>
|
158 |
+
<code class="bookly-text-wrap">wget -q -O - <?php echo site_url( 'wp-cron.php' ) ?></code>
|
159 |
<?php endif ?>
|
160 |
</div>
|
161 |
<div class="col-md-2">
|
162 |
+
<?php Selects::renderSingle( '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 ) ?>
|
163 |
</div>
|
164 |
</div>
|
165 |
</div>
|
166 |
+
<?php endif ?>
|
167 |
</div>
|
168 |
|
169 |
<div class="panel-footer">
|
170 |
+
<?php Inputs::renderCsrf() ?>
|
171 |
+
<?php Buttons::renderSubmit() ?>
|
172 |
+
<?php Buttons::renderReset() ?>
|
173 |
|
174 |
<div class="pull-left">
|
175 |
+
<button type="button" class="btn btn-default bookly-test-email-notifications btn-lg" ng-click="showTestEmailNotificationDialog(); $event.stopPropagation();">
|
176 |
<?php _e( 'Test Email Notifications', 'bookly' ) ?>
|
177 |
</button>
|
178 |
</div>
|
181 |
</form>
|
182 |
|
183 |
<?php include '_test_email_notifications_modal.php' ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
</div>
|
185 |
</div>
|
backend/modules/payments/Ajax.php
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Payments;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Payments
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Get payments.
|
15 |
+
*/
|
16 |
+
public static function getPayments()
|
17 |
+
{
|
18 |
+
$columns = self::parameter( 'columns' );
|
19 |
+
$order = self::parameter( 'order' );
|
20 |
+
$filter = self::parameter( 'filter' );
|
21 |
+
|
22 |
+
$query = Lib\Entities\Payment::query( 'p' )
|
23 |
+
->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' )
|
24 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
|
25 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
26 |
+
->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
|
27 |
+
->leftJoin( 'Service', 's', 's.id = COALESCE(ca.compound_service_id, a.service_id)' )
|
28 |
+
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
29 |
+
->groupBy( 'p.id' );
|
30 |
+
|
31 |
+
// Filters.
|
32 |
+
list ( $start, $end ) = explode( ' - ', $filter['created'], 2 );
|
33 |
+
$end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
|
34 |
+
|
35 |
+
$query->whereBetween( 'p.created', $start, $end );
|
36 |
+
|
37 |
+
if ( $filter['id'] != '' ) {
|
38 |
+
$query->where( 'p.id', $filter['id'] );
|
39 |
+
}
|
40 |
+
|
41 |
+
if ( $filter['type'] != '' ) {
|
42 |
+
$query->where( 'p.type', $filter['type'] );
|
43 |
+
}
|
44 |
+
|
45 |
+
if ( $filter['staff'] != '' ) {
|
46 |
+
$query->where( 'st.id', $filter['staff'] );
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( $filter['service'] != '' ) {
|
50 |
+
$query->where( 's.id', $filter['service'] );
|
51 |
+
}
|
52 |
+
|
53 |
+
if ( $filter['status'] != '' ) {
|
54 |
+
$query->where( 'p.status', $filter['status'] );
|
55 |
+
}
|
56 |
+
|
57 |
+
if ( $filter['customer'] != '' ) {
|
58 |
+
$query->where( 'ca.customer_id', $filter['customer'] );
|
59 |
+
}
|
60 |
+
|
61 |
+
foreach ( $order as $sort_by ) {
|
62 |
+
$query->sortBy( $columns[ $sort_by['column'] ]['data'] )
|
63 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
64 |
+
}
|
65 |
+
|
66 |
+
$payments = $query->fetchArray();
|
67 |
+
|
68 |
+
$data = array();
|
69 |
+
$total = 0;
|
70 |
+
foreach ( $payments as $payment ) {
|
71 |
+
$details = json_decode( $payment['details'], true );
|
72 |
+
$multiple = count( $details['items'] ) > 1
|
73 |
+
? ' <span class="glyphicon glyphicon-shopping-cart" title="' . esc_attr( __( 'See details for more items', 'bookly' ) ) . '"></span>'
|
74 |
+
: '';
|
75 |
+
|
76 |
+
$paid_title = Lib\Utils\Price::format( $payment['paid'] );
|
77 |
+
if ( $payment['paid'] != $payment['total'] ) {
|
78 |
+
$paid_title = sprintf( __( '%s of %s', 'bookly' ), $paid_title, Lib\Utils\Price::format( $payment['total'] ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
$data[] = array(
|
82 |
+
'id' => $payment['id'],
|
83 |
+
'created' => Lib\Utils\DateTime::formatDateTime( $payment['created'] ),
|
84 |
+
'type' => Lib\Entities\Payment::typeToString( $payment['type'] ),
|
85 |
+
'customer' => $payment['customer'] ?: $details['customer'],
|
86 |
+
'provider' => ( $payment['provider'] ?: $details['items'][0]['staff_name'] ) . $multiple,
|
87 |
+
'service' => ( $payment['service'] ?: $details['items'][0]['service_name'] ) . $multiple,
|
88 |
+
'start_date' => ( $payment['start_date']
|
89 |
+
? Lib\Utils\DateTime::formatDateTime( $payment['start_date'] )
|
90 |
+
: ( $details['items'][0]['appointment_date'] ? Lib\Utils\DateTime::formatDateTime( $details['items'][0]['appointment_date'] ) . $multiple : __( 'N/A', 'bookly' ) . $multiple ) ),
|
91 |
+
'paid' => $paid_title,
|
92 |
+
'status' => Lib\Entities\Payment::statusToString( $payment['status'] ),
|
93 |
+
);
|
94 |
+
|
95 |
+
$total += $payment['paid'];
|
96 |
+
}
|
97 |
+
|
98 |
+
wp_send_json( array(
|
99 |
+
'draw' => ( int ) self::parameter( 'draw' ),
|
100 |
+
'recordsTotal' => count( $data ),
|
101 |
+
'recordsFiltered' => count( $data ),
|
102 |
+
'data' => $data,
|
103 |
+
'total' => Lib\Utils\Price::format( $total ),
|
104 |
+
) );
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Delete payments.
|
109 |
+
*/
|
110 |
+
public static function deletePayments()
|
111 |
+
{
|
112 |
+
$payment_ids = array_map( 'intval', self::parameter( 'data', array() ) );
|
113 |
+
Lib\Entities\Payment::query()->delete()->whereIn( 'id', $payment_ids )->execute();
|
114 |
+
wp_send_json_success();
|
115 |
+
}
|
116 |
+
|
117 |
+
}
|
backend/modules/payments/Components.php
DELETED
@@ -1,34 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Payments;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Components
|
8 |
-
* @package BooklyLite\Backend\Modules\Payments
|
9 |
-
*/
|
10 |
-
class Components extends Lib\Base\Components
|
11 |
-
{
|
12 |
-
/**
|
13 |
-
* Render payment details dialog.
|
14 |
-
* @throws \Exception
|
15 |
-
*/
|
16 |
-
public function renderPaymentDetailsDialog()
|
17 |
-
{
|
18 |
-
$this->enqueueStyles( array(
|
19 |
-
'frontend' => array( 'css/ladda.min.css', ),
|
20 |
-
) );
|
21 |
-
|
22 |
-
$this->enqueueScripts( array(
|
23 |
-
'backend' => array( 'js/angular.min.js' => array( 'jquery' ), ),
|
24 |
-
'frontend' => array(
|
25 |
-
'js/spin.min.js' => array( 'jquery' ),
|
26 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
27 |
-
),
|
28 |
-
'module' => array( 'js/ng-payment_details_dialog.js' => array( 'bookly-angular.min.js' ), ),
|
29 |
-
) );
|
30 |
-
|
31 |
-
$this->render( '_payment_details_dialog' );
|
32 |
-
}
|
33 |
-
|
34 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payments/Controller.php
DELETED
@@ -1,257 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Payments;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Payments
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-payments';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* @return array
|
16 |
-
*/
|
17 |
-
protected function getPermissions()
|
18 |
-
{
|
19 |
-
return array(
|
20 |
-
'executeGetPaymentDetails' => 'user',
|
21 |
-
'executeCompletePayment' => 'user',
|
22 |
-
);
|
23 |
-
}
|
24 |
-
|
25 |
-
public function index()
|
26 |
-
{
|
27 |
-
/** @var \WP_Locale $wp_locale */
|
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(
|
40 |
-
'backend' => array(
|
41 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
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' ),
|
49 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
50 |
-
),
|
51 |
-
'module' => array( 'js/payments.js' => array( 'bookly-datatables.min.js', 'bookly-ng-payment_details_dialog.js' ) ),
|
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' ),
|
59 |
-
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
60 |
-
'this_month' => __( 'This Month', 'bookly' ),
|
61 |
-
'last_month' => __( 'Last Month', 'bookly' ),
|
62 |
-
'custom_range' => __( 'Custom Range', 'bookly' ),
|
63 |
-
'apply' => __( 'Apply', 'bookly' ),
|
64 |
-
'cancel' => __( 'Cancel', 'bookly' ),
|
65 |
-
'to' => __( 'To', 'bookly' ),
|
66 |
-
'from' => __( 'From', 'bookly' ),
|
67 |
-
'calendar' => array(
|
68 |
-
'longMonths' => array_values( $wp_locale->month ),
|
69 |
-
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
70 |
-
'longDays' => array_values( $wp_locale->weekday ),
|
71 |
-
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
72 |
-
),
|
73 |
-
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
74 |
-
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
75 |
-
'zeroRecords' => __( 'No payments for selected period and criteria.', '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(
|
83 |
-
Lib\Entities\Payment::TYPE_LOCAL,
|
84 |
-
Lib\Entities\Payment::TYPE_2CHECKOUT,
|
85 |
-
Lib\Entities\Payment::TYPE_PAYPAL,
|
86 |
-
Lib\Entities\Payment::TYPE_AUTHORIZENET,
|
87 |
-
Lib\Entities\Payment::TYPE_STRIPE,
|
88 |
-
Lib\Entities\Payment::TYPE_PAYULATAM,
|
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();
|
96 |
-
|
97 |
-
$this->render( 'index', compact( 'types', 'providers', 'services' ) );
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Get payments.
|
102 |
-
*/
|
103 |
-
public function executeGetPayments()
|
104 |
-
{
|
105 |
-
$columns = $this->getParameter( 'columns' );
|
106 |
-
$order = $this->getParameter( 'order' );
|
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' )
|
114 |
-
->leftJoin( 'Service', 's', 's.id = COALESCE(ca.compound_service_id, a.service_id)' )
|
115 |
-
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
116 |
-
->groupBy( 'p.id' );
|
117 |
-
|
118 |
-
// Filters.
|
119 |
-
list ( $start, $end ) = explode( ' - ', $filter['created'], 2 );
|
120 |
-
$end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
|
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 |
-
|
136 |
-
foreach ( $order as $sort_by ) {
|
137 |
-
$query->sortBy( $columns[ $sort_by['column'] ]['data'] )
|
138 |
-
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
139 |
-
}
|
140 |
-
|
141 |
-
$payments = $query->fetchArray();
|
142 |
-
|
143 |
-
$data = array();
|
144 |
-
$total = 0;
|
145 |
-
foreach ( $payments as $payment ) {
|
146 |
-
$details = json_decode( $payment['details'], true );
|
147 |
-
$multiple = count( $details['items'] ) > 1
|
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(
|
157 |
-
'id' => $payment['id'],
|
158 |
-
'created' => Lib\Utils\DateTime::formatDateTime( $payment['created'] ),
|
159 |
-
'type' => Lib\Entities\Payment::typeToString( $payment['type'] ),
|
160 |
-
'customer' => $payment['customer'] ?: $details['customer'],
|
161 |
-
'provider' => ( $payment['provider'] ?: $details['items'][0]['staff_name'] ) . $multiple,
|
162 |
-
'service' => ( $payment['service'] ?: $details['items'][0]['service_name'] ) . $multiple,
|
163 |
-
'start_date' => ( $payment['start_date']
|
164 |
-
? Lib\Utils\DateTime::formatDateTime( $payment['start_date'] )
|
165 |
-
: Lib\Utils\DateTime::formatDateTime( $details['items'][0]['appointment_date'] ) ) . $multiple,
|
166 |
-
'paid' => $paid_title,
|
167 |
-
'status' => Lib\Entities\Payment::statusToString( $payment['status'] ),
|
168 |
-
|
169 |
-
);
|
170 |
-
|
171 |
-
$total += $payment['paid'];
|
172 |
-
}
|
173 |
-
|
174 |
-
wp_send_json( array(
|
175 |
-
'draw' => ( int ) $this->getParameter( 'draw' ),
|
176 |
-
'recordsTotal' => count( $data ),
|
177 |
-
'recordsFiltered' => count( $data ),
|
178 |
-
'data' => $data,
|
179 |
-
'total' => Lib\Utils\Price::format( $total ),
|
180 |
-
) );
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Get payment details.
|
185 |
-
*
|
186 |
-
* @throws \Exception
|
187 |
-
*/
|
188 |
-
public function executeGetPaymentDetails()
|
189 |
-
{
|
190 |
-
$data = array();
|
191 |
-
$payment = Lib\Entities\Payment::query( 'p' )
|
192 |
-
->select( 'p.total,
|
193 |
-
p.status,
|
194 |
-
p.created AS created,
|
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' ) )
|
202 |
-
->fetchRow();
|
203 |
-
if ( $payment ) {
|
204 |
-
$details = json_decode( $payment['details'], true );
|
205 |
-
$data = array(
|
206 |
-
'payment' => array(
|
207 |
-
'status' => $payment['status'],
|
208 |
-
'type' => $payment['type'],
|
209 |
-
'coupon' => $details['coupon'],
|
210 |
-
'created' => $payment['created'],
|
211 |
-
'customer' => empty ( $payment['customer'] ) ? $details['customer'] : $payment['customer'],
|
212 |
-
'total' => $payment['total'],
|
213 |
-
'paid' => $payment['paid'],
|
214 |
-
),
|
215 |
-
'items' => $details['items'],
|
216 |
-
'deposit_enabled' => Lib\Config::depositPaymentsEnabled()
|
217 |
-
);
|
218 |
-
}
|
219 |
-
|
220 |
-
wp_send_json_success( array( 'html' => $this->render( 'details', $data, false ) ) );
|
221 |
-
}
|
222 |
-
|
223 |
-
/**
|
224 |
-
* Delete payments.
|
225 |
-
*/
|
226 |
-
public function executeDeletePayments()
|
227 |
-
{
|
228 |
-
$payment_ids = array_map( 'intval', $this->getParameter( 'data', array() ) );
|
229 |
-
Lib\Entities\Payment::query()->delete()->whereIn( 'id', $payment_ids )->execute();
|
230 |
-
wp_send_json_success();
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Complete payment.
|
235 |
-
*/
|
236 |
-
public function executeCompletePayment()
|
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/Page.php
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Payments;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Payments
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
self::enqueueStyles( array(
|
21 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
22 |
+
'backend' => array(
|
23 |
+
'css/select2.min.css',
|
24 |
+
'bootstrap/css/bootstrap-theme.min.css' => array( 'bookly-select2.min.css' ),
|
25 |
+
'css/daterangepicker.css',
|
26 |
+
),
|
27 |
+
) );
|
28 |
+
|
29 |
+
self::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' ),
|
39 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
40 |
+
),
|
41 |
+
'module' => array( 'js/payments.js' => array( 'bookly-datatables.min.js', 'bookly-ng-payment_details.js' ) ),
|
42 |
+
) );
|
43 |
+
|
44 |
+
wp_localize_script( 'bookly-daterangepicker.js', 'BooklyL10n', array(
|
45 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
46 |
+
'today' => __( 'Today', 'bookly' ),
|
47 |
+
'yesterday' => __( 'Yesterday', 'bookly' ),
|
48 |
+
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
49 |
+
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
50 |
+
'this_month' => __( 'This Month', 'bookly' ),
|
51 |
+
'last_month' => __( 'Last Month', 'bookly' ),
|
52 |
+
'custom_range' => __( 'Custom Range', 'bookly' ),
|
53 |
+
'apply' => __( 'Apply', 'bookly' ),
|
54 |
+
'cancel' => __( 'Cancel', 'bookly' ),
|
55 |
+
'to' => __( 'To', 'bookly' ),
|
56 |
+
'from' => __( 'From', 'bookly' ),
|
57 |
+
'calendar' => array(
|
58 |
+
'longMonths' => array_values( $wp_locale->month ),
|
59 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
60 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
61 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
62 |
+
),
|
63 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
64 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
65 |
+
'zeroRecords' => __( 'No payments for selected period and criteria.', 'bookly' ),
|
66 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
67 |
+
'details' => __( 'Details', 'bookly' ),
|
68 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
69 |
+
'no_result_found' => __( 'No result found', 'bookly' ),
|
70 |
+
'invoice' => array(
|
71 |
+
'enabled' => (int) Lib\Config::invoicesActive(),
|
72 |
+
'button' => __( 'Invoice', 'bookly' ),
|
73 |
+
),
|
74 |
+
) );
|
75 |
+
|
76 |
+
$types = array(
|
77 |
+
Lib\Entities\Payment::TYPE_LOCAL,
|
78 |
+
Lib\Entities\Payment::TYPE_2CHECKOUT,
|
79 |
+
Lib\Entities\Payment::TYPE_PAYPAL,
|
80 |
+
Lib\Entities\Payment::TYPE_AUTHORIZENET,
|
81 |
+
Lib\Entities\Payment::TYPE_STRIPE,
|
82 |
+
Lib\Entities\Payment::TYPE_PAYUBIZ,
|
83 |
+
Lib\Entities\Payment::TYPE_PAYULATAM,
|
84 |
+
Lib\Entities\Payment::TYPE_PAYSON,
|
85 |
+
Lib\Entities\Payment::TYPE_MOLLIE,
|
86 |
+
Lib\Entities\Payment::TYPE_COUPON,
|
87 |
+
Lib\Entities\Payment::TYPE_WOOCOMMERCE,
|
88 |
+
);
|
89 |
+
|
90 |
+
$providers = Lib\Entities\Staff::query()->select( 'id, full_name' )->sortBy( 'full_name' )->fetchArray();
|
91 |
+
$services = Lib\Entities\Service::query()->select( 'id, title' )->sortBy( 'title' )->fetchArray();
|
92 |
+
$customers = Lib\Entities\Customer::query( 'c' )->select( 'c.id, c.full_name, c.first_name, c.last_name' )->fetchArray();
|
93 |
+
|
94 |
+
self::renderTemplate( 'index', compact( 'types', 'providers', 'services', 'customers' ) );
|
95 |
+
}
|
96 |
+
}
|
backend/modules/payments/proxy/Invoices.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Payments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Invoices
|
8 |
+
* @package Bookly\Backend\Modules\Notifications\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderDownloadButton() Render button for downloading invoice(s).
|
11 |
+
*/
|
12 |
+
abstract class Invoices extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/payments/proxy/Pro.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Payments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Notifications\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderManualAdjustmentButton() Render manual adjustment button.
|
11 |
+
* @method static void renderManualAdjustmentForm( array $show ) Render manual adjustment form.
|
12 |
+
*/
|
13 |
+
abstract class Pro extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
}
|
backend/modules/payments/proxy/Shared.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Payments\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Payments\Proxy
|
9 |
+
*
|
10 |
+
* @method static bool paymentSpecificPriceExists( string $gateway ) Check whether specific price exists for given gateway.
|
11 |
+
*/
|
12 |
+
abstract class Shared extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/payments/resources/js/ng-payment_details_dialog.js
DELETED
@@ -1,69 +0,0 @@
|
|
1 |
-
;(function() {
|
2 |
-
|
3 |
-
angular.module('paymentDetailsDialog', []).directive('paymentDetailsDialog', function() {
|
4 |
-
return {
|
5 |
-
restrict: 'A',
|
6 |
-
replace: true,
|
7 |
-
scope: {
|
8 |
-
callback: '&paymentDetailsDialog'
|
9 |
-
},
|
10 |
-
templateUrl: 'bookly-payment-details-dialog.tpl',
|
11 |
-
// The linking function will add behavior to the template.
|
12 |
-
link: function (scope, element, attrs) {
|
13 |
-
var $body = element.find('.modal-body'),
|
14 |
-
spinner = $body.html();
|
15 |
-
|
16 |
-
element
|
17 |
-
.on('show.bs.modal', function (e, payment_id) {
|
18 |
-
if (payment_id === undefined) {
|
19 |
-
payment_id = e.relatedTarget.getAttribute('data-payment_id');
|
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) {
|
27 |
-
$body.html(response.data.html);
|
28 |
-
$body.find('#bookly-complete-payment').on('click',function () {
|
29 |
-
var ladda = Ladda.create(this);
|
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) {
|
37 |
-
if (response.success) {
|
38 |
-
element.trigger('show.bs.modal', [payment_id]);
|
39 |
-
if (scope.callback) {
|
40 |
-
scope.$apply(function ($scope) {
|
41 |
-
$scope.callback({
|
42 |
-
payment_id : payment_id,
|
43 |
-
payment_title : response.data.payment_title
|
44 |
-
});
|
45 |
-
});
|
46 |
-
}
|
47 |
-
// Reload DataTable.
|
48 |
-
var $table = jQuery(e.relatedTarget).closest('table.dataTable');
|
49 |
-
if ($table.length) {
|
50 |
-
$table.DataTable().ajax.reload();
|
51 |
-
}
|
52 |
-
}
|
53 |
-
}
|
54 |
-
});
|
55 |
-
});
|
56 |
-
}
|
57 |
-
}
|
58 |
-
});
|
59 |
-
})
|
60 |
-
.on('hidden.bs.modal', function () {
|
61 |
-
$body.html(spinner);
|
62 |
-
if ((jQuery("#bookly-appointment-dialog").data('bs.modal') || {isShown: false}).isShown) {
|
63 |
-
jQuery('body').addClass('modal-open');
|
64 |
-
}
|
65 |
-
});
|
66 |
-
}
|
67 |
-
}
|
68 |
-
});
|
69 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payments/resources/js/payments.js
CHANGED
@@ -3,12 +3,16 @@ jQuery(function($) {
|
|
3 |
var
|
4 |
$payments_list = $('#bookly-payments-list'),
|
5 |
$check_all_button = $('#bookly-check-all'),
|
|
|
6 |
$date_filter = $('#bookly-filter-date'),
|
7 |
$type_filter = $('#bookly-filter-type'),
|
|
|
8 |
$staff_filter = $('#bookly-filter-staff'),
|
9 |
$service_filter = $('#bookly-filter-service'),
|
|
|
10 |
$payment_total = $('#bookly-payment-total'),
|
11 |
-
$delete_button = $('#bookly-delete')
|
|
|
12 |
;
|
13 |
$('.bookly-js-select')
|
14 |
.val(null)
|
@@ -23,6 +27,42 @@ jQuery(function($) {
|
|
23 |
noResults: function() { return BooklyL10n.no_result_found; }
|
24 |
}
|
25 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
/**
|
27 |
* Init DataTables.
|
28 |
*/
|
@@ -42,10 +82,13 @@ jQuery(function($) {
|
|
42 |
action: 'bookly_get_payments',
|
43 |
csrf_token: BooklyL10n.csrf_token,
|
44 |
filter: {
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
49 |
}
|
50 |
} );
|
51 |
},
|
@@ -55,35 +98,10 @@ jQuery(function($) {
|
|
55 |
return json.data;
|
56 |
}
|
57 |
},
|
58 |
-
columns:
|
59 |
-
{ data: 'created' },
|
60 |
-
{ data: 'type' },
|
61 |
-
{ data: 'customer', render: $.fn.dataTable.render.text() },
|
62 |
-
{ data: 'provider' },
|
63 |
-
{ data: 'service' },
|
64 |
-
{ data: 'start_date' },
|
65 |
-
{ data: 'paid' },
|
66 |
-
{ data: 'status' },
|
67 |
-
{
|
68 |
-
responsivePriority: 1,
|
69 |
-
orderable: false,
|
70 |
-
searchable: false,
|
71 |
-
render: function ( data, type, row, meta ) {
|
72 |
-
return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-payment-details-modal" data-payment_id="' + row.id + '"><i class="glyphicon glyphicon-list-alt"></i> ' + BooklyL10n.details + '</a>';
|
73 |
-
}
|
74 |
-
},
|
75 |
-
{
|
76 |
-
responsivePriority: 1,
|
77 |
-
orderable: false,
|
78 |
-
searchable: false,
|
79 |
-
render: function ( data, type, row, meta ) {
|
80 |
-
return '<input type="checkbox" value="' + row.id + '">';
|
81 |
-
}
|
82 |
-
}
|
83 |
-
],
|
84 |
language: {
|
85 |
zeroRecords: BooklyL10n.zeroRecords,
|
86 |
-
processing:
|
87 |
}
|
88 |
});
|
89 |
|
@@ -100,6 +118,7 @@ jQuery(function($) {
|
|
100 |
$payments_list.on('change', 'tbody input:checkbox', function () {
|
101 |
$check_all_button.prop('checked', $payments_list.find('tbody input:not(:checked)').length == 0);
|
102 |
});
|
|
|
103 |
/**
|
104 |
* Init date range picker.
|
105 |
*/
|
@@ -145,11 +164,13 @@ jQuery(function($) {
|
|
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(); });
|
152 |
$service_filter.on('change', function () { dt.ajax.reload(); });
|
|
|
153 |
|
154 |
/**
|
155 |
* Delete payments.
|
@@ -185,6 +206,20 @@ jQuery(function($) {
|
|
185 |
});
|
186 |
}
|
187 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
});
|
189 |
|
190 |
(function() {
|
3 |
var
|
4 |
$payments_list = $('#bookly-payments-list'),
|
5 |
$check_all_button = $('#bookly-check-all'),
|
6 |
+
$id_filter = $('#bookly-filter-id'),
|
7 |
$date_filter = $('#bookly-filter-date'),
|
8 |
$type_filter = $('#bookly-filter-type'),
|
9 |
+
$customer_filter = $('#bookly-filter-customer'),
|
10 |
$staff_filter = $('#bookly-filter-staff'),
|
11 |
$service_filter = $('#bookly-filter-service'),
|
12 |
+
$status_filter = $('#bookly-filter-status'),
|
13 |
$payment_total = $('#bookly-payment-total'),
|
14 |
+
$delete_button = $('#bookly-delete'),
|
15 |
+
$download_invoice = $('#bookly-download-invoices')
|
16 |
;
|
17 |
$('.bookly-js-select')
|
18 |
.val(null)
|
27 |
noResults: function() { return BooklyL10n.no_result_found; }
|
28 |
}
|
29 |
});
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Init Columns.
|
33 |
+
*/
|
34 |
+
var columns = [
|
35 |
+
{ data: 'id' },
|
36 |
+
{ data: 'created' },
|
37 |
+
{ data: 'type' },
|
38 |
+
{ data: 'customer', render: $.fn.dataTable.render.text() },
|
39 |
+
{ data: 'provider' },
|
40 |
+
{ data: 'service' },
|
41 |
+
{ data: 'start_date' },
|
42 |
+
{ data: 'paid' },
|
43 |
+
{ data: 'status' },
|
44 |
+
{
|
45 |
+
responsivePriority: 1,
|
46 |
+
orderable: false,
|
47 |
+
searchable: false,
|
48 |
+
render: function ( data, type, row, meta ) {
|
49 |
+
var buttons = '';
|
50 |
+
if (BooklyL10n.invoice.enabled) {
|
51 |
+
buttons += '<button type="button" class="btn btn-default bookly-margin-right-md" data-action="view-invoice" data-payment_id="' + row.id + '"><i class="dashicons dashicons-media-text"></i> ' + BooklyL10n.invoice.button + '</a>';
|
52 |
+
}
|
53 |
+
return buttons + '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-payment-details-modal" data-payment_id="' + row.id + '"><i class="glyphicon glyphicon-list-alt"></i> ' + BooklyL10n.details + '</a>';
|
54 |
+
}
|
55 |
+
},
|
56 |
+
{
|
57 |
+
responsivePriority: 1,
|
58 |
+
orderable: false,
|
59 |
+
searchable: false,
|
60 |
+
render: function ( data, type, row, meta ) {
|
61 |
+
return '<input type="checkbox" value="' + row.id + '">';
|
62 |
+
}
|
63 |
+
}
|
64 |
+
];
|
65 |
+
|
66 |
/**
|
67 |
* Init DataTables.
|
68 |
*/
|
82 |
action: 'bookly_get_payments',
|
83 |
csrf_token: BooklyL10n.csrf_token,
|
84 |
filter: {
|
85 |
+
id : $id_filter.val(),
|
86 |
+
created : $date_filter.data('date'),
|
87 |
+
type : $type_filter.val(),
|
88 |
+
customer: $customer_filter.val(),
|
89 |
+
staff : $staff_filter.val(),
|
90 |
+
service : $service_filter.val(),
|
91 |
+
status : $status_filter.val()
|
92 |
}
|
93 |
} );
|
94 |
},
|
98 |
return json.data;
|
99 |
}
|
100 |
},
|
101 |
+
columns: columns,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
language: {
|
103 |
zeroRecords: BooklyL10n.zeroRecords,
|
104 |
+
processing: BooklyL10n.processing
|
105 |
}
|
106 |
});
|
107 |
|
118 |
$payments_list.on('change', 'tbody input:checkbox', function () {
|
119 |
$check_all_button.prop('checked', $payments_list.find('tbody input:not(:checked)').length == 0);
|
120 |
});
|
121 |
+
|
122 |
/**
|
123 |
* Init date range picker.
|
124 |
*/
|
164 |
}
|
165 |
);
|
166 |
|
167 |
+
$id_filter.on('keyup', function () { dt.ajax.reload(); });
|
168 |
$date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
|
169 |
$type_filter.on('change', function () { dt.ajax.reload(); });
|
170 |
+
$customer_filter.on('change', function () { dt.ajax.reload(); });
|
171 |
$staff_filter.on('change', function () { dt.ajax.reload(); });
|
172 |
$service_filter.on('change', function () { dt.ajax.reload(); });
|
173 |
+
$status_filter.on('change', function () { dt.ajax.reload(); });
|
174 |
|
175 |
/**
|
176 |
* Delete payments.
|
206 |
});
|
207 |
}
|
208 |
});
|
209 |
+
|
210 |
+
$payments_list.on('click', '[data-action=view-invoice]', function () {
|
211 |
+
window.location = $download_invoice.data('action') + '&invoices=' + $(this).data('payment_id');
|
212 |
+
});
|
213 |
+
|
214 |
+
$download_invoice.on('click', function () {
|
215 |
+
var invoices = [];
|
216 |
+
$payments_list.find('tbody input:checked').each(function () {
|
217 |
+
invoices.push(this.value);
|
218 |
+
});
|
219 |
+
if (invoices.length) {
|
220 |
+
window.location = $(this).data('action') + '&invoices=' + invoices.join(',');
|
221 |
+
}
|
222 |
+
});
|
223 |
});
|
224 |
|
225 |
(function() {
|
backend/modules/payments/templates/details.php
DELETED
@@ -1,137 +0,0 @@
|
|
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">
|
12 |
-
<table class="table table-bordered">
|
13 |
-
<thead>
|
14 |
-
<tr>
|
15 |
-
<th width="50%"><?php _e( 'Customer', 'bookly' ) ?></th>
|
16 |
-
<th width="50%"><?php _e( 'Payment', 'bookly' ) ?></th>
|
17 |
-
</tr>
|
18 |
-
</thead>
|
19 |
-
<tbody>
|
20 |
-
<tr>
|
21 |
-
<td><?php echo esc_html( $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>
|
29 |
-
</table>
|
30 |
-
</div>
|
31 |
-
|
32 |
-
<div class="table-responsive">
|
33 |
-
<table class="table table-bordered">
|
34 |
-
<thead>
|
35 |
-
<tr>
|
36 |
-
<th><?php _e( 'Service', 'bookly' ) ?></th>
|
37 |
-
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
38 |
-
<th><?php _e( 'Provider', 'bookly' ) ?></th>
|
39 |
-
<?php if ( $deposit_enabled ): ?>
|
40 |
-
<th class="text-right"><?php _e( 'Deposit', 'bookly' ) ?></th>
|
41 |
-
<?php endif ?>
|
42 |
-
<th class="text-right"><?php _e( 'Price', 'bookly' ) ?></th>
|
43 |
-
</tr>
|
44 |
-
</thead>
|
45 |
-
<tbody>
|
46 |
-
<?php foreach ( $items as $item ) :
|
47 |
-
$extras_price = 0; ?>
|
48 |
-
<tr>
|
49 |
-
<td>
|
50 |
-
<?php if ( $item['number_of_persons'] > 1 ) echo $item['number_of_persons'] . ' × ' ?><?php echo esc_html( $item['service_name'] ) ?>
|
51 |
-
<?php if ( ! empty ( $item['extras'] ) ) : ?>
|
52 |
-
<ul class="bookly-list list-dots">
|
53 |
-
<?php foreach ( $item['extras'] as $extra ) : ?>
|
54 |
-
<li><?php if ( $extra['quantity'] > 1 ) echo $extra['quantity'] . ' × ' ?><?php echo esc_html( $extra['title'] ) ?></li>
|
55 |
-
<?php $extras_price += $extra['price'] * $extra['quantity'] ?>
|
56 |
-
<?php endforeach ?>
|
57 |
-
</ul>
|
58 |
-
<?php endif ?>
|
59 |
-
</td>
|
60 |
-
<td><?php echo DateTime::formatDateTime( $item['appointment_date'] ) ?></td>
|
61 |
-
<td><?php echo esc_html( $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'] . ' × ' . $service_price ?>
|
69 |
-
<?php echo $service_price ?>
|
70 |
-
<ul class="bookly-list">
|
71 |
-
<?php foreach ( $item['extras'] as $extra ) : ?>
|
72 |
-
<li>
|
73 |
-
<?php printf( '%s%s%s',
|
74 |
-
( $item['number_of_persons'] > 1 ) ? $item['number_of_persons'] . ' × ' : '',
|
75 |
-
( $extra['quantity'] > 1 ) ? $extra['quantity'] . ' × ' : '',
|
76 |
-
Price::format( $extra['price'] )
|
77 |
-
) ?>
|
78 |
-
</li>
|
79 |
-
<?php $subtotal += $item['number_of_persons'] * $extra['price'] * $extra['quantity'] ?>
|
80 |
-
<?php endforeach ?>
|
81 |
-
</ul>
|
82 |
-
</td>
|
83 |
-
</tr>
|
84 |
-
<?php $subtotal += $item['number_of_persons'] * $item['service_price'] ?>
|
85 |
-
<?php $subtotal_deposit += $deposit ?>
|
86 |
-
<?php endforeach ?>
|
87 |
-
</tbody>
|
88 |
-
<tfoot>
|
89 |
-
<tr>
|
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 ?>">
|
99 |
-
<?php _e( 'Discount', 'bookly' ) ?>
|
100 |
-
<?php if ( $payment['coupon'] ) : ?><div><small>(<?php echo $payment['coupon']['code'] ?>)</small></div><?php endif ?>
|
101 |
-
</th>
|
102 |
-
<th class="text-right">
|
103 |
-
<?php if ( $payment['coupon'] ) : ?>
|
104 |
-
<?php if ( $payment['coupon']['discount'] ) : ?>
|
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>
|
131 |
-
<td colspan="<?php echo 3 + (int) $deposit_enabled ?>" class="text-right"><button type="button" class="btn btn-success ladda-button" id="bookly-complete-payment" data-spinner-size="40" data-style="zoom-in"><i><?php _e( 'Complete payment', 'bookly' ) ?></i></button></td>
|
132 |
-
</tr>
|
133 |
-
<?php endif ?>
|
134 |
-
</tfoot>
|
135 |
-
</table>
|
136 |
-
</div>
|
137 |
-
<?php endif ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payments/templates/index.php
CHANGED
@@ -1,37 +1,55 @@
|
|
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( 'Payments', 'bookly' ) ?>
|
7 |
</div>
|
8 |
-
<?php \
|
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 col-lg-3">
|
14 |
<div class="bookly-margin-bottom-lg bookly-relative">
|
15 |
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( '-30 day' ) ) ?> - <?php echo date( 'Y-m-d' ) ?>">
|
16 |
<i class="dashicons dashicons-calendar-alt"></i>
|
17 |
<span>
|
18 |
-
<?php echo \
|
19 |
</span>
|
20 |
</button>
|
21 |
</div>
|
22 |
</div>
|
23 |
-
<div class="col-md-
|
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
|
29 |
</option>
|
30 |
<?php endforeach ?>
|
31 |
</select>
|
32 |
</div>
|
33 |
</div>
|
34 |
-
<div class="col-md-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ) : ?>
|
@@ -40,7 +58,7 @@
|
|
40 |
</select>
|
41 |
</div>
|
42 |
</div>
|
43 |
-
<div class="col-md-
|
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 ) : ?>
|
@@ -49,11 +67,21 @@
|
|
49 |
</select>
|
50 |
</div>
|
51 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
</div>
|
53 |
|
54 |
<table id="bookly-payments-list" class="table table-striped" width="100%">
|
55 |
<thead>
|
56 |
<tr>
|
|
|
57 |
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
58 |
<th><?php _e( 'Type', 'bookly' ) ?></th>
|
59 |
<th><?php _e( 'Customer', 'bookly' ) ?></th>
|
@@ -68,20 +96,21 @@
|
|
68 |
</thead>
|
69 |
<tfoot>
|
70 |
<tr>
|
71 |
-
<th colspan="
|
72 |
<th colspan="4"><span id="bookly-payment-total"></span></th>
|
73 |
</tr>
|
74 |
</tfoot>
|
75 |
</table>
|
76 |
<div class="text-right bookly-margin-top-lg">
|
77 |
-
<?php \
|
|
|
78 |
</div>
|
79 |
</div>
|
80 |
</div>
|
81 |
|
82 |
<div ng-app="paymentDetails" ng-controller="paymentDetailsCtrl">
|
83 |
<div payment-details-dialog></div>
|
84 |
-
<?php \
|
85 |
</div>
|
86 |
</div>
|
87 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Lib\Entities\Payment;
|
3 |
+
use Bookly\Backend\Components;
|
4 |
+
use Bookly\Backend\Modules\Payments\Proxy;
|
5 |
+
?>
|
6 |
<div id="bookly-tbs" class="wrap">
|
7 |
<div class="bookly-tbs-body">
|
8 |
<div class="page-header text-right clearfix">
|
9 |
<div class="bookly-page-title">
|
10 |
<?php _e( 'Payments', 'bookly' ) ?>
|
11 |
</div>
|
12 |
+
<?php Components\Support\Buttons::render( $self::pageSlug() ) ?>
|
13 |
</div>
|
14 |
<div class="panel panel-default bookly-main">
|
15 |
<div class="panel-body">
|
16 |
<div class="row">
|
17 |
+
<div class="col-md-1 col-lg-1">
|
18 |
+
<div class="form-group">
|
19 |
+
<input class="form-control" type="text" id="bookly-filter-id" placeholder="<?php esc_attr_e( 'No.', 'bookly' ) ?>" />
|
20 |
+
</div>
|
21 |
+
</div>
|
22 |
<div class="col-md-4 col-lg-3">
|
23 |
<div class="bookly-margin-bottom-lg bookly-relative">
|
24 |
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( '-30 day' ) ) ?> - <?php echo date( 'Y-m-d' ) ?>">
|
25 |
<i class="dashicons dashicons-calendar-alt"></i>
|
26 |
<span>
|
27 |
+
<?php echo Bookly\Lib\Utils\DateTime::formatDate( '-30 days' ) ?> - <?php echo Bookly\Lib\Utils\DateTime::formatDate( 'today' ) ?>
|
28 |
</span>
|
29 |
</button>
|
30 |
</div>
|
31 |
</div>
|
32 |
+
<div class="col-md-1 col-lg-1">
|
33 |
<div class="form-group">
|
34 |
<select id="bookly-filter-type" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Type', 'bookly' ) ?>">
|
35 |
<?php foreach ( $types as $type ) : ?>
|
36 |
<option value="<?php echo esc_attr( $type ) ?>">
|
37 |
+
<?php echo Payment::typeToString( $type ) ?>
|
38 |
</option>
|
39 |
<?php endforeach ?>
|
40 |
</select>
|
41 |
</div>
|
42 |
</div>
|
43 |
+
<div class="col-md-1 col-lg-2">
|
44 |
+
<div class="form-group">
|
45 |
+
<select class="form-control bookly-js-select" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>">
|
46 |
+
<?php foreach ( $customers as $customer ) : ?>
|
47 |
+
<option value="<?php echo $customer['id'] ?>"><?php echo esc_html( $customer['full_name'] ) ?></option>
|
48 |
+
<?php endforeach ?>
|
49 |
+
</select>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
<div class="col-md-1 col-lg-2">
|
53 |
<div class="form-group">
|
54 |
<select id="bookly-filter-staff" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Provider', 'bookly' ) ?>">
|
55 |
<?php foreach ( $providers as $provider ) : ?>
|
58 |
</select>
|
59 |
</div>
|
60 |
</div>
|
61 |
+
<div class="col-md-1 col-lg-2">
|
62 |
<div class="form-group">
|
63 |
<select id="bookly-filter-service" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Service', 'bookly' ) ?>">
|
64 |
<?php foreach ( $services as $service ) : ?>
|
67 |
</select>
|
68 |
</div>
|
69 |
</div>
|
70 |
+
<div class="col-md-1 col-lg-1">
|
71 |
+
<div class="form-group">
|
72 |
+
<select id="bookly-filter-status" class="form-control bookly-js-select" data-placeholder="<?php esc_attr_e( 'Status', 'bookly' ) ?>">
|
73 |
+
<option value="<?php echo Payment::STATUS_COMPLETED ?>"><?php echo Payment::statusToString( Payment::STATUS_COMPLETED ) ?></option>
|
74 |
+
<option value="<?php echo Payment::STATUS_PENDING ?>"><?php echo Payment::statusToString( Payment::STATUS_PENDING ) ?></option>
|
75 |
+
<option value="<?php echo Payment::STATUS_REJECTED ?>"><?php echo Payment::statusToString( Payment::STATUS_REJECTED ) ?></option>
|
76 |
+
</select>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
</div>
|
80 |
|
81 |
<table id="bookly-payments-list" class="table table-striped" width="100%">
|
82 |
<thead>
|
83 |
<tr>
|
84 |
+
<th><?php _e( 'No.', 'bookly' ) ?></th>
|
85 |
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
86 |
<th><?php _e( 'Type', 'bookly' ) ?></th>
|
87 |
<th><?php _e( 'Customer', 'bookly' ) ?></th>
|
96 |
</thead>
|
97 |
<tfoot>
|
98 |
<tr>
|
99 |
+
<th colspan="7"><div class="pull-right"><?php _e( 'Total', 'bookly' ) ?>:</div></th>
|
100 |
<th colspan="4"><span id="bookly-payment-total"></span></th>
|
101 |
</tr>
|
102 |
</tfoot>
|
103 |
</table>
|
104 |
<div class="text-right bookly-margin-top-lg">
|
105 |
+
<?php Proxy\Invoices::renderDownloadButton() ?>
|
106 |
+
<?php Components\Controls\Buttons::renderDelete() ?>
|
107 |
</div>
|
108 |
</div>
|
109 |
</div>
|
110 |
|
111 |
<div ng-app="paymentDetails" ng-controller="paymentDetailsCtrl">
|
112 |
<div payment-details-dialog></div>
|
113 |
+
<?php Components\Dialogs\Payment\Dialog::render() ?>
|
114 |
</div>
|
115 |
</div>
|
116 |
</div>
|
backend/modules/services/Ajax.php
ADDED
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services;
|
3 |
+
|
4 |
+
use Bookly\Backend\Components\Notices\Limitation;
|
5 |
+
use Bookly\Lib;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Ajax
|
9 |
+
* @package Bookly\Backend\Modules\Services
|
10 |
+
*/
|
11 |
+
class Ajax extends Page
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* Get category services
|
15 |
+
*/
|
16 |
+
public static function getCategoryServices()
|
17 |
+
{
|
18 |
+
wp_send_json_success( self::renderTemplate( '_list', self::_getCaSeStCollections(), false ) );
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Add category.
|
23 |
+
*/
|
24 |
+
public static function addCategory()
|
25 |
+
{
|
26 |
+
$html = '';
|
27 |
+
if ( ! empty ( $_POST ) && self::csrfTokenValid() ) {
|
28 |
+
$form = new Forms\Category();
|
29 |
+
$form->bind( self::postParameters() );
|
30 |
+
if ( $category = $form->save() ) {
|
31 |
+
$html = self::renderTemplate( '_category_item', array( 'category' => $category->getFields() ), false );
|
32 |
+
}
|
33 |
+
}
|
34 |
+
wp_send_json_success( compact( 'html' ) );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Update category.
|
39 |
+
*/
|
40 |
+
public static function updateCategory()
|
41 |
+
{
|
42 |
+
$form = new Forms\Category();
|
43 |
+
$form->bind( self::postParameters() );
|
44 |
+
$form->save();
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Update category position.
|
49 |
+
*/
|
50 |
+
public static function updateCategoryPosition()
|
51 |
+
{
|
52 |
+
$category_sorts = self::parameter( 'position' );
|
53 |
+
foreach ( $category_sorts as $position => $category_id ) {
|
54 |
+
$category_sort = new Lib\Entities\Category();
|
55 |
+
$category_sort->load( $category_id );
|
56 |
+
$category_sort->setPosition( $position );
|
57 |
+
$category_sort->save();
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Update services position.
|
63 |
+
*/
|
64 |
+
public static function updateServicesPosition()
|
65 |
+
{
|
66 |
+
$services_sorts = self::parameter( 'position' );
|
67 |
+
foreach ( $services_sorts as $position => $service_id ) {
|
68 |
+
$services_sort = new Lib\Entities\Service();
|
69 |
+
$services_sort->load( $service_id );
|
70 |
+
$services_sort->setPosition( $position );
|
71 |
+
$services_sort->save();
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Delete category.
|
77 |
+
*/
|
78 |
+
public static function deleteCategory()
|
79 |
+
{
|
80 |
+
$category = new Lib\Entities\Category();
|
81 |
+
$category->setId( self::parameter( 'id', 0 ) );
|
82 |
+
$category->delete();
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Add service.
|
87 |
+
*/
|
88 |
+
public static function addService()
|
89 |
+
{
|
90 |
+
! Lib\Config::proActive() &&
|
91 |
+
get_option( 'bookly_updated_from_legacy_version' ) != 'lite' &&
|
92 |
+
Lib\Entities\Service::query()->count() > 4 &&
|
93 |
+
wp_send_json_error( array( 'message' => Limitation::getHtml() ) );
|
94 |
+
|
95 |
+
$form = new Forms\Service();
|
96 |
+
$form->bind( self::postParameters() );
|
97 |
+
$form->getObject()->setDuration( Lib\Config::getTimeSlotLength() );
|
98 |
+
$service = $form->save();
|
99 |
+
$data = self::_getCaSeStCollections( $service->getCategoryId() );
|
100 |
+
|
101 |
+
Proxy\Shared::serviceCreated( $service, self::postParameters() );
|
102 |
+
|
103 |
+
wp_send_json_success( array( 'html' => self::renderTemplate( '_list', $data, false ), 'service_id' => $service->getId() ) );
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* 'Safely' remove services (report if there are future appointments)
|
108 |
+
*/
|
109 |
+
public static function removeServices()
|
110 |
+
{
|
111 |
+
$service_ids = self::parameter( 'service_ids', array() );
|
112 |
+
if ( self::parameter( 'force_delete', false ) ) {
|
113 |
+
if ( is_array( $service_ids ) && ! empty ( $service_ids ) ) {
|
114 |
+
foreach ( $service_ids as $service_id ) {
|
115 |
+
Proxy\Shared::serviceDeleted( $service_id );
|
116 |
+
}
|
117 |
+
Lib\Entities\Service::query( 's' )->delete()->whereIn( 's.id', $service_ids )->execute();
|
118 |
+
}
|
119 |
+
} else {
|
120 |
+
/** @var Lib\Entities\Appointment $appointment */
|
121 |
+
$appointment = Lib\Entities\Appointment::query( 'a' )
|
122 |
+
->whereIn( 'a.service_id', $service_ids )
|
123 |
+
->whereGt( 'a.start_date', current_time( 'mysql' ) )
|
124 |
+
->sortBy( 'a.start_date' )
|
125 |
+
->order( 'DESC' )
|
126 |
+
->limit( '1' )
|
127 |
+
->findOne();
|
128 |
+
|
129 |
+
if ( $appointment ) {
|
130 |
+
$last_month = date_create( $appointment->getStartDate() )->modify( 'last day of' )->format( 'Y-m-d' );
|
131 |
+
$action = 'show_modal';
|
132 |
+
$filter_url = sprintf( '%s#service=%d&range=%s-%s',
|
133 |
+
Lib\Utils\Common::escAdminUrl( \Bookly\Backend\Modules\Appointments\Page::pageSlug() ),
|
134 |
+
$appointment->getServiceId(),
|
135 |
+
date_create( current_time( 'mysql' ) )->format( 'Y-m-d' ),
|
136 |
+
$last_month );
|
137 |
+
wp_send_json_error( compact( 'action', 'filter_url' ) );
|
138 |
+
} else {
|
139 |
+
$action = 'confirm';
|
140 |
+
wp_send_json_error( compact( 'action' ) );
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
wp_send_json_success();
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Update service parameters and assign staff
|
149 |
+
*/
|
150 |
+
public static function updateService()
|
151 |
+
{
|
152 |
+
$form = new Forms\Service();
|
153 |
+
$form->bind( self::postParameters() );
|
154 |
+
$service = $form->save();
|
155 |
+
|
156 |
+
$staff_ids = self::parameter( 'staff_ids', array() );
|
157 |
+
if ( empty ( $staff_ids ) ) {
|
158 |
+
Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->getId() )->execute();
|
159 |
+
} else {
|
160 |
+
Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->getId() )->whereNotIn( 'staff_id', $staff_ids )->execute();
|
161 |
+
if ( $service->getType() == Lib\Entities\Service::TYPE_SIMPLE ) {
|
162 |
+
if ( self::parameter( 'update_staff', false ) ) {
|
163 |
+
Lib\Entities\StaffService::query()
|
164 |
+
->update()
|
165 |
+
->set( 'price', self::parameter( 'price' ) )
|
166 |
+
->set( 'capacity_min', $service->getCapacityMin() )
|
167 |
+
->set( 'capacity_max', $service->getCapacityMax() )
|
168 |
+
->where( 'service_id', self::parameter( 'id' ) )
|
169 |
+
->execute();
|
170 |
+
}
|
171 |
+
// Create records for newly linked staff.
|
172 |
+
$existing_staff_ids = array();
|
173 |
+
$res = Lib\Entities\StaffService::query()
|
174 |
+
->select( 'staff_id' )
|
175 |
+
->where( 'service_id', $service->getId() )
|
176 |
+
->fetchArray();
|
177 |
+
foreach ( $res as $staff ) {
|
178 |
+
$existing_staff_ids[] = $staff['staff_id'];
|
179 |
+
}
|
180 |
+
foreach ( $staff_ids as $staff_id ) {
|
181 |
+
if ( ! in_array( $staff_id, $existing_staff_ids ) ) {
|
182 |
+
$staff_service = new Lib\Entities\StaffService();
|
183 |
+
$staff_service->setStaffId( $staff_id )
|
184 |
+
->setServiceId( $service->getId() )
|
185 |
+
->setPrice( $service->getPrice() )
|
186 |
+
->setCapacityMin( $service->getCapacityMin() )
|
187 |
+
->setCapacityMax( $service->getCapacityMax() )
|
188 |
+
->save();
|
189 |
+
}
|
190 |
+
}
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
// Update services in addons.
|
195 |
+
$alert = Proxy\Shared::updateService( array( 'success' => array( __( 'Settings saved.', 'bookly' ) ) ), $service, self::postParameters() );
|
196 |
+
|
197 |
+
$price = Lib\Utils\Price::format( $service->getPrice() );
|
198 |
+
$nice_duration = Lib\Utils\DateTime::secondsToInterval( $service->getDuration() );
|
199 |
+
$title = $service->getTitle();
|
200 |
+
$colors = array_fill( 0, 3, $service->getColor() );
|
201 |
+
|
202 |
+
wp_send_json_success( Proxy\Shared::prepareUpdateServiceResponse( compact( 'title', 'price', 'colors', 'nice_duration', 'alert' ), $service, self::postParameters() ) );
|
203 |
+
}
|
204 |
+
}
|
backend/modules/services/Controller.php
DELETED
@@ -1,332 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Services;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Services
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-services';
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Index page.
|
16 |
-
*/
|
17 |
-
public function index()
|
18 |
-
{
|
19 |
-
wp_enqueue_media();
|
20 |
-
$this->enqueueStyles( array(
|
21 |
-
'wp' => array( 'wp-color-picker' ),
|
22 |
-
'frontend' => array( 'css/ladda.min.css' ),
|
23 |
-
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css' ),
|
24 |
-
) );
|
25 |
-
|
26 |
-
$this->enqueueScripts( array(
|
27 |
-
'wp' => array( 'wp-color-picker' ),
|
28 |
-
'backend' => array(
|
29 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
30 |
-
'js/help.js' => array( 'jquery' ),
|
31 |
-
'js/alert.js' => array( 'jquery' ),
|
32 |
-
'js/range_tools.js' => array( 'jquery' ),
|
33 |
-
),
|
34 |
-
'module' => array( 'js/service.js' => array( 'jquery-ui-sortable', 'jquery' ) ),
|
35 |
-
'frontend' => array(
|
36 |
-
'js/spin.min.js' => array( 'jquery' ),
|
37 |
-
'js/ladda.min.js' => array( 'bookly-spin.min.js', 'jquery' ),
|
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 |
-
/**
|
64 |
-
*
|
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 |
-
/**
|
90 |
-
* Update category.
|
91 |
-
*/
|
92 |
-
public function executeUpdateCategory()
|
93 |
-
{
|
94 |
-
$form = new Forms\Category();
|
95 |
-
$form->bind( $this->getPostParameters() );
|
96 |
-
$form->save();
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Update category position.
|
101 |
-
*/
|
102 |
-
public function executeUpdateCategoryPosition()
|
103 |
-
{
|
104 |
-
$category_sorts = $this->getParameter( 'position' );
|
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 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Update services position.
|
115 |
-
*/
|
116 |
-
public function executeUpdateServicesPosition()
|
117 |
-
{
|
118 |
-
$services_sorts = $this->getParameter( 'position' );
|
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 |
-
|
165 |
-
public function executeAddService()
|
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 |
-
/**
|
189 |
-
* Update service parameters and assign staff
|
190 |
-
*/
|
191 |
-
public function executeUpdateService()
|
192 |
-
{
|
193 |
-
/** @var \wpdb $wpdb */
|
194 |
-
global $wpdb;
|
195 |
-
|
196 |
-
$form = new Forms\Service();
|
197 |
-
$form->bind( $this->getPostParameters() );
|
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 |
-
/**
|
278 |
-
* @return array
|
279 |
-
*/
|
280 |
-
private function getCategoryCollection()
|
281 |
-
{
|
282 |
-
return Lib\Entities\Category::query()->sortBy( 'position' )->fetchArray();
|
283 |
-
}
|
284 |
-
|
285 |
-
/**
|
286 |
-
* @return array
|
287 |
-
*/
|
288 |
-
private function getStaffCollection()
|
289 |
-
{
|
290 |
-
return Lib\Entities\Staff::query()->fetchArray();
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* @param int $id
|
295 |
-
* @return array
|
296 |
-
*/
|
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/Page.php
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Services
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
wp_enqueue_media();
|
18 |
+
self::enqueueStyles( array(
|
19 |
+
'wp' => array( 'wp-color-picker' ),
|
20 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
21 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css' ),
|
22 |
+
) );
|
23 |
+
|
24 |
+
self::enqueueScripts( array(
|
25 |
+
'wp' => array( 'wp-color-picker' ),
|
26 |
+
'backend' => array(
|
27 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
+
'js/help.js' => array( 'jquery' ),
|
29 |
+
'js/alert.js' => array( 'jquery' ),
|
30 |
+
'js/range_tools.js' => array( 'jquery' ),
|
31 |
+
),
|
32 |
+
'module' => array( 'js/service.js' => array( 'jquery-ui-sortable', 'jquery' ) ),
|
33 |
+
'frontend' => array(
|
34 |
+
'js/spin.min.js' => array( 'jquery' ),
|
35 |
+
'js/ladda.min.js' => array( 'bookly-spin.min.js', 'jquery' ),
|
36 |
+
)
|
37 |
+
) );
|
38 |
+
|
39 |
+
$data = self::_getCaSeStCollections();
|
40 |
+
$staff = array();
|
41 |
+
foreach ( $data['staff_collection'] as $employee ) {
|
42 |
+
$staff[ $employee['id'] ] = $employee['full_name'];
|
43 |
+
}
|
44 |
+
|
45 |
+
wp_localize_script( 'bookly-service.js', 'BooklyL10n', array(
|
46 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
47 |
+
'capacity_error' => __( 'Min capacity should not be greater than max capacity.', 'bookly' ),
|
48 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
49 |
+
'service_special_day' => Lib\Config::specialDaysActive(),
|
50 |
+
'reorder' => esc_attr__( 'Reorder', 'bookly' ),
|
51 |
+
'staff' => $staff,
|
52 |
+
) );
|
53 |
+
|
54 |
+
// Allow add-ons to enqueue their assets.
|
55 |
+
Proxy\Shared::enqueueAssetsForServices();
|
56 |
+
|
57 |
+
self::renderTemplate( 'index', $data );
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Array for rendering service list.
|
62 |
+
*
|
63 |
+
* @param int $category_id
|
64 |
+
* @return array
|
65 |
+
*/
|
66 |
+
protected static function _getCaSeStCollections( $category_id = 0 )
|
67 |
+
{
|
68 |
+
if ( ! $category_id ) {
|
69 |
+
$category_id = self::parameter( 'category_id', 0 );
|
70 |
+
}
|
71 |
+
|
72 |
+
return array(
|
73 |
+
'service_collection' => self::_getServiceCollection( $category_id ),
|
74 |
+
'staff_collection' => self::_getStaffCollection(),
|
75 |
+
'category_collection' => self::_getCategoryCollection(),
|
76 |
+
);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Get category collection.
|
81 |
+
*
|
82 |
+
* @return array
|
83 |
+
*/
|
84 |
+
protected static function _getCategoryCollection()
|
85 |
+
{
|
86 |
+
return Lib\Entities\Category::query()->sortBy( 'position' )->fetchArray();
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Get staff collection.
|
91 |
+
*
|
92 |
+
* @return array
|
93 |
+
*/
|
94 |
+
protected static function _getStaffCollection()
|
95 |
+
{
|
96 |
+
return Lib\Entities\Staff::query()->fetchArray();
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Get service collection.
|
101 |
+
*
|
102 |
+
* @param int $id
|
103 |
+
* @return array
|
104 |
+
*/
|
105 |
+
protected static function _getServiceCollection( $id = 0 )
|
106 |
+
{
|
107 |
+
$services = Lib\Entities\Service::query( 's' )
|
108 |
+
->select( 's.*, COUNT(staff.id) AS total_staff, GROUP_CONCAT(DISTINCT staff.id) AS staff_ids' )
|
109 |
+
->leftJoin( 'StaffService', 'ss', 'ss.service_id = s.id' )
|
110 |
+
->leftJoin( 'Staff', 'staff', 'staff.id = ss.staff_id' )
|
111 |
+
->whereRaw( 's.category_id = %d OR !%d', array( $id, $id ) )
|
112 |
+
->groupBy( 's.id' )
|
113 |
+
->indexBy( 'id' )
|
114 |
+
->sortBy( 's.position' );
|
115 |
+
if ( ! Lib\Config::packagesActive() ) {
|
116 |
+
$services->whereNot( 's.type', Lib\Entities\Service::TYPE_PACKAGE );
|
117 |
+
}
|
118 |
+
$result = $services->fetchArray();
|
119 |
+
foreach ( $result as &$service ) {
|
120 |
+
$service['sub_services'] = Lib\Entities\SubService::query()
|
121 |
+
->where( 'service_id', $service['id'] )
|
122 |
+
->sortBy( 'position' )
|
123 |
+
->fetchArray()
|
124 |
+
;
|
125 |
+
$service['sub_services_count'] = array_sum( array_map( function ( $sub_service ) {
|
126 |
+
return (int) ( $sub_service['type'] == Lib\Entities\SubService::TYPE_SERVICE );
|
127 |
+
}, $service['sub_services'] ) );
|
128 |
+
$service['colors'] = Proxy\Shared::prepareServiceColors( array_fill( 0, 3, $service['color'] ), $service['id'], $service['type'] );
|
129 |
+
}
|
130 |
+
|
131 |
+
return $result;
|
132 |
+
}
|
133 |
+
}
|
backend/modules/services/forms/Category.php
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
|
6 |
/**
|
7 |
* Class Category
|
8 |
* @method Lib\Entities\Category save()
|
9 |
*
|
10 |
-
* @package
|
11 |
*/
|
12 |
class Category extends Lib\Base\Form
|
13 |
{
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Forms;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
|
6 |
/**
|
7 |
* Class Category
|
8 |
* @method Lib\Entities\Category save()
|
9 |
*
|
10 |
+
* @package Bookly\Backend\Modules\Services\Forms
|
11 |
*/
|
12 |
class Category extends Lib\Base\Form
|
13 |
{
|
backend/modules/services/forms/Service.php
CHANGED
@@ -1,13 +1,14 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
|
|
5 |
|
6 |
/**
|
7 |
* Class Service
|
8 |
* @method Lib\Entities\Service getObject
|
9 |
*
|
10 |
-
* @package
|
11 |
*/
|
12 |
class Service extends Lib\Base\Form
|
13 |
{
|
@@ -28,6 +29,7 @@ class Service extends Lib\Base\Form
|
|
28 |
'padding_right',
|
29 |
'package_life_time',
|
30 |
'package_size',
|
|
|
31 |
'appointments_limit',
|
32 |
'limit_period',
|
33 |
'info',
|
@@ -40,6 +42,10 @@ class Service extends Lib\Base\Form
|
|
40 |
'recurrence_frequencies',
|
41 |
'visibility',
|
42 |
'positions',
|
|
|
|
|
|
|
|
|
43 |
);
|
44 |
|
45 |
$this->setFields( $fields );
|
@@ -48,63 +54,40 @@ class Service extends Lib\Base\Form
|
|
48 |
/**
|
49 |
* Bind values to form.
|
50 |
*
|
51 |
-
* @param array $
|
52 |
* @param array $files
|
53 |
*/
|
54 |
-
public function bind( array $
|
55 |
{
|
56 |
// Field with NULL
|
57 |
-
if ( array_key_exists( 'category_id', $
|
58 |
-
$
|
59 |
}
|
60 |
|
61 |
-
parent::bind( $
|
62 |
}
|
63 |
|
64 |
/**
|
65 |
-
* @return \
|
66 |
*/
|
67 |
public function save()
|
68 |
{
|
69 |
if ( $this->isNew() ) {
|
70 |
// When adding new service - set its color randomly.
|
71 |
$this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
|
72 |
-
}
|
73 |
-
|
74 |
-
|
75 |
-
|
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 |
-
|
90 |
-
|
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
|
108 |
}
|
109 |
|
110 |
}
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Forms;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Backend\Modules\Services\Proxy;
|
6 |
|
7 |
/**
|
8 |
* Class Service
|
9 |
* @method Lib\Entities\Service getObject
|
10 |
*
|
11 |
+
* @package Bookly\Backend\Modules\Services\Forms
|
12 |
*/
|
13 |
class Service extends Lib\Base\Form
|
14 |
{
|
29 |
'padding_right',
|
30 |
'package_life_time',
|
31 |
'package_size',
|
32 |
+
'package_unassigned',
|
33 |
'appointments_limit',
|
34 |
'limit_period',
|
35 |
'info',
|
42 |
'recurrence_frequencies',
|
43 |
'visibility',
|
44 |
'positions',
|
45 |
+
'taxes',
|
46 |
+
'unit_duration',
|
47 |
+
'units_min',
|
48 |
+
'units_max',
|
49 |
);
|
50 |
|
51 |
$this->setFields( $fields );
|
54 |
/**
|
55 |
* Bind values to form.
|
56 |
*
|
57 |
+
* @param array $params
|
58 |
* @param array $files
|
59 |
*/
|
60 |
+
public function bind( array $params, array $files = array() )
|
61 |
{
|
62 |
// Field with NULL
|
63 |
+
if ( array_key_exists( 'category_id', $params ) && ! $params['category_id'] ) {
|
64 |
+
$params['category_id'] = null;
|
65 |
}
|
66 |
|
67 |
+
parent::bind( $params, $files );
|
68 |
}
|
69 |
|
70 |
/**
|
71 |
+
* @return \Bookly\Lib\Entities\Service
|
72 |
*/
|
73 |
public function save()
|
74 |
{
|
75 |
if ( $this->isNew() ) {
|
76 |
// When adding new service - set its color randomly.
|
77 |
$this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
|
78 |
+
} else {
|
79 |
+
if ( $this->data['type'] == Lib\Entities\Service::TYPE_SIMPLE ) {
|
80 |
+
Lib\Entities\SubService::query()->delete()->where( 'service_id', $this->data['id'] )->execute();
|
81 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
if ( $this->data['limit_period'] == 'off' || ! $this->data['appointments_limit'] ) {
|
84 |
+
$this->data['appointments_limit'] = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
+
|
87 |
+
$this->data = Proxy\Shared::prepareUpdateService( $this->data );
|
88 |
}
|
89 |
|
90 |
+
return parent::save();
|
91 |
}
|
92 |
|
93 |
}
|
backend/modules/services/proxy/CompoundServices.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CompoundServices
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSubForm( array $service, array $service_collection ) Render compound services sub-form.
|
11 |
+
*/
|
12 |
+
abstract class CompoundServices extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/services/proxy/CustomDuration.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerGroups
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareServiceDurationOptions( array $options, array $service ) Add "Custom" option to service duration select.
|
11 |
+
* @method static void renderServiceDurationFields( array $service ) Render services duration(units) fields.
|
12 |
+
* @method static void renderServiceDurationHelp() Render services duration help tip.
|
13 |
+
* @method static void renderServicePriceLabel( $service_id ) Render service price label.
|
14 |
+
*/
|
15 |
+
abstract class CustomDuration extends Lib\Base\Proxy
|
16 |
+
{
|
17 |
+
|
18 |
+
}
|
backend/modules/services/proxy/CustomerGroups.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class CustomerGroups
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSubForm( array $service ) Render customer groups sub-form.
|
11 |
+
* @method static void renderVisibilityOption( array $service ) Render services visibility option 'based on groups'.
|
12 |
+
*/
|
13 |
+
abstract class CustomerGroups extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/services/proxy/GroupBooking.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class GroupBooking
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCapacity( array $service ) Render Capacity (min & max) controls on service settings.
|
11 |
+
*/
|
12 |
+
abstract class GroupBooking extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/services/proxy/Packages.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Packages
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSubForm( array $service, array $service_collection )
|
11 |
+
*/
|
12 |
+
abstract class Packages extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/services/proxy/Pro.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
*
|
9 |
+
* @package Bookly\Backend\Components\Service\Proxy
|
10 |
+
*
|
11 |
+
* @method static void renderLimitAppointmentsPerCustomer( array $service ) Render limit appointments rules per customer.
|
12 |
+
* @method static void renderPadding( array $service ) Render padding settings for service.
|
13 |
+
* @method static void renderStaffPreference( array $service ) Render staff preference rules "any" is selected.
|
14 |
+
* @method static void renderVisibility( array $service ) Render visibility option for service.
|
15 |
+
*/
|
16 |
+
abstract class Pro extends Lib\Base\Proxy
|
17 |
+
{
|
18 |
+
|
19 |
+
}
|
backend/modules/services/proxy/Shared.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void enqueueAssetsForServices() Enqueue assets for page Services.
|
11 |
+
* @method static array prepareServiceColors( array $colors, int $service_id, int $service_type ) Prepare colors for service.
|
12 |
+
* @method static array prepareUpdateService( array $data ) Prepare update service settings in add-ons.
|
13 |
+
* @method static array prepareUpdateServiceResponse( array $response, Lib\Entities\Service $service, array $_post ) Prepare response for updated service.
|
14 |
+
* @method static void renderAfterServiceList( array $service_collection ) Render content after services forms.
|
15 |
+
* @method static void renderServiceForm( array $service ) Render content in service form.
|
16 |
+
* @method static void renderServiceFormHead( array $service ) Render top content in service form.
|
17 |
+
* @method static array serviceCreated( Lib\Entities\Service $service, array $_post ) Service created.
|
18 |
+
* @method static void serviceDeleted( int $service_id ) Service deleted.
|
19 |
+
* @method static array updateService( array $alert, Lib\Entities\Service $service, array $_post ) Update service settings in add-ons.
|
20 |
+
*/
|
21 |
+
abstract class Shared extends Lib\Base\Proxy
|
22 |
+
{
|
23 |
+
|
24 |
+
}
|
backend/modules/services/proxy/Taxes.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Services\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Taxes
|
8 |
+
* @package Bookly\Backend\Modules\Services\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderSubForm( array $service ) Render taxes sub-form.
|
11 |
+
*/
|
12 |
+
abstract class Taxes extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/services/resources/js/service.js
CHANGED
@@ -1,10 +1,13 @@
|
|
1 |
jQuery(function($) {
|
2 |
-
var $no_result = $('#bookly-services-wrapper .no-result');
|
3 |
// Remember user choice in the modal dialog.
|
4 |
-
var update_staff_choice
|
|
|
5 |
$new_category_popover = $('#bookly-new-category'),
|
6 |
-
$new_category_form
|
7 |
-
$new_category_name
|
|
|
|
|
|
|
8 |
|
9 |
$new_category_popover.popover({
|
10 |
html: true,
|
@@ -140,20 +143,28 @@ jQuery(function($) {
|
|
140 |
data['category_id'] = selected_category_id;
|
141 |
}
|
142 |
$.post(ajaxurl, data, function(response) {
|
143 |
-
|
|
|
|
|
|
|
|
|
144 |
ladda.stop();
|
145 |
});
|
146 |
})
|
147 |
// On click on 'Delete' button.
|
148 |
.on('click', '#bookly-delete', function(e) {
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
|
|
|
|
|
|
|
|
157 |
$for_delete.each(function(){
|
158 |
var panel = $(this).parents('.bookly-js-collapse');
|
159 |
$panels.push(panel);
|
@@ -167,9 +178,29 @@ jQuery(function($) {
|
|
167 |
}
|
168 |
});
|
169 |
data['service_ids[]'] = services;
|
170 |
-
|
171 |
-
|
172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
$.each($panels.reverse(), function (index) {
|
174 |
$(this).delay(500 * index).fadeOut(200, function () {
|
175 |
$(this).remove();
|
@@ -177,8 +208,11 @@ jQuery(function($) {
|
|
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 () {
|
@@ -189,9 +223,9 @@ jQuery(function($) {
|
|
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
|
193 |
service_type = $form.find('.bookly-js-service-type input[name="type"]:checked').val(),
|
194 |
-
staff_index
|
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) {
|
@@ -207,23 +241,37 @@ jQuery(function($) {
|
|
207 |
});
|
208 |
}
|
209 |
updateSelectorButton($container);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
});
|
211 |
|
212 |
// Modal window events.
|
213 |
-
|
214 |
-
$modal
|
215 |
.on('click', '.bookly-yes', function() {
|
216 |
-
$
|
217 |
if ( $('#bookly-remember-my-choice').prop('checked') ) {
|
218 |
update_staff_choice = true;
|
219 |
}
|
220 |
-
submitServiceFrom($
|
221 |
})
|
222 |
.on('click', '.bookly-no', function() {
|
223 |
if ( $('#bookly-remember-my-choice').prop('checked') ) {
|
224 |
update_staff_choice = false;
|
225 |
}
|
226 |
-
submitServiceFrom($
|
227 |
});
|
228 |
|
229 |
function refreshList(response,service_id) {
|
@@ -252,19 +300,6 @@ jQuery(function($) {
|
|
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);
|
259 |
-
}
|
260 |
-
}).on('change', '[name=padding_left],[name=padding_right]', function(){
|
261 |
-
if ($(this).val() > 0) {
|
262 |
-
booklyAlert({error: [BooklyL10n.limitations]});
|
263 |
-
$(this).val('0').prop('readonly', true);
|
264 |
-
$(this).find('option:gt(0)').prop('disabled', true);
|
265 |
-
}
|
266 |
-
});
|
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);
|
@@ -393,10 +428,9 @@ jQuery(function($) {
|
|
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() == '') {
|
@@ -430,7 +464,7 @@ jQuery(function($) {
|
|
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 |
|
@@ -439,6 +473,15 @@ jQuery(function($) {
|
|
439 |
checkCapacityError($(this).parents('.bookly-js-collapse'));
|
440 |
});
|
441 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
442 |
$panel
|
443 |
.find('.ajax-service-send').on('click', function (e) {
|
444 |
e.preventDefault();
|
@@ -452,7 +495,7 @@ jQuery(function($) {
|
|
452 |
});
|
453 |
}
|
454 |
if (show_modal) {
|
455 |
-
$
|
456 |
} else {
|
457 |
submitServiceFrom($form, update_staff_choice);
|
458 |
}
|
@@ -463,15 +506,21 @@ jQuery(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 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
|
|
475 |
});
|
476 |
|
477 |
$panel
|
@@ -491,7 +540,7 @@ jQuery(function($) {
|
|
491 |
$.ajax({
|
492 |
type : 'POST',
|
493 |
url : ajaxurl,
|
494 |
-
data : {action: '
|
495 |
});
|
496 |
}
|
497 |
});
|
1 |
jQuery(function($) {
|
|
|
2 |
// Remember user choice in the modal dialog.
|
3 |
+
var update_staff_choice = null,
|
4 |
+
$no_result = $('#bookly-services-wrapper .no-result'),
|
5 |
$new_category_popover = $('#bookly-new-category'),
|
6 |
+
$new_category_form = $('#new-category-form'),
|
7 |
+
$new_category_name = $('#bookly-category-name'),
|
8 |
+
$update_service_modal = $('#bookly-update-service-settings'),
|
9 |
+
$delete_cascade_modal = $('.bookly-js-delete-cascade-confirm')
|
10 |
+
;
|
11 |
|
12 |
$new_category_popover.popover({
|
13 |
html: true,
|
143 |
data['category_id'] = selected_category_id;
|
144 |
}
|
145 |
$.post(ajaxurl, data, function(response) {
|
146 |
+
if(response.success) {
|
147 |
+
refreshList(response.data.html, response.data.service_id);
|
148 |
+
} else {
|
149 |
+
booklyAlert({error: [response.data.message]});
|
150 |
+
}
|
151 |
ladda.stop();
|
152 |
});
|
153 |
})
|
154 |
// On click on 'Delete' button.
|
155 |
.on('click', '#bookly-delete', function(e) {
|
156 |
+
e.preventDefault();
|
157 |
+
var data = {
|
158 |
+
action: 'bookly_remove_services',
|
159 |
+
csrf_token: BooklyL10n.csrf_token
|
160 |
+
},
|
161 |
+
services = [],
|
162 |
+
$panels = [],
|
163 |
+
$for_delete = $('.service-checker:checked'),
|
164 |
+
button = this;
|
165 |
+
|
166 |
+
var delete_services = function (ajaxurl, data) {
|
167 |
+
var ladda = rangeTools.ladda(button);
|
168 |
$for_delete.each(function(){
|
169 |
var panel = $(this).parents('.bookly-js-collapse');
|
170 |
$panels.push(panel);
|
178 |
}
|
179 |
});
|
180 |
data['service_ids[]'] = services;
|
181 |
+
|
182 |
+
$.post(ajaxurl, data, function (response) {
|
183 |
+
if (!response.success) {
|
184 |
+
switch (response.data.action) {
|
185 |
+
case 'show_modal':
|
186 |
+
$delete_cascade_modal
|
187 |
+
.modal('show');
|
188 |
+
$('.bookly-js-delete', $delete_cascade_modal).off().on('click', function () {
|
189 |
+
delete_services(ajaxurl, $.extend(data, {force_delete: true}));
|
190 |
+
$delete_cascade_modal.modal('hide');
|
191 |
+
});
|
192 |
+
$('.bookly-js-edit', $delete_cascade_modal).off().on('click', function () {
|
193 |
+
rangeTools.ladda(this);
|
194 |
+
window.location.href = response.data.filter_url;
|
195 |
+
});
|
196 |
+
break;
|
197 |
+
case 'confirm':
|
198 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
199 |
+
delete_services(ajaxurl, $.extend(data, {force_delete: true}));
|
200 |
+
}
|
201 |
+
break;
|
202 |
+
}
|
203 |
+
} else {
|
204 |
$.each($panels.reverse(), function (index) {
|
205 |
$(this).delay(500 * index).fadeOut(200, function () {
|
206 |
$(this).remove();
|
208 |
});
|
209 |
$(document.body).trigger( 'service.deleted', [ services ] );
|
210 |
}
|
211 |
+
ladda.stop();
|
212 |
});
|
213 |
+
};
|
214 |
+
|
215 |
+
delete_services(ajaxurl, data);
|
216 |
})
|
217 |
|
218 |
.on('change', 'input.bookly-check-all-entities, input.bookly-js-check-entity', function () {
|
223 |
$container.find('.bookly-check-all-entities').prop('checked', $container.find('.bookly-js-check-entity:not(:checked)').length == 0);
|
224 |
}
|
225 |
var $form = $(this).closest('.panel.bookly-js-collapse'),
|
226 |
+
service_id = $form.data('service-id'),
|
227 |
service_type = $form.find('.bookly-js-service-type input[name="type"]:checked').val(),
|
228 |
+
staff_index = $(this).closest('li').index() + 1;
|
229 |
if (service_type == 'simple' && !$(this).is(':checked')) {
|
230 |
$('#services_list .panel.bookly-js-collapse').each(function () {
|
231 |
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) {
|
241 |
});
|
242 |
}
|
243 |
updateSelectorButton($container);
|
244 |
+
})
|
245 |
+
|
246 |
+
.on('change', '.bookly-js-service-unit-duration', function () {
|
247 |
+
var $service = $(this).closest('.panel'),
|
248 |
+
$custom_duration = $service.find('.bookly-js-service-unit-fields');
|
249 |
+
if ($(this).val() == 'custom') {
|
250 |
+
$service.find('.bookly-js-price-label').hide();
|
251 |
+
$service.find('.bookly-js-unit-price-label').show();
|
252 |
+
$custom_duration.show();
|
253 |
+
} else {
|
254 |
+
$service.find('.bookly-js-price-label').show();
|
255 |
+
$service.find('.bookly-js-unit-price-label').hide();
|
256 |
+
$custom_duration.find('[name="unit_duration"]').val($(this).val())
|
257 |
+
$custom_duration.hide();
|
258 |
+
}
|
259 |
});
|
260 |
|
261 |
// Modal window events.
|
262 |
+
$update_service_modal
|
|
|
263 |
.on('click', '.bookly-yes', function() {
|
264 |
+
$update_service_modal.modal('hide');
|
265 |
if ( $('#bookly-remember-my-choice').prop('checked') ) {
|
266 |
update_staff_choice = true;
|
267 |
}
|
268 |
+
submitServiceFrom($update_service_modal.data('input'),true);
|
269 |
})
|
270 |
.on('click', '.bookly-no', function() {
|
271 |
if ( $('#bookly-remember-my-choice').prop('checked') ) {
|
272 |
update_staff_choice = false;
|
273 |
}
|
274 |
+
submitServiceFrom($update_service_modal.data('input'),false);
|
275 |
});
|
276 |
|
277 |
function refreshList(response,service_id) {
|
300 |
});
|
301 |
}
|
302 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
303 |
function submitServiceFrom($form, update_staff) {
|
304 |
$form.find('input[name=update_staff]').val(update_staff ? 1 : 0);
|
305 |
$form.find('input[name=package_service_changed]').val($form.find('[name=package_service]').data('last_value') != $form.find('[name=package_service]').val() ? 1 : 0);
|
428 |
function onCollapseInitServiceForm() {
|
429 |
$('.panel-collapse').on('show.bs.collapse.bookly', function () {
|
430 |
var $panel = $(this),
|
431 |
+
$staff_preference = $panel.find('[name=staff_preference]:last-child'),
|
432 |
$staff_list = $panel.find('.bookly-staff-list'),
|
433 |
$staff_box = $panel.find('.bookly-preference-box');
|
|
|
434 |
$staff_preference.on('change', function () {
|
435 |
/** @see Service::PREFERRED_ORDER */
|
436 |
if ($(this).val() == 'order' && $staff_list.html() == '') {
|
464 |
});
|
465 |
|
466 |
$panel
|
467 |
+
.find('[name=duration], [name=unit_duration]').on('change', function () {
|
468 |
$panel.find('[name=start_time_info]').closest('.form-group').toggle($(this).val() >= 86400);
|
469 |
}).trigger('change');
|
470 |
|
473 |
checkCapacityError($(this).parents('.bookly-js-collapse'));
|
474 |
});
|
475 |
|
476 |
+
$panel
|
477 |
+
.find('.bookly-js-visibility').on('change', function () {
|
478 |
+
if ($(this).val() == 'group') {
|
479 |
+
$panel.find('.bookly-js-groups-list').show();
|
480 |
+
} else {
|
481 |
+
$panel.find('.bookly-js-groups-list').hide();
|
482 |
+
}
|
483 |
+
});
|
484 |
+
|
485 |
$panel
|
486 |
.find('.ajax-service-send').on('click', function (e) {
|
487 |
e.preventDefault();
|
495 |
});
|
496 |
}
|
497 |
if (show_modal) {
|
498 |
+
$update_service_modal.data('input', $form).modal('show');
|
499 |
} else {
|
500 |
submitServiceFrom($form, update_staff_choice);
|
501 |
}
|
506 |
$(this).parents('form').trigger('reset');
|
507 |
var $color = $(this).parents('form').find('.wp-color-picker'),
|
508 |
$panel = $(this).parents('.bookly-js-collapse');
|
509 |
+
$.each($('.bookly-js-entity-selector-container',$panel), function () {
|
510 |
+
updateSelectorButton($(this));
|
511 |
+
});
|
512 |
+
checkCapacityError($panel);
|
513 |
+
|
514 |
$staff_list.html('');
|
515 |
$staff_preference.trigger('change');
|
516 |
$color.val($color.data('last-color')).trigger('change');
|
517 |
$panel.find('.parent-range-start').trigger('change');
|
518 |
$panel.find('input[name=type]:checked').trigger('change');
|
519 |
+
$panel.find('.bookly-js-visibility').trigger('change');
|
520 |
+
$panel.find('.bookly-js-service-unit-duration').trigger('change');
|
521 |
+
setTimeout(function () {
|
522 |
+
$(document.body).trigger('service.resetForm', [$panel, $panel.closest('.panel').data('service-id')]);
|
523 |
+
}, 50);
|
524 |
});
|
525 |
|
526 |
$panel
|
540 |
$.ajax({
|
541 |
type : 'POST',
|
542 |
url : ajaxurl,
|
543 |
+
data : {action: 'bookly_pro_update_service_staff_preference_orders', service_id: $(this).data('service_id'), positions: positions, csrf_token: BooklyL10n.csrf_token}
|
544 |
});
|
545 |
}
|
546 |
});
|
backend/modules/services/templates/_list.php
CHANGED
@@ -1,11 +1,12 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
5 |
-
use
|
6 |
-
use
|
|
|
|
|
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">
|
@@ -27,9 +28,9 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
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] ) ?>"> </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'] ==
|
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'] !=
|
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"
|
@@ -77,7 +78,7 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
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'] ==
|
81 |
</label>
|
82 |
</div>
|
83 |
</div>
|
@@ -94,43 +95,31 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
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::
|
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="
|
107 |
-
|
108 |
-
<
|
109 |
-
<option value="public" <?php selected( $service['visibility'], 'public' ) ?>><?php _e( 'Public', 'bookly' ) ?></option>
|
110 |
-
<option value="private" <?php selected( $service['visibility'], 'private' ) ?>><?php _e( 'Private', 'bookly' ) ?></option>
|
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">
|
@@ -138,70 +127,45 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
138 |
<label for="duration_<?php echo $service_id ?>">
|
139 |
<?php _e( 'Duration', 'bookly' ) ?>
|
140 |
</label>
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
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' ) ?>
|
151 |
-
</label>
|
152 |
-
<p class="help-block"><?php _e( 'Set padding time before and/or after an appointment. For example, if you require 15 minutes to prepare for the next appointment then you should set "padding before" to 15 min. If there is an appointment from 8:00 to 9:00 then the next available time slot will be 9:15 rather than 9:00.', 'bookly' ) ?></p>
|
153 |
-
<div class="row">
|
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 |
-
<
|
189 |
-
|
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 |
|
@@ -253,36 +217,9 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
253 |
</div>
|
254 |
</div>
|
255 |
</div>
|
256 |
-
|
257 |
-
<
|
258 |
-
|
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' ) ?>
|
@@ -293,16 +230,18 @@ $time_interval = get_option( 'bookly_gen_time_slot_length' );
|
|
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::
|
|
|
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
|
304 |
-
<?php
|
305 |
-
<?php
|
306 |
</div>
|
307 |
</form>
|
308 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
use Bookly\Backend\Modules\Services\Proxy;
|
5 |
+
use Bookly\Lib\Utils\Common;
|
6 |
+
use Bookly\Lib\Utils\DateTime;
|
7 |
+
use Bookly\Lib\Utils\Price;
|
8 |
+
use Bookly\Lib\Entities\Service;
|
9 |
|
|
|
10 |
?>
|
11 |
<?php if ( ! empty( $service_collection ) ) : ?>
|
12 |
<div class="panel-group" id="services_list" role="tablist" aria-multiselectable="true">
|
28 |
<span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package"
|
29 |
style="background-color: <?php echo esc_attr( $service['colors'][0] == '-1' ? 'grey' : $service['colors'][0] ) ?>"> </span>
|
30 |
<span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-compound bookly-js-service-package"
|
31 |
+
style="background-color: <?php echo esc_attr( $service['colors'][1] == '-1' ? 'grey' : $service['colors'][1] ) ?>; <?php if ( $service['type'] == Service::TYPE_SIMPLE ) : ?>display: none;<?php endif ?>"> </span>
|
32 |
<span class="bookly-service-color bookly-margin-right-sm bookly-js-service bookly-js-service-package"
|
33 |
+
style="background-color: <?php echo esc_attr( $service['colors'][2] == '-1' ? 'grey' : $service['colors'][2] ) ?>; <?php if ( $service['type'] != Service::TYPE_PACKAGE ) : ?>display: none;<?php endif ?>"> </span>
|
34 |
</div>
|
35 |
<div class="bookly-flex-cell bookly-vertical-middle">
|
36 |
<a role="button" class="panel-title collapsed bookly-js-service-title" data-toggle="collapse"
|
78 |
<div class="form-group">
|
79 |
<div class="radio">
|
80 |
<label class="bookly-margin-right-md">
|
81 |
+
<input type="radio" name="type" value="simple" data-panel-class="panel-default" <?php echo checked( $service['type'] == Service::TYPE_SIMPLE ) ?>><?php _e( 'Simple', 'bookly' ) ?>
|
82 |
</label>
|
83 |
</div>
|
84 |
</div>
|
95 |
<div class="form-group">
|
96 |
<label><?php _e( 'Color', 'bookly' ) ?></label>
|
97 |
<div class="bookly-color-picker-wrapper">
|
98 |
+
<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" />
|
99 |
</div>
|
100 |
</div>
|
101 |
</div>
|
102 |
</div>
|
103 |
+
<?php Proxy\Packages::renderSubForm( $service, $service_collection ) ?>
|
104 |
<div class="row">
|
105 |
+
<input type="hidden" name="visibility" value="public">
|
106 |
+
<?php Proxy\Pro::renderVisibility( $service ) ?>
|
107 |
<div class="col-sm-4 bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
|
108 |
<div class="form-group">
|
109 |
+
<label for="price_<?php echo $service_id ?>" class="bookly-js-price-label"><?php _e( 'Price', 'bookly' ) ?></label>
|
110 |
+
<?php Proxy\CustomDuration::renderServicePriceLabel( $service_id ) ?>
|
111 |
+
<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'] ) ?>" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
</div>
|
113 |
</div>
|
114 |
+
<input type="hidden" name="capacity_min" value="1">
|
115 |
+
<input type="hidden" name="capacity_max" value="1">
|
116 |
+
<?php Proxy\GroupBooking::renderCapacity( $service ) ?>
|
117 |
</div>
|
118 |
|
119 |
+
<?php Proxy\Taxes::renderSubForm( $service ) ?>
|
120 |
+
|
121 |
+
<?php Proxy\CustomerGroups::renderSubForm( $service ) ?>
|
122 |
+
|
123 |
<div class="bookly-js-service bookly-js-service-simple">
|
124 |
<div class="row">
|
125 |
<div class="col-sm-4 bookly-js-service bookly-js-service-simple">
|
127 |
<label for="duration_<?php echo $service_id ?>">
|
128 |
<?php _e( 'Duration', 'bookly' ) ?>
|
129 |
</label>
|
130 |
+
<?php Proxy\CustomDuration::renderServiceDurationHelp() ?>
|
131 |
+
<?php
|
132 |
+
$options = Common::getDurationSelectOptions( $service['duration'] );
|
133 |
+
$options = Proxy\CustomDuration::prepareServiceDurationOptions( $options, $service );
|
134 |
+
?>
|
135 |
+
<select id="duration_<?php echo $service_id ?>" class="bookly-js-service-unit-duration form-control" name="duration">
|
136 |
+
<?php foreach ( $options as $option ): ?><option value="<?php echo $option['value']; ?>" <?php echo $option['selected']; ?>><?php echo $option['label']; ?></option><?php endforeach; ?>
|
137 |
</select>
|
138 |
</div>
|
139 |
</div>
|
140 |
+
|
141 |
+
<input type="hidden" name="padding_left" value="0">
|
142 |
+
<input type="hidden" name="padding_right" value="0">
|
143 |
+
<?php Proxy\Pro::renderPadding( $service ) ?>
|
144 |
+
</div>
|
145 |
+
<?php Proxy\CustomDuration::renderServiceDurationFields( $service ); ?>
|
146 |
+
</div>
|
147 |
+
<div class="bookly-js-service bookly-js-service-simple">
|
148 |
+
<div class="row">
|
149 |
<div class="col-sm-8 bookly-js-service bookly-js-service-simple">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
<div class="form-group">
|
151 |
<label for="start_time_info_<?php echo $service_id ?>"><?php _e( 'Start and end times of the appointment', 'bookly' ) ?></label>
|
152 |
+
<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, backend calendar and codes for booking form.', 'bookly' ) ?></p>
|
153 |
<div class="row">
|
154 |
<div class="col-xs-6">
|
155 |
+
<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'] ) ?>" />
|
156 |
</div>
|
157 |
<div class="col-xs-6">
|
158 |
+
<input class="form-control" type="text" name="end_time_info" value="<?php echo esc_attr( $service['end_time_info'] ) ?>" />
|
159 |
</div>
|
160 |
</div>
|
161 |
</div>
|
162 |
</div>
|
163 |
</div>
|
164 |
</div>
|
|
|
165 |
<div class="bookly-js-service bookly-js-service-simple">
|
166 |
<div class="row">
|
167 |
+
<input type="hidden" name="staff_preference" value="<?php echo Service::PREFERRED_LEAST_EXPENSIVE ?>" data-default="[]">
|
168 |
+
<?php Proxy\Pro::renderStaffPreference( $service ) ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
</div>
|
170 |
</div>
|
171 |
|
217 |
</div>
|
218 |
</div>
|
219 |
</div>
|
220 |
+
<input type="hidden" name="appointments_limit" value="">
|
221 |
+
<input type="hidden" name="limit_period" value="off">
|
222 |
+
<?php Proxy\Pro::renderLimitAppointmentsPerCustomer( $service ) ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
<div class="form-group bookly-js-service bookly-js-service-simple bookly-js-service-compound bookly-js-service-package">
|
224 |
<label for="info_<?php echo $service_id ?>">
|
225 |
<?php _e( 'Info', 'bookly' ) ?>
|
230 |
<textarea class="form-control" id="info_<?php echo $service_id ?>" name="info" rows="3" type="text"><?php echo esc_textarea( $service['info'] ) ?></textarea>
|
231 |
</div>
|
232 |
|
233 |
+
<?php Proxy\CompoundServices::renderSubForm( $service, $service_collection ) ?>
|
234 |
+
|
235 |
<?php Proxy\Shared::renderServiceForm( $service ) ?>
|
236 |
+
|
237 |
<div class="panel-footer">
|
238 |
<input type="hidden" name="action" value="bookly_update_service">
|
239 |
<input type="hidden" name="id" value="<?php echo esc_html( $service_id ) ?>">
|
240 |
<input type="hidden" name="update_staff" value="0">
|
241 |
<span class="bookly-js-services-error text-danger"></span>
|
242 |
+
<?php Inputs::renderCsrf() ?>
|
243 |
+
<?php Buttons::renderSubmit( null, 'ajax-service-send' ) ?>
|
244 |
+
<?php Buttons::renderReset( null, 'js-reset' ) ?>
|
245 |
</div>
|
246 |
</form>
|
247 |
</div>
|
backend/modules/services/templates/index.php
CHANGED
@@ -1,11 +1,16 @@
|
|
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( 'Services', 'bookly' ) ?>
|
7 |
</div>
|
8 |
-
<?php
|
9 |
</div>
|
10 |
<div class="row">
|
11 |
<div id="bookly-sidebar" class="col-sm-4">
|
@@ -14,7 +19,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 ) $
|
18 |
</ul>
|
19 |
</div>
|
20 |
|
@@ -32,7 +37,7 @@
|
|
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
|
36 |
</div>
|
37 |
</div>
|
38 |
|
@@ -66,7 +71,7 @@
|
|
66 |
<?php include '_list.php' ?>
|
67 |
</div>
|
68 |
<div class="text-right">
|
69 |
-
<?php
|
70 |
</div>
|
71 |
</div>
|
72 |
</div>
|
@@ -99,4 +104,5 @@
|
|
99 |
</div>
|
100 |
</div>
|
101 |
</div>
|
|
|
102 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
use Bookly\Backend\Components\Dialogs;
|
5 |
+
use Bookly\Backend\Components\Support;
|
6 |
+
?>
|
7 |
<div id="bookly-tbs" class="wrap">
|
8 |
<div class="bookly-tbs-body">
|
9 |
<div class="page-header text-right clearfix">
|
10 |
<div class="bookly-page-title">
|
11 |
<?php _e( 'Services', 'bookly' ) ?>
|
12 |
</div>
|
13 |
+
<?php Support\Buttons::render( $self::pageSlug() ) ?>
|
14 |
</div>
|
15 |
<div class="row">
|
16 |
<div id="bookly-sidebar" class="col-sm-4">
|
19 |
<div class="bookly-padding-vertical-xs"><?php _e( 'All Services', 'bookly' ) ?></div>
|
20 |
</div>
|
21 |
<ul id="bookly-category-item-list">
|
22 |
+
<?php foreach ( $category_collection as $category ) $self::renderTemplate( '_category_item', compact( 'category' ) ) ?>
|
23 |
</ul>
|
24 |
</div>
|
25 |
|
37 |
<label for="bookly-category-name"><?php _e( 'Name', 'bookly' ) ?></label>
|
38 |
<input class="form-control" id="bookly-category-name" type="text" name="name" />
|
39 |
<input type="hidden" name="action" value="bookly_add_category" />
|
40 |
+
<?php Inputs::renderCsrf() ?>
|
41 |
</div>
|
42 |
</div>
|
43 |
|
71 |
<?php include '_list.php' ?>
|
72 |
</div>
|
73 |
<div class="text-right">
|
74 |
+
<?php Buttons::renderDelete() ?>
|
75 |
</div>
|
76 |
</div>
|
77 |
</div>
|
104 |
</div>
|
105 |
</div>
|
106 |
</div>
|
107 |
+
<?php Dialogs\Common\CascadeDelete::render() ?>
|
108 |
</div>
|
backend/modules/settings/Ajax.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Settings
|
9 |
+
*/
|
10 |
+
class Ajax extends Page
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Ajax request for Holidays calendar
|
14 |
+
*/
|
15 |
+
public static function settingsHoliday()
|
16 |
+
{
|
17 |
+
$id = self::parameter( 'id', false );
|
18 |
+
$day = self::parameter( 'day', false );
|
19 |
+
$holiday = self::parameter( 'holiday' ) == 'true';
|
20 |
+
$repeat = (int) ( self::parameter( 'repeat' ) == 'true' );
|
21 |
+
|
22 |
+
// update or delete the event
|
23 |
+
if ( $id ) {
|
24 |
+
if ( $holiday ) {
|
25 |
+
Lib\Entities\Holiday::query()
|
26 |
+
->update()
|
27 |
+
->set( 'repeat_event', $repeat )
|
28 |
+
->where( 'id', $id )
|
29 |
+
->where( 'parent_id', $id , 'OR' )
|
30 |
+
->execute();
|
31 |
+
} else {
|
32 |
+
Lib\Entities\Holiday::query()
|
33 |
+
->delete()
|
34 |
+
->where( 'id', $id )
|
35 |
+
->where( 'parent_id', $id, 'OR' )
|
36 |
+
->execute();
|
37 |
+
}
|
38 |
+
// add the new event
|
39 |
+
} elseif ( $holiday && $day ) {
|
40 |
+
$holiday = new Lib\Entities\Holiday( );
|
41 |
+
$holiday
|
42 |
+
->setDate( $day )
|
43 |
+
->setRepeatEvent( $repeat )
|
44 |
+
->save();
|
45 |
+
foreach ( Lib\Entities\Staff::query()->fetchArray() as $employee ) {
|
46 |
+
$staff_holiday = new Lib\Entities\Holiday();
|
47 |
+
$staff_holiday
|
48 |
+
->setDate( $day)
|
49 |
+
->setRepeatEvent( $repeat )
|
50 |
+
->setStaffId( $employee['id'] )
|
51 |
+
->setParent( $holiday )
|
52 |
+
->save();
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
// and return refreshed events
|
57 |
+
echo json_encode( self::_getHolidays() );
|
58 |
+
exit;
|
59 |
+
}
|
60 |
+
}
|
backend/modules/settings/Components.php
DELETED
@@ -1,30 +0,0 @@
|
|
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
DELETED
@@ -1,299 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Settings;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Settings
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-settings';
|
13 |
-
|
14 |
-
public function index()
|
15 |
-
{
|
16 |
-
/** @var \WP_Locale $wp_locale */
|
17 |
-
global $wp_locale;
|
18 |
-
|
19 |
-
wp_enqueue_media();
|
20 |
-
$this->enqueueStyles( array(
|
21 |
-
'frontend' => array( 'css/ladda.min.css' ),
|
22 |
-
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', )
|
23 |
-
) );
|
24 |
-
|
25 |
-
$this->enqueueScripts( array(
|
26 |
-
'backend' => array(
|
27 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
-
'js/jCal.js' => array( 'jquery' ),
|
29 |
-
'js/alert.js' => array( 'jquery' ),
|
30 |
-
),
|
31 |
-
'module' => array( 'js/settings.js' => array( 'jquery', 'bookly-intlTelInput.min.js', 'jquery-ui-sortable' ) ),
|
32 |
-
'frontend' => array(
|
33 |
-
'js/intlTelInput.min.js' => array( 'jquery' ),
|
34 |
-
'js/spin.min.js' => array( 'jquery' ),
|
35 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
-
)
|
37 |
-
) );
|
38 |
-
|
39 |
-
$current_tab = $this->hasParameter( 'tab' ) ? $this->getParameter( 'tab' ) : 'general';
|
40 |
-
$alert = array( 'success' => array(), 'error' => array() );
|
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.
|
139 |
-
if ( get_option( 'bookly_wc_enabled' ) && class_exists( 'WooCommerce', false ) ) {
|
140 |
-
$post = get_post( wc_get_page_id( 'cart' ) );
|
141 |
-
if ( $post === null || $post->post_status != 'publish' ) {
|
142 |
-
$alert['error'][] = sprintf(
|
143 |
-
__( 'WooCommerce cart is not set up. Follow the <a href="%s">link</a> to correct this problem.', 'bookly' ),
|
144 |
-
Lib\Utils\Common::escAdminUrl( 'wc-status', array( 'tab' => 'tools' ) )
|
145 |
-
);
|
146 |
-
}
|
147 |
-
}
|
148 |
-
$cart_columns = array(
|
149 |
-
'service' => Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' ),
|
150 |
-
'date' => __( 'Date', 'bookly' ),
|
151 |
-
'time' => __( 'Time', 'bookly' ),
|
152 |
-
'employee' => Lib\Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' ),
|
153 |
-
'price' => __( 'Price', 'bookly' ),
|
154 |
-
'deposit' => __( 'Deposit', 'bookly' ),
|
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 |
-
);
|
178 |
-
$wp_roles = new \WP_Roles();
|
179 |
-
foreach ( $wp_roles->get_names() as $role => $name ) {
|
180 |
-
$values['bookly_cst_new_account_role'][] = array( $role, $name );
|
181 |
-
}
|
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 |
-
/**
|
196 |
-
* Ajax request for Holidays calendar
|
197 |
-
*/
|
198 |
-
public function executeSettingsHoliday()
|
199 |
-
{
|
200 |
-
global $wpdb;
|
201 |
-
|
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 |
-
*/
|
261 |
-
protected function getHolidays()
|
262 |
-
{
|
263 |
-
$collection = Lib\Entities\Holiday::query()->where( 'staff_id', null )->fetchArray();
|
264 |
-
$holidays = array();
|
265 |
-
if ( count( $collection ) ) {
|
266 |
-
foreach ( $collection as $holiday ) {
|
267 |
-
$holidays[ $holiday['id'] ] = array(
|
268 |
-
'm' => (int) date( 'm', strtotime( $holiday['date'] ) ),
|
269 |
-
'd' => (int) date( 'd', strtotime( $holiday['date'] ) ),
|
270 |
-
);
|
271 |
-
// If not repeated holiday, add the year
|
272 |
-
if ( ! $holiday['repeat_event'] ) {
|
273 |
-
$holidays[ $holiday['id'] ]['y'] = (int) date( 'Y', strtotime( $holiday['date'] ) );
|
274 |
-
}
|
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/Page.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Settings
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
wp_enqueue_media();
|
21 |
+
self::enqueueStyles( array(
|
22 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
23 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', )
|
24 |
+
) );
|
25 |
+
|
26 |
+
self::enqueueScripts( array(
|
27 |
+
'backend' => array(
|
28 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
29 |
+
'js/jCal.js' => array( 'jquery' ),
|
30 |
+
'js/alert.js' => array( 'jquery' ),
|
31 |
+
),
|
32 |
+
'module' => array( 'js/settings.js' => array( 'jquery', 'bookly-intlTelInput.min.js', 'jquery-ui-sortable' ) ),
|
33 |
+
'frontend' => array(
|
34 |
+
'js/intlTelInput.min.js' => array( 'jquery' ),
|
35 |
+
'js/spin.min.js' => array( 'jquery' ),
|
36 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
37 |
+
)
|
38 |
+
) );
|
39 |
+
|
40 |
+
$current_tab = self::hasParameter( 'tab' ) ? self::parameter( 'tab' ) : 'general';
|
41 |
+
$alert = array( 'success' => array(), 'error' => array() );
|
42 |
+
|
43 |
+
// Save the settings.
|
44 |
+
if ( ! empty ( $_POST ) ) {
|
45 |
+
if ( self::csrfTokenValid() ) {
|
46 |
+
switch ( self::parameter( 'tab' ) ) {
|
47 |
+
case 'calendar': // Calendar form.
|
48 |
+
update_option( 'bookly_cal_one_participant', self::parameter( 'bookly_cal_one_participant' ) );
|
49 |
+
update_option( 'bookly_cal_many_participants', self::parameter( 'bookly_cal_many_participants' ) );
|
50 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
51 |
+
break;
|
52 |
+
case 'payments': // Payments form.
|
53 |
+
update_option( 'bookly_pmt_order', self::parameter( 'bookly_pmt_order' ) );
|
54 |
+
update_option( 'bookly_pmt_currency', self::parameter( 'bookly_pmt_currency' ) );
|
55 |
+
update_option( 'bookly_pmt_price_format', self::parameter( 'bookly_pmt_price_format' ) );
|
56 |
+
update_option( 'bookly_pmt_local', self::parameter( 'bookly_pmt_local' ) );
|
57 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
58 |
+
break;
|
59 |
+
case 'business_hours': // Business hours form.
|
60 |
+
$form = new Forms\BusinessHours();
|
61 |
+
$form->bind( self::postParameters(), $_FILES );
|
62 |
+
$form->save();
|
63 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
64 |
+
break;
|
65 |
+
case 'general': // General form.
|
66 |
+
$bookly_gen_time_slot_length = self::parameter( 'bookly_gen_time_slot_length' );
|
67 |
+
if ( in_array( $bookly_gen_time_slot_length, array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) ) ) {
|
68 |
+
update_option( 'bookly_gen_time_slot_length', $bookly_gen_time_slot_length );
|
69 |
+
}
|
70 |
+
update_option( 'bookly_gen_service_duration_as_slot_length', (int) self::parameter( 'bookly_gen_service_duration_as_slot_length' ) );
|
71 |
+
update_option( 'bookly_gen_allow_staff_edit_profile', (int) self::parameter( 'bookly_gen_allow_staff_edit_profile' ) );
|
72 |
+
update_option( 'bookly_gen_default_appointment_status', self::parameter( 'bookly_gen_default_appointment_status' ) );
|
73 |
+
update_option( 'bookly_gen_link_assets_method', self::parameter( 'bookly_gen_link_assets_method' ) );
|
74 |
+
update_option( 'bookly_gen_max_days_for_booking', (int) self::parameter( 'bookly_gen_max_days_for_booking' ) );
|
75 |
+
update_option( 'bookly_gen_use_client_time_zone', (int) self::parameter( 'bookly_gen_use_client_time_zone' ) );
|
76 |
+
update_option( 'bookly_gen_collect_stats', self::parameter( 'bookly_gen_collect_stats' ) );
|
77 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
78 |
+
break;
|
79 |
+
case 'url': // URL settings form.
|
80 |
+
update_option( 'bookly_url_approve_page_url', self::parameter( 'bookly_url_approve_page_url' ) );
|
81 |
+
update_option( 'bookly_url_approve_denied_page_url', self::parameter( 'bookly_url_approve_denied_page_url' ) );
|
82 |
+
update_option( 'bookly_url_cancel_page_url', self::parameter( 'bookly_url_cancel_page_url' ) );
|
83 |
+
update_option( 'bookly_url_cancel_denied_page_url', self::parameter( 'bookly_url_cancel_denied_page_url' ) );
|
84 |
+
update_option( 'bookly_url_reject_denied_page_url', self::parameter( 'bookly_url_reject_denied_page_url' ) );
|
85 |
+
update_option( 'bookly_url_reject_page_url', self::parameter( 'bookly_url_reject_page_url' ) );
|
86 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
87 |
+
break;
|
88 |
+
case 'customers': // Customers form.
|
89 |
+
update_option( 'bookly_cst_allow_duplicates', self::parameter( 'bookly_cst_allow_duplicates' ) );
|
90 |
+
update_option( 'bookly_cst_combined_notifications', self::parameter( 'bookly_cst_combined_notifications' ) );
|
91 |
+
update_option( 'bookly_cst_default_country_code', self::parameter( 'bookly_cst_default_country_code' ) );
|
92 |
+
update_option( 'bookly_cst_phone_default_country', self::parameter( 'bookly_cst_phone_default_country' ) );
|
93 |
+
update_option( 'bookly_cst_remember_in_cookie', self::parameter( 'bookly_cst_remember_in_cookie' ) );
|
94 |
+
update_option( 'bookly_cst_show_update_details_dialog', self::parameter( 'bookly_cst_show_update_details_dialog' ) );
|
95 |
+
// Update email required option if creating wordpress account for customers
|
96 |
+
$bookly_cst_required_details = get_option( 'bookly_cst_required_details', array() );
|
97 |
+
if ( self::parameter( 'bookly_cst_create_account' ) && ! in_array( 'email', $bookly_cst_required_details ) ) {
|
98 |
+
$bookly_cst_required_details[] = 'email';
|
99 |
+
update_option( 'bookly_cst_required_details', $bookly_cst_required_details );
|
100 |
+
}
|
101 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
102 |
+
break;
|
103 |
+
case 'company': // Company form.
|
104 |
+
update_option( 'bookly_co_address', self::parameter( 'bookly_co_address' ) );
|
105 |
+
update_option( 'bookly_co_logo_attachment_id', self::parameter( 'bookly_co_logo_attachment_id' ) );
|
106 |
+
update_option( 'bookly_co_name', self::parameter( 'bookly_co_name' ) );
|
107 |
+
update_option( 'bookly_co_phone', self::parameter( 'bookly_co_phone' ) );
|
108 |
+
update_option( 'bookly_co_website', self::parameter( 'bookly_co_website' ) );
|
109 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
110 |
+
break;
|
111 |
+
}
|
112 |
+
|
113 |
+
// Let Add-ons save their settings.
|
114 |
+
$alert = Proxy\Shared::saveSettings( $alert, self::parameter( 'tab' ), self::postParameters() );
|
115 |
+
}
|
116 |
+
}
|
117 |
+
|
118 |
+
// Check if WooCommerce cart exists.
|
119 |
+
if ( get_option( 'bookly_wc_enabled' ) && class_exists( 'WooCommerce', false ) ) {
|
120 |
+
$post = get_post( wc_get_page_id( 'cart' ) );
|
121 |
+
if ( $post === null || $post->post_status != 'publish' ) {
|
122 |
+
$alert['error'][] = sprintf(
|
123 |
+
__( 'WooCommerce cart is not set up. Follow the <a href="%s">link</a> to correct this problem.', 'bookly' ),
|
124 |
+
Lib\Utils\Common::escAdminUrl( 'wc-status', array( 'tab' => 'tools' ) )
|
125 |
+
);
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
wp_localize_script( 'bookly-jCal.js', 'BooklyL10n', array(
|
130 |
+
'alert' => $alert,
|
131 |
+
'current_tab' => $current_tab,
|
132 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
133 |
+
'default_country' => get_option( 'bookly_cst_phone_default_country' ),
|
134 |
+
'holidays' => self::_getHolidays(),
|
135 |
+
'loading_img' => plugins_url( 'bookly-responsive-appointment-booking-tool/backend/resources/images/loading.gif' ),
|
136 |
+
'start_of_week' => get_option( 'start_of_week' ),
|
137 |
+
'days' => array_values( $wp_locale->weekday_abbrev ),
|
138 |
+
'months' => array_values( $wp_locale->month ),
|
139 |
+
'close' => __( 'Close', 'bookly' ),
|
140 |
+
'repeat' => __( 'Repeat every year', 'bookly' ),
|
141 |
+
'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
|
142 |
+
'sample_price' => number_format_i18n( 10, 3 ),
|
143 |
+
) );
|
144 |
+
$values = array();
|
145 |
+
foreach ( array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) as $duration ) {
|
146 |
+
$values['bookly_gen_time_slot_length'][] = array( $duration, Lib\Utils\DateTime::secondsToInterval( $duration * MINUTE_IN_SECONDS ) );
|
147 |
+
}
|
148 |
+
|
149 |
+
// Payments tab
|
150 |
+
$payments = array();
|
151 |
+
$payment_data = array(
|
152 |
+
'local' => self::renderTemplate( '_payment_local', array(), false ),
|
153 |
+
);
|
154 |
+
$payment_data = Proxy\Shared::preparePaymentGatewaySettings( $payment_data );
|
155 |
+
$order = explode( ',', get_option( 'bookly_pmt_order' ) );
|
156 |
+
foreach ( $order as $payment_system ) {
|
157 |
+
if ( array_key_exists( $payment_system, $payment_data ) ) {
|
158 |
+
$payments[] = $payment_data[ $payment_system ];
|
159 |
+
}
|
160 |
+
}
|
161 |
+
foreach ( $payment_data as $slug => $data ) {
|
162 |
+
if ( ! $order || ! in_array( $slug, $order ) ) {
|
163 |
+
$payments[] = $data;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
self::renderTemplate( 'index', compact( 'values', 'payments' ) );
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Get holidays.
|
172 |
+
*
|
173 |
+
* @return array
|
174 |
+
*/
|
175 |
+
protected static function _getHolidays()
|
176 |
+
{
|
177 |
+
$collection = Lib\Entities\Holiday::query()->where( 'staff_id', null )->fetchArray();
|
178 |
+
$holidays = array();
|
179 |
+
if ( count( $collection ) ) {
|
180 |
+
foreach ( $collection as $holiday ) {
|
181 |
+
$holidays[ $holiday['id'] ] = array(
|
182 |
+
'm' => (int) date( 'm', strtotime( $holiday['date'] ) ),
|
183 |
+
'd' => (int) date( 'd', strtotime( $holiday['date'] ) ),
|
184 |
+
);
|
185 |
+
// If not repeated holiday, add the year
|
186 |
+
if ( ! $holiday['repeat_event'] ) {
|
187 |
+
$holidays[ $holiday['id'] ]['y'] = (int) date( 'Y', strtotime( $holiday['date'] ) );
|
188 |
+
}
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
return $holidays;
|
193 |
+
}
|
194 |
+
}
|
backend/modules/settings/forms/BusinessHours.php
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
<?php
|
2 |
-
namespace
|
3 |
|
4 |
-
use
|
5 |
|
6 |
/**
|
7 |
* Class BusinessHours
|
8 |
-
* @package
|
9 |
*/
|
10 |
class BusinessHours extends Lib\Base\Form
|
11 |
{
|
1 |
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Forms;
|
3 |
|
4 |
+
use Bookly\Lib;
|
5 |
|
6 |
/**
|
7 |
* Class BusinessHours
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Forms
|
9 |
*/
|
10 |
class BusinessHours extends Lib\Base\Form
|
11 |
{
|
backend/modules/settings/forms/Payments.php
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Settings\Forms;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Payments
|
8 |
-
* @package BooklyLite\Backend\Modules\Settings
|
9 |
-
*/
|
10 |
-
class Payments extends Lib\Base\Form
|
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()
|
31 |
-
{
|
32 |
-
foreach ( $this->data as $field => $value ) {
|
33 |
-
update_option( $field, $value );
|
34 |
-
}
|
35 |
-
}
|
36 |
-
|
37 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/proxy/AdvancedGoogleCalendar.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class AdvancedGoogleCalendar
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static array preSaveSettings( array $alert, array $params ) Pre-save Google Calendar settings.
|
11 |
+
* @method static void renderSettings() Render Advanced Google Calendar settings.
|
12 |
+
*/
|
13 |
+
abstract class AdvancedGoogleCalendar extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/settings/proxy/DepositPayments.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class DepositPayments
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static array renderPayments() Render "Deposit options" in "Payments" tab.
|
11 |
+
*/
|
12 |
+
abstract class DepositPayments extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/settings/proxy/PaypalPaymentsStandard.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class PaypalPaymentsStandard
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareToggleOptions( array $options ) Add option to enable PayPal Payments Standard.
|
11 |
+
* @method static string renderSetUpOptions() Print list of options to set up PayPal Payments Standard.
|
12 |
+
*/
|
13 |
+
abstract class PaypalPaymentsStandard extends Lib\Base\Proxy
|
14 |
+
{
|
15 |
+
|
16 |
+
}
|
backend/modules/settings/proxy/Pro.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Pro
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static void renderCancelAppointmentAction() Render Cancel appointment action.
|
11 |
+
* @method static void renderCancellationConfirmationUrl() Render cancellation confirmation URL setting.
|
12 |
+
* @method static void renderCombinedNotifications() Render combined notifications in customers setting.
|
13 |
+
* @method static void renderCreateWordPressUser() Render Create WordPress user account for customers.
|
14 |
+
* @method static void renderCustomersAddress() Render address settings in the customers tab.
|
15 |
+
* @method static void renderCustomersBirthday() Render birthday settings in the customers tab.
|
16 |
+
* @method static void renderFinalStepUrl() Render final step URL setting.
|
17 |
+
* @method static void renderGoogleCalendarMenuItem() Render 'Google Calendar' menu item.
|
18 |
+
* @method static void renderGoogleCalendarTab() Render 'Google Calendar' tab.
|
19 |
+
* @method static void renderMinimumTimeRequirement() Render minimum time requirement prior to booking and canceling.
|
20 |
+
* @method static void renderNewUserAccountRole() Render New user account role.
|
21 |
+
* @method static void renderPurchaseCodeMenuItem() Render 'Purchase Code' menu item.
|
22 |
+
* @method static void renderPurchaseCodeTab() Render 'Purchase Code' tab.
|
23 |
+
*/
|
24 |
+
abstract class Pro extends Lib\Base\Proxy
|
25 |
+
{
|
26 |
+
|
27 |
+
}
|
backend/modules/settings/proxy/ProSettings.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib\Base\Component;
|
5 |
+
use Bookly\Lib\Config;
|
6 |
+
use BooklyPro\Lib\Base\Proxy as ProxyPro;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class ProSettings
|
10 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
11 |
+
*
|
12 |
+
* @method static void renderProMenuItem()
|
13 |
+
* @method static void renderProTab()
|
14 |
+
*/
|
15 |
+
abstract class ProSettings extends Component
|
16 |
+
{
|
17 |
+
/**
|
18 |
+
* Register proxy methods.
|
19 |
+
*/
|
20 |
+
public static function init()
|
21 |
+
{
|
22 |
+
ProxyPro::init( get_called_class(), static::reflection() );
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Invoke proxy method.
|
27 |
+
*
|
28 |
+
* @param string $method
|
29 |
+
* @param mixed $args
|
30 |
+
* @return mixed
|
31 |
+
*/
|
32 |
+
public static function __callStatic( $method, $args )
|
33 |
+
{
|
34 |
+
if ( Config::proActive() && ProxyPro::canInvoke( get_called_class(), $method ) ) {
|
35 |
+
return ProxyPro::invoke( get_called_class(), $method, $args );
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @inheritdoc
|
41 |
+
*/
|
42 |
+
protected static function directory()
|
43 |
+
{
|
44 |
+
return dirname( parent::directory() );
|
45 |
+
}
|
46 |
+
}
|
backend/modules/settings/proxy/Shared.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Shared
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static array prepareCalendarAppointmentCodes( array $codes, string $participants ) Prepare codes for appointment description displayed in calendar.
|
11 |
+
* @method static array preparePaymentGatewaySettings( array $payment_data ) Prepare payment gateway settings.
|
12 |
+
* @method static array prepareWooCommerceCodes( array $codes ) Alter array of codes to be displayed in WooCommerce (Order,Cart,Checkout etc.).
|
13 |
+
* @method static void renderMenuItem() Render tab in settings page.
|
14 |
+
* @method static void renderTab() Render add-on settings form.
|
15 |
+
* @method static void renderUrlSettings() Render URL settings on Settings page.
|
16 |
+
* @method static array saveSettings( array $alert, string $tab, array $params ) Save add-on settings.
|
17 |
+
*/
|
18 |
+
abstract class Shared extends Lib\Base\Proxy
|
19 |
+
{
|
20 |
+
|
21 |
+
}
|
backend/modules/settings/proxy/Taxes.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Taxes
|
8 |
+
* @package Bookly\Backend\Modules\Settings\Proxy
|
9 |
+
*
|
10 |
+
* @method static array renderPayments() Render "Deposit options" in "Payments" tab.
|
11 |
+
*/
|
12 |
+
abstract class Taxes extends Lib\Base\Proxy
|
13 |
+
{
|
14 |
+
|
15 |
+
}
|
backend/modules/settings/resources/js/collect-stats-notice.js
DELETED
@@ -1,13 +0,0 @@
|
|
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,103 +1,88 @@
|
|
1 |
jQuery(function ($) {
|
2 |
-
var $
|
3 |
-
$
|
4 |
-
$
|
5 |
-
$
|
6 |
-
$participants
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
booklyAlert(BooklyL10n.alert);
|
10 |
|
11 |
-
Ladda.bind('button[type=submit]'
|
12 |
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
booklyAlert({error: [BooklyL10n.limitations]});
|
17 |
-
$(this).prop('disabled', true);
|
18 |
});
|
19 |
-
$(
|
20 |
-
|
21 |
-
|
22 |
-
$(this).find('option:gt(0)').prop('disabled', true);
|
23 |
});
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
booklyAlert({error: [BooklyL10n.limitations]});
|
28 |
});
|
29 |
-
$('#
|
30 |
-
$(
|
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),
|
36 |
-
start_time = this.value;
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
var old_value = $end_select.val();
|
44 |
-
var new_value = null;
|
45 |
-
$('option', $end_select).each(function () {
|
46 |
-
if (this.value <= start_time) {
|
47 |
-
var span = document.createElement('span');
|
48 |
-
span.style.display = 'none';
|
49 |
-
span.appendChild(this.cloneNode(true));
|
50 |
-
frag.appendChild(span);
|
51 |
-
} else {
|
52 |
-
frag.appendChild(this.cloneNode(true));
|
53 |
-
if (new_value === null || old_value == this.value) {
|
54 |
-
new_value = this.value;
|
55 |
-
}
|
56 |
-
}
|
57 |
-
});
|
58 |
-
$end_select.empty().append(frag).val(new_value);
|
59 |
-
} else { // OFF
|
60 |
-
$flexbox.find('.bookly-hide-on-off').hide();
|
61 |
-
}
|
62 |
-
}).each(function () {
|
63 |
-
$(this).data('default_value', this.value);
|
64 |
}).trigger('change');
|
65 |
|
66 |
-
//
|
67 |
-
$('#bookly-hours-reset', $form).on('click', function () {
|
68 |
-
$('.select_start', $form).each(function () {
|
69 |
-
$(this).val($(this).data('default_value')).trigger('change');
|
70 |
-
});
|
71 |
-
});
|
72 |
-
|
73 |
-
// Customers Tab
|
74 |
-
var $default_country = $('#bookly_cst_phone_default_country'),
|
75 |
-
$default_country_code = $('#bookly_cst_default_country_code');
|
76 |
-
|
77 |
-
$.each($.fn.intlTelInput.getCountryData(), function (index, value) {
|
78 |
-
$default_country.append('<option value="' + value.iso2 + '" data-code="' + value.dialCode + '">' + value.name + ' +' + value.dialCode + '</option>');
|
79 |
-
});
|
80 |
-
$default_country.val(BooklyL10n.default_country);
|
81 |
-
|
82 |
-
$default_country.on('change', function () {
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
$('#bookly-company-reset').on('click', function () {
|
102 |
var $div = $('#bookly-js-logo .bookly-js-image'),
|
103 |
$input = $('[name=bookly_co_logo_attachment_id]');
|
@@ -105,16 +90,24 @@ jQuery(function ($) {
|
|
105 |
$input.val($input.data('default'));
|
106 |
});
|
107 |
|
108 |
-
// Cart
|
109 |
$('#bookly_cart_show_columns').sortable({
|
110 |
axis : 'y',
|
111 |
handle : '.bookly-js-handle'
|
112 |
});
|
113 |
|
114 |
-
//
|
115 |
-
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
$currency.on('change', function () {
|
119 |
$formats.find('option').each(function () {
|
120 |
var decimals = this.value.match(/{price\|(\d)}/)[1],
|
@@ -123,48 +116,84 @@ jQuery(function ($) {
|
|
123 |
if (decimals < 3) {
|
124 |
price = price.slice(0, -(decimals == 0 ? 4 : 3 - decimals));
|
125 |
}
|
126 |
-
|
|
|
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 |
-
|
136 |
-
|
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 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
$(this).find('option:gt(0)').prop('disabled', true);
|
158 |
-
$final_step_url.hide().val('');
|
159 |
-
});
|
160 |
-
|
161 |
-
// Change link to Help page according to activated tab.
|
162 |
-
var help_link = $help_btn.attr('href');
|
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),
|
@@ -182,44 +211,58 @@ jQuery(function ($) {
|
|
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 |
-
//
|
193 |
-
$('
|
|
|
|
|
|
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
var
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
|
|
207 |
} else {
|
208 |
-
|
|
|
|
|
|
|
209 |
}
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
215 |
});
|
216 |
-
|
217 |
-
frame.open();
|
218 |
});
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
$('
|
224 |
});
|
|
|
|
|
|
|
225 |
});
|
1 |
jQuery(function ($) {
|
2 |
+
var $helpBtn = $('#bookly-help-btn'),
|
3 |
+
$form = $('#business-hours'),
|
4 |
+
$finalStepUrl = $('input[name=bookly_url_final_step_url]'),
|
5 |
+
$finalStepUrlMode = $('#bookly_settings_final_step_url_mode'),
|
6 |
+
$participants = $('#bookly_appointment_participants'),
|
7 |
+
$defaultCountry = $('#bookly_cst_phone_default_country'),
|
8 |
+
$defaultCountryCode = $('#bookly_cst_default_country_code'),
|
9 |
+
$gcSyncMode = $('#bookly_gc_sync_mode'),
|
10 |
+
$gcLimitEvents = $('#bookly_gc_limit_events'),
|
11 |
+
$gcFullSyncOffset = $('#bookly_gc_full_sync_offset_days'),
|
12 |
+
$gcFullSyncTitles = $('#bookly_gc_full_sync_titles'),
|
13 |
+
$currency = $('#bookly_pmt_currency'),
|
14 |
+
$formats = $('#bookly_pmt_price_format')
|
15 |
+
;
|
16 |
|
17 |
booklyAlert(BooklyL10n.alert);
|
18 |
|
19 |
+
Ladda.bind('button[type=submit]');
|
20 |
|
21 |
+
// Customers tab.
|
22 |
+
$.each($.fn.intlTelInput.getCountryData(), function (index, value) {
|
23 |
+
$defaultCountry.append('<option value="' + value.iso2 + '" data-code="' + value.dialCode + '">' + value.name + ' +' + value.dialCode + '</option>');
|
|
|
|
|
24 |
});
|
25 |
+
$defaultCountry.val(BooklyL10n.default_country);
|
26 |
+
$defaultCountry.on('change', function () {
|
27 |
+
$defaultCountryCode.val($defaultCountry.find('option:selected').data('code'));
|
|
|
28 |
});
|
29 |
+
$('#bookly_cst_address_show_fields').sortable({
|
30 |
+
axis : 'y',
|
31 |
+
handle : '.bookly-js-handle'
|
|
|
32 |
});
|
33 |
+
$('#bookly-customer-reset').on('click', function (event) {
|
34 |
+
$defaultCountry.val($defaultCountry.data('country'));
|
|
|
35 |
});
|
|
|
|
|
|
|
|
|
36 |
|
37 |
+
// Google Calendar tab.
|
38 |
+
$gcSyncMode.on('change', function () {
|
39 |
+
$gcLimitEvents.closest('.form-group').toggle(this.value == '1.5-way');
|
40 |
+
$gcFullSyncOffset.closest('.form-group').toggle(this.value == '2-way');
|
41 |
+
$gcFullSyncTitles.closest('.form-group').toggle(this.value == '2-way');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
}).trigger('change');
|
43 |
|
44 |
+
// Calendar tab.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
$participants.on('change', function () {
|
|
|
46 |
$('#bookly_cal_one_participant').hide();
|
47 |
$('#bookly_cal_many_participants').hide();
|
48 |
$('#' + $(this).val() ).show();
|
49 |
}).trigger('change');
|
|
|
50 |
$("#bookly_settings_calendar button[type=reset]").on( 'click', function () {
|
51 |
setTimeout(function () {
|
52 |
$participants.trigger('change');
|
53 |
}, 50 );
|
54 |
});
|
55 |
|
56 |
+
// Company tab.
|
57 |
+
$('#bookly-js-logo .bookly-thumb-delete').on('click', function () {
|
58 |
+
var $thumb = $(this).parents('.bookly-js-image');
|
59 |
+
$thumb.attr('style', '');
|
60 |
+
$('[name=bookly_co_logo_attachment_id]').val('');
|
61 |
+
});
|
62 |
+
$('#bookly-js-logo .bookly-pretty-indicator').on('click', function(){
|
63 |
+
var frame = wp.media({
|
64 |
+
library: {type: 'image'},
|
65 |
+
multiple: false
|
66 |
+
});
|
67 |
+
frame.on('select', function () {
|
68 |
+
var selection = frame.state().get('selection').toJSON(),
|
69 |
+
img_src
|
70 |
+
;
|
71 |
+
if (selection.length) {
|
72 |
+
if (selection[0].sizes['thumbnail'] !== undefined) {
|
73 |
+
img_src = selection[0].sizes['thumbnail'].url;
|
74 |
+
} else {
|
75 |
+
img_src = selection[0].url;
|
76 |
+
}
|
77 |
+
$('[name=bookly_co_logo_attachment_id]').val(selection[0].id);
|
78 |
+
$('#bookly-js-logo .bookly-js-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
|
79 |
+
$('#bookly-js-logo .bookly-thumb-delete').show();
|
80 |
+
$(this).hide();
|
81 |
+
}
|
82 |
+
});
|
83 |
+
|
84 |
+
frame.open();
|
85 |
+
});
|
86 |
$('#bookly-company-reset').on('click', function () {
|
87 |
var $div = $('#bookly-js-logo .bookly-js-image'),
|
88 |
$input = $('[name=bookly_co_logo_attachment_id]');
|
90 |
$input.val($input.data('default'));
|
91 |
});
|
92 |
|
93 |
+
// Cart tab.
|
94 |
$('#bookly_cart_show_columns').sortable({
|
95 |
axis : 'y',
|
96 |
handle : '.bookly-js-handle'
|
97 |
});
|
98 |
|
99 |
+
// Payments tab.
|
100 |
+
$('#bookly-payment-systems').sortable({
|
101 |
+
axis : 'y',
|
102 |
+
handle: '.bookly-js-handle',
|
103 |
+
update: function () {
|
104 |
+
var order = [];
|
105 |
+
$('#bookly_settings_payments .panel[data-slug]').each(function () {
|
106 |
+
order.push($(this).data('slug'));
|
107 |
+
});
|
108 |
+
$('#bookly_settings_payments [name="bookly_pmt_order"]').val(order.join(','));
|
109 |
+
}
|
110 |
+
});
|
111 |
$currency.on('change', function () {
|
112 |
$formats.find('option').each(function () {
|
113 |
var decimals = this.value.match(/{price\|(\d)}/)[1],
|
116 |
if (decimals < 3) {
|
117 |
price = price.slice(0, -(decimals == 0 ? 4 : 3 - decimals));
|
118 |
}
|
119 |
+
var html = this.value
|
120 |
+
.replace('{sign}', '')
|
121 |
.replace('{symbol}', $currency.find('option:selected').data('symbol'))
|
122 |
.replace(/{price\|\d}/, price)
|
123 |
;
|
124 |
+
html += ' (' + this.value
|
125 |
+
.replace('{sign}', '-')
|
126 |
+
.replace('{symbol}', $currency.find('option:selected').data('symbol'))
|
127 |
+
.replace(/{price\|\d}/, price) + ')'
|
128 |
+
;
|
129 |
+
this.innerHTML = html;
|
130 |
});
|
131 |
}).trigger('change');
|
132 |
|
|
|
133 |
$('#bookly_paypal_enabled').change(function () {
|
134 |
+
$('.bookly-paypal-ec').toggle(this.value == 'ec');
|
135 |
+
$('.bookly-paypal-ps').toggle(this.value == 'ps');
|
|
|
|
|
|
|
136 |
$('.bookly-paypal').toggle(this.value != '0');
|
137 |
}).change();
|
138 |
|
139 |
+
$('#bookly_authorize_net_enabled').change(function () {
|
140 |
+
$('.bookly-authorize-net').toggle(this.value != '0');
|
141 |
+
}).change();
|
142 |
+
|
143 |
+
$('#bookly_stripe_enabled').change(function () {
|
144 |
+
$('.bookly-stripe').toggle(this.value == 1);
|
145 |
+
}).change();
|
146 |
+
|
147 |
+
$('#bookly_2checkout_enabled').change(function () {
|
148 |
+
$('.bookly-2checkout').toggle(this.value != '0');
|
149 |
+
}).change();
|
150 |
+
|
151 |
+
$('#bookly_payu_biz_enabled').change(function () {
|
152 |
+
$('.bookly-payu_biz').toggle(this.value != '0');
|
153 |
+
}).change();
|
154 |
+
|
155 |
+
$('#bookly_payu_latam_enabled').change(function () {
|
156 |
+
$('.bookly-payu_latam').toggle(this.value != '0');
|
157 |
+
}).change();
|
158 |
+
|
159 |
+
$('#bookly_payson_enabled').change(function () {
|
160 |
+
$('.bookly-payson').toggle(this.value != '0');
|
161 |
+
}).change();
|
162 |
+
|
163 |
+
$('#bookly_mollie_enabled').change(function () {
|
164 |
+
$('.bookly-mollie').toggle(this.value != '0');
|
165 |
+
}).change();
|
166 |
+
|
167 |
+
$('#bookly_payu_biz_sandbox').change(function () {
|
168 |
+
var live = this.value != 1;
|
169 |
+
$('.bookly-payu_biz > .form-group:eq(1)').toggle(live);
|
170 |
+
$('.bookly-payu_biz > .form-group:eq(2)').toggle(live);
|
171 |
+
}).change();
|
172 |
+
|
173 |
+
$('#bookly_payu_latam_sandbox').change(function () {
|
174 |
+
var live = this.value != 1;
|
175 |
+
$('.bookly-payu_latam > .form-group:eq(1)').toggle(live);
|
176 |
+
$('.bookly-payu_latam > .form-group:eq(2)').toggle(live);
|
177 |
+
$('.bookly-payu_latam > .form-group:eq(3)').toggle(live);
|
178 |
+
}).change();
|
179 |
+
|
180 |
$('#bookly-payments-reset').on('click', function (event) {
|
181 |
setTimeout(function () {
|
182 |
+
$('#bookly_pmt_currency,#bookly_paypal_enabled,#bookly_authorize_net_enabled,#bookly_stripe_enabled,#bookly_2checkout_enabled,#bookly_payu_biz_enabled,#bookly_payu_latam_enabled,#bookly_payson_enabled,#bookly_mollie_enabled,#bookly_payu_biz_sandbox,#bookly_payu_latam_sandbox').change();
|
183 |
}, 0);
|
184 |
});
|
185 |
|
186 |
+
// URL tab.
|
187 |
+
if ($finalStepUrl.val()) { $finalStepUrlMode.val(1); }
|
188 |
+
$finalStepUrlMode.change(function () {
|
189 |
+
if (this.value == 0) {
|
190 |
+
$finalStepUrl.hide().val('');
|
191 |
+
} else {
|
192 |
+
$finalStepUrl.show();
|
193 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
});
|
195 |
|
196 |
+
// Holidays Tab.
|
197 |
var d = new Date();
|
198 |
$('.bookly-js-annual-calendar').jCal({
|
199 |
day: new Date(d.getFullYear(), 0, 1),
|
211 |
repeat: BooklyL10n.repeat,
|
212 |
close: BooklyL10n.close
|
213 |
});
|
|
|
214 |
$('.bookly-js-jCalBtn').on('click', function (e) {
|
215 |
e.preventDefault();
|
216 |
var trigger = $(this).data('trigger');
|
217 |
$('.bookly-js-annual-calendar').find($(trigger)).trigger('click');
|
218 |
});
|
219 |
|
220 |
+
// Business Hours tab.
|
221 |
+
$('.select_start', $form).on('change', function () {
|
222 |
+
var $flexbox = $(this).closest('.bookly-flexbox'),
|
223 |
+
$end_select = $('.select_end', $flexbox),
|
224 |
+
start_time = this.value;
|
225 |
|
226 |
+
if (start_time) {
|
227 |
+
$flexbox.find('.bookly-hide-on-off').show();
|
228 |
+
|
229 |
+
// Hides end time options with value less than in the start time.
|
230 |
+
var frag = document.createDocumentFragment();
|
231 |
+
var old_value = $end_select.val();
|
232 |
+
var new_value = null;
|
233 |
+
$('option', $end_select).each(function () {
|
234 |
+
if (this.value <= start_time) {
|
235 |
+
var span = document.createElement('span');
|
236 |
+
span.style.display = 'none';
|
237 |
+
span.appendChild(this.cloneNode(true));
|
238 |
+
frag.appendChild(span);
|
239 |
} else {
|
240 |
+
frag.appendChild(this.cloneNode(true));
|
241 |
+
if (new_value === null || old_value == this.value) {
|
242 |
+
new_value = this.value;
|
243 |
+
}
|
244 |
}
|
245 |
+
});
|
246 |
+
$end_select.empty().append(frag).val(new_value);
|
247 |
+
} else { // OFF
|
248 |
+
$flexbox.find('.bookly-hide-on-off').hide();
|
249 |
+
}
|
250 |
+
}).each(function () {
|
251 |
+
$(this).data('default_value', this.value);
|
252 |
+
}).trigger('change');
|
253 |
+
// Reset.
|
254 |
+
$('#bookly-hours-reset', $form).on('click', function () {
|
255 |
+
$('.select_start', $form).each(function () {
|
256 |
+
$(this).val($(this).data('default_value')).trigger('change');
|
257 |
});
|
|
|
|
|
258 |
});
|
259 |
|
260 |
+
// Change link to Help page according to activated tab.
|
261 |
+
var help_link = $helpBtn.attr('href');
|
262 |
+
$('.bookly-nav li[data-toggle="tab"]').on('shown.bs.tab', function(e) {
|
263 |
+
$helpBtn.attr('href', help_link + e.target.getAttribute('data-target').substring(1).replace(/_/g, '-'));
|
264 |
});
|
265 |
+
|
266 |
+
// Activate tab.
|
267 |
+
$('li[data-target="#bookly_settings_' + BooklyL10n.current_tab + '"]').tab('show');
|
268 |
});
|
backend/modules/settings/templates/_calendarForm.php
CHANGED
@@ -1,21 +1,34 @@
|
|
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 |
-
|
4 |
-
<
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 $
|
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 $
|
14 |
</div>
|
15 |
|
16 |
<div class="panel-footer">
|
17 |
-
<?php
|
18 |
-
<?php
|
19 |
-
<?php
|
20 |
</div>
|
21 |
</form>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
?>
|
5 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'calendar' ) ) ?>">
|
6 |
+
<?php if ( Bookly\Lib\Config::groupBookingActive() ) : ?>
|
7 |
+
<div class="form-group"><label for="bookly_appointment_participants"><?php _e( 'Calendar', 'bookly' ) ?></label>
|
8 |
+
<p class="help-block"><?php _e( 'Set order of the fields in calendar', 'bookly' ) ?></p>
|
9 |
+
<select class="form-control" id="bookly_appointment_participants">
|
10 |
+
<option value="bookly_cal_one_participant"><?php _e( 'Appointment with one participant', 'bookly' ) ?></option>
|
11 |
+
<option value="bookly_cal_many_participants"><?php _e( 'Appointment with many participants', 'bookly' ) ?></option>
|
12 |
+
</select>
|
13 |
+
</div>
|
14 |
+
<?php else : ?>
|
15 |
+
<div class="form-group"><label for="bookly_appointment_participants"><?php _e( 'Calendar', 'bookly' ) ?></label>
|
16 |
+
<p class="help-block"><?php _e( 'Set order of the fields in calendar', 'bookly' ) ?></p>
|
17 |
+
</div>
|
18 |
+
<input id="bookly_appointment_participants" type="hidden" name="bookly_appointment_participants" value="bookly_cal_one_participant">
|
19 |
+
<?php endif ?>
|
20 |
<div class="form-group" id="bookly_cal_one_participant">
|
21 |
<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/>
|
22 |
+
<?php $self::renderTemplate( '_calendar_codes', array( 'participants' => 'one' ) ) ?>
|
23 |
</div>
|
24 |
<div class="form-group" id="bookly_cal_many_participants">
|
25 |
<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/>
|
26 |
+
<?php $self::renderTemplate( '_calendar_codes', array( 'participants' => 'many' ) ) ?>
|
27 |
</div>
|
28 |
|
29 |
<div class="panel-footer">
|
30 |
+
<?php Inputs::renderCsrf() ?>
|
31 |
+
<?php Buttons::renderSubmit() ?>
|
32 |
+
<?php Buttons::renderReset() ?>
|
33 |
</div>
|
34 |
</form>
|
backend/modules/settings/templates/_calendar_codes.php
CHANGED
@@ -1,4 +1,6 @@
|
|
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' ) ),
|
@@ -30,6 +32,6 @@ if ( $participants == 'one' ) {
|
|
30 |
$codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
|
31 |
}
|
32 |
|
33 |
-
$codes =
|
34 |
|
35 |
-
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
|
4 |
$codes = array(
|
5 |
array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) ),
|
6 |
array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) ),
|
32 |
$codes[] = array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) );
|
33 |
}
|
34 |
|
35 |
+
$codes = Proxy\Shared::prepareCalendarAppointmentCodes( $codes, $participants );
|
36 |
|
37 |
+
echo Bookly\Lib\Utils\Common::codes( $codes );
|
backend/modules/settings/templates/_cartForm.php
DELETED
@@ -1,33 +0,0 @@
|
|
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>
|
13 |
-
<div class="bookly-flex-cell" style="width: 100%">
|
14 |
-
<div class="checkbox">
|
15 |
-
<label>
|
16 |
-
<input type="hidden" name="bookly_cart_show_columns[<?php echo $column ?>][show]" value="0">
|
17 |
-
<input type="checkbox"
|
18 |
-
name="bookly_cart_show_columns[<?php echo $column ?>][show]"
|
19 |
-
value="1" <?php checked( $attr['show'], true ) ?>>
|
20 |
-
<?php echo isset( $cart_columns[ $column ] ) ? $cart_columns[ $column ] : '' ?>
|
21 |
-
</label>
|
22 |
-
</div>
|
23 |
-
</div>
|
24 |
-
</div>
|
25 |
-
<?php endforeach ?>
|
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
DELETED
@@ -1,24 +0,0 @@
|
|
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
@@ -1,4 +1,8 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
|
|
|
|
|
2 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'company' ) ) ?>">
|
3 |
<div class="row">
|
4 |
<div class="col-xs-3 col-lg-2">
|
@@ -33,7 +37,7 @@
|
|
33 |
</div>
|
34 |
<div class="col-xs-9 col-lg-10">
|
35 |
<div class="bookly-flex-cell bookly-vertical-middle">
|
36 |
-
<?php
|
37 |
</div>
|
38 |
</div>
|
39 |
</div>
|
@@ -43,12 +47,12 @@
|
|
43 |
<textarea id="bookly_co_address" class="form-control" rows="5"
|
44 |
name="bookly_co_address"><?php form_option( 'bookly_co_address' ) ?></textarea>
|
45 |
</div>
|
46 |
-
<?php
|
47 |
-
<?php
|
48 |
|
49 |
<div class="panel-footer">
|
50 |
-
<?php
|
51 |
-
<?php
|
52 |
-
<?php
|
53 |
</div>
|
54 |
</form>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs as ControlInputs;
|
4 |
+
use Bookly\Backend\Components\Settings\Inputs;
|
5 |
+
?>
|
6 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'company' ) ) ?>">
|
7 |
<div class="row">
|
8 |
<div class="col-xs-3 col-lg-2">
|
37 |
</div>
|
38 |
<div class="col-xs-9 col-lg-10">
|
39 |
<div class="bookly-flex-cell bookly-vertical-middle">
|
40 |
+
<?php Inputs::renderText( 'bookly_co_name', __( 'Company name', 'bookly' ) ) ?>
|
41 |
</div>
|
42 |
</div>
|
43 |
</div>
|
47 |
<textarea id="bookly_co_address" class="form-control" rows="5"
|
48 |
name="bookly_co_address"><?php form_option( 'bookly_co_address' ) ?></textarea>
|
49 |
</div>
|
50 |
+
<?php Inputs::renderText( 'bookly_co_phone', __( 'Phone', 'bookly' ) ) ?>
|
51 |
+
<?php Inputs::renderText( 'bookly_co_website', __( 'Website', 'bookly' ) ) ?>
|
52 |
|
53 |
<div class="panel-footer">
|
54 |
+
<?php ControlInputs::renderCsrf() ?>
|
55 |
+
<?php Buttons::renderSubmit() ?>
|
56 |
+
<?php Buttons::renderReset( 'bookly-company-reset' ) ?>
|
57 |
</div>
|
58 |
</form>
|
backend/modules/settings/templates/_customers.php
CHANGED
@@ -1,22 +1,29 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
|
|
|
|
|
|
|
|
3 |
?>
|
4 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'customers' ) ) ?>">
|
5 |
-
<?php
|
6 |
-
|
7 |
-
|
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 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
16 |
?>
|
17 |
<div class="panel-footer">
|
18 |
-
<?php
|
19 |
-
<?php
|
20 |
-
<?php
|
21 |
</div>
|
22 |
</form>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Inputs as ControlInputs;
|
3 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
4 |
+
use Bookly\Backend\Components\Settings\Inputs;
|
5 |
+
use Bookly\Backend\Components\Settings\Selects;
|
6 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
7 |
?>
|
8 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'customers' ) ) ?>">
|
9 |
+
<?php Proxy\Pro::renderCreateWordPressUser();
|
10 |
+
Proxy\Pro::renderNewUserAccountRole();
|
11 |
+
Selects::renderSingle( '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' ),
|
|
|
12 |
array( array( 'disabled', __( 'Disabled', 'bookly' ) ), array( 'auto', __( 'Guess country by user\'s IP address', 'bookly' ) ) ) );
|
13 |
+
Inputs::renderText( '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' ) );
|
14 |
+
|
15 |
+
Proxy\Pro::renderCustomersBirthday();
|
16 |
+
Proxy\Pro::renderCustomersAddress();
|
17 |
+
|
18 |
+
Proxy\Pro::renderCancelAppointmentAction();
|
19 |
+
Proxy\Pro::renderCombinedNotifications();
|
20 |
+
Selects::renderSingle( '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' ) );
|
21 |
+
Selects::renderSingle( 'bookly_cst_allow_duplicates', __( 'Allow duplicate customers', 'bookly' ), __( 'If enabled, a new user will be created if any of the registration data during the booking is different.', 'bookly' ) );
|
22 |
+
Selects::renderSingle( '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' ) );
|
23 |
?>
|
24 |
<div class="panel-footer">
|
25 |
+
<?php ControlInputs::renderCsrf() ?>
|
26 |
+
<?php Buttons::renderSubmit() ?>
|
27 |
+
<?php Buttons::renderReset( 'bookly-customer-reset' ) ?>
|
28 |
</div>
|
29 |
</form>
|
backend/modules/settings/templates/_facebookForm.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs as ControlInputs;
|
4 |
+
use Bookly\Backend\Components\Settings\Inputs;
|
5 |
+
?>
|
6 |
+
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'facebook' ) ) ?>">
|
7 |
+
<div class="form-group">
|
8 |
+
<h4><?php esc_html_e( 'Instructions', 'bookly' ) ?></h4>
|
9 |
+
<p><?php esc_html_e( 'To set up Facebook integration, do the following:', 'bookly' ) ?></p>
|
10 |
+
<ol>
|
11 |
+
<li><?php _e( 'Follow the steps at <a href="https://developers.facebook.com/docs/apps/register" target="_blank">https://developers.facebook.com/docs/apps/register</a> to create a Developer Account, register and configure your <b>Facebook App</b>. Below the App Details Panel click <b>Add Platform</b> button, select Website and enter your website URL.', 'bookly' ) ?></li>
|
12 |
+
<li><?php _e( 'Go to your <a href="https://developers.facebook.com/apps/" target="_blank">App Dashboard</a>. In the left side navigation panel of the App Dashboard, click <b>Settings > Basic</b> to view the App Details Panel with your <b>App ID</b>. Use it in the form below.', 'bookly' ) ?></li>
|
13 |
+
</ol>
|
14 |
+
</div>
|
15 |
+
<?php Inputs::renderText( 'bookly_fb_app_id', __( 'App ID', 'bookly' ) ) ?>
|
16 |
+
<div class="panel-footer">
|
17 |
+
<?php ControlInputs::renderCsrf() ?>
|
18 |
+
<?php Buttons::renderSubmit() ?>
|
19 |
+
<?php Buttons::renderReset() ?>
|
20 |
+
</div>
|
21 |
+
</form>
|
backend/modules/settings/templates/_generalForm.php
CHANGED
@@ -1,36 +1,27 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
|
|
|
|
|
|
5 |
?>
|
6 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'general' ) ) ?>">
|
7 |
<?php
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
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
|
33 |
-
<?php
|
34 |
-
<?php
|
35 |
</div>
|
36 |
</form>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs as ControlInputs;
|
4 |
+
use Bookly\Backend\Components\Settings\Inputs;
|
5 |
+
use Bookly\Backend\Components\Settings\Selects;
|
6 |
+
use Bookly\Lib\Entities\CustomerAppointment;
|
7 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
8 |
?>
|
9 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'general' ) ) ?>">
|
10 |
<?php
|
11 |
+
Selects::renderSingle( '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' ),
|
12 |
+
$values['bookly_gen_time_slot_length'] );
|
13 |
+
Selects::renderSingle( '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' ) );
|
14 |
+
Selects::renderSingle( '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' ) ), ) );
|
15 |
+
Proxy\Pro::renderMinimumTimeRequirement();
|
16 |
+
Inputs::renderNumber( '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 );
|
17 |
+
Selects::renderSingle( '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' ) );
|
18 |
+
Selects::renderSingle( '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' ) );
|
19 |
+
Selects::renderSingle( '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' ), array( array( 'enqueue', 'Enqueue' ), array( 'print', 'Print' ) ) );
|
20 |
+
Selects::renderSingle( 'bookly_gen_collect_stats', __( 'Help us improve Bookly by sending anonymous usage stats', 'bookly' ) );
|
|
|
|
|
|
|
|
|
21 |
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
<div class="panel-footer">
|
23 |
+
<?php ControlInputs::renderCsrf() ?>
|
24 |
+
<?php Buttons::renderSubmit() ?>
|
25 |
+
<?php Buttons::renderReset() ?>
|
26 |
</div>
|
27 |
</form>
|
backend/modules/settings/templates/_googleCalendarForm.php
DELETED
@@ -1,36 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'google_calendar' ) ) ?>">
|
3 |
-
<div class="form-group">
|
4 |
-
<h4 class="bookly-bold"><?php _e( 'Instructions', 'bookly' ) ?></h4>
|
5 |
-
<p><?php _e( 'To find your client ID and client secret, do the following:', 'bookly' ) ?></p>
|
6 |
-
<ol>
|
7 |
-
<li><?php _e( 'Go to the <a href="https://console.developers.google.com/" target="_blank">Google Developers Console</a>.', 'bookly' ) ?></li>
|
8 |
-
<li><?php _e( 'Select a project, or create a new one.', 'bookly' ) ?></li>
|
9 |
-
<li><?php _e( 'Click in the upper left part to see a sliding sidebar. Next, click <b>API Manager</b>. In the list of APIs look for <b>Calendar API</b> and make sure it is enabled.', 'bookly' ) ?></li>
|
10 |
-
<li><?php _e( 'In the sidebar on the left, select <b>Credentials</b>.', 'bookly' ) ?></li>
|
11 |
-
<li><?php _e( 'Go to <b>OAuth consent screen</b> tab and give a name to the product, then click <b>Save</b>.', 'bookly' ) ?></li>
|
12 |
-
<li><?php _e( 'Go to <b>Credentials</b> tab and in <b>New credentials</b> drop-down menu select <b>OAuth client ID</b>.', 'bookly' ) ?></li>
|
13 |
-
<li><?php _e( 'Select <b>Web application</b> and create your project\'s OAuth 2.0 credentials by providing the necessary information. For <b>Authorized redirect URIs</b> enter the <b>Redirect URI</b> found below on this page. Click <b>Create</b>.', 'bookly' ) ?></li>
|
14 |
-
<li><?php _e( 'In the popup window look for the <b>Client ID</b> and <b>Client secret</b>. Use them in the form below on this page.', 'bookly' ) ?></li>
|
15 |
-
<li><?php _e( 'Go to Staff Members, select a staff member and click <b>Connect</b> which is located at the bottom of the page.', 'bookly' ) ?></li>
|
16 |
-
</ol>
|
17 |
-
</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>
|
27 |
-
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gc_two_way_sync', __( '2 way sync', 'bookly' ), __( 'By default Bookly pushes new appointments and any further changes to Google Calendar. If you enable this option then Bookly will fetch events from Google Calendar and remove corresponding time slots before displaying the second step of the booking form (this may lead to a delay when users click Next at the first step).', 'bookly' ) ) ?>
|
28 |
-
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_gc_limit_events', __( 'Limit number of fetched events', 'bookly' ), __( 'If there is a lot of events in Google Calendar sometimes this leads to a lack of memory in PHP when Bookly tries to fetch all events. You can limit the number of fetched events here. This only works when 2 way sync is enabled.', 'bookly' ),
|
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>
|
36 |
-
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/templates/_hoursForm.php
CHANGED
@@ -1,13 +1,16 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
4 |
?>
|
5 |
<p>
|
6 |
<?php _e( 'Please note, the business hours below work as a template for all new staff members. To render a list of available time slots the system takes into account only staff members\' schedule, not the company business hours. Be sure to check the schedule of your staff members if you have some unexpected behavior of the booking system.', 'bookly' ) ?>
|
7 |
</p>
|
8 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'business_hours' ) ) ?>" id="business-hours">
|
9 |
<?php for ( $i = 0; $i < 7; $i ++ ) :
|
10 |
-
$day = strtolower( \
|
11 |
?>
|
12 |
<div class="row">
|
13 |
<div class="form-group col-sm-7 col-xs-8">
|
@@ -28,8 +31,8 @@
|
|
28 |
<?php endfor ?>
|
29 |
|
30 |
<div class="panel-footer">
|
31 |
-
<?php
|
32 |
-
<?php
|
33 |
-
<?php
|
34 |
</div>
|
35 |
</form>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
4 |
+
|
5 |
+
$start_of_week = (int) get_option( 'start_of_week' );
|
6 |
+
$form = new Bookly\Backend\Modules\Settings\Forms\BusinessHours()
|
7 |
?>
|
8 |
<p>
|
9 |
<?php _e( 'Please note, the business hours below work as a template for all new staff members. To render a list of available time slots the system takes into account only staff members\' schedule, not the company business hours. Be sure to check the schedule of your staff members if you have some unexpected behavior of the booking system.', 'bookly' ) ?>
|
10 |
</p>
|
11 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'business_hours' ) ) ?>" id="business-hours">
|
12 |
<?php for ( $i = 0; $i < 7; $i ++ ) :
|
13 |
+
$day = strtolower( Bookly\Lib\Utils\DateTime::getWeekDayByNumber( ( $i + $start_of_week ) % 7 ) );
|
14 |
?>
|
15 |
<div class="row">
|
16 |
<div class="form-group col-sm-7 col-xs-8">
|
31 |
<?php endfor ?>
|
32 |
|
33 |
<div class="panel-footer">
|
34 |
+
<?php Inputs::renderCsrf() ?>
|
35 |
+
<?php Buttons::renderSubmit() ?>
|
36 |
+
<?php Buttons::renderReset( 'bookly-hours-reset' ) ?>
|
37 |
</div>
|
38 |
</form>
|
backend/modules/settings/templates/_payment_local.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Settings\Selects;
|
3 |
+
?>
|
4 |
+
<div class="panel panel-default bookly-js-collapse" data-slug="local">
|
5 |
+
<div class="panel-heading">
|
6 |
+
<i class="bookly-js-handle bookly-margin-right-sm bookly-icon bookly-icon-draghandle bookly-cursor-move ui-sortable-handle" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
7 |
+
<a href="#bookly_pmt_local" class="panel-title" role="button" data-toggle="collapse">
|
8 |
+
<?php _e( 'Service paid locally', 'bookly' ) ?>
|
9 |
+
</a>
|
10 |
+
</div>
|
11 |
+
<div id="bookly_pmt_local" class="panel-collapse collapse in">
|
12 |
+
<div class="panel-body">
|
13 |
+
<?php Selects::renderSingle( 'bookly_pmt_local' ) ?>
|
14 |
+
</div>
|
15 |
+
</div>
|
16 |
+
</div>
|
backend/modules/settings/templates/_paymentsForm.php
CHANGED
@@ -1,11 +1,12 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
4 |
-
use
|
|
|
5 |
?>
|
6 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'payments' ) ) ?>">
|
7 |
<div class="row">
|
8 |
-
<div class="col-lg-
|
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">
|
@@ -15,7 +16,7 @@ use BooklyLite\Lib\Proxy;
|
|
15 |
</select>
|
16 |
</div>
|
17 |
</div>
|
18 |
-
<div class="col-lg-
|
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">
|
@@ -25,49 +26,18 @@ use BooklyLite\Lib\Proxy;
|
|
25 |
</select>
|
26 |
</div>
|
27 |
</div>
|
28 |
-
<?php Proxy\Coupons::renderSettings() ?>
|
29 |
</div>
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
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
|
70 |
-
<?php
|
71 |
-
<?php
|
|
|
72 |
</div>
|
73 |
</form>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Inputs;
|
3 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
4 |
+
use Bookly\Lib\Utils\Price;
|
5 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
6 |
?>
|
7 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'payments' ) ) ?>">
|
8 |
<div class="row">
|
9 |
+
<div class="col-lg-6">
|
10 |
<div class="form-group">
|
11 |
<label for="bookly_pmt_currency"><?php _e( 'Currency', 'bookly' ) ?></label>
|
12 |
<select id="bookly_pmt_currency" class="form-control" name="bookly_pmt_currency">
|
16 |
</select>
|
17 |
</div>
|
18 |
</div>
|
19 |
+
<div class="col-lg-6">
|
20 |
<div class="form-group">
|
21 |
<label for="bookly_pmt_price_format"><?php _e( 'Price format', 'bookly' ) ?></label>
|
22 |
<select id="bookly_pmt_price_format" class="form-control" name="bookly_pmt_price_format">
|
26 |
</select>
|
27 |
</div>
|
28 |
</div>
|
|
|
29 |
</div>
|
30 |
+
<?php Proxy\DepositPayments::renderPayments() ?>
|
31 |
+
<?php Proxy\Taxes::renderPayments() ?>
|
32 |
+
<div id="bookly-payment-systems">
|
33 |
+
<?php foreach ( $payments as $payment ) : ?>
|
34 |
+
<?php echo $payment ?>
|
35 |
+
<?php endforeach ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
</div>
|
|
|
|
|
|
|
37 |
<div class="panel-footer">
|
38 |
+
<input type="hidden" name="bookly_pmt_order" value="<?php echo get_option( 'bookly_pmt_order' ) ?>"/>
|
39 |
+
<?php Inputs::renderCsrf() ?>
|
40 |
+
<?php Buttons::renderSubmit() ?>
|
41 |
+
<?php Buttons::renderReset( 'bookly-payments-reset' ) ?>
|
42 |
</div>
|
43 |
</form>
|
backend/modules/settings/templates/_urlForm.php
CHANGED
@@ -1,35 +1,24 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
use
|
3 |
-
use
|
|
|
|
|
4 |
?>
|
5 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'url' ) ) ?>">
|
6 |
<?php
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
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
|
32 |
-
<?php
|
33 |
-
<?php
|
34 |
</div>
|
35 |
</form>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components\Controls\Buttons;
|
3 |
+
use Bookly\Backend\Components\Controls\Inputs as ControlInputs;
|
4 |
+
use Bookly\Backend\Components\Settings\Inputs;
|
5 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
6 |
?>
|
7 |
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'url' ) ) ?>">
|
8 |
<?php
|
9 |
+
Inputs::renderText( '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' ) );
|
10 |
+
Inputs::renderText( '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' ) );
|
11 |
+
Inputs::renderText( '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' ) );
|
12 |
+
Inputs::renderText( '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' ) );
|
13 |
+
Proxy\Pro::renderCancellationConfirmationUrl();
|
14 |
+
Inputs::renderText( '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' ) );
|
15 |
+
Inputs::renderText( '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' ) );
|
16 |
+
Proxy\Pro::renderFinalStepUrl();
|
17 |
+
Proxy\Shared::renderUrlSettings();
|
18 |
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
<div class="panel-footer">
|
20 |
+
<?php ControlInputs::renderCsrf() ?>
|
21 |
+
<?php Buttons::renderSubmit() ?>
|
22 |
+
<?php Buttons::renderReset() ?>
|
23 |
</div>
|
24 |
</form>
|
backend/modules/settings/templates/_woocommerce.php
DELETED
@@ -1,45 +0,0 @@
|
|
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>
|
6 |
-
<p>
|
7 |
-
<?php _e( 'You need to install and activate WooCommerce plugin before using the options below.<br/><br/>Once the plugin is activated do the following steps:', 'bookly' ) ?>
|
8 |
-
</p>
|
9 |
-
<ol>
|
10 |
-
<li><?php _e( 'Create a product in WooCommerce that can be placed in cart.', 'bookly' ) ?></li>
|
11 |
-
<li><?php _e( 'In the form below enable WooCommerce option.', 'bookly' ) ?></li>
|
12 |
-
<li><?php _e( 'Select the product that you created at step 1 in the drop down list of products.', 'bookly' ) ?></li>
|
13 |
-
<li><?php _e( 'If needed, edit item data which will be displayed in the cart.', 'bookly' ) ?></li>
|
14 |
-
</ol>
|
15 |
-
<p>
|
16 |
-
<?php _e( 'Note that once you have enabled WooCommerce option in Bookly the built-in payment methods will no longer work. All your customers will be redirected to WooCommerce cart instead of standard payment step.', 'bookly' ) ?>
|
17 |
-
</p>
|
18 |
-
</div>
|
19 |
-
|
20 |
-
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'bookly_wc_enabled', 'WooCommerce' ) ?>
|
21 |
-
|
22 |
-
<div class="form-group">
|
23 |
-
<label for="bookly_wc_product"><?php _e( 'Booking product', 'bookly' ) ?></label>
|
24 |
-
<select id="bookly_wc_product" class="form-control" name="bookly_wc_product">
|
25 |
-
<?php foreach ( $candidates as $item ) : ?>
|
26 |
-
<option value="<?php echo $item['id'] ?>" <?php selected( get_option( 'bookly_wc_product' ), $item['id'] ) ?>>
|
27 |
-
<?php echo $item['name'] ?>
|
28 |
-
</option>
|
29 |
-
<?php endforeach ?>
|
30 |
-
</select>
|
31 |
-
</div>
|
32 |
-
|
33 |
-
<?php \BooklyLite\Lib\Utils\Common::optionText( 'bookly_l10n_wc_cart_info_name', __( 'Cart item data', 'bookly' ) ) ?>
|
34 |
-
<div class="form-group">
|
35 |
-
<textarea class="form-control" rows="8" name="bookly_l10n_wc_cart_info_value"
|
36 |
-
placeholder="<?php _e( 'Enter a value', 'bookly' ) ?>"><?php echo esc_textarea( get_option( 'bookly_l10n_wc_cart_info_value' ) ) ?></textarea><br/>
|
37 |
-
<?php $this->render( '_woocommerce_codes' ) ?>
|
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>
|
45 |
-
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/templates/_woocommerce_codes.php
DELETED
@@ -1,13 +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' => 'category_name', 'description' => __( 'name of category', 'bookly' ), ),
|
6 |
-
array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ), ),
|
7 |
-
array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ), ),
|
8 |
-
array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ), ),
|
9 |
-
array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ), ),
|
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
@@ -1,11 +1,14 @@
|
|
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( 'Settings', 'bookly' ) ?>
|
7 |
</div>
|
8 |
-
<?php \
|
9 |
</div>
|
10 |
<div class="row">
|
11 |
<div id="bookly-sidebar" class="col-sm-4">
|
@@ -25,16 +28,9 @@
|
|
25 |
<li class="bookly-nav-item" data-target="#bookly_settings_customers" data-toggle="tab">
|
26 |
<?php _e( 'Customers', 'bookly' ) ?>
|
27 |
</li>
|
28 |
-
|
29 |
-
|
30 |
-
|
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>
|
@@ -44,6 +40,7 @@
|
|
44 |
<li class="bookly-nav-item" data-target="#bookly_settings_holidays" data-toggle="tab">
|
45 |
<?php _e( 'Holidays', 'bookly' ) ?>
|
46 |
</li>
|
|
|
47 |
</ul>
|
48 |
</div>
|
49 |
|
@@ -66,16 +63,9 @@
|
|
66 |
<div class="tab-pane" id="bookly_settings_customers">
|
67 |
<?php include '_customers.php' ?>
|
68 |
</div>
|
69 |
-
|
70 |
-
|
71 |
-
|
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>
|
@@ -85,6 +75,7 @@
|
|
85 |
<div class="tab-pane" id="bookly_settings_holidays">
|
86 |
<?php include '_holidaysForm.php' ?>
|
87 |
</div>
|
|
|
88 |
</div>
|
89 |
</div>
|
90 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Modules\Settings\Proxy;
|
3 |
+
use Bookly\Backend\Components;
|
4 |
+
?>
|
5 |
<div id="bookly-tbs" class="wrap">
|
6 |
<div class="bookly-tbs-body">
|
7 |
<div class="page-header text-right clearfix">
|
8 |
<div class="bookly-page-title">
|
9 |
<?php _e( 'Settings', 'bookly' ) ?>
|
10 |
</div>
|
11 |
+
<?php Components\Support\Buttons::render( '' ) ?>
|
12 |
</div>
|
13 |
<div class="row">
|
14 |
<div id="bookly-sidebar" class="col-sm-4">
|
28 |
<li class="bookly-nav-item" data-target="#bookly_settings_customers" data-toggle="tab">
|
29 |
<?php _e( 'Customers', 'bookly' ) ?>
|
30 |
</li>
|
31 |
+
<?php Proxy\Pro::renderGoogleCalendarMenuItem() ?>
|
32 |
+
<?php Proxy\ProSettings::renderProMenuItem() ?>
|
33 |
+
<?php Proxy\Shared::renderMenuItem() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
<li class="bookly-nav-item" data-target="#bookly_settings_payments" data-toggle="tab">
|
35 |
<?php _e( 'Payments', 'bookly' ) ?>
|
36 |
</li>
|
40 |
<li class="bookly-nav-item" data-target="#bookly_settings_holidays" data-toggle="tab">
|
41 |
<?php _e( 'Holidays', 'bookly' ) ?>
|
42 |
</li>
|
43 |
+
<?php Proxy\Pro::renderPurchaseCodeMenuItem() ?>
|
44 |
</ul>
|
45 |
</div>
|
46 |
|
63 |
<div class="tab-pane" id="bookly_settings_customers">
|
64 |
<?php include '_customers.php' ?>
|
65 |
</div>
|
66 |
+
<?php Proxy\Pro::renderGoogleCalendarTab() ?>
|
67 |
+
<?php Proxy\ProSettings::renderProTab() ?>
|
68 |
+
<?php Proxy\Shared::renderTab() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
<div class="tab-pane" id="bookly_settings_payments">
|
70 |
<?php include '_paymentsForm.php' ?>
|
71 |
</div>
|
75 |
<div class="tab-pane" id="bookly_settings_holidays">
|
76 |
<?php include '_holidaysForm.php' ?>
|
77 |
</div>
|
78 |
+
<?php Proxy\Pro::renderPurchaseCodeTab() ?>
|
79 |
</div>
|
80 |
</div>
|
81 |
</div>
|
backend/modules/shop/Ajax.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Shop;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Shop
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Get data for shop page.
|
14 |
+
*/
|
15 |
+
public static function getShopData()
|
16 |
+
{
|
17 |
+
$response = array();
|
18 |
+
$order = self::parameter( 'sort' );
|
19 |
+
if ( ! Lib\Entities\Shop::query()->count() ) {
|
20 |
+
Lib\Routines::handleDailyInfo();
|
21 |
+
$order = 'date';
|
22 |
+
}
|
23 |
+
$query = Lib\Entities\Shop::query();
|
24 |
+
switch ( $order ) {
|
25 |
+
case 'sales':
|
26 |
+
$query = $query
|
27 |
+
->sortBy( 'sales' )
|
28 |
+
->order( 'DESC' );
|
29 |
+
break;
|
30 |
+
case 'rating':
|
31 |
+
$query = $query
|
32 |
+
->sortBy( 'rating' )
|
33 |
+
->order( 'DESC' );
|
34 |
+
break;
|
35 |
+
case 'date':
|
36 |
+
$query = $query
|
37 |
+
->sortBy( 'published' )
|
38 |
+
->order( 'DESC' );
|
39 |
+
break;
|
40 |
+
case 'price_low':
|
41 |
+
$query = $query
|
42 |
+
->sortBy( 'price' );
|
43 |
+
break;
|
44 |
+
case 'price_high':
|
45 |
+
$query = $query
|
46 |
+
->sortBy( 'price' )
|
47 |
+
->order( 'DESC' );
|
48 |
+
break;
|
49 |
+
default:
|
50 |
+
$query = $query
|
51 |
+
->sortBy( 'type DESC, created' )
|
52 |
+
->order( 'DESC' );
|
53 |
+
break;
|
54 |
+
}
|
55 |
+
$shop = $query->fetchArray();
|
56 |
+
|
57 |
+
// Get a list of installed plugins
|
58 |
+
$plugins_installed = array_keys( apply_filters( 'bookly_plugins', array() ) );
|
59 |
+
foreach ( glob( Lib\Plugin::getDirectory() . '/../bookly-addon-*', GLOB_ONLYDIR ) as $path ) {
|
60 |
+
$plugins_installed[] = basename( $path );
|
61 |
+
}
|
62 |
+
|
63 |
+
$disabled = Lib\Config::proActive() ? null : ' disabled';
|
64 |
+
// Build a list of plugins for a shop page
|
65 |
+
$response['shop'] = array();
|
66 |
+
foreach ( $shop as $plugin ) {
|
67 |
+
$installed = in_array( $plugin['slug'], $plugins_installed );
|
68 |
+
$response['shop'][] = array(
|
69 |
+
'plugin_class' => $plugin['type'] == 'bundle' ? 'bookly-shop-bundle' : '',
|
70 |
+
'title' => $plugin['title'],
|
71 |
+
'description' => $plugin['description'],
|
72 |
+
'icon' => '<img src="' . $plugin['icon'] . '"/>',
|
73 |
+
'new' => ( $plugin['seen'] == 0 || ( strtotime( $plugin['published'] ) > strtotime( '-2 weeks' ) ) ) ? __( 'New', 'bookly' ) : '',
|
74 |
+
'price' => '$' . $plugin['price'],
|
75 |
+
'sales' => sprintf( _n( '%d sale', '%d sales', $plugin['sales'], 'bookly' ), $plugin['sales'] ),
|
76 |
+
'rating_class' => (int) $plugin['rating'] ? '': 'collapse',
|
77 |
+
'rating' => $plugin['rating'],
|
78 |
+
'reviews' => sprintf( _n( '%d review', '%d reviews', $plugin['reviews'], 'bookly' ), $plugin['reviews'] ),
|
79 |
+
'url_class' => $installed ? 'btn-default' : $plugin['slug'] == 'bookly-addon-pro' ? 'btn-success' : 'btn-success' . $disabled,
|
80 |
+
'url_text' => $installed ? __( 'Installed', 'bookly' ) : __( 'Get it!', 'bookly' ),
|
81 |
+
'url' => Lib\Utils\Common::prepareUrlReferrers( $plugin['url'] . '?ref=ladela', 'shop' ),
|
82 |
+
);
|
83 |
+
}
|
84 |
+
|
85 |
+
// Mark all plugins as seen
|
86 |
+
Lib\Entities\Shop::query()->update()->set( 'seen', 1 )->execute();
|
87 |
+
|
88 |
+
return wp_send_json_success( $response );
|
89 |
+
}
|
90 |
+
}
|
backend/modules/shop/Page.php
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Shop;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Page
|
8 |
+
* @package Bookly\Backend\Modules\Shop
|
9 |
+
*/
|
10 |
+
class Page extends Lib\Base\Component
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render page.
|
14 |
+
*/
|
15 |
+
public static function render()
|
16 |
+
{
|
17 |
+
self::enqueueStyles( array(
|
18 |
+
'backend' => array(
|
19 |
+
'css/select2.min.css',
|
20 |
+
'bootstrap/css/bootstrap-theme.min.css',
|
21 |
+
),
|
22 |
+
'module' => array( 'css/shop.css', ),
|
23 |
+
) );
|
24 |
+
|
25 |
+
self::enqueueScripts( array(
|
26 |
+
'backend' => array(
|
27 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
+
'js/select2.full.min.js' => array( 'jquery' ),
|
29 |
+
),
|
30 |
+
'module' => array( 'js/shop.js' => array( 'jquery' ) ),
|
31 |
+
) );
|
32 |
+
|
33 |
+
wp_localize_script( 'bookly-shop.js', 'BooklyL10n', array(
|
34 |
+
'csrf_token' => Lib\Utils\Common::getCsrfToken(),
|
35 |
+
) );
|
36 |
+
|
37 |
+
$has_new_items = Lib\Entities\Shop::query()
|
38 |
+
->whereGt( 'published', date_create( 'now' )->modify( '-2 weeks' )->format( 'Y-m-d H:i:s' ) )
|
39 |
+
->where( 'seen', 0, 'OR' )
|
40 |
+
->count();
|
41 |
+
|
42 |
+
self::renderTemplate( 'index', compact( 'has_new_items' ) );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @return int
|
47 |
+
*/
|
48 |
+
public static function getNotSeenCount()
|
49 |
+
{
|
50 |
+
return Lib\Entities\Shop::query()
|
51 |
+
->where( 'seen', 0 )
|
52 |
+
->count();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Show 'Addons' submenu with counter inside Bookly main menu
|
57 |
+
*/
|
58 |
+
public static function addBooklyMenuItem()
|
59 |
+
{
|
60 |
+
$title = __( 'Addons', 'bookly' );
|
61 |
+
$count = self::getNotSeenCount();
|
62 |
+
if ( $count ) {
|
63 |
+
add_submenu_page( 'bookly-menu', $title, sprintf( '%s <span class="update-plugins count-%d"><span class="update-count">%d</span></span>', $title, $count, $count ), 'manage_options',
|
64 |
+
self::pageSlug(), function () { Page::render(); } );
|
65 |
+
} else {
|
66 |
+
add_submenu_page( 'bookly-menu', $title, $title, 'manage_options',
|
67 |
+
self::pageSlug(), function () { Page::render(); } );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
backend/modules/shop/resources/css/shop.css
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#bookly-tbs .panel.bookly-shop-plugin.bookly-shop-bundle {
|
2 |
+
border-color: #e0e0a0;
|
3 |
+
}
|
4 |
+
|
5 |
+
#bookly-tbs .panel.bookly-shop-plugin.bookly-shop-bundle .panel-heading {
|
6 |
+
background-color: #ffffe0;
|
7 |
+
}
|
8 |
+
|
9 |
+
#bookly-tbs .panel.bookly-shop-plugin .panel-heading .badge-danger {
|
10 |
+
background-color: #ff0000;
|
11 |
+
}
|
12 |
+
|
13 |
+
#bookly-tbs .panel.bookly-shop-plugin .panel-heading .bookly-shop-rating {
|
14 |
+
color: #ffa500;
|
15 |
+
}
|
backend/modules/shop/resources/js/shop.js
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function ($) {
|
2 |
+
var $sort = $('#bookly-shop-sort'),
|
3 |
+
$shop = $('#bookly-shop'),
|
4 |
+
$loading = $('#bookly-shop-loading'),
|
5 |
+
$template = $('#bookly-shop-template')
|
6 |
+
;
|
7 |
+
$('.bookly-js-select').select2({
|
8 |
+
width : '100%',
|
9 |
+
theme : 'bootstrap',
|
10 |
+
allowClear : true,
|
11 |
+
minimumResultsForSearch: -1
|
12 |
+
});
|
13 |
+
$sort.on('change', function () {
|
14 |
+
$loading.show();
|
15 |
+
$shop.hide();
|
16 |
+
$.ajax({
|
17 |
+
url : ajaxurl,
|
18 |
+
type : 'GET',
|
19 |
+
data : {
|
20 |
+
action : 'bookly_get_shop_data',
|
21 |
+
csrf_token: BooklyL10n.csrf_token,
|
22 |
+
sort : $sort.val()
|
23 |
+
},
|
24 |
+
dataType: 'json',
|
25 |
+
success : function (response) {
|
26 |
+
if (response.data.shop.length) {
|
27 |
+
$shop.html('');
|
28 |
+
$.each(response.data.shop, function (id, plugin) {
|
29 |
+
var rating = '';
|
30 |
+
for (var i = 0; i < 5; i++) {
|
31 |
+
if (plugin.rating - i > 0.5) {
|
32 |
+
rating += '<i class="dashicons dashicons-star-filled"></i>';
|
33 |
+
} else if (plugin.rating - i > 0) {
|
34 |
+
rating += '<i class="dashicons dashicons-star-half"></i>';
|
35 |
+
} else {
|
36 |
+
rating += '<i class="dashicons dashicons-star-empty"></i>';
|
37 |
+
}
|
38 |
+
}
|
39 |
+
$shop.append(
|
40 |
+
$template.clone().show().html()
|
41 |
+
.replace(/{{rating}}/g, rating)
|
42 |
+
.replace(/{{(.+?)}}/g, function (match) {
|
43 |
+
return plugin[match.substring(2, match.length - 2)];
|
44 |
+
})
|
45 |
+
);
|
46 |
+
});
|
47 |
+
|
48 |
+
}
|
49 |
+
$shop.show();
|
50 |
+
$loading.hide();
|
51 |
+
}
|
52 |
+
});
|
53 |
+
}).trigger('change');
|
54 |
+
});
|
backend/modules/shop/templates/index.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use Bookly\Backend\Components;
|
3 |
+
use Bookly\Lib as BooklyLib;
|
4 |
+
?>
|
5 |
+
<div id="bookly-tbs" class="wrap">
|
6 |
+
<div class="bookly-tbs-body">
|
7 |
+
<div class="page-header text-right clearfix">
|
8 |
+
<div class="bookly-page-title">
|
9 |
+
<?php _e( 'Addons', 'bookly' ) ?>
|
10 |
+
</div>
|
11 |
+
<?php Components\Support\Buttons::render( $self::pageSlug() ) ?>
|
12 |
+
</div>
|
13 |
+
<div class="panel panel-default bookly-main">
|
14 |
+
<div class="panel-body">
|
15 |
+
<div class="row">
|
16 |
+
<div class="col-sm-2">
|
17 |
+
<div class="form-group">
|
18 |
+
<select class="form-control bookly-js-select" id="bookly-shop-sort" data-placeholder="<?php echo esc_attr( __( 'Sort by', 'bookly' ) ) ?>">
|
19 |
+
<option></option>
|
20 |
+
<option value="sales"<?php selected( ! $has_new_items ) ?>><?php esc_html_e( 'Best Sellers', 'bookly' ) ?></option>
|
21 |
+
<option value="rating"><?php esc_html_e( 'Best Rated', 'bookly' ) ?></option>
|
22 |
+
<option value="date"<?php selected( $has_new_items ) ?>><?php esc_html_e( 'Newest Items', 'bookly' ) ?></option>
|
23 |
+
<option value="price_low"><?php esc_html_e( 'Price: low to high', 'bookly' ) ?></option>
|
24 |
+
<option value="price_high"><?php esc_html_e( 'Price: high to low', 'bookly' ) ?></option>
|
25 |
+
</select>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
<div class="panel-group">
|
30 |
+
<div id="bookly-shop" class="collapse"></div>
|
31 |
+
</div>
|
32 |
+
<div id="bookly-shop-loading" class="bookly-loading"></div>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
</div>
|
36 |
+
<div id="bookly-shop-template" class="collapse">
|
37 |
+
<div class="bookly-shop-plugin {{plugin_class}} panel panel-default">
|
38 |
+
<div class="panel-heading">
|
39 |
+
<div class="row">
|
40 |
+
<div class="col-lg-10 col-md-9 col-xs-7">
|
41 |
+
<div class="row">
|
42 |
+
<div class="col-lg-4 col-md-6 bookly-margin-bottom-md">
|
43 |
+
<div class="bookly-flexbox">
|
44 |
+
<div class="bookly-flex-cell bookly-margin-bottom-md bookly-vertical-top" style="width: 1%">
|
45 |
+
<a href="{{url}}" target="_blank">{{icon}}</a>
|
46 |
+
</div>
|
47 |
+
<div class="bookly-flex-cell bookly-vertical-top">
|
48 |
+
<div class="h2 bookly-margin-top-remove bookly-margin-left-lg"><a href="{{url}}" target="_blank">{{title}}</a> <span class="badge badge-danger">{{new}}</span></div>
|
49 |
+
<a class="bookly-margin-bottom-lg bookly-margin-left-lg" href="<?php echo BooklyLib\Utils\Common::prepareUrlReferrers( 'https://codecanyon.net/user/ladela/portfolio?ref=ladela', 'shop' ) ?>" target="_blank">Ladela</a>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
<div class="col-lg-8 col-md-6">{{description}}</div>
|
54 |
+
</div>
|
55 |
+
</div>
|
56 |
+
<div class="col-lg-2 col-md-3 col-xs-5">
|
57 |
+
<div class="text-center">
|
58 |
+
<div class="h2 bookly-margin-top-remove bookly-margin-bottom-remove">{{price}}</div>
|
59 |
+
<div>{{sales}}</div>
|
60 |
+
<div class="bookly-shop-rating {{rating_class}}">{{rating}}</div>
|
61 |
+
<div class="bookly-margin-bottom-lg">{{reviews}}</div>
|
62 |
+
<a href="{{url}}" class="btn btn-lg {{url_class}}" target="_blank"><b>{{url_text}}</b></a><br/>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
</div>
|
backend/modules/sms/Ajax.php
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Backend\Modules\Sms;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Ajax
|
8 |
+
* @package Bookly\Backend\Modules\Sms
|
9 |
+
*/
|
10 |
+
class Ajax extends Lib\Base\Ajax
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Get purchases list.
|
14 |
+
*/
|
15 |
+
public static function getPurchasesList()
|
16 |
+
{
|
17 |
+
$sms = new Lib\SMS();
|
18 |
+
|
19 |
+
$dates = explode( ' - ', self::parameter( 'range' ), 2 );
|
20 |
+
$start = Lib\Utils\DateTime::applyTimeZoneOffset( $dates[0], 0 );
|
21 |
+
$end = Lib\Utils\DateTime::applyTimeZoneOffset( date( 'Y-m-d', strtotime( '+1 day', strtotime( $dates[1] ) ) ), 0 );
|
22 |
+
|
23 |
+
wp_send_json( $sms->getPurchasesList( $start, $end ) );
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Get SMS list.
|
28 |
+
*/
|
29 |
+
public static function getSmsList()
|
30 |
+
{
|
31 |
+
$sms = new Lib\SMS();
|
32 |
+
|
33 |
+
$dates = explode( ' - ', self::parameter( 'range' ), 2 );
|
34 |
+
$start = Lib\Utils\DateTime::applyTimeZoneOffset( $dates[0], 0 );
|
35 |
+
$end = Lib\Utils\DateTime::applyTimeZoneOffset( date( 'Y-m-d', strtotime( '+1 day', strtotime( $dates[1] ) ) ), 0 );
|
36 |
+
|
37 |
+
wp_send_json( $sms->getSmsList( $start, $end ) );
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Get price-list.
|
42 |
+
*/
|
43 |
+
public static function getPriceList()
|
44 |
+
{
|
45 |
+
$sms = new Lib\SMS();
|
46 |
+
wp_send_json( $sms->getPriceList() );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Initial for enabling Auto-Recharge balance
|
51 |
+
*/
|
52 |
+
public static function initAutoRecharge()
|
53 |
+
{
|
54 |
+
$sms = new Lib\SMS();
|
55 |
+
$key = $sms->getPreapprovalKey( self::parameter( 'amount' ) );
|
56 |
+
if ( $key !== false ) {
|
57 |
+
wp_send_json_success( array( 'paypal_preapproval' => 'https://www.paypal.com/cgi-bin/webscr?cmd=_ap-preapproval&preapprovalkey=' . $key ) );
|
58 |
+
} else {
|
59 |
+
wp_send_json_error( array( 'message' => __( 'Auto-Recharge has failed, please replenish your balance directly.', 'bookly' ) ) );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Disable Auto-Recharge balance
|
65 |
+
*/
|
66 |
+
public static function declineAutoRecharge()
|
67 |
+
{
|
68 |
+
$sms = new Lib\SMS();
|
69 |
+
$declined = $sms->declinePreapproval();
|
70 |
+
if ( $declined !== false ) {
|
71 |
+
wp_send_json_success( array( 'message' => __( 'Auto-Recharge disabled', 'bookly' ) ) );
|
72 |
+
} else {
|
73 |
+
wp_send_json_error( array( 'message' => __( 'Error. Can\'t disable Auto-Recharge, you can perform this action in your PayPal account.', 'bookly' ) ) );
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Change password.
|
79 |
+
*/
|
80 |
+
public static function changePassword()
|
81 |
+
{
|
82 |
+
$sms = new Lib\SMS();
|
83 |
+
$old_password = self::parameter( 'old_password' );
|
84 |
+
$new_password = self::parameter( 'new_password' );
|
85 |
+
|
86 |
+
$result = $sms->changePassword( $new_password, $old_password );
|
87 |
+
if ( $result === false ) {
|
88 |
+
wp_send_json_error( array( 'message' => current( $sms->getErrors() ) ) );
|
89 |
+
} else {
|
90 |
+
wp_send_json_success();
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Send test SMS.
|
96 |
+
*/
|
97 |
+
public static function sendTestSms()
|
98 |
+
{
|
99 |
+
$sms = new Lib\SMS();
|
100 |
+
|
101 |
+
$response = array( 'success' => $sms->sendSms(
|
102 |
+
self::parameter( 'phone_number' ),
|
103 |
+
'Bookly test SMS.',
|
104 |
+
Lib\Entities\Notification::$type_ids['test_message']
|
105 |
+
) );
|
106 |
+
|
107 |
+
if ( $response['success'] ) {
|
108 |
+
$response['message'] = __( 'SMS has been sent successfully.', 'bookly' );
|
109 |
+
} else {
|
110 |
+
$response['message'] = implode( ' ', $sms->getErrors() );
|
111 |
+
}
|
112 |
+
|
113 |
+
wp_send_json( $response );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Forgot password.
|
118 |
+
*/
|
119 |
+
public static function forgotPassword()
|
120 |
+
{
|
121 |
+
$sms = new Lib\SMS();
|
122 |
+
$step = self::parameter( 'step' );
|
123 |
+
$code = self::parameter( 'code' );
|
124 |
+
$username = self::parameter( 'username' );
|
125 |
+
$password = self::parameter( 'password' );
|
126 |
+
$result = $sms->forgotPassword( $username, $step, $code, $password );
|
127 |
+
if ( $result === false ) {
|
128 |
+
wp_send_json_error( array( 'message' => current( $sms->getErrors() ) ) );
|
129 |
+
} else {
|
130 |
+
wp_send_json_success();
|
131 |
+
}
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Get Sender IDs list.
|
136 |
+
*/
|
137 |
+
public static function getSenderIdsList()
|
138 |
+
{
|
139 |
+
$sms = new Lib\SMS();
|
140 |
+
wp_send_json( $sms->getSenderIdsList() );
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Request new Sender ID.
|
145 |
+
*/
|
146 |
+
public static function requestSenderId()
|
147 |
+
{
|
148 |
+
$sms = new Lib\SMS();
|
149 |
+
$result = $sms->requestSenderId( self::parameter( 'sender_id' ) );
|
150 |
+
if ( $result === false ) {
|
151 |
+
wp_send_json_error( array( 'message' => current( $sms->getErrors() ) ) );
|
152 |
+
} else {
|
153 |
+
wp_send_json_success( array( 'request_id' => $result->request_id ) );
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Cancel request for Sender ID.
|
159 |
+
*/
|
160 |
+
public static function cancelSenderId()
|
161 |
+
{
|
162 |
+
$sms = new Lib\SMS();
|
163 |
+
$result = $sms->cancelSenderId();
|
164 |
+
if ( $result === false ) {
|
165 |
+
wp_send_json_error( array( 'message' => current( $sms->getErrors() ) ) );
|
166 |
+
} else {
|
167 |
+
wp_send_json_success();
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Reset Sender ID to default (Bookly).
|
173 |
+
*/
|
174 |
+
public static function resetSenderId()
|
175 |
+
{
|
176 |
+
$sms = new Lib\SMS();
|
177 |
+
$result = $sms->resetSenderId();
|
178 |
+
if ( $result === false ) {
|
179 |
+
wp_send_json_error( array( 'message' => current( $sms->getErrors() ) ) );
|
180 |
+
} else {
|
181 |
+
wp_send_json_success();
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Enable or Disable administrators email reports.
|
187 |
+
*/
|
188 |
+
public static function adminNotify()
|
189 |
+
{
|
190 |
+
if ( in_array( self::parameter( 'option_name' ), array( 'bookly_sms_notify_low_balance', 'bookly_sms_notify_weekly_summary' ) ) ) {
|
191 |
+
update_option( self::parameter( 'option_name' ), self::parameter( 'value' ) );
|
192 |
+
}
|
193 |
+
wp_send_json_success();
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Create new custom sms notification
|
198 |
+
*/
|
199 |
+
public static function createCustomSms()
|
200 |
+
{
|
201 |
+
$notification = new Lib\Entities\Notification();
|
202 |
+
$notification
|
203 |
+
->setType( Lib\Entities\Notification::TYPE_APPOINTMENT_START_TIME )
|
204 |
+
->setToCustomer( 1 )
|
205 |
+
->setToStaff( 1 )
|
206 |
+
->setSettings( json_encode( Lib\DataHolders\Notification\Settings::getDefault() ) )
|
207 |
+
->setGateway( 'sms' )
|
208 |
+
->save();
|
209 |
+
|
210 |
+
$notification = $notification->getFields();
|
211 |
+
$id = $notification['id'];
|
212 |
+
|
213 |
+
$form = new \Bookly\Backend\Modules\Notifications\Forms\Notifications( 'sms' );
|
214 |
+
$statuses = Lib\Entities\CustomerAppointment::getStatuses();
|
215 |
+
|
216 |
+
$html = self::renderTemplate( '_custom_notification', compact( 'form', 'notification', 'statuses' ), false );
|
217 |
+
wp_send_json_success( compact( 'html', 'id' ) );
|
218 |
+
}
|
219 |
+
}
|
backend/modules/sms/Components.php
DELETED
@@ -1,348 +0,0 @@
|
|
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
DELETED
@@ -1,360 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
namespace BooklyLite\Backend\Modules\Sms;
|
3 |
-
|
4 |
-
use BooklyLite\Lib;
|
5 |
-
|
6 |
-
/**
|
7 |
-
* Class Controller
|
8 |
-
* @package BooklyLite\Backend\Modules\Sms
|
9 |
-
*/
|
10 |
-
class Controller extends Lib\Base\Controller
|
11 |
-
{
|
12 |
-
const page_slug = 'bookly-sms';
|
13 |
-
|
14 |
-
public function index()
|
15 |
-
{
|
16 |
-
global $wp_locale;
|
17 |
-
|
18 |
-
$this->enqueueStyles( array(
|
19 |
-
'frontend' => array_merge(
|
20 |
-
array( 'css/ladda.min.css', ),
|
21 |
-
get_option( 'bookly_cst_phone_default_country' ) == 'disabled'
|
22 |
-
? array()
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|