/wp-content/plugins/elementor/data/base/controller.php

https://gitlab.com/campus-academy/krowkaramel · PHP · 346 lines · 149 code · 56 blank · 141 comment · 8 complexity · 8a4a03ed1b682902a519a541a36e0e70 MD5 · raw file

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