PluginProbe
Elementor Website Builder – more than just a page builder / 3.28.0-dev2
Elementor Website Builder – more than just a page builder v3.28.0-dev2
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / modules / global-classes / global-classes-rest-api.php

global-classes-rest-api.php in Elementor Website Builder – more than just a page builder 3.28.0-dev2, at modules/global-classes/global-classes-rest-api.php

184 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\GlobalClasses;
4
5 use Elementor\Core\Utils\Collection;
6 use Elementor\Modules\AtomicWidgets\Styles\Style_Schema;
7 use Elementor\Modules\AtomicWidgets\Parsers\Style_Parser;
8 use Elementor\Modules\GlobalClasses\Utils\Error_Builder;
9 use Elementor\Modules\GlobalClasses\Utils\Response_Builder;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 class Global_Classes_REST_API {
16 const API_NAMESPACE = 'elementor/v1';
17 const API_BASE = 'global-classes';
18
19 private $repository = null;
20
21 public function register_hooks() {
22 add_action( 'rest_api_init', fn() => $this->register_routes() );
23 }
24
25 private function get_repository() {
26 if ( ! $this->repository ) {
27 $this->repository = new Global_Classes_Repository();
28 }
29
30 return $this->repository;
31 }
32
33 /**
34 * TODO: Add sanitization when implemented on prop types [EDS-574]
35 */
36 private function register_routes() {
37 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
38 [
39 'methods' => 'GET',
40 'callback' => fn() => $this->route_wrapper( fn() => $this->all() ),
41 'permission_callback' => fn() => current_user_can( 'manage_options' ),
42 ],
43 ] );
44
45 register_rest_route( self::API_NAMESPACE, '/' . self::API_BASE, [
46 [
47 'methods' => 'PUT',
48 'callback' => fn( $request ) => $this->route_wrapper( fn() => $this->put( $request ) ),
49 'permission_callback' => fn() => current_user_can( 'manage_options' ),
50 'args' => [
51 'items' => [
52 'required' => true,
53 'type' => 'object',
54 'additionalProperties' => false,
55 'patternProperties' => [
56 '^g-[a-z0-9]+$' => [
57 'type' => 'object',
58 'properties' => [
59 'id' => [
60 'type' => 'string',
61 'pattern' => '^g-[a-z0-9]+$',
62 'required' => true,
63 ],
64 'variants' => [
65 'type' => 'array',
66 'required' => true,
67 ],
68 'type' => [
69 'type' => 'string',
70 'enum' => [ 'class' ],
71 'required' => true,
72 ],
73 'label' => [
74 'type' => 'string',
75 'required' => true,
76 ],
77 ],
78 ],
79 ],
80 ],
81 'order' => [
82 'required' => true,
83 'type' => 'array',
84 'items' => [
85 'type' => 'string',
86 'pattern' => '^g-[a-z0-9]+$',
87 ],
88 ],
89 ],
90 ],
91 ] );
92 }
93
94 private function all() {
95 $classes = $this->get_repository()->all();
96
97 return Response_Builder::make( (object) $classes->get_items()->all() )
98 ->set_meta( [ 'order' => $classes->get_order()->all() ] )
99 ->build();
100 }
101
102 private function put( \WP_REST_Request $request ) {
103 $items = $request->get_param( 'items' );
104
105 [$is_valid, $sanitized_items, $errors] = $this->sanitize_items( $items );
106
107 if ( ! $is_valid ) {
108 return Error_Builder::make( 'invalid_items' )
109 ->set_status( 400 )
110 ->set_message( 'Invalid items: ' . join( ', ', array_keys( $errors ) ) )
111 ->build();
112 }
113
114 $order = $request->get_param( 'order' );
115
116 if ( ! $this->is_valid_order( $order, $sanitized_items ) ) {
117 return Error_Builder::make( 'invalid_order' )
118 ->set_status( 400 )
119 ->set_message( 'Invalid order' )
120 ->build();
121 }
122
123 $this->get_repository()->put(
124 $sanitized_items,
125 $order
126 );
127
128 return Response_Builder::make()->no_content()->build();
129 }
130
131 private function sanitize_items( array $items ) {
132 $errors = [];
133 $sanitized_items = [];
134
135 foreach ( $items as $item_id => $item ) {
136 [$is_item_valid, $sanitized_item, $item_errors] = Style_Parser::make( Style_Schema::get() )->parse( $item );
137
138 if ( ! $is_item_valid ) {
139 $errors[ $item_id ] = $item_errors;
140 continue;
141 }
142
143 if ( $item_id !== $sanitized_item['id'] ) {
144 $errors[ $item_id ] = [ 'id' ];
145
146 continue;
147 }
148
149 $sanitized_items[ $sanitized_item['id'] ] = $sanitized_item;
150 }
151
152 $is_valid = count( $errors ) === 0;
153
154 return [ $is_valid, $sanitized_items, $errors ];
155 }
156
157 private function is_valid_order( array $order, array $items ) {
158 $existing_ids = array_keys( $items );
159
160 $excess_ids = Collection::make( $order )->diff( $existing_ids );
161 $missing_ids = Collection::make( $existing_ids )->diff( $order );
162
163 $has_duplications = Collection::make( $order )->unique()->all() !== $order;
164
165 return (
166 $excess_ids->is_empty() &&
167 $missing_ids->is_empty() &&
168 ! $has_duplications
169 );
170 }
171
172 private function route_wrapper( callable $cb ) {
173 try {
174 $response = $cb();
175 } catch ( \Exception $e ) {
176 return Error_Builder::make( 'unexpected_error' )
177 ->set_message( __( 'Something went wrong', 'elementor' ) )
178 ->build();
179 }
180
181 return $response;
182 }
183 }
184