diff options
Diffstat (limited to 'phpBB/phpbb/db')
41 files changed, 3234 insertions, 69 deletions
| diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php index 4d78c84c8a..e497e6dda1 100644 --- a/phpBB/phpbb/db/driver/driver.php +++ b/phpBB/phpbb/db/driver/driver.php @@ -1097,7 +1097,7 @@ abstract class driver implements driver_interface  				{  					if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))  					{ -						$this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | '; +						$this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</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>';  				} diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php index f5c2dd225b..b7f6e60337 100644 --- a/phpBB/phpbb/db/driver/sqlite3.php +++ b/phpBB/phpbb/db/driver/sqlite3.php @@ -48,6 +48,7 @@ class sqlite3 extends \phpbb\db\driver\driver  		try  		{  			$this->dbo = new \SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); +			$this->dbo->busyTimeout(60000);  			$this->db_connect_id = true;  		}  		catch (\Exception $e) diff --git a/phpBB/phpbb/db/extractor/base_extractor.php b/phpBB/phpbb/db/extractor/base_extractor.php new file mode 100644 index 0000000000..547c85f066 --- /dev/null +++ b/phpBB/phpbb/db/extractor/base_extractor.php @@ -0,0 +1,252 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\invalid_format_exception; +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +/** + * Abstract base class for database extraction + */ +abstract class base_extractor implements extractor_interface +{ +	/** +	 * @var    string    phpBB root path +	 */ +	protected $phpbb_root_path; + +	/** +	 * @var    \phpbb\request\request_interface +	 */ +	protected $request; + +	/** +	 * @var    \phpbb\db\driver\driver_interface +	 */ +	protected $db; + +	/** +	 * @var    bool +	 */ +	protected $download; + +	/** +	 * @var    bool +	 */ +	protected $store; + +	/** +	 * @var    int +	 */ +	protected $time; + +	/** +	 * @var    string +	 */ +	protected $format; + +	/** +	 * @var    resource +	 */ +	protected $fp; + +	/** +	 * @var string +	 */ +	protected $write; + +	/** +	 * @var string +	 */ +	protected $close; + +	/** +	 * @var bool +	 */ +	protected $run_comp; + +	/** +	 * @var bool +	 */ +	protected $is_initialized; + +	/** +	 * Constructor +	 * +	 * @param string $phpbb_root_path +	 * @param \phpbb\request\request_interface $request +	 * @param \phpbb\db\driver\driver_interface $db +	 */ +	public function __construct($phpbb_root_path, \phpbb\request\request_interface $request, \phpbb\db\driver\driver_interface $db) +	{ +		$this->phpbb_root_path	= $phpbb_root_path; +		$this->request			= $request; +		$this->db				= $db; +		$this->fp				= null; + +		$this->is_initialized   = false; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function init_extractor($format, $filename, $time, $download = false, $store = false) +	{ +		$this->download			= $download; +		$this->store			= $store; +		$this->time				= $time; +		$this->format			= $format; + +		switch ($format) +		{ +			case 'text': +				$ext = '.sql'; +				$open = 'fopen'; +				$this->write = 'fwrite'; +				$this->close = 'fclose'; +				$mimetype = 'text/x-sql'; +			break; +			case 'bzip2': +				$ext = '.sql.bz2'; +				$open = 'bzopen'; +				$this->write = 'bzwrite'; +				$this->close = 'bzclose'; +				$mimetype = 'application/x-bzip2'; +			break; +			case 'gzip': +				$ext = '.sql.gz'; +				$open = 'gzopen'; +				$this->write = 'gzwrite'; +				$this->close = 'gzclose'; +				$mimetype = 'application/x-gzip'; +			break; +			default: +				throw new invalid_format_exception(); +			break; +		} + +		if ($download === true) +		{ +			$name = $filename . $ext; +			header('Cache-Control: private, no-cache'); +			header("Content-Type: $mimetype; name=\"$name\""); +			header("Content-disposition: attachment; filename=$name"); + +			switch ($format) +			{ +				case 'bzip2': +					ob_start(); +				break; + +				case 'gzip': +					if (strpos($this->request->header('Accept-Encoding'), 'gzip') !== false && strpos(strtolower($this->request->header('User-Agent')), 'msie') === false) +					{ +						ob_start('ob_gzhandler'); +					} +					else +					{ +						$this->run_comp = true; +					} +				break; +			} +		} + +		if ($store === true) +		{ +			$file = $this->phpbb_root_path . 'store/' . $filename . $ext; + +			$this->fp = $open($file, 'w'); + +			if (!$this->fp) +			{ +				trigger_error('FILE_WRITE_FAIL', E_USER_ERROR); +			} +		} + +		$this->is_initialized = true; +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_end() +	{ +		static $close; + +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		if ($this->store) +		{ +			if ($close === null) +			{ +				$close = $this->close; +			} +			$close($this->fp); +		} + +		// bzip2 must be written all the way at the end +		if ($this->download && $this->format === 'bzip2') +		{ +			$c = ob_get_clean(); +			echo bzcompress($c); +		} +	} + +	/** +	* {@inheritdoc} +	*/ +	public function flush($data) +	{ +		static $write; + +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		if ($this->store === true) +		{ +			if ($write === null) +			{ +				$write = $this->write; +			} +			$write($this->fp, $data); +		} + +		if ($this->download === true) +		{ +			if ($this->format === 'bzip2' || $this->format === 'text' || ($this->format === 'gzip' && !$this->run_comp)) +			{ +				echo $data; +			} + +			// we can write the gzip data as soon as we get it +			if ($this->format === 'gzip') +			{ +				if ($this->run_comp) +				{ +					echo gzencode($data); +				} +				else +				{ +					ob_flush(); +					flush(); +				} +			} +		} +	} +} diff --git a/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php b/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php new file mode 100644 index 0000000000..62eb434be1 --- /dev/null +++ b/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php @@ -0,0 +1,24 @@ +<?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\extractor\exception; + +use phpbb\exception\runtime_exception; + +/** +* This exception is thrown when invalid format is given to the extractor +*/ +class extractor_not_initialized_exception extends runtime_exception +{ + +} diff --git a/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php b/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php new file mode 100644 index 0000000000..6be24cb5dc --- /dev/null +++ b/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php @@ -0,0 +1,22 @@ +<?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\extractor\exception; + +/** +* This exception is thrown when invalid format is given to the extractor +*/ +class invalid_format_exception extends \InvalidArgumentException +{ + +} diff --git a/phpBB/phpbb/db/extractor/extractor_interface.php b/phpBB/phpbb/db/extractor/extractor_interface.php new file mode 100644 index 0000000000..ff45df9bb7 --- /dev/null +++ b/phpBB/phpbb/db/extractor/extractor_interface.php @@ -0,0 +1,80 @@ +<?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\extractor; + +/** +* Database extractor interface +*/ +interface extractor_interface +{ +	/** +	* Start the extraction of the database +	* +	* This function initialize the database extraction. It is required to call this +	* function before calling any other extractor functions. +	* +	* @param string	$format +	* @param string	$filename +	* @param int	$time +	* @param bool	$download +	* @param bool	$store +	* @return null +	* @throws \phpbb\db\extractor\exception\invalid_format_exception when $format is invalid +	*/ +	public function init_extractor($format, $filename, $time, $download = false, $store = false); + +	/** +	* Writes header comments to the database backup +	* +	* @param	string	$table_prefix	prefix of phpBB database tables +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_start($table_prefix); + +	/** +	* Closes file and/or dumps download data +	* +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_end(); + +	/** +	* Extracts database table structure +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_table($table_name); + +	/** +	* Extracts data from database table +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_data($table_name); + +	/** +	* Writes data to file/download content +	* +	* @param string	$data +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function flush($data); +} diff --git a/phpBB/phpbb/db/extractor/factory.php b/phpBB/phpbb/db/extractor/factory.php new file mode 100644 index 0000000000..a1ffb65595 --- /dev/null +++ b/phpBB/phpbb/db/extractor/factory.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\extractor; + +/** +* A factory which serves the suitable extractor instance for the given dbal +*/ +class factory +{ +	/** +	 * @var \phpbb\db\driver\driver_interface +	 */ +	protected $db; + +	/** +	 * @var \Symfony\Component\DependencyInjection\ContainerInterface +	 */ +	protected $container; + +	/** +	* Extractor factory constructor +	* +	* @param \phpbb\db\driver\driver_interface							$db +	* @param \Symfony\Component\DependencyInjection\ContainerInterface	$container +	*/ +	public function __construct(\phpbb\db\driver\driver_interface $db, \Symfony\Component\DependencyInjection\ContainerInterface $container) +	{ +		$this->db			= $db; +		$this->container	= $container; +	} + +	/** +	* DB extractor factory getter +	* +	* @return \phpbb\db\extractor\extractor_interface an appropriate instance of the database extractor for the used database driver +	* @throws \InvalidArgumentException when the database driver is unknown +	*/ +	public function get() +	{ +		// Return the appropriate DB extractor +		if ($this->db instanceof \phpbb\db\driver\mssql || $this->db instanceof \phpbb\db\driver\mssql_base) +		{ +			return $this->container->get('dbal.extractor.extractors.mssql_extractor'); +		} +		else if ($this->db instanceof \phpbb\db\driver\mysql_base) +		{ +			return $this->container->get('dbal.extractor.extractors.mysql_extractor'); +		} +		else if ($this->db instanceof \phpbb\db\driver\oracle) +		{ +			return $this->container->get('dbal.extractor.extractors.oracle_extractor'); +		} +		else if ($this->db instanceof \phpbb\db\driver\postgres) +		{ +			return $this->container->get('dbal.extractor.extractors.postgres_extractor'); +		} +		else if ($this->db instanceof \phpbb\db\driver\sqlite) +		{ +			return $this->container->get('dbal.extractor.extractors.sqlite_extractor'); +		} +		else if ($this->db instanceof \phpbb\db\driver\sqlite3) +		{ +			return $this->container->get('dbal.extractor.extractors.sqlite3_extractor'); +		} + +		throw new \InvalidArgumentException('Invalid database driver given'); +	} +} diff --git a/phpBB/phpbb/db/extractor/mssql_extractor.php b/phpBB/phpbb/db/extractor/mssql_extractor.php new file mode 100644 index 0000000000..fc30f4789d --- /dev/null +++ b/phpBB/phpbb/db/extractor/mssql_extractor.php @@ -0,0 +1,524 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class mssql_extractor extends base_extractor +{ +	/** +	* Writes closing line(s) to database backup +	* +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_end() +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$this->flush("COMMIT\nGO\n"); +		parent::write_end(); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "--\n"; +		$sql_data .= "-- phpBB Backup Script\n"; +		$sql_data .= "-- Dump of tables for $table_prefix\n"; +		$sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "--\n"; +		$sql_data .= "BEGIN TRANSACTION\n"; +		$sql_data .= "GO\n"; +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = '-- Table: ' . $table_name . "\n"; +		$sql_data .= "IF OBJECT_ID(N'$table_name', N'U') IS NOT NULL\n"; +		$sql_data .= "DROP TABLE $table_name;\n"; +		$sql_data .= "GO\n"; +		$sql_data .= "\nCREATE TABLE [$table_name] (\n"; +		$rows = array(); + +		$text_flag = false; + +		$sql = "SELECT COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as IS_IDENTITY +			FROM INFORMATION_SCHEMA.COLUMNS +			WHERE TABLE_NAME = '$table_name'"; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$line = "\t[{$row['COLUMN_NAME']}] [{$row['DATA_TYPE']}]"; + +			if ($row['DATA_TYPE'] == 'text') +			{ +				$text_flag = true; +			} + +			if ($row['IS_IDENTITY']) +			{ +				$line .= ' IDENTITY (1 , 1)'; +			} + +			if ($row['CHARACTER_MAXIMUM_LENGTH'] && $row['DATA_TYPE'] !== 'text') +			{ +				$line .= ' (' . $row['CHARACTER_MAXIMUM_LENGTH'] . ')'; +			} + +			if ($row['IS_NULLABLE'] == 'YES') +			{ +				$line .= ' NULL'; +			} +			else +			{ +				$line .= ' NOT NULL'; +			} + +			if ($row['COLUMN_DEFAULT']) +			{ +				$line .= ' DEFAULT ' . $row['COLUMN_DEFAULT']; +			} + +			$rows[] = $line; +		} +		$this->db->sql_freeresult($result); + +		$sql_data .= implode(",\n", $rows); +		$sql_data .= "\n) ON [PRIMARY]"; + +		if ($text_flag) +		{ +			$sql_data .= " TEXTIMAGE_ON [PRIMARY]"; +		} + +		$sql_data .= "\nGO\n\n"; +		$rows = array(); + +		$sql = "SELECT CONSTRAINT_NAME, COLUMN_NAME +			FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE +			WHERE TABLE_NAME = '$table_name'"; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (!sizeof($rows)) +			{ +				$sql_data .= "ALTER TABLE [$table_name] WITH NOCHECK ADD\n"; +				$sql_data .= "\tCONSTRAINT [{$row['CONSTRAINT_NAME']}] PRIMARY KEY  CLUSTERED \n\t(\n"; +			} +			$rows[] = "\t\t[{$row['COLUMN_NAME']}]"; +		} +		if (sizeof($rows)) +		{ +			$sql_data .= implode(",\n", $rows); +			$sql_data .= "\n\t)  ON [PRIMARY] \nGO\n"; +		} +		$this->db->sql_freeresult($result); + +		$index = array(); +		$sql = "EXEC sp_statistics '$table_name'"; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if ($row['TYPE'] == 3) +			{ +				$index[$row['INDEX_NAME']][] = '[' . $row['COLUMN_NAME'] . ']'; +			} +		} +		$this->db->sql_freeresult($result); + +		foreach ($index as $index_name => $column_name) +		{ +			$index[$index_name] = implode(', ', $column_name); +		} + +		foreach ($index as $index_name => $columns) +		{ +			$sql_data .= "\nCREATE  INDEX [$index_name] ON [$table_name]($columns) ON [PRIMARY]\nGO\n"; +		} +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		if ($this->db->get_sql_layer() === 'mssql') +		{ +			$this->write_data_mssql($table_name); +		} +		else if ($this->db->get_sql_layer() === 'mssqlnative') +		{ +			$this->write_data_mssqlnative($table_name); +		} +		else +		{ +			$this->write_data_odbc($table_name); +		} +	} + +	/** +	* Extracts data from database table (for MSSQL driver) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function write_data_mssql($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$ary_type = $ary_name = array(); +		$ident_set = false; +		$sql_data = ''; + +		// Grab all of the data from current table. +		$sql = "SELECT * +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		$retrieved_data = mssql_num_rows($result); + +		$i_num_fields = mssql_num_fields($result); + +		for ($i = 0; $i < $i_num_fields; $i++) +		{ +			$ary_type[$i] = mssql_field_type($result, $i); +			$ary_name[$i] = mssql_field_name($result, $i); +		} + +		if ($retrieved_data) +		{ +			$sql = "SELECT 1 as has_identity +				FROM INFORMATION_SCHEMA.COLUMNS +				WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1"; +			$result2 = $this->db->sql_query($sql); +			$row2 = $this->db->sql_fetchrow($result2); +			if (!empty($row2['has_identity'])) +			{ +				$sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n"; +				$ident_set = true; +			} +			$this->db->sql_freeresult($result2); +		} + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$schema_vals = $schema_fields = array(); + +			// Build the SQL statement to recreate the data. +			for ($i = 0; $i < $i_num_fields; $i++) +			{ +				$str_val = $row[$ary_name[$i]]; + +				if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i])) +				{ +					$str_quote = ''; +					$str_empty = "''"; +					$str_val = sanitize_data_mssql(str_replace("'", "''", $str_val)); +				} +				else if (preg_match('#date|timestamp#i', $ary_type[$i])) +				{ +					if (empty($str_val)) +					{ +						$str_quote = ''; +					} +					else +					{ +						$str_quote = "'"; +					} +				} +				else +				{ +					$str_quote = ''; +					$str_empty = 'NULL'; +				} + +				if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val))) +				{ +					$str_val = $str_empty; +				} + +				$schema_vals[$i] = $str_quote . $str_val . $str_quote; +				$schema_fields[$i] = $ary_name[$i]; +			} + +			// Take the ordered fields and their associated data and build it +			// into a valid sql statement to recreate that field in the data. +			$sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n"; + +			$this->flush($sql_data); +			$sql_data = ''; +		} +		$this->db->sql_freeresult($result); + +		if ($retrieved_data && $ident_set) +		{ +			$sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n"; +		} +		$this->flush($sql_data); +	} + +	/** +	* Extracts data from database table (for MSSQL Native driver) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function write_data_mssqlnative($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$ary_type = $ary_name = array(); +		$ident_set = false; +		$sql_data = ''; + +		// Grab all of the data from current table. +		$sql = "SELECT * FROM $table_name"; +		$this->db->mssqlnative_set_query_options(array('Scrollable' => SQLSRV_CURSOR_STATIC)); +		$result = $this->db->sql_query($sql); + +		$retrieved_data = $this->db->mssqlnative_num_rows($result); + +		if (!$retrieved_data) +		{ +			$this->db->sql_freeresult($result); +			return; +		} + +		$sql = "SELECT COLUMN_NAME, DATA_TYPE +			FROM INFORMATION_SCHEMA.COLUMNS +			WHERE INFORMATION_SCHEMA.COLUMNS.TABLE_NAME = '" . $this->db->sql_escape($table_name) . "'"; +		$result_fields = $this->db->sql_query($sql); + +		$i_num_fields = 0; +		while ($row = $this->db->sql_fetchrow($result_fields)) +		{ +			$ary_type[$i_num_fields] = $row['DATA_TYPE']; +			$ary_name[$i_num_fields] = $row['COLUMN_NAME']; +			$i_num_fields++; +		} +		$this->db->sql_freeresult($result_fields); + +		$sql = "SELECT 1 as has_identity +			FROM INFORMATION_SCHEMA.COLUMNS +			WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1"; +		$result2 = $this->db->sql_query($sql); +		$row2 = $this->db->sql_fetchrow($result2); + +		if (!empty($row2['has_identity'])) +		{ +			$sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n"; +			$ident_set = true; +		} +		$this->db->sql_freeresult($result2); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$schema_vals = $schema_fields = array(); + +			// Build the SQL statement to recreate the data. +			for ($i = 0; $i < $i_num_fields; $i++) +			{ +				$str_val = $row[$ary_name[$i]]; + +				// defaults to type number - better quote just to be safe, so check for is_int too +				if (is_int($ary_type[$i]) || preg_match('#char|text|bool|varbinary#i', $ary_type[$i])) +				{ +					$str_quote = ''; +					$str_empty = "''"; +					$str_val = sanitize_data_mssql(str_replace("'", "''", $str_val)); +				} +				else if (preg_match('#date|timestamp#i', $ary_type[$i])) +				{ +					if (empty($str_val)) +					{ +						$str_quote = ''; +					} +					else +					{ +						$str_quote = "'"; +					} +				} +				else +				{ +					$str_quote = ''; +					$str_empty = 'NULL'; +				} + +				if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val))) +				{ +					$str_val = $str_empty; +				} + +				$schema_vals[$i] = $str_quote . $str_val . $str_quote; +				$schema_fields[$i] = $ary_name[$i]; +			} + +			// Take the ordered fields and their associated data and build it +			// into a valid sql statement to recreate that field in the data. +			$sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n"; + +			$this->flush($sql_data); +			$sql_data = ''; +		} +		$this->db->sql_freeresult($result); + +		if ($ident_set) +		{ +			$sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n"; +		} +		$this->flush($sql_data); +	} + +	/** +	* Extracts data from database table (for ODBC driver) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function write_data_odbc($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$ary_type = $ary_name = array(); +		$ident_set = false; +		$sql_data = ''; + +		// Grab all of the data from current table. +		$sql = "SELECT * +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		$retrieved_data = odbc_num_rows($result); + +		if ($retrieved_data) +		{ +			$sql = "SELECT 1 as has_identity +				FROM INFORMATION_SCHEMA.COLUMNS +				WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1"; +			$result2 = $this->db->sql_query($sql); +			$row2 = $this->db->sql_fetchrow($result2); +			if (!empty($row2['has_identity'])) +			{ +				$sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n"; +				$ident_set = true; +			} +			$this->db->sql_freeresult($result2); +		} + +		$i_num_fields = odbc_num_fields($result); + +		for ($i = 0; $i < $i_num_fields; $i++) +		{ +			$ary_type[$i] = odbc_field_type($result, $i + 1); +			$ary_name[$i] = odbc_field_name($result, $i + 1); +		} + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$schema_vals = $schema_fields = array(); + +			// Build the SQL statement to recreate the data. +			for ($i = 0; $i < $i_num_fields; $i++) +			{ +				$str_val = $row[$ary_name[$i]]; + +				if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i])) +				{ +					$str_quote = ''; +					$str_empty = "''"; +					$str_val = sanitize_data_mssql(str_replace("'", "''", $str_val)); +				} +				else if (preg_match('#date|timestamp#i', $ary_type[$i])) +				{ +					if (empty($str_val)) +					{ +						$str_quote = ''; +					} +					else +					{ +						$str_quote = "'"; +					} +				} +				else +				{ +					$str_quote = ''; +					$str_empty = 'NULL'; +				} + +				if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val))) +				{ +					$str_val = $str_empty; +				} + +				$schema_vals[$i] = $str_quote . $str_val . $str_quote; +				$schema_fields[$i] = $ary_name[$i]; +			} + +			// Take the ordered fields and their associated data and build it +			// into a valid sql statement to recreate that field in the data. +			$sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n"; + +			$this->flush($sql_data); + +			$sql_data = ''; + +		} +		$this->db->sql_freeresult($result); + +		if ($retrieved_data && $ident_set) +		{ +			$sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n"; +		} +		$this->flush($sql_data); +	} +} diff --git a/phpBB/phpbb/db/extractor/mysql_extractor.php b/phpBB/phpbb/db/extractor/mysql_extractor.php new file mode 100644 index 0000000000..34e309c19e --- /dev/null +++ b/phpBB/phpbb/db/extractor/mysql_extractor.php @@ -0,0 +1,403 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class mysql_extractor extends base_extractor +{ +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "#\n"; +		$sql_data .= "# phpBB Backup Script\n"; +		$sql_data .= "# Dump of tables for $table_prefix\n"; +		$sql_data .= "# DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "#\n"; +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		static $new_extract; + +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		if ($new_extract === null) +		{ +			if ($this->db->get_sql_layer() === 'mysqli' || version_compare($this->db->sql_server_info(true), '3.23.20', '>=')) +			{ +				$new_extract = true; +			} +			else +			{ +				$new_extract = false; +			} +		} + +		if ($new_extract) +		{ +			$this->new_write_table($table_name); +		} +		else +		{ +			$this->old_write_table($table_name); +		} +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		if ($this->db->get_sql_layer() === 'mysqli') +		{ +			$this->write_data_mysqli($table_name); +		} +		else +		{ +			$this->write_data_mysql($table_name); +		} +	} + +	/** +	* Extracts data from database table (for MySQLi driver) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function write_data_mysqli($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql = "SELECT * +			FROM $table_name"; +		$result = mysqli_query($this->db->get_db_connect_id(), $sql, MYSQLI_USE_RESULT); +		if ($result != false) +		{ +			$fields_cnt = mysqli_num_fields($result); + +			// Get field information +			$field = mysqli_fetch_fields($result); +			$field_set = array(); + +			for ($j = 0; $j < $fields_cnt; $j++) +			{ +				$field_set[] = $field[$j]->name; +			} + +			$search			= array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"'); +			$replace		= array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"'); +			$fields			= implode(', ', $field_set); +			$sql_data		= 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES '; +			$first_set		= true; +			$query_len		= 0; +			$max_len		= get_usable_memory(); + +			while ($row = mysqli_fetch_row($result)) +			{ +				$values	= array(); +				if ($first_set) +				{ +					$query = $sql_data . '('; +				} +				else +				{ +					$query  .= ',('; +				} + +				for ($j = 0; $j < $fields_cnt; $j++) +				{ +					if (!isset($row[$j]) || is_null($row[$j])) +					{ +						$values[$j] = 'NULL'; +					} +					else if (($field[$j]->flags & 32768) && !($field[$j]->flags & 1024)) +					{ +						$values[$j] = $row[$j]; +					} +					else +					{ +						$values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'"; +					} +				} +				$query .= implode(', ', $values) . ')'; + +				$query_len += strlen($query); +				if ($query_len > $max_len) +				{ +					$this->flush($query . ";\n\n"); +					$query = ''; +					$query_len = 0; +					$first_set = true; +				} +				else +				{ +					$first_set = false; +				} +			} +			mysqli_free_result($result); + +			// check to make sure we have nothing left to flush +			if (!$first_set && $query) +			{ +				$this->flush($query . ";\n\n"); +			} +		} +	} + +	/** +	* Extracts data from database table (for MySQL driver) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function write_data_mysql($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql = "SELECT * +			FROM $table_name"; +		$result = mysql_unbuffered_query($sql, $this->db->get_db_connect_id()); + +		if ($result != false) +		{ +			$fields_cnt = mysql_num_fields($result); + +			// Get field information +			$field = array(); +			for ($i = 0; $i < $fields_cnt; $i++) +			{ +				$field[] = mysql_fetch_field($result, $i); +			} +			$field_set = array(); + +			for ($j = 0; $j < $fields_cnt; $j++) +			{ +				$field_set[] = $field[$j]->name; +			} + +			$search			= array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"'); +			$replace		= array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"'); +			$fields			= implode(', ', $field_set); +			$sql_data		= 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES '; +			$first_set		= true; +			$query_len		= 0; +			$max_len		= get_usable_memory(); + +			while ($row = mysql_fetch_row($result)) +			{ +				$values = array(); +				if ($first_set) +				{ +					$query = $sql_data . '('; +				} +				else +				{ +					$query  .= ',('; +				} + +				for ($j = 0; $j < $fields_cnt; $j++) +				{ +					if (!isset($row[$j]) || is_null($row[$j])) +					{ +						$values[$j] = 'NULL'; +					} +					else if ($field[$j]->numeric && ($field[$j]->type !== 'timestamp')) +					{ +						$values[$j] = $row[$j]; +					} +					else +					{ +						$values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'"; +					} +				} +				$query .= implode(', ', $values) . ')'; + +				$query_len += strlen($query); +				if ($query_len > $max_len) +				{ +					$this->flush($query . ";\n\n"); +					$query = ''; +					$query_len = 0; +					$first_set = true; +				} +				else +				{ +					$first_set = false; +				} +			} +			mysql_free_result($result); + +			// check to make sure we have nothing left to flush +			if (!$first_set && $query) +			{ +				$this->flush($query . ";\n\n"); +			} +		} +	} + +	/** +	* Extracts database table structure (for MySQLi or MySQL 3.23.20+) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function new_write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql = 'SHOW CREATE TABLE ' . $table_name; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); + +		$sql_data = '# Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE IF EXISTS $table_name;\n"; +		$this->flush($sql_data . $row['Create Table'] . ";\n\n"); + +		$this->db->sql_freeresult($result); +	} + +	/** +	* Extracts database table structure (for MySQL verisons older than 3.23.20) +	* +	* @param	string	$table_name	name of the database table +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	protected function old_write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = '# Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE IF EXISTS $table_name;\n"; +		$sql_data .= "CREATE TABLE $table_name(\n"; +		$rows = array(); + +		$sql = "SHOW FIELDS +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$line = '   ' . $row['Field'] . ' ' . $row['Type']; + +			if (!is_null($row['Default'])) +			{ +				$line .= " DEFAULT '{$row['Default']}'"; +			} + +			if ($row['Null'] != 'YES') +			{ +				$line .= ' NOT NULL'; +			} + +			if ($row['Extra'] != '') +			{ +				$line .= ' ' . $row['Extra']; +			} + +			$rows[] = $line; +		} +		$this->db->sql_freeresult($result); + +		$sql = "SHOW KEYS +			FROM $table_name"; + +		$result = $this->db->sql_query($sql); + +		$index = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$kname = $row['Key_name']; + +			if ($kname != 'PRIMARY') +			{ +				if ($row['Non_unique'] == 0) +				{ +					$kname = "UNIQUE|$kname"; +				} +			} + +			if ($row['Sub_part']) +			{ +				$row['Column_name'] .= '(' . $row['Sub_part'] . ')'; +			} +			$index[$kname][] = $row['Column_name']; +		} +		$this->db->sql_freeresult($result); + +		foreach ($index as $key => $columns) +		{ +			$line = '   '; + +			if ($key == 'PRIMARY') +			{ +				$line .= 'PRIMARY KEY (' . implode(', ', $columns) . ')'; +			} +			else if (strpos($key, 'UNIQUE') === 0) +			{ +				$line .= 'UNIQUE ' . substr($key, 7) . ' (' . implode(', ', $columns) . ')'; +			} +			else if (strpos($key, 'FULLTEXT') === 0) +			{ +				$line .= 'FULLTEXT ' . substr($key, 9) . ' (' . implode(', ', $columns) . ')'; +			} +			else +			{ +				$line .= "KEY $key (" . implode(', ', $columns) . ')'; +			} + +			$rows[] = $line; +		} + +		$sql_data .= implode(",\n", $rows); +		$sql_data .= "\n);\n\n"; + +		$this->flush($sql_data); +	} +} diff --git a/phpBB/phpbb/db/extractor/oracle_extractor.php b/phpBB/phpbb/db/extractor/oracle_extractor.php new file mode 100644 index 0000000000..05f7b8ac95 --- /dev/null +++ b/phpBB/phpbb/db/extractor/oracle_extractor.php @@ -0,0 +1,265 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class oracle_extractor extends base_extractor +{ +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = '-- Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE $table_name\n/\n"; +		$sql_data .= "\nCREATE TABLE $table_name (\n"; + +		$sql = "SELECT COLUMN_NAME, DATA_TYPE, DATA_PRECISION, DATA_LENGTH, NULLABLE, DATA_DEFAULT +			FROM ALL_TAB_COLS +			WHERE table_name = '{$table_name}'"; +		$result = $this->db->sql_query($sql); + +		$rows = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$line = '  "' . $row['column_name'] . '" ' . $row['data_type']; + +			if ($row['data_type'] !== 'CLOB') +			{ +				if ($row['data_type'] !== 'VARCHAR2' && $row['data_type'] !== 'CHAR') +				{ +					$line .= '(' . $row['data_precision'] . ')'; +				} +				else +				{ +					$line .= '(' . $row['data_length'] . ')'; +				} +			} + +			if (!empty($row['data_default'])) +			{ +				$line .= ' DEFAULT ' . $row['data_default']; +			} + +			if ($row['nullable'] == 'N') +			{ +				$line .= ' NOT NULL'; +			} +			$rows[] = $line; +		} +		$this->db->sql_freeresult($result); + +		$sql = "SELECT A.CONSTRAINT_NAME, A.COLUMN_NAME +			FROM USER_CONS_COLUMNS A, USER_CONSTRAINTS B +			WHERE A.CONSTRAINT_NAME = B.CONSTRAINT_NAME +				AND B.CONSTRAINT_TYPE = 'P' +				AND A.TABLE_NAME = '{$table_name}'"; +		$result = $this->db->sql_query($sql); + +		$primary_key = array(); +		$constraint_name = ''; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$constraint_name = '"' . $row['constraint_name'] . '"'; +			$primary_key[] = '"' . $row['column_name'] . '"'; +		} +		$this->db->sql_freeresult($result); + +		if (sizeof($primary_key)) +		{ +			$rows[] = "  CONSTRAINT {$constraint_name} PRIMARY KEY (" . implode(', ', $primary_key) . ')'; +		} + +		$sql = "SELECT A.CONSTRAINT_NAME, A.COLUMN_NAME +			FROM USER_CONS_COLUMNS A, USER_CONSTRAINTS B +			WHERE A.CONSTRAINT_NAME = B.CONSTRAINT_NAME +				AND B.CONSTRAINT_TYPE = 'U' +				AND A.TABLE_NAME = '{$table_name}'"; +		$result = $this->db->sql_query($sql); + +		$unique = array(); +		$constraint_name = ''; +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$constraint_name = '"' . $row['constraint_name'] . '"'; +			$unique[] = '"' . $row['column_name'] . '"'; +		} +		$this->db->sql_freeresult($result); + +		if (sizeof($unique)) +		{ +			$rows[] = "  CONSTRAINT {$constraint_name} UNIQUE (" . implode(', ', $unique) . ')'; +		} + +		$sql_data .= implode(",\n", $rows); +		$sql_data .= "\n)\n/\n"; + +		$sql = "SELECT A.REFERENCED_NAME, C.* +			FROM USER_DEPENDENCIES A, USER_TRIGGERS B, USER_SEQUENCES C +			WHERE A.REFERENCED_TYPE = 'SEQUENCE' +				AND A.NAME = B.TRIGGER_NAME +				AND B.TABLE_NAME = '{$table_name}' +				AND C.SEQUENCE_NAME = A.REFERENCED_NAME"; +		$result = $this->db->sql_query($sql); + +		$type = $this->request->variable('type', ''); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_data .= "\nDROP SEQUENCE \"{$row['referenced_name']}\"\n/\n"; +			$sql_data .= "\nCREATE SEQUENCE \"{$row['referenced_name']}\""; + +			if ($type == 'full') +			{ +				$sql_data .= ' START WITH ' . $row['last_number']; +			} + +			$sql_data .= "\n/\n"; +		} +		$this->db->sql_freeresult($result); + +		$sql = "SELECT DESCRIPTION, WHEN_CLAUSE, TRIGGER_BODY +			FROM USER_TRIGGERS +			WHERE TABLE_NAME = '{$table_name}'"; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$sql_data .= "\nCREATE OR REPLACE TRIGGER {$row['description']}WHEN ({$row['when_clause']})\n{$row['trigger_body']}\n/\n"; +		} +		$this->db->sql_freeresult($result); + +		$sql = "SELECT A.INDEX_NAME, B.COLUMN_NAME +			FROM USER_INDEXES A, USER_IND_COLUMNS B +			WHERE A.UNIQUENESS = 'NONUNIQUE' +				AND A.INDEX_NAME = B.INDEX_NAME +				AND B.TABLE_NAME = '{$table_name}'"; +		$result = $this->db->sql_query($sql); + +		$index = array(); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$index[$row['index_name']][] = $row['column_name']; +		} + +		foreach ($index as $index_name => $column_names) +		{ +			$sql_data .= "\nCREATE INDEX $index_name ON $table_name(" . implode(', ', $column_names) . ")\n/\n"; +		} +		$this->db->sql_freeresult($result); +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$ary_type = $ary_name = array(); + +		// Grab all of the data from current table. +		$sql = "SELECT * +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		$i_num_fields = ocinumcols($result); + +		for ($i = 0; $i < $i_num_fields; $i++) +		{ +			$ary_type[$i] = ocicolumntype($result, $i + 1); +			$ary_name[$i] = ocicolumnname($result, $i + 1); +		} + +		$sql_data = ''; + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$schema_vals = $schema_fields = array(); + +			// Build the SQL statement to recreate the data. +			for ($i = 0; $i < $i_num_fields; $i++) +			{ +				// Oracle uses uppercase - we use lowercase +				$str_val = $row[strtolower($ary_name[$i])]; + +				if (preg_match('#char|text|bool|raw|clob#i', $ary_type[$i])) +				{ +					$str_quote = ''; +					$str_empty = "''"; +					$str_val = sanitize_data_oracle($str_val); +				} +				else if (preg_match('#date|timestamp#i', $ary_type[$i])) +				{ +					if (empty($str_val)) +					{ +						$str_quote = ''; +					} +					else +					{ +						$str_quote = "'"; +					} +				} +				else +				{ +					$str_quote = ''; +					$str_empty = 'NULL'; +				} + +				if (empty($str_val) && $str_val !== '0') +				{ +					$str_val = $str_empty; +				} + +				$schema_vals[$i] = $str_quote . $str_val . $str_quote; +				$schema_fields[$i] = '"' . $ary_name[$i] . '"'; +			} + +			// Take the ordered fields and their associated data and build it +			// into a valid sql statement to recreate that field in the data. +			$sql_data = "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ")\n/\n"; + +			$this->flush($sql_data); +		} +		$this->db->sql_freeresult($result); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "--\n"; +		$sql_data .= "-- phpBB Backup Script\n"; +		$sql_data .= "-- Dump of tables for $table_prefix\n"; +		$sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "--\n"; +		$this->flush($sql_data); +	} +} diff --git a/phpBB/phpbb/db/extractor/postgres_extractor.php b/phpBB/phpbb/db/extractor/postgres_extractor.php new file mode 100644 index 0000000000..a98e39621c --- /dev/null +++ b/phpBB/phpbb/db/extractor/postgres_extractor.php @@ -0,0 +1,339 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class postgres_extractor extends base_extractor +{ +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "--\n"; +		$sql_data .= "-- phpBB Backup Script\n"; +		$sql_data .= "-- Dump of tables for $table_prefix\n"; +		$sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "--\n"; +		$sql_data .= "BEGIN TRANSACTION;\n"; +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		static $domains_created = array(); + +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql = "SELECT a.domain_name, a.data_type, a.character_maximum_length, a.domain_default +			FROM INFORMATION_SCHEMA.domains a, INFORMATION_SCHEMA.column_domain_usage b +			WHERE a.domain_name = b.domain_name +				AND b.table_name = '{$table_name}'"; +		$result = $this->db->sql_query($sql); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (empty($domains_created[$row['domain_name']])) +			{ +				$domains_created[$row['domain_name']] = true; +				//$sql_data = "DROP DOMAIN {$row['domain_name']};\n"; +				$sql_data = "CREATE DOMAIN {$row['domain_name']} as {$row['data_type']}"; +				if (!empty($row['character_maximum_length'])) +				{ +					$sql_data .= '(' . $row['character_maximum_length'] . ')'; +				} +				$sql_data .= ' NOT NULL'; +				if (!empty($row['domain_default'])) +				{ +					$sql_data .= ' DEFAULT ' . $row['domain_default']; +				} +				$this->flush($sql_data . ";\n"); +			} +		} +		$this->db->sql_freeresult($result); + +		$sql_data = '-- Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE $table_name;\n"; +		// 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)) +		{ +			$sql_data .= "DROP SEQUENCE {$table_name}_seq;\n"; +			$sql_data .= "CREATE SEQUENCE {$table_name}_seq;\n"; +		} +		$this->db->sql_freeresult($result); + +		$field_query = "SELECT a.attnum, a.attname as field, t.typname as type, a.attlen as length, a.atttypmod as lengthvar, a.attnotnull as notnull +			FROM pg_class c, pg_attribute a, pg_type t +			WHERE c.relname = '" . $this->db->sql_escape($table_name) . "' +				AND a.attnum > 0 +				AND a.attrelid = c.oid +				AND a.atttypid = t.oid +			ORDER BY a.attnum"; +		$result = $this->db->sql_query($field_query); + +		$sql_data .= "CREATE TABLE $table_name(\n"; +		$lines = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			// Get the data from the table +			$sql_get_default = "SELECT pg_get_expr(d.adbin, d.adrelid) as rowdefault +				FROM pg_attrdef d, pg_class c +				WHERE (c.relname = '" . $this->db->sql_escape($table_name) . "') +					AND (c.oid = d.adrelid) +					AND d.adnum = " . $row['attnum']; +			$def_res = $this->db->sql_query($sql_get_default); +			$def_row = $this->db->sql_fetchrow($def_res); +			$this->db->sql_freeresult($def_res); + +			if (empty($def_row)) +			{ +				unset($row['rowdefault']); +			} +			else +			{ +				$row['rowdefault'] = $def_row['rowdefault']; +			} + +			if ($row['type'] == 'bpchar') +			{ +				// Internally stored as bpchar, but isn't accepted in a CREATE TABLE statement. +				$row['type'] = 'char'; +			} + +			$line = '  ' . $row['field'] . ' ' . $row['type']; + +			if (strpos($row['type'], 'char') !== false) +			{ +				if ($row['lengthvar'] > 0) +				{ +					$line .= '(' . ($row['lengthvar'] - 4) . ')'; +				} +			} + +			if (strpos($row['type'], 'numeric') !== false) +			{ +				$line .= '('; +				$line .= sprintf("%s,%s", (($row['lengthvar'] >> 16) & 0xffff), (($row['lengthvar'] - 4) & 0xffff)); +				$line .= ')'; +			} + +			if (isset($row['rowdefault'])) +			{ +				$line .= ' DEFAULT ' . $row['rowdefault']; +			} + +			if ($row['notnull'] == 't') +			{ +				$line .= ' NOT NULL'; +			} + +			$lines[] = $line; +		} +		$this->db->sql_freeresult($result); + +		// Get the listing of primary keys. +		$sql_pri_keys = "SELECT ic.relname as index_name, bc.relname as tab_name, ta.attname as column_name, i.indisunique as unique_key, i.indisprimary as primary_key +			FROM pg_class bc, pg_class ic, pg_index i, pg_attribute ta, pg_attribute ia +			WHERE (bc.oid = i.indrelid) +				AND (ic.oid = i.indexrelid) +				AND (ia.attrelid = i.indexrelid) +				AND	(ta.attrelid = bc.oid) +				AND (bc.relname = '" . $this->db->sql_escape($table_name) . "') +				AND (ta.attrelid = i.indrelid) +				AND (ta.attnum = i.indkey[ia.attnum-1]) +			ORDER BY index_name, tab_name, column_name"; + +		$result = $this->db->sql_query($sql_pri_keys); + +		$index_create = $index_rows = $primary_key = array(); + +		// We do this in two steps. It makes placing the comma easier +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if ($row['primary_key'] == 't') +			{ +				$primary_key[] = $row['column_name']; +				$primary_key_name = $row['index_name']; +			} +			else +			{ +				// We have to store this all this info because it is possible to have a multi-column key... +				// we can loop through it again and build the statement +				$index_rows[$row['index_name']]['table'] = $table_name; +				$index_rows[$row['index_name']]['unique'] = ($row['unique_key'] == 't') ? true : false; +				$index_rows[$row['index_name']]['column_names'][] = $row['column_name']; +			} +		} +		$this->db->sql_freeresult($result); + +		if (!empty($index_rows)) +		{ +			foreach ($index_rows as $idx_name => $props) +			{ +				$index_create[] = 'CREATE ' . ($props['unique'] ? 'UNIQUE ' : '') . "INDEX $idx_name ON $table_name (" . implode(', ', $props['column_names']) . ");"; +			} +		} + +		if (!empty($primary_key)) +		{ +			$lines[] = "  CONSTRAINT $primary_key_name PRIMARY KEY (" . implode(', ', $primary_key) . ")"; +		} + +		// Generate constraint clauses for CHECK constraints +		$sql_checks = "SELECT conname as index_name, consrc +			FROM pg_constraint, pg_class bc +			WHERE conrelid = bc.oid +				AND bc.relname = '" . $this->db->sql_escape($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 +				)"; +		$result = $this->db->sql_query($sql_checks); + +		// Add the constraints to the sql file. +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (!is_null($row['consrc'])) +			{ +				$lines[] = '  CONSTRAINT ' . $row['index_name'] . ' CHECK ' . $row['consrc']; +			} +		} +		$this->db->sql_freeresult($result); + +		$sql_data .= implode(", \n", $lines); +		$sql_data .= "\n);\n"; + +		if (!empty($index_create)) +		{ +			$sql_data .= implode("\n", $index_create) . "\n\n"; +		} +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		// Grab all of the data from current table. +		$sql = "SELECT * +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		$i_num_fields = pg_num_fields($result); +		$seq = ''; + +		for ($i = 0; $i < $i_num_fields; $i++) +		{ +			$ary_type[] = pg_field_type($result, $i); +			$ary_name[] = pg_field_name($result, $i); + +			$sql = "SELECT pg_get_expr(d.adbin, d.adrelid) as rowdefault +				FROM pg_attrdef d, pg_class c +				WHERE (c.relname = '{$table_name}') +					AND (c.oid = d.adrelid) +					AND d.adnum = " . strval($i + 1); +			$result2 = $this->db->sql_query($sql); +			if ($row = $this->db->sql_fetchrow($result2)) +			{ +				// Determine if we must reset the sequences +				if (strpos($row['rowdefault'], "nextval('") === 0) +				{ +					$seq .= "SELECT SETVAL('{$table_name}_seq',(select case when max({$ary_name[$i]})>0 then max({$ary_name[$i]})+1 else 1 end FROM {$table_name}));\n"; +				} +			} +		} + +		$this->flush("COPY $table_name (" . implode(', ', $ary_name) . ') FROM stdin;' . "\n"); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$schema_vals = array(); + +			// Build the SQL statement to recreate the data. +			for ($i = 0; $i < $i_num_fields; $i++) +			{ +				$str_val = $row[$ary_name[$i]]; + +				if (preg_match('#char|text|bool|bytea#i', $ary_type[$i])) +				{ +					$str_val = str_replace(array("\n", "\t", "\r", "\b", "\f", "\v"), array('\n', '\t', '\r', '\b', '\f', '\v'), addslashes($str_val)); +					$str_empty = ''; +				} +				else +				{ +					$str_empty = '\N'; +				} + +				if (empty($str_val) && $str_val !== '0') +				{ +					$str_val = $str_empty; +				} + +				$schema_vals[] = $str_val; +			} + +			// Take the ordered fields and their associated data and build it +			// into a valid sql statement to recreate that field in the data. +			$this->flush(implode("\t", $schema_vals) . "\n"); +		} +		$this->db->sql_freeresult($result); +		$this->flush("\\.\n"); + +		// Write out the sequence statements +		$this->flush($seq); +	} + +	/** +	* Writes closing line(s) to database backup +	* +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_end() +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$this->flush("COMMIT;\n"); +		parent::write_end(); +	} +} diff --git a/phpBB/phpbb/db/extractor/sqlite3_extractor.php b/phpBB/phpbb/db/extractor/sqlite3_extractor.php new file mode 100644 index 0000000000..ce8da6a652 --- /dev/null +++ b/phpBB/phpbb/db/extractor/sqlite3_extractor.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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class sqlite3_extractor extends base_extractor +{ +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "--\n"; +		$sql_data .= "-- phpBB Backup Script\n"; +		$sql_data .= "-- Dump of tables for $table_prefix\n"; +		$sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "--\n"; +		$sql_data .= "BEGIN TRANSACTION;\n"; +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = '-- Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE $table_name;\n"; + +		$sql = "SELECT sql +			FROM sqlite_master +			WHERE type = 'table' +				AND name = '" . $this->db->sql_escape($table_name) . "' +			ORDER BY name ASC;"; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		// Create Table +		$sql_data .= $row['sql'] . ";\n"; + +		$result = $this->db->sql_query("PRAGMA index_list('" . $this->db->sql_escape($table_name) . "');"); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			if (strpos($row['name'], 'autoindex') !== false) +			{ +				continue; +			} + +			$result2 = $this->db->sql_query("PRAGMA index_info('" . $this->db->sql_escape($row['name']) . "');"); + +			$fields = array(); +			while ($row2 = $this->db->sql_fetchrow($result2)) +			{ +				$fields[] = $row2['name']; +			} +			$this->db->sql_freeresult($result2); + +			$sql_data .= 'CREATE ' . ($row['unique'] ? 'UNIQUE ' : '') . 'INDEX ' . $row['name'] . ' ON ' . $table_name . ' (' . implode(', ', $fields) . ");\n"; +		} +		$this->db->sql_freeresult($result); + +		$this->flush($sql_data . "\n"); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$result = $this->db->sql_query("PRAGMA table_info('" . $this->db->sql_escape($table_name) . "');"); + +		$col_types = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$col_types[$row['name']] = $row['type']; +		} +		$this->db->sql_freeresult($result); + +		$sql_insert = 'INSERT INTO ' . $table_name . ' (' . implode(', ', array_keys($col_types)) . ') VALUES ('; + +		$sql = "SELECT * +			FROM $table_name"; +		$result = $this->db->sql_query($sql); + +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			foreach ($row as $column_name => $column_data) +			{ +				if (is_null($column_data)) +				{ +					$row[$column_name] = 'NULL'; +				} +				else if ($column_data === '') +				{ +					$row[$column_name] = "''"; +				} +				else if (stripos($col_types[$column_name], 'text') !== false || stripos($col_types[$column_name], 'char') !== false || stripos($col_types[$column_name], 'blob') !== false) +				{ +					$row[$column_name] = sanitize_data_generic(str_replace("'", "''", $column_data)); +				} +			} +			$this->flush($sql_insert . implode(', ', $row) . ");\n"); +		} +	} + +	/** +	* Writes closing line(s) to database backup +	* +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_end() +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$this->flush("COMMIT;\n"); +		parent::write_end(); +	} +} diff --git a/phpBB/phpbb/db/extractor/sqlite_extractor.php b/phpBB/phpbb/db/extractor/sqlite_extractor.php new file mode 100644 index 0000000000..2734e23235 --- /dev/null +++ b/phpBB/phpbb/db/extractor/sqlite_extractor.php @@ -0,0 +1,149 @@ +<?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\extractor; + +use phpbb\db\extractor\exception\extractor_not_initialized_exception; + +class sqlite_extractor extends base_extractor +{ +	/** +	* {@inheritdoc} +	*/ +	public function write_start($table_prefix) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = "--\n"; +		$sql_data .= "-- phpBB Backup Script\n"; +		$sql_data .= "-- Dump of tables for $table_prefix\n"; +		$sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n"; +		$sql_data .= "--\n"; +		$sql_data .= "BEGIN TRANSACTION;\n"; +		$this->flush($sql_data); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_table($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$sql_data = '-- Table: ' . $table_name . "\n"; +		$sql_data .= "DROP TABLE $table_name;\n"; + +		$sql = "SELECT sql +			FROM sqlite_master +			WHERE type = 'table' +				AND name = '" . $this->db->sql_escape($table_name) . "' +			ORDER BY type DESC, name;"; +		$result = $this->db->sql_query($sql); +		$row = $this->db->sql_fetchrow($result); +		$this->db->sql_freeresult($result); + +		// Create Table +		$sql_data .= $row['sql'] . ";\n"; + +		$result = $this->db->sql_query("PRAGMA index_list('" . $this->db->sql_escape($table_name) . "');"); + +		$ar = array(); +		while ($row = $this->db->sql_fetchrow($result)) +		{ +			$ar[] = $row; +		} +		$this->db->sql_freeresult($result); + +		foreach ($ar as $value) +		{ +			if (strpos($value['name'], 'autoindex') !== false) +			{ +				continue; +			} + +			$result = $this->db->sql_query("PRAGMA index_info('" . $this->db->sql_escape($value['name']) . "');"); + +			$fields = array(); +			while ($row = $this->db->sql_fetchrow($result)) +			{ +				$fields[] = $row['name']; +			} +			$this->db->sql_freeresult($result); + +			$sql_data .= 'CREATE ' . ($value['unique'] ? 'UNIQUE ' : '') . 'INDEX ' . $value['name'] . ' on ' . $table_name . ' (' . implode(', ', $fields) . ");\n"; +		} + +		$this->flush($sql_data . "\n"); +	} + +	/** +	* {@inheritdoc} +	*/ +	public function write_data($table_name) +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$col_types = sqlite_fetch_column_types($this->db->get_db_connect_id(), $table_name); + +		$sql = "SELECT * +			FROM $table_name"; +		$result = sqlite_unbuffered_query($this->db->get_db_connect_id(), $sql); +		$rows = sqlite_fetch_all($result, SQLITE_ASSOC); +		$sql_insert = 'INSERT INTO ' . $table_name . ' (' . implode(', ', array_keys($col_types)) . ') VALUES ('; +		foreach ($rows as $row) +		{ +			foreach ($row as $column_name => $column_data) +			{ +				if (is_null($column_data)) +				{ +					$row[$column_name] = 'NULL'; +				} +				else if ($column_data == '') +				{ +					$row[$column_name] = "''"; +				} +				else if (strpos($col_types[$column_name], 'text') !== false || strpos($col_types[$column_name], 'char') !== false || strpos($col_types[$column_name], 'blob') !== false) +				{ +					$row[$column_name] = sanitize_data_generic(str_replace("'", "''", $column_data)); +				} +			} +			$this->flush($sql_insert . implode(', ', $row) . ");\n"); +		} +	} + +	/** +	* Writes closing line(s) to database backup +	* +	* @return null +	* @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() +	*/ +	public function write_end() +	{ +		if (!$this->is_initialized) +		{ +			throw new extractor_not_initialized_exception(); +		} + +		$this->flush("COMMIT;\n"); +		parent::write_end(); +	} +} diff --git a/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php b/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php index 94c293dc45..4c85bf4d67 100644 --- a/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php +++ b/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php @@ -38,16 +38,23 @@ class log_wrapper_migrator_output_handler implements migrator_output_handler_int  	protected $file_handle = false;  	/** +	 * @var \phpbb\filesystem\filesystem_interface +	 */ +	protected $filesystem; + +	/**  	 * Constructor  	 *  	 * @param user $user	User object  	 * @param migrator_output_handler_interface $migrator Migrator output handler  	 * @param string $log_file	File to log to +	 * @param \phpbb\filesystem\filesystem_interface	phpBB filesystem object  	 */ -	public function __construct(user $user, migrator_output_handler_interface $migrator, $log_file) +	public function __construct(user $user, migrator_output_handler_interface $migrator, $log_file, \phpbb\filesystem\filesystem_interface $filesystem)  	{  		$this->user = $user;  		$this->migrator = $migrator; +		$this->filesystem = $filesystem;  		$this->file_open($log_file);  	} @@ -58,7 +65,7 @@ class log_wrapper_migrator_output_handler implements migrator_output_handler_int  	 */  	protected function file_open($file)  	{ -		if (phpbb_is_writable(dirname($file))) +		if ($this->filesystem->is_writable(dirname($file)))  		{  			$this->file_handle = fopen($file, 'w');  		} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php new file mode 100644 index 0000000000..51475f5a05 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.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_14 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.14', '>=') && 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_14_rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				phpbb_version_compare($this->config['version'], '3.0.14', '<'), +				array('config.update', array('version', '3.0.14')), +			)), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php new file mode 100644 index 0000000000..421ef06dd3 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.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_14_rc1 extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return phpbb_version_compare($this->config['version'], '3.0.14-RC1', '>=') && 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_13'); +	} + +	public function update_data() +	{ +		return array( +			array('if', array( +				phpbb_version_compare($this->config['version'], '3.0.14-RC1', '<'), +				array('config.update', array('version', '3.0.14-RC1')), +			)), +		); +	} +} 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 index 0ca4f2f19c..725c57ca86 100644 --- a/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php +++ b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php @@ -13,7 +13,7 @@  namespace phpbb\db\migration\data\v310; -class acp_prune_users_module extends \phpbb\db\migration\migration +class acp_prune_users_module extends \phpbb\db\migration\container_aware_migration  {  	public function effectively_installed()  	{ @@ -70,12 +70,7 @@ class acp_prune_users_module extends \phpbb\db\migration\migration  		$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); +		$module_manager = $this->container->get('module.manager'); +		$module_manager->move_module($acp_prune_users_id, $acp_cat_users_id, 'acp');  	}  } diff --git a/phpBB/phpbb/db/migration/data/v310/avatars.php b/phpBB/phpbb/db/migration/data/v310/avatars.php index 2698adeed5..9b03a8fa94 100644 --- a/phpBB/phpbb/db/migration/data/v310/avatars.php +++ b/phpBB/phpbb/db/migration/data/v310/avatars.php @@ -17,7 +17,29 @@ class avatars extends \phpbb\db\migration\migration  {  	public function effectively_installed()  	{ -		return isset($this->config['allow_avatar_gravatar']); +		// Get current avatar type of guest user +		$sql = 'SELECT user_avatar_type +			FROM ' . $this->table_prefix . 'users +			WHERE user_id = ' . ANONYMOUS; +		$result = $this->db->sql_query($sql); +		$backup_type = $this->db->sql_fetchfield('user_avatar_type'); +		$this->db->sql_freeresult($result); + +		// Try to set avatar type to string +		$sql = 'UPDATE ' . $this->table_prefix . "users +			SET user_avatar_type = 'avatar.driver.upload' +			WHERE user_id = " . ANONYMOUS; +		$this->db->sql_return_on_error(true); +		$effectively_installed = $this->db->sql_query($sql); +		$this->db->sql_return_on_error(); + +		// Restore avatar type of guest user to previous state +		$sql = 'UPDATE ' . $this->table_prefix . "users +			SET user_avatar_type = '{$backup_type}' +			WHERE user_id = " . ANONYMOUS; +		$this->db->sql_query($sql); + +		return $effectively_installed !== false;  	}  	static public function depends_on() diff --git a/phpBB/phpbb/db/migration/data/v310/dev.php b/phpBB/phpbb/db/migration/data/v310/dev.php index f037191c2a..250258eea7 100644 --- a/phpBB/phpbb/db/migration/data/v310/dev.php +++ b/phpBB/phpbb/db/migration/data/v310/dev.php @@ -13,7 +13,7 @@  namespace phpbb\db\migration\data\v310; -class dev extends \phpbb\db\migration\migration +class dev extends \phpbb\db\migration\container_aware_migration  {  	public function effectively_installed()  	{ @@ -204,18 +204,13 @@ class dev extends \phpbb\db\migration\migration  		$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); +		$module_manager = $this->container->get('module.manager'); +		$module_manager->move_module($language_module_id, $language_management_module_id, 'acp');  	}  	public function update_ucp_pm_basename() diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php index 918a565e06..2c7b7edf2e 100644 --- a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php +++ b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php @@ -62,8 +62,6 @@ class style_update_p1 extends \phpbb\db\migration\migration  	public function styles_update()  	{ -		global $config; -  		// Get list of valid 3.1 styles  		$available_styles = array('prosilver'); @@ -138,7 +136,7 @@ class style_update_p1 extends \phpbb\db\migration\migration  		if (!sizeof($valid_styles))  		{  			// No valid styles: remove everything and add prosilver -			$this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary); +			$this->sql_query('DELETE FROM ' . STYLES_TABLE);  			$sql_ary = array(  				'style_name'		=> 'prosilver', @@ -159,13 +157,13 @@ class style_update_p1 extends \phpbb\db\migration\migration  			$this->sql_query($sql);  			$sql = 'SELECT style_id -				FROM ' . $table . " +				FROM ' . STYLES_TABLE . "  				WHERE style_name = 'prosilver'";  			$result = $this->sql_query($sql); -			$default_style = $this->db->sql_fetchfield($result); +			$default_style = $this->db->sql_fetchfield('style_id');  			$this->db->sql_freeresult($result); -			$config->set('default_style', $default_style); +			$this->config->set('default_style', $default_style);  			$sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0';  			$this->sql_query($sql); diff --git a/phpBB/phpbb/db/migration/data/v31x/m_pm_report.php b/phpBB/phpbb/db/migration/data/v31x/m_pm_report.php new file mode 100644 index 0000000000..9b5710c639 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/m_pm_report.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\v31x; + +class m_pm_report extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v31x\v316rc1'); +	} + +	public function update_data() +	{ +		return array( +			array('permission.add', array('m_pm_report', true, 'm_report')), +			array('custom', array( +					array($this, 'update_module_auth'), +				), +			), +		); +	} + +	public function revert_data() +	{ +		return array( +			array('permission.remove', array('m_pm_report')), +			array('custom', array( +					array($this, 'revert_module_auth'), +				), +			), +		); +	} + +	public function update_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'acl_m_pm_report' +			WHERE module_class = 'mcp' +				AND module_basename = 'mcp_pm_reports' +				AND module_auth = 'aclf_m_report'"; +		$this->db->sql_query($sql); +	} + +	public function revert_module_auth() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET module_auth = 'aclf_m_report' +			WHERE module_class = 'mcp' +				AND module_basename = 'mcp_pm_reports' +				AND module_auth = 'acl_m_pm_report'"; +		$this->db->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v314.php b/phpBB/phpbb/db/migration/data/v31x/v314.php new file mode 100644 index 0000000000..b7793ca569 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v314.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\v31x; + +class v314 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v30x\release_3_0_14', +			'\phpbb\db\migration\data\v31x\v314rc2', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.4')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc1.php b/phpBB/phpbb/db/migration/data/v31x/v314rc1.php new file mode 100644 index 0000000000..10cdbe3f9c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v314rc1.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\v31x; + +class v314rc1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v31x\v313', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.4-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc2.php b/phpBB/phpbb/db/migration/data/v31x/v314rc2.php new file mode 100644 index 0000000000..b75b7a9be8 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v314rc2.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\v31x; + +class v314rc2 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v30x\release_3_0_14_rc1', +			'\phpbb\db\migration\data\v31x\v314rc1', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.4-RC2')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v315.php b/phpBB/phpbb/db/migration/data/v31x/v315.php new file mode 100644 index 0000000000..778cdf717e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v315.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\v31x; + +class v315 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v31x\v315rc1', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.5')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v315rc1.php b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php new file mode 100644 index 0000000000..4cf4472aa7 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v315rc1.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\v31x; + +class v315rc1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v31x\v314', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.5-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v316.php b/phpBB/phpbb/db/migration/data/v31x/v316.php new file mode 100644 index 0000000000..cec113eff2 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v316.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\v31x; + +class v316 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v31x\v316rc1', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.6')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v316rc1.php b/phpBB/phpbb/db/migration/data/v31x/v316rc1.php new file mode 100644 index 0000000000..487cd05e5d --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v316rc1.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\v31x; + +class v316rc1 extends \phpbb\db\migration\migration +{ +	static public function depends_on() +	{ +		return array( +			'\phpbb\db\migration\data\v31x\v315', +		); +	} + +	public function update_data() +	{ +		return array( +			array('config.update', array('version', '3.1.6-RC1')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.php b/phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.php new file mode 100644 index 0000000000..de127e3745 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.php @@ -0,0 +1,24 @@ +<?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\v320; + +class allowed_schemes_links extends \phpbb\db\migration\migration +{ +	public function update_data() +	{ +		return array( +			array('config.add', array('allowed_schemes_links', 'http,https,ftp')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/announce_global_permission.php b/phpBB/phpbb/db/migration/data/v320/announce_global_permission.php new file mode 100644 index 0000000000..fe30a1c1b8 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/announce_global_permission.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\v320; + +class announce_global_permission extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		$sql = 'SELECT auth_option_id +			FROM ' . ACL_OPTIONS_TABLE . " +			WHERE auth_option = 'f_announce_global'"; +		$result = $this->db->sql_query($sql); +		$auth_option_id = $this->db->sql_fetchfield('auth_option_id'); +		$this->db->sql_freeresult($result); + +		return $auth_option_id !== false; +	} + +	static public function depends_on() +	{ +		return array('\phpbb\db\migration\data\v310\rc2'); +	} + +	public function update_data() +	{ +		return array( +			array('permission.add', array('f_announce_global', false, 'f_announce')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/font_awesome_update.php b/phpBB/phpbb/db/migration/data/v320/font_awesome_update.php new file mode 100644 index 0000000000..6ffaf18b4a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/font_awesome_update.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\v320; + +class font_awesome_update extends \phpbb\db\migration\migration +{ +	public function effectively_installed() +	{ +		return isset($this->config['load_font_awesome_url']); +	} + +	public function update_data() +	{ +		return array( +			array('config.add', array('load_font_awesome_url', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css')), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/icons_alt.php b/phpBB/phpbb/db/migration/data/v320/icons_alt.php new file mode 100644 index 0000000000..7071ae78db --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/icons_alt.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\v320; + +class icons_alt 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 . 'icons'        => array( +					'icons_alt'    => array('VCHAR', '', 'after' => 'icons_height'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'        => array( +				$this->table_prefix . 'icons'        => array( +					'icons_alt', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/log_post_id.php b/phpBB/phpbb/db/migration/data/v320/log_post_id.php new file mode 100644 index 0000000000..0f155d543c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/log_post_id.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\v320; + +class log_post_id 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 . 'log'        => array( +					'post_id'    => array('UINT', 0, 'after' => 'topic_id'), +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'drop_columns'        => array( +				$this->table_prefix . 'log'        => array( +					'post_id', +				), +			), +		); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/notifications_board.php b/phpBB/phpbb/db/migration/data/v320/notifications_board.php new file mode 100644 index 0000000000..fd9f1a2ad6 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/notifications_board.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\v320; + +class notifications_board 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('allow_board_notifications', 1)), +			array('custom', array(array($this, 'update_user_subscriptions'))), +			array('custom', array(array($this, 'update_module'))), +		); +	} + +	public function update_module() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET auth = 'cfg_allow_board_notifications' +			WHERE module_basename = 'ucp_notifications' +				AND module_mode = 'notification_list'"; +		$this->sql_query($sql); +	} + +	public function update_user_subscriptions() +	{ +		$sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . " +			SET method = 'notification.method.board' +			WHERE method = ''"; +		$this->sql_query($sql); +	} + +	public function revert_data() +	{ +		return array( +			array('custom', array(array($this, 'revert_user_subscriptions'))), +			array('custom', array(array($this, 'revert_module'))), +		); +	} + +	public function revert_user_subscriptions() +	{ +		$sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . " +			SET method = '' +			WHERE method = 'notification.method.board'"; +		$this->sql_query($sql); +	} + +	public function revert_module() +	{ +		$sql = 'UPDATE ' . MODULES_TABLE . " +			SET auth = '' +			WHERE module_basename = 'ucp_notifications' +				AND module_mode = 'notification_list'"; +		$this->sql_query($sql); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php b/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php new file mode 100644 index 0000000000..59208be4dc --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php @@ -0,0 +1,83 @@ +<?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\v320; + +class remove_outdated_media extends \phpbb\db\migration\migration +{ +	protected $cat_id = array( +			ATTACHMENT_CATEGORY_WM, +			ATTACHMENT_CATEGORY_RM, +			ATTACHMENT_CATEGORY_QUICKTIME, +		); + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'change_extension_group'))), +		); +	} + +	public function change_extension_group() +	{ +		// select group ids of outdated media +		$sql = 'SELECT group_id +			FROM ' . EXTENSION_GROUPS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('cat_id', $this->cat_id); +		$result = $this->db->sql_query($sql); + +		$group_ids = array(); +		while ($group_id = (int) $this->db->sql_fetchfield('group_id')) +		{ +			$group_ids[] = $group_id; +		} +		$this->db->sql_freeresult($result); + +		// nothing to do, admin has removed all the outdated media extension groups +		if (empty($group_ids)) +		{ +			return true; +		} + +		// get the group id of downloadable files +		$sql = 'SELECT group_id +			FROM ' . EXTENSION_GROUPS_TABLE . " +			WHERE group_name = 'DOWNLOADABLE_FILES'"; +		$result = $this->db->sql_query($sql); +		$download_id = (int) $this->db->sql_fetchfield('group_id'); +		$this->db->sql_freeresult($result); + +		if (empty($download_id)) +		{ +			$sql = 'UPDATE ' . EXTENSIONS_TABLE . ' +				SET group_id = 0 +				WHERE ' . $this->db->sql_in_set('group_id', $group_ids); +		} +		else +		{ +			// move outdated media extensions to downloadable files +			$sql = 'UPDATE ' . EXTENSIONS_TABLE . " +				SET group_id = $download_id" . ' +				WHERE ' . $this->db->sql_in_set('group_id', $group_ids); +		} + +		$result = $this->db->sql_query($sql); +		$this->db->sql_freeresult($result); + +		// delete the now empty, outdated media extension groups +		$sql = 'DELETE FROM ' . EXTENSION_GROUPS_TABLE . ' +			WHERE ' . $this->db->sql_in_set('group_id', $group_ids); +		$result = $this->db->sql_query($sql); +		$this->db->sql_freeresult($result); +	} +} diff --git a/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php b/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php new file mode 100644 index 0000000000..2898c708f8 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php @@ -0,0 +1,150 @@ +<?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\v320; + +class remove_profilefield_wlm extends \phpbb\db\migration\migration +{ +	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 . 'profile_fields_data'			=> array( +					'pf_phpbb_wlm', +				), +			), +		); +	} + +	public function revert_schema() +	{ +		return array( +			'add_columns'	=> array( +				$this->table_prefix . 'profile_fields_data'			=> array( +					'pf_phpbb_wlm'		=> array('VCHAR', ''), +				), +			), +		); +	} + +	public function update_data() +	{ +		return array( +			array('custom', array(array($this, 'delete_custom_profile_field_data'))), +		); +	} + +	public function revert_data() +	{ +		return array( +			array('custom', array(array($this, 'create_custom_field'))), +		); +	} + +	public function delete_custom_profile_field_data() +	{ +		$field_id = $this->get_custom_profile_field_id(); + +		$sql = 'DELETE FROM ' . PROFILE_FIELDS_TABLE . ' +			WHERE field_id = ' . (int) $field_id; +		$this->db->sql_query($sql); + +		$sql = 'DELETE FROM ' . PROFILE_LANG_TABLE . ' +			WHERE field_id = ' . (int) $field_id; +		$this->db->sql_query($sql); + +		$sql = 'DELETE FROM ' . PROFILE_FIELDS_LANG_TABLE . ' +			WHERE field_id = ' . (int) $field_id; +		$this->db->sql_query($sql); +	} + +	/** +	 * Get custom profile field id +	 * @return	int	custom profile filed id +	 */ +	public function get_custom_profile_field_id() +	{ +		$sql = 'SELECT field_id +			FROM ' . PROFILE_FIELDS_TABLE . " +			WHERE field_name = 'phpbb_wlm'"; +		$result = $this->db->sql_query($sql); +		$field_id = (int) $this->db->sql_fetchfield('field_id'); +		$this->db->sql_freeresult($result); + +		return $field_id; +	} + +	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( +			'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'		=> '', +			'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); +		$lang_name = 'WLM'; +		while ($lang_id = (int) $this->db->sql_fetchfield('lang_id')) +		{ +			$insert_buffer->insert(array( +				'field_id'				=> (int) $field_id, +				'lang_id'				=> (int) $lang_id, +				'lang_name'				=> $lang_name, +				'lang_explain'			=> '', +				'lang_default_value'	=> '', +			)); +		} +		$this->db->sql_freeresult($result); + +		$insert_buffer->flush(); +	} +} diff --git a/phpBB/phpbb/db/migration/tool/module.php b/phpBB/phpbb/db/migration/tool/module.php index b6f0372181..69ac71abb7 100644 --- a/phpBB/phpbb/db/migration/tool/module.php +++ b/phpBB/phpbb/db/migration/tool/module.php @@ -13,6 +13,8 @@  namespace phpbb\db\migration\tool; +use phpbb\module\exception\module_exception; +  /**  * Migration module management tool  */ @@ -27,6 +29,9 @@ class module implements \phpbb\db\migration\tool\tool_interface  	/** @var \phpbb\user */  	protected $user; +	/** @var \phpbb\module\module_manager */ +	protected $module_manager; +  	/** @var string */  	protected $phpbb_root_path; @@ -42,15 +47,17 @@ class module implements \phpbb\db\migration\tool\tool_interface  	* @param \phpbb\db\driver\driver_interface $db  	* @param \phpbb\cache\service $cache  	* @param \phpbb\user $user +	* @param \phpbb\module\module_manager	$module_manager  	* @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) +	public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, \phpbb\module\module_manager $module_manager, $phpbb_root_path, $php_ext, $modules_table)  	{  		$this->db = $db;  		$this->cache = $cache;  		$this->user = $user; +		$this->module_manager = $module_manager;  		$this->phpbb_root_path = $phpbb_root_path;  		$this->php_ext = $php_ext;  		$this->modules_table = $modules_table; @@ -188,7 +195,6 @@ class module implements \phpbb\db\migration\tool\tool_interface  			$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'])) @@ -239,13 +245,6 @@ class module implements \phpbb\db\migration\tool\tool_interface  			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, @@ -256,16 +255,11 @@ class module implements \phpbb\db\migration\tool\tool_interface  			'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 +		try  		{ +			$this->module_manager->update_module_data($module_data); +  			// Success  			$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']);  			$phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_MODULE_ADD', false, array($module_log_name)); @@ -318,6 +312,11 @@ class module implements \phpbb\db\migration\tool\tool_interface  				$this->db->sql_query($sql);  			}  		} +		catch (module_exception $e) +		{ +			// Error +			throw new \phpbb\db\migration\exception('MODULE_ERROR', $e->getMessage()); +		}  		// Clear the Modules Cache  		$this->cache->destroy("_modules_$class"); @@ -417,21 +416,9 @@ class module implements \phpbb\db\migration\tool\tool_interface  				$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->module_manager->delete_module($module_id, $class);  			}  			$this->cache->destroy("_modules_$class"); @@ -474,13 +461,7 @@ class module implements \phpbb\db\migration\tool\tool_interface  	*/  	protected function get_module_info($class, $basename)  	{ -		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 = $acp_modules->get_module_infos($basename, $class, true); +		$module = $this->module_manager->get_module_infos($class, $basename, true);  		if (empty($module))  		{ diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php index 1a91127d2d..ceff6d7d5a 100644 --- a/phpBB/phpbb/db/migration/tool/permission.php +++ b/phpBB/phpbb/db/migration/tool/permission.php @@ -425,13 +425,27 @@ class permission implements \phpbb\db\migration\tool\tool_interface  				$role_id = (int) $this->db->sql_fetchfield('auth_role_id');  				if ($role_id)  				{ -					$sql = 'SELECT role_name +					$sql = 'SELECT role_name, role_type  						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); +					$role_data = $this->db->sql_fetchrow(); +					$role_name = $role_data['role_name']; +					$role_type = $role_data['role_type']; + +					// Filter new auth options to match the role type: a_ | f_ | m_ | u_ +					// Set new auth options to the role only if options matching the role type were found +					$auth_option = array_filter($auth_option, +						function ($option) use ($role_type) +						{ +							return strpos($option, $role_type) === 0; +						} +					); + +					if (sizeof($auth_option)) +					{ +						return $this->permission_set($role_name, $auth_option, 'role', $has_permission); +					}  				}  				$sql = 'SELECT auth_option_id, auth_setting diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php index 6902913c64..18c6403c07 100644 --- a/phpBB/phpbb/db/migrator.php +++ b/phpBB/phpbb/db/migrator.php @@ -416,6 +416,9 @@ class migrator  		if ($state['migration_data_done'])  		{ +			$this->output_handler->write(array('MIGRATION_REVERT_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE); +			$elapsed_time = microtime(true); +  			if ($state['migration_data_state'] !== 'revert_data')  			{  				$result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true); @@ -431,9 +434,22 @@ class migrator  			}  			$this->set_migration_state($name, $state); + +			$elapsed_time = microtime(true) - $elapsed_time; +			if ($state['migration_data_done']) +			{ +				$this->output_handler->write(array('MIGRATION_REVERT_DATA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL); +			} +			else +			{ +				$this->output_handler->write(array('MIGRATION_REVERT_DATA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE); +			}  		}  		else if ($state['migration_schema_done'])  		{ +			$this->output_handler->write(array('MIGRATION_REVERT_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE); +			$elapsed_time = microtime(true); +  			$steps = $this->helper->get_schema_steps($migration->revert_schema());  			$result = $this->process_data_step($steps, $state['migration_data_state']); @@ -448,6 +464,9 @@ class migrator  				unset($this->migration_state[$name]);  			} + +			$elapsed_time = microtime(true) - $elapsed_time; +			$this->output_handler->write(array('MIGRATION_REVERT_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);  		}  		return true; diff --git a/phpBB/phpbb/db/sql_insert_buffer.php b/phpBB/phpbb/db/sql_insert_buffer.php index 14e3c54f09..18e4814a77 100644 --- a/phpBB/phpbb/db/sql_insert_buffer.php +++ b/phpBB/phpbb/db/sql_insert_buffer.php @@ -107,7 +107,7 @@ class sql_insert_buffer  	* first building a huge rowset. Or at least sizeof($rows) should be kept  	* small.  	* -	* @param array $rows  +	* @param array $rows  	*  	* @return bool		True when some data was flushed to the database.  	*					False otherwise. diff --git a/phpBB/phpbb/db/tools/tools.php b/phpBB/phpbb/db/tools/tools.php index 0d1eb63c47..1d7b2ddfff 100644 --- a/phpBB/phpbb/db/tools/tools.php +++ b/phpBB/phpbb/db/tools/tools.php @@ -40,7 +40,7 @@ class tools implements tools_interface  	*  	* @return array  	*/ -	public static function get_dbms_type_map() +	static public function get_dbms_type_map()  	{  		return array(  			'mysql_41'	=> array( | 
