PluginProbe
Plugin Check (PCP) / 1.5.0
Plugin Check (PCP) v1.5.0
2.1.0 trunk 0.1 0.2.0 0.2.1 0.2.2 0.2.3 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 ci-artifacts
plugin-check / assets / js / plugin-check-admin.js

plugin-check-admin.js in Plugin Check (PCP) 1.5.0, at assets/js/plugin-check-admin.js

489 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function ( pluginCheck ) {
2 const checkItButton = document.getElementById( 'plugin-check__submit' );
3 const resultsContainer = document.getElementById( 'plugin-check__results' );
4 const spinner = document.getElementById( 'plugin-check__spinner' );
5 const pluginsList = document.getElementById(
6 'plugin-check__plugins-dropdown'
7 );
8 const categoriesList = document.querySelectorAll(
9 'input[name=categories]'
10 );
11 const templates = {};
12
13 // Return early if the elements cannot be found on the page.
14 if (
15 ! checkItButton ||
16 ! pluginsList ||
17 ! resultsContainer ||
18 ! spinner ||
19 ! categoriesList.length
20 ) {
21 console.error( 'Missing form elements on page' );
22 return;
23 }
24
25 const includeExperimental = document.getElementById(
26 'plugin-check__include-experimental'
27 );
28
29 // Handle disabling the Check it button when a plugin is not selected.
30 function canRunChecks() {
31 if ( '' === pluginsList.value ) {
32 checkItButton.disabled = true;
33 } else {
34 checkItButton.disabled = false;
35 }
36 }
37
38 // Run on page load to test if dropdown is auto populated.
39 canRunChecks();
40 pluginsList.addEventListener( 'change', canRunChecks );
41
42 function saveUserSettings() {
43 const selectedCategories = [];
44
45 // Assuming you have a list of category checkboxes, find the selected ones.
46 categoriesList.forEach( function ( checkbox ) {
47 if ( checkbox.checked ) {
48 selectedCategories.push( checkbox.value );
49 }
50 } );
51
52 // Join the selected category slugs with '__' and save it as a user setting.
53 const settingValue = selectedCategories.join( '__' );
54 window.setUserSetting(
55 'plugin_check_category_preferences',
56 settingValue
57 );
58 }
59
60 // Attach the saveUserSettings function when a category checkbox is clicked.
61 categoriesList.forEach( function ( checkbox ) {
62 checkbox.addEventListener( 'change', saveUserSettings );
63 } );
64
65 // When the Check it button is clicked.
66 checkItButton.addEventListener( 'click', ( e ) => {
67 e.preventDefault();
68
69 resetResults();
70 checkItButton.disabled = true;
71 pluginsList.disabled = true;
72 spinner.classList.add( 'is-active' );
73 for ( let i = 0; i < categoriesList.length; i++ ) {
74 categoriesList[ i ].disabled = true;
75 }
76
77 getChecksToRun()
78 .then( setUpEnvironment )
79 .then( runChecks )
80 .then( cleanUpEnvironment )
81 .then( ( data ) => {
82 console.log( data.message );
83
84 resetForm();
85 } )
86 .catch( ( error ) => {
87 console.error( error );
88
89 resetForm();
90 } );
91 } );
92
93 /**
94 * Reset the results container.
95 *
96 * @since 1.0.0
97 */
98 function resetResults() {
99 // Empty the results container.
100 resultsContainer.innerText = '';
101 }
102
103 /**
104 * Resets the form controls once checks have completed or failed.
105 *
106 * @since 1.0.0
107 */
108 function resetForm() {
109 spinner.classList.remove( 'is-active' );
110 checkItButton.disabled = false;
111 pluginsList.disabled = false;
112 for ( let i = 0; i < categoriesList.length; i++ ) {
113 categoriesList[ i ].disabled = false;
114 }
115 }
116
117 /**
118 * Setup the runtime environment if needed.
119 *
120 * @since 1.0.0
121 *
122 * @param {Object} data Data object with props passed to form data.
123 */
124 function setUpEnvironment( data ) {
125 const pluginCheckData = new FormData();
126 pluginCheckData.append( 'nonce', pluginCheck.nonce );
127 pluginCheckData.append( 'plugin', data.plugin );
128 pluginCheckData.append(
129 'action',
130 pluginCheck.actionSetUpRuntimeEnvironment
131 );
132 pluginCheckData.append(
133 'include-experimental',
134 includeExperimental && includeExperimental.checked ? 1 : 0
135 );
136
137 for ( let i = 0; i < data.checks.length; i++ ) {
138 pluginCheckData.append( 'checks[]', data.checks[ i ] );
139 }
140
141 return fetch( ajaxurl, {
142 method: 'POST',
143 credentials: 'same-origin',
144 body: pluginCheckData,
145 } )
146 .then( ( response ) => {
147 return response.json();
148 } )
149 .then( handleDataErrors )
150 .then( ( responseData ) => {
151 if ( ! responseData.data || ! responseData.data.message ) {
152 throw new Error( 'Response contains no data.' );
153 }
154
155 console.log( responseData.data.message );
156
157 return responseData.data;
158 } );
159 }
160
161 /**
162 * Cleanup the runtime environment.
163 *
164 * @since 1.0.0
165 *
166 * @return {Object} The response data.
167 */
168 function cleanUpEnvironment() {
169 const pluginCheckData = new FormData();
170 pluginCheckData.append( 'nonce', pluginCheck.nonce );
171 pluginCheckData.append(
172 'action',
173 pluginCheck.actionCleanUpRuntimeEnvironment
174 );
175
176 return fetch( ajaxurl, {
177 method: 'POST',
178 credentials: 'same-origin',
179 body: pluginCheckData,
180 } )
181 .then( ( response ) => {
182 return response.json();
183 } )
184 .then( handleDataErrors )
185 .then( ( responseData ) => {
186 if ( ! responseData.data || ! responseData.data.message ) {
187 throw new Error( 'Response contains no data.' );
188 }
189
190 return responseData.data;
191 } );
192 }
193
194 /**
195 * Get the Checks to run.
196 *
197 * @since 1.0.0
198 */
199 function getChecksToRun() {
200 const pluginCheckData = new FormData();
201 pluginCheckData.append( 'nonce', pluginCheck.nonce );
202 pluginCheckData.append( 'plugin', pluginsList.value );
203 pluginCheckData.append( 'action', pluginCheck.actionGetChecksToRun );
204 pluginCheckData.append(
205 'include-experimental',
206 includeExperimental && includeExperimental.checked ? 1 : 0
207 );
208
209 for ( let i = 0; i < categoriesList.length; i++ ) {
210 if ( categoriesList[ i ].checked ) {
211 pluginCheckData.append(
212 'categories[]',
213 categoriesList[ i ].value
214 );
215 }
216 }
217
218 return fetch( ajaxurl, {
219 method: 'POST',
220 credentials: 'same-origin',
221 body: pluginCheckData,
222 } )
223 .then( ( response ) => {
224 return response.json();
225 } )
226 .then( handleDataErrors )
227 .then( ( responseData ) => {
228 if (
229 ! responseData.data ||
230 ! responseData.data.plugin ||
231 ! responseData.data.checks
232 ) {
233 throw new Error(
234 'Plugin and Checks are missing from the response.'
235 );
236 }
237
238 return responseData.data;
239 } );
240 }
241
242 /**
243 * Run Checks.
244 *
245 * @since 1.0.0
246 *
247 * @param {Object} data The response data.
248 */
249 async function runChecks( data ) {
250 let isSuccessMessage = true;
251 for ( let i = 0; i < data.checks.length; i++ ) {
252 try {
253 const results = await runCheck( data.plugin, data.checks[ i ] );
254 const errorsLength = Object.values( results.errors ).length;
255 const warningsLength = Object.values( results.warnings ).length;
256 if (
257 isSuccessMessage &&
258 ( errorsLength > 0 || warningsLength > 0 )
259 ) {
260 isSuccessMessage = false;
261 }
262 renderResults( results );
263 } catch ( e ) {
264 // Ignore for now.
265 }
266 }
267
268 renderResultsMessage( isSuccessMessage );
269 }
270
271 /**
272 * Renders result message.
273 *
274 * @since 1.0.0
275 *
276 * @param {boolean} isSuccessMessage Whether the message is a success message.
277 */
278 function renderResultsMessage( isSuccessMessage ) {
279 const messageType = isSuccessMessage ? 'success' : 'error';
280 const messageText = isSuccessMessage
281 ? pluginCheck.successMessage
282 : pluginCheck.errorMessage;
283
284 resultsContainer.innerHTML =
285 renderTemplate( 'plugin-check-results-complete', {
286 type: messageType,
287 message: messageText,
288 } ) + resultsContainer.innerHTML;
289 }
290
291 /**
292 * Run a single check.
293 *
294 * @since 1.0.0
295 *
296 * @param {string} plugin The plugin to check.
297 * @param {string} check The check to run.
298 * @return {Object} The check results.
299 */
300 function runCheck( plugin, check ) {
301 const pluginCheckData = new FormData();
302 pluginCheckData.append( 'nonce', pluginCheck.nonce );
303 pluginCheckData.append( 'plugin', plugin );
304 pluginCheckData.append( 'checks[]', check );
305 pluginCheckData.append( 'action', pluginCheck.actionRunChecks );
306 pluginCheckData.append(
307 'include-experimental',
308 includeExperimental && includeExperimental.checked ? 1 : 0
309 );
310
311 return fetch( ajaxurl, {
312 method: 'POST',
313 credentials: 'same-origin',
314 body: pluginCheckData,
315 } )
316 .then( ( response ) => {
317 return response.json();
318 } )
319 .then( handleDataErrors )
320 .then( ( responseData ) => {
321 // If the response is successful and there is no message in the response.
322 if ( ! responseData.data || ! responseData.data.message ) {
323 throw new Error( 'Response contains no data' );
324 }
325
326 return responseData.data;
327 } );
328 }
329
330 /**
331 * Handles any errors in the data returned from the response.
332 *
333 * @since 1.0.0
334 *
335 * @param {Object} data The response data.
336 * @return {Object} The response data.
337 */
338 function handleDataErrors( data ) {
339 if ( ! data ) {
340 throw new Error( 'Response contains no data' );
341 }
342
343 if ( ! data.success ) {
344 // If not successful and no message in the response.
345 if ( ! data.data || ! data.data[ 0 ].message ) {
346 throw new Error( 'Response contains no data' );
347 }
348
349 // If not successful and there is a message in the response.
350 throw new Error( data.data[ 0 ].message );
351 }
352
353 return data;
354 }
355
356 /**
357 * Renders results for each check on the page.
358 *
359 * @since 1.0.0
360 *
361 * @param {Object} results The results object.
362 */
363 function renderResults( results ) {
364 const { errors, warnings } = results;
365 // Render errors and warnings for files.
366 for ( const file in errors ) {
367 if ( warnings[ file ] ) {
368 renderFileResults( file, errors[ file ], warnings[ file ] );
369 delete warnings[ file ];
370 } else {
371 renderFileResults( file, errors[ file ], [] );
372 }
373 }
374
375 // Render remaining files with only warnings.
376 for ( const file in warnings ) {
377 renderFileResults( file, [], warnings[ file ] );
378 }
379 }
380
381 /**
382 * Renders the file results table.
383 *
384 * @since 1.0.0
385 *
386 * @param {string} file The file name for the results.
387 * @param {Object} errors The file errors.
388 * @param {Object} warnings The file warnings.
389 */
390 function renderFileResults( file, errors, warnings ) {
391 const index =
392 Date.now().toString( 36 ) +
393 Math.random().toString( 36 ).substr( 2 );
394
395 // Check if any errors or warnings have links.
396 const hasLinks =
397 hasLinksInResults( errors ) || hasLinksInResults( warnings );
398
399 // Render the file table.
400 resultsContainer.innerHTML += renderTemplate(
401 'plugin-check-results-table',
402 { file, index, hasLinks }
403 );
404 const resultsTable = document.getElementById(
405 'plugin-check__results-body-' + index
406 );
407
408 // Render results to the table.
409 renderResultRows( 'ERROR', errors, resultsTable, hasLinks );
410 renderResultRows( 'WARNING', warnings, resultsTable, hasLinks );
411 }
412
413 /**
414 * Checks if there are any links in the results object.
415 *
416 * @since 1.0.0
417 *
418 * @param {Object} results The results object.
419 * @return {boolean} True if there are links, false otherwise.
420 */
421 function hasLinksInResults( results ) {
422 for ( const line in results ) {
423 for ( const column in results[ line ] ) {
424 for ( let i = 0; i < results[ line ][ column ].length; i++ ) {
425 if ( results[ line ][ column ][ i ].link ) {
426 return true;
427 }
428 }
429 }
430 }
431 return false;
432 }
433
434 /**
435 * Renders a result row onto the file table.
436 *
437 * @since 1.0.0
438 *
439 * @param {string} type The result type. Either ERROR or WARNING.
440 * @param {Object} results The results object.
441 * @param {Object} table The HTML table to append a result row to.
442 * @param {boolean} hasLinks Whether any result has links.
443 */
444 function renderResultRows( type, results, table, hasLinks ) {
445 // Loop over each result by the line, column and messages.
446 for ( const line in results ) {
447 for ( const column in results[ line ] ) {
448 for ( let i = 0; i < results[ line ][ column ].length; i++ ) {
449 const message = results[ line ][ column ][ i ].message;
450 const docs = results[ line ][ column ][ i ].docs;
451 const code = results[ line ][ column ][ i ].code;
452 const link = results[ line ][ column ][ i ].link;
453
454 table.innerHTML += renderTemplate(
455 'plugin-check-results-row',
456 {
457 line,
458 column,
459 type,
460 message,
461 docs,
462 code,
463 link,
464 hasLinks,
465 }
466 );
467 }
468 }
469 }
470 }
471
472 /**
473 * Renders the template with data.
474 *
475 * @since 1.0.0
476 *
477 * @param {string} templateSlug The template slug
478 * @param {Object} data Template data.
479 * @return {string} Template HTML.
480 */
481 function renderTemplate( templateSlug, data ) {
482 if ( ! templates[ templateSlug ] ) {
483 templates[ templateSlug ] = wp.template( templateSlug );
484 }
485 const template = templates[ templateSlug ];
486 return template( data );
487 }
488 } )( PLUGIN_CHECK ); /* global PLUGIN_CHECK */
489