widgetName = JText::translate('VBO_W_BULKMESSAGING_TITLE'); $this->widgetDescr = JText::translate('VBO_W_BULKMESSAGING_DESCR'); $this->widgetId = basename(__FILE__, '.php'); // define widget and icon and style name $this->widgetIcon = ''; $this->widgetStyleName = 'yellow'; // load widget's settings $this->widgetSettings = $this->loadSettings(); if (!is_object($this->widgetSettings)) { $this->widgetSettings = new stdClass; } } /** * Custom method for this widget only to load the reservations. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * any content sent to output will be returned to the AJAX response. * In this case we return an array because this method requires "return":1. */ public function loadBookings() { // get today's date $today_ymd = date('Y-m-d'); $wrapper = VikRequest::getString('wrapper', '', 'request'); $type = VikRequest::getString('type', 'stayover', 'request'); $from_dt = VikRequest::getString('from_dt', $today_ymd, 'request'); $to_dt = VikRequest::getString('to_dt', '', 'request') ?: $from_dt; if (empty($from_dt)) { VBOHttpDocument::getInstance()->close(500, JText::translate('VBO_PLEASE_FILL_FIELDS')); } // get date timestamps $from_ts = VikBooking::getDateTimestamp($from_dt, 0, 0); $to_ts = VikBooking::getDateTimestamp($to_dt, 23, 59, 59); // query the db $dbo = JFactory::getDbo(); $q = $dbo->getQuery(true) ->select($dbo->qn('o') . '.*') ->select($dbo->qn('co.idcustomer')) ->select('CONCAT_WS(" ", ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') AS ' . $dbo->qn('customer_fullname')) ->select($dbo->qn('c.country', 'customer_country')) ->select($dbo->qn('c.pic')) ->from($dbo->qn('#__vikbooking_orders', 'o')) ->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id')) ->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer')) ->where($dbo->qn('o.closure') . ' = 0'); if ($type == 'arrival') { $q->where($dbo->qn('o.checkin') . ' >= ' . $from_ts); $q->where($dbo->qn('o.checkin') . ' <= ' . $to_ts); } elseif ($type == 'departure') { $q->where($dbo->qn('o.checkout') . ' >= ' . $from_ts); $q->where($dbo->qn('o.checkout') . ' <= ' . $to_ts); } elseif ($type == 'bookdate') { $q->where($dbo->qn('o.ts') . ' >= ' . $from_ts); $q->where($dbo->qn('o.ts') . ' <= ' . $to_ts); } else { // stayover $q->where('( (' . $dbo->qn('o.checkin') . ' >= ' . $from_ts . ' AND ' . $dbo->qn('o.checkin') . ' <= ' . $to_ts . ') OR (' . $dbo->qn('o.checkout') . ' >= ' . $from_ts . ' AND ' . $dbo->qn('o.checkout') . ' <= ' . $to_ts . ') )'); } $dbo->setQuery($q); $bookings = $dbo->loadAssocList(); // total checkboxes checked $tot_checked = 0; // first booking ID "checked" $first_checked_bid = 0; $widget_id = $this->widgetId; // start output buffering ob_start(); if (!$bookings) { ?>
$booking) { // get channel logo and other details $ch_logo_obj = VikBooking::getVcmChannelsLogo($booking['channel'], true); $channel_logo = is_object($ch_logo_obj) ? $ch_logo_obj->getSmallLogoURL() : ''; $nights_lbl = $booking['days'] > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY'); $rooms_lbl = !empty($booking['roomsnum']) && $booking['roomsnum'] > 1 ? ', ' . $booking['roomsnum'] . ' ' . JText::translate('VBPVIEWORDERSTHREE') : ''; // compose customer name $customer_name = !empty($booking['customer_fullname']) ? $booking['customer_fullname'] : ''; if ($booking['closure'] > 0 || !strcasecmp($booking['custdata'], JText::translate('VBDBTEXTROOMCLOSED'))) { $customer_name = ' ' . JText::translate('VBDBTEXTROOMCLOSED') . ''; } if (empty($customer_name)) { $customer_name = VikBooking::getFirstCustDataField($booking['custdata']); } // customer country flag $customer_country = ''; $customer_cflag = ''; if (!empty($booking['customer_country'])) { $customer_country = $booking['customer_country']; } elseif (!empty($booking['country'])) { $customer_country = $booking['country']; } if ($customer_country && is_file(VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'countries' . DIRECTORY_SEPARATOR . $customer_country . '.png')) { $customer_cflag = '
';
}
// check for any previous event triggered by this widget for the current reservation
$last_notified = null;
$history_obj = VikBooking::getBookingHistoryInstance($booking['id']);
$prev_ev_data = $history_obj->getEventsWithData('CE', function($data) use ($widget_id) {
return (is_object($data) && !empty($data->widget) && $data->widget == $widget_id);
}, $onlydata = false);
if (is_array($prev_ev_data) && $prev_ev_data) {
$last_notified = $prev_ev_data[0]['dt'];
}
// default checked status
$booking_is_checked = ($booking['status'] == 'confirmed' && !$last_notified);
if ($booking_is_checked) {
$tot_checked++;
if (!$first_checked_bid) {
$first_checked_bid = $booking['id'];
}
}
?>
$html_content,
'tot_bookings' => count($bookings),
'tot_checked' => $tot_checked,
];
}
/**
* Custom method for this widget only to send the communication message.
* The method is called by the admin controller through an AJAX request.
* The visibility should be public, it should not exit the process, and
* any content sent to output will be returned to the AJAX response.
* In this case we return an array because this method requires "return":1.
*/
public function sendOneMessage()
{
$wrapper = VikRequest::getString('wrapper', '', 'request');
$bid = VikRequest::getInt('bid', 0, 'request');
$index = VikRequest::getInt('index', 0, 'request');
$message = VikRequest::getString('message', '', 'request');
$subject = VikRequest::getString('subject', '', 'request');
$booking = VikBooking::getBookingInfoFromID($bid);
if (!$booking) {
VBOHttpDocument::getInstance()->close(500, JText::translate('VBPEDITBUSYONE'));
}
if (empty($message)) {
VBOHttpDocument::getInstance()->close(500, JText::translate('VBO_PLEASE_FILL_FIELDS'));
}
if ($index === 0) {
// update widget's settings with last message and subject
$this->widgetSettings->last_subject = $subject;
$this->widgetSettings->last_message = $message;
$this->updateSettings(json_encode($this->widgetSettings));
}
// determine the message dispatching method for this booking
$sending_method = 'eMail';
// check if support for OTA messaging is available
$ota_messaging_supported = class_exists('VCMChatMessaging');
if ($ota_messaging_supported && VCMChatMessaging::getInstance($booking)->supportsOtaMessaging($mandatory = true)) {
// set flag to identify an OTA messaging notification over the regular email
$sending_method = 'Message';
} elseif (preg_match("/^no-email-[^@]+@[a-z0-9\.]+\.com$/i", $booking['custmail'])) {
// false email address, typical of Airbnb, ignore reservation record
// this statement should be entered only if VCM is outdated, or the Guest Messaging API will be used
VBOHttpDocument::getInstance()->close(500, 'The booking ID ' . $booking['id'] . ' does not have a valid email address that can be notified.');
}
if ($sending_method === 'eMail' && empty($booking['custmail'])) {
VBOHttpDocument::getInstance()->close(500, 'The booking ID ' . $booking['id'] . ' is missing the guest email address.');
}
// language translation
$lang = JFactory::getLanguage();
$vbo_tn = VikBooking::getTranslator();
$vbo_tn::$force_tolang = null;
$website_def_lang = $vbo_tn->getDefaultLang();
if (!empty($booking['lang'])) {
if ($lang->getTag() != $booking['lang']) {
if (VBOPlatformDetection::isWordPress()) {
// wp
$lang->load('com_vikbooking', VIKBOOKING_SITE_LANG, $booking['lang'], true);
$lang->load('com_vikbooking', VIKBOOKING_ADMIN_LANG, $booking['lang'], true);
} else {
// J
$lang->load('com_vikbooking', JPATH_SITE, $booking['lang'], true);
$lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $booking['lang'], true);
$lang->load('joomla', JPATH_SITE, $booking['lang'], true);
$lang->load('joomla', JPATH_ADMINISTRATOR, $booking['lang'], true);
}
}
if ($website_def_lang != $booking['lang']) {
// force the translation to start because contents should be translated
$vbo_tn::$force_tolang = $booking['lang'];
}
}
// dispatch the message to the guest reservation
if ($sending_method === 'eMail') {
// regular email notification
$message = $this->notifyThroughEmail($booking, $message, $subject);
} else {
// notification through guest messaging API
$message = $this->notifyThroughMessage($booking, $message);
}
// update history for this booking with the information about the communication sent
VikBooking::getBookingHistoryInstance($booking['id'])
->setExtraData(['widget' => $this->widgetId])
->store('CE', $message);
// return an associative array of values
return [
'result' => 1,
];
}
/**
* Preload the necessary CSS/JS assets.
*
* @return void
*/
public function preload()
{
// load assets
$this->vbo_app->loadDatePicker();
if (VBOPlatformDetection::isJoomla()) {
// load assets
$this->vbo_app->loadVisualEditorAssets();
}
// JS lang defs
JText::script('VBO_PLEASE_SELECT');
JText::script('VBPVIEWORDERSPEOPLE');
JText::script('VBO_WANT_PROCEED');
JText::script('VBO_CONT_WRAPPER');
JText::script('VBO_CONT_WRAPPER_HELP');
}
/**
* Main method to invoke the widget. Contents will be loaded
* through AJAX requests, not via PHP when the page loads.
*
* @param VBOMultitaskData $data
*
* @return void
*/
public function render(VBOMultitaskData $data = null)
{
// increase widget's instance counter
static::$instance_counter++;
// check whether the widget is being rendered via AJAX when adding it through the customizer
$is_ajax = $this->isAjaxRendering();
// generate a unique ID for the sticky notes wrapper instance
$wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
$wrapper_id = 'vbo-widget-bulkmess-' . $wrapper_instance;
// get permissions
$vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking');
if (!$vbo_auth_bookings) {
// display nothing
return;
}
// date format
$dtpicker_df = $this->getDateFormat('jui');
// build the default message subject
$def_subject = !empty($this->widgetSettings->last_subject) ? $this->widgetSettings->last_subject : JText::sprintf('VBOMAILSUBJECT', VikBooking::getFrontTitle());
?>
vbo_app->loadVisualEditorAssets();
// build a list of all special tags for the visual editor
$special_tags_base = [
'{customer_name}',
'{customer_pin}',
'{booking_id}',
'{checkin_date}',
'{checkout_date}',
'{num_nights}',
'{rooms_booked}',
'{tot_adults}',
'{tot_children}',
'{tot_guests}',
'{total}',
'{total_paid}',
'{remaining_balance}',
'{booking_link}',
];
// load all conditional text special tags
$condtext_tags = array_keys(VikBooking::getConditionalRulesInstance()->getSpecialTags());
// join special tags with conditional texts to construct a list of editor buttons,
// displayed within the toolbar of Quill editor
$editor_btns = array_merge($special_tags_base, $condtext_tags);
// convert special tags into HTML buttons, displayed under the text editor
$special_tags_base = array_map(function($tag)
{
return '';
}, $special_tags_base);
// convert conditional texts into HTML buttons, displayed under the text editor
$condtext_tags = array_map(function($tag)
{
return '';
}, $condtext_tags);
// build the default message value
$def_message = !empty($this->widgetSettings->last_message) ? $this->widgetSettings->last_message : $this->getDefaultMessage();
return $this->vbo_app->renderVisualEditor(
'vbo_widget_bulkmess_mess',
$def_message,
[
'class' => 'vbo-widget-bulkmess-messagecont',
'style' => 'width: 96%; height: 150px;',
],
[
'modes' => [
'text',
'visual',
],
],
$editor_btns
);
}
/**
* Returns the default message.
*
* @return string
*/
private function getDefaultMessage()
{
$message = '';
$logo_html = '';
$sitelogo = VBOFactory::getConfig()->get('sitelogo');
$company_name = VikBooking::getFrontTitle();
if ($sitelogo && is_file(VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources'. DIRECTORY_SEPARATOR . $sitelogo)) {
$logo_html = ''
. '
This is a message for your stay from {checkin_date} to {checkout_date}.
Thank you.
$company_name