Passed
Push — master ( 78492a...4f268f )
by Paul
04:26
created

ValidateReview   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 46.02%

Importance

Changes 0
Metric Value
dl 0
loc 246
ccs 52
cts 113
cp 0.4602
rs 9.28
c 0
b 0
f 0
wmc 39

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getOption() 0 3 1
A isRecaptchaValid() 0 26 4
A validateHoneyPot() 0 6 3
A validateRecaptcha() 0 12 4
A getValidationRules() 0 10 2
A isRequestValid() 0 10 2
A validate() 0 14 2
A validateBlacklist() 0 11 4
A isRecaptchaResponseValid() 0 17 5
A validateRequest() 0 10 3
A validateCustom() 0 10 4
A validateAkismet() 0 6 3
A setSessionValues() 0 6 2
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 VALIDATION_RULES = [
16
		'content' => 'required|min:0',
17
		'email' => 'required|email|min:5',
18
		'name' => 'required',
19
		'rating' => 'required|numeric|between:1,5',
20
		'terms' => 'accepted',
21
		'title' => 'required',
22
	];
23
24
	/**
25
	 * @var string|void
26
	 */
27
	public $error;
28
29
	/**
30
	 * @var string
31
	 */
32
	public $form_id;
33
34
	/**
35
	 * @var array
36
	 */
37
	public $options;
38
39
	/**
40
	 * @var bool
41
	 */
42
	public $recaptchaIsUnset = false;
43
44
	/**
45
	 * @var array
46
	 */
47
	public $request;
48
49
	/**
50
	 * @return static
51
	 */
52 1
	public function validate( array $request )
53
	{
54 1
		$this->form_id = $request['form_id'];
55 1
		$this->options = glsr( OptionManager::class )->all();
56 1
		$this->request = $this->validateRequest( $request );
57 1
		$this->validateCustom();
58 1
		$this->validateHoneyPot();
59 1
		$this->validateBlacklist();
60 1
		$this->validateAkismet();
61 1
		$this->validateRecaptcha();
62 1
		if( !empty( $this->error )) {
63
			$this->setSessionValues( 'message', $this->error );
64
		}
65 1
		return $this;
66
	}
67
68
	/**
69
	 * @return array
70
	 */
71 1
	public function validateRequest( array $request )
72
	{
73 1
		if( !$this->isRequestValid( $request )) {
74
			$this->error = __( 'Please fix the submission errors.', 'site-reviews' );
75
			return $request;
76
		}
77 1
		if( empty( $request['title'] )) {
78
			$request['title'] = __( 'No Title', 'site-reviews' );
79
		}
80 1
		return array_merge( glsr( ValidateReviewDefaults::class )->defaults(), $request );
81
	}
82
83
	/**
84
	 * @param string $path
85
	 * @param mixed $fallback
86
	 * @return mixed
87
	 */
88 1
	protected function getOption( $path, $fallback = '' )
89
	{
90 1
		return glsr( Helper::class )->getPathValue( $path, $this->options, $fallback );
91
	}
92
93
	/**
94
	 * @return array
95
	 */
96 1
	protected function getValidationRules( array $request )
97
	{
98 1
		$rules = array_intersect_key(
99 1
			apply_filters( 'site-reviews/validation/rules', static::VALIDATION_RULES ),
100 1
			$this->getOption( 'settings.submissions.required', [] )
101
		);
102 1
		$excluded = isset( $request['excluded'] )
103 1
			? (array)json_decode( $request['excluded'] )
104 1
			: [];
105 1
		return array_diff_key( $rules, array_flip( $excluded ));
106
	}
107
108
	/**
109
	 * @return bool|null
110
	 */
111 1
	protected function isRecaptchaResponseValid()
112
	{
113 1
		$integration = $this->getOption( 'settings.submissions.recaptcha.integration' );
114 1
		if( !$integration ) {
115 1
			return true;
116
		}
117
		$recaptchaResponse = glsr( Helper::class )->filterInput( 'g-recaptcha-response' ); // @todo site-reviews[g-recaptcha-response]
118
		if( empty( $recaptchaResponse )) {
119
			return null; //if response is empty we need to return null
120
		}
121
		if( $integration == 'custom' ) {
122
			return $this->isRecaptchaValid( $recaptchaResponse );
123
		}
124
		if( $integration == 'invisible-recaptcha' ) {
125
			return boolval( apply_filters( 'google_invre_is_valid_request_filter', true ));
126
		}
127
		return false;
128
	}
129
130
	/**
131
	 * @return bool
132
	 */
133
	protected function isRecaptchaValid( $recaptchaResponse )
