Jetpack_Recipes::recipe_shortcode_html()   F
last analyzed

Complexity

Conditions 24
Paths 5208

Size

Total Lines 126

Duplication

Lines 24
Ratio 19.05 %

Importance

Changes 0
Metric Value
cc 24
nc 5208
nop 2
dl 24
loc 126
rs 0
c 0
b 0
f 0

How to fix   Long Method    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 //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3
use Automattic\Jetpack\Assets;
4
5
/**
6
 * Embed recipe 'cards' in post, with basic styling and print functionality
7
 *
8
 * To Do
9
 * - defaults settings
10
 * - basic styles/themecolor styles
11
 * - validation/sanitization
12
 * - print styles
13
 *
14
 * @package automattic/jetpack
15
 */
16
17
/**
18
 * Register and display Recipes in posts.
19
 */
20
class Jetpack_Recipes {
21
22
	/**
23
	 * Have scripts and styles been enqueued already.
24
	 *
25
	 * @var bool
26
	 */
27
	private $scripts_and_style_included = false;
28
29
	/**
30
	 * Constructor
31
	 */
32
	public function __construct() {
33
		add_action( 'init', array( $this, 'action_init' ) );
34
	}
35
36
	/**
37
	 * Returns KSES tags with Schema-specific attributes.
38
	 *
39
	 * @since 8.0.0
40
	 *
41
	 * @return array Array to be used by KSES.
42
	 */
43
	private static function kses_tags() {
44
		$allowedtags = wp_kses_allowed_html( 'post' );
45
		// Create an array of all the tags we'd like to add the itemprop attribute to.
46
		$tags = array( 'li', 'ol', 'ul', 'img', 'p', 'h3', 'time', 'span' );
47
		foreach ( $tags as $tag ) {
48
			if ( ! isset( $allowedtags[ $tag ] ) ) {
49
				$allowedtags[ $tag ] = array();
50
			}
51
			$allowedtags[ $tag ]['class']    = array();
52
			$allowedtags[ $tag ]['itemprop'] = array();
53
			$allowedtags[ $tag ]['datetime'] = array();
54
		}
55
56
		// Allow the handler <a on=""> in AMP.
57
		$allowedtags['a']['on'] = array();
58
59
		// Allow itemscope and itemtype for divs.
60
		if ( ! isset( $allowedtags['div'] ) ) {
61
			$allowedtags['div'] = array();
62
		}
63
		$allowedtags['div']['class']     = array();
64
		$allowedtags['div']['itemscope'] = array();
65
		$allowedtags['div']['itemtype']  = array();
66
		return $allowedtags;
67
	}
68
69
	/**
70
	 * Register our shortcode and enqueue necessary files.
71
	 */
72
	public function action_init() {
73
		// Enqueue styles if [recipe] exists.
74
		add_action( 'wp_head', array( $this, 'add_scripts' ), 1 );
75
76
		// Render [recipe], along with other shortcodes that can be nested within.
77
		add_shortcode( 'recipe', array( $this, 'recipe_shortcode' ) );
78
		add_shortcode( 'recipe-notes', array( $this, 'recipe_notes_shortcode' ) );
79
		add_shortcode( 'recipe-ingredients', array( $this, 'recipe_ingredients_shortcode' ) );
80
		add_shortcode( 'recipe-directions', array( $this, 'recipe_directions_shortcode' ) );
81
		add_shortcode( 'recipe-nutrition', array( $this, 'recipe_nutrition_shortcode' ) );
82
		add_shortcode( 'recipe-image', array( $this, 'recipe_image_shortcode' ) );
83
	}
84
85
	/**
86
	 * Enqueue scripts and styles
87
	 */
88
	public function add_scripts() {
89
		if ( empty( $GLOBALS['posts'] ) || ! is_array( $GLOBALS['posts'] ) ) {
90
			return;
91
		}
92
93 View Code Duplication
		foreach ( $GLOBALS['posts'] as $p ) {
94
			if ( has_shortcode( $p->post_content, 'recipe' ) ) {
95
				$this->scripts_and_style_included = true;
96
				break;
97
			}
98
		}
99
100
		if ( ! $this->scripts_and_style_included ) {
101
			return;
102
		}
103
104
		wp_enqueue_style( 'jetpack-recipes-style', plugins_url( '/css/recipes.css', __FILE__ ), array(), '20130919' );
105
		wp_style_add_data( 'jetpack-recipes-style', 'rtl', 'replace' );
106
107
		// add $themecolors-defined styles.
108
		wp_add_inline_style( 'jetpack-recipes-style', self::themecolor_styles() );
109
110
		if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
111
			return;
112
		}
113
114
		wp_enqueue_script(
115
			'jetpack-recipes-printthis',
116
			Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes-printthis.min.js', 'modules/shortcodes/js/recipes-printthis.js' ),
117
			array( 'jquery' ),
118
			'20170202',
119
			false
120
		);
121
122
		wp_enqueue_script(
123
			'jetpack-recipes-js',
124
			Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes.min.js', 'modules/shortcodes/js/recipes.js' ),
125
			array( 'jquery', 'jetpack-recipes-printthis' ),
126
			'20131230',
127
			false
128
		);
129
130
		$title_var     = wp_title( '|', false, 'right' );
131
		$rtl           = is_rtl() ? '-rtl' : '';
132
		$print_css_var = plugins_url( "/css/recipes-print{$rtl}.css", __FILE__ );
133
134
		wp_localize_script(
135
			'jetpack-recipes-js',
136
			'jetpack_recipes_vars',
137
			array(
138
				'pageTitle' => $title_var,
139
				'loadCSS'   => $print_css_var,
140
			)
141
		);
142
	}
