GravityView  2.1.1
The best, easiest way to display Gravity Forms entries on your website.
class-gv-rest-views-route.php
Go to the documentation of this file.
1 <?php
2 /**
3  * @package GravityView
4  * @license GPL2+
5  * @author Josh Pollock <josh@joshpress.net>
6  * @link http://gravityview.co
7  * @copyright Copyright 2015, Katz Web Services, Inc.
8  *
9  * @since 2.0
10  */
11 namespace GV\REST;
12 
13 /** If this file is called directly, abort. */
14 if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
15  die();
16 }
17 
18 class Views_Route extends Route {
19  /**
20  * Route Name
21  *
22  * @since 2.0
23  *
24  * @access protected
25  * @string
26  */
27  protected $route_name = 'views';
28 
29  /**
30  * Sub type, forms {$namespace}/route_name/{id}/sub_type type endpoints
31  *
32  * @since 2.0
33  * @access protected
34  * @var string
35  */
36  protected $sub_type = 'entries';
37 
38 
39  /**
40  * Get a collection of views
41  *
42  * Callback for GET /v1/views/
43  *
44  * @param \WP_REST_Request $request Full data about the request.
45  * @return \WP_Error|\WP_REST_Response
46  */
47  public function get_items( $request ) {
48 
49  $page = $request->get_param( 'page' );
50  $limit = $request->get_param( 'limit' );
51 
52  $items = \GVCommon::get_all_views( array(
53  'posts_per_page' => $limit,
54  'paged' => $page,
55  ) );
56 
57  if ( empty( $items ) ) {
58  return new \WP_Error( 'gravityview-no-views', __( 'No Views found.', 'gravityview' ) ); //@todo message
59  }
60 
61  $data = array(
62  'views' => array(),
63  'total' => wp_count_posts( 'gravityview' )->publish,
64  );
65  foreach ( $items as $item ) {
66  $data['views'][] = $this->prepare_view_for_response( $item, $request );
67  }
68 
69  return new \WP_REST_Response( $data, 200 );
70  }
71 
72  /**
73  * Get one view
74  *
75  * Callback for /v1/views/{id}/
76  *
77  * @since 2.0
78  * @param \WP_REST_Request $request Full data about the request.
79  * @return \WP_Error|\WP_REST_Response
80  */
81  public function get_item( $request ) {
82 
83  $url = $request->get_url_params();
84 
85  $view_id = intval( $url['id'] );
86 
87  $item = get_post( $view_id );
88 
89  //return a response or error based on some conditional
90  if ( $item && ! is_wp_error( $item ) ) {
91  $data = $this->prepare_view_for_response( $item, $request );
92  return new \WP_REST_Response( $data, 200 );
93  }
94 
95  return new \WP_Error( 'code', sprintf( 'A View with ID #%d was not found.', $view_id ) );
96  }
97 
98  /**
99  * Prepare the item for the REST response
100  *
101  * @since 2.0
102  * @param \GV\View $view The view.
103  * @param \GV\Entry $entry WordPress representation of the item.
104  * @param \WP_REST_Request $request Request object.
105  * @param string $context The context (directory, single)
106  * @param string $class The value renderer. Default: null (raw value)
107  *
108  * @since 2.1 Add value renderer override $class parameter.
109  *
110  * @return mixed The data that is sent.
111  */
112  public function prepare_entry_for_response( $view, $entry, \WP_REST_Request $request, $context, $class = null ) {
113 
114  // Only output the fields that should be displayed.
115  $allowed = array();
116  foreach ( $view->fields->by_position( "{$context}_*" )->by_visible()->all() as $field ) {
117  $allowed[ $field->ID ] = $field;
118  }
119 
120  /**
121  * @filter `gravityview/rest/entry/fields` Whitelist more entry fields that are output in regular REST requests.
122  * @param[in,out] array $allowed The allowed ones, default by_visible, by_position( "context_*" ), i.e. as set in the view.
123  * @param \GV\View $view The view.
124  * @param \GV\Entry $entry WordPress representation of the item.
125  * @param \WP_REST_Request $request Request object.
126  * @param string $context The context (directory, single)
127  */
128  $allowed_field_ids = apply_filters( 'gravityview/rest/entry/fields', array_keys( $allowed ), $view, $entry, $request, $context );
129 
130  $r = new Request( $request );
131  $return = array();
132  $renderer = null;
133 
134  if ( $class ) {
135  $renderer = new \GV\Field_Renderer();
136  }
137 
138  foreach ( $allowed_field_ids as $field_id ) {
139  $source = is_numeric( $field_id ) ? $view->form : new \GV\Internal_Source();
140 
141  if ( isset( $allowed[ $field_id ] ) ) {
142  $field = $allowed[ $field_id ];
143  } else {
144  $field = is_numeric( $field_id ) ? \GV\GF_Field::by_id( $view->form, $field_id ) : \GV\Internal_Field::by_id( $field_id );
145  }
146 
147  if ( $class ) {
148  $return[ $field->ID ] = $renderer->render( $field, $view, $source, $entry, $r, $class );
149  } else {
150  $return[ $field->ID ] = $field->get_value( $view, $source, $entry, $r );
151  }
152  }
153 
154  return $return;
155  }
156 
157  /**
158  * Get entries from a view
159  *
160  * Callback for /v1/views/{id}/entries/
161  *
162  * @since 2.0
163  * @param \WP_REST_Request $request Full data about the request.
164  * @return \WP_Error|\WP_REST_Response
165  */
166  public function get_sub_items( $request ) {
167 
168  $url = $request->get_url_params();
169  $view_id = intval( $url['id'] );
170  $format = \GV\Utils::get( $url, 'format', 'json' );
171 
172  if( $post_id = $request->get_param('post_id') ) {
173  global $post;
174 
175  $post = get_post( $post_id );
176 
177  if ( ! $post || is_wp_error( $post ) ) {
178  return new \WP_Error( 'gravityview-post-not-found', sprintf( 'A post with ID #%d was not found.', $post_id ) );
179  }
180 
181  $collection = \GV\View_Collection::from_post( $post );
182 
183  if ( ! $collection->contains( $view_id ) ) {
184  return new \WP_Error( 'gravityview-post-not-contains', sprintf( 'The post with ID #%d does not contain a View with ID #%d', $post_id, $view_id ) );
185  }
186  }
187 
188  $view = \GV\View::by_id( $view_id );
189 
190  if ( 'html' === $format ) {
191 
192  $renderer = new \GV\View_Renderer();
193  $count = $total = 0;
194 
195  /** @var \GV\Template_Context $context */
196  add_action( 'gravityview/template/view/render', function( $context ) use ( &$count, &$total ) {
197  $count = $context->entries->count();
198  $total = $context->entries->total();
199  } );
200 
201  $output = $renderer->render( $view, new Request( $request ) );
202 
203  /**
204  * @filter `gravityview/rest/entries/html/insert_meta` Whether to include `http-equiv` meta tags in the HTML output describing the data
205  * @since 2.0
206  * @param bool $insert_meta Add <meta> tags? [Default: true]
207  * @param int $count The number of entries being rendered
208  * @param \GV\View $view The view.
209  * @param \WP_REST_Request $request Request object.
210  * @param int $total The number of total entries for the request
211  */
212  $insert_meta = apply_filters( 'gravityview/rest/entries/html/insert_meta', true, $count, $view, $request, $total );
213 
214  if ( $insert_meta ) {
215  $output = '<meta http-equiv="X-Item-Count" content="' . $count . '" />' . $output;
216  $output = '<meta http-equiv="X-Item-Total" content="' . $total . '" />' . $output;
217  }
218 
219  $response = new \WP_REST_Response( $output, 200 );
220  $response->header( 'X-Item-Count', $count );
221  $response->header( 'X-Item-Total', $total );
222 
223  return $response;
224  }
225 
226  $entries = $view->get_entries( new Request( $request ) );
227 
228  if ( ! $entries->all() ) {
229  return new \WP_Error( 'gravityview-no-entries', __( 'No Entries found.', 'gravityview' ) );
230  }
231 
232  if ( 'csv' === $format ) {
233  ob_start();
234 
235  $csv = fopen( 'php://output', 'w' );
236 
237  /** Da' BOM :) */
238  if ( apply_filters( 'gform_include_bom_export_entries', true, $view->form ? $view->form->form : null ) ) {
239  fputs( $csv, "\xef\xbb\xbf" );
240  }
241 
242  $headers_done = false;
243 
244  foreach ( $entries->all() as $entry ) {
245  $entry = $this->prepare_entry_for_response( $view, $entry, $request, 'directory', '\GV\Field_CSV_Template' );
246 
247  if ( ! $headers_done ) {
248  $headers_done = fputcsv( $csv, array_map( array( '\GV\Utils', 'strip_excel_formulas' ), array_keys( $entry ) ) );
249  }
250 
251  fputcsv( $csv, array_map( array( '\GV\Utils', 'strip_excel_formulas' ), $entry ) );
252  }
253 
254  $response = new \WP_REST_Response( rtrim( ob_get_clean() ), 200 );
255  $response->header( 'X-Item-Count', $entries->count() );
256  $response->header( 'X-Item-Total', $entries->total() );
257 
258  return $response;
259  }
260 
261  $data = array( 'entries' => $entries->all(), 'total' => $entries->total() );
262 
263  foreach ( $data['entries'] as &$entry ) {
264  $entry = $this->prepare_entry_for_response( $view, $entry, $request, 'directory' );
265  }
266 
267  return new \WP_REST_Response( $data, 200 );
268  }
269 
270  /**
271  * Get one entry from view
272  *
273  * Callback for /v1/views/{id}/entries/{id}/
274  *
275  * @uses GVCommon::get_entry
276  * @since 2.0
277  * @param \WP_REST_Request $request Full data about the request.
278  * @return \WP_Error|\WP_REST_Response
279  */
280  public function get_sub_item( $request ) {
281  $url = $request->get_url_params();
282  $view_id = intval( $url['id'] );
283  $entry_id = intval( $url['s_id'] );
284  $format = \GV\Utils::get( $url, 'format', 'json' );
285 
286  $view = \GV\View::by_id( $view_id );
287  $entry = \GV\GF_Entry::by_id( $entry_id );
288 
289  if ( $format === 'html' ) {
290  $renderer = new \GV\Entry_Renderer();
291  return $renderer->render( $entry, $view, new Request( $request ) );
292  }
293 
294  return $this->prepare_entry_for_response( $view, $entry, $request, 'single' );
295  }
296 
297  /**
298  * Prepare the item for the REST response
299  *
300  * @since 2.0
301  * @param \WP_Post $view_post WordPress representation of the item.
302  * @param \WP_REST_Request $request Request object.
303  * @return mixed
304  */
305  public function prepare_view_for_response( $view_post, \WP_REST_Request $request ) {
306  if ( is_wp_error( $this->get_item_permissions_check( $request, $view_post->ID ) ) ) {
307  // Redacted out view.
308  return array( 'ID' => $view_post->ID, 'post_content' => __( 'You are not allowed to access this content.', 'gravityview' ) );
309  }
310 
311  $view = \GV\View::from_post( $view_post );
312 
313  $item = $view->as_data();
314 
315  // Add all the WP_Post data
316  $view_post = $view_post->to_array();
317 
318  unset( $view_post['to_ping'], $view_post['ping_status'], $view_post['pinged'], $view_post['post_type'], $view_post['filter'], $view_post['post_category'], $view_post['tags_input'], $view_post['post_content'], $view_post['post_content_filtered'] );
319 
320  $return = wp_parse_args( $item, $view_post );
321 
322  $return['title'] = $return['post_title'];
323 
324  $return['settings'] = isset( $return['atts'] ) ? $return['atts'] : array();
325  unset( $return['atts'], $return['view_id'] );
326 
327  $return['search_criteria'] = array(
328  'page_size' => rgars( $return, 'settings/page_size' ),
329  'sort_field' => rgars( $return, 'settings/sort_field' ),
330  'sort_direction' => rgars( $return, 'settings/sort_direction' ),
331  'offset' => rgars( $return, 'settings/offset' ),
332  );
333 
334  unset( $return['settings']['page_size'], $return['settings']['sort_field'], $return['settings']['sort_direction'] );
335 
336  // Redact for non-logged ins
337  if ( ! \GVCommon::has_cap( 'edit_others_gravityviews' ) ) {
338  unset( $return['settings'] );
339  unset( $return['search_criteria'] );
340  }
341 
342  if ( ! \GFCommon::current_user_can_any( 'gravityforms_edit_forms' ) ) {
343  unset( $return['form'] );
344  }
345 
346  return $return;
347  }
348 
349  /**
350  * @param \WP_REST_Request $request
351  *
352  * @return bool|\WP_Error
353  */
354  public function get_item_permissions_check( $request ) {
355  if ( func_num_args() === 2 ) {
356  $view_id = func_get_arg( 1 ); // $view_id override
357  } else {
358  $url = $request->get_url_params();
359  $view_id = intval( $url['id'] );
360  }
361 
362  if ( ! $view = \GV\View::by_id( $view_id ) ) {
363  return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
364  }
365 
366  while ( $error = $view->can_render( array( 'rest' ), $request ) ) {
367 
368  if ( ! is_wp_error( $error ) ) {
369  break;
370  }
371 
372  switch ( str_replace( 'gravityview/', '', $error->get_error_code() ) ) {
373  case 'rest_disabled':
374  case 'post_password_required':
375  case 'not_public':
376  case 'embed_only':
377  case 'no_direct_access':
378  return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
379  case 'no_form_attached':
380  return new \WP_Error( 'rest_forbidden', __( 'This View is not configured properly.', 'gravityview' ) );
381  default:
382  return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
383  }
384  }
385 
386  /**
387  * @filter `gravityview/view/output/rest` Disable rest output. Final chance.
388  * @param[in,out] bool Enable or not.
389  * @param \GV\View $view The view.
390  */
391  if ( ! apply_filters( 'gravityview/view/output/rest', true, $view ) ) {
392  return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
393  }
394 
395  return true;
396  }
397 
398  public function get_sub_item_permissions_check( $request ) {
399  // Accessing a single entry needs the View access permissions.
400  if ( is_wp_error( $error = $this->get_items_permissions_check( $request ) ) ) {
401  return $error;
402  }
403 
404  $url = $request->get_url_params();
405  $view_id = intval( $url['id'] );
406  $entry_id = intval( $url['s_id'] );
407 
408  $view = \GV\View::by_id( $view_id );
409 
410  if ( ! $entry = \GV\GF_Entry::by_id( $entry_id ) ) {
411  return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
412  }
413 
414  if ( $entry['form_id'] != $view->form->ID ) {
415  return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
416  }
417 
418  if ( $entry['status'] != 'active' ) {
419  return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
420  }
421 
422  if ( apply_filters( 'gravityview_custom_entry_slug', false ) && $entry->slug != get_query_var( \GV\Entry::get_endpoint_name() ) ) {
423  return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
424  }
425 
426  $is_admin_and_can_view = $view->settings->get( 'admin_show_all_statuses' ) && \GVCommon::has_cap('gravityview_moderate_entries', $view->ID );
427 
428  if ( $view->settings->get( 'show_only_approved' ) && ! $is_admin_and_can_view ) {
430  return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
431  }
432  }
433 
434  return true;
435  }
436 
437  public function get_items_permissions_check( $request ) {
438  // Getting a list of all Views is always possible.
439  return true;
440  }
441 
442  public function get_sub_items_permissions_check( $request ) {
443  // Accessing all entries of a View needs the same permissions as accessing the View.
444  return $this->get_item_permissions_check( $request );
445  }
446 }
$url
Definition: post_image.php:25
prepare_entry_for_response( $view, $entry, \WP_REST_Request $request, $context, $class=null)
Prepare the item for the REST response.
If this file is called directly, abort.
get_items( $request)
Get a collection of views.
if(gv_empty( $field['value'], false, false)) $format
$class
$entries
prepare_view_for_response( $view_post, \WP_REST_Request $request)
Prepare the item for the REST response.
If this file is called directly, abort.
static from_post(\WP_Post $post)
Get a list of objects inside the supplied .
static by_id( $form, $field_id)
Get a by and Field ID.
static get_endpoint_name()
Return the endpoint name for a single Entry.
get_item( $request)
Get one view.
If this file is called directly, abort.
static by_id( $post_id)
Construct a instance from a post ID.
get_sub_item( $request)
Get one entry from view.
$field_id
Definition: time.php:17
get_sub_items( $request)
Get a collection of items.
static by_id( $field_id)
Get a from an internal Gravity Forms field ID.
static get( $array, $key, $default=null)
Grab a value from an array or an object or default.
global $post
static by_id( $entry_id, $form_id=0)
Construct a instance by ID.
static is_approved( $status)
static has_cap( $caps='', $object_id=null, $user_id=null)
Alias of GravityView_Roles_Capabilities::has_cap()
$entry
Definition: notes.php:27
static get_all_views( $args=array())
Get all existing Views.
static from_post( $post)
Construct a instance from a .
const meta_key
$field
Definition: gquiz_grade.php:11