Started work on the new plugin manager and associated management code. Very incomplete at this point and not usable.
authorDan
Sun, 06 Apr 2008 15:30:39 -0400
changeset 519 94214ec0871c
parent 518 2b826f2640e9
child 522 fd46b1bf708e
Started work on the new plugin manager and associated management code. Very incomplete at this point and not usable.
includes/functions.php
includes/lang.php
includes/plugins.php
install/schemas/mysql_stage2.sql
install/schemas/postgresql_stage2.sql
install/schemas/upgrade/1.1.3-1.1.4-mysql.sql
install/schemas/upgrade/1.1.3-1.1.4-postgresql.sql
language/english/admin.json
plugins/PrivateMessages.php
plugins/SpecialAdmin.php
plugins/SpecialCSS.php
plugins/SpecialGroups.php
plugins/SpecialPageFuncs.php
plugins/SpecialRecentChanges.php
plugins/SpecialSearch.php
plugins/SpecialUpdownload.php
plugins/SpecialUserFuncs.php
plugins/SpecialUserPrefs.php
plugins/admin/PluginManager.php
--- a/includes/functions.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/includes/functions.php	Sun Apr 06 15:30:39 2008 -0400
@@ -4078,6 +4078,17 @@
 }
 
 /**
+ * Trims a snippet of text to the first and last curly braces. Useful for JSON.
+ * @param string Text to trim
+ * @return string
+ */
+
+function enano_trim_json($json)
+{
+  return preg_replace('/^([^{]+)\{/', '{', preg_replace('/\}([^}]+)$/', '}', $json));
+}
+
+/**
  * Starts the profiler.
  */
 
--- a/includes/lang.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/includes/lang.php	Sun Apr 06 15:30:39 2008 -0400
@@ -389,8 +389,7 @@
     $contents =& $block[0]['value'];
     
     // Trim off all text before and after the starting and ending braces
-    $contents = preg_replace('/^([^{]+)\{/', '{', $contents);
-    $contents = preg_replace('/\}([^}]+)$/', '}', $contents);
+    $contents = enano_trim_json($contents);
     
     // Correct syntax to be nice to the json parser
     $contents = enano_clean_json($contents);
--- a/includes/plugins.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/includes/plugins.php	Sun Apr 06 15:30:39 2008 -0400
@@ -224,9 +224,22 @@
       if ( is_string($type) && $blocks[1][$i] !== $type )
         continue;
       
+      $value =& $blocks[4][$i];
+      // parse includes
+      preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes);
+      foreach ( $includes[0] as $i => $replace )
+      {
+        $filename = ENANO_ROOT . '/' . $includes[1][$i];
+        if ( @file_exists( $filename ) && @is_readable( $filename ) )
+        {
+          $contents = @file_get_contents($filename);
+          $value = str_replace_once($replace, $contents, $value);
+        }
+      }
+      
       $el = self::parse_vars($blocks[2][$i]);
       $el['block'] = $blocks[1][$i];
-      $el['value'] = $blocks[4][$i];
+      $el['value'] = $value;
       $return[] = $el;
     }
     
--- a/install/schemas/mysql_stage2.sql	Sun Apr 06 14:02:20 2008 -0400
+++ b/install/schemas/mysql_stage2.sql	Sun Apr 06 15:30:39 2008 -0400
@@ -318,6 +318,17 @@
   PRIMARY KEY ( key_id )
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
 
+-- Added in 1.1.4
+-- This is really honestly a better way to handle plugins.
+
+CREATE TABLE {{TABLE_PREFIX}}plugins (
+  plugin_id int(12) NOT NULL auto_increment,
+  plugin_filename varchar(63),
+  plugin_flags int(12),
+  plugin_version varchar(16),
+  PRIMARY KEY ( plugin_id )
+) ENGINE `MyISAM` CHARACTER SET `utf8` COLLATE `utf8_bin`;
+
 INSERT INTO {{TABLE_PREFIX}}config(config_name, config_value) VALUES
   ('site_name', '{{SITE_NAME}}'),
   ('main_page', 'Main_Page'),
@@ -335,10 +346,6 @@
   ('w3c_vcss', '0'),
   ('approve_comments', '0'),
   ('enable_comments', '1'),
-  ('plugin_SpecialAdmin.php', '1'),
-  ('plugin_SpecialPageFuncs.php', '1'),
-  ('plugin_SpecialUserFuncs.php', '1'),
-  ('plugin_SpecialCSS.php', '1'),
   ('copyright_notice', '{{COPYRIGHT}}'),
   ('wiki_edit_notice_text', '== Why can I edit this page? ==\n\nEveryone can edit almost any page in this website. This concept is called a wiki. It gives everyone the opportunity to make a change for the best. While some spam and vandalism may occur, it is believed that most contributions will be legitimate and helpful.\n\nFor security purposes, a history of all page edits is kept, and administrators are able to restore vandalized or spammed pages with just a few clicks.'),
   ('cache_thumbs', '{{ENABLE_CACHE}}'),