143
144
	/**
145
	 * Our [recipe] shortcode.
146
	 * Prints recipe data styled to look good on *any* theme.
147
	 *
148
	 * @param array  $atts    Array of shortcode attributes.
149
	 * @param string $content Post content.
150
	 *
151
	 * @return string HTML for recipe shortcode.
152
	 */
153
	public static function recipe_shortcode( $atts, $content = '' ) {
154
		$atts = shortcode_atts(
155
			array(
156
				'title'       => '', // string.
157
				'servings'    => '', // intval.
158
				'time'        => '', // strtotime-compatible time description.
159
				'difficulty'  => '', // string.
160
				'print'       => '', // URL for external print version.
161
				'source'      => '', // string.
162
				'sourceurl'   => '', // URL string. Only used if source set.
163
				'image'       => '', // URL or attachment ID.
164
				'description' => '', // string.
165
				'cooktime'    => '', // strtotime-compatible time description.
166
				'preptime'    => '', // strtotime-compatible time description.
167
				'rating'      => '', // string.
168
			),
169
			$atts,
170
			'recipe'
171
		);
172
173
		return self::recipe_shortcode_html( $atts, $content );
174
	}
175
176
	/**
177
	 * The recipe output
178
	 *
179
	 * @param array  $atts    Array of shortcode attributes.
180
	 * @param string $content Post content.
181
	 *
182
	 * @return string HTML output
183
	 */
