PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.6.10
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.6.10
5.6.10 5.6.9 5.6.8 5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / abilities / class-latepoint-abilities.php
latepoint / lib / abilities Last commit date
activities 5 months ago agents 5 months ago analytics 5 months ago bookings 1 month ago calendar 5 months ago configs 5 months ago customers 1 month ago locations 5 months ago orders 1 month ago services 5 months ago abstract-ability.php 3 weeks ago class-latepoint-abilities.php 1 week ago
class-latepoint-abilities.php
225 lines
1 <?php
2 /**
3 * LatePoint Abilities — Main loader & hook wiring.
4 *
5 * Registers the LatePoint category and all abilities with the WordPress
6 * Abilities API (requires WordPress 6.9+). The guarded loader in latepoint.php
7 * ensures this file is only included when the API is available.
8 *
9 * @package LatePoint\Abilities
10 * @since 5.3.0
11 */
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 class LatePointAbilities {
18
19 /**
20 * Module map: slug => config class, or array( 'class' => …, 'base_dir' => … ) for add-ons.
21 *
22 * @var array<string,string|array{class:string,base_dir?:string}>
23 */
24 private static array $config_modules = [
25 'bookings' => 'LatePointAbilitiesBookings',
26 'customers' => 'LatePointAbilitiesCustomers',
27 'services' => 'LatePointAbilitiesServices',
28 'agents' => 'LatePointAbilitiesAgents',
29 'orders' => 'LatePointAbilitiesOrders',
30 'locations' => 'LatePointAbilitiesLocations',
31 'calendar' => 'LatePointAbilitiesCalendar',
32 'analytics' => 'LatePointAbilitiesAnalytics',
33 'activities' => 'LatePointAbilitiesActivities',
34 ];
35
36 /**
37 * Boot: include module files and wire hooks.
38 */
39 public static function init(): void {
40 // Reset LatePoint's current user on every request to ensure it reflects the latest WP user state.
41 // This is crucial for accurate permission checks in abilities, especially when user roles or capabilities have changed during the request lifecycle.
42 add_action( 'set_current_user', [ OsAuthHelper::class, 'reset_current_user' ], 1 );
43
44 add_action( 'wp_abilities_api_categories_init', [ __CLASS__, 'register_category' ] );
45 add_action( 'wp_abilities_api_init', [ __CLASS__, 'register_all' ] );
46
47 // Wire the dedicated MCP server once all plugins are fully loaded. We use a
48 // late plugins_loaded priority (99, matching SureForms) so the MCP Adapter's
49 // classes are reliably available to mcp_adapter_enabled() regardless of plugin
50 // load order — important when another active plugin bundles its own adapter copy.
51 add_action( 'plugins_loaded', [ __CLASS__, 'include_modules' ], 1 );
52 add_action( 'plugins_loaded', [ __CLASS__, 'maybe_register_mcp_server' ], 99999 );
53 }
54
55 /**
56 * Attach the dedicated MCP server registration when the adapter is active.
57 */
58 public static function maybe_register_mcp_server(): void {
59 if ( self::mcp_adapter_enabled() ) {
60 add_action( 'mcp_adapter_init', [ __CLASS__, 'register_mcp_server' ] );
61 }
62 }
63
64 /**
65 * Whether the dedicated LatePoint MCP server should be registered.
66 *
67 * Requires the WordPress Abilities API and the MCP Adapter (WP\MCP\Plugin)
68 * to be available, and the MCP server toggle to be on.
69 */
70 public static function mcp_adapter_enabled(): bool {
71 return function_exists( 'wp_register_ability' )
72 && class_exists( 'WP\\MCP\\Plugin' )
73 && OsSettingsHelper::is_on( 'latepoint_mcp_server' );
74 }
75
76 /**
77 * Register a dedicated LatePoint MCP server with the MCP Adapter.
78 *
79 * Endpoint: {site_url}/wp-json/latepoint/v1/mcp — exposes every registered
80 * latepoint/* ability as a tool. register_all() only registers abilities the
81 * Enable Edit / Enable Delete toggles allow, so the published tool set follows
82 * those toggles automatically (read-only when both are off; create/update and
83 * delete tools appear as those toggles are enabled).
84 *
85 * @param \WP\MCP\Core\McpAdapter $adapter The MCP adapter instance.
86 */
87 public static function register_mcp_server( $adapter ): void {
88 $tools = [];
89 foreach ( wp_get_abilities() as $ability ) {
90 $name = $ability->get_name();
91 if ( 0 === strpos( $name, 'latepoint/' ) ) {
92 $tools[] = $name;
93 }
94 }
95
96 /**
97 * Filters the tool ids exposed by the dedicated LatePoint MCP server.
98 *
99 * Defaults to every registered latepoint/* ability (already gated by the
100 * Enable Edit / Enable Delete toggles). Return a subset to publish fewer
101 * tools to AI clients.
102 *
103 * @param string[] $tools Ability ids exposed by the dedicated MCP server.
104 */
105 $tools = apply_filters( 'latepoint_mcp_server_tool_ids', $tools );
106 if ( empty( $tools ) ) {
107 return;
108 }
109
110 $transport_class = class_exists( '\\WP\\MCP\\Transport\\HttpTransport' )
111 ? \WP\MCP\Transport\HttpTransport::class
112 : \WP\MCP\Transport\Http\RestTransport::class;
113
114 $adapter->create_server(
115 'latepoint',
116 'latepoint/v1',
117 'mcp',
118 OsSettingsHelper::get_brand_name() . ' ' . __( 'MCP Server', 'latepoint' ),
119 sprintf(
120 /* translators: %s: brand name. */
121 __( '%s MCP Server for managing bookings, customers, services, agents, locations, and orders.', 'latepoint' ),
122 OsSettingsHelper::get_brand_name()
123 ),
124 LATEPOINT_VERSION,
125 [ $transport_class ],
126 \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class,
127 \WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler::class,
128 $tools,
129 [],
130 []
131 );
132 }
133
134 /**
135 * Config modules, filtered so add-ons can register their own via `latepoint_ability_config_modules`.
136 *
137 * @return array<string,string|array{class:string,base_dir?:string}>
138 */
139 private static function get_config_modules(): array {
140 return apply_filters( 'latepoint_ability_config_modules', self::$config_modules );
141 }
142
143 /**
144 * The config class name for a module entry (a string, or an array with a 'class' key).
145 *
146 * @param string|array{class?:string} $module
147 * @return string
148 */
149 private static function module_class( $module ): string {
150 if ( is_array( $module ) ) {
151 return isset( $module['class'] ) ? (string) $module['class'] : '';
152 }
153 return (string) $module;
154 }
155
156 /**
157 * Path to a module's config file — from its 'base_dir' (array form) or core by default.
158 *
159 * @param string $slug
160 * @param string|array{base_dir?:string} $module
161 * @return string
162 */
163 private static function module_config_file( string $slug, $module ): string {
164 $base = ( is_array( $module ) && ! empty( $module['base_dir'] ) ) ? $module['base_dir'] : LATEPOINT_ABSPATH . 'lib/abilities/';
165 return $base . 'configs/class-latepoint-abilities-' . $slug . '.php';
166 }
167
168 /**
169 * Include the abstract base and each available module's config file (from its base_dir).
170 * Add-on modules registered later via the filter are loaded on demand in register_all().
171 */
172 public static function include_modules(): void {
173 require_once LATEPOINT_ABSPATH . 'lib/abilities/abstract-ability.php';
174 foreach ( self::get_config_modules() as $slug => $module ) {
175 $config_file = self::module_config_file( $slug, $module );
176 if ( file_exists( $config_file ) ) {
177 require_once $config_file;
178 }
179 }
180 }
181
182 /**
183 * Register the top-level LatePoint category.
184 */
185 public static function register_category(): void {
186 wp_register_ability_category(
187 'latepoint',
188 [
189 'label' => OsSettingsHelper::get_brand_name(),
190 'description' => __( 'Appointment scheduling — bookings, customers, services, and staff.', 'latepoint' ),
191 ]
192 );
193 }
194
195 /**
196 * Register all abilities from every module.
197 *
198 * Read-only abilities register whenever the master toggle is on.
199 * Mutating abilities require the write toggle; destructive ones
200 * require the delete toggle.
201 */
202 public static function register_all(): void {
203 $allow_write = OsSettingsHelper::is_on( 'latepoint_abilities_api_edit' );
204 $allow_delete = OsSettingsHelper::is_on( 'latepoint_abilities_api_delete' );
205
206 foreach ( self::get_config_modules() as $slug => $module ) {
207 $class = self::module_class( $module );
208 if ( empty( $class ) || ! class_exists( $class ) ) {
209 continue;
210 }
211 foreach ( $class::get_abilities() as $ability ) {
212 if ( $ability->is_destructive() && ! $allow_delete ) {
213 continue;
214 }
215 if ( ! $ability->is_read_only() && ! $ability->is_destructive() && ! $allow_write ) {
216 continue;
217 }
218 wp_register_ability( $ability->get_id(), $ability->to_definition() );
219 }
220 }
221 }
222 }
223
224 LatePointAbilities::init();
225