134
	{
135
		$endpoint = add_query_arg([
136
			'remoteip' => glsr( Helper::class )->getIpAddress(),
137
			'response' => $recaptchaResponse,
138
			'secret' => $this->getOption( 'settings.submissions.recaptcha.secret' ),
139
		], 'https://www.google.com/recaptcha/api/siteverify' );
140
		if( is_wp_error( $response = wp_remote_get( $endpoint ))) {
141
			glsr_log()->error( $response->get_error_message() );
142
			return false;
143
		}
144
		$response = json_decode( wp_remote_retrieve_body( $response ));
145
		if( !empty( $response->success )) {
146
			return boolval( $response->success );
147
		}
148
		$errorCodes = [
149
			'missing-input-secret' => 'The secret parameter is missing.',
150
			'invalid-input-secret' => 'The secret parameter is invalid or malformed.',
151
			'missing-input-response' => 'The response parameter is missing.',
152
			'invalid-input-response' => 'The response parameter is invalid or malformed.',
153
			'bad-request' => 'The request is invalid or malformed.',
154
		];
155
		foreach( $response->{'error-codes'} as $error ) {
156
			glsr_log()->error( 'reCAPTCHA: '.$errorCodes[$error] );
157
		}
158
		return false;
159
	}
160
161
	/**
162
	 * @return bool
163
	 */
164 1
	protected function isRequestValid( array $request )
165
	{
166 1
		$rules = $this->getValidationRules( $request );
167 1
		$errors = glsr( Validator::class )->validate( $request, $rules );
168 1
		if( empty( $errors )) {
169 1
			return true;
170
		}
171
		$this->setSessionValues( 'errors', $errors );
172
		$this->setSessionValues( 'values', $request );
173
		return false;
174
	}
175
176
	/**
177
	 * @param string $type
178
	 * @param mixed $value
179
	 * @param string $loggedMessage
180
	 * @return void
181
	 */
182
	protected function setSessionValues( $type, $value, $loggedMessage = '' )
183
	{
184
		glsr( Session::class )->set( $this->form_id.$type, $value );
185
		if( !empty( $loggedMessage )) {
186
			glsr_log()->warning( $loggedMessage );
187
			glsr_log()->warning( $this->request );
188
		}
189
	}
190
191
	/**
192
	 * @return void
193
	 */
194 1
	protected function validateAkismet()
195
	{
196 1
		if( !empty( $this->error ))return;
197 1
		if( !glsr( Akismet::class )->isSpam( $this->request ))return;
198
		$this->setSessionValues( 'errors', [], 'Akismet caught a spam submission:' );
199
		$this->error = __( 'Your review cannot be submitted at this time. Please try again later.', 'site-reviews' );
200
	}
201
202
	/**
203
	 * @return void
204
	 */
205 1
	protected function validateBlacklist()
206
	{
207 1
		if( !empty( $this->error ))return;
208 1
		if( !glsr( Blacklist::class )->isBlacklisted( $this->request ))return;
209
		$blacklistAction = $this->getOption( 'settings.submissions.blacklist.action' );
210
		if( $blacklistAction == 'reject' ) {
211
			$this->setSessionValues( 'errors', [], 'Blacklisted submission detected:' );
212
			$this->error = __( 'Your review cannot be submitted at this time.', 'site-reviews' );
213
			return;
214
		}
215
		$this->request['blacklisted'] = true;
216
	}
217
218
	/**
219
	 * @return void
220
	 */
221 1
	protected function validateCustom()
222
	{
223 1
		if( !empty( $this->error ))return;
224 1
		$validated = apply_filters( 'site-reviews/validate/review/submission', true, $this->request );
225 1
		if( $validated === true )return;
226
		$this->setSessionValues( 'errors', [] );
227
		$this->setSessionValues( 'values', $this->request );
228
		$this->error = is_string( $validated )
229
			? $validated
230
			: __( 'The review submission failed. Please notify the site administrator.', 'site-reviews' );
231
	}
232
233
	/**
234
	 * @return void
235
	 */
236 1
	protected function validateHoneyPot()
237
	{
238 1
		if( !empty( $this->error ))return;
239 1
		if( empty( $this->request['gotcha'] ))return;
240
		$this->setSessionValues( 'errors', [], 'The Honeypot caught a bad submission:' );
241
		$this->error = __( 'The review submission failed. Please notify the site administrator.', 'site-reviews' );
242
	}
243
244
	/**
245
	 * @return void
246
	 */
247 1
	protected function validateRecaptcha()
248
	{
249 1
		if( !empty( $this->error ))return;
250 1
		$isValid = $this->isRecaptchaResponseValid();
251 1
		if( is_null( $isValid )) {
252
			$this->setSessionValues( 'recaptcha', true );
253
			$this->recaptchaIsUnset = true;
254
		}
255 1
		else if( !$isValid ) {
256
			$this->setSessionValues( 'errors', [] );
257
			$this->setSessionValues( 'recaptcha', 'reset' );
258
			$this->error = __( 'The reCAPTCHA verification failed. Please notify the site administrator.', 'site-reviews' );
259
		}
260 1
	}
261
}
262