settingsCapability(), 'abece-booking-settings' ],
[ 'Getting Started', 'manage_options', 'abece-booking-getting-started' ],
[ 'System Status', 'manage_options', 'abece-booking-system-status' ],
];
if ( \ABeCe\Booking\Edition\Entitlements::current()->has('memberships') && ! \ABeCe\Booking\Service\MembershipModeService::enabled() ) { $entries=array_values(array_filter($entries,static fn($entry)=>$entry[0]!=='Members')); }
$unavailable=array_values(array_filter($entries,fn($entry)=>($feature=$this->slugFeature($entry[2]))&&!\ABeCe\Booking\Edition\Entitlements::current()->has($feature)));
$entries=array_values(array_filter($entries,fn($entry)=>!($feature=$this->slugFeature($entry[2]))||\ABeCe\Booking\Edition\Entitlements::current()->has($feature)));
$parent = 'abece-booking'; $top_capability = Capabilities::MANAGE_BOOKINGS;
foreach ( $entries as [ , $cap, $slug ] ) { if ( current_user_can( $cap ) ) { $top_capability = $cap; $this->landingSlug = $slug; break; } }
$this->pages[] = add_menu_page( __( 'ABeCe Booking', 'abece-booking' ), __( 'ABeCe Booking', 'abece-booking' ), $top_capability, $parent, [ $this, 'render' ], 'dashicons-calendar-alt', 26 );
// Literal source strings remain extractable for localization.
$labels = [
'Reservations' => __( 'Reservations', 'abece-booking' ), 'Resources' => __( 'Resources', 'abece-booking' ),
'Sports' => __( 'Sports', 'abece-booking' ), 'Price lists' => __( 'Price lists', 'abece-booking' ),
'Members' => __( 'Members', 'abece-booking' ), 'Credits' => __( 'Credits', 'abece-booking' ),
'Operations' => __( 'Operations', 'abece-booking' ), 'Seasons' => __( 'Seasons', 'abece-booking' ),
'Statistics' => __( 'Statistics', 'abece-booking' ),
'Settings' => __( 'Settings', 'abece-booking' ),
'Outstanding amounts'=>__('Outstanding amounts','abece-booking'),'Editions and features'=>__('Editions and features','abece-booking'),
'Getting Started'=>__('Getting Started','abece-booking'),'System Status'=>__('System Status','abece-booking'),
];
foreach ( $entries as [ $label, $cap, $slug ] ) {
$this->pages[] = add_submenu_page( $parent, $labels[ $label ], $labels[ $label ], $cap, $slug, [ $this, 'render' ] );
}
foreach($unavailable as [$label,$cap,$slug])$this->addHiddenPage($labels[$label],$cap,$slug);
// Retain direct links and capability protection for existing child pages.
foreach ( [
[ __( 'Customer restrictions', 'abece-booking' ), Capabilities::MANAGE_BOOKINGS, 'abece-booking-customer-restrictions' ],
[ __( 'External contacts', 'abece-booking' ), Capabilities::MANAGE_BOOKINGS, 'abece-booking-external-contacts' ],
[ __( 'Credits', 'abece-booking' ), Capabilities::MANAGE_CREDIT, 'abece-booking-credits' ],
[ __( 'Playing time limits', 'abece-booking' ), Capabilities::MANAGE_MEMBERS, 'abece-booking-usage-limits' ],
[ __( 'Membership Types', 'abece-booking' ), Capabilities::MANAGE_MEMBERS, 'abece-booking-membership-types' ],
[ __( 'Cancellation and penalties', 'abece-booking' ), Capabilities::MANAGE_CANCELLATION_POLICIES, 'abece-booking-cancellation-policies' ],
[ __( 'Opening hours', 'abece-booking' ), Capabilities::MANAGE_RESOURCES, 'abece-booking-opening-hours' ],
[ __( 'Operating exceptions', 'abece-booking' ), Capabilities::MANAGE_RESOURCES, 'abece-booking-schedule-exceptions' ],
[ __( 'Resource blocks', 'abece-booking' ), Capabilities::MANAGE_RESOURCES, 'abece-booking-resource-blocks' ],
] as [ $label, $cap, $slug ] ) {
if ( \ABeCe\Booking\Edition\Entitlements::current()->has('memberships') && ! \ABeCe\Booking\Service\MembershipModeService::enabled() && in_array($slug,['abece-booking-members','abece-booking-membership-types','abece-booking-usage-limits'],true) ) { continue; }
$this->addHiddenPage( $label, $cap, $slug );
}
}
/** Hidden screens still need their registered title before WordPress renders admin-header.php. */
private function addHiddenPage( string $label, string $capability, string $slug ): void {
$hook = add_submenu_page( '', $label, $label, $capability, $slug, [ $this, 'render' ] );
if ( is_string( $hook ) ) {
$this->pages[] = $hook;
add_action( 'load-' . $hook, static function () use ( $label ): void {
$GLOBALS['title'] = $label;
} );
}
remove_submenu_page( '', $slug );
}
public function render(): void {
if ( ! current_user_can( $this->capability() ) ) {
wp_die( esc_html__( 'You do not have permission to access this ABeCe Booking page.', 'abece-booking' ) );
}
if(($feature=$this->slugFeature(self::requestedPage()))&&!\ABeCe\Booking\Edition\Entitlements::current()->has($feature)){echo '
'.esc_html__('This feature is not available in this edition.','abece-booking').'
';return;}
echo '';
}
public function enqueue( string $hook ): void {
if ( ! in_array( $hook, $this->pages, true ) || ! current_user_can( $this->capability() ) ) {
return;
}
if(($feature=$this->slugFeature(self::requestedPage()))&&!\ABeCe\Booking\Edition\Entitlements::current()->has($feature))return;
$path = dirname( $this->file ) . '/assets/build/';
if ( ! is_readable( $path . 'admin.asset.php' ) || ! is_readable( $path . 'admin.js' ) ) {
add_action( 'admin_notices', static function (): void {
echo '' . esc_html__( 'The admin package is incomplete. Install the complete ABeCe Booking release ZIP.', 'abece-booking' ) . '
';
} );
return;
}
$asset = require $path . 'admin.asset.php';
$style = dirname( $this->file ) . '/admin-ui/admin.css';
$style_version = is_readable( $style ) ? substr( hash_file( 'sha256', $style ), 0, 16 ) : $asset['version'];
wp_enqueue_script( 'abece-booking-admin', plugins_url( 'assets/build/admin.js', $this->file ), $asset['dependencies'], $asset['version'], true );
wp_enqueue_style( 'abece-booking-admin', plugins_url( 'admin-ui/admin.css', $this->file ), [], $style_version );
wp_enqueue_style( 'abece-booking-calendar', plugins_url( 'assets/build/calendar.css', $this->file ), [], $asset['version'] );
wp_set_script_translations( 'abece-booking-admin', 'abece-booking', dirname( $this->file ) . '/languages' );
$config = [
'entitlements'=>\ABeCe\Booking\Edition\Entitlements::current()->manifest(),
'restUrl' => rest_url( 'abece-booking/v1/' ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'page' => $this->page(),
'timezone' => wp_timezone()->getName(),
'locale' => \ABeCe\Booking\Support\LocaleContext::current(),
'requestLocale'=>'user', 'timeFormat'=>get_option('time_format'),
'membershipsEnabled' => \ABeCe\Booking\Service\MembershipModeService::enabled(),
'membershipSettingsUrl' => admin_url( 'admin.php?page=abece-booking-membership-types' ),
'usageLimitsUrl'=>admin_url('admin.php?page=abece-booking-usage-limits'),
'membersUrl' => admin_url( 'admin.php?page=abece-booking-members' ),
'pricesUrl' => admin_url( 'admin.php?page=abece-booking-price-lists' ),
'canViewFinance'=>current_user_can(Capabilities::MANAGE_CREDIT)&&\ABeCe\Booking\Edition\Entitlements::current()->has('financial_reporting'),
'canManageBookings'=>current_user_can(Capabilities::MANAGE_BOOKINGS),
'canManageMembers'=>current_user_can(Capabilities::MANAGE_MEMBERS),
'canManagePrices' => current_user_can( Capabilities::MANAGE_PRICES ),
'canManageCalendarSettings' => current_user_can( Capabilities::MANAGE_RESOURCES ),
'operationsUrl' => admin_url( 'admin.php?page=abece-booking-operations' ),
];
wp_add_inline_script( 'abece-booking-admin', 'window.ABeCeBookingFeatures = '.wp_json_encode($config['entitlements']['features']).';window.ABeCeBookingConfig = ' . wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ';', 'before' );
}
/** Read-only navigation; capabilities and entitlements are checked separately. */
private static function requestedPage(): string {
return isset($_GET['page']) && is_string($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
}
private static function requestedTab(): string {
return isset($_GET['tab']) && is_string($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : '';
}
private function page(): string {
$slug = self::requestedPage();
if ($slug === 'abece-booking' && !current_user_can(Capabilities::MANAGE_RESOURCES)) $slug = $this->landingSlug;
return match ( $slug ) {
'abece-booking-sports'=>'sports',
'abece-booking-editions'=>'editions',
'abece-booking-getting-started'=>'getting-started',
'abece-booking-system-status'=>'system-status',
'abece-booking-statistics' => 'statistics',
'abece-booking-customer-restrictions' => 'customer-restrictions',
'abece-booking-receivables' => 'receivables',
'abece-booking-external-contacts' => 'external-contacts',
'abece-booking-resources' => 'resources',
'abece-booking-bookings' => 'bookings',
'abece-booking-price-lists' => 'price-lists',
'abece-booking-credits' => 'credits',
'abece-booking-cancellation-policies' => 'cancellation-policies',
'abece-booking-seasons' => 'seasons',
'abece-booking-members' => 'members',
'abece-booking-usage-limits' => 'usage-limits',
'abece-booking-membership-types' => 'membership-types',
'abece-booking-settings' => 'settings',
'abece-booking-operations' => match ( self::requestedTab() ) { 'exceptions' => 'schedule-exceptions', 'blocks' => 'resource-blocks', default => 'opening-hours' },
'abece-booking-opening-hours' => 'opening-hours',
'abece-booking-schedule-exceptions' => 'schedule-exceptions',
'abece-booking-resource-blocks' => 'resource-blocks',
default => current_user_can( Capabilities::MANAGE_RESOURCES ) ? 'sports' : ( current_user_can( Capabilities::MANAGE_MEMBERS ) ? 'seasons' : 'bookings' ),
};
}
private function capability(): string {
if(in_array($this->page(),['getting-started','system-status','editions'],true))return 'manage_options';
if ( 'settings' === $this->page() ) { return $this->settingsCapability(); }
if ( 'cancellation-policies' === $this->page() ) { return Capabilities::MANAGE_CANCELLATION_POLICIES; }
if ( 'credits' === $this->page() ) { return Capabilities::MANAGE_CREDIT; }
if ( 'price-lists' === $this->page() ) { return Capabilities::MANAGE_PRICES; }
if ( in_array($this->page(),[ 'statistics', 'bookings', 'external-contacts', 'receivables', 'customer-restrictions' ],true) ) { return Capabilities::MANAGE_BOOKINGS; }
return in_array( $this->page(), [ 'seasons', 'members', 'membership-types', 'usage-limits' ], true ) ? Capabilities::MANAGE_MEMBERS : Capabilities::MANAGE_RESOURCES;
}
private function slugFeature(string $slug):?string{return match($slug){'abece-booking-members','abece-booking-seasons','abece-booking-membership-types'=>'memberships','abece-booking-usage-limits'=>'usage_limits','abece-booking-customer-restrictions'=>'customer_restrictions','abece-booking-credits'=>'credits',default=>null};}
private function settingsCapability(): string {
foreach ( [ Capabilities::MANAGE_RESOURCES, Capabilities::MANAGE_MEMBERS, Capabilities::MANAGE_CREDIT, Capabilities::MANAGE_CANCELLATION_POLICIES, Capabilities::MANAGE_BOOKINGS ] as $cap ) { if ( current_user_can( $cap ) ) { return $cap; } }
return Capabilities::MANAGE_RESOURCES;
}
}