PageRenderTime 103ms CodeModel.GetById 49ms app.highlight 42ms RepoModel.GetById 1ms app.codeStats 0ms

/umil/umil.php

https://code.google.com/p/phpbbex/
PHP | 2353 lines | 1689 code | 259 blank | 405 comment | 180 complexity | 67f8ba241deb2a5c366b716fcc83f0fd MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 *
   4 * @author Nathan Guse (EXreaction) http://lithiumstudios.org
   5 * @author David Lewis (Highway of Life) highwayoflife@gmail.com
   6 * @package umil
   7 * @version $Id$
   8 * @copyright (c) 2008 phpBB Group
   9 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
  10 *
  11 */
  12
  13/**
  14 * @ignore
  15 */
  16if (!defined('IN_PHPBB'))
  17{
  18	exit;
  19}
  20
  21define('UMIL_VERSION', '1.0.4');
  22
  23/**
  24* Multicall instructions
  25*
  26* With the "multicall" (as I am calling it) you can make a single function call and have it repeat the actions multiple times on information sent from an array.
  27*
  28* To do this (it does not work on the _exists functions), all you must do is send the first variable in the function call as an array and for each item, send an array for each of the variables in order.
  29*
  30* Example:
  31* $umil->config_add(array(
  32*	array('config_name', 'config_value'),
  33*	array('config_name1', 'config_value1'),
  34*	array('config_name2', 'config_value2', true),
  35*	array('config_name3', 'config_value3', true),
  36* );
  37*/
  38
  39/**
  40* UMIL - Unified MOD Installation Library class
  41*
  42* Cache Functions
  43*	cache_purge($type = '', $style_id = 0)
  44*
  45* Config Functions:
  46*	config_exists($config_name, $return_result = false)
  47*	config_add($config_name, $config_value = '', $is_dynamic = false)
  48*	config_update($config_name, $config_value, $is_dynamic = false)
  49*	config_remove($config_name)
  50*
  51* Module Functions
  52*	module_exists($class, $parent, $module)
  53*	module_add($class, $parent = 0, $data = array())
  54*	module_remove($class, $parent = 0, $module = '')
  55*
  56* Permissions/Auth Functions
  57*	permission_exists($auth_option, $global = true)
  58*	permission_add($auth_option, $global = true)
  59*	permission_remove($auth_option, $global = true)
  60*	permission_set($name, $auth_option = array(), $type = 'role', $global = true, $has_permission = true)
  61*	permission_unset($name, $auth_option = array(), $type = 'role', $global = true)
  62*
  63* Table Functions
  64*	table_exists($table_name)
  65*	table_add($table_name, $table_data = array())
  66*	table_remove($table_name)
  67*
  68* Table Column Functions
  69*	table_column_exists($table_name, $column_name)
  70*	table_column_add($table_name, $column_name = '', $column_data = array())
  71*	table_column_update($table_name, $column_name = '', $column_data = array())
  72*	table_column_remove($table_name, $column_name = '')
  73*
  74* Table Key/Index Functions
  75*	table_index_exists($table_name, $index_name)
  76*	table_index_add($table_name, $index_name = '', $column = array())
  77*	table_index_remove($table_name, $index_name = '')
  78*
  79* Table Row Functions (note that these actions are not reversed automatically during uninstallation)
  80*	table_row_insert($table_name, $data = array())
  81*	table_row_remove($table_name, $data = array())
  82*	table_row_update($table_name, $data = array(), $new_data = array())
  83*
  84* Version Check Function
  85* 	version_check($url, $path, $file)
  86*/
  87class umil
  88{
  89	/**
  90	* This will hold the text output for the inputted command (if the mod author would like to display the command that was ran)
  91	*
  92	* @var string
  93	*/
  94	var $command = '';
  95
  96	/**
  97	* This will hold the text output for the result of the command.  $user->lang['SUCCESS'] if everything worked.
  98	*
  99	* @var string
 100	*/
 101	var $result = '';
 102
 103	/**
 104	* Auto run $this->display_results after running a command
 105	*/
 106	var $auto_display_results = false;
 107
 108	/**
 109	* Stand Alone option (this makes it possible to just use the single umil file and not worry about any language stuff
 110	*/
 111	var $stand_alone = false;
 112
 113	/**
 114	* Were any new permissions added (used in umil_frontend)?
 115	*/
 116	var $permissions_added = false;
 117
 118	/**
 119	* Database Object
 120	*/
 121	var $db = false;
 122
 123	/**
 124	* Database Tools Object
 125	*/
 126	var $db_tools = false;
 127
 128	/**
 129	* Do we want a custom prefix besides the phpBB table prefix?  You *probably* should not change this...
 130	*/
 131	var $table_prefix = false;
 132
 133	/**
 134	* Constructor
 135	*/
 136	function umil($stand_alone = false, $db = false)
 137	{
 138		// Setup $this->db
 139		if ($db !== false)
 140		{
 141			if (!is_object($db) || !method_exists($db, 'sql_query'))
 142			{
 143				trigger_error('Invalid $db Object');
 144			}
 145
 146			$this->db = $db;
 147		}
 148		else
 149		{
 150			global $db;
 151			$this->db = $db;
 152		}
 153
 154		// Setup $this->db_tools
 155		if (!class_exists('phpbb_db_tools'))
 156		{
 157			global $phpbb_root_path, $phpEx;
 158			include($phpbb_root_path . 'includes/db/db_tools.' . $phpEx);
 159		}
 160		$this->db_tools = new phpbb_db_tools($this->db);
 161
 162		$this->stand_alone = $stand_alone;
 163
 164		if (!$stand_alone)
 165		{
 166			global $config, $user, $phpbb_root_path, $phpEx;
 167
 168			/* Does not have the fall back option to use en/ if the user's language file does not exist, so we will not use it...unless that is changed.
 169			if (method_exists('user', 'set_custom_lang_path'))
 170			{
 171				$user->set_custom_lang_path($phpbb_root_path . 'umil/language/');
 172				$user->add_lang('umil');
 173				$user->set_custom_lang_path($phpbb_root_path . 'language/');
 174			}
 175			else
 176			{*/
 177				// Include the umil language file.  First we check if the language file for the user's language is available, if not we check if the board's default language is available, if not we use the english file.
 178				if (isset($user->data['user_lang']) && file_exists("{$phpbb_root_path}umil/language/{$user->data['user_lang']}/umil.$phpEx"))
 179				{
 180					$path = $user->data['user_lang'];
 181				}
 182				else if (file_exists("{$phpbb_root_path}umil/language/" . basename($config['default_lang']) . "/umil.$phpEx"))
 183				{
 184					$path = basename($config['default_lang']);
 185				}
 186				else if (file_exists("{$phpbb_root_path}umil/language/en/umil.$phpEx"))
 187				{
 188					$path = 'en';
 189				}
 190				else
 191				{
 192					trigger_error('Language Files Missing.<br /><br />Please download the latest UMIL (Unified MOD Install Library) from: <a href="http://www.phpbb.com/mods/umil/">phpBB.com/mods/umil</a>', E_USER_ERROR);
 193				}
 194
 195				$user->add_lang('./../../umil/language/' . $path . '/umil');
 196			//}
 197
 198			$user->add_lang(array('acp/common', 'acp/permissions'));
 199
 200			// Check to see if a newer version is available.
 201			$info = $this->version_check('version.phpbb.com', '/umil', ((defined('PHPBB_QA')) ? 'umil_qa.txt' : 'umil.txt'));
 202			if (is_array($info) && isset($info[0]) && isset($info[1]))
 203			{
 204				if (version_compare(UMIL_VERSION, $info[0], '<'))
 205				{
 206					global $template;
 207
 208					// Make sure user->setup() has been called
 209					if (empty($user->lang))
 210					{
 211						$user->setup();
 212					}
 213
 214					page_header('', false);
 215
 216					$user->lang['UPDATE_UMIL'] = (isset($user->lang['UPDATE_UMIL'])) ? $user->lang['UPDATE_UMIL'] : 'This version of UMIL is outdated.<br /><br />Please download the latest UMIL (Unified MOD Install Library) from: <a href="%1$s">%1$s</a>';
 217					$template->assign_vars(array(
 218						'S_BOARD_DISABLED'		=> true,
 219						'L_BOARD_DISABLED'		=> sprintf($user->lang['UPDATE_UMIL'], $info[1]),
 220					));
 221				}
 222			}
 223		}
 224	}
 225
 226	/**
 227	* umil_start
 228	*
 229	* A function which runs (almost) every time a function here is ran
 230	*/
 231	function umil_start()
 232	{
 233		global $user;
 234
 235		// Set up the command.  This will get the arguments sent to the function.
 236		$args = func_get_args();
 237		$this->command = call_user_func_array(array($this, 'get_output_text'), $args);
 238
 239		$this->result = (isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS';
 240		$this->db->sql_return_on_error(true);
 241
 242		//$this->db->sql_transaction('begin');
 243	}
 244
 245	/**
 246	* umil_end
 247	*
 248	* A function which runs (almost) every time a function here is ran
 249	*/
 250	function umil_end()
 251	{
 252		global $user;
 253
 254		// Set up the result.  This will get the arguments sent to the function.
 255		$args = func_get_args();
 256		$result = call_user_func_array(array($this, 'get_output_text'), $args);
 257		$this->result = ($result) ? $result : $this->result;
 258
 259		if ($this->db->sql_error_triggered)
 260		{
 261			if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
 262			{
 263				$this->result = 'SQL ERROR ' . $this->db->sql_error_returned['message'];
 264			}
 265			else
 266			{
 267				$this->result .= '<br /><br />SQL ERROR ' . $this->db->sql_error_returned['message'];
 268			}
 269
 270			//$this->db->sql_transaction('rollback');
 271		}
 272		else
 273		{
 274			//$this->db->sql_transaction('commit');
 275		}
 276
 277		$this->db->sql_return_on_error(false);
 278
 279		// Auto output if requested.
 280		if ($this->auto_display_results && method_exists($this, 'display_results'))
 281		{
 282			$this->display_results();
 283		}
 284
 285		return '<strong>' . $this->command . '</strong><br />' . $this->result;
 286	}
 287
 288	/**
 289	* Get text for output
 290	*
 291	* Takes the given arguments and prepares them for the UI
 292	*
 293	* First argument sent is used as the language key
 294	* Further arguments (if send) are used on the language key through vsprintf()
 295	*
 296	* @return string Returns the prepared string for output
 297	*/
 298	function get_output_text()
 299	{
 300		global $user;
 301
 302		// Set up the command.  This will get the arguments sent to the function.
 303		$args = func_get_args();
 304		if (sizeof($args))
 305		{
 306			$lang_key = array_shift($args);
 307
 308			if (sizeof($args))
 309			{
 310				$lang_args = array();
 311				foreach ($args as $arg)
 312				{
 313					$lang_args[] = (isset($user->lang[$arg])) ? $user->lang[$arg] : $arg;
 314				}
 315
 316				return @vsprintf(((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key), $lang_args);
 317			}
 318			else
 319			{
 320				return ((isset($user->lang[$lang_key])) ? $user->lang[$lang_key] : $lang_key);
 321			}
 322		}
 323
 324		return '';
 325	}
 326
 327	/**
 328	* Run Actions
 329	*
 330	* Do-It-All function that can do everything required for installing/updating/uninstalling a mod based on an array of actions and the versions.
 331	*
 332	* @param string $action The action. install|update|uninstall
 333	* @param array $versions The array of versions and the actions for each
 334	* @param string $version_config_name The name of the config setting which holds/will hold the currently installed version
 335	* @param string $version_select Added for the UMIL Auto system to allow you to select the version you want to install/update/uninstall to.
 336	*/
 337	function run_actions($action, $versions, $version_config_name, $version_select = '')
 338	{
 339		// We will sort the actions to prevent issues from mod authors incorrectly listing the version numbers
 340		uksort($versions, 'version_compare');
 341
 342		// Find the current version to install
 343		$current_version = '0.0.0';
 344		foreach ($versions as $version => $actions)
 345		{
 346			$current_version = $version;
 347		}
 348
 349		$db_version = '';
 350		if ($this->config_exists($version_config_name))
 351		{
 352			global $config;
 353			$db_version = $config[$version_config_name];
 354		}
 355
 356		// Set the action to install from update if nothing is currently installed
 357		if ($action == 'update' && !$db_version)
 358		{
 359			$action = 'install';
 360		}
 361
 362		if ($action == 'install' || $action == 'update')
 363		{
 364			$version_installed = $db_version;
 365			foreach ($versions as $version => $version_actions)
 366			{
 367				// If we are updating
 368				if ($db_version && version_compare($version, $db_version, '<='))
 369				{
 370					continue;
 371				}
 372
 373				if ($version_select && version_compare($version, $version_select, '>'))
 374				{
 375					break;
 376				}
 377
 378				foreach ($version_actions as $method => $params)
 379				{
 380					if ($method == 'custom')
 381					{
 382						$this->_call_custom_function($params, $action, $version);
 383					}
 384					else
 385					{
 386						if (method_exists($this, $method))
 387						{
 388							call_user_func(array($this, $method), $params);
 389						}
 390					}
 391				}
 392
 393				$version_installed = $version;
 394			}
 395
 396			// update the version number or add it
 397			if ($this->config_exists($version_config_name))
 398			{
 399				$this->config_update($version_config_name, $version_installed);
 400			}
 401			else
 402			{
 403				$this->config_add($version_config_name, $version_installed);
 404			}
 405		}
 406		else if ($action == 'uninstall' && $db_version)
 407		{
 408			// reverse version list
 409			$versions = array_reverse($versions);
 410
 411			foreach ($versions as $version => $version_actions)
 412			{
 413				// Uninstalling and this listed version is newer than installed
 414				if (version_compare($version, $db_version, '>'))
 415				{
 416					continue;
 417				}
 418
 419				// Version selection stuff
 420				if ($version_select && version_compare($version, $version_select, '<='))
 421				{
 422					// update the version number
 423					$this->config_update($version_config_name, $version);
 424					break;
 425				}
 426
 427				$cache_purge = false;
 428				$version_actions = array_reverse($version_actions);
 429				foreach ($version_actions as $method => $params)
 430				{
 431					if ($method == 'custom')
 432					{
 433						$this->_call_custom_function($params, $action, $version);
 434					}
 435					else
 436					{
 437						// This way we always run the cache purge at the end of the version (done for the uninstall because the instructions are reversed, which would cause the cache purge to be run at the beginning if it was meant to run at the end).
 438						if ($method == 'cache_purge')
 439						{
 440							$cache_purge = $params;
 441							continue;
 442						}
 443
 444						// A few things are not possible for uninstallations update actions and table_row actions
 445						if (strpos($method, 'update') !== false || strpos($method, 'table_insert') !== false || strpos($method, 'table_row_') !== false)
 446						{
 447							continue;
 448						}
 449
 450						// reverse function call
 451						$method = str_replace(array('add', 'remove', 'temp'), array('temp', 'add', 'remove'), $method);
 452						$method = str_replace(array('set', 'unset', 'temp'), array('temp', 'set', 'unset'), $method);
 453
 454						if (method_exists($this, $method))
 455						{
 456							call_user_func(array($this, $method), ((is_array($params) ? array_reverse($params) : $params)));
 457						}
 458					}
 459				}
 460
 461				if ($cache_purge !== false)
 462				{
 463					$this->cache_purge($cache_purge);
 464				}
 465			}
 466
 467			if (!$version_select)
 468			{
 469				// Unset the version number
 470				$this->config_remove($version_config_name);
 471			}
 472		}
 473	}
 474
 475	/**
 476	* Call custom function helper
 477	*/
 478	function _call_custom_function($functions, $action, $version)
 479	{
 480		if (!is_array($functions))
 481		{
 482			$functions = array($functions);
 483		}
 484
 485		$return = '';
 486
 487		foreach ($functions as $function)
 488		{
 489			if (function_exists($function))
 490			{
 491				// Must reset before calling the function
 492				$this->umil_start();
 493
 494				$returned = call_user_func($function, $action, $version);
 495				if (is_string($returned))
 496				{
 497					$this->command = $this->get_output_text($returned);
 498				}
 499				else if (is_array($returned) && isset($returned['command']))
 500				{
 501					if (is_array($returned['command']))
 502					{
 503						$this->command = call_user_func_array(array($this, 'get_output_text'), $returned['command']);
 504					}
 505					else
 506					{
 507						$this->command = $this->get_output_text($returned['command']);
 508					}
 509
 510					if (isset($returned['result']))
 511					{
 512						$this->result = $this->get_output_text($returned['result']);
 513					}
 514				}
 515				else
 516				{
 517					$this->command = $this->get_output_text('UNKNOWN');
 518				}
 519
 520				$return .= $this->umil_end() . '<br />';
 521			}
 522		}
 523
 524		return $return;
 525	}
 526
 527	/**
 528	* Multicall Helper
 529	*
 530	* @param mixed $function Function name to call
 531	* @param mixed $params The parameters array
 532	*
 533	* @return bool True if we have done a multicall ($params is an array), false if not ($params is not an array)
 534	*/
 535	function multicall($function, $params)
 536	{
 537		if (is_array($params) && !empty($params))
 538		{
 539			foreach ($params as $param)
 540			{
 541				if (!is_array($param))
 542				{
 543					call_user_func(array($this, $function), $param);
 544				}
 545				else
 546				{
 547					call_user_func_array(array($this, $function), $param);
 548				}
 549			}
 550			return true;
 551		}
 552
 553		return false;
 554	}
 555
 556	/**
 557	* Cache Purge
 558	*
 559	* This function is for purging either phpBB3â&#x20AC;&#x2122;s data cache, authorization cache, or the styles cache.
 560	*
 561	* @param string $type The type of cache you want purged.  Available types: auth, imageset, template, theme.  Anything else sent will purge the forum's cache.
 562	* @param int $style_id The id of the item you want purged (if the type selected is imageset/template/theme, 0 for all items in that section)
 563	*/
 564	function cache_purge($type = '', $style_id = 0)
 565	{
 566		global $auth, $cache, $user, $phpbb_root_path, $phpEx;
 567
 568		// Multicall
 569		if ($this->multicall(__FUNCTION__, $type))
 570		{
 571			return;
 572		}
 573
 574		$style_id = (int) $style_id;
 575		$type = (string) $type; // Prevent PHP bug.
 576
 577		switch ($type)
 578		{
 579			case 'auth' :
 580				$this->umil_start('AUTH_CACHE_PURGE');
 581				$cache->destroy('_acl_options');
 582				$auth->acl_clear_prefetch();
 583
 584				return $this->umil_end();
 585			break;
 586
 587			case 'imageset' :
 588				if ($style_id == 0)
 589				{
 590					$return = array();
 591					$sql = 'SELECT imageset_id
 592						FROM ' . STYLES_IMAGESET_TABLE;
 593					$result = $this->db->sql_query($sql);
 594					while ($row = $this->db->sql_fetchrow($result))
 595					{
 596						$return[] = $this->cache_purge('imageset', $row['imageset_id']);
 597					}
 598					$this->db->sql_freeresult($result);
 599
 600					return implode('<br /><br />', $return);
 601				}
 602				else
 603				{
 604					$sql = 'SELECT *
 605						FROM ' . STYLES_IMAGESET_TABLE . "
 606						WHERE imageset_id = $style_id";
 607					$result = $this->db->sql_query($sql);
 608					$imageset_row = $this->db->sql_fetchrow($result);
 609					$this->db->sql_freeresult($result);
 610
 611					if (!$imageset_row)
 612					{
 613						$this->umil_start('IMAGESET_CACHE_PURGE', 'UNKNOWN');
 614						return $this->umil_end('FAIL');
 615					}
 616
 617					$this->umil_start('IMAGESET_CACHE_PURGE', $imageset_row['imageset_name']);
 618
 619					// The following is from includes/acp/acp_styles.php (edited)
 620					$sql_ary = array();
 621
 622					$cfg_data_imageset = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/imageset.cfg");
 623
 624					$sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
 625						WHERE imageset_id = ' . $style_id;
 626					$result = $this->db->sql_query($sql);
 627
 628					foreach ($cfg_data_imageset as $image_name => $value)
 629					{
 630						if (strpos($value, '*') !== false)
 631						{
 632							if (substr($value, -1, 1) === '*')
 633							{
 634								list($image_filename, $image_height) = explode('*', $value);
 635								$image_width = 0;
 636							}
 637							else
 638							{
 639								list($image_filename, $image_height, $image_width) = explode('*', $value);
 640							}
 641						}
 642						else
 643						{
 644							$image_filename = $value;
 645							$image_height = $image_width = 0;
 646						}
 647
 648						if (strpos($image_name, 'img_') === 0 && $image_filename)
 649						{
 650							$image_name = substr($image_name, 4);
 651
 652							$sql_ary[] = array(
 653								'image_name'		=> (string) $image_name,
 654								'image_filename'	=> (string) $image_filename,
 655								'image_height'		=> (int) $image_height,
 656								'image_width'		=> (int) $image_width,
 657								'imageset_id'		=> (int) $style_id,
 658								'image_lang'		=> '',
 659							);
 660						}
 661					}
 662
 663					$sql = 'SELECT lang_dir
 664						FROM ' . LANG_TABLE;
 665					$result = $this->db->sql_query($sql);
 666
 667					while ($row = $this->db->sql_fetchrow($result))
 668					{
 669						if (@file_exists("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg"))
 670						{
 671							$cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$imageset_row['imageset_path']}/imageset/{$row['lang_dir']}/imageset.cfg");
 672							foreach ($cfg_data_imageset_data as $image_name => $value)
 673							{
 674								if (strpos($value, '*') !== false)
 675								{
 676									if (substr($value, -1, 1) === '*')
 677									{
 678										list($image_filename, $image_height) = explode('*', $value);
 679										$image_width = 0;
 680									}
 681									else
 682									{
 683										list($image_filename, $image_height, $image_width) = explode('*', $value);
 684									}
 685								}
 686								else
 687								{
 688									$image_filename = $value;
 689									$image_height = $image_width = 0;
 690								}
 691
 692								if (strpos($image_name, 'img_') === 0 && $image_filename)
 693								{
 694									$image_name = substr($image_name, 4);
 695									$sql_ary[] = array(
 696										'image_name'		=> (string) $image_name,
 697										'image_filename'	=> (string) $image_filename,
 698										'image_height'		=> (int) $image_height,
 699										'image_width'		=> (int) $image_width,
 700										'imageset_id'		=> (int) $style_id,
 701										'image_lang'		=> (string) $row['lang_dir'],
 702									);
 703								}
 704							}
 705						}
 706					}
 707					$this->db->sql_freeresult($result);
 708
 709					$this->db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
 710
 711					$cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
 712
 713					return $this->umil_end();
 714				}
 715			break;
 716			//case 'imageset' :
 717
 718			case 'template' :
 719				if ($style_id == 0)
 720				{
 721					$return = array();
 722					$sql = 'SELECT template_id
 723						FROM ' . STYLES_TEMPLATE_TABLE;
 724					$result = $this->db->sql_query($sql);
 725					while ($row = $this->db->sql_fetchrow($result))
 726					{
 727						$return[] = $this->cache_purge('template', $row['template_id']);
 728					}
 729					$this->db->sql_freeresult($result);
 730
 731					return implode('<br /><br />', $return);
 732				}
 733				else
 734				{
 735					$sql = 'SELECT *
 736						FROM ' . STYLES_TEMPLATE_TABLE . "
 737						WHERE template_id = $style_id";
 738					$result = $this->db->sql_query($sql);
 739					$template_row = $this->db->sql_fetchrow($result);
 740					$this->db->sql_freeresult($result);
 741
 742					if (!$template_row)
 743					{
 744						$this->umil_start('TEMPLATE_CACHE_PURGE', 'UNKNOWN');
 745						return $this->umil_end('FAIL');
 746					}
 747
 748					$this->umil_start('TEMPLATE_CACHE_PURGE', $template_row['template_name']);
 749
 750					// The following is from includes/acp/acp_styles.php
 751					if ($template_row['template_storedb'] && file_exists("{$phpbb_root_path}styles/{$template_row['template_path']}/template/"))
 752					{
 753						$filelist = array('' => array());
 754
 755						$sql = 'SELECT template_filename, template_mtime
 756							FROM ' . STYLES_TEMPLATE_DATA_TABLE . "
 757							WHERE template_id = $style_id";
 758						$result = $this->db->sql_query($sql);
 759
 760						while ($row = $this->db->sql_fetchrow($result))
 761						{
 762//							if (@filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}/template/" . $row['template_filename']) > $row['template_mtime'])
 763//							{
 764								// get folder info from the filename
 765								if (($slash_pos = strrpos($row['template_filename'], '/')) === false)
 766								{
 767									$filelist[''][] = $row['template_filename'];
 768								}
 769								else
 770								{
 771									$filelist[substr($row['template_filename'], 0, $slash_pos + 1)][] = substr($row['template_filename'], $slash_pos + 1, strlen($row['template_filename']) - $slash_pos - 1);
 772								}
 773//							}
 774						}
 775						$this->db->sql_freeresult($result);
 776
 777						$includes = array();
 778						foreach ($filelist as $pathfile => $file_ary)
 779						{
 780							foreach ($file_ary as $file)
 781							{
 782								if (!($fp = @fopen("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file", 'r')))
 783								{
 784									return $this->umil_end('FILE_COULD_NOT_READ', "{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file");
 785								}
 786								$template_data = fread($fp, filesize("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"));
 787								fclose($fp);
 788
 789								if (preg_match_all('#<!-- INCLUDE (.*?\.html) -->#is', $template_data, $matches))
 790								{
 791									foreach ($matches[1] as $match)
 792									{
 793										$includes[trim($match)][] = $file;
 794									}
 795								}
 796							}
 797						}
 798
 799						foreach ($filelist as $pathfile => $file_ary)
 800						{
 801							foreach ($file_ary as $file)
 802							{
 803								// Skip index.
 804								if (strpos($file, 'index.') === 0)
 805								{
 806									continue;
 807								}
 808
 809								// We could do this using extended inserts ... but that could be one
 810								// heck of a lot of data ...
 811								$sql_ary = array(
 812									'template_id'			=> (int) $style_id,
 813									'template_filename'		=> "$pathfile$file",
 814									'template_included'		=> (isset($includes[$file])) ? implode(':', $includes[$file]) . ':' : '',
 815									'template_mtime'		=> (int) filemtime("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
 816									'template_data'			=> (string) file_get_contents("{$phpbb_root_path}styles/{$template_row['template_path']}$pathfile$file"),
 817								);
 818
 819								$sql = 'UPDATE ' . STYLES_TEMPLATE_DATA_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
 820									WHERE template_id = $style_id
 821										AND template_filename = '" . $this->db->sql_escape("$pathfile$file") . "'";
 822								$this->db->sql_query($sql);
 823							}
 824						}
 825						unset($filelist);
 826					}
 827
 828					// Purge the forum's cache as well.
 829					$cache->purge();
 830
 831					return $this->umil_end();
 832				}
 833			break;
 834			//case 'template' :
 835
 836			case 'theme' :
 837				if ($style_id == 0)
 838				{
 839					$return = array();
 840					$sql = 'SELECT theme_id
 841						FROM ' . STYLES_THEME_TABLE;
 842					$result = $this->db->sql_query($sql);
 843					while ($row = $this->db->sql_fetchrow($result))
 844					{
 845						$return[] = $this->cache_purge('theme', $row['theme_id']);
 846					}
 847					$this->db->sql_freeresult($result);
 848
 849					return implode('<br /><br />', $return);
 850				}
 851				else
 852				{
 853					$sql = 'SELECT *
 854						FROM ' . STYLES_THEME_TABLE . "
 855						WHERE theme_id = $style_id";
 856					$result = $this->db->sql_query($sql);
 857					$theme_row = $this->db->sql_fetchrow($result);
 858					$this->db->sql_freeresult($result);
 859
 860					if (!$theme_row)
 861					{
 862						$this->umil_start('THEME_CACHE_PURGE', 'UNKNOWN');
 863						return $this->umil_end('FAIL');
 864					}
 865
 866					$this->umil_start('THEME_CACHE_PURGE', $theme_row['theme_name']);
 867
 868					// The following is from includes/acp/acp_styles.php
 869					if ($theme_row['theme_storedb'] && file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"))
 870					{
 871						$stylesheet = file_get_contents($phpbb_root_path . 'styles/' . $theme_row['theme_path'] . '/theme/stylesheet.css');
 872
 873						// Match CSS imports
 874						$matches = array();
 875						preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
 876
 877						if (sizeof($matches))
 878						{
 879							foreach ($matches[0] as $idx => $match)
 880							{
 881								if (!file_exists("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"))
 882								{
 883									continue;
 884								}
 885
 886								$content = trim(file_get_contents("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/{$matches[1][$idx]}"));
 887								$stylesheet = str_replace($match, $content, $stylesheet);
 888							}
 889						}
 890
 891						// adjust paths
 892						$db_theme_data = str_replace('./', 'styles/' . $theme_row['theme_path'] . '/theme/', $stylesheet);
 893
 894						// Save CSS contents
 895						$sql_ary = array(
 896							'theme_mtime'	=> (int) filemtime("{$phpbb_root_path}styles/{$theme_row['theme_path']}/theme/stylesheet.css"),
 897							'theme_data'	=> $db_theme_data,
 898						);
 899
 900						$sql = 'UPDATE ' . STYLES_THEME_TABLE . ' SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
 901							WHERE theme_id = $style_id";
 902						$this->db->sql_query($sql);
 903
 904						$cache->destroy('sql', STYLES_THEME_TABLE);
 905					}
 906
 907					return $this->umil_end();
 908				}
 909			break;
 910			//case 'theme' :
 911
 912			default:
 913				$this->umil_start('CACHE_PURGE');
 914				$cache->purge();
 915
 916				return $this->umil_end();
 917			break;
 918		}
 919	}
 920
 921	/**
 922	* Config Exists
 923	*
 924	* This function is to check to see if a config variable exists or if it does not.
 925	*
 926	* @param string $config_name The name of the config setting you wish to check for.
 927	* @param bool $return_result - return the config value/default if true : default false.
 928	*
 929	* @return bool true/false if config exists
 930	*/
 931	function config_exists($config_name, $return_result = false)
 932	{
 933		global $config, $cache;
 934
 935		$sql = 'SELECT *
 936				FROM ' . CONFIG_TABLE . "
 937				WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
 938		$result = $this->db->sql_query($sql);
 939		$row = $this->db->sql_fetchrow($result);
 940		$this->db->sql_freeresult($result);
 941
 942		if ($row)
 943		{
 944			if (!isset($config[$config_name]))
 945			{
 946				$config[$config_name] = $row['config_value'];
 947
 948				if (!$row['is_dynamic'])
 949				{
 950					$cache->destroy('config');
 951				}
 952			}
 953
 954			return ($return_result) ? $row : true;
 955		}
 956
 957		// this should never happen, but if it does, we need to remove the config from the array
 958		if (isset($config[$config_name]))
 959		{
 960			unset($config[$config_name]);
 961			$cache->destroy('config');
 962		}
 963
 964		return false;
 965	}
 966
 967	/**
 968	* Config Add
 969	*
 970	* This function allows you to add a config setting.
 971	*
 972	* @param string $config_name The name of the config setting you would like to add
 973	* @param mixed $config_value The value of the config setting
 974	* @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
 975	*
 976	* @return result
 977	*/
 978	function config_add($config_name, $config_value = '', $is_dynamic = false)
 979	{
 980		// Multicall
 981		if ($this->multicall(__FUNCTION__, $config_name))
 982		{
 983			return;
 984		}
 985
 986		$this->umil_start('CONFIG_ADD', $config_name);
 987
 988		if ($this->config_exists($config_name))
 989		{
 990			return $this->umil_end('CONFIG_ALREADY_EXISTS', $config_name);
 991		}
 992
 993		set_config($config_name, $config_value, $is_dynamic);
 994
 995		return $this->umil_end();
 996	}
 997
 998	/**
 999	* Config Update
1000	*
1001	* This function allows you to update an existing config setting.
1002	*
1003	* @param string $config_name The name of the config setting you would like to update
1004	* @param mixed $config_value The value of the config setting
1005	* @param bool $is_dynamic True if it is dynamic (changes very often) and should not be stored in the cache, false if not.
1006	*
1007	* @return result
1008	*/
1009	function config_update($config_name, $config_value = '', $is_dynamic = false)
1010	{
1011		// Multicall
1012		if ($this->multicall(__FUNCTION__, $config_name))
1013		{
1014			return;
1015		}
1016
1017		$this->umil_start('CONFIG_UPDATE', $config_name);
1018
1019		if (!$this->config_exists($config_name))
1020		{
1021			return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
1022		}
1023
1024		set_config($config_name, $config_value, $is_dynamic);
1025
1026		return $this->umil_end();
1027	}
1028
1029	/**
1030	* Config Remove
1031	*
1032	* This function allows you to remove an existing config setting.
1033	*
1034	* @param string $config_name The name of the config setting you would like to remove
1035	*
1036	* @return result
1037	*/
1038	function config_remove($config_name)
1039	{
1040		global $cache, $config;
1041
1042		// Multicall
1043		if ($this->multicall(__FUNCTION__, $config_name))
1044		{
1045			return;
1046		}
1047
1048		$this->umil_start('CONFIG_REMOVE', $config_name);
1049
1050		if (!$this->config_exists($config_name))
1051		{
1052			return $this->umil_end('CONFIG_NOT_EXIST', $config_name);
1053		}
1054
1055		$sql = 'DELETE FROM ' . CONFIG_TABLE . " WHERE config_name = '" . $this->db->sql_escape($config_name) . "'";
1056		$this->db->sql_query($sql);
1057
1058		unset($config[$config_name]);
1059		$cache->destroy('config');
1060
1061		return $this->umil_end();
1062	}
1063
1064	/**
1065	* Module Exists
1066	*
1067	* Check if a module exists
1068	*
1069	* @param string $class The module class(acp|mcp|ucp)
1070	* @param int|string|bool $parent The parent module_id|module_langname (0 for no parent).  Use false to ignore the parent check and check class wide.
1071	* @param int|string $module The module_id|module_langname you would like to check for to see if it exists
1072	*/
1073	function module_exists($class, $parent, $module)
1074	{
1075		// the main root directory should return true
1076		if (!$module)
1077		{
1078			return true;
1079		}
1080
1081		$class = $this->db->sql_escape($class);
1082		$module = $this->db->sql_escape($module);
1083
1084		$parent_sql = '';
1085		if ($parent !== false)
1086		{
1087			// Allows '' to be sent as 0
1088			$parent = (!$parent) ? 0 : $parent;
1089
1090			if (!is_numeric($parent))
1091			{
1092				$sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1093					WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1094					AND module_class = '$class'";
1095				$result = $this->db->sql_query($sql);
1096				$row = $this->db->sql_fetchrow($result);
1097				$this->db->sql_freeresult($result);
1098
1099				if (!$row)
1100				{
1101					return false;
1102				}
1103
1104				$parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
1105			}
1106			else
1107			{
1108				$parent_sql = 'AND parent_id = ' . (int) $parent;
1109			}
1110		}
1111
1112		$sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1113			WHERE module_class = '$class'
1114			$parent_sql
1115			AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '$module'");
1116		$result = $this->db->sql_query($sql);
1117		$row = $this->db->sql_fetchrow($result);
1118		$this->db->sql_freeresult($result);
1119
1120		if ($row)
1121		{
1122			return true;
1123		}
1124
1125		return false;
1126	}
1127
1128	/**
1129	* Module Add
1130	*
1131	* Add a new module
1132	*
1133	* @param string $class The module class(acp|mcp|ucp)
1134	* @param int|string $parent The parent module_id|module_langname (0 for no parent)
1135	* @param array $data an array of the data on the new module.  This can be setup in two different ways.
1136	*	1. The "manual" way.  For inserting a category or one at a time.  It will be merged with the base array shown a bit below,
1137	*		but at the least requires 'module_langname' to be sent, and, if you want to create a module (instead of just a category) you must send module_basename and module_mode.
1138	* array(
1139	*		'module_enabled'	=> 1,
1140	*		'module_display'	=> 1,
1141	*		'module_basename'	=> '',
1142	*		'module_class'		=> $class,
1143	*		'parent_id'			=> (int) $parent,
1144	*		'module_langname'	=> '',
1145	*		'module_mode'		=> '',
1146	*		'module_auth'		=> '',
1147	*	)
1148	*	2. The "automatic" way.  For inserting multiple at a time based on the specs in the info file for the module(s).  For this to work the modules must be correctly setup in the info file.
1149	*		An example follows (this would insert the settings, log, and flag modes from the includes/acp/info/acp_asacp.php file):
1150	* array(
1151	* 		'module_basename'	=> 'asacp',
1152	* 		'modes'				=> array('settings', 'log', 'flag'),
1153	* )
1154	* 		Optionally you may not send 'modes' and it will insert all of the modules in that info file.
1155	*  @param string|bool $include_path If you would like to use a custom include path, specify that here
1156	*/
1157	function module_add($class, $parent = 0, $data = array(), $include_path = false)
1158	{
1159		global $cache, $user, $phpbb_root_path, $phpEx;
1160
1161		// Multicall
1162		if ($this->multicall(__FUNCTION__, $class))
1163		{
1164			return;
1165		}
1166
1167		// Prevent stupid things like trying to add a module with no name or any data on it
1168		if (empty($data))
1169		{
1170			$this->umil_start('MODULE_ADD', $class, 'UNKNOWN');
1171			return $this->umil_end('FAIL');
1172		}
1173
1174        // Allows '' to be sent as 0
1175		$parent = (!$parent) ? 0 : $parent;
1176
1177		// allow sending the name as a string in $data to create a category
1178		if (!is_array($data))
1179		{
1180			$data = array('module_langname' => $data);
1181		}
1182
1183		if (!isset($data['module_langname']))
1184		{
1185			// The "automatic" way
1186			$basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
1187			$basename = str_replace(array('/', '\\'), '', $basename);
1188			$class = str_replace(array('/', '\\'), '', $class);
1189			$info_file = "$class/info/{$class}_$basename.$phpEx";
1190
1191			// The manual and automatic ways both failed...
1192			if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
1193			{
1194				$this->umil_start('MODULE_ADD', $class, $info_file);
1195				return $this->umil_end('FAIL');
1196			}
1197
1198			$classname = "{$class}_{$basename}_info";
1199
1200			if (!class_exists($classname))
1201			{
1202				include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
1203			}
1204
1205			$info = new $classname;
1206			$module = $info->module();
1207			unset($info);
1208
1209			$result = '';
1210			foreach ($module['modes'] as $mode => $module_info)
1211			{
1212				if (!isset($data['modes']) || in_array($mode, $data['modes']))
1213				{
1214					$new_module = array(
1215						'module_basename'	=> $basename,
1216						'module_langname'	=> $module_info['title'],
1217						'module_mode'		=> $mode,
1218						'module_auth'		=> $module_info['auth'],
1219						'module_display'	=> (isset($module_info['display'])) ? $module_info['display'] : true,
1220						'before'			=> (isset($module_info['before'])) ? $module_info['before'] : false,
1221						'after'				=> (isset($module_info['after'])) ? $module_info['after'] : false,
1222					);
1223
1224					// Run the "manual" way with the data we've collected.
1225					$result .= ((isset($data['spacer'])) ? $data['spacer'] : '<br />') . $this->module_add($class, $parent, $new_module);
1226				}
1227			}
1228
1229			return $result;
1230		}
1231
1232		// The "manual" way
1233		$this->umil_start('MODULE_ADD', $class, ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
1234		add_log('admin', 'LOG_MODULE_ADD', ((isset($user->lang[$data['module_langname']])) ? $user->lang[$data['module_langname']] : $data['module_langname']));
1235
1236		$class = $this->db->sql_escape($class);
1237
1238		if (!is_numeric($parent))
1239		{
1240			$sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1241				WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1242				AND module_class = '$class'";
1243			$result = $this->db->sql_query($sql);
1244			$row = $this->db->sql_fetchrow($result);
1245			$this->db->sql_freeresult($result);
1246
1247			if (!$row)
1248			{
1249				return $this->umil_end('PARENT_NOT_EXIST');
1250			}
1251
1252			$parent = $data['parent_id'] = $row['module_id'];
1253		}
1254		else if (!$this->module_exists($class, false, $parent))
1255		{
1256			return $this->umil_end('PARENT_NOT_EXIST');
1257		}
1258
1259		if ($this->module_exists($class, $parent, $data['module_langname']))
1260		{
1261			return $this->umil_end('MODULE_ALREADY_EXIST');
1262		}
1263
1264		if (!class_exists('acp_modules'))
1265		{
1266			include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
1267			$user->add_lang('acp/modules');
1268		}
1269		$acp_modules = new acp_modules();
1270
1271		$module_data = array(
1272			'module_enabled'	=> (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
1273			'module_display'	=> (isset($data['module_display'])) ? $data['module_display'] : 1,
1274			'module_basename'	=> (isset($data['module_basename'])) ? $data['module_basename'] : '',
1275			'module_class'		=> $class,
1276			'parent_id'			=> (int) $parent,
1277			'module_langname'	=> (isset($data['module_langname'])) ? $data['module_langname'] : '',
1278			'module_mode'		=> (isset($data['module_mode'])) ? $data['module_mode'] : '',
1279			'module_auth'		=> (isset($data['module_auth'])) ? $data['module_auth'] : '',
1280		);
1281		$result = $acp_modules->update_module_data($module_data, true);
1282
1283		// update_module_data can either return a string or an empty array...
1284		if (is_string($result))
1285		{
1286			// Error
1287			$this->result = $this->get_output_text($result);
1288		}
1289		else
1290		{
1291			// Success
1292
1293			// Move the module if requested above/below an existing one
1294			if (isset($data['before']) && $data['before'])
1295			{
1296				$sql = 'SELECT left_id FROM ' . MODULES_TABLE . '
1297					WHERE module_class = \'' . $class . '\'
1298					AND parent_id = ' . (int) $parent . '
1299					AND module_langname = \'' . $this->db->sql_escape($data['before']) . '\'';
1300				$this->db->sql_query($sql);
1301				$to_left = $this->db->sql_fetchfield('left_id');
1302
1303				$sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
1304					WHERE module_class = '$class'
1305					AND left_id >= $to_left
1306					AND left_id < {$module_data['left_id']}";
1307				$this->db->sql_query($sql);
1308
1309				$sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = $to_left, right_id = " . ($to_left + 1) . "
1310					WHERE module_class = '$class'
1311					AND module_id = {$module_data['module_id']}";
1312				$this->db->sql_query($sql);
1313			}
1314			else if (isset($data['after']) && $data['after'])
1315			{
1316				$sql = 'SELECT right_id FROM ' . MODULES_TABLE . '
1317					WHERE module_class = \'' . $class . '\'
1318					AND parent_id = ' . (int) $parent . '
1319					AND module_langname = \'' . $this->db->sql_escape($data['after']) . '\'';
1320				$this->db->sql_query($sql);
1321				$to_right = $this->db->sql_fetchfield('right_id');
1322
1323				$sql = 'UPDATE ' . MODULES_TABLE . " SET left_id = left_id + 2, right_id = right_id + 2
1324					WHERE module_class = '$class'
1325					AND left_id >= $to_right
1326					AND left_id < {$module_data['left_id']}";
1327				$this->db->sql_query($sql);
1328
1329				$sql = 'UPDATE ' . MODULES_TABLE . ' SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
1330					WHERE module_class = '$class'
1331					AND module_id = {$module_data['module_id']}";
1332				$this->db->sql_query($sql);
1333			}
1334		}
1335
1336		// Clear the Modules Cache
1337		$cache->destroy("_modules_$class");
1338
1339		return $this->umil_end();
1340	}
1341
1342	/**
1343	* Module Remove
1344	*
1345	* Remove a module
1346	*
1347	* @param string $class The module class(acp|mcp|ucp)
1348	* @param int|string|bool $parent The parent module_id|module_langname (0 for no parent).  Use false to ignore the parent check and check class wide.
1349	* @param int|string $module The module id|module_langname
1350	* @param string|bool $include_path If you would like to use a custom include path, specify that here
1351	*/
1352	function module_remove($class, $parent = 0, $module = '', $include_path = false)
1353	{
1354		global $cache, $user, $phpbb_root_path, $phpEx;
1355
1356		// Multicall
1357		if ($this->multicall(__FUNCTION__, $class))
1358		{
1359			return;
1360		}
1361
1362		// Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
1363		if (is_array($module))
1364		{
1365			if (isset($module['module_langname']))
1366			{
1367				// Manual Method
1368				return $this->module_remove($class, $parent, $module['module_langname'], $include_path);
1369			}
1370
1371			// Failed.
1372			if (!isset($module['module_basename']))
1373			{
1374				$this->umil_start('MODULE_REMOVE', $class, 'UNKNOWN');
1375				return $this->umil_end('FAIL');
1376			}
1377
1378			// Automatic method
1379			$basename = str_replace(array('/', '\\'), '', $module['module_basename']);
1380			$class = str_replace(array('/', '\\'), '', $class);
1381			$info_file = "$class/info/{$class}_$basename.$phpEx";
1382
1383			if (!file_exists((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file))
1384			{
1385				$this->umil_start('MODULE_REMOVE', $class, $info_file);
1386				return $this->umil_end('FAIL');
1387			}
1388
1389			$classname = "{$class}_{$basename}_info";
1390
1391			if (!class_exists($classname))
1392			{
1393				include((($include_path === false) ? $phpbb_root_path . 'includes/' : $include_path) . $info_file);
1394			}
1395
1396			$info = new $classname;
1397			$module_info = $info->module();
1398			unset($info);
1399
1400			$result = '';
1401			foreach ($module_info['modes'] as $mode => $info)
1402			{
1403				if (!isset($module['modes']) || in_array($mode, $module['modes']))
1404				{
1405					$result .= $this->module_remove($class, $parent, $info['title']) . '<br />';
1406				}
1407			}
1408			return $result;
1409		}
1410		else
1411		{
1412			$class = $this->db->sql_escape($class);
1413
1414			if (!$this->module_exists($class, $parent, $module))
1415			{
1416				$this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module])) ? $user->lang[$module] : $module));
1417				return $this->umil_end('MODULE_NOT_EXIST');
1418			}
1419
1420			$parent_sql = '';
1421			if ($parent !== false)
1422			{
1423				// Allows '' to be sent as 0
1424				$parent = (!$parent) ? 0 : $parent;
1425
1426				if (!is_numeric($parent))
1427				{
1428					$sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1429						WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
1430						AND module_class = '$class'";
1431					$result = $this->db->sql_query($sql);
1432					$row = $this->db->sql_fetchrow($result);
1433					$this->db->sql_freeresult($result);
1434
1435					// we know it exists from the module_exists check
1436					$parent_sql = 'AND parent_id = ' . (int) $row['module_id'];
1437				}
1438				else
1439				{
1440					$parent_sql = 'AND parent_id = ' . (int) $parent;
1441				}
1442			}
1443
1444			$module_ids = array();
1445			if (!is_numeric($module))
1446			{
1447				$module = $this->db->sql_escape($module);
1448				$sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
1449					WHERE module_langname = '$module'
1450					AND module_class = '$class'
1451					$parent_sql";
1452				$result = $this->db->sql_query($sql);
1453				while ($row = $this->db->sql_fetchrow($result))
1454				{
1455					$module_ids[] = (int) $row['module_id'];
1456				}
1457				$this->db->sql_freeresult($result);
1458
1459				$module_name = $module;
1460			}
1461			else
1462			{
1463				$module = (int) $module;
1464				$sql = 'SELECT module_langname FROM ' . MODULES_TABLE . "
1465					WHERE module_id = $module
1466					AND module_class = '$class'
1467					$parent_sql";
1468				$result = $this->db->sql_query($sql);
1469				$row = $this->db->sql_fetchrow($result);
1470				$this->db->sql_freeresult($result);
1471
1472				$module_name = $row['module_langname'];
1473				$module_ids[] = $module;
1474			}
1475
1476			$this->umil_start('MODULE_REMOVE', $class, ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
1477			add_log('admin', 'LOG_MODULE_REMOVED', ((isset($user->lang[$module_name])) ? $user->lang[$module_name] : $module_name));
1478
1479			if (!class_exists('acp_modules'))
1480			{
1481				include($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
1482				$user->add_lang('acp/modules');
1483			}
1484			$acp_modules = new acp_modules();
1485			$acp_modules->module_class = $class;
1486
1487			foreach ($module_ids as $module_id)
1488			{
1489				$result = $acp_modules->delete_module($module_id);
1490				if (!empty($result))
1491				{
1492					if ($this->result == ((isset($user->lang['SUCCESS'])) ? $user->lang['SUCCESS'] : 'SUCCESS'))
1493					{
1494						$this->result = implode('<br />', $result);
1495					}
1496					else
1497					{
1498						$this->result .= '<br />' . implode('<br />', $result);
1499					}
1500				}
1501			}
1502
1503			$cache->destroy("_modules_$class");
1504
1505			return $this->umil_end();
1506		}
1507	}
1508
1509	/**
1510	* Permission Exists
1511	*
1512	* Check if a permission (auth) setting exists
1513	*
1514	* @param string $auth_option The name of the permission (auth) option
1515	* @param bool $global True for checking a global permission setting, False for a local permission setting
1516	*
1517	* @return bool true if it exists, false if not
1518	*/
1519	function permission_exists($auth_option, $global = true)
1520	{
1521		if ($global)
1522		{
1523			$type_sql = ' AND is_global = 1';
1524		}
1525		else
1526		{
1527			$type_sql = ' AND is_local = 1';
1528		}
1529
1530		$sql = 'SELECT auth_option_id
1531				FROM ' . ACL_OPTIONS_TABLE . "
1532				WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
1533				. $type_sql;
1534		$result = $this->db->sql_query($sql);
1535
1536		$row = $this->db->sql_fetchrow($result);
1537		$this->db->sql_freeresult($result);
1538
1539		if ($row)
1540		{
1541			return true;
1542		}
1543
1544		return false;
1545	}
1546
1547	/**
1548	* Permission Add
1549	*
1550	* Add a permission (auth) option
1551	*
1552	* @param string $auth_option The name of the permission (auth) option
1553	* @param bool $global True for checking a global permission setting, False for a local permission setting
1554	*
1555	* @return result
1556	*/
1557	function permission_add($auth_option, $global = true)
1558	{
1559		// Multicall
1560		if ($this->multicall(__FUNCTION__, $auth_option))
1561		{
1562			return;
1563		}
1564
1565		$this->umil_start('PERMISSION_ADD', $auth_option);
1566
1567		if ($this->permission_exists($auth_option, $global))
1568		{
1569			return $this->umil_end('PERMISSION_ALREADY_EXISTS', $auth_option);
1570		}
1571
1572		// We've added permissions, so set to true to notify the user.
1573		$this->permissions_added = true;
1574
1575		if (!class_exists('auth_admin'))
1576		{
1577			global $phpbb_root_path, $phpEx;
1578
1579			include($phpbb_root_path . 'includes/acp/auth.' . $phpEx);
1580		}
1581		$auth_admin = new auth_admin();
1582
1583		// We have to add a check to see if the !$global (if global, local, and if local, global) permission already exists.  If it does, acl_add_option currently has a bug which would break the ACL system, so we are having a work-around here.
1584		if ($this->permission_exists($auth_option, !$global))
1585		{
1586			$sql_ary = array(
1587				'is_global'	=> 1,
1588				'is_local'	=> 1,
1589			);
1590			$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
1591				SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
1592				WHERE auth_option = \'' . $this->db->sql_escape($auth_option) . "'";
1593			$this->db->sql_query($sql);
1594		}
1595		else
1596		{
1597			if ($global)
1598			{
1599				$auth_admin->acl_add_option(array('global' => array($auth_option)));
1600			}
1601			else
1602			{
1603				$auth_admin->acl_add_option(array('local' => array($auth_option)));
1604			}
1605		}
1606
1607		return $this->umil_end();
1608	}
1609
1610	/**
1611	* Permission Remove
1612	*
1613	* Remove a permission (auth) option
1614	*
1615	* @param string $auth_option The name of the permission (auth) option
1616	* @param bool $global True for checking a global permission setting, False for a local permission setting
1617	*
1618	* @return result
1619	*/
1620	function permission_remove($auth_option, $global = true)
1621	{
1622		global $auth, $cache;
1623
1624		// Multicall
1625		if ($this->multicall(__FUNCTION__, $auth_option))
1626		{
1627			return;
1628		}
1629
1630		$this->umil_start('PERMISSION_REMOVE', $auth_option);
1631
1632		if (!$this->permission_exists($auth_option, $global))
1633		{
1634			return $this->umil_end('PERMISSION_NOT_EXIST', $auth_option);
1635		}
1636
1637		if ($global)
1638		{
1639			$type_sql = ' AND is_global = 1';
1640		}
1641		else
1642		{
1643			$type_sql = ' AND is_local = 1';
1644		}
1645		$sql = 'SELECT auth_option_id, is_global, is_local FROM ' . ACL_OPTIONS_TABLE . "
1646			WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
1647			$type_sql;
1648		$result = $this->db->sql_query($sql);
1649		$row = $this->db->sql_fetchrow($result);
1650		$this->db->sql_freeresult($result);
1651
1652		$id = $row['auth_option_id'];
1653
1654		// If it is a local and global permission, do not remove the row! :P
1655		if ($row['is_global'] && $row['is_local'])
1656		{
1657			$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
1658				SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
1659				WHERE auth_option_id = ' . $id;
1660			$this->db->sql_query($sql);
1661		}
1662		else
1663		{
1664			// Delete time
1665			$this->db->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $id);
1666			$this->db->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $id);
1667			$this->db->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $id);
1668			$this->db->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $id);
1669		}
1670
1671		// Purge the auth cache
1672		$cache->destroy('_acl_options');
1673		$auth->acl_clear_prefetch();
1674
1675		return $this->umil_end();
1676	}
1677
1678	/**
1679	* Add a new permission role
1680	*
1681	* @param string $role_name The new role name
1682	* @param sting $role_type The type (u_, m_, a_)
1683	*/
1684	function permission_role_add($role_name, $role_type = '', $role_description = '')
1685	{
1686		// Multicall
1687		if ($this->multicall(__FUNCTION__, $role_name))
1688		{
1689			return;
1690		}
1691
1692		$this->umil_start('PERMISSION_ROLE_ADD', $role_name);
1693
1694		$sql = 'SELECT role_id FROM ' . ACL_ROLES_TABLE . '
1695			WHERE role_name = \'' . $this->db->sql_escape($role_name) . '\'';
1696		$this->db->sql_query($sql);
1697		$role_id = $this->db->sql_fetchfield('role_id');
1698
1699		if ($role_id)
1700		{
1701			return $this->umil_end('ROLE_ALREADY_EXISTS', $old_role_name);
1702		}
1703
1704		$sql = 'SELECT MAX(role_order) AS max FROM ' . ACL_ROLES_TABLE . '
1705			WHERE role_type = \'' . $this->db->sql_escape($role_type) . '\'';
1706		$this->db->sql_query($sql);
1707		$role_order = $this->db->sql_fetchfield('max');
1708		$role_order = (!$role_order) ? 1 : $role_order + 1;
1709
1710		$sql_ary = array(
1711			'role_name'			=> $role_name,
1712			'role_description'	=> $role_description,
1713			'role_type'			=> $role_type,
1714			'role_order'		=> $role_order,
1715		);
1716
1717		$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
1718		$this->db->sql_que…

Large files files are truncated, but you can click here to view the full file