Completed
Push — develop ( d231fb...9ab8cc )
by Zack
18:08
created

Views_Route::get_sub_item_permissions_check()   C

Complexity

Conditions 11
Paths 11

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 20
nc 11
nop 1
dl 0
loc 38
rs 5.2653
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 18 and the first side effect is on line 15.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @package   GravityView
4
 * @license   GPL2+
5
 * @author    Josh Pollock <[email protected]>
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
	 * @return mixed The data that is sent.
107
	 */
108
	public function prepare_entry_for_response( $view, $entry, \WP_REST_Request $request, $context ) {
109
		$return = $entry->as_entry();
110
111
		// Only output the fields that should be displayed.
112
		$allowed = array();
113
		foreach ( $view->fields->by_position( "{$context}_*" )->by_visible()->all() as $field ) {
114
			$allowed[] = $field->ID;
115
		}
116
117
		/**
118
		 * @filter `gravityview/rest/entry/fields` Whitelist more entry fields that are output in regular REST requests.
119
		 * @param[in,out] array $allowed The allowed ones, default by_visible, by_position( "context_*" ), i.e. as set in the view.
120
		 * @param \GV\View $view The view.
121
		 * @param \GV\Entry $entry WordPress representation of the item.
122
		 * @param \WP_REST_Request $request Request object.
123
		 * @param string $context The context (directory, single)
124
		 */
125
		$allowed = apply_filters( 'gravityview/rest/entry/fields', $allowed, $view, $entry, $request, $context );
126
127
		foreach ( $return as $key => $value ) {
128
			if ( ! in_array( $key, $allowed ) ) {
129
				unset( $return[ $key ] );
130
			}
131
		}
132
133
		$r = new Request( $request );
134
135
		foreach ( $allowed as $field ) {
136
			$source = is_numeric( $field ) ? $view->form : new \GV\Internal_Source();
137
			$field  = is_numeric( $field ) ? \GV\GF_Field::by_id( $view->form, $field ) : \GV\Internal_Field::by_id( $field );
138
139
			$return[ $field->ID ] = $field->get_value( $view, $source, $entry, $r );
140
		}
141
142
		// @todo Set the labels!
143
144
		return $return;
145
	}
146
147
	/**
148
	 * Get entries from a view
149
	 *
150
	 * Callback for /v1/views/{id}/entries/
151
	 *
152
	 * @since 2.0
153
	 * @param \WP_REST_Request $request Full data about the request.
154
	 * @return \WP_Error|\WP_REST_Response
155
	 */
156
	public function get_sub_items( $request ) {
157
158
		$url     = $request->get_url_params();
159
		$view_id = intval( $url['id'] );
160
		$format  = \GV\Utils::get( $url, 'format', 'json' );
161
162
		$view = \GV\View::by_id( $view_id );
163
164
		if ( $format == 'html' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
165
166
			$renderer = new \GV\View_Renderer();
167
			$total = 0;
168
169
			add_action( 'gravityview/template/view/render', function( $context ) use ( &$total ) {
170
				$total = $context->entries->count();
171
			} );
172
173
			$output = $renderer->render( $view, new Request( $request ) );
174
175
			/**
176
			 * @filter `gravityview/rest/entries/html/insert_meta` Whether to include `http-equiv` meta tags in the HTML output describing the data
177
			 * @param bool $insert_meta Add <meta> tags? [Default: true]
178
			 * @param int $total The number of entries being rendered
179
			 * @param \GV\View $view The view.
180
			 * @param \WP_REST_Request $request Request object.
181
			 */
182
			$insert_meta = apply_filters( 'gravityview/rest/entries/html/insert_meta', true, $total, $view, $request );
183
184
			if ( $insert_meta ) {
185
				$output = '<meta http-equiv="X-Item-Count" content="' . $total . '" />' . $output;
186
			}
187
188
			$response = new \WP_REST_Response( $output, 200 );
189
			$response->header( 'X-Item-Count', $total );
190
191
			return $response;
192
		}
193
194
		$entries = $view->get_entries( new Request( $request ) );
195
196
		if ( ! $entries->all() ) {
197
			return new \WP_Error( 'gravityview-no-entries', __( 'No Entries found.', 'gravityview' ) );
198
		}
199
200
		$data = array( 'entries' => $entries->all(), 'total' => $entries->total() );
201
202
		foreach ( $data['entries'] as &$entry ) {
203
			$entry = $this->prepare_entry_for_response( $view, $entry, $request, 'directory' );
204
		}
205
206
		return new \WP_REST_Response( $data, 200 );
207
	}
