Passed
Push — master ( 1d1e0e...f469ea )
by Paul
04:52
created

SiteReviews::getExcerpt()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 16
ccs 0
cts 16
cp 0
crap 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules\Html\Partials;
4
5
use GeminiLabs\SiteReviews\Database;
6
use GeminiLabs\SiteReviews\Database\OptionManager;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Modules\Date;
9
use GeminiLabs\SiteReviews\Modules\Html;
10
use GeminiLabs\SiteReviews\Modules\Html\Builder;
11
use GeminiLabs\SiteReviews\Modules\Html\Partial;
12
use GeminiLabs\SiteReviews\Modules\Html\Review;
13
use GeminiLabs\SiteReviews\Modules\Html\Template;
14
use GeminiLabs\SiteReviews\Modules\Rating;
15
use GeminiLabs\SiteReviews\Modules\Schema;
16
use WP_Post;
17
18
class SiteReviews
19
{
20
	/**
21
	 * @var array
22
	 */
23
	protected $args;
24
25
	/**
26
	 * @var int
27
	 */
28
	protected $current;
29
30
	/**
31
	 * @var array
32
	 */
33
	protected $options;
34
35
	/**
36
	 * @var object
37
	 */
38
	protected $reviews;
39
40
	/**
41
	 * @return void|string
42
	 */
43
	public function build( array $args = [] )
44
	{
45
		$this->args = $args;
46
		$this->options = glsr( Helper::class )->flattenArray( glsr( OptionManager::class )->all() );
47
		$this->reviews = glsr( Database::class )->getReviews( $args );
48
		$this->generateSchema();
49
		$navigation = wp_validate_boolean( $this->args['pagination'] )
50
			? glsr( Partial::class )->build( 'pagination', ['total' => $this->reviews->max_num_pages] )
51
			: '';
52
		return glsr( Template::class )->build( 'templates/reviews', [
53
			'context' => [
54
				'class' => $this->getClass(),
55
				'id' => $this->args['id'],
56
				'navigation' => $navigation,
57
			],
58
			'reviews' => $this->buildReviews(),
59
		]);
60
	}
61
62
	/**
63
	 * @return array
64
	 */
65
	public function buildReviews()
66
	{
67
		$reviews = [];
68
		foreach( $this->reviews->results as $index => $review ) {
69
			$this->current = $index;
70
			$reviews[] = $this->buildReview( $review );
71
		}
72
		return $reviews;
73
	}
74
75
	/**
76
	 * @param object $review
77
	 * @return object
78
	 */
79
	protected function buildReview( $review )
80
	{
81
		$review = apply_filters( 'site-reviews/review/build/before', (array)$review );
82
		$reviewValues = [];
83
		foreach( $review as $key => $value ) {
84
			$method = glsr( Helper::class )->buildMethodName( $key, 'buildOption' );
85
			if( !method_exists( $this, $method ))continue;
86
			$reviewValues[$key] = $this->$method( $key, $value );
87
		}
88
		$reviewValues = apply_filters( 'site-reviews/review/build/after', $reviewValues );
89
		return new Review( $reviewValues );
90
	}
91
92
	/**
93
	 * @param string $key
94
	 * @param string $value
95
	 * @return void|string
96
	 */
97
	protected function buildOptionAssignedTo( $key, $value )
98
	{
99
		if( $this->isHiddenOrEmpty( $key, 'settings.reviews.assigned_links.enabled' ))return;
100
		$post = get_post( intval( $value ));
101
		if( !( $post instanceof WP_Post ))return;
102
		$permalink = glsr( Builder::class )->a( get_the_title( $post->ID ), [
103
			'href' => get_the_permalink( $post->ID ),
104
		]);
105
		$assignedTo = sprintf( __( 'Review of %s', 'site-reviews' ), $permalink );
106
		return $this->wrap( $key, '<span>'.$assignedTo.'</span>' );
107
	}
108
109
	/**
110
	 * @param string $key
111
	 * @param string $value
112
	 * @return void|string
113
	 */
114
	protected function buildOptionAuthor( $key, $value )
115
	{
116
		if( $this->isHidden( $key ))return;
117
		$prefix = !$this->isOptionEnabled( 'settings.reviews.avatars.enabled' )
118
			? apply_filters( 'site-reviews/review/author/prefix', '&mdash;' )
119
			: '';
120
		return $this->wrap( $key, $prefix.'<span>'.$value.'</span>' );
121
	}
122
123
	/**
124
	 * @param string $key
125
	 * @param string $value
126
	 * @return void|string
127
	 */
128
	protected function buildOptionAvatar( $key, $value )
129
	{
130
		if( $this->isHidden( $key, 'settings.reviews.avatars.enabled' ))return;
131
		$size = $this->getOption( 'settings.reviews.avatars.size', 40 );
132
		return $this->wrap( $key, glsr( Builder::class )->img([
133
			'src' => $this->generateAvatar( $value ),
134
			'height' => $size,
135
			'width' => $size,
136
		]));
137
	}
138
139
	/**
140
	 * @param string $key
141
	 * @param string $value
142
	 * @return void|string
143
	 */
144
	protected function buildOptionContent( $key, $value )
145
	{
146
		$text = $this->normalizeText( $value );
147
		if( $this->isHiddenOrEmpty( $key, $text ))return;
148
		return $this->wrap( $key, '<p>'.$text.'</p>' );
149
	}
150
151
	/**
152
	 * @param string $key
153
	 * @param string $value
154
	 * @return void|string
155
	 */
156
	protected function buildOptionDate( $key, $value )
157
	{
158
		if( $this->isHidden( $key ))return;
159
		$dateFormat = $this->getOption( 'settings.reviews.date.format', 'default' );
160
		if( $dateFormat == 'relative' ) {
161
			$date = glsr( Date::class )->relative( $value );
162
		}
163
		else {
164
			$format = $dateFormat == 'custom'
165
				? $this->getOption( 'settings.reviews.date.custom', 'M j, Y' )
166
				: (string)get_option( 'date_format' );
167
			$date = date_i18n( $format, strtotime( $value ));
168
		}
169
		return $this->wrap( $key, '<span>'.$date.'</span>' );
170
	}
171
172
	/**
173
	 * @param string $key
174
	 * @param string $value
175
	 * @return void|string
176
	 */
177
	protected function buildOptionRating( $key, $value )
178
	{
179
		if( $this->isHiddenOrEmpty( $key, $value ))return;
180
		$rating = glsr( Html::class )->buildPartial( 'star-rating', [
181
			'rating' => $value,
182
		]);
183
		return $this->wrap( $key, $rating );
184
	}
185
186
	/**
187
	 * @param string $key
188
	 * @param string $value
189
	 * @return void|string
190
	 */
191
	protected function buildOptionResponse( $key, $value )
192
	{
193
		if( $this->isHiddenOrEmpty( $key, $value ))return;
194
		$title = sprintf( __( 'Response from %s', 'site-reviews' ), get_bloginfo( 'name' ));
195
		$text = $this->normalizeText( $value );
196
		$text = '<p><strong>'.$title.'</strong></p><p>'.$text.'</p>';
197
		return $this->wrap( $key,
198
			glsr( Builder::class )->div( $text, ['class' => 'glsr-review-response-inner'] ).
199
			glsr( Builder::class )->div( ['class' => 'glsr-review-response-background'] )
200
		);
201
	}
202
203
	/**
204
	 * @param string $key
205
	 * @param string $value
206
	 * @return void|string
207
	 */
208
	protected function buildOptionTitle( $key, $value )
209
	{
210
		if( $this->isHidden( $key ))return;
211
		if( empty( $value )) {
212
			$value = __( 'No Title', 'site-reviews' );
213
		}
214
		return $this->wrap( $key, '<h3>'.$value.'</h3>' );
215
	}
216
217
	/**
218
	 * @param string $avatarUrl
219
	 * @return string
220
	 */
221
	protected function generateAvatar( $avatarUrl )
222
	{
223
		$review = $this->reviews->results[$this->current];
224
		if( !$this->isOptionEnabled( 'settings.reviews.avatars.regenerate' )
225
			|| $review->review_type != 'local' ) {
226
			return $avatarUrl;
227
		}
228
		$authorIdOrEmail = get_the_author_meta( 'ID', $review->user_id );
229
		if( empty( $authorIdOrEmail )) {
230
			$authorIdOrEmail = $review->email;
231
		}
232
		return (string)get_avatar_url( $authorIdOrEmail );
233
	}
234
235
	/**
236
	 * @return void
237
	 */
238
	protected function generateSchema()
239
	{
240
		if( !wp_validate_boolean( $this->args['schema'] ))return;
241
		glsr( Schema::class )->store(
242
			glsr( Schema::class )->build( $this->args )
243
		);
244
	}
245
246
	/**
247
	 * @return string
248
	 */
249
	protected function getClass()
250
	{
251
		$style = apply_filters( 'site-reviews/reviews/style', 'glsr-style' );
252
		$pagination = $this->args['pagination'] == 'ajax'
253
			? 'glsr-ajax-pagination'
254
			: '';
255
		return trim( 'glsr-reviews '.$style.' '.$pagination.' '.$this->args['class'] );
256
	}
257
258
	/**
259
	 * @param string $text
260
	 * @return string
261
	 */
262
	protected function getExcerpt( $text )
263
	{
264
		$limit = intval( $this->getOption( 'settings.reviews.excerpt.length', 55 ));
265
		$split = extension_loaded( 'intl' )
266
			? $this->getExcerptIntlSplit( $text, $limit )
267
			: $this->getExcerptSplit( $text, $limit );
268
		$hiddenText = substr( $text, $split );
269
		if( !empty( $hiddenText )) {
270
			$showMore = glsr( Builder::class )->span( $hiddenText, [
271
				'class' => 'glsr-hidden glsr-hidden-text',
272
				'data-show-less' => __( 'Show less', 'site-reviews' ),
273
				'data-show-more' => __( 'Show more', 'site-reviews' ),
274
			]);
275
			$text = ltrim( substr( $text, 0, $split )).$showMore;
276
		}
277
		return nl2br( $text );
278
	}
279
280
	/**
281
	 * @param string $text
282
	 * @param int $limit
283
	 * @return int
284
	 */
285
	public function getExcerptIntlSplit( $text, $limit )
286
	{
287
		$words = \IntlBreakIterator::createWordInstance( '' );
288
		$words->setText( $text );
289
		$count = 0;
290
		$split = strlen( $text );
291
		foreach( $words as $offset ){
292
			if( \IntlBreakIterator::WORD_NONE === $words->getRuleStatus() )continue;
0 ignored issues
show
Bug introduced by
The method getRuleStatus() does not exist on IntlBreakIterator. It seems like you code against a sub-type of IntlBreakIterator such as IntlRuleBasedBreakIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

292
			if( \IntlBreakIterator::WORD_NONE === $words->/** @scrutinizer ignore-call */ getRuleStatus() )continue;
Loading history...
293
			$count++;
294
			if( $count == $limit ) {
295
				$split = $offset;
296
				break;
297
			}
298
		}
299
		return $split;
300
	}
301
302
	/**
303
	 * @param string $text
304
	 * @param int $limit
305
	 * @return int
306
	 */
307
	public function getExcerptSplit( $text, $limit )
308
	{
309
		if( str_word_count( $text, 0 ) > $limit ) {
310
			$words = array_keys( str_word_count( $text, 2 ));
311
			return $words[$limit];
312
		}
313
		return strlen( $text );
314
	}
315
316
	/**
317
	 * @param string $path
318
	 * @param mixed $fallback
319
	 * @return mixed
320
	 */
321
	protected function getOption( $path, $fallback = '' )
322
	{
323
		if( array_key_exists( $path, $this->options )) {
324
			return $this->options[$path];
325
		}
326
		return $fallback;
327
	}
328
329
	/**
330
	 * @param string $key
331
	 * @param string $path
332
	 * @return bool
333
	 */
334
	protected function isHidden( $key, $path = '' )
335
	{
336
		$isOptionEnabled = !empty( $path )
337
			? $this->isOptionEnabled( $path )
338
			: true;
339
		return in_array( $key, $this->args['hide'] ) || !$isOptionEnabled;
340
	}
341
342
	/**
343
	 * @param string $key
344
	 * @param string $value
345
	 * @return bool
346
	 */
347
	protected function isHiddenOrEmpty( $key, $value )
348
	{
349
		return $this->isHidden( $key ) || empty( $value );
350
	}
351
352
	/**
353
	 * @param string $path
354
	 * @return bool
355
	 */
356
	protected function isOptionEnabled( $path )
357
	{
358
		return $this->getOption( $path ) == 'yes';
359
	}
360
361
	/**
362
	 * @param string $text
363
	 * @return string
364
	 */
365
	protected function normalizeText( $text )
366
	{
367
		$text = wp_kses( $text, wp_kses_allowed_html() );
368
		$text = convert_smilies( wptexturize( strip_shortcodes( $text )));
369
		$text = str_replace( ']]>', ']]&gt;', $text );
370
		$text = preg_replace( '/(\R){2,}/', '$1', $text );
371
		return $this->isOptionEnabled( 'settings.reviews.excerpt.enabled' )
372
			? $this->getExcerpt( $text )
373
			: $text;
374
	}
375
376
	/**
377
	 * @param string $key
378
	 * @param string $value
379
	 * @return string
380
	 */
381
	protected function wrap( $key, $value )
382
	{
383
		return glsr( Builder::class )->div( $value, [
384
			'class' => 'glsr-review-'.$key,
385
		]);
386
	}
387
}
388