Completed
Push — fix/sync_published_post_action ( 131f15...e65cb4 )
by
unknown
09:19 queued 01:28
created

Quiz_Shortcode::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Quiz shortcode.
4
 *
5
 * Usage:
6
 *
7
 * [quiz]
8
 * [question]What's the right answer?[/question]
9
 * [wrong]This one?[explanation]Nope[/explanation][/wrong]
10
 * [answer]Yes, this is the one![explanation]Yay![/explanation][/answer]
11
 * [wrong]Maybe this one[explanation]Keep trying[/explanation][/wrong]
12
 * [wrong]How about this one?[explanation]Try again[/explanation][/wrong]
13
 * [/quiz]
14
 */
15
class Quiz_Shortcode {
16
17
	/**
18
	 * Parameters admitted by [quiz] shortcode.
19
	 *
20
	 * @since 4.5.0
21
	 *
22
	 * @var array
23
	 */
24
	private static $quiz_params = array();
25
26
	/**
27
	 * Whether the scripts were enqueued.
28
	 *
29
	 * @since 4.5.0
30
	 *
31
	 * @var bool
32
	 */
33
	private static $scripts_enqueued = false;
34
35
	/**
36
	 * In a8c training, store user currently logged in.
37
	 *
38
	 * @since 4.5.0
39
	 *
40
	 * @var null
41
	 */
42
	private static $username = null;
43
44
	/**
45
	 * Whether the noscript tag was already printed.
46
	 *
47
	 * @since 4.5.0
48
	 *
49
	 * @var bool
50
	 */
51
	private static $noscript_info_printed = false;
52
53
	/**
54
	 * Whether JavaScript is available.
55
	 *
56
	 * @since 4.5.0
57
	 *
58
	 * @var null
59
	 */
60
	private static $javascript_unavailable = null;
61
62
	/**
63
	 * Register all shortcodes.
64
	 *
65
	 * @since 4.5.0
66
	 */
67
	public static function init() {
68
		add_shortcode( 'quiz', array( __CLASS__, 'shortcode' ) );
69
		add_shortcode( 'question', array( __CLASS__, 'question_shortcode' ) );
70
		add_shortcode( 'answer', array( __CLASS__, 'answer_shortcode' ) );
71
		add_shortcode( 'wrong', array( __CLASS__, 'wrong_shortcode' ) );
72
		add_shortcode( 'explanation', array( __CLASS__, 'explanation_shortcode' ) );
73
	}
74
75
	/**
76
	 * Enqueue assets needed by the quiz,
77
	 *
78
	 * @since 4.5.0
79
	 */
80
	private static function enqueue_scripts() {
81
		wp_enqueue_style( 'quiz', plugins_url( 'css/quiz.css', __FILE__ ) );
82
		wp_enqueue_script( 'quiz', plugins_url( 'js/quiz.js', __FILE__ ), array( 'jquery' ), null, true );
83
	}
84
85
	/**
86
	 * Check if this is a feed and thus JS is unavailable.
87
	 *
88
	 * @since 4.5.0
89
	 *
90
	 * @return bool|null
91
	 */
92
	private static function is_javascript_unavailable() {
93
		if ( ! is_null( self::$javascript_unavailable ) ) {
94
			return self::$javascript_unavailable;
95
		}
96
97
		if ( is_feed() ) {
98
			return self::$javascript_unavailable = true;
0 ignored issues
show
Documentation Bug introduced by
It seems like true of type boolean is incompatible with the declared type null of property $javascript_unavailable.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
99
		}
100
101
		return self::$javascript_unavailable = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type null of property $javascript_unavailable.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
102
	}
103
104
	/**
105
	 * Display message when JS is not available.
106
	 *
107
	 * @since 4.5.0
108
	 *
109
	 * @return string
110
	 */
111
	private static function noscript_info() {
112
		if ( self::$noscript_info_printed ) {
113
			return '';
114
		}
115
		self::$noscript_info_printed = true;
116
		return '<noscript><div><i>' . esc_html__( 'Please view this post in your web browser to complete the quiz.', 'jetpack' ) . '</i></div></noscript>';
117
	}
118
119
	/**
120
	 * Check if we're in WordPress.com.
121
	 *
122
	 * @since 4.5.0
123
	 *
124
	 * @return bool
125
	 */
126
	public static function is_wpcom() {
127
		return defined( 'IS_WPCOM' ) && IS_WPCOM;
128
	}
129
130
	/**
131
	 * Parse shortcode arguments and render its output.
132
	 *
133
	 * @since 4.5.0
134
	 *
135
	 * @param array  $atts    Shortcode parameters.
136
	 * @param string $content Content enclosed by shortcode tags.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $content not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
137
	 *
138
	 * @return string
139
	 */
140
	public static function shortcode( $atts, $content = null ) {
141
142
		// There's nothing to do if there's nothing enclosed.
143
		if ( null == $content ) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $content of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
144
			return '';
145
		}
146
147
		$id = '';
148
149
		if ( self::is_javascript_unavailable() ) {
150
			// in an e-mail print the question and the info sentence once per question, too
151
			self::$noscript_info_printed = false;
152
		} else {
153
154
			if ( ! self::$scripts_enqueued ) {
155
				// lazy enqueue cannot use the wp_enqueue_scripts action anymore
156
				self::enqueue_scripts();
157
				self::$scripts_enqueued = true;
158
			}
159
160
			$default_atts = self::is_wpcom()
161
				? array(
162
					'trackid' => '',
163
					'a8ctraining' => '',
164
				)
165
				: array(
166
					'trackid' => '',
167
				);
168
169
170
			self::$quiz_params = shortcode_atts( $default_atts, $atts );
171
172
			if ( ! empty( self::$quiz_params[ 'trackid' ] ) ) {
173
				$id .= ' data-trackid="' . esc_attr( self::$quiz_params[ 'trackid' ] ) . '"';
174
			}
175
			if ( self::is_wpcom() && ! empty( self::$quiz_params[ 'a8ctraining' ] ) ) {
176
				if ( is_null( self::$username ) ) {
177
					self::$username = wp_get_current_user()->user_login;
178
				}
179
				$id .= ' data-a8ctraining="'. esc_attr( self::$quiz_params[ 'a8ctraining' ] ) . '" data-username="' . esc_attr( self::$username ) . '"';
180
			}
181
		}
182
183
		$quiz = self::do_shortcode( $content );
184
		return '<div class="quiz"' . $id . '>' . $quiz . '</div>';
185
	}
