mirror of
https://github.com/phpbb/phpbb.git
synced 2025-06-27 21:58:52 +00:00
Merge remote-tracking branch 'EXreaction/feature/migrations' into develop
* EXreaction/feature/migrations: (48 commits) [feature/migrations] Remove default values from necessary parameters [feature/migrations] Revert unrelated changes to functions.php [ticket/9737] Fix some comments [ticket/9737] Fix a few minor things in migrations [feature/migrations] Make sure migration data not done before running data step [feature/migrations] Function to populate the migrations table (for install) [feature/migrations] Function effectively_installed() in migrations [feature/migrations] Make load_migrations recursive (optionally) [feature/migrations] Make the test depends_on methods static [feature/migrations] Make depends_on static to call it without dependencies [feature/migrations] install/database_update_migrations.php [feature/migrations] Move test.php -> install/database_update_migrations.php [feature/migrations] Store depends on in the database (serialized) [feature/migrations] Revert method completed [feature/migrations] Basic reverting test [feature/migrations] Test for calling a step multiple times [feature/migrations] Creating revert method to attempt reverting a migration [feature/migrations] Some comments in db_tools [feature/migrations] Reverse data functionality [feature/migrations] Comment ... Conflicts: phpBB/install/schemas/firebird_schema.sql phpBB/install/schemas/mssql_schema.sql phpBB/install/schemas/mysql_40_schema.sql phpBB/install/schemas/mysql_41_schema.sql phpBB/install/schemas/oracle_schema.sql phpBB/install/schemas/postgres_schema.sql phpBB/install/schemas/sqlite_schema.sql
This commit is contained in:
commit
effaef6bdd
35 changed files with 3570 additions and 0 deletions
49
phpBB/config/migrator.yml
Normal file
49
phpBB/config/migrator.yml
Normal file
|
@ -0,0 +1,49 @@
|
|||
services:
|
||||
migrator:
|
||||
class: phpbb_db_migrator
|
||||
arguments:
|
||||
- @config
|
||||
- @dbal.conn
|
||||
- @dbal.tools
|
||||
- %tables.migrations%
|
||||
- %core.root_path%
|
||||
- %core.php_ext%
|
||||
- %core.table_prefix%
|
||||
- @migrator.tool_collection
|
||||
|
||||
migrator.tool_collection:
|
||||
class: phpbb_di_service_collection
|
||||
arguments:
|
||||
- @service_container
|
||||
tags:
|
||||
- { name: service_collection, tag: migrator.tool }
|
||||
|
||||
migrator.tool.config:
|
||||
class: phpbb_db_migration_tool_config
|
||||
arguments:
|
||||
- @config
|
||||
tags:
|
||||
- { name: migrator.tool }
|
||||
|
||||
migrator.tool.module:
|
||||
class: phpbb_db_migration_tool_module
|
||||
arguments:
|
||||
- @dbal.conn
|
||||
- @cache
|
||||
- @user
|
||||
- %core.root_path%
|
||||
- %core.php_ext%
|
||||
- %tables.modules%
|
||||
tags:
|
||||
- { name: migrator.tool }
|
||||
|
||||
migrator.tool.permission:
|
||||
class: phpbb_db_migration_tool_permission
|
||||
arguments:
|
||||
- @dbal.conn
|
||||
- @cache
|
||||
- @auth
|
||||
- %core.root_path%
|
||||
- %core.php_ext%
|
||||
tags:
|
||||
- { name: migrator.tool }
|
|
@ -1,6 +1,7 @@
|
|||
imports:
|
||||
- { resource: tables.yml }
|
||||
- { resource: cron_tasks.yml }
|
||||
- { resource: migrator.yml }
|
||||
|
||||
services:
|
||||
auth:
|
||||
|
@ -94,6 +95,12 @@ services:
|
|||
calls:
|
||||
- [sql_connect, [%dbal.dbhost%, %dbal.dbuser%, %dbal.dbpasswd%, %dbal.dbname%, %dbal.dbport%, false, %dbal.new_link%]]
|
||||
|
||||
dbal.tools:
|
||||
file: %core.root_path%includes/db/db_tools.%core.php_ext%
|
||||
class: phpbb_db_tools
|
||||
arguments:
|
||||
- @dbal.conn
|
||||
|
||||
event.subscriber_loader:
|
||||
class: phpbb_event_extension_subscriber_loader
|
||||
arguments:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
parameters:
|
||||
tables.config: %core.table_prefix%config
|
||||
tables.ext: %core.table_prefix%ext
|
||||
tables.migrations: %core.table_prefix%migrations
|
||||
tables.modules: %core.table_prefix%modules
|
||||
|
|
|
@ -1273,6 +1273,19 @@ function get_schema_struct()
|
|||
),
|
||||
);
|
||||
|
||||
$schema_data['phpbb_migrations'] = array(
|
||||
'COLUMNS' => array(
|
||||
'migration_name' => array('VCHAR', ''),
|
||||
'migration_depends_on' => array('TEXT', ''),
|
||||
'migration_schema_done' => array('BOOL', 0),
|
||||
'migration_data_done' => array('BOOL', 0),
|
||||
'migration_data_state' => array('TEXT', ''),
|
||||
'migration_start_time' => array('TIMESTAMP', 0),
|
||||
'migration_end_time' => array('TIMESTAMP', 0),
|
||||
),
|
||||
'PRIMARY_KEY' => 'migration_name',
|
||||
);
|
||||
|
||||
$schema_data['phpbb_modules'] = array(
|
||||
'COLUMNS' => array(
|
||||
'module_id' => array('UINT', NULL, 'auto_increment'),
|
||||
|
|
|
@ -237,6 +237,7 @@ define('ICONS_TABLE', $table_prefix . 'icons');
|
|||
define('LANG_TABLE', $table_prefix . 'lang');
|
||||
define('LOG_TABLE', $table_prefix . 'log');
|
||||
define('LOGIN_ATTEMPT_TABLE', $table_prefix . 'login_attempts');
|
||||
define('MIGRATIONS_TABLE', $table_prefix . 'migrations');
|
||||
define('MODERATOR_CACHE_TABLE', $table_prefix . 'moderator_cache');
|
||||
define('MODULES_TABLE', $table_prefix . 'modules');
|
||||
define('POLL_OPTIONS_TABLE', $table_prefix . 'poll_options');
|
||||
|
|
|
@ -345,6 +345,17 @@ class phpbb_db_tools
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for {@link $return_statements return_statements}.
|
||||
*
|
||||
* @param bool $return_statements True if SQL should not be executed but returned as strings
|
||||
* @return null
|
||||
*/
|
||||
public function set_return_statements($return_statements)
|
||||
{
|
||||
$this->return_statements = $return_statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of tables in the database.
|
||||
*
|
||||
|
@ -674,6 +685,8 @@ class phpbb_db_tools
|
|||
* Handle passed database update array.
|
||||
* Expected structure...
|
||||
* Key being one of the following
|
||||
* drop_tables: Drop tables
|
||||
* add_tables: Add tables
|
||||
* change_columns: Column changes (only type, not name)
|
||||
* add_columns: Add columns to a table
|
||||
* drop_keys: Dropping keys
|
||||
|
|
55
phpBB/includes/db/migration/exception.php
Normal file
55
phpBB/includes/db/migration/exception.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package db
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The migrator is responsible for applying new migrations in the correct order.
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
class phpbb_db_migration_exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* Extra parameters sent to exception to aid in debugging
|
||||
* @var array
|
||||
*/
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* Throw an exception.
|
||||
*
|
||||
* First argument is the error message.
|
||||
* Additional arguments will be output with the error message.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$parameters = func_get_args();
|
||||
$message = array_shift($parameters);
|
||||
parent::__construct($message);
|
||||
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the error as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->message . ': ' . var_export($this->parameters, true);
|
||||
}
|
||||
}
|
190
phpBB/includes/db/migration/migration.php
Normal file
190
phpBB/includes/db/migration/migration.php
Normal file
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package db
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for database migrations
|
||||
*
|
||||
* Each migration consists of a set of schema and data changes to be implemented
|
||||
* in a subclass. This class provides various utility methods to simplify editing
|
||||
* a phpBB.
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
abstract class phpbb_db_migration
|
||||
{
|
||||
/** @var phpbb_config */
|
||||
protected $config;
|
||||
|
||||
/** @var phpbb_db_driver */
|
||||
protected $db;
|
||||
|
||||
/** @var phpbb_db_tools */
|
||||
protected $db_tools;
|
||||
|
||||
/** @var string */
|
||||
protected $table_prefix;
|
||||
|
||||
/** @var string */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string */
|
||||
protected $php_ext;
|
||||
|
||||
/** @var array Errors, if any occured */
|
||||
protected $errors;
|
||||
|
||||
/** @var array List of queries executed through $this->sql_query() */
|
||||
protected $queries = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param phpbb_config $config
|
||||
* @param phpbb_db_driver $db
|
||||
* @param phpbb_db_tools $db_tools
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
* @param string $table_prefix
|
||||
*/
|
||||
public function __construct(phpbb_config $config, phpbb_db_driver $db, phpbb_db_tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->db_tools = $db_tools;
|
||||
$this->table_prefix = $table_prefix;
|
||||
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines other migrations to be applied first
|
||||
*
|
||||
* @return array An array of migration class names
|
||||
*/
|
||||
static public function depends_on()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to check if the migration is effectively installed (entirely optional)
|
||||
*
|
||||
* This is checked when a migration is installed. If true is returned, the migration will be set as
|
||||
* installed without performing the database changes.
|
||||
* This function is intended to help moving to migrations from a previous database updater, where some
|
||||
* migrations may have been installed already even though they are not yet listed in the migrations table.
|
||||
*
|
||||
* @return bool True if this migration is installed, False if this migration is not installed (checked on install)
|
||||
*/
|
||||
public function effectively_installed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the database schema by providing a set of change instructions
|
||||
*
|
||||
* @return array Array of schema changes (compatible with db_tools->perform_schema_changes())
|
||||
*/
|
||||
public function update_schema()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the database schema by providing a set of change instructions
|
||||
*
|
||||
* @return array Array of schema changes (compatible with db_tools->perform_schema_changes())
|
||||
*/
|
||||
public function revert_schema()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates data by returning a list of instructions to be executed
|
||||
*
|
||||
* @return array Array of data update instructions
|
||||
*/
|
||||
public function update_data()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts data by returning a list of instructions to be executed
|
||||
*
|
||||
* @return array Array of data instructions that will be performed on revert
|
||||
* NOTE: calls to tools (such as config.add) are automatically reverted when
|
||||
* possible, so you should not attempt to revert those, this is mostly for
|
||||
* otherwise unrevertable calls (custom functions for example)
|
||||
*/
|
||||
public function revert_data()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for running queries to generate user feedback on updates
|
||||
*
|
||||
* @param string $sql SQL query to run on the database
|
||||
* @return mixed Query result from db->sql_query()
|
||||
*/
|
||||
protected function sql_query($sql)
|
||||
{
|
||||
$this->queries[] = $sql;
|
||||
|
||||
$this->db->sql_return_on_error(true);
|
||||
|
||||
if ($sql === 'begin')
|
||||
{
|
||||
$result = $this->db->sql_transaction('begin');
|
||||
}
|
||||
else if ($sql === 'commit')
|
||||
{
|
||||
$result = $this->db->sql_transaction('commit');
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = $this->db->sql_query($sql);
|
||||
if ($this->db->sql_error_triggered)
|
||||
{
|
||||
$this->errors[] = array(
|
||||
'sql' => $this->db->sql_error_sql,
|
||||
'code' => $this->db->sql_error_returned,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->sql_return_on_error(false);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of queries run
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_queries()
|
||||
{
|
||||
return $this->queries;
|
||||
}
|
||||
}
|
150
phpBB/includes/db/migration/tool/config.php
Normal file
150
phpBB/includes/db/migration/tool/config.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package migration
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration config tool
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
class phpbb_db_migration_tool_config implements phpbb_db_migration_tool_interface
|
||||
{
|
||||
/** @var phpbb_config */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param phpbb_config $config
|
||||
*/
|
||||
public function __construct(phpbb_config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return 'config';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a config setting.
|
||||
*
|
||||
* @param string $config_name The name of the config setting
|
||||
* you would like to add
|
||||
* @param mixed $config_value The value of the config setting
|
||||
* @param bool $is_dynamic True if it is dynamic (changes very often)
|
||||
* and should not be stored in the cache, false if not.
|
||||
* @return null
|
||||
*/
|
||||
public function add($config_name, $config_value, $is_dynamic = false)
|
||||
{
|
||||
if (isset($this->config[$config_name]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXISTS', $config_name);
|
||||
}
|
||||
|
||||
$this->config->set($config_name, $config_value, !$is_dynamic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing config setting.
|
||||
*
|
||||
* @param string $config_name The name of the config setting you would
|
||||
* like to update
|
||||
* @param mixed $config_value The value of the config setting
|
||||
* @return null
|
||||
*/
|
||||
public function update($config_name, $config_value)
|
||||
{
|
||||
if (!isset($this->config[$config_name]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('CONFIG_NOT_EXIST', $config_name);
|
||||
}
|
||||
|
||||
$this->config->set($config_name, $config_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a config setting if the first argument equal to the
|
||||
* current config value
|
||||
*
|
||||
* @param string $compare If equal to the current config value, will be
|
||||
* updated to the new config value, otherwise not
|
||||
* @param string $config_name The name of the config setting you would
|
||||
* like to update
|
||||
* @param mixed $config_value The value of the config setting
|
||||
* @return null
|
||||
*/
|
||||
public function update_if_equals($compare, $config_name, $config_value)
|
||||
{
|
||||
if (!isset($this->config[$config_name]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('CONFIG_NOT_EXIST', $config_name);
|
||||
}
|
||||
|
||||
$this->config->set_atomic($config_name, $compare, $config_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an existing config setting.
|
||||
*
|
||||
* @param string $config_name The name of the config setting you would
|
||||
* like to remove
|
||||
* @return null
|
||||
*/
|
||||
public function remove($config_name)
|
||||
{
|
||||
if (!isset($this->config[$config_name]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('CONFIG_NOT_EXIST', $config_name);
|
||||
}
|
||||
|
||||
$this->config->delete($config_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$original_call = array_shift($arguments);
|
||||
|
||||
$call = false;
|
||||
switch ($original_call)
|
||||
{
|
||||
case 'add':
|
||||
$call = 'remove';
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$call = 'add';
|
||||
break;
|
||||
|
||||
case 'update_if_equals':
|
||||
$call = 'update_if_equals';
|
||||
|
||||
// Set to the original value if the current value is what we compared to originally
|
||||
$arguments = array(
|
||||
$arguments[2],
|
||||
$arguments[1],
|
||||
$arguments[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($call)
|
||||
{
|
||||
return call_user_func_array(array(&$this, $call), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
33
phpBB/includes/db/migration/tool/interface.php
Normal file
33
phpBB/includes/db/migration/tool/interface.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package migration
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration tool interface
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
interface phpbb_db_migration_tool_interface
|
||||
{
|
||||
/**
|
||||
* Retrieve a short name used for commands in migrations.
|
||||
*
|
||||
* @return string short name
|
||||
*/
|
||||
public function get_name();
|
||||
|
||||
/**
|
||||
* Reverse an original install action
|
||||
*
|
||||
* First argument is the original call to the class (e.g. add, remove)
|
||||
* After the first argument, send the original arguments to the function in the original call
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function reverse();
|
||||
}
|
513
phpBB/includes/db/migration/tool/module.php
Normal file
513
phpBB/includes/db/migration/tool/module.php
Normal file
|
@ -0,0 +1,513 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package migration
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration module management tool
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interface
|
||||
{
|
||||
/** @var phpbb_cache_service */
|
||||
protected $cache;
|
||||
|
||||
/** @var dbal */
|
||||
protected $db;
|
||||
|
||||
/** @var phpbb_user */
|
||||
protected $user;
|
||||
|
||||
/** @var string */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string */
|
||||
protected $php_ext;
|
||||
|
||||
/** @var string */
|
||||
protected $modules_table;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param phpbb_db_driver $db
|
||||
* @param mixed $cache
|
||||
* @param phpbb_user $user
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
* @param string $modules_table
|
||||
*/
|
||||
public function __construct(phpbb_db_driver $db, phpbb_cache_service $cache, phpbb_user $user, $phpbb_root_path, $php_ext, $modules_table)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->cache = $cache;
|
||||
$this->user = $user;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
$this->modules_table = $modules_table;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return 'module';
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Exists
|
||||
*
|
||||
* Check if a module exists
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string|bool $parent The parent module_id|module_langname (0 for no parent).
|
||||
* Use false to ignore the parent check and check class wide.
|
||||
* @param int|string $module The module_id|module_langname you would like to
|
||||
* check for to see if it exists
|
||||
* @return bool true/false if module exists
|
||||
*/
|
||||
public function exists($class, $parent, $module)
|
||||
{
|
||||
// the main root directory should return true
|
||||
if (!$module)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$parent_sql = '';
|
||||
if ($parent !== false)
|
||||
{
|
||||
// Allows '' to be sent as 0
|
||||
$parent = $parent ?: 0;
|
||||
|
||||
if (!is_numeric($parent))
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_id = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$module_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$parent_sql = 'AND parent_id = ' . (int) $module_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent_sql = 'AND parent_id = ' . (int) $parent;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
$parent_sql
|
||||
AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '" . $this->db->sql_escape($module) . "'");
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_id = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($module_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Add
|
||||
*
|
||||
* Add a new module
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string $parent The parent module_id|module_langname (0 for no parent)
|
||||
* @param array $data an array of the data on the new module.
|
||||
* This can be setup in two different ways.
|
||||
* 1. The "manual" way. For inserting a category or one at a time.
|
||||
* It will be merged with the base array shown a bit below,
|
||||
* but at the least requires 'module_langname' to be sent, and,
|
||||
* if you want to create a module (instead of just a category) you must
|
||||
* send module_basename and module_mode.
|
||||
* array(
|
||||
* 'module_enabled' => 1,
|
||||
* 'module_display' => 1,
|
||||
* 'module_basename' => '',
|
||||
* 'module_class' => $class,
|
||||
* 'parent_id' => (int) $parent,
|
||||
* 'module_langname' => '',
|
||||
* 'module_mode' => '',
|
||||
* 'module_auth' => '',
|
||||
* )
|
||||
* 2. The "automatic" way. For inserting multiple at a time based on the
|
||||
* specs in the info file for the module(s). For this to work the
|
||||
* modules must be correctly setup in the info file.
|
||||
* An example follows (this would insert the settings, log, and flag
|
||||
* modes from the includes/acp/info/acp_asacp.php file):
|
||||
* array(
|
||||
* 'module_basename' => 'asacp',
|
||||
* 'modes' => array('settings', 'log', 'flag'),
|
||||
* )
|
||||
* Optionally you may not send 'modes' and it will insert all of the
|
||||
* modules in that info file.
|
||||
* @param string|bool $include_path If you would like to use a custom include
|
||||
* path, specify that here
|
||||
* @return null
|
||||
*/
|
||||
public function add($class, $parent = 0, $data = array(), $include_path = false)
|
||||
{
|
||||
// Allows '' to be sent as 0
|
||||
$parent = $parent ?: 0;
|
||||
|
||||
// allow sending the name as a string in $data to create a category
|
||||
if (!is_array($data))
|
||||
{
|
||||
$data = array('module_langname' => $data);
|
||||
}
|
||||
|
||||
if (!isset($data['module_langname']))
|
||||
{
|
||||
// The "automatic" way
|
||||
$basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
|
||||
$basename = str_replace(array('/', '\\'), '', $basename);
|
||||
$class = str_replace(array('/', '\\'), '', $class);
|
||||
|
||||
$include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path;
|
||||
$info_file = "$class/info/$basename.{$this->php_ext}";
|
||||
|
||||
// The manual and automatic ways both failed...
|
||||
if (!file_exists($include_path . $info_file))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_INFO_FILE_NOT_EXIST', $class, $info_file);
|
||||
}
|
||||
|
||||
$classname = "{$basename}_info";
|
||||
|
||||
if (!class_exists($classname))
|
||||
{
|
||||
include($include_path . $info_file);
|
||||
}
|
||||
|
||||
$info = new $classname;
|
||||
$module = $info->module();
|
||||
unset($info);
|
||||
|
||||
$result = '';
|
||||
foreach ($module['modes'] as $mode => $module_info)
|
||||
{
|
||||
if (!isset($data['modes']) || in_array($mode, $data['modes']))
|
||||
{
|
||||
$new_module = array(
|
||||
'module_basename' => $basename,
|
||||
'module_langname' => $module_info['title'],
|
||||
'module_mode' => $mode,
|
||||
'module_auth' => $module_info['auth'],
|
||||
'module_display' => (isset($module_info['display'])) ? $module_info['display'] : true,
|
||||
'before' => (isset($module_info['before'])) ? $module_info['before'] : false,
|
||||
'after' => (isset($module_info['after'])) ? $module_info['after'] : false,
|
||||
);
|
||||
|
||||
// Run the "manual" way with the data we've collected.
|
||||
$this->add($class, $parent, $new_module);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The "manual" way
|
||||
$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']);
|
||||
add_log('admin', 'LOG_MODULE_ADD', $module_log_name);
|
||||
|
||||
if (!is_numeric($parent))
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_id = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!$module_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent);
|
||||
}
|
||||
|
||||
$parent = $data['parent_id'] = $module_id;
|
||||
}
|
||||
else if (!$this->exists($class, false, $parent))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent);
|
||||
}
|
||||
|
||||
if ($this->exists($class, $parent, $data['module_langname']))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_ALREADY_EXIST', $data['module_langname']);
|
||||
}
|
||||
|
||||
if (!class_exists('acp_modules'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
|
||||
$this->user->add_lang('acp/modules');
|
||||
}
|
||||
$acp_modules = new acp_modules();
|
||||
|
||||
$module_data = array(
|
||||
'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
|
||||
'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1,
|
||||
'module_basename' => (isset($data['module_basename'])) ? $data['module_basename'] : '',
|
||||
'module_class' => $class,
|
||||
'parent_id' => (int) $parent,
|
||||
'module_langname' => (isset($data['module_langname'])) ? $data['module_langname'] : '',
|
||||
'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '',
|
||||
'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '',
|
||||
);
|
||||
$result = $acp_modules->update_module_data($module_data, true);
|
||||
|
||||
// update_module_data can either return a string or an empty array...
|
||||
if (is_string($result))
|
||||
{
|
||||
// Error
|
||||
throw new phpbb_db_migration_exception('MODULE_ERROR', $result);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Success
|
||||
|
||||
// Move the module if requested above/below an existing one
|
||||
if (isset($data['before']) && $data['before'])
|
||||
{
|
||||
$sql = 'SELECT left_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND parent_id = " . (int) $parent . "
|
||||
AND module_langname = '" . $this->db->sql_escape($data['before']) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$to_left = (int) $this->db->sql_fetchfield('left_id');
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = left_id + 2, right_id = right_id + 2
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND left_id >= $to_left
|
||||
AND left_id < {$module_data['left_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = $to_left, right_id = " . ($to_left + 1) . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND module_id = {$module_data['module_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else if (isset($data['after']) && $data['after'])
|
||||
{
|
||||
$sql = 'SELECT right_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND parent_id = " . (int) $parent . "
|
||||
AND module_langname = '" . $this->db->sql_escape($data['after']) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$to_right = (int) $this->db->sql_fetchfield('right_id');
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . "
|
||||
SET left_id = left_id + 2, right_id = right_id + 2
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND left_id >= $to_right
|
||||
AND left_id < {$module_data['left_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'UPDATE ' . $this->modules_table . '
|
||||
SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
|
||||
WHERE module_class = '" . $this->db->sql_escape($class) . "'
|
||||
AND module_id = {$module_data['module_id']}";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the Modules Cache
|
||||
$this->cache->destroy("_modules_$class");
|
||||
}
|
||||
|
||||
/**
|
||||
* Module Remove
|
||||
*
|
||||
* Remove a module
|
||||
*
|
||||
* @param string $class The module class(acp|mcp|ucp)
|
||||
* @param int|string|bool $parent The parent module_id|module_langname(0 for no parent).
|
||||
* Use false to ignore the parent check and check class wide.
|
||||
* @param int|string $module The module id|module_langname
|
||||
* @param string|bool $include_path If you would like to use a custom include path,
|
||||
* specify that here
|
||||
* @return null
|
||||
*/
|
||||
public function remove($class, $parent = 0, $module = '', $include_path = false)
|
||||
{
|
||||
// Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
|
||||
if (is_array($module))
|
||||
{
|
||||
if (isset($module['module_langname']))
|
||||
{
|
||||
// Manual Method
|
||||
return $this->remove($class, $parent, $module['module_langname'], $include_path);
|
||||
}
|
||||
|
||||
// Failed.
|
||||
if (!isset($module['module_basename']))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_NOT_EXIST');
|
||||
}
|
||||
|
||||
// Automatic method
|
||||
$basename = str_replace(array('/', '\\'), '', $module['module_basename']);
|
||||
$class = str_replace(array('/', '\\'), '', $class);
|
||||
|
||||
$include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path;
|
||||
$info_file = "$class/info/$basename.{$this->php_ext}";
|
||||
|
||||
if (!file_exists($include_path . $info_file))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $info_file);
|
||||
}
|
||||
|
||||
$classname = "{$basename}_info";
|
||||
|
||||
if (!class_exists($classname))
|
||||
{
|
||||
include($include_path . $info_file);
|
||||
}
|
||||
|
||||
$info = new $classname;
|
||||
$module_info = $info->module();
|
||||
unset($info);
|
||||
|
||||
foreach ($module_info['modes'] as $mode => $info)
|
||||
{
|
||||
if (!isset($module['modes']) || in_array($mode, $module['modes']))
|
||||
{
|
||||
$this->remove($class, $parent, $info['title']) . '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!$this->exists($class, $parent, $module))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', ((isset($this->user->lang[$module])) ? $this->user->lang[$module] : $module));
|
||||
}
|
||||
|
||||
$parent_sql = '';
|
||||
if ($parent !== false)
|
||||
{
|
||||
// Allows '' to be sent as 0
|
||||
$parent = ($parent) ?: 0;
|
||||
|
||||
if (!is_numeric($parent))
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_langname = '" . $this->db->sql_escape($parent) . "'
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_id = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
// we know it exists from the module_exists check
|
||||
$parent_sql = 'AND parent_id = ' . (int) $module_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$parent_sql = 'AND parent_id = ' . (int) $parent;
|
||||
}
|
||||
}
|
||||
|
||||
$module_ids = array();
|
||||
if (!is_numeric($module))
|
||||
{
|
||||
$sql = 'SELECT module_id
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_langname = '" . $this->db->sql_escape($module) . "'
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'
|
||||
$parent_sql";
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($module_id = $this->db->sql_fetchfield('module_id'))
|
||||
{
|
||||
$module_ids[] = (int) $module_id;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$module_name = $module;
|
||||
}
|
||||
else
|
||||
{
|
||||
$module = (int) $module;
|
||||
$sql = 'SELECT module_langname
|
||||
FROM ' . $this->modules_table . "
|
||||
WHERE module_id = $module
|
||||
AND module_class = '" . $this->db->sql_escape($class) . "'
|
||||
$parent_sql";
|
||||
$result = $this->db->sql_query($sql);
|
||||
$module_name = $this->db->sql_fetchfield('module_id');
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$module_ids[] = $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))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('CANNOT_REMOVE_MODULE', $module_id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->cache->destroy("_modules_$class");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$original_call = array_shift($arguments);
|
||||
|
||||
$call = false;
|
||||
switch ($original_call)
|
||||
{
|
||||
case 'add':
|
||||
$call = 'remove';
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$call = 'add';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($call)
|
||||
{
|
||||
return call_user_func_array(array(&$this, $call), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
622
phpBB/includes/db/migration/tool/permission.php
Normal file
622
phpBB/includes/db/migration/tool/permission.php
Normal file
|
@ -0,0 +1,622 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package migration
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Migration permission management tool
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_interface
|
||||
{
|
||||
/** @var phpbb_auth */
|
||||
protected $auth;
|
||||
|
||||
/** @var phpbb_cache_service */
|
||||
protected $cache;
|
||||
|
||||
/** @var dbal */
|
||||
protected $db;
|
||||
|
||||
/** @var string */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string */
|
||||
protected $php_ext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param phpbb_db_driver $db
|
||||
* @param mixed $cache
|
||||
* @param phpbb_auth $auth
|
||||
* @param string $phpbb_root_path
|
||||
* @param string $php_ext
|
||||
*/
|
||||
public function __construct(phpbb_db_driver $db, phpbb_cache_service $cache, phpbb_auth $auth, $phpbb_root_path, $php_ext)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->cache = $cache;
|
||||
$this->auth = $auth;
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return 'permission';
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission Exists
|
||||
*
|
||||
* Check if a permission (auth) setting exists
|
||||
*
|
||||
* @param string $auth_option The name of the permission (auth) option
|
||||
* @param bool $global True for checking a global permission setting,
|
||||
* False for a local permission setting
|
||||
* @return bool true if it exists, false if not
|
||||
*/
|
||||
public function exists($auth_option, $global = true)
|
||||
{
|
||||
if ($global)
|
||||
{
|
||||
$type_sql = ' AND is_global = 1';
|
||||
}
|
||||
else
|
||||
{
|
||||
$type_sql = ' AND is_local = 1';
|
||||
}
|
||||
|
||||
$sql = 'SELECT auth_option_id
|
||||
FROM ' . ACL_OPTIONS_TABLE . "
|
||||
WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
|
||||
. $type_sql;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if ($row)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission Add
|
||||
*
|
||||
* Add a permission (auth) option
|
||||
*
|
||||
* @param string $auth_option The name of the permission (auth) option
|
||||
* @param bool $global True for checking a global permission setting,
|
||||
* False for a local permission setting
|
||||
* @return null
|
||||
*/
|
||||
public function add($auth_option, $global = true, $copy_from = false)
|
||||
{
|
||||
if ($this->exists($auth_option, $global))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXISTS', $auth_option);
|
||||
}
|
||||
|
||||
// We've added permissions, so set to true to notify the user.
|
||||
$this->permissions_added = true;
|
||||
|
||||
if (!class_exists('auth_admin'))
|
||||
{
|
||||
include($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
|
||||
}
|
||||
$auth_admin = new auth_admin();
|
||||
|
||||
// We have to add a check to see if the !$global (if global, local, and if local, global) permission already exists. If it does, acl_add_option currently has a bug which would break the ACL system, so we are having a work-around here.
|
||||
if ($this->exists($auth_option, !$global))
|
||||
{
|
||||
$sql_ary = array(
|
||||
'is_global' => 1,
|
||||
'is_local' => 1,
|
||||
);
|
||||
$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
|
||||
WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($global)
|
||||
{
|
||||
$auth_admin->acl_add_option(array('global' => array($auth_option)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$auth_admin->acl_add_option(array('local' => array($auth_option)));
|
||||
}
|
||||
}
|
||||
|
||||
// The permission has been added, now we can copy it if needed
|
||||
if ($copy_from && isset($auth_admin->acl_options['id'][$copy_from]))
|
||||
{
|
||||
$old_id = $auth_admin->acl_options['id'][$copy_from];
|
||||
$new_id = $auth_admin->acl_options['id'][$auth_option];
|
||||
|
||||
$tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE);
|
||||
|
||||
foreach ($tables as $table)
|
||||
{
|
||||
$sql = 'SELECT *
|
||||
FROM ' . $table . '
|
||||
WHERE auth_option_id = ' . $old_id;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
$sql_ary = array();
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$row['auth_option_id'] = $new_id;
|
||||
$sql_ary[] = $row;
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (!empty($sql_ary))
|
||||
{
|
||||
$this->db->sql_multi_insert($table, $sql_ary);
|
||||
}
|
||||
}
|
||||
|
||||
$auth_admin->acl_clear_prefetch();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission Remove
|
||||
*
|
||||
* Remove a permission (auth) option
|
||||
*
|
||||
* @param string $auth_option The name of the permission (auth) option
|
||||
* @param bool $global True for checking a global permission setting,
|
||||
* False for a local permission setting
|
||||
* @return null
|
||||
*/
|
||||
public function remove($auth_option, $global = true)
|
||||
{
|
||||
if (!$this->exists($auth_option, $global))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('PERMISSION_NOT_EXIST', $auth_option);
|
||||
}
|
||||
|
||||
if ($global)
|
||||
{
|
||||
$type_sql = ' AND is_global = 1';
|
||||
}
|
||||
else
|
||||
{
|
||||
$type_sql = ' AND is_local = 1';
|
||||
}
|
||||
$sql = 'SELECT auth_option_id, is_global, is_local
|
||||
FROM ' . ACL_OPTIONS_TABLE . "
|
||||
WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
|
||||
$type_sql;
|
||||
$result = $this->db->sql_query($sql);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
$id = (int) $row['auth_option_id'];
|
||||
|
||||
// If it is a local and global permission, do not remove the row! :P
|
||||
if ($row['is_global'] && $row['is_local'])
|
||||
{
|
||||
$sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
|
||||
SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
|
||||
WHERE auth_option_id = ' . $id;
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delete time
|
||||
$tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE, ACL_OPTIONS_TABLE);
|
||||
foreach ($tables as $table)
|
||||
{
|
||||
$this->db->sql_query('DELETE FROM ' . $table . '
|
||||
WHERE auth_option_id = ' . $id);
|
||||
}
|
||||
}
|
||||
|
||||
// Purge the auth cache
|
||||
$this->cache->destroy('_acl_options');
|
||||
$this->auth->acl_clear_prefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new permission role
|
||||
*
|
||||
* @param string $role_name The new role name
|
||||
* @param sting $role_type The type (u_, m_, a_)
|
||||
* @return null
|
||||
*/
|
||||
public function role_add($role_name, $role_type, $role_description = '')
|
||||
{
|
||||
$sql = 'SELECT role_id
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('role_id');
|
||||
|
||||
if ($role_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('ROLE_ALREADY_EXISTS', $old_role_name);
|
||||
}
|
||||
|
||||
$sql = 'SELECT MAX(role_order) AS max_role_order
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_type = '" . $this->db->sql_escape($role_type) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_order = (int) $this->db->sql_fetchfield('max_role_order');
|
||||
$role_order = (!$role_order) ? 1 : $role_order + 1;
|
||||
|
||||
$sql_ary = array(
|
||||
'role_name' => $role_name,
|
||||
'role_description' => $role_description,
|
||||
'role_type' => $role_type,
|
||||
'role_order' => $role_order,
|
||||
);
|
||||
|
||||
$sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the name on a permission role
|
||||
*
|
||||
* @param string $old_role_name The old role name
|
||||
* @param string $new_role_name The new role name
|
||||
* @return null
|
||||
*/
|
||||
public function role_update($old_role_name, $new_role_name)
|
||||
{
|
||||
$sql = 'SELECT role_id
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('role_id');
|
||||
|
||||
if (!$role_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('ROLE_NOT_EXISTS', $old_role_name);
|
||||
}
|
||||
|
||||
$sql = 'UPDATE ' . ACL_ROLES_TABLE . "
|
||||
SET role_name = '" . $this->db->sql_escape($new_role_name) . "'
|
||||
WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a permission role
|
||||
*
|
||||
* @param string $role_name The role name to remove
|
||||
* @return null
|
||||
*/
|
||||
public function role_remove($role_name)
|
||||
{
|
||||
$sql = 'SELECT role_id
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('role_id');
|
||||
|
||||
if (!$role_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $role_name);
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
|
||||
WHERE role_id = ' . $role_id;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$sql = 'DELETE FROM ' . ACL_ROLES_TABLE . '
|
||||
WHERE role_id = ' . $role_id;
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$this->auth->acl_clear_prefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission Set
|
||||
*
|
||||
* Allows you to set permissions for a certain group/role
|
||||
*
|
||||
* @param string $name The name of the role/group
|
||||
* @param string|array $auth_option The auth_option or array of
|
||||
* auth_options you would like to set
|
||||
* @param string $type The type (role|group)
|
||||
* @param bool $has_permission True if you want to give them permission,
|
||||
* false if you want to deny them permission
|
||||
* @return null
|
||||
*/
|
||||
public function permission_set($name, $auth_option, $type = 'role', $has_permission = true)
|
||||
{
|
||||
if (!is_array($auth_option))
|
||||
{
|
||||
$auth_option = array($auth_option);
|
||||
}
|
||||
|
||||
$new_auth = array();
|
||||
$sql = 'SELECT auth_option_id
|
||||
FROM ' . ACL_OPTIONS_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$new_auth[] = (int) $row['auth_option_id'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($new_auth))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$current_auth = array();
|
||||
|
||||
$type = (string) $type; // Prevent PHP bug.
|
||||
|
||||
switch ($type)
|
||||
{
|
||||
case 'role':
|
||||
$sql = 'SELECT role_id
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('role_id');
|
||||
|
||||
if (!$role_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $name);
|
||||
}
|
||||
|
||||
$sql = 'SELECT auth_option_id, auth_setting
|
||||
FROM ' . ACL_ROLES_DATA_TABLE . '
|
||||
WHERE role_id = ' . $role_id;
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$current_auth[$row['auth_option_id']] = $row['auth_setting'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
break;
|
||||
|
||||
case 'group':
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . GROUPS_TABLE . "
|
||||
WHERE group_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$group_id = (int) $this->db->sql_fetchfield('group_id');
|
||||
|
||||
if (!$group_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('GROUP_NOT_EXIST', $name);
|
||||
}
|
||||
|
||||
// If the group has a role set for them we will add the requested permissions to that role.
|
||||
$sql = 'SELECT auth_role_id
|
||||
FROM ' . ACL_GROUPS_TABLE . '
|
||||
WHERE group_id = ' . $group_id . '
|
||||
AND auth_role_id <> 0
|
||||
AND forum_id = 0';
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('auth_role_id');
|
||||
if ($role_id)
|
||||
{
|
||||
$sql = 'SELECT role_name
|
||||
FROM ' . ACL_ROLES_TABLE . '
|
||||
WHERE role_id = ' . $role_id;
|
||||
$this->db->sql_query($sql);
|
||||
$role_name = $this->db->sql_fetchfield('role_name');
|
||||
|
||||
return $this->set($role_name, $auth_option, 'role', $has_permission);
|
||||
}
|
||||
|
||||
$sql = 'SELECT auth_option_id, auth_setting
|
||||
FROM ' . ACL_GROUPS_TABLE . '
|
||||
WHERE group_id = ' . $group_id;
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$current_auth[$row['auth_option_id']] = $row['auth_setting'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
break;
|
||||
}
|
||||
|
||||
$sql_ary = array();
|
||||
switch ($type)
|
||||
{
|
||||
case 'role':
|
||||
foreach ($new_auth as $auth_option_id)
|
||||
{
|
||||
if (!isset($current_auth[$auth_option_id]))
|
||||
{
|
||||
$sql_ary[] = array(
|
||||
'role_id' => $role_id,
|
||||
'auth_option_id' => $auth_option_id,
|
||||
'auth_setting' => $has_permission,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
|
||||
break;
|
||||
|
||||
case 'group':
|
||||
foreach ($new_auth as $auth_option_id)
|
||||
{
|
||||
if (!isset($current_auth[$auth_option_id]))
|
||||
{
|
||||
$sql_ary[] = array(
|
||||
'group_id' => $group_id,
|
||||
'auth_option_id' => $auth_option_id,
|
||||
'auth_setting' => $has_permission,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->auth->acl_clear_prefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission Unset
|
||||
*
|
||||
* Allows you to unset (remove) permissions for a certain group/role
|
||||
*
|
||||
* @param string $name The name of the role/group
|
||||
* @param string|array $auth_option The auth_option or array of
|
||||
* auth_options you would like to set
|
||||
* @param string $type The type (role|group)
|
||||
* @return null
|
||||
*/
|
||||
public function permission_unset($name, $auth_option, $type = 'role')
|
||||
{
|
||||
if (!is_array($auth_option))
|
||||
{
|
||||
$auth_option = array($auth_option);
|
||||
}
|
||||
|
||||
$to_remove = array();
|
||||
$sql = 'SELECT auth_option_id
|
||||
FROM ' . ACL_OPTIONS_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
|
||||
$result = $this->db->sql_query($sql);
|
||||
while ($row = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$to_remove[] = (int) $row['auth_option_id'];
|
||||
}
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (empty($to_remove))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$type = (string) $type; // Prevent PHP bug.
|
||||
|
||||
switch ($type)
|
||||
{
|
||||
case 'role':
|
||||
$sql = 'SELECT role_id
|
||||
FROM ' . ACL_ROLES_TABLE . "
|
||||
WHERE role_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('role_id');
|
||||
|
||||
if (!$role_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $name);
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
|
||||
$this->db->sql_query($sql);
|
||||
break;
|
||||
|
||||
case 'group':
|
||||
$sql = 'SELECT group_id
|
||||
FROM ' . GROUPS_TABLE . "
|
||||
WHERE group_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
$group_id = (int) $this->db->sql_fetchfield('group_id');
|
||||
|
||||
if (!$group_id)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('GROUP_NOT_EXIST', $name);
|
||||
}
|
||||
|
||||
// If the group has a role set for them we will remove the requested permissions from that role.
|
||||
$sql = 'SELECT auth_role_id
|
||||
FROM ' . ACL_GROUPS_TABLE . '
|
||||
WHERE group_id = ' . $group_id . '
|
||||
AND auth_role_id <> 0';
|
||||
$this->db->sql_query($sql);
|
||||
$role_id = (int) $this->db->sql_fetchfield('auth_role_id');
|
||||
if ($role_id)
|
||||
{
|
||||
$sql = 'SELECT role_name
|
||||
FROM ' . ACL_ROLES_TABLE . '
|
||||
WHERE role_id = ' . $role_id;
|
||||
$this->db->sql_query($sql);
|
||||
$role_name = $this->db->sql_fetchfield('role_name');
|
||||
|
||||
return $this->permission_unset($role_name, $auth_option, 'role');
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
|
||||
WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
|
||||
$this->db->sql_query($sql);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->auth->acl_clear_prefetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverse()
|
||||
{
|
||||
$arguments = func_get_args();
|
||||
$original_call = array_shift($arguments);
|
||||
|
||||
$call = false;
|
||||
switch ($original_call)
|
||||
{
|
||||
case 'add':
|
||||
$call = 'remove';
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$call = 'add';
|
||||
break;
|
||||
|
||||
case 'permission_set':
|
||||
$call = 'permission_unset';
|
||||
break;
|
||||
|
||||
case 'permission_unset':
|
||||
$call = 'permission_set';
|
||||
break;
|
||||
|
||||
case 'role_add':
|
||||
$call = 'role_remove';
|
||||
break;
|
||||
|
||||
case 'role_remove':
|
||||
$call = 'role_add';
|
||||
break;
|
||||
|
||||
case 'role_update':
|
||||
// Set to the original value if the current value is what we compared to originally
|
||||
$arguments = array(
|
||||
$arguments[1],
|
||||
$arguments[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($call)
|
||||
{
|
||||
return call_user_func_array(array(&$this, $call), $arguments);
|
||||
}
|
||||
}
|
||||
}
|
762
phpBB/includes/db/migrator.php
Normal file
762
phpBB/includes/db/migrator.php
Normal file
|
@ -0,0 +1,762 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package db
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
if (!defined('IN_PHPBB'))
|
||||
{
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* The migrator is responsible for applying new migrations in the correct order.
|
||||
*
|
||||
* @package db
|
||||
*/
|
||||
class phpbb_db_migrator
|
||||
{
|
||||
/** @var phpbb_config */
|
||||
protected $config;
|
||||
|
||||
/** @var phpbb_db_driver */
|
||||
protected $db;
|
||||
|
||||
/** @var phpbb_db_tools */
|
||||
protected $db_tools;
|
||||
|
||||
/** @var string */
|
||||
protected $table_prefix;
|
||||
|
||||
/** @var string */
|
||||
protected $phpbb_root_path;
|
||||
|
||||
/** @var string */
|
||||
protected $php_ext;
|
||||
|
||||
/** @var string */
|
||||
protected $migrations_table;
|
||||
|
||||
/**
|
||||
* State of all migrations
|
||||
*
|
||||
* (SELECT * FROM migrations table)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migration_state = array();
|
||||
|
||||
/**
|
||||
* Array of all migrations available to be run
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrations = array();
|
||||
|
||||
/**
|
||||
* 'name' and 'class' of the last migration run
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $last_run_migration = false;
|
||||
|
||||
/**
|
||||
* Constructor of the database migrator
|
||||
*/
|
||||
public function __construct(phpbb_config $config, phpbb_db_driver $db, phpbb_db_tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->db_tools = $db_tools;
|
||||
|
||||
$this->migrations_table = $migrations_table;
|
||||
|
||||
$this->phpbb_root_path = $phpbb_root_path;
|
||||
$this->php_ext = $php_ext;
|
||||
|
||||
$this->table_prefix = $table_prefix;
|
||||
|
||||
foreach ($tools as $tool)
|
||||
{
|
||||
$this->tools[$tool->get_name()] = $tool;
|
||||
}
|
||||
|
||||
$this->load_migration_state();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all migrations and their application state from the database.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function load_migration_state()
|
||||
{
|
||||
$this->migration_state = array();
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM " . $this->migrations_table;
|
||||
$result = $this->db->sql_query($sql);
|
||||
|
||||
while ($migration = $this->db->sql_fetchrow($result))
|
||||
{
|
||||
$this->migration_state[$migration['migration_name']] = $migration;
|
||||
|
||||
$this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
|
||||
}
|
||||
|
||||
$this->db->sql_freeresult($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of available migration class names to the given array.
|
||||
*
|
||||
* @param array $class_names An array of migration class names
|
||||
* @return null
|
||||
*/
|
||||
public function set_migrations($class_names)
|
||||
{
|
||||
$this->migrations = $class_names;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds all migrations in a specified directory to the migrations table
|
||||
*
|
||||
* THIS SHOULD NOT GENERALLY BE USED! THIS IS FOR THE PHPBB INSTALLER.
|
||||
* THIS WILL THROW ERRORS IF MIGRATIONS ALREADY EXIST IN THE TABLE, DO NOT CALL MORE THAN ONCE!
|
||||
*
|
||||
* @param string $path Path to migration data files
|
||||
* @param bool $recursive Set to true to also load data files from subdirectories
|
||||
* @return null
|
||||
*/
|
||||
public function populate_migrations_from_directory($path, $recursive = true)
|
||||
{
|
||||
$existing_migrations = $this->migrations;
|
||||
|
||||
$this->migrations = array();
|
||||
$this->load_migrations($path, true, $recursive);
|
||||
|
||||
foreach ($this->migrations as $name)
|
||||
{
|
||||
if ($this->migration_state($name) === false)
|
||||
{
|
||||
$state = array(
|
||||
'migration_depends_on' => $name::depends_on(),
|
||||
'migration_schema_done' => true,
|
||||
'migration_data_done' => true,
|
||||
'migration_data_state' => '',
|
||||
'migration_start_time' => time(),
|
||||
'migration_end_time' => time(),
|
||||
);
|
||||
$this->insert_migration($name, $state);
|
||||
}
|
||||
}
|
||||
|
||||
$this->migrations = $existing_migrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load migration data files from a directory
|
||||
*
|
||||
* Migration data files loaded with this function MUST contain
|
||||
* ONLY ONE class in them (or an exception will be thrown).
|
||||
*
|
||||
* @param string $path Path to migration data files
|
||||
* @param bool $check_fulfillable If TRUE (default), we will check
|
||||
* if all of the migrations are fulfillable after loading them.
|
||||
* If FALSE, we will not check. You SHOULD check at least once
|
||||
* to prevent errors (if including multiple directories, check
|
||||
* with the last call to prevent throwing errors unnecessarily).
|
||||
* @param bool $recursive Set to true to also load data files from subdirectories
|
||||
* @return array Array of migration names
|
||||
*/
|
||||
public function load_migrations($path, $check_fulfillable = true, $recursive = true)
|
||||
{
|
||||
if (!is_dir($path))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('DIRECTORY INVALID', $path);
|
||||
}
|
||||
|
||||
$handle = opendir($path);
|
||||
while (($file = readdir($handle)) !== false)
|
||||
{
|
||||
if ($file == '.' || $file == '..')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recursion through subdirectories
|
||||
if (is_dir($path . $file) && $recursive)
|
||||
{
|
||||
$this->load_migrations($path . $file . '/', $check_fulfillable, $recursive);
|
||||
}
|
||||
|
||||
if (strpos($file, '_') !== 0 && strrpos($file, '.' . $this->php_ext) === (strlen($file) - strlen($this->php_ext) - 1))
|
||||
{
|
||||
// We try to find what class existed by comparing the classes declared before and after including the file.
|
||||
$declared_classes = get_declared_classes();
|
||||
|
||||
include ($path . $file);
|
||||
|
||||
$added_classes = array_diff(get_declared_classes(), $declared_classes);
|
||||
|
||||
if (
|
||||
// If two classes have been added and phpbb_db_migration is one of them, we've only added one real migration
|
||||
!(sizeof($added_classes) == 2 && in_array('phpbb_db_migration', $added_classes)) &&
|
||||
// Otherwise there should only be one class added
|
||||
sizeof($added_classes) != 1
|
||||
)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION DATA FILE INVALID', $path . $file);
|
||||
}
|
||||
|
||||
$name = array_pop($added_classes);
|
||||
|
||||
if (!in_array($name, $this->migrations))
|
||||
{
|
||||
$this->migrations[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($check_fulfillable)
|
||||
{
|
||||
foreach ($this->migrations as $name)
|
||||
{
|
||||
if ($this->unfulfillable($name))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION NOT FULFILLABLE', $name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->migrations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single update step from the next migration to be applied.
|
||||
*
|
||||
* The update step can either be a schema or a (partial) data update. To
|
||||
* check if update() needs to be called again use the finished() method.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
foreach ($this->migrations as $name)
|
||||
{
|
||||
if (!isset($this->migration_state[$name]) ||
|
||||
!$this->migration_state[$name]['migration_schema_done'] ||
|
||||
!$this->migration_state[$name]['migration_data_done'])
|
||||
{
|
||||
if (!$this->try_apply($name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to apply a step of the given migration or one of its dependencies
|
||||
*
|
||||
* @param string The class name of the migration
|
||||
* @return bool Whether any update step was successfully run
|
||||
*/
|
||||
protected function try_apply($name)
|
||||
{
|
||||
if (!class_exists($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration = $this->get_migration($name);
|
||||
|
||||
$state = (isset($this->migration_state[$name])) ?
|
||||
$this->migration_state[$name] :
|
||||
array(
|
||||
'migration_depends_on' => $migration->depends_on(),
|
||||
'migration_schema_done' => false,
|
||||
'migration_data_done' => false,
|
||||
'migration_data_state' => '',
|
||||
'migration_start_time' => 0,
|
||||
'migration_end_time' => 0,
|
||||
);
|
||||
|
||||
foreach ($state['migration_depends_on'] as $depend)
|
||||
{
|
||||
if (!isset($this->migration_state[$depend]) ||
|
||||
!$this->migration_state[$depend]['migration_schema_done'] ||
|
||||
!$this->migration_state[$depend]['migration_data_done'])
|
||||
{
|
||||
return $this->try_apply($depend);
|
||||
}
|
||||
}
|
||||
|
||||
$this->last_run_migration = array(
|
||||
'name' => $name,
|
||||
'class' => $migration,
|
||||
);
|
||||
|
||||
if ($migration->effectively_installed())
|
||||
{
|
||||
$state = array(
|
||||
'migration_depends_on' => $migration->depends_on(),
|
||||
'migration_schema_done' => true,
|
||||
'migration_data_done' => true,
|
||||
'migration_data_state' => '',
|
||||
'migration_start_time' => 0,
|
||||
'migration_end_time' => 0,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset($this->migration_state[$name]))
|
||||
{
|
||||
$state['migration_start_time'] = time();
|
||||
$this->insert_migration($name, $state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$state['migration_schema_done'])
|
||||
{
|
||||
$this->apply_schema_changes($migration->update_schema());
|
||||
$state['migration_schema_done'] = true;
|
||||
}
|
||||
else if (!$state['migration_data_done'])
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
|
||||
|
||||
$state['migration_data_state'] = ($result === true) ? '' : $result;
|
||||
$state['migration_data_done'] = ($result === true);
|
||||
$state['migration_end_time'] = ($result === true) ? time() : 0;
|
||||
}
|
||||
catch (phpbb_db_migration_exception $e)
|
||||
{
|
||||
// Revert the schema changes
|
||||
$this->revert($name);
|
||||
|
||||
// Rethrow exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$insert = $state;
|
||||
$insert['migration_depends_on'] = serialize($state['migration_depends_on']);
|
||||
$sql = 'UPDATE ' . $this->migrations_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $insert) . "
|
||||
WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$this->migration_state[$name] = $state;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a single revert step from the last migration installed
|
||||
*
|
||||
* YOU MUST ADD/SET ALL MIGRATIONS THAT COULD BE DEPENDENT ON THE MIGRATION TO REVERT TO BEFORE CALLING THIS METHOD!
|
||||
* The revert step can either be a schema or a (partial) data revert. To
|
||||
* check if revert() needs to be called again use the migration_state() method.
|
||||
*
|
||||
* @param string $migration String migration name to revert (including any that depend on this migration)
|
||||
* @return null
|
||||
*/
|
||||
public function revert($migration)
|
||||
{
|
||||
if (!isset($this->migration_state[$migration]))
|
||||
{
|
||||
// Not installed
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->migration_state as $name => $state)
|
||||
{
|
||||
if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on']))
|
||||
{
|
||||
$this->revert($name);
|
||||
}
|
||||
}
|
||||
|
||||
$this->try_revert($migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to revert a step of the given migration or one of its dependencies
|
||||
*
|
||||
* @param string The class name of the migration
|
||||
* @return bool Whether any update step was successfully run
|
||||
*/
|
||||
protected function try_revert($name)
|
||||
{
|
||||
if (!class_exists($name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration = $this->get_migration($name);
|
||||
|
||||
$state = $this->migration_state[$name];
|
||||
|
||||
$this->last_run_migration = array(
|
||||
'name' => $name,
|
||||
'class' => $migration,
|
||||
);
|
||||
|
||||
if ($state['migration_data_done'])
|
||||
{
|
||||
if ($state['migration_data_state'] !== 'revert_data')
|
||||
{
|
||||
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true);
|
||||
|
||||
$state['migration_data_state'] = ($result === true) ? 'revert_data' : $result;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = $this->process_data_step($migration->revert_data(), $state['migration_data_state'], false);
|
||||
|
||||
$state['migration_data_state'] = ($result === true) ? '' : $result;
|
||||
$state['migration_data_done'] = ($result === true) ? false : true;
|
||||
}
|
||||
|
||||
$insert = $state;
|
||||
$insert['migration_depends_on'] = serialize($state['migration_depends_on']);
|
||||
$sql = 'UPDATE ' . $this->migrations_table . '
|
||||
SET ' . $this->db->sql_build_array('UPDATE', $insert) . "
|
||||
WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$this->migration_state[$name] = $state;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->apply_schema_changes($migration->revert_schema());
|
||||
|
||||
$sql = 'DELETE FROM ' . $this->migrations_table . "
|
||||
WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
unset($this->migration_state[$name]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply schema changes from a migration
|
||||
*
|
||||
* Just calls db_tools->perform_schema_changes
|
||||
*
|
||||
* @param array $schema_changes from migration
|
||||
*/
|
||||
protected function apply_schema_changes($schema_changes)
|
||||
{
|
||||
$this->db_tools->perform_schema_changes($schema_changes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the data step of the migration
|
||||
*
|
||||
* @param array $steps The steps to run
|
||||
* @param bool|string $state Current state of the migration
|
||||
* @param bool $revert true to revert a data step
|
||||
* @return bool|string migration state. True if completed, serialized array if not finished
|
||||
*/
|
||||
protected function process_data_step($steps, $state, $revert = false)
|
||||
{
|
||||
$state = ($state) ? unserialize($state) : false;
|
||||
|
||||
foreach ($steps as $step_identifier => $step)
|
||||
{
|
||||
$last_result = false;
|
||||
if ($state)
|
||||
{
|
||||
// Continue until we reach the step that matches the last step called
|
||||
if ($state['step'] != $step_identifier)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We send the result from last time to the callable function
|
||||
$last_result = $state['result'];
|
||||
|
||||
// Set state to false since we reached the point we were at
|
||||
$state = false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Result will be null or true if everything completed correctly
|
||||
$result = $this->run_step($step, $last_result, $revert);
|
||||
if ($result !== null && $result !== true)
|
||||
{
|
||||
return serialize(array(
|
||||
'result' => $result,
|
||||
'step' => $step_identifier,
|
||||
));
|
||||
}
|
||||
}
|
||||
catch (phpbb_db_migration_exception $e)
|
||||
{
|
||||
// We should try rolling back here
|
||||
foreach ($steps as $reverse_step_identifier => $reverse_step)
|
||||
{
|
||||
// If we've reached the current step we can break because we reversed everything that was run
|
||||
if ($reverse_step_identifier == $step_identifier)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Reverse the step that was run
|
||||
$result = $this->run_step($reverse_step, false, !$revert);
|
||||
}
|
||||
|
||||
// rethrow the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single step
|
||||
*
|
||||
* An exception should be thrown if an error occurs
|
||||
*
|
||||
* @param mixed $step Data step from migration
|
||||
* @param mixed $last_result Result to pass to the callable (only for 'custom' method)
|
||||
* @param bool $reverse False to install, True to attempt uninstallation by reversing the call
|
||||
* @return null
|
||||
*/
|
||||
protected function run_step($step, $last_result = false, $reverse = false)
|
||||
{
|
||||
$callable_and_parameters = $this->get_callable_from_step($step, $last_result, $reverse);
|
||||
|
||||
if ($callable_and_parameters === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$callable = $callable_and_parameters[0];
|
||||
$parameters = $callable_and_parameters[1];
|
||||
|
||||
return call_user_func_array($callable, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a callable statement from a data step
|
||||
*
|
||||
* @param array $step Data step from migration
|
||||
* @param mixed $last_result Result to pass to the callable (only for 'custom' method)
|
||||
* @param bool $reverse False to install, True to attempt uninstallation by reversing the call
|
||||
* @return array Array with parameters for call_user_func_array(), 0 is the callable, 1 is parameters
|
||||
*/
|
||||
protected function get_callable_from_step(array $step, $last_result = false, $reverse = false)
|
||||
{
|
||||
$type = $step[0];
|
||||
$parameters = $step[1];
|
||||
|
||||
$parts = explode('.', $type);
|
||||
|
||||
$class = $parts[0];
|
||||
$method = false;
|
||||
|
||||
if (isset($parts[1]))
|
||||
{
|
||||
$method = $parts[1];
|
||||
}
|
||||
|
||||
switch ($class)
|
||||
{
|
||||
case 'if':
|
||||
if (!isset($parameters[0]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_MISSING_CONDITION', $step);
|
||||
}
|
||||
|
||||
if (!isset($parameters[1]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
|
||||
}
|
||||
|
||||
$condition = $parameters[0];
|
||||
|
||||
if (!$condition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$step = $parameters[1];
|
||||
|
||||
return $this->get_callable_from_step($step);
|
||||
break;
|
||||
case 'custom':
|
||||
if (!is_callable($parameters[0]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step);
|
||||
}
|
||||
|
||||
return array(
|
||||
$parameters[0],
|
||||
array($last_result),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!$method)
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_UNKNOWN_TYPE', $step);
|
||||
}
|
||||
|
||||
if (!isset($this->tools[$class]))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_UNDEFINED_TOOL', $step);
|
||||
}
|
||||
|
||||
if (!method_exists(get_class($this->tools[$class]), $method))
|
||||
{
|
||||
throw new phpbb_db_migration_exception('MIGRATION_INVALID_DATA_UNDEFINED_METHOD', $step);
|
||||
}
|
||||
|
||||
// Attempt to reverse operations
|
||||
if ($reverse)
|
||||
{
|
||||
array_unshift($parameters, $method);
|
||||
|
||||
return array(
|
||||
array($this->tools[$class], 'reverse'),
|
||||
$parameters,
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
array($this->tools[$class], $method),
|
||||
$parameters,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert migration row into the database
|
||||
*
|
||||
* @param string $name Name of the migration
|
||||
* @param array $state
|
||||
* @return null
|
||||
*/
|
||||
protected function insert_migration($name, $state)
|
||||
{
|
||||
$migration_row = $state;
|
||||
$migration_row['migration_name'] = $name;
|
||||
$migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
|
||||
|
||||
$sql = 'INSERT INTO ' . $this->migrations_table . '
|
||||
' . $this->db->sql_build_array('INSERT', $migration_row);
|
||||
$this->db->sql_query($sql);
|
||||
|
||||
$this->migration_state[$name] = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a migration's dependencies can even theoretically be satisfied.
|
||||
*
|
||||
* @param string $name The class name of the migration
|
||||
* @return bool Whether the migration cannot be fulfilled
|
||||
*/
|
||||
public function unfulfillable($name)
|
||||
{
|
||||
if (isset($this->migration_state[$name]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!class_exists($name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
$migration = $this->get_migration($name);
|
||||
$depends = $migration->depends_on();
|
||||
|
||||
foreach ($depends as $depend)
|
||||
{
|
||||
if ($this->unfulfillable($depend))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all available, fulfillable migrations have been applied.
|
||||
*
|
||||
* @return bool Whether the migrations have been applied
|
||||
*/
|
||||
public function finished()
|
||||
{
|
||||
foreach ($this->migrations as $name)
|
||||
{
|
||||
if (!isset($this->migration_state[$name]))
|
||||
{
|
||||
// skip unfulfillable migrations, but fulfillables mean we
|
||||
// are not finished yet
|
||||
if ($this->unfulfillable($name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration = $this->migration_state[$name];
|
||||
|
||||
if (!$migration['migration_schema_done'] || !$migration['migration_data_done'])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a migration state (whether it is installed and to what extent)
|
||||
*
|
||||
* @param string $migration String migration name to check if it is installed
|
||||
* @return bool|array False if the migration has not at all been installed, array
|
||||
*/
|
||||
public function migration_state($migration)
|
||||
{
|
||||
if (!isset($this->migration_state[$migration]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->migration_state[$migration];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get a migration
|
||||
*
|
||||
* @param string $name Name of the migration
|
||||
* @return phpbb_db_migration
|
||||
*/
|
||||
protected function get_migration($name)
|
||||
{
|
||||
return new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
|
||||
}
|
||||
}
|
|
@ -586,6 +586,20 @@ CREATE TABLE phpbb_moderator_cache (
|
|||
CREATE INDEX phpbb_moderator_cache_disp_idx ON phpbb_moderator_cache(display_on_index);;
|
||||
CREATE INDEX phpbb_moderator_cache_forum_id ON phpbb_moderator_cache(forum_id);;
|
||||
|
||||
# Table: 'phpbb_migrations'
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL,
|
||||
migration_depends_on BLOB SUB_TYPE TEXT CHARACTER SET NONE DEFAULT '' NOT NULL,
|
||||
migration_schema_done INTEGER DEFAULT 0 NOT NULL,
|
||||
migration_data_done INTEGER DEFAULT 0 NOT NULL,
|
||||
migration_data_state BLOB SUB_TYPE TEXT CHARACTER SET NONE DEFAULT '' NOT NULL,
|
||||
migration_start_time INTEGER DEFAULT 0 NOT NULL,
|
||||
migration_end_time INTEGER DEFAULT 0 NOT NULL
|
||||
);;
|
||||
|
||||
ALTER TABLE phpbb_migrations ADD PRIMARY KEY (migration_name);;
|
||||
|
||||
|
||||
# Table: 'phpbb_modules'
|
||||
CREATE TABLE phpbb_modules (
|
||||
module_id INTEGER NOT NULL,
|
||||
|
|
|
@ -716,6 +716,28 @@ CREATE INDEX [forum_id] ON [phpbb_moderator_cache]([forum_id]) ON [PRIMARY]
|
|||
GO
|
||||
|
||||
|
||||
/*
|
||||
Table: 'phpbb_migrations'
|
||||
*/
|
||||
CREATE TABLE [phpbb_migrations] (
|
||||
[migration_name] [varchar] (255) DEFAULT ('') NOT NULL ,
|
||||
[migration_depends_on] [varchar] (8000) DEFAULT ('') NOT NULL ,
|
||||
[migration_schema_done] [int] DEFAULT (0) NOT NULL ,
|
||||
[migration_data_done] [int] DEFAULT (0) NOT NULL ,
|
||||
[migration_data_state] [varchar] (8000) DEFAULT ('') NOT NULL ,
|
||||
[migration_start_time] [int] DEFAULT (0) NOT NULL ,
|
||||
[migration_end_time] [int] DEFAULT (0) NOT NULL
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
ALTER TABLE [phpbb_migrations] WITH NOCHECK ADD
|
||||
CONSTRAINT [PK_phpbb_migrations] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[migration_name]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
|
||||
/*
|
||||
Table: 'phpbb_modules'
|
||||
*/
|
||||
|
|
|
@ -410,6 +410,19 @@ CREATE TABLE phpbb_moderator_cache (
|
|||
);
|
||||
|
||||
|
||||
# Table: 'phpbb_migrations'
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name varbinary(255) DEFAULT '' NOT NULL,
|
||||
migration_depends_on blob NOT NULL,
|
||||
migration_schema_done tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_data_done tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_data_state blob NOT NULL,
|
||||
migration_start_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_end_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (migration_name)
|
||||
);
|
||||
|
||||
|
||||
# Table: 'phpbb_modules'
|
||||
CREATE TABLE phpbb_modules (
|
||||
module_id mediumint(8) UNSIGNED NOT NULL auto_increment,
|
||||
|
|
|
@ -410,6 +410,19 @@ CREATE TABLE phpbb_moderator_cache (
|
|||
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
|
||||
|
||||
|
||||
# Table: 'phpbb_migrations'
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name varchar(255) DEFAULT '' NOT NULL,
|
||||
migration_depends_on text NOT NULL,
|
||||
migration_schema_done tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_data_done tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_data_state text NOT NULL,
|
||||
migration_start_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
migration_end_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
|
||||
PRIMARY KEY (migration_name)
|
||||
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
|
||||
|
||||
|
||||
# Table: 'phpbb_modules'
|
||||
CREATE TABLE phpbb_modules (
|
||||
module_id mediumint(8) UNSIGNED NOT NULL auto_increment,
|
||||
|
|
|
@ -798,6 +798,22 @@ CREATE INDEX phpbb_moderator_cache_disp_idx ON phpbb_moderator_cache (display_on
|
|||
CREATE INDEX phpbb_moderator_cache_forum_id ON phpbb_moderator_cache (forum_id)
|
||||
/
|
||||
|
||||
/*
|
||||
Table: 'phpbb_migrations'
|
||||
*/
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name varchar2(255) DEFAULT '' ,
|
||||
migration_depends_on clob DEFAULT '' ,
|
||||
migration_schema_done number(1) DEFAULT '0' NOT NULL,
|
||||
migration_data_done number(1) DEFAULT '0' NOT NULL,
|
||||
migration_data_state clob DEFAULT '' ,
|
||||
migration_start_time number(11) DEFAULT '0' NOT NULL,
|
||||
migration_end_time number(11) DEFAULT '0' NOT NULL,
|
||||
CONSTRAINT pk_phpbb_migrations PRIMARY KEY (migration_name)
|
||||
)
|
||||
/
|
||||
|
||||
|
||||
/*
|
||||
Table: 'phpbb_modules'
|
||||
*/
|
||||
|
|
|
@ -572,6 +572,21 @@ CREATE TABLE phpbb_moderator_cache (
|
|||
CREATE INDEX phpbb_moderator_cache_disp_idx ON phpbb_moderator_cache (display_on_index);
|
||||
CREATE INDEX phpbb_moderator_cache_forum_id ON phpbb_moderator_cache (forum_id);
|
||||
|
||||
/*
|
||||
Table: 'phpbb_migrations'
|
||||
*/
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name varchar(255) DEFAULT '' NOT NULL,
|
||||
migration_depends_on varchar(8000) DEFAULT '' NOT NULL,
|
||||
migration_schema_done INT2 DEFAULT '0' NOT NULL CHECK (migration_schema_done >= 0),
|
||||
migration_data_done INT2 DEFAULT '0' NOT NULL CHECK (migration_data_done >= 0),
|
||||
migration_data_state varchar(8000) DEFAULT '' NOT NULL,
|
||||
migration_start_time INT4 DEFAULT '0' NOT NULL CHECK (migration_start_time >= 0),
|
||||
migration_end_time INT4 DEFAULT '0' NOT NULL CHECK (migration_end_time >= 0),
|
||||
PRIMARY KEY (migration_name)
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
Table: 'phpbb_modules'
|
||||
*/
|
||||
|
|
|
@ -398,6 +398,19 @@ CREATE TABLE phpbb_moderator_cache (
|
|||
CREATE INDEX phpbb_moderator_cache_disp_idx ON phpbb_moderator_cache (display_on_index);
|
||||
CREATE INDEX phpbb_moderator_cache_forum_id ON phpbb_moderator_cache (forum_id);
|
||||
|
||||
# Table: 'phpbb_migrations'
|
||||
CREATE TABLE phpbb_migrations (
|
||||
migration_name varchar(255) NOT NULL DEFAULT '',
|
||||
migration_depends_on text(65535) NOT NULL DEFAULT '',
|
||||
migration_schema_done INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
migration_data_done INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
migration_data_state text(65535) NOT NULL DEFAULT '',
|
||||
migration_start_time INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
migration_end_time INTEGER UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (migration_name)
|
||||
);
|
||||
|
||||
|
||||
# Table: 'phpbb_modules'
|
||||
CREATE TABLE phpbb_modules (
|
||||
module_id INTEGER PRIMARY KEY NOT NULL ,
|
||||
|
|
29
tests/dbal/fixtures/migrator.xml
Normal file
29
tests/dbal/fixtures/migrator.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<dataset>
|
||||
<table name="phpbb_migrations">
|
||||
<column>migration_name</column>
|
||||
<column>migration_depends_on</column>
|
||||
<column>migration_schema_done</column>
|
||||
<column>migration_data_done</column>
|
||||
<column>migration_data_state</column>
|
||||
<column>migration_start_time</column>
|
||||
<column>migration_end_time</column>
|
||||
<row>
|
||||
<value>installed_migration</value>
|
||||
<value></value>
|
||||
<value>1</value>
|
||||
<value>1</value>
|
||||
<value></value>
|
||||
<value>1234</value>
|
||||
<value>5678</value>
|
||||
</row>
|
||||
</table>
|
||||
<table name="phpbb_config">
|
||||
<column>config_name</column>
|
||||
<column>config_value</column>
|
||||
<row>
|
||||
<value>foo</value>
|
||||
<value>bar</value>
|
||||
</row>
|
||||
</table>
|
||||
</dataset>
|
42
tests/dbal/fixtures/migrator_module.xml
Normal file
42
tests/dbal/fixtures/migrator_module.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<dataset>
|
||||
<table name="phpbb_modules">
|
||||
<column>module_id</column>
|
||||
<column>module_enabled</column>
|
||||
<column>module_display</column>
|
||||
<column>module_basename</column>
|
||||
<column>module_class</column>
|
||||
<column>parent_id</column>
|
||||
<column>left_id</column>
|
||||
<column>right_id</column>
|
||||
<column>module_langname</column>
|
||||
<column>module_mode</column>
|
||||
<column>module_auth</column>
|
||||
<row>
|
||||
<value>1</value>
|
||||
<value>1</value>
|
||||
<value>1</value>
|
||||
<value></value>
|
||||
<value>acp</value>
|
||||
<value>0</value>
|
||||
<value>1</value>
|
||||
<value>4</value>
|
||||
<value>ACP_CAT</value>
|
||||
<value></value>
|
||||
<value></value>
|
||||
</row>
|
||||
<row>
|
||||
<value>2</value>
|
||||
<value>1</value>
|
||||
<value>1</value>
|
||||
<value>acp_test</value>
|
||||
<value>acp</value>
|
||||
<value>1</value>
|
||||
<value>2</value>
|
||||
<value>3</value>
|
||||
<value>ACP_MODULE</value>
|
||||
<value>test</value>
|
||||
<value></value>
|
||||
</row>
|
||||
</table>
|
||||
</dataset>
|
31
tests/dbal/fixtures/migrator_permission.xml
Normal file
31
tests/dbal/fixtures/migrator_permission.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<dataset>
|
||||
<table name="phpbb_acl_options">
|
||||
<column>auth_option_id</column>
|
||||
<column>auth_option</column>
|
||||
<column>is_global</column>
|
||||
<column>is_local</column>
|
||||
<column>founder_only</column>
|
||||
<row>
|
||||
<value>1</value>
|
||||
<value>global</value>
|
||||
<value>1</value>
|
||||
<value>0</value>
|
||||
<value>0</value>
|
||||
</row>
|
||||
<row>
|
||||
<value>2</value>
|
||||
<value>local</value>
|
||||
<value>0</value>
|
||||
<value>1</value>
|
||||
<value>0</value>
|
||||
</row>
|
||||
<row>
|
||||
<value>3</value>
|
||||
<value>both</value>
|
||||
<value>1</value>
|
||||
<value>1</value>
|
||||
<value>0</value>
|
||||
</row>
|
||||
</table>
|
||||
</dataset>
|
27
tests/dbal/migration/dummy.php
Normal file
27
tests/dbal/migration/dummy.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_dummy extends phpbb_db_migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('installed_migration');
|
||||
}
|
||||
|
||||
function update_schema()
|
||||
{
|
||||
return array(
|
||||
'add_columns' => array(
|
||||
'phpbb_config' => array(
|
||||
'extra_column' => array('UINT', 1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
41
tests/dbal/migration/fail.php
Normal file
41
tests/dbal/migration/fail.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package migration
|
||||
* @copyright (c) 2012 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_fail extends phpbb_db_migration
|
||||
{
|
||||
function update_schema()
|
||||
{
|
||||
return array(
|
||||
'add_columns' => array(
|
||||
$this->table_prefix . 'config' => array(
|
||||
'test_column' => array('BOOL', 1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function revert_schema()
|
||||
{
|
||||
return array(
|
||||
'drop_columns' => array(
|
||||
$this->table_prefix . 'config' => array(
|
||||
'test_column',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.add', array('foobar3', true)),
|
||||
array('config.update', array('does_not_exist', true)),
|
||||
);
|
||||
}
|
||||
}
|
44
tests/dbal/migration/if.php
Normal file
44
tests/dbal/migration/if.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_if extends phpbb_db_migration
|
||||
{
|
||||
function update_schema()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
return array(
|
||||
array('if', array(
|
||||
true,
|
||||
array('custom', array(array(&$this, 'test_true'))),
|
||||
)),
|
||||
array('if', array(
|
||||
false,
|
||||
array('custom', array(array(&$this, 'test_false'))),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
function test_true()
|
||||
{
|
||||
global $migrator_test_if_true_failed;
|
||||
|
||||
$migrator_test_if_true_failed = false;
|
||||
}
|
||||
|
||||
function test_false()
|
||||
{
|
||||
global $migrator_test_if_false_failed;
|
||||
|
||||
$migrator_test_if_false_failed = true;
|
||||
}
|
||||
}
|
30
tests/dbal/migration/installed.php
Normal file
30
tests/dbal/migration/installed.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_installed extends phpbb_db_migration
|
||||
{
|
||||
function effectively_installed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
return array(
|
||||
array('custom', array(array(&$this, 'test'))),
|
||||
);
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
global $migrator_test_installed_failed;
|
||||
|
||||
$migrator_test_installed_failed = true;
|
||||
}
|
||||
}
|
38
tests/dbal/migration/recall.php
Normal file
38
tests/dbal/migration/recall.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_recall extends phpbb_db_migration
|
||||
{
|
||||
function update_schema()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
return array(
|
||||
array('custom', array(array(&$this, 'test_call'))),
|
||||
);
|
||||
}
|
||||
|
||||
// This function should be called 10 times
|
||||
function test_call($input)
|
||||
{
|
||||
global $migrator_test_call_input;
|
||||
|
||||
$migrator_test_call_input = (int) $input;
|
||||
|
||||
if ($migrator_test_call_input < 10)
|
||||
{
|
||||
return ($migrator_test_call_input + 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
40
tests/dbal/migration/revert.php
Normal file
40
tests/dbal/migration/revert.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_revert extends phpbb_db_migration
|
||||
{
|
||||
function update_schema()
|
||||
{
|
||||
return array(
|
||||
'add_columns' => array(
|
||||
'phpbb_config' => array(
|
||||
'bar_column' => array('UINT', 1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function revert_schema()
|
||||
{
|
||||
return array(
|
||||
'drop_columns' => array(
|
||||
'phpbb_config' => array(
|
||||
'bar_column',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
return array(
|
||||
array('config.add', array('foobartest', 0)),
|
||||
);
|
||||
}
|
||||
}
|
16
tests/dbal/migration/revert_with_dependency.php
Normal file
16
tests/dbal/migration/revert_with_dependency.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_revert_with_dependency extends phpbb_db_migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('phpbb_dbal_migration_revert');
|
||||
}
|
||||
}
|
26
tests/dbal/migration/unfulfillable.php
Normal file
26
tests/dbal/migration/unfulfillable.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
class phpbb_dbal_migration_unfulfillable extends phpbb_db_migration
|
||||
{
|
||||
static public function depends_on()
|
||||
{
|
||||
return array('installed_migration', 'phpbb_dbal_migration_dummy', 'non_existant_migration');
|
||||
}
|
||||
|
||||
function update_schema()
|
||||
{
|
||||
trigger_error('Schema update of migration with unfulfillable dependency was run!');
|
||||
}
|
||||
|
||||
function update_data()
|
||||
{
|
||||
trigger_error('Data update of migration with unfulfillable dependency was run!');
|
||||
}
|
||||
}
|
257
tests/dbal/migrator_test.php
Normal file
257
tests/dbal/migrator_test.php
Normal file
|
@ -0,0 +1,257 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migrator.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/migration.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/db_tools.php';
|
||||
|
||||
require_once dirname(__FILE__) . '/migration/dummy.php';
|
||||
require_once dirname(__FILE__) . '/migration/unfulfillable.php';
|
||||
require_once dirname(__FILE__) . '/migration/if.php';
|
||||
require_once dirname(__FILE__) . '/migration/recall.php';
|
||||
require_once dirname(__FILE__) . '/migration/revert.php';
|
||||
require_once dirname(__FILE__) . '/migration/revert_with_dependency.php';
|
||||
require_once dirname(__FILE__) . '/migration/fail.php';
|
||||
require_once dirname(__FILE__) . '/migration/installed.php';
|
||||
|
||||
class phpbb_dbal_migrator_test extends phpbb_database_test_case
|
||||
{
|
||||
protected $db;
|
||||
protected $db_tools;
|
||||
protected $migrator;
|
||||
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/migrator.xml');
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->db = $this->new_dbal();
|
||||
$this->db_tools = new phpbb_db_tools($this->db);
|
||||
|
||||
$this->config = new phpbb_config_db($this->db, new phpbb_mock_cache, 'phpbb_config');
|
||||
|
||||
$tools = array(
|
||||
new phpbb_db_migration_tool_config($this->config),
|
||||
);
|
||||
$this->migrator = new phpbb_db_migrator($this->config, $this->db, $this->db_tools, 'phpbb_migrations', dirname(__FILE__) . '/../../phpBB/', 'php', 'phpbb_', $tools);
|
||||
}
|
||||
|
||||
public function test_update()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_dummy'));
|
||||
|
||||
// schema
|
||||
$this->migrator->update();
|
||||
$this->assertFalse($this->migrator->finished());
|
||||
|
||||
$this->assertSqlResultEquals(
|
||||
array(array('success' => '1')),
|
||||
"SELECT 1 as success
|
||||
FROM phpbb_migrations
|
||||
WHERE migration_name = 'phpbb_dbal_migration_dummy'
|
||||
AND migration_start_time >= " . (time() - 1) . "
|
||||
AND migration_start_time <= " . (time() + 1),
|
||||
'Start time set correctly'
|
||||
);
|
||||
|
||||
// data
|
||||
$this->migrator->update();
|
||||
$this->assertTrue($this->migrator->finished());
|
||||
|
||||
$this->assertSqlResultEquals(
|
||||
array(array('extra_column' => '1')),
|
||||
"SELECT extra_column FROM phpbb_config WHERE config_name = 'foo'",
|
||||
'Dummy migration created extra_column with value 1 in all rows.'
|
||||
);
|
||||
|
||||
$this->assertSqlResultEquals(
|
||||
array(array('success' => '1')),
|
||||
"SELECT 1 as success
|
||||
FROM phpbb_migrations
|
||||
WHERE migration_name = 'phpbb_dbal_migration_dummy'
|
||||
AND migration_start_time <= migration_end_time
|
||||
AND migration_end_time >= " . (time() - 1) . "
|
||||
AND migration_end_time <= " . (time() + 1),
|
||||
'End time set correctly'
|
||||
);
|
||||
|
||||
// cleanup
|
||||
$this->db_tools->sql_column_remove('phpbb_config', 'extra_column');
|
||||
}
|
||||
|
||||
public function test_unfulfillable()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_unfulfillable', 'phpbb_dbal_migration_dummy'));
|
||||
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
}
|
||||
|
||||
$this->assertTrue($this->migrator->finished());
|
||||
|
||||
$this->assertSqlResultEquals(
|
||||
array(array('extra_column' => '1')),
|
||||
"SELECT extra_column FROM phpbb_config WHERE config_name = 'foo'",
|
||||
'Dummy migration was run, even though an unfulfillable migration was found.'
|
||||
);
|
||||
|
||||
$this->db_tools->sql_column_remove('phpbb_config', 'extra_column');
|
||||
}
|
||||
|
||||
public function test_if()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_if'));
|
||||
|
||||
// Don't like this, but I'm not sure there is any other way to do this
|
||||
global $migrator_test_if_true_failed, $migrator_test_if_false_failed;
|
||||
$migrator_test_if_true_failed = true;
|
||||
$migrator_test_if_false_failed = false;
|
||||
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
}
|
||||
|
||||
if ($migrator_test_if_true_failed)
|
||||
{
|
||||
$this->fail('True test failed');
|
||||
}
|
||||
|
||||
if ($migrator_test_if_false_failed)
|
||||
{
|
||||
$this->fail('False test failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function test_recall()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_recall'));
|
||||
|
||||
global $migrator_test_call_input;
|
||||
|
||||
// Run the schema first
|
||||
$this->migrator->update();
|
||||
|
||||
$i = 0;
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
|
||||
$this->assertSame($i, $migrator_test_call_input);
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
$this->assertSame(10, $migrator_test_call_input);
|
||||
}
|
||||
|
||||
public function test_revert()
|
||||
{
|
||||
// Make sure there are no other migrations in the db, this could cause issues
|
||||
$this->db->sql_query("DELETE FROM phpbb_migrations");
|
||||
$this->migrator->load_migration_state();
|
||||
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_revert', 'phpbb_dbal_migration_revert_with_dependency'));
|
||||
|
||||
$this->assertFalse($this->migrator->migration_state('phpbb_dbal_migration_revert'));
|
||||
$this->assertFalse($this->migrator->migration_state('phpbb_dbal_migration_revert_with_dependency'));
|
||||
|
||||
// Install the migration first
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
}
|
||||
|
||||
$this->assertTrue($this->migrator->migration_state('phpbb_dbal_migration_revert') !== false);
|
||||
$this->assertTrue($this->migrator->migration_state('phpbb_dbal_migration_revert_with_dependency') !== false);
|
||||
|
||||
$this->assertSqlResultEquals(
|
||||
array(array('bar_column' => '1')),
|
||||
"SELECT bar_column FROM phpbb_config WHERE config_name = 'foo'",
|
||||
'Installing revert migration failed to create bar_column.'
|
||||
);
|
||||
|
||||
$this->assertTrue(isset($this->config['foobartest']));
|
||||
|
||||
while ($this->migrator->migration_state('phpbb_dbal_migration_revert') !== false)
|
||||
{
|
||||
$this->migrator->revert('phpbb_dbal_migration_revert');
|
||||
}
|
||||
|
||||
$this->assertFalse($this->migrator->migration_state('phpbb_dbal_migration_revert'));
|
||||
$this->assertFalse($this->migrator->migration_state('phpbb_dbal_migration_revert_with_dependency'));
|
||||
|
||||
$this->assertFalse(isset($this->config['foobartest']));
|
||||
|
||||
$sql = 'SELECT * FROM phpbb_config';
|
||||
$result = $this->db->sql_query_limit($sql, 1);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (isset($row['bar_column']))
|
||||
{
|
||||
$this->fail('Revert did not remove test_column.');
|
||||
}
|
||||
}
|
||||
|
||||
public function test_fail()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_fail'));
|
||||
|
||||
$this->assertFalse(isset($this->config['foobar3']));
|
||||
|
||||
try
|
||||
{
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
}
|
||||
}
|
||||
catch (phpbb_db_migration_exception $e) {}
|
||||
|
||||
// Failure should have caused an automatic roll-back, so this should not exist.
|
||||
$this->assertFalse(isset($this->config['foobar3']));
|
||||
|
||||
$sql = 'SELECT * FROM phpbb_config';
|
||||
$result = $this->db->sql_query_limit($sql, 1);
|
||||
$row = $this->db->sql_fetchrow($result);
|
||||
$this->db->sql_freeresult($result);
|
||||
|
||||
if (isset($row['test_column']))
|
||||
{
|
||||
$this->fail('Revert did not remove test_column.');
|
||||
}
|
||||
}
|
||||
|
||||
public function test_installed()
|
||||
{
|
||||
$this->migrator->set_migrations(array('phpbb_dbal_migration_installed'));
|
||||
|
||||
global $migrator_test_installed_failed;
|
||||
$migrator_test_installed_failed = false;
|
||||
|
||||
while (!$this->migrator->finished())
|
||||
{
|
||||
$this->migrator->update();
|
||||
}
|
||||
|
||||
$this->assertTrue($this->migrator->migration_state('phpbb_dbal_migration_installed') !== false);
|
||||
|
||||
if ($migrator_test_installed_failed)
|
||||
{
|
||||
$this->fail('Installed test failed');
|
||||
}
|
||||
}
|
||||
}
|
124
tests/dbal/migrator_tool_config_test.php
Normal file
124
tests/dbal/migrator_tool_config_test.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/tool/config.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/exception.php';
|
||||
|
||||
class phpbb_dbal_migrator_tool_config_test extends phpbb_test_case
|
||||
{
|
||||
public function setup()
|
||||
{
|
||||
$this->config = new phpbb_config(array());
|
||||
|
||||
$this->tool = new phpbb_db_migration_tool_config($this->config);
|
||||
|
||||
parent::setup();
|
||||
}
|
||||
|
||||
public function test_add()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->add('foo', 'bar');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals('bar', $this->config['foo']);
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->add('foo', 'bar');
|
||||
$this->fail('Exception not thrown');
|
||||
}
|
||||
catch (Exception $e) {}
|
||||
}
|
||||
|
||||
public function test_update()
|
||||
{
|
||||
$this->config->set('foo', 'bar');
|
||||
try
|
||||
{
|
||||
$this->tool->update('foo', 'bar2');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals('bar2', $this->config['foo']);
|
||||
}
|
||||
|
||||
public function test_update_if_equals()
|
||||
{
|
||||
$this->config->set('foo', 'bar');
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->update_if_equals('', 'foo', 'bar2');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals('bar', $this->config['foo']);
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->update_if_equals('bar', 'foo', 'bar2');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals('bar2', $this->config['foo']);
|
||||
}
|
||||
|
||||
public function test_remove()
|
||||
{
|
||||
$this->config->set('foo', 'bar');
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->remove('foo');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertFalse(isset($this->config['foo']));
|
||||
}
|
||||
|
||||
public function test_reverse()
|
||||
{
|
||||
$this->config->set('foo', 'bar');
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->reverse('add', 'foo');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertFalse(isset($this->config['foo']));
|
||||
|
||||
$this->config->set('foo', 'bar');
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->reverse('update_if_equals', 'test', 'foo', 'bar');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals('test', $this->config['foo']);
|
||||
}
|
||||
}
|
150
tests/dbal/migrator_tool_module.php
Normal file
150
tests/dbal/migrator_tool_module.php
Normal file
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/tool/module.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/exception.php';
|
||||
|
||||
class phpbb_dbal_migrator_tool_module_test extends phpbb_database_test_case
|
||||
{
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/migrator_module.xml');
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
// Need global $db, $user for delete_module function in acp_modules
|
||||
global $phpbb_root_path, $phpEx, $skip_add_log, $db, $user;
|
||||
|
||||
parent::setup();
|
||||
|
||||
// Force add_log function to not be used
|
||||
$skip_add_log = true;
|
||||
|
||||
$db = $this->db = $this->new_dbal();
|
||||
$this->cache = new phpbb_cache_service(new phpbb_cache_driver_null(), new phpbb_config(array()), $this->db, $phpbb_root_path, $phpEx);
|
||||
$user = $this->user = new phpbb_user();
|
||||
|
||||
$this->tool = new phpbb_db_migration_tool_module($this->db, $this->cache, $this->user, $phpbb_root_path, $phpEx, 'phpbb_modules');
|
||||
}
|
||||
|
||||
public function exists_data()
|
||||
{
|
||||
return array(
|
||||
// Test the category
|
||||
array(
|
||||
'',
|
||||
'ACP_CAT',
|
||||
true,
|
||||
),
|
||||
array(
|
||||
0,
|
||||
'ACP_CAT',
|
||||
true,
|
||||
),
|
||||
|
||||
// Test the module
|
||||
array(
|
||||
'',
|
||||
'ACP_MODULE',
|
||||
false,
|
||||
),
|
||||
array(
|
||||
false,
|
||||
'ACP_MODULE',
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'ACP_CAT',
|
||||
'ACP_MODULE',
|
||||
true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider exists_data
|
||||
*/
|
||||
public function test_exists($parent, $module, $expected)
|
||||
{
|
||||
$this->assertEquals($expected, $this->tool->exists('acp', $parent, $module));
|
||||
}
|
||||
|
||||
public function test_add()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->add('acp', 0, 'ACP_NEW_CAT');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(true, $this->tool->exists('acp', 0, 'ACP_NEW_CAT'));
|
||||
|
||||
// Should throw an exception when trying to add a module that already exists
|
||||
try
|
||||
{
|
||||
$this->tool->add('acp', 0, 'ACP_NEW_CAT');
|
||||
$this->fail('Exception not thrown');
|
||||
}
|
||||
catch (Exception $e) {}
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->add('acp', 'ACP_NEW_CAT', array(
|
||||
'module_basename' => 'acp_new_module',
|
||||
'module_langname' => 'ACP_NEW_MODULE',
|
||||
'module_mode' => 'test',
|
||||
'module_auth' => '',
|
||||
));
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(true, $this->tool->exists('acp', 'ACP_NEW_CAT', 'ACP_NEW_MODULE'));
|
||||
}
|
||||
|
||||
public function test_remove()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->remove('acp', 'ACP_CAT', 'ACP_MODULE');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(false, $this->tool->exists('acp', 'ACP_CAT', 'ACP_MODULE'));
|
||||
}
|
||||
|
||||
public function test_reverse()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->add('acp', 0, 'ACP_NEW_CAT');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->reverse('add', 'acp', 0, 'ACP_NEW_CAT');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertFalse($this->tool->exists('acp', 0, 'ACP_NEW_CAT'));
|
||||
}
|
||||
}
|
159
tests/dbal/migrator_tool_permission.php
Normal file
159
tests/dbal/migrator_tool_permission.php
Normal file
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
/**
|
||||
*
|
||||
* @package testing
|
||||
* @copyright (c) 2011 phpBB Group
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*
|
||||
*/
|
||||
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/tool/permission.php';
|
||||
require_once dirname(__FILE__) . '/../../phpBB/includes/db/migration/exception.php';
|
||||
|
||||
class phpbb_dbal_migrator_tool_permission_test extends phpbb_database_test_case
|
||||
{
|
||||
public function getDataSet()
|
||||
{
|
||||
return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/migrator_permission.xml');
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
// Global $db and $cache are needed in acp/auth.php constructor
|
||||
global $phpbb_root_path, $phpEx, $db, $cache;
|
||||
|
||||
parent::setup();
|
||||
|
||||
$db = $this->db = $this->new_dbal();
|
||||
$cache = $this->cache = new phpbb_cache_service(new phpbb_cache_driver_null(), new phpbb_config(array()), $this->db, $phpbb_root_path, $phpEx);
|
||||
$this->auth = new phpbb_auth();
|
||||
|
||||
$this->tool = new phpbb_db_migration_tool_permission($this->db, $this->cache, $this->auth, $phpbb_root_path, $phpEx);
|
||||
}
|
||||
|
||||
public function exists_data()
|
||||
{
|
||||
return array(
|
||||
array(
|
||||
'global',
|
||||
true,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'local',
|
||||
false,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'both',
|
||||
true,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'both',
|
||||
false,
|
||||
true,
|
||||
),
|
||||
array(
|
||||
'does_not_exist',
|
||||
true,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider exists_data
|
||||
*/
|
||||
public function test_exists($auth_option, $global, $expected)
|
||||
{
|
||||
$this->assertEquals($expected, $this->tool->exists($auth_option, $global));
|
||||
}
|
||||
|
||||
public function test_add()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->add('new', true);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(true, $this->tool->exists('new', true));
|
||||
$this->assertEquals(false, $this->tool->exists('new', false));
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->add('new', false);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(true, $this->tool->exists('new', false));
|
||||
|
||||
// Should fail (duplicate)
|
||||
try
|
||||
{
|
||||
$this->tool->add('new', true);
|
||||
$this->fail('Did not throw exception on duplicate');
|
||||
}
|
||||
catch (Exception $e) {}
|
||||
}
|
||||
|
||||
public function test_remove()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->remove('global', true);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(false, $this->tool->exists('global', true));
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->remove('both', false);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertEquals(false, $this->tool->exists('both', false));
|
||||
|
||||
// Should fail (does not exist)
|
||||
try
|
||||
{
|
||||
$this->tool->remove('new', true);
|
||||
$this->fail('Did not throw exception on duplicate');
|
||||
}
|
||||
catch (Exception $e) {}
|
||||
}
|
||||
|
||||
public function test_reverse()
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->tool->reverse('remove', 'global_test', true);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertTrue($this->tool->exists('global_test', true));
|
||||
|
||||
try
|
||||
{
|
||||
$this->tool->reverse('add', 'global_test', true);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->fail($e);
|
||||
}
|
||||
$this->assertFalse($this->tool->exists('global_test', true));
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue