diff --git a/.travis.yml b/.travis.yml
index f296f10230..d6c23da993 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,21 +3,28 @@ php:
- 5.3.3
- 5.3
- 5.4
+ - 5.5
env:
- DB=mysql
- DB=postgres
+matrix:
+ allow_failures:
+ - php: 5.5
+
before_script:
- sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'DROP DATABASE IF EXISTS phpbb_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'create database phpbb_tests;' -U postgres; fi"
- sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS phpbb_tests;'; fi"
+ - travis/install-php-extensions.sh
- pyrus set auto_discover 1
- pyrus install --force phpunit/DbUnit
- phpenv rehash
- cd phpBB
- php ../composer.phar install --dev
- - cd ../
+ - cd ..
+ - sh -c "if [ `php -r "echo (int) version_compare(PHP_VERSION, '5.3.19', '>=');"` = "1" ]; then travis/setup-webserver.sh; fi"
script:
- phpunit --configuration travis/phpunit-$DB-travis.xml
diff --git a/phpBB/adm/style/acp_ext_enable.html b/phpBB/adm/style/acp_ext_enable.html
index 3f7be2c847..35585207eb 100644
--- a/phpBB/adm/style/acp_ext_enable.html
+++ b/phpBB/adm/style/acp_ext_enable.html
@@ -7,7 +7,13 @@
{L_EXTENSIONS_EXPLAIN}
{L_ENABLE_EXPLAIN}
-
+
+
+
{L_MIGRATION_EXCEPTION_ERROR}
+
{MIGRATOR_ERROR}
+
{L_RETURN}
+
+
diff --git a/phpBB/adm/style/acp_ext_purge.html b/phpBB/adm/style/acp_ext_purge.html
index 00a58721cb..94bef82ca5 100644
--- a/phpBB/adm/style/acp_ext_purge.html
+++ b/phpBB/adm/style/acp_ext_purge.html
@@ -7,7 +7,13 @@
{L_EXTENSIONS_EXPLAIN}
{L_PURGE_EXPLAIN}
-
+
+
+
{L_MIGRATION_EXCEPTION_ERROR}
+
{MIGRATOR_ERROR}
+
{L_RETURN}
+
+
diff --git a/phpBB/config/migrator.yml b/phpBB/config/migrator.yml
new file mode 100644
index 0000000000..999a2d41a3
--- /dev/null
+++ b/phpBB/config/migrator.yml
@@ -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 }
diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml
index 5c450a5cf6..ef5359129f 100644
--- a/phpBB/config/services.yml
+++ b/phpBB/config/services.yml
@@ -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:
@@ -105,8 +112,10 @@ services:
ext.manager:
class: phpbb_extension_manager
arguments:
+ - @service_container
- @dbal.conn
- @config
+ - @migrator
- %tables.ext%
- %core.root_path%
- .%core.php_ext%
diff --git a/phpBB/config/tables.yml b/phpBB/config/tables.yml
index cfc6dbcfed..dd53417b1c 100644
--- a/phpBB/config/tables.yml
+++ b/phpBB/config/tables.yml
@@ -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
diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php
index 3d3e478032..550396c7ea 100644
--- a/phpBB/develop/create_schema_files.php
+++ b/phpBB/develop/create_schema_files.php
@@ -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'),
@@ -1520,18 +1533,21 @@ function get_schema_struct()
$schema_data['phpbb_reports'] = array(
'COLUMNS' => array(
- 'report_id' => array('UINT', NULL, 'auto_increment'),
- 'reason_id' => array('USINT', 0),
- 'post_id' => array('UINT', 0),
- 'pm_id' => array('UINT', 0),
- 'user_id' => array('UINT', 0),
- 'user_notify' => array('BOOL', 0),
- 'report_closed' => array('BOOL', 0),
- 'report_time' => array('TIMESTAMP', 0),
- 'report_text' => array('MTEXT_UNI', ''),
- 'reported_post_text' => array('MTEXT_UNI', ''),
- 'reported_post_uid' => array('VCHAR:8', ''),
- 'reported_post_bitfield' => array('VCHAR:255', ''),
+ 'report_id' => array('UINT', NULL, 'auto_increment'),
+ 'reason_id' => array('USINT', 0),
+ 'post_id' => array('UINT', 0),
+ 'pm_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'user_notify' => array('BOOL', 0),
+ 'report_closed' => array('BOOL', 0),
+ 'report_time' => array('TIMESTAMP', 0),
+ 'report_text' => array('MTEXT_UNI', ''),
+ 'reported_post_text' => array('MTEXT_UNI', ''),
+ 'reported_post_uid' => array('VCHAR:8', ''),
+ 'reported_post_bitfield' => array('VCHAR:255', ''),
+ 'reported_post_enable_magic_url' => array('BOOL', 1),
+ 'reported_post_enable_smilies' => array('BOOL', 1),
+ 'reported_post_enable_bbcode' => array('BOOL', 1)
),
'PRIMARY_KEY' => 'report_id',
'KEYS' => array(
diff --git a/phpBB/docs/AUTHORS b/phpBB/docs/AUTHORS
index c63bd743a7..be82f06c96 100644
--- a/phpBB/docs/AUTHORS
+++ b/phpBB/docs/AUTHORS
@@ -23,11 +23,11 @@ involved in phpBB.
phpBB Lead Developer: naderman (Nils Adermann)
phpBB Developers: bantu (Andreas Fischer)
+ EXreaction (Nathan Guse)
igorw (Igor Wiedler)
imkingdavid (David King)
nickvergessen (Joas Schilling)
Oleg (Oleg Pudeyev)
- rxu (Ruslan Uzdenov)
Contributions by: leviatan21 (Gabriel Vazquez)
Raimon (Raimon Meuldijk)
@@ -53,6 +53,7 @@ phpBB Developers: A_Jelly_Doughnut (Josh Woody) [01/2010 - 11/2010]
dhn (Dominik Dröscher) [05/2007 - 01/2011]
GrahamJE (Graham Eames) [09/2005 - 11/2006]
kellanved (Henry Sudhof) [04/2007 - 03/2011]
+ rxu (Ruslan Uzdenov) [04/2010 - 12/2012]
TerraFrost (Jim Wigginton) [04/2009 - 01/2011]
ToonArmy (Chris Smith) [06/2008 - 11/2011]
Vic D'Elfant (Vic D'Elfant) [04/2007 - 04/2009]
diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php
index 322e1c55d8..960cc89db5 100644
--- a/phpBB/includes/acp/acp_board.php
+++ b/phpBB/includes/acp/acp_board.php
@@ -413,8 +413,8 @@ class acp_board
'board_email_form' => array('lang' => 'BOARD_EMAIL_FORM', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true),
'email_function_name' => array('lang' => 'EMAIL_FUNCTION_NAME', 'validate' => 'string', 'type' => 'text:20:50', 'explain' => true),
'email_package_size' => array('lang' => 'EMAIL_PACKAGE_SIZE', 'validate' => 'int:0', 'type' => 'text:5:5', 'explain' => true),
- 'board_contact' => array('lang' => 'CONTACT_EMAIL', 'validate' => 'string', 'type' => 'text:25:100', 'explain' => true),
- 'board_email' => array('lang' => 'ADMIN_EMAIL', 'validate' => 'string', 'type' => 'text:25:100', 'explain' => true),
+ 'board_contact' => array('lang' => 'CONTACT_EMAIL', 'validate' => 'email', 'type' => 'text:25:100', 'explain' => true),
+ 'board_email' => array('lang' => 'ADMIN_EMAIL', 'validate' => 'email', 'type' => 'text:25:100', 'explain' => true),
'board_email_sig' => array('lang' => 'EMAIL_SIG', 'validate' => 'string', 'type' => 'textarea:5:30', 'explain' => true),
'board_hide_emails' => array('lang' => 'BOARD_HIDE_EMAILS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php
index a0bcf62ecc..24211196bd 100644
--- a/phpBB/includes/acp/acp_extensions.php
+++ b/phpBB/includes/acp/acp_extensions.php
@@ -37,7 +37,7 @@ class acp_extensions
$this->template = $template;
$this->user = $user;
- $user->add_lang(array('install', 'acp/extensions'));
+ $user->add_lang(array('install', 'acp/extensions', 'migrator'));
$this->page_title = 'ACP_EXTENSIONS';
@@ -103,11 +103,18 @@ class acp_extensions
trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action));
}
- if ($phpbb_extension_manager->enable_step($ext_name))
+ try
{
- $template->assign_var('S_NEXT_STEP', true);
+ if ($phpbb_extension_manager->enable_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
- meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name));
+ meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name));
+ }
+ }
+ catch (phpbb_db_migration_exception $e)
+ {
+ $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));
}
$this->tpl_name = 'acp_ext_enable';
@@ -156,11 +163,18 @@ class acp_extensions
break;
case 'purge':
- if ($phpbb_extension_manager->purge_step($ext_name))
+ try
{
- $template->assign_var('S_NEXT_STEP', true);
+ if ($phpbb_extension_manager->purge_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
- meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name));
+ meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name));
+ }
+ }
+ catch (phpbb_db_migration_exception $e)
+ {
+ $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));
}
$this->tpl_name = 'acp_ext_purge';
diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php
index b604e20094..21b1d4b837 100644
--- a/phpBB/includes/acp/acp_groups.php
+++ b/phpBB/includes/acp/acp_groups.php
@@ -126,13 +126,34 @@ class acp_groups
{
trigger_error($user->lang['NO_GROUP'] . adm_back_link($this->u_action), E_USER_WARNING);
}
+ else if (empty($mark_ary))
+ {
+ trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING);
+ }
if (confirm_box(true))
{
$group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+ group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row);
+ trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
+ }
+ else
+ {
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
+ }
- if (!sizeof($mark_ary))
+ break;
+ case 'set_default_on_all':
+ if (confirm_box(true))
{
+ $group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+
$start = 0;
do
@@ -163,28 +184,25 @@ class acp_groups
$db->sql_freeresult($result);
}
while ($start);
+
+ trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
}
else
{
- group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row);
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
}
-
- trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
- }
- else
- {
- confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
- 'mark' => $mark_ary,
- 'g' => $group_id,
- 'i' => $id,
- 'mode' => $mode,
- 'action' => $action))
- );
- }
-
break;
-
case 'deleteusers':
+ if (empty($mark_ary))
+ {
+ trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING);
+ }
case 'delete':
if (!$group_id)
{
@@ -439,7 +457,7 @@ class acp_groups
foreach ($test_variables as $test => $type)
{
- if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || in_array($test, $set_attributes)))
+ if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0 || in_array($test, $set_attributes)))
{
settype($submit_ary[$test], $type);
$group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test];
@@ -698,7 +716,7 @@ class acp_groups
'U_ACTION' => $this->u_action . "&g=$group_id",
'U_BACK' => $this->u_action,
'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&form=list&field=usernames'),
- 'U_DEFAULT_ALL' => "{$this->u_action}&action=default&g=$group_id",
+ 'U_DEFAULT_ALL' => "{$this->u_action}&action=set_default_on_all&g=$group_id",
));
// Grab the members
diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php
index 03d7059165..174b365af0 100644
--- a/phpBB/includes/acp/info/acp_extensions.php
+++ b/phpBB/includes/acp/info/acp_extensions.php
@@ -16,10 +16,10 @@ class acp_extensions_info
{
return array(
'filename' => 'acp_extensions',
- 'title' => 'ACP_EXTENSIONS_MANAGEMENT',
+ 'title' => 'ACP_EXTENSION_MANAGEMENT',
'version' => '1.0.0',
'modes' => array(
- 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSIONS_MANAGEMENT')),
+ 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSION_MANAGEMENT')),
),
);
}
diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php
index 68af41ab20..68c96a2759 100644
--- a/phpBB/includes/constants.php
+++ b/phpBB/includes/constants.php
@@ -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');
diff --git a/phpBB/includes/datetime.php b/phpBB/includes/datetime.php
index b3462ddf67..3c6d4971b9 100644
--- a/phpBB/includes/datetime.php
+++ b/phpBB/includes/datetime.php
@@ -143,7 +143,7 @@ class phpbb_datetime extends DateTime
'is_short' => strpos($format, self::RELATIVE_WRAPPER) !== false,
'format_short' => substr($format, 0, strpos($format, self::RELATIVE_WRAPPER)) . self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER . substr(strrchr($format, self::RELATIVE_WRAPPER), 1),
'format_long' => str_replace(self::RELATIVE_WRAPPER, '', $format),
- 'lang' => $user->lang['datetime'],
+ 'lang' => array_filter($user->lang['datetime'], 'is_string'),
);
// Short representation of month in format? Some languages use different terms for the long and short format of May
diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php
index 2bb016cebd..983cdc18ea 100644
--- a/phpBB/includes/db/db_tools.php
+++ b/phpBB/includes/db/db_tools.php
@@ -303,7 +303,7 @@ class phpbb_db_tools
* @param phpbb_db_driver $db Database connection
* @param bool $return_statements True if only statements should be returned and no SQL being executed
*/
- function phpbb_db_tools(&$db, $return_statements = false)
+ function phpbb_db_tools(phpbb_db_driver $db, $return_statements = false)
{
$this->db = $db;
$this->return_statements = $return_statements;
@@ -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
@@ -1817,6 +1830,22 @@ class phpbb_db_tools
case 'mssql':
case 'mssqlnative':
+ // remove default cosntraints first
+ // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
+ $statements[] = "DECLARE @drop_default_name VARCHAR(100), @cmd VARCHAR(1000)
+ SET @drop_default_name =
+ (SELECT so.name FROM sysobjects so
+ JOIN sysconstraints sc ON so.id = sc.constid
+ WHERE object_name(so.parent_obj) = '{$table_name}'
+ AND so.xtype = 'D'
+ AND sc.colid = (SELECT colid FROM syscolumns
+ WHERE id = object_id('{$table_name}')
+ AND name = '{$column_name}'))
+ IF @drop_default_name <> ''
+ BEGIN
+ SET @cmd = 'ALTER TABLE [{$table_name}] DROP CONSTRAINT [' + @drop_default_name + ']'
+ EXEC(@cmd)
+ END";
$statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
break;
diff --git a/phpBB/includes/db/migration/data/30x/3_0_1.php b/phpBB/includes/db/migration/data/30x/3_0_1.php
new file mode 100644
index 0000000000..c996a0138a
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_1.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_1_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10.php b/phpBB/includes/db/migration/data/30x/3_0_10.php
new file mode 100644
index 0000000000..122f93d6b4
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php
new file mode 100644
index 0000000000..0ed05812dc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php
@@ -0,0 +1,30 @@
+config['version'], '3.0.10-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('email_max_chunk_size', 50)),
+
+ array('config.update', array('version', '3.0.10-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php
new file mode 100644
index 0000000000..b14b3b00aa
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php
new file mode 100644
index 0000000000..473057d65d
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-rc3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11.php b/phpBB/includes/db/migration/data/30x/3_0_11.php
new file mode 100644
index 0000000000..e063c699cc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.11', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php
new file mode 100644
index 0000000000..dddfc0e0e7
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php
@@ -0,0 +1,95 @@
+config['version'], '3.0.11-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'cleanup_deactivated_styles'))),
+ array('custom', array(array(&$this, 'delete_orphan_private_messages'))),
+
+ array('config.update', array('version', '3.0.11-rc1')),
+ );
+ }
+
+ public function cleanup_deactivated_styles()
+ {
+ // Updates users having current style a deactivated one
+ $sql = 'SELECT style_id
+ FROM ' . STYLES_TABLE . '
+ WHERE style_active = 0';
+ $result = $this->sql_query($sql);
+
+ $deactivated_style_ids = array();
+ while ($style_id = $this->db->sql_fetchfield('style_id', false, $result))
+ {
+ $deactivated_style_ids[] = (int) $style_id;
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($deactivated_style_ids))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_style = ' . (int) $this->config['default_style'] .'
+ WHERE ' . $this->db->sql_in_set('user_style', $deactivated_style_ids);
+ $this->sql_query($sql);
+ }
+ }
+
+ public function delete_orphan_private_messages()
+ {
+ // Delete orphan private messages
+ $batch_size = 500;
+
+ $sql_array = array(
+ 'SELECT' => 'p.msg_id',
+ 'FROM' => array(
+ PRIVMSGS_TABLE => 'p',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(PRIVMSGS_TO_TABLE => 't'),
+ 'ON' => 'p.msg_id = t.msg_id',
+ ),
+ ),
+ 'WHERE' => 't.user_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $delete_pms = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $delete_pms[] = (int) $row['msg_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($delete_pms))
+ {
+ $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('msg_id', $delete_pms);
+ $this->sql_query($sql);
+
+ // Return false to have the Migrator call this function again
+ return false;
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php
new file mode 100644
index 0000000000..fac8523e8c
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php
@@ -0,0 +1,50 @@
+config['version'], '3.0.11-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php
new file mode 100644
index 0000000000..6a31a51201
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php
@@ -0,0 +1,123 @@
+config['version'], '3.0.12-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, 'update_bots'))),
+ array('custom', array(array(&$this, 'disable_bots_from_receiving_pms'))),
+
+ array('config.update', array('version', '3.0.12-rc1')),
+ );
+ }
+
+ public function disable_bots_from_receiving_pms()
+ {
+ // Disable receiving pms for bots
+ $sql = 'SELECT user_id
+ FROM ' . BOTS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ $bot_user_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $bot_user_ids[] = (int) $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($bot_user_ids))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_allow_pm = 0
+ WHERE ' . $this->db->sql_in_set('user_id', $bot_user_ids);
+ $this->sql_query($sql);
+ }
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET module_auth = \'acl_u_sig\'
+ WHERE module_class = \'ucp\'
+ AND module_basename = \'profile\'
+ AND module_mode = \'signature\'';
+ $this->sql_query($sql);
+ }
+
+ public function update_bots()
+ {
+ // Update bots
+ if (!function_exists('user_delete'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $bots_updates = array(
+ // Bot Deletions
+ 'NG-Search [Bot]' => false,
+ 'Nutch/CVS [Bot]' => false,
+ 'OmniExplorer [Bot]' => false,
+ 'Seekport [Bot]' => false,
+ 'Synoo [Bot]' => false,
+ 'WiseNut [Bot]' => false,
+
+ // Bot Updates
+ // Bot name to bot user agent map
+ 'Baidu [Spider]' => 'Baiduspider',
+ 'Exabot [Bot]' => 'Exabot',
+ 'Voyager [Bot]' => 'voyager/',
+ 'W3C [Validator]' => 'W3C_Validator',
+ );
+
+ foreach ($bots_updates as $bot_name => $bot_agent)
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE user_type = ' . USER_IGNORE . "
+ AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'";
+ $result = $this->db->sql_query($sql);
+ $bot_user_id = (int) $this->db->sql_fetchfield('user_id');
+ $this->db->sql_freeresult($result);
+
+ if ($bot_user_id)
+ {
+ if ($bot_agent === false)
+ {
+ $sql = 'DELETE FROM ' . BOTS_TABLE . "
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+
+ user_delete('remove', $bot_user_id);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . BOTS_TABLE . "
+ SET bot_agent = '" . $this->db->sql_escape($bot_agent) . "'
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php
new file mode 100644
index 0000000000..562ccf077c
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php
@@ -0,0 +1,108 @@
+config['version'], '3.0.1-rc1', '>=');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list' => array('BOOL', 1),
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('UINT', 0),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('session_forum_id'),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name' => array('group_legend', 'group_name'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list',
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('group_legend'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'fix_unset_last_view_time'))),
+ array('custom', array(array(&$this, 'reset_smiley_size'))),
+
+ array('config.update', array('version', '3.0.1-rc1')),
+ );
+ }
+
+ public function fix_unset_last_view_time()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "topics
+ SET topic_last_view_time = topic_last_post_time
+ WHERE topic_last_view_time = 0";
+ $this->sql_query($sql);
+ }
+
+ public function reset_smiley_size()
+ {
+ // Update smiley sizes
+ $smileys = array('icon_e_surprised.gif', 'icon_eek.gif', 'icon_cool.gif', 'icon_lol.gif', 'icon_mad.gif', 'icon_razz.gif', 'icon_redface.gif', 'icon_cry.gif', 'icon_evil.gif', 'icon_twisted.gif', 'icon_rolleyes.gif', 'icon_exclaim.gif', 'icon_question.gif', 'icon_idea.gif', 'icon_arrow.gif', 'icon_neutral.gif', 'icon_mrgreen.gif', 'icon_e_ugeek.gif');
+
+ foreach ($smileys as $smiley)
+ {
+ if (file_exists($this->phpbb_root_path . 'images/smilies/' . $smiley))
+ {
+ list($width, $height) = getimagesize($this->phpbb_root_path . 'images/smilies/' . $smiley);
+
+ $sql = 'UPDATE ' . SMILIES_TABLE . '
+ SET smiley_width = ' . $width . ', smiley_height = ' . $height . "
+ WHERE smiley_url = '" . $this->db->sql_escape($smiley) . "'";
+
+ $this->sql_query($sql);
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2.php b/phpBB/includes/db/migration/data/30x/3_0_2.php
new file mode 100644
index 0000000000..eed5acef82
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php
new file mode 100644
index 0000000000..a960e90765
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php
@@ -0,0 +1,32 @@
+config['version'], '3.0.2-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('referer_validation', '1')),
+ array('config.add', array('check_attachment_content', '1')),
+ array('config.add', array('mime_triggers', 'body|head|html|img|plaintext|a href|pre|script|table|title')),
+
+ array('config.update', array('version', '3.0.2-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php
new file mode 100644
index 0000000000..8917dfea77
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php
@@ -0,0 +1,80 @@
+config['version'], '3.0.2-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'drafts' => array(
+ 'draft_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_subject' => array('STEXT_UNI', '', 'true_sort'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_title' => array('STEXT_UNI', '', 'true_sort'),
+ 'topic_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid' => array('session_forum_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array(
+ 'session_forum_id',
+ ),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_3.php b/phpBB/includes/db/migration/data/30x/3_0_3.php
new file mode 100644
index 0000000000..8984cf7b76
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_3_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php
new file mode 100644
index 0000000000..4b102e1a2e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php
@@ -0,0 +1,83 @@
+config['version'], '3.0.3-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id',
+ 'template_inherit_path',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('enable_queue_trigger', '0')),
+ array('config.add', array('queue_trigger_posts', '3')),
+ array('config.add', array('pm_max_recipients', '0')),
+ array('custom', array(array(&$this, 'set_group_default_max_recipients'))),
+ array('config.add', array('dbms_version', $this->db->sql_server_info(true))),
+ array('permission.add', array('u_masspm_group', true, 'u_masspm')),
+ array('custom', array(array(&$this, 'correct_acp_email_permissions'))),
+
+ array('config.update', array('version', '3.0.3-rc1')),
+ );
+ }
+
+ public function correct_acp_email_permissions()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'modules
+ SET module_auth = \'acl_a_email && cfg_email_enable\'
+ WHERE module_class = \'acp\'
+ AND module_basename = \'email\'';
+ $this->sql_query($sql);
+ }
+
+ public function set_group_default_max_recipients()
+ {
+ // Set maximum number of recipients for the registered users, bots, guests group
+ $sql = 'UPDATE ' . GROUPS_TABLE . ' SET group_max_recipients = 5
+ WHERE ' . $this->db->sql_in_set('group_name', array('GUESTS', 'REGISTERED', 'REGISTERED_COPPA', 'BOTS'));
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_4.php b/phpBB/includes/db/migration/data/30x/3_0_4.php
new file mode 100644
index 0000000000..9a0c132e78
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_4.php
@@ -0,0 +1,49 @@
+config['version'], '3.0.4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_4_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'rename_log_delete_topic'))),
+
+ array('config.update', array('version', '3.0.4')),
+ );
+ }
+
+ public function rename_log_delete_topic()
+ {
+ if ($this->db->sql_layer == 'oracle')
+ {
+ // log_operation is CLOB - but we can change this later
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation LIKE 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation = 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php
new file mode 100644
index 0000000000..8ad75a557b
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php
@@ -0,0 +1,123 @@
+config['version'], '3.0.4-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_3');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile' => array('BOOL', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'style_id' => array('UINT', NULL, 'auto_increment'),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_imageset' => array(
+ 'imageset_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'image_id' => array('UINT', NULL, 'auto_increment'),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'theme_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'template_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'template_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_custom_profile_fields'))),
+
+ array('config.update', array('version', '3.0.4-rc1')),
+ );
+ }
+
+ public function update_custom_profile_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new format
+ $sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide
+ FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'field_required' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_hide' => 0,
+ 'field_show_profile'=> 0,
+ );
+
+ if ($row['field_required'])
+ {
+ $sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_show_on_reg'])
+ {
+ $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_hide'])
+ {
+ // Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module
+ $sql_ary['field_hide'] = 1;
+ }
+ else
+ {
+ // equivelant to "none", which is the "Display in user control panel" option
+ $sql_ary['field_show_profile'] = 1;
+ }
+
+ $this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary);
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5.php b/phpBB/includes/db/migration/data/30x/3_0_5.php
new file mode 100644
index 0000000000..16d2dee457
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.5', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5_rc1part2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php
new file mode 100644
index 0000000000..ea17cc1e31
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php
@@ -0,0 +1,124 @@
+config['version'], '3.0.5-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_4');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ $search_indexing_state = $this->config['search_indexing_state'];
+
+ return array(
+ array('config.add', array('captcha_gd_wave', 0)),
+ array('config.add', array('captcha_gd_3d_noise', 1)),
+ array('config.add', array('captcha_gd_fonts', 1)),
+ array('config.add', array('confirm_refresh', 1)),
+ array('config.add', array('max_num_search_keywords', 10)),
+ array('config.remove', array('search_indexing_state')),
+ array('config.add', array('search_indexing_state', $search_indexing_state, true)),
+ array('custom', array(array(&$this, 'hash_old_passwords'))),
+ array('custom', array(array(&$this, 'update_ichiro_bot'))),
+ );
+ }
+
+ public function hash_old_passwords()
+ {
+ $sql = 'SELECT user_id, user_password
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_pass_convert = 1';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (strlen($row['user_password']) == 32)
+ {
+ $sql_ary = array(
+ 'user_password' => phpbb_hash($row['user_password']),
+ );
+
+ $this->sql_query('UPDATE ' . $this->table_prefix . 'users SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE user_id = ' . $row['user_id']);
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_ichiro_bot()
+ {
+ // Adjust bot entry
+ $sql = 'UPDATE ' . $this->table_prefix . "bots
+ SET bot_agent = 'ichiro/'
+ WHERE bot_agent = 'ichiro/2'";
+ $this->sql_query($sql);
+ }
+
+ public function remove_duplicate_auth_options()
+ {
+ // Before we are able to add a unique key to auth_option, we need to remove duplicate entries
+ $sql = 'SELECT auth_option
+ FROM ' . $this->table_prefix . 'acl_options
+ GROUP BY auth_option
+ HAVING COUNT(*) >= 2';
+ $result = $this->db->sql_query($sql);
+
+ $auth_options = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $auth_options[] = $row['auth_option'];
+ }
+ $this->db->sql_freeresult($result);
+
+ // Remove specific auth options
+ if (!empty($auth_options))
+ {
+ foreach ($auth_options as $option)
+ {
+ // Select auth_option_ids... the largest id will be preserved
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . "
+ WHERE auth_option = '" . $db->sql_escape($option) . "'
+ ORDER BY auth_option_id DESC";
+ // sql_query_limit not possible here, due to bug in postgresql layer
+ $result = $this->db->sql_query($sql);
+
+ // Skip first row, this is our original auth option we want to preserve
+ $row = $this->db->sql_fetchrow($result);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Ok, remove this auth option...
+ $this->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php
new file mode 100644
index 0000000000..8538347b1a
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php
@@ -0,0 +1,42 @@
+config['version'], '3.0.5-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'acl_options' => array('auth_option'),
+ ),
+ 'add_unique_index' => array(
+ $this->table_prefix . 'acl_options' => array(
+ 'auth_option' => array('auth_option'),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6.php b/phpBB/includes/db/migration/data/30x/3_0_6.php
new file mode 100644
index 0000000000..bb651dc7cd
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php
new file mode 100644
index 0000000000..38c282ebf0
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php
@@ -0,0 +1,324 @@
+config['version'], '3.0.6-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new' => array('BOOL', 1),
+ 'user_reminded' => array('TINT:4', 0),
+ 'user_reminded_time' => array('TIMESTAMP', 0),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth' => array('BOOL', 0, 'after' => 'group_founder_manage'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options' => array('UINT:20', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_options' => array('UINT:11', 230271),
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id' => array('post_id'),
+ 'pm_id' => array('pm_id'),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username' => array('post_username:255'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts',
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new',
+ 'user_reminded',
+ 'user_reminded_time',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth',
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt',
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options',
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id',
+ 'pm_id',
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('captcha_plugin', 'phpbb_captcha_nogd')),
+ array('if', array(
+ ($this->config['captcha_gd']),
+ array('config.update', array('captcha_plugin', 'phpbb_captcha_gd')),
+ )),
+
+ array('config.add', array('feed_enable', 0)),
+ array('config.add', array('feed_limit', 10)),
+ array('config.add', array('feed_overall_forums', 1)),
+ array('config.add', array('feed_overall_forums_limit', 15)),
+ array('config.add', array('feed_overall_topics', 0)),
+ array('config.add', array('feed_overall_topics_limit', 15)),
+ array('config.add', array('feed_forum', 1)),
+ array('config.add', array('feed_topic', 1)),
+ array('config.add', array('feed_item_statistics', 1)),
+
+ array('config.add', array('smilies_per_page', 50)),
+ array('config.add', array('allow_pm_report', 1)),
+ array('config.add', array('min_post_chars', 1)),
+ array('config.add', array('allow_quick_reply', 1)),
+ array('config.add', array('new_member_post_limit', 0)),
+ array('config.add', array('new_member_group_default', 0)),
+ array('config.add', array('delete_time', $this->config['edit_time'])),
+
+ array('config.add', array('allow_avatar', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_upload'] || $this->config['allow_avatar_local'] || $this->config['allow_avatar_remote']),
+ array('config.update', array('allow_avatar', 1)),
+ )),
+ array('config.add', array('allow_avatar_remote_upload', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_remote'] && $this->config['allow_avatar_upload']),
+ array('config.update', array('allow_avatar_remote_upload', 1)),
+ )),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_BOARD_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_board',
+ 'modes' => array('feed'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_USERS',
+ array(
+ 'module_basename' => 'acp_users',
+ 'modes' => array('warnings'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_SERVER_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_send_statistics',
+ 'modes' => array('send_statistics'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_FORUM_BASED_PERMISSIONS',
+ array(
+ 'module_basename' => 'acp_permissions',
+ 'modes' => array('setting_forum_copy'),
+ ),
+ )),
+ array('module.add', array(
+ 'mcp',
+ 'MCP_REPORTS',
+ array(
+ 'module_basename' => 'mcp_pm_reports',
+ 'modes' => array('pm_reports','pm_reports_closed','pm_report_details'),
+ ),
+ )),
+ array('custom', array(array(&$this, 'add_newly_registered_group'))),
+ array('custom', array(array(&$this, 'set_user_options_default'))),
+
+ array('config.update', array('version', '3.0.6-rc1')),
+ );
+ }
+
+ public function set_user_options_default()
+ {
+ // 229376 is the added value to enable all three signature options
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_options = user_options + 229376';
+ $this->sql_query($sql);
+ }
+
+ public function add_newly_registered_group()
+ {
+ // Add newly_registered group... but check if it already exists (we always supported running the updater on any schema)
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'NEWLY_REGISTERED'";
+ $result = $this->db->sql_query($sql);
+ $group_id = (int) $this->db->sql_fetchfield('group_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$group_id)
+ {
+ $sql = 'INSERT INTO ' . GROUPS_TABLE . " (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('NEWLY_REGISTERED', 3, 0, '', 0, '', '', '', 5)";
+ $this->sql_query($sql);
+
+ $group_id = $this->db->sql_nextid();
+ }
+
+ // Insert new user role... at the end of the chain
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_USER_NEW_MEMBER'
+ AND role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $u_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$u_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_USER_NEW_MEMBER', 'ROLE_DESCRIPTION_USER_NEW_MEMBER', 'u_', $next_order_id)";
+ $this->sql_query($sql);
+ $u_role = $this->db->sql_nextid();
+
+ // Now add the correct data to the roles...
+ // The standard role says that new users are not able to send a PM, Mass PM, are not able to PM groups
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $u_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'u_%' AND auth_option IN ('u_sendpm', 'u_masspm', 'u_masspm_group')";
+ $this->sql_query($sql);
+
+ // Add user role to group
+ $sql = 'INSERT INTO ' . ACL_GROUPS_TABLE . " (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES ($group_id, 0, 0, $u_role, 0)";
+ $this->sql_query($sql);
+ }
+
+ // Insert new forum role
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_FORUM_NEW_MEMBER'
+ AND role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $f_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$f_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_FORUM_NEW_MEMBER', 'ROLE_DESCRIPTION_FORUM_NEW_MEMBER', 'f_', $next_order_id)";
+ $this->sql_query($sql);
+ $f_role = $this->db->sql_nextid();
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $f_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'f_%' AND auth_option IN ('f_noapprove')";
+ $this->sql_query($sql);
+ }
+
+ // Set every members user_new column to 0 (old users) only if there is no one yet (this makes sure we do not execute this more than once)
+ $sql = 'SELECT 1
+ FROM ' . USERS_TABLE . '
+ WHERE user_new = 0';
+ $result = $this->db->sql_query_limit($sql, 1);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_new = 0';
+ $this->sql_query($sql);
+ }
+
+ // To mimick the old "feature" we will assign the forum role to every forum, regardless of the setting (this makes sure there are no "this does not work!!!! YUO!!!" posts...
+ // Check if the role is already assigned...
+ $sql = 'SELECT forum_id
+ FROM ' . ACL_GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id . '
+ AND auth_role_id = ' . $f_role;
+ $result = $this->db->sql_query($sql);
+ $is_options = (int) $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ // Not assigned at all... :/
+ if (!$is_options)
+ {
+ // Get postable forums
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type != ' . FORUM_LINK;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->sql_query('INSERT INTO ' . ACL_GROUPS_TABLE . ' (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (' . $group_id . ', ' . (int) $row['forum_id'] . ', 0, ' . $f_role . ', 0)');
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ // Clear permissions...
+ include_once($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
+ $auth_admin = new auth_admin();
+ $auth_admin->acl_clear_prefetch();
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php
new file mode 100644
index 0000000000..a939dbd489
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php
new file mode 100644
index 0000000000..b3f09d8ab8
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php
@@ -0,0 +1,40 @@
+config['version'], '3.0.6-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_cp_fields'))),
+
+ array('config.update', array('version', '3.0.6-rc3')),
+ );
+ }
+
+ public function update_cp_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new format
+ $sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . '
+ SET field_show_on_vt = 1
+ WHERE field_hide = 0
+ AND (field_required = 1 OR field_show_on_reg = 1 OR field_show_profile = 1)';
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php
new file mode 100644
index 0000000000..fc2923f99b
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6-rc4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-rc4')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7.php b/phpBB/includes/db/migration/data/30x/3_0_7.php
new file mode 100644
index 0000000000..9ff2e9e4ab
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.7', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php
new file mode 100644
index 0000000000..c9cc9d19ac
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.7-pl1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7-pl1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php
new file mode 100644
index 0000000000..ffebf66f2d
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php
@@ -0,0 +1,76 @@
+config['version'], '3.0.7-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id' => array('topic_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time' => array('log_time'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('feed_overall', 1)),
+ array('config.add', array('feed_http_auth', 0)),
+ array('config.add', array('feed_limit_post', $this->config['feed_limit'])),
+ array('config.add', array('feed_limit_topic', $this->config['feed_overall_topics_limit'])),
+ array('config.add', array('feed_topics_new', $this->config['feed_overall_topics'])),
+ array('config.add', array('feed_topics_active', $this->config['feed_overall_topics'])),
+ array('custom', array(array(&$this, 'delete_text_templates'))),
+
+ array('config.update', array('version', '3.0.7-rc1')),
+ );
+ }
+
+ public function delete_text_templates()
+ {
+ // Delete all text-templates from the template_data
+ $sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . '
+ WHERE template_filename ' . $this->db->sql_like_expression($this->db->any_char . '.txt');
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php
new file mode 100644
index 0000000000..55bc2bc679
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php
@@ -0,0 +1,73 @@
+config['version'], '3.0.7-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_email_hash'))),
+
+ array('config.update', array('version', '3.0.7-rc2')),
+ );
+ }
+
+ public function update_email_hash($start = 0)
+ {
+ $limit = 1000;
+
+ $sql = 'SELECT user_id, user_email, user_email_hash
+ FROM ' . USERS_TABLE . '
+ WHERE user_type <> ' . USER_IGNORE . "
+ AND user_email <> ''";
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $i = 0;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $i++;
+
+ // Snapshot of the phpbb_email_hash() function
+ // We cannot call it directly because the auto updater updates the DB first. :/
+ $user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']);
+
+ if ($user_email_hash != $row['user_email_hash'])
+ {
+ $sql_ary = array(
+ 'user_email_hash' => $user_email_hash,
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . (int) $row['user_id'];
+ $this->sql_query($sql);
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($i < $limit)
+ {
+ // Completed
+ return;
+ }
+
+ // Return the next start, will be sent to $start when this function is called again
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_8.php b/phpBB/includes/db/migration/data/30x/3_0_8.php
new file mode 100644
index 0000000000..8998ef9627
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_8.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.8', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_8_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.8')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php
new file mode 100644
index 0000000000..aeff35333e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php
@@ -0,0 +1,221 @@
+config['version'], '3.0.8-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_pl1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, 'update_bots'))),
+ array('custom', array(array(&$this, 'delete_orphan_shadow_topics'))),
+ array('module.add', array(
+ 'acp',
+ 'ACP_MESSAGES',
+ array(
+ 'module_basename' => 'acp_board',
+ 'modes' => array('post'),
+ ),
+ )),
+ array('config.add', array('load_unreads_search', 1)),
+ array('config.update_if_equals', array(600, 'queue_interval', 60)),
+ array('config.update_if_equals', array(50, 'email_package_size', 20)),
+
+ array('config.update', array('version', '3.0.8-rc1')),
+ );
+ }
+
+ public function update_file_extension_group_names()
+ {
+ // Update file extension group names to use language strings.
+ $sql = 'SELECT lang_dir
+ FROM ' . LANG_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ $extension_groups_updated = array();
+ while ($lang_dir = $this->db->sql_fetchfield('lang_dir'))
+ {
+ $lang_dir = basename($lang_dir);
+
+ // The language strings we need are either in language/.../acp/attachments.php
+ // in the update package if we're updating to 3.0.8-RC1 or later,
+ // or they are in language/.../install.php when we're updating from 3.0.7-PL1 or earlier.
+ // On an already updated board, they can also already be in language/.../acp/attachments.php
+ // in the board root.
+ $lang_files = array(
+ "{$this->phpbb_root_path}install/update/new/language/$lang_dir/acp/attachments.{$this->php_ext}",
+ "{$this->phpbb_root_path}language/$lang_dir/install.{$this->php_ext}",
+ "{$this->phpbb_root_path}language/$lang_dir/acp/attachments.{$this->php_ext}",
+ );
+
+ foreach ($lang_files as $lang_file)
+ {
+ if (!file_exists($lang_file))
+ {
+ continue;
+ }
+
+ $lang = array();
+ include($lang_file);
+
+ foreach($lang as $lang_key => $lang_val)
+ {
+ if (isset($extension_groups_updated[$lang_key]) || strpos($lang_key, 'EXT_GROUP_') !== 0)
+ {
+ continue;
+ }
+
+ $sql_ary = array(
+ 'group_name' => substr($lang_key, 10), // Strip off 'EXT_GROUP_'
+ );
+
+ $sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
+ WHERE group_name = '" . $this->db->sql_escape($lang_val) . "'";
+ $this->sql_query($sql);
+
+ $extension_groups_updated[$lang_key] = true;
+ }
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET module_auth = \'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)\'
+ WHERE module_class = \'ucp\'
+ AND module_basename = \'profile\'
+ AND module_mode = \'avatar\'';
+ $this->sql_query($sql);
+ }
+
+ public function update_bots()
+ {
+ $bot_name = 'Bing [Bot]';
+ $bot_name_clean = utf8_clean_string($bot_name);
+
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . "
+ WHERE username_clean = '" . $this->db->sql_escape($bot_name_clean) . "'";
+ $result = $this->db->sql_query($sql);
+ $bing_already_added = (bool) $this->db->sql_fetchfield('user_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$bing_already_added)
+ {
+ $bot_agent = 'bingbot/';
+ $bot_ip = '';
+ $sql = 'SELECT group_id, group_colour
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'BOTS'";
+ $result = $this->db->sql_query($sql);
+ $group_row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$group_row)
+ {
+ // default fallback, should never get here
+ $group_row['group_id'] = 6;
+ $group_row['group_colour'] = '9E8DA7';
+ }
+
+ if (!function_exists('user_add'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $user_row = array(
+ 'user_type' => USER_IGNORE,
+ 'group_id' => $group_row['group_id'],
+ 'username' => $bot_name,
+ 'user_regdate' => time(),
+ 'user_password' => '',
+ 'user_colour' => $group_row['group_colour'],
+ 'user_email' => '',
+ 'user_lang' => $this->config['default_lang'],
+ 'user_style' => $this->config['default_style'],
+ 'user_timezone' => 0,
+ 'user_dateformat' => $this->config['default_dateformat'],
+ 'user_allow_massemail' => 0,
+ );
+
+ $user_id = user_add($user_row);
+
+ $sql = 'INSERT INTO ' . BOTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', array(
+ 'bot_active' => 1,
+ 'bot_name' => (string) $bot_name,
+ 'user_id' => (int) $user_id,
+ 'bot_agent' => (string) $bot_agent,
+ 'bot_ip' => (string) $bot_ip,
+ ));
+
+ $this->sql_query($sql);
+ }
+ }
+
+ public function delete_orphan_shadow_topics()
+ {
+ // Delete shadow topics pointing to not existing topics
+ $batch_size = 500;
+
+ // Set of affected forums we have to resync
+ $sync_forum_ids = array();
+
+ $sql_array = array(
+ 'SELECT' => 't1.topic_id, t1.forum_id',
+ 'FROM' => array(
+ TOPICS_TABLE => 't1',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(TOPICS_TABLE => 't2'),
+ 'ON' => 't1.topic_moved_id = t2.topic_id',
+ ),
+ ),
+ 'WHERE' => 't1.topic_moved_id <> 0
+ AND t2.topic_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $topic_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $topic_ids[] = (int) $row['topic_id'];
+
+ $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($topic_ids))
+ {
+ $sql = 'DELETE FROM ' . TOPICS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('topic_id', $topic_ids);
+ $this->db->sql_query($sql);
+
+ // Sync the forums we have deleted shadow topics from.
+ sync('forum', 'forum_id', $sync_forum_ids, true, true);
+
+ return false;
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9.php b/phpBB/includes/db/migration/data/30x/3_0_9.php
new file mode 100644
index 0000000000..d5269ea6f0
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php
new file mode 100644
index 0000000000..1f8622798e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php
@@ -0,0 +1,124 @@
+config['version'], '3.0.9-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_8');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'login_attempts' => array(
+ 'COLUMNS' => array(
+ // this column was removed from the database updater
+ // after 3.0.9-RC3 was released. It might still exist
+ // in 3.0.9-RCX installations and has to be dropped in
+ // 3.0.12 after the db_tools class is capable of properly
+ // removing a primary key.
+ // 'attempt_id' => array('UINT', NULL, 'auto_increment'),
+ 'attempt_ip' => array('VCHAR:40', ''),
+ 'attempt_browser' => array('VCHAR:150', ''),
+ 'attempt_forwarded_for' => array('VCHAR:255', ''),
+ 'attempt_time' => array('TIMESTAMP', 0),
+ 'user_id' => array('UINT', 0),
+ 'username' => array('VCHAR_UNI:255', 0),
+ 'username_clean' => array('VCHAR_CI', 0),
+ ),
+ //'PRIMARY_KEY' => 'attempt_id',
+ 'KEYS' => array(
+ 'att_ip' => array('INDEX', array('attempt_ip', 'attempt_time')),
+ 'att_for' => array('INDEX', array('attempt_forwarded_for', 'attempt_time')),
+ 'att_time' => array('INDEX', array('attempt_time')),
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'bbcodes' => array(
+ 'bbcode_id' => array('USINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'login_attempts',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('ip_login_limit_max', 50)),
+ array('config.add', array('ip_login_limit_time', 21600)),
+ array('config.add', array('ip_login_limit_use_forwarded', 0)),
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'fix_firebird_qa_captcha'))),
+
+ array('config.update', array('version', '3.0.9-rc1')),
+ );
+ }
+
+ public function update_file_extension_group_names()
+ {
+ // Update file extension group names to use language strings, again.
+ $sql = 'SELECT group_id, group_name
+ FROM ' . EXTENSION_GROUPS_TABLE . '
+ WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->any_char);
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'group_name' => substr($row['group_name'], 10), // Strip off 'EXT_GROUP_'
+ );
+
+ $sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE group_id = ' . $row['group_id'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function fix_firebird_qa_captcha()
+ {
+ // Recover from potentially broken Q&A CAPTCHA table on firebird
+ // Q&A CAPTCHA was uninstallable, so it's safe to remove these
+ // without data loss
+ if ($this->db_tools->sql_layer == 'firebird')
+ {
+ $tables = array(
+ $this->table_prefix . 'captcha_questions',
+ $this->table_prefix . 'captcha_answers',
+ $this->table_prefix . 'qa_confirm',
+ );
+ foreach ($tables as $table)
+ {
+ if ($this->db_tools->sql_table_exists($table))
+ {
+ $this->db_tools->sql_table_drop($table);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php
new file mode 100644
index 0000000000..c0e662aa45
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php
new file mode 100644
index 0000000000..d6d1f14b2e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php
new file mode 100644
index 0000000000..e673249343
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc4')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/dev.php b/phpBB/includes/db/migration/data/310/dev.php
new file mode 100644
index 0000000000..13b36bbf30
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/dev.php
@@ -0,0 +1,405 @@
+config['version'], '3.1.0-dev', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ 'phpbb_db_migration_data_310_extensions',
+ 'phpbb_db_migration_data_310_style_update_p2',
+ 'phpbb_db_migration_data_310_timezone_p2',
+ 'phpbb_db_migration_data_310_reported_posts_display',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'style_parent_id' => array('UINT:4', 0),
+ 'style_parent_tree' => array('TEXT', ''),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text' => array('MTEXT_UNI', ''),
+ 'reported_post_uid' => array('VCHAR:8', ''),
+ 'reported_post_bitfield' => array('VCHAR:255', ''),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm',
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path',
+ 'bbcode_bitfield',
+ 'style_parent_id',
+ 'style_parent_tree',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text',
+ 'reported_post_uid',
+ 'reported_post_bitfield',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('search_type', 'phpbb_search_' . $this->config['search_type'])),
+
+ array('config.add', array('fulltext_postgres_ts_name', 'simple')),
+ array('config.add', array('fulltext_postgres_min_word_len', 4)),
+ array('config.add', array('fulltext_postgres_max_word_len', 254)),
+ array('config.add', array('fulltext_sphinx_stopwords', 0)),
+ array('config.add', array('fulltext_sphinx_indexer_mem_limit', 512)),
+
+ array('config.add', array('load_jquery_cdn', 0)),
+ array('config.add', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')),
+
+ array('config.add', array('use_system_cron', 0)),
+
+ array('config.add', array('legend_sort_groupname', 0)),
+ array('config.add', array('teampage_forums', 1)),
+ array('config.add', array('teampage_memberships', 1)),
+
+ array('config.add', array('load_cpf_pm', 0)),
+
+ array('config.add', array('display_last_subject', 1)),
+
+ array('config.add', array('assets_version', 1)),
+
+ array('config.add', array('site_home_url', '')),
+ array('config.add', array('site_home_text', '')),
+
+ array('permission.add', array('u_chgprofileinfo', true, 'u_sig')),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_GROUPS',
+ array(
+ 'module_basename' => 'acp_groups',
+ 'modes' => array('position'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_ATTACHMENTS',
+ array(
+ 'module_basename' => 'acp_attachments',
+ 'modes' => array('manage'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_STYLE_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_styles',
+ 'modes' => array('install', 'cache'),
+ ),
+ )),
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PROFILE',
+ array(
+ 'module_basename' => 'ucp_profile',
+ 'modes' => array('autologin_keys'),
+ ),
+ )),
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_LANGUAGE'
+ )),
+
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_TEMPLATES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_THEMES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_IMAGESETS',
+ )),
+
+ array('custom', array(array($this, 'rename_module_basenames'))),
+ array('custom', array(array($this, 'rename_styles_module'))),
+ array('custom', array(array($this, 'add_group_teampage'))),
+ array('custom', array(array($this, 'update_group_legend'))),
+ array('custom', array(array($this, 'localise_global_announcements'))),
+ array('custom', array(array($this, 'update_ucp_pm_basename'))),
+ array('custom', array(array($this, 'update_ucp_profile_auth'))),
+ array('custom', array(array($this, 'move_customise_modules'))),
+
+ array('config.update', array('version', '3.1.0-dev')),
+ );
+ }
+
+ public function move_customise_modules()
+ {
+ // Move language management to new location in the Customise tab
+ // First get language module id
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_language'";
+ $result = $this->db->sql_query($sql);
+ $language_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+ // Next get language management module id of the one just created
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_langname = 'ACP_LANGUAGE'";
+ $result = $this->db->sql_query($sql);
+ $language_management_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ if (!class_exists('acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ }
+ // acp_modules calls adm_back_link, which is undefined at this point
+ if (!function_exists('adm_back_link'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext);
+ }
+ $module_manager = new acp_modules();
+ $module_manager->module_class = 'acp';
+ $module_manager->move_module($language_module_id, $language_management_module_id);
+ }
+
+ public function update_ucp_pm_basename()
+ {
+ $sql = 'SELECT module_id, module_basename
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename <> 'ucp_pm' AND
+ module_langname='UCP_PM'";
+ $result = $this->db->sql_query_limit($sql, 1);
+
+ if ($row = $this->db->sql_fetchrow($result))
+ {
+ // This update is still not applied. Applying it
+
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_basename = 'ucp_pm'
+ WHERE module_id = " . (int) $row['module_id'];
+
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_ucp_profile_auth()
+ {
+ // Update the auth setting for the module
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'acl_u_chgprofileinfo'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'profile_info'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_styles_module()
+ {
+ // Rename styles module to Customise
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_langname = 'ACP_CAT_CUSTOMISE'
+ WHERE module_langname = 'ACP_CAT_STYLES'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_module_basenames()
+ {
+ // rename all module basenames to full classname
+ $sql = 'SELECT module_id, module_basename, module_class
+ FROM ' . MODULES_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $module_id = (int) $row['module_id'];
+ unset($row['module_id']);
+
+ if (!empty($row['module_basename']) && !empty($row['module_class']))
+ {
+ // all the class names start with class name or with phpbb_ for auto loading
+ if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 &&
+ strpos($row['module_basename'], 'phpbb_') !== 0)
+ {
+ $row['module_basename'] = $row['module_class'] . '_' . $row['module_basename'];
+
+ $sql_update = $this->db->sql_build_array('UPDATE', $row);
+
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET ' . $sql_update . '
+ WHERE module_id = ' . $module_id;
+ $this->sql_query($sql);
+ }
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+
+ public function add_group_teampage()
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 1
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'ADMINISTRATORS'";
+ $this->sql_query($sql);
+
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 2
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'GLOBAL_MODERATORS'";
+ $this->sql_query($sql);
+ }
+
+ public function update_group_legend()
+ {
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_legend = 1
+ ORDER BY group_name ASC';
+ $result = $this->db->sql_query($sql);
+
+ $next_legend = 1;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_legend = ' . $next_legend . '
+ WHERE group_id = ' . (int) $row['group_id'];
+ $this->sql_query($sql);
+
+ $next_legend++;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function localise_global_announcements()
+ {
+ // Localise Global Announcements
+ $sql = 'SELECT topic_id, topic_approved, (topic_replies + 1) AS topic_posts, topic_last_post_id, topic_last_post_subject, topic_last_post_time, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour
+ FROM ' . TOPICS_TABLE . '
+ WHERE forum_id = 0
+ AND topic_type = ' . POST_GLOBAL;
+ $result = $this->db->sql_query($sql);
+
+ $global_announcements = $update_lastpost_data = array();
+ $update_lastpost_data['forum_last_post_time'] = 0;
+ $update_forum_data = array(
+ 'forum_posts' => 0,
+ 'forum_topics' => 0,
+ 'forum_topics_real' => 0,
+ );
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $global_announcements[] = (int) $row['topic_id'];
+
+ $update_forum_data['forum_posts'] += (int) $row['topic_posts'];
+ $update_forum_data['forum_topics_real']++;
+ if ($row['topic_approved'])
+ {
+ $update_forum_data['forum_topics']++;
+ }
+
+ if ($update_lastpost_data['forum_last_post_time'] < $row['topic_last_post_time'])
+ {
+ $update_lastpost_data = array(
+ 'forum_last_post_id' => (int) $row['topic_last_post_id'],
+ 'forum_last_post_subject' => $row['topic_last_post_subject'],
+ 'forum_last_post_time' => (int) $row['topic_last_post_time'],
+ 'forum_last_poster_id' => (int) $row['topic_last_poster_id'],
+ 'forum_last_poster_name' => $row['topic_last_poster_name'],
+ 'forum_last_poster_colour' => $row['topic_last_poster_colour'],
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($global_announcements))
+ {
+ // Update the post/topic-count for the forum and the last-post if needed
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type = ' . FORUM_POST;
+ $result = $this->db->sql_query_limit($sql, 1);
+ $ga_forum_id = $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'SELECT forum_last_post_time
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $result = $this->db->sql_query($sql);
+ $lastpost = (int) $this->db->sql_fetchfield('forum_last_post_time');
+ $this->db->sql_freeresult($result);
+
+ $sql_update = 'forum_posts = forum_posts + ' . $update_forum_data['forum_posts'] . ', ';
+ $sql_update .= 'forum_topics_real = forum_topics_real + ' . $update_forum_data['forum_topics_real'] . ', ';
+ $sql_update .= 'forum_topics = forum_topics + ' . $update_forum_data['forum_topics'];
+ if ($lastpost < $update_lastpost_data['forum_last_post_time'])
+ {
+ $sql_update .= ', ' . $this->db->sql_build_array('UPDATE', $update_lastpost_data);
+ }
+
+ $sql = 'UPDATE ' . FORUMS_TABLE . '
+ SET ' . $sql_update . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $this->sql_query($sql);
+
+ // Update some forum_ids
+ $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
+ foreach ($table_ary as $table)
+ {
+ $sql = "UPDATE $table
+ SET forum_id = $ga_forum_id
+ WHERE " . $this->db->sql_in_set('topic_id', $global_announcements);
+ $this->sql_query($sql);
+ }
+ unset($table_ary);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/extensions.php b/phpBB/includes/db/migration/data/310/extensions.php
new file mode 100644
index 0000000000..6a9caa1cfc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/extensions.php
@@ -0,0 +1,69 @@
+db_tools->sql_table_exists($this->table_prefix . 'ext');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'ext' => array(
+ 'COLUMNS' => array(
+ 'ext_name' => array('VCHAR', ''),
+ 'ext_active' => array('BOOL', 0),
+ 'ext_state' => array('TEXT', ''),
+ ),
+ 'KEYS' => array(
+ 'ext_name' => array('UNIQUE', 'ext_name'),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'ext',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_EXTENSION_MANAGEMENT'
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_EXTENSION_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_extensions',
+ 'modes' => array('main'),
+ ),
+ )),
+ array('permission.add', array('a_extensions', true, 'a_styles')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/reported_posts_display.php b/phpBB/includes/db/migration/data/310/reported_posts_display.php
new file mode 100644
index 0000000000..80a0a0e43f
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/reported_posts_display.php
@@ -0,0 +1,47 @@
+db_tools->sql_column_exists($this->table_prefix . 'reports', 'reported_post_enable_bbcode');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode' => array('BOOL', 1),
+ 'reported_post_enable_smilies' => array('BOOL', 1),
+ 'reported_post_enable_magic_url' => array('BOOL', 1),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode',
+ 'reported_post_enable_smilies',
+ 'reported_post_enable_magic_url',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/style_update_p1.php b/phpBB/includes/db/migration/data/310/style_update_p1.php
new file mode 100644
index 0000000000..e324ce7f24
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/style_update_p1.php
@@ -0,0 +1,157 @@
+db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'styles_update'))),
+ );
+ }
+
+ public function styles_update()
+ {
+ // Get list of valid 3.1 styles
+ $available_styles = array('prosilver');
+
+ $iterator = new DirectoryIterator($this->phpbb_root_path . 'styles');
+ $skip_dirs = array('.', '..', 'prosilver');
+ foreach ($iterator as $fileinfo)
+ {
+ if ($fileinfo->isDir() && !in_array($fileinfo->getFilename(), $skip_dirs) && file_exists($fileinfo->getPathname() . '/style.cfg'))
+ {
+ $style_cfg = parse_cfg_file($fileinfo->getPathname() . '/style.cfg');
+ if (isset($style_cfg['phpbb_version']) && version_compare($style_cfg['phpbb_version'], '3.1.0-dev', '>='))
+ {
+ // 3.1 style
+ $available_styles[] = $fileinfo->getFilename();
+ }
+ }
+ }
+
+ // Get all installed styles
+ if ($this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'))
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id, i.imageset_path
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . 'styles_theme c, ' . $this->table_prefix . "styles_imageset i
+ WHERE t.template_id = s.template_id
+ AND c.theme_id = s.theme_id
+ AND i.imageset_id = s.imageset_id";
+ }
+ else
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "stles_theme c
+ WHERE t.template_id = s.template_id
+ AND c.theme_id = s.theme_id";
+ }
+ $result = $this->db->sql_query($sql);
+
+ $styles = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $styles[] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ // Decide which styles to keep, all others will be deleted
+ $valid_styles = array();
+ foreach ($styles as $style_row)
+ {
+ if (
+ // Delete styles with parent style (not supported yet)
+ $style_row['template_inherits_id'] == 0 &&
+ // Check if components match
+ $style_row['template_path'] == $style_row['theme_path'] && (!isset($style_row['imageset_path']) || $style_row['template_path'] == $style_row['imageset_path']) &&
+ // Check if components are valid
+ in_array($style_row['template_path'], $available_styles)
+ )
+ {
+ // Valid style. Keep it
+ $sql_ary = array(
+ 'style_path' => $style_row['template_path'],
+ 'bbcode_bitfield' => $style_row['bbcode_bitfield'],
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+ );
+ $this->sql_query('UPDATE ' . STYLES_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE style_id = ' . $style_row['style_id']);
+ $valid_styles[] = (int) $style_row['style_id'];
+ }
+ }
+
+ // Remove old entries from styles table
+ if (!sizeof($valid_styles))
+ {
+ // No valid styles: remove everything and add prosilver
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary);
+
+ $sql_ary = array(
+ 'style_name' => 'prosilver',
+ 'style_copyright' => '© phpBB Group',
+ 'style_active' => 1,
+ 'style_path' => 'prosilver',
+ 'bbcode_bitfield' => 'lNg=',
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+
+ // Will be removed in the next step
+ 'imageset_id' => 0,
+ 'template_id' => 0,
+ 'theme_id' => 0,
+ );
+
+ $sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->sql_query($sql);
+
+ $sql = 'SELECT style_id
+ FROM ' . $table . "
+ WHERE style_name = 'prosilver'";
+ $result = $this->sql_query($sql);
+ $default_style = $this->db->sql_fetchfield($result);
+ $this->db->sql_freeresult($result);
+
+ set_config('default_style', $default_style);
+
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0';
+ $this->sql_query($sql);
+ }
+ else
+ {
+ // There are valid styles in styles table. Remove styles that are outdated
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE . '
+ WHERE ' . $this->db->sql_in_set('style_id', $valid_styles, true));
+
+ // Change default style
+ if (!in_array($this->config['default_style'], $valid_styles))
+ {
+ $this->sql_query('UPDATE ' . CONFIG_TABLE . "
+ SET config_value = '" . $valid_styles[0] . "'
+ WHERE config_name = 'default_style'");
+ }
+
+ // Reset styles for users
+ $this->sql_query('UPDATE ' . USERS_TABLE . '
+ SET user_style = 0
+ WHERE ' . $this->db->sql_in_set('user_style', $valid_styles, true));
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/style_update_p2.php b/phpBB/includes/db/migration/data/310/style_update_p2.php
new file mode 100644
index 0000000000..7b10518a66
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/style_update_p2.php
@@ -0,0 +1,129 @@
+db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_310_style_update_p1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id',
+ 'template_id',
+ 'theme_id',
+ ),
+ ),
+
+ 'drop_tables' => array(
+ $this->table_prefix . 'styles_imageset',
+ $this->table_prefix . 'styles_imageset_data',
+ $this->table_prefix . 'styles_template',
+ $this->table_prefix . 'styles_template_data',
+ $this->table_prefix . 'styles_theme',
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id' => array('UINT', 0),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ ),
+ ),
+
+ 'add_tables' => array(
+ $this->table_prefix . 'styles_imageset' => array(
+ 'COLUMNS' => array(
+ 'imageset_id' => array('UINT', NULL, 'auto_increment'),
+ 'imageset_name' => array('VCHAR_UNI:255', ''),
+ 'imageset_copyright' => array('VCHAR_UNI', ''),
+ 'imageset_path' => array('VCHAR:100', ''),
+ ),
+ 'PRIMARY_KEY' => 'imageset_id',
+ 'KEYS' => array(
+ 'imgset_nm' => array('UNIQUE', 'imageset_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'COLUMNS' => array(
+ 'image_id' => array('UINT', NULL, 'auto_increment'),
+ 'image_name' => array('VCHAR:200', ''),
+ 'image_filename' => array('VCHAR:200', ''),
+ 'image_lang' => array('VCHAR:30', ''),
+ 'image_height' => array('USINT', 0),
+ 'image_width' => array('USINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'image_id',
+ 'KEYS' => array(
+ 'i_d' => array('INDEX', 'imageset_id'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', NULL, 'auto_increment'),
+ 'template_name' => array('VCHAR_UNI:255', ''),
+ 'template_copyright' => array('VCHAR_UNI', ''),
+ 'template_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'template_storedb' => array('BOOL', 0),
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'template_id',
+ 'KEYS' => array(
+ 'tmplte_nm' => array('UNIQUE', 'template_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', 0),
+ 'template_filename' => array('VCHAR:100', ''),
+ 'template_included' => array('TEXT', ''),
+ 'template_mtime' => array('TIMESTAMP', 0),
+ 'template_data' => array('MTEXT_UNI', ''),
+ ),
+ 'KEYS' => array(
+ 'tid' => array('INDEX', 'template_id'),
+ 'tfn' => array('INDEX', 'template_filename'),
+ ),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'COLUMNS' => array(
+ 'theme_id' => array('UINT', NULL, 'auto_increment'),
+ 'theme_name' => array('VCHAR_UNI:255', ''),
+ 'theme_copyright' => array('VCHAR_UNI', ''),
+ 'theme_path' => array('VCHAR:100', ''),
+ 'theme_storedb' => array('BOOL', 0),
+ 'theme_mtime' => array('TIMESTAMP', 0),
+ 'theme_data' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'theme_id',
+ 'KEYS' => array(
+ 'theme_name' => array('UNIQUE', 'theme_name'),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/update_helpers.php b/phpBB/includes/db/migration/data/310/timezone.php
similarity index 72%
rename from phpBB/includes/update_helpers.php
rename to phpBB/includes/db/migration/data/310/timezone.php
index 69d678b2f8..6e50cbe45f 100644
--- a/phpBB/includes/update_helpers.php
+++ b/phpBB/includes/db/migration/data/310/timezone.php
@@ -1,16 +1,67 @@
db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_timezone' => array('VCHAR:100', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_timezones'))),
+ );
+ }
+
+ public function update_timezones()
+ {
+ // Update user timezones
+ $sql = 'SELECT user_dst, user_timezone
+ FROM ' . $this->table_prefix . 'users
+ GROUP BY user_timezone, user_dst';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_timezone = '" . $this->db->sql_escape($this->convert_phpbb30_timezone($row['user_timezone'], $row['user_dst'])) . "'
+ WHERE user_timezone = '" . $this->db->sql_escape($row['user_timezone']) . "'
+ AND user_dst = " . (int) $row['user_dst'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+
+ // Update board default timezone
+ $sql = 'UPDATE ' . $this->table_prefix . "config
+ SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "'
+ WHERE config_name = 'board_timezone'";
+ $this->sql_query($sql);
+ }
+
/**
* Determine the new timezone for a given phpBB 3.0 timezone and
* "Daylight Saving Time" option
@@ -19,7 +70,7 @@ class phpbb_update_helpers
* @param $dst int Users daylight saving time
* @return string Users new php Timezone which is used since 3.1
*/
- function convert_phpbb30_timezone($timezone, $dst)
+ public function convert_phpbb30_timezone($timezone, $dst)
{
$offset = $timezone + $dst;
diff --git a/phpBB/includes/db/migration/data/310/timezone_p2.php b/phpBB/includes/db/migration/data/310/timezone_p2.php
new file mode 100644
index 0000000000..113b979e4f
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/timezone_p2.php
@@ -0,0 +1,43 @@
+db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_310_timezone');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/exception.php b/phpBB/includes/db/migration/exception.php
new file mode 100644
index 0000000000..e84330dd71
--- /dev/null
+++ b/phpBB/includes/db/migration/exception.php
@@ -0,0 +1,79 @@
+parameters = $parameters;
+ }
+
+ /**
+ * Output the error as a string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->message . ': ' . var_export($this->parameters, true);
+ }
+
+ /**
+ * Get the parameters
+ *
+ * @return array
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Get localised message (with $user->lang())
+ *
+ * @param phpbb_user $user
+ * @return string
+ */
+ public function getLocalisedMessage(phpbb_user $user)
+ {
+ $parameters = $this->getParameters();
+ array_unshift($parameters, $this->getMessage());
+
+ return call_user_func_array(array($user, 'lang'), $parameters);
+ }
+}
diff --git a/phpBB/includes/db/migration/migration.php b/phpBB/includes/db/migration/migration.php
new file mode 100644
index 0000000000..5f14a6953c
--- /dev/null
+++ b/phpBB/includes/db/migration/migration.php
@@ -0,0 +1,190 @@
+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;
+ }
+}
diff --git a/phpBB/includes/db/migration/tool/config.php b/phpBB/includes/db/migration/tool/config.php
new file mode 100644
index 0000000000..458a25fb66
--- /dev/null
+++ b/phpBB/includes/db/migration/tool/config.php
@@ -0,0 +1,150 @@
+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_EXIST', $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);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/tool/interface.php b/phpBB/includes/db/migration/tool/interface.php
new file mode 100644
index 0000000000..ced53b2023
--- /dev/null
+++ b/phpBB/includes/db/migration/tool/interface.php
@@ -0,0 +1,33 @@
+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_NOT_EXIST', $parent);
+ }
+
+ $parent = $data['parent_id'] = $module_id;
+ }
+ else if (!$this->exists($class, false, $parent))
+ {
+ throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent);
+ }
+
+ if ($this->exists($class, $parent, $data['module_langname']))
+ {
+ 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']) . '
';
+ }
+ }
+ }
+ 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('MODULE_NOT_REMOVABLE', $module_id, $result);
+ }
+ }
+
+ $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);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/tool/permission.php b/phpBB/includes/db/migration/tool/permission.php
new file mode 100644
index 0000000000..4231fbe1dd
--- /dev/null
+++ b/phpBB/includes/db/migration/tool/permission.php
@@ -0,0 +1,622 @@
+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_EXIST', $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)
+ {
+ return;
+ }
+
+ $sql = 'SELECT MAX(role_order) AS max_role_order
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = '" . $this->db->sql_escape($role_type) . "'";
+ $this->db->sql_query($sql);
+ $role_order = (int) $this->db->sql_fetchfield('max_role_order');
+ $role_order = (!$role_order) ? 1 : $role_order + 1;
+
+ $sql_ary = array(
+ 'role_name' => $role_name,
+ 'role_description' => $role_description,
+ 'role_type' => $role_type,
+ 'role_order' => $role_order,
+ );
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Update the name on a permission role
+ *
+ * @param string $old_role_name The old role name
+ * @param string $new_role_name The new role name
+ * @return null
+ */
+ public function role_update($old_role_name, $new_role_name)
+ {
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $old_role_name);
+ }
+
+ $sql = 'UPDATE ' . ACL_ROLES_TABLE . "
+ SET role_name = '" . $this->db->sql_escape($new_role_name) . "'
+ WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Remove a permission role
+ *
+ * @param string $role_name The role name to remove
+ * @return null
+ */
+ public function role_remove($role_name)
+ {
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ 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);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migrator.php b/phpBB/includes/db/migrator.php
new file mode 100644
index 0000000000..74f71775f3
--- /dev/null
+++ b/phpBB/includes/db/migrator.php
@@ -0,0 +1,772 @@
+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();
+
+ // prevent errors in case the table does not exist yet
+ $this->db->sql_return_on_error(true);
+
+ $sql = "SELECT *
+ FROM " . $this->migrations_table;
+ $result = $this->db->sql_query($sql);
+
+ if (!$this->db->sql_error_triggered)
+ {
+ while ($migration = $this->db->sql_fetchrow($result))
+ {
+ $this->migration_state[$migration['migration_name']] = $migration;
+
+ $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+
+ $this->db->sql_return_on_error(false);
+ }
+
+ /**
+ * Sets the list of available migration class names to the given array.
+ *
+ * @param array $class_names An array of migration class names
+ * @return null
+ */
+ public function set_migrations($class_names)
+ {
+ $this->migrations = $class_names;
+ }
+
+ /**
+ * 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)
+ {
+ $unfulfillable = $this->unfulfillable($name);
+ if ($unfulfillable !== false)
+ {
+ throw new phpbb_db_migration_exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable);
+ }
+ }
+ }
+
+ 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|string False if fulfillable, string of missing migration name if unfulfillable
+ */
+ public function unfulfillable($name)
+ {
+ if (isset($this->migration_state[$name]))
+ {
+ return false;
+ }
+
+ if (!class_exists($name))
+ {
+ return $name;
+ }
+
+ $migration = $this->get_migration($name);
+ $depends = $migration->depends_on();
+
+ foreach ($depends as $depend)
+ {
+ $unfulfillable = $this->unfulfillable($depend);
+ if ($unfulfillable !== false)
+ {
+ return $unfulfillable;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether all available, fulfillable migrations have been applied.
+ *
+ * @return bool Whether the migrations have been applied
+ */
+ public function finished()
+ {
+ foreach ($this->migrations as $name)
+ {
+ if (!isset($this->migration_state[$name]))
+ {
+ // skip unfulfillable migrations, but fulfillables mean we
+ // are not finished yet
+ if ($this->unfulfillable($name) !== false)
+ {
+ continue;
+ }
+ return false;
+ }
+
+ $migration = $this->migration_state[$name];
+
+ if (!$migration['migration_schema_done'] || !$migration['migration_data_done'])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets a migration state (whether it is installed and to what extent)
+ *
+ * @param string $migration String migration name to check if it is installed
+ * @return bool|array False if the migration has not at all been installed, array
+ */
+ public function migration_state($migration)
+ {
+ if (!isset($this->migration_state[$migration]))
+ {
+ return false;
+ }
+
+ return $this->migration_state[$migration];
+ }
+
+ /**
+ * Helper to get a migration
+ *
+ * @param string $name Name of the migration
+ * @return phpbb_db_migration
+ */
+ 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);
+ }
+}
diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php
index 9d076eb6c5..d51589d719 100644
--- a/phpBB/includes/extension/base.php
+++ b/phpBB/includes/extension/base.php
@@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))
exit;
}
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* A base class for extensions without custom enable/disable/purge code.
*
@@ -22,6 +24,19 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_extension_base implements phpbb_extension_interface
{
+ /** @var ContainerInterface */
+ protected $container;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Container object
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
/**
* Single enable step that does nothing
*
diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php
index de6f364320..21a9ec1370 100644
--- a/phpBB/includes/extension/manager.php
+++ b/phpBB/includes/extension/manager.php
@@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))
exit;
}
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* The extension manager provides means to activate/deactivate extensions.
*
@@ -22,8 +24,12 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_extension_manager
{
+ /** @var ContainerInterface */
+ protected $container;
+
protected $db;
protected $config;
+ protected $migrator;
protected $cache;
protected $php_ext;
protected $extensions;
@@ -34,6 +40,7 @@ class phpbb_extension_manager
/**
* Creates a manager and loads information from database
*
+ * @param ContainerInterface $container A container
* @param phpbb_db_driver $db A database connection
* @param phpbb_config $config phpbb_config
* @param string $extension_table The name of the table holding extensions
@@ -42,11 +49,13 @@ class phpbb_extension_manager
* @param phpbb_cache_driver_interface $cache A cache instance or null
* @param string $cache_name The name of the cache variable, defaults to _ext
*/
- public function __construct(phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
+ public function __construct(ContainerInterface $container, phpbb_db_driver $db, phpbb_config $config, phpbb_db_migrator $migrator, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
{
+ $this->container = $container;
$this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
$this->config = $config;
+ $this->migrator = $migrator;
$this->cache = $cache;
$this->php_ext = $php_ext;
$this->extension_table = $extension_table;
@@ -126,11 +135,11 @@ class phpbb_extension_manager
if (class_exists($extension_class_name))
{
- return new $extension_class_name;
+ return new $extension_class_name($this->container);
}
else
{
- return new phpbb_extension_base;
+ return new phpbb_extension_base($this->container);
}
}
@@ -166,6 +175,12 @@ class phpbb_extension_manager
$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
+ // Returns false if not completed
+ if (!$this->handle_migrations($name, 'enable'))
+ {
+ return true;
+ }
+
$extension = $this->get_extension($name);
$state = $extension->enable_step($old_state);
@@ -317,6 +332,12 @@ class phpbb_extension_manager
$old_state = unserialize($this->extensions[$name]['ext_state']);
+ // Returns false if not completed
+ if (!$this->handle_migrations($name, 'purge'))
+ {
+ return true;
+ }
+
$extension = $this->get_extension($name);
$state = $extension->purge_step($old_state);
@@ -490,4 +511,58 @@ class phpbb_extension_manager
{
return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
}
+
+ /**
+ * Handle installing/reverting migrations
+ *
+ * @param string $extension_name Name of the extension
+ * @param string $mode enable or purge
+ * @return bool True if completed, False if not completed
+ */
+ protected function handle_migrations($extension_name, $mode)
+ {
+ $migrations_path = $this->phpbb_root_path . $this->get_extension_path($extension_name) . 'migrations/';
+ if (!file_exists($migrations_path) || !is_dir($migrations_path))
+ {
+ return true;
+ }
+
+ $migrations = $this->migrator->load_migrations($migrations_path);
+
+ // What is a safe limit of execution time? Half the max execution time should be safe.
+ $safe_time_limit = (ini_get('max_execution_time') / 2);
+ $start_time = time();
+
+ if ($mode == 'enable')
+ {
+ while (!$this->migrator->finished())
+ {
+ $this->migrator->update();
+
+ // Are we approaching the time limit? If so we want to pause the update and continue after refreshing
+ if ((time() - $start_time) >= $safe_time_limit)
+ {
+ return false;
+ }
+ }
+ }
+ else if ($mode == 'purge')
+ {
+ foreach ($migrations as $migration)
+ {
+ while ($this->migrator->migration_state($migration) !== false)
+ {
+ $this->migrator->revert($migration);
+
+ // Are we approaching the time limit? If so we want to pause the update and continue after refreshing
+ if ((time() - $start_time) >= $safe_time_limit)
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index d0ef2759d5..d9af268f80 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -97,7 +97,18 @@ function request_var($var_name, $default, $multibyte = false, $cookie = false, $
}
/**
-* Set config value. Creates missing config entry.
+* Sets a configuration option's value.
+*
+* Please note that this function does not update the is_dynamic value for
+* an already existing config option.
+*
+* @param string $config_name The configuration option's name
+* @param string $config_value New configuration value
+* @param bool $is_dynamic Whether this variable should be cached (false) or
+* if it changes too frequently (true) to be
+* efficiently cached.
+*
+* @return null
*
* @deprecated
*/
@@ -119,7 +130,15 @@ function set_config($config_name, $config_value, $is_dynamic = false, phpbb_conf
}
/**
-* Set dynamic config value with arithmetic operation.
+* Increments an integer config value directly in the database.
+*
+* @param string $config_name The configuration option's name
+* @param int $increment Amount to increment by
+* @param bool $is_dynamic Whether this variable should be cached (false) or
+* if it changes too frequently (true) to be
+* efficiently cached.
+*
+* @return null
*
* @deprecated
*/
@@ -5582,7 +5601,7 @@ function phpbb_convert_30_dbms_to_31($dbms)
/*
$reflection = new \ReflectionClass($dbms);
-
+
if ($reflection->isSubclassOf('phpbb_db_driver'))
{
return $dbms;
diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php
index 32fd76e74d..d6bd9e35dd 100644
--- a/phpBB/includes/functions_acp.php
+++ b/phpBB/includes/functions_acp.php
@@ -443,6 +443,13 @@ function validate_config_vars($config_vars, &$cfg_array, &$error)
}
break;
+ case 'email':
+ if (!preg_match('/^' . get_preg_expression('email') . '$/i', $cfg_array[$config_name]))
+ {
+ $error[] = $user->lang['EMAIL_INVALID_EMAIL'];
+ }
+ break;
+
// Absolute path
case 'script_path':
if (!$cfg_array[$config_name])
diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php
index d0a02567ad..821f0d970d 100644
--- a/phpBB/includes/functions_messenger.php
+++ b/phpBB/includes/functions_messenger.php
@@ -392,6 +392,28 @@ class messenger
}
}
+ /**
+ * Generates a valid message id to be used in emails
+ *
+ * @return string message id
+ */
+ function generate_message_id()
+ {
+ global $config;
+
+ $domain = 'phpbb.generated';
+ if ($config['server_name'])
+ {
+ $domain = $config['server_name'];
+ }
+ else if (!empty($_SERVER['SERVER_NAME']))
+ {
+ $domain = $_SERVER['SERVER_NAME'];
+ }
+
+ return md5(unique_id(time())) . '@' . $domain;
+ }
+
/**
* Return email header
*/
@@ -418,7 +440,7 @@ class messenger
$headers[] = 'Return-Path: <' . $config['board_email'] . '>';
$headers[] = 'Sender: <' . $config['board_email'] . '>';
$headers[] = 'MIME-Version: 1.0';
- $headers[] = 'Message-ID: <' . md5(unique_id(time())) . '@' . $config['server_name'] . '>';
+ $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';
$headers[] = 'Date: ' . date('r', time());
$headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
$headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php
index 6abcdee8f2..b7878ddfc7 100644
--- a/phpBB/includes/functions_user.php
+++ b/phpBB/includes/functions_user.php
@@ -2698,12 +2698,12 @@ function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow
}
$db->sql_freeresult($result);
- if (isset($sql_ary['group_avatar']) && !$sql_ary['group_avatar'])
+ if (isset($sql_ary['group_avatar']))
{
remove_default_avatar($group_id, $user_ary);
}
- if (isset($sql_ary['group_rank']) && !$sql_ary['group_rank'])
+ if (isset($sql_ary['group_rank']))
{
remove_default_rank($group_id, $user_ary);
}
@@ -3208,8 +3208,8 @@ function remove_default_avatar($group_id, $user_ids)
user_avatar_width = 0,
user_avatar_height = 0
WHERE group_id = " . (int) $group_id . "
- AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
- AND " . $db->sql_in_set('user_id', $user_ids);
+ AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
+ AND " . $db->sql_in_set('user_id', $user_ids);
$db->sql_query($sql);
}
@@ -3246,9 +3246,9 @@ function remove_default_rank($group_id, $user_ids)
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_rank = 0
WHERE group_id = ' . (int)$group_id . '
- AND user_rank <> 0
- AND user_rank = ' . (int)$row['group_rank'] . '
- AND ' . $db->sql_in_set('user_id', $user_ids);
+ AND user_rank <> 0
+ AND user_rank = ' . (int)$row['group_rank'] . '
+ AND ' . $db->sql_in_set('user_id', $user_ids);
$db->sql_query($sql);
}
@@ -3277,7 +3277,8 @@ function group_user_attributes($action, $group_id, $user_id_ary = false, $userna
case 'demote':
case 'promote':
- $sql = 'SELECT user_id FROM ' . USER_GROUP_TABLE . "
+ $sql = 'SELECT user_id
+ FROM ' . USER_GROUP_TABLE . "
WHERE group_id = $group_id
AND user_pending = 1
AND " . $db->sql_in_set('user_id', $user_id_ary);
@@ -3375,7 +3376,8 @@ function group_user_attributes($action, $group_id, $user_id_ary = false, $userna
return 'NO_USERS';
}
- $sql = 'SELECT user_id, group_id FROM ' . USERS_TABLE . '
+ $sql = 'SELECT user_id, group_id
+ FROM ' . USERS_TABLE . '
WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
$result = $db->sql_query($sql);
@@ -3509,45 +3511,69 @@ function group_set_user_default($group_id, $user_id_ary, $group_attributes = fal
}
}
- // Before we update the user attributes, we will make a list of those having now the group avatar assigned
+ $updated_sql_ary = $sql_ary;
+
+ // Before we update the user attributes, we will update the rank for users that don't have a custom rank
+ if (isset($sql_ary['user_rank']))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
+ WHERE user_rank = 0
+ AND ' . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
+ unset($sql_ary['user_rank']);
+ }
+
+ // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
+ $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
+
if (isset($sql_ary['user_avatar']))
{
- // Ok, get the original avatar data from users having an uploaded one (we need to remove these from the filesystem)
- $sql = 'SELECT user_id, group_id, user_avatar
- FROM ' . USERS_TABLE . '
- WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . '
- AND user_avatar_type = ' . AVATAR_UPLOAD;
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
+ $avatar_sql_ary = array();
+ foreach ($avatar_options as $avatar_option)
{
- avatar_delete('user', $row);
- }
- $db->sql_freeresult($result);
- }
- else
- {
- unset($sql_ary['user_avatar_type']);
- unset($sql_ary['user_avatar_height']);
- unset($sql_ary['user_avatar_width']);
+ if (isset($sql_ary[$avatar_option]))
+ {
+ $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
+ }
+ }
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
+ WHERE user_avatar = ''
+ AND " . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
}
- $sql = 'UPDATE ' . USERS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
- WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
- $db->sql_query($sql);
+ // Remove the avatar options, as we already updated them
+ foreach ($avatar_options as $avatar_option)
+ {
+ unset($sql_ary[$avatar_option]);
+ }
+
+ if (!empty($sql_ary))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
+ $db->sql_query($sql);
+ }
if (isset($sql_ary['user_colour']))
{
// Update any cached colour information for these users
- $sql = 'UPDATE ' . FORUMS_TABLE . " SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . FORUMS_TABLE . "
+ SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
$db->sql_query($sql);
- $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . TOPICS_TABLE . "
+ SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
$db->sql_query($sql);
- $sql = 'UPDATE ' . TOPICS_TABLE . " SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
+ $sql = 'UPDATE ' . TOPICS_TABLE . "
+ SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
$db->sql_query($sql);
@@ -3559,6 +3585,9 @@ function group_set_user_default($group_id, $user_id_ary, $group_attributes = fal
}
}
+ // Make all values available for the event
+ $sql_ary = $updated_sql_ary;
+
/**
* Event when the default group is set for an array of users
*
diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php
index 3426d62cdb..8da303f6e3 100644
--- a/phpBB/includes/mcp/mcp_reports.php
+++ b/phpBB/includes/mcp/mcp_reports.php
@@ -71,7 +71,7 @@ class mcp_reports
// closed reports are accessed by report id
$report_id = request_var('r', 0);
- $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, r.reported_post_text, r.reported_post_uid, r.reported_post_bitfield, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour
+ $sql = 'SELECT r.post_id, r.user_id, r.report_id, r.report_closed, report_time, r.report_text, r.reported_post_text, r.reported_post_uid, r.reported_post_bitfield, r.reported_post_enable_magic_url, r.reported_post_enable_smilies, r.reported_post_enable_bbcode, rr.reason_title, rr.reason_description, u.username, u.username_clean, u.user_colour
FROM ' . REPORTS_TABLE . ' r, ' . REPORTS_REASONS_TABLE . ' rr, ' . USERS_TABLE . ' u
WHERE ' . (($report_id) ? 'r.report_id = ' . $report_id : "r.post_id = $post_id") . '
AND rr.reason_id = r.reason_id
@@ -94,6 +94,10 @@ class mcp_reports
$post_id = $report['post_id'];
$report_id = $report['report_id'];
+
+ $parse_post_flags = $report['reported_post_enable_bbcode'] ? OPTION_FLAG_BBCODE : 0;
+ $parse_post_flags += $report['reported_post_enable_smilies'] ? OPTION_FLAG_SMILIES : 0;
+ $parse_post_flags += $report['reported_post_enable_magic_url'] ? OPTION_FLAG_LINKS : 0;
$post_info = get_post_data(array($post_id), 'm_report', true);
@@ -136,18 +140,7 @@ class mcp_reports
$post_unread = (isset($topic_tracking_info[$post_info['topic_id']]) && $post_info['post_time'] > $topic_tracking_info[$post_info['topic_id']]) ? true : false;
- // Process message, leave it uncensored
- $message = $post_info['post_text'];
- if ($post_info['bbcode_bitfield'])
- {
- include_once($phpbb_root_path . 'includes/bbcode.' . $phpEx);
- $bbcode = new bbcode($post_info['bbcode_bitfield']);
- $bbcode->bbcode_second_pass($message, $post_info['bbcode_uid'], $post_info['bbcode_bitfield']);
- }
-
- $message = bbcode_nl2br($message);
- $message = smiley_text($message);
$report['report_text'] = make_clickable(bbcode_nl2br($report['report_text']));
if ($post_info['post_attachment'] && $auth->acl_get('u_download') && $auth->acl_get('f_download', $post_info['forum_id']))
@@ -168,7 +161,7 @@ class mcp_reports
if (sizeof($attachments))
{
$update_count = array();
- parse_attachments($post_info['forum_id'], $message, $attachments, $update_count);
+ parse_attachments($post_info['forum_id'], $report['reported_post_text'], $attachments, $update_count);
}
// Display not already displayed Attachments for this post, we already parsed them. ;)
@@ -227,7 +220,7 @@ class mcp_reports
'REPORTER_NAME' => get_username_string('username', $report['user_id'], $report['username'], $report['user_colour']),
'U_VIEW_REPORTER_PROFILE' => get_username_string('profile', $report['user_id'], $report['username'], $report['user_colour']),
- 'POST_PREVIEW' => generate_text_for_display($report['reported_post_text'], $report['reported_post_uid'], $report['reported_post_bitfield'], OPTION_FLAG_BBCODE | OPTION_FLAG_SMILIES, false),
+ 'POST_PREVIEW' => generate_text_for_display($report['reported_post_text'], $report['reported_post_uid'], $report['reported_post_bitfield'], $parse_post_flags, false),
'POST_SUBJECT' => ($post_info['post_subject']) ? $post_info['post_subject'] : $user->lang['NO_SUBJECT'],
'POST_DATE' => $user->format_date($post_info['post_time']),
'POST_IP' => $post_info['poster_ip'],
diff --git a/phpBB/includes/search/base.php b/phpBB/includes/search/base.php
index b364dead9a..914cef9167 100644
--- a/phpBB/includes/search/base.php
+++ b/phpBB/includes/search/base.php
@@ -94,7 +94,7 @@ class phpbb_search_base
*
* @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE
*/
- function obtain_ids($search_key, &$result_count, &$id_ary, $start, $per_page, $sort_dir)
+ function obtain_ids($search_key, &$result_count, &$id_ary, &$start, $per_page, $sort_dir)
{
global $cache;
@@ -109,6 +109,19 @@ class phpbb_search_base
$reverse_ids = ($stored_ids[-2] != $sort_dir) ? true : false;
$complete = true;
+ // Change start parameter in case out of bounds
+ if ($result_count)
+ {
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+ else if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+ }
+ }
+
// change the start to the actual end of the current request if the sort direction differs
// from the dirction in the cache and reverse the ids later
if ($reverse_ids)
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 324c214e91..adaf025730 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -353,7 +353,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts
if (!$this->search_query)
@@ -375,6 +375,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
implode(',', $author_ary)
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -488,16 +493,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$id_ary = array_unique($id_ary);
- if (!sizeof($id_ary))
- {
- return false;
- }
-
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
- $sql = 'SELECT FOUND_ROWS() as result_count';
- $result = $this->db->sql_query($sql);
+ $sql_found_rows = 'SELECT FOUND_ROWS() as result_count';
+ $result = $this->db->sql_query($sql_found_rows);
$result_count = (int) $this->db->sql_fetchfield('result_count');
$this->db->sql_freeresult($result);
@@ -507,6 +507,21 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
}
}
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -533,7 +548,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -557,6 +572,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$author_name,
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -662,8 +682,8 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
// retrieve the total result count if needed
if (!$result_count)
{
- $sql = 'SELECT FOUND_ROWS() as result_count';
- $result = $this->db->sql_query($sql);
+ $sql_found_rows = 'SELECT FOUND_ROWS() as result_count';
+ $result = $this->db->sql_query($sql_found_rows);
$result_count = (int) $this->db->sql_fetchfield('result_count');
$this->db->sql_freeresult($result);
@@ -673,6 +693,20 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
}
}
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php
index 53df8348ae..c9f33054fc 100644
--- a/phpBB/includes/search/fulltext_native.php
+++ b/phpBB/includes/search/fulltext_native.php
@@ -516,7 +516,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts.
if (empty($this->search_query))
@@ -855,10 +855,6 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
$this->db->sql_freeresult($result);
- if (!sizeof($id_ary))
- {
- return false;
- }
// if we use mysql and the total result count is not cached yet, retrieve it from the db
if (!$total_results && $is_mysql)
@@ -867,14 +863,14 @@ class phpbb_search_fulltext_native extends phpbb_search_base
$sql_array_copy = $sql_array;
$sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id ';
- $sql = $this->db->sql_build_query('SELECT', $sql_array_copy);
+ $sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy);
unset($sql_array_copy);
- $this->db->sql_query($sql);
+ $this->db->sql_query($sql_calc);
$this->db->sql_freeresult($result);
- $sql = 'SELECT FOUND_ROWS() as total_results';
- $result = $this->db->sql_query($sql);
+ $sql_count = 'SELECT FOUND_ROWS() as total_results';
+ $result = $this->db->sql_query($sql_count);
$total_results = (int) $this->db->sql_fetchfield('total_results');
$this->db->sql_freeresult($result);
@@ -884,6 +880,20 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
}
+ if ($start >= $total_results)
+ {
+ $start = floor(($total_results - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[(($type == 'posts') ? 'post_id' : 'topic_id')];
+ }
+ $this->db->sql_freeresult($result);
+
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -910,7 +920,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -1096,13 +1106,13 @@ class phpbb_search_fulltext_native extends phpbb_search_base
if (!$total_results && $is_mysql)
{
// Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it.
- $sql = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql);
+ $sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql);
- $this->db->sql_query($sql);
+ $this->db->sql_query($sql_calc);
$this->db->sql_freeresult($result);
- $sql = 'SELECT FOUND_ROWS() as total_results';
- $result = $this->db->sql_query($sql);
+ $sql_count = 'SELECT FOUND_ROWS() as total_results';
+ $result = $this->db->sql_query($sql_count);
$total_results = (int) $this->db->sql_fetchfield('total_results');
$this->db->sql_freeresult($result);
@@ -1112,6 +1122,19 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
}
+ if ($start >= $total_results)
+ {
+ $start = floor(($total_results - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $total_results, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php
index 1475cc31d0..eeb628b18f 100644
--- a/phpBB/includes/search/fulltext_postgres.php
+++ b/phpBB/includes/search/fulltext_postgres.php
@@ -343,7 +343,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts
if (!$this->search_query)
@@ -371,6 +371,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
implode(',', $author_ary)
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -495,11 +500,6 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$id_ary = array_unique($id_ary);
- if (!sizeof($id_ary))
- {
- return false;
- }
-
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
@@ -518,6 +518,21 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$this->db->sql_transaction('commit');
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -544,7 +559,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -568,6 +583,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$author_name,
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -710,6 +730,20 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$this->db->sql_transaction('commit');
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php
index 4bacf74f93..48445d0794 100644
--- a/phpBB/includes/search/fulltext_sphinx.php
+++ b/phpBB/includes/search/fulltext_sphinx.php
@@ -454,7 +454,7 @@ class phpbb_search_fulltext_sphinx
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts.
if (!strlen($this->search_query) && !sizeof($author_ary))
@@ -609,6 +609,25 @@ class phpbb_search_fulltext_sphinx
}
}
+ $result_count = $result['total_found'];
+
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $this->sphinx->SetLimits((int) $start, (int) $per_page, SPHINX_MAX_MATCHES);
+ $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes);
+
+ // Could be connection to localhost:9312 failed (errno=111,
+ // msg=Connection refused) during rotate, retry if so
+ $retries = SPHINX_CONNECT_RETRIES;
+ while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--)
+ {
+ usleep(SPHINX_CONNECT_WAIT_TIME);
+ $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes);
+ }
+ }
+
$id_ary = array();
if (isset($result['matches']))
{
@@ -629,8 +648,6 @@ class phpbb_search_fulltext_sphinx
return false;
}
- $result_count = $result['total_found'];
-
$id_ary = array_slice($id_ary, 0, (int) $per_page);
return $result_count;
@@ -878,8 +895,8 @@ class phpbb_search_fulltext_sphinx
' . $this->user->lang['MIB'] . '
-
' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
- - ' . (($this->config_generate()) ? '' : $this->config_file_data) . '
+
' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
+ - ' . (($this->config_generate()) ? '' : $this->config_file_data) . '
';
diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php
index ee8a4094c7..6bc71da0c1 100644
--- a/phpBB/includes/session.php
+++ b/phpBB/includes/session.php
@@ -346,7 +346,7 @@ class phpbb_session
$session_id = $request->variable('sid', '');
if (defined('NEED_SID') && (empty($session_id) || $this->session_id !== $session_id))
{
- send_status_line(401, 'Not authorized');
+ send_status_line(401, 'Unauthorized');
redirect(append_sid("{$phpbb_root_path}index.$phpEx"));
}
diff --git a/phpBB/includes/ucp/ucp_activate.php b/phpBB/includes/ucp/ucp_activate.php
index a0d0baf10f..577761dfde 100644
--- a/phpBB/includes/ucp/ucp_activate.php
+++ b/phpBB/includes/ucp/ucp_activate.php
@@ -50,7 +50,7 @@ class ucp_activate
trigger_error('ALREADY_ACTIVATED');
}
- if (($user_row['user_inactive_reason'] == INACTIVE_MANUAL) || $user_row['user_actkey'] != $key)
+ if ($user_row['user_inactive_reason'] == INACTIVE_MANUAL || $user_row['user_actkey'] !== $key)
{
trigger_error('WRONG_ACTIVATION');
}
diff --git a/phpBB/includes/ucp/ucp_groups.php b/phpBB/includes/ucp/ucp_groups.php
index d92aea91f8..b9a06bc3b4 100644
--- a/phpBB/includes/ucp/ucp_groups.php
+++ b/phpBB/includes/ucp/ucp_groups.php
@@ -618,7 +618,7 @@ class ucp_groups
foreach ($test_variables as $test => $type)
{
- if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test]))
+ if (isset($submit_ary[$test]) && ($action == 'add' || $group_row['group_' . $test] != $submit_ary[$test] || isset($group_attributes['group_avatar']) && strpos($test, 'avatar') === 0))
{
settype($submit_ary[$test], $type);
$group_attributes['group_' . $test] = $group_row['group_' . $test] = $submit_ary[$test];
diff --git a/phpBB/includes/ucp/ucp_pm_compose.php b/phpBB/includes/ucp/ucp_pm_compose.php
index 5577e8dab3..c2d12d17c2 100644
--- a/phpBB/includes/ucp/ucp_pm_compose.php
+++ b/phpBB/includes/ucp/ucp_pm_compose.php
@@ -353,7 +353,7 @@ function compose_pm($id, $mode, $action, $user_folders = array())
$message_attachment = 0;
$message_text = $message_subject = '';
- if ($to_user_id && $action == 'post')
+ if ($to_user_id && $to_user_id != ANONYMOUS && $action == 'post')
{
$address_list['u'][$to_user_id] = 'to';
}
diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php
index 8950d677ae..4938ef0f87 100644
--- a/phpBB/install/database_update.php
+++ b/phpBB/install/database_update.php
@@ -1,39 +1,23 @@
purge();
+
+?>
+
+
+
+
+
+
+
+
+
+
+