208
209
	/**
210
	 * Get one entry from view
211
	 *
212
	 * Callback for /v1/views/{id}/entries/{id}/
213
	 *
214
	 * @uses GVCommon::get_entry
215
	 * @since 2.0
216
	 * @param \WP_REST_Request $request Full data about the request.
217
	 * @return \WP_Error|\WP_REST_Response
218
	 */
219
	public function get_sub_item( $request ) {
220
		$url      = $request->get_url_params();
221
		$view_id  = intval( $url['id'] );
222
		$entry_id = intval( $url['s_id'] );
223
		$format   = \GV\Utils::get( $url, 'format', 'json' );
224
225
		$view  = \GV\View::by_id( $view_id );
226
		$entry = \GV\GF_Entry::by_id( $entry_id );
227
228
		if ( $format == 'html' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
229
			$renderer = new \GV\Entry_Renderer();
230
			return $renderer->render( $entry, $view, new Request( $request ) );
231
		}
232
233
		return $this->prepare_entry_for_response( $view, $entry, $request, 'single' );
234
	}
235
236
	/**
237
	 * Prepare the item for the REST response
238
	 *
239
	 * @since 2.0
240
	 * @param \WP_Post $view_post WordPress representation of the item.
241
	 * @param \WP_REST_Request $request Request object.
242
	 * @return mixed
243
	 */
244
	public function prepare_view_for_response( $view_post, \WP_REST_Request $request ) {
245
		if ( is_wp_error( $this->get_item_permissions_check( $request, $view_post->ID ) ) ) {
246
			// Redacted out view.
247
			return array( 'ID' => $view_post->ID, 'post_content' => __( 'You are not allowed to access this content.', 'gravityview' ) );
248
		}
249
250
		$view = \GV\View::from_post( $view_post );
251
252
		$item = $view->as_data();
253
254
		// Add all the WP_Post data
255
		$view_post = $view_post->to_array();
256
257
		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'] );
258
259
		$return = wp_parse_args( $item, $view_post );
260
261
		$return['title'] = $return['post_title'];
262
263
		$return['settings'] = isset( $return['atts'] ) ? $return['atts'] : array();
264
		unset( $return['atts'], $return['view_id'] );
265
266
		$return['search_criteria'] = array(
267
			'page_size' => rgars( $return, 'settings/page_size' ),
268
			'sort_field' => rgars( $return, 'settings/sort_field' ),
269
			'sort_direction' => rgars( $return, 'settings/sort_direction' ),
270
			'offset' => rgars( $return, 'settings/offset' ),
271
		);
272
273
		unset( $return['settings']['page_size'], $return['settings']['sort_field'], $return['settings']['sort_direction'] );
274
275
		// Redact for non-logged ins
276
		if ( ! \GVCommon::has_cap( 'edit_others_gravityviews' ) ) {
277
			unset( $return['settings'] );
278
			unset( $return['search_criteria'] );
279
		}
280
		
281
		if ( ! \GFCommon::current_user_can_any( 'gravityforms_edit_forms' ) ) {
282
			unset( $return['form'] );
283
		}
284
285
		return $return;
286
	}