184
	private static function recipe_shortcode_html( $atts, $content = '' ) {
185
186
		$html = '<div class="hrecipe h-recipe jetpack-recipe" itemscope itemtype="https://schema.org/Recipe">';
187
188
		// Print the recipe title if exists.
189
		if ( '' !== $atts['title'] ) {
190
			$html .= '<h3 class="p-name jetpack-recipe-title fn" itemprop="name">' . esc_html( $atts['title'] ) . '</h3>';
191
		}
192
193
		// Print the recipe meta if exists.
194
		if (
195
			'' !== $atts['servings']
196
			|| '' !== $atts['time']
197
			|| '' !== $atts['difficulty']
198
			|| '' !== $atts['print']
199
			|| '' !== $atts['preptime']
200
			|| '' !== $atts['cooktime']
201
			|| '' !== $atts['rating']
202
		) {
203
			$html .= '<ul class="jetpack-recipe-meta">';
204
205 View Code Duplication
			if ( '' !== $atts['servings'] ) {
206
				$html .= sprintf(
207
					'<li class="jetpack-recipe-servings p-yield yield" itemprop="recipeYield"><strong>%1$s: </strong>%2$s</li>',
208
					esc_html_x( 'Servings', 'recipe', 'jetpack' ),
209
					esc_html( $atts['servings'] )
210
				);
211
			}
212
213
			$time_types = array( 'preptime', 'cooktime', 'time' );
214
			foreach ( $time_types as $time_type ) {
215
				if ( '' === $atts[ $time_type ] ) {
216
					continue;
217
				}
218
				$html .= self::output_time( $atts[ $time_type ], $time_type );
219
			}
220
221 View Code Duplication
			if ( '' !== $atts['difficulty'] ) {
222
				$html .= sprintf(
223
					'<li class="jetpack-recipe-difficulty"><strong>%1$s: </strong>%2$s</li>',
224
					esc_html_x( 'Difficulty', 'recipe', 'jetpack' ),
225
					esc_html( $atts['difficulty'] )
226
				);
227
			}
228
229 View Code Duplication
			if ( '' !== $atts['rating'] ) {
230
				$html .= sprintf(
231
					'<li class="jetpack-recipe-rating">
232
						<strong>%1$s: </strong>
233
						<span itemprop="contentRating">%2$s</span>
234
					</li>',
235
					esc_html_x( 'Rating', 'recipe', 'jetpack' ),
236
					esc_html( $atts['rating'] )
237
				);
238
			}
239
240
			if ( '' !== $atts['source'] ) {
241
				$html .= sprintf(
242
					'<li class="jetpack-recipe-source"><strong>%1$s: </strong>',
243
					esc_html_x( 'Source', 'recipe', 'jetpack' )
244
				);
245
246
				if ( '' !== $atts['sourceurl'] ) :
247
					// Show the link if we have one.
248
					$html .= sprintf(
249
						'<a href="%2$s">%1$s</a>',
250
						esc_html( $atts['source'] ),
251
						esc_url( $atts['sourceurl'] )
252
					);
253
				else :
254
					// Skip the link.
255
					$html .= sprintf(
256
						'%1$s',
257
						esc_html( $atts['source'] )
258
					);
259
				endif;
260
261
				$html .= '</li>';
262
			}
263
264
			if ( 'false' !== $atts['print'] ) {
265
				$is_amp       = class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
266
				$print_action = $is_amp ? 'on="tap:AMP.print"' : '';
267
				$print_text   = $is_amp ? esc_html__( 'Print page', 'jetpack' ) : esc_html_x( 'Print', 'recipe', 'jetpack' );
268
				$html        .= sprintf(
269
					'<li class="jetpack-recipe-print"><a href="#" %1$s>%2$s</a></li>',
270
					$print_action,
271
					$print_text
272
				);
273
			}
274
275
			$html .= '</ul>';
276
		}
277
278
		// Output the image if we have one and it's not shown elsewhere.
279
		if ( '' !== $atts['image'] ) {
280
			if ( ! has_shortcode( $content, 'recipe-image' ) ) {
281
				$html .= self::output_image_html( $atts['image'] );
282
			}
283
		}
284
285
		// Output the description, if we have one.
286
		if ( '' !== $atts['description'] ) {
287
			$html .= sprintf(
288
				'<p class="jetpack-recipe-description" itemprop="description">%1$s</p>',
289
				esc_html( $atts['description'] )
290
			);
291
		}
292
293
		// Print content between codes.
294
		$html .= '<div class="jetpack-recipe-content">' . do_shortcode( $content ) . '</div>';
295
296
		// Close it up.
297
		$html .= '</div>';
298
299
		// If there is a recipe within a recipe, remove the shortcode.
300
		if ( has_shortcode( $html, 'recipe' ) ) {
301
			remove_shortcode( 'recipe' );
302
		}
303
304
		// Sanitize html.
305
		$html = wp_kses( $html, self::kses_tags() );
306
307
		// Return the HTML block.
308
		return $html;
309
	}
310
311
	/**
312
	 * Our [recipe-image] shortcode.
313
	 * Controls placement of image in recipe.
314
	 *
315
	 * @param array $atts Array of shortcode attributes.
316
	 *
317
	 * @return string HTML for recipe notes shortcode.
318
	 */
319
	public static function recipe_image_shortcode( $atts ) {
320
		$atts = shortcode_atts(
321
			array(
322
				'image' => '', // string.
323
				0       => '', // string.
324
			),
325
			$atts,
326
			'recipe-image'
327
		);
328
		$src  = $atts['image'];
329
		if ( ! empty( $atts[0] ) ) {
330
			$src = $atts[0];
331
		}
332
		return self::output_image_html( $src );
333
	}
334
335
	/**
336
	 * Our [recipe-notes] shortcode.
337
	 * Outputs ingredients, styled in a div.
338
	 *
339
	 * @param array  $atts    Array of shortcode attributes.
340
	 * @param string $content Post content.
341
	 *
342
	 * @return string HTML for recipe notes shortcode.
343
	 */
