Passed
Push — master ( 81c350...2e0505 )
by Paul
04:39
created

ValidateReview::setSessionValues()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 3
dl 0
loc 6
ccs 0
cts 5
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Validator;
4
5
use GeminiLabs\SiteReviews\Database\OptionManager;
6
use GeminiLabs\SiteReviews\Defaults\ValidateReviewDefaults;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Modules\Akismet;
9
use GeminiLabs\SiteReviews\Modules\Blacklist;
10
use GeminiLabs\SiteReviews\Modules\Session;
11
use GeminiLabs\SiteReviews\Modules\Validator;
12
13
class ValidateReview
14
{
15
	const RECAPTCHA_ENDPOINT = 'https://www.google.com/recaptcha/api/siteverify';
16
17
	const VALIDATION_RULES = [
18
		'content' => 'required|min:0',
19
		'email' => 'required|email|min:5',
20
		'name' => 'required',
21
		'rating' => 'required|numeric|between:1,5',
22
		'terms' => 'accepted',
23
		'title' => 'required',
24
	];
25
26
	/**
27
	 * @var string|void
28
	 */
29
	public $error;
30
31
	/**
32
	 * @var string
33
	 */
34
	public $form_id;
35
36
	/**
37
	 * @var bool
38
	 */
39
	public $recaptchaIsUnset = false;
40
41
	/**
42
	 * @var array
43
	 */
44
	public $request;
45
46
	/**
47
	 * @var array
48
	 */
49
	protected $options;
50
51
	/**
52
	 * @return static
53
	 */
54 1
	public function validate( array $request )
55
	{
56 1
		$this->form_id = $request['form_id'];
57 1
		$this->options = glsr( OptionManager::class )->all();
58 1
		$this->request = $this->validateRequest( $request );
59 1
		$this->validateCustom();
60 1
		$this->validateHoneyPot();
61 1
		$this->validateBlacklist();
62 1
		$this->validateAkismet();
63 1
		$this->validateRecaptcha();
64 1
		if( !empty( $this->error )) {
65
			$this->setSessionValues( 'message', $this->error );
66
		}
67 1
		return $this;
68
	}
69
70
	/**
71
	 * @param string $path
72
	 * @param mixed $fallback
73
	 * @return mixed
74
	 */
75 1
	protected function getOption( $path, $fallback = '' )
76
	{
77 1
		return glsr( Helper::class )->getPathValue( $path, $this->options, $fallback );
78
	}
79
80
	/**
81
	 * @return array
82
	 */
83 1
	protected function getValidationRules( array $request )
84
	{
85 1
		$rules = array_intersect_key(
86 1
			apply_filters( 'site-reviews/validation/rules', static::VALIDATION_RULES ),
87 1
			$this->getOption( 'settings.submissions.required', [] )
88
		);
89 1
		$excluded = isset( $request['excluded'] )
90 1
			? (array)json_decode( $request['excluded'] )
91 1
			: [];
92 1
		return array_diff_key( $rules, array_flip( $excluded ));
93
	}
94
95
	/**
96
	 * @return bool|null
97
	 */
98 1
	protected function isRecaptchaResponseValid()
99
	{
100 1
		$integration = $this->getOption( 'settings.submissions.recaptcha.integration' );
101 1
		if( !$integration ) {
102 1
			return true;
103
		}
104
		if( empty( $this->request['recaptcha-token'] )) {
105
			return null; // @see $this->validateRecaptcha()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
106
		}
107
		if( $integration == 'custom' ) {
108
			return $this->isRecaptchaValid( $this->request['recaptcha-token'] );
109
		}
110
		if( $integration == 'invisible-recaptcha' ) {
111
			return boolval( apply_filters( 'google_invre_is_valid_request_filter', true ));
112
		}
113
		return false;
114
	}
115
116
	/**
117
	 * @param string $recaptchaToken
118
	 * @return bool
119
	 */
120
	protected function isRecaptchaValid( $recaptchaToken )
121
	{
122
		$endpoint = add_query_arg([
123
			'remoteip' => glsr( Helper::class )->getIpAddress(),
124
			'response' => $recaptchaToken,
125
			'secret' => $this->getOption( 'settings.submissions.recaptcha.secret' ),
126
		], static::RECAPTCHA_ENDPOINT );
127
		if( is_wp_error( $response = wp_remote_get( $endpoint ))) {
128
			glsr_log()->error( $response->get_error_message() );
129
			return false;
130
		}
131
		$response = json_decode( wp_remote_retrieve_body( $response ));
132
		if( !empty( $response->success )) {
133
			return boolval( $response->success );
134
		}
135
		$errorCodes = [
136
			'missing-input-secret' => 'The secret parameter is missing.',
137
			'invalid-input-secret' => 'The secret parameter is invalid or malformed.',
138
			'missing-input-response' => 'The response parameter is missing.',
139
			'invalid-input-response' => 'The response parameter is invalid or malformed.',
140
			'bad-request' => 'The request is invalid or malformed.',
141
		];
142
		foreach( $response->{'error-codes'} as $error ) {
143
			glsr_log()->error( 'reCAPTCHA: '.$errorCodes[$error] );
144
		}
145
		return false;
146
	}
147
148
	/**
149
	 * @return bool
150
	 */
151 1
	protected function isRequestValid( array $request )
152
	{
153 1
		$rules = $this->getValidationRules( $request );
154 1
		$errors = glsr( Validator::class )->validate( $request, $rules );
155 1
		if( empty( $errors )) {
156 1
			return true;
157
		}
158
		$this->setSessionValues( 'errors', $errors );
159
		$this->setSessionValues( 'values', $request );
160
		return false;
161
	}
162
163
	/**
164
	 * @param string $type
165
	 * @param mixed $value
166
	 * @param string $loggedMessage
167
	 * @return void
168
	 */
169
	protected function setSessionValues( $type, $value, $loggedMessage = '' )
170
	{
171
		glsr( Session::class )->set( $this->form_id.$type, $value );
172
		if( !empty( $loggedMessage )) {
173
			glsr_log()->warning( $loggedMessage );
174
			glsr_log()->warning( $this->request );
175
		}
176
	}
177
178
	/**
179
	 * @return void
180
	 */
181 1
	protected function validateAkismet()
182
	{
183 1
		if( !empty( $this->error ))return;
184 1
		if( !glsr( Akismet::class )->isSpam( $this->request ))return;
185
		$this->setSessionValues( 'errors', [], 'Akismet caught a spam submission:' );
186
		$this->error = __( 'Your review cannot be submitted at this time. Please try again later.', 'site-reviews' );
187
	}
188
189
	/**
190
	 * @return void
191
	 */
192 1
	protected function validateBlacklist()
193
	{
194 1
		if( !empty( $this->error ))return;
195 1
		if( !glsr( Blacklist::class )->isBlacklisted( $this->request ))return;
196
		$blacklistAction = $this->getOption( 'settings.submissions.blacklist.action' );
197
		if( $blacklistAction == 'reject' ) {
198
			$this->setSessionValues( 'errors', [], 'Blacklisted submission detected:' );
199
			$this->error = __( 'Your review cannot be submitted at this time.', 'site-reviews' );
200
			return;
201
		}
202
		$this->request['blacklisted'] = true;
203
	}
204
205
	/**
206
	 * @return void
207
	 */
208 1
	protected function validateCustom()
209
	{
210 1
		if( !empty( $this->error ))return;
211 1
		$validated = apply_filters( 'site-reviews/validate/review/submission', true, $this->request );
212 1
		if( $validated === true )return;
213
		$this->setSessionValues( 'errors', [] );
214
		$this->setSessionValues( 'values', $this->request );
215
		$this->error = is_string( $validated )
216
			? $validated
217
			: __( 'The review submission failed. Please notify the site administrator.', 'site-reviews' );
218
	}
219
220
	/**
221
	 * @return void
222
	 */
223 1
	protected function validateHoneyPot()
224
	{
225 1
		if( !empty( $this->error ))return;
226 1
		if( empty( $this->request['gotcha'] ))return;
227
		$this->setSessionValues( 'errors', [], 'The Honeypot caught a bad submission:' );
228
		$this->error = __( 'The review submission failed. Please notify the site administrator.', 'site-reviews' );
229
	}
230
231
	/**
232
	 * @return void
233
	 */
234 1
	protected function validateRecaptcha()
235
	{
236 1
		if( !empty( $this->error ))return;
237 1
		$isValid = $this->isRecaptchaResponseValid();
238 1
		if( is_null( $isValid )) {
239
			$this->setSessionValues( 'recaptcha', true );
240
			$this->recaptchaIsUnset = true;
241
		}
242 1
		else if( !$isValid ) {
243
			$this->setSessionValues( 'errors', [] );
244
			$this->setSessionValues( 'recaptcha', 'reset' );
245
			$this->error = __( 'The reCAPTCHA verification failed. Please notify the site administrator.', 'site-reviews' );
246
		}
247 1
	}
248
249
	/**
250
	 * @return array
251
	 */
252 1
	protected function validateRequest( array $request )
253
	{
254 1
		if( !$this->isRequestValid( $request )) {
255
			$this->error = __( 'Please fix the submission errors.', 'site-reviews' );
256
			return $request;
257
		}
258 1
		if( empty( $request['title'] )) {
259
			$request['title'] = __( 'No Title', 'site-reviews' );
260
		}
261 1
		return array_merge( glsr( ValidateReviewDefaults::class )->defaults(), $request );
262
	}
263
}
264