plugins/admin/PluginManager.php
changeset 519 94214ec0871c
child 524 26287ae2449d
equal deleted inserted replaced
518:2b826f2640e9 519:94214ec0871c
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
       
     5  * Version 1.1.3 (Caoineag alpha 3)
       
     6  * Copyright (C) 2006-2007 Dan Fuhry
       
     7  *
       
     8  * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
       
     9  * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
       
    12  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
       
    13  */
       
    14 
       
    15 /**
       
    16  * SYNOPSIS OF PLUGIN FRAMEWORK
       
    17  *
       
    18  * The new plugin manager is making an alternative approach to managing plugin files by allowing metadata to be embedded in them
       
    19  * or optionally included from external files. This method is API- and format-compatible with old plugins. The change is being
       
    20  * made because we believe this will provide greater flexibility within plugin files.
       
    21  * 
       
    22  * Plugin files can contain one or more specially formatted comment blocks with metadata, language strings, and installation or
       
    23  * upgrade SQL schemas. For this to work, plugins need to define their version numbers in an Enano-readable and standardized
       
    24  * format, and we think the best way to do this is with JSON. It is important that plugins define both the current version and
       
    25  * a list of all past versions, and then have upgrade sections telling which version they go from and which one they go to.
       
    26  * 
       
    27  * The format for the special comment blocks is:
       
    28  <code>
       
    29  /**!blocktype( param1 = "value1" [ param2 = "value2" ... ] )**
       
    30  
       
    31  ... block content ...
       
    32  
       
    33  **!* / (remove that last space)
       
    34  </code>
       
    35  * The format inside blocks varies. Metadata and language strings will be in JSON; installation and upgrade schemas will be in
       
    36  * SQL. You can include an external file into a block using the following syntax inside of a block:
       
    37  <code>
       
    38  !include "path/to/file"
       
    39  </code>
       
    40  * The file will always be relative to the Enano root. So if your plugin has a language file in ENANO_ROOT/plugins/fooplugin/,
       
    41  * you would use "plugins/fooplugin/language.json".
       
    42  *
       
    43  * The format for plugin metadata is as follows:
       
    44  <code>
       
    45  /**!info**
       
    46  {
       
    47    "Plugin Name" : "Foo plugin",
       
    48    "Plugin URI" : "http://fooplugin.enanocms.org/",
       
    49    "Description" : "Some short descriptive text",
       
    50    "Author" : "John Doe",
       
    51    "Version" : "0.1",
       
    52    "Author URI" : "http://yourdomain.com/",
       
    53    "Version list" : [ "0.1-alpha1", "0.1-alpha2", "0.1-beta1", "0.1" ]
       
    54  }
       
    55  **!* /
       
    56  </code>
       
    57  * This is the format for language data:
       
    58  <code>
       
    59  /**!language**
       
    60  {
       
    61    eng: {
       
    62      categories: [ 'meta', 'foo', 'bar' ],
       
    63      strings: {
       
    64        meta: {
       
    65          foo: "Foo strings",
       
    66          bar: "Bar strings"
       
    67        },
       
    68        foo: {
       
    69          string_name: "string value",
       
    70          string_name_2: "string value 2"
       
    71        }
       
    72      }
       
    73    }
       
    74  }
       
    75  **!* / (once more, remove the space in there)
       
    76  </code>
       
    77  * Here is the format for installation schemas:
       
    78  <code>
       
    79  /**!install**
       
    80  
       
    81  CREATE TABLE {{TABLE_PREFIX}}foo_table(
       
    82    ...
       
    83  )
       
    84  
       
    85  **!* /
       
    86  </code>
       
    87  * And finally, the format for upgrade schemas:
       
    88  <code>
       
    89  /**!upgrade from = "0.1-alpha1" to = "0.1-alpha2" **
       
    90  
       
    91  **!* /
       
    92  </code>
       
    93  * Remember that upgrades will always be done incrementally, so if the user is upgrading 0.1-alpha2 to 0.1, Enano's plugin
       
    94  * 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
       
    95  * the example metadata block above.
       
    96  * 
       
    97  * All of this information is effective as of Enano 1.1.4.
       
    98  */
       
    99 
       
   100 // Plugin manager "2.0"
       
   101 
       
   102 function page_Admin_PluginManager()
       
   103 {
       
   104   global $db, $session, $paths, $template, $plugins; // Common objects
       
   105   global $lang;
       
   106   if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
       
   107   {
       
   108     $login_link = makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true);
       
   109     echo '<h3>' . $lang->get('adm_err_not_auth_title') . '</h3>';
       
   110     echo '<p>' . $lang->get('adm_err_not_auth_body', array( 'login_link' => $login_link )) . '</p>';
       
   111     return;
       
   112   }
       
   113   
       
   114   // Are we processing an AJAX request from the smartform?
       
   115   if ( $paths->getParam(0) == 'action.json' )
       
   116   {
       
   117     // Set to application/json to discourage advertisement scripts
       
   118     header('Content-Type: application/json');
       
   119     
       
   120     // Init return data
       
   121     $return = array('mode' => 'error', 'error' => 'undefined');
       
   122     
       
   123     // Start parsing process
       
   124     try
       
   125     {
       
   126       // Is the request properly sent on POST?
       
   127       if ( isset($_POST['r']) )
       
   128       {
       
   129         // Try to decode the request
       
   130         $request = enano_json_decode($_POST['r']);
       
   131         // Is the action to perform specified?
       
   132         if ( isset($request['mode']) )
       
   133         {
       
   134           switch ( $request['mode'] )
       
   135           {
       
   136             default:
       
   137               // The requested action isn't something this script knows how to do
       
   138               $return = array(
       
   139                 'mode' => 'error',
       
   140                 'error' => 'Unknown mode "' . $request['mode'] . '" sent in request'
       
   141               );
       
   142               break;
       
   143           }
       
   144         }
       
   145         else
       
   146         {
       
   147           // Didn't specify action
       
   148           $return = array(
       
   149             'mode' => 'error',
       
   150             'error' => 'Missing key "mode" in request'
       
   151           );
       
   152         }
       
   153       }
       
   154       else
       
   155       {
       
   156         // Didn't send a request
       
   157         $return = array(
       
   158           'mode' => 'error',
       
   159           'error' => 'No request specified'
       
   160         );
       
   161       }
       
   162     }
       
   163     catch ( Exception $e )
       
   164     {
       
   165       // Sent a request but it's not valid JSON
       
   166       $return = array(
       
   167           'mode' => 'error',
       
   168           'error' => 'Invalid request - JSON parsing failed'
       
   169         );
       
   170     }
       
   171     
       
   172     echo enano_json_encode($return);
       
   173     
       
   174     return true;
       
   175   }
       
   176   
       
   177   //
       
   178   // Not a JSON request, output normal HTML interface
       
   179   //
       
   180   
       
   181   // Scan all plugins
       
   182   $plugin_list = array();
       
   183   
       
   184   if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
       
   185   {
       
   186     while ( $dh = @readdir($dirh) )
       
   187     {
       
   188       if ( !preg_match('/\.php$/i', $dh) )
       
   189         continue;
       
   190       $fullpath = ENANO_ROOT . "/plugins/$dh";
       
   191       // it's a PHP file, attempt to read metadata
       
   192       // pass 1: try to read a !info block
       
   193       $blockdata = $plugins->parse_plugin_blocks($fullpath, 'info');
       
   194       if ( empty($blockdata) )
       
   195       {
       
   196         // no !info block, check for old header
       
   197         $fh = @fopen($fullpath, 'r');
       
   198         if ( !$fh )
       
   199           // can't read, bail out
       
   200           continue;
       
   201         $plugin_data = array();
       
   202         for ( $i = 0; $i < 8; $i++ )
       
   203         {
       
   204           $plugin_data[] = @fgets($fh, 8096);
       
   205         }
       
   206         // close our file handle
       
   207         fclose($fh);
       
   208         // is the header correct?
       
   209         if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
       
   210         {
       
   211           // nope. get out.
       
   212           continue;
       
   213         }
       
   214         // parse all the variables
       
   215         $plugin_meta = array();
       
   216         for ( $i = 2; $i <= 7; $i++ )
       
   217         {
       
   218           if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
       
   219             continue 2;
       
   220           $plugin_meta[ strtolower($match[1]) ] = $match[2];
       
   221         }
       
   222       }
       
   223       else
       
   224       {
       
   225         // parse JSON block
       
   226         $plugin_data =& $blockdata[0]['value'];
       
   227         $plugin_data = enano_clean_json(enano_trim_json($plugin_data));
       
   228         try
       
   229         {
       
   230           $plugin_meta_uc = enano_json_decode($plugin_data);
       
   231         }
       
   232         catch ( Exception $e )
       
   233         {
       
   234           continue;
       
   235         }
       
   236         // convert all the keys to lowercase
       
   237         $plugin_meta = array();
       
   238         foreach ( $plugin_meta_uc as $key => $value )
       
   239         {
       
   240           $plugin_meta[ strtolower($key) ] = $value;
       
   241         }
       
   242       }
       
   243       if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
       
   244       {
       
   245         // parsing didn't work.
       
   246         continue;
       
   247       }
       
   248       // check for required keys
       
   249       $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
       
   250       foreach ( $required_keys as $key )
       
   251       {
       
   252         if ( !isset($plugin_meta[$key]) )
       
   253           // not set, skip this plugin
       
   254           continue 2;
       
   255       }
       
   256       // all checks passed
       
   257       $plugin_list[$dh] = $plugin_meta;
       
   258     }
       
   259   }
       
   260   echo '<pre>' . print_r($plugin_list, true) . '</pre>';
       
   261 }