PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
← All changes | includes/page-appointment-services/appointment_services__functions.php +33 -276 11.5 → 11.9 View file →
@@ -103,172 +103,8 @@
103 103 return current_user_can( $availability_role );
104 104 }
105 105
106 106 /**
107 - * Render the compact Monday-to-Sunday labels in the Service listing header.
108 - *
109 - * @param array<string,mixed> $column Normalized shared listing column settings.
110 - * @param WPBC_UI_Listing $listing Shared listing instance.
111 - *
112 - * @return void
113 - */
114 -function wpbc_appointment_services_render_weekday_listing_header( $column, $listing ) {
115 - unset( $column, $listing );
116 - ?>
117 - <span><?php esc_html_e( 'Weekly Availability', 'booking' ); ?></span>
118 - <span class="wpbc_appointment_services__weekday_labels" aria-hidden="true">
119 - <i><?php esc_html_e( 'Mon', 'booking' ); ?></i>
120 - <i><?php esc_html_e( 'Tue', 'booking' ); ?></i>
121 - <i><?php esc_html_e( 'Wed', 'booking' ); ?></i>
122 - <i><?php esc_html_e( 'Thu', 'booking' ); ?></i>
123 - <i><?php esc_html_e( 'Fri', 'booking' ); ?></i>
124 - <i><?php esc_html_e( 'Sat', 'booking' ); ?></i>
125 - <i><?php esc_html_e( 'Sun', 'booking' ); ?></i>
126 - </span>
127 - <?php
128 -}
129 -
130 -/**
131 - * Render an accessible-only title for the Service row-actions column.
132 - *
133 - * @param array<string,mixed> $column Normalized shared listing column settings.
134 - * @param WPBC_UI_Listing $listing Shared listing instance.
135 - *
136 - * @return void
137 - */
138 -function wpbc_appointment_services_render_actions_listing_header( $column, $listing ) {
139 - unset( $column, $listing );
140 - ?><span class="screen-reader-text"><?php esc_html_e( 'Actions', 'booking' ); ?></span><?php
141 -}
142 -
143 -/**
144 - * Render Status and Service ID sorting controls in one listing column.
145 - *
146 - * Both sort keys use the shared listing request and AJAX controller. Combining
147 - * their controls keeps Service ID visible without dedicating another column.
148 - *
149 - * @param array<string,mixed> $column Normalized shared listing column settings.
150 - * @param WPBC_UI_Listing $listing Shared listing instance.
151 - *
152 - * @return void
153 - */
154 -function wpbc_appointment_services_render_status_listing_header( $column, $listing ) {
155 - unset( $column );
156 - $current_sorting = $listing->get_sorting_request();
157 - $sort_controls = array(
158 - 'status' => array(
159 - 'label' => __( 'Status', 'booking' ),
160 - 'class' => 'wpbc_appointment_services__status_sort',
161 - ),
162 - 'service_id' => array(
163 - 'label' => __( 'ID', 'booking' ),
164 - 'class' => 'wpbc_appointment_services__id_sort',
165 - ),
166 - );
167 - ?>
168 - <div class="wpbc_appointment_services__status_header">
169 - <?php foreach ( $sort_controls as $sort_key => $sort_control ) : ?>
170 - <?php
171 - $is_active = $sort_key === $current_sorting['sort_by'];
172 - $link_class = 'wpbc_ui_listing__sort_link ' . $sort_control['class'] . ( $is_active ? ' is-active' : '' );
173 - $icon_class = $is_active
174 - ? ( 'desc' === $current_sorting['sort_order'] ? 'wpbc-bi-arrow-down' : 'wpbc-bi-arrow-up' )
175 - : 'wpbc_icn_import_export';
176 - ?>
177 - <a href="#"
178 - class="<?php echo esc_attr( $link_class ); ?>"
179 - data-wpbc-listing-sort="<?php echo esc_attr( $listing->get_listing_id() ); ?>"
180 - data-wpbc-listing-sort-key="<?php echo esc_attr( $sort_key ); ?>">
181 - <span><?php echo esc_html( $sort_control['label'] ); ?></span>
182 - <i class="wpbc_ui_listing__sort_icon <?php echo esc_attr( $icon_class ); ?>" aria-hidden="true"></i>
183 - </a>
184 - <?php endforeach; ?>
185 - </div>
186 - <?php
187 -}
188 -
189 -/**
190 - * Return the shared listing configured for the Services catalog.
191 - *
192 - * The same instance is used by the page renderer and AJAX endpoint so the
193 - * displayed and persisted page sizes always follow one allow-list.
194 - *
195 - * @return WPBC_UI_Listing Services catalog listing component.
196 - */
197 -function wpbc_appointment_services_get_catalog_listing() {
198 - static $service_listing = null;
199 -
200 - if ( null === $service_listing ) {
201 - $columns = array(
202 - 'service' => array(
203 - 'label' => __( 'Service', 'booking' ),
204 - 'class' => 'column-service',
205 - 'sortable' => 'title',
206 - ),
207 - 'duration' => array(
208 - 'label' => __( 'Duration', 'booking' ),
209 - 'class' => 'column-duration',
210 - 'sortable' => 'duration',
211 - ),
212 - );
213 - if ( wpbc_appointment_services_is_pricing_available() ) {
214 - $columns['price'] = array(
215 - 'label' => __( 'Price', 'booking' ),
216 - 'class' => 'column-price',
217 - 'sortable' => 'price',
218 - );
219 - }
220 - $columns += array(
221 - 'providers' => array(
222 - 'label' => __( 'Providers', 'booking' ),
223 - 'class' => 'column-providers',
224 - ),
225 - 'weekdays' => array(
226 - 'class' => 'column-weekdays',
227 - 'header_callback' => 'wpbc_appointment_services_render_weekday_listing_header',
228 - ),
229 - 'status' => array(
230 - 'label' => __( 'Status', 'booking' ),
231 - 'class' => 'column-status',
232 - 'sortable' => 'status',
233 - 'header_callback' => 'wpbc_appointment_services_render_status_listing_header',
234 - ),
235 - 'actions' => array(
236 - 'class' => 'column-actions',
237 - 'header_callback' => 'wpbc_appointment_services_render_actions_listing_header',
238 - ),
239 - );
240 -
241 - $service_listing = new WPBC_UI_Listing(
242 - 'appointment_services_catalog',
243 - array(
244 - 'aria_label' => __( 'Services catalog', 'booking' ),
245 - 'items_per_page_default' => 10,
246 - 'items_per_page_options' => array( 5, 10, 50, 100 ),
247 - 'sort_by' => 'service_id',
248 - 'sort_order' => 'desc',
249 - 'sort_keys' => array( 'service_id' ),
250 - 'columns' => $columns,
251 - 'classes' => array(
252 - 'container' => 'wpbc_appointment_services__list',
253 - 'table_wrap' => 'wpbc_appointment_services__table_wrap',
254 - 'table' => 'wpbc_appointment_services__table',
255 - 'footer' => 'wpbc_appointment_services__list_footer',
256 - 'result_count' => 'wpbc_appointment_services__result_count',
257 - 'items_per_page' => 'wpbc_appointment_services__items_per_page',
258 - 'pagination' => 'wpbc_appointment_services__pagination',
259 - 'previous' => 'wpbc_appointment_services__page_prev',
260 - 'page_label' => 'wpbc_appointment_services__page_label',
261 - 'next' => 'wpbc_appointment_services__page_next',
262 - ),
263 - )
264 - );
265 - }
266 -
267 - return $service_listing;
268 -}
269 -
270 -/**
271 107 * Resolve the MultiUser-aware owner for Service queries and writes.
272 108 *
273 109 * @return int Owner user ID, or zero for site-wide ownership.
274 110 */
@@ -405,12 +241,16 @@
405 241
406 242 /**
407 243 * Resolve the public image configured for one Provider Booking Resource.
408 244 *
409 - * Provider images are stored by the Business Large Searchable Resources
410 - * module. Callers pass that module's option collection so it is loaded only
411 - * once when several Providers are rendered. Other editions pass an empty
412 - * collection and retain their existing avatar or initials fallback.
245 + * Existing Business Large Searchable Resources values remain the fast path
246 + * because callers already load that option collection once for a Provider
247 + * list. When that legacy field is not configured, the resolver uses the
248 + * cross-edition Booking Resource content repository so Free's bundled default
249 + * image and pictures saved by the Resource catalog reach every Provider UI.
250 + * The guarded fallback keeps this presentation helper safe during partial
251 + * bootstrap and compatibility requests where the content repository has not
252 + * been loaded yet.
413 253 *
414 254 * @param int $provider_id Provider Booking Resource ID.
415 255 * @param array<int,array<string,mixed>> $search_options Searchable Resource options keyed by resource ID.
416 256 *
@@ -417,21 +257,39 @@
417 257 * @return string Sanitized Provider image URL or an empty string.
418 258 */
419 259 function wpbc_appointment_services_get_provider_image_url( $provider_id, $search_options ) {
420 260 $provider_id = absint( $provider_id );
421 - if ( ! $provider_id || empty( $search_options[ $provider_id ]['picture'] ) ) {
261 + if ( ! $provider_id ) {
422 262 return '';
423 263 }
424 264
425 - $image_value = $search_options[ $provider_id ]['picture'];
426 - if ( is_array( $image_value ) ) {
427 - $image_value = reset( $image_value );
265 + if ( ! empty( $search_options[ $provider_id ]['picture'] ) ) {
266 + $image_value = $search_options[ $provider_id ]['picture'];
267 + if ( is_array( $image_value ) ) {
268 + $image_value = reset( $image_value );
269 + }
270 + if ( ! is_scalar( $image_value ) ) {
271 + return '';
272 + }
273 +
274 + return esc_url_raw( wpbc_lang( (string) $image_value ) );
428 275 }
429 - if ( ! is_scalar( $image_value ) ) {
276 +
277 + if ( ! function_exists( 'wpbc_booking_resource_content_repository' ) ) {
430 278 return '';
431 279 }
432 280
433 - return esc_url_raw( wpbc_lang( (string) $image_value ) );
281 + // Booking Calendar Free has exactly one implicit Booking Resource (ID 1).
282 + if ( ! class_exists( 'wpdev_bk_personal' ) && 1 !== $provider_id ) {
283 + return '';
284 + }
285 +
286 + $resource_content = wpbc_booking_resource_content_repository()->get( $provider_id );
287 + if ( empty( $resource_content['picture_url'] ) || ! is_scalar( $resource_content['picture_url'] ) ) {
288 + return '';
289 + }
290 +
291 + return esc_url_raw( wpbc_lang( (string) $resource_content['picture_url'] ) );
434 292 }
435 293
436 294 /**
437 295 * Provider presentation data for the Services management table.
@@ -437,9 +295,9 @@
437 295 * Provider presentation data for the Services management table.
438 296 *
439 297 * Booking resources remain the availability authority. The default weekday
440 298 * map combines General Availability with each resource's effective Working
441 - * Time rule. Business Large Searchable Resource pictures are reused before a
299 + * Time rule. Cross-edition Booking Resource pictures are reused before a
442 300 * WordPress user avatar, keeping the administration catalog consistent with
443 301 * the public Appointment flow. Extensions may replace the presentation or
444 302 * recurring-weekday summary through the filters below without changing the
445 303 * Services page contract.
@@ -676,109 +534,8 @@
676 534 );
677 535 $counts['all'] = $counts['active'] + $counts['inactive'] + $counts['archived'];
678 536
679 537 return $counts;
680 -}
681 -
682 -/**
683 - * Load one exact page of Services through the shared listing contract.
684 - *
685 - * Providers implementing count_items() receive the efficient contract: one
686 - * grouped count followed by one LIMIT/OFFSET page query. Existing replacement
687 - * providers remain compatible through a bounded in-memory pagination fallback,
688 - * but should implement count_items() and honor list_items() limit/offset values
689 - * before serving large catalogs.
690 - *
691 - * @param object $provider Service data provider.
692 - * @param array $query Search, status, Provider, page, and page-size values.
693 - * @param WPBC_UI_Listing $listing Shared listing instance.
694 - *
695 - * @return array<string,mixed>|WP_Error Normalized Services, counts, and pagination metadata.
696 - */
697 -function wpbc_appointment_services_get_catalog_page( $provider, $query, $listing ) {
698 - $query = wp_parse_args(
699 - is_array( $query ) ? $query : array(),
700 - array(
701 - 'search' => '',
702 - 'status' => 'all',
703 - 'resource_id' => 0,
704 - 'page_number' => 1,
705 - 'items_per_page' => $listing->get_items_per_page(),
706 - 'sort_by' => null,
707 - 'sort_order' => null,
708 - )
709 - );
710 - $status = in_array( $query['status'], array( 'all', 'active', 'inactive', 'archived' ), true ) ? $query['status'] : 'all';
711 - $sorting = $listing->get_sorting_request( $query['sort_by'], $query['sort_order'] );
712 - $query['sort_by'] = $sorting['sort_by'];
713 - $query['sort_order'] = $sorting['sort_order'];
714 -
715 - if ( method_exists( $provider, 'count_items' ) ) {
716 - $raw_counts = $provider->count_items( $query );
717 - if ( is_wp_error( $raw_counts ) ) {
718 - return $raw_counts;
719 - }
720 -
721 - $counts = wpbc_appointment_services_normalize_status_counts( $raw_counts );
722 - $total_items = 'all' === $status ? $counts['all'] : $counts[ $status ];
723 - $pagination = $listing->get_pagination_data( $total_items, $query['page_number'], $query['items_per_page'] );
724 - $page_query = array_merge(
725 - $query,
726 - array(
727 - 'status' => $status,
728 - 'limit' => $pagination['limit'],
729 - 'offset' => $pagination['offset'],
730 - )
731 - );
732 - $services = $provider->list_items( $page_query );
733 - if ( is_wp_error( $services ) ) {
734 - return $services;
735 - }
736 -
737 - $services = array_slice( (array) $services, 0, $pagination['limit'] );
738 - } else {
739 - $services = $provider->list_items(
740 - array_merge(
741 - $query,
742 - array( 'status' => 'all' )
743 - )
744 - );
745 - if ( is_wp_error( $services ) ) {
746 - return $services;
747 - }
748 -
749 - $all_services = array_map( 'wpbc_appointment_services_normalize_item', (array) $services );
750 - $counts = array(
751 - 'all' => count( $all_services ),
752 - 'active' => 0,
753 - 'inactive' => 0,
754 - 'archived' => 0,
755 - );
756 - foreach ( $all_services as $service ) {
757 - if ( isset( $counts[ $service['status'] ] ) ) {
758 - ++$counts[ $service['status'] ];
759 - }
760 - }
761 - $services = 'all' === $status
762 - ? $all_services
763 - : array_values(
764 - array_filter(
765 - $all_services,
766 - static function ( $service ) use ( $status ) {
767 - return $status === $service['status'];
768 - }
769 - )
770 - );
771 - $pagination = $listing->get_pagination_data( count( $services ), $query['page_number'], $query['items_per_page'] );
772 - $services = array_slice( $services, $pagination['offset'], $pagination['limit'] );
773 - }
774 -
775 - return array(
776 - 'services' => array_map( 'wpbc_appointment_services_normalize_item', (array) $services ),
777 - 'counts' => $counts,
778 - 'pagination' => $pagination,
779 - 'sorting' => $sorting,
780 - );
781 538 }
782 539
783 540 /**
784 541 * Authorize an Appointment Services AJAX request.