summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/modules/shortcodes/recipe.php')
-rw-r--r--plugins/jetpack/modules/shortcodes/recipe.php361
1 files changed, 257 insertions, 104 deletions
diff --git a/plugins/jetpack/modules/shortcodes/recipe.php b/plugins/jetpack/modules/shortcodes/recipe.php
index 7846b154..983f01ee 100644
--- a/plugins/jetpack/modules/shortcodes/recipe.php
+++ b/plugins/jetpack/modules/shortcodes/recipe.php
@@ -1,4 +1,6 @@
-<?php
+<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+
+use Automattic\Jetpack\Assets;
/**
* Embed recipe 'cards' in post, with basic styling and print functionality
@@ -8,80 +10,63 @@
* - basic styles/themecolor styles
* - validation/sanitization
* - print styles
+ *
+ * @package Jetpack
*/
-class Jetpack_Recipes {
-
- private $scripts_and_style_included = false;
-
- function __construct() {
- add_action( 'init', array( $this, 'action_init' ) );
- add_filter( 'wp_kses_allowed_html', array( $this, 'add_recipes_kses_rules' ), 10, 2 );
- }
+/**
+ * Register and display Recipes in posts.
+ */
+class Jetpack_Recipes {
/**
- * Add Schema-specific attributes to our allowed tags in wp_kses,
- * so we can have better Schema.org compliance.
+ * Have scripts and styles been enqueued already.
*
- * @param array $allowedtags Array of allowed HTML tags in recipes.
- * @param array $context Context to judge allowed tags by.
+ * @var bool
*/
- function add_recipes_kses_rules( $allowedtags, $context ) {
- if ( in_array( $context, array( '', 'post', 'data' ) ) ) :
- // Create an array of all the tags we'd like to add the itemprop attribute to.
- $tags = array( 'li', 'ol', 'ul', 'img', 'p', 'h3', 'time' );
- foreach ( $tags as $tag ) {
- $allowedtags = $this->add_kses_rule(
- $allowedtags,
- $tag,
- array(
- 'class' => array(),
- 'itemprop' => array(),
- 'datetime' => array(),
- )
- );
- }
-
- // Allow itemscope and itemtype for divs.
- $allowedtags = $this->add_kses_rule(
- $allowedtags,
- 'div',
- array(
- 'class' => array(),
- 'itemscope' => array(),
- 'itemtype' => array(),
- )
- );
- endif;
+ private $scripts_and_style_included = false;
- return $allowedtags;
+ /**
+ * Constructor
+ */
+ public function __construct() {
+ add_action( 'init', array( $this, 'action_init' ) );
}
/**
- * Function to add a new property rule to our kses array.
- * Used by add_recipe_kses_rules() above.
+ * Returns KSES tags with Schema-specific attributes.
*
- * @param array $all_tags Array of allowed HTML tags in recipes.
- * @param string $tag New HTML tag to add to the array of allowed HTML.
- * @param array $rules Array of allowed attributes for that HTML tag.
+ * @since 8.0.0
+ *
+ * @return array Array to be used by KSES.
*/
- private function add_kses_rule( $all_tags, $tag, $rules ) {
-
- // If the tag doesn't already exist, add it.
- if ( ! isset( $all_tags[ $tag ] ) ) {
- $all_tags[ $tag ] = array();
+ private static function kses_tags() {
+ $allowedtags = wp_kses_allowed_html( 'post' );
+ // Create an array of all the tags we'd like to add the itemprop attribute to.
+ $tags = array( 'li', 'ol', 'ul', 'img', 'p', 'h3', 'time', 'span' );
+ foreach ( $tags as $tag ) {
+ if ( ! isset( $allowedtags[ $tag ] ) ) {
+ $allowedtags[ $tag ] = array();
+ }
+ $allowedtags[ $tag ]['class'] = array();
+ $allowedtags[ $tag ]['itemprop'] = array();
+ $allowedtags[ $tag ]['datetime'] = array();
}
- // Merge the new tags with existing tags.
- $all_tags[ $tag ] = array_merge( $all_tags[ $tag ], $rules );
-
- return $all_tags;
+ // Allow itemscope and itemtype for divs.
+ if ( ! isset( $allowedtags['div'] ) ) {
+ $allowedtags['div'] = array();
+ }
+ $allowedtags['div']['class'] = array();
+ $allowedtags['div']['itemscope'] = array();
+ $allowedtags['div']['itemtype'] = array();
+ return $allowedtags;
}
/**
* Register our shortcode and enqueue necessary files.
*/
- function action_init() {
+ public function action_init() {
// Enqueue styles if [recipe] exists.
add_action( 'wp_head', array( $this, 'add_scripts' ), 1 );
@@ -90,12 +75,14 @@ class Jetpack_Recipes {
add_shortcode( 'recipe-notes', array( $this, 'recipe_notes_shortcode' ) );
add_shortcode( 'recipe-ingredients', array( $this, 'recipe_ingredients_shortcode' ) );
add_shortcode( 'recipe-directions', array( $this, 'recipe_directions_shortcode' ) );
+ add_shortcode( 'recipe-nutrition', array( $this, 'recipe_nutrition_shortcode' ) );
+ add_shortcode( 'recipe-image', array( $this, 'recipe_image_shortcode' ) );
}
/**
* Enqueue scripts and styles
*/
- function add_scripts() {
+ public function add_scripts() {
if ( empty( $GLOBALS['posts'] ) || ! is_array( $GLOBALS['posts'] ) ) {
return;
}
@@ -116,19 +103,20 @@ class Jetpack_Recipes {
// add $themecolors-defined styles.
wp_add_inline_style( 'jetpack-recipes-style', self::themecolor_styles() );
-
wp_enqueue_script(
'jetpack-recipes-printthis',
- Jetpack::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes-printthis.min.js', 'modules/shortcodes/js/recipes-printthis.js' ),
+ Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes-printthis.min.js', 'modules/shortcodes/js/recipes-printthis.js' ),
array( 'jquery' ),
- '20170202'
+ '20170202',
+ false
);
wp_enqueue_script(
'jetpack-recipes-js',
- Jetpack::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes.min.js', 'modules/shortcodes/js/recipes.js' ),
+ Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/recipes.min.js', 'modules/shortcodes/js/recipes.js' ),
array( 'jquery', 'jetpack-recipes-printthis' ),
- '20131230'
+ '20131230',
+ false
);
$title_var = wp_title( '|', false, 'right' );
@@ -154,18 +142,21 @@ class Jetpack_Recipes {
*
* @return string HTML for recipe shortcode.
*/
- static function recipe_shortcode( $atts, $content = '' ) {
+ public static function recipe_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => '', // string.
'servings' => '', // intval.
- 'time' => '', // string.
+ 'time' => '', // strtotime-compatible time description.
'difficulty' => '', // string.
- 'print' => '', // string.
+ 'print' => '', // URL for external print version.
'source' => '', // string.
- 'sourceurl' => '', // string.
- 'image' => '', // string.
+ 'sourceurl' => '', // URL string. Only used if source set.
+ 'image' => '', // URL or attachment ID.
'description' => '', // string.
+ 'cooktime' => '', // strtotime-compatible time description.
+ 'preptime' => '', // strtotime-compatible time description.
+ 'rating' => '', // string.
),
$atts,
'recipe'
@@ -182,43 +173,41 @@ class Jetpack_Recipes {
*
* @return string HTML output
*/
- static function recipe_shortcode_html( $atts, $content = '' ) {
+ private static function recipe_shortcode_html( $atts, $content = '' ) {
- $html = '<div class="hrecipe jetpack-recipe" itemscope itemtype="https://schema.org/Recipe">';
+ $html = '<div class="hrecipe h-recipe jetpack-recipe" itemscope itemtype="https://schema.org/Recipe">';
// Print the recipe title if exists.
if ( '' !== $atts['title'] ) {
- $html .= '<h3 class="jetpack-recipe-title" itemprop="name">' . esc_html( $atts['title'] ) . '</h3>';
+ $html .= '<h3 class="p-name jetpack-recipe-title fn" itemprop="name">' . esc_html( $atts['title'] ) . '</h3>';
}
// Print the recipe meta if exists.
- if ( '' !== $atts['servings'] || '' != $atts['time'] || '' != $atts['difficulty'] || '' != $atts['print'] ) {
+ if (
+ '' !== $atts['servings']
+ || '' !== $atts['time']
+ || '' !== $atts['difficulty']
+ || '' !== $atts['print']
+ || '' !== $atts['preptime']
+ || '' !== $atts['cooktime']
+ || '' !== $atts['rating']
+ ) {
$html .= '<ul class="jetpack-recipe-meta">';
if ( '' !== $atts['servings'] ) {
$html .= sprintf(
- '<li class="jetpack-recipe-servings" itemprop="recipeYield"><strong>%1$s: </strong>%2$s</li>',
+ '<li class="jetpack-recipe-servings p-yield yield" itemprop="recipeYield"><strong>%1$s: </strong>%2$s</li>',
esc_html_x( 'Servings', 'recipe', 'jetpack' ),
esc_html( $atts['servings'] )
);
}
- if ( '' !== $atts['time'] ) {
- // Get a time that's supported by Schema.org.
- $duration = WPCOM_JSON_API_Date::format_duration( $atts['time'] );
- // If no duration can be calculated, let's output what the user provided.
- if ( empty( $duration ) ) {
- $duration = $atts['time'];
+ $time_types = array( 'cooktime', 'preptime', 'time' );
+ foreach ( $time_types as $time_type ) {
+ if ( '' === $atts[ $time_type ] ) {
+ continue;
}
-
- $html .= sprintf(
- '<li class="jetpack-recipe-time">
- <time itemprop="totalTime" datetime="%3$s"><strong>%1$s: </strong>%2$s</time>
- </li>',
- esc_html_x( 'Time', 'recipe', 'jetpack' ),
- esc_html( $atts['time'] ),
- esc_attr( $duration )
- );
+ $html .= self::output_time( $atts[ $time_type ], $time_type );
}
if ( '' !== $atts['difficulty'] ) {
@@ -229,6 +218,17 @@ class Jetpack_Recipes {
);
}
+ if ( '' !== $atts['rating'] ) {
+ $html .= sprintf(
+ '<li class="jetpack-recipe-rating">
+ <strong>%1$s: </strong>
+ <span itemprop="contentRating">%2$s</span>
+ </li>',
+ esc_html_x( 'Rating', 'recipe', 'jetpack' ),
+ esc_html( $atts['rating'] )
+ );
+ }
+
if ( '' !== $atts['source'] ) {
$html .= sprintf(
'<li class="jetpack-recipe-source"><strong>%1$s: </strong>',
@@ -261,14 +261,13 @@ class Jetpack_Recipes {
}
$html .= '</ul>';
- } // End if().
+ }
- // Output the image, if we have one.
+ // Output the image if we have one and it's not shown elsewhere.
if ( '' !== $atts['image'] ) {
- $html .= sprintf(
- '<img class="jetpack-recipe-image" itemprop="image" src="%1$s" />',
- esc_url( $atts['image'] )
- );
+ if ( ! has_shortcode( $content, 'recipe-image' ) ) {
+ $html .= self::output_image_html( $atts['image'] );
+ }
}
// Output the description, if we have one.
@@ -291,13 +290,37 @@ class Jetpack_Recipes {
}
// Sanitize html.
- $html = wp_kses_post( $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.
*
@@ -306,7 +329,7 @@ class Jetpack_Recipes {
*
* @return string HTML for recipe notes shortcode.
*/
- static function recipe_notes_shortcode( $atts, $content = '' ) {
+ public static function recipe_notes_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => '', // string.
@@ -330,7 +353,7 @@ class Jetpack_Recipes {
$html .= '</div>';
// Sanitize html.
- $html = wp_kses_post( $html );
+ $html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
@@ -345,7 +368,7 @@ class Jetpack_Recipes {
*
* @return string HTML for recipe ingredients shortcode.
*/
- static function recipe_ingredients_shortcode( $atts, $content = '' ) {
+ public static function recipe_ingredients_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => esc_html_x( 'Ingredients', 'recipe', 'jetpack' ), // string.
@@ -367,7 +390,44 @@ class Jetpack_Recipes {
$html .= '</div>';
// Sanitize html.
- $html = wp_kses_post( $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 = '<div class="jetpack-recipe-nutrition p-nutrition nutrition">';
+
+ // Print a title unless the user has opted to exclude it.
+ if ( 'false' !== $atts['title'] ) {
+ $html .= '<h4 class="jetpack-recipe-nutrition-title">' . esc_html( $atts['title'] ) . '</h4>';
+ }
+
+ // Format content using list functionality.
+ $html .= self::output_list_content( $content, 'nutrition' );
+
+ $html .= '</div>';
+
+ // Sanitize html.
+ $html = wp_kses( $html, self::kses_tags() );
// Return the HTML block.
return $html;
@@ -387,7 +447,7 @@ class Jetpack_Recipes {
*
* @return string content formatted as a list item
*/
- static function output_list_content( $content, $type ) {
+ private static function output_list_content( $content, $type ) {
$html = '';
switch ( $type ) {
@@ -397,10 +457,20 @@ class Jetpack_Recipes {
$listtype = 'ol';
break;
case 'ingredients':
- $list_item_replacement = '<li class="jetpack-recipe-ingredient" itemprop="recipeIngredient">${1}</li>';
+ $list_item_replacement = '<li class="jetpack-recipe-ingredient p-ingredient ingredient" itemprop="recipeIngredient">${1}</li>';
$itemprop = '';
$listtype = 'ul';
break;
+ case 'nutrition':
+ $list_item_replacement = '<li class="jetpack-recipe-nutrition">${1}</li>';
+ $itemprop = ' itemprop="nutrition"';
+ $listtype = 'ul';
+ break;
+ case 'nutrition':
+ $list_item_replacement = '<li class="jetpack-recipe-nutrition nutrition">${1}</li>';
+ $itemprop = ' itemprop="nutrition"';
+ $listtype = 'ul';
+ break;
default:
$list_item_replacement = '<li class="jetpack-recipe-notes">${1}</li>';
$itemprop = '';
@@ -464,7 +534,7 @@ class Jetpack_Recipes {
*
* @return string HTML for recipe directions shortcode.
*/
- static function recipe_directions_shortcode( $atts, $content = '' ) {
+ public static function recipe_directions_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => esc_html_x( 'Directions', 'recipe', 'jetpack' ), // string.
@@ -473,7 +543,7 @@ class Jetpack_Recipes {
'recipe-directions'
);
- $html = '<div class="jetpack-recipe-directions">';
+ $html = '<div class="jetpack-recipe-directions e-instructions">';
// Print a title unless the user has specified to exclude it.
if ( 'false' !== $atts['title'] ) {
@@ -486,19 +556,102 @@ class Jetpack_Recipes {
$html .= '</div>';
// Sanitize html.
- $html = wp_kses_post( $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(
+ '<li class="jetpack-recipe-%3$s">
+ <time itemprop="%4$s" datetime="%5$s"><strong>%1$s:</strong> <span class="%3$s">%2$s</span></time>
+ </li>',
+ 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 '';
+ }
+
+ // If it's numeric, this may be an attachment.
+ if ( is_numeric( $src ) ) {
+ return wp_get_attachment_image(
+ $src,
+ 'full',
+ false,
+ array(
+ 'class' => 'jetpack-recipe-image u-photo photo',
+ 'itemprop' => 'image',
+ )
+ );
+ }
+
+ // Check if it's an absolute or relative URL, and return if not.
+ if (
+ 0 !== strpos( $src, '/' )
+ && false === filter_var( $src, FILTER_VALIDATE_URL )
+ ) {
+ return '';
+ }
+
+ return sprintf(
+ '<img class="jetpack-recipe-image u-photo photo" itemprop="image" src="%1$s" />',
+ esc_url( $src )
+ );
+ }
+
+ /**
* Use $themecolors array to style the Recipes shortcode
*
* @print style block
* @return string $style
*/
- function themecolor_styles() {
+ public function themecolor_styles() {
global $themecolors;
$style = '';