344 View Code Duplication
	public static function recipe_notes_shortcode( $atts, $content = '' ) {
345
		$atts = shortcode_atts(
346
			array(
347
				'title' => '', // string.
348
			),
349
			$atts,
350
			'recipe-notes'
351
		);
352
353
		$html = '';
354
355
		// Print a title if one exists.
356
		if ( '' !== $atts['title'] ) {
357
			$html .= '<h4 class="jetpack-recipe-notes-title">' . esc_html( $atts['title'] ) . '</h4>';
358
		}
359
360
		$html .= '<div class="jetpack-recipe-notes">';
361
362
		// Format content using list functionality, if desired.
363
		$html .= self::output_list_content( $content, 'notes' );
364
365
		$html .= '</div>';
366
367
		// Sanitize html.
368
		$html = wp_kses( $html, self::kses_tags() );
369
370
		// Return the HTML block.
371
		return $html;
372
	}
373
374
	/**
375
	 * Our [recipe-ingredients] shortcode.
376
	 * Outputs notes, styled in a div.
377
	 *
378
	 * @param array  $atts    Array of shortcode attributes.
379
	 * @param string $content Post content.
380
	 *
381
	 * @return string HTML for recipe ingredients shortcode.
382
	 */
383 View Code Duplication
	public static function recipe_ingredients_shortcode( $atts, $content = '' ) {
384
		$atts = shortcode_atts(
385
			array(
386
				'title' => esc_html_x( 'Ingredients', 'recipe', 'jetpack' ), // string.
387
			),
388
			$atts,
389
			'recipe-ingredients'
390
		);
391
392
		$html = '<div class="jetpack-recipe-ingredients">';
393
394
		// Print a title unless the user has opted to exclude it.
395
		if ( 'false' !== $atts['title'] ) {
396
			$html .= '<h4 class="jetpack-recipe-ingredients-title">' . esc_html( $atts['title'] ) . '</h4>';
397
		}
398
399
		// Format content using list functionality.
400
		$html .= self::output_list_content( $content, 'ingredients' );
401
402
		$html .= '</div>';
403
404
		// Sanitize html.
405
		$html = wp_kses( $html, self::kses_tags() );
406
407
		// Return the HTML block.
408
		return $html;
409
	}
410
411
	/**
412
	 * Our [recipe-nutrition] shortcode.
413
	 * Outputs notes, styled in a div.
414
	 *
415
	 * @param array  $atts    Array of shortcode attributes.
416
	 * @param string $content Post content.
417
	 *
418
	 * @return string HTML for recipe nutrition shortcode.
419
	 */
420 View Code Duplication
	public static function recipe_nutrition_shortcode( $atts, $content = '' ) {
421
		$atts = shortcode_atts(
422
			array(
423
				'title' => esc_html_x( 'Nutrition', 'recipe', 'jetpack' ), // string.
424
			),
425
			$atts,
426
			'recipe-nutrition'
427
		);
428
429
		$html = '<div class="jetpack-recipe-nutrition p-nutrition nutrition">';
430
431
		// Print a title unless the user has opted to exclude it.
432
		if ( 'false' !== $atts['title'] ) {
433
			$html .= '<h4 class="jetpack-recipe-nutrition-title">' . esc_html( $atts['title'] ) . '</h4>';
434
		}
435
436
		// Format content using list functionality.
437
		$html .= self::output_list_content( $content, 'nutrition' );
438
439
		$html .= '</div>';
440
441
		// Sanitize html.
442
		$html = wp_kses( $html, self::kses_tags() );
443
444
		// Return the HTML block.
445
		return $html;
446
	}
447
448
	/**
449
	 * Reusable function to check for shortened formatting.
450
	 * Basically, users can create lists with the following shorthand:
451
	 * - item one
452
	 * - item two
453
	 * - item three
454
	 * And we'll magically convert it to a list. This has the added benefit
455
	 * of including itemprops for the recipe schema.
456
	 *
457
	 * @param string $content HTML content.
458
	 * @param string $type    Type of list.
459
	 *
460
	 * @return string content formatted as a list item
461
	 */
