PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.6.8
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.6.8
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 / abstract-ability.php
latepoint / lib / abilities Last commit date
activities 4 months ago agents 4 months ago analytics 4 months ago bookings 1 week ago calendar 4 months ago configs 4 months ago customers 1 week ago locations 4 months ago orders 1 day ago services 4 months ago abstract-ability.php 1 day ago class-latepoint-abilities.php 1 day ago
abstract-ability.php
186 lines
1 <?php
2 /**
3 * Abstract base class for all LatePoint abilities.
4 *
5 * @package LatePoint\Abilities
6 * @since 5.3.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 abstract class LatePointAbstractAbility {
14
15 protected string $id;
16 protected string $category = 'latepoint';
17 protected string $label = '';
18 protected string $description = '';
19 protected string $permission = 'manage_options';
20 protected bool $read_only = true;
21 protected bool $destructive = false;
22 protected bool $idempotent = false;
23
24 public function __construct() {
25 $this->configure();
26 }
27
28 /**
29 * Set $id, $label, $description, $permission, $read_only, $destructive.
30 */
31 abstract protected function configure(): void;
32
33 abstract public function get_input_schema(): array;
34
35 abstract public function get_output_schema(): array;
36
37 /**
38 * @param array $args
39 * @return array|\WP_Error
40 */
41 abstract public function execute( array $args );
42
43 public function get_id(): string {
44 return $this->id;
45 }
46
47 public function is_read_only(): bool {
48 return $this->read_only;
49 }
50
51 public function is_destructive(): bool {
52 return $this->destructive;
53 }
54
55 public function check_permission(): bool {
56 // Master gate. In case the ability was registered
57 // while the master toggle was on but has since been disabled.
58 if ( ! OsSettingsHelper::is_on( 'latepoint_abilities_api' ) ) {
59 return false;
60 }
61 if ( $this->destructive && ! OsSettingsHelper::is_on( 'latepoint_abilities_api_delete' ) ) {
62 return false;
63 }
64 if ( ! $this->read_only && ! $this->destructive
65 && ! OsSettingsHelper::is_on( 'latepoint_abilities_api_edit' ) ) {
66 return false;
67 }
68
69 return OsRolesHelper::can_user( $this->permission );
70 }
71
72 public function to_definition(): array {
73 return [
74 'label' => $this->label,
75 'description' => $this->description,
76 'category' => $this->category,
77 'permission_callback' => [ $this, 'check_permission' ],
78 'input_schema' => self::normalize_schema( $this->get_input_schema() ),
79 'output_schema' => self::normalize_schema( $this->get_output_schema() ),
80 'execute_callback' => [ $this, 'execute' ],
81 'meta' => $this->build_meta(),
82 ];
83 }
84
85 /**
86 * Ensure a JSON Schema serializes to valid JSON.
87 *
88 * An empty PHP array encodes as a JSON array ([]), but JSON Schema requires
89 * `properties` to be an object ({}). A tool advertising `"properties":[]`
90 * is invalid and causes strict AI clients (e.g. Claude Desktop) to reject the
91 * whole connector. Recursively coerce empty `properties` to objects.
92 *
93 * @param mixed $schema
94 * @return mixed
95 */
96 protected static function normalize_schema( $schema ) {
97 if ( ! is_array( $schema ) ) {
98 return $schema;
99 }
100 if ( array_key_exists( 'properties', $schema ) ) {
101 if ( empty( $schema['properties'] ) ) {
102 $schema['properties'] = new \stdClass();
103 } else {
104 foreach ( $schema['properties'] as $key => $value ) {
105 $schema['properties'][ $key ] = self::normalize_schema( $value );
106 }
107 }
108 }
109 if ( isset( $schema['items'] ) ) {
110 $schema['items'] = self::normalize_schema( $schema['items'] );
111 }
112 return $schema;
113 }
114
115 protected function build_meta(): array {
116
117 // Use the WordPress Abilities API annotation keys (readonly/destructive/
118 // idempotent/openWorldHint), matching SureForms, so the MCP Adapter emits
119 // concrete boolean values. Supplying only the *Hint variants leaves these
120 // base keys as null in the tool output, which strict AI clients (e.g. Claude
121 // Desktop) reject — causing the whole connector to show "no tools available".
122 $annotations = [
123 'readonly' => $this->read_only,
124 'destructive' => false,
125 'idempotent' => $this->idempotent,
126 'openWorldHint' => false,
127 'priority' => $this->read_only ? 1.0 : 2.0 ,
128 ];
129
130 // Destructive overrides everything.
131 if ( $this->destructive ) {
132 $annotations['readonly'] = false;
133 $annotations['destructive'] = true;
134 $annotations['priority'] = 3.0;
135 }
136
137 $meta = [
138 'annotations' => $annotations,
139 'show_in_rest' => true,
140 'mcp' => [
141 'public' => true,
142 'type' => 'tool',
143 ],
144 ];
145
146 return $meta;
147 }
148
149 /**
150 * Per-record ownership/scope check. Returns WP_Error (403) when the current
151 * user is not allowed to act on this specific record. Admins always pass.
152 *
153 * @param OsModel $model
154 * @param string $action one of 'view' | 'edit' | 'delete'
155 * @return true|\WP_Error
156 */
157 protected function authorize_record( OsModel $model, string $action ) {
158 if ( ! OsRolesHelper::can_user_make_action_on_model_record( $model, $action ) ) {
159 return new WP_Error(
160 'forbidden',
161 __( 'You are not allowed to access this record.', 'latepoint' ),
162 [ 'status' => 403 ]
163 );
164 }
165 return true;
166 }
167
168 protected static function pagination(): array {
169 return [
170 'page' => [
171 'type' => 'integer',
172 'default' => 1,
173 'minimum' => 1,
174 'description' => __( 'Page number.', 'latepoint' ),
175 ],
176 'per_page' => [
177 'type' => 'integer',
178 'default' => 20,
179 'minimum' => 1,
180 'maximum' => 100,
181 'description' => __( 'Items per page.', 'latepoint' ),
182 ],
183 ];
184 }
185 }
186