';
}
// Output the image if we have one and it's not shown elsewhere.
if ( '' !== $atts['image'] ) {
if ( ! has_shortcode( $content, 'recipe-image' ) ) {
$html .= self::output_image_html( $atts['image'] );
}
}
// Output the description, if we have one.
if ( '' !== $atts['description'] ) {
$html .= sprintf(
'
';
// If there is a recipe within a recipe, remove the shortcode.
if ( has_shortcode( $html, 'recipe' ) ) {
remove_shortcode( 'recipe' );
}
// Sanitize html.
$html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
}
/**
* Our [recipe-image] shortcode.
* Controls placement of image in recipe.
*
* @param array $atts Array of shortcode attributes.
*
* @return string HTML for recipe notes shortcode.
*/
public static function recipe_image_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'image' => '', // string.
0 => '', // string.
),
$atts,
'recipe-image'
);
$src = $atts['image'];
if ( ! empty( $atts[0] ) ) {
$src = $atts[0];
}
return self::output_image_html( $src );
}
/**
* Our [recipe-notes] shortcode.
* Outputs ingredients, styled in a div.
*
* @param array $atts Array of shortcode attributes.
* @param string $content Post content.
*
* @return string HTML for recipe notes shortcode.
*/
public static function recipe_notes_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => '', // string.
),
$atts,
'recipe-notes'
);
$html = '';
// Print a title if one exists.
if ( '' !== $atts['title'] ) {
$html .= '
' . esc_html( $atts['title'] ) . '
';
}
$html .= '
';
// Format content using list functionality, if desired.
$html .= self::output_list_content( $content, 'notes' );
$html .= '
';
// Sanitize html.
$html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
}
/**
* Our [recipe-ingredients] shortcode.
* Outputs notes, styled in a div.
*
* @param array $atts Array of shortcode attributes.
* @param string $content Post content.
*
* @return string HTML for recipe ingredients shortcode.
*/
public static function recipe_ingredients_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => esc_html_x( 'Ingredients', 'recipe', 'jetpack' ), // string.
),
$atts,
'recipe-ingredients'
);
$html = '
';
// Print a title unless the user has opted to exclude it.
if ( 'false' !== $atts['title'] ) {
$html .= '
' . esc_html( $atts['title'] ) . '
';
}
// Format content using list functionality.
$html .= self::output_list_content( $content, 'ingredients' );
$html .= '
';
// Sanitize html.
$html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
}
/**
* Our [recipe-nutrition] shortcode.
* Outputs notes, styled in a div.
*
* @param array $atts Array of shortcode attributes.
* @param string $content Post content.
*
* @return string HTML for recipe nutrition shortcode.
*/
public static function recipe_nutrition_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => esc_html_x( 'Nutrition', 'recipe', 'jetpack' ), // string.
),
$atts,
'recipe-nutrition'
);
$html = '
';
// Print a title unless the user has opted to exclude it.
if ( 'false' !== $atts['title'] ) {
$html .= '
' . esc_html( $atts['title'] ) . '
';
}
// Format content using list functionality.
$html .= self::output_list_content( $content, 'nutrition' );
$html .= '
';
// Sanitize html.
$html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
}
/**
* Reusable function to check for shortened formatting.
* Basically, users can create lists with the following shorthand:
* - item one
* - item two
* - item three
* And we'll magically convert it to a list. This has the added benefit
* of including itemprops for the recipe schema.
*
* @param string $content HTML content.
* @param string $type Type of list.
*
* @return string content formatted as a list item
*/
private static function output_list_content( $content, $type ) {
$html = '';
switch ( $type ) {
case 'directions':
$list_item_replacement = '
';
// Print a title unless the user has specified to exclude it.
if ( 'false' !== $atts['title'] ) {
$html .= '
' . esc_html( $atts['title'] ) . '
';
}
// Format content using list functionality.
$html .= self::output_list_content( $content, 'directions' );
$html .= '
';
// Sanitize html.
$html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
}
/**
* Outputs time meta tag.
*
* @param string $time_str Raw time to output.
* @param string $time_type Type of time to show.
*
* @return string HTML for recipe time meta.
*/
private static function output_time( $time_str, $time_type ) {
// Get a time that's supported by Schema.org.
$duration = WPCOM_JSON_API_Date::format_duration( $time_str );
// If no duration can be calculated, let's output what the user provided.
if ( ! $duration ) {
$duration = $time_str;
}
switch ( $time_type ) {
case 'cooktime':
$title = _x( 'Cook Time', 'recipe', 'jetpack' );
$itemprop = 'cookTime';
break;
case 'preptime':
$title = _x( 'Prep Time', 'recipe', 'jetpack' );
$itemprop = 'prepTime';
break;
default:
$title = _x( 'Time', 'recipe', 'jetpack' );
$itemprop = 'totalTime';
break;
}
return sprintf(
'
',
esc_html( $title ),
esc_html( $time_str ),
esc_attr( $time_type ),
esc_attr( $itemprop ),
esc_attr( $duration )
);
}
/**
* Outputs image tag for recipe.
*
* @param string $src The image source.
*
* @return string
*/
private static function output_image_html( $src ) {
// Exit if there is no provided source.
if ( ! $src ) {
return '';
}
$image_attrs = array(
'class' => 'jetpack-recipe-image u-photo photo',
'itemprop' => 'image',
);
if ( wp_lazy_loading_enabled( 'img', 'wp_get_attachment_image' ) ) {
$image_attrs['loading'] = 'lazy';
}
// If it's numeric, this may be an attachment.
if ( is_numeric( $src ) ) {
return wp_get_attachment_image(
$src,
'full',
false,
$image_attrs
);
}
// Check if it's an absolute or relative URL, and return if not.
if (
! str_starts_with( $src, '/' )
&& false === filter_var( $src, FILTER_VALIDATE_URL )
) {
return '';
}
$image_attrs_markup = '';
foreach ( $image_attrs as $name => $value ) {
$image_attrs_markup .= sprintf(
' %1$s="%2$s"',
esc_attr( $name ),
esc_attr( $value )
);
}
return sprintf(
'',
$image_attrs_markup,
esc_url( $src )
);
}
/**
* Use $themecolors array to style the Recipes shortcode
*
* @print style block
* @return string $style
*/
public function themecolor_styles() {
global $themecolors;
$style = '';
if ( isset( $themecolors ) ) {
$style .= '.jetpack-recipe { border-color: #' . esc_attr( $themecolors['border'] ) . '; }';
$style .= '.jetpack-recipe-title { border-bottom-color: #' . esc_attr( $themecolors['link'] ) . '; }';
}
return $style;
}
}
new Jetpack_Recipes();