462
	private static function output_list_content( $content, $type ) {
463
		$html = '';
464
465
		switch ( $type ) {
466
			case 'directions':
467
				$list_item_replacement = '<li class="jetpack-recipe-directions">${1}</li>';
468
				$itemprop              = ' itemprop="recipeInstructions"';
469
				$listtype              = 'ol';
0 ignored issues
show
Unused Code introduced by
$listtype is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
470
				break;
471
			case 'ingredients':
472
				$list_item_replacement = '<li class="jetpack-recipe-ingredient p-ingredient ingredient" itemprop="recipeIngredient">${1}</li>';
473
				$itemprop              = '';
474
				$listtype              = 'ul';
0 ignored issues
show
Unused Code introduced by
$listtype is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
475
				break;
476
			case 'nutrition':
477
				$list_item_replacement = '<li class="jetpack-recipe-nutrition">${1}</li>';
478
				$itemprop              = ' itemprop="nutrition"';
479
				$listtype              = 'ul';
0 ignored issues
show
Unused Code introduced by
$listtype is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
480
				break;
481
			case 'nutrition':
482
				$list_item_replacement = '<li class="jetpack-recipe-nutrition nutrition">${1}</li>';
483
				$itemprop              = ' itemprop="nutrition"';
484
				$listtype              = 'ul';
0 ignored issues
show
Unused Code introduced by
$listtype is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
485
				break;
486
			default:
487
				$list_item_replacement = '<li class="jetpack-recipe-notes">${1}</li>';
488
				$itemprop              = '';
489
				$listtype              = 'ul';
0 ignored issues
show
Unused Code introduced by
$listtype is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
490
		}
491
492
		// Check to see if the user is trying to use shortened formatting.
493
		if (
494
			strpos( $content, '&#8211;' ) !== false ||
495
			strpos( $content, '&#8212;' ) !== false ||
496
			strpos( $content, '-' ) !== false ||
497
			strpos( $content, '*' ) !== false ||
498
			strpos( $content, '#' ) !== false ||
499
			strpos( $content, '–' ) !== false || // ndash.
500
			strpos( $content, '—' ) !== false || // mdash.
501
			preg_match( '/\d+\.\s/', $content )
502
		) {
503
			// Remove breaks and extra whitespace.
504
			$content = str_replace( "<br />\n", "\n", $content );
505
			$content = trim( $content );
506
507
			$ul_pattern = '/(?:^|\n|\<p\>)+(?:[\-–—]+|\&#8211;|\&#8212;|\*)+\h+(.*)/mi';
508
			$ol_pattern = '/(?:^|\n|\<p\>)+(?:\d+\.|#+)+\h+(.*)/mi';
509
510
			preg_match_all( $ul_pattern, $content, $ul_matches );
511
			preg_match_all( $ol_pattern, $content, $ol_matches );
512
513
			if ( 0 !== count( $ul_matches[0] ) || 0 !== count( $ol_matches[0] ) ) {
514
515
				if ( 0 !== count( $ol_matches[0] ) ) {
516
					$listtype          = 'ol';
517
					$list_item_pattern = $ol_pattern;
518
				} else {
519
					$listtype          = 'ul';
520
					$list_item_pattern = $ul_pattern;
521
				}
522
				$html .= '<' . $listtype . $itemprop . '>';
523
				$html .= preg_replace( $list_item_pattern, $list_item_replacement, $content );
524
				$html .= '</' . $listtype . '>';
525
526
				// Strip out any empty <p> tags and stray </p> tags, because those are just silly.
527
				$empty_p_pattern = '/(<p>)*\s*<\/p>/mi';
528
				$html            = preg_replace( $empty_p_pattern, '', $html );
529
			} else {
530
				$html .= do_shortcode( $content );
531
			}
532
		} else {
533
			$html .= do_shortcode( $content );
534
		}
535
536
		// Return our formatted content.
537
		return $html;
538
	}
539
540
	/**
541
	 * Our [recipe-directions] shortcode.
542
	 * Outputs directions, styled in a div.
543
	 *
544
	 * @param array  $atts    Array of shortcode attributes.
545
	 * @param string $content Post content.
546
	 *
547
	 * @return string HTML for recipe directions shortcode.
548
	 */
549 View Code Duplication
	public static function recipe_directions_shortcode( $atts, $content = '' ) {
550
		$atts = shortcode_atts(
551
			array(
552
				'title' => esc_html_x( 'Directions', 'recipe', 'jetpack' ), // string.
553
			),
554
			$atts,
555
			'recipe-directions'
556
		);
557
558
		$html = '<div class="jetpack-recipe-directions e-instructions">';
559
560
		// Print a title unless the user has specified to exclude it.
561
		if ( 'false' !== $atts['title'] ) {
562
			$html .= '<h4 class="jetpack-recipe-directions-title">' . esc_html( $atts['title'] ) . '</h4>';
563
		}
564
565
		// Format content using list functionality.
566
		$html .= self::output_list_content( $content, 'directions' );
567
568
		$html .= '</div>';
569
570
		// Sanitize html.
571
		$html = wp_kses( $html, self::kses_tags() );
572
573
		// Return the HTML block.
574
		return $html;
575
	}
