249 |
250 |
250 /** |
251 /** |
251 * Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins |
252 * Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins |
252 * on the site. |
253 * on the site. |
253 * @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned. |
254 * @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned. |
|
255 * @param bool If true, allows using cached information. Defaults to true. |
254 * @return array |
256 * @return array |
255 */ |
257 */ |
256 |
258 |
257 function get_plugin_list($restrict = null) |
259 function get_plugin_list($restrict = null, $use_cache = true) |
258 { |
260 { |
259 global $db, $session, $paths, $template, $plugins; // Common objects |
261 global $db, $session, $paths, $template, $plugins; // Common objects |
260 |
262 |
261 // Scan all plugins |
263 // Scan all plugins |
262 $plugin_list = array(); |
264 $plugin_list = array(); |
|
265 $ta = 0; |
|
266 // won't load twice (failsafe automatic skip) |
|
267 $this->load_plugins_cache(); |
|
268 if ( $use_cache ) |
|
269 { |
|
270 global $plugins_cache; |
|
271 } |
|
272 else |
|
273 { |
|
274 // blank array - effectively skips importing the cache |
|
275 $plugins_cache = array(); |
|
276 } |
263 |
277 |
264 if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) |
278 if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) ) |
265 { |
279 { |
266 while ( $dh = @readdir($dirh) ) |
280 while ( $dh = @readdir($dirh) ) |
267 { |
281 { |
270 |
284 |
271 if ( is_array($restrict) ) |
285 if ( is_array($restrict) ) |
272 if ( !in_array($dh, $restrict) ) |
286 if ( !in_array($dh, $restrict) ) |
273 continue; |
287 continue; |
274 |
288 |
|
289 // it's a PHP file, attempt to read metadata |
275 $fullpath = ENANO_ROOT . "/plugins/$dh"; |
290 $fullpath = ENANO_ROOT . "/plugins/$dh"; |
276 // it's a PHP file, attempt to read metadata |
291 // first can we use cached info? |
277 // pass 1: try to read a !info block |
292 if ( isset($plugins_cache[$dh]) && $plugins_cache[$dh]['file md5'] === $this->md5_header($fullpath) ) |
278 $blockdata = $this->parse_plugin_blocks($fullpath, 'info'); |
|
279 if ( empty($blockdata) ) |
|
280 { |
293 { |
281 // no !info block, check for old header |
294 $plugin_meta = $plugins_cache[$dh]; |
282 $fh = @fopen($fullpath, 'r'); |
|
283 if ( !$fh ) |
|
284 // can't read, bail out |
|
285 continue; |
|
286 $plugin_data = array(); |
|
287 for ( $i = 0; $i < 8; $i++ ) |
|
288 { |
|
289 $plugin_data[] = @fgets($fh, 8096); |
|
290 } |
|
291 // close our file handle |
|
292 fclose($fh); |
|
293 // is the header correct? |
|
294 if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' ) |
|
295 { |
|
296 // nope. get out. |
|
297 continue; |
|
298 } |
|
299 // parse all the variables |
|
300 $plugin_meta = array(); |
|
301 for ( $i = 2; $i <= 7; $i++ ) |
|
302 { |
|
303 if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) ) |
|
304 continue 2; |
|
305 $plugin_meta[ strtolower($match[1]) ] = $match[2]; |
|
306 } |
|
307 } |
295 } |
308 else |
296 else |
309 { |
297 { |
310 // parse JSON block |
298 // the cache is out of date if we reached here -- regenerate |
311 $plugin_data =& $blockdata[0]['value']; |
299 if ( $use_cache ) |
312 $plugin_data = enano_clean_json(enano_trim_json($plugin_data)); |
300 $this->generate_plugins_cache(); |
313 try |
301 |
|
302 // pass 1: try to read a !info block |
|
303 $blockdata = $this->parse_plugin_blocks($fullpath, 'info'); |
|
304 if ( empty($blockdata) ) |
314 { |
305 { |
315 $plugin_meta_uc = enano_json_decode($plugin_data); |
306 // no !info block, check for old header |
|
307 $fh = @fopen($fullpath, 'r'); |
|
308 if ( !$fh ) |
|
309 // can't read, bail out |
|
310 continue; |
|
311 $plugin_data = array(); |
|
312 for ( $i = 0; $i < 8; $i++ ) |
|
313 { |
|
314 $plugin_data[] = @fgets($fh, 8096); |
|
315 } |
|
316 // close our file handle |
|
317 fclose($fh); |
|
318 // is the header correct? |
|
319 if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' ) |
|
320 { |
|
321 // nope. get out. |
|
322 continue; |
|
323 } |
|
324 // parse all the variables |
|
325 $plugin_meta = array(); |
|
326 for ( $i = 2; $i <= 7; $i++ ) |
|
327 { |
|
328 if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) ) |
|
329 continue 2; |
|
330 $plugin_meta[ strtolower($match[1]) ] = $match[2]; |
|
331 } |
316 } |
332 } |
317 catch ( Exception $e ) |
333 else |
318 { |
334 { |
319 continue; |
335 // parse JSON block |
320 } |
336 $plugin_data =& $blockdata[0]['value']; |
321 // convert all the keys to lowercase |
337 $plugin_data = enano_clean_json(enano_trim_json($plugin_data)); |
322 $plugin_meta = array(); |
338 try |
323 foreach ( $plugin_meta_uc as $key => $value ) |
339 { |
324 { |
340 $plugin_meta_uc = enano_json_decode($plugin_data); |
325 $plugin_meta[ strtolower($key) ] = $value; |
341 } |
|
342 catch ( Exception $e ) |
|
343 { |
|
344 continue; |
|
345 } |
|
346 // convert all the keys to lowercase |
|
347 $plugin_meta = array(); |
|
348 foreach ( $plugin_meta_uc as $key => $value ) |
|
349 { |
|
350 $plugin_meta[ strtolower($key) ] = $value; |
|
351 } |
326 } |
352 } |
327 } |
353 } |
328 if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) |
354 if ( !isset($plugin_meta) || !is_array(@$plugin_meta) ) |
329 { |
355 { |
330 // parsing didn't work. |
356 // parsing didn't work. |
380 // done |
406 // done |
381 return $plugin_list; |
407 return $plugin_list; |
382 } |
408 } |
383 |
409 |
384 /** |
410 /** |
|
411 * Attempts to cache plugin information in a file to speed fetching. |
|
412 */ |
|
413 |
|
414 function generate_plugins_cache() |
|
415 { |
|
416 if ( getConfig('cache_thumbs') != '1' ) |
|
417 return; |
|
418 |
|
419 // fetch the most current info |
|
420 $plugin_info = $this->get_plugin_list(null, false); |
|
421 foreach ( $plugin_info as $plugin => &$info ) |
|
422 { |
|
423 $info['file md5'] = $this->md5_header(ENANO_ROOT . "/plugins/$plugin"); |
|
424 } |
|
425 |
|
426 $this->update_plugins_cache($plugin_info); |
|
427 $GLOBALS['plugins_cache'] = $plugin_info; |
|
428 @define('ENANO_PLUGINS_CACHE_LOADED', true); |
|
429 } |
|
430 |
|
431 /** |
|
432 * Writes an information array to the cache file. |
|
433 * @param array |
|
434 * @access private |
|
435 */ |
|
436 |
|
437 function update_plugins_cache($plugin_info) |
|
438 { |
|
439 $plugin_info = Language::var_export_string($plugin_info); |
|
440 |
|
441 $file = ENANO_ROOT . '/cache/cache_plugins.php'; |
|
442 $fh = @fopen($file, 'w'); |
|
443 if ( !$fh ) |
|
444 // can't open for writing |
|
445 return false; |
|
446 |
|
447 $contents = <<<EOF |
|
448 <?php |
|
449 /** |
|
450 * Cache of plugin files. Automatically generated, any modifications will soon be lost |
|
451 */ |
|
452 |
|
453 @define('ENANO_PLUGINS_CACHE_LOADED', true); |
|
454 \$GLOBALS['plugins_cache'] = $plugin_info; |
|
455 |
|
456 EOF; |
|
457 fwrite($fh, $contents); |
|
458 fclose($fh); |
|
459 } |
|
460 |
|
461 /** |
|
462 * Loads the plugins cache if any. |
|
463 */ |
|
464 |
|
465 function load_plugins_cache() |
|
466 { |
|
467 if ( file_exists(ENANO_ROOT . '/cache/cache_plugins.php') && !defined('ENANO_PLUGINS_CACHE_LOADED') ) |
|
468 { |
|
469 require(ENANO_ROOT . '/cache/cache_plugins.php'); |
|
470 } |
|
471 } |
|
472 |
|
473 /** |
|
474 * Calculates the MD5 sum of the first 10 lines of a file. Useful for caching plugin header information. |
|
475 * @param string File |
|
476 * @return string |
|
477 */ |
|
478 |
|
479 function md5_header($file) |
|
480 { |
|
481 $fh = @fopen($file, 'r'); |
|
482 if ( !$fh ) |
|
483 return false; |
|
484 $i = 0; |
|
485 $h = ''; |
|
486 while ( $i < 10 ) |
|
487 { |
|
488 $line = fgets($fh, 8096); |
|
489 $h .= $line . "\n"; |
|
490 $i++; |
|
491 } |
|
492 fclose($fh); |
|
493 return md5($h); |
|
494 } |
|
495 |
|
496 /** |
385 * Installs a plugin. |
497 * Installs a plugin. |
386 * @param string Filename of plugin. |
498 * @param string Filename of plugin. |
387 * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. |
499 * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time. |
388 * @return array JSON-formatted but not encoded response |
500 * @return array JSON-formatted but not encoded response |
389 */ |
501 */ |