PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 4.2.5
Jetpack – WP Security, Backup, Speed, & Growth v4.2.5
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / modules / shortcodes / recipe.php

recipe.php in Jetpack – WP Security, Backup, Speed, & Growth 4.2.5, at modules/shortcodes/recipe.php

380 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Embed recipe 'cards' in post, with basic styling and print functionality
5 *
6 * To Do
7 * - defaults settings
8 * - basic styles/themecolor styles
9 * - validation/sanitization
10 * - print styles
11 */
12 class Jetpack_Recipes {
13
14 private $scripts_and_style_included = false;
15
16 function __construct() {
17 add_action( 'init', array( $this, 'action_init' ) );
18
19 // Add itemprop to allowed tags for wp_kses_post, so we can use it for better Schema compliance.
20 global $allowedposttags;
21 $tags = array( 'li', 'ol', 'img' );
22 foreach ( $tags as $tag ) {
23 if ( ! is_array( $allowedposttags[ $tag ] ) ) {
24 $allowedposttags[ $tag ] = array();
25 }
26 $allowedposttags[ $tag ]['itemprop'] = array();
27 }
28 }
29
30 function action_init() {
31 // Enqueue styles if [recipe] exists
32 add_action( 'wp_head', array( $this, 'add_scripts' ), 1 );
33
34 // Render [recipe], along with other shortcodes that can be nested within.
35 add_shortcode( 'recipe', array( $this, 'recipe_shortcode' ) );
36 add_shortcode( 'recipe-notes', array( $this, 'recipe_notes_shortcode' ) );
37 add_shortcode( 'recipe-ingredients', array( $this, 'recipe_ingredients_shortcode' ) );
38 add_shortcode( 'recipe-directions', array( $this, 'recipe_directions_shortcode' ) );
39 }
40
41 /**
42 * Enqueue scripts and styles
43 */
44 function add_scripts() {
45 if ( empty( $GLOBALS['posts'] ) || ! is_array( $GLOBALS['posts'] ) ) {
46 return;
47 }
48
49 foreach ( $GLOBALS['posts'] as $p ) {
50 if ( has_shortcode( $p->post_content, 'recipe' ) ) {
51 $this->scripts_and_style_included = true;
52 break;
53 }
54 }
55
56 if ( ! $this->scripts_and_style_included ) {
57 return;
58 }
59
60 if ( is_rtl() ) {
61 wp_enqueue_style( 'jetpack-recipes-style', plugins_url( '/css/rtl/recipes-rtl.css', __FILE__ ), array(), '20130919' );
62 } else {
63 wp_enqueue_style( 'jetpack-recipes-style', plugins_url( '/css/recipes.css', __FILE__ ), array(), '20130919' );
64 }
65
66 wp_add_inline_style( 'jetpack-recipes-style', self::themecolor_styles() ); // add $themecolors-defined styles
67
68 wp_enqueue_script( 'jetpack-recipes-printthis', plugins_url( '/js/recipes-printthis.js', __FILE__ ), array( 'jquery' ), '20131230' );
69 wp_enqueue_script( 'jetpack-recipes-js', plugins_url( '/js/recipes.js', __FILE__ ), array( 'jquery', 'jetpack-recipes-printthis' ), '20131230' );
70
71 $title_var = wp_title( '|', false, 'right' );
72 $print_css_var = plugins_url( '/css/recipes-print.css', __FILE__ );
73
74 wp_localize_script( 'jetpack-recipes-js', 'jetpack_recipes_vars', array( 'pageTitle' => $title_var, 'loadCSS' => $print_css_var ) );
75 }
76
77 /**
78 * Our [recipe] shortcode.
79 * Prints recipe data styled to look good on *any* theme.
80 *
81 * @return string HTML for recipe shortcode.
82 */
83 static function recipe_shortcode( $atts, $content = '' ) {
84 $atts = shortcode_atts(
85 array(
86 'title' => '', //string
87 'servings' => '', //intval
88 'time' => '', //string
89 'difficulty' => '', //string
90 'print' => '', //string
91 'image' => '', //string
92 'description' => '', //string
93 ), $atts, 'recipe'
94 );
95
96 return self::recipe_shortcode_html( $atts, $content );
97 }
98
99 /**
100 * The recipe output
101 *
102 * @return string HTML output
103 */
104 static function recipe_shortcode_html( $atts, $content = '' ) {
105 // Add itemprop to allowed tags for wp_kses_post, so we can use it for better Schema compliance.
106 global $allowedtags;
107 $allowedtags['li'] = array( 'itemprop' => array () );
108
109 $html = '<div class="hrecipe jetpack-recipe" itemscope itemtype="http://schema.org/Recipe">';
110
111 // Print the recipe title if exists
112 if ( '' !== $atts['title'] ) {
113 $html .= '<h3 class="jetpack-recipe-title" itemprop="name">' . esc_html( $atts['title'] ) . '</h3>';
114 }
115
116 // Print the recipe meta if exists
117 if ( '' !== $atts['servings'] || '' != $atts['time'] || '' != $atts['difficulty'] || '' != $atts['print'] ) {
118 $html .= '<ul class="jetpack-recipe-meta">';
119
120 if ( '' !== $atts['servings'] ) {
121 $html .= sprintf(
122 '<li class="jetpack-recipe-servings" itemprop="recipeYield"><strong>%1$s: </strong>%2$s</li>',
123 esc_html_x( 'Servings', 'recipe', 'jetpack' ),
124 esc_html( $atts['servings'] )
125 );
126 }
127
128 if ( '' !== $atts['time'] ) {
129 $html .= sprintf(
130 '<li class="jetpack-recipe-time" itemprop="totalTime"><strong>%1$s: </strong>%2$s</li>',
131 esc_html_x( 'Time', 'recipe', 'jetpack' ),
132 esc_html( $atts['time'] )
133 );
134 }
135
136 if ( '' !== $atts['difficulty'] ) {
137 $html .= sprintf(
138 '<li class="jetpack-recipe-difficulty"><strong>%1$s: </strong>%2$s</li>',
139 esc_html_x( 'Difficulty', 'recipe', 'jetpack' ),
140 esc_html( $atts['difficulty'] )
141 );
142 }
143
144 if ( 'false' !== $atts['print'] ) {
145 $html .= sprintf(
146 '<li class="jetpack-recipe-print"><a href="#">%1$s</a></li>',
147 esc_html_x( 'Print', 'recipe', 'jetpack' )
148 );
149 }
150
151 $html .= '</ul>';
152 }
153
154 // Output the image, if we have one.
155 if ( '' !== $atts['image'] ) {
156 $html .= sprintf(
157 '<img class="jetpack-recipe-image" itemprop="thumbnailUrl" src="%1$s" />',
158 esc_url( $atts['image'] )
159 );
160 }
161
162 // Output the description, if we have one.
163 if ( '' !== $atts['description'] ) {
164 $html .= sprintf(
165 '<p class="jetpack-recipe-description">%1$s</p>',
166 esc_html( $atts['description'] )
167 );
168 }
169
170 // Print content between codes
171 $html .= '<div class="jetpack-recipe-content">' . do_shortcode( $content ) . '</div>';
172
173 // Close it up
174 $html .= '</div>';
175
176 // If there is a recipe within a recipe, remove the shortcode
177 if ( has_shortcode( $html, 'recipe' ) ) {
178 remove_shortcode( 'recipe' );
179 }
180
181 // Sanitize html
182 $html = wp_kses_post( $html );
183
184 // Return the HTML block
185 return $html;
186 }
187
188 /**
189 * Our [recipe-notes] shortcode.
190 * Outputs notes, styled in a div.
191 *
192 * @return string HTML for recipe notes shortcode.
193 */
194 static function recipe_notes_shortcode( $atts, $content = '' ) {
195 $atts = shortcode_atts( array(
196 'title' => '', //string
197 ), $atts, 'recipe-notes' );
198
199 $html ='';
200
201 // Print a title if one exists.
202 if ( '' !== $atts['title'] ) {
203 $html .= '<h4 class="jetpack-recipe-notes-title">' . esc_html( $atts['title'] ) . '</h4>';
204 }
205
206 $html .= '<div class="jetpack-recipe-notes">';
207
208 // Format content using list functionality, if desired.
209 $html .= self::output_list_content( $content, 'notes' );
210
211 $html .= '</div>';
212
213 // Sanitize html.
214 $html = wp_kses_post( $html );
215
216 // Return the HTML block.
217 return $html;
218 }
219
220 /**
221 * Our [recipe-ingredients] shortcode.
222 * Outputs notes, styled in a div.
223 *
224 * @return string HTML for recipe ingredients shortcode.
225 */
226 static function recipe_ingredients_shortcode( $atts, $content = '' ) {
227 $atts = shortcode_atts( array(
228 'title' => esc_html_x( 'Ingredients', 'recipe', 'jetpack' ), //string
229 ), $atts, 'recipe-ingredients' );
230
231 $html = '<div class="jetpack-recipe-ingredients">';
232
233 // Print a title unless the user has opted to exclude it.
234 if ( 'false' !== $atts['title'] ) {
235 $html .= '<h4 class="jetpack-recipe-ingredients-title">' . esc_html( $atts['title'] ) . '</h4>';
236 }
237
238 // Format content using list functionality.
239 $html .= self::output_list_content( $content, 'ingredients' );
240
241 $html .= '</div>';
242
243 // Sanitize html.
244 $html = wp_kses_post( $html );
245
246 // Return the HTML block.
247 return $html;
248 }
249
250 /**
251 * Reusable function to check for shortened formatting.
252 * Basically, users can create lists with the following shorthand:
253 * - item one
254 * - item two
255 * - item three
256 * And we'll magically convert it to a list. This has the added benefit
257 * of including itemprops for the recipe schema.
258 *
259 * @return string content formatted as a list item
260 */
261 static function output_list_content( $content, $type ) {
262 $html ='';
263
264 switch ( $type ) {
265 case 'directions' :
266 $list_item_replacement = '<li class="jetpack-recipe-directions">${1}</li>';
267 $itemprop = ' itemprop="recipeInstructions"';
268 $listtype = 'ol';
269 break;
270 case 'ingredients' :
271 $list_item_replacement = '<li class="jetpack-recipe-ingredient">${1}</li>';
272 $itemprop = ' itemprop="recipeIngredient"';
273 $listtype = 'ul';
274 break;
275 default:
276 $list_item_replacement = '<li class="jetpack-recipe-notes">${1}</li>';
277 $itemprop = '';
278 $listtype = 'ul';
279 }
280
281 // Check to see if the user is trying to use shortened formatting.
282 if (
283 strpos( $content, '&#8211;' ) !== false ||
284 strpos( $content, '&#8212;' ) !== false ||
285 strpos( $content, '-' ) !== false ||
286 strpos( $content, '*' ) !== false ||
287 strpos( $content, '#' ) !== false ||
288 strpos( $content, '' ) !== false || // ndash
289 strpos( $content, '' ) !== false || // mdash
290 preg_match( '/\d+\.\s/', $content )
291 ) {
292 // Remove breaks and extra whitespace
293 $content = str_replace( "<br />\n", "\n", $content );
294 $content = trim( $content );
295
296 $ul_pattern = '/(?:^|\n|\<p\>)+(?:[\-–—]+|\&#8211;|\&#8212;|\*)+\h+(.*)/mi';
297 $ol_pattern = '/(?:^|\n|\<p\>)+(?:\d+\.|#+)+\h+(.*)/mi';
298
299 preg_match_all( $ul_pattern, $content, $ul_matches );
300 preg_match_all( $ol_pattern, $content, $ol_matches );
301
302 if ( 0 !== count( $ul_matches[0] ) || 0 !== count( $ol_matches[0] ) ) {
303
304 if ( 0 !== count( $ol_matches[0] ) ) {
305 $listtype = 'ol';
306 $list_item_pattern = $ol_pattern;
307 } else {
308 $listtype = 'ul';
309 $list_item_pattern = $ul_pattern;
310 }
311 $html .= '<' . $listtype . $itemprop . '>';
312 $html .= preg_replace( $list_item_pattern, $list_item_replacement, $content );
313 $html .= '</' . $listtype . '>';
314
315 // Strip out any empty <p> tags and stray </p> tags, because those are just silly.
316 $empty_p_pattern = '/(<p>)*\s*<\/p>/mi';
317 $html = preg_replace( $empty_p_pattern, '', $html );
318 } else {
319 $html .= do_shortcode( $content );
320 }
321 } else {
322 $html .= do_shortcode( $content );
323 }
324
325 // Return our formatted content.
326 return $html;
327 }
328
329 /**
330 * Our [recipe-directions] shortcode.
331 * Outputs notes, styled in a div.
332 *
333 * @return string HTML for recipe notes shortcode.
334 */
335 static function recipe_directions_shortcode( $atts, $content = '' ) {
336 $atts = shortcode_atts( array(
337 'title' => esc_html_x( 'Directions', 'recipe', 'jetpack' ), //string
338 ), $atts, 'recipe-directions' );
339
340 $html = '<div class="jetpack-recipe-directions">';
341
342 // Print a title unless the user has specified to exclude it.
343 if ( 'false' !== $atts['title'] ) {
344 $html .= '<h4 class="jetpack-recipe-directions-title">' . esc_html( $atts['title'] ) . '</h4>';
345 }
346
347 // Format content using list functionality.
348 $html .= self::output_list_content( $content, 'directions' );
349
350 $html .= '</div>';
351
352 // Sanitize html.
353 $html = wp_kses_post( $html );
354
355 // Return the HTML block.
356 return $html;
357 }
358
359 /**
360 * Use $themecolors array to style the Recipes shortcode
361 *
362 * @print style block
363 * @return string $style
364 */
365 function themecolor_styles() {
366 global $themecolors;
367 $style = '';
368
369 if ( isset( $themecolors ) ) {
370 $style .= '.jetpack-recipe { border-color: #' . esc_attr( $themecolors['border'] ) . '; }';
371 $style .= '.jetpack-recipe-title { border-bottom-color: #' . esc_attr( $themecolors['link'] ) . '; }';
372 }
373
374 return $style;
375 }
376
377 }
378
379 new Jetpack_Recipes();
380