576
577
	/**
578
	 * Outputs time meta tag.
579
	 *
580
	 * @param string $time_str  Raw time to output.
581
	 * @param string $time_type Type of time to show.
582
	 *
583
	 * @return string HTML for recipe time meta.
584
	 */
585
	private static function output_time( $time_str, $time_type ) {
586
		// Get a time that's supported by Schema.org.
587
		$duration = WPCOM_JSON_API_Date::format_duration( $time_str );
588
		// If no duration can be calculated, let's output what the user provided.
589
		if ( ! $duration ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $duration of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
590
			$duration = $time_str;
591
		}
592
593
		switch ( $time_type ) {
594
			case 'cooktime':
595
				$title    = _x( 'Cook Time', 'recipe', 'jetpack' );
596
				$itemprop = 'cookTime';
597
				break;
598
			case 'preptime':
599
				$title    = _x( 'Prep Time', 'recipe', 'jetpack' );
600
				$itemprop = 'prepTime';
601
				break;
602
			default:
603
				$title    = _x( 'Time', 'recipe', 'jetpack' );
604
				$itemprop = 'totalTime';
605
				break;
606
		}
607
608
		return sprintf(
609
			'<li class="jetpack-recipe-%3$s">
610
				<time itemprop="%4$s" datetime="%5$s"><strong>%1$s:</strong> <span class="%3$s">%2$s</span></time>
611
			</li>',
612
			esc_html( $title ),
613
			esc_html( $time_str ),
614
			esc_attr( $time_type ),
615
			esc_attr( $itemprop ),
616
			esc_attr( $duration )
617
		);
618
	}
619
620
	/**
621
	 * Outputs image tag for recipe.
622
	 *
623
	 * @param string $src The image source.
624
	 *
625
	 * @return string
626
	 */
627
	private static function output_image_html( $src ) {
628
		// Exit if there is no provided source.
629
		if ( ! $src ) {
630
			return '';
631
		}
632
633
		$image_attrs = array(
634
			'class'    => 'jetpack-recipe-image u-photo photo',
635
			'itemprop' => 'image',
636
		);
637
638
		if (
639
			function_exists( 'wp_lazy_loading_enabled' )
640
			&& wp_lazy_loading_enabled( 'img', 'wp_get_attachment_image' )
641
		) {
642
			$image_attrs['loading'] = 'lazy';
643
		}
644
645
		// If it's numeric, this may be an attachment.
646
		if ( is_numeric( $src ) ) {
647
			return wp_get_attachment_image(
648
				$src,
649
				'full',
650
				false,
651
				$image_attrs
652
			);
653
		}
654
655
		// Check if it's an absolute or relative URL, and return if not.
656
		if (
657
			0 !== strpos( $src, '/' )
658
			&& false === filter_var( $src, FILTER_VALIDATE_URL )
659
		) {
660
			return '';
661
		}
662
663
		$image_attrs_markup = '';
664
		foreach ( $image_attrs as $name => $value ) {
665
			$image_attrs_markup .= sprintf(
666
				' %1$s="%2$s"',
667
				esc_attr( $name ),
668
				esc_attr( $value )
669
			);
670
		}
671
672
		return sprintf(
673
			'<img%1$s src="%2$s" />',
674
			$image_attrs_markup,
675
			esc_url( $src )
676
		);
677
	}
678
679
	/**
680
	 * Use $themecolors array to style the Recipes shortcode
681
	 *
682
	 * @print style block
683
	 * @return string $style
684
	 */
685
	public function themecolor_styles() {
686
		global $themecolors;
687
		$style = '';
688
689
		if ( isset( $themecolors ) ) {
690
			$style .= '.jetpack-recipe { border-color: #' . esc_attr( $themecolors['border'] ) . '; }';
691
			$style .= '.jetpack-recipe-title { border-bottom-color: #' . esc_attr( $themecolors['link'] ) . '; }';
692
		}
693
694
		return $style;
695
	}
696
697
}
698
699
new Jetpack_Recipes();
700