186
187
	/**
188
	 * Strip line breaks, restrict allowed HTML to a few whitelisted tags and execute nested shortcodes.
189
	 *
190
	 * @since 4.5.0
191
	 *
192
	 * @param string $content
193
	 *
194
	 * @return mixed|string
195
	 */
196
	private static function do_shortcode( $content ) {
197
		// strip autoinserted line breaks
198
		$content = preg_replace( '#(<(?:br /|/?p)>\n?)*(\[/?[a-z]+\])(<(?:br /|/?p)>\n?)*#', '$2', $content );
199
200
		// Add internal parameter so it's only rendered when it has it
201
		$content = preg_replace( '/\[(question|answer|wrong|explanation)\]/i', '[$1 quiz_item="true"]', $content );
202
		$content = do_shortcode( $content );
203
		$content = wp_kses( $content, array(
204
			'tt' => array(),
205
			'pre' => array(),
206
			'strong' => array(),
207
			'i' => array(),
208
			'br' => array(),
209
			'img' => array( 'src' => true),
210
			'div' => array( 'class' => true, 'data-correct' => 1, 'data-track-id' => 1, 'data-a8ctraining' => 1, 'data-username' => 1 ),
211
		) );
212
		return $content;
213
	}
214
215
	/**
216
	 * Render question.
217
	 *
218
	 * @since 4.5.0
219
	 *
220
	 * @param array $atts
221
	 * @param null  $content
222
	 *
223
	 * @return string
224
	 */
225
	public static function question_shortcode( $atts, $content = null ) {
226
		return isset( $atts['quiz_item'] )
227
			? '<div class="question">' . self::do_shortcode( $content ) . '</div>'
228
			: '';
229
	}
230
231
	/**
232
	 * Render correct answer.
233
	 *
234
	 * @since 4.5.0
235
	 *
236
	 * @param array $atts
237
	 * @param null  $content
238
	 *
239
	 * @return string
240
	 */
241 View Code Duplication
	public static function answer_shortcode( $atts, $content = null ) {
242
		if ( self::is_javascript_unavailable() ) {
243
			return self::noscript_info();
244
		}
245
246
		return isset( $atts['quiz_item'] )
247
			? '<div class="answer" data-correct="1">' . self::do_shortcode( $content ) . '</div>'
248
			: '';
249
	}
250
251
	/**
252
	 * Render wrong response.
253
	 *
254
	 * @since 4.5.0
255
	 *
256
	 * @param array $atts
257
	 * @param null  $content
258
	 *
259
	 * @return string
260
	 */
261 View Code Duplication
	public static function wrong_shortcode( $atts, $content = null ) {
262
		if ( self::is_javascript_unavailable() ) {
263
			return self::noscript_info();
264
		}
265
266
		return isset( $atts['quiz_item'] )
267
			? '<div class="answer">' . self::do_shortcode( $content ) . '</div>'
268
			: '';
269
	}
270
271
	/**
272
	 * Render explanation for wrong or right answer.
273
	 *
274
	 * @since 4.5.0
275
	 *
276
	 * @param array $atts
277
	 * @param null  $content
278
	 *
279
	 * @return string
280
	 */
281 View Code Duplication
	public static function explanation_shortcode( $atts, $content = null ) {
282
		if ( self::is_javascript_unavailable() ) {
283
			return self::noscript_info();
284
		}
285
286
		return isset( $atts['quiz_item'] )
287
			? '<div class="explanation">' . self::do_shortcode( $content ) . '</div>'
288
			: '';
289
	}
290
}
291
292
Quiz_Shortcode::init();
293