diff options
Diffstat (limited to 'phpBB/phpbb/db')
148 files changed, 20632 insertions, 0 deletions
diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php new file mode 100644 index 0000000000..3d4aa95606 --- /dev/null +++ b/phpBB/phpbb/db/driver/driver.php @@ -0,0 +1,970 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* Database Abstraction Layer +*/ +abstract class driver implements driver_interface +{ +	var $db_connect_id; +	var $query_result; +	var $return_on_error = false; +	var $transaction = false; +	var $sql_time = 0; +	var $num_queries = array(); +	var $open_queries = array(); + +	var $curtime = 0; +	var $query_hold = ''; +	var $html_hold = ''; +	var $sql_report = ''; + +	var $persistency = false; +	var $user = ''; +	var $server = ''; +	var $dbname = ''; + +	// Set to true if error triggered +	var $sql_error_triggered = false; + +	// Holding the last sql query on sql error +	var $sql_error_sql = ''; +	// Holding the error information - only populated if sql_error_triggered is set +	var $sql_error_returned = array(); + +	// Holding transaction count +	var $transactions = 0; + +	// Supports multi inserts? +	var $multi_insert = false; + +	/** +	* Current sql layer +	*/ +	var $sql_layer = ''; + +	/** +	* Wildcards for matching any (%) or exactly one (_) character within LIKE expressions +	*/ +	var $any_char; +	var $one_char; + +	/** +	* Exact version of the DBAL, directly queried +	*/ +	var $sql_server_version = false; + +	/** +	* Constructor +	*/ +	function __construct() +	{ +		$this->num_queries = array( +			'cached'	=> 0, +			'normal'	=> 0, +			'total'		=> 0, +		); + +		// Fill default sql layer based on the class being called. +		// This can be changed by the specified layer itself later if needed. +		$this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\')); + +		// Do not change this please! This variable is used to easy the use of it - and is hardcoded. +		$this->any_char = chr(0) . '%'; +		$this->one_char = chr(0) . '_'; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_return_on_error($fail = false) +	{ +		$this->sql_error_triggered = false; +		$this->sql_error_sql = ''; + +		$this->return_on_error = $fail; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_num_queries($cached = false) +	{ +		return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal']; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_add_num_queries($cached = false) +	{ +		$this->num_queries['cached'] += ($cached !== false) ? 1 : 0; +		$this->num_queries['normal'] += ($cached !== false) ? 0 : 1; +		$this->num_queries['total'] += 1; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_close() +	{ +		if (!$this->db_connect_id) +		{ +			return false; +		} + +		if ($this->transaction) +		{ +			do +			{ +				$this->sql_transaction('commit'); +			} +			while ($this->transaction); +		} + +		foreach ($this->open_queries as $query_id) +		{ +			$this->sql_freeresult($query_id); +		} + +		// Connection closed correctly. Set db_connect_id to false to prevent errors +		if ($result = $this->_sql_close()) +		{ +			$this->db_connect_id = false; +		} + +		return $result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		if (empty($query)) +		{ +			return false; +		} + +		// Never use a negative total or offset +		$total = ($total < 0) ? 0 : $total; +		$offset = ($offset < 0) ? 0 : $offset; + +		return $this->_sql_query_limit($query, $total, $offset, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrowset($query_id = false) +	{ +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($query_id !== false) +		{ +			$result = array(); +			while ($row = $this->sql_fetchrow($query_id)) +			{ +				$result[] = $row; +			} + +			return $result; +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		if ($query_id === false) +		{ +			return false; +		} + +		$this->sql_freeresult($query_id); +		$query_id = $this->sql_query($this->last_query_text); + +		if ($query_id === false) +		{ +			return false; +		} + +		// We do not fetch the row for rownum == 0 because then the next resultset would be the second row +		for ($i = 0; $i < $rownum; $i++) +		{ +			if (!$this->sql_fetchrow($query_id)) +			{ +				return false; +			} +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchfield($field, $rownum = false, $query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($query_id !== false) +		{ +			if ($rownum !== false) +			{ +				$this->sql_rowseek($rownum, $query_id); +			} + +			if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +			{ +				return $cache->sql_fetchfield($query_id, $field); +			} + +			$row = $this->sql_fetchrow($query_id); +			return (isset($row[$field])) ? $row[$field] : false; +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_like_expression($expression) +	{ +		$expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression); +		$expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression); + +		return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\''); +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_case($condition, $action_true, $action_false = false) +	{ +		$sql_case = 'CASE WHEN ' . $condition; +		$sql_case .= ' THEN ' . $action_true; +		$sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : ''; +		$sql_case .= ' END'; +		return $sql_case; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return $expr1 . ' || ' . $expr2; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_buffer_nested_transactions() +	{ +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				// If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit) +				if ($this->transaction) +				{ +					$this->transactions++; +					return true; +				} + +				$result = $this->_sql_transaction('begin'); + +				if (!$result) +				{ +					$this->sql_error(); +				} + +				$this->transaction = true; +			break; + +			case 'commit': +				// If there was a previously opened transaction we do not commit yet... +				// but count back the number of inner transactions +				if ($this->transaction && $this->transactions) +				{ +					$this->transactions--; +					return true; +				} + +				// Check if there is a transaction (no transaction can happen if +				// there was an error, with a combined rollback and error returning enabled) +				// This implies we have transaction always set for autocommit db's +				if (!$this->transaction) +				{ +					return false; +				} + +				$result = $this->_sql_transaction('commit'); + +				if (!$result) +				{ +					$this->sql_error(); +				} + +				$this->transaction = false; +				$this->transactions = 0; +			break; + +			case 'rollback': +				$result = $this->_sql_transaction('rollback'); +				$this->transaction = false; +				$this->transactions = 0; +			break; + +			default: +				$result = $this->_sql_transaction($status); +			break; +		} + +		return $result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_build_array($query, $assoc_ary = false) +	{ +		if (!is_array($assoc_ary)) +		{ +			return false; +		} + +		$fields = $values = array(); + +		if ($query == 'INSERT' || $query == 'INSERT_SELECT') +		{ +			foreach ($assoc_ary as $key => $var) +			{ +				$fields[] = $key; + +				if (is_array($var) && is_string($var[0])) +				{ +					// This is used for INSERT_SELECT(s) +					$values[] = $var[0]; +				} +				else +				{ +					$values[] = $this->_sql_validate_value($var); +				} +			} + +			$query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' '; +		} +		else if ($query == 'MULTI_INSERT') +		{ +			trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR); +		} +		else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE') +		{ +			$values = array(); +			foreach ($assoc_ary as $key => $var) +			{ +				$values[] = "$key = " . $this->_sql_validate_value($var); +			} +			$query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values); +		} + +		return $query; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_in_set($field, $array, $negate = false, $allow_empty_set = false) +	{ +		if (!sizeof($array)) +		{ +			if (!$allow_empty_set) +			{ +				// Print the backtrace to help identifying the location of the problematic code +				$this->sql_error('No values specified for SQL IN comparison'); +			} +			else +			{ +				// NOT IN () actually means everything so use a tautology +				if ($negate) +				{ +					return '1=1'; +				} +				// IN () actually means nothing so use a contradiction +				else +				{ +					return '1=0'; +				} +			} +		} + +		if (!is_array($array)) +		{ +			$array = array($array); +		} + +		if (sizeof($array) == 1) +		{ +			@reset($array); +			$var = current($array); + +			return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var); +		} +		else +		{ +			return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')'; +		} +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_bit_and($column_name, $bit, $compare = '') +	{ +		if (method_exists($this, '_sql_bit_and')) +		{ +			return $this->_sql_bit_and($column_name, $bit, $compare); +		} + +		return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : ''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_bit_or($column_name, $bit, $compare = '') +	{ +		if (method_exists($this, '_sql_bit_or')) +		{ +			return $this->_sql_bit_or($column_name, $bit, $compare); +		} + +		return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : ''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function cast_expr_to_bigint($expression) +	{ +		return $expression; +	} + +	/** +	* {@inheritDoc} +	*/ +	function cast_expr_to_string($expression) +	{ +		return $expression; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_lower_text($column_name) +	{ +		return "LOWER($column_name)"; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_multi_insert($table, $sql_ary) +	{ +		if (!sizeof($sql_ary)) +		{ +			return false; +		} + +		if ($this->multi_insert) +		{ +			$ary = array(); +			foreach ($sql_ary as $id => $_sql_ary) +			{ +				// If by accident the sql array is only one-dimensional we build a normal insert statement +				if (!is_array($_sql_ary)) +				{ +					return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary)); +				} + +				$values = array(); +				foreach ($_sql_ary as $key => $var) +				{ +					$values[] = $this->_sql_validate_value($var); +				} +				$ary[] = '(' . implode(', ', $values) . ')'; +			} + +			return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary)); +		} +		else +		{ +			foreach ($sql_ary as $ary) +			{ +				if (!is_array($ary)) +				{ +					return false; +				} + +				$result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary)); + +				if (!$result) +				{ +					return false; +				} +			} +		} + +		return true; +	} + +	/** +	* Function for validating values +	* @access private +	*/ +	function _sql_validate_value($var) +	{ +		if (is_null($var)) +		{ +			return 'NULL'; +		} +		else if (is_string($var)) +		{ +			return "'" . $this->sql_escape($var) . "'"; +		} +		else +		{ +			return (is_bool($var)) ? intval($var) : $var; +		} +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_build_query($query, $array) +	{ +		$sql = ''; +		switch ($query) +		{ +			case 'SELECT': +			case 'SELECT_DISTINCT'; + +				$sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM '; + +				// Build table array. We also build an alias array for later checks. +				$table_array = $aliases = array(); +				$used_multi_alias = false; + +				foreach ($array['FROM'] as $table_name => $alias) +				{ +					if (is_array($alias)) +					{ +						$used_multi_alias = true; + +						foreach ($alias as $multi_alias) +						{ +							$table_array[] = $table_name . ' ' . $multi_alias; +							$aliases[] = $multi_alias; +						} +					} +					else +					{ +						$table_array[] = $table_name . ' ' . $alias; +						$aliases[] = $alias; +					} +				} + +				// We run the following code to determine if we need to re-order the table array. ;) +				// The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison. +				// DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is. +				if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false) +				{ +					// Take first LEFT JOIN +					$join = current($array['LEFT_JOIN']); + +					// Determine the table used there (even if there are more than one used, we only want to have one +					preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches); + +					// If there is a first join match, we need to make sure the table order is correct +					if (!empty($matches[1])) +					{ +						$first_join_match = trim($matches[1]); +						$table_array = $last = array(); + +						foreach ($array['FROM'] as $table_name => $alias) +						{ +							if (is_array($alias)) +							{ +								foreach ($alias as $multi_alias) +								{ +									($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias; +								} +							} +							else +							{ +								($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias; +							} +						} + +						$table_array = array_merge($table_array, $last); +					} +				} + +				$sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array)); + +				if (!empty($array['LEFT_JOIN'])) +				{ +					foreach ($array['LEFT_JOIN'] as $join) +					{ +						$sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')'; +					} +				} + +				if (!empty($array['WHERE'])) +				{ +					$sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']); +				} + +				if (!empty($array['GROUP_BY'])) +				{ +					$sql .= ' GROUP BY ' . $array['GROUP_BY']; +				} + +				if (!empty($array['ORDER_BY'])) +				{ +					$sql .= ' ORDER BY ' . $array['ORDER_BY']; +				} + +			break; +		} + +		return $sql; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_error($sql = '') +	{ +		global $auth, $user, $config; + +		// Set var to retrieve errored status +		$this->sql_error_triggered = true; +		$this->sql_error_sql = $sql; + +		$this->sql_error_returned = $this->_sql_error(); + +		if (!$this->return_on_error) +		{ +			$message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']'; + +			// Show complete SQL error and path to administrators only +			// Additionally show complete error on installation or if extended debug mode is enabled +			// The DEBUG constant is for development only! +			if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG')) +			{ +				$message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : ''; +			} +			else +			{ +				// If error occurs in initiating the session we need to use a pre-defined language string +				// This could happen if the connection could not be established for example (then we are not able to grab the default language) +				if (!isset($user->lang['SQL_ERROR_OCCURRED'])) +				{ +					$message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.'; +				} +				else +				{ +					if (!empty($config['board_contact'])) +					{ +						$message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'); +					} +					else +					{ +						$message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', ''); +					} +				} +			} + +			if ($this->transaction) +			{ +				$this->sql_transaction('rollback'); +			} + +			if (strlen($message) > 1024) +			{ +				// We need to define $msg_long_text here to circumvent text stripping. +				global $msg_long_text; +				$msg_long_text = $message; + +				trigger_error(false, E_USER_ERROR); +			} + +			trigger_error($message, E_USER_ERROR); +		} + +		if ($this->transaction) +		{ +			$this->sql_transaction('rollback'); +		} + +		return $this->sql_error_returned; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_report($mode, $query = '') +	{ +		global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper, $user; +		global $request; + +		if (is_object($request) && !$request->variable('explain', false)) +		{ +			return false; +		} + +		if (!$query && $this->query_hold != '') +		{ +			$query = $this->query_hold; +		} + +		switch ($mode) +		{ +			case 'display': +				if (!empty($cache)) +				{ +					$cache->unload(); +				} +				$this->sql_close(); + +				$mtime = explode(' ', microtime()); +				$totaltime = $mtime[0] + $mtime[1] - $starttime; + +				echo '<!DOCTYPE html> +					<html dir="ltr"> +					<head> +						<meta charset="utf-8"> +						<title>SQL Report</title> +						<link href="' . htmlspecialchars($phpbb_path_helper->update_web_root_path($phpbb_root_path) . $phpbb_path_helper->get_adm_relative_path()) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" /> +					</head> +					<body id="errorpage"> +					<div id="wrap"> +						<div id="page-header"> +							<a href="' . build_url('explain') . '">Return to previous page</a> +						</div> +						<div id="page-body"> +							<div id="acp"> +							<div class="panel"> +								<span class="corners-top"><span></span></span> +								<div id="content"> +									<h1>SQL Report</h1> +									<br /> +									<p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" . (($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></p> + +									<p>Time spent on ' . $this->sql_layer . ' queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p> + +									<br /><br /> +									' . $this->sql_report . ' +								</div> +								<span class="corners-bottom"><span></span></span> +							</div> +							</div> +						</div> +						<div id="page-footer"> +							Powered by <a href="https://www.phpbb.com/">phpBB</a>® Forum Software © phpBB Limited +						</div> +					</div> +					</body> +					</html>'; + +				exit_handler(); + +			break; + +			case 'stop': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$this->sql_report .= ' + +					<table cellspacing="1"> +					<thead> +					<tr> +						<th>Query #' . $this->num_queries['total'] . '</th> +					</tr> +					</thead> +					<tbody> +					<tr> +						<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td> +					</tr> +					</tbody> +					</table> + +					' . $this->html_hold . ' + +					<p style="text-align: center;"> +				'; + +				if ($this->query_result) +				{ +					if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query)) +					{ +						$this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | '; +					} +					$this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>'; +				} +				else +				{ +					$error = $this->sql_error(); +					$this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']); +				} + +				$this->sql_report .= '</p><br /><br />'; + +				$this->sql_time += $endtime - $this->curtime; +			break; + +			case 'start': +				$this->query_hold = $query; +				$this->html_hold = ''; + +				$this->_sql_report($mode, $query); + +				$this->curtime = explode(' ', microtime()); +				$this->curtime = $this->curtime[0] + $this->curtime[1]; + +			break; + +			case 'add_select_row': + +				$html_table = func_get_arg(2); +				$row = func_get_arg(3); + +				if (!$html_table && sizeof($row)) +				{ +					$html_table = true; +					$this->html_hold .= '<table cellspacing="1"><tr>'; + +					foreach (array_keys($row) as $val) +					{ +						$this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : ' ') . '</th>'; +					} +					$this->html_hold .= '</tr>'; +				} +				$this->html_hold .= '<tr>'; + +				$class = 'row1'; +				foreach (array_values($row) as $val) +				{ +					$class = ($class == 'row1') ? 'row2' : 'row1'; +					$this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : ' ') . '</td>'; +				} +				$this->html_hold .= '</tr>'; + +				return $html_table; + +			break; + +			case 'fromcache': + +				$this->_sql_report($mode, $query); + +			break; + +			case 'record_fromcache': + +				$endtime = func_get_arg(2); +				$splittime = func_get_arg(3); + +				$time_cache = $endtime - $this->curtime; +				$time_db = $splittime - $endtime; +				$color = ($time_db > $time_cache) ? 'green' : 'red'; + +				$this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>'; +				$this->sql_report .= '<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></tbody></table>'; +				$this->sql_report .= '<p style="text-align: center;">'; +				$this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />'; + +				// Pad the start time to not interfere with page timing +				$starttime += $time_db; + +			break; + +			default: + +				$this->_sql_report($mode, $query); + +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function get_estimated_row_count($table_name) +	{ +		return $this->get_row_count($table_name); +	} + +	/** +	* {@inheritDoc} +	*/ +	function get_row_count($table_name) +	{ +		$sql = 'SELECT COUNT(*) AS rows_total +			FROM ' . $this->sql_escape($table_name); +		$result = $this->sql_query($sql); +		$rows_total = $this->sql_fetchfield('rows_total'); +		$this->sql_freeresult($result); + +		return $rows_total; +	} +} diff --git a/phpBB/phpbb/db/driver/driver_interface.php b/phpBB/phpbb/db/driver/driver_interface.php new file mode 100644 index 0000000000..7f7d341e2d --- /dev/null +++ b/phpBB/phpbb/db/driver/driver_interface.php @@ -0,0 +1,359 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +interface driver_interface +{ +	/** +	* Gets the exact number of rows in a specified table. +	* +	* @param string $table_name Table name +	* @return string	Exact number of rows in $table_name. +	*/ +	public function get_row_count($table_name); + +	/** +	* Gets the estimated number of rows in a specified table. +	* +	* @param string $table_name Table name +	* @return string	Number of rows in $table_name. +	*					Prefixed with ~ if estimated (otherwise exact). +	*/ +	public function get_estimated_row_count($table_name); + +	/** +	* Run LOWER() on DB column of type text (i.e. neither varchar nor char). +	* +	* @param string $column_name	The column name to use +	* @return string		A SQL statement like "LOWER($column_name)" +	*/ +	public function sql_lower_text($column_name); + +	/** +	* Display sql error page +	* +	* @param string		$sql	The SQL query causing the error +	* @return mixed		Returns the full error message, if $this->return_on_error +	*					is set, null otherwise +	*/ +	public function sql_error($sql = ''); + +	/** +	* Returns whether results of a query need to be buffered to run a +	* transaction while iterating over them. +	* +	* @return bool	Whether buffering is required. +	*/ +	public function sql_buffer_nested_transactions(); + +	/** +	* Run binary OR operator on DB column. +	* +	* @param string	$column_name	The column name to use +	* @param int	$bit			The value to use for the OR operator, +	*					will be converted to (1 << $bit). Is used by options, +	*					using the number schema... 0, 1, 2...29 +	* @param string	$compare	Any custom SQL code after the check (e.g. "= 0") +	* @return string	A SQL statement like "$column | (1 << $bit) {$compare}" +	*/ +	public function sql_bit_or($column_name, $bit, $compare = ''); + +	/** +	* Version information about used database +	* +	* @param bool $raw			Only return the fetched sql_server_version +	* @param bool $use_cache	Is it safe to retrieve the value from the cache +	* @return string sql server version +	*/ +	public function sql_server_info($raw = false, $use_cache = true); + +	/** +	* Return on error or display error message +	* +	* @param bool	$fail		Should we return on errors, or stop +	* @return null +	*/ +	public function sql_return_on_error($fail = false); + +	/** +	* Build sql statement from an array +	* +	* @param	string	$query		Should be on of the following strings: +	*						INSERT, INSERT_SELECT, UPDATE, SELECT, DELETE +	* @param	array	$assoc_ary	Array with "column => value" pairs +	* @return	string		A SQL statement like "c1 = 'a' AND c2 = 'b'" +	*/ +	public function sql_build_array($query, $assoc_ary = array()); + +	/** +	* Fetch all rows +	* +	* @param	mixed	$query_id	Already executed query to get the rows from, +	*								if false, the last query will be used. +	* @return	mixed		Nested array if the query had rows, false otherwise +	*/ +	public function sql_fetchrowset($query_id = false); + +	/** +	* SQL Transaction +	* +	* @param	string	$status		Should be one of the following strings: +	*								begin, commit, rollback +	* @return	mixed	Buffered, seekable result handle, false on error +	*/ +	public function sql_transaction($status = 'begin'); + +	/** +	* Build a concatenated expression +	* +	* @param	string	$expr1		Base SQL expression where we append the second one +	* @param	string	$expr2		SQL expression that is appended to the first expression +	* @return	string		Concatenated string +	*/ +	public function sql_concatenate($expr1, $expr2); + +	/** +	* Build a case expression +	* +	* Note: The two statements action_true and action_false must have the same +	* data type (int, vchar, ...) in the database! +	* +	* @param	string	$condition		The condition which must be true, +	*							to use action_true rather then action_else +	* @param	string	$action_true	SQL expression that is used, if the condition is true +	* @param	mixed	$action_false	SQL expression that is used, if the condition is false +	* @return	string		CASE expression including the condition and statements +	*/ +	public function sql_case($condition, $action_true, $action_false = false); + +	/** +	* Build sql statement from array for select and select distinct statements +	* +	* Possible query values: SELECT, SELECT_DISTINCT +	* +	* @param	string	$query	Should be one of: SELECT, SELECT_DISTINCT +	* @param	array	$array	Array with the query data: +	*					SELECT		A comma imploded list of columns to select +	*					FROM		Array with "table => alias" pairs, +	*								(alias can also be an array) +	*		Optional:	LEFT_JOIN	Array of join entries: +	*						FROM		Table that should be joined +	*						ON			Condition for the join +	*		Optional:	WHERE		Where SQL statement +	*		Optional:	GROUP_BY	Group by SQL statement +	*		Optional:	ORDER_BY	Order by SQL statement +	* @return	string		A SQL statement ready for execution +	*/ +	public function sql_build_query($query, $array); + +	/** +	* Fetch field +	* if rownum is false, the current row is used, else it is pointing to the row (zero-based) +	* +	* @param	string	$field		Name of the column +	* @param	mixed	$rownum		Row number, if false the current row will be used +	*								and the row curser will point to the next row +	*								Note: $rownum is 0 based +	* @param	mixed	$query_id	Already executed query to get the rows from, +	*								if false, the last query will be used. +	* @return	mixed		String value of the field in the selected row, +	*						false, if the row does not exist +	*/ +	public function sql_fetchfield($field, $rownum = false, $query_id = false); + +	/** +	* Fetch current row +	* +	* @param	mixed	$query_id	Already executed query to get the rows from, +	*								if false, the last query will be used. +	* @return	mixed		Array with the current row, +	*						false, if the row does not exist +	*/ +	public function sql_fetchrow($query_id = false); + +	/** +	* Returns SQL string to cast a string expression to an int. +	* +	* @param  string $expression An expression evaluating to string +	* @return string             Expression returning an int +	*/ +	public function cast_expr_to_bigint($expression); + +	/** +	* Get last inserted id after insert statement +	* +	* @return	string		Autoincrement value of the last inserted row +	*/ +	public function sql_nextid(); + +	/** +	* Add to query count +	* +	* @param bool $cached	Is this query cached? +	* @return null +	*/ +	public function sql_add_num_queries($cached = false); + +	/** +	* Build LIMIT query +	* +	* @param	string	$query		The SQL query to execute +	* @param	int		$total		The number of rows to select +	* @param	int		$offset +	* @param	int		$cache_ttl	Either 0 to avoid caching or +	*				the time in seconds which the result shall be kept in cache +	* @return	mixed	Buffered, seekable result handle, false on error +	*/ +	public function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0); + +	/** +	* Base query method +	* +	* @param	string	$query		The SQL query to execute +	* @param	int		$cache_ttl	Either 0 to avoid caching or +	*				the time in seconds which the result shall be kept in cache +	* @return	mixed	Buffered, seekable result handle, false on error +	*/ +	public function sql_query($query = '', $cache_ttl = 0); + +	/** +	* Returns SQL string to cast an integer expression to a string. +	* +	* @param	string	$expression		An expression evaluating to int +	* @return string		Expression returning a string +	*/ +	public function cast_expr_to_string($expression); + +	/** +	 * Connect to server +	 * +	 * @param	string	$sqlserver		Address of the database server +	 * @param	string	$sqluser		User name of the SQL user +	 * @param	string	$sqlpassword	Password of the SQL user +	 * @param	string	$database		Name of the database +	 * @param	mixed	$port			Port of the database server +	 * @param	bool	$persistency +	 * @param	bool	$new_link		Should a new connection be established +	 * @return	mixed	Connection ID on success, string error message otherwise +	 */ +	public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false); + +	/** +	* Run binary AND operator on DB column. +	* Results in sql statement: "{$column_name} & (1 << {$bit}) {$compare}" +	* +	* @param string	$column_name	The column name to use +	* @param int	$bit			The value to use for the AND operator, +	*								will be converted to (1 << $bit). Is used by +	*								options, using the number schema: 0, 1, 2...29 +	* @param string	$compare		Any custom SQL code after the check (for example "= 0") +	* @return string	A SQL statement like: "{$column} & (1 << {$bit}) {$compare}" +	*/ +	public function sql_bit_and($column_name, $bit, $compare = ''); + +	/** +	* Free sql result +	* +	* @param	mixed	$query_id	Already executed query result, +	*								if false, the last query will be used. +	* @return	null +	*/ +	public function sql_freeresult($query_id = false); + +	/** +	* Return number of sql queries and cached sql queries used +	* +	* @param	bool	$cached		Should we return the number of cached or normal queries? +	* @return	int		Number of queries that have been executed +	*/ +	public function sql_num_queries($cached = false); + +	/** +	* Run more than one insert statement. +	* +	* @param string	$table		Table name to run the statements on +	* @param array	$sql_ary	Multi-dimensional array holding the statement data +	* @return bool		false if no statements were executed. +	*/ +	public function sql_multi_insert($table, $sql_ary); + +	/** +	* Return number of affected rows +	* +	* @return	mixed		Number of the affected rows by the last query +	*						false if no query has been run before +	*/ +	public function sql_affectedrows(); + +	/** +	* DBAL garbage collection, close SQL connection +	* +	* @return	mixed		False if no connection was opened before, +	*						Server response otherwise +	*/ +	public function sql_close(); + +	/** +	* Seek to given row number +	* +	* @param	mixed	$rownum		Row number the curser should point to +	*								Note: $rownum is 0 based +	* @param	mixed	$query_id	ID of the query to set the row cursor on +	*								if false, the last query will be used. +	*								$query_id will then be set correctly +	* @return	bool		False if something went wrong +	*/ +	public function sql_rowseek($rownum, &$query_id); + +	/** +	* Escape string used in sql query +	* +	* @param	string	$msg	String to be escaped +	* @return	string		Escaped version of $msg +	*/ +	public function sql_escape($msg); + +	/** +	* Correctly adjust LIKE expression for special characters +	* Some DBMS are handling them in a different way +	* +	* @param	string	$expression	The expression to use. Every wildcard is +	*						escaped, except $this->any_char and $this->one_char +	* @return string	A SQL statement like: "LIKE 'bertie_%'" +	*/ +	public function sql_like_expression($expression); + +	/** +	* Explain queries +	* +	* @param	string	$mode		Available modes: display, start, stop, +	 *								add_select_row, fromcache, record_fromcache +	* @param	string	$query		The Query that should be explained +	* @return	mixed		Either a full HTML page, boolean or null +	*/ +	public function sql_report($mode, $query = ''); + +	/** +	* Build IN or NOT IN sql comparison string, uses <> or = on single element +	* arrays to improve comparison speed +	* +	* @param	string	$field			Name of the sql column that shall be compared +	* @param	array	$array			Array of values that are (not) allowed +	* @param	bool	$negate			true for NOT IN (), false for IN () +	* @param	bool	$allow_empty_set	If true, allow $array to be empty, +	*								this function will return 1=1 or 1=0 then. +	* @return string	A SQL statement like: "IN (1, 2, 3, 4)" or "= 1" +	*/ +	public function sql_in_set($field, $array, $negate = false, $allow_empty_set = false); +} diff --git a/phpBB/phpbb/db/driver/mssql.php b/phpBB/phpbb/db/driver/mssql.php new file mode 100644 index 0000000000..268463a151 --- /dev/null +++ b/phpBB/phpbb/db/driver/mssql.php @@ -0,0 +1,467 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* MSSQL Database Abstraction Layer +* Minimum Requirement is MSSQL 2000+ +*/ +class mssql extends \phpbb\db\driver\driver +{ +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		if (!function_exists('mssql_connect')) +		{ +			$this->connect_error = 'mssql_connect function does not exist, is mssql extension installed?'; +			return $this->sql_error(''); +		} + +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->dbname = $database; + +		$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':'; +		$this->server = $sqlserver . (($port) ? $port_delimiter . $port : ''); + +		@ini_set('mssql.charset', 'UTF-8'); +		@ini_set('mssql.textlimit', 2147483647); +		@ini_set('mssql.textsize', 2147483647); + +		$this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword, $new_link) : @mssql_connect($this->server, $this->user, $sqlpassword, $new_link); + +		if ($this->db_connect_id && $this->dbname != '') +		{ +			if (!@mssql_select_db($this->dbname, $this->db_connect_id)) +			{ +				@mssql_close($this->db_connect_id); +				return false; +			} +		} + +		return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false) +		{ +			$result_id = @mssql_query("SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')", $this->db_connect_id); + +			$row = false; +			if ($result_id) +			{ +				$row = @mssql_fetch_assoc($result_id); +				@mssql_free_result($result_id); +			} + +			$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('mssql_version', $this->sql_server_version); +			} +		} + +		if ($raw) +		{ +			return $this->sql_server_version; +		} + +		return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL'; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return $expr1 . ' + ' . $expr2; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @mssql_query('BEGIN TRANSACTION', $this->db_connect_id); +			break; + +			case 'commit': +				return @mssql_query('COMMIT TRANSACTION', $this->db_connect_id); +			break; + +			case 'rollback': +				return @mssql_query('ROLLBACK TRANSACTION', $this->db_connect_id); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows) +		if ($total) +		{ +			// We need to grab the total number of rows + the offset number of rows to get the correct result +			if (strpos($query, 'SELECT DISTINCT') === 0) +			{ +				$query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15); +			} +			else +			{ +				$query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6); +			} +		} + +		$result = $this->sql_query($query, $cache_ttl); + +		// Seek by $offset rows +		if ($offset) +		{ +			$this->sql_rowseek($offset, $result); +		} + +		return $result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		if ($query_id === false) +		{ +			return false; +		} + +		$row = @mssql_fetch_assoc($query_id); + +		// I hope i am able to remove this later... hopefully only a PHP or MSSQL bug +		if ($row) +		{ +			foreach ($row as $key => $value) +			{ +				$row[$key] = ($value === ' ' || $value === null) ? '' : $value; +			} +		} + +		return $row; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		return ($query_id !== false) ? @mssql_data_seek($query_id, $rownum) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		$result_id = @mssql_query('SELECT SCOPE_IDENTITY()', $this->db_connect_id); +		if ($result_id) +		{ +			if ($row = @mssql_fetch_assoc($result_id)) +			{ +				@mssql_free_result($result_id); +				return $row['computed']; +			} +			@mssql_free_result($result_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @mssql_free_result($query_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return str_replace(array("'", "\0"), array("''", ''), $msg); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_lower_text($column_name) +	{ +		return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))"; +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression . " ESCAPE '\\'"; +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if (function_exists('mssql_get_last_message')) +		{ +			$error = array( +				'message'	=> @mssql_get_last_message(), +				'code'		=> '', +			); + +			// Get error code number +			$result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id); +			if ($result_id) +			{ +				$row = @mssql_fetch_assoc($result_id); +				$error['code'] = $row['code']; +				@mssql_free_result($result_id); +			} + +			// Get full error message if possible +			$sql = 'SELECT CAST(description as varchar(255)) as message +				FROM master.dbo.sysmessages +				WHERE error = ' . $error['code']; +			$result_id = @mssql_query($sql); + +			if ($result_id) +			{ +				$row = @mssql_fetch_assoc($result_id); +				if (!empty($row['message'])) +				{ +					$error['message'] .= '<br />' . $row['message']; +				} +				@mssql_free_result($result_id); +			} +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @mssql_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': +				$html_table = false; +				@mssql_query('SET SHOWPLAN_TEXT ON;', $this->db_connect_id); +				if ($result = @mssql_query($query, $this->db_connect_id)) +				{ +					@mssql_next_result($result); +					while ($row = @mssql_fetch_row($result)) +					{ +						$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +					} +				} +				@mssql_query('SET SHOWPLAN_TEXT OFF;', $this->db_connect_id); +				@mssql_free_result($result); + +				if ($html_table) +				{ +					$this->html_hold .= '</table>'; +				} +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @mssql_query($query, $this->db_connect_id); +				while ($void = @mssql_fetch_assoc($result)) +				{ +					// Take the time spent on parsing rows into account +				} +				@mssql_free_result($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/mssql_base.php b/phpBB/phpbb/db/driver/mssql_base.php new file mode 100644 index 0000000000..e7101903b8 --- /dev/null +++ b/phpBB/phpbb/db/driver/mssql_base.php @@ -0,0 +1,62 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* MSSQL Database Base Abstraction Layer + */ +abstract class mssql_base extends \phpbb\db\driver\driver +{ +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return $expr1 . ' + ' . $expr2; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return str_replace(array("'", "\0"), array("''", ''), $msg); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_lower_text($column_name) +	{ +		return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))"; +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression . " ESCAPE '\\'"; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} +} diff --git a/phpBB/phpbb/db/driver/mssql_odbc.php b/phpBB/phpbb/db/driver/mssql_odbc.php new file mode 100644 index 0000000000..8e5d4c7a4c --- /dev/null +++ b/phpBB/phpbb/db/driver/mssql_odbc.php @@ -0,0 +1,377 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* Unified ODBC functions +* Unified ODBC functions support any database having ODBC driver, for example Adabas D, IBM DB2, iODBC, Solid, Sybase SQL Anywhere... +* Here we only support MSSQL Server 2000+ because of the provided schema +* +* @note number of bytes returned for returning data depends on odbc.defaultlrl php.ini setting. +* If it is limited to 4K for example only 4K of data is returned max, resulting in incomplete theme data for example. +* @note odbc.defaultbinmode may affect UTF8 characters +*/ +class mssql_odbc extends \phpbb\db\driver\mssql_base +{ +	var $last_query_text = ''; +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->dbname = $database; + +		$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':'; +		$this->server = $sqlserver . (($port) ? $port_delimiter . $port : ''); + +		$max_size = @ini_get('odbc.defaultlrl'); +		if (!empty($max_size)) +		{ +			$unit = strtolower(substr($max_size, -1, 1)); +			$max_size = (int) $max_size; + +			if ($unit == 'k') +			{ +				$max_size = floor($max_size / 1024); +			} +			else if ($unit == 'g') +			{ +				$max_size *= 1024; +			} +			else if (is_numeric($unit)) +			{ +				$max_size = floor((int) ($max_size . $unit) / 1048576); +			} +			$max_size = max(8, $max_size) . 'M'; + +			@ini_set('odbc.defaultlrl', $max_size); +		} + +		if ($this->persistency) +		{ +			if (!function_exists('odbc_pconnect')) +			{ +				$this->connect_error = 'odbc_pconnect function does not exist, is odbc extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @odbc_pconnect($this->server, $this->user, $sqlpassword); +		} +		else +		{ +			if (!function_exists('odbc_connect')) +			{ +				$this->connect_error = 'odbc_connect function does not exist, is odbc extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @odbc_connect($this->server, $this->user, $sqlpassword); +		} + +		return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssqlodbc_version')) === false) +		{ +			$result_id = @odbc_exec($this->db_connect_id, "SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')"); + +			$row = false; +			if ($result_id) +			{ +				$row = @odbc_fetch_array($result_id); +				@odbc_free_result($result_id); +			} + +			$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('mssqlodbc_version', $this->sql_server_version); +			} +		} + +		if ($raw) +		{ +			return $this->sql_server_version; +		} + +		return ($this->sql_server_version) ? 'MSSQL (ODBC)<br />' . $this->sql_server_version : 'MSSQL (ODBC)'; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @odbc_exec($this->db_connect_id, 'BEGIN TRANSACTION'); +			break; + +			case 'commit': +				return @odbc_exec($this->db_connect_id, 'COMMIT TRANSACTION'); +			break; + +			case 'rollback': +				return @odbc_exec($this->db_connect_id, 'ROLLBACK TRANSACTION'); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->last_query_text = $query; +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @odbc_exec($this->db_connect_id, $query)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows) +		if ($total) +		{ +			// We need to grab the total number of rows + the offset number of rows to get the correct result +			if (strpos($query, 'SELECT DISTINCT') === 0) +			{ +				$query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15); +			} +			else +			{ +				$query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6); +			} +		} + +		$result = $this->sql_query($query, $cache_ttl); + +		// Seek by $offset rows +		if ($offset) +		{ +			$this->sql_rowseek($offset, $result); +		} + +		return $result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? @odbc_num_rows($this->query_result) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		return ($query_id !== false) ? @odbc_fetch_array($query_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		$result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY'); + +		if ($result_id) +		{ +			if (@odbc_fetch_array($result_id)) +			{ +				$id = @odbc_result($result_id, 1); +				@odbc_free_result($result_id); +				return $id; +			} +			@odbc_free_result($result_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @odbc_free_result($query_id); +		} + +		return false; +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if (function_exists('odbc_errormsg')) +		{ +			$error = array( +				'message'	=> @odbc_errormsg(), +				'code'		=> @odbc_error(), +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @odbc_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @odbc_exec($this->db_connect_id, $query); +				while ($void = @odbc_fetch_array($result)) +				{ +					// Take the time spent on parsing rows into account +				} +				@odbc_free_result($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/mssqlnative.php b/phpBB/phpbb/db/driver/mssqlnative.php new file mode 100644 index 0000000000..46a9b3a477 --- /dev/null +++ b/phpBB/phpbb/db/driver/mssqlnative.php @@ -0,0 +1,442 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +/** +* This is the MS SQL Server Native database abstraction layer. +* PHP mssql native driver required. +* @author Chris Pucci +* +*/ + +namespace phpbb\db\driver; + +class mssqlnative extends \phpbb\db\driver\mssql_base +{ +	var $m_insert_id = null; +	var $last_query_text = ''; +	var $query_options = array(); +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		// Test for driver support, to avoid suppressed fatal error +		if (!function_exists('sqlsrv_connect')) +		{ +			$this->connect_error = 'Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx'; +			return $this->sql_error(''); +		} + +		//set up connection variables +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->dbname = $database; +		$port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':'; +		$this->server = $sqlserver . (($port) ? $port_delimiter . $port : ''); + +		//connect to database +		$this->db_connect_id = sqlsrv_connect($this->server, array( +			'Database' => $this->dbname, +			'UID' => $this->user, +			'PWD' => $sqlpassword +		)); + +		return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false) +		{ +			$arr_server_info = sqlsrv_server_info($this->db_connect_id); +			$this->sql_server_version = $arr_server_info['SQLServerVersion']; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('mssql_version', $this->sql_server_version); +			} +		} + +		if ($raw) +		{ +			return $this->sql_server_version; +		} + +		return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL'; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_buffer_nested_transactions() +	{ +		return true; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return sqlsrv_begin_transaction($this->db_connect_id); +			break; + +			case 'commit': +				return sqlsrv_commit($this->db_connect_id); +			break; + +			case 'rollback': +				return sqlsrv_rollback($this->db_connect_id); +			break; +		} +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->last_query_text = $query; +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false) +				{ +					$this->sql_error($query); +				} +				// reset options for next query +				$this->query_options = array(); + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// total == 0 means all results - not zero results +		if ($offset == 0 && $total !== 0) +		{ +			if (strpos($query, "SELECT") === false) +			{ +				$query = "TOP {$total} " . $query; +			} +			else +			{ +				$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query); +			} +		} +		else if ($offset > 0) +		{ +			$query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query); +			$query = 'SELECT * +					FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3 +					FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3'; + +			if ($total > 0) +			{ +				$query .= ' WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total); +			} +			else +			{ +				$query .= ' WHERE line3 > ' . $offset; +			} +		} + +		$result = $this->sql_query($query, $cache_ttl); + +		return $result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->query_result) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		if ($query_id === false) +		{ +			return false; +		} + +		$row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC); + +		if ($row) +		{ +			foreach ($row as $key => $value) +			{ +				$row[$key] = ($value === ' ' || $value === null) ? '' : $value; +			} + +			// remove helper values from LIMIT queries +			if (isset($row['line2'])) +			{ +				unset($row['line2'], $row['line3']); +			} +		} +		return (sizeof($row)) ? $row : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		$result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY'); + +		if ($result_id !== false) +		{ +			$row = @sqlsrv_fetch_array($result_id); +			$id = $row[0]; +			@sqlsrv_free_stmt($result_id); +			return $id; +		} +		else +		{ +			return false; +		} +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @sqlsrv_free_stmt($query_id); +		} + +		return false; +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if (function_exists('sqlsrv_errors')) +		{ +			$errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS); +			$error_message = ''; +			$code = 0; + +			if ($errors != null) +			{ +				foreach ($errors as $error) +				{ +					$error_message .= "SQLSTATE: " . $error['SQLSTATE'] . "\n"; +					$error_message .= "code: " . $error['code'] . "\n"; +					$code = $error['code']; +					$error_message .= "message: " . $error['message'] . "\n"; +				} +				$this->last_error_result = $error_message; +				$error = $this->last_error_result; +			} +			else +			{ +				$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array(); +			} + +			$error = array( +				'message'	=> $error, +				'code'		=> $code, +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @sqlsrv_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': +				$html_table = false; +				@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;'); +				if ($result = @sqlsrv_query($this->db_connect_id, $query)) +				{ +					@sqlsrv_next_result($result); +					while ($row = @sqlsrv_fetch_array($result)) +					{ +						$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +					} +				} +				@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;'); +				@sqlsrv_free_stmt($result); + +				if ($html_table) +				{ +					$this->html_hold .= '</table>'; +				} +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @sqlsrv_query($this->db_connect_id, $query); +				while ($void = @sqlsrv_fetch_array($result)) +				{ +					// Take the time spent on parsing rows into account +				} +				@sqlsrv_free_stmt($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} + +	/** +	* Utility method used to retrieve number of rows +	* Emulates mysql_num_rows +	* Used in acp_database.php -> write_data_mssqlnative() +	* Requires a static or keyset cursor to be definde via +	* mssqlnative_set_query_options() +	*/ +	function mssqlnative_num_rows($res) +	{ +		if ($res !== false) +		{ +			return sqlsrv_num_rows($res); +		} +		else +		{ +			return false; +		} +	} + +	/** +	* Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter. +	*/ +	function mssqlnative_set_query_options($options) +	{ +		$this->query_options = $options; +	} +} diff --git a/phpBB/phpbb/db/driver/mysql.php b/phpBB/phpbb/db/driver/mysql.php new file mode 100644 index 0000000000..e93c7239e8 --- /dev/null +++ b/phpBB/phpbb/db/driver/mysql.php @@ -0,0 +1,485 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* MySQL4 Database Abstraction Layer +* Compatible with: +* MySQL 3.23+ +* MySQL 4.0+ +* MySQL 4.1+ +* MySQL 5.0+ +*/ +class mysql extends \phpbb\db\driver\mysql_base +{ +	var $multi_insert = true; +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->server = $sqlserver . (($port) ? ':' . $port : ''); +		$this->dbname = $database; + +		$this->sql_layer = 'mysql4'; + +		if ($this->persistency) +		{ +			if (!function_exists('mysql_pconnect')) +			{ +				$this->connect_error = 'mysql_pconnect function does not exist, is mysql extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @mysql_pconnect($this->server, $this->user, $sqlpassword); +		} +		else +		{ +			if (!function_exists('mysql_connect')) +			{ +				$this->connect_error = 'mysql_connect function does not exist, is mysql extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @mysql_connect($this->server, $this->user, $sqlpassword, $new_link); +		} + +		if ($this->db_connect_id && $this->dbname != '') +		{ +			if (@mysql_select_db($this->dbname, $this->db_connect_id)) +			{ +				// Determine what version we are using and if it natively supports UNICODE +				if (version_compare($this->sql_server_info(true), '4.1.0', '>=')) +				{ +					@mysql_query("SET NAMES 'utf8'", $this->db_connect_id); + +					// enforce strict mode on databases that support it +					if (version_compare($this->sql_server_info(true), '5.0.2', '>=')) +					{ +						$result = @mysql_query('SELECT @@session.sql_mode AS sql_mode', $this->db_connect_id); +						$row = @mysql_fetch_assoc($result); +						@mysql_free_result($result); +						$modes = array_map('trim', explode(',', $row['sql_mode'])); + +						// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES +						if (!in_array('TRADITIONAL', $modes)) +						{ +							if (!in_array('STRICT_ALL_TABLES', $modes)) +							{ +								$modes[] = 'STRICT_ALL_TABLES'; +							} + +							if (!in_array('STRICT_TRANS_TABLES', $modes)) +							{ +								$modes[] = 'STRICT_TRANS_TABLES'; +							} +						} + +						$mode = implode(',', $modes); +						@mysql_query("SET SESSION sql_mode='{$mode}'", $this->db_connect_id); +					} +				} +				else if (version_compare($this->sql_server_info(true), '4.0.0', '<')) +				{ +					$this->sql_layer = 'mysql'; +				} + +				return $this->db_connect_id; +			} +		} + +		return $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysql_version')) === false) +		{ +			$result = @mysql_query('SELECT VERSION() AS version', $this->db_connect_id); +			$row = @mysql_fetch_assoc($result); +			@mysql_free_result($result); + +			$this->sql_server_version = $row['version']; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('mysql_version', $this->sql_server_version); +			} +		} + +		return ($raw) ? $this->sql_server_version : 'MySQL ' . $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @mysql_query('BEGIN', $this->db_connect_id); +			break; + +			case 'commit': +				return @mysql_query('COMMIT', $this->db_connect_id); +			break; + +			case 'rollback': +				return @mysql_query('ROLLBACK', $this->db_connect_id); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		if ($this->db_connect_id) +		{ +			// We always want the number of matched rows +			// instead of changed rows, when running an update. +			// So when mysql_info() returns the number of matched rows +			// we return that one instead of mysql_affected_rows() +			$mysql_info = @mysql_info($this->db_connect_id); +			if ($mysql_info !== false) +			{ +				$match = array(); +				preg_match('#^Rows matched: (\d)+  Changed: (\d)+  Warnings: (\d)+$#', $mysql_info, $match); +				if (isset($match[1])) +				{ +					return $match[1]; +				} +			} + +			return @mysql_affected_rows($this->db_connect_id); +		} +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		return ($query_id !== false) ? @mysql_fetch_assoc($query_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		return ($query_id !== false) ? @mysql_data_seek($query_id, $rownum) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @mysql_free_result($query_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		if (!$this->db_connect_id) +		{ +			return @mysql_real_escape_string($msg); +		} + +		return @mysql_real_escape_string($msg, $this->db_connect_id); +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if ($this->db_connect_id) +		{ +			$error = array( +				'message'	=> @mysql_error($this->db_connect_id), +				'code'		=> @mysql_errno($this->db_connect_id), +			); +		} +		else if (function_exists('mysql_error')) +		{ +			$error = array( +				'message'	=> @mysql_error(), +				'code'		=> @mysql_errno(), +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @mysql_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		static $test_prof; + +		// current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING +		if ($test_prof === null) +		{ +			$test_prof = false; +			if (version_compare($this->sql_server_info(true), '5.0.37', '>=') && version_compare($this->sql_server_info(true), '5.1', '<')) +			{ +				$test_prof = true; +			} +		} + +		switch ($mode) +		{ +			case 'start': + +				$explain_query = $query; +				if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} +				else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} + +				if (preg_match('/^SELECT/', $explain_query)) +				{ +					$html_table = false; + +					// begin profiling +					if ($test_prof) +					{ +						@mysql_query('SET profiling = 1;', $this->db_connect_id); +					} + +					if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id)) +					{ +						while ($row = @mysql_fetch_assoc($result)) +						{ +							$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +						} +					} +					@mysql_free_result($result); + +					if ($html_table) +					{ +						$this->html_hold .= '</table>'; +					} + +					if ($test_prof) +					{ +						$html_table = false; + +						// get the last profile +						if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id)) +						{ +							$this->html_hold .= '<br />'; +							while ($row = @mysql_fetch_assoc($result)) +							{ +								// make <unknown> HTML safe +								if (!empty($row['Source_function'])) +								{ +									$row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']); +								} + +								// remove unsupported features +								foreach ($row as $key => $val) +								{ +									if ($val === null) +									{ +										unset($row[$key]); +									} +								} +								$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +							} +						} +						@mysql_free_result($result); + +						if ($html_table) +						{ +							$this->html_hold .= '</table>'; +						} + +						@mysql_query('SET profiling = 0;', $this->db_connect_id); +					} +				} + +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @mysql_query($query, $this->db_connect_id); +				while ($void = @mysql_fetch_assoc($result)) +				{ +					// Take the time spent on parsing rows into account +				} +				@mysql_free_result($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/mysql_base.php b/phpBB/phpbb/db/driver/mysql_base.php new file mode 100644 index 0000000000..e7c9b63f20 --- /dev/null +++ b/phpBB/phpbb/db/driver/mysql_base.php @@ -0,0 +1,129 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* Abstract MySQL Database Base Abstraction Layer +*/ +abstract class mysql_base extends \phpbb\db\driver\driver +{ +	/** +	* {@inheritDoc} +	*/ +	public function sql_concatenate($expr1, $expr2) +	{ +		return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')'; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// if $total is set to 0 we do not want to limit the number of rows +		if ($total == 0) +		{ +			// MySQL 4.1+ no longer supports -1 in limit queries +			$total = '18446744073709551615'; +		} + +		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	function get_estimated_row_count($table_name) +	{ +		$table_status = $this->get_table_status($table_name); + +		if (isset($table_status['Engine'])) +		{ +			if ($table_status['Engine'] === 'MyISAM') +			{ +				return $table_status['Rows']; +			} +			else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000) +			{ +				return '~' . $table_status['Rows']; +			} +		} + +		return parent::get_row_count($table_name); +	} + +	/** +	* {@inheritDoc} +	*/ +	function get_row_count($table_name) +	{ +		$table_status = $this->get_table_status($table_name); + +		if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM') +		{ +			return $table_status['Rows']; +		} + +		return parent::get_row_count($table_name); +	} + +	/** +	* Gets some information about the specified table. +	* +	* @param string $table_name		Table name +	* +	* @return array +	* +	* @access protected +	*/ +	function get_table_status($table_name) +	{ +		$sql = "SHOW TABLE STATUS +			LIKE '" . $this->sql_escape($table_name) . "'"; +		$result = $this->sql_query($sql); +		$table_status = $this->sql_fetchrow($result); +		$this->sql_freeresult($result); + +		return $table_status; +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		switch ($stage) +		{ +			case 'FROM': +				$data = '(' . $data . ')'; +			break; +		} + +		return $data; +	} +} diff --git a/phpBB/phpbb/db/driver/mysqli.php b/phpBB/phpbb/db/driver/mysqli.php new file mode 100644 index 0000000000..8fc306b2cc --- /dev/null +++ b/phpBB/phpbb/db/driver/mysqli.php @@ -0,0 +1,472 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* MySQLi Database Abstraction Layer +* mysqli-extension has to be compiled with: +* MySQL 4.1+ or MySQL 5.0+ +*/ +class mysqli extends \phpbb\db\driver\mysql_base +{ +	var $multi_insert = true; +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		if (!function_exists('mysqli_connect')) +		{ +			$this->connect_error = 'mysqli_connect function does not exist, is mysqli extension installed?'; +			return $this->sql_error(''); +		} + +		// Mysqli extension supports persistent connection since PHP 5.3.0 +		$this->persistency = (version_compare(PHP_VERSION, '5.3.0', '>=')) ? $persistency : false; +		$this->user = $sqluser; + +		// If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix +		$this->server = ($this->persistency) ? 'p:' . (($sqlserver) ? $sqlserver : 'localhost') : $sqlserver; + +		$this->dbname = $database; +		$port = (!$port) ? null : $port; + +		// If port is set and it is not numeric, most likely mysqli socket is set. +		// Try to map it to the $socket parameter. +		$socket = null; +		if ($port) +		{ +			if (is_numeric($port)) +			{ +				$port = (int) $port; +			} +			else +			{ +				$socket = $port; +				$port = null; +			} +		} + +		$this->db_connect_id = mysqli_init(); +		@mysqli_real_connect($this->db_connect_id, $this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket, MYSQLI_CLIENT_FOUND_ROWS); + +		if ($this->db_connect_id && $this->dbname != '') +		{ +			@mysqli_query($this->db_connect_id, "SET NAMES 'utf8'"); + +			// enforce strict mode on databases that support it +			if (version_compare($this->sql_server_info(true), '5.0.2', '>=')) +			{ +				$result = @mysqli_query($this->db_connect_id, 'SELECT @@session.sql_mode AS sql_mode'); +				if ($result !== null) +				{ +					$row = @mysqli_fetch_assoc($result); + +					$modes = array_map('trim', explode(',', $row['sql_mode'])); +				} +				else +				{ +					$modes = array(); +				} +				@mysqli_free_result($result); + +				// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES +				if (!in_array('TRADITIONAL', $modes)) +				{ +					if (!in_array('STRICT_ALL_TABLES', $modes)) +					{ +						$modes[] = 'STRICT_ALL_TABLES'; +					} + +					if (!in_array('STRICT_TRANS_TABLES', $modes)) +					{ +						$modes[] = 'STRICT_TRANS_TABLES'; +					} +				} + +				$mode = implode(',', $modes); +				@mysqli_query($this->db_connect_id, "SET SESSION sql_mode='{$mode}'"); +			} +			return $this->db_connect_id; +		} + +		return $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysqli_version')) === false) +		{ +			$result = @mysqli_query($this->db_connect_id, 'SELECT VERSION() AS version'); +			if ($result !== null) +			{ +				$row = @mysqli_fetch_assoc($result); + +				$this->sql_server_version = $row['version']; + +				if (!empty($cache) && $use_cache) +				{ +					$cache->put('mysqli_version', $this->sql_server_version); +				} +			} +			@mysqli_free_result($result); +		} + +		return ($raw) ? $this->sql_server_version : 'MySQL(i) ' . $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @mysqli_autocommit($this->db_connect_id, false); +			break; + +			case 'commit': +				$result = @mysqli_commit($this->db_connect_id); +				@mysqli_autocommit($this->db_connect_id, true); +				return $result; +			break; + +			case 'rollback': +				$result = @mysqli_rollback($this->db_connect_id); +				@mysqli_autocommit($this->db_connect_id, true); +				return $result; +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		if ($query_id !== false && $query_id !== null) +		{ +			$result = @mysqli_fetch_assoc($query_id); +			return $result !== null ? $result : false; +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		return ($query_id !== false) ? @mysqli_data_seek($query_id, $rownum) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		return @mysqli_free_result($query_id); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return @mysqli_real_escape_string($this->db_connect_id, $msg); +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if ($this->db_connect_id) +		{ +			$error = array( +				'message'	=> @mysqli_error($this->db_connect_id), +				'code'		=> @mysqli_errno($this->db_connect_id) +			); +		} +		else if (function_exists('mysqli_connect_error')) +		{ +			$error = array( +				'message'	=> @mysqli_connect_error(), +				'code'		=> @mysqli_connect_errno(), +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @mysqli_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		static $test_prof; + +		// current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING +		if ($test_prof === null) +		{ +			$test_prof = false; +			if (strpos(mysqli_get_server_info($this->db_connect_id), 'community') !== false) +			{ +				$ver = mysqli_get_server_version($this->db_connect_id); +				if ($ver >= 50037 && $ver < 50100) +				{ +					$test_prof = true; +				} +			} +		} + +		switch ($mode) +		{ +			case 'start': + +				$explain_query = $query; +				if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} +				else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} + +				if (preg_match('/^SELECT/', $explain_query)) +				{ +					$html_table = false; + +					// begin profiling +					if ($test_prof) +					{ +						@mysqli_query($this->db_connect_id, 'SET profiling = 1;'); +					} + +					if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query")) +					{ +						while ($row = @mysqli_fetch_assoc($result)) +						{ +							$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +						} +					} +					@mysqli_free_result($result); + +					if ($html_table) +					{ +						$this->html_hold .= '</table>'; +					} + +					if ($test_prof) +					{ +						$html_table = false; + +						// get the last profile +						if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;')) +						{ +							$this->html_hold .= '<br />'; +							while ($row = @mysqli_fetch_assoc($result)) +							{ +								// make <unknown> HTML safe +								if (!empty($row['Source_function'])) +								{ +									$row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']); +								} + +								// remove unsupported features +								foreach ($row as $key => $val) +								{ +									if ($val === null) +									{ +										unset($row[$key]); +									} +								} +								$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +							} +						} +						@mysqli_free_result($result); + +						if ($html_table) +						{ +							$this->html_hold .= '</table>'; +						} + +						@mysqli_query($this->db_connect_id, 'SET profiling = 0;'); +					} +				} + +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @mysqli_query($this->db_connect_id, $query); +				if ($result !== null) +				{ +					while ($void = @mysqli_fetch_assoc($result)) +					{ +						// Take the time spent on parsing rows into account +					} +				} +				@mysqli_free_result($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/oracle.php b/phpBB/phpbb/db/driver/oracle.php new file mode 100644 index 0000000000..d1a186f1ba --- /dev/null +++ b/phpBB/phpbb/db/driver/oracle.php @@ -0,0 +1,798 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* Oracle Database Abstraction Layer +*/ +class oracle extends \phpbb\db\driver\driver +{ +	var $last_query_text = ''; +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->server = $sqlserver . (($port) ? ':' . $port : ''); +		$this->dbname = $database; + +		$connect = $database; + +		// support for "easy connect naming" +		if ($sqlserver !== '' && $sqlserver !== '/') +		{ +			if (substr($sqlserver, -1, 1) == '/') +			{ +				$sqlserver == substr($sqlserver, 0, -1); +			} +			$connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database; +		} + +		if ($new_link) +		{ +			if (!function_exists('ocinlogon')) +			{ +				$this->connect_error = 'ocinlogon function does not exist, is oci extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8'); +		} +		else if ($this->persistency) +		{ +			if (!function_exists('ociplogon')) +			{ +				$this->connect_error = 'ociplogon function does not exist, is oci extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @ociplogon($this->user, $sqlpassword, $connect, 'UTF8'); +		} +		else +		{ +			if (!function_exists('ocilogon')) +			{ +				$this->connect_error = 'ocilogon function does not exist, is oci extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @ocilogon($this->user, $sqlpassword, $connect, 'UTF8'); +		} + +		return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		/** +		* force $use_cache false.  I didn't research why the caching code below is commented out +		* but I assume its because the Oracle extension provides a direct method to access it +		* without a query. +		*/ + +		$use_cache = false; +/* +		global $cache; + +		if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false) +		{ +			$result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\''); +			@ociexecute($result, OCI_DEFAULT); +			@ocicommit($this->db_connect_id); + +			$row = array(); +			@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS); +			@ocifreestatement($result); +			$this->sql_server_version = trim($row['BANNER']); + +			$cache->put('oracle_version', $this->sql_server_version); +		} +*/ +		$this->sql_server_version = @ociserverversion($this->db_connect_id); + +		return $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return true; +			break; + +			case 'commit': +				return @ocicommit($this->db_connect_id); +			break; + +			case 'rollback': +				return @ocirollback($this->db_connect_id); +			break; +		} + +		return true; +	} + +	/** +	* Oracle specific code to handle the fact that it does not compare columns properly +	* @access private +	*/ +	function _rewrite_col_compare($args) +	{ +		if (sizeof($args) == 4) +		{ +			if ($args[2] == '=') +			{ +				return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))'; +			} +			else if ($args[2] == '<>') +			{ +				// really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P +				return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))'; +			} +		} +		else +		{ +			return $this->_rewrite_where($args[0]); +		} +	} + +	/** +	* Oracle specific code to handle it's lack of sanity +	* @access private +	*/ +	function _rewrite_where($where_clause) +	{ +		preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER); +		$out = ''; +		foreach ($result as $val) +		{ +			if (!isset($val[5])) +			{ +				if ($val[4] !== "''") +				{ +					$out .= $val[0]; +				} +				else +				{ +					$out .= ' ' . $val[1] . ' ' . $val[2]; +					if ($val[3] == '=') +					{ +						$out .= ' is NULL'; +					} +					else if ($val[3] == '<>') +					{ +						$out .= ' is NOT NULL'; +					} +				} +			} +			else +			{ +				$in_clause = array(); +				$sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1); +				$extra = false; +				preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER); +				$i = 0; +				foreach ($sub_vals[0] as $sub_val) +				{ +					// two things: +					// 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison +					// 2) This fixes the 1000 list limit that Oracle has (ORA-01795) +					if ($sub_val !== "''") +					{ +						$in_clause[(int) $i++/1000][] = $sub_val; +					} +					else +					{ +						$extra = true; +					} +				} +				if (!$extra && $i < 1000) +				{ +					$out .= $val[0]; +				} +				else +				{ +					$out .= ' ' . $val[1] . '('; +					$in_array = array(); + +					// constuct each IN() clause +					foreach ($in_clause as $in_values) +					{ +						$in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')'; +					} + +					// Join the IN() clauses against a few ORs (IN is just a nicer OR anyway) +					$out .= implode(' OR ', $in_array); + +					// handle the empty string case +					if ($extra) +					{ +						$out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL'; +					} +					$out .= ')'; + +					unset($in_array, $in_clause); +				} +			} +		} + +		return $out; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->last_query_text = $query; +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				$in_transaction = false; +				if (!$this->transaction) +				{ +					$this->sql_transaction('begin'); +				} +				else +				{ +					$in_transaction = true; +				} + +				$array = array(); + +				// We overcome Oracle's 4000 char limit by binding vars +				if (strlen($query) > 4000) +				{ +					if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs)) +					{ +						if (strlen($regs[3]) > 4000) +						{ +							$cols = explode(', ', $regs[2]); + +							preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER); + +/*						The code inside this comment block breaks clob handling, but does allow the +						database restore script to work.  If you want to allow no posts longer than 4KB +						and/or need the db restore script, uncomment this. + + +							if (sizeof($cols) !== sizeof($vals)) +							{ +								// Try to replace some common data we know is from our restore script or from other sources +								$regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]); +								$_vals = explode(', ', $regs[3]); + +								$vals = array(); +								$is_in_val = false; +								$i = 0; +								$string = ''; + +								foreach ($_vals as $value) +								{ +									if (strpos($value, "'") === false && !$is_in_val) +									{ +										$vals[$i++] = $value; +										continue; +									} + +									if (substr($value, -1) === "'") +									{ +										$vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value; +										$string = ''; +										$is_in_val = false; + +										if ($vals[$i][0] !== "'") +										{ +											$vals[$i] = "''" . $vals[$i]; +										} +										$i++; +										continue; +									} +									else +									{ +										$string .= (($is_in_val) ? ', ' : '') . $value; +										$is_in_val = true; +									} +								} + +								if ($string) +								{ +									// New value if cols != value +									$vals[(sizeof($cols) !== sizeof($vals)) ? $i : $i - 1] .= $string; +								} + +								$vals = array(0 => $vals); +							} +*/ + +							$inserts = $vals[0]; +							unset($vals); + +							foreach ($inserts as $key => $value) +							{ +								if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2 +								{ +									$inserts[$key] = ':' . strtoupper($cols[$key]); +									$array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1)); +								} +							} + +							$query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')'; +						} +					} +					else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER)) +					{ +						if (strlen($data[0][2]) > 4000) +						{ +							$update = $data[0][1]; +							$where = $data[0][3]; +							preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER); +							unset($data); + +							$cols = array(); +							foreach ($temp as $value) +							{ +								if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2 +								{ +									$cols[] = $value[1] . '=:' . strtoupper($value[1]); +									$array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1)); +								} +								else +								{ +									$cols[] = $value[1] . '=' . $value[2]; +								} +							} + +							$query = $update . implode(', ', $cols) . ' ' . $where; +							unset($cols); +						} +					} +				} + +				switch (substr($query, 0, 6)) +				{ +					case 'DELETE': +						if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs)) +						{ +							$query = $regs[1] . $this->_rewrite_where($regs[2]); +							unset($regs); +						} +					break; + +					case 'UPDATE': +						if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s',  $query, $regs)) +						{ +							$query = $regs[1] . $this->_rewrite_where($regs[2]); +							unset($regs); +						} +					break; + +					case 'SELECT': +						$query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query); +					break; +				} + +				$this->query_result = @ociparse($this->db_connect_id, $query); + +				foreach ($array as $key => $value) +				{ +					@ocibindbyname($this->query_result, $key, $array[$key], -1); +				} + +				$success = @ociexecute($this->query_result, OCI_DEFAULT); + +				if (!$success) +				{ +					$this->sql_error($query); +					$this->query_result = false; +				} +				else +				{ +					if (!$in_transaction) +					{ +						$this->sql_transaction('commit'); +					} +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		$query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset; + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->query_result) ? @ocirowcount($this->query_result) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		if ($query_id !== false) +		{ +			$row = array(); +			$result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS); + +			if (!$result || !$row) +			{ +				return false; +			} + +			$result_row = array(); +			foreach ($row as $key => $value) +			{ +				// Oracle treats empty strings as null +				if (is_null($value)) +				{ +					$value = ''; +				} + +				// OCI->CLOB? +				if (is_object($value)) +				{ +					$value = $value->load(); +				} + +				$result_row[strtolower($key)] = $value; +			} + +			return $result_row; +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		if ($query_id === false) +		{ +			return false; +		} + +		// Reset internal pointer +		@ociexecute($query_id, OCI_DEFAULT); + +		// We do not fetch the row for rownum == 0 because then the next resultset would be the second row +		for ($i = 0; $i < $rownum; $i++) +		{ +			if (!$this->sql_fetchrow($query_id)) +			{ +				return false; +			} +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		$query_id = $this->query_result; + +		if ($query_id !== false && $this->last_query_text != '') +		{ +			if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename)) +			{ +				$query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL'; +				$stmt = @ociparse($this->db_connect_id, $query); +				@ociexecute($stmt, OCI_DEFAULT); + +				$temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS); +				@ocifreestatement($stmt); + +				if ($temp_result) +				{ +					return $temp_array['CURRVAL']; +				} +				else +				{ +					return false; +				} +			} +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @ocifreestatement($query_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return str_replace(array("'", "\0"), array("''", ''), $msg); +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression . " ESCAPE '\\'"; +	} + +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} + +	function _sql_bit_and($column_name, $bit, $compare = '') +	{ +		return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); +	} + +	function _sql_bit_or($column_name, $bit, $compare = '') +	{ +		return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : ''); +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if (function_exists('ocierror')) +		{ +			$error = @ocierror(); +			$error = (!$error) ? @ocierror($this->query_result) : $error; +			$error = (!$error) ? @ocierror($this->db_connect_id) : $error; + +			if ($error) +			{ +				$this->last_error_result = $error; +			} +			else +			{ +				$error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array(); +			} +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @ocilogoff($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': + +				$html_table = false; + +				// Grab a plan table, any will do +				$sql = "SELECT table_name +					FROM USER_TABLES +					WHERE table_name LIKE '%PLAN_TABLE%'"; +				$stmt = ociparse($this->db_connect_id, $sql); +				ociexecute($stmt); +				$result = array(); + +				if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS)) +				{ +					$table = $result['TABLE_NAME']; + +					// This is the statement_id that will allow us to track the plan +					$statement_id = substr(md5($query), 0, 30); + +					// Remove any stale plans +					$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'"); +					ociexecute($stmt2); +					ocifreestatement($stmt2); + +					// Explain the plan +					$sql = "EXPLAIN PLAN +						SET STATEMENT_ID = '$statement_id' +						FOR $query"; +					$stmt2 = ociparse($this->db_connect_id, $sql); +					ociexecute($stmt2); +					ocifreestatement($stmt2); + +					// Get the data from the plan +					$sql = "SELECT operation, options, object_name, object_type, cardinality, cost +						FROM plan_table +						START WITH id = 0 AND statement_id = '$statement_id' +						CONNECT BY PRIOR id = parent_id +							AND statement_id = '$statement_id'"; +					$stmt2 = ociparse($this->db_connect_id, $sql); +					ociexecute($stmt2); + +					$row = array(); +					while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS)) +					{ +						$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +					} + +					ocifreestatement($stmt2); + +					// Remove the plan we just made, we delete them on request anyway +					$stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'"); +					ociexecute($stmt2); +					ocifreestatement($stmt2); +				} + +				ocifreestatement($stmt); + +				if ($html_table) +				{ +					$this->html_hold .= '</table>'; +				} + +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @ociparse($this->db_connect_id, $query); +				$success = @ociexecute($result, OCI_DEFAULT); +				$row = array(); + +				while (@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS)) +				{ +					// Take the time spent on parsing rows into account +				} +				@ocifreestatement($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/postgres.php b/phpBB/phpbb/db/driver/postgres.php new file mode 100644 index 0000000000..a67cd9f7c2 --- /dev/null +++ b/phpBB/phpbb/db/driver/postgres.php @@ -0,0 +1,485 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* PostgreSQL Database Abstraction Layer +* Minimum Requirement is Version 7.3+ +*/ +class postgres extends \phpbb\db\driver\driver +{ +	var $last_query_text = ''; +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$connect_string = ''; + +		if ($sqluser) +		{ +			$connect_string .= "user=$sqluser "; +		} + +		if ($sqlpassword) +		{ +			$connect_string .= "password=$sqlpassword "; +		} + +		if ($sqlserver) +		{ +			// $sqlserver can carry a port separated by : for compatibility reasons +			// If $sqlserver has more than one : it's probably an IPv6 address. +			// In this case we only allow passing a port via the $port variable. +			if (substr_count($sqlserver, ':') === 1) +			{ +				list($sqlserver, $port) = explode(':', $sqlserver); +			} + +			if ($sqlserver !== 'localhost') +			{ +				$connect_string .= "host=$sqlserver "; +			} + +			if ($port) +			{ +				$connect_string .= "port=$port "; +			} +		} + +		$schema = ''; + +		if ($database) +		{ +			$this->dbname = $database; +			if (strpos($database, '.') !== false) +			{ +				list($database, $schema) = explode('.', $database); +			} +			$connect_string .= "dbname=$database"; +		} + +		$this->persistency = $persistency; + +		if ($this->persistency) +		{ +			if (!function_exists('pg_pconnect')) +			{ +				$this->connect_error = 'pg_pconnect function does not exist, is pgsql extension installed?'; +				return $this->sql_error(''); +			} +			$collector = new \phpbb\error_collector; +			$collector->install(); +			$this->db_connect_id = (!$new_link) ? @pg_pconnect($connect_string) : @pg_pconnect($connect_string, PGSQL_CONNECT_FORCE_NEW); +		} +		else +		{ +			if (!function_exists('pg_connect')) +			{ +				$this->connect_error = 'pg_connect function does not exist, is pgsql extension installed?'; +				return $this->sql_error(''); +			} +			$collector = new \phpbb\error_collector; +			$collector->install(); +			$this->db_connect_id = (!$new_link) ? @pg_connect($connect_string) : @pg_connect($connect_string, PGSQL_CONNECT_FORCE_NEW); +		} + +		$collector->uninstall(); + +		if ($this->db_connect_id) +		{ +			if (version_compare($this->sql_server_info(true), '8.2', '>=')) +			{ +				$this->multi_insert = true; +			} + +			if ($schema !== '') +			{ +				@pg_query($this->db_connect_id, 'SET search_path TO ' . $schema); +			} +			return $this->db_connect_id; +		} + +		$this->connect_error = $collector->format_errors(); +		return $this->sql_error(''); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('pgsql_version')) === false) +		{ +			$query_id = @pg_query($this->db_connect_id, 'SELECT VERSION() AS version'); +			$row = @pg_fetch_assoc($query_id, null); +			@pg_free_result($query_id); + +			$this->sql_server_version = (!empty($row['version'])) ? trim(substr($row['version'], 10)) : 0; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('pgsql_version', $this->sql_server_version); +			} +		} + +		return ($raw) ? $this->sql_server_version : 'PostgreSQL ' . $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @pg_query($this->db_connect_id, 'BEGIN'); +			break; + +			case 'commit': +				return @pg_query($this->db_connect_id, 'COMMIT'); +			break; + +			case 'rollback': +				return @pg_query($this->db_connect_id, 'ROLLBACK'); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->last_query_text = $query; +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @pg_query($this->db_connect_id, $query)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// if $total is set to 0 we do not want to limit the number of rows +		if ($total == 0) +		{ +			$total = 'ALL'; +		} + +		$query .= "\n LIMIT $total OFFSET $offset"; + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->query_result) ? @pg_affected_rows($this->query_result) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		return ($query_id !== false) ? @pg_fetch_assoc($query_id, null) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		return ($query_id !== false) ? @pg_result_seek($query_id, $rownum) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		$query_id = $this->query_result; + +		if ($query_id !== false && $this->last_query_text != '') +		{ +			if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename)) +			{ +				$query = "SELECT currval('" . $tablename[1] . "_seq') AS last_value"; +				$temp_q_id = @pg_query($this->db_connect_id, $query); + +				if (!$temp_q_id) +				{ +					return false; +				} + +				$temp_result = @pg_fetch_assoc($temp_q_id, null); +				@pg_free_result($query_id); + +				return ($temp_result) ? $temp_result['last_value'] : false; +			} +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if (isset($this->open_queries[(int) $query_id])) +		{ +			unset($this->open_queries[(int) $query_id]); +			return @pg_free_result($query_id); +		} + +		return false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return @pg_escape_string($msg); +	} + +	/** +	* Build LIKE expression +	* @access private +	*/ +	function _sql_like_expression($expression) +	{ +		return $expression; +	} + +	/** +	* {@inheritDoc} +	*/ +	function cast_expr_to_bigint($expression) +	{ +		return 'CAST(' . $expression . ' as DECIMAL(255, 0))'; +	} + +	/** +	* {@inheritDoc} +	*/ +	function cast_expr_to_string($expression) +	{ +		return 'CAST(' . $expression . ' as VARCHAR(255))'; +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		// pg_last_error only works when there is an established connection. +		// Connection errors have to be tracked by us manually. +		if ($this->db_connect_id) +		{ +			$message = @pg_last_error($this->db_connect_id); +		} +		else +		{ +			$message = $this->connect_error; +		} + +		return array( +			'message'	=> $message, +			'code'		=> '' +		); +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @pg_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': + +				$explain_query = $query; +				if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} +				else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} + +				if (preg_match('/^SELECT/', $explain_query)) +				{ +					$html_table = false; + +					if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query")) +					{ +						while ($row = @pg_fetch_assoc($result, null)) +						{ +							$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +						} +					} +					@pg_free_result($result); + +					if ($html_table) +					{ +						$this->html_hold .= '</table>'; +					} +				} + +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @pg_query($this->db_connect_id, $query); +				while ($void = @pg_fetch_assoc($result, null)) +				{ +					// Take the time spent on parsing rows into account +				} +				@pg_free_result($result); + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php new file mode 100644 index 0000000000..2112e5ba2f --- /dev/null +++ b/phpBB/phpbb/db/driver/sqlite.php @@ -0,0 +1,361 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* Sqlite Database Abstraction Layer +* Minimum Requirement: 2.8.2+ +*/ +class sqlite extends \phpbb\db\driver\driver +{ +	var $connect_error = ''; + +	/** +	* {@inheritDoc} +	*/ +	function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$this->persistency = $persistency; +		$this->user = $sqluser; +		$this->server = $sqlserver . (($port) ? ':' . $port : ''); +		$this->dbname = $database; + +		$error = ''; +		if ($this->persistency) +		{ +			if (!function_exists('sqlite_popen')) +			{ +				$this->connect_error = 'sqlite_popen function does not exist, is sqlite extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @sqlite_popen($this->server, 0666, $error); +		} +		else +		{ +			if (!function_exists('sqlite_open')) +			{ +				$this->connect_error = 'sqlite_open function does not exist, is sqlite extension installed?'; +				return $this->sql_error(''); +			} +			$this->db_connect_id = @sqlite_open($this->server, 0666, $error); +		} + +		if ($this->db_connect_id) +		{ +			@sqlite_query('PRAGMA short_column_names = 1', $this->db_connect_id); +//			@sqlite_query('PRAGMA encoding = "UTF-8"', $this->db_connect_id); +		} + +		return ($this->db_connect_id) ? true : array('message' => $error); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false) +		{ +			$result = @sqlite_query('SELECT sqlite_version() AS version', $this->db_connect_id); +			$row = @sqlite_fetch_array($result, SQLITE_ASSOC); + +			$this->sql_server_version = (!empty($row['version'])) ? $row['version'] : 0; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('sqlite_version', $this->sql_server_version); +			} +		} + +		return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* @access private +	*/ +	function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return @sqlite_query('BEGIN', $this->db_connect_id); +			break; + +			case 'commit': +				return @sqlite_query('COMMIT', $this->db_connect_id); +			break; + +			case 'rollback': +				return @sqlite_query('ROLLBACK', $this->db_connect_id); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @sqlite_query($query, $this->db_connect_id)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +				else if (strpos($query, 'SELECT') === 0 && $this->query_result) +				{ +					$this->open_queries[(int) $this->query_result] = $this->query_result; +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	*/ +	function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// if $total is set to 0 we do not want to limit the number of rows +		if ($total == 0) +		{ +			$total = -1; +		} + +		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? @sqlite_changes($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		return ($query_id !== false) ? @sqlite_fetch_array($query_id, SQLITE_ASSOC) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_rowseek($rownum, &$query_id) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_rowseek($rownum, $query_id); +		} + +		return ($query_id !== false) ? @sqlite_seek($query_id, $rownum) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_nextid() +	{ +		return ($this->db_connect_id) ? @sqlite_last_insert_rowid($this->db_connect_id) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	function sql_escape($msg) +	{ +		return @sqlite_escape_string($msg); +	} + +	/** +	* {@inheritDoc} +	* +	* For SQLite an underscore is a not-known character... this may change with SQLite3 +	*/ +	function sql_like_expression($expression) +	{ +		// Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! +		// We only catch * and ? here, not the character map possible on file globbing. +		$expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); + +		$expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); +		$expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); + +		return 'GLOB \'' . $this->sql_escape($expression) . '\''; +	} + +	/** +	* return sql error array +	* @access private +	*/ +	function _sql_error() +	{ +		if (function_exists('sqlite_error_string')) +		{ +			$error = array( +				'message'	=> @sqlite_error_string(@sqlite_last_error($this->db_connect_id)), +				'code'		=> @sqlite_last_error($this->db_connect_id), +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Build db-specific query data +	* @access private +	*/ +	function _sql_custom_build($stage, $data) +	{ +		return $data; +	} + +	/** +	* Close sql connection +	* @access private +	*/ +	function _sql_close() +	{ +		return @sqlite_close($this->db_connect_id); +	} + +	/** +	* Build db-specific report +	* @access private +	*/ +	function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = @sqlite_query($query, $this->db_connect_id); +				while ($void = @sqlite_fetch_array($result, SQLITE_ASSOC)) +				{ +					// Take the time spent on parsing rows into account +				} + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php new file mode 100644 index 0000000000..6511c755a0 --- /dev/null +++ b/phpBB/phpbb/db/driver/sqlite3.php @@ -0,0 +1,386 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\driver; + +/** +* SQLite3 Database Abstraction Layer +* Minimum Requirement: 3.6.15+ +*/ +class sqlite3 extends \phpbb\db\driver\driver +{ +	/** +	* @var	string		Stores errors during connection setup in case the driver is not available +	*/ +	protected $connect_error = ''; + +	/** +	* @var	\SQLite3	The SQLite3 database object to operate against +	*/ +	protected $dbo = null; + +	/** +	* {@inheritDoc} +	*/ +	public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) +	{ +		$this->persistency = false; +		$this->user = $sqluser; +		$this->server = $sqlserver . (($port) ? ':' . $port : ''); +		$this->dbname = $database; + +		if (!class_exists('SQLite3', false)) +		{ +			$this->connect_error = 'SQLite3 not found, is the extension installed?'; +			return $this->sql_error(''); +		} + +		try +		{ +			$this->dbo = new \SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); +			$this->db_connect_id = true; +		} +		catch (Exception $e) +		{ +			return array('message' => $e->getMessage()); +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_server_info($raw = false, $use_cache = true) +	{ +		global $cache; + +		if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false) +		{ +			$version = \SQLite3::version(); + +			$this->sql_server_version = $version['versionString']; + +			if (!empty($cache) && $use_cache) +			{ +				$cache->put('sqlite_version', $this->sql_server_version); +			} +		} + +		return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version; +	} + +	/** +	* SQL Transaction +	* +	* @param	string	$status		Should be one of the following strings: +	*								begin, commit, rollback +	* @return	bool	Success/failure of the transaction query +	*/ +	protected function _sql_transaction($status = 'begin') +	{ +		switch ($status) +		{ +			case 'begin': +				return $this->dbo->exec('BEGIN IMMEDIATE'); +			break; + +			case 'commit': +				return $this->dbo->exec('COMMIT'); +			break; + +			case 'rollback': +				return $this->dbo->exec('ROLLBACK'); +			break; +		} + +		return true; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_query($query = '', $cache_ttl = 0) +	{ +		if ($query != '') +		{ +			global $cache; + +			// EXPLAIN only in extra debug mode +			if (defined('DEBUG')) +			{ +				$this->sql_report('start', $query); +			} +			else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +			{ +				$this->curtime = microtime(true); +			} + +			$this->last_query_text = $query; +			$this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; +			$this->sql_add_num_queries($this->query_result); + +			if ($this->query_result === false) +			{ +				if (($this->query_result = @$this->dbo->query($query)) === false) +				{ +					$this->sql_error($query); +				} + +				if (defined('DEBUG')) +				{ +					$this->sql_report('stop', $query); +				} +				else if (defined('PHPBB_DISPLAY_LOAD_TIME')) +				{ +					$this->sql_time += microtime(true) - $this->curtime; +				} + +				if ($cache && $cache_ttl) +				{ +					$this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); +				} +			} +			else if (defined('DEBUG')) +			{ +				$this->sql_report('fromcache', $query); +			} +		} +		else +		{ +			return false; +		} + +		return $this->query_result; +	} + +	/** +	* Build LIMIT query +	* +	* @param	string	$query		The SQL query to execute +	* @param	int		$total		The number of rows to select +	* @param	int		$offset +	* @param	int		$cache_ttl	Either 0 to avoid caching or +	*				the time in seconds which the result shall be kept in cache +	* @return	mixed	Buffered, seekable result handle, false on error +	*/ +	protected function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0) +	{ +		$this->query_result = false; + +		// if $total is set to 0 we do not want to limit the number of rows +		if ($total == 0) +		{ +			$total = -1; +		} + +		$query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total); + +		return $this->sql_query($query, $cache_ttl); +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_affectedrows() +	{ +		return ($this->db_connect_id) ? $this->dbo->changes() : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_fetchrow($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_fetchrow($query_id); +		} + +		return is_object($query_id) ? $query_id->fetchArray(SQLITE3_ASSOC) : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_nextid() +	{ +		return ($this->db_connect_id) ? $this->dbo->lastInsertRowID() : false; +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_freeresult($query_id = false) +	{ +		global $cache; + +		if ($query_id === false) +		{ +			$query_id = $this->query_result; +		} + +		if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) +		{ +			return $cache->sql_freeresult($query_id); +		} + +		if ($query_id) +		{ +			return @$query_id->finalize(); +		} +	} + +	/** +	* {@inheritDoc} +	*/ +	public function sql_escape($msg) +	{ +		return \SQLite3::escapeString($msg); +	} + +	/** +	* {@inheritDoc} +	* +	* For SQLite an underscore is a not-known character... +	*/ +	public function sql_like_expression($expression) +	{ +		// Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it! +		// We only catch * and ? here, not the character map possible on file globbing. +		$expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression); + +		$expression = str_replace(array('?', '*'), array("\?", "\*"), $expression); +		$expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression); + +		return 'GLOB \'' . $this->sql_escape($expression) . '\''; +	} + +	/** +	* return sql error array +	* +	* @return array +	*/ +	protected function _sql_error() +	{ +		if (class_exists('SQLite3', false)) +		{ +			$error = array( +				'message'	=> $this->dbo->lastErrorMsg(), +				'code'		=> $this->dbo->lastErrorCode(), +			); +		} +		else +		{ +			$error = array( +				'message'	=> $this->connect_error, +				'code'		=> '', +			); +		} + +		return $error; +	} + +	/** +	* Build db-specific query data +	* +	* @param	string	$stage		Available stages: FROM, WHERE +	* @param	mixed	$data		A string containing the CROSS JOIN query or an array of WHERE clauses +	* +	* @return	string	The db-specific query fragment +	*/ +	protected function _sql_custom_build($stage, $data) +	{ +		return $data; +	} + +	/** +	* Close sql connection +	* +	* @return	bool		False if failure +	*/ +	protected function _sql_close() +	{ +		return $this->dbo->close(); +	} + +	/** +	* Build db-specific report +	* +	* @param	string	$mode		Available modes: display, start, stop, +	*								add_select_row, fromcache, record_fromcache +	* @param	string	$query		The Query that should be explained +	* @return	mixed		Either a full HTML page, boolean or null +	*/ +	protected function _sql_report($mode, $query = '') +	{ +		switch ($mode) +		{ +			case 'start': + +				$explain_query = $query; +				if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} +				else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) +				{ +					$explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; +				} + +				if (preg_match('/^SELECT/', $explain_query)) +				{ +					$html_table = false; + +					if ($result = $this->dbo->query("EXPLAIN QUERY PLAN $explain_query")) +					{ +						while ($row = $result->fetchArray(SQLITE3_ASSOC)) +						{ +							$html_table = $this->sql_report('add_select_row', $query, $html_table, $row); +						} +					} + +					if ($html_table) +					{ +						$this->html_hold .= '</table>'; +					} +				} + +			break; + +			case 'fromcache': +				$endtime = explode(' ', microtime()); +				$endtime = $endtime[0] + $endtime[1]; + +				$result = $this->dbo->query($query); +				while ($void = $result->fetchArray(SQLITE3_ASSOC)) +				{ +					// Take the time spent on parsing rows into account +				} + +				$splittime = explode(' ', microtime()); +				$splittime = $splittime[0] + $splittime[1]; + +				$this->sql_report('record_fromcache', $query, $endtime, $splittime); + +			break; +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php new file mode 100644 index 0000000000..edcc69e1bf --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php @@ -0,0 +1,70 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class local_url_bbcode extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_local_url_bbcode'))), +		); +	} + +	/** +	* Update BBCodes that currently use the LOCAL_URL tag +	* +	* To fix http://tracker.phpbb.com/browse/PHPBB3-8319 we changed +	* the second_pass_replace value, so that needs updating for existing ones +	*/ +	public function update_local_url_bbcode() +	{ +		$sql = 'SELECT * +			FROM ' . BBCODES_TABLE . ' +			WHERE bbcode_match ' . $this->db->sql_like_expression($this->db->any_char . 'LOCAL_URL' . $this->db->any_char); +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (!class_exists('acp_bbcodes')) +			{ +				if (function_exists('phpbb_require_updated')) +				{ +					phpbb_require_updated('includes/acp/acp_bbcodes.' . $this->php_ext); +				} +				else +				{ +					require($this->phpbb_root_path . 'includes/acp/acp_bbcodes.' . $this->php_ext); +				} +			} + +			$bbcode_match = $row['bbcode_match']; +			$bbcode_tpl = $row['bbcode_tpl']; + +			$acp_bbcodes = new \acp_bbcodes(); +			$sql_ary = $acp_bbcodes->build_regexp($bbcode_match, $bbcode_tpl); + +			$sql = 'UPDATE ' . BBCODES_TABLE . ' +				SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +				WHERE bbcode_id = ' . (int) $row['bbcode_id']; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php new file mode 100644 index 0000000000..26937d6d80 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php @@ -0,0 +1,1181 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_0 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.0', '>='); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables' => array( +				$this->table_prefix . 'attachments'	=> array( +					'COLUMNS'	=> array( +						'attach_id'	=> array('UINT', NULL, 'auto_increment'), +						'post_msg_id'	=> array('UINT', 0), +						'topic_id'	=> array('UINT', 0), +						'in_message'	=> array('BOOL', 0), +						'poster_id'	=> array('UINT', 0), +						'is_orphan'	=> array('BOOL', 1), +						'physical_filename'	=> array('VCHAR', ''), +						'real_filename'	=> array('VCHAR', ''), +						'download_count'	=> array('UINT', 0), +						'attach_comment'	=> array('TEXT_UNI', ''), +						'extension'	=> array('VCHAR:100', ''), +						'mimetype'	=> array('VCHAR:100', ''), +						'filesize'	=> array('UINT:20', 0), +						'filetime'	=> array('TIMESTAMP', 0), +						'thumbnail'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'attach_id', +					'KEYS'	=> array( +						'filetime'	=> array('INDEX', 'filetime'), +						'post_msg_id'	=> array('INDEX', 'post_msg_id'), +						'topic_id'	=> array('INDEX', 'topic_id'), +						'poster_id'	=> array('INDEX', 'poster_id'), +						'is_orphan'	=> array('INDEX', 'is_orphan'), +					), +				), + +				$this->table_prefix . 'acl_groups'	=> array( +					'COLUMNS'	=> array( +						'group_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'auth_option_id'	=> array('UINT', 0), +						'auth_role_id'	=> array('UINT', 0), +						'auth_setting'	=> array('TINT:2', 0), +					), +					'KEYS'	=> array( +						'group_id'	=> array('INDEX', 'group_id'), +						'auth_opt_id'	=> array('INDEX', 'auth_option_id'), +						'auth_role_id'	=> array('INDEX', 'auth_role_id'), +					), +				), + +				$this->table_prefix . 'acl_options'	=> array( +					'COLUMNS'	=> array( +						'auth_option_id'	=> array('UINT', NULL, 'auto_increment'), +						'auth_option'	=> array('VCHAR:50', ''), +						'is_global'	=> array('BOOL', 0), +						'is_local'	=> array('BOOL', 0), +						'founder_only'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'auth_option_id', +					'KEYS'	=> array( +						'auth_option'	=> array('INDEX', 'auth_option'), +					), +				), + +				$this->table_prefix . 'acl_roles'	=> array( +					'COLUMNS'	=> array( +						'role_id'	=> array('UINT', NULL, 'auto_increment'), +						'role_name'	=> array('VCHAR_UNI', ''), +						'role_description'	=> array('TEXT_UNI', ''), +						'role_type'	=> array('VCHAR:10', ''), +						'role_order'	=> array('USINT', 0), +					), +					'PRIMARY_KEY'	=> 'role_id', +					'KEYS'	=> array( +						'role_type'	=> array('INDEX', 'role_type'), +						'role_order'	=> array('INDEX', 'role_order'), +					), +				), + +				$this->table_prefix . 'acl_roles_data'	=> array( +					'COLUMNS'	=> array( +						'role_id'	=> array('UINT', 0), +						'auth_option_id'	=> array('UINT', 0), +						'auth_setting'	=> array('TINT:2', 0), +					), +					'PRIMARY_KEY'	=> array('role_id', 'auth_option_id'), +					'KEYS'	=> array( +						'ath_op_id'	=> array('INDEX', 'auth_option_id'), +					), +				), + +				$this->table_prefix . 'acl_users'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'auth_option_id'	=> array('UINT', 0), +						'auth_role_id'	=> array('UINT', 0), +						'auth_setting'	=> array('TINT:2', 0), +					), +					'KEYS'	=> array( +						'user_id'	=> array('INDEX', 'user_id'), +						'auth_option_id'	=> array('INDEX', 'auth_option_id'), +						'auth_role_id'	=> array('INDEX', 'auth_role_id'), +					), +				), + +				$this->table_prefix . 'banlist'	=> array( +					'COLUMNS'	=> array( +						'ban_id'	=> array('UINT', NULL, 'auto_increment'), +						'ban_userid'	=> array('UINT', 0), +						'ban_ip'	=> array('VCHAR:40', ''), +						'ban_email'	=> array('VCHAR_UNI:100', ''), +						'ban_start'	=> array('TIMESTAMP', 0), +						'ban_end'	=> array('TIMESTAMP', 0), +						'ban_exclude'	=> array('BOOL', 0), +						'ban_reason'	=> array('VCHAR_UNI', ''), +						'ban_give_reason'	=> array('VCHAR_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'ban_id', +					'KEYS'	=> array( +						'ban_end'	=> array('INDEX', 'ban_end'), +						'ban_user'	=> array('INDEX', array('ban_userid', 'ban_exclude')), +						'ban_email'	=> array('INDEX', array('ban_email', 'ban_exclude')), +						'ban_ip'	=> array('INDEX', array('ban_ip', 'ban_exclude')), +					), +				), + +				$this->table_prefix . 'bbcodes'	=> array( +					'COLUMNS'	=> array( +						'bbcode_id'	=> array('TINT:3', 0), +						'bbcode_tag'	=> array('VCHAR:16', ''), +						'bbcode_helpline'	=> array('VCHAR_UNI', ''), +						'display_on_posting'	=> array('BOOL', 0), +						'bbcode_match'	=> array('TEXT_UNI', ''), +						'bbcode_tpl'	=> array('MTEXT_UNI', ''), +						'first_pass_match'	=> array('MTEXT_UNI', ''), +						'first_pass_replace'	=> array('MTEXT_UNI', ''), +						'second_pass_match'	=> array('MTEXT_UNI', ''), +						'second_pass_replace'	=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'bbcode_id', +					'KEYS'	=> array( +						'display_on_post'	=> array('INDEX', 'display_on_posting'), +					), +				), + +				$this->table_prefix . 'bookmarks'	=> array( +					'COLUMNS'	=> array( +						'topic_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> array('topic_id', 'user_id'), +				), + +				$this->table_prefix . 'bots'	=> array( +					'COLUMNS'	=> array( +						'bot_id'	=> array('UINT', NULL, 'auto_increment'), +						'bot_active'	=> array('BOOL', 1), +						'bot_name'	=> array('STEXT_UNI', ''), +						'user_id'	=> array('UINT', 0), +						'bot_agent'	=> array('VCHAR', ''), +						'bot_ip'	=> array('VCHAR', ''), +					), +					'PRIMARY_KEY'	=> 'bot_id', +					'KEYS'	=> array( +						'bot_active'	=> array('INDEX', 'bot_active'), +					), +				), + +				$this->table_prefix . 'config'	=> array( +					'COLUMNS'	=> array( +						'config_name'	=> array('VCHAR', ''), +						'config_value'	=> array('VCHAR_UNI', ''), +						'is_dynamic'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'config_name', +					'KEYS'	=> array( +						'is_dynamic'	=> array('INDEX', 'is_dynamic'), +					), +				), + +				$this->table_prefix . 'confirm'	=> array( +					'COLUMNS'	=> array( +						'confirm_id'	=> array('CHAR:32', ''), +						'session_id'	=> array('CHAR:32', ''), +						'confirm_type'	=> array('TINT:3', 0), +						'code'	=> array('VCHAR:8', ''), +						'seed'	=> array('UINT:10', 0), +					), +					'PRIMARY_KEY'	=> array('session_id', 'confirm_id'), +					'KEYS'	=> array( +						'confirm_type'	=> array('INDEX', 'confirm_type'), +					), +				), + +				$this->table_prefix . 'disallow'	=> array( +					'COLUMNS'	=> array( +						'disallow_id'	=> array('UINT', NULL, 'auto_increment'), +						'disallow_username'	=> array('VCHAR_UNI:255', ''), +					), +					'PRIMARY_KEY'	=> 'disallow_id', +				), + +				$this->table_prefix . 'drafts'	=> array( +					'COLUMNS'	=> array( +						'draft_id'	=> array('UINT', NULL, 'auto_increment'), +						'user_id'	=> array('UINT', 0), +						'topic_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'save_time'	=> array('TIMESTAMP', 0), +						'draft_subject'	=> array('XSTEXT_UNI', ''), +						'draft_message'	=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'draft_id', +					'KEYS'	=> array( +						'save_time'	=> array('INDEX', 'save_time'), +					), +				), + +				$this->table_prefix . 'extensions'	=> array( +					'COLUMNS'	=> array( +						'extension_id'	=> array('UINT', NULL, 'auto_increment'), +						'group_id'	=> array('UINT', 0), +						'extension'	=> array('VCHAR:100', ''), +					), +					'PRIMARY_KEY'	=> 'extension_id', +				), + +				$this->table_prefix . 'extension_groups'	=> array( +					'COLUMNS'	=> array( +						'group_id'	=> array('UINT', NULL, 'auto_increment'), +						'group_name'	=> array('VCHAR_UNI', ''), +						'cat_id'	=> array('TINT:2', 0), +						'allow_group'	=> array('BOOL', 0), +						'download_mode'	=> array('BOOL', 1), +						'upload_icon'	=> array('VCHAR', ''), +						'max_filesize'	=> array('UINT:20', 0), +						'allowed_forums'	=> array('TEXT', ''), +						'allow_in_pm'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'group_id', +				), + +				$this->table_prefix . 'forums'	=> array( +					'COLUMNS'	=> array( +						'forum_id'	=> array('UINT', NULL, 'auto_increment'), +						'parent_id'	=> array('UINT', 0), +						'left_id'	=> array('UINT', 0), +						'right_id'	=> array('UINT', 0), +						'forum_parents'	=> array('MTEXT', ''), +						'forum_name'	=> array('STEXT_UNI', ''), +						'forum_desc'	=> array('TEXT_UNI', ''), +						'forum_desc_bitfield'	=> array('VCHAR:255', ''), +						'forum_desc_options'	=> array('UINT:11', 7), +						'forum_desc_uid'	=> array('VCHAR:8', ''), +						'forum_link'	=> array('VCHAR_UNI', ''), +						'forum_password'	=> array('VCHAR_UNI:40', ''), +						'forum_style'	=> array('USINT', 0), +						'forum_image'	=> array('VCHAR', ''), +						'forum_rules'	=> array('TEXT_UNI', ''), +						'forum_rules_link'	=> array('VCHAR_UNI', ''), +						'forum_rules_bitfield'	=> array('VCHAR:255', ''), +						'forum_rules_options'	=> array('UINT:11', 7), +						'forum_rules_uid'	=> array('VCHAR:8', ''), +						'forum_topics_per_page'	=> array('TINT:4', 0), +						'forum_type'	=> array('TINT:4', 0), +						'forum_status'	=> array('TINT:4', 0), +						'forum_posts'	=> array('UINT', 0), +						'forum_topics'	=> array('UINT', 0), +						'forum_topics_real'	=> array('UINT', 0), +						'forum_last_post_id'	=> array('UINT', 0), +						'forum_last_poster_id'	=> array('UINT', 0), +						'forum_last_post_subject' => array('XSTEXT_UNI', ''), +						'forum_last_post_time'	=> array('TIMESTAMP', 0), +						'forum_last_poster_name'=> array('VCHAR_UNI', ''), +						'forum_last_poster_colour'=> array('VCHAR:6', ''), +						'forum_flags'	=> array('TINT:4', 32), +						'display_on_index'	=> array('BOOL', 1), +						'enable_indexing'	=> array('BOOL', 1), +						'enable_icons'	=> array('BOOL', 1), +						'enable_prune'	=> array('BOOL', 0), +						'prune_next'	=> array('TIMESTAMP', 0), +						'prune_days'	=> array('UINT', 0), +						'prune_viewed'	=> array('UINT', 0), +						'prune_freq'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> 'forum_id', +					'KEYS'	=> array( +						'left_right_id'	=> array('INDEX', array('left_id', 'right_id')), +						'forum_lastpost_id'	=> array('INDEX', 'forum_last_post_id'), +					), +				), + +				$this->table_prefix . 'forums_access'	=> array( +					'COLUMNS'	=> array( +						'forum_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'session_id'	=> array('CHAR:32', ''), +					), +					'PRIMARY_KEY'	=> array('forum_id', 'user_id', 'session_id'), +				), + +				$this->table_prefix . 'forums_track'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'mark_time'	=> array('TIMESTAMP', 0), +					), +					'PRIMARY_KEY'	=> array('user_id', 'forum_id'), +				), + +				$this->table_prefix . 'forums_watch'	=> array( +					'COLUMNS'	=> array( +						'forum_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'notify_status'	=> array('BOOL', 0), +					), +					'KEYS'	=> array( +						'forum_id'	=> array('INDEX', 'forum_id'), +						'user_id'	=> array('INDEX', 'user_id'), +						'notify_stat'	=> array('INDEX', 'notify_status'), +					), +				), + +				$this->table_prefix . 'groups'	=> array( +					'COLUMNS'	=> array( +						'group_id'	=> array('UINT', NULL, 'auto_increment'), +						'group_type'	=> array('TINT:4', 1), +						'group_founder_manage'	=> array('BOOL', 0), +						'group_name'	=> array('VCHAR_CI', ''), +						'group_desc'	=> array('TEXT_UNI', ''), +						'group_desc_bitfield'	=> array('VCHAR:255', ''), +						'group_desc_options'	=> array('UINT:11', 7), +						'group_desc_uid'	=> array('VCHAR:8', ''), +						'group_display'	=> array('BOOL', 0), +						'group_avatar'	=> array('VCHAR', ''), +						'group_avatar_type'	=> array('TINT:2', 0), +						'group_avatar_width'	=> array('USINT', 0), +						'group_avatar_height'	=> array('USINT', 0), +						'group_rank'	=> array('UINT', 0), +						'group_colour'	=> array('VCHAR:6', ''), +						'group_sig_chars'	=> array('UINT', 0), +						'group_receive_pm'	=> array('BOOL', 0), +						'group_message_limit'	=> array('UINT', 0), +						'group_legend'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'	=> 'group_id', +					'KEYS'	=> array( +						'group_legend'	=> array('INDEX', 'group_legend'), +					), +				), + +				$this->table_prefix . 'icons'	=> array( +					'COLUMNS'	=> array( +						'icons_id'	=> array('UINT', NULL, 'auto_increment'), +						'icons_url'	=> array('VCHAR', ''), +						'icons_width'	=> array('TINT:4', 0), +						'icons_height'	=> array('TINT:4', 0), +						'icons_order'	=> array('UINT', 0), +						'display_on_posting'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'	=> 'icons_id', +					'KEYS'	=> array( +						'display_on_posting'	=> array('INDEX', 'display_on_posting'), +					), +				), + +				$this->table_prefix . 'lang'	=> array( +					'COLUMNS'	=> array( +						'lang_id'	=> array('TINT:4', NULL, 'auto_increment'), +						'lang_iso'	=> array('VCHAR:30', ''), +						'lang_dir'	=> array('VCHAR:30', ''), +						'lang_english_name'	=> array('VCHAR_UNI:100', ''), +						'lang_local_name'	=> array('VCHAR_UNI:255', ''), +						'lang_author'	=> array('VCHAR_UNI:255', ''), +					), +					'PRIMARY_KEY'	=> 'lang_id', +					'KEYS'	=> array( +						'lang_iso'	=> array('INDEX', 'lang_iso'), +					), +				), + +				$this->table_prefix . 'log'	=> array( +					'COLUMNS'	=> array( +						'log_id'	=> array('UINT', NULL, 'auto_increment'), +						'log_type'	=> array('TINT:4', 0), +						'user_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'topic_id'	=> array('UINT', 0), +						'reportee_id'	=> array('UINT', 0), +						'log_ip'	=> array('VCHAR:40', ''), +						'log_time'	=> array('TIMESTAMP', 0), +						'log_operation'	=> array('TEXT_UNI', ''), +						'log_data'	=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'log_id', +					'KEYS'	=> array( +						'log_type'	=> array('INDEX', 'log_type'), +						'forum_id'	=> array('INDEX', 'forum_id'), +						'topic_id'	=> array('INDEX', 'topic_id'), +						'reportee_id'	=> array('INDEX', 'reportee_id'), +						'user_id'	=> array('INDEX', 'user_id'), +					), +				), + +				$this->table_prefix . 'moderator_cache'	=> array( +					'COLUMNS'	=> array( +						'forum_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'username'	=> array('VCHAR_UNI:255', ''), +						'group_id'	=> array('UINT', 0), +						'group_name'	=> array('VCHAR_UNI', ''), +						'display_on_index'	=> array('BOOL', 1), +					), +					'KEYS'	=> array( +						'disp_idx'	=> array('INDEX', 'display_on_index'), +						'forum_id'	=> array('INDEX', 'forum_id'), +					), +				), + +				$this->table_prefix . 'modules'	=> array( +					'COLUMNS'	=> array( +						'module_id'	=> array('UINT', NULL, 'auto_increment'), +						'module_enabled'	=> array('BOOL', 1), +						'module_display'	=> array('BOOL', 1), +						'module_basename'	=> array('VCHAR', ''), +						'module_class'	=> array('VCHAR:10', ''), +						'parent_id'	=> array('UINT', 0), +						'left_id'	=> array('UINT', 0), +						'right_id'	=> array('UINT', 0), +						'module_langname'	=> array('VCHAR', ''), +						'module_mode'	=> array('VCHAR', ''), +						'module_auth'	=> array('VCHAR', ''), +					), +					'PRIMARY_KEY'	=> 'module_id', +					'KEYS'	=> array( +						'left_right_id'	=> array('INDEX', array('left_id', 'right_id')), +						'module_enabled'	=> array('INDEX', 'module_enabled'), +						'class_left_id'	=> array('INDEX', array('module_class', 'left_id')), +					), +				), + +				$this->table_prefix . 'poll_options'	=> array( +					'COLUMNS'	=> array( +						'poll_option_id'	=> array('TINT:4', 0), +						'topic_id'	=> array('UINT', 0), +						'poll_option_text'	=> array('TEXT_UNI', ''), +						'poll_option_total'	=> array('UINT', 0), +					), +					'KEYS'	=> array( +						'poll_opt_id'	=> array('INDEX', 'poll_option_id'), +						'topic_id'	=> array('INDEX', 'topic_id'), +					), +				), + +				$this->table_prefix . 'poll_votes'	=> array( +					'COLUMNS'	=> array( +						'topic_id'	=> array('UINT', 0), +						'poll_option_id'	=> array('TINT:4', 0), +						'vote_user_id'	=> array('UINT', 0), +						'vote_user_ip'	=> array('VCHAR:40', ''), +					), +					'KEYS'	=> array( +						'topic_id'	=> array('INDEX', 'topic_id'), +						'vote_user_id'	=> array('INDEX', 'vote_user_id'), +						'vote_user_ip'	=> array('INDEX', 'vote_user_ip'), +					), +				), + +				$this->table_prefix . 'posts'	=> array( +					'COLUMNS'	=> array( +						'post_id'	=> array('UINT', NULL, 'auto_increment'), +						'topic_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'poster_id'	=> array('UINT', 0), +						'icon_id'	=> array('UINT', 0), +						'poster_ip'	=> array('VCHAR:40', ''), +						'post_time'	=> array('TIMESTAMP', 0), +						'post_approved'	=> array('BOOL', 1), +						'post_reported'	=> array('BOOL', 0), +						'enable_bbcode'	=> array('BOOL', 1), +						'enable_smilies'	=> array('BOOL', 1), +						'enable_magic_url'	=> array('BOOL', 1), +						'enable_sig'	=> array('BOOL', 1), +						'post_username'	=> array('VCHAR_UNI:255', ''), +						'post_subject'	=> array('XSTEXT_UNI', '', 'true_sort'), +						'post_text'	=> array('MTEXT_UNI', ''), +						'post_checksum'	=> array('VCHAR:32', ''), +						'post_attachment'	=> array('BOOL', 0), +						'bbcode_bitfield'	=> array('VCHAR:255', ''), +						'bbcode_uid'	=> array('VCHAR:8', ''), +						'post_postcount'	=> array('BOOL', 1), +						'post_edit_time'	=> array('TIMESTAMP', 0), +						'post_edit_reason'	=> array('STEXT_UNI', ''), +						'post_edit_user'	=> array('UINT', 0), +						'post_edit_count'	=> array('USINT', 0), +						'post_edit_locked'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'post_id', +					'KEYS'	=> array( +						'forum_id'	=> array('INDEX', 'forum_id'), +						'topic_id'	=> array('INDEX', 'topic_id'), +						'poster_ip'	=> array('INDEX', 'poster_ip'), +						'poster_id'	=> array('INDEX', 'poster_id'), +						'post_approved'	=> array('INDEX', 'post_approved'), +						'tid_post_time'	=> array('INDEX', array('topic_id', 'post_time')), +					), +				), + +				$this->table_prefix . 'privmsgs'	=> array( +					'COLUMNS'	=> array( +						'msg_id'	=> array('UINT', NULL, 'auto_increment'), +						'root_level'	=> array('UINT', 0), +						'author_id'	=> array('UINT', 0), +						'icon_id'	=> array('UINT', 0), +						'author_ip'	=> array('VCHAR:40', ''), +						'message_time'	=> array('TIMESTAMP', 0), +						'enable_bbcode'	=> array('BOOL', 1), +						'enable_smilies'	=> array('BOOL', 1), +						'enable_magic_url'	=> array('BOOL', 1), +						'enable_sig'	=> array('BOOL', 1), +						'message_subject'	=> array('XSTEXT_UNI', ''), +						'message_text'	=> array('MTEXT_UNI', ''), +						'message_edit_reason'	=> array('STEXT_UNI', ''), +						'message_edit_user'	=> array('UINT', 0), +						'message_attachment'	=> array('BOOL', 0), +						'bbcode_bitfield'	=> array('VCHAR:255', ''), +						'bbcode_uid'	=> array('VCHAR:8', ''), +						'message_edit_time'	=> array('TIMESTAMP', 0), +						'message_edit_count'	=> array('USINT', 0), +						'to_address'	=> array('TEXT_UNI', ''), +						'bcc_address'	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'msg_id', +					'KEYS'	=> array( +						'author_ip'	=> array('INDEX', 'author_ip'), +						'message_time'	=> array('INDEX', 'message_time'), +						'author_id'	=> array('INDEX', 'author_id'), +						'root_level'	=> array('INDEX', 'root_level'), +					), +				), + +				$this->table_prefix . 'privmsgs_folder'	=> array( +					'COLUMNS'	=> array( +						'folder_id'	=> array('UINT', NULL, 'auto_increment'), +						'user_id'	=> array('UINT', 0), +						'folder_name'	=> array('VCHAR_UNI', ''), +						'pm_count'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> 'folder_id', +					'KEYS'	=> array( +						'user_id'	=> array('INDEX', 'user_id'), +					), +				), + +				$this->table_prefix . 'privmsgs_rules'	=> array( +					'COLUMNS'	=> array( +						'rule_id'	=> array('UINT', NULL, 'auto_increment'), +						'user_id'	=> array('UINT', 0), +						'rule_check'	=> array('UINT', 0), +						'rule_connection'	=> array('UINT', 0), +						'rule_string'	=> array('VCHAR_UNI', ''), +						'rule_user_id'	=> array('UINT', 0), +						'rule_group_id'	=> array('UINT', 0), +						'rule_action'	=> array('UINT', 0), +						'rule_folder_id'	=> array('INT:11', 0), +					), +					'PRIMARY_KEY'	=> 'rule_id', +					'KEYS'	=> array( +						'user_id'	=> array('INDEX', 'user_id'), +					), +				), + +				$this->table_prefix . 'privmsgs_to'	=> array( +					'COLUMNS'	=> array( +						'msg_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'author_id'	=> array('UINT', 0), +						'pm_deleted'	=> array('BOOL', 0), +						'pm_new'	=> array('BOOL', 1), +						'pm_unread'	=> array('BOOL', 1), +						'pm_replied'	=> array('BOOL', 0), +						'pm_marked'	=> array('BOOL', 0), +						'pm_forwarded'	=> array('BOOL', 0), +						'folder_id'	=> array('INT:11', 0), +					), +					'KEYS'	=> array( +						'msg_id'	=> array('INDEX', 'msg_id'), +						'author_id'	=> array('INDEX', 'author_id'), +						'usr_flder_id'	=> array('INDEX', array('user_id', 'folder_id')), +					), +				), + +				$this->table_prefix . 'profile_fields'	=> array( +					'COLUMNS'	=> array( +						'field_id'	=> array('UINT', NULL, 'auto_increment'), +						'field_name'	=> array('VCHAR_UNI', ''), +						'field_type'	=> array('TINT:4', 0), +						'field_ident'	=> array('VCHAR:20', ''), +						'field_length'	=> array('VCHAR:20', ''), +						'field_minlen'	=> array('VCHAR', ''), +						'field_maxlen'	=> array('VCHAR', ''), +						'field_novalue'	=> array('VCHAR_UNI', ''), +						'field_default_value'	=> array('VCHAR_UNI', ''), +						'field_validation'	=> array('VCHAR_UNI:20', ''), +						'field_required'	=> array('BOOL', 0), +						'field_show_on_reg'	=> array('BOOL', 0), +						'field_hide'	=> array('BOOL', 0), +						'field_no_view'	=> array('BOOL', 0), +						'field_active'	=> array('BOOL', 0), +						'field_order'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> 'field_id', +					'KEYS'	=> array( +						'fld_type'	=> array('INDEX', 'field_type'), +						'fld_ordr'	=> array('INDEX', 'field_order'), +					), +				), + +				$this->table_prefix . 'profile_fields_data'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> 'user_id', +				), + +				$this->table_prefix . 'profile_fields_lang'	=> array( +					'COLUMNS'	=> array( +						'field_id'	=> array('UINT', 0), +						'lang_id'	=> array('UINT', 0), +						'option_id'	=> array('UINT', 0), +						'field_type'	=> array('TINT:4', 0), +						'lang_value'	=> array('VCHAR_UNI', ''), +					), +					'PRIMARY_KEY'	=> array('field_id', 'lang_id', 'option_id'), +				), + +				$this->table_prefix . 'profile_lang'	=> array( +					'COLUMNS'	=> array( +						'field_id'	=> array('UINT', 0), +						'lang_id'	=> array('UINT', 0), +						'lang_name'	=> array('VCHAR_UNI', ''), +						'lang_explain'	=> array('TEXT_UNI', ''), +						'lang_default_value'	=> array('VCHAR_UNI', ''), +					), +					'PRIMARY_KEY'	=> array('field_id', 'lang_id'), +				), + +				$this->table_prefix . 'ranks'	=> array( +					'COLUMNS'	=> array( +						'rank_id'	=> array('UINT', NULL, 'auto_increment'), +						'rank_title'	=> array('VCHAR_UNI', ''), +						'rank_min'	=> array('UINT', 0), +						'rank_special'	=> array('BOOL', 0), +						'rank_image'	=> array('VCHAR', ''), +					), +					'PRIMARY_KEY'	=> 'rank_id', +				), + +				$this->table_prefix . 'reports'	=> array( +					'COLUMNS'	=> array( +						'report_id'	=> array('UINT', NULL, 'auto_increment'), +						'reason_id'	=> array('USINT', 0), +						'post_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'user_notify'	=> array('BOOL', 0), +						'report_closed'	=> array('BOOL', 0), +						'report_time'	=> array('TIMESTAMP', 0), +						'report_text'	=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'report_id', +				), + +				$this->table_prefix . 'reports_reasons'	=> array( +					'COLUMNS'	=> array( +						'reason_id'	=> array('USINT', NULL, 'auto_increment'), +						'reason_title'	=> array('VCHAR_UNI', ''), +						'reason_description'	=> array('MTEXT_UNI', ''), +						'reason_order'	=> array('USINT', 0), +					), +					'PRIMARY_KEY'	=> 'reason_id', +				), + +				$this->table_prefix . 'search_results'	=> array( +					'COLUMNS'	=> array( +						'search_key'	=> array('VCHAR:32', ''), +						'search_time'	=> array('TIMESTAMP', 0), +						'search_keywords'	=> array('MTEXT_UNI', ''), +						'search_authors'	=> array('MTEXT', ''), +					), +					'PRIMARY_KEY'	=> 'search_key', +				), + +				$this->table_prefix . 'search_wordlist'	=> array( +					'COLUMNS'	=> array( +						'word_id'	=> array('UINT', NULL, 'auto_increment'), +						'word_text'	=> array('VCHAR_UNI', ''), +						'word_common'	=> array('BOOL', 0), +						'word_count'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'	=> 'word_id', +					'KEYS'	=> array( +						'wrd_txt'	=> array('UNIQUE', 'word_text'), +						'wrd_cnt'	=> array('INDEX', 'word_count'), +					), +				), + +				$this->table_prefix . 'search_wordmatch'	=> array( +					'COLUMNS'	=> array( +						'post_id'	=> array('UINT', 0), +						'word_id'	=> array('UINT', 0), +						'title_match'	=> array('BOOL', 0), +					), +					'KEYS'	=> array( +						'unq_mtch'	=> array('UNIQUE', array('word_id', 'post_id', 'title_match')), +						'word_id'	=> array('INDEX', 'word_id'), +						'post_id'	=> array('INDEX', 'post_id'), +					), +				), + +				$this->table_prefix . 'sessions'	=> array( +					'COLUMNS'	=> array( +						'session_id'	=> array('CHAR:32', ''), +						'session_user_id'	=> array('UINT', 0), +						'session_last_visit'	=> array('TIMESTAMP', 0), +						'session_start'	=> array('TIMESTAMP', 0), +						'session_time'	=> array('TIMESTAMP', 0), +						'session_ip'	=> array('VCHAR:40', ''), +						'session_browser'	=> array('VCHAR:150', ''), +						'session_forwarded_for'	=> array('VCHAR:255', ''), +						'session_page'	=> array('VCHAR_UNI', ''), +						'session_viewonline'	=> array('BOOL', 1), +						'session_autologin'	=> array('BOOL', 0), +						'session_admin'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'session_id', +					'KEYS'	=> array( +						'session_time'	=> array('INDEX', 'session_time'), +						'session_user_id'	=> array('INDEX', 'session_user_id'), +					), +				), + +				$this->table_prefix . 'sessions_keys'	=> array( +					'COLUMNS'	=> array( +						'key_id'	=> array('CHAR:32', ''), +						'user_id'	=> array('UINT', 0), +						'last_ip'	=> array('VCHAR:40', ''), +						'last_login'	=> array('TIMESTAMP', 0), +					), +					'PRIMARY_KEY'	=> array('key_id', 'user_id'), +					'KEYS'	=> array( +						'last_login'	=> array('INDEX', 'last_login'), +					), +				), + +				$this->table_prefix . 'sitelist'	=> array( +					'COLUMNS'	=> array( +						'site_id'	=> array('UINT', NULL, 'auto_increment'), +						'site_ip'	=> array('VCHAR:40', ''), +						'site_hostname'	=> array('VCHAR', ''), +						'ip_exclude'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'site_id', +				), + +				$this->table_prefix . 'smilies'	=> array( +					'COLUMNS'	=> array( +						'smiley_id'	=> array('UINT', NULL, 'auto_increment'), +// We may want to set 'code' to VCHAR:50 or check if unicode support is possible... at the moment only ASCII characters are allowed. +						'code'	=> array('VCHAR_UNI:50', ''), +						'emotion'	=> array('VCHAR_UNI:50', ''), +						'smiley_url'	=> array('VCHAR:50', ''), +						'smiley_width'	=> array('USINT', 0), +						'smiley_height'	=> array('USINT', 0), +						'smiley_order'	=> array('UINT', 0), +						'display_on_posting'=> array('BOOL', 1), +					), +					'PRIMARY_KEY'	=> 'smiley_id', +					'KEYS'	=> array( +						'display_on_post'	=> array('INDEX', 'display_on_posting'), +					), +				), + +				$this->table_prefix . 'styles'	=> array( +					'COLUMNS'	=> array( +						'style_id'	=> array('USINT', NULL, 'auto_increment'), +						'style_name'	=> array('VCHAR_UNI:255', ''), +						'style_copyright'	=> array('VCHAR_UNI', ''), +						'style_active'	=> array('BOOL', 1), +						'template_id'	=> array('USINT', 0), +						'theme_id'	=> array('USINT', 0), +						'imageset_id'	=> array('USINT', 0), +					), +					'PRIMARY_KEY'	=> 'style_id', +					'KEYS'	=> array( +						'style_name'	=> array('UNIQUE', 'style_name'), +						'template_id'	=> array('INDEX', 'template_id'), +						'theme_id'	=> array('INDEX', 'theme_id'), +						'imageset_id'	=> array('INDEX', 'imageset_id'), +					), +				), + +				$this->table_prefix . 'styles_template'	=> array( +					'COLUMNS'	=> array( +						'template_id'	=> array('USINT', NULL, 'auto_increment'), +						'template_name'	=> array('VCHAR_UNI:255', ''), +						'template_copyright'	=> array('VCHAR_UNI', ''), +						'template_path'	=> array('VCHAR:100', ''), +						'bbcode_bitfield'	=> array('VCHAR:255', 'kNg='), +						'template_storedb'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'template_id', +					'KEYS'	=> array( +						'tmplte_nm'	=> array('UNIQUE', 'template_name'), +					), +				), + +				$this->table_prefix . 'styles_template_data'	=> array( +					'COLUMNS'	=> array( +						'template_id'	=> array('USINT', 0), +						'template_filename'	=> array('VCHAR:100', ''), +						'template_included'	=> array('TEXT', ''), +						'template_mtime'	=> array('TIMESTAMP', 0), +						'template_data'	=> array('MTEXT_UNI', ''), +					), +					'KEYS'	=> array( +						'tid'	=> array('INDEX', 'template_id'), +						'tfn'	=> array('INDEX', 'template_filename'), +					), +				), + +				$this->table_prefix . 'styles_theme'	=> array( +					'COLUMNS'	=> array( +						'theme_id'	=> array('USINT', NULL, 'auto_increment'), +						'theme_name'	=> array('VCHAR_UNI:255', ''), +						'theme_copyright'	=> array('VCHAR_UNI', ''), +						'theme_path'	=> array('VCHAR:100', ''), +						'theme_storedb'	=> array('BOOL', 0), +						'theme_mtime'	=> array('TIMESTAMP', 0), +						'theme_data'	=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'theme_id', +					'KEYS'	=> array( +						'theme_name'	=> array('UNIQUE', 'theme_name'), +					), +				), + +				$this->table_prefix . 'styles_imageset'	=> array( +					'COLUMNS'	=> array( +						'imageset_id'	=> array('USINT', NULL, 'auto_increment'), +						'imageset_name'	=> array('VCHAR_UNI:255', ''), +						'imageset_copyright'	=> array('VCHAR_UNI', ''), +						'imageset_path'	=> array('VCHAR:100', ''), +					), +					'PRIMARY_KEY'	=> 'imageset_id', +					'KEYS'	=> array( +						'imgset_nm'	=> array('UNIQUE', 'imageset_name'), +					), +				), + +				$this->table_prefix . 'styles_imageset_data'	=> array( +					'COLUMNS'	=> array( +						'image_id'	=> array('USINT', NULL, 'auto_increment'), +						'image_name'	=> array('VCHAR:200', ''), +						'image_filename'	=> array('VCHAR:200', ''), +						'image_lang'	=> array('VCHAR:30', ''), +						'image_height'	=> array('USINT', 0), +						'image_width'	=> array('USINT', 0), +						'imageset_id'	=> array('USINT', 0), +					), +					'PRIMARY_KEY'	=> 'image_id', +					'KEYS'	=> array( +						'i_d'	=> array('INDEX', 'imageset_id'), +					), +				), + +				$this->table_prefix . 'topics'	=> array( +					'COLUMNS'	=> array( +						'topic_id'	=> array('UINT', NULL, 'auto_increment'), +						'forum_id'	=> array('UINT', 0), +						'icon_id'	=> array('UINT', 0), +						'topic_attachment'	=> array('BOOL', 0), +						'topic_approved'	=> array('BOOL', 1), +						'topic_reported'	=> array('BOOL', 0), +						'topic_title'	=> array('XSTEXT_UNI', '', 'true_sort'), +						'topic_poster'	=> array('UINT', 0), +						'topic_time'	=> array('TIMESTAMP', 0), +						'topic_time_limit'	=> array('TIMESTAMP', 0), +						'topic_views'	=> array('UINT', 0), +						'topic_replies'	=> array('UINT', 0), +						'topic_replies_real'	=> array('UINT', 0), +						'topic_status'	=> array('TINT:3', 0), +						'topic_type'	=> array('TINT:3', 0), +						'topic_first_post_id'	=> array('UINT', 0), +						'topic_first_poster_name'	=> array('VCHAR_UNI', ''), +						'topic_first_poster_colour'	=> array('VCHAR:6', ''), +						'topic_last_post_id'	=> array('UINT', 0), +						'topic_last_poster_id'	=> array('UINT', 0), +						'topic_last_poster_name'	=> array('VCHAR_UNI', ''), +						'topic_last_poster_colour'	=> array('VCHAR:6', ''), +						'topic_last_post_subject'	=> array('XSTEXT_UNI', ''), +						'topic_last_post_time'	=> array('TIMESTAMP', 0), +						'topic_last_view_time'	=> array('TIMESTAMP', 0), +						'topic_moved_id'	=> array('UINT', 0), +						'topic_bumped'	=> array('BOOL', 0), +						'topic_bumper'	=> array('UINT', 0), +						'poll_title'	=> array('STEXT_UNI', ''), +						'poll_start'	=> array('TIMESTAMP', 0), +						'poll_length'	=> array('TIMESTAMP', 0), +						'poll_max_options'	=> array('TINT:4', 1), +						'poll_last_vote'	=> array('TIMESTAMP', 0), +						'poll_vote_change'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> 'topic_id', +					'KEYS'	=> array( +						'forum_id'	=> array('INDEX', 'forum_id'), +						'forum_id_type'	=> array('INDEX', array('forum_id', 'topic_type')), +						'last_post_time'	=> array('INDEX', 'topic_last_post_time'), +						'topic_approved'	=> array('INDEX', 'topic_approved'), +						'forum_appr_last'	=> array('INDEX', array('forum_id', 'topic_approved', 'topic_last_post_id')), +						'fid_time_moved'	=> array('INDEX', array('forum_id', 'topic_last_post_time', 'topic_moved_id')), +					), +				), + +				$this->table_prefix . 'topics_track'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +						'topic_id'	=> array('UINT', 0), +						'forum_id'	=> array('UINT', 0), +						'mark_time'	=> array('TIMESTAMP', 0), +					), +					'PRIMARY_KEY'	=> array('user_id', 'topic_id'), +					'KEYS'	=> array( +						'forum_id'	=> array('INDEX', 'forum_id'), +					), +				), + +				$this->table_prefix . 'topics_posted'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +						'topic_id'	=> array('UINT', 0), +						'topic_posted'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> array('user_id', 'topic_id'), +				), + +				$this->table_prefix . 'topics_watch'	=> array( +					'COLUMNS'	=> array( +						'topic_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'notify_status'	=> array('BOOL', 0), +					), +					'KEYS'	=> array( +						'topic_id'	=> array('INDEX', 'topic_id'), +						'user_id'	=> array('INDEX', 'user_id'), +						'notify_stat'	=> array('INDEX', 'notify_status'), +					), +				), + +				$this->table_prefix . 'user_group'	=> array( +					'COLUMNS'	=> array( +						'group_id'	=> array('UINT', 0), +						'user_id'	=> array('UINT', 0), +						'group_leader'	=> array('BOOL', 0), +						'user_pending'	=> array('BOOL', 1), +					), +					'KEYS'	=> array( +						'group_id'	=> array('INDEX', 'group_id'), +						'user_id'	=> array('INDEX', 'user_id'), +						'group_leader'	=> array('INDEX', 'group_leader'), +					), +				), + +				$this->table_prefix . 'users'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', NULL, 'auto_increment'), +						'user_type'	=> array('TINT:2', 0), +						'group_id'	=> array('UINT', 3), +						'user_permissions'	=> array('MTEXT', ''), +						'user_perm_from'	=> array('UINT', 0), +						'user_ip'	=> array('VCHAR:40', ''), +						'user_regdate'	=> array('TIMESTAMP', 0), +						'username'	=> array('VCHAR_CI', ''), +						'username_clean'	=> array('VCHAR_CI', ''), +						'user_password'	=> array('VCHAR_UNI:40', ''), +						'user_passchg'	=> array('TIMESTAMP', 0), +						'user_pass_convert'	=> array('BOOL', 0), +						'user_email'	=> array('VCHAR_UNI:100', ''), +						'user_email_hash'	=> array('BINT', 0), +						'user_birthday'	=> array('VCHAR:10', ''), +						'user_lastvisit'	=> array('TIMESTAMP', 0), +						'user_lastmark'	=> array('TIMESTAMP', 0), +						'user_lastpost_time'	=> array('TIMESTAMP', 0), +						'user_lastpage'	=> array('VCHAR_UNI:200', ''), +						'user_last_confirm_key'	=> array('VCHAR:10', ''), +						'user_last_search'	=> array('TIMESTAMP', 0), +						'user_warnings'	=> array('TINT:4', 0), +						'user_last_warning'	=> array('TIMESTAMP', 0), +						'user_login_attempts'	=> array('TINT:4', 0), +						'user_inactive_reason'	=> array('TINT:2', 0), +						'user_inactive_time'	=> array('TIMESTAMP', 0), +						'user_posts'	=> array('UINT', 0), +						'user_lang'	=> array('VCHAR:30', ''), +						'user_timezone'	=> array('DECIMAL', 0), +						'user_dst'	=> array('BOOL', 0), +						'user_dateformat'	=> array('VCHAR_UNI:30', 'd M Y H:i'), +						'user_style'	=> array('USINT', 0), +						'user_rank'	=> array('UINT', 0), +						'user_colour'	=> array('VCHAR:6', ''), +						'user_new_privmsg'	=> array('INT:4', 0), +						'user_unread_privmsg'	=> array('INT:4', 0), +						'user_last_privmsg'	=> array('TIMESTAMP', 0), +						'user_message_rules'	=> array('BOOL', 0), +						'user_full_folder'	=> array('INT:11', -3), +						'user_emailtime'	=> array('TIMESTAMP', 0), +						'user_topic_show_days'	=> array('USINT', 0), +						'user_topic_sortby_type'	=> array('VCHAR:1', 't'), +						'user_topic_sortby_dir'	=> array('VCHAR:1', 'd'), +						'user_post_show_days'	=> array('USINT', 0), +						'user_post_sortby_type'	=> array('VCHAR:1', 't'), +						'user_post_sortby_dir'	=> array('VCHAR:1', 'a'), +						'user_notify'	=> array('BOOL', 0), +						'user_notify_pm'	=> array('BOOL', 1), +						'user_notify_type'	=> array('TINT:4', 0), +						'user_allow_pm'	=> array('BOOL', 1), +						'user_allow_viewonline'	=> array('BOOL', 1), +						'user_allow_viewemail'	=> array('BOOL', 1), +						'user_allow_massemail'	=> array('BOOL', 1), +						'user_options'	=> array('UINT:11', 895), +						'user_avatar'	=> array('VCHAR', ''), +						'user_avatar_type'	=> array('TINT:2', 0), +						'user_avatar_width'	=> array('USINT', 0), +						'user_avatar_height'	=> array('USINT', 0), +						'user_sig'	=> array('MTEXT_UNI', ''), +						'user_sig_bbcode_uid'	=> array('VCHAR:8', ''), +						'user_sig_bbcode_bitfield'	=> array('VCHAR:255', ''), +						'user_from'	=> array('VCHAR_UNI:100', ''), +						'user_icq'	=> array('VCHAR:15', ''), +						'user_aim'	=> array('VCHAR_UNI', ''), +						'user_yim'	=> array('VCHAR_UNI', ''), +						'user_msnm'	=> array('VCHAR_UNI', ''), +						'user_jabber'	=> array('VCHAR_UNI', ''), +						'user_website'	=> array('VCHAR_UNI:200', ''), +						'user_occ'	=> array('TEXT_UNI', ''), +						'user_interests'	=> array('TEXT_UNI', ''), +						'user_actkey'	=> array('VCHAR:32', ''), +						'user_newpasswd'	=> array('VCHAR_UNI:40', ''), +						'user_form_salt'	=> array('VCHAR_UNI:32', ''), + +					), +					'PRIMARY_KEY'	=> 'user_id', +					'KEYS'	=> array( +						'user_birthday'	=> array('INDEX', 'user_birthday'), +						'user_email_hash'	=> array('INDEX', 'user_email_hash'), +						'user_type'	=> array('INDEX', 'user_type'), +						'username_clean'	=> array('UNIQUE', 'username_clean'), +					), +				), + +				$this->table_prefix . 'warnings'	=> array( +					'COLUMNS'	=> array( +						'warning_id'	=> array('UINT', NULL, 'auto_increment'), +						'user_id'	=> array('UINT', 0), +						'post_id'	=> array('UINT', 0), +						'log_id'	=> array('UINT', 0), +						'warning_time'	=> array('TIMESTAMP', 0), +					), +					'PRIMARY_KEY'	=> 'warning_id', +				), + +				$this->table_prefix . 'words'	=> array( +					'COLUMNS'	=> array( +						'word_id'	=> array('UINT', NULL, 'auto_increment'), +						'word'	=> array('VCHAR_UNI', ''), +						'replacement'	=> array('VCHAR_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'word_id', +				), + +				$this->table_prefix . 'zebra'	=> array( +					'COLUMNS'	=> array( +						'user_id'	=> array('UINT', 0), +						'zebra_id'	=> array('UINT', 0), +						'friend'	=> array('BOOL', 0), +						'foe'	=> array('BOOL', 0), +					), +					'PRIMARY_KEY'	=> array('user_id', 'zebra_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables' => array( +				$this->table_prefix . 'attachments', +				$this->table_prefix . 'acl_groups', +				$this->table_prefix . 'acl_options', +				$this->table_prefix . 'acl_roles', +				$this->table_prefix . 'acl_roles_data', +				$this->table_prefix . 'acl_users', +				$this->table_prefix . 'banlist', +				$this->table_prefix . 'bbcodes', +				$this->table_prefix . 'bookmarks', +				$this->table_prefix . 'bots', +				$this->table_prefix . 'config', +				$this->table_prefix . 'confirm', +				$this->table_prefix . 'disallow', +				$this->table_prefix . 'drafts', +				$this->table_prefix . 'extensions', +				$this->table_prefix . 'extension_groups', +				$this->table_prefix . 'forums', +				$this->table_prefix . 'forums_access', +				$this->table_prefix . 'forums_track', +				$this->table_prefix . 'forums_watch', +				$this->table_prefix . 'groups', +				$this->table_prefix . 'icons', +				$this->table_prefix . 'lang', +				$this->table_prefix . 'log', +				$this->table_prefix . 'moderator_cache', +				$this->table_prefix . 'modules', +				$this->table_prefix . 'poll_options', +				$this->table_prefix . 'poll_votes', +				$this->table_prefix . 'posts', +				$this->table_prefix . 'privmsgs', +				$this->table_prefix . 'privmsgs_folder', +				$this->table_prefix . 'privmsgs_rules', +				$this->table_prefix . 'privmsgs_to', +				$this->table_prefix . 'profile_fields', +				$this->table_prefix . 'profile_fields_data', +				$this->table_prefix . 'profile_fields_lang', +				$this->table_prefix . 'profile_lang', +				$this->table_prefix . 'ranks', +				$this->table_prefix . 'reports', +				$this->table_prefix . 'reports_reasons', +				$this->table_prefix . 'search_results', +				$this->table_prefix . 'search_wordlist', +				$this->table_prefix . 'search_wordmatch', +				$this->table_prefix . 'sessions', +				$this->table_prefix . 'sessions_keys', +				$this->table_prefix . 'sitelist', +				$this->table_prefix . 'smilies', +				$this->table_prefix . 'styles', +				$this->table_prefix . 'styles_template', +				$this->table_prefix . 'styles_template_data', +				$this->table_prefix . 'styles_theme', +				$this->table_prefix . 'styles_imageset', +				$this->table_prefix . 'styles_imageset_data', +				$this->table_prefix . 'topics', +				$this->table_prefix . 'topics_track', +				$this->table_prefix . 'topics_posted', +				$this->table_prefix . 'topics_watch', +				$this->table_prefix . 'user_group', +				$this->table_prefix . 'users', +				$this->table_prefix . 'warnings', +				$this->table_prefix . 'words', +				$this->table_prefix . 'zebra', +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php new file mode 100644 index 0000000000..f5c7e56a81 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_1_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php new file mode 100644 index 0000000000..0d3a1ca80b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_10 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.10', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php new file mode 100644 index 0000000000..293c46cb06 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php @@ -0,0 +1,36 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_10_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.10-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_9'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('email_max_chunk_size', 50)), + +			array('config.update', array('version', '3.0.10-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php new file mode 100644 index 0000000000..f2889120e6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_10_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.10-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php new file mode 100644 index 0000000000..9d6697ab7f --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_10_rc3 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.10-RC3', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.10-RC3')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php new file mode 100644 index 0000000000..e77b54a1b5 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_11 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.11', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.11')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php new file mode 100644 index 0000000000..ed2dabf51c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php @@ -0,0 +1,101 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_11_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.11-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_10'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'cleanup_deactivated_styles'))), +			array('custom', array(array(&$this, 'delete_orphan_private_messages'))), + +			array('config.update', array('version', '3.0.11-RC1')), +		); +	} + +	public function cleanup_deactivated_styles() +	{ +		// Updates users having current style a deactivated one +		$sql = 'SELECT style_id +			FROM ' . STYLES_TABLE . ' +			WHERE style_active = 0'; +		$result = $this->sql_query($sql); + +		$deactivated_style_ids = array(); +		while ($style_id = $this->db->sql_fetchfield('style_id', false, $result)) +		{ +			$deactivated_style_ids[] = (int) $style_id; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($deactivated_style_ids)) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' +				SET user_style = ' . (int) $this->config['default_style'] .' +				WHERE ' . $this->db->sql_in_set('user_style', $deactivated_style_ids); +			$this->sql_query($sql); +		} +	} + +	public function delete_orphan_private_messages() +	{ +		// Delete orphan private messages +		$batch_size = 500; + +		$sql_array = array( +			'SELECT'	=> 'p.msg_id', +			'FROM'		=> array( +				PRIVMSGS_TABLE	=> 'p', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(PRIVMSGS_TO_TABLE => 't'), +					'ON'	=> 'p.msg_id = t.msg_id', +				), +			), +			'WHERE'		=> 't.user_id IS NULL', +		); +		$sql = $this->db->sql_build_query('SELECT', $sql_array); + +		$result = $this->db->sql_query_limit($sql, $batch_size); + +		$delete_pms = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$delete_pms[] = (int) $row['msg_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($delete_pms)) +		{ +			$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' +				WHERE ' . $this->db->sql_in_set('msg_id', $delete_pms); +			$this->sql_query($sql); + +			// Return false to have the Migrator call this function again +			return false; +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php new file mode 100644 index 0000000000..45d88708bd --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php @@ -0,0 +1,56 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_11_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.11-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue' => array('BOOL', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.11-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php new file mode 100644 index 0000000000..c489c0c0e8 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_12 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.12', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				phpbb_version_compare($this->config['version'], '3.0.12', '<'), +				array('config.update', array('version', '3.0.12')), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php new file mode 100644 index 0000000000..8600472cca --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php @@ -0,0 +1,129 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +/** @todo DROP LOGIN_ATTEMPT_TABLE.attempt_id in 3.0.12-RC1 **/ + +class release_3_0_12_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.12-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_module_auth'))), +			array('custom', array(array(&$this, 'update_bots'))), +			array('custom', array(array(&$this, 'disable_bots_from_receiving_pms'))), + +			array('config.update', array('version', '3.0.12-RC1')), +		); +	} + +	public function disable_bots_from_receiving_pms() +	{ +		// Disable receiving pms for bots +		$sql = 'SELECT user_id +			FROM ' . BOTS_TABLE; +		$result = $this->db->sql_query($sql); + +		$bot_user_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$bot_user_ids[] = (int) $row['user_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($bot_user_ids)) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' +				SET user_allow_pm = 0 +				WHERE ' . $this->db->sql_in_set('user_id', $bot_user_ids); +			$this->sql_query($sql); +		} +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . ' +			SET module_auth = \'acl_u_sig\' +			WHERE module_class = \'ucp\' +				AND module_basename = \'profile\' +				AND module_mode = \'signature\''; +		$this->sql_query($sql); +	} + +	public function update_bots() +	{ +		// Update bots +		if (!function_exists('user_delete')) +		{ +			include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +		} + +		$bots_updates = array( +			// Bot Deletions +			'NG-Search [Bot]'		=> false, +			'Nutch/CVS [Bot]'		=> false, +			'OmniExplorer [Bot]'	=> false, +			'Seekport [Bot]'		=> false, +			'Synoo [Bot]'			=> false, +			'WiseNut [Bot]'			=> false, + +			// Bot Updates +			// Bot name to bot user agent map +			'Baidu [Spider]'	=> 'Baiduspider', +			'Exabot [Bot]'		=> 'Exabot', +			'Voyager [Bot]'		=> 'voyager/', +			'W3C [Validator]'	=> 'W3C_Validator', +		); + +		foreach ($bots_updates as $bot_name => $bot_agent) +		{ +			$sql = 'SELECT user_id +				FROM ' . USERS_TABLE . ' +				WHERE user_type = ' . USER_IGNORE . " +					AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'"; +			$result = $this->db->sql_query($sql); +			$bot_user_id = (int) $this->db->sql_fetchfield('user_id'); +			$this->db->sql_freeresult($result); + +			if ($bot_user_id) +			{ +				if ($bot_agent === false) +				{ +					$sql = 'DELETE FROM ' . BOTS_TABLE . " +						WHERE user_id = $bot_user_id"; +					$this->sql_query($sql); + +					user_delete('retain', $bot_user_id); +				} +				else +				{ +					$sql = 'UPDATE ' . BOTS_TABLE . " +						SET bot_agent = '" .  $this->db->sql_escape($bot_agent) . "' +						WHERE user_id = $bot_user_id"; +					$this->sql_query($sql); +				} +			} +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php new file mode 100644 index 0000000000..8fac273073 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_12_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.12-RC2', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				phpbb_version_compare($this->config['version'], '3.0.12-RC2', '<'), +				array('config.update', array('version', '3.0.12-RC2')), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php new file mode 100644 index 0000000000..fb1b8014a4 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_12_rc3 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.12-RC3', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				phpbb_version_compare($this->config['version'], '3.0.12-RC3', '<'), +				array('config.update', array('version', '3.0.12-RC3')), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php new file mode 100644 index 0000000000..d1ae0b9cbc --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php @@ -0,0 +1,119 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_1_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.1-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_0'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'forums' => array( +					'display_subforum_list' => array('BOOL', 1), +				), +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array('UINT', 0), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'groups' => array( +					'group_legend', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array('session_forum_id'), +				), +				$this->table_prefix . 'groups' => array( +					'group_legend_name' => array('group_legend', 'group_name'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'forums' => array( +					'display_subforum_list', +				), +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'groups' => array( +					'group_legend' => array('group_legend'), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +				$this->table_prefix . 'groups' => array( +					'group_legend_name', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'fix_unset_last_view_time'))), +			array('custom', array(array(&$this, 'reset_smiley_size'))), + +			array('config.update', array('version', '3.0.1-RC1')), +		); +	} + +	public function fix_unset_last_view_time() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . "topics +			SET topic_last_view_time = topic_last_post_time +			WHERE topic_last_view_time = 0"; +		$this->sql_query($sql); +	} + +	public function reset_smiley_size() +	{ +		// Update smiley sizes +		$smileys = array('icon_e_surprised.gif', 'icon_eek.gif', 'icon_cool.gif', 'icon_lol.gif', 'icon_mad.gif', 'icon_razz.gif', 'icon_redface.gif', 'icon_cry.gif', 'icon_evil.gif', 'icon_twisted.gif', 'icon_rolleyes.gif', 'icon_exclaim.gif', 'icon_question.gif', 'icon_idea.gif', 'icon_arrow.gif', 'icon_neutral.gif', 'icon_mrgreen.gif', 'icon_e_ugeek.gif'); + +		foreach ($smileys as $smiley) +		{ +			if (file_exists($this->phpbb_root_path . 'images/smilies/' . $smiley)) +			{ +				list($width, $height) = getimagesize($this->phpbb_root_path . 'images/smilies/' . $smiley); + +				$sql = 'UPDATE ' . SMILIES_TABLE . ' +					SET smiley_width = ' . $width . ', smiley_height = ' . $height . " +					WHERE smiley_url = '" . $this->db->sql_escape($smiley) . "'"; + +				$this->sql_query($sql); +			} +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php new file mode 100644 index 0000000000..c08f01dbeb --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_2_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php new file mode 100644 index 0000000000..2e7f141a9b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php @@ -0,0 +1,38 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_2_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.2-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('referer_validation', '1')), +			array('config.add', array('check_attachment_content', '1')), +			array('config.add', array('mime_triggers', 'body|head|html|img|plaintext|a href|pre|script|table|title')), + +			array('config.update', array('version', '3.0.2-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php new file mode 100644 index 0000000000..bde5febc59 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php @@ -0,0 +1,86 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_2_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.2-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_2_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns' => array( +				$this->table_prefix . 'drafts' => array( +					'draft_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'forums' => array( +					'forum_last_post_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'posts' => array( +					'post_subject' => array('STEXT_UNI', '', 'true_sort'), +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_subject' => array('STEXT_UNI', ''), +				), +				$this->table_prefix . 'topics' => array( +					'topic_title' => array('STEXT_UNI', '', 'true_sort'), +					'topic_last_post_subject' => array('STEXT_UNI', ''), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_fid' => array('session_forum_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_index' => array( +				$this->table_prefix . 'sessions' => array( +					'session_forum_id' => array( +						'session_forum_id', +					), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'sessions' => array( +					'session_fid', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.2-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php new file mode 100644 index 0000000000..c277da224c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_3 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.3', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_3_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.3')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php new file mode 100644 index 0000000000..530eaf47e6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php @@ -0,0 +1,89 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_3_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.3-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_2'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'styles_template' => array( +					'template_inherits_id' => array('UINT:4', 0), +					'template_inherit_path' => array('VCHAR', ''), +				), +				$this->table_prefix . 'groups' => array( +					'group_max_recipients' => array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'styles_template' => array( +					'template_inherits_id', +					'template_inherit_path', +				), +				$this->table_prefix . 'groups' => array( +					'group_max_recipients', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('enable_queue_trigger', '0')), +			array('config.add', array('queue_trigger_posts', '3')), +			array('config.add', array('pm_max_recipients', '0')), +			array('custom', array(array(&$this, 'set_group_default_max_recipients'))), +			array('config.add', array('dbms_version', $this->db->sql_server_info(true))), +			array('permission.add', array('u_masspm_group', true, 'u_masspm')), +			array('custom', array(array(&$this, 'correct_acp_email_permissions'))), + +			array('config.update', array('version', '3.0.3-RC1')), +		); +	} + +	public function correct_acp_email_permissions() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . 'modules +			SET module_auth = \'acl_a_email && cfg_email_enable\' +			WHERE module_class = \'acp\' +				AND module_basename = \'email\''; +		$this->sql_query($sql); +	} + +	public function set_group_default_max_recipients() +	{ +		// Set maximum number of recipients for the registered users, bots, guests group +		$sql = 'UPDATE ' . GROUPS_TABLE . ' SET group_max_recipients = 5 +			WHERE ' . $this->db->sql_in_set('group_name', array('GUESTS', 'REGISTERED', 'REGISTERED_COPPA', 'BOTS')); +		$this->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php new file mode 100644 index 0000000000..db3c587f82 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_4 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.4', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_4_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'rename_log_delete_topic'))), + +			array('config.update', array('version', '3.0.4')), +		); +	} + +	public function rename_log_delete_topic() +	{ +		if ($this->db->sql_layer == 'oracle') +		{ +			// log_operation is CLOB - but we can change this later +			$sql = 'UPDATE ' . $this->table_prefix . "log +				SET log_operation = 'LOG_DELETE_TOPIC' +				WHERE log_operation LIKE 'LOG_TOPIC_DELETED'"; +			$this->sql_query($sql); +		} +		else +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "log +				SET log_operation = 'LOG_DELETE_TOPIC' +				WHERE log_operation = 'LOG_TOPIC_DELETED'"; +			$this->sql_query($sql); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php new file mode 100644 index 0000000000..10343438b3 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php @@ -0,0 +1,129 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_4_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.4-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_3'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_profile' => array('BOOL', 0), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'styles' => array( +					'style_id' => array('UINT', NULL, 'auto_increment'), +					'template_id' => array('UINT', 0), +					'theme_id' => array('UINT', 0), +					'imageset_id' => array('UINT', 0), +				), +				$this->table_prefix . 'styles_imageset' => array( +					'imageset_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_imageset_data' => array( +					'image_id' => array('UINT', NULL, 'auto_increment'), +					'imageset_id' => array('UINT', 0), +				), +				$this->table_prefix . 'styles_theme' => array( +					'theme_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_template' => array( +					'template_id' => array('UINT', NULL, 'auto_increment'), +				), +				$this->table_prefix . 'styles_template_data' => array( +					'template_id' => array('UINT', 0), +				), +				$this->table_prefix . 'forums' => array( +					'forum_style' => array('UINT', 0), +				), +				$this->table_prefix . 'users' => array( +					'user_style' => array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_profile', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_custom_profile_fields'))), + +			array('config.update', array('version', '3.0.4-RC1')), +		); +	} + +	public function update_custom_profile_fields() +	{ +		// Update the Custom Profile Fields based on previous settings to the new \format +		$sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide +				FROM ' . PROFILE_FIELDS_TABLE; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_ary = array( +				'field_required'	=> 0, +				'field_show_on_reg'	=> 0, +				'field_hide'		=> 0, +				'field_show_profile'=> 0, +			); + +			if ($row['field_required']) +			{ +				$sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1; +			} +			else if ($row['field_show_on_reg']) +			{ +				$sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1; +			} +			else if ($row['field_hide']) +			{ +				// Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module +				$sql_ary['field_hide'] = 1; +			} +			else +			{ +				// equivelant to "none", which is the "Display in user control panel" option +				$sql_ary['field_show_profile'] = 1; +			} + +			$this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary); +		} + +		$this->db->sql_freeresult($result); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php new file mode 100644 index 0000000000..09c2bfea50 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_5 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.5', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_5_rc1part2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.5')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php new file mode 100644 index 0000000000..f593c32181 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php @@ -0,0 +1,130 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_5_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.5-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_4'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns' => array( +				$this->table_prefix . 'forums' => array( +					'forum_style' => array('UINT', 0), +				), +			), +		); +	} + +	public function update_data() +	{ +		$search_indexing_state = $this->config['search_indexing_state']; + +		return array( +			array('config.add', array('captcha_gd_wave', 0)), +			array('config.add', array('captcha_gd_3d_noise', 1)), +			array('config.add', array('captcha_gd_fonts', 1)), +			array('config.add', array('confirm_refresh', 1)), +			array('config.add', array('max_num_search_keywords', 10)), +			array('config.remove', array('search_indexing_state')), +			array('config.add', array('search_indexing_state', $search_indexing_state, true)), +			array('custom', array(array(&$this, 'hash_old_passwords'))), +			array('custom', array(array(&$this, 'update_ichiro_bot'))), +		); +	} + +	public function hash_old_passwords() +	{ +		$sql = 'SELECT user_id, user_password +				FROM ' . $this->table_prefix . 'users +				WHERE user_pass_convert = 1'; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (strlen($row['user_password']) == 32) +			{ +				$sql_ary = array( +					'user_password'	=> phpbb_hash($row['user_password']), +				); + +				$this->sql_query('UPDATE ' . $this->table_prefix . 'users SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE user_id = ' . $row['user_id']); +			} +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_ichiro_bot() +	{ +		// Adjust bot entry +		$sql = 'UPDATE ' . $this->table_prefix . "bots +			SET bot_agent = 'ichiro/' +			WHERE bot_agent = 'ichiro/2'"; +		$this->sql_query($sql); +	} + +	public function remove_duplicate_auth_options() +	{ +		// Before we are able to add a unique key to auth_option, we need to remove duplicate entries +		$sql = 'SELECT auth_option +			FROM ' . $this->table_prefix . 'acl_options +			GROUP BY auth_option +			HAVING COUNT(*) >= 2'; +		$result = $this->db->sql_query($sql); + +		$auth_options = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$auth_options[] = $row['auth_option']; +		} +		$this->db->sql_freeresult($result); + +		// Remove specific auth options +		if (!empty($auth_options)) +		{ +			foreach ($auth_options as $option) +			{ +				// Select auth_option_ids... the largest id will be preserved +				$sql = 'SELECT auth_option_id +					FROM ' . ACL_OPTIONS_TABLE . " +					WHERE auth_option = '" . $db->sql_escape($option) . "' +					ORDER BY auth_option_id DESC"; +				// sql_query_limit not possible here, due to bug in postgresql layer +				$result = $this->db->sql_query($sql); + +				// Skip first row, this is our original auth option we want to preserve +				$row = $this->db->sql_fetchrow($result); + +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					// Ok, remove this auth option... +					$this->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +					$this->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']); +				} +				$this->db->sql_freeresult($result); +			} +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php new file mode 100644 index 0000000000..a9041ef354 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php @@ -0,0 +1,48 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_5_rc1part2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.5-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_5_rc1'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_keys'			=> array( +				$this->table_prefix . 'acl_options'		=> array('auth_option'), +			), +			'add_unique_index'	=> array( +				$this->table_prefix . 'acl_options'		=> array( +					'auth_option'		=> array('auth_option'), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.5-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php new file mode 100644 index 0000000000..74c338a9c5 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_6 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.6', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc4'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php new file mode 100644 index 0000000000..faef68121d --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php @@ -0,0 +1,330 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_6_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.6-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_5'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'confirm' => array( +					'attempts' => array('UINT', 0), +				), +				$this->table_prefix . 'users' => array( +					'user_new' => array('BOOL', 1), +					'user_reminded' => array('TINT:4', 0), +					'user_reminded_time' => array('TIMESTAMP', 0), +				), +				$this->table_prefix . 'groups' => array( +					'group_skip_auth' => array('BOOL', 0, 'after' => 'group_founder_manage'), +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_reported' => array('BOOL', 0), +				), +				$this->table_prefix . 'reports' => array( +					'pm_id' => array('UINT', 0), +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_vt' => array('BOOL', 0), +				), +				$this->table_prefix . 'forums' => array( +					'forum_options' => array('UINT:20', 0), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'users' => array( +					'user_options' => array('UINT:11', 230271), +				), +			), +			'add_index' => array( +				$this->table_prefix . 'reports' => array( +					'post_id' => array('post_id'), +					'pm_id' => array('pm_id'), +				), +				$this->table_prefix . 'posts' => array( +					'post_username' => array('post_username:255'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'confirm' => array( +					'attempts', +				), +				$this->table_prefix . 'users' => array( +					'user_new', +					'user_reminded', +					'user_reminded_time', +				), +				$this->table_prefix . 'groups' => array( +					'group_skip_auth', +				), +				$this->table_prefix . 'privmsgs' => array( +					'message_reported', +				), +				$this->table_prefix . 'reports' => array( +					'pm_id', +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_vt', +				), +				$this->table_prefix . 'forums' => array( +					'forum_options', +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'reports' => array( +					'post_id', +					'pm_id', +				), +				$this->table_prefix . 'posts' => array( +					'post_username', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('captcha_plugin', 'phpbb_captcha_nogd')), +			array('if', array( +				($this->config['captcha_gd']), +				array('config.update', array('captcha_plugin', 'phpbb_captcha_gd')), +			)), + +			array('config.add', array('feed_enable', 0)), +			array('config.add', array('feed_limit', 10)), +			array('config.add', array('feed_overall_forums', 1)), +			array('config.add', array('feed_overall_forums_limit', 15)), +			array('config.add', array('feed_overall_topics', 0)), +			array('config.add', array('feed_overall_topics_limit', 15)), +			array('config.add', array('feed_forum', 1)), +			array('config.add', array('feed_topic', 1)), +			array('config.add', array('feed_item_statistics', 1)), + +			array('config.add', array('smilies_per_page', 50)), +			array('config.add', array('allow_pm_report', 1)), +			array('config.add', array('min_post_chars', 1)), +			array('config.add', array('allow_quick_reply', 1)), +			array('config.add', array('new_member_post_limit', 0)), +			array('config.add', array('new_member_group_default', 0)), +			array('config.add', array('delete_time', $this->config['edit_time'])), + +			array('config.add', array('allow_avatar', 0)), +			array('if', array( +				($this->config['allow_avatar_upload'] || $this->config['allow_avatar_local'] || $this->config['allow_avatar_remote']), +				array('config.update', array('allow_avatar', 1)), +			)), +			array('config.add', array('allow_avatar_remote_upload', 0)), +			array('if', array( +				($this->config['allow_avatar_remote'] && $this->config['allow_avatar_upload']), +				array('config.update', array('allow_avatar_remote_upload', 1)), +			)), + +			array('module.add', array( +				'acp', +				'ACP_BOARD_CONFIGURATION', +				array( +					'module_basename'	=> 'acp_board', +					'modes'				=> array('feed'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_CAT_USERS', +				array( +					'module_basename'	=> 'acp_users', +					'modes'				=> array('warnings'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_SERVER_CONFIGURATION', +				array( +					'module_basename'	=> 'acp_send_statistics', +					'modes'				=> array('send_statistics'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_FORUM_BASED_PERMISSIONS', +				array( +					'module_basename'	=> 'acp_permissions', +					'modes'				=> array('setting_forum_copy'), +				), +			)), +			array('module.add', array( +				'mcp', +				'MCP_REPORTS', +				array( +					'module_basename'	=> 'mcp_pm_reports', +					'modes'				=> array('pm_reports','pm_reports_closed','pm_report_details'), +				), +			)), +			array('custom', array(array(&$this, 'add_newly_registered_group'))), +			array('custom', array(array(&$this, 'set_user_options_default'))), + +			array('config.update', array('version', '3.0.6-RC1')), +		); +	} + +	public function set_user_options_default() +	{ +		// 229376 is the added value to enable all three signature options +		$sql = 'UPDATE ' . USERS_TABLE . ' SET user_options = user_options + 229376'; +		$this->sql_query($sql); +	} + +	public function add_newly_registered_group() +	{ +		// Add newly_registered group... but check if it already exists (we always supported running the updater on any schema) +		$sql = 'SELECT group_id +			FROM ' . GROUPS_TABLE . " +			WHERE group_name = 'NEWLY_REGISTERED'"; +		$result = $this->db->sql_query($sql); +		$group_id = (int) $this->db->sql_fetchfield('group_id'); +		$this->db->sql_freeresult($result); + +		if (!$group_id) +		{ +			$sql = 'INSERT INTO ' .  GROUPS_TABLE . " (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('NEWLY_REGISTERED', 3, 0, '', 0, '', '', '', 5)"; +			$this->sql_query($sql); + +			$group_id = $this->db->sql_nextid(); +		} + +		// Insert new user role... at the end of the chain +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = 'ROLE_USER_NEW_MEMBER' +				AND role_type = 'u_'"; +		$result = $this->db->sql_query($sql); +		$u_role = (int) $this->db->sql_fetchfield('role_id'); +		$this->db->sql_freeresult($result); + +		if (!$u_role) +		{ +			$sql = 'SELECT MAX(role_order) as max_order_id +				FROM ' . ACL_ROLES_TABLE . " +				WHERE role_type = 'u_'"; +			$result = $this->db->sql_query($sql); +			$next_order_id = (int) $this->db->sql_fetchfield('max_order_id'); +			$this->db->sql_freeresult($result); + +			$next_order_id++; + +			$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_USER_NEW_MEMBER', 'ROLE_DESCRIPTION_USER_NEW_MEMBER', 'u_', $next_order_id)"; +			$this->sql_query($sql); +			$u_role = $this->db->sql_nextid(); + +			// Now add the correct data to the roles... +			// The standard role says that new users are not able to send a PM, Mass PM, are not able to PM groups +			$sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $u_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'u_%' AND auth_option IN ('u_sendpm', 'u_masspm', 'u_masspm_group')"; +			$this->sql_query($sql); + +			// Add user role to group +			$sql = 'INSERT INTO ' . ACL_GROUPS_TABLE . " (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES ($group_id, 0, 0, $u_role, 0)"; +			$this->sql_query($sql); +		} + +		// Insert new forum role +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = 'ROLE_FORUM_NEW_MEMBER' +				AND role_type = 'f_'"; +		$result = $this->db->sql_query($sql); +		$f_role = (int) $this->db->sql_fetchfield('role_id'); +		$this->db->sql_freeresult($result); + +		if (!$f_role) +		{ +			$sql = 'SELECT MAX(role_order) as max_order_id +				FROM ' . ACL_ROLES_TABLE . " +				WHERE role_type = 'f_'"; +			$result = $this->db->sql_query($sql); +			$next_order_id = (int) $this->db->sql_fetchfield('max_order_id'); +			$this->db->sql_freeresult($result); + +			$next_order_id++; + +			$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES  ('ROLE_FORUM_NEW_MEMBER', 'ROLE_DESCRIPTION_FORUM_NEW_MEMBER', 'f_', $next_order_id)"; +			$this->sql_query($sql); +			$f_role = $this->db->sql_nextid(); + +			$sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $f_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'f_%' AND auth_option IN ('f_noapprove')"; +			$this->sql_query($sql); +		} + +		// Set every members user_new column to 0 (old users) only if there is no one yet (this makes sure we do not execute this more than once) +		$sql = 'SELECT 1 +			FROM ' . USERS_TABLE . ' +			WHERE user_new = 0'; +		$result = $this->db->sql_query_limit($sql, 1); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if (!$row) +		{ +			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_new = 0'; +			$this->sql_query($sql); +		} + +		// To mimick the old "feature" we will assign the forum role to every forum, regardless of the setting (this makes sure there are no "this does not work!!!! YUO!!!" posts... +		// Check if the role is already assigned... +		$sql = 'SELECT forum_id +			FROM ' . ACL_GROUPS_TABLE . ' +			WHERE group_id = ' . $group_id . ' +				AND auth_role_id = ' . $f_role; +		$result = $this->db->sql_query($sql); +		$is_options = (int) $this->db->sql_fetchfield('forum_id'); +		$this->db->sql_freeresult($result); + +		// Not assigned at all... :/ +		if (!$is_options) +		{ +			// Get postable forums +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_type != ' . FORUM_LINK; +			$result = $this->db->sql_query($sql); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$this->sql_query('INSERT INTO ' . ACL_GROUPS_TABLE . ' (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (' . $group_id . ', ' . (int) $row['forum_id'] . ', 0, ' . $f_role . ', 0)'); +			} +			$this->db->sql_freeresult($result); +		} + +		// Clear permissions... +		include_once($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext); +		$auth_admin = new \auth_admin(); +		$auth_admin->acl_clear_prefetch(); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php new file mode 100644 index 0000000000..c52b71d8fa --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_6_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.6-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php new file mode 100644 index 0000000000..2db3341b0a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php @@ -0,0 +1,46 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_6_rc3 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.6-RC3', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_cp_fields'))), + +			array('config.update', array('version', '3.0.6-RC3')), +		); +	} + +	public function update_cp_fields() +	{ +		// Update the Custom Profile Fields based on previous settings to the new \format +		$sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . ' +			SET field_show_on_vt = 1 +			WHERE field_hide = 0 +				AND (field_required = 1 OR field_show_on_reg = 1 OR field_show_profile = 1)'; +		$this->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php new file mode 100644 index 0000000000..5734db25e6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_6_rc4 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.6-RC4', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.6-RC4')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php new file mode 100644 index 0000000000..d1d602819d --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_7 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.7', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_7_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.7')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php new file mode 100644 index 0000000000..784e810f14 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_7_pl1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.7-pl1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_7'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.7-pl1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php new file mode 100644 index 0000000000..d86c4d6986 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php @@ -0,0 +1,82 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_7_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.7-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_6'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_keys' => array( +				$this->table_prefix . 'log' => array( +					'log_time', +				), +			), +			'add_index' => array( +				$this->table_prefix . 'topics_track' => array( +					'topic_id' => array('topic_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_index' => array( +				$this->table_prefix . 'log' => array( +					'log_time'	=> array('log_time'), +				), +			), +			'drop_keys' => array( +				$this->table_prefix . 'topics_track' => array( +					'topic_id', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('feed_overall', 1)), +			array('config.add', array('feed_http_auth', 0)), +			array('config.add', array('feed_limit_post', $this->config['feed_limit'])), +			array('config.add', array('feed_limit_topic', $this->config['feed_overall_topics_limit'])), +			array('config.add', array('feed_topics_new', $this->config['feed_overall_topics'])), +			array('config.add', array('feed_topics_active', $this->config['feed_overall_topics'])), +			array('custom', array(array(&$this, 'delete_text_templates'))), + +			array('config.update', array('version', '3.0.7-RC1')), +		); +	} + +	public function delete_text_templates() +	{ +		// Delete all text-templates from the template_data +		$sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . ' +			WHERE template_filename ' . $this->db->sql_like_expression($this->db->any_char . '.txt'); +		$this->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php new file mode 100644 index 0000000000..e497a38765 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php @@ -0,0 +1,79 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_7_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.7-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_7_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_email_hash'))), + +			array('config.update', array('version', '3.0.7-RC2')), +		); +	} + +	public function update_email_hash($start = 0) +	{ +		$limit = 1000; + +		$sql = 'SELECT user_id, user_email, user_email_hash +			FROM ' . USERS_TABLE . ' +			WHERE user_type <> ' . USER_IGNORE . " +				AND user_email <> ''"; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		$i = 0; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$i++; + +			// Snapshot of the phpbb_email_hash() function +			// We cannot call it directly because the auto updater updates the DB first. :/ +			$user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']); + +			if ($user_email_hash != $row['user_email_hash']) +			{ +				$sql_ary = array( +					'user_email_hash'	=> $user_email_hash, +				); + +				$sql = 'UPDATE ' . USERS_TABLE . ' +					SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +					WHERE user_id = ' . (int) $row['user_id']; +				$this->sql_query($sql); +			} +		} +		$this->db->sql_freeresult($result); + +		if ($i < $limit) +		{ +			// Completed +			return; +		} + +		// Return the next start, will be sent to $start when this function is called again +		return $start + $limit; +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php new file mode 100644 index 0000000000..04b5bd4a13 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_8 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.8', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_8_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.8')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php new file mode 100644 index 0000000000..4e863fa143 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php @@ -0,0 +1,227 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_8_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.8-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_7_pl1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array(&$this, 'update_file_extension_group_names'))), +			array('custom', array(array(&$this, 'update_module_auth'))), +			array('custom', array(array(&$this, 'update_bots'))), +			array('custom', array(array(&$this, 'delete_orphan_shadow_topics'))), +			array('module.add', array( +				'acp', +				'ACP_MESSAGES', +				array( +					'module_basename'	=> 'acp_board', +					'modes'				=> array('post'), +				), +			)), +			array('config.add', array('load_unreads_search', 1)), +			array('config.update_if_equals', array(600, 'queue_interval', 60)), +			array('config.update_if_equals', array(50, 'email_package_size', 20)), + +			array('config.update', array('version', '3.0.8-RC1')), +		); +	} + +	public function update_file_extension_group_names() +	{ +		// Update file extension group names to use language strings. +		$sql = 'SELECT lang_dir +			FROM ' . LANG_TABLE; +		$result = $this->db->sql_query($sql); + +		$extension_groups_updated = array(); +		while ($lang_dir = $this->db->sql_fetchfield('lang_dir')) +		{ +			$lang_dir = basename($lang_dir); + +			// The language strings we need are either in language/.../acp/attachments.php +			// in the update package if we're updating to 3.0.8-RC1 or later, +			// or they are in language/.../install.php when we're updating from 3.0.7-PL1 or earlier. +			// On an already updated board, they can also already be in language/.../acp/attachments.php +			// in the board root. +			$lang_files = array( +				"{$this->phpbb_root_path}install/update/new/language/$lang_dir/acp/attachments.{$this->php_ext}", +				"{$this->phpbb_root_path}language/$lang_dir/install.{$this->php_ext}", +				"{$this->phpbb_root_path}language/$lang_dir/acp/attachments.{$this->php_ext}", +			); + +			foreach ($lang_files as $lang_file) +			{ +				if (!file_exists($lang_file)) +				{ +					continue; +				} + +				$lang = array(); +				include($lang_file); + +				foreach($lang as $lang_key => $lang_val) +				{ +					if (isset($extension_groups_updated[$lang_key]) || strpos($lang_key, 'EXT_GROUP_') !== 0) +					{ +						continue; +					} + +					$sql_ary = array( +						'group_name'	=> substr($lang_key, 10), // Strip off 'EXT_GROUP_' +					); + +					$sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . ' +						SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . " +						WHERE group_name = '" . $this->db->sql_escape($lang_val) . "'"; +					$this->sql_query($sql); + +					$extension_groups_updated[$lang_key] = true; +				} +			} +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . ' +			SET module_auth = \'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)\' +			WHERE module_class = \'ucp\' +				AND module_basename = \'profile\' +				AND module_mode = \'avatar\''; +		$this->sql_query($sql); +	} + +	public function update_bots() +	{ +		$bot_name = 'Bing [Bot]'; +		$bot_name_clean = utf8_clean_string($bot_name); + +		$sql = 'SELECT user_id +			FROM ' . USERS_TABLE . " +			WHERE username_clean = '" . $this->db->sql_escape($bot_name_clean) . "'"; +		$result = $this->db->sql_query($sql); +		$bing_already_added = (bool) $this->db->sql_fetchfield('user_id'); +		$this->db->sql_freeresult($result); + +		if (!$bing_already_added) +		{ +			$bot_agent = 'bingbot/'; +			$bot_ip = ''; +			$sql = 'SELECT group_id, group_colour +				FROM ' . GROUPS_TABLE . " +				WHERE group_name = 'BOTS'"; +			$result = $this->db->sql_query($sql); +			$group_row = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); + +			if (!$group_row) +			{ +				// default fallback, should never get here +				$group_row['group_id'] = 6; +				$group_row['group_colour'] = '9E8DA7'; +			} + +			if (!function_exists('user_add')) +			{ +				include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext); +			} + +			$user_row = array( +				'user_type'				=> USER_IGNORE, +				'group_id'				=> $group_row['group_id'], +				'username'				=> $bot_name, +				'user_regdate'			=> time(), +				'user_password'			=> '', +				'user_colour'			=> $group_row['group_colour'], +				'user_email'			=> '', +				'user_lang'				=> $this->config['default_lang'], +				'user_style'			=> $this->config['default_style'], +				'user_timezone'			=> 0, +				'user_dateformat'		=> $this->config['default_dateformat'], +				'user_allow_massemail'	=> 0, +			); + +			$user_id = user_add($user_row); + +			$sql = 'INSERT INTO ' . BOTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', array( +				'bot_active'	=> 1, +				'bot_name'		=> (string) $bot_name, +				'user_id'		=> (int) $user_id, +				'bot_agent'		=> (string) $bot_agent, +				'bot_ip'		=> (string) $bot_ip, +			)); + +			$this->sql_query($sql); +		} +	} + +	public function delete_orphan_shadow_topics() +	{ +		// Delete shadow topics pointing to not existing topics +		$batch_size = 500; + +		// Set of affected forums we have to resync +		$sync_forum_ids = array(); + +		$sql_array = array( +			'SELECT'	=> 't1.topic_id, t1.forum_id', +			'FROM'		=> array( +				TOPICS_TABLE	=> 't1', +			), +			'LEFT_JOIN'	=> array( +				array( +					'FROM'	=> array(TOPICS_TABLE	=> 't2'), +					'ON'	=> 't1.topic_moved_id = t2.topic_id', +				), +			), +			'WHERE'		=> 't1.topic_moved_id <> 0 +						AND t2.topic_id IS NULL', +		); +		$sql = $this->db->sql_build_query('SELECT', $sql_array); +		$result = $this->db->sql_query_limit($sql, $batch_size); + +		$topic_ids = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$topic_ids[] = (int) $row['topic_id']; + +			$sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id']; +		} +		$this->db->sql_freeresult($result); + +		if (!empty($topic_ids)) +		{ +			$sql = 'DELETE FROM ' . TOPICS_TABLE . ' +				WHERE ' . $this->db->sql_in_set('topic_id', $topic_ids); +			$this->db->sql_query($sql); + +			// Sync the forums we have deleted shadow topics from. +			sync('forum', 'forum_id', $sync_forum_ids, true, true); + +			return false; +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php new file mode 100644 index 0000000000..e69134c538 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_9 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.9', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc4'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php new file mode 100644 index 0000000000..24340c8cf1 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php @@ -0,0 +1,130 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_9_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.9-RC1', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_8'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables' => array( +				$this->table_prefix . 'login_attempts' => array( +					'COLUMNS' => array( +						// this column was removed from the database updater +						// after 3.0.9-RC3 was released. It might still exist +						// in 3.0.9-RCX installations and has to be dropped as +						// soon as the db_tools class is capable of properly +						// removing a primary key. +						// 'attempt_id'			=> array('UINT', NULL, 'auto_increment'), +						'attempt_ip'			=> array('VCHAR:40', ''), +						'attempt_browser'		=> array('VCHAR:150', ''), +						'attempt_forwarded_for'	=> array('VCHAR:255', ''), +						'attempt_time'			=> array('TIMESTAMP', 0), +						'user_id'				=> array('UINT', 0), +						'username'				=> array('VCHAR_UNI:255', 0), +						'username_clean'		=> array('VCHAR_CI', 0), +					), +					//'PRIMARY_KEY' => 'attempt_id', +					'KEYS' => array( +						'att_ip' => array('INDEX', array('attempt_ip', 'attempt_time')), +						'att_for' => array('INDEX', array('attempt_forwarded_for', 'attempt_time')), +						'att_time' => array('INDEX', array('attempt_time')), +						'user_id' => array('INDEX', 'user_id'), +					), +				), +			), +			'change_columns' => array( +				$this->table_prefix . 'bbcodes' => array( +					'bbcode_id' => array('USINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables' => array( +				$this->table_prefix . 'login_attempts', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('ip_login_limit_max', 50)), +			array('config.add', array('ip_login_limit_time', 21600)), +			array('config.add', array('ip_login_limit_use_forwarded', 0)), +			array('custom', array(array(&$this, 'update_file_extension_group_names'))), +			array('custom', array(array(&$this, 'fix_firebird_qa_captcha'))), + +			array('config.update', array('version', '3.0.9-RC1')), +		); +	} + +	public function update_file_extension_group_names() +	{ +		// Update file extension group names to use language strings, again. +		$sql = 'SELECT group_id, group_name +			FROM ' . EXTENSION_GROUPS_TABLE . ' +			WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->any_char); +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_ary = array( +				'group_name'	=> substr($row['group_name'], 10), // Strip off 'EXT_GROUP_' +			); + +			$sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . ' +				SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +				WHERE group_id = ' . $row['group_id']; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	public function fix_firebird_qa_captcha() +	{ +		// Recover from potentially broken Q&A CAPTCHA table on firebird +		// Q&A CAPTCHA was uninstallable, so it's safe to remove these +		// without data loss +		if ($this->db_tools->sql_layer == 'firebird') +		{ +			$tables = array( +				$this->table_prefix . 'captcha_questions', +				$this->table_prefix . 'captcha_answers', +				$this->table_prefix . 'qa_confirm', +			); +			foreach ($tables as $table) +			{ +				if ($this->db_tools->sql_table_exists($table)) +				{ +					$this->db_tools->sql_table_drop($table); +				} +			} +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php new file mode 100644 index 0000000000..46fd51e7fe --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_9_rc2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.9-RC2', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php new file mode 100644 index 0000000000..1696060735 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_9_rc3 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.9-RC3', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-RC3')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php new file mode 100644 index 0000000000..fdc92b52f9 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v30x; + +class release_3_0_9_rc4 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.9-RC4', '>='); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc3'); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.0.9-RC4')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php new file mode 100644 index 0000000000..0ca4f2f19c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php @@ -0,0 +1,81 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class acp_prune_users_module extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'acp' +				AND module_langname = 'ACP_CAT_USERS'"; +		$result = $this->db->sql_query($sql); +		$acp_cat_users_id = (int) $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		$sql = 'SELECT parent_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'acp' +				AND module_basename = 'acp_prune' +				AND module_mode = 'users'"; +		$result = $this->db->sql_query($sql); +		$acp_prune_users_parent = (int) $this->db->sql_fetchfield('parent_id'); +		$this->db->sql_freeresult($result); + +		// Skip migration if "Users" category has been deleted +		// or the module has already been moved to that category +		return !$acp_cat_users_id || $acp_cat_users_id === $acp_prune_users_parent; +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\beta1'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'move_prune_users_module'))), +		); +	} + +	public function move_prune_users_module() +	{ +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'acp' +				AND module_basename = 'acp_prune' +				AND module_mode = 'users'"; +		$result = $this->db->sql_query($sql); +		$acp_prune_users_id = (int) $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'acp' +				AND module_langname = 'ACP_CAT_USERS'"; +		$result = $this->db->sql_query($sql); +		$acp_cat_users_id = (int) $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		if (!class_exists('\acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +		} +		$module_manager = new \acp_modules(); +		$module_manager->module_class = 'acp'; +		$module_manager->move_module($acp_prune_users_id, $acp_cat_users_id); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php new file mode 100644 index 0000000000..4bd29f87d7 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php @@ -0,0 +1,46 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class acp_style_components_module extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'acp' +				AND module_langname = 'ACP_STYLE_COMPONENTS'"; +		$result = $this->db->sql_query($sql); +		$module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		return $module_id == false; +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('module.remove', array( +				'acp', +				false, +				'ACP_STYLE_COMPONENTS', +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/allow_cdn.php b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php new file mode 100644 index 0000000000..286d20e20b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class allow_cdn extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['allow_cdn']); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\jquery_update', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('allow_cdn', (int) $this->config['load_jquery_cdn'])), +			array('config.remove', array('load_jquery_cdn')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/alpha1.php b/phpBB/phpbb/db/migration/data/v310/alpha1.php new file mode 100644 index 0000000000..1df85bc64c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/alpha1.php @@ -0,0 +1,48 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class alpha1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v30x\local_url_bbcode', +			'\phpbb\db\migration\data\v30x\release_3_0_12', +			'\phpbb\db\migration\data\v310\acp_style_components_module', +			'\phpbb\db\migration\data\v310\allow_cdn', +			'\phpbb\db\migration\data\v310\auth_provider_oauth', +			'\phpbb\db\migration\data\v310\avatars', +			'\phpbb\db\migration\data\v310\boardindex', +			'\phpbb\db\migration\data\v310\config_db_text', +			'\phpbb\db\migration\data\v310\forgot_password', +			'\phpbb\db\migration\data\v310\mod_rewrite', +			'\phpbb\db\migration\data\v310\mysql_fulltext_drop', +			'\phpbb\db\migration\data\v310\namespaces', +			'\phpbb\db\migration\data\v310\notifications_cron', +			'\phpbb\db\migration\data\v310\notification_options_reconvert', +			'\phpbb\db\migration\data\v310\plupload', +			'\phpbb\db\migration\data\v310\signature_module_auth', +			'\phpbb\db\migration\data\v310\softdelete_mcp_modules', +			'\phpbb\db\migration\data\v310\teampage', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-a1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/alpha2.php b/phpBB/phpbb/db/migration/data/v310/alpha2.php new file mode 100644 index 0000000000..78bc755ec9 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/alpha2.php @@ -0,0 +1,32 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class alpha2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\alpha1', +			'\phpbb\db\migration\data\v310\notifications_cron_p2', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-a2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/alpha3.php b/phpBB/phpbb/db/migration/data/v310/alpha3.php new file mode 100644 index 0000000000..574d19d2f4 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/alpha3.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class alpha3 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\alpha2', +			'\phpbb\db\migration\data\v310\avatar_types', +			'\phpbb\db\migration\data\v310\passwords', +			'\phpbb\db\migration\data\v310\profilefield_types', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-a3')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php new file mode 100644 index 0000000000..2d51bd53e4 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php @@ -0,0 +1,77 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class auth_provider_oauth extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'auth_provider_oauth'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'	=> array( +				$this->table_prefix . 'oauth_tokens'	=> array( +					'COLUMNS' => array( +						'user_id'			=> array('UINT', 0), // phpbb_users.user_id +						'session_id'		=> array('CHAR:32', ''), // phpbb_sessions.session_id used only when user_id not set +						'provider'			=> array('VCHAR', ''), // Name of the OAuth provider +						'oauth_token'		=> array('MTEXT', ''), // Serialized token +					), +					'KEYS' => array( +						'user_id'			=> array('INDEX', 'user_id'), +						'provider'			=> array('INDEX', 'provider'), +					), +				), +				$this->table_prefix . 'oauth_accounts'	=> array( +					'COLUMNS' => array( +						'user_id'			=> array('UINT', 0), +						'provider'			=> array('VCHAR', ''), +						'oauth_provider_id'	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY' => array( +						'user_id', +						'provider', +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'	=> array( +				$this->table_prefix . 'oauth_tokens', +				$this->table_prefix . 'oauth_accounts', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('module.add', array( +				'ucp', +				'UCP_PROFILE', +				array( +					'module_basename'	=> 'ucp_auth_link', +					'modes'				=> array('auth_link'), +				), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php new file mode 100644 index 0000000000..e9e726ae20 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php @@ -0,0 +1,44 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class auth_provider_oauth2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\auth_provider_oauth', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array( +				array($this, 'update_auth_link_module_auth'), +			)), +		); +	} + +	public function update_auth_link_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'authmethod_oauth' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_auth_link' +				AND module_mode = 'auth_link' +				AND module_auth = ''"; +		$this->db->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/avatar_types.php b/phpBB/phpbb/db/migration/data/v310/avatar_types.php new file mode 100644 index 0000000000..117e93239d --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/avatar_types.php @@ -0,0 +1,64 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class avatar_types extends \phpbb\db\migration\migration +{ +	/** +	* @var avatar type map +	*/ +	protected $avatar_type_map = array( +		AVATAR_UPLOAD	=> 'avatar.driver.upload', +		AVATAR_REMOTE	=> 'avatar.driver.remote', +		AVATAR_GALLERY	=> 'avatar.driver.local', +	); + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +			'\phpbb\db\migration\data\v310\avatars', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_user_avatar_type'))), +			array('custom', array(array($this, 'update_group_avatar_type'))), +		); +	} + +	public function update_user_avatar_type() +	{ +		foreach ($this->avatar_type_map as $old => $new) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "users +				SET user_avatar_type = '$new' +				WHERE user_avatar_type = '$old'"; +			$this->db->sql_query($sql); +		} +	} + +	public function update_group_avatar_type() +	{ +		foreach ($this->avatar_type_map as $old => $new) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "groups +				SET group_avatar_type = '$new' +				WHERE group_avatar_type = '$old'"; +			$this->db->sql_query($sql); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/avatars.php b/phpBB/phpbb/db/migration/data/v310/avatars.php new file mode 100644 index 0000000000..2698adeed5 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/avatars.php @@ -0,0 +1,73 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class avatars extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['allow_avatar_gravatar']); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_avatar_type'		=> array('VCHAR:255', ''), +				), +				$this->table_prefix . 'groups'			=> array( +					'group_avatar_type'		=> array('VCHAR:255', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_avatar_type'		=> array('TINT:2', ''), +				), +				$this->table_prefix . 'groups'			=> array( +					'group_avatar_type'		=> array('TINT:2', ''), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('allow_avatar_gravatar', 0)), +			array('custom', array(array($this, 'update_module_auth'))), +		); +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . "modules +			SET module_auth = 'cfg_allow_avatar' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'avatar'"; +		$this->db->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/beta1.php b/phpBB/phpbb/db/migration/data/v310/beta1.php new file mode 100644 index 0000000000..84887bd58e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/beta1.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class beta1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\alpha3', +			'\phpbb\db\migration\data\v310\passwords_p2', +			'\phpbb\db\migration\data\v310\postgres_fulltext_drop', +			'\phpbb\db\migration\data\v310\profilefield_change_load_settings', +			'\phpbb\db\migration\data\v310\profilefield_location', +			'\phpbb\db\migration\data\v310\soft_delete_mod_convert2', +			'\phpbb\db\migration\data\v310\ucp_popuppm_module', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-b1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/beta2.php b/phpBB/phpbb/db/migration/data/v310/beta2.php new file mode 100644 index 0000000000..458e305c7b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/beta2.php @@ -0,0 +1,33 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class beta2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\beta1', +			'\phpbb\db\migration\data\v310\acp_prune_users_module', +			'\phpbb\db\migration\data\v310\profilefield_location_cleanup', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-b2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/beta3.php b/phpBB/phpbb/db/migration/data/v310/beta3.php new file mode 100644 index 0000000000..a6c62bf936 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/beta3.php @@ -0,0 +1,36 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class beta3 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\beta2', +			'\phpbb\db\migration\data\v310\auth_provider_oauth2', +			'\phpbb\db\migration\data\v310\board_contact_name', +			'\phpbb\db\migration\data\v310\jquery_update2', +			'\phpbb\db\migration\data\v310\live_searches_config', +			'\phpbb\db\migration\data\v310\prune_shadow_topics', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-b3')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/beta4.php b/phpBB/phpbb/db/migration/data/v310/beta4.php new file mode 100644 index 0000000000..3e91d95178 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/beta4.php @@ -0,0 +1,33 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class beta4 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\beta3', +			'\phpbb\db\migration\data\v310\extensions_version_check_force_unstable', +			'\phpbb\db\migration\data\v310\reset_missing_captcha_plugin', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-b4')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/board_contact_name.php b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php new file mode 100644 index 0000000000..6f5188720b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class board_contact_name extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['board_contact_name']); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\beta2'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('board_contact_name', '')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/boardindex.php b/phpBB/phpbb/db/migration/data/v310/boardindex.php new file mode 100644 index 0000000000..77a8558f21 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/boardindex.php @@ -0,0 +1,29 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class boardindex extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['board_index_text']); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('board_index_text', '')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/config_db_text.php b/phpBB/phpbb/db/migration/data/v310/config_db_text.php new file mode 100644 index 0000000000..438883c438 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/config_db_text.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class config_db_text extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'config_text'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables' => array( +				$this->table_prefix . 'config_text' => array( +					'COLUMNS' => array( +						'config_name'	=> array('VCHAR', ''), +						'config_value'	=> array('MTEXT', ''), +					), +					'PRIMARY_KEY' => 'config_name', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables' => array( +				$this->table_prefix . 'config_text', +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php b/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php new file mode 100644 index 0000000000..20bd547ac3 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php @@ -0,0 +1,31 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class contact_admin_acp_module extends \phpbb\db\migration\migration +{ +	public function update_data() +	{ +		return array( +			array('module.add', array( +				'acp', +				'ACP_BOARD_CONFIGURATION', +				array( +					'module_basename'	=> 'acp_contact', +					'modes'				=> array('contact'), +				), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php b/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php new file mode 100644 index 0000000000..c2dd09ddf6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php @@ -0,0 +1,41 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class contact_admin_form extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['contact_admin_form_enable']); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('contact_admin_form_enable', 1)), +			array('custom', array(array($this, 'contact_admin_info'))), +		); +	} + +	public function contact_admin_info() +	{ +		$text_config = new \phpbb\config\db_text($this->db, $this->table_prefix . 'config_text'); +		$text_config->set_array(array( +			'contact_admin_info'			=> '', +			'contact_admin_info_uid'		=> '', +			'contact_admin_info_bitfield'	=> '', +			'contact_admin_info_flags'		=> OPTION_FLAG_BBCODE + OPTION_FLAG_SMILIES + OPTION_FLAG_LINKS, +		)); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/dev.php b/phpBB/phpbb/db/migration/data/v310/dev.php new file mode 100644 index 0000000000..f037191c2a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/dev.php @@ -0,0 +1,415 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class dev extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return version_compare($this->config['version'], '3.1.0-dev', '>='); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\extensions', +			'\phpbb\db\migration\data\v310\style_update_p2', +			'\phpbb\db\migration\data\v310\timezone_p2', +			'\phpbb\db\migration\data\v310\reported_posts_display', +			'\phpbb\db\migration\data\v310\migrations_table', +		); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage'	=> array('UINT', 0, 'after' => 'group_legend'), +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_pm'		=> array('BOOL', 0), +				), +				$this->table_prefix . 'styles'		=> array( +					'style_path'			=> array('VCHAR:100', ''), +					'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +					'style_parent_id'		=> array('UINT:4', 0), +					'style_parent_tree'		=> array('TEXT', ''), +				), +				$this->table_prefix . 'reports'		=> array( +					'reported_post_text'		=> array('MTEXT_UNI', ''), +					'reported_post_uid'			=> array('VCHAR:8', ''), +					'reported_post_bitfield'	=> array('VCHAR:255', ''), +				), +			), +			'change_columns'	=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_legend'		=> array('UINT', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage', +				), +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_pm', +				), +				$this->table_prefix . 'styles'		=> array( +					'style_path', +					'bbcode_bitfield', +					'style_parent_id', +					'style_parent_tree', +				), +				$this->table_prefix . 'reports'		=> array( +					'reported_post_text', +					'reported_post_uid', +					'reported_post_bitfield', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				(strpos('phpbb_search_', $this->config['search_type']) !== 0), +				array('config.update', array('search_type', 'phpbb_search_' . $this->config['search_type'])), +			)), + +			array('config.add', array('fulltext_postgres_ts_name', 'simple')), +			array('config.add', array('fulltext_postgres_min_word_len', 4)), +			array('config.add', array('fulltext_postgres_max_word_len', 254)), +			array('config.add', array('fulltext_sphinx_stopwords', 0)), +			array('config.add', array('fulltext_sphinx_indexer_mem_limit', 512)), + +			array('config.add', array('load_jquery_cdn', 0)), +			array('config.add', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')), + +			array('config.add', array('use_system_cron', 0)), + +			array('config.add', array('legend_sort_groupname', 0)), +			array('config.add', array('teampage_forums', 1)), +			array('config.add', array('teampage_memberships', 1)), + +			array('config.add', array('load_cpf_pm', 0)), + +			array('config.add', array('display_last_subject', 1)), + +			array('config.add', array('assets_version', 1)), + +			array('config.add', array('site_home_url', '')), +			array('config.add', array('site_home_text', '')), + +			array('permission.add', array('u_chgprofileinfo', true, 'u_sig')), + +			array('module.add', array( +				'acp', +				'ACP_GROUPS', +				array( +					'module_basename'	=> 'acp_groups', +					'modes'				=> array('position'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_ATTACHMENTS', +				array( +					'module_basename'	=> 'acp_attachments', +					'modes'				=> array('manage'), +				), +			)), +			array('module.add', array( +				'acp', +				'ACP_STYLE_MANAGEMENT', +				array( +					'module_basename'	=> 'acp_styles', +					'modes'				=> array('install', 'cache'), +				), +			)), +			array('module.add', array( +				'ucp', +				'UCP_PROFILE', +				array( +					'module_basename'	=> 'ucp_profile', +					'modes'				=> array('autologin_keys'), +				), +			)), +			// Module will be renamed later +			array('module.add', array( +				'acp', +				'ACP_CAT_STYLES', +				'ACP_LANGUAGE' +			)), + +			array('module.remove', array( +				'acp', +				false, +				'ACP_TEMPLATES', +			)), +			array('module.remove', array( +				'acp', +				false, +				'ACP_THEMES', +			)), +			array('module.remove', array( +				'acp', +				false, +				'ACP_IMAGESETS', +			)), + +			array('custom', array(array($this, 'rename_module_basenames'))), +			array('custom', array(array($this, 'rename_styles_module'))), +			array('custom', array(array($this, 'add_group_teampage'))), +			array('custom', array(array($this, 'update_group_legend'))), +			array('custom', array(array($this, 'localise_global_announcements'))), +			array('custom', array(array($this, 'update_ucp_pm_basename'))), +			array('custom', array(array($this, 'update_ucp_profile_auth'))), +			array('custom', array(array($this, 'move_customise_modules'))), + +			array('config.update', array('version', '3.1.0-dev')), +		); +	} + +	public function move_customise_modules() +	{ +		// Move language management to new location in the Customise tab +		// First get language module id +		$sql = 'SELECT module_id FROM ' . MODULES_TABLE . " +			WHERE module_basename = 'acp_language'"; +		$result = $this->db->sql_query($sql); +		$language_module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); +		// Next get language management module id of the one just created +		$sql = 'SELECT module_id FROM ' . MODULES_TABLE . " +			WHERE module_langname = 'ACP_LANGUAGE'"; +		$result = $this->db->sql_query($sql); +		$language_management_module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		if (!class_exists('acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +		} +		// acp_modules calls adm_back_link, which is undefined at this point +		if (!function_exists('adm_back_link')) +		{ +			include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext); +		} +		$module_manager = new \acp_modules(); +		$module_manager->module_class = 'acp'; +		$module_manager->move_module($language_module_id, $language_management_module_id); +	} + +	public function update_ucp_pm_basename() +	{ +		$sql = 'SELECT module_id, module_basename +			FROM ' . MODULES_TABLE . " +			WHERE module_basename <> 'ucp_pm' AND +				module_langname='UCP_PM'"; +		$result = $this->db->sql_query_limit($sql, 1); + +		if ($row = $this->db->sql_fetchrow($result)) +		{ +			// This update is still not applied. Applying it + +			$sql = 'UPDATE ' . MODULES_TABLE . " +				SET module_basename = 'ucp_pm' +				WHERE  module_id = " . (int) $row['module_id']; + +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_ucp_profile_auth() +	{ +		// Update the auth setting for the module +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'acl_u_chgprofileinfo' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'profile_info'"; +		$this->sql_query($sql); +	} + +	public function rename_styles_module() +	{ +		// Rename styles module to Customise +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_langname = 'ACP_CAT_CUSTOMISE' +			WHERE module_langname = 'ACP_CAT_STYLES'"; +		$this->sql_query($sql); +	} + +	public function rename_module_basenames() +	{ +		// rename all module basenames to full classname +		$sql = 'SELECT module_id, module_basename, module_class +			FROM ' . MODULES_TABLE; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$module_id = (int) $row['module_id']; +			unset($row['module_id']); + +			if (!empty($row['module_basename']) && !empty($row['module_class'])) +			{ +				// all the class names start with class name or with phpbb_ for auto loading +				if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 && +					strpos($row['module_basename'], 'phpbb_') !== 0) +				{ +					$row['module_basename'] = $row['module_class'] . '_' . $row['module_basename']; + +					$sql_update = $this->db->sql_build_array('UPDATE', $row); + +					$sql = 'UPDATE ' . MODULES_TABLE . ' +						SET ' . $sql_update . ' +						WHERE module_id = ' . $module_id; +					$this->sql_query($sql); +				} +			} +		} + +		$this->db->sql_freeresult($result); +	} + +	public function add_group_teampage() +	{ +		$sql = 'UPDATE ' . GROUPS_TABLE . ' +			SET group_teampage = 1 +			WHERE group_type = ' . GROUP_SPECIAL . " +				AND group_name = 'ADMINISTRATORS'"; +		$this->sql_query($sql); + +		$sql = 'UPDATE ' . GROUPS_TABLE . ' +			SET group_teampage = 2 +			WHERE group_type = ' . GROUP_SPECIAL . " +				AND group_name = 'GLOBAL_MODERATORS'"; +		$this->sql_query($sql); +	} + +	public function update_group_legend() +	{ +		$sql = 'SELECT group_id +			FROM ' . GROUPS_TABLE . ' +			WHERE group_legend = 1 +			ORDER BY group_name ASC'; +		$result = $this->db->sql_query($sql); + +		$next_legend = 1; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql = 'UPDATE ' . GROUPS_TABLE . ' +				SET group_legend = ' . $next_legend . ' +				WHERE group_id = ' . (int) $row['group_id']; +			$this->sql_query($sql); + +			$next_legend++; +		} +		$this->db->sql_freeresult($result); +	} + +	public function localise_global_announcements() +	{ +		// Localise Global Announcements +		$sql = 'SELECT topic_id, topic_approved, (topic_replies + 1) AS topic_posts, topic_last_post_id, topic_last_post_subject, topic_last_post_time, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour +			FROM ' . TOPICS_TABLE . ' +			WHERE forum_id = 0 +				AND topic_type = ' . POST_GLOBAL; +		$result = $this->db->sql_query($sql); + +		$global_announcements = $update_lastpost_data = array(); +		$update_lastpost_data['forum_last_post_time'] = 0; +		$update_forum_data = array( +			'forum_posts'		=> 0, +			'forum_topics'		=> 0, +			'forum_topics_real'	=> 0, +		); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$global_announcements[] = (int) $row['topic_id']; + +			$update_forum_data['forum_posts'] += (int) $row['topic_posts']; +			$update_forum_data['forum_topics_real']++; +			if ($row['topic_approved']) +			{ +				$update_forum_data['forum_topics']++; +			} + +			if ($update_lastpost_data['forum_last_post_time'] < $row['topic_last_post_time']) +			{ +				$update_lastpost_data = array( +					'forum_last_post_id'		=> (int) $row['topic_last_post_id'], +					'forum_last_post_subject'	=> $row['topic_last_post_subject'], +					'forum_last_post_time'		=> (int) $row['topic_last_post_time'], +					'forum_last_poster_id'		=> (int) $row['topic_last_poster_id'], +					'forum_last_poster_name'	=> $row['topic_last_poster_name'], +					'forum_last_poster_colour'	=> $row['topic_last_poster_colour'], +				); +			} +		} +		$this->db->sql_freeresult($result); + +		if (!empty($global_announcements)) +		{ +			// Update the post/topic-count for the forum and the last-post if needed +			$sql = 'SELECT forum_id +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_type = ' . FORUM_POST; +			$result = $this->db->sql_query_limit($sql, 1); +			$ga_forum_id = $this->db->sql_fetchfield('forum_id'); +			$this->db->sql_freeresult($result); + +			$sql = 'SELECT forum_last_post_time +				FROM ' . FORUMS_TABLE . ' +				WHERE forum_id = ' . $ga_forum_id; +			$result = $this->db->sql_query($sql); +			$lastpost = (int) $this->db->sql_fetchfield('forum_last_post_time'); +			$this->db->sql_freeresult($result); + +			$sql_update = 'forum_posts = forum_posts + ' . $update_forum_data['forum_posts'] . ', '; +			$sql_update .= 'forum_topics_real = forum_topics_real + ' . $update_forum_data['forum_topics_real'] . ', '; +			$sql_update .= 'forum_topics = forum_topics + ' . $update_forum_data['forum_topics']; +			if ($lastpost < $update_lastpost_data['forum_last_post_time']) +			{ +				$sql_update .= ', ' . $this->db->sql_build_array('UPDATE', $update_lastpost_data); +			} + +			$sql = 'UPDATE ' . FORUMS_TABLE . ' +				SET ' . $sql_update . ' +				WHERE forum_id = ' . $ga_forum_id; +			$this->sql_query($sql); + +			// Update some forum_ids +			$table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE); +			foreach ($table_ary as $table) +			{ +				$sql = "UPDATE $table +					SET forum_id = $ga_forum_id +					WHERE " . $this->db->sql_in_set('topic_id', $global_announcements); +				$this->sql_query($sql); +			} +			unset($table_ary); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/extensions.php b/phpBB/phpbb/db/migration/data/v310/extensions.php new file mode 100644 index 0000000000..3171435482 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/extensions.php @@ -0,0 +1,75 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class extensions extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'ext'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'ext'	=> array( +					'COLUMNS'			=> array( +						'ext_name'		=> array('VCHAR', ''), +						'ext_active'	=> array('BOOL', 0), +						'ext_state'		=> array('TEXT', ''), +					), +					'KEYS'				=> array( +						'ext_name'		=> array('UNIQUE', 'ext_name'), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'ext', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			// Module will be renamed later +			array('module.add', array( +				'acp', +				'ACP_CAT_STYLES', +				'ACP_EXTENSION_MANAGEMENT' +			)), +			array('module.add', array( +				'acp', +				'ACP_EXTENSION_MANAGEMENT', +				array( +					'module_basename'	=> 'acp_extensions', +					'modes'				=> array('main'), +				), +			)), +			array('permission.add', array('a_extensions', true, 'a_styles')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php b/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php new file mode 100644 index 0000000000..1d6276f484 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php @@ -0,0 +1,29 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class extensions_version_check_force_unstable extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('extension_force_unstable', false)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/forgot_password.php b/phpBB/phpbb/db/migration/data/v310/forgot_password.php new file mode 100644 index 0000000000..362457cf23 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/forgot_password.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class forgot_password extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['allow_password_reset']); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('allow_password_reset', 1)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/jquery_update.php b/phpBB/phpbb/db/migration/data/v310/jquery_update.php new file mode 100644 index 0000000000..8011331e80 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/jquery_update.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class jquery_update extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->config['load_jquery_url'] !== '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js'; +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js')), +		); +	} + +} diff --git a/phpBB/phpbb/db/migration/data/v310/jquery_update2.php b/phpBB/phpbb/db/migration/data/v310/jquery_update2.php new file mode 100644 index 0000000000..4061be5940 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/jquery_update2.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class jquery_update2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->config['load_jquery_url'] !== '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js'; +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\jquery_update', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js')), +		); +	} + +} diff --git a/phpBB/phpbb/db/migration/data/v310/live_searches_config.php b/phpBB/phpbb/db/migration/data/v310/live_searches_config.php new file mode 100644 index 0000000000..3d87e04ac2 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/live_searches_config.php @@ -0,0 +1,29 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class live_searches_config extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['allow_live_searches']); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('allow_live_searches', '1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/migrations_table.php b/phpBB/phpbb/db/migration/data/v310/migrations_table.php new file mode 100644 index 0000000000..48508b05c2 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/migrations_table.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class migrations_table extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'migrations'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'migrations'	=> array( +					'COLUMNS'		=> array( +						'migration_name'			=> array('VCHAR', ''), +						'migration_depends_on'		=> array('TEXT', ''), +						'migration_schema_done'		=> array('BOOL', 0), +						'migration_data_done'		=> array('BOOL', 0), +						'migration_data_state'		=> array('TEXT', ''), +						'migration_start_time'		=> array('TIMESTAMP', 0), +						'migration_end_time'		=> array('TIMESTAMP', 0), +					), +					'PRIMARY_KEY'	=> 'migration_name', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'migrations', +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php b/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php new file mode 100644 index 0000000000..85e479db59 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php @@ -0,0 +1,31 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class mod_rewrite extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('enable_mod_rewrite', '0')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php new file mode 100644 index 0000000000..df1560f5b1 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class mysql_fulltext_drop extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		// This migration is irrelevant for all non-MySQL DBMSes. +		return strpos($this->db->sql_layer, 'mysql') === false; +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +		); +	} + +	public function update_schema() +	{ +		/* +		* Drop FULLTEXT indexes related to MySQL fulltext search. +		* Doing so is equivalent to dropping the search index from the ACP. +		* Possibly time-consuming recreation of the search index (i.e. +		* FULLTEXT indexes) is left as a task to the admin to not +		* unnecessarily stall the upgrade process. The new search index will +		* then require about 40% less table space (also see PHPBB3-11621). +		*/ +		return array( +			'drop_keys' => array( +				$this->table_prefix . 'posts' => array( +					'post_subject', +					'post_text', +					'post_content', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/namespaces.php b/phpBB/phpbb/db/migration/data/v310/namespaces.php new file mode 100644 index 0000000000..2a4935395e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/namespaces.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class namespaces extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +		); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				(preg_match('#^phpbb_search_#', $this->config['search_type'])), +				array('config.update', array('search_type', str_replace('phpbb_search_', '\\phpbb\\search\\', $this->config['search_type']))), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php new file mode 100644 index 0000000000..2d4d26ae61 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php @@ -0,0 +1,141 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class notification_options_reconvert extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\notifications_schema_fix'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'purge_notifications'))), +			array('custom', array(array($this, 'convert_notifications'))), +		); +	} + +	public function purge_notifications() +	{ +		$sql = 'DELETE FROM ' . $this->table_prefix . 'user_notifications'; +		$this->sql_query($sql); +	} + +	public function convert_notifications($start) +	{ +		$insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->table_prefix . 'user_notifications'); + +		return $this->perform_conversion($insert_buffer, $start); +	} + +	/** +	* Perform the conversion (separate for testability) +	* +	* @param \phpbb\db\sql_insert_buffer		$insert_buffer +	* @param int			$start		Start of staggering step +	* @return		mixed		int start of the next step, null if the end was reached +	*/ +	public function perform_conversion(\phpbb\db\sql_insert_buffer $insert_buffer, $start) +	{ +		$limit = 250; +		$converted_users = 0; + +		$sql = 'SELECT user_id, user_notify_type, user_notify_pm +			FROM ' . $this->table_prefix . 'users +			ORDER BY user_id'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$converted_users++; +			$notification_methods = array(); + +			// In-board notification +			$notification_methods[] = ''; + +			if ($row['user_notify_type'] == NOTIFY_EMAIL || $row['user_notify_type'] == NOTIFY_BOTH) +			{ +				$notification_methods[] = 'email'; +			} + +			if ($row['user_notify_type'] == NOTIFY_IM || $row['user_notify_type'] == NOTIFY_BOTH) +			{ +				$notification_methods[] = 'jabber'; +			} + +			// Notifications for posts +			foreach (array('post', 'topic') as $item_type) +			{ +				$this->add_method_rows( +					$insert_buffer, +					$item_type, +					0, +					$row['user_id'], +					$notification_methods +				); +			} + +			if ($row['user_notify_pm']) +			{ +				// Notifications for private messages +				// User either gets all methods or no method +				$this->add_method_rows( +					$insert_buffer, +					'pm', +					0, +					$row['user_id'], +					$notification_methods +				); +			} +		} +		$this->db->sql_freeresult($result); + +		$insert_buffer->flush(); + +		if ($converted_users < $limit) +		{ +			// No more users left, we are done... +			return; +		} + +		return $start + $limit; +	} + +	/** +	* Insert method rows to DB +	* +	* @param \phpbb\db\sql_insert_buffer $insert_buffer +	* @param string $item_type +	* @param int $item_id +	* @param int $user_id +	* @param string $methods +	*/ +	protected function add_method_rows(\phpbb\db\sql_insert_buffer $insert_buffer, $item_type, $item_id, $user_id, array $methods) +	{ +		$row_base = array( +			'item_type'		=> $item_type, +			'item_id'		=> (int) $item_id, +			'user_id'		=> (int) $user_id, +			'notify'		=> 1 +		); + +		foreach ($methods as $method) +		{ +			$row_base['method'] = $method; +			$insert_buffer->insert($row_base); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/notifications.php b/phpBB/phpbb/db/migration/data/v310/notifications.php new file mode 100644 index 0000000000..f4d012b5ac --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/notifications.php @@ -0,0 +1,102 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class notifications extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'notifications'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type'			=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type', 'notification_type_enabled'), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'  				=> array('UINT', null, 'auto_increment'), +						'item_type'			   			=> array('VCHAR:255', ''), +						'item_id'		  				=> array('UINT', 0), +						'item_parent_id'   				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'			   	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('item_type', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +				$this->table_prefix . 'user_notifications'	=> array( +					'COLUMNS'			=> array( +						'item_type'			=> array('VCHAR:255', ''), +						'item_id'			=> array('UINT', 0), +						'user_id'			=> array('UINT', 0), +						'method'			=> array('VCHAR:255', ''), +						'notify'			=> array('BOOL', 1), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'	=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +				$this->table_prefix . 'user_notifications', +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('module.add', array( +				'ucp', +				'UCP_MAIN', +				array( +					'module_basename'	=> 'ucp_notifications', +					'modes'				=> array('notification_list'), +				), +			)), +			array('module.add', array( +				'ucp', +				'UCP_PREFS', +				array( +					'module_basename'	=> 'ucp_notifications', +					'modes'				=> array('notification_options'), +				), +			)), +			array('config.add', array('load_notifications', 1)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php new file mode 100644 index 0000000000..ba600f7bf5 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php @@ -0,0 +1,31 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class notifications_cron extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\notifications'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('read_notification_expire_days', 30)), +			array('config.add', array('read_notification_last_gc', 0)), // last run +			array('config.add', array('read_notification_gc', (60 * 60 * 24))), // seconds between run; 1 day +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php new file mode 100644 index 0000000000..263584b343 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php @@ -0,0 +1,31 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class notifications_cron_p2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\notifications_cron'); +	} + +	public function update_data() +	{ +		return array( +			// Make read_notification_last_gc dynamic. +			array('config.remove', array('read_notification_last_gc')), +			array('config.add', array('read_notification_last_gc', 0, 1)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php new file mode 100644 index 0000000000..21a39a7c91 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php @@ -0,0 +1,98 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class notifications_schema_fix extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\notifications'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +			), +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type_id'		=> array('USINT', null, 'auto_increment'), +						'notification_type_name'	=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type_id'), +					'KEYS'				=> array( +						'type'			=> array('UNIQUE', array('notification_type_name')), +					), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'				=> array('UINT:10', null, 'auto_increment'), +						'notification_type_id'			=> array('USINT', 0), +						'item_id'						=> array('UINT', 0), +						'item_parent_id'				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'				=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('notification_type_id', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'	=> array( +				$this->table_prefix . 'notification_types', +				$this->table_prefix . 'notifications', +			), +			'add_tables'		=> array( +				$this->table_prefix . 'notification_types'	=> array( +					'COLUMNS'			=> array( +						'notification_type'			=> array('VCHAR:255', ''), +						'notification_type_enabled'	=> array('BOOL', 1), +					), +					'PRIMARY_KEY'		=> array('notification_type', 'notification_type_enabled'), +				), +				$this->table_prefix . 'notifications'		=> array( +					'COLUMNS'			=> array( +						'notification_id'  				=> array('UINT', null, 'auto_increment'), +						'item_type'			   			=> array('VCHAR:255', ''), +						'item_id'		  				=> array('UINT', 0), +						'item_parent_id'   				=> array('UINT', 0), +						'user_id'						=> array('UINT', 0), +						'notification_read'				=> array('BOOL', 0), +						'notification_time'				=> array('TIMESTAMP', 1), +						'notification_data'			   	=> array('TEXT_UNI', ''), +					), +					'PRIMARY_KEY'		=> 'notification_id', +					'KEYS'				=> array( +						'item_ident'		=> array('INDEX', array('item_type', 'item_id')), +						'user'				=> array('INDEX', array('user_id', 'notification_read')), +					), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/passwords.php b/phpBB/phpbb/db/migration/data/v310/passwords.php new file mode 100644 index 0000000000..adee44147f --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/passwords.php @@ -0,0 +1,50 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class passwords extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'	=> array( +					'user_password'		=> array('VCHAR:255', ''), +				), +				$this->table_prefix . 'forums'	=> array( +					'forum_password'	=> array('VCHAR:255', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'	=> array( +					'user_password'		=> array('VCHAR:40', ''), +				), +				$this->table_prefix . 'forums'	=> array( +					'forum_password'	=> array('VCHAR:40', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php new file mode 100644 index 0000000000..aad8e44681 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php @@ -0,0 +1,84 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class passwords_convert_p1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\passwords_p2'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_passwords'))), +		); +	} + +	public function update_passwords($start) +	{ +		// Nothing to do if user_pass_convert column doesn't exist +		if (!$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert')) +		{ +			return; +		} + +		$start = (int) $start; +		$limit = 1000; +		$converted_users = 0; + +		$sql = 'SELECT user_password, user_id +			FROM ' . $this->table_prefix . 'users +			WHERE user_pass_convert = 1 +			ORDER BY user_id'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		$update_users = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$converted_users++; + +			$user_id = (int) $row['user_id']; +			// Only prefix passwords without proper prefix +			if (!isset($update_users[$user_id]) && !preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $row['user_password'])) +			{ +				// Use $CP$ prefix for passwords that need to +				// be converted and set pass convert to false. +				$update_users[$user_id] = array( +					'user_password'		=> '$CP$' . $row['user_password'], +					'user_pass_convert'	=> 0, +				); +			} +		} +		$this->db->sql_freeresult($result); + +		foreach ($update_users as $user_id => $user_data) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . 'users +				SET ' . $this->db->sql_build_array('UPDATE', $user_data) . ' +				WHERE user_id = ' . $user_id; +			$this->sql_query($sql); +		} + +		if ($converted_users < $limit) +		{ +			// There are no more users to be converted +			return; +		} + +		// There are still more users to query, return the next start value +		return $start + $limit; +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php new file mode 100644 index 0000000000..26a99184a6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php @@ -0,0 +1,49 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class passwords_convert_p2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\passwords_convert_p1'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'users'		=> array( +					'user_pass_convert', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'users'		=> array( +					'user_pass_convert'	=> array('BOOL', 0, 'after' => 'user_passchg'), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php new file mode 100644 index 0000000000..afc7ba2813 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php @@ -0,0 +1,44 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class passwords_p2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\passwords'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'	=> array( +					'user_newpasswd'		=> array('VCHAR:255', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'	=> array( +					'user_newpasswd'		=> array('VCHAR:40', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/plupload.php b/phpBB/phpbb/db/migration/data/v310/plupload.php new file mode 100644 index 0000000000..69367f86a9 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/plupload.php @@ -0,0 +1,36 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class plupload extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['plupload_last_gc']) && +			isset($this->config['plupload_salt']); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('plupload_last_gc', 0)), +			array('config.add', array('plupload_salt', unique_id())), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php b/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php new file mode 100644 index 0000000000..ca784656e7 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class postgres_fulltext_drop extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		// This migration is irrelevant for all non-PostgreSQL DBMSes. +		return strpos($this->db->sql_layer, 'postgres') === false; +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +		); +	} + +	public function update_schema() +	{ +		/* +		* Drop FULLTEXT indexes related to PostgreSQL fulltext search. +		* Doing so is equivalent to dropping the search index from the ACP. +		* Possibly time-consuming recreation of the search index (i.e. +		* FULLTEXT indexes) is left as a task to the admin to not +		* unnecessarily stall the upgrade process. The new search index will +		* then require about 40% less table space (also see PHPBB3-11040). +		*/ +		return array( +			'drop_keys' => array( +				$this->table_prefix . 'posts' => array( +					'post_subject', +					'post_text', +					'post_content', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php b/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php new file mode 100644 index 0000000000..65d4fe1078 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_aol extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_yahoo_cleanup', +		); +	} + +	protected $profilefield_name = 'phpbb_aol'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_aol', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_aol', +		'field_length'			=> '40', +		'field_minlen'			=> '5', +		'field_maxlen'			=> '255', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_on_ml'		=> 0, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> '', +		'field_contact_url'		=> '', +	); + +	protected $user_column_name = 'user_aim'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php new file mode 100644 index 0000000000..f884d83d26 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_aol_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_aim'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_aol', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_aim', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_aim'	=> array('VCHAR_UNI', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php b/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php new file mode 100644 index 0000000000..7cc4fd8daa --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php @@ -0,0 +1,34 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_change_load_settings extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_aol_cleanup', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('load_cpf_memberlist', '1')), +			array('config.update', array('load_cpf_pm', '1')), +			array('config.update', array('load_cpf_viewprofile', '1')), +			array('config.update', array('load_cpf_viewtopic', '1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php new file mode 100644 index 0000000000..c44167dbfe --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_occ') && +			!$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_interests'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_interests', +			'\phpbb\db\migration\data\v310\profilefield_occupation', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_occ', +					'user_interests', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_occ'			=> array('MTEXT', ''), +					'user_interests'	=> array('MTEXT', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php b/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php new file mode 100644 index 0000000000..02cd420c0f --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_contact_field extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_is_contact'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_on_memberlist', +		); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'profile_fields'	=> array( +					'field_is_contact'			=> array('BOOL', 0), +					'field_contact_desc'		=> array('VCHAR', ''), +					'field_contact_url'			=> array('VCHAR', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'profile_fields'	=> array( +					'field_is_contact', +					'field_contact_desc', +					'field_contact_url', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php b/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php new file mode 100644 index 0000000000..5964e7a997 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php @@ -0,0 +1,60 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_facebook extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	protected $profilefield_name = 'phpbb_facebook'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_facebook', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_facebook', +		'field_length'			=> '20', +		'field_minlen'			=> '5', +		'field_maxlen'			=> '50', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[\w.]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VIEW_FACEBOOK_PROFILE', +		'field_contact_url'		=> 'http://facebook.com/%s/', +	); +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php b/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php new file mode 100644 index 0000000000..9bef0a4c0b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php @@ -0,0 +1,60 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_googleplus extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	protected $profilefield_name = 'phpbb_googleplus'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_googleplus', +		'field_type'			=> 'profilefields.type.googleplus', +		'field_ident'			=> 'phpbb_googleplus', +		'field_length'			=> '20', +		'field_minlen'			=> '3', +		'field_maxlen'			=> '255', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[\w]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VIEW_GOOGLEPLUS_PROFILE', +		'field_contact_url'		=> 'http://plus.google.com/%s', +	); +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php b/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php new file mode 100644 index 0000000000..e61653f3db --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php @@ -0,0 +1,54 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_icq extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_contact_field', +		); +	} + +	protected $profilefield_name = 'phpbb_icq'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_icq', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_icq', +		'field_length'			=> '20', +		'field_minlen'			=> '3', +		'field_maxlen'			=> '15', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[0-9]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'SEND_ICQ_MESSAGE', +		'field_contact_url'		=> 'https://www.icq.com/people/%s/', +	); + +	protected $user_column_name = 'user_icq'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php new file mode 100644 index 0000000000..516c690093 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_icq_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_icq'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_icq', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_icq', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_icq'	=> array('VCHAR:20', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php b/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php new file mode 100644 index 0000000000..33a5ba15ae --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php @@ -0,0 +1,52 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_interests extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	protected $profilefield_name = 'phpbb_interests'; + +	protected $profilefield_database_type = array('MTEXT', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_interests', +		'field_type'			=> 'profilefields.type.text', +		'field_ident'			=> 'phpbb_interests', +		'field_length'			=> '3|30', +		'field_minlen'			=> '2', +		'field_maxlen'			=> '500', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 0, +		'field_show_on_vt'		=> 0, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +	); + +	protected $user_column_name = 'user_interests'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_location.php b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php new file mode 100644 index 0000000000..2d27c09e68 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php @@ -0,0 +1,52 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_location extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_on_memberlist', +		); +	} + +	protected $profilefield_name = 'phpbb_location'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_location', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_location', +		'field_length'			=> '20', +		'field_minlen'			=> '2', +		'field_maxlen'			=> '100', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +	); + +	protected $user_column_name = 'user_from'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php new file mode 100644 index 0000000000..b824e3406a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_location_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_from'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_location', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_from', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_from'	=> array('VCHAR_UNI:100', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php b/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php new file mode 100644 index 0000000000..75df2bcdee --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_occupation extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_interests', +		); +	} + +	protected $profilefield_name = 'phpbb_occupation'; + +	protected $profilefield_database_type = array('MTEXT', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_occupation', +		'field_type'			=> 'profilefields.type.text', +		'field_ident'			=> 'phpbb_occupation', +		'field_length'			=> '3|30', +		'field_minlen'			=> '2', +		'field_maxlen'			=> '500', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 0, +		'field_show_on_vt'		=> 0, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +	); + +	protected $user_column_name = 'user_occ'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php new file mode 100644 index 0000000000..7ce5de0f00 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_on_memberlist extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_show_on_ml'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_cleanup', +		); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_ml'		=> array('BOOL', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'profile_fields'	=> array( +					'field_show_on_ml', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php b/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php new file mode 100644 index 0000000000..5fc88b6809 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php @@ -0,0 +1,49 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_show_novalue extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_show_novalue'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\profilefield_types'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue' => array('BOOL', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns' => array( +				$this->table_prefix . 'profile_fields' => array( +					'field_show_novalue', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php b/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php new file mode 100644 index 0000000000..9a5de9d0eb --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php @@ -0,0 +1,60 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_skype extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	protected $profilefield_name = 'phpbb_skype'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_skype', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_skype', +		'field_length'			=> '20', +		'field_minlen'			=> '6', +		'field_maxlen'			=> '32', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[a-zA-Z][\w\.,\-_]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VIEW_SKYPE_PROFILE', +		'field_contact_url'		=> 'skype:%s?userinfo', +	); +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php b/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php new file mode 100644 index 0000000000..68d038f609 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php @@ -0,0 +1,60 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_twitter extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	protected $profilefield_name = 'phpbb_twitter'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_twitter', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_twitter', +		'field_length'			=> '20', +		'field_minlen'			=> '1', +		'field_maxlen'			=> '15', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[\w_]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VIEW_TWITTER_PROFILE', +		'field_contact_url'		=> 'http://twitter.com/%s', +	); +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_types.php b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php new file mode 100644 index 0000000000..5045eb8807 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php @@ -0,0 +1,110 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_types extends \phpbb\db\migration\migration +{ + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\alpha2', +		); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'profile_fields'			=> array( +					'field_type'		=> array('VCHAR:100', ''), +				), +				$this->table_prefix . 'profile_fields_lang'		=> array( +					'field_type'		=> array('VCHAR:100', ''), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_profile_fields_type'))), +			array('custom', array(array($this, 'update_profile_fields_lang_type'))), +		); +	} + +	public function update_profile_fields_type() +	{ +		// Update profile field types +		$sql = 'SELECT field_type +			FROM ' . $this->table_prefix . 'profile_fields +			GROUP BY field_type'; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "profile_fields +				SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "' +				WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'"; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	public function update_profile_fields_lang_type() +	{ +		// Update profile field language types +		$sql = 'SELECT field_type +			FROM ' . $this->table_prefix . 'profile_fields_lang +			GROUP BY field_type'; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql = 'UPDATE ' . $this->table_prefix . "profile_fields_lang +				SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "' +				WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'"; +			$this->sql_query($sql); +		} +		$this->db->sql_freeresult($result); +	} + +	/** +	* Determine the new field type for a given phpBB 3.0 field type +	* +	*	@param	$field_type	string		Field type in 3.0 +	*	@return		string		Field new type which is used since 3.1 +	*/ +	public function convert_phpbb30_field_type($field_type) +	{ +		switch ($field_type) +		{ +			case FIELD_INT: +				return 'profilefields.type.int'; +			case FIELD_STRING: +				return 'profilefields.type.string'; +			case FIELD_TEXT: +				return 'profilefields.type.text'; +			case FIELD_BOOL: +				return 'profilefields.type.bool'; +			case FIELD_DROPDOWN: +				return 'profilefields.type.dropdown'; +			case FIELD_DATE: +				return 'profilefields.type.date'; +			default: +				return $field_type; +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_website.php b/phpBB/phpbb/db/migration/data/v310/profilefield_website.php new file mode 100644 index 0000000000..e1e10f09f4 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_website.php @@ -0,0 +1,56 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_website extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_on_memberlist', +			'\phpbb\db\migration\data\v310\profilefield_icq_cleanup', +		); +	} + +	protected $profilefield_name = 'phpbb_website'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_website', +		'field_type'			=> 'profilefields.type.url', +		'field_ident'			=> 'phpbb_website', +		'field_length'			=> '40', +		'field_minlen'			=> '12', +		'field_maxlen'			=> '255', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_on_ml'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VISIT_WEBSITE', +		'field_contact_url'		=> '%s', +	); + +	protected $user_column_name = 'user_website'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php new file mode 100644 index 0000000000..94442f0497 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_website_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_website'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_website', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_website', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_website'	=> array('VCHAR_UNI:200', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php new file mode 100644 index 0000000000..2cd333fcbd --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_wlm extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_website_cleanup', +		); +	} + +	protected $profilefield_name = 'phpbb_wlm'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_wlm', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_wlm', +		'field_length'			=> '40', +		'field_minlen'			=> '5', +		'field_maxlen'			=> '255', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_on_ml'		=> 0, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> '', +		'field_contact_url'		=> '', +	); + +	protected $user_column_name = 'user_msnm'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php new file mode 100644 index 0000000000..7ef9e44020 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_wlm_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_msnm'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_wlm', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_msnm', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_msnm'	=> array('VCHAR_UNI', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php new file mode 100644 index 0000000000..e269f88420 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php @@ -0,0 +1,55 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_yahoo extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_wlm_cleanup', +		); +	} + +	protected $profilefield_name = 'phpbb_yahoo'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_yahoo', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_yahoo', +		'field_length'			=> '40', +		'field_minlen'			=> '5', +		'field_maxlen'			=> '255', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '.*', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_on_ml'		=> 0, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'SEND_YIM_MESSAGE', +		'field_contact_url'		=> 'http://edit.yahoo.com/config/send_webmesg?.target=%s&.src=pg', +	); + +	protected $user_column_name = 'user_yim'; +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php new file mode 100644 index 0000000000..bd724ff7db --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php @@ -0,0 +1,51 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_yahoo_cleanup extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_yim'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_yahoo', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_yim', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_yim'	=> array('VCHAR_UNI', ''), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php b/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php new file mode 100644 index 0000000000..bb90c0aa5c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php @@ -0,0 +1,60 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class profilefield_youtube extends \phpbb\db\migration\profilefield_base_migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\profilefield_types', +			'\phpbb\db\migration\data\v310\profilefield_show_novalue', +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	protected $profilefield_name = 'phpbb_youtube'; + +	protected $profilefield_database_type = array('VCHAR', ''); + +	protected $profilefield_data = array( +		'field_name'			=> 'phpbb_youtube', +		'field_type'			=> 'profilefields.type.string', +		'field_ident'			=> 'phpbb_youtube', +		'field_length'			=> '20', +		'field_minlen'			=> '3', +		'field_maxlen'			=> '60', +		'field_novalue'			=> '', +		'field_default_value'	=> '', +		'field_validation'		=> '[a-zA-Z][\w\.,\-_]+', +		'field_required'		=> 0, +		'field_show_novalue'	=> 0, +		'field_show_on_reg'		=> 0, +		'field_show_on_pm'		=> 1, +		'field_show_on_vt'		=> 1, +		'field_show_profile'	=> 1, +		'field_hide'			=> 0, +		'field_no_view'			=> 0, +		'field_active'			=> 1, +		'field_is_contact'		=> 1, +		'field_contact_desc'	=> 'VIEW_YOUTUBE_CHANNEL', +		'field_contact_url'		=> 'http://youtube.com/user/%s', +	); +} diff --git a/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php b/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php new file mode 100644 index 0000000000..f6d27d385e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php @@ -0,0 +1,50 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class prune_shadow_topics extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'forums'		=> array( +					'enable_shadow_prune'	=> array('BOOL', 0), +					'prune_shadow_days'	=> array('UINT', 7), +					'prune_shadow_freq'	=> array('UINT', 1), +					'prune_shadow_next'	=> array('INT:11', 0), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'forums'		=> array( +					'enable_shadow_prune', +					'prune_shadow_days', +					'prune_shadow_freq', +					'prune_shadow_next', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/rc1.php b/phpBB/phpbb/db/migration/data/v310/rc1.php new file mode 100644 index 0000000000..10ba7fefff --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/rc1.php @@ -0,0 +1,39 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class rc1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\beta4', +			'\phpbb\db\migration\data\v310\contact_admin_acp_module', +			'\phpbb\db\migration\data\v310\contact_admin_form', +			'\phpbb\db\migration\data\v310\passwords_convert_p2', +			'\phpbb\db\migration\data\v310\profilefield_facebook', +			'\phpbb\db\migration\data\v310\profilefield_googleplus', +			'\phpbb\db\migration\data\v310\profilefield_skype', +			'\phpbb\db\migration\data\v310\profilefield_twitter', +			'\phpbb\db\migration\data\v310\profilefield_youtube', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.0-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php b/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php new file mode 100644 index 0000000000..575a65d9dd --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php @@ -0,0 +1,53 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class reported_posts_display extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'reports', 'reported_post_enable_bbcode'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'reports'		=> array( +					'reported_post_enable_bbcode'		=> array('BOOL', 1), +					'reported_post_enable_smilies'		=> array('BOOL', 1), +					'reported_post_enable_magic_url'	=> array('BOOL', 1), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'reports'		=> array( +					'reported_post_enable_bbcode', +					'reported_post_enable_smilies', +					'reported_post_enable_magic_url', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php b/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php new file mode 100644 index 0000000000..d5f9076196 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +/** +* Class captcha_plugin +* +* Reset the captcha setting to the default plugin if the defined 'captcha_plugin' is missing. +*/ +class reset_missing_captcha_plugin extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				(!is_file($this->phpbb_root_path . "includes/captcha/plugins/{$this->config['captcha_plugin']}_plugin." . $this->php_ext)), +				array('config.update', array('captcha_plugin', 'phpbb_captcha_nogd')), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php b/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php new file mode 100644 index 0000000000..e50f5e53a0 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php @@ -0,0 +1,57 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class signature_module_auth extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_auth +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'signature'"; +		$result = $this->db->sql_query($sql); +		$module_auth = $this->db->sql_fetchfield('module_auth'); +		$this->db->sql_freeresult($result); + +		return $module_auth === 'acl_u_sig' || $module_auth === false; +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array( +					array($this, 'update_signature_module_auth'), +				), +			), +		); +	} + +	public function update_signature_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'acl_u_sig' +			WHERE module_class = 'ucp' +				AND module_basename = 'ucp_profile' +				AND module_mode = 'signature' +				AND module_auth = ''"; +		$this->db->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php new file mode 100644 index 0000000000..f5970e74b2 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php @@ -0,0 +1,132 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +/** + * Migration to convert the Soft Delete MOD for 3.0 + * + * https://www.phpbb.com/customise/db/mod/soft_delete/ + */ +class soft_delete_mod_convert extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\alpha3', +		); +	} + +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_deleted'); +	} + +	public function update_data() +	{ +		return array( +			array('permission.remove', array('m_harddelete', true)), +			array('permission.remove', array('m_harddelete', false)), + +			array('custom', array(array($this, 'convert_posts'))), +			array('custom', array(array($this, 'convert_topics'))), +		); +	} + +	public function convert_posts($start) +	{ +		$content_visibility = $this->get_content_visibility(); + +		$limit = 250; +		$i = 0; + +		$sql = 'SELECT p.*, t.topic_first_post_id, t.topic_last_post_id +			FROM ' . $this->table_prefix . 'posts p, ' . $this->table_prefix . 'topics t +			WHERE p.post_deleted > 0 +				AND t.topic_id = p.topic_id'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$content_visibility->set_post_visibility( +				ITEM_DELETED, +				$row['post_id'], +				$row['topic_id'], +				$row['forum_id'], +				$row['post_deleted'], +				$row['post_deleted_time'], +				'', +				($row['post_id'] == $row['topic_first_post_id']) ? true : false, +				($row['post_id'] == $row['topic_last_post_id']) ? true : false +			); + +			$i++; +		} + +		$this->db->sql_freeresult($result); + +		if ($i == $limit) +		{ +			return $start + $i; +		} +	} + +	public function convert_topics($start) +	{ +		$content_visibility = $this->get_content_visibility(); + +		$limit = 100; +		$i = 0; + +		$sql = 'SELECT * +			FROM ' . $this->table_prefix . 'topics +			WHERE topic_deleted > 0'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$content_visibility->set_topic_visibility( +				ITEM_DELETED, +				$row['topic_id'], +				$row['forum_id'], +				$row['topic_deleted'], +				$row['topic_deleted_time'], +				'' +			); + +			$i++; +		} + +		$this->db->sql_freeresult($result); + +		if ($i == $limit) +		{ +			return $start + $i; +		} +	} + +	protected function get_content_visibility() +	{ +		return new \phpbb\content_visibility( +			new \phpbb\auth\auth(), +			$this->db, +			new \phpbb\user(), +			$this->phpbb_root_path, +			$this->php_ext, +			$this->table_prefix . 'forums', +			$this->table_prefix . 'posts', +			$this->table_prefix . 'topics', +			$this->table_prefix . 'users' +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php new file mode 100644 index 0000000000..246a267a8c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php @@ -0,0 +1,66 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +/** + * Migration to convert the Soft Delete MOD for 3.0 + * + * https://www.phpbb.com/customise/db/mod/soft_delete/ + */ +class soft_delete_mod_convert2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\soft_delete_mod_convert', +		); +	} + +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_deleted'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'forums'			=> array('forum_deleted_topic_count', 'forum_deleted_reply_count'), +				$this->table_prefix . 'posts'			=> array('post_deleted', 'post_deleted_time'), +				$this->table_prefix . 'topics'			=> array('topic_deleted', 'topic_deleted_time', 'topic_deleted_reply_count'), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'forums'			=> array( +					'forum_deleted_topic_count'		=> array('UINT', 0), +					'forum_deleted_reply_count'		=> array('UINT', 0), +				), +				$this->table_prefix . 'posts'			=> array( +					'post_deleted'					=> array('UINT', 0), +					'post_deleted_time'				=> array('TIMESTAMP', 0), +				), +				$this->table_prefix . 'topics'			=> array( +					'topic_deleted'					=> array('UINT', 0), +					'topic_deleted_time'			=> array('TIMESTAMP', 0), +					'topic_deleted_reply_count'		=> array('UINT', 0), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php b/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php new file mode 100644 index 0000000000..5e68db5889 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php @@ -0,0 +1,61 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class softdelete_mcp_modules extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'mcp' +				AND module_basename = 'mcp_queue' +				AND module_mode = 'deleted_topics'"; +		$result = $this->db->sql_query($sql); +		$module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		return $module_id !== false; +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +			'\phpbb\db\migration\data\v310\softdelete_p2', +		); +	} + +	public function update_data() +	{ +		return array( +			array('module.add', array( +				'mcp', +				'MCP_QUEUE', +				array( +					'module_basename'	=> 'mcp_queue', +					'modes'				=> array('deleted_topics'), +				), +			)), +			array('module.add', array( +				'mcp', +				'MCP_QUEUE', +				array( +					'module_basename'	=> 'mcp_queue', +					'modes'				=> array('deleted_posts'), +				), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php new file mode 100644 index 0000000000..b1e7486e24 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php @@ -0,0 +1,211 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class softdelete_p1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_visibility'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'forums'		=> array( +					'forum_posts_approved'		=> array('UINT', 0), +					'forum_posts_unapproved'	=> array('UINT', 0), +					'forum_posts_softdeleted'	=> array('UINT', 0), +					'forum_topics_approved'		=> array('UINT', 0), +					'forum_topics_unapproved'	=> array('UINT', 0), +					'forum_topics_softdeleted'	=> array('UINT', 0), +				), +				$this->table_prefix . 'posts'		=> array( +					'post_visibility'		=> array('TINT:3', 0), +					'post_delete_time'		=> array('TIMESTAMP', 0), +					'post_delete_reason'	=> array('STEXT_UNI', ''), +					'post_delete_user'		=> array('UINT', 0), +				), +				$this->table_prefix . 'topics'		=> array( +					'topic_visibility'		=> array('TINT:3', 0), +					'topic_delete_time'		=> array('TIMESTAMP', 0), +					'topic_delete_reason'	=> array('STEXT_UNI', ''), +					'topic_delete_user'		=> array('UINT', 0), +					'topic_posts_approved'		=> array('UINT', 0), +					'topic_posts_unapproved'	=> array('UINT', 0), +					'topic_posts_softdeleted'	=> array('UINT', 0), +				), +			), +			'add_index'		=> array( +				$this->table_prefix . 'posts'		=> array( +					'post_visibility'		=> array('post_visibility'), +				), +				$this->table_prefix . 'topics'		=> array( +					'topic_visibility'		=> array('topic_visibility'), +					'forum_vis_last'		=> array('forum_id', 'topic_visibility', 'topic_last_post_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'forums'		=> array( +					'forum_posts_approved', +					'forum_posts_unapproved', +					'forum_posts_softdeleted', +					'forum_topics_approved', +					'forum_topics_unapproved', +					'forum_topics_softdeleted', +				), +				$this->table_prefix . 'posts'		=> array( +					'post_visibility', +					'post_delete_time', +					'post_delete_reason', +					'post_delete_user', +				), +				$this->table_prefix . 'topics'		=> array( +					'topic_visibility', +					'topic_delete_time', +					'topic_delete_reason', +					'topic_delete_user', +					'topic_posts_approved', +					'topic_posts_unapproved', +					'topic_posts_softdeleted', +				), +			), +			'drop_keys'		=> array( +				$this->table_prefix . 'posts'		=> array('post_visibility'), +				$this->table_prefix . 'topics'	=> array('topic_visibility', 'forum_vis_last'), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_post_visibility'))), +			array('custom', array(array($this, 'update_topic_visibility'))), +			array('custom', array(array($this, 'update_topics_post_counts'))), +			array('custom', array(array($this, 'update_forums_topic_and_post_counts'))), + +			array('permission.add', array('f_softdelete', false)), +			array('permission.add', array('m_softdelete', false)), +		); +	} + +	public function update_post_visibility() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . 'posts +			SET post_visibility = post_approved'; +		$this->sql_query($sql); +	} + +	public function update_topic_visibility() +	{ +		$sql = 'UPDATE ' . $this->table_prefix . 'topics +			SET topic_visibility = topic_approved'; +		$this->sql_query($sql); +	} + +	public function update_topics_post_counts() +	{ +		/* +		* Using sql_case here to avoid "BIGINT UNSIGNED value is out of range" errors. +		* As we update all topics in 2 queries, one broken topic would stop the conversion +		* for all topics and the surpressed error will cause the admin to not even notice it. +		*/ +		$sql = 'UPDATE ' . $this->table_prefix . 'topics +			SET topic_posts_approved = topic_replies + 1, +				topic_posts_unapproved = ' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . ' +			WHERE topic_visibility = ' . ITEM_APPROVED; +		$this->sql_query($sql); + +		$sql = 'UPDATE ' . $this->table_prefix . 'topics +			SET topic_posts_approved = 0, +				topic_posts_unapproved = (' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . ') + 1 +			WHERE topic_visibility = ' . ITEM_UNAPPROVED; +		$this->sql_query($sql); +	} + +	public function update_forums_topic_and_post_counts($start) +	{ +		$start = (int) $start; +		$limit = 10; +		$converted_forums = 0; + +		if (!$start) +		{ +			// Preserve the forum_posts value for link forums as it represents redirects. +			$sql = 'UPDATE ' . $this->table_prefix . 'forums +				SET forum_posts_approved = forum_posts +				WHERE forum_type = ' . FORUM_LINK; +			$this->db->sql_query($sql); +		} + +		$sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS sum_topics, SUM(topic_posts_approved) AS sum_posts_approved, SUM(topic_posts_unapproved) AS sum_posts_unapproved +			FROM ' . $this->table_prefix . 'topics +			GROUP BY forum_id, topic_visibility +			ORDER BY forum_id, topic_visibility'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		$update_forums = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$converted_forums++; + +			$forum_id = (int) $row['forum_id']; +			if (!isset($update_forums[$forum_id])) +			{ +				$update_forums[$forum_id] = array( +					'forum_posts_approved'		=> 0, +					'forum_posts_unapproved'	=> 0, +					'forum_topics_approved'		=> 0, +					'forum_topics_unapproved'	=> 0, +				); +			} + +			$update_forums[$forum_id]['forum_posts_approved'] += (int) $row['sum_posts_approved']; +			$update_forums[$forum_id]['forum_posts_unapproved'] += (int) $row['sum_posts_unapproved']; + +			$update_forums[$forum_id][(($row['topic_visibility'] == ITEM_APPROVED) ? 'forum_topics_approved' : 'forum_topics_unapproved')] += (int) $row['sum_topics']; +		} +		$this->db->sql_freeresult($result); + +		foreach ($update_forums as $forum_id => $forum_data) +		{ +			$sql = 'UPDATE ' . FORUMS_TABLE . ' +				SET ' . $this->db->sql_build_array('UPDATE', $forum_data) . ' +				WHERE forum_id = ' . $forum_id; +			$this->sql_query($sql); +		} + +		if ($converted_forums < $limit) +		{ +			// There are no more topics, we are done +			return; +		} + +		// There are still more topics to query, return the next start value +		return $start + $limit; +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php new file mode 100644 index 0000000000..849a996c1b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php @@ -0,0 +1,78 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class softdelete_p2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_approved'); +	} + +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v310\dev', +			'\phpbb\db\migration\data\v310\softdelete_p1', +		); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'		=> array( +				$this->table_prefix . 'forums'			=> array('forum_posts', 'forum_topics', 'forum_topics_real'), +				$this->table_prefix . 'posts'			=> array('post_approved'), +				$this->table_prefix . 'topics'			=> array('topic_approved', 'topic_replies', 'topic_replies_real'), +			), +			'drop_keys'		=> array( +				$this->table_prefix . 'posts'			=> array('post_approved'), +				$this->table_prefix . 'topics'			=> array( +					'forum_appr_last', +					'topic_approved', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'		=> array( +				$this->table_prefix . 'forums'			=> array( +					'forum_posts'			=> array('UINT', 0), +					'forum_topics'			=> array('UINT', 0), +					'forum_topics_real'		=> array('UINT', 0), +				), +				$this->table_prefix . 'posts'			=> array( +					'post_approved'			=> array('BOOL', 1), +				), +				$this->table_prefix . 'topics'		=> array( +					'topic_approved'			=> array('BOOL', 1), +					'topic_replies'				=> array('UINT', 0), +					'topic_replies_real'		=> array('UINT', 0), +				), +			), +			'add_index'		=> array( +				$this->table_prefix . 'posts'			=> array( +					'post_approved'		=> array('post_approved'), +				), +				$this->table_prefix . 'topics'		=> array( +					'forum_appr_last'	=> array('forum_id', 'topic_approved', 'topic_last_post_id'), +					'topic_approved'	=> array('topic_approved'), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php new file mode 100644 index 0000000000..5a3a1d5de7 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php @@ -0,0 +1,191 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class style_update_p1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'style_path'			=> array('VCHAR:100', ''), +					'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +					'style_parent_id'		=> array('UINT', 0), +					'style_parent_tree'		=> array('TEXT', ''), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'style_path', +					'bbcode_bitfield', +					'style_parent_id', +					'style_parent_tree', +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'styles_update'))), +		); +	} + +	public function styles_update() +	{ +		// Get list of valid 3.1 styles +		$available_styles = array('prosilver'); + +		$iterator = new \DirectoryIterator($this->phpbb_root_path . 'styles'); +		$skip_dirs = array('.', '..', 'prosilver'); +		foreach ($iterator as $fileinfo) +		{ +			if ($fileinfo->isDir() && !in_array($fileinfo->getFilename(), $skip_dirs) && file_exists($fileinfo->getPathname() . '/style.cfg')) +			{ +				$style_cfg = parse_cfg_file($fileinfo->getPathname() . '/style.cfg'); +				if (isset($style_cfg['phpbb_version']) && version_compare($style_cfg['phpbb_version'], '3.1.0-dev', '>=')) +				{ +					// 3.1 style +					$available_styles[] = $fileinfo->getFilename(); +				} +			} +		} + +		// Get all installed styles +		if ($this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset')) +		{ +			$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id, i.imageset_path +				FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . 'styles_theme c, ' . $this->table_prefix . "styles_imageset i +				WHERE t.template_id = s.template_id +					AND c.theme_id = s.theme_id +					AND i.imageset_id = s.imageset_id"; +		} +		else +		{ +			$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id +				FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "stles_theme c +				WHERE t.template_id = s.template_id +					AND c.theme_id = s.theme_id"; +		} +		$result = $this->db->sql_query($sql); + +		$styles = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$styles[] = $row; +		} +		$this->db->sql_freeresult($result); + +		// Decide which styles to keep, all others will be deleted +		$valid_styles = array(); +		foreach ($styles as $style_row) +		{ +			if ( +				// Delete styles with parent style (not supported yet) +				$style_row['template_inherits_id'] == 0 && +				// Check if components match +				$style_row['template_path'] == $style_row['theme_path'] && (!isset($style_row['imageset_path']) || $style_row['template_path'] == $style_row['imageset_path']) && +				// Check if components are valid +				in_array($style_row['template_path'], $available_styles) +				) +			{ +				// Valid style. Keep it +				$sql_ary = array( +					'style_path'	=> $style_row['template_path'], +					'bbcode_bitfield'	=> $style_row['bbcode_bitfield'], +					'style_parent_id'	=> 0, +					'style_parent_tree'	=> '', +				); +				$this->sql_query('UPDATE ' . STYLES_TABLE . ' +					SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' +					WHERE style_id = ' . $style_row['style_id']); +				$valid_styles[] = (int) $style_row['style_id']; +			} +		} + +		// Remove old entries from styles table +		if (!sizeof($valid_styles)) +		{ +			// No valid styles: remove everything and add prosilver +			$this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary); + +			$sql_ary = array( +				'style_name'		=> 'prosilver', +				'style_copyright'	=> '© phpBB Limited', +				'style_active'		=> 1, +				'style_path'		=> 'prosilver', +				'bbcode_bitfield'	=> 'lNg=', +				'style_parent_id'	=> 0, +				'style_parent_tree'	=> '', + +				// Will be removed in the next step +				'imageset_id'		=> 0, +				'template_id'		=> 0, +				'theme_id'			=> 0, +			); + +			$sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); +			$this->sql_query($sql); + +			$sql = 'SELECT style_id +				FROM ' . $table . " +				WHERE style_name = 'prosilver'"; +			$result = $this->sql_query($sql); +			$default_style = $this->db->sql_fetchfield($result); +			$this->db->sql_freeresult($result); + +			set_config('default_style', $default_style); + +			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0'; +			$this->sql_query($sql); +		} +		else +		{ +			// There are valid styles in styles table. Remove styles that are outdated +			$this->sql_query('DELETE FROM ' . STYLES_TABLE . ' +				WHERE ' . $this->db->sql_in_set('style_id', $valid_styles, true)); + +			// Change default style +			if (!in_array($this->config['default_style'], $valid_styles)) +			{ +				$this->sql_query('UPDATE ' . CONFIG_TABLE . " +					SET config_value = '" . $valid_styles[0] . "' +					WHERE config_name = 'default_style'"); +			} + +			// Reset styles for users +			$this->sql_query('UPDATE ' . USERS_TABLE . ' +				SET user_style = 0 +				WHERE ' . $this->db->sql_in_set('user_style', $valid_styles, true)); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p2.php b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php new file mode 100644 index 0000000000..52c8ffb2e2 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php @@ -0,0 +1,151 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class style_update_p2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\style_update_p1'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_keys'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'imageset_id', +					'template_id', +					'theme_id', +				), +			), + +			'drop_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'imageset_id', +					'template_id', +					'theme_id', +				), +			), + +			'drop_tables'	=> array( +				$this->table_prefix . 'styles_imageset', +				$this->table_prefix . 'styles_imageset_data', +				$this->table_prefix . 'styles_template', +				$this->table_prefix . 'styles_template_data', +				$this->table_prefix . 'styles_theme', +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'styles'		=> array( +					'imageset_id'	=> array('UINT', 0), +					'template_id'	=> array('UINT', 0), +					'theme_id'		=> array('UINT', 0), +				), +			), + +			'add_index'		=> array( +				$this->table_prefix . 'styles'			=> array( +					'imageset_id'		=> array('imageset_id'), +					'template_id'		=> array('template_id'), +					'theme_id'			=> array('theme_id'), +				), +			), + +			'add_tables'	=> array( +				$this->table_prefix . 'styles_imageset'		=> array( +					'COLUMNS'		=> array( +						'imageset_id'				=> array('UINT', null, 'auto_increment'), +						'imageset_name'				=> array('VCHAR_UNI:255', ''), +						'imageset_copyright'		=> array('VCHAR_UNI', ''), +						'imageset_path'				=> array('VCHAR:100', ''), +					), +					'PRIMARY_KEY'		=> 'imageset_id', +					'KEYS'				=> array( +						'imgset_nm'			=> array('UNIQUE', 'imageset_name'), +					), +				), +				$this->table_prefix . 'styles_imageset_data'	=> array( +					'COLUMNS'		=> array( +						'image_id'				=> array('UINT', null, 'auto_increment'), +						'image_name'			=> array('VCHAR:200', ''), +						'image_filename'		=> array('VCHAR:200', ''), +						'image_lang'			=> array('VCHAR:30', ''), +						'image_height'			=> array('USINT', 0), +						'image_width'			=> array('USINT', 0), +						'imageset_id'			=> array('UINT', 0), +					), +					'PRIMARY_KEY'		=> 'image_id', +					'KEYS'				=> array( +						'i_d'			=> array('INDEX', 'imageset_id'), +					), +				), +				$this->table_prefix . 'styles_template'		=> array( +					'COLUMNS'		=> array( +						'template_id'			=> array('UINT', null, 'auto_increment'), +						'template_name'			=> array('VCHAR_UNI:255', ''), +						'template_copyright'	=> array('VCHAR_UNI', ''), +						'template_path'			=> array('VCHAR:100', ''), +						'bbcode_bitfield'		=> array('VCHAR:255', 'kNg='), +						'template_storedb'		=> array('BOOL', 0), +						'template_inherits_id'		=> array('UINT:4', 0), +						'template_inherit_path'		=> array('VCHAR', ''), +					), +					'PRIMARY_KEY'	=> 'template_id', +					'KEYS'			=> array( +						'tmplte_nm'				=> array('UNIQUE', 'template_name'), +					), +				), +				$this->table_prefix . 'styles_template_data'	=> array( +					'COLUMNS'		=> array( +						'template_id'			=> array('UINT', 0), +						'template_filename'		=> array('VCHAR:100', ''), +						'template_included'		=> array('TEXT', ''), +						'template_mtime'		=> array('TIMESTAMP', 0), +						'template_data'			=> array('MTEXT_UNI', ''), +					), +					'KEYS'			=> array( +						'tid'					=> array('INDEX', 'template_id'), +						'tfn'					=> array('INDEX', 'template_filename'), +					), +				), +				$this->table_prefix . 'styles_theme'			=> array( +					'COLUMNS'		=> array( +						'theme_id'				=> array('UINT', null, 'auto_increment'), +						'theme_name'			=> array('VCHAR_UNI:255', ''), +						'theme_copyright'		=> array('VCHAR_UNI', ''), +						'theme_path'			=> array('VCHAR:100', ''), +						'theme_storedb'			=> array('BOOL', 0), +						'theme_mtime'			=> array('TIMESTAMP', 0), +						'theme_data'			=> array('MTEXT_UNI', ''), +					), +					'PRIMARY_KEY'	=> 'theme_id', +					'KEYS'			=> array( +						'theme_name'		=> array('UNIQUE', 'theme_name'), +					), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/teampage.php b/phpBB/phpbb/db/migration/data/v310/teampage.php new file mode 100644 index 0000000000..f8edbc3492 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/teampage.php @@ -0,0 +1,110 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class teampage extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return $this->db_tools->sql_table_exists($this->table_prefix . 'teampage'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_schema() +	{ +		return array( +			'add_tables'		=> array( +				$this->table_prefix . 'teampage'	=> array( +					'COLUMNS'		=> array( +						'teampage_id'		=> array('UINT', null, 'auto_increment'), +						'group_id'			=> array('UINT', 0), +						'teampage_name'		=> array('VCHAR_UNI:255', ''), +						'teampage_position'	=> array('UINT', 0), +						'teampage_parent'	=> array('UINT', 0), +					), +					'PRIMARY_KEY'		=> 'teampage_id', +				), +			), +			'drop_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_tables'		=> array( +				$this->table_prefix . 'teampage', +			), +			'add_columns'		=> array( +				$this->table_prefix . 'groups'		=> array( +					'group_teampage'	=> array('UINT', 0, 'after' => 'group_legend'), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'add_groups_teampage'))), +		); +	} + +	public function add_groups_teampage() +	{ +		$sql = 'SELECT teampage_id +			FROM ' . TEAMPAGE_TABLE; +		$result = $this->db->sql_query_limit($sql, 1); +		$added_groups_teampage = (bool) $this->db->sql_fetchfield('teampage_id'); +		$this->db->sql_freeresult($result); + +		if (!$added_groups_teampage) +		{ +			$sql = 'SELECT * +				FROM ' . GROUPS_TABLE . ' +				WHERE group_type = ' . GROUP_SPECIAL . " +					AND (group_name = 'ADMINISTRATORS' +						OR group_name = 'GLOBAL_MODERATORS') +				ORDER BY group_name ASC"; +			$result = $this->db->sql_query($sql); + +			$teampage_entries = array(); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$teampage_entries[] = array( +					'group_id'			=> (int) $row['group_id'], +					'teampage_name'		=> '', +					'teampage_position'	=> sizeof($teampage_entries) + 1, +					'teampage_parent'	=> 0, +				); +			} +			$this->db->sql_freeresult($result); + +			if (sizeof($teampage_entries)) +			{ +				$this->db->sql_multi_insert(TEAMPAGE_TABLE, $teampage_entries); +			} +			unset($teampage_entries); +		} + +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/timezone.php b/phpBB/phpbb/db/migration/data/v310/timezone.php new file mode 100644 index 0000000000..1f6b47ad50 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/timezone.php @@ -0,0 +1,194 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class timezone extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v30x\release_3_0_11'); +	} + +	public function update_schema() +	{ +		return array( +			'change_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_timezone'		=> array('VCHAR:100', ''), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'update_timezones'))), +		); +	} + +	public function update_timezones($start) +	{ +		$start = (int) $start; +		$limit = 500; +		$converted = 0; + +		$update_blocks = array(); + +		$sql = 'SELECT user_id, user_timezone, user_dst +			FROM ' . $this->table_prefix . 'users +			ORDER BY user_id ASC'; +		$result = $this->db->sql_query_limit($sql, $limit, $start); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$converted++; + +			// In case this is somehow run twice on a row. +			// Otherwise it would just end up as UTC on the second run +			if (is_numeric($row['user_timezone'])) +			{ +				$update_blocks[$row['user_timezone'] . ':' . $row['user_dst']][] = (int) $row['user_id']; +			} +		} +		$this->db->sql_freeresult($result); + +		// Update blocks of users who share the same timezone/dst +		foreach ($update_blocks as $timezone => $user_ids) +		{ +			$timezone = explode(':', $timezone); +			$converted_timezone = $this->convert_phpbb30_timezone($timezone[0], $timezone[1]); + +			$sql = 'UPDATE ' . $this->table_prefix . "users +				SET user_timezone = '" . $this->db->sql_escape($converted_timezone) . "' +				WHERE " . $this->db->sql_in_set('user_id', $user_ids); +			$this->sql_query($sql); +		} + +		if ($converted == $limit) +		{ +			// There are still more to convert +			return $start + $limit; +		} + +		// Update board default timezone +		$sql = 'UPDATE ' . $this->table_prefix . "config +			SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "' +			WHERE config_name = 'board_timezone'"; +		$this->sql_query($sql); +	} + +	/** +	* Determine the new timezone for a given phpBB 3.0 timezone and +	* "Daylight Saving Time" option +	* +	*	@param	$timezone	float	Users timezone in 3.0 +	*	@param	$dst		int		Users daylight saving time +	*	@return		string		Users new php Timezone which is used since 3.1 +	*/ +	public function convert_phpbb30_timezone($timezone, $dst) +	{ +		$offset = $timezone + $dst; + +		switch ($timezone) +		{ +			case '-12': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 12] Baker Island Time' +			case '-11': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 11] Niue Time, Samoa Standard Time' +			case '-10': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time' +			case '-9.5': +				return 'Pacific/Marquesas';			//'[UTC - 9:30] Marquesas Islands Time' +			case '-9': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 9] Alaska Standard Time, Gambier Island Time' +			case '-8': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 8] Pacific Standard Time' +			case '-7': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 7] Mountain Standard Time' +			case '-6': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 6] Central Standard Time' +			case '-5': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 5] Eastern Standard Time' +			case '-4.5': +				return 'America/Caracas';			//'[UTC - 4:30] Venezuelan Standard Time' +			case '-4': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 4] Atlantic Standard Time' +			case '-3.5': +				return 'America/St_Johns';			//'[UTC - 3:30] Newfoundland Standard Time' +			case '-3': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 3] Amazon Standard Time, Central Greenland Time' +			case '-2': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 2] Fernando de Noronha Time, South Georgia & the South Sandwich Islands Time' +			case '-1': +				return 'Etc/GMT+' . abs($offset);	//'[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time' +			case '0': +				return (!$dst) ? 'UTC' : 'Etc/GMT-1';	//'[UTC] Western European Time, Greenwich Mean Time' +			case '1': +				return 'Etc/GMT-' . $offset;		//'[UTC + 1] Central European Time, West African Time' +			case '2': +				return 'Etc/GMT-' . $offset;		//'[UTC + 2] Eastern European Time, Central African Time' +			case '3': +				return 'Etc/GMT-' . $offset;		//'[UTC + 3] Moscow Standard Time, Eastern African Time' +			case '3.5': +				return 'Asia/Tehran';				//'[UTC + 3:30] Iran Standard Time' +			case '4': +				return 'Etc/GMT-' . $offset;		//'[UTC + 4] Gulf Standard Time, Samara Standard Time' +			case '4.5': +				return 'Asia/Kabul';				//'[UTC + 4:30] Afghanistan Time' +			case '5': +				return 'Etc/GMT-' . $offset;		//'[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time' +			case '5.5': +				return 'Asia/Kolkata';				//'[UTC + 5:30] Indian Standard Time, Sri Lanka Time' +			case '5.75': +				return 'Asia/Kathmandu';			//'[UTC + 5:45] Nepal Time' +			case '6': +				return 'Etc/GMT-' . $offset;		//'[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time' +			case '6.5': +				return 'Indian/Cocos';				//'[UTC + 6:30] Cocos Islands Time, Myanmar Time' +			case '7': +				return 'Etc/GMT-' . $offset;		//'[UTC + 7] Indochina Time, Krasnoyarsk Standard Time' +			case '8': +				return 'Etc/GMT-' . $offset;		//'[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time' +			case '8.75': +				return 'Australia/Eucla';			//'[UTC + 8:45] Southeastern Western Australia Standard Time' +			case '9': +				return 'Etc/GMT-' . $offset;		//'[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time' +			case '9.5': +				return 'Australia/ACT';				//'[UTC + 9:30] Australian Central Standard Time' +			case '10': +				return 'Etc/GMT-' . $offset;		//'[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time' +			case '10.5': +				return 'Australia/Lord_Howe';		//'[UTC + 10:30] Lord Howe Standard Time' +			case '11': +				return 'Etc/GMT-' . $offset;		//'[UTC + 11] Solomon Island Time, Magadan Standard Time' +			case '11.5': +				return 'Pacific/Norfolk';			//'[UTC + 11:30] Norfolk Island Time' +			case '12': +				return 'Etc/GMT-12';				//'[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time' +			case '12.75': +				return 'Pacific/Chatham';			//'[UTC + 12:45] Chatham Islands Time' +			case '13': +				return 'Pacific/Tongatapu';			//'[UTC + 13] Tonga Time, Phoenix Islands Time' +			case '14': +				return 'Pacific/Kiritimati';		//'[UTC + 14] Line Island Time' +			default: +				return 'UTC'; +		} +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/timezone_p2.php b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php new file mode 100644 index 0000000000..3ac7ab3c51 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php @@ -0,0 +1,49 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class timezone_p2 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst'); +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\timezone'); +	} + +	public function update_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_dst', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'users'			=> array( +					'user_dst'		=> array('BOOL', 0), +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php b/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php new file mode 100644 index 0000000000..8600f6ee27 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php @@ -0,0 +1,46 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v310; + +class ucp_popuppm_module extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT module_id +			FROM ' . MODULES_TABLE . " +			WHERE module_class = 'ucp' +				AND module_langname = 'UCP_PM_POPUP_TITLE'"; +		$result = $this->db->sql_query($sql); +		$module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		return $module_id == false; +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\dev'); +	} + +	public function update_data() +	{ +		return array( +			array('module.remove', array( +				'ucp', +				'UCP_PM', +				'UCP_PM_POPUP_TITLE', +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/exception.php b/phpBB/phpbb/db/migration/exception.php new file mode 100644 index 0000000000..7990e85f2d --- /dev/null +++ b/phpBB/phpbb/db/migration/exception.php @@ -0,0 +1,75 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration; + +/** +* The migrator is responsible for applying new migrations in the correct order. +*/ +class exception extends \Exception +{ +	/** +	* Extra parameters sent to exception to aid in debugging +	* @var array +	*/ +	protected $parameters; + +	/** +	* Throw an exception. +	* +	* First argument is the error message. +	* Additional arguments will be output with the error message. +	*/ +	public function __construct() +	{ +		$parameters = func_get_args(); +		$message = array_shift($parameters); +		parent::__construct($message); + +		$this->parameters = $parameters; +	} + +	/** +	* Output the error as a string +	* +	* @return string +	*/ +	public function __toString() +	{ +		return $this->message . ': ' . var_export($this->parameters, true); +	} + +	/** +	* Get the parameters +	* +	* @return array +	*/ +	public function getParameters() +	{ +		return $this->parameters; +	} + +	/** +	* Get localised message (with $user->lang()) +	* +	* @param \phpbb\user $user +	* @return string +	*/ +	public function getLocalisedMessage(\phpbb\user $user) +	{ +		$parameters = $this->getParameters(); +		array_unshift($parameters, $this->getMessage()); + +		return call_user_func_array(array($user, 'lang'), $parameters); +	} +} diff --git a/phpBB/phpbb/db/migration/helper.php b/phpBB/phpbb/db/migration/helper.php new file mode 100644 index 0000000000..e40deeb37b --- /dev/null +++ b/phpBB/phpbb/db/migration/helper.php @@ -0,0 +1,84 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration; + +/** +* The migrator is responsible for applying new migrations in the correct order. +*/ +class helper +{ +	/** +	 * Get the schema steps from an array of schema changes +	 * +	 * This splits up $schema_changes into individual changes so that the +	 * changes can be chunked +	 * +	 * @param array $schema_changes from migration +	 * @return array +	 */ +	public function get_schema_steps($schema_changes) +	{ +		$steps = array(); + +		// Nested level of data (only supports 1/2 currently) +		$nested_level = array( +			'drop_tables'		=> 1, +			'add_tables'		=> 1, +			'change_columns'	=> 2, +			'add_columns'		=> 2, +			'drop_keys'			=> 2, +			'drop_columns'		=> 2, +			'add_primary_keys'	=> 2, // perform_schema_changes only uses one level, but second is in the function +			'add_unique_index'	=> 2, +			'add_index'			=> 2, +		); + +		foreach ($nested_level as $change_type => $data_depth) +		{ +			if (!empty($schema_changes[$change_type])) +			{ +				foreach ($schema_changes[$change_type] as $key => $value) +				{ +					if ($data_depth === 1) +					{ +						$steps[] = array( +							'dbtools.perform_schema_changes', array(array( +									$change_type	=> array( +										(!is_int($key)) ? $key : 0	=> $value, +								), +							)), +						); +					} +					else if ($data_depth === 2) +					{ +						foreach ($value as $key2 => $value2) +						{ +							$steps[] = array( +								'dbtools.perform_schema_changes', array(array( +									$change_type	=> array( +										$key => array( +											$key2	=> $value2, +										), +									), +								)), +							); +						} +					} +				} +			} +		} + +		return $steps; +	} +} diff --git a/phpBB/phpbb/db/migration/migration.php b/phpBB/phpbb/db/migration/migration.php new file mode 100644 index 0000000000..fdfc31da3a --- /dev/null +++ b/phpBB/phpbb/db/migration/migration.php @@ -0,0 +1,186 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration; + +/** +* Abstract base class for database migrations +* +* Each migration consists of a set of schema and data changes to be implemented +* in a subclass. This class provides various utility methods to simplify editing +* a phpBB. +*/ +abstract class migration +{ +	/** @var \phpbb\config\config */ +	protected $config; + +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var \phpbb\db\tools */ +	protected $db_tools; + +	/** @var string */ +	protected $table_prefix; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var array Errors, if any occurred */ +	protected $errors; + +	/** @var array List of queries executed through $this->sql_query() */ +	protected $queries = array(); + +	/** +	* Constructor +	* +	* @param \phpbb\config\config $config +	* @param \phpbb\db\driver\driver_interface $db +	* @param \phpbb\db\tools $db_tools +	* @param string $phpbb_root_path +	* @param string $php_ext +	* @param string $table_prefix +	*/ +	public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix) +	{ +		$this->config = $config; +		$this->db = $db; +		$this->db_tools = $db_tools; +		$this->table_prefix = $table_prefix; + +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; + +		$this->errors = array(); +	} + +	/** +	* Defines other migrations to be applied first +	* +	* @return array An array of migration class names +	*/ +	static public function depends_on() +	{ +		return array(); +	} + +	/** +	* Allows you to check if the migration is effectively installed (entirely optional) +	* +	* This is checked when a migration is installed. If true is returned, the migration will be set as +	* installed without performing the database changes. +	* This function is intended to help moving to migrations from a previous database updater, where some +	* migrations may have been installed already even though they are not yet listed in the migrations table. +	* +	* @return bool True if this migration is installed, False if this migration is not installed (checked on install) +	*/ +	public function effectively_installed() +	{ +		return false; +	} + +	/** +	* Updates the database schema by providing a set of change instructions +	* +	* @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) +	*/ +	public function update_schema() +	{ +		return array(); +	} + +	/** +	* Reverts the database schema by providing a set of change instructions +	* +	* @return array Array of schema changes (compatible with db_tools->perform_schema_changes()) +	*/ +	public function revert_schema() +	{ +		return array(); +	} + +	/** +	* Updates data by returning a list of instructions to be executed +	* +	* @return array Array of data update instructions +	*/ +	public function update_data() +	{ +		return array(); +	} + +	/** +	* Reverts data by returning a list of instructions to be executed +	* +	* @return array Array of data instructions that will be performed on revert +	* 	NOTE: calls to tools (such as config.add) are automatically reverted when +	* 		possible, so you should not attempt to revert those, this is mostly for +	* 		otherwise unrevertable calls (custom functions for example) +	*/ +	public function revert_data() +	{ +		return array(); +	} + +	/** +	* Wrapper for running queries to generate user feedback on updates +	* +	* @param string $sql SQL query to run on the database +	* @return mixed Query result from db->sql_query() +	*/ +	protected function sql_query($sql) +	{ +		$this->queries[] = $sql; + +		$this->db->sql_return_on_error(true); + +		if ($sql === 'begin') +		{ +			$result = $this->db->sql_transaction('begin'); +		} +		else if ($sql === 'commit') +		{ +			$result = $this->db->sql_transaction('commit'); +		} +		else +		{ +			$result = $this->db->sql_query($sql); +			if ($this->db->sql_error_triggered) +			{ +				$this->errors[] = array( +					'sql'	=> $this->db->sql_error_sql, +					'code'	=> $this->db->sql_error_returned, +				); +			} +		} + +		$this->db->sql_return_on_error(false); + +		return $result; +	} + +	/** +	* Get the list of queries run +	* +	* @return array +	*/ +	public function get_queries() +	{ +		return $this->queries; +	} +} diff --git a/phpBB/phpbb/db/migration/profilefield_base_migration.php b/phpBB/phpbb/db/migration/profilefield_base_migration.php new file mode 100644 index 0000000000..9cdd5d0927 --- /dev/null +++ b/phpBB/phpbb/db/migration/profilefield_base_migration.php @@ -0,0 +1,159 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration; + +abstract class profilefield_base_migration extends \phpbb\db\migration\migration +{ +	protected $profilefield_name; + +	protected $profilefield_database_type; + +	protected $profilefield_data; + +	protected $user_column_name; + +	public function effectively_installed() +	{ +		return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields_data', 'pf_' . $this->profilefield_name); +	} + +	public function update_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'profile_fields_data'			=> array( +					'pf_' . $this->profilefield_name		=> $this->profilefield_database_type, +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'	=> array( +				$this->table_prefix . 'profile_fields_data'			=> array( +					'pf_' . $this->profilefield_name, +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +			array('custom', array(array($this, 'convert_user_field_to_custom_field'))), +		); +	} + +	public function create_custom_field() +	{ +		$sql = 'SELECT MAX(field_order) as max_field_order +			FROM ' . PROFILE_FIELDS_TABLE; +		$result = $this->db->sql_query($sql); +		$max_field_order = (int) $this->db->sql_fetchfield('max_field_order'); +		$this->db->sql_freeresult($result); + +		$sql_ary = array_merge($this->profilefield_data, array( +			'field_order'			=> $max_field_order + 1, +		)); + +		$sql = 'INSERT INTO ' . PROFILE_FIELDS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); +		$this->db->sql_query($sql); +		$field_id = (int) $this->db->sql_nextid(); + +		$insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, PROFILE_LANG_TABLE); + +		$sql = 'SELECT lang_id +			FROM ' . LANG_TABLE; +		$result = $this->db->sql_query($sql); +		while ($lang_id = (int) $this->db->sql_fetchfield('lang_id')) +		{ +			$insert_buffer->insert(array( +				'field_id'				=> $field_id, +				'lang_id'				=> $lang_id, +				'lang_name'				=> strtoupper(substr($this->profilefield_name, 6)),// Remove phpbb_ from field name +				'lang_explain'			=> '', +				'lang_default_value'	=> '', +			)); +		} +		$this->db->sql_freeresult($result); + +		$insert_buffer->flush(); +	} + +	/** +	* @param int			$start		Start of staggering step +	* @return		mixed		int start of the next step, null if the end was reached +	*/ +	public function convert_user_field_to_custom_field($start) +	{ +		$insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->table_prefix . 'profile_fields_data'); +		$limit = 250; +		$converted_users = 0; + +		$sql = 'SELECT user_id, ' . $this->user_column_name . ' +			FROM ' . $this->table_prefix . 'users +			WHERE ' . $this->user_column_name . " <> '' +			ORDER BY user_id"; +		$result = $this->db->sql_query_limit($sql, $limit, $start); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$converted_users++; + +			$cp_data = array( +				'pf_' . $this->profilefield_name		=> $row[$this->user_column_name], +			); + +			$sql = 'UPDATE ' . $this->table_prefix . 'profile_fields_data +				SET ' . $this->db->sql_build_array('UPDATE', $cp_data) . ' +				WHERE user_id = ' . (int) $row['user_id']; +			$this->db->sql_query($sql); + +			if (!$this->db->sql_affectedrows()) +			{ +				$cp_data['user_id'] = (int) $row['user_id']; +				$cp_data = array_merge($this->get_insert_sql_array(), $cp_data); +				$insert_buffer->insert($cp_data); +			} +		} +		$this->db->sql_freeresult($result); + +		$insert_buffer->flush(); + +		if ($converted_users < $limit) +		{ +			// No more users left, we are done... +			return; +		} + +		return $start + $limit; +	} + +	protected function get_insert_sql_array() +	{ +		static $profile_row; + +		if ($profile_row === null) +		{ +			global $phpbb_container; +			$manager = $phpbb_container->get('profilefields.manager'); +			$profile_row = $manager->build_insert_sql_array(array()); +		} + +		return $profile_row; +	} +} diff --git a/phpBB/phpbb/db/migration/schema_generator.php b/phpBB/phpbb/db/migration/schema_generator.php new file mode 100644 index 0000000000..91d8307d91 --- /dev/null +++ b/phpBB/phpbb/db/migration/schema_generator.php @@ -0,0 +1,235 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration; + +/** +* The schema generator generates the schema based on the existing migrations +*/ +class schema_generator +{ +	/** @var \phpbb\config\config */ +	protected $config; + +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var \phpbb\db\tools */ +	protected $db_tools; + +	/** @var array */ +	protected $class_names; + +	/** @var string */ +	protected $table_prefix; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var array */ +	protected $tables; + +	/** @var array */ +	protected $dependencies = array(); + +	/** +	* Constructor +	*/ +	public function __construct(array $class_names, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix) +	{ +		$this->config = $config; +		$this->db = $db; +		$this->db_tools = $db_tools; +		$this->class_names = $class_names; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +		$this->table_prefix = $table_prefix; +	} + +	/** +	* Loads all migrations and their application state from the database. +	* +	* @return array +	*/ +	public function get_schema() +	{ +		if (!empty($this->tables)) +		{ +			return $this->tables; +		} + +		$migrations = $this->class_names; + +		$tree = array(); +		$check_dependencies = true; +		while (!empty($migrations)) +		{ +			foreach ($migrations as $migration_class) +			{ +				$open_dependencies = array_diff($migration_class::depends_on(), $tree); + +				if (empty($open_dependencies)) +				{ +					$migration = new $migration_class($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix); +					$tree[] = $migration_class; +					$migration_key = array_search($migration_class, $migrations); + +					foreach ($migration->update_schema() as $change_type => $data) +					{ +						if ($change_type === 'add_tables') +						{ +							foreach ($data as $table => $table_data) +							{ +								$this->tables[$table] = $table_data; +							} +						} +						else if ($change_type === 'drop_tables') +						{ +							foreach ($data as $table) +							{ +								unset($this->tables[$table]); +							} +						} +						else if ($change_type === 'add_columns') +						{ +							foreach ($data as $table => $add_columns) +							{ +								foreach ($add_columns as $column => $column_data) +								{ +									if (isset($column_data['after'])) +									{ +										$columns = $this->tables[$table]['COLUMNS']; +										$offset = array_search($column_data['after'], array_keys($columns)); +										unset($column_data['after']); + +										if ($offset === false) +										{ +											$this->tables[$table]['COLUMNS'][$column] = array_values($column_data); +										} +										else +										{ +											$this->tables[$table]['COLUMNS'] = array_merge(array_slice($columns, 0, $offset + 1, true), array($column => array_values($column_data)), array_slice($columns, $offset)); +										} +									} +									else +									{ +										$this->tables[$table]['COLUMNS'][$column] = $column_data; +									} +								} +							} +						} +						else if ($change_type === 'change_columns') +						{ +							foreach ($data as $table => $change_columns) +							{ +								foreach ($change_columns as $column => $column_data) +								{ +									$this->tables[$table]['COLUMNS'][$column] = $column_data; +								} +							} +						} +						else if ($change_type === 'drop_columns') +						{ +							foreach ($data as $table => $drop_columns) +							{ +								if (is_array($drop_columns)) +								{ +									foreach ($drop_columns as $column) +									{ +										unset($this->tables[$table]['COLUMNS'][$column]); +									} +								} +								else +								{ +									unset($this->tables[$table]['COLUMNS'][$drop_columns]); +								} +							} +						} +						else if ($change_type === 'add_unique_index') +						{ +							foreach ($data as $table => $add_index) +							{ +								foreach ($add_index as $key => $index_data) +								{ +									$this->tables[$table]['KEYS'][$key] = array('UNIQUE', $index_data); +								} +							} +						} +						else if ($change_type === 'add_index') +						{ +							foreach ($data as $table => $add_index) +							{ +								foreach ($add_index as $key => $index_data) +								{ +									$this->tables[$table]['KEYS'][$key] = array('INDEX', $index_data); +								} +							} +						} +						else if ($change_type === 'drop_keys') +						{ +							foreach ($data as $table => $drop_keys) +							{ +								foreach ($drop_keys as $key) +								{ +									unset($this->tables[$table]['KEYS'][$key]); +								} +							} +						} +						else +						{ +							var_dump($change_type); +						} +					} +					unset($migrations[$migration_key]); +				} +				else if ($check_dependencies) +				{ +					$this->dependencies = array_merge($this->dependencies, $open_dependencies); +				} +			} + +			// Only run this check after the first run +			if ($check_dependencies) +			{ +				$this->check_dependencies(); +				$check_dependencies = false; +			} +		} + +		ksort($this->tables); +		return $this->tables; +	} + +	/** +	* Check if one of the migrations files' dependencies can't be resolved +	* by the supplied list of migrations +	* +	* @throws \UnexpectedValueException If a dependency can't be resolved +	*/ +	protected function check_dependencies() +	{ +		// Strip duplicate values from array +		$this->dependencies = array_unique($this->dependencies); + +		foreach ($this->dependencies as $dependency) +		{ +			if (!in_array($dependency, $this->class_names)) +			{ +				throw new \UnexpectedValueException("Unable to resolve the dependency '$dependency'"); +			} +		} +	} +} diff --git a/phpBB/phpbb/db/migration/tool/config.php b/phpBB/phpbb/db/migration/tool/config.php new file mode 100644 index 0000000000..f93e7118c4 --- /dev/null +++ b/phpBB/phpbb/db/migration/tool/config.php @@ -0,0 +1,160 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\tool; + +/** +* Migration config tool +*/ +class config implements \phpbb\db\migration\tool\tool_interface +{ +	/** @var \phpbb\config\config */ +	protected $config; + +	/** +	* Constructor +	* +	* @param \phpbb\config\config $config +	*/ +	public function __construct(\phpbb\config\config $config) +	{ +		$this->config = $config; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_name() +	{ +		return 'config'; +	} + +	/** +	* Add a config setting. +	* +	* @param string $config_name The name of the config setting +	* 	you would like to add +	* @param mixed $config_value The value of the config setting +	* @param bool $is_dynamic True if it is dynamic (changes very often) +	* 	and should not be stored in the cache, false if not. +	* @return null +	*/ +	public function add($config_name, $config_value, $is_dynamic = false) +	{ +		if (isset($this->config[$config_name])) +		{ +			return; +		} + +		$this->config->set($config_name, $config_value, !$is_dynamic); +	} + +	/** +	* Update an existing config setting. +	* +	* @param string $config_name The name of the config setting you would +	* 	like to update +	* @param mixed $config_value The value of the config setting +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function update($config_name, $config_value) +	{ +		if (!isset($this->config[$config_name])) +		{ +			throw new \phpbb\db\migration\exception('CONFIG_NOT_EXIST', $config_name); +		} + +		$this->config->set($config_name, $config_value); +	} + +	/** +	* Update a config setting if the first argument equal to the +	* current config value +	* +	* @param string $compare If equal to the current config value, will be +	* 	updated to the new config value, otherwise not +	* @param string $config_name The name of the config setting you would +	* 	like to update +	* @param mixed $config_value The value of the config setting +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function update_if_equals($compare, $config_name, $config_value) +	{ +		if (!isset($this->config[$config_name])) +		{ +			throw new \phpbb\db\migration\exception('CONFIG_NOT_EXIST', $config_name); +		} + +		$this->config->set_atomic($config_name, $compare, $config_value); +	} + +	/** +	* Remove an existing config setting. +	* +	* @param string $config_name The name of the config setting you would +	* 	like to remove +	* @return null +	*/ +	public function remove($config_name) +	{ +		if (!isset($this->config[$config_name])) +		{ +			return; +		} + +		$this->config->delete($config_name); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function reverse() +	{ +		$arguments = func_get_args(); +		$original_call = array_shift($arguments); + +		$call = false; +		switch ($original_call) +		{ +			case 'add': +				$call = 'remove'; +			break; + +			case 'remove': +				$call = 'add'; +				if (sizeof($arguments) == 1) +				{ +					$arguments[] = ''; +				} +			break; + +			case 'update_if_equals': +				$call = 'update_if_equals'; + +				// Set to the original value if the current value is what we compared to originally +				$arguments = array( +					$arguments[2], +					$arguments[1], +					$arguments[0], +				); +			break; +		} + +		if ($call) +		{ +			return call_user_func_array(array(&$this, $call), $arguments); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/tool/module.php b/phpBB/phpbb/db/migration/tool/module.php new file mode 100644 index 0000000000..db43046a95 --- /dev/null +++ b/phpBB/phpbb/db/migration/tool/module.php @@ -0,0 +1,489 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\tool; + +/** +* Migration module management tool +*/ +class module implements \phpbb\db\migration\tool\tool_interface +{ +	/** @var \phpbb\cache\service */ +	protected $cache; + +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var \phpbb\user */ +	protected $user; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var string */ +	protected $modules_table; + +	/** +	* Constructor +	* +	* @param \phpbb\db\driver\driver_interface $db +	* @param \phpbb\cache\service $cache +	* @param \phpbb\user $user +	* @param string $phpbb_root_path +	* @param string $php_ext +	* @param string $modules_table +	*/ +	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, $phpbb_root_path, $php_ext, $modules_table) +	{ +		$this->db = $db; +		$this->cache = $cache; +		$this->user = $user; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +		$this->modules_table = $modules_table; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_name() +	{ +		return 'module'; +	} + +	/** +	* Module Exists +	* +	* Check if a module exists +	* +	* @param string $class The module class(acp|mcp|ucp) +	* @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. +	* @param int|string $module The module_id|module_langname you would like to +	* 		check for to see if it exists +	* @return bool true/false if module exists +	*/ +	public function exists($class, $parent, $module) +	{ +		// the main root directory should return true +		if (!$module) +		{ +			return true; +		} + +		$parent_sql = ''; +		if ($parent !== false) +		{ +			// Allows '' to be sent as 0 +			$parent = $parent ?: 0; + +			if (!is_numeric($parent)) +			{ +				$sql = 'SELECT module_id +					FROM ' . $this->modules_table . " +					WHERE module_langname = '" . $this->db->sql_escape($parent) . "' +						AND module_class = '" . $this->db->sql_escape($class) . "'"; +				$result = $this->db->sql_query($sql); +				$module_id = $this->db->sql_fetchfield('module_id'); +				$this->db->sql_freeresult($result); + +				if (!$module_id) +				{ +					return false; +				} + +				$parent_sql = 'AND parent_id = ' . (int) $module_id; +			} +			else +			{ +				$parent_sql = 'AND parent_id = ' . (int) $parent; +			} +		} + +		$sql = 'SELECT module_id +			FROM ' . $this->modules_table . " +			WHERE module_class = '" . $this->db->sql_escape($class) . "' +				$parent_sql +				AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '" . $this->db->sql_escape($module) . "'"); +		$result = $this->db->sql_query($sql); +		$module_id = $this->db->sql_fetchfield('module_id'); +		$this->db->sql_freeresult($result); + +		if ($module_id) +		{ +			return true; +		} + +		return false; +	} + +	/** +	* Module Add +	* +	* Add a new module +	* +	* @param string $class The module class(acp|mcp|ucp) +	* @param int|string $parent The parent module_id|module_langname (0 for no parent) +	* @param array $data an array of the data on the new \module. +	* 	This can be setup in two different ways. +	*	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, +	*			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. +	*		array( +	*			'module_enabled'	=> 1, +	*			'module_display'	=> 1, +	*	   		'module_basename'	=> '', +	*			'module_class'		=> $class, +	*	   		'parent_id'			=> (int) $parent, +	*			'module_langname'	=> '', +	*	   		'module_mode'		=> '', +	*	   		'module_auth'		=> '', +	*		) +	*	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. +	*		An example follows (this would insert the settings, log, and flag +	*			modes from the includes/acp/info/acp_asacp.php file): +	* 		array( +	* 			'module_basename'	=> 'asacp', +	* 			'modes'				=> array('settings', 'log', 'flag'), +	* 		) +	* 		Optionally you may not send 'modes' and it will insert all of the +	* 			modules in that info file. +	* 	path, specify that here +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function add($class, $parent = 0, $data = array()) +	{ +		// Allows '' to be sent as 0 +		$parent = $parent ?: 0; + +		// allow sending the name as a string in $data to create a category +		if (!is_array($data)) +		{ +			$data = array('module_langname' => $data); +		} + +		if (!isset($data['module_langname'])) +		{ +			// The "automatic" way +			$basename = (isset($data['module_basename'])) ? $data['module_basename'] : ''; +			$module = $this->get_module_info($class, $basename); + +			$result = ''; +			foreach ($module['modes'] as $mode => $module_info) +			{ +				if (!isset($data['modes']) || in_array($mode, $data['modes'])) +				{ +					$new_module = array( +						'module_basename'	=> $basename, +						'module_langname'	=> $module_info['title'], +						'module_mode'		=> $mode, +						'module_auth'		=> $module_info['auth'], +						'module_display'	=> (isset($module_info['display'])) ? $module_info['display'] : true, +						'before'			=> (isset($module_info['before'])) ? $module_info['before'] : false, +						'after'				=> (isset($module_info['after'])) ? $module_info['after'] : false, +					); + +					// Run the "manual" way with the data we've collected. +					$this->add($class, $parent, $new_module); +				} +			} + +			return; +		} + +		// The "manual" way +		if (!is_numeric($parent)) +		{ +			$sql = 'SELECT module_id +				FROM ' . $this->modules_table . " +				WHERE module_langname = '" . $this->db->sql_escape($parent) . "' +					AND module_class = '" . $this->db->sql_escape($class) . "'"; +			$result = $this->db->sql_query($sql); +			$module_id = $this->db->sql_fetchfield('module_id'); +			$this->db->sql_freeresult($result); + +			if (!$module_id) +			{ +				throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent); +			} + +			$parent = $data['parent_id'] = $module_id; +		} +		else if (!$this->exists($class, false, $parent)) +		{ +			throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent); +		} + +		if ($this->exists($class, $parent, $data['module_langname'])) +		{ +			return; +		} + +		if (!class_exists('acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +			$this->user->add_lang('acp/modules'); +		} +		$acp_modules = new \acp_modules(); + +		$module_data = array( +			'module_enabled'	=> (isset($data['module_enabled'])) ? $data['module_enabled'] : 1, +			'module_display'	=> (isset($data['module_display'])) ? $data['module_display'] : 1, +			'module_basename'	=> (isset($data['module_basename'])) ? $data['module_basename'] : '', +			'module_class'		=> $class, +			'parent_id'			=> (int) $parent, +			'module_langname'	=> (isset($data['module_langname'])) ? $data['module_langname'] : '', +			'module_mode'		=> (isset($data['module_mode'])) ? $data['module_mode'] : '', +			'module_auth'		=> (isset($data['module_auth'])) ? $data['module_auth'] : '', +		); +		$result = $acp_modules->update_module_data($module_data, true); + +		// update_module_data can either return a string or an empty array... +		if (is_string($result)) +		{ +			// Error +			throw new \phpbb\db\migration\exception('MODULE_ERROR', $result); +		} +		else +		{ +			// Success +			$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']); +			add_log('admin', 'LOG_MODULE_ADD', $module_log_name); + +			// Move the module if requested above/below an existing one +			if (isset($data['before']) && $data['before']) +			{ +				$sql = 'SELECT left_id +					FROM ' . $this->modules_table . " +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND parent_id = " . (int) $parent . " +						AND module_langname = '" . $this->db->sql_escape($data['before']) . "'"; +				$this->db->sql_query($sql); +				$to_left = (int) $this->db->sql_fetchfield('left_id'); + +				$sql = 'UPDATE ' . $this->modules_table . " +					SET left_id = left_id + 2, right_id = right_id + 2 +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND left_id >= $to_left +						AND left_id < {$module_data['left_id']}"; +				$this->db->sql_query($sql); + +				$sql = 'UPDATE ' . $this->modules_table . " +					SET left_id = $to_left, right_id = " . ($to_left + 1) . " +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND module_id = {$module_data['module_id']}"; +				$this->db->sql_query($sql); +			} +			else if (isset($data['after']) && $data['after']) +			{ +				$sql = 'SELECT right_id +					FROM ' . $this->modules_table . " +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND parent_id = " . (int) $parent . " +						AND module_langname = '" . $this->db->sql_escape($data['after']) . "'"; +				$this->db->sql_query($sql); +				$to_right = (int) $this->db->sql_fetchfield('right_id'); + +				$sql = 'UPDATE ' . $this->modules_table . " +					SET left_id = left_id + 2, right_id = right_id + 2 +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND left_id >= $to_right +						AND left_id < {$module_data['left_id']}"; +				$this->db->sql_query($sql); + +				$sql = 'UPDATE ' . $this->modules_table . ' +					SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . " +					WHERE module_class = '" . $this->db->sql_escape($class) . "' +						AND module_id = {$module_data['module_id']}"; +				$this->db->sql_query($sql); +			} +		} + +		// Clear the Modules Cache +		$this->cache->destroy("_modules_$class"); +	} + +	/** +	* Module Remove +	* +	* Remove a module +	* +	* @param string $class The module class(acp|mcp|ucp) +	* @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. +	* @param int|string $module The module id|module_langname +	* 	specify that here +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function remove($class, $parent = 0, $module = '') +	{ +		// Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto +		if (is_array($module)) +		{ +			if (isset($module['module_langname'])) +			{ +				// Manual Method +				return $this->remove($class, $parent, $module['module_langname']); +			} + +			// Failed. +			if (!isset($module['module_basename'])) +			{ +				throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST'); +			} + +			// Automatic method +			$basename = $module['module_basename']; +			$module_info = $this->get_module_info($class, $basename); + +			foreach ($module_info['modes'] as $mode => $info) +			{ +				if (!isset($module['modes']) || in_array($mode, $module['modes'])) +				{ +					$this->remove($class, $parent, $info['title']); +				} +			} +		} +		else +		{ +			if (!$this->exists($class, $parent, $module)) +			{ +				return; +			} + +			$parent_sql = ''; +			if ($parent !== false) +			{ +				// Allows '' to be sent as 0 +				$parent = ($parent) ?: 0; + +				if (!is_numeric($parent)) +				{ +					$sql = 'SELECT module_id +						FROM ' . $this->modules_table . " +						WHERE module_langname = '" . $this->db->sql_escape($parent) . "' +							AND module_class = '" . $this->db->sql_escape($class) . "'"; +					$result = $this->db->sql_query($sql); +					$module_id = $this->db->sql_fetchfield('module_id'); +					$this->db->sql_freeresult($result); + +					// we know it exists from the module_exists check +					$parent_sql = 'AND parent_id = ' . (int) $module_id; +				} +				else +				{ +					$parent_sql = 'AND parent_id = ' . (int) $parent; +				} +			} + +			$module_ids = array(); +			if (!is_numeric($module)) +			{ +				$sql = 'SELECT module_id +					FROM ' . $this->modules_table . " +					WHERE module_langname = '" . $this->db->sql_escape($module) . "' +						AND module_class = '" . $this->db->sql_escape($class) . "' +						$parent_sql"; +				$result = $this->db->sql_query($sql); +				while ($module_id = $this->db->sql_fetchfield('module_id')) +				{ +					$module_ids[] = (int) $module_id; +				} +				$this->db->sql_freeresult($result); +			} +			else +			{ +				$module_ids[] = (int) $module; +			} + +			if (!class_exists('acp_modules')) +			{ +				include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +				$this->user->add_lang('acp/modules'); +			} +			$acp_modules = new \acp_modules(); +			$acp_modules->module_class = $class; + +			foreach ($module_ids as $module_id) +			{ +				$result = $acp_modules->delete_module($module_id); +				if (!empty($result)) +				{ +					return; +				} +			} + +			$this->cache->destroy("_modules_$class"); +		} +	} + +	/** +	* {@inheritdoc} +	*/ +	public function reverse() +	{ +		$arguments = func_get_args(); +		$original_call = array_shift($arguments); + +		$call = false; +		switch ($original_call) +		{ +			case 'add': +				$call = 'remove'; +			break; + +			case 'remove': +				$call = 'add'; +			break; +		} + +		if ($call) +		{ +			return call_user_func_array(array(&$this, $call), $arguments); +		} +	} + +	/** +	* Wrapper for \acp_modules::get_module_infos() +	* +	* @param string $class Module Class +	* @param string $basename Module Basename +	* @return array Module Information +	* @throws \phpbb\db\migration\exception +	*/ +	protected function get_module_info($class, $basename) +	{ +		if (!class_exists('acp_modules')) +		{ +			include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); +		} +		$acp_modules = new \acp_modules(); +		$module = $acp_modules->get_module_infos($basename, $class, true); + +		if (empty($module)) +		{ +			throw new \phpbb\db\migration\exception('MODULE_INFO_FILE_NOT_EXIST', $class, $basename); +		} + +		return array_pop($module); +	} +} diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php new file mode 100644 index 0000000000..d2df27613a --- /dev/null +++ b/phpBB/phpbb/db/migration/tool/permission.php @@ -0,0 +1,629 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\tool; + +/** +* Migration permission management tool +*/ +class permission implements \phpbb\db\migration\tool\tool_interface +{ +	/** @var \phpbb\auth\auth */ +	protected $auth; + +	/** @var \phpbb\cache\service */ +	protected $cache; + +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** +	* Constructor +	* +	* @param \phpbb\db\driver\driver_interface $db +	* @param \phpbb\cache\service $cache +	* @param \phpbb\auth\auth $auth +	* @param string $phpbb_root_path +	* @param string $php_ext +	*/ +	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\auth\auth $auth, $phpbb_root_path, $php_ext) +	{ +		$this->db = $db; +		$this->cache = $cache; +		$this->auth = $auth; +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function get_name() +	{ +		return 'permission'; +	} + +	/** +	* Permission Exists +	* +	* Check if a permission (auth) setting exists +	* +	* @param string $auth_option The name of the permission (auth) option +	* @param bool $global True for checking a global permission setting, +	* 	False for a local permission setting +	* @return bool true if it exists, false if not +	*/ +	public function exists($auth_option, $global = true) +	{ +		if ($global) +		{ +			$type_sql = ' AND is_global = 1'; +		} +		else +		{ +			$type_sql = ' AND is_local = 1'; +		} + +		$sql = 'SELECT auth_option_id +			FROM ' . ACL_OPTIONS_TABLE . " +			WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" +				. $type_sql; +		$result = $this->db->sql_query($sql); + +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		if ($row) +		{ +			return true; +		} + +		return false; +	} + +	/** +	* Permission Add +	* +	* Add a permission (auth) option +	* +	* @param string $auth_option The name of the permission (auth) option +	* @param bool $global True for checking a global permission setting, +	* 	False for a local permission setting +	* @return null +	*/ +	public function add($auth_option, $global = true, $copy_from = false) +	{ +		if ($this->exists($auth_option, $global)) +		{ +			return; +		} + +		// We've added permissions, so set to true to notify the user. +		$this->permissions_added = true; + +		if (!class_exists('auth_admin')) +		{ +			include($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext); +		} +		$auth_admin = new \auth_admin(); + +		// 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. +		if ($this->exists($auth_option, !$global)) +		{ +			$sql_ary = array( +				'is_global'	=> 1, +				'is_local'	=> 1, +			); +			$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . ' +				SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . " +				WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"; +			$this->db->sql_query($sql); +		} +		else +		{ +			if ($global) +			{ +				$auth_admin->acl_add_option(array('global' => array($auth_option))); +			} +			else +			{ +				$auth_admin->acl_add_option(array('local' => array($auth_option))); +			} +		} + +		// The permission has been added, now we can copy it if needed +		if ($copy_from && isset($auth_admin->acl_options['id'][$copy_from])) +		{ +			$old_id = $auth_admin->acl_options['id'][$copy_from]; +			$new_id = $auth_admin->acl_options['id'][$auth_option]; + +			$tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE); + +			foreach ($tables as $table) +			{ +				$sql = 'SELECT * +					FROM ' . $table . ' +					WHERE auth_option_id = ' . $old_id; +				$result = $this->db->sql_query($sql); + +				$sql_ary = array(); +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$row['auth_option_id'] = $new_id; +					$sql_ary[] = $row; +				} +				$this->db->sql_freeresult($result); + +				if (!empty($sql_ary)) +				{ +					$this->db->sql_multi_insert($table, $sql_ary); +				} +			} + +			$auth_admin->acl_clear_prefetch(); +		} +	} + +	/** +	* Permission Remove +	* +	* Remove a permission (auth) option +	* +	* @param string $auth_option The name of the permission (auth) option +	* @param bool $global True for checking a global permission setting, +	* 	False for a local permission setting +	* @return null +	*/ +	public function remove($auth_option, $global = true) +	{ +		if (!$this->exists($auth_option, $global)) +		{ +			return; +		} + +		if ($global) +		{ +			$type_sql = ' AND is_global = 1'; +		} +		else +		{ +			$type_sql = ' AND is_local = 1'; +		} +		$sql = 'SELECT auth_option_id, is_global, is_local +			FROM ' . ACL_OPTIONS_TABLE . " +			WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" . +				$type_sql; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		$id = (int) $row['auth_option_id']; + +		// If it is a local and global permission, do not remove the row! :P +		if ($row['is_global'] && $row['is_local']) +		{ +			$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . ' +				SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . ' +				WHERE auth_option_id = ' . $id; +			$this->db->sql_query($sql); +		} +		else +		{ +			// Delete time +			$tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE, ACL_OPTIONS_TABLE); +			foreach ($tables as $table) +			{ +				$this->db->sql_query('DELETE FROM ' . $table . ' +					WHERE auth_option_id = ' . $id); +			} +		} + +		// Purge the auth cache +		$this->cache->destroy('_acl_options'); +		$this->auth->acl_clear_prefetch(); +	} + +	/** +	* Add a new permission role +	* +	* @param string $role_name The new role name +	* @param sting $role_type The type (u_, m_, a_) +	* @return null +	*/ +	public function role_add($role_name, $role_type, $role_description = '') +	{ +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = '" . $this->db->sql_escape($role_name) . "'"; +		$this->db->sql_query($sql); +		$role_id = (int) $this->db->sql_fetchfield('role_id'); + +		if ($role_id) +		{ +			return; +		} + +		$sql = 'SELECT MAX(role_order) AS max_role_order +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_type = '" . $this->db->sql_escape($role_type) . "'"; +		$this->db->sql_query($sql); +		$role_order = (int) $this->db->sql_fetchfield('max_role_order'); +		$role_order = (!$role_order) ? 1 : $role_order + 1; + +		$sql_ary = array( +			'role_name'			=> $role_name, +			'role_description'	=> $role_description, +			'role_type'			=> $role_type, +			'role_order'		=> $role_order, +		); + +		$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); +		$this->db->sql_query($sql); +	} + +	/** +	* Update the name on a permission role +	* +	* @param string $old_role_name The old role name +	* @param string $new_role_name The new role name +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function role_update($old_role_name, $new_role_name) +	{ +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'"; +		$this->db->sql_query($sql); +		$role_id = (int) $this->db->sql_fetchfield('role_id'); + +		if (!$role_id) +		{ +			throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $old_role_name); +		} + +		$sql = 'UPDATE ' . ACL_ROLES_TABLE . " +			SET role_name = '" . $this->db->sql_escape($new_role_name) . "' +			WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'"; +		$this->db->sql_query($sql); +	} + +	/** +	* Remove a permission role +	* +	* @param string $role_name The role name to remove +	* @return null +	*/ +	public function role_remove($role_name) +	{ +		$sql = 'SELECT role_id +			FROM ' . ACL_ROLES_TABLE . " +			WHERE role_name = '" . $this->db->sql_escape($role_name) . "'"; +		$this->db->sql_query($sql); +		$role_id = (int) $this->db->sql_fetchfield('role_id'); + +		if (!$role_id) +		{ +			return; +		} + +		$sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' +			WHERE role_id = ' . $role_id; +		$this->db->sql_query($sql); + +		$sql = 'DELETE FROM ' . ACL_ROLES_TABLE . ' +			WHERE role_id = ' . $role_id; +		$this->db->sql_query($sql); + +		$this->auth->acl_clear_prefetch(); +	} + +	/** +	* Permission Set +	* +	* Allows you to set permissions for a certain group/role +	* +	* @param string $name The name of the role/group +	* @param string|array $auth_option The auth_option or array of +	* 	auth_options you would like to set +	* @param string $type The type (role|group) +	* @param bool $has_permission True if you want to give them permission, +	* 	false if you want to deny them permission +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function permission_set($name, $auth_option, $type = 'role', $has_permission = true) +	{ +		if (!is_array($auth_option)) +		{ +			$auth_option = array($auth_option); +		} + +		$new_auth = array(); +		$sql = 'SELECT auth_option_id +			FROM ' . ACL_OPTIONS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('auth_option', $auth_option); +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$new_auth[] = (int) $row['auth_option_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($new_auth)) +		{ +			return; +		} + +		$current_auth = array(); + +		$type = (string) $type; // Prevent PHP bug. + +		switch ($type) +		{ +			case 'role': +				$sql = 'SELECT role_id +					FROM ' . ACL_ROLES_TABLE . " +					WHERE role_name = '" . $this->db->sql_escape($name) . "'"; +				$this->db->sql_query($sql); +				$role_id = (int) $this->db->sql_fetchfield('role_id'); + +				if (!$role_id) +				{ +					throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name); +				} + +				$sql = 'SELECT auth_option_id, auth_setting +					FROM ' . ACL_ROLES_DATA_TABLE . ' +					WHERE role_id = ' . $role_id; +				$result = $this->db->sql_query($sql); +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$current_auth[$row['auth_option_id']] = $row['auth_setting']; +				} +				$this->db->sql_freeresult($result); +			break; + +			case 'group': +				$sql = 'SELECT group_id +					FROM ' . GROUPS_TABLE . " +					WHERE group_name = '" . $this->db->sql_escape($name) . "'"; +				$this->db->sql_query($sql); +				$group_id = (int) $this->db->sql_fetchfield('group_id'); + +				if (!$group_id) +				{ +					throw new \phpbb\db\migration\exception('GROUP_NOT_EXIST', $name); +				} + +				// If the group has a role set for them we will add the requested permissions to that role. +				$sql = 'SELECT auth_role_id +					FROM ' . ACL_GROUPS_TABLE . ' +					WHERE group_id = ' . $group_id . ' +						AND auth_role_id <> 0 +						AND forum_id = 0'; +				$this->db->sql_query($sql); +				$role_id = (int) $this->db->sql_fetchfield('auth_role_id'); +				if ($role_id) +				{ +					$sql = 'SELECT role_name +						FROM ' . ACL_ROLES_TABLE . ' +						WHERE role_id = ' . $role_id; +					$this->db->sql_query($sql); +					$role_name = $this->db->sql_fetchfield('role_name'); + +					return $this->permission_set($role_name, $auth_option, 'role', $has_permission); +				} + +				$sql = 'SELECT auth_option_id, auth_setting +					FROM ' . ACL_GROUPS_TABLE . ' +					WHERE group_id = ' . $group_id; +				$result = $this->db->sql_query($sql); +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$current_auth[$row['auth_option_id']] = $row['auth_setting']; +				} +				$this->db->sql_freeresult($result); +			break; +		} + +		$sql_ary = array(); +		switch ($type) +		{ +			case 'role': +				foreach ($new_auth as $auth_option_id) +				{ +					if (!isset($current_auth[$auth_option_id])) +					{ +						$sql_ary[] = array( +							'role_id'			=> $role_id, +							'auth_option_id'	=> $auth_option_id, +							'auth_setting'		=> $has_permission, +						); +					} +				} + +				$this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary); +			break; + +			case 'group': +				foreach ($new_auth as $auth_option_id) +				{ +					if (!isset($current_auth[$auth_option_id])) +					{ +						$sql_ary[] = array( +							'group_id'			=> $group_id, +							'auth_option_id'	=> $auth_option_id, +							'auth_setting'		=> $has_permission, +						); +					} +				} + +				$this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary); +			break; +		} + +		$this->auth->acl_clear_prefetch(); +	} + +	/** +	* Permission Unset +	* +	* Allows you to unset (remove) permissions for a certain group/role +	* +	* @param string $name The name of the role/group +	* @param string|array $auth_option The auth_option or array of +	* 	auth_options you would like to set +	* @param string $type The type (role|group) +	* @return null +	* @throws \phpbb\db\migration\exception +	*/ +	public function permission_unset($name, $auth_option, $type = 'role') +	{ +		if (!is_array($auth_option)) +		{ +			$auth_option = array($auth_option); +		} + +		$to_remove = array(); +		$sql = 'SELECT auth_option_id +			FROM ' . ACL_OPTIONS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('auth_option', $auth_option); +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$to_remove[] = (int) $row['auth_option_id']; +		} +		$this->db->sql_freeresult($result); + +		if (empty($to_remove)) +		{ +			return; +		} + +		$type = (string) $type; // Prevent PHP bug. + +		switch ($type) +		{ +			case 'role': +				$sql = 'SELECT role_id +					FROM ' . ACL_ROLES_TABLE . " +					WHERE role_name = '" . $this->db->sql_escape($name) . "'"; +				$this->db->sql_query($sql); +				$role_id = (int) $this->db->sql_fetchfield('role_id'); + +				if (!$role_id) +				{ +					throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name); +				} + +				$sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' +					WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove); +				$this->db->sql_query($sql); +			break; + +			case 'group': +				$sql = 'SELECT group_id +					FROM ' . GROUPS_TABLE . " +					WHERE group_name = '" . $this->db->sql_escape($name) . "'"; +				$this->db->sql_query($sql); +				$group_id = (int) $this->db->sql_fetchfield('group_id'); + +				if (!$group_id) +				{ +					throw new \phpbb\db\migration\exception('GROUP_NOT_EXIST', $name); +				} + +				// If the group has a role set for them we will remove the requested permissions from that role. +				$sql = 'SELECT auth_role_id +					FROM ' . ACL_GROUPS_TABLE . ' +					WHERE group_id = ' . $group_id . ' +						AND auth_role_id <> 0'; +				$this->db->sql_query($sql); +				$role_id = (int) $this->db->sql_fetchfield('auth_role_id'); +				if ($role_id) +				{ +					$sql = 'SELECT role_name +						FROM ' . ACL_ROLES_TABLE . ' +						WHERE role_id = ' . $role_id; +					$this->db->sql_query($sql); +					$role_name = $this->db->sql_fetchfield('role_name'); + +					return $this->permission_unset($role_name, $auth_option, 'role'); +				} + +				$sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . ' +					WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove); +				$this->db->sql_query($sql); +			break; +		} + +		$this->auth->acl_clear_prefetch(); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function reverse() +	{ +		$arguments = func_get_args(); +		$original_call = array_shift($arguments); + +		$call = false; +		switch ($original_call) +		{ +			case 'add': +				$call = 'remove'; +			break; + +			case 'remove': +				$call = 'add'; +			break; + +			case 'permission_set': +				$call = 'permission_unset'; +			break; + +			case 'permission_unset': +				$call = 'permission_set'; +			break; + +			case 'role_add': +				$call = 'role_remove'; +			break; + +			case 'role_remove': +				$call = 'role_add'; +			break; + +			case 'role_update': +				// Set to the original value if the current value is what we compared to originally +				$arguments = array( +					$arguments[1], +					$arguments[0], +				); +			break; +		} + +		if ($call) +		{ +			return call_user_func_array(array(&$this, $call), $arguments); +		} +	} +} diff --git a/phpBB/phpbb/db/migration/tool/tool_interface.php b/phpBB/phpbb/db/migration/tool/tool_interface.php new file mode 100644 index 0000000000..07cd2435e4 --- /dev/null +++ b/phpBB/phpbb/db/migration/tool/tool_interface.php @@ -0,0 +1,37 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\tool; + +/** +* Migration tool interface +*/ +interface tool_interface +{ +	/** +	* Retrieve a short name used for commands in migrations. +	* +	* @return string short name +	*/ +	public function get_name(); + +	/** +	* Reverse an original install action +	* +	* First argument is the original call to the class (e.g. add, remove) +	* After the first argument, send the original arguments to the function in the original call +	* +	* @return null +	*/ +	public function reverse(); +} diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php new file mode 100644 index 0000000000..c2f7b5ab23 --- /dev/null +++ b/phpBB/phpbb/db/migrator.php @@ -0,0 +1,770 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db; + +/** +* The migrator is responsible for applying new migrations in the correct order. +*/ +class migrator +{ +	/** @var \phpbb\config\config */ +	protected $config; + +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var \phpbb\db\tools */ +	protected $db_tools; + +	/** @var \phpbb\db\migration\helper */ +	protected $helper; + +	/** @var string */ +	protected $table_prefix; + +	/** @var string */ +	protected $phpbb_root_path; + +	/** @var string */ +	protected $php_ext; + +	/** @var string */ +	protected $migrations_table; + +	/** +	* State of all migrations +	* +	* (SELECT * FROM migrations table) +	* +	* @var array +	*/ +	protected $migration_state = array(); + +	/** +	* Array of all migrations available to be run +	* +	* @var array +	*/ +	protected $migrations = array(); + +	/** +	* 'name,' 'class,' and 'state' of the last migration run +	* +	* 'effectively_installed' set and set to true if the migration was effectively_installed +	* +	* @var array +	*/ +	public $last_run_migration = false; + +	/** +	* Constructor of the database migrator +	*/ +	public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper) +	{ +		$this->config = $config; +		$this->db = $db; +		$this->db_tools = $db_tools; +		$this->helper = $helper; + +		$this->migrations_table = $migrations_table; + +		$this->phpbb_root_path = $phpbb_root_path; +		$this->php_ext = $php_ext; + +		$this->table_prefix = $table_prefix; + +		foreach ($tools as $tool) +		{ +			$this->tools[$tool->get_name()] = $tool; +		} + +		$this->tools['dbtools'] = $this->db_tools; + +		$this->load_migration_state(); +	} + +	/** +	* Loads all migrations and their application state from the database. +	* +	* @return null +	*/ +	public function load_migration_state() +	{ +		$this->migration_state = array(); + +		// prevent errors in case the table does not exist yet +		$this->db->sql_return_on_error(true); + +		$sql = "SELECT * +			FROM " . $this->migrations_table; +		$result = $this->db->sql_query($sql); + +		if (!$this->db->sql_error_triggered) +		{ +			while ($migration = $this->db->sql_fetchrow($result)) +			{ +				$this->migration_state[$migration['migration_name']] = $migration; + +				$this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']); +			} +		} + +		$this->db->sql_freeresult($result); + +		$this->db->sql_return_on_error(false); +	} + +	/** +	* Sets the list of available migration class names to the given array. +	* +	* @param array $class_names An array of migration class names +	* @return null +	*/ +	public function set_migrations($class_names) +	{ +		$this->migrations = $class_names; +	} + +	/** +	* Runs a single update step from the next migration to be applied. +	* +	* The update step can either be a schema or a (partial) data update. To +	* check if update() needs to be called again use the finished() method. +	* +	* @return null +	*/ +	public function update() +	{ +		foreach ($this->migrations as $name) +		{ +			if (!isset($this->migration_state[$name]) || +				!$this->migration_state[$name]['migration_schema_done'] || +				!$this->migration_state[$name]['migration_data_done']) +			{ +				if (!$this->try_apply($name)) +				{ +					continue; +				} +				else +				{ +					return; +				} +			} +		} +	} + +	/** +	* Attempts to apply a step of the given migration or one of its dependencies +	* +	* @param	string	$name The class name of the migration +	* @return	bool	Whether any update step was successfully run +	* @throws \phpbb\db\migration\exception +	*/ +	protected function try_apply($name) +	{ +		if (!class_exists($name)) +		{ +			return false; +		} + +		$migration = $this->get_migration($name); + +		$state = (isset($this->migration_state[$name])) ? +			$this->migration_state[$name] : +			array( +				'migration_depends_on'	=> $migration->depends_on(), +				'migration_schema_done' => false, +				'migration_data_done'	=> false, +				'migration_data_state'	=> '', +				'migration_start_time'	=> 0, +				'migration_end_time'	=> 0, +			); + +		foreach ($state['migration_depends_on'] as $depend) +		{ +			if ($this->unfulfillable($depend) !== false) +			{ +				throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend); +			} + +			if (!isset($this->migration_state[$depend]) || +				!$this->migration_state[$depend]['migration_schema_done'] || +				!$this->migration_state[$depend]['migration_data_done']) +			{ +				return $this->try_apply($depend); +			} +		} + +		$this->last_run_migration = array( +			'name'	=> $name, +			'class'	=> $migration, +			'state'	=> $state, +			'task'	=> '', +		); + +		if (!isset($this->migration_state[$name])) +		{ +			if ($state['migration_start_time'] == 0 && $migration->effectively_installed()) +			{ +				$state = array( +					'migration_depends_on'	=> $migration->depends_on(), +					'migration_schema_done' => true, +					'migration_data_done'	=> true, +					'migration_data_state'	=> '', +					'migration_start_time'	=> 0, +					'migration_end_time'	=> 0, +				); + +				$this->last_run_migration['effectively_installed'] = true; +			} +			else +			{ +				$state['migration_start_time'] = time(); +			} +		} + +		$this->set_migration_state($name, $state); + +		if (!$state['migration_schema_done']) +		{ +			$this->last_run_migration['task'] = 'process_schema_step'; +			$steps = $this->helper->get_schema_steps($migration->update_schema()); +			$result = $this->process_data_step($steps, $state['migration_data_state']); + +			$state['migration_data_state'] = ($result === true) ? '' : $result; +			$state['migration_schema_done'] = ($result === true); +		} +		else if (!$state['migration_data_done']) +		{ +			try +			{ +				$this->last_run_migration['task'] = 'process_data_step'; +				$result = $this->process_data_step($migration->update_data(), $state['migration_data_state']); + +				$state['migration_data_state'] = ($result === true) ? '' : $result; +				$state['migration_data_done'] = ($result === true); +				$state['migration_end_time'] = ($result === true) ? time() : 0; +			} +			catch (\phpbb\db\migration\exception $e) +			{ +				// Revert the schema changes +				$this->revert($name); + +				// Rethrow exception +				throw $e; +			} +		} + +		$this->set_migration_state($name, $state); + +		return true; +	} + +	/** +	* Runs a single revert step from the last migration installed +	* +	* YOU MUST ADD/SET ALL MIGRATIONS THAT COULD BE DEPENDENT ON THE MIGRATION TO REVERT TO BEFORE CALLING THIS METHOD! +	* The revert step can either be a schema or a (partial) data revert. To +	* check if revert() needs to be called again use the migration_state() method. +	* +	* @param string $migration String migration name to revert (including any that depend on this migration) +	* @return null +	*/ +	public function revert($migration) +	{ +		if (!isset($this->migration_state[$migration])) +		{ +			// Not installed +			return; +		} + +		foreach ($this->migration_state as $name => $state) +		{ +			if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on'])) +			{ +				$this->revert($name); +			} +		} + +		$this->try_revert($migration); +	} + +	/** +	* Attempts to revert a step of the given migration or one of its dependencies +	* +	* @param	string	$name The class name of the migration +	* @return	bool	Whether any update step was successfully run +	*/ +	protected function try_revert($name) +	{ +		if (!class_exists($name)) +		{ +			return false; +		} + +		$migration = $this->get_migration($name); + +		$state = $this->migration_state[$name]; + +		$this->last_run_migration = array( +			'name'	=> $name, +			'class'	=> $migration, +			'task'	=> '', +		); + +		if ($state['migration_data_done']) +		{ +			if ($state['migration_data_state'] !== 'revert_data') +			{ +				$result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true); + +				$state['migration_data_state'] = ($result === true) ? 'revert_data' : $result; +			} +			else +			{ +				$result = $this->process_data_step($migration->revert_data(), '', false); + +				$state['migration_data_state'] = ($result === true) ? '' : $result; +				$state['migration_data_done'] = ($result === true) ? false : true; +			} + +			$this->set_migration_state($name, $state); +		} +		else if ($state['migration_schema_done']) +		{ +			$steps = $this->helper->get_schema_steps($migration->revert_schema()); +			$result = $this->process_data_step($steps, $state['migration_data_state']); + +			$state['migration_data_state'] = ($result === true) ? '' : $result; +			$state['migration_schema_done'] = ($result === true) ? false : true; + +			if (!$state['migration_schema_done']) +			{ +				$sql = 'DELETE FROM ' . $this->migrations_table . " +					WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; +				$this->db->sql_query($sql); + +				unset($this->migration_state[$name]); +			} +		} + +		return true; +	} + +	/** +	* Process the data step of the migration +	* +	* @param array $steps The steps to run +	* @param bool|string $state Current state of the migration +	* @param bool $revert true to revert a data step +	* @return bool|string migration state. True if completed, serialized array if not finished +	* @throws \phpbb\db\migration\exception +	*/ +	protected function process_data_step($steps, $state, $revert = false) +	{ +		$state = ($state) ? unserialize($state) : false; + +		// reverse order of steps if reverting +		if ($revert === true) +		{ +			$steps = array_reverse($steps); +		} + +		foreach ($steps as $step_identifier => $step) +		{ +			$last_result = 0; +			if ($state) +			{ +				// Continue until we reach the step that matches the last step called +				if ($state['step'] != $step_identifier) +				{ +					continue; +				} + +				// We send the result from last time to the callable function +				$last_result = $state['result']; + +				// Set state to false since we reached the point we were at +				$state = false; +			} + +			try +			{ +				// Result will be null or true if everything completed correctly +				$result = $this->run_step($step, $last_result, $revert); +				if ($result !== null && $result !== true) +				{ +					return serialize(array( +						'result'	=> $result, +						'step'		=> $step_identifier, +					)); +				} +			} +			catch (\phpbb\db\migration\exception $e) +			{ +				// We should try rolling back here +				foreach ($steps as $reverse_step_identifier => $reverse_step) +				{ +					// If we've reached the current step we can break because we reversed everything that was run +					if ($reverse_step_identifier == $step_identifier) +					{ +						break; +					} + +					// Reverse the step that was run +					$result = $this->run_step($reverse_step, false, !$revert); +				} + +				// rethrow the exception +				throw $e; +			} +		} + +		return true; +	} + +	/** +	* Run a single step +	* +	* An exception should be thrown if an error occurs +	* +	* @param mixed $step Data step from migration +	* @param mixed $last_result Result to pass to the callable (only for 'custom' method) +	* @param bool $reverse False to install, True to attempt uninstallation by reversing the call +	* @return null +	*/ +	protected function run_step($step, $last_result = 0, $reverse = false) +	{ +		$callable_and_parameters = $this->get_callable_from_step($step, $last_result, $reverse); + +		if ($callable_and_parameters === false) +		{ +			return; +		} + +		$callable = $callable_and_parameters[0]; +		$parameters = $callable_and_parameters[1]; + +		return call_user_func_array($callable, $parameters); +	} + +	/** +	* Get a callable statement from a data step +	* +	* @param array $step Data step from migration +	* @param mixed $last_result Result to pass to the callable (only for 'custom' method) +	* @param bool $reverse False to install, True to attempt uninstallation by reversing the call +	* @return array Array with parameters for call_user_func_array(), 0 is the callable, 1 is parameters +	* @throws \phpbb\db\migration\exception +	*/ +	protected function get_callable_from_step(array $step, $last_result = 0, $reverse = false) +	{ +		$type = $step[0]; +		$parameters = $step[1]; + +		$parts = explode('.', $type); + +		$class = $parts[0]; +		$method = false; + +		if (isset($parts[1])) +		{ +			$method = $parts[1]; +		} + +		switch ($class) +		{ +			case 'if': +				if (!isset($parameters[0])) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_CONDITION', $step); +				} + +				if (!isset($parameters[1])) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step); +				} + +				$condition = $parameters[0]; + +				if (!$condition) +				{ +					return false; +				} + +				$step = $parameters[1]; + +				return $this->get_callable_from_step($step); +			break; + +			case 'custom': +				if (!is_callable($parameters[0])) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step); +				} + +				if ($reverse) +				{ +					return false; +				} +				else +				{ +					return array( +						$parameters[0], +						array($last_result), +					); +				} +			break; + +			default: +				if (!$method) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNKNOWN_TYPE', $step); +				} + +				if (!isset($this->tools[$class])) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_TOOL', $step); +				} + +				if (!method_exists(get_class($this->tools[$class]), $method)) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_METHOD', $step); +				} + +				// Attempt to reverse operations +				if ($reverse) +				{ +					array_unshift($parameters, $method); + +					return array( +						array($this->tools[$class], 'reverse'), +						$parameters, +					); +				} + +				return array( +					array($this->tools[$class], $method), +					$parameters, +				); +			break; +		} +	} + +	/** +	* Insert/Update migration row into the database +	* +	* @param string $name Name of the migration +	* @param array $state +	* @return null +	*/ +	protected function set_migration_state($name, $state) +	{ +		$migration_row = $state; +		$migration_row['migration_depends_on'] = serialize($state['migration_depends_on']); + +		if (isset($this->migration_state[$name])) +		{ +			$sql = 'UPDATE ' . $this->migrations_table . ' +				SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . " +				WHERE migration_name = '" . $this->db->sql_escape($name) . "'"; +			$this->db->sql_query($sql); +		} +		else +		{ +			$migration_row['migration_name'] = $name; +			$sql = 'INSERT INTO ' . $this->migrations_table . ' +				' . $this->db->sql_build_array('INSERT', $migration_row); +			$this->db->sql_query($sql); +		} + +		$this->migration_state[$name] = $state; + +		$this->last_run_migration['state'] = $state; +	} + +	/** +	* Checks if a migration's dependencies can even theoretically be satisfied. +	* +	* @param string	$name The class name of the migration +	* @return bool|string False if fulfillable, string of missing migration name if unfulfillable +	*/ +	public function unfulfillable($name) +	{ +		if (isset($this->migration_state[$name])) +		{ +			return false; +		} + +		if (!class_exists($name)) +		{ +			return $name; +		} + +		$migration = $this->get_migration($name); +		$depends = $migration->depends_on(); + +		foreach ($depends as $depend) +		{ +			$unfulfillable = $this->unfulfillable($depend); +			if ($unfulfillable !== false) +			{ +				return $unfulfillable; +			} +		} + +		return false; +	} + +	/** +	* Checks whether all available, fulfillable migrations have been applied. +	* +	* @return bool Whether the migrations have been applied +	*/ +	public function finished() +	{ +		foreach ($this->migrations as $name) +		{ +			if (!isset($this->migration_state[$name])) +			{ +				// skip unfulfillable migrations, but fulfillables mean we +				// are not finished yet +				if ($this->unfulfillable($name) !== false) +				{ +					continue; +				} + +				return false; +			} + +			$migration = $this->migration_state[$name]; + +			if (!$migration['migration_schema_done'] || !$migration['migration_data_done']) +			{ +				return false; +			} +		} + +		return true; +	} + +	/** +	* Gets a migration state (whether it is installed and to what extent) +	* +	* @param string $migration String migration name to check if it is installed +	* @return bool|array False if the migration has not at all been installed, array +	*/ +	public function migration_state($migration) +	{ +		if (!isset($this->migration_state[$migration])) +		{ +			return false; +		} + +		return $this->migration_state[$migration]; +	} + +	/** +	* Helper to get a migration +	* +	* @param string $name Name of the migration +	* @return \phpbb\db\migration\migration +	*/ +	protected function get_migration($name) +	{ +		return new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix); +	} + +	/** +	* This function adds all migrations sent to it to the migrations table +	* +	* THIS SHOULD NOT GENERALLY BE USED! THIS IS FOR THE PHPBB INSTALLER. +	* THIS WILL THROW ERRORS IF MIGRATIONS ALREADY EXIST IN THE TABLE, DO NOT CALL MORE THAN ONCE! +	* +	* @param array $migrations Array of migrations (names) to add to the migrations table +	* @return null +	*/ +	public function populate_migrations($migrations) +	{ +		foreach ($migrations as $name) +		{ +			if ($this->migration_state($name) === false) +			{ +				$state = array( +					'migration_depends_on'	=> $name::depends_on(), +					'migration_schema_done' => true, +					'migration_data_done'	=> true, +					'migration_data_state'	=> '', +					'migration_start_time'	=> time(), +					'migration_end_time'	=> time(), +				); +				$this->set_migration_state($name, $state); +			} +		} +	} + +	/** +	* Load migration data files from a directory +	* +	* @param \phpbb\finder $finder +	* @param string $path Path to migration data files +	* @param bool $check_fulfillable If TRUE (default), we will check +	* 	if all of the migrations are fulfillable after loading them. +	* 	If FALSE, we will not check. You SHOULD check at least once +	* 	to prevent errors (if including multiple directories, check +	* 	with the last call to prevent throwing errors unnecessarily). +	* @return array Array of migration names +	* @throws \phpbb\db\migration\exception +	*/ +	public function load_migrations(\phpbb\finder $finder, $path, $check_fulfillable = true) +	{ +		if (!is_dir($path)) +		{ +			throw new \phpbb\db\migration\exception('DIRECTORY INVALID', $path); +		} + +		$migrations = array(); + +		$files = $finder +			->extension_directory("/") +			->find_from_paths(array('/' => $path)); +		foreach ($files as $file) +		{ +			$migrations[$file['path'] . $file['filename']] = ''; +		} +		$migrations = $finder->get_classes_from_files($migrations); + +		foreach ($migrations as $migration) +		{ +			if (!in_array($migration, $this->migrations)) +			{ +				$this->migrations[] = $migration; +			} +		} + +		if ($check_fulfillable) +		{ +			foreach ($this->migrations as $name) +			{ +				$unfulfillable = $this->unfulfillable($name); +				if ($unfulfillable !== false) +				{ +					throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable); +				} +			} +		} + +		return $this->migrations; +	} +} diff --git a/phpBB/phpbb/db/sql_insert_buffer.php b/phpBB/phpbb/db/sql_insert_buffer.php new file mode 100644 index 0000000000..6b6f556571 --- /dev/null +++ b/phpBB/phpbb/db/sql_insert_buffer.php @@ -0,0 +1,146 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db; + +/** +* Collects rows for insert into a database until the buffer size is reached. +* Then flushes the buffer to the database and starts over again. +* +* Benefits over collecting a (possibly huge) insert array and then using +* $db->sql_multi_insert() include: +* +*  - Going over max packet size of the database connection is usually prevented +*    because the data is submitted in batches. +* +*  - Reaching database connection timeout is usually prevented because +*    submission of batches talks to the database every now and then. +* +*  - Usage of less PHP memory because data no longer needed is discarded on +*    buffer flush. +* +* Attention: +* Please note that users of this class have to call flush() to flush the +* remaining rows to the database after their batch insert operation is +* finished. +* +* Usage: +* <code> +*	$buffer = new \phpbb\db\sql_insert_buffer($db, 'test_table', 1234); +* +*	while (do_stuff()) +*	{ +*		$buffer->insert(array( +*			'column1' => 'value1', +*			'column2' => 'value2', +*		)); +*	} +* +*	$buffer->flush(); +* </code> +*/ +class sql_insert_buffer +{ +	/** @var \phpbb\db\driver\driver_interface */ +	protected $db; + +	/** @var string */ +	protected $table_name; + +	/** @var int */ +	protected $max_buffered_rows; + +	/** @var array */ +	protected $buffer = array(); + +	/** +	* @param \phpbb\db\driver\driver_interface $db +	* @param string          $table_name +	* @param int             $max_buffered_rows +	*/ +	public function __construct(\phpbb\db\driver\driver_interface $db, $table_name, $max_buffered_rows = 500) +	{ +		$this->db = $db; +		$this->table_name = $table_name; +		$this->max_buffered_rows = $max_buffered_rows; +	} + +	/** +	* Inserts a single row into the buffer if multi insert is supported by the +	* database (otherwise an insert query is sent immediately). Then flushes +	* the buffer if the number of rows in the buffer is now greater than or +	* equal to $max_buffered_rows. +	* +	* @param array $row +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function insert(array $row) +	{ +		$this->buffer[] = $row; + +		// Flush buffer if it is full or when DB does not support multi inserts. +		// In the later case, the buffer will always only contain one row. +		if (!$this->db->multi_insert || sizeof($this->buffer) >= $this->max_buffered_rows) +		{ +			return $this->flush(); +		} + +		return false; +	} + +	/** +	* Inserts a row set, i.e. an array of rows, by calling insert(). +	* +	* Please note that it is in most cases better to use insert() instead of +	* first building a huge rowset. Or at least sizeof($rows) should be kept +	* small. +	* +	* @param array $rows  +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function insert_all(array $rows) +	{ +		// Using bitwise |= because PHP does not have logical ||= +		$result = 0; + +		foreach ($rows as $row) +		{ +			$result |= (int) $this->insert($row); +		} + +		return (bool) $result; +	} + +	/** +	* Flushes the buffer content to the DB and clears the buffer. +	* +	* @return bool		True when some data was flushed to the database. +	*					False otherwise. +	*/ +	public function flush() +	{ +		if (!empty($this->buffer)) +		{ +			$this->db->sql_multi_insert($this->table_name, $this->buffer); +			$this->buffer = array(); + +			return true; +		} + +		return false; +	} +} diff --git a/phpBB/phpbb/db/tools.php b/phpBB/phpbb/db/tools.php new file mode 100644 index 0000000000..2ec46ed239 --- /dev/null +++ b/phpBB/phpbb/db/tools.php @@ -0,0 +1,2669 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db; + +/** +* Database Tools for handling cross-db actions such as altering columns, etc. +* Currently not supported is returning SQL for creating tables. +*/ +class tools +{ +	/** +	* Current sql layer +	*/ +	var $sql_layer = ''; + +	/** +	* @var object DB object +	*/ +	var $db = null; + +	/** +	* The Column types for every database we support +	* @var array +	*/ +	var $dbms_type_map = array(); + +	/** +	* Is the used MS SQL Server a SQL Server 2000? +	* @var bool +	*/ +	protected $is_sql_server_2000; + +	/** +	* Get the column types for every database we support +	* +	* @return array +	*/ +	public static function get_dbms_type_map() +	{ +		return array( +			'mysql_41'	=> array( +				'INT:'		=> 'int(%d)', +				'BINT'		=> 'bigint(20)', +				'UINT'		=> 'mediumint(8) UNSIGNED', +				'UINT:'		=> 'int(%d) UNSIGNED', +				'TINT:'		=> 'tinyint(%d)', +				'USINT'		=> 'smallint(4) UNSIGNED', +				'BOOL'		=> 'tinyint(1) UNSIGNED', +				'VCHAR'		=> 'varchar(255)', +				'VCHAR:'	=> 'varchar(%d)', +				'CHAR:'		=> 'char(%d)', +				'XSTEXT'	=> 'text', +				'XSTEXT_UNI'=> 'varchar(100)', +				'STEXT'		=> 'text', +				'STEXT_UNI'	=> 'varchar(255)', +				'TEXT'		=> 'text', +				'TEXT_UNI'	=> 'text', +				'MTEXT'		=> 'mediumtext', +				'MTEXT_UNI'	=> 'mediumtext', +				'TIMESTAMP'	=> 'int(11) UNSIGNED', +				'DECIMAL'	=> 'decimal(5,2)', +				'DECIMAL:'	=> 'decimal(%d,2)', +				'PDECIMAL'	=> 'decimal(6,3)', +				'PDECIMAL:'	=> 'decimal(%d,3)', +				'VCHAR_UNI'	=> 'varchar(255)', +				'VCHAR_UNI:'=> 'varchar(%d)', +				'VCHAR_CI'	=> 'varchar(255)', +				'VARBINARY'	=> 'varbinary(255)', +			), + +			'mysql_40'	=> array( +				'INT:'		=> 'int(%d)', +				'BINT'		=> 'bigint(20)', +				'UINT'		=> 'mediumint(8) UNSIGNED', +				'UINT:'		=> 'int(%d) UNSIGNED', +				'TINT:'		=> 'tinyint(%d)', +				'USINT'		=> 'smallint(4) UNSIGNED', +				'BOOL'		=> 'tinyint(1) UNSIGNED', +				'VCHAR'		=> 'varbinary(255)', +				'VCHAR:'	=> 'varbinary(%d)', +				'CHAR:'		=> 'binary(%d)', +				'XSTEXT'	=> 'blob', +				'XSTEXT_UNI'=> 'blob', +				'STEXT'		=> 'blob', +				'STEXT_UNI'	=> 'blob', +				'TEXT'		=> 'blob', +				'TEXT_UNI'	=> 'blob', +				'MTEXT'		=> 'mediumblob', +				'MTEXT_UNI'	=> 'mediumblob', +				'TIMESTAMP'	=> 'int(11) UNSIGNED', +				'DECIMAL'	=> 'decimal(5,2)', +				'DECIMAL:'	=> 'decimal(%d,2)', +				'PDECIMAL'	=> 'decimal(6,3)', +				'PDECIMAL:'	=> 'decimal(%d,3)', +				'VCHAR_UNI'	=> 'blob', +				'VCHAR_UNI:'=> array('varbinary(%d)', 'limit' => array('mult', 3, 255, 'blob')), +				'VCHAR_CI'	=> 'blob', +				'VARBINARY'	=> 'varbinary(255)', +			), + +			'mssql'		=> array( +				'INT:'		=> '[int]', +				'BINT'		=> '[float]', +				'UINT'		=> '[int]', +				'UINT:'		=> '[int]', +				'TINT:'		=> '[int]', +				'USINT'		=> '[int]', +				'BOOL'		=> '[int]', +				'VCHAR'		=> '[varchar] (255)', +				'VCHAR:'	=> '[varchar] (%d)', +				'CHAR:'		=> '[char] (%d)', +				'XSTEXT'	=> '[varchar] (1000)', +				'STEXT'		=> '[varchar] (3000)', +				'TEXT'		=> '[varchar] (8000)', +				'MTEXT'		=> '[text]', +				'XSTEXT_UNI'=> '[varchar] (100)', +				'STEXT_UNI'	=> '[varchar] (255)', +				'TEXT_UNI'	=> '[varchar] (4000)', +				'MTEXT_UNI'	=> '[text]', +				'TIMESTAMP'	=> '[int]', +				'DECIMAL'	=> '[float]', +				'DECIMAL:'	=> '[float]', +				'PDECIMAL'	=> '[float]', +				'PDECIMAL:'	=> '[float]', +				'VCHAR_UNI'	=> '[varchar] (255)', +				'VCHAR_UNI:'=> '[varchar] (%d)', +				'VCHAR_CI'	=> '[varchar] (255)', +				'VARBINARY'	=> '[varchar] (255)', +			), + +			'mssqlnative'	=> array( +				'INT:'		=> '[int]', +				'BINT'		=> '[float]', +				'UINT'		=> '[int]', +				'UINT:'		=> '[int]', +				'TINT:'		=> '[int]', +				'USINT'		=> '[int]', +				'BOOL'		=> '[int]', +				'VCHAR'		=> '[varchar] (255)', +				'VCHAR:'	=> '[varchar] (%d)', +				'CHAR:'		=> '[char] (%d)', +				'XSTEXT'	=> '[varchar] (1000)', +				'STEXT'		=> '[varchar] (3000)', +				'TEXT'		=> '[varchar] (8000)', +				'MTEXT'		=> '[text]', +				'XSTEXT_UNI'=> '[varchar] (100)', +				'STEXT_UNI'	=> '[varchar] (255)', +				'TEXT_UNI'	=> '[varchar] (4000)', +				'MTEXT_UNI'	=> '[text]', +				'TIMESTAMP'	=> '[int]', +				'DECIMAL'	=> '[float]', +				'DECIMAL:'	=> '[float]', +				'PDECIMAL'	=> '[float]', +				'PDECIMAL:'	=> '[float]', +				'VCHAR_UNI'	=> '[varchar] (255)', +				'VCHAR_UNI:'=> '[varchar] (%d)', +				'VCHAR_CI'	=> '[varchar] (255)', +				'VARBINARY'	=> '[varchar] (255)', +			), + +			'oracle'	=> array( +				'INT:'		=> 'number(%d)', +				'BINT'		=> 'number(20)', +				'UINT'		=> 'number(8)', +				'UINT:'		=> 'number(%d)', +				'TINT:'		=> 'number(%d)', +				'USINT'		=> 'number(4)', +				'BOOL'		=> 'number(1)', +				'VCHAR'		=> 'varchar2(255)', +				'VCHAR:'	=> 'varchar2(%d)', +				'CHAR:'		=> 'char(%d)', +				'XSTEXT'	=> 'varchar2(1000)', +				'STEXT'		=> 'varchar2(3000)', +				'TEXT'		=> 'clob', +				'MTEXT'		=> 'clob', +				'XSTEXT_UNI'=> 'varchar2(300)', +				'STEXT_UNI'	=> 'varchar2(765)', +				'TEXT_UNI'	=> 'clob', +				'MTEXT_UNI'	=> 'clob', +				'TIMESTAMP'	=> 'number(11)', +				'DECIMAL'	=> 'number(5, 2)', +				'DECIMAL:'	=> 'number(%d, 2)', +				'PDECIMAL'	=> 'number(6, 3)', +				'PDECIMAL:'	=> 'number(%d, 3)', +				'VCHAR_UNI'	=> 'varchar2(765)', +				'VCHAR_UNI:'=> array('varchar2(%d)', 'limit' => array('mult', 3, 765, 'clob')), +				'VCHAR_CI'	=> 'varchar2(255)', +				'VARBINARY'	=> 'raw(255)', +			), + +			'sqlite'	=> array( +				'INT:'		=> 'int(%d)', +				'BINT'		=> 'bigint(20)', +				'UINT'		=> 'INTEGER UNSIGNED', //'mediumint(8) UNSIGNED', +				'UINT:'		=> 'INTEGER UNSIGNED', // 'int(%d) UNSIGNED', +				'TINT:'		=> 'tinyint(%d)', +				'USINT'		=> 'INTEGER UNSIGNED', //'mediumint(4) UNSIGNED', +				'BOOL'		=> 'INTEGER UNSIGNED', //'tinyint(1) UNSIGNED', +				'VCHAR'		=> 'varchar(255)', +				'VCHAR:'	=> 'varchar(%d)', +				'CHAR:'		=> 'char(%d)', +				'XSTEXT'	=> 'text(65535)', +				'STEXT'		=> 'text(65535)', +				'TEXT'		=> 'text(65535)', +				'MTEXT'		=> 'mediumtext(16777215)', +				'XSTEXT_UNI'=> 'text(65535)', +				'STEXT_UNI'	=> 'text(65535)', +				'TEXT_UNI'	=> 'text(65535)', +				'MTEXT_UNI'	=> 'mediumtext(16777215)', +				'TIMESTAMP'	=> 'INTEGER UNSIGNED', //'int(11) UNSIGNED', +				'DECIMAL'	=> 'decimal(5,2)', +				'DECIMAL:'	=> 'decimal(%d,2)', +				'PDECIMAL'	=> 'decimal(6,3)', +				'PDECIMAL:'	=> 'decimal(%d,3)', +				'VCHAR_UNI'	=> 'varchar(255)', +				'VCHAR_UNI:'=> 'varchar(%d)', +				'VCHAR_CI'	=> 'varchar(255)', +				'VARBINARY'	=> 'blob', +			), + +			'sqlite3'	=> array( +				'INT:'		=> 'INT(%d)', +				'BINT'		=> 'BIGINT(20)', +				'UINT'		=> 'INTEGER UNSIGNED', +				'UINT:'		=> 'INTEGER UNSIGNED', +				'TINT:'		=> 'TINYINT(%d)', +				'USINT'		=> 'INTEGER UNSIGNED', +				'BOOL'		=> 'INTEGER UNSIGNED', +				'VCHAR'		=> 'VARCHAR(255)', +				'VCHAR:'	=> 'VARCHAR(%d)', +				'CHAR:'		=> 'CHAR(%d)', +				'XSTEXT'	=> 'TEXT(65535)', +				'STEXT'		=> 'TEXT(65535)', +				'TEXT'		=> 'TEXT(65535)', +				'MTEXT'		=> 'MEDIUMTEXT(16777215)', +				'XSTEXT_UNI'=> 'TEXT(65535)', +				'STEXT_UNI'	=> 'TEXT(65535)', +				'TEXT_UNI'	=> 'TEXT(65535)', +				'MTEXT_UNI'	=> 'MEDIUMTEXT(16777215)', +				'TIMESTAMP'	=> 'INTEGER UNSIGNED', //'int(11) UNSIGNED', +				'DECIMAL'	=> 'DECIMAL(5,2)', +				'DECIMAL:'	=> 'DECIMAL(%d,2)', +				'PDECIMAL'	=> 'DECIMAL(6,3)', +				'PDECIMAL:'	=> 'DECIMAL(%d,3)', +				'VCHAR_UNI'	=> 'VARCHAR(255)', +				'VCHAR_UNI:'=> 'VARCHAR(%d)', +				'VCHAR_CI'	=> 'VARCHAR(255)', +				'VARBINARY'	=> 'BLOB', +			), + +			'postgres'	=> array( +				'INT:'		=> 'INT4', +				'BINT'		=> 'INT8', +				'UINT'		=> 'INT4', // unsigned +				'UINT:'		=> 'INT4', // unsigned +				'USINT'		=> 'INT2', // unsigned +				'BOOL'		=> 'INT2', // unsigned +				'TINT:'		=> 'INT2', +				'VCHAR'		=> 'varchar(255)', +				'VCHAR:'	=> 'varchar(%d)', +				'CHAR:'		=> 'char(%d)', +				'XSTEXT'	=> 'varchar(1000)', +				'STEXT'		=> 'varchar(3000)', +				'TEXT'		=> 'varchar(8000)', +				'MTEXT'		=> 'TEXT', +				'XSTEXT_UNI'=> 'varchar(100)', +				'STEXT_UNI'	=> 'varchar(255)', +				'TEXT_UNI'	=> 'varchar(4000)', +				'MTEXT_UNI'	=> 'TEXT', +				'TIMESTAMP'	=> 'INT4', // unsigned +				'DECIMAL'	=> 'decimal(5,2)', +				'DECIMAL:'	=> 'decimal(%d,2)', +				'PDECIMAL'	=> 'decimal(6,3)', +				'PDECIMAL:'	=> 'decimal(%d,3)', +				'VCHAR_UNI'	=> 'varchar(255)', +				'VCHAR_UNI:'=> 'varchar(%d)', +				'VCHAR_CI'	=> 'varchar_ci', +				'VARBINARY'	=> 'bytea', +			), +		); +	} + +	/** +	* A list of types being unsigned for better reference in some db's +	* @var array +	*/ +	var $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP'); + +	/** +	* A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules. +	* @var array +	*/ +	var $supported_dbms = array('mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite', 'sqlite3'); + +	/** +	* This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array). +	* This mode has no effect on some methods (inserting of data for example). This is expressed within the methods command. +	*/ +	var $return_statements = false; + +	/** +	* Constructor. Set DB Object and set {@link $return_statements return_statements}. +	* +	* @param \phpbb\db\driver\driver_interface	$db					Database connection +	* @param bool		$return_statements	True if only statements should be returned and no SQL being executed +	*/ +	public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false) +	{ +		$this->db = $db; +		$this->return_statements = $return_statements; + +		$this->dbms_type_map = self::get_dbms_type_map(); + +		// Determine mapping database type +		switch ($this->db->sql_layer) +		{ +			case 'mysql': +				$this->sql_layer = 'mysql_40'; +			break; + +			case 'mysql4': +				if (version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) +				{ +					$this->sql_layer = 'mysql_41'; +				} +				else +				{ +					$this->sql_layer = 'mysql_40'; +				} +			break; + +			case 'mysqli': +				$this->sql_layer = 'mysql_41'; +			break; + +			case 'mssql': +			case 'mssql_odbc': +				$this->sql_layer = 'mssql'; +			break; + +			case 'mssqlnative': +				$this->sql_layer = 'mssqlnative'; +			break; + +			default: +				$this->sql_layer = $this->db->sql_layer; +			break; +		} +	} + +	/** +	* Setter for {@link $return_statements return_statements}. +	* +	* @param bool $return_statements True if SQL should not be executed but returned as strings +	* @return null +	*/ +	public function set_return_statements($return_statements) +	{ +		$this->return_statements = $return_statements; +	} + +	/** +	* Gets a list of tables in the database. +	* +	* @return array		Array of table names  (all lower case) +	*/ +	function sql_list_tables() +	{ +		switch ($this->db->sql_layer) +		{ +			case 'mysql': +			case 'mysql4': +			case 'mysqli': +				$sql = 'SHOW TABLES'; +			break; + +			case 'sqlite': +				$sql = 'SELECT name +					FROM sqlite_master +					WHERE type = "table"'; +			break; + +			case 'sqlite3': +				$sql = 'SELECT name +					FROM sqlite_master +					WHERE type = "table" +						AND name <> "sqlite_sequence"'; +			break; + +			case 'mssql': +			case 'mssql_odbc': +			case 'mssqlnative': +				$sql = "SELECT name +					FROM sysobjects +					WHERE type='U'"; +			break; + +			case 'postgres': +				$sql = 'SELECT relname +					FROM pg_stat_user_tables'; +			break; + +			case 'oracle': +				$sql = 'SELECT table_name +					FROM USER_TABLES'; +			break; +		} + +		$result = $this->db->sql_query($sql); + +		$tables = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$name = current($row); +			$tables[$name] = $name; +		} +		$this->db->sql_freeresult($result); + +		return $tables; +	} + +	/** +	* Check if table exists +	* +	* +	* @param string	$table_name	The table name to check for +	* @return bool true if table exists, else false +	*/ +	function sql_table_exists($table_name) +	{ +		$this->db->sql_return_on_error(true); +		$result = $this->db->sql_query_limit('SELECT * FROM ' . $table_name, 1); +		$this->db->sql_return_on_error(false); + +		if ($result) +		{ +			$this->db->sql_freeresult($result); +			return true; +		} + +		return false; +	} + +	/** +	* Create SQL Table +	* +	* @param string	$table_name	The table name to create +	* @param array	$table_data	Array containing table data. +	* @return array	Statements if $return_statements is true. +	*/ +	function sql_create_table($table_name, $table_data) +	{ +		// holds the DDL for a column +		$columns = $statements = array(); + +		if ($this->sql_table_exists($table_name)) +		{ +			return $this->_sql_run_sql($statements); +		} + +		// Begin transaction +		$statements[] = 'begin'; + +		// Determine if we have created a PRIMARY KEY in the earliest +		$primary_key_gen = false; + +		// Determine if the table requires a sequence +		$create_sequence = false; + +		// Begin table sql statement +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				$table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n"; +			break; + +			default: +				$table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n"; +			break; +		} + +		if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') +		{ +			if (!isset($table_data['PRIMARY_KEY'])) +			{ +				$table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment'); +				$table_data['PRIMARY_KEY'] = 'mssqlindex'; +			} +		} + +		// Iterate through the columns to create a table +		foreach ($table_data['COLUMNS'] as $column_name => $column_data) +		{ +			// here lies an array, filled with information compiled on the column's data +			$prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data); + +			if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen" +			{ +				trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR); +			} + +			// here we add the definition of the new column to the list of columns +			switch ($this->sql_layer) +			{ +				case 'mssql': +				case 'mssqlnative': +					$columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default']; +				break; + +				default: +					$columns[] = "\t {$column_name} " . $prepared_column['column_type_sql']; +				break; +			} + +			// see if we have found a primary key set due to a column definition if we have found it, we can stop looking +			if (!$primary_key_gen) +			{ +				$primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set']; +			} + +			// create sequence DDL based off of the existance of auto incrementing columns +			if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment']) +			{ +				$create_sequence = $column_name; +			} +		} + +		// this makes up all the columns in the create table statement +		$table_sql .= implode(",\n", $columns); + +		// Close the table for two DBMS and add to the statements +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				$table_sql .= "\n);"; +				$statements[] = $table_sql; +			break; +		} + +		// we have yet to create a primary key for this table, +		// this means that we can add the one we really wanted instead +		if (!$primary_key_gen) +		{ +			// Write primary key +			if (isset($table_data['PRIMARY_KEY'])) +			{ +				if (!is_array($table_data['PRIMARY_KEY'])) +				{ +					$table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']); +				} + +				switch ($this->sql_layer) +				{ +					case 'mysql_40': +					case 'mysql_41': +					case 'postgres': +					case 'sqlite': +					case 'sqlite3': +						$table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; +					break; + +					case 'mssql': +					case 'mssqlnative': +						// We need the data here +						$old_return_statements = $this->return_statements; +						$this->return_statements = true; + +						$primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']); +						foreach ($primary_key_stmts as $pk_stmt) +						{ +							$statements[] = $pk_stmt; +						} + +						$this->return_statements = $old_return_statements; +					break; + +					case 'oracle': +						$table_sql .= ",\n\t CONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; +					break; +				} +			} +		} + +		// close the table +		switch ($this->sql_layer) +		{ +			case 'mysql_41': +				// make sure the table is in UTF-8 mode +				$table_sql .= "\n) CHARACTER SET `utf8` COLLATE `utf8_bin`;"; +				$statements[] = $table_sql; +			break; + +			case 'mysql_40': +			case 'sqlite': +			case 'sqlite3': +				$table_sql .= "\n);"; +				$statements[] = $table_sql; +			break; + +			case 'postgres': +				// do we need to add a sequence for auto incrementing columns? +				if ($create_sequence) +				{ +					$statements[] = "CREATE SEQUENCE {$table_name}_seq;"; +				} + +				$table_sql .= "\n);"; +				$statements[] = $table_sql; +			break; + +			case 'oracle': +				$table_sql .= "\n)"; +				$statements[] = $table_sql; + +				// do we need to add a sequence and a tigger for auto incrementing columns? +				if ($create_sequence) +				{ +					// create the actual sequence +					$statements[] = "CREATE SEQUENCE {$table_name}_seq"; + +					// the trigger is the mechanism by which we increment the counter +					$trigger = "CREATE OR REPLACE TRIGGER t_{$table_name}\n"; +					$trigger .= "BEFORE INSERT ON {$table_name}\n"; +					$trigger .= "FOR EACH ROW WHEN (\n"; +					$trigger .= "\tnew.{$create_sequence} IS NULL OR new.{$create_sequence} = 0\n"; +					$trigger .= ")\n"; +					$trigger .= "BEGIN\n"; +					$trigger .= "\tSELECT {$table_name}_seq.nextval\n"; +					$trigger .= "\tINTO :new.{$create_sequence}\n"; +					$trigger .= "\tFROM dual;\n"; +					$trigger .= "END;"; + +					$statements[] = $trigger; +				} +			break; +		} + +		// Write Keys +		if (isset($table_data['KEYS'])) +		{ +			foreach ($table_data['KEYS'] as $key_name => $key_data) +			{ +				if (!is_array($key_data[1])) +				{ +					$key_data[1] = array($key_data[1]); +				} + +				$old_return_statements = $this->return_statements; +				$this->return_statements = true; + +				$key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]); + +				foreach ($key_stmts as $key_stmt) +				{ +					$statements[] = $key_stmt; +				} + +				$this->return_statements = $old_return_statements; +			} +		} + +		// Commit Transaction +		$statements[] = 'commit'; + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Handle passed database update array. +	* Expected structure... +	* Key being one of the following +	*	drop_tables: Drop tables +	*	add_tables: Add tables +	*	change_columns: Column changes (only type, not name) +	*	add_columns: Add columns to a table +	*	drop_keys: Dropping keys +	*	drop_columns: Removing/Dropping columns +	*	add_primary_keys: adding primary keys +	*	add_unique_index: adding an unique index +	*	add_index: adding an index (can be column:index_size if you need to provide size) +	* +	* The values are in this format: +	*		{TABLE NAME}		=> array( +	*			{COLUMN NAME}		=> array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}), +	*			{KEY/INDEX NAME}	=> array({COLUMN NAMES}), +	*		) +	* +	* For more information have a look at /develop/create_schema_files.php (only available through SVN) +	*/ +	function perform_schema_changes($schema_changes) +	{ +		if (empty($schema_changes)) +		{ +			return; +		} + +		$statements = array(); +		$sqlite = false; + +		// For SQLite we need to perform the schema changes in a much more different way +		if (($this->db->sql_layer == 'sqlite' || $this->db->sql_layer == 'sqlite3') && $this->return_statements) +		{ +			$sqlite_data = array(); +			$sqlite = true; +		} + +		// Drop tables? +		if (!empty($schema_changes['drop_tables'])) +		{ +			foreach ($schema_changes['drop_tables'] as $table) +			{ +				// only drop table if it exists +				if ($this->sql_table_exists($table)) +				{ +					$result = $this->sql_table_drop($table); +					if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		// Add tables? +		if (!empty($schema_changes['add_tables'])) +		{ +			foreach ($schema_changes['add_tables'] as $table => $table_data) +			{ +				$result = $this->sql_create_table($table, $table_data); +				if ($this->return_statements) +				{ +					$statements = array_merge($statements, $result); +				} +			} +		} + +		// Change columns? +		if (!empty($schema_changes['change_columns'])) +		{ +			foreach ($schema_changes['change_columns'] as $table => $columns) +			{ +				foreach ($columns as $column_name => $column_data) +				{ +					// If the column exists we change it, else we add it ;) +					if ($column_exists = $this->sql_column_exists($table, $column_name)) +					{ +						$result = $this->sql_column_change($table, $column_name, $column_data, true); +					} +					else +					{ +						$result = $this->sql_column_add($table, $column_name, $column_data, true); +					} + +					if ($sqlite) +					{ +						if ($column_exists) +						{ +							$sqlite_data[$table]['change_columns'][] = $result; +						} +						else +						{ +							$sqlite_data[$table]['add_columns'][] = $result; +						} +					} +					else if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		// Add columns? +		if (!empty($schema_changes['add_columns'])) +		{ +			foreach ($schema_changes['add_columns'] as $table => $columns) +			{ +				foreach ($columns as $column_name => $column_data) +				{ +					// Only add the column if it does not exist yet +					if ($column_exists = $this->sql_column_exists($table, $column_name)) +					{ +						continue; +						// This is commented out here because it can take tremendous time on updates +//						$result = $this->sql_column_change($table, $column_name, $column_data, true); +					} +					else +					{ +						$result = $this->sql_column_add($table, $column_name, $column_data, true); +					} + +					if ($sqlite) +					{ +						if ($column_exists) +						{ +							continue; +//							$sqlite_data[$table]['change_columns'][] = $result; +						} +						else +						{ +							$sqlite_data[$table]['add_columns'][] = $result; +						} +					} +					else if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		// Remove keys? +		if (!empty($schema_changes['drop_keys'])) +		{ +			foreach ($schema_changes['drop_keys'] as $table => $indexes) +			{ +				foreach ($indexes as $index_name) +				{ +					if (!$this->sql_index_exists($table, $index_name)) +					{ +						continue; +					} + +					$result = $this->sql_index_drop($table, $index_name); + +					if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		// Drop columns? +		if (!empty($schema_changes['drop_columns'])) +		{ +			foreach ($schema_changes['drop_columns'] as $table => $columns) +			{ +				foreach ($columns as $column) +				{ +					// Only remove the column if it exists... +					if ($this->sql_column_exists($table, $column)) +					{ +						$result = $this->sql_column_remove($table, $column, true); + +						if ($sqlite) +						{ +							$sqlite_data[$table]['drop_columns'][] = $result; +						} +						else if ($this->return_statements) +						{ +							$statements = array_merge($statements, $result); +						} +					} +				} +			} +		} + +		// Add primary keys? +		if (!empty($schema_changes['add_primary_keys'])) +		{ +			foreach ($schema_changes['add_primary_keys'] as $table => $columns) +			{ +				$result = $this->sql_create_primary_key($table, $columns, true); + +				if ($sqlite) +				{ +					$sqlite_data[$table]['primary_key'] = $result; +				} +				else if ($this->return_statements) +				{ +					$statements = array_merge($statements, $result); +				} +			} +		} + +		// Add unique indexes? +		if (!empty($schema_changes['add_unique_index'])) +		{ +			foreach ($schema_changes['add_unique_index'] as $table => $index_array) +			{ +				foreach ($index_array as $index_name => $column) +				{ +					if ($this->sql_unique_index_exists($table, $index_name)) +					{ +						continue; +					} + +					$result = $this->sql_create_unique_index($table, $index_name, $column); + +					if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		// Add indexes? +		if (!empty($schema_changes['add_index'])) +		{ +			foreach ($schema_changes['add_index'] as $table => $index_array) +			{ +				foreach ($index_array as $index_name => $column) +				{ +					if ($this->sql_index_exists($table, $index_name)) +					{ +						continue; +					} + +					$result = $this->sql_create_index($table, $index_name, $column); + +					if ($this->return_statements) +					{ +						$statements = array_merge($statements, $result); +					} +				} +			} +		} + +		if ($sqlite) +		{ +			foreach ($sqlite_data as $table_name => $sql_schema_changes) +			{ +				// Create temporary table with original data +				$statements[] = 'begin'; + +				$sql = "SELECT sql +					FROM sqlite_master +					WHERE type = 'table' +						AND name = '{$table_name}' +					ORDER BY type DESC, name;"; +				$result = $this->db->sql_query($sql); + +				if (!$result) +				{ +					continue; +				} + +				$row = $this->db->sql_fetchrow($result); +				$this->db->sql_freeresult($result); + +				// Create a backup table and populate it, destroy the existing one +				$statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']); +				$statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; +				$statements[] = 'DROP TABLE ' . $table_name; + +				// Get the columns... +				preg_match('#\((.*)\)#s', $row['sql'], $matches); + +				$plain_table_cols = trim($matches[1]); +				$new_table_cols = preg_split('/,(?![\s\w]+\))/m', $plain_table_cols); +				$column_list = array(); + +				foreach ($new_table_cols as $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY') +					{ +						continue; +					} +					$column_list[] = $entities[0]; +				} + +				// note down the primary key notation because sqlite only supports adding it to the end for the new table +				$primary_key = false; +				$_new_cols = array(); + +				foreach ($new_table_cols as $key => $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY') +					{ +						$primary_key = $declaration; +						continue; +					} +					$_new_cols[] = $declaration; +				} + +				$new_table_cols = $_new_cols; + +				// First of all... change columns +				if (!empty($sql_schema_changes['change_columns'])) +				{ +					foreach ($sql_schema_changes['change_columns'] as $column_sql) +					{ +						foreach ($new_table_cols as $key => $declaration) +						{ +							$entities = preg_split('#\s+#', trim($declaration)); +							if (strpos($column_sql, $entities[0] . ' ') === 0) +							{ +								$new_table_cols[$key] = $column_sql; +							} +						} +					} +				} + +				if (!empty($sql_schema_changes['add_columns'])) +				{ +					foreach ($sql_schema_changes['add_columns'] as $column_sql) +					{ +						$new_table_cols[] = $column_sql; +					} +				} + +				// Now drop them... +				if (!empty($sql_schema_changes['drop_columns'])) +				{ +					foreach ($sql_schema_changes['drop_columns'] as $column_name) +					{ +						// Remove from column list... +						$new_column_list = array(); +						foreach ($column_list as $key => $value) +						{ +							if ($value === $column_name) +							{ +								continue; +							} + +							$new_column_list[] = $value; +						} + +						$column_list = $new_column_list; + +						// Remove from table... +						$_new_cols = array(); +						foreach ($new_table_cols as $key => $declaration) +						{ +							$entities = preg_split('#\s+#', trim($declaration)); +							if (strpos($column_name . ' ', $entities[0] . ' ') === 0) +							{ +								continue; +							} +							$_new_cols[] = $declaration; +						} +						$new_table_cols = $_new_cols; +					} +				} + +				// Primary key... +				if (!empty($sql_schema_changes['primary_key'])) +				{ +					$new_table_cols[] = 'PRIMARY KEY (' . implode(', ', $sql_schema_changes['primary_key']) . ')'; +				} +				// Add a new one or the old primary key +				else if ($primary_key !== false) +				{ +					$new_table_cols[] = $primary_key; +				} + +				$columns = implode(',', $column_list); + +				// create a new table and fill it up. destroy the temp one +				$statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $new_table_cols) . ');'; +				$statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; +				$statements[] = 'DROP TABLE ' . $table_name . '_temp'; + +				$statements[] = 'commit'; +			} +		} + +		if ($this->return_statements) +		{ +			return $statements; +		} +	} + +	/** +	* Gets a list of columns of a table. +	* +	* @param string $table		Table name +	* +	* @return array				Array of column names (all lower case) +	*/ +	function sql_list_columns($table) +	{ +		$columns = array(); + +		switch ($this->sql_layer) +		{ +			case 'mysql_40': +			case 'mysql_41': +				$sql = "SHOW COLUMNS FROM $table"; +			break; + +			// PostgreSQL has a way of doing this in a much simpler way but would +			// not allow us to support all versions of PostgreSQL +			case 'postgres': +				$sql = "SELECT a.attname +					FROM pg_class c, pg_attribute a +					WHERE c.relname = '{$table}' +						AND a.attnum > 0 +						AND a.attrelid = c.oid"; +			break; + +			// same deal with PostgreSQL, we must perform more complex operations than +			// we technically could +			case 'mssql': +			case 'mssqlnative': +				$sql = "SELECT c.name +					FROM syscolumns c +					LEFT JOIN sysobjects o ON c.id = o.id +					WHERE o.name = '{$table}'"; +			break; + +			case 'oracle': +				$sql = "SELECT column_name +					FROM user_tab_columns +					WHERE LOWER(table_name) = '" . strtolower($table) . "'"; +			break; + +			case 'sqlite': +			case 'sqlite3': +				$sql = "SELECT sql +					FROM sqlite_master +					WHERE type = 'table' +						AND name = '{$table}'"; + +				$result = $this->db->sql_query($sql); + +				if (!$result) +				{ +					return false; +				} + +				$row = $this->db->sql_fetchrow($result); +				$this->db->sql_freeresult($result); + +				preg_match('#\((.*)\)#s', $row['sql'], $matches); + +				$cols = trim($matches[1]); +				$col_array = preg_split('/,(?![\s\w]+\))/m', $cols); + +				foreach ($col_array as $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY') +					{ +						continue; +					} + +					$column = strtolower($entities[0]); +					$columns[$column] = $column; +				} + +				return $columns; +			break; +		} + +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$column = strtolower(current($row)); +			$columns[$column] = $column; +		} +		$this->db->sql_freeresult($result); + +		return $columns; +	} + +	/** +	* Check whether a specified column exist in a table +	* +	* @param string	$table			Table to check +	* @param string	$column_name	Column to check +	* +	* @return bool		True if column exists, false otherwise +	*/ +	function sql_column_exists($table, $column_name) +	{ +		$columns = $this->sql_list_columns($table); + +		return isset($columns[$column_name]); +	} + +	/** +	* Check if a specified index exists in table. Does not return PRIMARY KEY and UNIQUE indexes. +	* +	* @param string	$table_name		Table to check the index at +	* @param string	$index_name		The index name to check +	* +	* @return bool True if index exists, else false +	*/ +	function sql_index_exists($table_name, $index_name) +	{ +		if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') +		{ +			$sql = "EXEC sp_statistics '$table_name'"; +			$result = $this->db->sql_query($sql); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if ($row['TYPE'] == 3) +				{ +					if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) +					{ +						$this->db->sql_freeresult($result); +						return true; +					} +				} +			} +			$this->db->sql_freeresult($result); + +			return false; +		} + +		switch ($this->sql_layer) +		{ +			case 'postgres': +				$sql = "SELECT ic.relname as index_name +					FROM pg_class bc, pg_class ic, pg_index i +					WHERE (bc.oid = i.indrelid) +						AND (ic.oid = i.indexrelid) +						AND (bc.relname = '" . $table_name . "') +						AND (i.indisunique != 't') +						AND (i.indisprimary != 't')"; +				$col = 'index_name'; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$sql = 'SHOW KEYS +					FROM ' . $table_name; +				$col = 'Key_name'; +			break; + +			case 'oracle': +				$sql = "SELECT index_name +					FROM user_indexes +					WHERE table_name = '" . strtoupper($table_name) . "' +						AND generated = 'N' +						AND uniqueness = 'NONUNIQUE'"; +				$col = 'index_name'; +			break; + +			case 'sqlite': +			case 'sqlite3': +				$sql = "PRAGMA index_list('" . $table_name . "');"; +				$col = 'name'; +			break; +		} + +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) +			{ +				continue; +			} + +			// These DBMS prefix index name with the table name +			switch ($this->sql_layer) +			{ +				case 'oracle': +				case 'postgres': +				case 'sqlite': +				case 'sqlite3': +					$row[$col] = substr($row[$col], strlen($table_name) + 1); +				break; +			} + +			if (strtolower($row[$col]) == strtolower($index_name)) +			{ +				$this->db->sql_freeresult($result); +				return true; +			} +		} +		$this->db->sql_freeresult($result); + +		return false; +	} + +	/** +	* Check if a specified index exists in table. Does not return PRIMARY KEY indexes. +	* +	* @param string	$table_name		Table to check the index at +	* @param string	$index_name		The index name to check +	* +	* @return bool True if index exists, else false +	*/ +	function sql_unique_index_exists($table_name, $index_name) +	{ +		if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') +		{ +			$sql = "EXEC sp_statistics '$table_name'"; +			$result = $this->db->sql_query($sql); + +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				// Usually NON_UNIQUE is the column we want to check, but we allow for both +				if ($row['TYPE'] == 3) +				{ +					if (strtolower($row['INDEX_NAME']) == strtolower($index_name)) +					{ +						$this->db->sql_freeresult($result); +						return true; +					} +				} +			} +			$this->db->sql_freeresult($result); +			return false; +		} + +		switch ($this->sql_layer) +		{ +			case 'postgres': +				$sql = "SELECT ic.relname as index_name, i.indisunique +					FROM pg_class bc, pg_class ic, pg_index i +					WHERE (bc.oid = i.indrelid) +						AND (ic.oid = i.indexrelid) +						AND (bc.relname = '" . $table_name . "') +						AND (i.indisprimary != 't')"; +				$col = 'index_name'; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$sql = 'SHOW KEYS +					FROM ' . $table_name; +				$col = 'Key_name'; +			break; + +			case 'oracle': +				$sql = "SELECT index_name, table_owner +					FROM user_indexes +					WHERE table_name = '" . strtoupper($table_name) . "' +						AND generated = 'N' +						AND uniqueness = 'UNIQUE'"; +				$col = 'index_name'; +			break; + +			case 'sqlite': +			case 'sqlite3': +				$sql = "PRAGMA index_list('" . $table_name . "');"; +				$col = 'name'; +			break; +		} + +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && ($row['Non_unique'] || $row[$col] == 'PRIMARY')) +			{ +				continue; +			} + +			if (($this->sql_layer == 'sqlite' || $this->sql_layer == 'sqlite3') && !$row['unique']) +			{ +				continue; +			} + +			if ($this->sql_layer == 'postgres' && $row['indisunique'] != 't') +			{ +				continue; +			} + +			// These DBMS prefix index name with the table name +			switch ($this->sql_layer) +			{ +				case 'oracle': +					// Two cases here... prefixed with U_[table_owner] and not prefixed with table_name +					if (strpos($row[$col], 'U_') === 0) +					{ +						$row[$col] = substr($row[$col], strlen('U_' . $row['table_owner']) + 1); +					} +					else if (strpos($row[$col], strtoupper($table_name)) === 0) +					{ +						$row[$col] = substr($row[$col], strlen($table_name) + 1); +					} +				break; + +				case 'postgres': +				case 'sqlite': +				case 'sqlite3': +					$row[$col] = substr($row[$col], strlen($table_name) + 1); +				break; +			} + +			if (strtolower($row[$col]) == strtolower($index_name)) +			{ +				$this->db->sql_freeresult($result); +				return true; +			} +		} +		$this->db->sql_freeresult($result); + +		return false; +	} + +	/** +	* Private method for performing sql statements (either execute them or return them) +	* @access private +	*/ +	function _sql_run_sql($statements) +	{ +		if ($this->return_statements) +		{ +			return $statements; +		} + +		// We could add error handling here... +		foreach ($statements as $sql) +		{ +			if ($sql === 'begin') +			{ +				$this->db->sql_transaction('begin'); +			} +			else if ($sql === 'commit') +			{ +				$this->db->sql_transaction('commit'); +			} +			else +			{ +				$this->db->sql_query($sql); +			} +		} + +		return true; +	} + +	/** +	* Function to prepare some column information for better usage +	* @access private +	*/ +	function sql_prepare_column_data($table_name, $column_name, $column_data) +	{ +		if (strlen($column_name) > 30) +		{ +			trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR); +		} + +		// Get type +		list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]); + +		// Adjust default value if db-dependent specified +		if (is_array($column_data[1])) +		{ +			$column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default']; +		} + +		$sql = ''; + +		$return_array = array(); + +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				$sql .= " {$column_type} "; +				$sql_default = " {$column_type} "; + +				// For adding columns we need the default definition +				if (!is_null($column_data[1])) +				{ +					// For hexadecimal values do not use single quotes +					if (strpos($column_data[1], '0x') === 0) +					{ +						$return_array['default'] = 'DEFAULT (' . $column_data[1] . ') '; +						$sql_default .= $return_array['default']; +					} +					else +					{ +						$return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') '; +						$sql_default .= $return_array['default']; +					} +				} + +				if (isset($column_data[2]) && $column_data[2] == 'auto_increment') +				{ +//					$sql .= 'IDENTITY (1, 1) '; +					$sql_default .= 'IDENTITY (1, 1) '; +				} + +				$return_array['textimage'] = $column_type === '[text]'; + +				$sql .= 'NOT NULL'; +				$sql_default .= 'NOT NULL'; + +				$return_array['column_type_sql_default'] = $sql_default; + +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$sql .= " {$column_type} "; + +				// For hexadecimal values do not use single quotes +				if (!is_null($column_data[1]) && substr($column_type, -4) !== 'text' && substr($column_type, -4) !== 'blob') +				{ +					$sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' "; +				} +				$sql .= 'NOT NULL'; + +				if (isset($column_data[2])) +				{ +					if ($column_data[2] == 'auto_increment') +					{ +						$sql .= ' auto_increment'; +					} +					else if ($this->sql_layer === 'mysql_41' && $column_data[2] == 'true_sort') +					{ +						$sql .= ' COLLATE utf8_unicode_ci'; +					} +				} + +			break; + +			case 'oracle': +				$sql .= " {$column_type} "; +				$sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : ''; + +				// In Oracle empty strings ('') are treated as NULL. +				// Therefore in oracle we allow NULL's for all DEFAULT '' entries +				// Oracle does not like setting NOT NULL on a column that is already NOT NULL (this happens only on number fields) +				if (!preg_match('/number/i', $column_type)) +				{ +					$sql .= ($column_data[1] === '') ? '' : 'NOT NULL'; +				} + +				$return_array['auto_increment'] = false; +				if (isset($column_data[2]) && $column_data[2] == 'auto_increment') +				{ +					$return_array['auto_increment'] = true; +				} + +			break; + +			case 'postgres': +				$return_array['column_type'] = $column_type; + +				$sql .= " {$column_type} "; + +				$return_array['auto_increment'] = false; +				if (isset($column_data[2]) && $column_data[2] == 'auto_increment') +				{ +					$default_val = "nextval('{$table_name}_seq')"; +					$return_array['auto_increment'] = true; +				} +				else if (!is_null($column_data[1])) +				{ +					$default_val = "'" . $column_data[1] . "'"; +					$return_array['null'] = 'NOT NULL'; +					$sql .= 'NOT NULL '; +				} + +				$return_array['default'] = $default_val; + +				$sql .= "DEFAULT {$default_val}"; + +				// Unsigned? Then add a CHECK contraint +				if (in_array($orig_column_type, $this->unsigned_types)) +				{ +					$return_array['constraint'] = "CHECK ({$column_name} >= 0)"; +					$sql .= " CHECK ({$column_name} >= 0)"; +				} + +			break; + +			case 'sqlite': +			case 'sqlite3': +				$return_array['primary_key_set'] = false; +				if (isset($column_data[2]) && $column_data[2] == 'auto_increment') +				{ +					$sql .= ' INTEGER PRIMARY KEY'; +					$return_array['primary_key_set'] = true; + +					if ($this->sql_layer === 'sqlite3') +					{ +						$sql .= ' AUTOINCREMENT'; +					} +				} +				else +				{ +					$sql .= ' ' . $column_type; +				} + +				$sql .= ' NOT NULL '; +				$sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}'" : ''; + +			break; +		} + +		$return_array['column_type_sql'] = $sql; + +		return $return_array; +	} + +	/** +	* Get the column's database type from the type map +	* +	* @param string $column_map_type +	* @return array		column type for this database +	*					and map type without length +	*/ +	function get_column_type($column_map_type) +	{ +		if (strpos($column_map_type, ':') !== false) +		{ +			list($orig_column_type, $column_length) = explode(':', $column_map_type); +			if (!is_array($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'])) +			{ +				$column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'], $column_length); +			} +			else +			{ +				if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'])) +				{ +					switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][0]) +					{ +						case 'div': +							$column_length /= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][1]; +							$column_length = ceil($column_length); +							$column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length); +						break; +					} +				} + +				if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'])) +				{ +					switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][0]) +					{ +						case 'mult': +							$column_length *= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][1]; +							if ($column_length > $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][2]) +							{ +								$column_type = $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][3]; +							} +							else +							{ +								$column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length); +							} +						break; +					} +				} +			} +			$orig_column_type .= ':'; +		} +		else +		{ +			$orig_column_type = $column_map_type; +			$column_type = $this->dbms_type_map[$this->sql_layer][$column_map_type]; +		} + +		return array($column_type, $orig_column_type); +	} + +	/** +	* Add new column +	*/ +	function sql_column_add($table_name, $column_name, $column_data, $inline = false) +	{ +		$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); +		$statements = array(); + +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				// Does not support AFTER, only through temporary table +				$statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default']; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : ''; +				$statements[] = 'ALTER TABLE `' . $table_name . '` ADD COLUMN `' . $column_name . '` ' . $column_data['column_type_sql'] . $after; +			break; + +			case 'oracle': +				// Does not support AFTER, only through temporary table +				$statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql']; +			break; + +			case 'postgres': +				// Does not support AFTER, only through temporary table +				if (version_compare($this->db->sql_server_info(true), '8.0', '>=')) +				{ +					$statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql']; +				} +				else +				{ +					// old versions cannot add columns with default and null information +					$statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint']; + +					if (isset($column_data['null'])) +					{ +						if ($column_data['null'] == 'NOT NULL') +						{ +							$statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL'; +						} +					} + +					if (isset($column_data['default'])) +					{ +						$statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default']; +					} +				} + +			break; + +			case 'sqlite': +				if ($inline && $this->return_statements) +				{ +					return $column_name . ' ' . $column_data['column_type_sql']; +				} + +				$recreate_queries = $this->sqlite_get_recreate_table_queries($table_name); +				if (empty($recreate_queries)) +				{ +					break; +				} + +				$statements[] = 'begin'; + +				$sql_create_table = array_shift($recreate_queries); + +				// Create a backup table and populate it, destroy the existing one +				$statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table); +				$statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; +				$statements[] = 'DROP TABLE ' . $table_name; + +				preg_match('#\((.*)\)#s', $sql_create_table, $matches); + +				$new_table_cols = trim($matches[1]); +				$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); +				$column_list = array(); + +				foreach ($old_table_cols as $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY') +					{ +						continue; +					} +					$column_list[] = $entities[0]; +				} + +				$columns = implode(',', $column_list); + +				$new_table_cols = $column_name . ' ' . $column_data['column_type_sql'] . ',' . $new_table_cols; + +				// create a new table and fill it up. destroy the temp one +				$statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');'; +				$statements = array_merge($statements, $recreate_queries); + +				$statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; +				$statements[] = 'DROP TABLE ' . $table_name . '_temp'; + +				$statements[] = 'commit'; +			break; + +			case 'sqlite3': +				if ($inline && $this->return_statements) +				{ +					return $column_name . ' ' . $column_data['column_type_sql']; +				} + +				$statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql']; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Drop column +	*/ +	function sql_column_remove($table_name, $column_name, $inline = false) +	{ +		$statements = array(); + +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				// We need the data here +				$old_return_statements = $this->return_statements; +				$this->return_statements = true; + +				$indexes = $this->mssql_get_existing_indexes($table_name, $column_name); + +				// Drop any indexes +				$recreate_indexes = array(); +				if (!empty($indexes)) +				{ +					foreach ($indexes as $index_name => $index_data) +					{ +						$result = $this->sql_index_drop($table_name, $index_name); +						$statements = array_merge($statements, $result); +						if (sizeof($index_data) > 1) +						{ +							// Remove this column from the index and recreate it +							$recreate_indexes[$index_name] = array_diff($index_data, array($column_name)); +						} +					} +				} + +				// Drop default value constraint +				$result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); +				$statements = array_merge($statements, $result); + +				// Remove the column +				$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']'; + +				if (!empty($recreate_indexes)) +				{ +					// Recreate indexes after we removed the column +					foreach ($recreate_indexes as $index_name => $index_data) +					{ +						$result = $this->sql_create_index($table_name, $index_name, $index_data); +						$statements = array_merge($statements, $result); +					} +				} + +				$this->return_statements = $old_return_statements; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`'; +			break; + +			case 'oracle': +				$statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name; +			break; + +			case 'postgres': +				$statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"'; +			break; + +			case 'sqlite': +			case 'sqlite3': + +				if ($inline && $this->return_statements) +				{ +					return $column_name; +				} + +				$recreate_queries = $this->sqlite_get_recreate_table_queries($table_name, $column_name); +				if (empty($recreate_queries)) +				{ +					break; +				} + +				$statements[] = 'begin'; + +				$sql_create_table = array_shift($recreate_queries); + +				// Create a backup table and populate it, destroy the existing one +				$statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table); +				$statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; +				$statements[] = 'DROP TABLE ' . $table_name; + +				preg_match('#\((.*)\)#s', $sql_create_table, $matches); + +				$new_table_cols = trim($matches[1]); +				$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); +				$column_list = array(); + +				foreach ($old_table_cols as $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name) +					{ +						continue; +					} +					$column_list[] = $entities[0]; +				} + +				$columns = implode(',', $column_list); + +				$new_table_cols = trim(preg_replace('/' . $column_name . '\b[^,]+(?:,|$)/m', '', $new_table_cols)); +				if (substr($new_table_cols, -1) === ',') +				{ +					// Remove the comma from the last entry again +					$new_table_cols = substr($new_table_cols, 0, -1); +				} + +				// create a new table and fill it up. destroy the temp one +				$statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');'; +				$statements = array_merge($statements, $recreate_queries); + +				$statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; +				$statements[] = 'DROP TABLE ' . $table_name . '_temp'; + +				$statements[] = 'commit'; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Drop Index +	*/ +	function sql_index_drop($table_name, $index_name) +	{ +		$statements = array(); + +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				$statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name; +			break; + +			case 'oracle': +			case 'postgres': +			case 'sqlite': +			case 'sqlite3': +				$statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Drop Table +	*/ +	function sql_table_drop($table_name) +	{ +		$statements = array(); + +		if (!$this->sql_table_exists($table_name)) +		{ +			return $this->_sql_run_sql($statements); +		} + +		// the most basic operation, get rid of the table +		$statements[] = 'DROP TABLE ' . $table_name; + +		switch ($this->sql_layer) +		{ +			case 'oracle': +				$sql = 'SELECT A.REFERENCED_NAME +					FROM USER_DEPENDENCIES A, USER_TRIGGERS B +					WHERE A.REFERENCED_TYPE = \'SEQUENCE\' +						AND A.NAME = B.TRIGGER_NAME +						AND B.TABLE_NAME = \'' . strtoupper($table_name) . "'"; +				$result = $this->db->sql_query($sql); + +				// any sequences ref'd to this table's triggers? +				while ($row = $this->db->sql_fetchrow($result)) +				{ +					$statements[] = "DROP SEQUENCE {$row['referenced_name']}"; +				} +				$this->db->sql_freeresult($result); +			break; + +			case 'postgres': +				// PGSQL does not "tightly" bind sequences and tables, we must guess... +				$sql = "SELECT relname +					FROM pg_class +					WHERE relkind = 'S' +						AND relname = '{$table_name}_seq'"; +				$result = $this->db->sql_query($sql); + +				// We don't even care about storing the results. We already know the answer if we get rows back. +				if ($this->db->sql_fetchrow($result)) +				{ +					$statements[] =  "DROP SEQUENCE {$table_name}_seq;\n"; +				} +				$this->db->sql_freeresult($result); +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Add primary key +	*/ +	function sql_create_primary_key($table_name, $column, $inline = false) +	{ +		$statements = array(); + +		switch ($this->sql_layer) +		{ +			case 'postgres': +			case 'mysql_40': +			case 'mysql_41': +				$statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')'; +			break; + +			case 'mssql': +			case 'mssqlnative': +				$sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD "; +				$sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY  CLUSTERED ("; +				$sql .= '[' . implode("],\n\t\t[", $column) . ']'; +				$sql .= ')'; + +				$statements[] = $sql; +			break; + +			case 'oracle': +				$statements[] = 'ALTER TABLE ' . $table_name . 'add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')'; +			break; + +			case 'sqlite': +			case 'sqlite3': + +				if ($inline && $this->return_statements) +				{ +					return $column; +				} + +				$recreate_queries = $this->sqlite_get_recreate_table_queries($table_name); +				if (empty($recreate_queries)) +				{ +					break; +				} + +				$statements[] = 'begin'; + +				$sql_create_table = array_shift($recreate_queries); + +				// Create a backup table and populate it, destroy the existing one +				$statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table); +				$statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; +				$statements[] = 'DROP TABLE ' . $table_name; + +				preg_match('#\((.*)\)#s', $sql_create_table, $matches); + +				$new_table_cols = trim($matches[1]); +				$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); +				$column_list = array(); + +				foreach ($old_table_cols as $declaration) +				{ +					$entities = preg_split('#\s+#', trim($declaration)); +					if ($entities[0] == 'PRIMARY') +					{ +						continue; +					} +					$column_list[] = $entities[0]; +				} + +				$columns = implode(',', $column_list); + +				// create a new table and fill it up. destroy the temp one +				$statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ', PRIMARY KEY (' . implode(', ', $column) . '));'; +				$statements = array_merge($statements, $recreate_queries); + +				$statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; +				$statements[] = 'DROP TABLE ' . $table_name . '_temp'; + +				$statements[] = 'commit'; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Add unique index +	*/ +	function sql_create_unique_index($table_name, $index_name, $column) +	{ +		$statements = array(); + +		$table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) +		if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) +		{ +			$max_length = strlen($table_prefix) + 24; +			trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); +		} + +		switch ($this->sql_layer) +		{ +			case 'postgres': +			case 'oracle': +			case 'sqlite': +			case 'sqlite3': +				$statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; +			break; + +			case 'mssql': +			case 'mssqlnative': +				$statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Add index +	*/ +	function sql_create_index($table_name, $index_name, $column) +	{ +		$statements = array(); + +		$table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) +		if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) +		{ +			$max_length = strlen($table_prefix) + 24; +			trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); +		} + +		// remove index length unless MySQL4 +		if ('mysql_40' != $this->sql_layer) +		{ +			$column = preg_replace('#:.*$#', '', $column); +		} + +		switch ($this->sql_layer) +		{ +			case 'postgres': +			case 'oracle': +			case 'sqlite': +			case 'sqlite3': +				$statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; +			break; + +			case 'mysql_40': +				// add index size to definition as required by MySQL4 +				foreach ($column as $i => $col) +				{ +					if (false !== strpos($col, ':')) +					{ +						list($col, $index_size) = explode(':', $col); +						$column[$i] = "$col($index_size)"; +					} +				} +			// no break +			case 'mysql_41': +				$statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; +			break; + +			case 'mssql': +			case 'mssqlnative': +				$statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* List all of the indices that belong to a table, +	* does not count: +	* * UNIQUE indices +	* * PRIMARY keys +	*/ +	function sql_list_index($table_name) +	{ +		$index_array = array(); + +		if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative') +		{ +			$sql = "EXEC sp_statistics '$table_name'"; +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if ($row['TYPE'] == 3) +				{ +					$index_array[] = $row['INDEX_NAME']; +				} +			} +			$this->db->sql_freeresult($result); +		} +		else +		{ +			switch ($this->sql_layer) +			{ +				case 'postgres': +					$sql = "SELECT ic.relname as index_name +						FROM pg_class bc, pg_class ic, pg_index i +						WHERE (bc.oid = i.indrelid) +							AND (ic.oid = i.indexrelid) +							AND (bc.relname = '" . $table_name . "') +							AND (i.indisunique != 't') +							AND (i.indisprimary != 't')"; +					$col = 'index_name'; +				break; + +				case 'mysql_40': +				case 'mysql_41': +					$sql = 'SHOW KEYS +						FROM ' . $table_name; +					$col = 'Key_name'; +				break; + +				case 'oracle': +					$sql = "SELECT index_name +						FROM user_indexes +						WHERE table_name = '" . strtoupper($table_name) . "' +							AND generated = 'N' +							AND uniqueness = 'NONUNIQUE'"; +					$col = 'index_name'; +				break; + +				case 'sqlite': +				case 'sqlite3': +					$sql = "PRAGMA index_info('" . $table_name . "');"; +					$col = 'name'; +				break; +			} + +			$result = $this->db->sql_query($sql); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) +				{ +					continue; +				} + +				switch ($this->sql_layer) +				{ +					case 'oracle': +					case 'postgres': +					case 'sqlite': +					case 'sqlite3': +						$row[$col] = substr($row[$col], strlen($table_name) + 1); +					break; +				} + +				$index_array[] = $row[$col]; +			} +			$this->db->sql_freeresult($result); +		} + +		return array_map('strtolower', $index_array); +	} + +	/** +	* Change column type (not name!) +	*/ +	function sql_column_change($table_name, $column_name, $column_data, $inline = false) +	{ +		$column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data); +		$statements = array(); + +		switch ($this->sql_layer) +		{ +			case 'mssql': +			case 'mssqlnative': +				// We need the data here +				$old_return_statements = $this->return_statements; +				$this->return_statements = true; + +				$indexes = $this->mssql_get_existing_indexes($table_name, $column_name); + +				// Drop any indexes +				if (!empty($indexes)) +				{ +					foreach ($indexes as $index_name => $index_data) +					{ +						$result = $this->sql_index_drop($table_name, $index_name); +						$statements = array_merge($statements, $result); +					} +				} + +				// Drop default value constraint +				$result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name); +				$statements = array_merge($statements, $result); + +				// Change the column +				$statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; + +				if (!empty($column_data['default'])) +				{ +					// Add new default value constraint +					$statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $this->db->sql_escape($column_data['default']) . ' FOR [' . $column_name . ']'; +				} + +				if (!empty($indexes)) +				{ +					// Recreate indexes after we changed the column +					foreach ($indexes as $index_name => $index_data) +					{ +						$result = $this->sql_create_index($table_name, $index_name, $index_data); +						$statements = array_merge($statements, $result); +					} +				} + +				$this->return_statements = $old_return_statements; +			break; + +			case 'mysql_40': +			case 'mysql_41': +				$statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql']; +			break; + +			case 'oracle': +				$statements[] = 'ALTER TABLE ' . $table_name . ' MODIFY ' . $column_name . ' ' . $column_data['column_type_sql']; +			break; + +			case 'postgres': +				$sql = 'ALTER TABLE ' . $table_name . ' '; + +				$sql_array = array(); +				$sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type']; + +				if (isset($column_data['null'])) +				{ +					if ($column_data['null'] == 'NOT NULL') +					{ +						$sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL'; +					} +					else if ($column_data['null'] == 'NULL') +					{ +						$sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL'; +					} +				} + +				if (isset($column_data['default'])) +				{ +					$sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default']; +				} + +				// we don't want to double up on constraints if we change different number data types +				if (isset($column_data['constraint'])) +				{ +					$constraint_sql = "SELECT consrc as constraint_data +								FROM pg_constraint, pg_class bc +								WHERE conrelid = bc.oid +									AND bc.relname = '{$table_name}' +									AND NOT EXISTS ( +										SELECT * +											FROM pg_constraint as c, pg_inherits as i +											WHERE i.inhrelid = pg_constraint.conrelid +												AND c.conname = pg_constraint.conname +												AND c.consrc = pg_constraint.consrc +												AND c.conrelid = i.inhparent +									)"; + +					$constraint_exists = false; + +					$result = $this->db->sql_query($constraint_sql); +					while ($row = $this->db->sql_fetchrow($result)) +					{ +						if (trim($row['constraint_data']) == trim($column_data['constraint'])) +						{ +							$constraint_exists = true; +							break; +						} +					} +					$this->db->sql_freeresult($result); + +					if (!$constraint_exists) +					{ +						$sql_array[] = 'ADD ' . $column_data['constraint']; +					} +				} + +				$sql .= implode(', ', $sql_array); + +				$statements[] = $sql; +			break; + +			case 'sqlite': +			case 'sqlite3': + +				if ($inline && $this->return_statements) +				{ +					return $column_name . ' ' . $column_data['column_type_sql']; +				} + +				$recreate_queries = $this->sqlite_get_recreate_table_queries($table_name); +				if (empty($recreate_queries)) +				{ +					break; +				} + +				$statements[] = 'begin'; + +				$sql_create_table = array_shift($recreate_queries); + +				// Create a temp table and populate it, destroy the existing one +				$statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table); +				$statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name; +				$statements[] = 'DROP TABLE ' . $table_name; + +				preg_match('#\((.*)\)#s', $sql_create_table, $matches); + +				$new_table_cols = trim($matches[1]); +				$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols); +				$column_list = array(); + +				foreach ($old_table_cols as $key => $declaration) +				{ +					$declaration = trim($declaration); + +					// Check for the beginning of the constraint section and stop +					if (preg_match('/[^\(]*\s*PRIMARY KEY\s+\(/', $declaration) || +						preg_match('/[^\(]*\s*UNIQUE\s+\(/', $declaration) || +						preg_match('/[^\(]*\s*FOREIGN KEY\s+\(/', $declaration) || +						preg_match('/[^\(]*\s*CHECK\s+\(/', $declaration)) +					{ +						break; +					} + +					$entities = preg_split('#\s+#', $declaration); +					$column_list[] = $entities[0]; +					if ($entities[0] == $column_name) +					{ +						$old_table_cols[$key] = $column_name . ' ' . $column_data['column_type_sql']; +					} +				} + +				$columns = implode(',', $column_list); + +				// Create a new table and fill it up. destroy the temp one +				$statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $old_table_cols) . ');'; +				$statements = array_merge($statements, $recreate_queries); + +				$statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;'; +				$statements[] = 'DROP TABLE ' . $table_name . '_temp'; + +				$statements[] = 'commit'; + +			break; +		} + +		return $this->_sql_run_sql($statements); +	} + +	/** +	* Get queries to drop the default constraints of a column +	* +	* We need to drop the default constraints of a column, +	* before being able to change their type or deleting them. +	* +	* @param string $table_name +	* @param string $column_name +	* @return array		Array with SQL statements +	*/ +	protected function mssql_get_drop_default_constraints_queries($table_name, $column_name) +	{ +		$statements = array(); +		if ($this->mssql_is_sql_server_2000()) +		{ +			// http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx +			// Deprecated in SQL Server 2005 +			$sql = "SELECT so.name AS def_name +				FROM sysobjects so +				JOIN sysconstraints sc ON so.id = sc.constid +				WHERE object_name(so.parent_obj) = '{$table_name}' +					AND so.xtype = 'D' +					AND sc.colid = (SELECT colid FROM syscolumns +						WHERE id = object_id('{$table_name}') +							AND name = '{$column_name}')"; +		} +		else +		{ +			$sql = "SELECT dobj.name AS def_name +				FROM sys.columns col +					LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D') +				WHERE col.object_id = object_id('{$table_name}') +					AND col.name = '{$column_name}' +					AND dobj.name IS NOT NULL"; +		} + +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']'; +		} +		$this->db->sql_freeresult($result); + +		return $statements; +	} + +	/** +	* Get a list with existing indexes for the column +	* +	* @param string $table_name +	* @param string $column_name +	* @return array		Array with Index name => columns +	*/ +	protected function mssql_get_existing_indexes($table_name, $column_name) +	{ +		$existing_indexes = array(); +		if ($this->mssql_is_sql_server_2000()) +		{ +			// http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx +			// Deprecated in SQL Server 2005 +			$sql = "SELECT DISTINCT ix.name AS phpbb_index_name +				FROM sysindexes ix +				INNER JOIN sysindexkeys ixc +					ON ixc.id = ix.id +						AND ixc.indid = ix.indid +				INNER JOIN syscolumns cols +					ON cols.colid = ixc.colid +						AND cols.id = ix.id +				WHERE ix.id = object_id('{$table_name}') +					AND cols.name = '{$column_name}'"; +		} +		else +		{ +			$sql = "SELECT DISTINCT ix.name AS phpbb_index_name +				FROM sys.indexes ix +				INNER JOIN sys.index_columns ixc +					ON ixc.object_id = ix.object_id +						AND ixc.index_id = ix.index_id +				INNER JOIN sys.columns cols +					ON cols.column_id = ixc.column_id +						AND cols.object_id = ix.object_id +				WHERE ix.object_id = object_id('{$table_name}') +					AND cols.name = '{$column_name}'"; +		} + +		$result = $this->db->sql_query($sql); +		$existing_indexes = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$existing_indexes[$row['phpbb_index_name']] = array(); +		} +		$this->db->sql_freeresult($result); + +		if (empty($existing_indexes)) +		{ +			return array(); +		} + +		if ($this->mssql_is_sql_server_2000()) +		{ +			$sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name +				FROM sysindexes ix +				INNER JOIN sysindexkeys ixc +					ON ixc.id = ix.id +						AND ixc.indid = ix.indid +				INNER JOIN syscolumns cols +					ON cols.colid = ixc.colid +						AND cols.id = ix.id +				WHERE ix.id = object_id('{$table_name}') +					AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); +		} +		else +		{ +			$sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name +				FROM sys.indexes ix +				INNER JOIN sys.index_columns ixc +					ON ixc.object_id = ix.object_id +						AND ixc.index_id = ix.index_id +				INNER JOIN sys.columns cols +					ON cols.column_id = ixc.column_id +						AND cols.object_id = ix.object_id +				WHERE ix.object_id = object_id('{$table_name}') +					AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes)); +		} + +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name']; +		} +		$this->db->sql_freeresult($result); + +		return $existing_indexes; +	} + +	/** +	* Is the used MS SQL Server a SQL Server 2000? +	* +	* @return bool +	*/ +	protected function mssql_is_sql_server_2000() +	{ +		if ($this->is_sql_server_2000 === null) +		{ +			$sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version"; +			$result = $this->db->sql_query($sql); +			$properties = $this->db->sql_fetchrow($result); +			$this->db->sql_freeresult($result); +			$this->is_sql_server_2000 = $properties['mssql_version'][0] == '8'; +		} + +		return $this->is_sql_server_2000; +	} + +	/** +	* Returns the Queries which are required to recreate a table including indexes +	* +	* @param string $table_name +	* @param string $remove_column	When we drop a column, we remove the column +	*								from all indexes. If the index has no other +	*								column, we drop it completly. +	* @return array +	*/ +	protected function sqlite_get_recreate_table_queries($table_name, $remove_column = '') +	{ +		$queries = array(); + +		$sql = "SELECT sql +			FROM sqlite_master +			WHERE type = 'table' +				AND name = '{$table_name}'"; +		$result = $this->db->sql_query($sql); +		$sql_create_table = $this->db->sql_fetchfield('sql'); +		$this->db->sql_freeresult($result); + +		if (!$sql_create_table) +		{ +			return array(); +		} +		$queries[] = $sql_create_table; + +		$sql = "SELECT sql +			FROM sqlite_master +			WHERE type = 'index' +				AND tbl_name = '{$table_name}'"; +		$result = $this->db->sql_query($sql); +		while ($sql_create_index = $this->db->sql_fetchfield('sql')) +		{ +			if ($remove_column) +			{ +				$match = array(); +				preg_match('#(?:[\w ]+)\((.*)\)#', $sql_create_index, $match); +				if (!isset($match[1])) +				{ +					continue; +				} + +				// Find and remove $remove_column from the index +				$columns = explode(', ', $match[1]); +				$found_column = array_search($remove_column, $columns); +				if ($found_column !== false) +				{ +					unset($columns[$found_column]); + +					// If the column list is not empty add the index to the list +					if (!empty($columns)) +					{ +						$queries[] = str_replace($match[1], implode(', ', $columns), $sql_create_index); +					} +				} +				else +				{ +					$queries[] = $sql_create_index; +				} +			} +			else +			{ +				$queries[] = $sql_create_index; +			} +		} +		$this->db->sql_freeresult($result); + +		return $queries; +	} +}  | 
