controller.php (7786B)
1 <?php 2 namespace Elementor\Data\Base; 3 4 use Elementor\Data\Manager; 5 use WP_REST_Controller; 6 use WP_REST_Server; 7 8 abstract class Controller extends WP_REST_Controller { 9 /** 10 * Loaded endpoint(s). 11 * 12 * @var \Elementor\Data\Base\Endpoint[] 13 */ 14 public $endpoints = []; 15 16 /** 17 * Loaded processor(s). 18 * 19 * @var \Elementor\Data\Base\Processor[][] 20 */ 21 public $processors = []; 22 23 /** 24 * Controller constructor. 25 * 26 * Register endpoints on 'rest_api_init'. 27 * 28 */ 29 public function __construct() { 30 // TODO: Controllers and endpoints can have common interface. 31 32 $this->namespace = Manager::ROOT_NAMESPACE . '/v' . Manager::VERSION; 33 $this->rest_base = Manager::REST_BASE . $this->get_name(); 34 35 add_action( 'rest_api_init', function () { 36 $this->register(); // Because 'register' is protected. 37 } ); 38 39 /** 40 * Since all actions were removed for custom internal REST server. 41 * Re-add the actions. 42 */ 43 add_action( 'elementor_rest_api_before_init', function () { 44 add_action( 'rest_api_init', function() { 45 $this->register(); 46 } ); 47 } ); 48 } 49 50 /** 51 * Get controller name. 52 * 53 * @return string 54 */ 55 abstract public function get_name(); 56 57 /** 58 * Get controller namespace. 59 * 60 * @return string 61 */ 62 public function get_namespace() { 63 return $this->namespace; 64 } 65 66 /** 67 * Get controller reset base. 68 * 69 * @return string 70 */ 71 public function get_rest_base() { 72 return $this->rest_base; 73 } 74 75 /** 76 * Get controller route. 77 * 78 * @return string 79 */ 80 public function get_controller_route() { 81 return $this->get_namespace() . '/' . $this->get_rest_base(); 82 } 83 84 /** 85 * Retrieves the index for a controller. 86 * 87 * @return \WP_REST_Response|\WP_Error 88 */ 89 public function get_controller_index() { 90 $server = rest_get_server(); 91 $routes = $server->get_routes(); 92 93 $endpoints = array_intersect_key( $server->get_routes(), $routes ); 94 95 $controller_route = $this->get_controller_route(); 96 97 array_walk( $endpoints, function ( &$item, $endpoint ) use ( &$endpoints, $controller_route ) { 98 if ( ! strstr( $endpoint, $controller_route ) ) { 99 unset( $endpoints[ $endpoint ] ); 100 } 101 } ); 102 103 $data = [ 104 'namespace' => $this->get_namespace(), 105 'controller' => $controller_route, 106 'routes' => $server->get_data_for_routes( $endpoints ), 107 ]; 108 109 $response = rest_ensure_response( $data ); 110 111 // Link to the root index. 112 $response->add_link( 'up', rest_url( '/' ) ); 113 114 return $response; 115 } 116 117 /** 118 * Get processors. 119 * 120 * @param string $command 121 * 122 * @return \Elementor\Data\Base\Processor[] 123 */ 124 public function get_processors( $command ) { 125 $result = []; 126 127 if ( isset( $this->processors[ $command ] ) ) { 128 $result = $this->processors[ $command ]; 129 } 130 131 return $result; 132 } 133 134 public function get_items( $request ) { 135 return $this->get_controller_index(); 136 } 137 138 /** 139 * Creates multiple items. 140 * 141 * @param \WP_REST_Request $request Full data about the request. 142 * 143 * @return \WP_Error|\WP_REST_Response Response object on success, or WP_Error object on failure. 144 */ 145 public function create_items( $request ) { 146 return new \WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be overridden in subclass.", __METHOD__ ), [ 'status' => 405 ] ); 147 } 148 149 /** 150 * Updates multiple items. 151 * 152 * @param \WP_REST_Request $request Full data about the request. 153 * 154 * @return \WP_Error|\WP_REST_Response Response object on success, or WP_Error object on failure. 155 */ 156 public function update_items( $request ) { 157 return new \WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be overridden in subclass.", __METHOD__ ), [ 'status' => 405 ] ); 158 } 159 160 /** 161 * Delete multiple items. 162 * 163 * @param \WP_REST_Request $request Full data about the request. 164 * 165 * @return \WP_Error|\WP_REST_Response Response object on success, or WP_Error object on failure. 166 */ 167 public function delete_items( $request ) { 168 return new \WP_Error( 'invalid-method', sprintf( "Method '%s' not implemented. Must be overridden in subclass.", __METHOD__ ), [ 'status' => 405 ] ); 169 } 170 171 /** 172 * Register endpoints. 173 */ 174 abstract public function register_endpoints(); 175 176 /** 177 * Register processors. 178 */ 179 public function register_processors() { 180 } 181 182 /** 183 * Register internal endpoints. 184 */ 185 protected function register_internal_endpoints() { 186 register_rest_route( $this->get_namespace(), '/' . $this->get_rest_base(), [ 187 [ 188 'methods' => WP_REST_Server::READABLE, 189 'callback' => array( $this, 'get_items' ), 190 'args' => [], 191 'permission_callback' => function ( $request ) { 192 return $this->get_permission_callback( $request ); 193 }, 194 ], 195 ] ); 196 } 197 198 /** 199 * Register endpoint. 200 * 201 * @param string $endpoint_class 202 * 203 * @return \Elementor\Data\Base\Endpoint 204 */ 205 protected function register_endpoint( $endpoint_class ) { 206 $endpoint_instance = new $endpoint_class( $this ); 207 208 // TODO: Validate instance like in register_sub_endpoint(). 209 210 $endpoint_route = $this->get_name() . '/' . $endpoint_instance->get_name(); 211 212 $this->endpoints[ $endpoint_route ] = $endpoint_instance; 213 214 $command = $endpoint_route; 215 $format = $endpoint_instance::get_format(); 216 217 if ( $command ) { 218 $format = $command . '/' . $format; 219 } else { 220 $format = $format . $command; 221 } 222 223 // `$e.data.registerFormat()`. 224 Manager::instance()->register_endpoint_format( $command, $format ); 225 226 return $endpoint_instance; 227 } 228 229 /** 230 * Register a processor. 231 * 232 * That will be later attached to the endpoint class. 233 * 234 * @param string $processor_class 235 * 236 * @return \Elementor\Data\Base\Processor $processor_instance 237 */ 238 protected function register_processor( $processor_class ) { 239 $processor_instance = new $processor_class( $this ); 240 241 // TODO: Validate processor instance. 242 243 $command = $processor_instance->get_command(); 244 245 if ( ! isset( $this->processors[ $command ] ) ) { 246 $this->processors[ $command ] = []; 247 } 248 249 $this->processors[ $command ] [] = $processor_instance; 250 251 return $processor_instance; 252 } 253 254 /** 255 * Register. 256 * 257 * Endpoints & processors. 258 */ 259 protected function register() { 260 $this->register_internal_endpoints(); 261 $this->register_endpoints(); 262 263 // Aka hooks. 264 $this->register_processors(); 265 } 266 267 /** 268 * Retrieves a recursive collection of all endpoint(s), items. 269 * 270 * Get items recursive, will run overall endpoints of the current controller. 271 * For each endpoint it will run `$endpoint->getItems( $request ) // the $request passed in get_items_recursive`. 272 * Will skip $skip_endpoints endpoint(s). 273 * 274 * Example, scenario: 275 * Controller 'test-controller'. 276 * Controller endpoints: 'endpoint1', 'endpoint2'. 277 * Endpoint2 get_items method: `get_items() { return 'test' }`. 278 * Call `Controller.get_items_recursive( ['endpoint1'] )`, result: [ 'endpoint2' => 'test' ]; 279 * 280 * @param array $skip_endpoints 281 * 282 * @return array 283 */ 284 public function get_items_recursive( $skip_endpoints = [] ) { 285 $response = []; 286 287 foreach ( $this->endpoints as $endpoint ) { 288 // Skip self. 289 if ( in_array( $endpoint, $skip_endpoints, true ) ) { 290 continue; 291 } 292 293 $response[ $endpoint->get_name() ] = $endpoint->get_items( null ); 294 } 295 296 return $response; 297 } 298 299 /** 300 * Get permission callback. 301 * 302 * Default controller permission callback. 303 * By default endpoint will inherit the permission callback from the controller. 304 * By default permission is `current_user_can( 'administrator' );`. 305 * 306 * @param \WP_REST_Request $request 307 * 308 * @return bool 309 */ 310 public function get_permission_callback( $request ) { 311 // The function is public since endpoint need to access it. 312 switch ( $request->get_method() ) { 313 case 'GET': 314 case 'POST': 315 case 'UPDATE': 316 case 'PUT': 317 case 'DELETE': 318 case 'PATCH': 319 return current_user_can( 'administrator' ); 320 } 321 322 return false; 323 } 324 }