287
288
	public function get_item_permissions_check( $request ) {
289
		if ( func_num_args() == 2 ) {
0 ignored issues
show
introduced by
Found "== 2". Use Yoda Condition checks, you must
Loading history...
290
			$view_id = func_get_arg( 1 ); // $view_id override
291
		} else {
292
			$url     = $request->get_url_params();
293
			$view_id = intval( $url['id'] );
294
		}
295
296
		if ( ! $view = \GV\View::by_id( $view_id ) ) {
297
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
298
		}
299
300
		if ( post_password_required( $view->ID ) ) {
301
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
302
		}
303
304
		$public_states = get_post_stati( array( 'public' => true ) );
305
		if ( ! in_array( $view->post_status, $public_states ) && ! \GVCommon::has_cap( 'read_gravityview', $view->ID ) ) {
0 ignored issues
show
Documentation introduced by
The property post_status does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
306
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
307
		}
308
309
		// Shortcodes only
310
		$direct_access = apply_filters( 'gravityview_direct_access', true, $view->ID );
311
		if ( ! apply_filters( 'gravityview/view/output/direct', $direct_access, $view, $request ) ) {
312
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
313
		}
314
315
		// Embed only
316
		if ( $view->settings->get( 'embed_only' ) && ! \GVCommon::has_cap( 'read_private_gravityviews' ) ) {
317
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
318
		}
319
320
		// REST
321
		if ( gravityview()->plugin->settings->get( 'rest_api' ) === '1' && $view->settings->get( 'rest_disable' ) === '1' ) {
0 ignored issues
show
introduced by
Found "=== '". Use Yoda Condition checks, you must
Loading history...
322
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
323
		} elseif ( gravityview()->plugin->settings->get( 'rest_api' ) !== '1' && $view->settings->get( 'rest_enable' ) !== '1' ) {
0 ignored issues
show
introduced by
Found "!== '". Use Yoda Condition checks, you must
Loading history...
324
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
325
		}
326
327
		/**
328
		 * @filter `gravityview/view/output/rest` Disable rest output. Final chance.
329
		 * @param[in,out] bool Enable or not.
330
		 * @param \GV\View $view The view.
331
		 */
332
		if ( ! apply_filters( 'gravityview/view/output/rest', true, $view ) ) {
333
			return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gravityview' ) );
334
		}
335
336
		return true;
337
	}
338
339
	public function get_sub_item_permissions_check( $request ) {
340
		// Accessing a single entry needs the View access permissions.
341
		if ( is_wp_error( $error = $this->get_items_permissions_check( $request ) ) ) {
342
			return $error;
343
		}
344
345
		$url     = $request->get_url_params();
346
		$view_id = intval( $url['id'] );
347
		$entry_id = intval( $url['s_id'] );
348
349
		$view = \GV\View::by_id( $view_id );
350
351
		if ( ! $entry = \GV\GF_Entry::by_id( $entry_id ) ) {
352
			return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
353
		}
354
355
		if ( $entry['form_id'] != $view->form->ID ) {
356
			return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
357
		}
358
359
		if ( $entry['status'] != 'active' ) {
0 ignored issues
show
introduced by
Found "!= '". Use Yoda Condition checks, you must
Loading history...
360
			return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
361
		}
362
363
		if ( apply_filters( 'gravityview_custom_entry_slug', false ) && $entry->slug != get_query_var( \GV\Entry::get_endpoint_name() ) ) {
0 ignored issues
show
Bug introduced by
The property slug does not seem to exist in GV\Entry.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
364
			return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
365
		}
366
367
		$is_admin_and_can_view = $view->settings->get( 'admin_show_all_statuses' ) && \GVCommon::has_cap('gravityview_moderate_entries', $view->ID );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
368
369
		if ( $view->settings->get( 'show_only_approved' ) && ! $is_admin_and_can_view ) {
370
			if ( ! \GravityView_Entry_Approval_Status::is_approved( gform_get_meta( $entry->ID, \GravityView_Entry_Approval::meta_key ) )  ) {
371
				return new \WP_Error( 'rest_forbidden', 'You are not allowed to view this content.', 'gravityview' );
372
			}
373
		}
374
375
		return true;
376
	}
377
378
	public function get_items_permissions_check( $request ) {
379
		// Getting a list of all Views is always possible.
380
		return true;
381
	}
382
383
	public function get_sub_items_permissions_check( $request ) {
384
		// Accessing all entries of a View needs the same permissions as accessing the View.
385
		return $this->get_item_permissions_check( $request );
386
	}
387
}
388