Passed
Push — master ( f05c43...e0ac9d )
by Paul
04:29
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()
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