PluginProbe
Post Grid Gutenberg Blocks – PostX / 5.1.1
Post Grid Gutenberg Blocks – PostX v5.1.1
5.1.1 5.1.0 5.0.41 5.0.39 5.0.38 5.0.37 5.0.36 5.0.35 5.0.34 5.0.33 5.0.32 5.0.31 5.0.30 5.0.29 5.0.28 5.0.27 5.0.26 5.0.25 5.0.23 5.0.24 5.0.22 5.0.21 5.0.20 5.0.19 5.0.18 All 240 releases
← All changes | addons/dynamic_content/includes/DCController.php +114 -395 5.0.24 → 5.1.1 View file →
@@ -23,8 +23,14 @@
23 23 */
24 24 public function __construct() {
25 25 require_once ULTP_PATH . '/addons/dynamic_content/includes/DCService.php';
26 26
27 + // PostX Pro before 1.9.0 does not answer the custom field filters below.
28 + if ( \ULTP\Includes\Durbin\Xpo::is_pro_before_1_9_0() ) {
29 + require_once ULTP_PATH . '/addons/dynamic_content/includes/DCLegacyFields.php';
30 + new DCLegacyFields();
31 + }
32 +
27 33 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
28 34 }
29 35
30 36 public function register_routes() {
@@ -34,57 +40,24 @@
34 40 array(
35 41 array(
36 42 'methods' => 'POST',
37 43 'callback' => array( $this, 'handle_dynamic_data' ),
38 - // 'permission_callback' => function () {
39 - // return is_user_logged_in() && current_user_can( 'edit_posts' );
40 - // },
44 + // there is an unauthenticated route for fetching user data against post, that can be used to fetch user sensitive data like user email, user password reset token hast ( user_activation_key ) , private or draft post title e.t.c as unauthenticated user.
41 45 'permission_callback' => function ( $request ) {
42 - // there is an unauthenticated route for fetching user data against post, that can be used to fetch user sensitive data like user email, user password reset token hast ( user_activation_key ) , private or draft post title e.t.c as unauthenticated user.
43 - $post_id = method_exists( $request, 'get_param' )
44 - ? intval( $request->get_param('post_id') )
45 - : ( isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0 );
46 + $post_id = $this->get_request_post_id( $request );
46 47
48 + if ( ! $this->user_can_access_post( $post_id ) ) {
49 + return false;
50 + }
51 +
47 52 $data_type = method_exists( $request, 'get_param' )
48 53 ? $request->get_param('data_type')
49 54 : ( isset( $request['data_type'] ) ? $request['data_type'] : '' );
50 55
51 - // User must be logged in
52 - if ( ! is_user_logged_in() ) {
53 - return false;
54 - }
55 -
56 - // If no post_id, require edit_posts capability
57 - if ( $post_id <= 0 ) {
58 - return current_user_can( 'edit_posts' );
59 - }
60 -
61 - // Get the post
62 - $post = get_post( $post_id );
63 - if ( ! $post ) {
64 - return false;
65 - }
66 -
67 - // Check if post is password protected
68 - if ( post_password_required( $post ) ) {
69 - return false;
70 - }
71 -
72 - // User must be able to edit this specific post
73 - if ( ! current_user_can( 'edit_post', $post_id ) ) {
74 - return false;
75 - }
76 -
77 - // For non-published posts, user must be author OR have edit_posts capability
78 - if ( $post->post_status !== 'publish' ) {
79 - if ( (int) $post->post_author !== get_current_user_id() && ! current_user_can( 'edit_posts' ) ) {
80 - return false;
81 - }
82 - }
83 -
84 56 // Restrict author_info to users who can edit the author OR admins
85 57 if ( $data_type === 'author_info' ) {
86 - $author_id = (int) $post->post_author;
58 + $post = get_post( $post_id );
59 + $author_id = $post ? (int) $post->post_author : 0;
87 60 if ( ! current_user_can( 'edit_user', $author_id ) && ! current_user_can( 'manage_options' ) ) {
88 61 return false;
89 62 }
90 63 }
@@ -102,9 +75,12 @@
102 75 array(
103 76 array(
104 77 'methods' => 'POST',
105 78 'callback' => array( $this, 'handle_custom_fields' ),
106 - 'permission_callback' => '__return_true',
79 + // custom field keys can reveal the meta-field schema of posts the caller shouldn't be able to see (private/draft/pending/scheduled/password-protected), so require the same access check as get_dynamic_content.
80 + 'permission_callback' => function ( $request ) {
81 + return $this->user_can_access_post( $this->get_request_post_id( $request ) );
82 + },
107 83 'args' => array(),
108 84 ),
109 85 )
110 86 );
@@ -110,8 +86,63 @@
110 86 );
111 87 }
112 88
113 89 /**
90 + * Reads the `post_id` request param, compatible with both WP_REST_Request and plain array access.
91 + *
92 + * @param WP_REST_Request|array $request Request object or params array.
93 + * @return int
94 + */
95 + private function get_request_post_id( $request ) {
96 + return method_exists( $request, 'get_param' )
97 + ? intval( $request->get_param( 'post_id' ) )
98 + : ( isset( $request['post_id'] ) ? intval( $request['post_id'] ) : 0 );
99 + }
100 +
101 + /**
102 + * Whether the current user may access dynamic data/custom fields for the given post.
103 + *
104 + * @param int $post_id
105 + * @return bool
106 + */
107 + private function user_can_access_post( $post_id ) {
108 + // User must be logged in
109 + if ( ! is_user_logged_in() ) {
110 + return false;
111 + }
112 +
113 + // If no post_id, require edit_posts capability
114 + if ( $post_id <= 0 ) {
115 + return current_user_can( 'edit_posts' );
116 + }
117 +
118 + // Get the post
119 + $post = get_post( $post_id );
120 + if ( ! $post ) {
121 + return false;
122 + }
123 +
124 + // Check if post is password protected
125 + if ( post_password_required( $post ) ) {
126 + return false;
127 + }
128 +
129 + // User must be able to edit this specific post
130 + if ( ! current_user_can( 'edit_post', $post_id ) ) {
131 + return false;
132 + }
133 +
134 + // For non-published posts, user must be author OR have edit_posts capability
135 + if ( $post->post_status !== 'publish' ) {
136 + if ( (int) $post->post_author !== get_current_user_id() && ! current_user_can( 'edit_posts' ) ) {
137 + return false;
138 + }
139 + }
140 +
141 + return true;
142 + }
143 +
144 + /**
114 145 * Dynamic data to show in the editor
115 146 *
116 147 * @since v.4.1.1
117 148 * @param WP_REST_Request $server
@@ -154,31 +185,46 @@
154 185
155 186 // All metas
156 187 $all_custom_metas = $this->get_custom_metas( $post_id, $post_type );
157 188
158 - // ACF
159 - $acf_field_keys = $this->get_acf_fields( $post_id, $field_type, $post_type );
189 + // Field sources other than plain custom metas, added by PostX Pro.
190 + $sources = apply_filters(
191 + 'ultp_dc_custom_field_sources',
192 + array(),
193 + array(
194 + 'post_id' => $post_id,
195 + 'post_type' => $post_type,
196 + 'field_type' => $field_type,
197 + )
198 + );
199 + $sources = is_array( $sources ) ? $sources : array();
160 200
161 - // Meta Box
162 - $mb_field_keys = $this->get_mb_fields( $post_id, $field_type, $post_type );
201 + // Keys and key prefixes that belong to those sources are not plain custom metas.
202 + $source_keys = array();
203 + $source_prefixes = array();
204 + foreach ( $sources as $source ) {
205 + if ( ! empty( $source['_fields'] ) && is_array( $source['_fields'] ) ) {
206 + $source_keys = array_merge( $source_keys, $source['_fields'] );
207 + }
208 + if ( ! empty( $source['prefixes'] ) && is_array( $source['prefixes'] ) ) {
209 + $source_prefixes = array_merge( $source_prefixes, $source['prefixes'] );
210 + }
211 + }
163 212
164 - // Pods
165 - $pods_field_keys = $this->get_pods_fields( $post_id, $field_type, $post_type );
166 -
167 - $filtered_custom_metas = array_values( array_diff( $all_custom_metas, $acf_field_keys['_fields'] ) );
168 - $filtered_custom_metas = array_filter(
169 - $filtered_custom_metas,
170 - function ( $item ) use ( $acf_field_keys ) {
171 - foreach ( $acf_field_keys['prefixes'] as $prefix ) {
172 - if ( str_starts_with( $item, $prefix ) ) {
173 - return false;
213 + $filtered_custom_metas = array_values( array_diff( $all_custom_metas, $source_keys ) );
214 + $filtered_custom_metas = array_values(
215 + array_filter(
216 + $filtered_custom_metas,
217 + function ( $item ) use ( $source_prefixes ) {
218 + foreach ( $source_prefixes as $prefix ) {
219 + if ( str_starts_with( $item, $prefix ) ) {
220 + return false;
221 + }
174 222 }
223 + return true;
175 224 }
176 - return true;
177 - }
225 + )
178 226 );
179 - $filtered_custom_metas = array_values( array_diff( $filtered_custom_metas, $mb_field_keys['_fields'] ) );
180 - $filtered_custom_metas = array_values( array_diff( $filtered_custom_metas, $pods_field_keys['_fields'] ) );
181 227
182 228 // Return value
183 229 $res['custom_metas'] = array(
184 230 'fields' => array_map(
@@ -191,348 +237,21 @@
191 237 $filtered_custom_metas
192 238 ),
193 239 );
194 240
195 - $res['acf'] = array(
196 - 'fields' => $acf_field_keys['fields'],
197 - );
241 + foreach ( $sources as $key => $source ) {
242 + if ( 'custom_metas' === $key || ! is_array( $source ) ) {
243 + continue;
244 + }
245 + $res[ $key ] = array(
246 + 'fields' => isset( $source['fields'] ) && is_array( $source['fields'] ) ? $source['fields'] : array(),
247 + );
248 + }
198 249
199 - $res['mb'] = array(
200 - 'fields' => $mb_field_keys['fields'],
201 - );
202 -
203 - $res['pods'] = array(
204 - 'fields' => $pods_field_keys['fields'],
205 - );
206 -
207 250 return rest_ensure_response(
208 251 array(
209 252 'data' => $res,
210 253 )
211 - );
212 - }
213 -
214 - /**
215 - * Get ACF fields
216 - *
217 - * @param int $post_id
218 - * @param string $field_type
219 - * @param string $post_type
220 - *
221 - * @return array
222 - * @since 4.1.1
223 - */
224 - public function get_acf_fields( $post_id, $field_type, $post_type ) {
225 - $res = array(
226 - 'fields' => array(),
227 - '_fields' => array(),
228 - 'prefixes' => array(),
229 - );
230 -
231 - if ( ! class_exists( 'ACF' ) ) {
232 - return $res;
233 - }
234 -
235 - if ( empty( $post_id ) && empty( $post_type ) ) {
236 - global $post;
237 - $post_id = isset( $post->ID ) ? $post->ID : '';
238 - }
239 -
240 - $allowed_types = $this->get_allowed_acf_field_types( $field_type );
241 -
242 - // If post_id set set, return ACF fields for that post
243 - if ( ! empty( $post_id ) ) {
244 - $fields = get_field_objects( $post_id );
245 - if ( is_array( $fields ) ) {
246 - foreach ( $fields as $field ) {
247 - if ( in_array( $field['type'], $allowed_types, true ) ) {
248 - $res['fields'][] = array(
249 - 'value' => $field['name'],
250 - 'label' => $field['label'],
251 - );
252 - }
253 -
254 - $res['_fields'][] = $field['name'];
255 -
256 - // For filtering out pesky repeater field's sub fields
257 - if ( $field['type'] === 'repeater' ) {
258 - $res['prefixes'][] = $field['name'];
259 - }
260 - }
261 - }
262 - } elseif ( ! empty( $post_type ) ) {
263 - $groups = acf_get_field_groups( array( 'post_type' => $post_type ) );
264 - foreach ( $groups as $group ) {
265 - $fields = acf_get_fields( $group['ID'] );
266 - if ( is_array( $fields ) ) {
267 - foreach ( $fields as $field ) {
268 - if ( in_array( $field['type'], $allowed_types, true ) ) {
269 - $res['fields'][] = array(
270 - 'value' => $field['name'],
271 - 'label' => $field['label'] . ' [' . $group['title'] . ']',
272 - );
273 - }
274 - $res['_fields'][] = $field['name'];
275 -
276 - if ( $field['type'] === 'repeater' ) {
277 - $res['prefixes'][] = $field['name'];
278 - }
279 - }
280 - }
281 - }
282 - }
283 -
284 - return $res;
285 - }
286 -
287 - /**
288 - * Get Meta Box fields
289 - *
290 - * @param int $post_id
291 - * @param string $field_type
292 - * @param string $post_type
293 - *
294 - * @return array
295 - * @since 4.1.1
296 - */
297 - public function get_mb_fields( $post_id, $field_type, $post_type ) {
298 - $res = array(
299 - 'fields' => array(),
300 - '_fields' => array(),
301 - 'prefixes' => array(),
302 - );
303 -
304 - if ( ! function_exists( 'rwmb_get_field_settings' ) ) {
305 - return $res;
306 - }
307 -
308 - $value = null;
309 -
310 - if ( empty( $post_id ) && empty( $post_type ) ) {
311 - global $post;
312 - $value = isset( $post->ID ) ? $post->ID : '';
313 - } elseif ( ! empty( $post_id ) ) {
314 - $value = $post_id;
315 - } elseif ( ! empty( $post_type ) ) {
316 - $value = $post_type;
317 - }
318 -
319 - if ( empty( $value ) ) {
320 - return $res;
321 - }
322 -
323 - $allowed_types = self::get_allowed_mb_field_type( $field_type );
324 -
325 - $fields = rwmb_get_object_fields( $value );
326 -
327 - if ( is_array( $fields ) ) {
328 - foreach ( $fields as $field ) {
329 - if ( in_array( $field['type'], $allowed_types, true ) ) {
330 - $res['fields'][] = array(
331 - 'value' => $field['id'],
332 - 'label' => $field['name'],
333 - );
334 - }
335 -
336 - $res['_fields'][] = $field['id'];
337 - }
338 - }
339 -
340 - return $res;
341 - }
342 -
343 - /**
344 - * Get Pods fields
345 - *
346 - * @param int $post_id
347 - * @param string $field_type
348 - * @param string $post_type
349 - *
350 - * @return array
351 - * @since 4.1.1
352 - */
353 - public function get_pods_fields( $post_id, $field_type, $post_type ) {
354 - $res = array(
355 - 'fields' => array(),
356 - '_fields' => array(),
357 - 'prefixes' => array(),
358 - );
359 -
360 - if ( ! function_exists( 'pods' ) ) {
361 - return $res;
362 - }
363 -
364 - if ( empty( $post_id ) ) {
365 - global $post;
366 - $post_id = isset( $post->ID ) ? $post->ID : '';
367 - }
368 -
369 - if ( empty( $post_type ) ) {
370 - $post_type = get_post_type( $post_id );
371 - }
372 -
373 - if ( empty( $post_type ) ) {
374 - return $res;
375 - }
376 -
377 - $allowed_types = self::get_allowed_pods_field_types( $field_type );
378 -
379 - $pods = pods( $post_type, $post_id );
380 - if ( is_object( $pods ) && method_exists( $pods, 'exists' ) && $pods->exists() && method_exists( $pods, 'fields' ) ) {
381 - $fields = $pods->fields();
382 - if ( is_array( $fields ) ) {
383 - foreach ( $fields as $field ) {
384 - if ( in_array( $field['type'], $allowed_types, true ) &&
385 - 'field' === $field['object_type'] &&
386 - 'post_type' === $field['object_storage_type']
387 - ) {
388 - $res['fields'][] = array(
389 - 'value' => $field['name'],
390 - 'label' => $field['label'],
391 - );
392 - }
393 -
394 - $res['_fields'][] = $field['name'];
395 - }
396 - }
397 - }
398 -
399 - return $res;
400 - }
401 -
402 - /**
403 - * Get ACF allowed field types
404 - *
405 - * @param string $type optional, possible values 'text'|'image'|'url'.
406 - *
407 - * @return array
408 - * @since 4.1.1
409 - */
410 - public static function get_allowed_acf_field_types( $type = 'text' ) {
411 -
412 - if ( 'image' === $type ) {
413 - return array(
414 - 'image',
415 - );
416 - } elseif ( 'url' === $type ) {
417 - return array(
418 - 'text',
419 - 'email',
420 - 'image',
421 - 'file',
422 - 'page_link',
423 - 'url',
424 - 'link',
425 - );
426 - }
427 -
428 - return array(
429 - 'text',
430 - 'textarea',
431 - 'number',
432 - 'range',
433 - 'email',
434 - 'url',
435 - 'password',
436 - 'wysiwyg',
437 - 'select',
438 - 'checkbox',
439 - 'radio',
440 - 'true_false',
441 - 'date_picker',
442 - 'time_picker',
443 - 'date_time_picker',
444 - 'color_picker',
445 - );
446 - }
447 -
448 - /**
449 - * Get Meta Box allowed field types
450 - *
451 - * @param string $type optional, possible values 'text'|'image'|'url'.
452 - *
453 - * @return array
454 - * @since 4.1.1
455 - */
456 - public static function get_allowed_mb_field_type( $type ) {
457 -
458 - if ( $type === 'image' ) {
459 - return array(
460 - 'image',
461 - 'image_advanced',
462 - 'image_upload',
463 - 'single_image',
464 - 'url',
465 - 'file',
466 - 'file_advanced',
467 - 'file_input',
468 - 'file_upload',
469 - );
470 - } elseif ( $type === 'url' ) {
471 - return array(
472 - 'url',
473 - 'file',
474 - 'file_advanced',
475 - 'file_input',
476 - 'file_upload',
477 - );
478 - }
479 -
480 - return array(
481 - 'text',
482 - 'email',
483 - 'number',
484 - 'textaraa',
485 - 'select',
486 - 'radio',
487 - 'checkbox',
488 - 'checkbox_list',
489 - );
490 - }
491 -
492 - /**
493 - * Get Pods allowed field types
494 - *
495 - * @param string $type optional, possible values 'text'|'image'|'url'.
496 - *
497 - * @return array
498 - * @since 4.1.1
499 - */
500 - public static function get_allowed_pods_field_types( $type = 'text' ) {
501 -
502 - if ( 'image' === $type ) {
503 - return array(
504 - 'images',
505 - 'file',
506 - );
507 - } elseif ( 'url' === $type ) {
508 - return array(
509 - 'text',
510 - 'email',
511 - 'images',
512 - 'file',
513 - 'link',
514 - 'website',
515 - );
516 - }
517 -
518 - return array(
519 - 'text',
520 - 'paragraph',
521 - 'password',
522 - 'phone',
523 - 'time',
524 - 'website',
525 - 'number',
526 - 'wysiwyg',
527 - 'email',
528 - 'link',
529 - 'boolean',
530 - 'code',
531 - 'number',
532 - 'currency',
533 - 'date',
534 - 'datetime',
535 254 );
536 255 }
537 256
538 257 /**