Version Description
Download this release
Release Info
Developer | Ladela |
Plugin | WordPress Online Booking and Scheduling Plugin – Bookly |
Version | 16.4 |
Comparing to | |
See all releases |
Code changes from version 16.3 to 16.4
- backend/modules/notifications/lib/Codes.php +1 -0
- backend/modules/staff/Ajax.php +11 -9
- backend/modules/staff/forms/StaffServices.php +10 -4
- backend/modules/staff/resources/js/staff.js +3 -1
- languages/bookly-ru_RU.mo +0 -0
- languages/bookly-ru_RU.po +1 -17
- lib/notifications/Codes.php +2 -2
- lib/notifications/Sender.php +2 -3
- lib/notifications/base/Codes.php +93 -0
- lib/notifications/base/Sender.php +222 -0
- lib/notifications/booking/Sender.php +250 -0
- lib/notifications/codes/Combined.php +154 -0
- lib/notifications/new_booking/Codes.php +306 -0
- lib/notifications/new_booking/Sender.php +142 -0
- lib/notifications/new_booking/proxy/Shared.php +25 -0
- lib/notifications/status_changed/StatusChanged.php +58 -0
- lib/slots/Staff.php +1 -1
- main.php +1 -1
- readme.txt +4 -3
backend/modules/notifications/lib/Codes.php
CHANGED
@@ -68,6 +68,7 @@ class Codes
|
|
68 |
),
|
69 |
'payment' => array(
|
70 |
'payment_type' => __( 'payment type', 'bookly' ),
|
|
|
71 |
'total_price' => __( 'total price of booking (sum of all cart items after applying coupon)' ),
|
72 |
),
|
73 |
'service' => array(
|
68 |
),
|
69 |
'payment' => array(
|
70 |
'payment_type' => __( 'payment type', 'bookly' ),
|
71 |
+
'payment_status' => __( 'payment status', 'bookly' ),
|
72 |
'total_price' => __( 'total price of booking (sum of all cart items after applying coupon)' ),
|
73 |
),
|
74 |
'service' => array(
|
backend/modules/staff/Ajax.php
CHANGED
@@ -45,16 +45,18 @@ class Ajax extends Lib\Base\Ajax
|
|
45 |
public static function updateStaffPosition()
|
46 |
{
|
47 |
$data = self::parameter( 'data' );
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
-
|
56 |
-
Proxy\Pro::updateCategoriesPositions( $data['categories'] );
|
57 |
-
|
58 |
wp_send_json_success();
|
59 |
}
|
60 |
|
45 |
public static function updateStaffPosition()
|
46 |
{
|
47 |
$data = self::parameter( 'data' );
|
48 |
+
if ( isset( $data['staff'] ) ) {
|
49 |
+
foreach ( $data['staff'] as $position => $staff_data ) {
|
50 |
+
$staff = Lib\Entities\Staff::find( $staff_data['staff_id'] );
|
51 |
+
$staff
|
52 |
+
->setPosition( $position )
|
53 |
+
->setCategoryId( $staff_data['category_id'] !== '' ? $staff_data['category_id'] : null )
|
54 |
+
->save();
|
55 |
+
}
|
56 |
+
}
|
57 |
+
if ( isset( $data['categories'] ) ) {
|
58 |
+
Proxy\Pro::updateCategoriesPositions( $data['categories'] );
|
59 |
}
|
|
|
|
|
|
|
60 |
wp_send_json_success();
|
61 |
}
|
62 |
|
backend/modules/staff/forms/StaffServices.php
CHANGED
@@ -77,14 +77,20 @@ class StaffServices extends Lib\Base\Form
|
|
77 |
->where( 'location_id', $location_id )
|
78 |
->whereNotIn( 'service_id', (array) $this->data['service'] )
|
79 |
->execute();
|
80 |
-
if ( isset ( $this->data['service'] ) && (
|
81 |
foreach ( $this->data['service'] as $service_id ) {
|
82 |
$staff_service = new Lib\Entities\StaffService();
|
83 |
$staff_service->loadBy( compact( 'staff_id', 'service_id', 'location_id' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
$staff_service
|
85 |
-
->setCapacityMin( $this->data['capacity_min'][ $service_id ] )
|
86 |
-
->setCapacityMax( $this->data['capacity_max'][ $service_id ] )
|
87 |
-
->setDeposit( isset ( $this->data['deposit'] ) ? $this->data['deposit'][ $service_id ] : '100%' )
|
88 |
->setPrice( $this->data['price'][ $service_id ] )
|
89 |
->setServiceId( $service_id )
|
90 |
->setStaffId( $staff_id )
|
77 |
->where( 'location_id', $location_id )
|
78 |
->whereNotIn( 'service_id', (array) $this->data['service'] )
|
79 |
->execute();
|
80 |
+
if ( isset ( $this->data['service'] ) && ( ! isset ( $this->data['custom_services'] ) || $this->data['custom_services'] == '1' ) ) {
|
81 |
foreach ( $this->data['service'] as $service_id ) {
|
82 |
$staff_service = new Lib\Entities\StaffService();
|
83 |
$staff_service->loadBy( compact( 'staff_id', 'service_id', 'location_id' ) );
|
84 |
+
if ( isset( $this->data['capacity_min'][ $service_id ] ) ) {
|
85 |
+
$staff_service->setCapacityMin( $this->data['capacity_min'][ $service_id ] );
|
86 |
+
}
|
87 |
+
if ( isset( $this->data['capacity_max'][ $service_id ] ) ) {
|
88 |
+
$staff_service->setCapacityMax( $this->data['capacity_max'][ $service_id ] );
|
89 |
+
}
|
90 |
+
if ( isset( $this->data['deposit'][ $service_id ] ) ) {
|
91 |
+
$staff_service->setDeposit( $this->data['deposit'][ $service_id ] );
|
92 |
+
}
|
93 |
$staff_service
|
|
|
|
|
|
|
94 |
->setPrice( $this->data['price'][ $service_id ] )
|
95 |
->setServiceId( $service_id )
|
96 |
->setStaffId( $staff_id )
|
backend/modules/staff/resources/js/staff.js
CHANGED
@@ -405,7 +405,9 @@ jQuery(function ($) {
|
|
405 |
function updateStaffPositions() {
|
406 |
var data = {'categories': [], 'staff': []};
|
407 |
$categories_list.find('.panel[data-category]').each(function () {
|
408 |
-
|
|
|
|
|
409 |
});
|
410 |
$categories_list.find('.bookly-js-staff-members').children('li').each(function () {
|
411 |
var $this = $(this),
|
405 |
function updateStaffPositions() {
|
406 |
var data = {'categories': [], 'staff': []};
|
407 |
$categories_list.find('.panel[data-category]').each(function () {
|
408 |
+
if ($(this).data('category')) {
|
409 |
+
data.categories.push($(this).data('category'));
|
410 |
+
}
|
411 |
});
|
412 |
$categories_list.find('.bookly-js-staff-members').children('li').each(function () {
|
413 |
var $this = $(this),
|
languages/bookly-ru_RU.mo
CHANGED
Binary file
|
languages/bookly-ru_RU.po
CHANGED
@@ -3757,18 +3757,6 @@ msgstr "Дорогой(ая) {client_name}.\n"
|
|
3757 |
"{company_phone}\n"
|
3758 |
"{company_website}"
|
3759 |
|
3760 |
-
#:
|
3761 |
-
msgid "New invoice #{invoice_number}"
|
3762 |
-
msgstr "Новый счёт №{invoice_number}"
|
3763 |
-
|
3764 |
-
#:
|
3765 |
-
msgid "Hello.\n"
|
3766 |
-
"\n"
|
3767 |
-
"Attached please find invoice #{invoice_number} for an appointment scheduled by {client_first_name} {client_last_name}"
|
3768 |
-
msgstr "Здравствуйте.\n"
|
3769 |
-
"\n"
|
3770 |
-
"В прикрепленном файле прилагается счёт №{invoice_number} за встречу, назначенную {client_first_name} {client_last_name}"
|
3771 |
-
|
3772 |
#:
|
3773 |
msgid "Invoice for your appointment"
|
3774 |
msgstr "Счёт за вашу встречу"
|
@@ -5870,10 +5858,6 @@ msgstr "общий налог для бронирования (суммарно
|
|
5870 |
msgid "total price without tax"
|
5871 |
msgstr "общая стоимость бронирования без налога"
|
5872 |
|
5873 |
-
#:
|
5874 |
-
msgid "Note if you use price correction to change the service cost according to the payment gateway used, tax will not be calculated for the additional amount to the cost. If you need to report the exact tax amount to the payment system, do not use additional charge."
|
5875 |
-
msgstr "Обратите внимание, если вы используете корректировку цены для изменения стоимости услуги в соответствии с используемой платежной системой, налог не будет рассчитываться для надбавки к стоимости. Если вам нужно сообщить точную сумму налога в платежную систему, не используйте надбавку к стоимости."
|
5876 |
-
|
5877 |
#:
|
5878 |
msgid "Dear {client_name}.\n"
|
5879 |
"\n"
|
@@ -6155,7 +6139,7 @@ msgstr "Необходимо указать название группы"
|
|
6155 |
|
6156 |
#:
|
6157 |
msgid "Important: for two-way sync, your website must use HTTPS. Google Calendar API will be able to send notifications to HTTPS address only if there is a valid SSL certificate installed on your web server. Follow the steps in this <a href=\"%s\" target=\"_blank\">document</a> to <b>verify and register your domain</b>."
|
6158 |
-
msgstr "Важно: для двусторонней синхронизации ваш сайт должен использовать HTTPS. API Google Календаря сможет отправлять уведомления на HTTPS-адрес только в том случае, если на вашем веб-сервере установлен действительный SSL сертификат. Выполните действия, описанные в этом <a href=\"%s\" target=\"_blank\"> документе </a>, чтобы <b> проверить и зарегистрировать свой домен </
|
6159 |
|
6160 |
#:
|
6161 |
msgid "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."
|
3757 |
"{company_phone}\n"
|
3758 |
"{company_website}"
|
3759 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3760 |
#:
|
3761 |
msgid "Invoice for your appointment"
|
3762 |
msgstr "Счёт за вашу встречу"
|
5858 |
msgid "total price without tax"
|
5859 |
msgstr "общая стоимость бронирования без налога"
|
5860 |
|
|
|
|
|
|
|
|
|
5861 |
#:
|
5862 |
msgid "Dear {client_name}.\n"
|
5863 |
"\n"
|
6139 |
|
6140 |
#:
|
6141 |
msgid "Important: for two-way sync, your website must use HTTPS. Google Calendar API will be able to send notifications to HTTPS address only if there is a valid SSL certificate installed on your web server. Follow the steps in this <a href=\"%s\" target=\"_blank\">document</a> to <b>verify and register your domain</b>."
|
6142 |
+
msgstr "Важно: для двусторонней синхронизации ваш сайт должен использовать HTTPS. API Google Календаря сможет отправлять уведомления на HTTPS-адрес только в том случае, если на вашем веб-сервере установлен действительный SSL сертификат. Выполните действия, описанные в этом <a href=\"%s\" target=\"_blank\"> документе </a>, чтобы <b> проверить и зарегистрировать свой домен </b>."
|
6143 |
|
6144 |
#:
|
6145 |
msgid "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."
|
lib/notifications/Codes.php
CHANGED
@@ -371,8 +371,8 @@ class Codes
|
|
371 |
$this->staff_name = $item->getStaff()->getTranslatedName();
|
372 |
|
373 |
if ( $order->hasPayment() ) {
|
374 |
-
$this->payment_type =
|
375 |
-
$this->payment_status =
|
376 |
}
|
377 |
|
378 |
Proxy\Shared::prepareNotificationCodesForOrder( $this );
|
371 |
$this->staff_name = $item->getStaff()->getTranslatedName();
|
372 |
|
373 |
if ( $order->hasPayment() ) {
|
374 |
+
$this->payment_type = $order->getPayment()->getType();
|
375 |
+
$this->payment_status = $order->getPayment()->getStatus();
|
376 |
}
|
377 |
|
378 |
Proxy\Shared::prepareNotificationCodesForOrder( $this );
|
lib/notifications/Sender.php
CHANGED
@@ -579,9 +579,8 @@ abstract class Sender
|
|
579 |
$subject = $codes->replace( $notification->getSubject(), 'text' );
|
580 |
|
581 |
// Message.
|
582 |
-
$message
|
583 |
-
|
584 |
-
if ( $send_as_html ) {
|
585 |
$message = wpautop( $codes->replace( $message, 'html' ) );
|
586 |
} else {
|
587 |
$message = $codes->replace( $message, 'text' );
|
579 |
$subject = $codes->replace( $notification->getSubject(), 'text' );
|
580 |
|
581 |
// Message.
|
582 |
+
$message = Proxy\Pro::prepareNotificationMessage( $notification->getMessage(), 'admin', $notification->getGateway() );
|
583 |
+
if ( Config::sendEmailAsHtml() ) {
|
|
|
584 |
$message = wpautop( $codes->replace( $message, 'html' ) );
|
585 |
} else {
|
586 |
$message = $codes->replace( $message, 'text' );
|
lib/notifications/base/Codes.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\Base;
|
3 |
+
|
4 |
+
use Bookly\Lib\Utils;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Codes
|
8 |
+
* @package Bookly\Lib\Notifications\Base
|
9 |
+
*/
|
10 |
+
abstract class Codes
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Protected constructor.
|
14 |
+
*/
|
15 |
+
protected function __construct()
|
16 |
+
{
|
17 |
+
// Use static methods for creating new objects.
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Do replacements.
|
22 |
+
*
|
23 |
+
* @param string $text
|
24 |
+
* @param string $format
|
25 |
+
* @return string
|
26 |
+
*/
|
27 |
+
public function replace( $text, $format = 'text' )
|
28 |
+
{
|
29 |
+
$codes = $this->getReplaceCodes( $format );
|
30 |
+
|
31 |
+
return strtr( $text, $codes );
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Do replacements for SMS.
|
36 |
+
* Returns both personal and impersonal text.
|
37 |
+
*
|
38 |
+
* @param string $text
|
39 |
+
* @return array
|
40 |
+
*/
|
41 |
+
public function replaceForSms( $text )
|
42 |
+
{
|
43 |
+
$codes = $this->getReplaceCodes( 'text' );
|
44 |
+
|
45 |
+
// Impersonal codes.
|
46 |
+
$impersonal_codes = array();
|
47 |
+
foreach ( $codes as $name => $code ) {
|
48 |
+
$count = Utils\SMSCounter::count( strval( $code ) );
|
49 |
+
if ( $count->encoding == Utils\SMSCounter::UTF16 ) {
|
50 |
+
$impersonal_symbol = "ϔ";
|
51 |
+
} else {
|
52 |
+
$impersonal_symbol = "X";
|
53 |
+
}
|
54 |
+
$impersonal_codes[ $name ] = preg_replace( '/[^\s]/', $impersonal_symbol, $code );
|
55 |
+
}
|
56 |
+
|
57 |
+
return array(
|
58 |
+
'personal' => strtr( $text, $codes ),
|
59 |
+
'impersonal' => strtr( $text, $impersonal_codes ),
|
60 |
+
);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Get replacement codes for given format.
|
65 |
+
*
|
66 |
+
* @param string $format
|
67 |
+
* @return array
|
68 |
+
*/
|
69 |
+
protected function getReplaceCodes( $format )
|
70 |
+
{
|
71 |
+
$company_logo = '';
|
72 |
+
|
73 |
+
if ( $format == 'html' ) {
|
74 |
+
$img = wp_get_attachment_image_src( get_option( 'bookly_co_logo_attachment_id' ), 'full' );
|
75 |
+
// Company logo as <img> tag.
|
76 |
+
if ( $img ) {
|
77 |
+
$company_logo = sprintf(
|
78 |
+
'<img src="%s" alt="%s" />',
|
79 |
+
esc_attr( $img[0] ),
|
80 |
+
esc_attr( get_option( 'bookly_co_name' ) )
|
81 |
+
);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
return array(
|
86 |
+
'{company_address}' => $format == 'html' ? nl2br( get_option( 'bookly_co_address' ) ) : get_option( 'bookly_co_address' ),
|
87 |
+
'{company_logo}' => $company_logo,
|
88 |
+
'{company_name}' => get_option( 'bookly_co_name' ),
|
89 |
+
'{company_phone}' => get_option( 'bookly_co_phone' ),
|
90 |
+
'{company_website}' => get_option( 'bookly_co_website' ),
|
91 |
+
);
|
92 |
+
}
|
93 |
+
}
|
lib/notifications/base/Sender.php
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\Base;
|
3 |
+
|
4 |
+
use Bookly\Lib\Config;
|
5 |
+
use Bookly\Lib\Entities\Notification;
|
6 |
+
use Bookly\Lib\Proxy;
|
7 |
+
use Bookly\Lib\SMS;
|
8 |
+
use Bookly\Lib\Utils;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Sender
|
12 |
+
* @package Bookly\Lib\Notifications\Base
|
13 |
+
*/
|
14 |
+
abstract class Sender
|
15 |
+
{
|
16 |
+
/** @var SMS */
|
17 |
+
protected static $sms;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Protected constructor.
|
21 |
+
*/
|
22 |
+
protected function __construct()
|
23 |
+
{
|
24 |
+
// Use static methods for creating new objects.
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Send email to administrators.
|
29 |
+
*
|
30 |
+
* @param Notification $notification
|
31 |
+
* @param Codes $codes
|
32 |
+
* @param array $attachments
|
33 |
+
* @param array $extra_headers
|
34 |
+
* @return bool
|
35 |
+
*/
|
36 |
+
protected function sendEmailToAdmins( Notification $notification, Codes $codes, $attachments = array(), $extra_headers = array() )
|
37 |
+
{
|
38 |
+
$admin_emails = Utils\Common::getAdminEmails();
|
39 |
+
if ( empty ( $admin_emails ) ) {
|
40 |
+
return false;
|
41 |
+
}
|
42 |
+
|
43 |
+
return $this->_sendEmail(
|
44 |
+
$admin_emails,
|
45 |
+
$notification->getSubject(),
|
46 |
+
Proxy\Pro::prepareNotificationMessage( $notification->getMessage(), 'admin', 'email' ),
|
47 |
+
$codes,
|
48 |
+
$attachments,
|
49 |
+
$extra_headers
|
50 |
+
);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Send email to client.
|
55 |
+
*
|
56 |
+
* @param string $email
|
57 |
+
* @param Notification $notification
|
58 |
+
* @param Codes $codes
|
59 |
+
* @param array $attachments
|
60 |
+
* @param array $extra_headers
|
61 |
+
* @return bool
|
62 |
+
*/
|
63 |
+
protected function sendEmailToClient( $email, Notification $notification, Codes $codes, $attachments = array(), $extra_headers = array() )
|
64 |
+
{
|
65 |
+
if ( $email == '' ) {
|
66 |
+
return false;
|
67 |
+
}
|
68 |
+
|
69 |
+
return $this->_sendEmail(
|
70 |
+
$email,
|
71 |
+
$notification->getTranslatedSubject(),
|
72 |
+
$notification->getTranslatedMessage(),
|
73 |
+
$codes,
|
74 |
+
$attachments,
|
75 |
+
$extra_headers
|
76 |
+
);
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Send email to staff.
|
81 |
+
*
|
82 |
+
* @param string $email
|
83 |
+
* @param Notification $notification
|
84 |
+
* @param Codes $codes
|
85 |
+
* @param array $attachments
|
86 |
+
* @param array $extra_headers
|
87 |
+
* @return bool
|
88 |
+
*/
|
89 |
+
protected function sendEmailToStaff( $email, Notification $notification, Codes $codes, $attachments = array(), $extra_headers = array() )
|
90 |
+
{
|
91 |
+
if ( $email == '' ) {
|
92 |
+
return false;
|
93 |
+
}
|
94 |
+
|
95 |
+
return $this->_sendEmail(
|
96 |
+
$email,
|
97 |
+
$notification->getSubject(),
|
98 |
+
Proxy\Pro::prepareNotificationMessage( $notification->getMessage(), 'staff', 'email' ),
|
99 |
+
$codes,
|
100 |
+
$attachments,
|
101 |
+
$extra_headers
|
102 |
+
);
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Send SMS to admin.
|
107 |
+
*
|
108 |
+
* @param Notification $notification
|
109 |
+
* @param Codes $codes
|
110 |
+
* @return bool
|
111 |
+
*/
|
112 |
+
protected function sendSmsToAdmin( Notification $notification, Codes $codes )
|
113 |
+
{
|
114 |
+
$phone = get_option( 'bookly_sms_administrator_phone', '' );
|
115 |
+
if ( $phone == '' ) {
|
116 |
+
return false;
|
117 |
+
}
|
118 |
+
|
119 |
+
return $this->_sendSms(
|
120 |
+
$phone,
|
121 |
+
Proxy\Pro::prepareNotificationMessage( $notification->getMessage(), 'admin', 'sms' ),
|
122 |
+
$codes,
|
123 |
+
$notification->getTypeId()
|
124 |
+
);
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Send SMS to client.
|
129 |
+
*
|
130 |
+
* @param string $phone
|
131 |
+
* @param Notification $notification
|
132 |
+
* @param Codes $codes
|
133 |
+
* @return bool
|
134 |
+
*/
|
135 |
+
protected function sendSmsToClient( $phone, Notification $notification, Codes $codes )
|
136 |
+
{
|
137 |
+
if ( $phone == '' ) {
|
138 |
+
return false;
|
139 |
+
}
|
140 |
+
|
141 |
+
return $this->_sendSms(
|
142 |
+
$phone,
|
143 |
+
$notification->getTranslatedMessage(),
|
144 |
+
$codes,
|
145 |
+
$notification->getTypeId()
|
146 |
+
);
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Send SMS to staff.
|
151 |
+
*
|
152 |
+
* @param string $phone
|
153 |
+
* @param Notification $notification
|
154 |
+
* @param Codes $codes
|
155 |
+
* @return bool
|
156 |
+
*/
|
157 |
+
protected function sendSmsToStaff( $phone, Notification $notification, Codes $codes )
|
158 |
+
{
|
159 |
+
if ( $phone == '' ) {
|
160 |
+
return false;
|
161 |
+
}
|
162 |
+
|
163 |
+
return $this->_sendSms(
|
164 |
+
$phone,
|
165 |
+
Proxy\Pro::prepareNotificationMessage( $notification->getMessage(), 'staff', 'sms' ),
|
166 |
+
$codes,
|
167 |
+
$notification->getTypeId()
|
168 |
+
);
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Send email.
|
173 |
+
*
|
174 |
+
* @param string|array $email
|
175 |
+
* @param string $subject
|
176 |
+
* @param string $message
|
177 |
+
* @param Codes $codes
|
178 |
+
* @param array $attachments
|
179 |
+
* @param array $extra_headers
|
180 |
+
* @return bool
|
181 |
+
*/
|
182 |
+
private function _sendEmail( $email, $subject, $message, Codes $codes, $attachments = array(), $extra_headers = array() )
|
183 |
+
{
|
184 |
+
// Subject.
|
185 |
+
$subject = $codes->replace( $subject, 'text' );
|
186 |
+
|
187 |
+
// Message.
|
188 |
+
if ( Config::sendEmailAsHtml() ) {
|
189 |
+
$message = wpautop( $codes->replace( $message, 'html' ) );
|
190 |
+
} else {
|
191 |
+
$message = $codes->replace( $message, 'text' );
|
192 |
+
}
|
193 |
+
|
194 |
+
// Headers.
|
195 |
+
$headers = Utils\Common::getEmailHeaders( $extra_headers );
|
196 |
+
|
197 |
+
// Do send.
|
198 |
+
return wp_mail( $email, $subject, $message, $headers, $attachments );
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Send SMS.
|
203 |
+
*
|
204 |
+
* @param string $phone
|
205 |
+
* @param string $message
|
206 |
+
* @param Codes $codes
|
207 |
+
* @param int $type_id
|
208 |
+
* @return bool
|
209 |
+
*/
|
210 |
+
private function _sendSms( $phone, $message, Codes $codes, $type_id )
|
211 |
+
{
|
212 |
+
if ( self::$sms === null ) {
|
213 |
+
self::$sms = new SMS();
|
214 |
+
}
|
215 |
+
|
216 |
+
// Message.
|
217 |
+
$message = $codes->replaceForSms( $message );
|
218 |
+
|
219 |
+
// Do send.
|
220 |
+
return self::$sms->sendSms( $phone, $message['personal'], $message['impersonal'], $type_id );
|
221 |
+
}
|
222 |
+
}
|
lib/notifications/booking/Sender.php
ADDED
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\Booking;
|
3 |
+
|
4 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
5 |
+
use Bookly\Lib\Entities\Notification;
|
6 |
+
use Bookly\Lib\Notifications;
|
7 |
+
use Bookly\Lib\Notifications\NewBooking\Codes;
|
8 |
+
use Bookly\Lib\Notifications\NewBooking\ICS;
|
9 |
+
use Bookly\Lib\Proxy;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Class Sender
|
13 |
+
* @package Bookly\Lib\Notifications\Booking
|
14 |
+
*/
|
15 |
+
class Sender extends Notifications\Base\Sender
|
16 |
+
{
|
17 |
+
/** @var DataHolders\Order */
|
18 |
+
protected $order;
|
19 |
+
/** @var Codes */
|
20 |
+
protected $codes;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Create new instance.
|
24 |
+
*
|
25 |
+
* @param DataHolders\Order $order
|
26 |
+
* @param Codes $codes
|
27 |
+
* @return static
|
28 |
+
*/
|
29 |
+
public static function create( DataHolders\Order $order, Codes $codes )
|
30 |
+
{
|
31 |
+
$sender = new static();
|
32 |
+
$sender->order = $order;
|
33 |
+
$sender->codes = $codes;
|
34 |
+
|
35 |
+
return $sender;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Send notifications to client.
|
40 |
+
*
|
41 |
+
* @param DataHolders\Item $item
|
42 |
+
* @param string $lang
|
43 |
+
*/
|
44 |
+
public function sendToClient( DataHolders\Item $item, $lang )
|
45 |
+
{
|
46 |
+
foreach ( $this->getNotificationsForClient() as $notification ) {
|
47 |
+
switch ( $notification->getGateway() ) {
|
48 |
+
case 'email':
|
49 |
+
$this->notifyClientByEmail( $notification, $item, $lang );
|
50 |
+
break;
|
51 |
+
case 'sms':
|
52 |
+
$this->notifyClientBySms( $notification, $item, $lang );
|
53 |
+
break;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Send notifications to staff and/or admins.
|
60 |
+
*
|
61 |
+
* @param DataHolders\Item $item
|
62 |
+
* @param string $lang
|
63 |
+
*/
|
64 |
+
public function sendToStaffAndAdmins( DataHolders\Item $item, $lang )
|
65 |
+
{
|
66 |
+
// Notify staff and admins.
|
67 |
+
foreach ( $this->getNotificationsForStaffAndAdmins() as $notification ) {
|
68 |
+
if ( ! $notification->getToAdmin() && $item->getStaff()->isArchived() ) {
|
69 |
+
// No recipient.
|
70 |
+
continue;
|
71 |
+
}
|
72 |
+
switch ( $notification->getGateway() ) {
|
73 |
+
case 'email':
|
74 |
+
$this->notifyStaffAndAdminsByEmail( $notification, $item, $lang );
|
75 |
+
break;
|
76 |
+
case 'sms':
|
77 |
+
$this->notifyStaffAndAdminBySms( $notification, $item, $lang );
|
78 |
+
break;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Notify client by email.
|
85 |
+
*
|
86 |
+
* @param Notification $notification
|
87 |
+
* @param DataHolders\Item $item
|
88 |
+
* @param string $lang
|
89 |
+
*/
|
90 |
+
protected function notifyClientByEmail( Notification $notification, DataHolders\Item $item, $lang )
|
91 |
+
{
|
92 |
+
if ( $this->order->getCustomer()->getEmail() == '' ) {
|
93 |
+
// No recipient.
|
94 |
+
return;
|
95 |
+
}
|
96 |
+
|
97 |
+
// Prepare codes for this item.
|
98 |
+
$this->codes->prepareForItem( $item, $lang, true );
|
99 |
+
|
100 |
+
// Attachments.
|
101 |
+
$attachments = $this->createAttachments( $notification );
|
102 |
+
|
103 |
+
// Send email to client.
|
104 |
+
$this->sendEmailToClient( $this->order->getCustomer()->getEmail(), $notification, $this->codes, $attachments );
|
105 |
+
|
106 |
+
// Clean up attachments.
|
107 |
+
$this->clearAttachments( $attachments );
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Notify client by SMS.
|
112 |
+
*
|
113 |
+
* @param Notification $notification
|
114 |
+
* @param DataHolders\Item $item
|
115 |
+
* @param string $lang
|
116 |
+
*/
|
117 |
+
protected function notifyClientBySms( Notification $notification, DataHolders\Item $item, $lang )
|
118 |
+
{
|
119 |
+
if ( $this->order->getCustomer()->getPhone() == '' ) {
|
120 |
+
// No recipient.
|
121 |
+
return;
|
122 |
+
}
|
123 |
+
|
124 |
+
// Prepare codes for this item.
|
125 |
+
$this->codes->prepareForItem( $item, $lang, true );
|
126 |
+
|
127 |
+
// Send SMS to client.
|
128 |
+
$this->sendSmsToClient( $this->order->getCustomer()->getPhone(), $notification, $this->codes );
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Notify staff and/or administrators by email.
|
133 |
+
*
|
134 |
+
* @param Notification $notification
|
135 |
+
* @param DataHolders\Item $item
|
136 |
+
* @param string $lang
|
137 |
+
*/
|
138 |
+
protected function notifyStaffAndAdminsByEmail( Notification $notification, DataHolders\Item $item, $lang )
|
139 |
+
{
|
140 |
+
if ( ! $notification->getToAdmin() && $item->getStaff()->getEmail() == '' ) {
|
141 |
+
// No recipient.
|
142 |
+
return;
|
143 |
+
}
|
144 |
+
|
145 |
+
// Prepare codes for this item.
|
146 |
+
$this->codes->prepareForItem( $item, $lang, false );
|
147 |
+
|
148 |
+
// Attachments.
|
149 |
+
$attachments = $this->createAttachments( $notification );
|
150 |
+
|
151 |
+
// Extra headers.
|
152 |
+
$extra_headers = array();
|
153 |
+
if ( get_option( 'bookly_email_reply_to_customers' ) ) {
|
154 |
+
$customer = $this->order->getCustomer();
|
155 |
+
$extra_headers = array( 'reply-to' => array( 'email' => $customer->getEmail(), 'name' => $customer->getFullName() ) );
|
156 |
+
}
|
157 |
+
|
158 |
+
// Send email to staff.
|
159 |
+
if ( $notification->getToStaff() ) {
|
160 |
+
$this->sendEmailToStaff( $item->getStaff()->getEmail(), $notification, $this->codes, $attachments, $extra_headers );
|
161 |
+
}
|
162 |
+
|
163 |
+
// Send email to administrators.
|
164 |
+
if ( $notification->getToAdmin() ) {
|
165 |
+
$this->sendEmailToAdmins( $notification, $this->codes, $attachments, $extra_headers );
|
166 |
+
}
|
167 |
+
|
168 |
+
// Clean up attachments.
|
169 |
+
$this->clearAttachments( $attachments );
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Notify staff and/or administrator by SMS.
|
174 |
+
*
|
175 |
+
* @param Notification $notification
|
176 |
+
* @param DataHolders\Item $item
|
177 |
+
* @param string $lang
|
178 |
+
*/
|
179 |
+
protected function notifyStaffAndAdminBySms( Notification $notification, DataHolders\Item $item, $lang )
|
180 |
+
{
|
181 |
+
if ( ! $notification->getToAdmin() && $item->getStaff()->getPhone() == '' ) {
|
182 |
+
// No recipients for this item.
|
183 |
+
return;
|
184 |
+
}
|
185 |
+
|
186 |
+
// Prepare codes for this item.
|
187 |
+
$this->codes->prepareForItem( $item, $lang, false );
|
188 |
+
|
189 |
+
// Send SMS to staff.
|
190 |
+
if ( $notification->getToStaff() ) {
|
191 |
+
$this->sendSmsToStaff( $item->getStaff()->getPhone(), $notification, $this->codes );
|
192 |
+
}
|
193 |
+
|
194 |
+
// Send SMS to administrator.
|
195 |
+
if ( $notification->getToAdmin() ) {
|
196 |
+
$this->sendSmsToAdmin( $notification, $this->codes );
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @inheritdoc
|
202 |
+
*/
|
203 |
+
protected function createAttachments( Notification $notification )
|
204 |
+
{
|
205 |
+
$attachments = array();
|
206 |
+
|
207 |
+
// ICS.
|
208 |
+
if ( $notification->getAttachIcs() ) {
|
209 |
+
$file = $this->createIcs( $this->codes );
|
210 |
+
if ( $file ) {
|
211 |
+
$attachments[] = $file;
|
212 |
+
}
|
213 |
+
}
|
214 |
+
|
215 |
+
// Invoices.
|
216 |
+
if ( $notification->getAttachInvoice() && $this->order->hasPayment() ) {
|
217 |
+
$file = Proxy\Invoices::getInvoice( $this->order->getPayment() );
|
218 |
+
if ( $file ) {
|
219 |
+
$attachments[] = $file;
|
220 |
+
}
|
221 |
+
}
|
222 |
+
|
223 |
+
return $attachments;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Create ICS attachment.
|
228 |
+
*
|
229 |
+
* @param Codes $codes
|
230 |
+
* @return bool|string
|
231 |
+
*/
|
232 |
+
protected function createIcs( Codes $codes )
|
233 |
+
{
|
234 |
+
$ics = new ICS( $codes );
|
235 |
+
|
236 |
+
return $ics->create();
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Remove attachment files.
|
241 |
+
*
|
242 |
+
* @param array $attachments
|
243 |
+
*/
|
244 |
+
protected function clearAttachments( array $attachments )
|
245 |
+
{
|
246 |
+
foreach ( $attachments as $file ) {
|
247 |
+
unlink( $file );
|
248 |
+
}
|
249 |
+
}
|
250 |
+
}
|
lib/notifications/codes/Combined.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\Codes;
|
3 |
+
|
4 |
+
use Bookly\Lib\Config;
|
5 |
+
use Bookly\Lib\Proxy;
|
6 |
+
use Bookly\Lib\Utils;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Codes
|
10 |
+
* @package Bookly\Lib\Notifications\Codes
|
11 |
+
*/
|
12 |
+
class Combined extends Codes
|
13 |
+
{
|
14 |
+
public $cart_info;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @inheritdoc
|
18 |
+
*/
|
19 |
+
protected function getCodes( $format )
|
20 |
+
{
|
21 |
+
$cart_info_c = $cart_info = '';
|
22 |
+
|
23 |
+
// Cart info.
|
24 |
+
$cart_info_data = $this->cart_info;
|
25 |
+
if ( ! empty ( $cart_info_data ) ) {
|
26 |
+
$cart_columns = get_option( 'bookly_cart_show_columns', array() );
|
27 |
+
if ( empty( $cart_columns ) ) {
|
28 |
+
$cart_columns = array(
|
29 |
+
'service' => array( 'show' => '1', ),
|
30 |
+
'date' => array( 'show' => '1', ),
|
31 |
+
'time' => array( 'show' => '1', ),
|
32 |
+
'employee' => array( 'show' => '1', ),
|
33 |
+
'price' => array( 'show' => '1', ),
|
34 |
+
'deposit' => array( 'show' => (int) Config::depositPaymentsActive() ),
|
35 |
+
'tax' => array( 'show' => (int) Config::taxesActive(), ),
|
36 |
+
);
|
37 |
+
}
|
38 |
+
if ( ! Proxy\Taxes::showTaxColumn() ) {
|
39 |
+
unset( $cart_columns['tax'] );
|
40 |
+
}
|
41 |
+
if ( ! Config::depositPaymentsActive() ) {
|
42 |
+
unset( $cart_columns['deposit'] );
|
43 |
+
}
|
44 |
+
$ths = array();
|
45 |
+
foreach ( $cart_columns as $column => $attr ) {
|
46 |
+
if ( $attr['show'] ) {
|
47 |
+
switch ( $column ) {
|
48 |
+
case 'service':
|
49 |
+
$ths[] = Utils\Common::getTranslatedOption( 'bookly_l10n_label_service' );
|
50 |
+
break;
|
51 |
+
case 'date':
|
52 |
+
$ths[] = __( 'Date', 'bookly' );
|
53 |
+
break;
|
54 |
+
case 'time':
|
55 |
+
$ths[] = __( 'Time', 'bookly' );
|
56 |
+
break;
|
57 |
+
case 'tax':
|
58 |
+
$ths[] = __( 'Tax', 'bookly' );
|
59 |
+
break;
|
60 |
+
case 'employee':
|
61 |
+
$ths[] = Utils\Common::getTranslatedOption( 'bookly_l10n_label_employee' );
|
62 |
+
break;
|
63 |
+
case 'price':
|
64 |
+
$ths[] = __( 'Price', 'bookly' );
|
65 |
+
break;
|
66 |
+
case 'deposit':
|
67 |
+
$ths[] = __( 'Deposit', 'bookly' );
|
68 |
+
break;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
}
|
72 |
+
$trs = array();
|
73 |
+
foreach ( $cart_info_data as $codes ) {
|
74 |
+
$tds = array();
|
75 |
+
foreach ( $cart_columns as $column => $attr ) {
|
76 |
+
if ( $attr['show'] ) {
|
77 |
+
switch ( $column ) {
|
78 |
+
case 'service':
|
79 |
+
$service_name = $codes['service_name'];
|
80 |
+
if ( ! empty ( $codes['extras'] ) ) {
|
81 |
+
$extras = '';
|
82 |
+
if ( $format == 'html' ) {
|
83 |
+
foreach ( $codes['extras'] as $extra ) {
|
84 |
+
$extras .= '<li>' . $extra['title'] . '</li>';
|
85 |
+
}
|
86 |
+
$extras = '<ul>' . $extras . '</ul>';
|
87 |
+
} else {
|
88 |
+
foreach ( $codes['extras'] as $extra ) {
|
89 |
+
$extras .= ', ' . str_replace( ' × ', ' x ', $extra['title'] );
|
90 |
+
}
|
91 |
+
}
|
92 |
+
$service_name .= $extras;
|
93 |
+
}
|
94 |
+
$tds[] = $service_name;
|
95 |
+
break;
|
96 |
+
case 'date':
|
97 |
+
$tds[] = $codes['appointment_start'] === null ? __( 'N/A', 'bookly' ) : Utils\DateTime::formatDate( $codes['appointment_start'] );
|
98 |
+
break;
|
99 |
+
case 'time':
|
100 |
+
if ( $codes['appointment_start_info'] !== null ) {
|
101 |
+
$tds[] = $codes['appointment_start_info'];
|
102 |
+
} else {
|
103 |
+
$tds[] = $codes['appointment_start'] === null ? __( 'N/A', 'bookly' ) : Utils\DateTime::formatTime( $codes['appointment_start'] );
|
104 |
+
}
|
105 |
+
break;
|
106 |
+
case 'tax':
|
107 |
+
$tds[] = Utils\Price::format( $codes['tax'] );
|
108 |
+
break;
|
109 |
+
case 'employee':
|
110 |
+
$tds[] = $codes['staff_name'];
|
111 |
+
break;
|
112 |
+
case 'price':
|
113 |
+
$tds[] = Utils\Price::format( $codes['appointment_price'] );
|
114 |
+
break;
|
115 |
+
case 'deposit':
|
116 |
+
$tds[] = $codes['deposit'];
|
117 |
+
break;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
}
|
121 |
+
$tds[] = $codes['cancel_url'];
|
122 |
+
$trs[] = $tds;
|
123 |
+
}
|
124 |
+
if ( $format == 'html' ) {
|
125 |
+
$cart_info = '<table cellspacing="1" border="1" cellpadding="5"><thead><tr><th>' . implode( '</th><th>', $ths ) . '</th></tr></thead><tbody>';
|
126 |
+
$cart_info_c = '<table cellspacing="1" border="1" cellpadding="5"><thead><tr><th>' . implode( '</th><th>', $ths ) . '</th><th>' . __( 'Cancel', 'bookly' ) . '</th></tr></thead><tbody>';
|
127 |
+
foreach ( $trs as $tr ) {
|
128 |
+
$cancel_url = array_pop( $tr );
|
129 |
+
$cart_info .= '<tr><td>' . implode( '</td><td>', $tr ) . '</td></tr>';
|
130 |
+
$cart_info_c .= '<tr><td>' . implode( '</td><td>', $tr ) . '</td><td><a href="' . $cancel_url . '">' . __( 'Cancel', 'bookly' ) . '</a></td></tr>';
|
131 |
+
}
|
132 |
+
$cart_info .= '</tbody></table>';
|
133 |
+
$cart_info_c .= '</tbody></table>';
|
134 |
+
} else {
|
135 |
+
foreach ( $trs as $tr ) {
|
136 |
+
$cancel_url = array_pop( $tr );
|
137 |
+
foreach ( $ths as $position => $column ) {
|
138 |
+
$cart_info .= $column . ' ' . $tr[ $position ] . "\r\n";
|
139 |
+
$cart_info_c .= $column . ' ' . $tr[ $position ] . "\r\n";
|
140 |
+
}
|
141 |
+
$cart_info .= "\r\n";
|
142 |
+
$cart_info_c .= __( 'Cancel', 'bookly' ) . ' ' . $cancel_url . "\r\n\r\n";
|
143 |
+
}
|
144 |
+
}
|
145 |
+
}
|
146 |
+
// Codes.
|
147 |
+
$codes = array_merge( parent::getCodes( $format ), array(
|
148 |
+
'{cart_info}' => $cart_info,
|
149 |
+
'{cart_info_c}' => $cart_info_c,
|
150 |
+
) );
|
151 |
+
|
152 |
+
return $codes;
|
153 |
+
}
|
154 |
+
}
|
lib/notifications/new_booking/Codes.php
ADDED
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\NewBooking;
|
3 |
+
|
4 |
+
use Bookly\Lib\Config;
|
5 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
6 |
+
use Bookly\Lib\Entities;
|
7 |
+
use Bookly\Lib\Notifications\Base;
|
8 |
+
use Bookly\Lib\Utils;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Codes
|
12 |
+
* @package Bookly\Lib\Notifications\NewBooking
|
13 |
+
*/
|
14 |
+
class Codes extends Base\Codes
|
15 |
+
{
|
16 |
+
public $agenda_date;
|
17 |
+
public $amount_due;
|
18 |
+
public $amount_paid;
|
19 |
+
public $appointment_end;
|
20 |
+
public $appointment_end_info;
|
21 |
+
public $appointment_notes;
|
22 |
+
public $appointment_schedule;
|
23 |
+
public $appointment_schedule_c;
|
24 |
+
public $appointment_start;
|
25 |
+
public $appointment_start_info;
|
26 |
+
public $appointment_token;
|
27 |
+
public $appointment_waiting_list;
|
28 |
+
public $booking_number;
|
29 |
+
public $cancellation_reason;
|
30 |
+
public $cart_info;
|
31 |
+
public $category_name;
|
32 |
+
public $client_email;
|
33 |
+
public $client_address;
|
34 |
+
public $client_first_name;
|
35 |
+
public $client_last_name;
|
36 |
+
public $client_name;
|
37 |
+
public $client_phone;
|
38 |
+
public $client_timezone;
|
39 |
+
public $custom_fields;
|
40 |
+
public $custom_fields_2c;
|
41 |
+
public $deposit_value;
|
42 |
+
public $extras;
|
43 |
+
public $extras_total_price;
|
44 |
+
public $files_count;
|
45 |
+
public $google_calendar_url;
|
46 |
+
public $invoice_date;
|
47 |
+
public $invoice_due_date;
|
48 |
+
public $invoice_due_days;
|
49 |
+
public $invoice_link;
|
50 |
+
public $invoice_number; // payment_id
|
51 |
+
public $location_info;
|
52 |
+
public $location_name;
|
53 |
+
public $new_password;
|
54 |
+
public $new_username;
|
55 |
+
public $next_day_agenda;
|
56 |
+
public $next_day_agenda_extended;
|
57 |
+
public $number_of_persons;
|
58 |
+
public $package_life_time;
|
59 |
+
public $package_name;
|
60 |
+
public $package_price;
|
61 |
+
public $package_size;
|
62 |
+
public $payment_type;
|
63 |
+
public $payment_status;
|
64 |
+
public $schedule;
|
65 |
+
public $series_token;
|
66 |
+
public $service_duration;
|
67 |
+
public $service_info;
|
68 |
+
public $service_name;
|
69 |
+
public $service_price;
|
70 |
+
public $service_tax;
|
71 |
+
public $service_tax_rate;
|
72 |
+
public $site_address;
|
73 |
+
public $staff_email;
|
74 |
+
public $staff_info;
|
75 |
+
public $staff_name;
|
76 |
+
public $staff_phone;
|
77 |
+
public $staff_photo;
|
78 |
+
public $staff_rating_url;
|
79 |
+
public $total_price;
|
80 |
+
public $total_tax;
|
81 |
+
|
82 |
+
/** @var DataHolders\Order */
|
83 |
+
protected $order;
|
84 |
+
/** @var DataHolders\Item */
|
85 |
+
protected $item;
|
86 |
+
/** @var string */
|
87 |
+
protected $lang;
|
88 |
+
/** @var bool */
|
89 |
+
protected $use_client_tz;
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Create new instance.
|
93 |
+
*
|
94 |
+
* @param DataHolders\Order $order
|
95 |
+
* @return static
|
96 |
+
*/
|
97 |
+
public static function create( DataHolders\Order $order )
|
98 |
+
{
|
99 |
+
$codes = new static();
|
100 |
+
|
101 |
+
$codes->order = $order;
|
102 |
+
|
103 |
+
$codes->client_address = $order->getCustomer()->getAddress();
|
104 |
+
$codes->client_email = $order->getCustomer()->getEmail();
|
105 |
+
$codes->client_first_name = $order->getCustomer()->getFirstName();
|
106 |
+
$codes->client_last_name = $order->getCustomer()->getLastName();
|
107 |
+
$codes->client_name = $order->getCustomer()->getFullName();
|
108 |
+
$codes->client_phone = $order->getCustomer()->getPhone();
|
109 |
+
if ( $order->hasPayment() ) {
|
110 |
+
$codes->amount_paid = $order->getPayment()->getPaid();
|
111 |
+
$codes->amount_due = $order->getPayment()->getTotal() - $order->getPayment()->getPaid();
|
112 |
+
$codes->total_price = $order->getPayment()->getTotal();
|
113 |
+
$codes->total_tax = $order->getPayment()->getTax();
|
114 |
+
$codes->invoice_number = $order->getPayment()->getId();
|
115 |
+
$codes->payment_status = $order->getPayment()->getStatus();
|
116 |
+
$codes->payment_type = $order->getPayment()->getType();
|
117 |
+
}
|
118 |
+
|
119 |
+
Proxy\Shared::prepareCodesForOrder( $codes );
|
120 |
+
|
121 |
+
return $codes;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Prepare codes for given order item.
|
126 |
+
*
|
127 |
+
* @param DataHolders\Item $item
|
128 |
+
* @param string $lang
|
129 |
+
* @param bool $use_client_tz
|
130 |
+
*/
|
131 |
+
public function prepareForItem( DataHolders\Item $item, $lang, $use_client_tz )
|
132 |
+
{
|
133 |
+
if (
|
134 |
+
$this->item === $item &&
|
135 |
+
$this->lang == $lang &&
|
136 |
+
( $this->use_client_tz == $use_client_tz || $item->getCA()->getTimeZoneOffset() === null )
|
137 |
+
) {
|
138 |
+
return;
|
139 |
+
}
|
140 |
+
|
141 |
+
$this->item = $item;
|
142 |
+
$this->lang = $lang;
|
143 |
+
$this->use_client_tz = $use_client_tz;
|
144 |
+
|
145 |
+
$staff_photo = wp_get_attachment_image_src( $item->getStaff()->getAttachmentId(), 'full' );
|
146 |
+
|
147 |
+
$this->appointment_end = $this->tz( $item->getTotalEnd()->format( 'Y-m-d H:i:s' ) );
|
148 |
+
$this->appointment_end_info = $item->getService()->getEndTimeInfo();
|
149 |
+
$this->appointment_notes = $item->getCA()->getNotes();
|
150 |
+
$this->appointment_start = $this->tz( $item->getAppointment()->getStartDate() );
|
151 |
+
$this->appointment_start_info = $item->getService()->getStartTimeInfo();
|
152 |
+
$this->appointment_token = $item->getCA()->getToken();
|
153 |
+
$this->booking_number = $item->getAppointment()->getId();
|
154 |
+
$this->category_name = $item->getService()->getTranslatedCategoryName();
|
155 |
+
$this->client_timezone = $item->getCA()->getTimeZone() ?: (
|
156 |
+
$item->getCA()->getTimeZoneOffset() !== null
|
157 |
+
? 'UTC' . Utils\DateTime::guessTimeZone( - $item->getCA()->getTimeZoneOffset() * 60 )
|
158 |
+
: ''
|
159 |
+
);
|
160 |
+
$this->number_of_persons = $item->getCA()->getNumberOfPersons();
|
161 |
+
$this->service_duration = $item->getServiceDuration();
|
162 |
+
$this->service_info = $item->getService()->getTranslatedInfo();
|
163 |
+
$this->service_name = $item->getService()->getTranslatedTitle();
|
164 |
+
$this->service_price = $item->getServicePrice();
|
165 |
+
$this->staff_email = $item->getStaff()->getEmail();
|
166 |
+
$this->staff_info = $item->getStaff()->getTranslatedInfo();
|
167 |
+
$this->staff_name = $item->getStaff()->getTranslatedName();
|
168 |
+
$this->staff_phone = $item->getStaff()->getPhone();
|
169 |
+
$this->staff_photo = $staff_photo ? $staff_photo[0] : '';
|
170 |
+
if ( ! $this->order->hasPayment() ) {
|
171 |
+
$this->total_price = $item->getTotalPrice();
|
172 |
+
$this->total_tax = $item->getTax();
|
173 |
+
if ( Config::taxesActive() && get_option( 'bookly_taxes_in_price' ) == 'excluded' ) {
|
174 |
+
$this->total_price += $this->total_tax;
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
Proxy\Shared::prepareCodesForItem( $this );
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @inheritdoc
|
183 |
+
*/
|
184 |
+
protected function getReplaceCodes( $format )
|
185 |
+
{
|
186 |
+
$replace_codes = parent::getReplaceCodes( $format );
|
187 |
+
|
188 |
+
// Prepare data.
|
189 |
+
$staff_photo = '';
|
190 |
+
if ( $format == 'html' ) {
|
191 |
+
if ( $this->staff_photo != '' ) {
|
192 |
+
// Staff photo as <img> tag.
|
193 |
+
$staff_photo = sprintf(
|
194 |
+
'<img src="%s" alt="%s" />',
|
195 |
+
esc_attr( $this->staff_photo ),
|
196 |
+
esc_attr( $this->staff_name )
|
197 |
+
);
|
198 |
+
}
|
199 |
+
}
|
200 |
+
$cancel_appointment_confirm_url = get_option( 'bookly_url_cancel_confirm_page_url' );
|
201 |
+
$cancel_appointment_confirm_url = $this->appointment_token
|
202 |
+
? add_query_arg( 'bookly-appointment-token', $this->appointment_token, $cancel_appointment_confirm_url )
|
203 |
+
: '';
|
204 |
+
|
205 |
+
// Add replace codes.
|
206 |
+
$replace_codes += array(
|
207 |
+
'{agenda_date}' => $this->agenda_date ? Utils\DateTime::formatDate( $this->agenda_date ) : '',
|
208 |
+
'{amount_due}' => Utils\Price::format( $this->amount_due ),
|
209 |
+
'{amount_paid}' => Utils\Price::format( $this->amount_paid ),
|
210 |
+
'{appointment_date}' => $this->appointment_start === null ? __( 'N/A', 'bookly' ) : Utils\DateTime::formatDate( $this->appointment_start ),
|
211 |
+
'{appointment_time}' => $this->appointment_start === null ? __( 'N/A', 'bookly' ) : ( $this->service_duration < DAY_IN_SECONDS ? Utils\DateTime::formatTime( $this->appointment_start ) : $this->appointment_start_info ),
|
212 |
+
'{appointment_end_date}' => $this->appointment_start === null ? __( 'N/A', 'bookly' ) : Utils\DateTime::formatDate( $this->appointment_end ),
|
213 |
+
'{appointment_end_time}' => $this->appointment_start === null ? __( 'N/A', 'bookly' ) : ( $this->service_duration < DAY_IN_SECONDS ? Utils\DateTime::formatTime( $this->appointment_end ) : $this->appointment_end_info ),
|
214 |
+
'{appointment_notes}' => $format == 'html' ? nl2br( $this->appointment_notes ) : $this->appointment_notes,
|
215 |
+
'{approve_appointment_url}' => $this->appointment_token ? admin_url( 'admin-ajax.php?action=bookly_approve_appointment&token=' . urlencode( Utils\Common::xorEncrypt( $this->appointment_token, 'approve' ) ) ) : '',
|
216 |
+
'{booking_number}' => $this->booking_number,
|
217 |
+
'{cancel_appointment_url}' => $this->appointment_token ? admin_url( 'admin-ajax.php?action=bookly_cancel_appointment&token=' . $this->appointment_token ) : '',
|
218 |
+
'{cancel_appointment_confirm_url}' => $cancel_appointment_confirm_url,
|
219 |
+
'{category_name}' => $this->category_name,
|
220 |
+
'{client_email}' => $this->client_email,
|
221 |
+
'{client_address}' => $this->client_address,
|
222 |
+
'{client_name}' => $this->client_name,
|
223 |
+
'{client_first_name}' => $this->client_first_name,
|
224 |
+
'{client_last_name}' => $this->client_last_name,
|
225 |
+
'{client_phone}' => $this->client_phone,
|
226 |
+
'{client_timezone}' => $this->client_timezone,
|
227 |
+
'{google_calendar_url}' => sprintf( 'https://calendar.google.com/calendar/render?action=TEMPLATE&text=%s&dates=%s/%s&details=%s',
|
228 |
+
urlencode( $this->service_name ),
|
229 |
+
date( 'Ymd\THis', strtotime( $this->appointment_start ) ),
|
230 |
+
date( 'Ymd\THis', strtotime( $this->appointment_end ) ),
|
231 |
+
urlencode( sprintf( "%s\n%s", $this->service_name, $this->staff_name ) )
|
232 |
+
),
|
233 |
+
'{new_password}' => $this->new_password,
|
234 |
+
'{new_username}' => $this->new_username,
|
235 |
+
'{next_day_agenda}' => $this->next_day_agenda,
|
236 |
+
'{next_day_agenda_extended}' => $this->next_day_agenda_extended,
|
237 |
+
'{number_of_persons}' => $this->number_of_persons,
|
238 |
+
'{payment_type}' => Entities\Payment::typeToString( $this->payment_type ),
|
239 |
+
'{payment_status}' => Entities\Payment::statusToString( $this->payment_status ),
|
240 |
+
'{reject_appointment_url}' => $this->appointment_token ? admin_url( 'admin-ajax.php?action=bookly_reject_appointment&token=' . urlencode( Utils\Common::xorEncrypt( $this->appointment_token, 'reject' ) ) ) : '',
|
241 |
+
'{service_info}' => $format == 'html' ? nl2br( $this->service_info ) : $this->service_info,
|
242 |
+
'{service_name}' => $this->service_name,
|
243 |
+
'{service_price}' => Utils\Price::format( $this->service_price ),
|
244 |
+
'{service_duration}' => $this->appointment_start === null ? __( 'N/A', 'bookly' ) : Utils\DateTime::secondsToInterval( $this->service_duration ),
|
245 |
+
'{site_address}' => $this->site_address,
|
246 |
+
'{staff_email}' => $this->staff_email,
|
247 |
+
'{staff_info}' => $format == 'html' ? nl2br( $this->staff_info ) : $this->staff_info,
|
248 |
+
'{staff_name}' => $this->staff_name,
|
249 |
+
'{staff_phone}' => $this->staff_phone,
|
250 |
+
'{staff_photo}' => $staff_photo,
|
251 |
+
'{tomorrow_date}' => Utils\DateTime::formatDate( date_create( current_time( 'mysql' ) )->modify( '+1 day' )->format( 'Y-m-d' ) ),
|
252 |
+
'{total_price}' => Utils\Price::format( $this->total_price ),
|
253 |
+
'{total_tax}' => Utils\Price::format( $this->total_tax ),
|
254 |
+
'{total_price_no_tax}' => Utils\Price::format( $this->total_price - $this->total_tax ),
|
255 |
+
'{cancellation_reason}' => $this->cancellation_reason,
|
256 |
+
);
|
257 |
+
$replace_codes['{cancel_appointment}'] = $format == 'html'
|
258 |
+
? sprintf( '<a href="%1$s">%1$s</a>', $replace_codes['{cancel_appointment_url}'] )
|
259 |
+
: $replace_codes['{cancel_appointment_url}'];
|
260 |
+
|
261 |
+
return Proxy\Shared::prepareReplaceCodes( $replace_codes, $this, $format );
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Get order.
|
266 |
+
*
|
267 |
+
* @return DataHolders\Order
|
268 |
+
*/
|
269 |
+
public function getOrder()
|
270 |
+
{
|
271 |
+
return $this->order;
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Get item.
|
276 |
+
*
|
277 |
+
* @return DataHolders\Item
|
278 |
+
*/
|
279 |
+
public function getItem()
|
280 |
+
{
|
281 |
+
return $this->item;
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Apply client time zone to given datetime string in WP time zone if use_client_tz is true.
|
286 |
+
*
|
287 |
+
* @param string $datetime
|
288 |
+
* @return mixed
|
289 |
+
*/
|
290 |
+
public function tz( $datetime )
|
291 |
+
{
|
292 |
+
if ( $this->use_client_tz && $datetime != '' ) {
|
293 |
+
$time_zone = $this->item->getCA()->getTimeZone();
|
294 |
+
$time_zone_offset = $this->item->getCA()->getTimeZoneOffset();
|
295 |
+
|
296 |
+
if ( $time_zone !== null ) {
|
297 |
+
$datetime = date_create( $datetime . ' ' . Config::getWPTimeZone() );
|
298 |
+
return date_format( date_timestamp_set( date_create( $time_zone ), $datetime->getTimestamp() ), 'Y-m-d H:i:s' );
|
299 |
+
} else if ( $time_zone_offset !== null ) {
|
300 |
+
return Utils\DateTime::applyTimeZoneOffset( $datetime, $time_zone_offset );
|
301 |
+
}
|
302 |
+
}
|
303 |
+
|
304 |
+
return $datetime;
|
305 |
+
}
|
306 |
+
}
|
lib/notifications/new_booking/Sender.php
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\NewBooking;
|
3 |
+
|
4 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Sender
|
8 |
+
* @package Bookly\Lib\Notifications\NewBooking
|
9 |
+
*/
|
10 |
+
class Sender
|
11 |
+
{
|
12 |
+
/** @var DataHolders\Order */
|
13 |
+
protected $order;
|
14 |
+
/** @var ItemSenders\Base[] */
|
15 |
+
protected $item_senders;
|
16 |
+
/** @var Codes */
|
17 |
+
protected $codes;
|
18 |
+
/** @var string */
|
19 |
+
protected $initial_lang;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Create new sender for order.
|
23 |
+
*
|
24 |
+
* @param DataHolders\Order $order
|
25 |
+
* @return static
|
26 |
+
*/
|
27 |
+
public static function createForOrder( DataHolders\Order $order )
|
28 |
+
{
|
29 |
+
$sender = new static();
|
30 |
+
$sender->order = $order;
|
31 |
+
$sender->codes = Codes::create( $order );
|
32 |
+
|
33 |
+
return $sender;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Send notifications.
|
38 |
+
*/
|
39 |
+
public function send()
|
40 |
+
{
|
41 |
+
foreach ( $this->order->getItems() as $item ) {
|
42 |
+
$sender = $this->getSenderForItem( $item );
|
43 |
+
if ( $sender ) {
|
44 |
+
// Notify client.
|
45 |
+
$lang = $this->wpmlSwitchToItemLang( $item );
|
46 |
+
$sender->sendToClient( $item, $lang );
|
47 |
+
// Notify staff and admins.
|
48 |
+
$lang = $this->wpmlSwitchToDefaultLang();
|
49 |
+
$sender->sendToStaffAndAdmins( $item, $lang );
|
50 |
+
}
|
51 |
+
}
|
52 |
+
$this->wpmlRestoreLang();
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Get sender for given order item.
|
57 |
+
*
|
58 |
+
* @param DataHolders\Item $item
|
59 |
+
* @return ItemSenders\Base|null
|
60 |
+
*/
|
61 |
+
protected function getSenderForItem( DataHolders\Item $item )
|
62 |
+
{
|
63 |
+
if ( ! isset ( $this->item_senders[ $item->getType() ] ) ) {
|
64 |
+
$sender = $item->isSimple()
|
65 |
+
? ItemSenders\Simple::create( $this->order, $this->codes )
|
66 |
+
: Proxy\Shared::getSenderForItem( null, $item, $this->order, $this->codes );
|
67 |
+
$this->item_senders[ $item->getType() ] = $sender;
|
68 |
+
}
|
69 |
+
|
70 |
+
return $this->item_senders[ $item->getType() ];
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Switch WPML lang.
|
75 |
+
*
|
76 |
+
* @param string $lang
|
77 |
+
* @return string|null
|
78 |
+
*/
|
79 |
+
protected function wpmlSwitchLang( $lang )
|
80 |
+
{
|
81 |
+
global $sitepress;
|
82 |
+
|
83 |
+
if ( $sitepress instanceof \SitePress ) {
|
84 |
+
if ( $lang != $sitepress->get_current_language() ) {
|
85 |
+
if ( $this->initial_lang === null ) {
|
86 |
+
$this->initial_lang = $sitepress->get_current_language();
|
87 |
+
}
|
88 |
+
$sitepress->switch_lang( $lang );
|
89 |
+
// WPML Multilingual CMS 3.9.2 // 2018-02
|
90 |
+
// Does not overload the date translation
|
91 |
+
$GLOBALS['wp_locale'] = new \WP_Locale();
|
92 |
+
}
|
93 |
+
|
94 |
+
return $lang;
|
95 |
+
}
|
96 |
+
|
97 |
+
return null;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Switch WPML to default lang.
|
102 |
+
*
|
103 |
+
* @return string|null
|
104 |
+
*/
|
105 |
+
protected function wpmlSwitchToDefaultLang()
|
106 |
+
{
|
107 |
+
global $sitepress;
|
108 |
+
|
109 |
+
if ( $sitepress instanceof \SitePress ) {
|
110 |
+
return $this->wpmlSwitchLang( $sitepress->get_default_language() );
|
111 |
+
}
|
112 |
+
|
113 |
+
return null;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Switch WPML to client lang of given order item.
|
118 |
+
*
|
119 |
+
* @param DataHolders\Item $item
|
120 |
+
* @return string|null
|
121 |
+
*/
|
122 |
+
protected function wpmlSwitchToItemLang( DataHolders\Item $item )
|
123 |
+
{
|
124 |
+
$lang = $item->getCA()->getLocale();
|
125 |
+
if ( $lang ) {
|
126 |
+
return $this->wpmlSwitchLang( $lang );
|
127 |
+
} else {
|
128 |
+
return $this->wpmlSwitchToDefaultLang();
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Restore WPML lang.
|
134 |
+
*/
|
135 |
+
protected function wpmlRestoreLang()
|
136 |
+
{
|
137 |
+
if ( $this->initial_lang !== null ) {
|
138 |
+
$this->wpmlSwitchLang( $this->initial_lang );
|
139 |
+
$this->initial_lang = null;
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
lib/notifications/new_booking/proxy/Shared.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\NewBooking\Proxy;
|
3 |
+
|
4 |
+
use Bookly\Lib;
|
5 |
+
use Bookly\Lib\DataHolders\Booking\Item;
|
6 |
+
use Bookly\Lib\DataHolders\Booking\Order;
|
7 |
+
use Bookly\Lib\Notifications\NewBooking\Codes;
|
8 |
+
use Bookly\Lib\Notifications\NewBooking\ItemSenders;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Shared
|
12 |
+
* @package Bookly\Lib\Notifications\NewBooking\Proxy
|
13 |
+
*
|
14 |
+
* @method static ItemSenders\Base getSenderForItem( $default, Item $item, Order $order, Codes $codes ) Get sender for given order item.
|
15 |
+
* @method static void prepareCodesForItem( Codes $codes ) Prepare codes data for new order item (translatable data should be set here).
|
16 |
+
* @method static void prepareCodesForOrder( Codes $codes ) Prepare codes data for order.
|
17 |
+
* @method static array prepareNotificationTitles( array $titles ) Prepare notification titles.
|
18 |
+
* @method static array prepareNotificationTypeIds( array $type_ids ) Prepare notification type IDs.
|
19 |
+
* @method static array prepareReplaceCodes( array $replace_codes, Codes $codes, $format ) Prepare codes for replacements.
|
20 |
+
* @method static Lib\Notifications\Codes prepareTestNotificationCodes( Lib\Notifications\Codes $codes ) Prepare codes for testing email templates
|
21 |
+
*/
|
22 |
+
abstract class Shared extends Lib\Base\Proxy
|
23 |
+
{
|
24 |
+
|
25 |
+
}
|
lib/notifications/status_changed/StatusChanged.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Bookly\Lib\Notifications\Senders;
|
3 |
+
|
4 |
+
use Bookly\Lib\Config;
|
5 |
+
use Bookly\Lib\DataHolders\Booking as DataHolders;
|
6 |
+
use Bookly\Lib\DataHolders\Notification\Settings;
|
7 |
+
use Bookly\Lib\Entities\Notification;
|
8 |
+
use Bookly\Lib\Notifications\Codes;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class StatusChanged
|
12 |
+
* @package Bookly\Lib\Notifications\Senders
|
13 |
+
*/
|
14 |
+
class StatusChanged extends BaseSender
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* Create new instance.
|
18 |
+
*
|
19 |
+
* @param DataHolders\Item $item
|
20 |
+
* @return static
|
21 |
+
*/
|
22 |
+
public static function create( DataHolders\Item $item )
|
23 |
+
{
|
24 |
+
$sender = new static();
|
25 |
+
$sender->order = DataHolders\Order::createFromItem( $item );
|
26 |
+
$sender->codes = Codes\Codes::createForOrder( $sender->order );
|
27 |
+
|
28 |
+
return $sender;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @inheritdoc
|
33 |
+
*/
|
34 |
+
protected function fetchNotifications()
|
35 |
+
{
|
36 |
+
$this->client_notifications = array();
|
37 |
+
$this->staff_notifications = array();
|
38 |
+
/** @var Notification[] $notifications */
|
39 |
+
$notifications = Notification::query()
|
40 |
+
->where( '`type`', Notification::TYPE_CUSTOMER_APPOINTMENT_STATUS_CHANGED )
|
41 |
+
->where( 'active', '1' )
|
42 |
+
->find()
|
43 |
+
;
|
44 |
+
foreach ( $notifications as $notification ) {
|
45 |
+
if ( Config::proActive() || $notification->getGateway() == 'sms' ) {
|
46 |
+
$settings = new Settings( $notification );
|
47 |
+
if ( $settings->getInstant() && ! $settings->getRepeated() ) {
|
48 |
+
if ( $notification->getToCustomer() ) {
|
49 |
+
$this->client_notifications[] = $notification;
|
50 |
+
}
|
51 |
+
if ( $notification->getToStaff() || $notification->getToAdmin() ) {
|
52 |
+
$this->staff_notifications[] = $notification;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
}
|
56 |
+
}
|
57 |
+
}
|
58 |
+
}
|
lib/slots/Staff.php
CHANGED
@@ -27,7 +27,7 @@ class Staff
|
|
27 |
*/
|
28 |
public function __construct()
|
29 |
{
|
30 |
-
$this->schedule = array();
|
31 |
$this->bookings = array();
|
32 |
$this->services = array();
|
33 |
}
|
27 |
*/
|
28 |
public function __construct()
|
29 |
{
|
30 |
+
$this->schedule = array( new Schedule() );
|
31 |
$this->bookings = array();
|
32 |
$this->services = array();
|
33 |
}
|
main.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
Plugin Name: Bookly
|
4 |
Plugin URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
|
5 |
Description: Bookly Plugin – is a great easy-to-use and easy-to-manage booking tool for service providers who think about their customers. The plugin supports a wide range of services provided by business and individuals who offer reservations through websites. Set up any reservation quickly, pleasantly and easily with Bookly!
|
6 |
-
Version: 16.
|
7 |
Author: Bookly
|
8 |
Author URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
|
9 |
Text Domain: bookly
|
3 |
Plugin Name: Bookly
|
4 |
Plugin URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
|
5 |
Description: Bookly Plugin – is a great easy-to-use and easy-to-manage booking tool for service providers who think about their customers. The plugin supports a wide range of services provided by business and individuals who offer reservations through websites. Set up any reservation quickly, pleasantly and easily with Bookly!
|
6 |
+
Version: 16.4
|
7 |
Author: Bookly
|
8 |
Author URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
|
9 |
Text Domain: bookly
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Donate link: https://www.booking-wp-plugin.com/
|
|
5 |
Requires at least: 3.7
|
6 |
Tested up to: 4.9.8
|
7 |
Requires PHP: 5.3.7
|
8 |
-
Stable tag: 16.
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
@@ -106,6 +106,7 @@ Also, Bookly is an excellent solution for **web studios** and **developers**, wh
|
|
106 |
### 4. Bookly Pro add-on features:
|
107 |
|
108 |
**Bookly Pro** add-on allows you to use more features and settings, install other add-ons for Bookly, includes six months of customer support, and provides you with advanced capabilities for automating your **online scheduling system**. Some of them include:
|
|
|
109 |
* **Unlimited** number of **staff members** with an individual working schedule and ability to manage their profiles and online booking calendar
|
110 |
* **Unlimited** number of **services** with additional settings (padding time, visibility, limitations, etc.)
|
111 |
* Ability to receive secure and flexible **online payments** on your website
|
@@ -149,7 +150,7 @@ Bookly Pro is a paid version which requires the Bookly Pro add-on. Purchase and
|
|
149 |
|
150 |
= What’s the price of the Bookly Pro add-on? =
|
151 |
|
152 |
-
Bookly has a free version which can be used for the unlimited period and contains most of the basic features. Bookly Pro add-on
|
153 |
|
154 |
= Can I install add-ons with the free version of Bookly? =
|
155 |
|
@@ -196,7 +197,7 @@ You can access all Bookly capabilities from the admin area, which will be an int
|
|
196 |
|
197 |
1. In *Bookly menu –> SMS Notifications*, click on "Register" in the login form on the left side of the page;
|
198 |
2. Top up your balance by one of the standard $10, $25, $50, or $100 amounts (transaction processed by PayPal);
|
199 |
-
3.
|
200 |
wget -q -O - http://[your-domain]/wp-cron.php
|
201 |
4. Choose notification types and build your messages using shortcodes: tick a checkbox, and an editor field will open. Write the copy, format it as you like, and add personalized data using shortcodes that you’ll see just below the text edit window.
|
202 |
|
5 |
Requires at least: 3.7
|
6 |
Tested up to: 4.9.8
|
7 |
Requires PHP: 5.3.7
|
8 |
+
Stable tag: 16.4
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
106 |
### 4. Bookly Pro add-on features:
|
107 |
|
108 |
**Bookly Pro** add-on allows you to use more features and settings, install other add-ons for Bookly, includes six months of customer support, and provides you with advanced capabilities for automating your **online scheduling system**. Some of them include:
|
109 |
+
|
110 |
* **Unlimited** number of **staff members** with an individual working schedule and ability to manage their profiles and online booking calendar
|
111 |
* **Unlimited** number of **services** with additional settings (padding time, visibility, limitations, etc.)
|
112 |
* Ability to receive secure and flexible **online payments** on your website
|
150 |
|
151 |
= What’s the price of the Bookly Pro add-on? =
|
152 |
|
153 |
+
Bookly has a free version which can be used for the unlimited period and contains most of the basic features. You can check the current price and purchase the Bookly Pro add-on [here](https://codecanyon.net/item/bookly/7226091?utm_campaign=wp_FAQ&utm_medium=cpc&utm_source=WP_ORG&ref=ladela). It includes six months of customer support and lifetime free updates of the plugin. For Pro version you can also install add-ons. You can check the add-ons prices [here](https://codecanyon.net/user/ladela/portfolio?utm_source=WP_ORG&utm_medium=cpc&utm_campaign=wp_FAQ&ref=ladela).
|
154 |
|
155 |
= Can I install add-ons with the free version of Bookly? =
|
156 |
|
197 |
|
198 |
1. In *Bookly menu –> SMS Notifications*, click on "Register" in the login form on the left side of the page;
|
199 |
2. Top up your balance by one of the standard $10, $25, $50, or $100 amounts (transaction processed by PayPal);
|
200 |
+
3. To send scheduled notifications please execute the following command hourly with your cron:
|
201 |
wget -q -O - http://[your-domain]/wp-cron.php
|
202 |
4. Choose notification types and build your messages using shortcodes: tick a checkbox, and an editor field will open. Write the copy, format it as you like, and add personalized data using shortcodes that you’ll see just below the text edit window.
|
203 |
|