includes/plugins.php
changeset 1227 bdac73ed481e
parent 1216 4125e19d3b27
child 1368 b19698a37d58
equal deleted inserted replaced
1226:de56132c008d 1227:bdac73ed481e
    18  * @copyright (C) 2006-2008 Enano Project
    18  * @copyright (C) 2006-2008 Enano Project
    19  * @license GNU General Public License <http://enanocms.org/Special:GNU_General_Public_License>
    19  * @license GNU General Public License <http://enanocms.org/Special:GNU_General_Public_License>
    20  */
    20  */
    21 
    21 
    22 class pluginLoader {
    22 class pluginLoader {
    23   
    23 	
    24   /**
    24 	/**
    25    * The list of hooks registered.
    25  	* The list of hooks registered.
    26    * @var array
    26  	* @var array
    27    * @access private
    27  	* @access private
    28    */
    28  	*/
    29   
    29 	
    30   var $hook_list;
    30 	var $hook_list;
    31   
    31 	
    32   /**
    32 	/**
    33    * The list of plugins that should be loaded. Used only by common.php.
    33  	* The list of plugins that should be loaded. Used only by common.php.
    34    * @var array
    34  	* @var array
    35    * @access private
    35  	* @access private
    36    */
    36  	*/
    37   
    37 	
    38   var $load_list;
    38 	var $load_list;
    39   
    39 	
    40   /**
    40 	/**
    41    * The list of plugins that are loaded currently. This is only used by the loaded() method which in turn is
    41  	* The list of plugins that are loaded currently. This is only used by the loaded() method which in turn is
    42    * used by template files with the <!-- IFPLUGIN --> special tag.
    42  	* used by template files with the <!-- IFPLUGIN --> special tag.
    43    * @var array
    43  	* @var array
    44    * @access private
    44  	* @access private
    45    */
    45  	*/
    46   
    46 	
    47   var $loaded_plugins;
    47 	var $loaded_plugins;
    48   
    48 	
    49   /**
    49 	/**
    50    * The list of plugins that are always loaded because they're part of the Enano core. This cannot be modified
    50  	* The list of plugins that are always loaded because they're part of the Enano core. This cannot be modified
    51    * by any external code because user plugins are loaded after the load_list is calculated. Can be useful in
    51  	* by any external code because user plugins are loaded after the load_list is calculated. Can be useful in
    52    * alternative administration panel frameworks that need the list of system plugins.
    52  	* alternative administration panel frameworks that need the list of system plugins.
    53    * @var array
    53  	* @var array
    54    */
    54  	*/
    55   
    55 	
    56   var $system_plugins = Array('SpecialUserFuncs.php','SpecialUserPrefs.php','SpecialPageFuncs.php','SpecialAdmin.php','SpecialCSS.php','SpecialUpdownload.php','SpecialSearch.php','PrivateMessages.php','SpecialGroups.php', 'SpecialLog.php', 'DemoMode.php');
    56 	var $system_plugins = Array('SpecialUserFuncs.php','SpecialUserPrefs.php','SpecialPageFuncs.php','SpecialAdmin.php','SpecialCSS.php','SpecialUpdownload.php','SpecialSearch.php','PrivateMessages.php','SpecialGroups.php', 'SpecialLog.php', 'DemoMode.php');
    57   
    57 	
    58   /**
    58 	/**
    59    * Name kept for compatibility. Effectively a constructor. Calculates the list of plugins that should be loaded
    59  	* Name kept for compatibility. Effectively a constructor. Calculates the list of plugins that should be loaded
    60    * and puts that list in the $load_list property. Plugin developers have absolutely no use for this whatsoever.
    60  	* and puts that list in the $load_list property. Plugin developers have absolutely no use for this whatsoever.
    61    */
    61  	*/
    62   
    62 	
    63   function loadAll() 
    63 	function loadAll() 
    64   {
    64 	{
    65     global $db, $session, $paths, $template, $plugins; // Common objects
    65 		global $db, $session, $paths, $template, $plugins; // Common objects
    66     $GLOBALS['plugins_cache'] = array();
    66 		$GLOBALS['plugins_cache'] = array();
    67     
    67 		
    68     // if we're in an upgrade, just skip this step
    68 		// if we're in an upgrade, just skip this step
    69     if ( defined('IN_ENANO_UPGRADE') )
    69 		if ( defined('IN_ENANO_UPGRADE') )
    70     {
    70 		{
    71       $this->load_list = array();
    71 			$this->load_list = array();
    72       return false;
    72 			return false;
    73     }
    73 		}
    74     
    74 		
    75     $dir = ENANO_ROOT.'/plugins/';
    75 		$dir = ENANO_ROOT.'/plugins/';
    76     
    76 		
    77     $plugin_list = $this->get_plugin_list();
    77 		$plugin_list = $this->get_plugin_list();
    78     $this->load_list = array();
    78 		$this->load_list = array();
    79   
    79 	
    80     foreach ( $plugin_list as $filename => $data )
    80 		foreach ( $plugin_list as $filename => $data )
    81     {
    81 		{
    82       if ( !$data['system plugin'] && ( $data['status'] & PLUGIN_OUTOFDATE || $data['status'] & PLUGIN_DISABLED || !$data['installed'] ) )
    82 			if ( !$data['system plugin'] && ( $data['status'] & PLUGIN_OUTOFDATE || $data['status'] & PLUGIN_DISABLED || !$data['installed'] ) )
    83         continue;
    83 				continue;
    84       
    84 			
    85       $this->load_list[] = $filename;
    85 			$this->load_list[] = $filename;
    86       $this->loaded_plugins[$filename] = $data;
    86 			$this->loaded_plugins[$filename] = $data;
    87     }
    87 		}
    88   }
    88 	}
    89   
    89 	
    90   /**
    90 	/**
    91    * Name kept for compatibility. This method is used to add a new hook into the code somewhere. Plugins are encouraged
    91  	* Name kept for compatibility. This method is used to add a new hook into the code somewhere. Plugins are encouraged
    92    * to set hooks and hook into other plugins in a fail-safe way, this encourages reuse of code. Returns an array, whose
    92  	* to set hooks and hook into other plugins in a fail-safe way, this encourages reuse of code. Returns an array, whose
    93    * values should be eval'ed.
    93  	* values should be eval'ed.
    94    * @example <code>
    94  	* @example <code>
    95    $code = $plugins->setHook('my_hook_name');
    95  	$code = $plugins->setHook('my_hook_name');
    96    foreach ( $code as $cmd )
    96  	foreach ( $code as $cmd )
    97    {
    97  	{
    98      eval($cmd);
    98  		eval($cmd);
    99    }
    99  	}
   100    </code>
   100  	</code>
   101    * @param string The name of the hook.
   101  	* @param string The name of the hook.
   102    * @param array Deprecated.
   102  	* @param array Deprecated.
   103    */
   103  	*/
   104   
   104 	
   105   function setHook($name, $dont_split = false)
   105 	function setHook($name, $dont_split = false)
   106   {
   106 	{
   107     if ( !empty($this->hook_list[$name]) && is_array($this->hook_list[$name]) )
   107 		if ( !empty($this->hook_list[$name]) && is_array($this->hook_list[$name]) )
   108     {
   108 		{
   109       if ( $dont_split )
   109 			if ( $dont_split )
   110         return $this->hook_list[$name];
   110 				return $this->hook_list[$name];
   111       
   111 			
   112       return array(implode("\n", $this->hook_list[$name]));
   112 			return array(implode("\n", $this->hook_list[$name]));
   113     }
   113 		}
   114     else
   114 		else
   115     {
   115 		{
   116       return Array();
   116 			return Array();
   117     }
   117 		}
   118   }
   118 	}
   119   
   119 	
   120   /**
   120 	/**
   121    * Attaches to a hook effectively scheduling some code to be run at that point. You should try to keep hooks clean by
   121  	* Attaches to a hook effectively scheduling some code to be run at that point. You should try to keep hooks clean by
   122    * making a function that has variables that need to be modified passed by reference.
   122  	* making a function that has variables that need to be modified passed by reference.
   123    * @example Simple example: <code>
   123  	* @example Simple example: <code>
   124    $plugins->attachHook('render_wikiformat_pre', '$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);');
   124  	$plugins->attachHook('render_wikiformat_pre', '$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);');
   125    </code>
   125  	</code>
   126    * @example More complicated example: <code>
   126  	* @example More complicated example: <code>
   127    $plugins->attachHook('render_wikiformat_pre', 'myplugin_parser_ext($text);');
   127  	$plugins->attachHook('render_wikiformat_pre', 'myplugin_parser_ext($text);');
   128    
   128  	
   129    // Notice that $text is passed by reference.
   129  	// Notice that $text is passed by reference.
   130    function myplugin_parser_ext(&$text)
   130  	function myplugin_parser_ext(&$text)
   131    {
   131  	{
   132      $text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);
   132  		$text = str_replace("Goodbye, Mr. Chips", "Hello, Mr. Carrots", $text);
   133    }
   133  	}
   134    </code>
   134  	</code>
   135    */
   135  	*/
   136   
   136 	
   137   function attachHook($name, $code)
   137 	function attachHook($name, $code)
   138   {
   138 	{
   139     if ( !isset($this->hook_list[$name]) )
   139 		if ( !isset($this->hook_list[$name]) )
   140     {
   140 		{
   141       $this->hook_list[$name] = Array();
   141 			$this->hook_list[$name] = Array();
   142     }
   142 		}
   143     $this->hook_list[$name][] = $code;
   143 		$this->hook_list[$name][] = $code;
   144   }
   144 	}
   145   
   145 	
   146   /**
   146 	/**
   147    * Tell whether a plugin is loaded or not.
   147  	* Tell whether a plugin is loaded or not.
   148    * @param string The filename of the plugin
   148  	* @param string The filename of the plugin
   149    * @return bool
   149  	* @return bool
   150    */
   150  	*/
   151   
   151 	
   152   function loaded($plugid)
   152 	function loaded($plugid)
   153   {
   153 	{
   154     return isset( $this->loaded_plugins[$plugid] );
   154 		return isset( $this->loaded_plugins[$plugid] );
   155   }
   155 	}
   156   
   156 	
   157   /**
   157 	/**
   158    * Parses all special comment blocks in a plugin and returns an array in the format:
   158  	* Parses all special comment blocks in a plugin and returns an array in the format:
   159    <code>
   159  	<code>
   160    array(
   160  	array(
   161        0 => array(
   161  			0 => array(
   162            'block' => 'upgrade',
   162  					'block' => 'upgrade',
   163            // parsed from the block's parameters section
   163  					// parsed from the block's parameters section
   164              'release_from' => '1.0b1',
   164  						'release_from' => '1.0b1',
   165              'release_to' => '1.0b2',
   165  						'release_to' => '1.0b2',
   166            'value' => 'foo'
   166  					'value' => 'foo'
   167          ),
   167  				),
   168        1 => array(
   168  			1 => array(
   169            ...
   169  					...
   170          )
   170  				)
   171      );
   171  		);
   172    </code>
   172  	</code>
   173    * @param string Path to plugin file
   173  	* @param string Path to plugin file
   174    * @param string Optional. The type of block to fetch. If this is specified, only the block type specified will be read, all others will be discarded.
   174  	* @param string Optional. The type of block to fetch. If this is specified, only the block type specified will be read, all others will be discarded.
   175    * @return array
   175  	* @return array
   176    */
   176  	*/
   177   
   177 	
   178   public static function parse_plugin_blocks($file, $type = false)
   178 	public static function parse_plugin_blocks($file, $type = false)
   179   {
   179 	{
   180     if ( !file_exists($file) )
   180 		if ( !file_exists($file) )
   181     {
   181 		{
   182       return array();
   182 			return array();
   183     }
   183 		}
   184     $blocks = array();
   184 		$blocks = array();
   185     $contents = @file_get_contents($file);
   185 		$contents = @file_get_contents($file);
   186     if ( empty($contents) )
   186 		if ( empty($contents) )
   187     {
   187 		{
   188       return array();
   188 			return array();
   189     }
   189 		}
   190     
   190 		
   191     // The "\r?" in this regular expression is the result of an embarrassing failure during a job related meeting.
   191 		// The "\r?" in this regular expression is the result of an embarrassing failure during a job related meeting.
   192     // I was demoing the authoring of an Enano plugin and simply could not figure out why the plugin would not
   192 		// I was demoing the authoring of an Enano plugin and simply could not figure out why the plugin would not
   193     // show up in the admin panel.
   193 		// show up in the admin panel.
   194 
   194 
   195     $regexp = '#^/\*\*!([a-z0-9_]+)'  // block header and type
   195 		$regexp = '#^/\*\*!([a-z0-9_]+)'  // block header and type
   196             . '(([\s]+[a-z0-9_]+[\s]*=[\s]*".+?"[\s]*;)*)' // parameters
   196 						. '(([\s]+[a-z0-9_]+[\s]*=[\s]*".+?"[\s]*;)*)' // parameters
   197             . '[\s]*\*\*' . "\r?\n"      // spacing and header close
   197 						. '[\s]*\*\*' . "\r?\n"      // spacing and header close
   198             . '([\w\W]+?)' . "\r?\n"     // value
   198 						. '([\w\W]+?)' . "\r?\n"     // value
   199             . '\*\*!\*/'              // closing comment
   199 						. '\*\*!\*/'              // closing comment
   200             . '#m';
   200 						. '#m';
   201             
   201 						
   202     // Match out all blocks
   202 		// Match out all blocks
   203     $results = preg_match_all($regexp, $contents, $blocks);
   203 		$results = preg_match_all($regexp, $contents, $blocks);
   204     
   204 		
   205     $return = array();
   205 		$return = array();
   206     foreach ( $blocks[0] as $i => $_ )
   206 		foreach ( $blocks[0] as $i => $_ )
   207     {
   207 		{
   208       if ( is_string($type) && $blocks[1][$i] !== $type )
   208 			if ( is_string($type) && $blocks[1][$i] !== $type )
   209         continue;
   209 				continue;
   210       
   210 			
   211       $value =& $blocks[4][$i];
   211 			$value =& $blocks[4][$i];
   212       // parse includes
   212 			// parse includes
   213       preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes);
   213 			preg_match_all('/^!include [\'"]?(.+?)[\'"]?$/m', $value, $includes);
   214       foreach ( $includes[0] as $i => $replace )
   214 			foreach ( $includes[0] as $i => $replace )
   215       {
   215 			{
   216         $filename = ENANO_ROOT . '/' . $includes[1][$i];
   216 				$filename = ENANO_ROOT . '/' . $includes[1][$i];
   217         if ( @file_exists( $filename ) && @is_readable( $filename ) )
   217 				if ( @file_exists( $filename ) && @is_readable( $filename ) )
   218         {
   218 				{
   219           $contents = @file_get_contents($filename);
   219 					$contents = @file_get_contents($filename);
   220           $value = str_replace_once($replace, $contents, $value);
   220 					$value = str_replace_once($replace, $contents, $value);
   221         }
   221 				}
   222       }
   222 			}
   223       
   223 			
   224       $el = self::parse_vars($blocks[2][$i]);
   224 			$el = self::parse_vars($blocks[2][$i]);
   225       $el['block'] = $blocks[1][$i];
   225 			$el['block'] = $blocks[1][$i];
   226       $el['value'] = $value;
   226 			$el['value'] = $value;
   227       $return[] = $el;
   227 			$return[] = $el;
   228     }
   228 		}
   229     
   229 		
   230     return $return;
   230 		return $return;
   231   }
   231 	}
   232   
   232 	
   233   private static function parse_vars($var_block)
   233 	private static function parse_vars($var_block)
   234   {
   234 	{
   235     preg_match_all('/[\s]+([a-z0-9_]+)[\s]*=[\s]*"(.+?)";/', $var_block, $matches);
   235 		preg_match_all('/[\s]+([a-z0-9_]+)[\s]*=[\s]*"(.+?)";/', $var_block, $matches);
   236     $return = array();
   236 		$return = array();
   237     foreach ( $matches[0] as $i => $_ )
   237 		foreach ( $matches[0] as $i => $_ )
   238     {
   238 		{
   239       $return[ $matches[1][$i] ] = $matches[2][$i];
   239 			$return[ $matches[1][$i] ] = $matches[2][$i];
   240     }
   240 		}
   241     return $return;
   241 		return $return;
   242   }
   242 	}
   243   
   243 	
   244   /**
   244 	/**
   245    * Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins
   245  	* Reads all plugins in the filesystem and cross-references them with the database, providing a very complete summary of plugins
   246    * on the site.
   246  	* on the site.
   247    * @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned.
   247  	* @param array If specified, will restrict scanned files to this list. Defaults to null, which means all PHP files will be scanned.
   248    * @param bool If true, allows using cached information. Defaults to true.
   248  	* @param bool If true, allows using cached information. Defaults to true.
   249    * @return array
   249  	* @return array
   250    */
   250  	*/
   251   
   251 	
   252   function get_plugin_list($restrict = null, $use_cache = true)
   252 	function get_plugin_list($restrict = null, $use_cache = true)
   253   {
   253 	{
   254     global $db, $session, $paths, $template, $plugins; // Common objects
   254 		global $db, $session, $paths, $template, $plugins; // Common objects
   255     
   255 		
   256     // Scan all plugins
   256 		// Scan all plugins
   257     $plugin_list = array();
   257 		$plugin_list = array();
   258     $ta = 0;
   258 		$ta = 0;
   259     // won't load twice (failsafe automatic skip)
   259 		// won't load twice (failsafe automatic skip)
   260     $this->load_plugins_cache();
   260 		$this->load_plugins_cache();
   261     global $plugins_cache;
   261 		global $plugins_cache;
   262     if ( $use_cache && !empty($plugins_cache) )
   262 		if ( $use_cache && !empty($plugins_cache) )
   263     {
   263 		{
   264       return $plugins_cache;
   264 			return $plugins_cache;
   265     }
   265 		}
   266     else
   266 		else
   267     {
   267 		{
   268       // blank array - effectively skips importing the cache
   268 			// blank array - effectively skips importing the cache
   269       $plugins_cache = array();
   269 			$plugins_cache = array();
   270     }
   270 		}
   271     
   271 		
   272     // List all plugins
   272 		// List all plugins
   273     if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
   273 		if ( $dirh = @opendir( ENANO_ROOT . '/plugins' ) )
   274     {
   274 		{
   275       while ( $dh = @readdir($dirh) )
   275 			while ( $dh = @readdir($dirh) )
   276       {
   276 			{
   277         if ( !preg_match('/\.php$/i', $dh) )
   277 				if ( !preg_match('/\.php$/i', $dh) )
   278           continue;
   278 					continue;
   279         
   279 				
   280         if ( is_array($restrict) )
   280 				if ( is_array($restrict) )
   281           if ( !in_array($dh, $restrict) )
   281 					if ( !in_array($dh, $restrict) )
   282             continue;
   282 						continue;
   283           
   283 					
   284         // it's a PHP file, attempt to read metadata
   284 				// it's a PHP file, attempt to read metadata
   285         $fullpath = ENANO_ROOT . "/plugins/$dh";
   285 				$fullpath = ENANO_ROOT . "/plugins/$dh";
   286         $plugin_meta = $this->read_plugin_headers($fullpath, $use_cache);
   286 				$plugin_meta = $this->read_plugin_headers($fullpath, $use_cache);
   287         
   287 				
   288         if ( is_array($plugin_meta) )
   288 				if ( is_array($plugin_meta) )
   289         {
   289 				{
   290           // all checks passed
   290 					// all checks passed
   291           $plugin_list[$dh] = $plugin_meta;
   291 					$plugin_list[$dh] = $plugin_meta;
   292         }
   292 				}
   293       }
   293 			}
   294     }
   294 		}
   295     
   295 		
   296     // Populate with additional metadata from database
   296 		// Populate with additional metadata from database
   297     $q = $db->sql_query('SELECT plugin_id, plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;');
   297 		$q = $db->sql_query('SELECT plugin_id, plugin_filename, plugin_version, plugin_flags FROM ' . table_prefix . 'plugins;');
   298     if ( !$q )
   298 		if ( !$q )
   299       $db->_die();
   299 			$db->_die();
   300     while ( $row = $db->fetchrow() )
   300 		while ( $row = $db->fetchrow() )
   301     {
   301 		{
   302       if ( !isset($plugin_list[ $row['plugin_filename'] ]) )
   302 			if ( !isset($plugin_list[ $row['plugin_filename'] ]) )
   303       {
   303 			{
   304         // missing plugin file, don't report (for now)
   304 				// missing plugin file, don't report (for now)
   305         continue;
   305 				continue;
   306       }
   306 			}
   307       $filename =& $row['plugin_filename'];
   307 			$filename =& $row['plugin_filename'];
   308       $plugin_list[$filename]['installed'] = true;
   308 			$plugin_list[$filename]['installed'] = true;
   309       $plugin_list[$filename]['status'] = PLUGIN_INSTALLED;
   309 			$plugin_list[$filename]['status'] = PLUGIN_INSTALLED;
   310       $plugin_list[$filename]['plugin id'] = $row['plugin_id'];
   310 			$plugin_list[$filename]['plugin id'] = $row['plugin_id'];
   311       if ( $row['plugin_version'] != $plugin_list[$filename]['version'] )
   311 			if ( $row['plugin_version'] != $plugin_list[$filename]['version'] )
   312       {
   312 			{
   313         $plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE;
   313 				$plugin_list[$filename]['status'] |= PLUGIN_OUTOFDATE;
   314         $plugin_list[$filename]['version installed'] = $row['plugin_version'];
   314 				$plugin_list[$filename]['version installed'] = $row['plugin_version'];
   315       }
   315 			}
   316       if ( $row['plugin_flags'] & PLUGIN_DISABLED )
   316 			if ( $row['plugin_flags'] & PLUGIN_DISABLED )
   317       {
   317 			{
   318         $plugin_list[$filename]['status'] |= PLUGIN_DISABLED;
   318 				$plugin_list[$filename]['status'] |= PLUGIN_DISABLED;
   319       }
   319 			}
   320     }
   320 		}
   321     $db->free_result();
   321 		$db->free_result();
   322     
   322 		
   323     // sort it all out by filename
   323 		// sort it all out by filename
   324     ksort($plugin_list);
   324 		ksort($plugin_list);
   325     
   325 		
   326     // done
   326 		// done
   327     return $plugin_list;
   327 		return $plugin_list;
   328   }
   328 	}
   329   
   329 	
   330   /**
   330 	/**
   331    * Retrieves the metadata block from a plugin file
   331  	* Retrieves the metadata block from a plugin file
   332    * @param string Path to plugin file (full path)
   332  	* @param string Path to plugin file (full path)
   333    * @return array
   333  	* @return array
   334    */
   334  	*/
   335   
   335 	
   336   function read_plugin_headers($fullpath, $use_cache = true)
   336 	function read_plugin_headers($fullpath, $use_cache = true)
   337   {
   337 	{
   338     global $plugins_cache;
   338 		global $plugins_cache;
   339     $dh = basename($fullpath);
   339 		$dh = basename($fullpath);
   340     
   340 		
   341     // first can we use cached info?
   341 		// first can we use cached info?
   342     if ( isset($plugins_cache[$dh]) && $plugins_cache[$dh]['file md5'] === $this->md5_header($fullpath) )
   342 		if ( isset($plugins_cache[$dh]) && $plugins_cache[$dh]['file md5'] === $this->md5_header($fullpath) )
   343     {
   343 		{
   344       $plugin_meta = $plugins_cache[$dh];
   344 			$plugin_meta = $plugins_cache[$dh];
   345     }
   345 		}
   346     else
   346 		else
   347     {
   347 		{
   348       // the cache is out of date if we reached here -- regenerate
   348 			// the cache is out of date if we reached here -- regenerate
   349       if ( $use_cache )
   349 			if ( $use_cache )
   350         $this->generate_plugins_cache();
   350 				$this->generate_plugins_cache();
   351       
   351 			
   352       // pass 1: try to read a !info block
   352 			// pass 1: try to read a !info block
   353       $blockdata = $this->parse_plugin_blocks($fullpath, 'info');
   353 			$blockdata = $this->parse_plugin_blocks($fullpath, 'info');
   354       if ( empty($blockdata) )
   354 			if ( empty($blockdata) )
   355       {
   355 			{
   356         // no !info block, check for old header
   356 				// no !info block, check for old header
   357         $fh = @fopen($fullpath, 'r');
   357 				$fh = @fopen($fullpath, 'r');
   358         if ( !$fh )
   358 				if ( !$fh )
   359           // can't read, bail out
   359 					// can't read, bail out
   360           return false;
   360 					return false;
   361         $plugin_data = array();
   361 				$plugin_data = array();
   362         for ( $i = 0; $i < 8; $i++ )
   362 				for ( $i = 0; $i < 8; $i++ )
   363         {
   363 				{
   364           $plugin_data[] = @fgets($fh, 8096);
   364 					$plugin_data[] = @fgets($fh, 8096);
   365         }
   365 				}
   366         // close our file handle
   366 				// close our file handle
   367         fclose($fh);
   367 				fclose($fh);
   368         // is the header correct?
   368 				// is the header correct?
   369         if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
   369 				if ( trim($plugin_data[0]) != '<?php' || trim($plugin_data[1]) != '/*' )
   370         {
   370 				{
   371           // nope. get out.
   371 					// nope. get out.
   372           return false;
   372 					return false;
   373         }
   373 				}
   374         // parse all the variables
   374 				// parse all the variables
   375         $plugin_meta = array();
   375 				$plugin_meta = array();
   376         for ( $i = 2; $i <= 7; $i++ )
   376 				for ( $i = 2; $i <= 7; $i++ )
   377         {
   377 				{
   378           if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
   378 					if ( !preg_match('/^([A-z0-9 ]+?): (.+?)$/', trim($plugin_data[$i]), $match) )
   379             return false;
   379 						return false;
   380           $plugin_meta[ strtolower($match[1]) ] = $match[2];
   380 					$plugin_meta[ strtolower($match[1]) ] = $match[2];
   381         }
   381 				}
   382       }
   382 			}
   383       else
   383 			else
   384       {
   384 			{
   385         // parse JSON block
   385 				// parse JSON block
   386         $plugin_data =& $blockdata[0]['value'];
   386 				$plugin_data =& $blockdata[0]['value'];
   387         $plugin_data = enano_clean_json(enano_trim_json($plugin_data));
   387 				$plugin_data = enano_clean_json(enano_trim_json($plugin_data));
   388         try
   388 				try
   389         {
   389 				{
   390           $plugin_meta_uc = enano_json_decode($plugin_data);
   390 					$plugin_meta_uc = enano_json_decode($plugin_data);
   391         }
   391 				}
   392         catch ( Exception $e )
   392 				catch ( Exception $e )
   393         {
   393 				{
   394           return false;
   394 					return false;
   395         }
   395 				}
   396         // convert all the keys to lowercase
   396 				// convert all the keys to lowercase
   397         $plugin_meta = array();
   397 				$plugin_meta = array();
   398         foreach ( $plugin_meta_uc as $key => $value )
   398 				foreach ( $plugin_meta_uc as $key => $value )
   399         {
   399 				{
   400           $plugin_meta[ strtolower($key) ] = $value;
   400 					$plugin_meta[ strtolower($key) ] = $value;
   401         }
   401 				}
   402       }
   402 			}
   403     }
   403 		}
   404     if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
   404 		if ( !isset($plugin_meta) || !is_array(@$plugin_meta) )
   405     {
   405 		{
   406       // parsing didn't work.
   406 			// parsing didn't work.
   407       return false;
   407 			return false;
   408     }
   408 		}
   409     // check for required keys
   409 		// check for required keys
   410     $required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
   410 		$required_keys = array('plugin name', 'plugin uri', 'description', 'author', 'version', 'author uri');
   411     foreach ( $required_keys as $key )
   411 		foreach ( $required_keys as $key )
   412     {
   412 		{
   413       if ( !isset($plugin_meta[$key]) )
   413 			if ( !isset($plugin_meta[$key]) )
   414         // not set, skip this plugin
   414 				// not set, skip this plugin
   415         return false;
   415 				return false;
   416     }
   416 		}
   417     // decide if it's a system plugin
   417 		// decide if it's a system plugin
   418     $plugin_meta['system plugin'] = in_array($dh, $this->system_plugins);
   418 		$plugin_meta['system plugin'] = in_array($dh, $this->system_plugins);
   419     // reset installed variable
   419 		// reset installed variable
   420     $plugin_meta['installed'] = false;
   420 		$plugin_meta['installed'] = false;
   421     $plugin_meta['status'] = 0;
   421 		$plugin_meta['status'] = 0;
   422     
   422 		
   423     return $plugin_meta;
   423 		return $plugin_meta;
   424   }
   424 	}
   425   
   425 	
   426   
   426 	
   427   /**
   427 	/**
   428    * Attempts to cache plugin information in a file to speed fetching.
   428  	* Attempts to cache plugin information in a file to speed fetching.
   429    */
   429  	*/
   430   
   430 	
   431   function generate_plugins_cache()
   431 	function generate_plugins_cache()
   432   {
   432 	{
   433     if ( getConfig('cache_thumbs') != '1' )
   433 		if ( getConfig('cache_thumbs') != '1' )
   434       return;
   434 			return;
   435     
   435 		
   436     // fetch the most current info
   436 		// fetch the most current info
   437     $plugin_info = $this->get_plugin_list(null, false);
   437 		$plugin_info = $this->get_plugin_list(null, false);
   438     foreach ( $plugin_info as $plugin => &$info )
   438 		foreach ( $plugin_info as $plugin => &$info )
   439     {
   439 		{
   440       $info['file md5'] = $this->md5_header(ENANO_ROOT . "/plugins/$plugin");
   440 			$info['file md5'] = $this->md5_header(ENANO_ROOT . "/plugins/$plugin");
   441     }
   441 		}
   442     
   442 		
   443     $this->update_plugins_cache($plugin_info);
   443 		$this->update_plugins_cache($plugin_info);
   444     $GLOBALS['plugins_cache'] = $plugin_info;
   444 		$GLOBALS['plugins_cache'] = $plugin_info;
   445     
   445 		
   446     return true;
   446 		return true;
   447   }
   447 	}
   448   
   448 	
   449   /**
   449 	/**
   450    * Writes an information array to the cache file.
   450  	* Writes an information array to the cache file.
   451    * @param array
   451  	* @param array
   452    * @access private
   452  	* @access private
   453    */
   453  	*/
   454   
   454 	
   455   function update_plugins_cache($plugin_info)
   455 	function update_plugins_cache($plugin_info)
   456   {
   456 	{
   457     global $cache;
   457 		global $cache;
   458     try
   458 		try
   459     {
   459 		{
   460       $result = $cache->store('plugins', $plugin_info, -1);
   460 			$result = $cache->store('plugins', $plugin_info, -1);
   461     }
   461 		}
   462     catch ( Exception $e )
   462 		catch ( Exception $e )
   463     {
   463 		{
   464       return false;
   464 			return false;
   465     }
   465 		}
   466     return $result;
   466 		return $result;
   467   }
   467 	}
   468   
   468 	
   469   /**
   469 	/**
   470    * Loads the plugins cache if any.
   470  	* Loads the plugins cache if any.
   471    */
   471  	*/
   472   
   472 	
   473   function load_plugins_cache()
   473 	function load_plugins_cache()
   474   {
   474 	{
   475     global $cache;
   475 		global $cache;
   476     if ( $data = $cache->fetch('plugins') )
   476 		if ( $data = $cache->fetch('plugins') )
   477     {
   477 		{
   478       $GLOBALS['plugins_cache'] = $data;
   478 			$GLOBALS['plugins_cache'] = $data;
   479     }
   479 		}
   480   }
   480 	}
   481   
   481 	
   482   /**
   482 	/**
   483    * Calculates the MD5 sum of the first 10 lines of a file. Useful for caching plugin header information.
   483  	* Calculates the MD5 sum of the first 10 lines of a file. Useful for caching plugin header information.
   484    * @param string File
   484  	* @param string File
   485    * @return string
   485  	* @return string
   486    */
   486  	*/
   487   
   487 	
   488   function md5_header($file)
   488 	function md5_header($file)
   489   {
   489 	{
   490     $fh = @fopen($file, 'r');
   490 		$fh = @fopen($file, 'r');
   491     if ( !$fh )
   491 		if ( !$fh )
   492       return false;
   492 			return false;
   493     $i = 0;
   493 		$i = 0;
   494     $h = '';
   494 		$h = '';
   495     while ( $i < 10 )
   495 		while ( $i < 10 )
   496     {
   496 		{
   497       $line = fgets($fh, 8096);
   497 			$line = fgets($fh, 8096);
   498       $h .= $line . "\n";
   498 			$h .= $line . "\n";
   499       $i++;
   499 			$i++;
   500     }
   500 		}
   501     fclose($fh);
   501 		fclose($fh);
   502     return md5($h);
   502 		return md5($h);
   503   }
   503 	}
   504   
   504 	
   505   /**
   505 	/**
   506    * Determines if a file is an authentication extension by looking at the file contents.
   506  	* Determines if a file is an authentication extension by looking at the file contents.
   507    * @param string Plugin filename
   507  	* @param string Plugin filename
   508    * @return bool
   508  	* @return bool
   509    */
   509  	*/
   510   
   510 	
   511   function is_file_auth_plugin($filename)
   511 	function is_file_auth_plugin($filename)
   512   {
   512 	{
   513     $filename = ENANO_ROOT . '/plugins/' . $filename;
   513 		$filename = ENANO_ROOT . '/plugins/' . $filename;
   514     if ( !file_exists($filename) )
   514 		if ( !file_exists($filename) )
   515       return false;
   515 			return false;
   516     
   516 		
   517     $info = $this->read_plugin_headers($filename);
   517 		$info = $this->read_plugin_headers($filename);
   518     if ( isset($info['auth plugin']) )
   518 		if ( isset($info['auth plugin']) )
   519       return true;
   519 			return true;
   520     
   520 		
   521     $contents = @file_get_contents($filename);
   521 		$contents = @file_get_contents($filename);
   522     if ( strstr($contents, 'login_process_userdata_json') )
   522 		if ( strstr($contents, 'login_process_userdata_json') )
   523       return true;
   523 			return true;
   524     
   524 		
   525     return false;
   525 		return false;
   526   }
   526 	}
   527   
   527 	
   528   /**
   528 	/**
   529    * Installs a plugin.
   529  	* Installs a plugin.
   530    * @param string Filename of plugin.
   530  	* @param string Filename of plugin.
   531    * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   531  	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   532    * @return array JSON-formatted but not encoded response
   532  	* @return array JSON-formatted but not encoded response
   533    */
   533  	*/
   534   
   534 	
   535   function install_plugin($filename, $plugin_list = null)
   535 	function install_plugin($filename, $plugin_list = null)
   536   {
   536 	{
   537     global $db, $session, $paths, $template, $plugins; // Common objects
   537 		global $db, $session, $paths, $template, $plugins; // Common objects
   538     global $lang;
   538 		global $lang;
   539     global $cache;
   539 		global $cache;
   540     
   540 		
   541     if ( defined('ENANO_DEMO_MODE') )
   541 		if ( defined('ENANO_DEMO_MODE') )
   542     {
   542 		{
   543       return array(
   543 			return array(
   544           'mode' => 'error',
   544 					'mode' => 'error',
   545           'error' => $lang->get('acppl_err_demo_mode')
   545 					'error' => $lang->get('acppl_err_demo_mode')
   546         );
   546 				);
   547     }
   547 		}
   548     
   548 		
   549     if ( !$plugin_list )
   549 		if ( !$plugin_list )
   550       $plugin_list = $this->get_plugin_list();
   550 			$plugin_list = $this->get_plugin_list();
   551     
   551 		
   552     // we're gonna need this
   552 		// we're gonna need this
   553     require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   553 		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   554     
   554 		
   555     switch ( true ): case true:
   555 		switch ( true ): case true:
   556       
   556 			
   557     // is the plugin in the directory and awaiting installation?
   557 		// is the plugin in the directory and awaiting installation?
   558     if ( !isset($plugin_list[$filename]) || (
   558 		if ( !isset($plugin_list[$filename]) || (
   559         isset($plugin_list[$filename]) && $plugin_list[$filename]['installed']
   559 				isset($plugin_list[$filename]) && $plugin_list[$filename]['installed']
   560       ))
   560 			))
   561     {
   561 		{
   562       $return = array(
   562 			$return = array(
   563         'mode' => 'error',
   563 				'mode' => 'error',
   564         'error' => 'Invalid plugin specified.',
   564 				'error' => 'Invalid plugin specified.',
   565         'debug' => $filename
   565 				'debug' => $filename
   566       );
   566 			);
   567       break;
   567 			break;
   568     }
   568 		}
   569     
   569 		
   570     $dataset =& $plugin_list[$filename];
   570 		$dataset =& $plugin_list[$filename];
   571     
   571 		
   572     // load up the installer schema
   572 		// load up the installer schema
   573     $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'install' );
   573 		$schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'install' );
   574     
   574 		
   575     $sql = array();
   575 		$sql = array();
   576     global $dbdriver;
   576 		global $dbdriver;
   577     if ( !empty($schema) )
   577 		if ( !empty($schema) )
   578     {
   578 		{
   579       // Decide which schema to use
   579 			// Decide which schema to use
   580       $use_schema = false;
   580 			$use_schema = false;
   581       foreach ( $schema as $current_schema )
   581 			foreach ( $schema as $current_schema )
   582       {
   582 			{
   583         if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
   583 				if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
   584         {
   584 				{
   585           $use_schema =& $current_schema['value'];
   585 					$use_schema =& $current_schema['value'];
   586           break;
   586 					break;
   587         }
   587 				}
   588       }
   588 			}
   589       if ( !$use_schema )
   589 			if ( !$use_schema )
   590       {
   590 			{
   591         if ( !isset($schema[0]['dbms']) )
   591 				if ( !isset($schema[0]['dbms']) )
   592         {
   592 				{
   593           $use_schema =& $schema[0]['value'];
   593 					$use_schema =& $schema[0]['value'];
   594         }
   594 				}
   595         else
   595 				else
   596         {
   596 				{
   597           $return = array(
   597 					$return = array(
   598             'mode' => 'error',
   598 						'mode' => 'error',
   599             'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
   599 						'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
   600           );
   600 					);
   601           break;
   601 					break;
   602         }
   602 				}
   603       }
   603 			}
   604       // parse SQL
   604 			// parse SQL
   605       $parser = new SQL_Parser($use_schema, true);
   605 			$parser = new SQL_Parser($use_schema, true);
   606       $parser->assign_vars(array(
   606 			$parser->assign_vars(array(
   607         'TABLE_PREFIX' => table_prefix
   607 				'TABLE_PREFIX' => table_prefix
   608         ));
   608 				));
   609       $sql = $parser->parse();
   609 			$sql = $parser->parse();
   610     }
   610 		}
   611     
   611 		
   612     // schema is final, check queries
   612 		// schema is final, check queries
   613     foreach ( $sql as $query )
   613 		foreach ( $sql as $query )
   614     {
   614 		{
   615       if ( !$db->check_query($query) )
   615 			if ( !$db->check_query($query) )
   616       {
   616 			{
   617         // aww crap, a query is bad
   617 				// aww crap, a query is bad
   618         $return = array(
   618 				$return = array(
   619           'mode' => 'error',
   619 					'mode' => 'error',
   620           'error' => $lang->get('acppl_err_upgrade_bad_query'),
   620 					'error' => $lang->get('acppl_err_upgrade_bad_query'),
   621         );
   621 				);
   622         break 2;
   622 				break 2;
   623       }
   623 			}
   624     }
   624 		}
   625     
   625 		
   626     // this is it, perform installation
   626 		// this is it, perform installation
   627     foreach ( $sql as $query )
   627 		foreach ( $sql as $query )
   628     {
   628 		{
   629       if ( substr($query, 0, 1) == '@' )
   629 			if ( substr($query, 0, 1) == '@' )
   630       {
   630 			{
   631         $query = substr($query, 1);
   631 				$query = substr($query, 1);
   632         $db->sql_query($query);
   632 				$db->sql_query($query);
   633       }
   633 			}
   634       else
   634 			else
   635       {
   635 			{
   636         if ( !$db->sql_query($query) )
   636 				if ( !$db->sql_query($query) )
   637         {
   637 				{
   638           $return = array(
   638 					$return = array(
   639               'mode' => 'error',
   639 							'mode' => 'error',
   640               'error' => "[SQL] " . $db->sql_error()
   640 							'error' => "[SQL] " . $db->sql_error()
   641             );
   641 						);
   642           break 2;
   642 					break 2;
   643         }
   643 				}
   644       }
   644 			}
   645     }
   645 		}
   646     
   646 		
   647     // log action
   647 		// log action
   648     $time        = time();
   648 		$time        = time();
   649     $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
   649 		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
   650     $username_db = $db->escape($session->username);
   650 		$username_db = $db->escape($session->username);
   651     $file_db     = $db->escape($filename);
   651 		$file_db     = $db->escape($filename);
   652     $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
   652 		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
   653                       . "  ('security', 'plugin_install', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
   653 											. "  ('security', 'plugin_install', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
   654     if ( !$q )
   654 		if ( !$q )
   655       $db->_die();
   655 			$db->_die();
   656     
   656 		
   657     // register plugin
   657 		// register plugin
   658     $version_db = $db->escape($dataset['version']);
   658 		$version_db = $db->escape($dataset['version']);
   659     $filename_db = $db->escape($filename);
   659 		$filename_db = $db->escape($filename);
   660     $flags = PLUGIN_INSTALLED;
   660 		$flags = PLUGIN_INSTALLED;
   661     
   661 		
   662     $q = $db->sql_query('INSERT INTO ' . table_prefix . "plugins ( plugin_version, plugin_filename, plugin_flags )\n"
   662 		$q = $db->sql_query('INSERT INTO ' . table_prefix . "plugins ( plugin_version, plugin_filename, plugin_flags )\n"
   663                       . "  VALUES ( '$version_db', '$filename_db', $flags );");
   663 											. "  VALUES ( '$version_db', '$filename_db', $flags );");
   664     if ( !$q )
   664 		if ( !$q )
   665       $db->die_json();
   665 			$db->die_json();
   666     
   666 		
   667     $plugin_list[$filename]['installed'] = true;
   667 		$plugin_list[$filename]['installed'] = true;
   668     $this->reimport_plugin_strings($filename, $plugin_list);
   668 		$this->reimport_plugin_strings($filename, $plugin_list);
   669     
   669 		
   670     $return = array(
   670 		$return = array(
   671       'success' => true
   671 			'success' => true
   672     );
   672 		);
   673     
   673 		
   674     endswitch;
   674 		endswitch;
   675     
   675 		
   676     $cache->purge('plugins');
   676 		$cache->purge('plugins');
   677     $cache->purge('page_meta');
   677 		$cache->purge('page_meta');
   678     $cache->purge('anon_sidebar');
   678 		$cache->purge('anon_sidebar');
   679     
   679 		
   680     return $return;
   680 		return $return;
   681   }
   681 	}
   682   
   682 	
   683   /**
   683 	/**
   684    * Uninstalls a plugin, removing it completely from the database and calling any custom uninstallation code the plugin specifies.
   684  	* Uninstalls a plugin, removing it completely from the database and calling any custom uninstallation code the plugin specifies.
   685    * @param string Filename of plugin.
   685  	* @param string Filename of plugin.
   686    * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   686  	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   687    * @return array JSON-formatted but not encoded response
   687  	* @return array JSON-formatted but not encoded response
   688    */
   688  	*/
   689   
   689 	
   690   function uninstall_plugin($filename, $plugin_list = null)
   690 	function uninstall_plugin($filename, $plugin_list = null)
   691   {
   691 	{
   692     global $db, $session, $paths, $template, $plugins; // Common objects
   692 		global $db, $session, $paths, $template, $plugins; // Common objects
   693     global $lang;
   693 		global $lang;
   694     global $cache;
   694 		global $cache;
   695     
   695 		
   696     if ( defined('ENANO_DEMO_MODE') )
   696 		if ( defined('ENANO_DEMO_MODE') )
   697     {
   697 		{
   698       return array(
   698 			return array(
   699           'mode' => 'error',
   699 					'mode' => 'error',
   700           'error' => $lang->get('acppl_err_demo_mode')
   700 					'error' => $lang->get('acppl_err_demo_mode')
   701         );
   701 				);
   702     }
   702 		}
   703     
   703 		
   704     if ( !$plugin_list )
   704 		if ( !$plugin_list )
   705       $plugin_list = $this->get_plugin_list();
   705 			$plugin_list = $this->get_plugin_list();
   706     
   706 		
   707     // we're gonna need this
   707 		// we're gonna need this
   708     require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   708 		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   709     
   709 		
   710     switch ( true ): case true:
   710 		switch ( true ): case true:
   711     
   711 		
   712     // is the plugin in the directory and already installed?
   712 		// is the plugin in the directory and already installed?
   713     if ( !isset($plugin_list[$filename]) || (
   713 		if ( !isset($plugin_list[$filename]) || (
   714         isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
   714 				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
   715       ))
   715 			))
   716     {
   716 		{
   717       $return = array(
   717 			$return = array(
   718         'mode' => 'error',
   718 				'mode' => 'error',
   719         'error' => 'Invalid plugin specified.',
   719 				'error' => 'Invalid plugin specified.',
   720       );
   720 			);
   721       break;
   721 			break;
   722     }
   722 		}
   723     // get plugin id
   723 		// get plugin id
   724     $dataset =& $plugin_list[$filename];
   724 		$dataset =& $plugin_list[$filename];
   725     if ( empty($dataset['plugin id']) )
   725 		if ( empty($dataset['plugin id']) )
   726     {
   726 		{
   727       $return = array(
   727 			$return = array(
   728         'mode' => 'error',
   728 				'mode' => 'error',
   729         'error' => 'Couldn\'t retrieve plugin ID.',
   729 				'error' => 'Couldn\'t retrieve plugin ID.',
   730       );
   730 			);
   731       break;
   731 			break;
   732     }
   732 		}
   733     
   733 		
   734     // load up the installer schema
   734 		// load up the installer schema
   735     $schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'uninstall' );
   735 		$schema = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'uninstall' );
   736     
   736 		
   737     $sql = array();
   737 		$sql = array();
   738     global $dbdriver;
   738 		global $dbdriver;
   739     if ( !empty($schema) )
   739 		if ( !empty($schema) )
   740     {
   740 		{
   741       // Decide which schema to use
   741 			// Decide which schema to use
   742       $use_schema = false;
   742 			$use_schema = false;
   743       foreach ( $schema as $current_schema )
   743 			foreach ( $schema as $current_schema )
   744       {
   744 			{
   745         if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
   745 				if ( isset($current_schema['dbms']) && $current_schema['dbms'] === $dbdriver )
   746         {
   746 				{
   747           $use_schema =& $current_schema['value'];
   747 					$use_schema =& $current_schema['value'];
   748           break;
   748 					break;
   749         }
   749 				}
   750       }
   750 			}
   751       if ( !$use_schema )
   751 			if ( !$use_schema )
   752       {
   752 			{
   753         if ( !isset($schema[0]['dbms']) )
   753 				if ( !isset($schema[0]['dbms']) )
   754         {
   754 				{
   755           $use_schema =& $schema[0]['value'];
   755 					$use_schema =& $schema[0]['value'];
   756         }
   756 				}
   757         else
   757 				else
   758         {
   758 				{
   759           $return = array(
   759 					$return = array(
   760             'mode' => 'error',
   760 						'mode' => 'error',
   761             'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
   761 						'error' => $lang->get('acppl_err_dmbs_not_supported', array('dbdriver' => $db->dbms_name))
   762           );
   762 					);
   763           break;
   763 					break;
   764         }
   764 				}
   765       }
   765 			}
   766       // parse SQL
   766 			// parse SQL
   767       $parser = new SQL_Parser($use_schema, true);
   767 			$parser = new SQL_Parser($use_schema, true);
   768       $parser->assign_vars(array(
   768 			$parser->assign_vars(array(
   769         'TABLE_PREFIX' => table_prefix
   769 				'TABLE_PREFIX' => table_prefix
   770         ));
   770 				));
   771       $sql = $parser->parse();
   771 			$sql = $parser->parse();
   772     }
   772 		}
   773     
   773 		
   774     // schema is final, check queries
   774 		// schema is final, check queries
   775     foreach ( $sql as $query )
   775 		foreach ( $sql as $query )
   776     {
   776 		{
   777       if ( !$db->check_query($query) )
   777 			if ( !$db->check_query($query) )
   778       {
   778 			{
   779         // aww crap, a query is bad
   779 				// aww crap, a query is bad
   780         $return = array(
   780 				$return = array(
   781           'mode' => 'error',
   781 					'mode' => 'error',
   782           'error' => $lang->get('acppl_err_upgrade_bad_query'),
   782 					'error' => $lang->get('acppl_err_upgrade_bad_query'),
   783         );
   783 				);
   784         break 2;
   784 				break 2;
   785       }
   785 			}
   786     }
   786 		}
   787     
   787 		
   788     // this is it, perform uninstallation
   788 		// this is it, perform uninstallation
   789     foreach ( $sql as $query )
   789 		foreach ( $sql as $query )
   790     {
   790 		{
   791       if ( substr($query, 0, 1) == '@' )
   791 			if ( substr($query, 0, 1) == '@' )
   792       {
   792 			{
   793         $query = substr($query, 1);
   793 				$query = substr($query, 1);
   794         $db->sql_query($query);
   794 				$db->sql_query($query);
   795       }
   795 			}
   796       else
   796 			else
   797       {
   797 			{
   798         if ( !$db->sql_query($query) )
   798 				if ( !$db->sql_query($query) )
   799         {
   799 				{
   800           $return = array(
   800 					$return = array(
   801               'mode' => 'error',
   801 							'mode' => 'error',
   802               'error' => "[SQL] " . $db->sql_error()
   802 							'error' => "[SQL] " . $db->sql_error()
   803             );
   803 						);
   804           break 2;
   804 					break 2;
   805         }
   805 				}
   806       }
   806 			}
   807     }
   807 		}
   808     
   808 		
   809     // log action
   809 		// log action
   810     $time        = time();
   810 		$time        = time();
   811     $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
   811 		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
   812     $username_db = $db->escape($session->username);
   812 		$username_db = $db->escape($session->username);
   813     $file_db     = $db->escape($filename);
   813 		$file_db     = $db->escape($filename);
   814     $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
   814 		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
   815                       . "  ('security', 'plugin_uninstall', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
   815 											. "  ('security', 'plugin_uninstall', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
   816     if ( !$q )
   816 		if ( !$q )
   817       $db->_die();
   817 			$db->_die();
   818     
   818 		
   819     // deregister plugin
   819 		// deregister plugin
   820     $q = $db->sql_query('DELETE FROM ' . table_prefix . "plugins WHERE plugin_id = {$dataset['plugin id']};");
   820 		$q = $db->sql_query('DELETE FROM ' . table_prefix . "plugins WHERE plugin_id = {$dataset['plugin id']};");
   821     if ( !$q )
   821 		if ( !$q )
   822       $db->die_json();
   822 			$db->die_json();
   823     
   823 		
   824     $return = array(
   824 		$return = array(
   825       'success' => true
   825 			'success' => true
   826     );
   826 		);
   827     
   827 		
   828     endswitch;
   828 		endswitch;
   829     
   829 		
   830     $cache->purge('plugins');
   830 		$cache->purge('plugins');
   831     $cache->purge('page_meta');
   831 		$cache->purge('page_meta');
   832     $cache->purge('anon_sidebar');
   832 		$cache->purge('anon_sidebar');
   833     
   833 		
   834     return $return;
   834 		return $return;
   835   }
   835 	}
   836   
   836 	
   837   /**
   837 	/**
   838    * Very intelligently upgrades a plugin to the version specified in the filesystem.
   838  	* Very intelligently upgrades a plugin to the version specified in the filesystem.
   839    * @param string Filename of plugin.
   839  	* @param string Filename of plugin.
   840    * @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   840  	* @param array The list of plugins as output by pluginLoader::get_plugin_list(). If not passed, the function is called, possibly wasting time.
   841    * @return array JSON-formatted but not encoded response
   841  	* @return array JSON-formatted but not encoded response
   842    */
   842  	*/
   843   
   843 	
   844   function upgrade_plugin($filename, $plugin_list = null)
   844 	function upgrade_plugin($filename, $plugin_list = null)
   845   {
   845 	{
   846     global $db, $session, $paths, $template, $plugins; // Common objects
   846 		global $db, $session, $paths, $template, $plugins; // Common objects
   847     global $lang, $cache;
   847 		global $lang, $cache;
   848     
   848 		
   849     if ( !$plugin_list )
   849 		if ( !$plugin_list )
   850       $plugin_list = $this->get_plugin_list();
   850 			$plugin_list = $this->get_plugin_list();
   851     
   851 		
   852     // we're gonna need this
   852 		// we're gonna need this
   853     require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   853 		require_once ( ENANO_ROOT . '/includes/sql_parse.php' );
   854     
   854 		
   855     switch ( true ): case true:
   855 		switch ( true ): case true:
   856     
   856 		
   857     // is the plugin in the directory and already installed?
   857 		// is the plugin in the directory and already installed?
   858     if ( !isset($plugin_list[$filename]) || (
   858 		if ( !isset($plugin_list[$filename]) || (
   859         isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
   859 				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
   860       ))
   860 			))
   861     {
   861 		{
   862       $return = array(
   862 			$return = array(
   863         'mode' => 'error',
   863 				'mode' => 'error',
   864         'error' => 'Invalid plugin specified.',
   864 				'error' => 'Invalid plugin specified.',
   865       );
   865 			);
   866       break;
   866 			break;
   867     }
   867 		}
   868     // get plugin id
   868 		// get plugin id
   869     $dataset =& $plugin_list[$filename];
   869 		$dataset =& $plugin_list[$filename];
   870     if ( empty($dataset['plugin id']) )
   870 		if ( empty($dataset['plugin id']) )
   871     {
   871 		{
   872       $return = array(
   872 			$return = array(
   873         'mode' => 'error',
   873 				'mode' => 'error',
   874         'error' => 'Couldn\'t retrieve plugin ID.',
   874 				'error' => 'Couldn\'t retrieve plugin ID.',
   875       );
   875 			);
   876       break;
   876 			break;
   877     }
   877 		}
   878     
   878 		
   879     //
   879 		//
   880     // Here we go with the main upgrade process. This is the same logic that the
   880 		// Here we go with the main upgrade process. This is the same logic that the
   881     // Enano official upgrader uses, in fact it's the same SQL parser. We need
   881 		// Enano official upgrader uses, in fact it's the same SQL parser. We need
   882     // list of all versions of the plugin to continue, though.
   882 		// list of all versions of the plugin to continue, though.
   883     //
   883 		//
   884     
   884 		
   885     if ( !isset($dataset['version list']) || ( isset($dataset['version list']) && !is_array($dataset['version list']) ) )
   885 		if ( !isset($dataset['version list']) || ( isset($dataset['version list']) && !is_array($dataset['version list']) ) )
   886     {
   886 		{
   887       // no version list - update the version number but leave the rest alone
   887 			// no version list - update the version number but leave the rest alone
   888       $version = $db->escape($dataset['version']);
   888 			$version = $db->escape($dataset['version']);
   889       $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
   889 			$q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
   890       if ( !$q )
   890 			if ( !$q )
   891         $db->die_json();
   891 				$db->die_json();
   892       
   892 			
   893       // send an error and notify the user even though it was technically a success
   893 			// send an error and notify the user even though it was technically a success
   894       $return = array(
   894 			$return = array(
   895         'mode' => 'error',
   895 				'mode' => 'error',
   896         'error' => $lang->get('acppl_err_upgrade_not_supported'),
   896 				'error' => $lang->get('acppl_err_upgrade_not_supported'),
   897       );
   897 			);
   898       break;
   898 			break;
   899     }
   899 		}
   900     
   900 		
   901     // build target list
   901 		// build target list
   902     $versions  = $dataset['version list'];
   902 		$versions  = $dataset['version list'];
   903     $indices   = array_flip($versions);
   903 		$indices   = array_flip($versions);
   904     $installed = $dataset['version installed'];
   904 		$installed = $dataset['version installed'];
   905     
   905 		
   906     // is the current version upgradeable?
   906 		// is the current version upgradeable?
   907     if ( !isset($indices[$installed]) )
   907 		if ( !isset($indices[$installed]) )
   908     {
   908 		{
   909       $return = array(
   909 			$return = array(
   910         'mode' => 'error',
   910 				'mode' => 'error',
   911         'error' => $lang->get('acppl_err_upgrade_bad_version'),
   911 				'error' => $lang->get('acppl_err_upgrade_bad_version'),
   912       );
   912 			);
   913       break;
   913 			break;
   914     }
   914 		}
   915     
   915 		
   916     // does the plugin support upgrading to its own version?
   916 		// does the plugin support upgrading to its own version?
   917     if ( !isset($indices[$installed]) )
   917 		if ( !isset($indices[$installed]) )
   918     {
   918 		{
   919       $return = array(
   919 			$return = array(
   920         'mode' => 'error',
   920 				'mode' => 'error',
   921         'error' => $lang->get('acppl_err_upgrade_bad_target_version'),
   921 				'error' => $lang->get('acppl_err_upgrade_bad_target_version'),
   922       );
   922 			);
   923       break;
   923 			break;
   924     }
   924 		}
   925     
   925 		
   926     // list out which versions to do
   926 		// list out which versions to do
   927     $index_start = @$indices[$installed];
   927 		$index_start = @$indices[$installed];
   928     $index_stop  = @$indices[$dataset['version']];
   928 		$index_stop  = @$indices[$dataset['version']];
   929     
   929 		
   930     // Are we trying to go backwards?
   930 		// Are we trying to go backwards?
   931     if ( $index_stop <= $index_start )
   931 		if ( $index_stop <= $index_start )
   932     {
   932 		{
   933       $return = array(
   933 			$return = array(
   934         'mode' => 'error',
   934 				'mode' => 'error',
   935         'error' => $lang->get('acppl_err_upgrade_to_older'),
   935 				'error' => $lang->get('acppl_err_upgrade_to_older'),
   936         // 'debug' => "going from $installed ($index_start) to {$dataset['version']} ($index_stop)"
   936 				// 'debug' => "going from $installed ($index_start) to {$dataset['version']} ($index_stop)"
   937       );
   937 			);
   938       break;
   938 			break;
   939     }
   939 		}
   940     
   940 		
   941     // build the list of version sets
   941 		// build the list of version sets
   942     $ver_previous = $installed;
   942 		$ver_previous = $installed;
   943     $targets = array();
   943 		$targets = array();
   944     for ( $i = $index_start; $i <= $index_stop; $i++ )
   944 		for ( $i = $index_start; $i <= $index_stop; $i++ )
   945     {
   945 		{
   946       $targets[] = array($ver_previous, $versions[$i]);
   946 			$targets[] = array($ver_previous, $versions[$i]);
   947       $ver_previous = $versions[$i];
   947 			$ver_previous = $versions[$i];
   948     }
   948 		}
   949     
   949 		
   950     // parse out upgrade sections in plugin file
   950 		// parse out upgrade sections in plugin file
   951     $plugin_blocks = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'upgrade' );
   951 		$plugin_blocks = $this->parse_plugin_blocks( ENANO_ROOT . '/plugins/' . $filename, 'upgrade' );
   952     $sql_blocks = array();
   952 		$sql_blocks = array();
   953     foreach ( $plugin_blocks as $block )
   953 		foreach ( $plugin_blocks as $block )
   954     {
   954 		{
   955       if ( !isset($block['from']) || !isset($block['to']) )
   955 			if ( !isset($block['from']) || !isset($block['to']) )
   956       {
   956 			{
   957         continue;
   957 				continue;
   958       }
   958 			}
   959       $key = "{$block['from']} TO {$block['to']}";
   959 			$key = "{$block['from']} TO {$block['to']}";
   960       $sql_blocks[$key] = $block['value'];
   960 			$sql_blocks[$key] = $block['value'];
   961     }
   961 		}
   962     
   962 		
   963     // do version list check
   963 		// do version list check
   964     // for now we won't fret if a specific version set isn't found, we'll just
   964 		// for now we won't fret if a specific version set isn't found, we'll just
   965     // not do that version and assume there were no DB changes.
   965 		// not do that version and assume there were no DB changes.
   966     foreach ( $targets as $i => $target )
   966 		foreach ( $targets as $i => $target )
   967     {
   967 		{
   968       list($from, $to) = $target;
   968 			list($from, $to) = $target;
   969       $key = "$from TO $to";
   969 			$key = "$from TO $to";
   970       if ( !isset($sql_blocks[$key]) )
   970 			if ( !isset($sql_blocks[$key]) )
   971       {
   971 			{
   972         unset($targets[$i]);
   972 				unset($targets[$i]);
   973       }
   973 			}
   974     }
   974 		}
   975     $targets = array_values($targets);
   975 		$targets = array_values($targets);
   976     
   976 		
   977     // parse and finalize schema
   977 		// parse and finalize schema
   978     $schema = array();
   978 		$schema = array();
   979     foreach ( $targets as $i => $target )
   979 		foreach ( $targets as $i => $target )
   980     {
   980 		{
   981       list($from, $to) = $target;
   981 			list($from, $to) = $target;
   982       $key = "$from TO $to";
   982 			$key = "$from TO $to";
   983       try
   983 			try
   984       {
   984 			{
   985         $parser = new SQL_Parser($sql_blocks[$key], true);
   985 				$parser = new SQL_Parser($sql_blocks[$key], true);
   986       }
   986 			}
   987       catch ( Exception $e )
   987 			catch ( Exception $e )
   988       {
   988 			{
   989         $return = array(
   989 				$return = array(
   990           'mode' => 'error',
   990 					'mode' => 'error',
   991           'error' => 'SQL parser init exception',
   991 					'error' => 'SQL parser init exception',
   992           'debug' => "$e"
   992 					'debug' => "$e"
   993         );
   993 				);
   994         break 2;
   994 				break 2;
   995       }
   995 			}
   996       $parser->assign_vars(array(
   996 			$parser->assign_vars(array(
   997         'TABLE_PREFIX' => table_prefix
   997 				'TABLE_PREFIX' => table_prefix
   998         ));
   998 				));
   999       $parsed = $parser->parse();
   999 			$parsed = $parser->parse();
  1000       foreach ( $parsed as $query )
  1000 			foreach ( $parsed as $query )
  1001       {
  1001 			{
  1002         $schema[] = $query;
  1002 				$schema[] = $query;
  1003       }
  1003 			}
  1004     }
  1004 		}
  1005     
  1005 		
  1006     // schema is final, check queries
  1006 		// schema is final, check queries
  1007     foreach ( $schema as $query )
  1007 		foreach ( $schema as $query )
  1008     {
  1008 		{
  1009       if ( !$db->check_query($query) )
  1009 			if ( !$db->check_query($query) )
  1010       {
  1010 			{
  1011         // aww crap, a query is bad
  1011 				// aww crap, a query is bad
  1012         $return = array(
  1012 				$return = array(
  1013           'mode' => 'error',
  1013 					'mode' => 'error',
  1014           'error' => $lang->get('acppl_err_upgrade_bad_query'),
  1014 					'error' => $lang->get('acppl_err_upgrade_bad_query'),
  1015         );
  1015 				);
  1016         break 2;
  1016 				break 2;
  1017       }
  1017 			}
  1018     }
  1018 		}
  1019     
  1019 		
  1020     // this is it, perform upgrade
  1020 		// this is it, perform upgrade
  1021     foreach ( $schema as $query )
  1021 		foreach ( $schema as $query )
  1022     {
  1022 		{
  1023       if ( substr($query, 0, 1) == '@' )
  1023 			if ( substr($query, 0, 1) == '@' )
  1024       {
  1024 			{
  1025         $query = substr($query, 1);
  1025 				$query = substr($query, 1);
  1026         $db->sql_query($query);
  1026 				$db->sql_query($query);
  1027       }
  1027 			}
  1028       else
  1028 			else
  1029       {
  1029 			{
  1030         if ( !$db->sql_query($query) )
  1030 				if ( !$db->sql_query($query) )
  1031         {
  1031 				{
  1032           $return = array(
  1032 					$return = array(
  1033               'mode' => 'error',
  1033 							'mode' => 'error',
  1034               'error' => "[SQL] " . $db->sql_error()
  1034 							'error' => "[SQL] " . $db->sql_error()
  1035             );
  1035 						);
  1036           break 2;
  1036 					break 2;
  1037         }
  1037 				}
  1038       }
  1038 			}
  1039     }
  1039 		}
  1040     
  1040 		
  1041     // log action
  1041 		// log action
  1042     $time        = time();
  1042 		$time        = time();
  1043     $ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
  1043 		$ip_db       = $db->escape($_SERVER['REMOTE_ADDR']);
  1044     $username_db = $db->escape($session->username);
  1044 		$username_db = $db->escape($session->username);
  1045     $file_db     = $db->escape($filename);
  1045 		$file_db     = $db->escape($filename);
  1046     $q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
  1046 		$q = $db->sql_query('INSERT INTO '.table_prefix."logs(log_type, action, time_id, edit_summary, author, author_uid, page_text) VALUES\n"
  1047                       . "  ('security', 'plugin_upgrade', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
  1047 											. "  ('security', 'plugin_upgrade', $time, '$ip_db', '$username_db', $session->user_id, '$file_db');");
  1048     if ( !$q )
  1048 		if ( !$q )
  1049       $db->_die();
  1049 			$db->_die();
  1050     
  1050 		
  1051     // update version number
  1051 		// update version number
  1052     $version = $db->escape($dataset['version']);
  1052 		$version = $db->escape($dataset['version']);
  1053     $q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
  1053 		$q = $db->sql_query('UPDATE ' . table_prefix . "plugins SET plugin_version = '$version' WHERE plugin_id = {$dataset['plugin id']};");
  1054     if ( !$q )
  1054 		if ( !$q )
  1055       $db->die_json();
  1055 			$db->die_json();
  1056     
  1056 		
  1057     // all done :-)
  1057 		// all done :-)
  1058     $return = array(
  1058 		$return = array(
  1059       'success' => true
  1059 			'success' => true
  1060     );
  1060 		);
  1061     
  1061 		
  1062     endswitch;
  1062 		endswitch;
  1063     
  1063 		
  1064     $cache->purge('plugins');
  1064 		$cache->purge('plugins');
  1065     $cache->purge('page_meta');
  1065 		$cache->purge('page_meta');
  1066     $cache->purge('anon_sidebar');
  1066 		$cache->purge('anon_sidebar');
  1067     
  1067 		
  1068     return $return;
  1068 		return $return;
  1069   }
  1069 	}
  1070   
  1070 	
  1071   /**
  1071 	/**
  1072    * Re-imports the language strings from a plugin.
  1072  	* Re-imports the language strings from a plugin.
  1073    * @param string File name
  1073  	* @param string File name
  1074    * @return array Enano JSON response protocol
  1074  	* @return array Enano JSON response protocol
  1075    */
  1075  	*/
  1076   
  1076 	
  1077   function reimport_plugin_strings($filename, $plugin_list = null)
  1077 	function reimport_plugin_strings($filename, $plugin_list = null)
  1078   {
  1078 	{
  1079     global $db, $session, $paths, $template, $plugins; // Common objects
  1079 		global $db, $session, $paths, $template, $plugins; // Common objects
  1080     global $lang;
  1080 		global $lang;
  1081     
  1081 		
  1082     if ( !$plugin_list )
  1082 		if ( !$plugin_list )
  1083       $plugin_list = $this->get_plugin_list();
  1083 			$plugin_list = $this->get_plugin_list();
  1084     
  1084 		
  1085     switch ( true ): case true:
  1085 		switch ( true ): case true:
  1086     
  1086 		
  1087     // is the plugin in the directory and already installed?
  1087 		// is the plugin in the directory and already installed?
  1088     if ( !isset($plugin_list[$filename]) || (
  1088 		if ( !isset($plugin_list[$filename]) || (
  1089         isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
  1089 				isset($plugin_list[$filename]) && !$plugin_list[$filename]['installed']
  1090       ))
  1090 			))
  1091     {
  1091 		{
  1092       $return = array(
  1092 			$return = array(
  1093         'mode' => 'error',
  1093 				'mode' => 'error',
  1094         'error' => 'Invalid plugin specified.',
  1094 				'error' => 'Invalid plugin specified.',
  1095       );
  1095 			);
  1096       break;
  1096 			break;
  1097     }
  1097 		}
  1098     // get plugin data
  1098 		// get plugin data
  1099     $dataset =& $plugin_list[$filename];
  1099 		$dataset =& $plugin_list[$filename];
  1100     
  1100 		
  1101     // check for a language block
  1101 		// check for a language block
  1102     $blocks = self::parse_plugin_blocks(ENANO_ROOT . '/plugins/' . $filename, 'language');
  1102 		$blocks = self::parse_plugin_blocks(ENANO_ROOT . '/plugins/' . $filename, 'language');
  1103     if ( count($blocks) < 1 )
  1103 		if ( count($blocks) < 1 )
  1104     {
  1104 		{
  1105       return array(
  1105 			return array(
  1106           'mode' => 'error',
  1106 					'mode' => 'error',
  1107           'error' => $lang->get('acppl_err_import_no_strings')
  1107 					'error' => $lang->get('acppl_err_import_no_strings')
  1108         );
  1108 				);
  1109     }
  1109 		}
  1110     
  1110 		
  1111     $result = $lang->import_plugin(ENANO_ROOT . '/plugins/' . $filename);
  1111 		$result = $lang->import_plugin(ENANO_ROOT . '/plugins/' . $filename);
  1112     if ( $result )
  1112 		if ( $result )
  1113     {
  1113 		{
  1114       return array(
  1114 			return array(
  1115         'success' => true
  1115 				'success' => true
  1116       );
  1116 			);
  1117     }
  1117 		}
  1118     else
  1118 		else
  1119     {
  1119 		{
  1120       return array(
  1120 			return array(
  1121         'mode' => 'error',
  1121 				'mode' => 'error',
  1122         'error' => 'Language API returned error'
  1122 				'error' => 'Language API returned error'
  1123       );
  1123 			);
  1124     }
  1124 		}
  1125     
  1125 		
  1126     endswitch;
  1126 		endswitch;
  1127   }
  1127 	}
  1128 }
  1128 }
  1129 
  1129 
  1130 ?>
  1130 ?>