--- a/install/schemas/postgresql_stage2.sql	Sun Apr 06 14:02:20 2008 -0400
+++ b/install/schemas/postgresql_stage2.sql	Sun Apr 06 15:30:39 2008 -0400
@@ -315,6 +315,17 @@
   PRIMARY KEY ( key_id )
 );
 
+-- Added in 1.1.4
+-- This is really honestly a better way to handle plugins.
+
+CREATE TABLE {{TABLE_PREFIX}}plugins (
+  plugin_id SERIAL,
+  plugin_filename varchar(63),
+  plugin_flags int,
+  plugin_version varchar(16),
+  PRIMARY KEY ( plugin_id )
+);
+
 INSERT INTO {{TABLE_PREFIX}}config(config_name, config_value) VALUES
   ('site_name', '{{SITE_NAME}}'),
   ('main_page', 'Main_Page'),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/install/schemas/upgrade/1.1.3-1.1.4-mysql.sql	Sun Apr 06 15:30:39 2008 -0400
@@ -0,0 +1,10 @@
+-- This is really honestly a better way to handle plugins.
+
+CREATE TABLE {{TABLE_PREFIX}}plugins (
+  plugin_id int(12) NOT NULL auto_increment,
+  plugin_filename varchar(63),
+  plugin_flags int(12),
+  plugin_version varchar(16),
+  PRIMARY KEY ( plugin_id )
+) ENGINE `MyISAM` CHARACTER SET `utf8` COLLATE `utf8_bin`;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/install/schemas/upgrade/1.1.3-1.1.4-postgresql.sql	Sun Apr 06 15:30:39 2008 -0400
@@ -0,0 +1,9 @@
+-- This is really honestly a better way to handle plugins.
+
+CREATE TABLE {{TABLE_PREFIX}}plugins (
+  plugin_id SERIAL,
+  plugin_filename varchar(63),
+  plugin_flags int,
+  plugin_version varchar(16),
+  PRIMARY KEY ( plugin_id )
+);
--- a/language/english/admin.json	Sun Apr 06 14:02:20 2008 -0400
+++ b/language/english/admin.json	Sun Apr 06 15:30:39 2008 -0400
@@ -382,24 +382,7 @@
       msg_demo_mode: 'Hmm, enabling executables, are we? Tsk tsk. I\'d love to know what\'s in that EXE file you want to upload. OK, maybe you didn\'t enable EXEs. But nevertheless, changing allowed filetypes is disabled in the demo.',
     },
     acppl: {
-      err_heading: 'Error disabling plugin',
-      err_demo_plugin: 'The demo lockdown plugin cannot be disabled in demo mode.',
-      err_system_plugin: 'The plugin you selected cannot be disabled because it is a system plugin.',
-      err_open_dir: 'The plugins/ directory could not be opened.',
-      err_missing_dir: 'The plugins/ directory is missing from your Enano installation.',
-      col_filename: 'Plugin filename',
-      col_name: 'Plugin name',
-      col_description: 'Description',
-      col_author: 'Author',
-      col_version: 'Version',
-      btn_enable: 'Enable',
-      btn_disable: 'Disable',
-      btn_reimport: 'Re-import',
-      btn_reimport_tip: 'Reloads all the language and meta data from this plugin file.',
-      msg_reimport_success: 'All language strings from this plugin have been re-imported.',
-      btn_hide_system: 'Hide system plugins',
-      btn_show_system: 'Show system plugins',
-      lbl_system_plugin: '[System]',
+      
     },
     acppm: {
       heading_main: 'Edit page properties',
--- a/plugins/PrivateMessages.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/PrivateMessages.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_privatemessages_title
-Plugin URI: http://enanocms.org/
-Description: plugin_privatemessages_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_privatemessages_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_privatemessages_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialAdmin.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialAdmin.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialadmin_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialadmin_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialadmin_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialadmin_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
@@ -48,6 +50,7 @@
 require(ENANO_ROOT . '/plugins/admin/UserManager.php');
 require(ENANO_ROOT . '/plugins/admin/LangManager.php');
 require(ENANO_ROOT . '/plugins/admin/ThemeManager.php');
+require(ENANO_ROOT . '/plugins/admin/PluginManager.php');
 
 // For convenience and nothing more.
 function acp_start_form()
@@ -1146,6 +1149,7 @@
   <?php
 }
 
