PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 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 All 508 releases
← All changes | modules/shortcodes/quiz.php +368 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,368 @@
1 +<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileNam
2 +
3 +use Automattic\Jetpack\Assets;
4 +
5 +if ( ! defined( 'ABSPATH' ) ) {
6 + exit( 0 );
7 +}
8 +
9 +/**
10 + * Quiz shortcode.
11 + *
12 + * Usage:
13 + *
14 + * [quiz]
15 + * [question]What's the right answer?[/question]
16 + * [wrong]This one?[explanation]Nope[/explanation][/wrong]
17 + * [answer]Yes, this is the one![explanation]Yay![/explanation][/answer]
18 + * [wrong]Maybe this one[explanation]Keep trying[/explanation][/wrong]
19 + * [wrong]How about this one?[explanation]Try again[/explanation][/wrong]
20 + * [/quiz]
21 + *
22 + * Can also be wrapped in [quiz-wrapper] to display all quizzes together.
23 + */
24 +class Quiz_Shortcode {
25 +
26 + /**
27 + * Parameters admitted by [quiz] shortcode.
28 + *
29 + * @since 4.5.0
30 + *
31 + * @var array
32 + */
33 + private static $quiz_params = array();
34 +
35 + /**
36 + * Whether the [quiz-wrapper] shortcode is used.
37 + *
38 + * @since 10.1
39 + *
40 + * @var bool
41 + */
42 + private static $quiz_wrapper = false;
43 +
44 + /**
45 + * Whether the scripts were enqueued.
46 + *
47 + * @since 4.5.0
48 + *
49 + * @var bool
50 + */
51 + private static $scripts_enqueued = false;
52 +
53 + /**
54 + * In a8c training, store user currently logged in.
55 + *
56 + * @since 4.5.0
57 + *
58 + * @var null
59 + */
60 + private static $username = null;
61 +
62 + /**
63 + * Whether the noscript tag was already printed.
64 + *
65 + * @since 4.5.0
66 + *
67 + * @var bool
68 + */
69 + private static $noscript_info_printed = false;
70 +
71 + /**
72 + * Whether JavaScript is available.
73 + *
74 + * @since 4.5.0
75 + *
76 + * @var null
77 + */
78 + private static $javascript_unavailable = null;
79 +
80 + /**
81 + * Register all shortcodes.
82 + *
83 + * @since 4.5.0
84 + */
85 + public static function init() {
86 + add_shortcode( 'quiz-wrapper', array( __CLASS__, 'shortcode_wrapper' ) );
87 + add_shortcode( 'quiz', array( __CLASS__, 'shortcode' ) );
88 + add_shortcode( 'question', array( __CLASS__, 'question_shortcode' ) );
89 + add_shortcode( 'answer', array( __CLASS__, 'answer_shortcode' ) );
90 + add_shortcode( 'wrong', array( __CLASS__, 'wrong_shortcode' ) );
91 + add_shortcode( 'explanation', array( __CLASS__, 'explanation_shortcode' ) );
92 + }
93 +
94 + /**
95 + * Enqueue assets needed by the quiz,
96 + *
97 + * @since 4.5.0
98 + */
99 + private static function enqueue_scripts() {
100 + wp_enqueue_style( 'quiz', plugins_url( 'css/quiz.css', __FILE__ ), array(), JETPACK__VERSION );
101 + wp_enqueue_script(
102 + 'quiz',
103 + Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/quiz.min.js', 'modules/shortcodes/js/quiz.js' ),
104 + array( 'jquery' ),
105 + JETPACK__VERSION,
106 + true
107 + );
108 + }
109 +
110 + /**
111 + * Check if this is a feed and thus JS is unavailable.
112 + *
113 + * @since 4.5.0
114 + *
115 + * @return bool|null
116 + */
117 + private static function is_javascript_unavailable() {
118 + if ( self::$javascript_unavailable !== null ) {
119 + return self::$javascript_unavailable;
120 + }
121 +
122 + if ( is_feed() ) {
123 + self::$javascript_unavailable = true;
124 + return self::$javascript_unavailable;
125 + }
126 +
127 + self::$javascript_unavailable = false;
128 + return self::$javascript_unavailable;
129 + }
130 +
131 + /**
132 + * Display message when JS is not available.
133 + *
134 + * @since 4.5.0
135 + *
136 + * @return string
137 + */
138 + private static function noscript_info() {
139 + if ( self::$noscript_info_printed ) {
140 + return '';
141 + }
142 + self::$noscript_info_printed = true;
143 + return '<noscript><div><i>' . esc_html__( 'Please view this post in your web browser to complete the quiz.', 'jetpack' ) . '</i></div></noscript>';
144 + }
145 +
146 + /**
147 + * Check if we're in WordPress.com.
148 + *
149 + * @since 4.5.0
150 + *
151 + * @return bool
152 + */
153 + public static function is_wpcom() {
154 + return defined( 'IS_WPCOM' ) && IS_WPCOM;
155 + }
156 +
157 + /**
158 + * Parse shortcode arguments and render its output.
159 + *
160 + * @since 4.5.0
161 + *
162 + * @param array $atts Shortcode parameters.
163 + * @param string $content Content enclosed by shortcode tags.
164 + *
165 + * @return string
166 + */
167 + public static function shortcode( $atts, $content = null ) {
168 +
169 + // There's nothing to do if there's nothing enclosed.
170 + if ( empty( $content ) ) {
171 + return '';
172 + }
173 +
174 + $id = '';
175 +
176 + if ( self::is_javascript_unavailable() ) {
177 + // in an e-mail print the question and the info sentence once per question, too.
178 + self::$noscript_info_printed = false;
179 + } else {
180 +
181 + if ( ! self::$scripts_enqueued ) {
182 + // lazy enqueue cannot use the wp_enqueue_scripts action anymore.
183 + self::enqueue_scripts();
184 + self::$scripts_enqueued = true;
185 + }
186 +
187 + $default_atts = self::is_wpcom()
188 + ? array(
189 + 'trackid' => '',
190 + 'a8ctraining' => '',
191 + )
192 + : array(
193 + 'trackid' => '',
194 + );
195 +
196 + self::$quiz_params = shortcode_atts( $default_atts, $atts );
197 +
198 + if ( ! empty( self::$quiz_params['trackid'] ) ) {
199 + $id .= ' data-trackid="' . esc_attr( self::$quiz_params['trackid'] ) . '"';
200 + }
201 + if ( self::is_wpcom() && ! empty( self::$quiz_params['a8ctraining'] ) ) {
202 + if ( self::$username === null ) {
203 + self::$username = wp_get_current_user()->user_login;
204 + }
205 + $id .= ' data-a8ctraining="' . esc_attr( self::$quiz_params['a8ctraining'] ) . '" data-username="' . esc_attr( self::$username ) . '"';
206 + }
207 + }
208 +
209 + $quiz = self::do_shortcode( $content );
210 + $quiz_options = '';
211 +
212 + if ( self::$quiz_wrapper ) {
213 + $quiz_options = '<div class="jetpack-quiz-options">
214 + <span class="jetpack-quiz-count"></span>
215 + <a class="jetpack-quiz-option-button" data-quiz-option="previous" role="button" aria-label="' . esc_attr__( 'Previous quiz', 'jetpack' ) . '">
216 + <svg viewBox="0 0 24 24" class="quiz-gridicon">
217 + <g><path d="M14 20l-8-8 8-8 1.414 1.414L8.828 12l6.586 6.586"></path></g></svg></a>
218 + <a class="jetpack-quiz-option-button" data-quiz-option="next" role="button" aria-label="' . esc_attr__( 'Next quiz', 'jetpack' ) . '">
219 + <svg viewBox="0 0 24 24" class="quiz-gridicon">
220 + <g><path d="M10 20l8-8-8-8-1.414 1.414L15.172 12l-6.586 6.586"></path></g></svg></a>
221 + </div>';
222 + }
223 +
224 + return '<div class="jetpack-quiz quiz"' . $id . '>' . $quiz . $quiz_options . '</div>';
225 + }
226 +
227 + /**
228 + * Wrap shortcode contents.
229 + *
230 + * @since 10.1
231 + *
232 + * @param array $atts Shortcode parameters.
233 + * @param string $content Content enclosed by shortcode tags.
234 + *
235 + * @return string
236 + */
237 + public static function shortcode_wrapper( $atts, $content = null ) {
238 + self::$quiz_wrapper = true;
239 + return '<div class="jetpack-quiz-wrapper">' . self::do_shortcode( $content ) . '</div>';
240 + }
241 +
242 + /**
243 + * Strip line breaks, restrict allowed HTML to a few allowed tags and execute nested shortcodes.
244 + *
245 + * @since 4.5.0
246 + *
247 + * @param string $content Post content.
248 + *
249 + * @return mixed|string
250 + */
251 + private static function do_shortcode( $content ) {
252 + // strip autoinserted line breaks.
253 + $content = preg_replace( '#(<(?:br /|/?p)>\n?)*(\[/?[a-z]+\])(<(?:br /|/?p)>\n?)*#', '$2', $content );
254 +
255 + // Add internal parameter so it's only rendered when it has it.
256 + $content = preg_replace( '/\[(question|answer|wrong|explanation)\]/i', '[$1 quiz_item="true"]', $content );
257 + $content = do_shortcode( $content );
258 + $content = wp_kses(
259 + $content,
260 + array(
261 + 'tt' => array(),
262 + 'a' => array(
263 + 'href' => true,
264 + 'class' => true,
265 + 'data-quiz-option' => true,
266 + 'aria-label' => true,
267 + 'role' => 'button',
268 + ),
269 + 'pre' => array(),
270 + 'strong' => array(),
271 + 'i' => array(),
272 + 'svg' => array(),
273 + 'g' => array(),
274 + 'path' => array( 'd' => true ),
275 + 'br' => array(),
276 + 'span' => array( 'class' => true ),
277 + 'img' => array( 'src' => true ),
278 + 'div' => array(
279 + 'class' => true,
280 + 'data-correct' => 1,
281 + 'data-track-id' => 1,
282 + 'data-a8ctraining' => 1,
283 + 'data-username' => 1,
284 + 'tabindex' => false,
285 + ),
286 + )
287 + );
288 + return $content;
289 + }
290 +
291 + /**
292 + * Render question.
293 + *
294 + * @since 4.5.0
295 + *
296 + * @param array $atts Shortcode attributes.
297 + * @param null $content Post content.
298 + *
299 + * @return string
300 + */
301 + public static function question_shortcode( $atts, $content = null ) {
302 + return isset( $atts['quiz_item'] )
303 + ? '<div class="jetpack-quiz-question question" tabindex="-1">' . self::do_shortcode( $content ) . '</div>'
304 + : '';
305 + }
306 +
307 + /**
308 + * Render correct answer.
309 + *
310 + * @since 4.5.0
311 + *
312 + * @param array $atts Shortcode attributes.
313 + * @param null $content Post content.
314 + *
315 + * @return string
316 + */
317 + public static function answer_shortcode( $atts, $content = null ) {
318 + if ( self::is_javascript_unavailable() ) {
319 + return self::noscript_info();
320 + }
321 +
322 + return isset( $atts['quiz_item'] )
323 + ? '<div class="jetpack-quiz-answer answer" data-correct="1">' . self::do_shortcode( $content ) . '</div>'
324 + : '';
325 + }
326 +
327 + /**
328 + * Render wrong response.
329 + *
330 + * @since 4.5.0
331 + *
332 + * @param array $atts Shortcode attributes.
333 + * @param null $content Post content.
334 + *
335 + * @return string
336 + */
337 + public static function wrong_shortcode( $atts, $content = null ) {
338 + if ( self::is_javascript_unavailable() ) {
339 + return self::noscript_info();
340 + }
341 +
342 + return isset( $atts['quiz_item'] )
343 + ? '<div class="jetpack-quiz-answer answer">' . self::do_shortcode( $content ) . '</div>'
344 + : '';
345 + }
346 +
347 + /**
348 + * Render explanation for wrong or right answer.
349 + *
350 + * @since 4.5.0
351 + *
352 + * @param array $atts Shortcode attributes.
353 + * @param null $content Post content.
354 + *
355 + * @return string
356 + */
357 + public static function explanation_shortcode( $atts, $content = null ) {
358 + if ( self::is_javascript_unavailable() ) {
359 + return self::noscript_info();
360 + }
361 +
362 + return isset( $atts['quiz_item'] )
363 + ? '<div class="jetpack-quiz-explanation explanation">' . self::do_shortcode( $content ) . '</div>'
364 + : '';
365 + }
366 +}
367 +
368 +Quiz_Shortcode::init();