Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/base-admin.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/base-admin.php
@@ -555,7 +555,7 @@
wpb_admin_access_check( 'manage_tools' );
?>
-<div class="wrap app-tools">
+<div class="wrap app-page app-tools">
<h2 class="app-dashicons-before dashicons-admin-tools"><?php echo __('Tools', 'wp-base' ); ?></h2>
<h3 class="nav-tab-wrapper"><?php
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/bookings.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/bookings.php
@@ -1488,7 +1488,7 @@
/* Services */
$html .= wpb_wrap_field( 'service',
$booking->is_event() ? __('Event', 'wp-base') : __('Service', 'wp-base'),
- $controller->select_service( !$app_id ),
+ $controller->select_service( false, !$app_id ),
apply_filters( 'app_inline_edit_service_helptip', '', $booking, $controller )
);
@@ -1993,7 +1993,7 @@
$out = array(
'locations_sel' => $controller->select_location(),
- 'services_sel' => $controller->select_service( ! $app_id ),
+ 'services_sel' => $controller->select_service( false, ! $app_id ),
'workers_sel' => wpb_is_admin_user() ? $controller->select_worker() : $controller->select_self_worker(),
);
@@ -2014,7 +2014,7 @@
$out = array_merge( $out, array( 'price' => $slot->get_subtotal() + $slot->get_fee(), 'deposit' => $slot->get_deposit() ) );
}
- $out = apply_filters( 'app_inline_edit_update', $out, $booking, $old_booking, $controller, $this );
+ $out = apply_filters( 'app_inline_edit_update', $out, $booking, $old_booking, $controller );
die( json_encode( $out ) );
}
@@ -2123,7 +2123,7 @@
$booking->check_update( $old_booking );
}
- do_action( 'app_inline_edit_save_before_save', $booking, $old_booking, $this );
+ do_action( 'app_inline_edit_save_before_save', $booking, $old_booking );
$updated = $inserted = null;
$changed = false;
@@ -2170,12 +2170,12 @@
$changed = true;
}
- $changed = apply_filters( 'app_inline_edit_save', $changed, $booking, $old_booking, $this );
+ $changed = apply_filters( 'app_inline_edit_save', $changed, $booking, $old_booking );
if ( $updated ) {
- do_action( 'app_inline_edit_updated', $booking, $old_booking, $this );
+ do_action( 'app_inline_edit_updated', $booking, $old_booking );
} else if ( $inserted ) {
- do_action( 'app_inline_edit_new_booking', $booking, $old_booking, $this );
+ do_action( 'app_inline_edit_new_booking', $booking, $old_booking );
}
$email_sent = $this->send_email( $booking );
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/functions-admin.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/functions-admin.php
@@ -22,10 +22,16 @@
* @return object WpB_Controller object
*/
function wpb_prepare_booking( &$booking ) {
+
+ if ( wpb_is_admin_user() ) {
+ $hier_forced = false;
+ } else {
+ $hier_forced = str_replace( 'W', '', wpb_setting( 'lsw_priority', WPB_DEFAULT_LSW_PRIORITY ) ) == 'LS' ? 'WLS' : 'WSL';
+ }
if ( $booking->get_ID() ) {
- $hier_forced = str_replace( 'W', '', wpb_setting( 'lsw_priority', WPB_DEFAULT_LSW_PRIORITY ) ) == 'LS' ? 'WLS' : 'WSL';
- $controller = new WpB_Controller( $booking, wpb_controller_order_by( $booking ), ( wpb_is_admin_user() ? false : $hier_forced ) );
+
+ $controller = new WpB_Controller( $booking, wpb_controller_order_by( $booking ), $hier_forced );
} else {
if ( ! empty( $_REQUEST['add_new_event'] ) ) {
$booking->set_as_event();
@@ -42,7 +48,7 @@
$booking->set_status( 'confirmed' );
$booking->set_payment_method('');
- $controller = new WpB_Controller( $booking, wpb_controller_order_by( $booking ) );
+ $controller = new WpB_Controller( $booking, wpb_controller_order_by( $booking ), $hier_forced );
} else { /* Add New */
$controller = wpb_init_controller();
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/global-settings.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/global-settings.php
@@ -43,6 +43,14 @@
}
/**
+ * Get a list of editable fields keys
+ * @return array
+ */
+ public static function editable_fields() {
+ return apply_filters( 'app_editable_fields_selections', array_merge( array('location','service','worker','date','time'), BASE()->get_user_fields() ) );
+ }
+
+ /**
* Save admin settings
*/
public function save_settings() {
@@ -87,6 +95,20 @@
$options['cancel_limit'] = preg_replace( '/[^0-9]/', '', $_POST['cancel_limit'] );
$options['cancel_page'] = isset( $_POST['cancel_page'] ) ? $_POST['cancel_page'] : '';
+ $options['allow_edit'] = $_POST['allow_edit'];
+ $options['edit_limit'] = preg_replace( '/[^0-9]/', '', $_POST['edit_limit'] );
+ $options['edit_upper_limit'] = preg_replace( '/[^0-9]/', '', $_POST['edit_upper_limit'] );
+
+ /* Which fields can be edited */
+ $temp = array();
+ foreach ( self::editable_fields() as $m ) {
+ if ( isset( $_POST['editable_'.$m ] ) ) {
+ $temp[] = $m;
+ }
+ }
+
+ $options["editable"] = implode( ',', $temp );
+
} else if ( 'save_global_advanced' == $_POST['action_app'] ) {
$settings_changed = true;
@@ -111,6 +133,7 @@
: $_POST['description_post_type'];
$options['person_type_template'] = wp_unslash( $_POST['person_type_template'] );
$options['refresh_url'] = trim( $_POST['refresh_url'] );
+ $options['account_page'] = $_POST['account_page'];
$options['auto_delete'] = $_POST['auto_delete'];
$options['auto_delete_time'] = preg_replace( '/[^0-9]/', '', $_POST['auto_delete_time'] );
@@ -120,10 +143,13 @@
$options['admin_toolbar'] = $_POST['admin_toolbar'];
$options['records_per_page'] = preg_replace( '/[^0-9]/', '', $_POST['records_per_page'] );
$options['records_per_page_business'] = preg_replace( '/[^0-9]/', '', $_POST['records_per_page_business'] );
- $options['schedule_show_images'] = $_POST['schedule_show_images'];
$options['schedule_allowed_stats'] = ! empty( $_POST['schedule_allowed_stats'] ) ? implode( ',', $_POST['schedule_allowed_stats'] ) : '';
+ $options['schedule_allowed_stats_client'] = ! empty( $_POST['schedule_allowed_stats_client'] ) ? implode( ',', $_POST['schedule_allowed_stats_client'] ) : '';
$options['schedule_desc_admin'] = wp_unslash( $_POST['schedule_desc_admin'] );
$options['schedule_desc_worker'] = wp_unslash( $_POST['schedule_desc_worker'] );
+ $options['schedule_desc_client'] = wp_unslash( $_POST['schedule_desc_client'] );
+ $options['schedule_client_can'] = $_POST['schedule_client_can'];
+ $options['schedule_show_images'] = $_POST['schedule_show_images'];
do_action( 'app_global_settings_maybe_updated' );
@@ -177,6 +203,16 @@
wpb_notice( 'saved' );
$this->settings_changed( $old_options, $options );
}
+
+ if ( ! empty( $_POST['schedule_clear_cache'] ) && 'yes' == $_POST['schedule_clear_cache'] ) {
+ foreach ( array( 'admin', 'worker', 'client' ) as $context ) {
+ wpb_schedules_delete_transients( $context );
+ }
+
+ delete_transient( 'wpbase_schedule_data' );
+
+ wpb_notice( __( 'Schedules cache cleared', 'wp-base' ) );
+ }
}
/**
@@ -234,6 +270,18 @@
if ( ! empty( $old_options['min_time'] ) && $old_options['min_time'] != $options['min_time'] ) {
wp_reschedule_event( strtotime( current_time( 'Y-m-d' ) ) - 24*3600, 'wpb_time_base_tick', 'app_time_base_tick' );
}
+
+ if ( $options['schedule_desc_admin'] != $old_options['schedule_desc_admin'] ) {
+ wpb_schedules_delete_transients( 'admin' );
+ }
+
+ if ( $options['schedule_desc_worker'] != $old_options['schedule_desc_worker'] ) {
+ wpb_schedules_delete_transients( 'worker' );
+ }
+
+ if ( empty( $old_options['schedule_desc_client'] ) || $options['schedule_desc_client'] != $old_options['schedule_desc_client'] ) {
+ wpb_schedules_delete_transients( 'client' );
+ }
}
/**
@@ -422,8 +470,45 @@
</div>
</div>
+ <p class="submit">
+ <input type="submit" class="button-primary" value="<?php _e('Save All Basic Settings', 'wp-base' ) ?>" />
+ </p>
+
<?php do_action( 'app_admin_settings_after_booking' ) ?>
+ <div class="postbox">
+ <div class="postbox-header">
+ <h3 class="hndle" id="front-end-edit"><span><?php _e('Editing & Rescheduling', 'wp-base'); ?></span></h3>
+ </div>
+ <div class="inside">
+ <table class="form-table">
+
+ <?php wpb_setting_yn( 'allow_edit' ) ?>
+ <?php wpb_setting_text( 'edit_limit' ) ?>
+ <?php wpb_setting_text( 'edit_upper_limit' ) ?>
+
+ <tr id="editable">
+ <th scope="row" ><?php WpBConstant::echo_setting_name('editable') ?></th>
+ <td class="has-checkbox">
+ <?php foreach ( self::editable_fields() as $m ) { ?>
+ <label>
+ <input type="checkbox" name="editable_<?php echo $m ?>" value="true" <?php if ( strpos( wpb_setting("editable"), $m ) !== false ) echo "checked='checked'"?>>
+ <span>
+ <?php
+ echo wpb_get_text($m);
+ ?></span>
+ </label>
+ <?php } ?>
+ <br><span class="description app-btm"><?php WpBConstant::echo_setting_desc('editable') ?></span>
+ </td>
+ </tr>
+
+ </table>
+ </div>
+ </div>
+
+ <?php do_action( 'app_admin_settings_after_editing' ) ?>
+
<div class="submit app-manage-row">
<input type="hidden" name="action_app" value="save_general" />
<?php wp_nonce_field( 'update_app_settings', 'app_nonce', true ) ?>
@@ -536,6 +621,24 @@
</td>
</tr>
+ <tr id="schedule-desc-client">
+ <th scope="row"><?php WpBConstant::echo_setting_name( 'schedule_desc_client' ) ?></th>
+ <td>
+ <?php wp_editor( wpb_setting( 'schedule_desc_client', WpBConstant::$_schedule_desc_client ), 'schedule_desc_client',
+ array( 'editor_height' => WPB_EDITOR_HEIGHT,
+ 'editor_class' => 'app-wp-editor',
+ 'editor_css' => '',
+ 'tinymce' => array( 'body_class' => 'app-editor-body' ),
+ 'teeny' => true,
+ 'wpautop' => false,
+ 'media_buttons' => false,
+ ));
+
+ do_action( 'app_admin_after_schedule_desc', 'client' ); ?>
+ <span class="description app-btm"><?php WpBConstant::echo_setting_desc( 'schedule_desc_client' ) ?></span>
+ </td>
+ </tr>
+
<tr id="schedule-allowed-stats">
<th scope="row" ><?php WpBConstant::echo_setting_name('schedule_allowed_stats') ?></th>
<td>
@@ -551,7 +654,26 @@
</td>
</tr>
- <?php wpb_setting_yn( 'schedule_show_images' ); ?>
+ <tr id="schedule-allowed-stats-client">
+ <th scope="row" ><?php WpBConstant::echo_setting_name('schedule_allowed_stats_client') ?></th>
+ <td>
+ <select multiple data-noneSelectedText="<?php echo esc_attr( __('Select statuses', 'wp-base' ) ) ?>" data-selectedlist="12" name="schedule_allowed_stats_client[]" class="app_ms">
+ <?php
+ $stat_arr = explode( ',', wpb_setting( 'schedule_allowed_stats_client', 'paid,confirmed,pending,running,completed,removed' ) );
+ foreach( $this->a->get_statuses() as $key => $stat ) {
+
+ echo '<option value="'.$key.'" '.selected( in_array( $key, $stat_arr ), true, false ) .'>' . $this->a->get_status_name( $key ) . '</option>';
+ }
+ ?></select>
+ <span class="description app-btm"><?php WpBConstant::echo_setting_desc('schedule_allowed_stats_client') ?></span>
+ </td>
+ </tr>
+
+ <?php
+ wpb_setting_yn( 'schedule_client_can' );
+ wpb_setting_yn( 'schedule_show_images' );
+ wpb_setting_yn( 'schedule_clear_cache' );
+ ?>
</table>
</div>
@@ -612,6 +734,18 @@
</td>
</tr>
+ <tr id="account-page">
+ <th scope="row"><?php WpBConstant::echo_setting_name('account_page') ?></th>
+ <td>
+ <select name="account_page">
+ <?php
+ echo wpb_description_page_selection( 'page', wpb_setting( 'account_page' ) );
+ ?>
+ </select>
+ <span class="description app-btm"><?php WpBConstant::echo_setting_desc('account_page') ?></span>
+ </td>
+ </tr>
+
</table>
</div>
</div>
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/service-rte.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/service-rte.php
@@ -0,0 +1,216 @@
+<?php
+/**
+ * WPB Service Rich Text editor
+ *
+ * @author Hakan Ozevin
+ * @package WP BASE
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
+ * @since 5.9.0
+ */
+
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+if ( ! class_exists( 'WpBServiceRTE' ) ) {
+
+class WpBServiceRTE {
+
+ /**
+ * Add actions and filters
+ */
+ public function add_hooks() {
+ if ( did_action( 'plugins_loaded' ) ) {
+ $this->plugins_loaded();
+ } else {
+ add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
+ }
+ }
+
+ /**
+ * Add actions and filters
+ */
+ public function plugins_loaded() {
+ if ( ! apply_filters( 'app_enable_service_rte_content', false ) ) {
+ return;
+ }
+
+ wpb_add_action_footer( $this );
+
+ add_action( 'app_service_inline_edit_after_description', array( $this, 'inline_edit' ) );
+
+ add_filter( 'wp_editor_widget_content', 'wptexturize' );
+ add_filter( 'wp_editor_widget_content', 'convert_smilies' );
+ add_filter( 'wp_editor_widget_content', 'convert_chars' );
+ add_filter( 'wp_editor_widget_content', 'wpautop' );
+ add_filter( 'wp_editor_widget_content', 'shortcode_unautop' );
+ add_filter( 'wp_editor_widget_content', 'do_shortcode', 11 );
+ }
+
+ public function inline_edit( $service ) {
+ $id = $service->get_ID();
+ $content = wpb_get_service_meta( $id, 'rte_content' ) ?: '';
+
+ echo wpb_wrap_field( 'rte_content', __('Content', 'wp-base'),
+ '<textarea readonly rows="6" class="stripped-content" name="stripped_content" id="stripped_content_'.$id.'" >'. esc_textarea( strip_tags( $content ) ) .'</textarea>'.
+ '<script type="text/html" class="rte_content" id="content_'.$id.'">'. $content .'</script>'.
+ '<input type="hidden" name="rte_content_check" value="1" />'.
+ '<a class="app-open-editor button" data-id="content_'. $id.'" href="javascript:void(0)">'. __( 'View/Edit Content', 'wp-base' ) .'</a>',
+ __( 'HTML tags not displayed. To view in full, click View/Edit.', 'wp-base' ) );
+ }
+
+ /**
+ * From: WP Editor Widget
+ */
+ public function footer() {
+ ?>
+<script type="text/javascript">
+jQuery(document).ready(function ($) {
+
+ function stripHtmlKeepPTags(htmlString) {
+ // 1. Create a new DOMParser instance.
+ const parser = new DOMParser();
+ // 2. Parse the input string into a full HTML document object.
+ const doc = parser.parseFromString(htmlString, 'text/html');
+
+ // 3. Select all elements in the document body.
+ const allElements = doc.body.children;
+
+ // 4. Iterate over the elements and remove any that are not a 'P' tag.
+ for (let i = allElements.length - 1; i >= 0; i--) {
+ const element = allElements[i];
+ if (element.tagName.toLowerCase() !== 'p') {
+ // Replace the element with its plain text content.
+ element.parentNode.replaceChild(document.createTextNode(element.textContent || ''), element);
+ }
+ // Note: The textContent will be raw text, without any tags.
+ }
+
+ // 5. Return the modified body's innerHTML, which now only contains <p> tags and raw text.
+ return doc.body.innerHTML;
+ }
+
+ function stripHtml(html) {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(html, "text/html");
+ return doc.body.textContent || "";
+ }
+
+ var WpB_Editor_Widget = {
+
+ /**
+ * @var string
+ */
+ currentContentId: '',
+
+ /**
+ * @var string
+ */
+ currentEditorPage: '',
+
+ /**
+ * @var int
+ */
+ wpFullOverlayOriginalZIndex: 0,
+
+ init: function() {
+ var me = this;
+
+ $(document).on( "click", ".app-open-editor", function () {
+ var id = $(this).data("id");
+ me.showEditor(id);
+ });
+
+ $(document).on( "click", ".app-close-editor", function () {
+ me.hideEditor();
+ });
+
+ $(document).on( "click", ".app-save-editor", function () {
+ me.updateWidgetAndCloseEditor();
+ });
+
+ },
+
+ /**
+ * Show the editor
+ * @param string contentId
+ */
+ showEditor: function(contentId) {
+ var me = this;
+ $('#app-editor-widget-backdrop').show();
+ $('#app-editor-widget-container').show();
+
+ me.currentContentId = contentId;
+
+ me.setEditorContent(contentId);
+ },
+
+ /**
+ * Hide editor
+ */
+ hideEditor: function() {
+ var me = this;
+ $('#app-editor-widget-backdrop').hide();
+ $('#app-editor-widget-container').hide();
+ },
+
+ /**
+ * Set editor content
+ */
+ setEditorContent: function(contentId) {
+ var editor = tinyMCE.EditorManager.get('WpB_Editor_Widget');
+ var content = $('#'+ contentId).html();
+
+ if (typeof editor === "object" && editor !== null) {
+ editor.setContent(content);
+ }
+ $('#WpB_Editor_Widget').val(content);
+ },
+
+ /**
+ * Update widget and close the editor
+ */
+ updateWidgetAndCloseEditor: function() {
+ var me = this;
+ var editor = tinyMCE.EditorManager.get('WpB_Editor_Widget');
+ var content = "";
+ if ( editor === undefined || editor === null || editor.isHidden()) {
+ content = $('#WpB_Editor_Widget').val();
+ }
+ else {
+ content = editor.getContent();
+ }
+
+ $('#'+ this.currentContentId).html(content);
+
+ $('#stripped_'+ this.currentContentId).val( stripHtml(content) );
+
+ me.hideEditor();
+ }
+
+ };
+
+ WpB_Editor_Widget.init();
+
+});
+</script>
+<div id="app-editor-widget-container" style="display: none;">
+ <a class="app-close-editor close" href="javascript:void(0)" title="<?php esc_attr_e( 'Close', 'wp-base' ); ?>"><span class="icon"></span></a>
+ <div class="editor">
+ <?php
+ $settings = array(
+ 'textarea_rows' => 20,
+ 'tinymce'=>true
+ );
+ wp_editor( '', 'WpB_Editor_Widget', $settings );
+ ?>
+ <p>
+ <a class="app-save-editor button button-primary" href="javascript:void(0)"><?php _e( 'Submit and Close', 'wp-base' ); ?></a>
+ </p>
+ </div>
+</div>
+<div id="app-editor-widget-backdrop" style="display:none;"></div>
+<?php
+ }
+}
+
+ BASE('ServiceRTE')->add_hooks();
+}
No newline at end of file
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/services-list.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/services-list.php
@@ -62,6 +62,7 @@
public function __construct() {
$this->a = BASE();
$this->table = $this->a->services_table;
+ include_once WPBASE_PLUGIN_DIR . '/includes/admin/service-rte.php';
}
/**
@@ -1559,6 +1560,8 @@
'<input type="hidden" name="service_description_check" value="1"/>', $service->get_ID(), 'service_description', 'textarea' ),
__( 'Optional description text for the service.', 'wp-base' ) );
+ do_action( 'app_service_inline_edit_after_description', $service );
+
?></div><?php
if ( apply_filters( 'app_service_inline_edit_show_image', true, $service ) ) { ?>
@@ -1763,6 +1766,12 @@
$changed = true;
}
}
+
+ if ( ! empty( $_POST['rte_content_check'] ) ) {
+ if ( $service->update_rte_content( ! empty( $_POST['rte_content'] ) ? wp_kses_post( $_POST['rte_content'] ) : '' ) ) {
+ $changed = true;
+ }
+ }
if ( ! empty( $_POST['service_image_check'] ) ) {
if ( $service->update_image_url( ! empty( $_POST['service_image_url']) ? wpb_clean( trim( $_POST['service_image_url'] ) ) : '' ) ) {
@@ -2274,6 +2283,8 @@
owner_check: par.find("input[name='owner_check']").val(),
service_description: par.find("textarea[name='service_description']").val(),
service_description_check: par.find("input[name='service_description_check']").val(),
+ rte_content: par.find(".rte_content").html(),
+ rte_content_check: par.find("input[name='rte_content_check']").val(),
service_image_url: par.find("input[name='service_image_url']").val(),
service_image_id: par.find("input[name='service_image_id']").val(),
service_image_check: par.find("input[name='service_image_check']").val(),
--- a/wp-base-booking-of-appointments-services-and-events/includes/admin/transactions.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/admin/transactions.php
@@ -575,7 +575,7 @@
</select>
<?php
- if ( $loc_ids = wpb_front_admin_loc_ids() ) {
+ if ( ! wpb_is_client() && $loc_ids = wpb_front_admin_loc_ids() ) {
$add_class = $filt['location_id'] ? 'class="app-option-selected"' : '';
?>
<select name="app_location_id" <?php echo $add_class ?>>
@@ -593,7 +593,9 @@
<select name="app_service_id" <?php echo $add_class ?>>
<option value=""><?php _e('Filter by service','wp-base'); ?></option>
<?php
- foreach ( wpb_front_admin_services() as $service ) {
+ $services = wpb_is_client() ? wpb_client_services() : wpb_front_admin_services();
+
+ foreach ( $services as $service ) {
echo '<option '.selected( $filt['service_id'], $service->ID ).' value="'.$service->ID.'">'. $this->a->get_service_name( $service->ID ) .'</option>';
}
?>
--- a/wp-base-booking-of-appointments-services-and-events/includes/assets.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/assets.php
@@ -46,35 +46,35 @@
add_action( 'app_shortcode_found', array( $this, 'request_load' ) );
add_action( 'app_load_assets', array( $this, 'request_load' ) );
-
+
add_filter( 'do_shortcode_tag', array( $this, 'do_shortcode_tag' ), 10, 2 );
-
+
add_action( 'template_redirect', array( $this, 'template_redirect' ) );
}
-
+
/**
* Find post object for non-single posts
* Get cart values and remaining time before headers sent
*/
public function template_redirect() {
-
+
if ( is_author() ) {
global $wp_query;
$this->author_id = $wp_query->queried_object_id;
}
-
+
global $wp_the_query;
-
+
if ( ! is_callable( array( $wp_the_query, 'get_queried_object' ) ) ) {
return;
}
-
+
$current_object = $wp_the_query->get_queried_object();
-
+
if ( empty( $current_object ) ) {
return;
}
-
+
$this->current_post_id = ! empty( $current_object->ID ) ? $current_object->ID : 0;
if ( ! headers_sent() ) {
@@ -124,11 +124,6 @@
wp_register_script( 'jquery-app-qtip', WPB_PLUGIN_URL . '/js/dev/jquery.qtip.min.js', array('jquery'), $this->ver() );
wp_register_script( 'jquery-blockui', WPB_PLUGIN_URL . '/js/dev/jquery.blockUI.js', array('jquery'), $this->ver() );
wp_register_script( 'wp-base-countdown', WPB_PLUGIN_URL . '/js/dev/jquery.countdown.min.js', array('jquery','jquery-plugin'), $this->ver() );
- wp_register_script( 'jquery-datatables', WPB_PLUGIN_URL . '/js/dev/jquery.dataTables.min.js', array('jquery-ui-core'), $this->ver() );
- wp_register_script( 'jquery-datatables-jqueryui', WPB_PLUGIN_URL . '/js/dev/dataTables.jqueryui.min.js', array('jquery-datatables'), $this->ver() );
- wp_register_script( 'jquery-datatables-jqueryui-responsive', WPB_PLUGIN_URL . '/js/dev/responsive.jqueryui.min.js', array('jquery-datatables-jqueryui','jquery-datatables-responsive'), $this->ver() );
- wp_register_script( 'jquery-datatables-moment', WPB_PLUGIN_URL . '/js/dev/moment.min.js', array('jquery-datatables'), $this->ver() );
- wp_register_script( 'jquery-datatables-responsive', WPB_PLUGIN_URL . '/js/dev/dataTables.responsive.min.js', array('jquery-datatables'), $this->ver() );
wp_register_script( 'jquery-plugin', WPB_PLUGIN_URL . '/js/mobile/jquery.plugin.min.js', array('jquery'), $this->ver() );
wp_register_script( 'jquery-qtip', WPB_PLUGIN_URL . "/js/dev/jquery.qtip.min.js", array('jquery'), $this->ver() );
wp_register_script( 'jquery-quickfit', WPB_PLUGIN_URL . '/js/dev/jquery.quickfit.js', array('jquery'), $this->ver() );
@@ -138,40 +133,31 @@
wp_register_script( 'jstz', WPB_PLUGIN_URL . '/js/dev/jstz.min.js', array(), $this->ver() );
wp_register_script( 'app-event-calendar', WPB_PLUGIN_URL . '/js/dev/event-calendar.min.js', array('jquery'), $this->ver() );
wp_register_script( 'app-schedules', WPB_PLUGIN_URL . '/js/schedules.js', array('app-event-calendar'), $this->ver() );
- wp_register_style( 'app-schedules', WPB_PLUGIN_URL . '/css/schedules.css', array(), $this->ver() );
- wp_register_style( 'wp-base-admin-front', WPB_PLUGIN_URL . '/css/front-admin.css', array(), WPB_VERSION );
wp_register_script( 'wp-base-common-scripts', WPB_PLUGIN_URL . '/js/common-scripts.js', array('jquery-ui-core','jquery-ui-widget', 'jquery-ui-position'), $this->ver(), true );
wp_register_script( 'wp-base-libs', WPB_PLUGIN_URL . '/js/libs.js', array('jquery-ui-widget','jquery-ui-button','jquery-ui-datepicker'), $this->ver(), true );
+ wp_register_script( 'wp-base-datatables', WPB_PLUGIN_URL . '/js/app-datatables.js', array('jquery'), $this->ver(), true );
+
+ wp_register_style( 'app-schedules', WPB_PLUGIN_URL . '/css/schedules.css', array(), $this->ver() );
+ wp_register_style( 'wp-base-admin-front', WPB_PLUGIN_URL . '/css/front-admin.css', array(), $this->ver() );
+ wp_register_style( 'wp-base-panels', WPB_PLUGIN_URL . '/css/panels.css', array(), $this->ver() );
+ wp_register_style( 'wp-base-admin', WPB_PLUGIN_URL . '/css/admin.css', array(), $this->ver() );
+ wp_register_style( 'wp-base-stripe', WPB_PLUGIN_URL . '/css/stripe.css', array(), $this->ver() );
}
/**
- * Register moment locale for non English websites
+ * Register moment locale for non English websites - Deprecated
* @since 3.6.2
+ * @until 6.0.0
* @return string Handle of the script to be used as dependency
*/
private function register_moment_locale() {
- $locale = strtolower( $this->a->get_locale() );
- $locale_short = current( explode( '-', $locale ) );
-
- if ( $locale && 'en' != $locale && 'en-us' != $locale ) {
- foreach( array( $locale, $locale_short ) as $lcl ) {
- if ( file_exists( WPBASE_PLUGIN_DIR . '/js/locale/'.$lcl.'.js' ) ) {
-
- wp_register_script( 'jquery-moment-locale',
- WPB_PLUGIN_URL . '/js/locale/'.$lcl.'.js',
- (self::is_debug() ? array('jquery-datatables-moment') : array('wp-base-libs')),
- $this->ver()
- );
-
- return 'jquery-moment-locale';
- }
- }
- }
+ return;
}
/**
* Enqueue used jQuery effects
* @since 3.0
+ * @return none
*/
public function enqueue_effects(){
$effects = 'yes' == wpb_setting( 'ms_use_effect' ) ? array( 'blind', 'drop' ) : array( 'drop' );
@@ -189,6 +175,8 @@
$deps = array(
'wp-base-common-scripts',
+ 'wp-base-libs',
+ 'wp-base-datatables',
'codemirror',
'jquery-ui-button',
'jquery-ui-datepicker',
@@ -196,7 +184,7 @@
'jquery-ui-sortable',
'jquery-ui-tabs',
'wp-color-picker',
- 'wp-base-libs',
+ 'moment',
);
# FEBM may call on front end - these are only registered for admin, so we exclude them
@@ -211,34 +199,15 @@
* Front end script dependencies
*/
private function deps_front() {
- if ( self::is_debug() ) {
- return array(
- 'wp-base-common-scripts',
- 'jquery-blockui',
- 'wp-base-countdown',
- 'jquery-datatables-jqueryui',
- 'jquery-ui-button',
- 'isotope',
- 'jquery-datatables-jqueryui-responsive',
- 'jquery-datatables-moment',
- 'jquery-quickfit',
- 'jquery-scrollto',
- 'signature-pad',
- 'app-flexslider',
- 'jquery-effects-drop',
- 'jquery-ui-button',
- 'jquery-ui-dialog',
- 'jstz',
- );
- } else {
- return array(
- 'wp-base-common-scripts',
- 'wp-base-libs',
- 'jquery-effects-drop',
- 'jquery-ui-button',
- 'jquery-ui-dialog',
- );
- }
+ return array(
+ 'wp-base-common-scripts',
+ 'wp-base-libs',
+ 'wp-base-datatables',
+ 'jquery-effects-drop',
+ 'jquery-ui-button',
+ 'jquery-ui-dialog',
+ 'moment',
+ );
}
/**
@@ -246,7 +215,7 @@
*/
public function common_data() {
global $wp_locale;
-
+
return array(
'menuHeight' => 'size',
'checkAll' => wpb_get_text( 'check_all' ),
@@ -278,7 +247,7 @@
'decimal_sep' => wpb_decimal_separator(),
'done' => wpb_get_text( 'done' ),
'filterLabel' => '', // If left empty "Filter:"
- 'filterPholder' => '', // If left empty "Enter keywords"
+ 'filterPholder' => '', // If left empty "Enter keywords"
'iedit_nonce' => wp_create_nonce('inline_edit'),
'js_date_format' => wpb_convert_dt_format( $this->a->safe_date_format() ),
'list' => wpb_get_text( 'list' ),
@@ -335,10 +304,10 @@
* Admin data for javascript
*/
public function admin_data(){
-
+
$wh_starts = wpb_setting( 'wh_starts', '07:00' );
$wh_ends = wpb_setting( 'wh_ends', '18:00' );
-
+
return array(
'all_day' => wpb_get_text( 'all_day' ),
'checkin' => wpb_get_text( 'checkin_text' ),
@@ -346,6 +315,7 @@
'colorPresets' => wpb_get_preset(),
'confirmDeleteLog' => __( 'Are you sure to clear the log file?', 'wp-base' ),
'confirmEmpty' => __( 'You are about to delete at least one record. Are you sure to do this?', 'wp-base' ),
+ 'confirmPay' => __( 'Do you want to pay commission to the selected records?', 'wp-base' ),
'confirmReset' => __( 'WARNING!! This action will clear all existing database records (bookings, transactions, locations, services, service providers, working hours). Are you sure to do this?', 'wp-base' ),
'confirmResetAgain' => __( 'Are you REALLY SURE TO DELETE the database records?', 'wp-base' ),
'confirmRestore' => __( 'This action will restore all WP BASE settings to the defaults. Database records (bookings, transactions, locations, services, service providers, working hours) will not be changed. Are you sure to do this?', 'wp-base' ),
@@ -391,7 +361,8 @@
'showImages' => 'yes' == wpb_setting( 'schedule_show_images', 'yes' ), # Show worker images
'showCheckout' => 'yes' != wpb_setting( 'end_date_for_venue' ), # Show checkin/Checkout texts
'showTimeline' => wpb_is_admin_user() && BASE()->get_nof_workers(), # Whether show Timeline in Schedules
- 'editable' => wpb_is_admin_user() || (wpb_is_worker() && in_array( wpb_setting( 'allow_worker_edit' ), array('yes','only_schedules','also_admin') ) ), # Whether allow workers editing in Schedules
+ 'client' => wpb_is_client(),
+ 'editable' => wpb_is_admin_user() || (wpb_is_worker() && in_array( wpb_setting( 'allow_worker_edit' ), array('yes','only_schedules','also_admin') ) ) || (wpb_is_client() && 'yes' == wpb_setting('allow_edit')), # Whether allow workers editing in Schedules
'lockEnabled' => (bool)BASE()->get_nof_workers(), # Interlocking (Global)
'lockEnabledMS' => wpb_is_multi_store(), # Interlocking (Multi Store)
'useFilter' => false, # User filter for Multiselect
@@ -404,8 +375,8 @@
*/
public function front_data(){
global $current_screen, $wp_locale, $post;
-
- $post_id = ! empty( $this->current_post_id )
+
+ $post_id = ! empty( $this->current_post_id )
? $this->current_post_id
: (!empty( $post->ID ) ? $post->ID : 0);
@@ -546,10 +517,6 @@
$data = apply_filters( 'app_js_data', wpb_array_merge( $data, $this->front_data() ), 'front' );
}
- if ( $handle = $this->register_moment_locale() ) {
- $deps[] = $handle;
- }
-
wp_register_script( 'wp-base-admin-scripts', WPB_PLUGIN_URL . '/js/admin/admin-scripts.js', $deps, $this->ver() );
$this->localize( $data );
@@ -573,10 +540,6 @@
}
}
- if ( $handle = $this->register_moment_locale() ) {
- $deps[] = $handle;
- }
-
wp_register_script( 'wp-base-front-scripts', WPB_PLUGIN_URL . '/js/front-scripts.js', $deps, $this->ver(), true );
$this->localize( $data );
$this->enqueue_effects();
@@ -590,7 +553,7 @@
* Load common styles
*/
private function load_common_css() {
- wp_enqueue_style( 'wp-base-updating', WPB_PLUGIN_URL . '/css/updating.css', array(), $this->ver() );
+ wp_enqueue_style( 'wp-base-panels' );
wp_enqueue_style( 'jquery-ui-'.sanitize_file_name( $this->a->selected_theme() ), $this->a->get_theme_file(), array(), $this->ver() );
if ( WpBDebug::is_debug() ) {
@@ -602,7 +565,7 @@
wp_enqueue_style( 'jquery-datatables-responsive-ui', WPB_PLUGIN_URL . '/css/responsive.jqueryui.css', array(), $this->ver() );
wp_enqueue_style( 'jquery-datatables-jqueryui', WPB_PLUGIN_URL . '/css/dataTables.jqueryui.css', array(), $this->ver() );
} else {
- wp_enqueue_style( "wp-base-libs-min", WPB_PLUGIN_URL . "/css/libs.min.css", array(), $this->ver() );
+ wp_enqueue_style( 'wp-base-libs-min', WPB_PLUGIN_URL . '/css/libs.min.css', array(), $this->ver() );
}
if ( is_rtl() ) {
@@ -622,7 +585,7 @@
return;
}
- wp_enqueue_style( 'wp-base-admin', WPB_PLUGIN_URL . '/css/admin.css', array(), $this->ver() );
+ wp_enqueue_style( 'wp-base-admin' );
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_style( 'editor-buttons' ); // Fix for: wp_editor call does not load editor.min.css on emails page
@@ -653,7 +616,7 @@
do_action( 'app_styles_enqueued', $this );
}
-
+
/**
* Load assets for unsupported page builders or templates upon WP BASE shortcode usage
* @uses do_shortcode_tag filter hook
@@ -661,18 +624,18 @@
* @return string
*/
public function do_shortcode_tag( $output, $tag ) {
-
+
if ( ! did_action( 'app_scripts_enqueued' ) && in_array( $tag, wpb_shortcodes() ) ) {
$this->load_front();
$this->load_front_css();
-
- if ( 'app_account' == $tag || 'app_manage' == $tag ) {
+
+ if ( in_array( $tag, array( 'app_account', 'app_manage', 'app_list', 'app_store' ) ) ) {
wp_enqueue_style( 'wp-base-admin-front' );
$this->load_admin();
$this->load_admin_css();
}
}
-
+
return $output;
}
--- a/wp-base-booking-of-appointments-services-and-events/includes/class.controller.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/class.controller.php
@@ -52,9 +52,9 @@
* @param $force_priority string Change priority by instance, e.g. in admin inline edit
*/
public function __construct( $norm_or_booking, $order_by = "sort_order", $force_priority = false, $event = null ) {
-
+
$this->a = BASE();
-
+
if ( $norm_or_booking instanceof WpB_Booking ) {
$booking = $norm_or_booking;
$this->req_location = $booking->get_location();
@@ -84,7 +84,7 @@
$this->set_worker = $this->a->get_def_wid();
return;
}
-
+
if ( WPB_GCAL_SERVICE_ID == $this->req_service ) {
$this->set_service = $this->req_service;
$this->locations = array();
@@ -110,7 +110,7 @@
case self::WORKER: $this->adjust_worker(); break;
}
}
-
+
add_filter( 'app_update_cals_reply', array( $this, 'update_cals_reply' ) );
}
@@ -124,23 +124,23 @@
if ( empty( $_POST['active_step'] ) ) {
return $reply;
}
-
+
$prev = ! empty( $reply['booking_info'] ) ? $reply['booking_info'] : array();
-
+
# Image
if ( $img_url = wpb_get_service_meta( $this->get_service(), 'image_url' ) ) {
$slider_image = '<div class="app-cover" style="background-image: url('.$img_url.')"></div>';
} else {
$slider_image = '';
}
-
+
# Duration
$s = BASE()->get_service( $this->get_service() );
- $lasts = ! empty( $_REQUEST['app_duration'] )
- ? $_REQUEST['app_duration']
+ $lasts = ! empty( $_REQUEST['app_duration'] )
+ ? $_REQUEST['app_duration']
: (! empty( $s->duration ) ? $s->duration : BASE()->get_min_time());
-
- $reply['booking_info'] = array_merge( $prev, array(
+
+ $reply['booking_info'] = array_merge( $prev, array(
'service_id' => $this->get_service(),
'location' => $this->is_loc_active() && $this->get_location() ? wpb_booking_info_line_html( 'location', $this->a->get_location_name( $this->get_location() ) ) : '',
'service' => wpb_booking_info_line_html( 'service', $this->a->get_service_name( $this->get_service() ) ),
@@ -149,7 +149,7 @@
'image' => $slider_image,
'cart_contents' => ! wpb_is_hidden('details') && BASE('Multiple')->values() ? BASE('Multiple')->cart_contents_html( BASE('Multiple')->values() ) : '',
) );
-
+
return $reply;
}
@@ -294,9 +294,9 @@
* @return none
*/
private function set_random_worker( $ids ) {
-
+
shuffle( $ids );
-
+
if ( empty( $_POST['app_value'] ) && defined( 'WPB_CHECK_WORKER_AVAIL' ) && WPB_CHECK_WORKER_AVAIL ) {
$today = strtotime( 'today', $this->a->_time );
foreach ( $ids as $id ) {
@@ -505,22 +505,27 @@
/**
* Create HTML for location select element
+ * @param $ro bool Read Only, e.g not editable items
* @since 3.0
* @return string
*/
- public function select_location( ) {
+ public function select_location( $ro = false ) {
$html = '<select class="app-admin-lsw app_select_locations app-no-ms" data-lsw="location" name="location">';
-
+
$loc_ids = wpb_is_manage_store() ? wpb_managed_stores() : array_keys( (array)$this->locations );
foreach ( $loc_ids as $loc_id ) {
$sel = $this->set_location == $loc_id ? ' selected="selected"' : '';
+ if ( $ro && ! $sel ) {
+ continue;
+ }
+
$html .= '<option value="'.$loc_id.'"'.$sel.'>'. $this->a->get_location_name( $loc_id ) . '</option>';
}
if ( ! $loc_ids ) {
- $html .= '<option disabled="disabled">' . wpb_get_text('no_free_time_slots'). '</option>';
+ $html .= '<option disabled>' . wpb_get_text('no_free_time_slots'). '</option>';
}
$html .= '</select>';
@@ -530,22 +535,27 @@
/**
* Create HTML for service select element
+ * @param $ro bool Read Only, e.g not editable items
* @param $new bool Package selection is allowed for new booking
* @since 3.0
* @return string
*/
- public function select_service( $new = false ) {
+ public function select_service( $ro = false, $new = false ) {
$html = '<select class="app-admin-lsw app_select_services app-no-ms" data-lsw="service" name="service">';
foreach ( (array)$this->services as $service ) {
$sel = $this->set_service == $service->ID ? ' selected="selected"' : '';
- $disabled = ! $new && $this->a->is_package( $service->ID ) ? ' disabled="disabled"' : '';
+ $disabled = ! $new && $this->a->is_package( $service->ID ) ? ' disabled' : '';
+
+ if ( $ro && ! $sel ) {
+ continue;
+ }
- $html .= '<option value="'.$service->ID.'"'.$sel.$disabled.'>'. $this->a->get_service_name( $service->ID ) . '</option>';
+ $html .= '<option '.($ro && ! $sel ? 'disabled' : '').' value="'.$service->ID.'"'.$sel.$disabled.'>'. $this->a->get_service_name( $service->ID ) . '</option>';
}
if ( ! $this->services ) {
- $html .= '<option disabled="disabled">'. wpb_get_text('no_free_time_slots') .'</option>';
+ $html .= '<option disabled>'. wpb_get_text('no_free_time_slots') .'</option>';
}
$html .= '</select>';
@@ -555,10 +565,11 @@
/**
* Create HTML for worker select element
+ * @param $ro bool Read Only, e.g not editable items
* @since 3.0
* @return string
*/
- public function select_worker( ) {
+ public function select_worker( $ro = false ) {
$html = '<select class="app-admin-lsw app_select_workers app-no-ms" data-lsw="worker" name="worker">';
if ( $this->workers ) {
@@ -575,6 +586,10 @@
$sel = $this->set_worker == $worker->ID ? ' selected="selected"' : '';
+ if ( $ro && ! $sel ) {
+ continue;
+ }
+
$html .= '<option value="'.$worker->ID.'"'.$sel.'>'. $this->a->get_worker_name( $worker->ID ) .'</option>';
}
@@ -646,12 +661,12 @@
if ( $maybe_desc = wpb_get_location_meta( $ID, 'description' ) ) {
$desc = $maybe_desc;
$sub_context = 'description';
- }
+ }
} else if ( 'worker' == $context ) {
if ( $maybe_img = get_user_meta( $ID, 'app_profile_image', true ) ) {
$slider_image = '<img src="'.esc_attr( $maybe_img ).'" alt="User Image" />';
}
-
+
if ( $maybe_desc = get_user_meta( $ID, 'app_description', true ) ) {
$desc = $maybe_desc;
$sub_context = 'description';
--- a/wp-base-booking-of-appointments-services-and-events/includes/class.service.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/class.service.php
@@ -580,6 +580,24 @@
}
/**
+ * Get Rich Text Editor content
+ * @since 5.9.1
+ * @return string
+ */
+ public function get_rte_content() {
+ return wpb_get_service_meta( $this->ID, 'rte_content' );
+ }
+
+ /**
+ * Update Rich Text Editor content
+ * @since 5.9.1
+ * @return bool
+ */
+ public function update_rte_content( $desc ) {
+ return wpb_update_service_meta( $this->ID, 'rte_content', $desc );
+ }
+
+ /**
* Get Featured Image Url
* @return string
*/
--- a/wp-base-booking-of-appointments-services-and-events/includes/constant-data.php
+++ b/wp-base-booking-of-appointments-services-and-events/includes/constant-data.php
@@ -151,6 +151,13 @@
Yours sincerely,
SITE_NAME";
+ public static $_commission_paid_message = "Dear VENDOR,
+
+A payment of AMOUNT has been made to your account.
+
+For your information,
+SITE_NAME";
+
public static $_waiting_list_message = "Dear CLIENT,
We have received your appointment submission for SITE_NAME.
@@ -255,6 +262,10 @@
<div><em>NOTE</em></div>
<div><strong>STATUS</strong> · APP_ID</div>';
+ public static $_schedule_desc_client = '<div>SERVICE · PAX pax</div>
+<div>WORKER</div>
+<div><strong>STATUS</strong> · APP_ID</div>';
+
public static function privacy_content() { return __('We collect information about you during the checkout process on our website. This information may include, but is not limited to, your name, email address, phone number and any other details that might be requested from you for the purpose of processing your orders.
Handling this data also allows us to:
- Send you important account/order/service information.
@@ -302,6 +313,7 @@
$currency = wpb_format_currency();
$defaults = array(
+ 'account_page' => array( '', __('Account Page', 'wp-base'), __('The page where <code>[app_account]</code> resides. Normally WP BASE automatically locates this page and sets this setting, however in some cases you may need to set it yourself.', 'wp-base') ),
'additional_css' => array( '', __('Additional css Rules (Front end)', 'wp-base'), __('You can add css rules to customize styling. These will be added to the front end appointment page(s) only.', 'wp-base') ),
'additional_css_admin' => array( '', __('Additional css Rules (Admin side)', 'wp-base'), __('You can add css rules to customize styling. These will be added to the admin side only, e.g. to user profile page.', 'wp-base') ),
'additional_min_time' => array( '', __('Additional Time Base (minutes)', 'wp-base'), __('If selectable time bases do not fit your business, you can add a new one, e.g. 90. Note: 1) After you save this additional time base, you must select it using the Time Base setting. 2) Minimum allowed time base setting is 5 minutes. 3) Entered value should be divisible by 5. For example, 24 is not allowed and it will be rounded to 25.', 'wp-base' ) ),
@@ -315,12 +327,11 @@
'agora_api_id' => array( '', __('App ID','wp-base'), sprintf( __('Get this value from your %s.', 'wp-base'), '<a class="app-btm" href="https://console.agora.io/" target="_blank">'.__('Agora account').'</a>' ), 'Online Meetings' ),
'agora_cert' => array( '', __('App Certificate','wp-base'), sprintf( __('Primary certificate from your %s.', 'wp-base'), '<a class="app-btm" href="https://console.agora.io/" target="_blank">'.__('Agora account').'</a>' ), 'Online Meetings' ),
'agora_enable' => array( 'yes', __('Enable Agora','wp-base'), __('Enables integration with Agora Online Meetings.', 'wp-base'), 'Online Meetings' ),
- 'agora_layout' => array( 0, __('Layout','wp-base'), __('Layout of the participant screens', 'wp-base'), 'Online Meetings' ),
'agora_subject' => array( 'SITE_NAME SERVICE Meeting', __('Agora Subject','wp-base'), sprintf( __('Meeting subject (title). <abbr title="%s">Booking placeholders</abbr> can be used. During meeting creation, these placeholders will be replaced by their real values.', 'wp-base'), WpBConstant::email_desc(1) ), 'Online Meetings' ),
'allow_cancel' => array( 'no', __('Allow Client Cancel Own Bookings', 'wp-base'), __('Whether to allow clients cancel their bookings using the link in confirmation and reminder emails or using Booking List table or for logged in users, using check boxes in their profile pages. For the email case, you will also need to add CANCEL placeholder to the email message content.', 'wp-base' ) ),
'allow_client_set_tz' => array( 'no', __('Allow Clients Select Own Timezone', 'wp-base'), __('If selected as "Yes", clients can manually select their timezone in their profile page. This setting overrides automatic dedection.', 'wp-base') ),
'allow_confirm' => array( 'yes', __('Allow Client Confirm Bookings by Email', 'wp-base'), __('Whether to allow clients confirm their bookings using the link in any email they receive. This link is added by using CONFIRM placeholder in email bodies.', 'wp-base' ) ),
- 'allow_edit' => array( 'no', __('Allow Client Edit Own Bookings','wp-base'), __('Whether you let client edit own appointments on the front end. Client can activate editing popup form by one of the following methods: 1) Clicking Edit button in WordPress user page, 2) Clicking Edit button in List Of Bookings, 4) Clicking Edit button in Bookings tab of Account page, 4) Clicking the link in emails. This link is created by inserting EDIT placeholder to the email body.','wp-base'),'Front End Edit' ),
+ 'allow_edit' => array( 'no', __('Allow Client Edit Own Bookings','wp-base'), __('Whether you let client edit own bookings on List of Bookings or Schedules tab of Account page.','wp-base') ),
'allow_now' => array( 'no', __('Allow Late Booking', 'wp-base'), __('Setting this as Yes will allow booking of a time slot when current time is within selected time slot, i.e. appointment start time has passed, but it has not ended yet.', 'wp-base' ) ),
'allow_register' => array( 'auto', __('Allow Registration at Checkout', 'wp-base'), __('Whether add registration fields at checkout. "Auto" follows WordPress "Anyone can register" setting.', 'wp-base' ) ),
'allow_worker_annual' => array( 'no', __('Set Own Seasonal Schedules', 'wp-base'), __('Requires Seasonal Working Hours Addon. Whether you let service providers to set their annual schedules using their navigation tab in BuddyPress (Requires BuddyPress addon) or their profile page in regular WordPress. They are also allowed to add new custom schedules, but not to delete them.', 'wp-base'), 'Service Providers' ),
@@ -406,9 +417,9 @@
'disable_tooltips' => array( 'no', __('Disable Tooltips in Booking Calendars', 'wp-base'), __('Selecting "No" will disable tooltips like "Click to pick date", etc. Note: In Debug mode, tooltips are displayed.', 'wp-base' ) ),
'dp_reminder_attach' => array( 'no', __('Create and Attach pdf File', 'wp-base'), __('Whether to attach a pdf file that will be created from the below fields. If attachment field is empty, file will not be attached (empty file will not be sent).', 'wp-base'), 'PDF' ),
'dp_reminder_limit' => array( '', sprintf( __('Due Payment Reminder Sending Limit of Balance (%s)', 'wp-base'), BASE()->get_options('currency', 'USD') ), __('Due payment reminder is only sent if balance is negative and absolute value of balance for the appointment is greater than this amount. For example, if this value is set as 10$, an appointment with -9$ balance will not result to a reminder email, but -11$ will. Leave empty if you want to remind client in case of any negative balance.', 'wp-base'), 'Reminder and Follow-up emails' ),
- 'dp_reminder_message' => array( '', __('Due Payment Reminder email Message', 'wp-base'), '', 'Reminder and Follow-up emails' ),
+ 'dp_reminder_message' => array( '', __('Due Payment Reminder Email Message', 'wp-base'), '', 'Reminder and Follow-up emails' ),
'dp_reminder_statuses' => array( 'paid,confirmed,completed', __('Booking Statuses Due Payment emails Applied to', 'wp-base'), __('Only clients having appointments with selected status(es) will receive due payment reminder email. If none selected, due payment emails will not be sent at all.', 'wp-base'), 'Reminder and Follow-up emails' ),
- 'dp_reminder_subject' => array( '', __('Due Payment Reminder email Subject', 'wp-base'), '', 'Reminder and Follow-up emails' ),
+ 'dp_reminder_subject' => array( '', __('Due Payment Reminder Email Subject', 'wp-base'), '', 'Reminder and Follow-up emails' ),
'dp_reminder_time' => array( '72,48', __('Due Payment Reminder email Sending Time (hours)', 'wp-base'), __('Defines the time in hours that reminder email will be sent after the appointment has been booked (creation time). Note that this is different than appointment reminder email where appointment start time is taken as reference. Multiple reminders are possible. To do so, enter reminding hours separated with a comma, e.g. 48,72.', 'wp-base'), 'Reminder and Follow-up emails' ),
'dummy_assigned_to' => array( 0, __('Assign Dummy Providers to', 'wp-base'), __('You can define "Dummy" service providers to enrich your service provider alternatives and variate your working schedules. Their availability and other properties will be exactly like ordinary providers except the emails they are supposed to receive will be forwarded to the user you select here.', 'wp-base'), 'Service Providers' ),
'duration_format' => array( 'hours_minutes', __('Service Duration Display Format', 'wp-base'), __('With this setting, you can select display format of durations on the front end (minutes, hours, hours+minutes).', 'wp-base' ) ),
@@ -417,10 +428,10 @@
'edd_price_name' => array( 'From: DATE_TIME To: END_DATE_TIME', __('Booking Info','wp-base'), sprintf( __('Short information about the booking, for example date, time, provider. All <abbr title="%s">booking placeholders</abbr> can be used.','wp-base'), WpBConstant::email_desc(1) ), 'EDD' ),
'edd_product_meta' => array( 'PRODUCT_LINK', __('Booking Details','wp-base'), sprintf( __('Details of booking that will be added below product name. All <abbr title="%s">booking placeholders</abbr> can be used.','wp-base'), WpBConstant::email_desc(1) ), 'EDD' ),
'edd_product_name' => array( 'SERVICE Booking', __('Booking Title','wp-base'), sprintf( __('Defines how the selected booking will be displayed in the cart and receipt. All <abbr title="%s">booking placeholders</abbr> can be used.','wp-base'), WpBConstant::email_desc(1) ), 'EDD' ),
- 'editable' => array( $editable, __('Editable Booking Fields','wp-base'), sprintf( __('Select which booking fields can be edited. Note: UDF fields can be limited using "Editable" column on %s page. ','wp-base'), '<a href="'.admin_url('admin.php?page=app_display&tab=udf').'" target="_blank">'.__('UDF settings','wp-base').'</a>' ),'Front End Edit' ),
- 'edit_change_price' => array( 'yes', __('Allow Price Display and Change'), __('Whether change in selections will affect price.','wp-base'),'Front End Edit' ),
- 'edit_limit' => array( '', __('Editing Lower Limit (hours)','wp-base'), __('Number of hours from appointment start time until which client can edit their appointment. For example, entering 24 will disable editing one day before the appointment is due. In such a case any editing request will be replied with "Too late" response. Note: Admins and those who have given editing capability with "cap" attribute are not limited with this setting.','wp-base'),'Front End Edit' ),
- 'edit_upper_limit' => array( 60, __('Editing Upper Limit (days)','wp-base'), __('Only bookings whose start date is earlier than this setting can be edited. If left empty, global Upper Limit will be used.','wp-base'),'Front End Edit' ),
+ 'editable' => array( $editable, __('Editable Booking Fields','wp-base'), sprintf( __('Select which booking fields can be edited. Note: UDF fields can be limited using "Editable" column on %s page. ','wp-base'), '<a href="'.admin_url('admin.php?page=app_display&tab=udf').'" target="_blank">'.__('UDF settings','wp-base').'</a>' ) ),
+ 'edit_change_price' => array( 'yes', __('Allow Price Display and Change'), __('Whether change in selections will affect price.','wp-base') ),
+ 'edit_limit' => array( '', __('Editing Lower Limit (hours)','wp-base'), __('Number of hours from appointment start time until which client can edit their appointment. For example, entering 24 will disable editing one day before the appointment is due. In such a case any editing request will be replied with "Too late" response. Note: Admins and those who have given editing capability with "cap" attribute are not limited with this setting.','wp-base') ),
+ 'edit_upper_limit' => array( 60, __('Editing Upper Limit (days)','wp-base'), __('Only bookings whose start date is earlier than this setting can be edited. If left empty, global Upper Limit will be used.','wp-base') ),
'enable_timezones' => array( 'no', __('Enable Timezones', 'wp-base'), __('If selected as "Yes", timezone of the client is taken into account during display of booking UI's, list of bookings and emails. Admin side and database records are not affected.', 'wp-base'), 'Advanced Features' ),
'end_date_for_venue' => array( 'no', __('Show End Date Based on Venue Bookings', 'wp-base'), __('For bookings that last one day and longer, whether display end date according to venue bookings. By default (Setting "No"), end date is displayed based on <i>nightly room bookings</i> and it shows the <i>checkout</i> date which is one day past the actual end timestamp. If you select "Yes", end date will be shown based on <i>daytime venue bookings</i> and there will not be an offset for checkout. Note: This selection does not affect how booking is saved to the database, but just how it is displayed on the front end.', 'wp-base' ) ),
'ep_if_several' => array( 'min', __('Price to Apply upon Multiple Rule Match', 'wp-base'), __('If there are several matching rules, price returned can be selected among minimum, maximum or average of the non-zero prices calculated by matching rules.', 'wp-base'), 'Custom Pricing' ),
@@ -522,7 +533,10 @@
'mv_client_approval_time' => array( 3, __('Auto Approval Time (days)', 'wp-base'), __('After this time, even if client did not approve, completed bookings will be automatically approved. Leaving empty means 3 days.', 'wp-base'), 'Marketplace' ),
'mv_commission_if_several' => array( 'max', __('Commission to Apply upon Multiple Role Match', 'wp-base'), __('If vendor has several matching roles, commission rate to be applied can be selected among minimum or maximum of the commissions of matching roles.', 'wp-base'), 'Marketplace' ),
'mv_commission_nof_rates' => array( 0, __('Number of Commission Rates', 'wp-base'), __('You can define commission percentage based on WordPress user role of the vendor, e.g. to give better commission to certain membership levels. If you will use them, select number of commission rates.', 'wp-base'), 'Marketplace' ),
+ 'mv_commission_payment' => array( 'manual', __('Commission Payment Method', 'wp-base'), __('Commissions can be paid to vendors automatically when booking is completed or booking is approved by the client or manually at any desired time. Note: When automatic payment is selected, you can also pay manually on Commissions page.', 'wp-base'), 'Marketplace' ),
'mv_commission_rate' => array( '60', __('Commission Rate (%)', 'wp-base'), __('Percentage of the booking revenue that will be received by the vendor.', 'wp-base'), 'Marketplace' ),
+ 'mv_enable_approved' => array( 'no', __('Enable Approved Status', 'wp-base'), __('Whether to use Approved status.', 'wp-base'), 'Marketplace' ),
+ 'mv_enable_stripe_connect' => array( 'no', __('Enable Stripe Connect', 'wp-base'), __('Whether to use Stripe Connect to pay commissions to vendors.', 'wp-base'), 'Marketplace' ),
'mv_fees_paid_by' => array( 'website', __('Fees Covered By', 'wp-base'), __('Who will cover the transaction fees. If covered by vendor, fees are deducted from client payment and vendor's earning will be lower.', 'wp-base'), 'Marketplace' ),
'mv_give_commission_own_sales' => array( 'no', __('Give Commission for Own Sales', 'wp-base'), __('Whether to give commission if client is the vendor themselves.', 'wp-base'), 'Marketplace' ),
'mv_hide_non_vendor_calendar' => array( 'yes', __('Hide Non-Vendor Booking Calendars', 'wp-base'), __('Whether to hide booking calendars on pages of non-vendors. Setting this to "Yes" will mean: For the above bio page CPTs, booking is only allowed on approved vendor bio pages.', 'wp-base'), 'Marketplace' ),
@@ -544,7 +558,6 @@
'mv_store_post_content' => array( '', __('Default Page Content', 'wp-base'), __('Content of the store page, typically including <code>[app_book]</code> shortcode in order to let clients book their services. Only services of this store are selectable from this shortcode.', 'wp-base'), 'Marketplace' ),
'mv_store_post_title' => array( 'COMPANY_NAME', __('Default Page Title', 'wp-base'), __('Title of the store page. Placeholders COMPANY_NAME and VENDOR_NAME will be replaced by vendor data.', 'wp-base'), 'Marketplace' ),
'mv_store_post_type' => array( 'post', __('Post Type for Store Pages', 'wp-base'), __('Post type that will be used for stores. A CPT is recommended, e.g. Place', 'wp-base' ), 'Marketplace' ),
- 'mv_enable_approved' => array( 'no', __('Enable Approved Status', 'wp-base'), __('Whether to use Approved status.', 'wp-base'), 'Marketplace' ),
'mv_um_account_page' => array( '', __('Account Page (only for UM)', 'wp-base'), __('UM Account Page may have a Bookings item to redirect to WP BASE Account page. The page which includes <code>[app_account]</code> should be selected here.', 'wp-base'), 'Marketplace' ),
'mv_use_wc_cart' => array( 'no', __('Use WooCommerce Cart for Payment', 'wp-base'), sprintf( __('When you enable WooCommerce cart, payments will be handled with WooCommerce checkout system even if bio page is not a WooCommerce product CPT. WooCommerce plugin and WP BASE WooCommerce Integration addon must be activated. Also see %s.', 'wp-base'), '<a href="'.admin_url( 'admin.php?page=app_settings&tab=advanced#woocommerce' ).'">'.__( 'WooCommerce settings', 'wp-base' ).'</a>'), 'Marketplace' ),
'mv_usership' => array( '', __('User Profile Plugin Integration', 'wp-base'), __('Select the user profile plugin you want to integrate from the list. With this integration, vendor can be directly booked from profile page of the user profile plugin.', 'wp-base'), 'Marketplace' ),
@@ -570,9 +583,13 @@
'reminder_time_sms_worker' => array( '4', __('Reminder SMS Sending Time for the Provider (hours)', 'wp-base'), __('Same as Reminder SMS Sending Time for the Client, but defines the time for service provider.', 'wp-base'), 'SMS' ),
'reminder_time_worker' => array( '4', __('Reminder email Sending Time for the Provider (hours)', 'wp-base'), __('Same as Reminder email Sending Time for the Client, but defines the time for service provider.', 'wp-base'), 'Reminder and Follow-up emails' ),
'reverse_log' => array( 'yes', __('Reverse Log', 'wp-base'), sprintf( __('Select "Yes" to reverse the display order of records in %s, from newest to oldest.', 'wp-base' ), '<a class="app-btm" href="'.admin_url("admin.php?page=app_tools&tab=log").'">'.__('log file').'</a>' ) ),
+ 'schedule_allowed_stats_client' => array( 'paid,confirmed,pending,