+/*
 function page_Admin_PluginManager()
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
@@ -1328,6 +1332,7 @@
     echo '<tr><th colspan="6" class="subhead">'.$showhide_link.'</th></tr>';
     echo '</table></div>';
 }
+*/
 
 function page_Admin_DBBackup()
 {
--- a/plugins/SpecialCSS.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialCSS.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialcss_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialcss_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialcss_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialcss_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialGroups.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialGroups.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialgroups_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialgroups_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialgroups_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialgroups_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialPageFuncs.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialPageFuncs.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialpagefuncs_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialpagefuncs_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialpagefuncs_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialpagefuncs_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialRecentChanges.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialRecentChanges.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialrecentchanges_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialrecentchanges_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialrecentchanges_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialrecentchanges_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialSearch.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialSearch.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialsearch_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialsearch_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialsearch_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialsearch_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialUpdownload.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialUpdownload.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialupdownload_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialupdownload_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialupdownload_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialupdownload_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialUserFuncs.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialUserFuncs.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialuserfuncs_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialuserfuncs_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialuserfuncs_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialuserfuncs_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- a/plugins/SpecialUserPrefs.php	Sun Apr 06 14:02:20 2008 -0400
+++ b/plugins/SpecialUserPrefs.php	Sun Apr 06 15:30:39 2008 -0400
@@ -1,12 +1,14 @@
 <?php
-/*
-Plugin Name: plugin_specialuserprefs_title
-Plugin URI: http://enanocms.org/
-Description: plugin_specialuserprefs_desc
-Author: Dan Fuhry
-Version: 1.1.3
-Author URI: http://enanocms.org/
-*/
+/**!info**
+{
+  "Plugin Name"  : "plugin_specialuserprefs_title",
+  "Plugin URI"   : "http://enanocms.org/",
+  "Description"  : "plugin_specialuserprefs_desc",
+  "Author"       : "Dan Fuhry",
+  "Version"      : "1.1.3",
+  "Author URI"   : "http://enanocms.org/"
+}
+**!*/
 
 /*
  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/admin/PluginManager.php	Sun Apr 06 15:30:39 2008 -0400
@@ -0,0 +1,261 @@
+<?php
+
+/*
+ * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
+ * Version 1.1.3 (Caoineag alpha 3)
+ * Copyright (C) 2006-2007 Dan Fuhry
+ *
+ * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ */
+
+/**
+ * SYNOPSIS OF PLUGIN FRAMEWORK
+ *
+ * The new plugin manager is making an alternative approach to managing plugin files by allowing metadata to be embedded in them
+ * or optionally included from external files. This method is API- and format-compatible with old plugins. The change is being
+ * made because we believe this will provide greater flexibility within plugin files.
+ * 
+ * Plugin files can contain one or more specially formatted comment blocks with metadata, language strings, and installation or
+ * upgrade SQL schemas. For this to work, plugins need to define their version numbers in an Enano-readable and standardized
+ * format, and we think the best way to do this is with JSON. It is important that plugins define both the current version and
+ * a list of all past versions, and then have upgrade sections telling which version they go from and which one they go to.
+ * 
+ * The format for the special comment blocks is:
+ <code>
+ /**!blocktype( param1 = "value1" [ param2 = "value2" ... ] )**
+ 
+ ... block content ...
+ 
+ **!* / (remove that last space)
+ </code>
+ * The format inside blocks varies. Metadata and language strings will be in JSON; installation and upgrade schemas will be in
+ * SQL. You can include an external file into a block using the following syntax inside of a block:
+ <code>
+ !include "path/to/file"
+ </code>
+ * The file will always be relative to the Enano root. So if your plugin has a language file in ENANO_ROOT/plugins/fooplugin/,
+ * you would use "plugins/fooplugin/language.json".
+ *
+ * The format for plugin metadata is as follows:
+ <code>
+ /**!info**
+ {
+   "Plugin Name" : "Foo plugin",
+   "Plugin URI" : "http://fooplugin.enanocms.org/",
+   "Description" : "Some short descriptive text",
+   "Author" : "John Doe",
+   "Version" : "0.1",
+   "Author URI" : "http://yourdomain.com/",
+   "Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ]
+ }
+ **!* /
+ </code>
+ * This is the format for language data:
+ <code>
+ /**!language**
+ {
+   eng: {
+     categories: [ 'meta', 'foo', 'bar' ],
+     strings: {
+       meta: {
+         foo: "Foo strings",
+         bar: "Bar strings"
+       },
+       foo: {
+         string_name: "string value",
+         string_name_2: "string value 2"
+       }
+     }
+   }
+ }
+ **!* / (once more, remove the space in there)
+ </code>
+ * Here is the format for installation schemas:
+ <code>
+ /**!install**
+ 
+ CREATE TABLE {{TABLE_PREFIX}}foo_table(
+   ...
+ )
+ 
+ **!* /
+ </code>
+ * And finally, the format for upgrade schemas:
+ <code>
+ /**!upgrade from = "0.1-alpha1" to = "0.1-alpha2" **
+ 
+ **!* /
+ </code>
+ * Remember that upgrades will always be done incrementally, so if the user is upgrading 0.1-alpha2 to 0.1, Enano's plugin
+ * engine will run the 0.1-alpha2 to 0.1-beta1 upgrader, then the 0.1-beta1 to 0.1 upgrader, going by the versions listed in
+ * the example metadata block above.
+ * 
+ * All of this information is effective as of Enano 1.1.4.
+ */
+
+// Plugin manager "2.0"
+
+function page_Admin_PluginManager()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  global $lang;
+  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+  {
+    $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
+    echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
+    echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
+    return;
+  }
+  
+  // Are we processing an AJAX request from the smartform?
+  if ( $paths->getParam(0) == 'action.json' )
+  {
+    // Set to application/json to discourage advertisement scripts
+    header('Content-Type: application/json');
+    
+    // Init return data
+    $return = array('mode' => 'error', 'error' => 'undefined');
+    
+    // Start parsing process
+    try
+    {
+      // Is the request properly sent on POST?
+      if ( isset($_POST['r']) )
+      {
+        // Try to decode the request
+        $request = enano_json_decode($_POST['r']);
+        // Is the action to perform specified?
+        if ( isset($request['mode']) )
+        {
+          switch ( $request['mode'] )
+          {
+            default:
+              // The requested action isn't something this script knows how to do
+              $return = array(
+                'mode' => 'error',
+                'error' => 'Unknown mode "' . $request['mode'] . '" sent in request'
+              );
+              break;
+          }
+        }
+        else
+        {
+          // Didn't specify action
+          $return = array(
+            'mode' => 'error',
+            'error' => 'Missing key "mode" in request'
+          );
+        }
+      }
+      else
+      {
+        // Didn't send a request
+        $return = array(
+          'mode' => 'error',
+          'error' => 'No request specified'
+        );
+      }
+    }
+    catch ( Exception $e )
+    {
+      // Sent a request but it's not valid JSON
+      $return = array(
+          'mode' => 'error',
+          'error' => 'Invalid request - JSON parsing failed'
+        );
+    }
+    
+    echo enano_json_encode($return);
+    
+    return true;
+  }
+  
+  //
+  // Not a JSON request, output normal HTML interface
+  //
+  
+  // Scan all plugins
+  $plugin_list = array();
+  
+  if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
+  {
+    while ( $dh = @readdir($dirh) )
+    {
+      if ( !preg_match('/\.php$/i', $dh) )
+        continue;
+      $fullpath = ENANO_ROOT . "/plugins/$dh";
+      // it's a PHP file, attempt to read metadata
+      // pass 1: try to read a !info block
+      $blockdata = $plugins->parse_plugin_blocks($fullpath, 'info');
+      if ( empty($blockdata) )
+      {
+        // no !info block, check for old header
+        $fh = @fopen($fullpath, 'r');
+        if ( !$fh )
+          // can't read, bail out
+          continue;
+        $plugin_data = array();
+        for ( $i = 0; $i < 8; $i++ )
+        {
+          $plugin_data[] = @fgets($fh, 8096);
+        }
+        // close our file handle
+        fclose($fh);
+        // is the header correct?
+        if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
+        {
+          // nope. get out.
+          continue;
+        }
+        // parse all the variables
+        $plugin_meta = array();
+        for ( $i = 2; $i <= 7; $i++ )
+        {
+          if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
+            continue 2;
+          $plugin_meta[ strtolower($match[1]) ] = $match[2];
+        }
+      }
+      else
+      {
+        // parse JSON block
+        $plugin_data =& $blockdata[0]['value'];
+        $plugin_data = enano_clean_json(enano_trim_json($plugin_data));
+        try
+        {
+          $plugin_meta_uc = enano_json_decode($plugin_data);
+        }
+        catch ( Exception $e )
+        {
+          continue;
+        }
+        // convert all the keys to lowercase
+        $plugin_meta = array();
+        foreach ( $plugin_meta_uc as $key => $value )
+        {
+          $plugin_meta[ strtolower($key) ] = $value;
+        }
+      }
+      if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
+      {
+        // parsing didn't work.
+        continue;
+      }
+      // check for required keys
+      $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
+      foreach ( $required_keys as $key )
+      {
+        if ( !isset($plugin_meta[$key]) )
+          // not set, skip this plugin
+          continue 2;
+      }
+      // all checks passed
+      $plugin_list[$dh] = $plugin_meta;
+    }
+  }
+  echo '<pre>' . print_r($plugin_list, true) . '</pre>';
+}