1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* CMB2 objects/fields endpoint for WordPres REST API. |
4
|
|
|
* Allows access to fields registered to a specific box. |
5
|
|
|
* |
6
|
|
|
* @todo Add better documentation. |
7
|
|
|
* @todo Research proper schema. |
8
|
|
|
* |
9
|
|
|
* @since 2.2.4 |
10
|
|
|
* |
11
|
|
|
* @category WordPress_Plugin |
12
|
|
|
* @package CMB2 |
13
|
|
|
* @author WebDevStudios |
14
|
|
|
* @license GPL-2.0+ |
15
|
|
|
* @link http://webdevstudios.com |
16
|
|
|
*/ |
17
|
|
|
class CMB2_REST_Controller_Fields extends CMB2_REST_Controller_Boxes { |
|
|
|
|
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Register the routes for the objects of the controller. |
21
|
|
|
* |
22
|
|
|
* @since 2.2.4 |
23
|
|
|
*/ |
24
|
1 |
|
public function register_routes() { |
25
|
|
|
$args = array( |
26
|
|
|
'_embed' => array( |
27
|
1 |
|
'description' => __( 'Includes the box object which the fields are registered to in the response.', 'cmb2' ), |
28
|
1 |
|
), |
29
|
|
|
'_rendered' => array( |
30
|
1 |
|
'description' => __( 'When the \'rendered\' argument is passed, the renderable field attributes will be returned fully rendered. By default, the names of the callback handers for the renderable attributes will be returned.', 'cmb2' ), |
31
|
1 |
|
), |
32
|
|
|
'object_id' => array( |
33
|
1 |
|
'description' => __( 'To view or modify the field\'s value, the \'object_id\' and \'object_type\' arguments are required.', 'cmb2' ), |
34
|
1 |
|
), |
35
|
|
|
'object_type' => array( |
36
|
1 |
|
'description' => __( 'To view or modify the field\'s value, the \'object_id\' and \'object_type\' arguments are required.', 'cmb2' ), |
37
|
1 |
|
), |
38
|
1 |
|
); |
39
|
|
|
|
40
|
|
|
// Returns specific box's fields. |
41
|
1 |
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/', array( |
42
|
|
|
array( |
43
|
1 |
|
'methods' => WP_REST_Server::READABLE, |
44
|
1 |
|
'callback' => array( $this, 'get_items' ), |
45
|
1 |
|
'permission_callback' => array( $this, 'get_items_permissions_check' ), |
46
|
1 |
|
'args' => $args, |
47
|
1 |
|
), |
48
|
1 |
|
'schema' => array( $this, 'get_item_schema' ), |
49
|
1 |
|
) ); |
50
|
|
|
|
51
|
1 |
|
$delete_args = $args; |
52
|
1 |
|
$delete_args['object_id']['required'] = true; |
53
|
1 |
|
$delete_args['object_type']['required'] = true; |
54
|
|
|
|
55
|
|
|
// Returns specific field data. |
56
|
1 |
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/(?P<field_id>[\w-]+)', array( |
57
|
|
|
array( |
58
|
1 |
|
'methods' => WP_REST_Server::READABLE, |
59
|
1 |
|
'callback' => array( $this, 'get_item' ), |
60
|
1 |
|
'permission_callback' => array( $this, 'get_item_permissions_check' ), |
61
|
1 |
|
'args' => $args, |
62
|
1 |
|
), |
63
|
|
|
array( |
64
|
1 |
|
'methods' => WP_REST_Server::EDITABLE, |
65
|
1 |
|
'callback' => array( $this, 'update_field_value' ), |
66
|
1 |
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), |
67
|
1 |
|
'permission_callback' => array( $this, 'update_field_value_permissions_check' ), |
68
|
1 |
|
'args' => $args, |
69
|
1 |
|
), |
70
|
|
|
array( |
71
|
1 |
|
'methods' => WP_REST_Server::DELETABLE, |
72
|
1 |
|
'callback' => array( $this, 'delete_field_value' ), |
73
|
1 |
|
'permission_callback' => array( $this, 'delete_field_value_permissions_check' ), |
74
|
1 |
|
'args' => $delete_args, |
75
|
1 |
|
), |
76
|
1 |
|
'schema' => array( $this, 'get_item_schema' ), |
77
|
1 |
|
) ); |
78
|
1 |
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Get all public CMB2 box fields. |
82
|
|
|
* |
83
|
|
|
* @since 2.2.4 |
84
|
|
|
* |
85
|
|
|
* @param WP_REST_Request $request Full data about the request. |
86
|
|
|
* @return WP_Error|WP_REST_Response |
87
|
|
|
*/ |
88
|
1 |
|
public function get_items( $request ) { |
89
|
1 |
|
$this->initiate_rest_read_box( $request, 'fields_read' ); |
90
|
|
|
|
91
|
1 |
|
if ( is_wp_error( $this->rest_box ) ) { |
92
|
|
|
return $this->rest_box; |
|
|
|
|
93
|
|
|
} |
94
|
|
|
|
95
|
1 |
|
$fields = array(); |
96
|
1 |
|
foreach ( $this->rest_box->cmb->prop( 'fields', array() ) as $field ) { |
97
|
1 |
|
$field_id = $field['id']; |
98
|
1 |
|
$rest_field = $this->get_rest_field( $field_id ); |
99
|
|
|
|
100
|
1 |
|
if ( ! is_wp_error( $rest_field ) ) { |
101
|
1 |
|
$fields[ $field_id ] = $this->server->response_to_data( $rest_field, isset( $this->request['_embed'] ) ); |
102
|
1 |
|
} else { |
103
|
|
|
$fields[ $field_id ] = array( 'error' => $rest_field->get_error_message() ); |
104
|
|
|
} |
105
|
1 |
|
} |
106
|
|
|
|
107
|
1 |
|
return $this->prepare_item( $fields ); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Get one CMB2 field from the collection. |
112
|
|
|
* |
113
|
|
|
* @since 2.2.4 |
114
|
|
|
* |
115
|
|
|
* @param WP_REST_Request $request Full data about the request. |
116
|
|
|
* @return WP_Error|WP_REST_Response |
117
|
|
|
*/ |
118
|
2 |
|
public function get_item( $request ) { |
119
|
2 |
|
$this->initiate_rest_read_box( $request, 'field_read' ); |
120
|
|
|
|
121
|
2 |
|
if ( is_wp_error( $this->rest_box ) ) { |
122
|
|
|
return $this->rest_box; |
123
|
|
|
} |
124
|
|
|
|
125
|
2 |
|
$field = $this->get_rest_field( $this->request->get_param( 'field_id' ) ); |
126
|
|
|
|
127
|
2 |
|
if ( is_wp_error( $field ) ) { |
128
|
1 |
|
return $field; |
|
|
|
|
129
|
|
|
} |
130
|
|
|
|
131
|
2 |
|
return $this->prepare_item( $field ); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Update CMB2 field value. |
136
|
|
|
* |
137
|
|
|
* @since 2.2.4 |
138
|
|
|
* |
139
|
|
|
* @param WP_REST_Request $request Full data about the request. |
140
|
|
|
* @return WP_Error|WP_REST_Response |
141
|
|
|
*/ |
142
|
2 |
|
public function update_field_value( $request ) { |
143
|
2 |
|
$this->initiate_rest_read_box( $request, 'field_value_update' ); |
144
|
|
|
|
145
|
2 |
|
if ( ! $this->request['value'] ) { |
146
|
1 |
|
return new WP_Error( 'cmb2_rest_update_field_error', __( 'CMB2 Field value cannot be updated without the value parameter specified.', 'cmb2' ), array( 'status' => 400 ) ); |
147
|
|
|
} |
148
|
|
|
|
149
|
2 |
|
$field = $this->rest_box->field_can_edit( $this->request->get_param( 'field_id' ), true ); |
150
|
|
|
|
151
|
2 |
|
return $this->modify_field_value( 'updated', $field ); |
|
|
|
|
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Delete CMB2 field value. |
156
|
|
|
* |
157
|
|
|
* @since 2.2.4 |
158
|
|
|
* |
159
|
|
|
* @param WP_REST_Request $request Full data about the request. |
160
|
|
|
* @return WP_Error|WP_REST_Response |
161
|
|
|
*/ |
162
|
1 |
|
public function delete_field_value( $request ) { |
163
|
1 |
|
$this->initiate_rest_read_box( $request, 'field_value_delete' ); |
164
|
|
|
|
165
|
1 |
|
$field = $this->rest_box->field_can_edit( $this->request->get_param( 'field_id' ), true ); |
166
|
|
|
|
167
|
1 |
|
return $this->modify_field_value( 'deleted', $field ); |
|
|
|
|
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* Modify CMB2 field value. |
172
|
|
|
* |
173
|
|
|
* @since 2.2.4 |
174
|
|
|
* |
175
|
|
|
* @param string $activity The modification activity (updated or deleted). |
176
|
|
|
* @param CMB2_Field $field The field object. |
177
|
|
|
* @return WP_Error|WP_REST_Response |
178
|
|
|
*/ |
179
|
3 |
|
public function modify_field_value( $activity, $field ) { |
180
|
|
|
|
181
|
3 |
|
if ( ! $this->request['object_id'] || ! $this->request['object_type'] ) { |
182
|
1 |
|
return new WP_Error( 'cmb2_rest_modify_field_value_error', __( 'CMB2 Field value cannot be modified without the object_id and object_type parameters specified.', 'cmb2' ), array( 'status' => 400 ) ); |
183
|
|
|
} |
184
|
|
|
|
185
|
2 |
|
if ( is_wp_error( $this->rest_box ) ) { |
186
|
|
|
return $this->rest_box; |
187
|
|
|
} |
188
|
|
|
|
189
|
2 |
|
if ( ! $field ) { |
190
|
|
|
return new WP_Error( 'cmb2_rest_no_field_by_id_error', __( 'No field found by that id.', 'cmb2' ), array( 'status' => 403 ) ); |
191
|
|
|
} |
192
|
|
|
|
193
|
2 |
|
$field->args["value_{$activity}"] = (bool) 'deleted' === $activity |
194
|
2 |
|
? $field->remove_data() |
195
|
2 |
|
: $field->save_field( $this->request['value'] ); |
196
|
|
|
|
197
|
|
|
// If options page, save the $activity options |
198
|
2 |
|
if ( 'options-page' == $this->request['object_type'] ) { |
199
|
|
|
$field->args["value_{$activity}"] = cmb2_options( $this->request['object_id'] )->set(); |
200
|
|
|
} |
201
|
|
|
|
202
|
2 |
|
$field_data = $this->get_rest_field( $field ); |
203
|
|
|
|
204
|
2 |
|
if ( is_wp_error( $field_data ) ) { |
205
|
|
|
return $field_data; |
206
|
|
|
} |
207
|
|
|
|
208
|
2 |
|
return $this->prepare_item( $field_data ); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Get a specific field |
213
|
|
|
* |
214
|
|
|
* @since 2.2.4 |
215
|
|
|
* |
216
|
|
|
* @param string Field id |
217
|
|
|
* @return array|WP_Error |
218
|
|
|
*/ |
219
|
5 |
|
public function get_rest_field( $field_id ) { |
220
|
5 |
|
$field = $this->rest_box->field_can_read( $field_id, true ); |
221
|
|
|
|
222
|
5 |
|
if ( ! $field ) { |
223
|
1 |
|
return new WP_Error( 'cmb2_rest_no_field_by_id_error', __( 'No field found by that id.', 'cmb2' ), array( 'status' => 403 ) ); |
224
|
|
|
} |
225
|
|
|
|
226
|
5 |
|
$field_data = $this->prepare_field_data( $field ); |
|
|
|
|
227
|
5 |
|
$response = rest_ensure_response( $field_data ); |
228
|
|
|
|
229
|
5 |
|
$response->add_links( $this->prepare_links( $field ) ); |
|
|
|
|
230
|
|
|
|
231
|
5 |
|
return $response; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Prepare the field data array for JSON. |
236
|
|
|
* |
237
|
|
|
* @since 2.2.4 |
238
|
|
|
* |
239
|
|
|
* @param CMB2_Field $field field object. |
240
|
|
|
* |
241
|
|
|
* @return array Array of field data. |
242
|
|
|
*/ |
243
|
6 |
|
protected function prepare_field_data( CMB2_Field $field ) { |
244
|
5 |
|
$field_data = array(); |
245
|
5 |
|
$params_to_ignore = array( 'show_in_rest', 'options' ); |
246
|
|
|
$params_to_rename = array( |
247
|
5 |
|
'label_cb' => 'label', |
248
|
5 |
|
'options_cb' => 'options', |
249
|
5 |
|
); |
250
|
|
|
|
251
|
|
|
// Run this first so the js_dependencies arg is populated. |
252
|
5 |
|
$rendered = ( $cb = $field->maybe_callback( 'render_row_cb' ) ) |
253
|
|
|
// Ok, callback is good, let's run it. |
254
|
5 |
|
? $this->get_cb_results( $cb, $field->args(), $field ) |
255
|
5 |
|
: false; |
256
|
|
|
|
257
|
5 |
|
$field_args = $field->args(); |
258
|
|
|
|
259
|
5 |
|
foreach ( $field_args as $key => $value ) { |
260
|
5 |
|
if ( in_array( $key, $params_to_ignore, true ) ) { |
261
|
5 |
|
continue; |
262
|
|
|
} |
263
|
|
|
|
264
|
5 |
|
if ( 'options_cb' === $key ) { |
265
|
5 |
|
$value = $field->options(); |
266
|
5 |
|
} elseif ( in_array( $key, CMB2_Field::$callable_fields, true ) ) { |
267
|
|
|
|
268
|
5 |
|
if ( isset( $this->request['_rendered'] ) ) { |
269
|
|
|
$value = $key === 'render_row_cb' ? $rendered : $field->get_param_callback_result( $key ); |
270
|
5 |
|
} elseif ( is_array( $value ) ) { |
271
|
|
|
// We need to rewrite callbacks as string as they will cause |
272
|
|
|
// JSON recursion errors. |
273
|
5 |
|
$class = is_string( $value[0] ) ? $value[0] : get_class( $value[0] ); |
274
|
5 |
|
$value = $class . '::' . $value[1]; |
275
|
5 |
|
} |
276
|
5 |
|
} |
277
|
|
|
|
278
|
5 |
|
$key = isset( $params_to_rename[ $key ] ) ? $params_to_rename[ $key ] : $key; |
279
|
|
|
|
280
|
5 |
|
if ( empty( $value ) || is_scalar( $value ) || is_array( $value ) ) { |
281
|
5 |
|
$field_data[ $key ] = $value; |
282
|
5 |
|
} else { |
283
|
|
|
$field_data[ $key ] = sprintf( __( 'Value Error for %s', 'cmb2' ), $key ); |
284
|
|
|
} |
285
|
5 |
|
} |
286
|
|
|
|
287
|
6 |
|
if ( $this->request['object_id'] && $this->request['object_type'] ) { |
288
|
3 |
|
$field_data['value'] = $field->get_data(); |
289
|
3 |
|
} |
290
|
|
|
|
291
|
5 |
|
return $field_data; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Return an array of contextual links for field/fields. |
296
|
|
|
* |
297
|
|
|
* @since 2.2.4 |
298
|
|
|
* |
299
|
|
|
* @param CMB2_Field $field Field object to build links from. |
300
|
|
|
* |
301
|
|
|
* @return array Array of links |
302
|
|
|
*/ |
303
|
5 |
|
protected function prepare_links( $field ) { |
304
|
5 |
|
$boxbase = $this->namespace_base . '/' . $this->rest_box->cmb->cmb_id; |
305
|
5 |
|
$query_string = $this->get_query_string(); |
306
|
|
|
|
307
|
|
|
$links = array( |
308
|
|
|
'self' => array( |
309
|
5 |
|
'href' => rest_url( trailingslashit( $boxbase ) . 'fields/' . $field->_id() . $query_string ), |
310
|
5 |
|
), |
311
|
|
|
'collection' => array( |
312
|
5 |
|
'href' => rest_url( trailingslashit( $boxbase ) . 'fields' . $query_string ), |
313
|
5 |
|
), |
314
|
|
|
'up' => array( |
315
|
5 |
|
'href' => rest_url( $boxbase . $query_string ), |
316
|
5 |
|
), |
317
|
5 |
|
); |
318
|
|
|
|
319
|
|
|
// Don't embed boxes when looking at boxes route. |
320
|
5 |
|
if ( '/cmb2/v1/boxes' !== CMB2_REST_Controller::get_intial_route() ) { |
321
|
|
|
$links['up']['embeddable'] = true; |
322
|
|
|
} |
323
|
|
|
|
324
|
5 |
|
return $links; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
} |
328
|
|
|
|
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.