PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 15.0
Jetpack – WP Security, Backup, Speed, & Growth v15.0
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 / quiz.php

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

369 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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();
369