aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/phpbb/db
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/phpbb/db')
-rw-r--r--phpBB/phpbb/db/driver/driver.php1078
-rw-r--r--phpBB/phpbb/db/driver/driver_interface.php453
-rw-r--r--phpBB/phpbb/db/driver/factory.php443
-rw-r--r--phpBB/phpbb/db/driver/mssql.php476
-rw-r--r--phpBB/phpbb/db/driver/mssql_base.php71
-rw-r--r--phpBB/phpbb/db/driver/mssql_odbc.php377
-rw-r--r--phpBB/phpbb/db/driver/mssqlnative.php442
-rw-r--r--phpBB/phpbb/db/driver/mysql.php485
-rw-r--r--phpBB/phpbb/db/driver/mysql_base.php138
-rw-r--r--phpBB/phpbb/db/driver/mysqli.php475
-rw-r--r--phpBB/phpbb/db/driver/oracle.php807
-rw-r--r--phpBB/phpbb/db/driver/postgres.php490
-rw-r--r--phpBB/phpbb/db/driver/sqlite.php378
-rw-r--r--phpBB/phpbb/db/driver/sqlite3.php405
-rw-r--r--phpBB/phpbb/db/html_migrator_output_handler.php48
-rw-r--r--phpBB/phpbb/db/log_wrapper_migrator_output_handler.php95
-rw-r--r--phpBB/phpbb/db/migration/container_aware_migration.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/.htaccess33
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php70
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php1181
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php101
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php56
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php72
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_13.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_13_pl1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_13_rc1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php119
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php86
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php89
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php129
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php133
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php48
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php330
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php82
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php79
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php162
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php130
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/.htaccess33
-rw-r--r--phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php81
-rw-r--r--phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v310/allow_cdn.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha1.php53
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha2.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha3.php39
-rw-r--r--phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php77
-rw-r--r--phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v310/avatar_types.php64
-rw-r--r--phpBB/phpbb/db/migration/data/v310/avatars.php95
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta1.php42
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta2.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta3.php41
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta4.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v310/board_contact_name.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/boardindex.php29
-rw-r--r--phpBB/phpbb/db/migration/data/v310/bot_update.php150
-rw-r--r--phpBB/phpbb/db/migration/data/v310/captcha_plugins.php48
-rw-r--r--phpBB/phpbb/db/migration/data/v310/config_db_text.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v310/contact_admin_form.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v310/dev.php415
-rw-r--r--phpBB/phpbb/db/migration/data/v310/extensions.php75
-rw-r--r--phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php29
-rw-r--r--phpBB/phpbb/db/migration/data/v310/forgot_password.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/gold.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/jquery_update.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/jquery_update2.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/live_searches_config.php29
-rw-r--r--phpBB/phpbb/db/migration/data/v310/migrations_table.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/mod_rewrite.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php80
-rw-r--r--phpBB/phpbb/db/migration/data/v310/namespaces.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php142
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications.php102
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_cron.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php98
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_use_full_name.php184
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords.php50
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php81
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php49
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_p2.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v310/plupload.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php80
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_aol.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_field_validation_length.php90
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_icq.php54
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_interests.php52
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_location.php52
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php49
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_skype.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_types.php110
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_website.php56
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php50
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc1.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc2.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc3.php40
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc4.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc5.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc6.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v310/remove_acp_styles_cache.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rename_too_long_indexes.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v310/reported_posts_display.php53
-rw-r--r--phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v310/search_type.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/signature_module_auth.php57
-rw-r--r--phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php127
-rw-r--r--phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php66
-rw-r--r--phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php61
-rw-r--r--phpBB/phpbb/db/migration/data/v310/softdelete_p1.php211
-rw-r--r--phpBB/phpbb/db/migration/data/v310/softdelete_p2.php78
-rw-r--r--phpBB/phpbb/db/migration/data/v310/style_update_p1.php191
-rw-r--r--phpBB/phpbb/db/migration/data/v310/style_update_p2.php151
-rw-r--r--phpBB/phpbb/db/migration/data/v310/teampage.php110
-rw-r--r--phpBB/phpbb/db/migration/data/v310/timezone.php194
-rw-r--r--phpBB/phpbb/db/migration/data/v310/timezone_p2.php49
-rw-r--r--phpBB/phpbb/db/migration/data/v310/topic_sort_username.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/.htaccess33
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/add_log_time_index.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/increase_size_of_dateformat.php35
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/increase_size_of_emotion.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/m_pm_report.php64
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/m_softdelete_global.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/plupload_last_gc_dynamic.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/profilefield_remove_underscore_from_alpha.php47
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/profilefield_yahoo_update_url.php38
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/remove_duplicate_migrations.php77
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/style_update.php136
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php70
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/update_hashes.php33
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v311.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v3110.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v3110rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v3111.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v3111rc1.php43
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v312.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v312rc1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313rc1.php40
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313rc2.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314rc2.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v316.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v316rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317pl1.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317rc1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v318.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v318rc1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v319.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v319rc1.php36
-rw-r--r--phpBB/phpbb/db/migration/exception.php75
-rw-r--r--phpBB/phpbb/db/migration/helper.php116
-rw-r--r--phpBB/phpbb/db/migration/migration.php186
-rw-r--r--phpBB/phpbb/db/migration/profilefield_base_migration.php247
-rw-r--r--phpBB/phpbb/db/migration/schema_generator.php235
-rw-r--r--phpBB/phpbb/db/migration/tool/config.php165
-rw-r--r--phpBB/phpbb/db/migration/tool/config_text.php130
-rw-r--r--phpBB/phpbb/db/migration/tool/module.php563
-rw-r--r--phpBB/phpbb/db/migration/tool/permission.php652
-rw-r--r--phpBB/phpbb/db/migration/tool/tool_interface.php37
-rw-r--r--phpBB/phpbb/db/migrator.php924
-rw-r--r--phpBB/phpbb/db/migrator_output_handler_interface.php31
-rw-r--r--phpBB/phpbb/db/null_migrator_output_handler.php24
-rw-r--r--phpBB/phpbb/db/sql_insert_buffer.php146
-rw-r--r--phpBB/phpbb/db/tools.php2840
216 files changed, 25071 insertions, 0 deletions
diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php
new file mode 100644
index 0000000000..01dd66cd6e
--- /dev/null
+++ b/phpBB/phpbb/db/driver/driver.php
@@ -0,0 +1,1078 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* Database Abstraction Layer
+*/
+abstract class driver implements driver_interface
+{
+ var $db_connect_id;
+ var $query_result;
+ var $return_on_error = false;
+ var $transaction = false;
+ var $sql_time = 0;
+ var $num_queries = array();
+ var $open_queries = array();
+
+ var $curtime = 0;
+ var $query_hold = '';
+ var $html_hold = '';
+ var $sql_report = '';
+
+ var $persistency = false;
+ var $user = '';
+ var $server = '';
+ var $dbname = '';
+
+ // Set to true if error triggered
+ var $sql_error_triggered = false;
+
+ // Holding the last sql query on sql error
+ var $sql_error_sql = '';
+ // Holding the error information - only populated if sql_error_triggered is set
+ var $sql_error_returned = array();
+
+ // Holding transaction count
+ var $transactions = 0;
+
+ // Supports multi inserts?
+ var $multi_insert = false;
+
+ /**
+ * Current sql layer
+ */
+ var $sql_layer = '';
+
+ /**
+ * Wildcards for matching any (%) or exactly one (_) character within LIKE expressions
+ */
+ var $any_char;
+ var $one_char;
+
+ /**
+ * Exact version of the DBAL, directly queried
+ */
+ var $sql_server_version = false;
+
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $this->num_queries = array(
+ 'cached' => 0,
+ 'normal' => 0,
+ 'total' => 0,
+ );
+
+ // Fill default sql layer based on the class being called.
+ // This can be changed by the specified layer itself later if needed.
+ $this->sql_layer = substr(get_class($this), strlen('phpbb\db\driver\\'));
+
+ // Do not change this please! This variable is used to easy the use of it - and is hardcoded.
+ $this->any_char = chr(0) . '%';
+ $this->one_char = chr(0) . '_';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_layer()
+ {
+ return $this->sql_layer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_name()
+ {
+ return $this->dbname;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_any_char()
+ {
+ return $this->any_char;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_one_char()
+ {
+ return $this->one_char;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_connect_id()
+ {
+ return $this->db_connect_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_triggered()
+ {
+ return $this->sql_error_triggered;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_sql()
+ {
+ return $this->sql_error_sql;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_transaction()
+ {
+ return $this->transaction;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_time()
+ {
+ return $this->sql_time;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_returned()
+ {
+ return $this->sql_error_returned;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_multi_insert()
+ {
+ return $this->multi_insert;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_multi_insert($multi_insert)
+ {
+ $this->multi_insert = $multi_insert;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_return_on_error($fail = false)
+ {
+ $this->sql_error_triggered = false;
+ $this->sql_error_sql = '';
+
+ $this->return_on_error = $fail;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_num_queries($cached = false)
+ {
+ return ($cached) ? $this->num_queries['cached'] : $this->num_queries['normal'];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_add_num_queries($cached = false)
+ {
+ $this->num_queries['cached'] += ($cached !== false) ? 1 : 0;
+ $this->num_queries['normal'] += ($cached !== false) ? 0 : 1;
+ $this->num_queries['total'] += 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_close()
+ {
+ if (!$this->db_connect_id)
+ {
+ return false;
+ }
+
+ if ($this->transaction)
+ {
+ do
+ {
+ $this->sql_transaction('commit');
+ }
+ while ($this->transaction);
+ }
+
+ foreach ($this->open_queries as $query_id)
+ {
+ $this->sql_freeresult($query_id);
+ }
+
+ // Connection closed correctly. Set db_connect_id to false to prevent errors
+ if ($result = $this->_sql_close())
+ {
+ $this->db_connect_id = false;
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ if (empty($query))
+ {
+ return false;
+ }
+
+ // Never use a negative total or offset
+ $total = ($total < 0) ? 0 : $total;
+ $offset = ($offset < 0) ? 0 : $offset;
+
+ return $this->_sql_query_limit($query, $total, $offset, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrowset($query_id = false)
+ {
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id !== false)
+ {
+ $result = array();
+ while ($row = $this->sql_fetchrow($query_id))
+ {
+ $result[] = $row;
+ }
+
+ return $result;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ $this->sql_freeresult($query_id);
+ $query_id = $this->sql_query($this->last_query_text);
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
+ for ($i = 0; $i < $rownum; $i++)
+ {
+ if (!$this->sql_fetchrow($query_id))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchfield($field, $rownum = false, $query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($query_id !== false)
+ {
+ if ($rownum !== false)
+ {
+ $this->sql_rowseek($rownum, $query_id);
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchfield($query_id, $field);
+ }
+
+ $row = $this->sql_fetchrow($query_id);
+ return (isset($row[$field])) ? $row[$field] : false;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_like_expression($expression)
+ {
+ $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
+
+ return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_not_like_expression($expression)
+ {
+ $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
+
+ return $this->_sql_not_like_expression('NOT LIKE \'' . $this->sql_escape($expression) . '\'');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_case($condition, $action_true, $action_false = false)
+ {
+ $sql_case = 'CASE WHEN ' . $condition;
+ $sql_case .= ' THEN ' . $action_true;
+ $sql_case .= ($action_false !== false) ? ' ELSE ' . $action_false : '';
+ $sql_case .= ' END';
+ return $sql_case;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' || ' . $expr2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_buffer_nested_transactions()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ // If we are within a transaction we will not open another one, but enclose the current one to not loose data (preventing auto commit)
+ if ($this->transaction)
+ {
+ $this->transactions++;
+ return true;
+ }
+
+ $result = $this->_sql_transaction('begin');
+
+ if (!$result)
+ {
+ $this->sql_error();
+ }
+
+ $this->transaction = true;
+ break;
+
+ case 'commit':
+ // If there was a previously opened transaction we do not commit yet...
+ // but count back the number of inner transactions
+ if ($this->transaction && $this->transactions)
+ {
+ $this->transactions--;
+ return true;
+ }
+
+ // Check if there is a transaction (no transaction can happen if
+ // there was an error, with a combined rollback and error returning enabled)
+ // This implies we have transaction always set for autocommit db's
+ if (!$this->transaction)
+ {
+ return false;
+ }
+
+ $result = $this->_sql_transaction('commit');
+
+ if (!$result)
+ {
+ $this->sql_error();
+ }
+
+ $this->transaction = false;
+ $this->transactions = 0;
+ break;
+
+ case 'rollback':
+ $result = $this->_sql_transaction('rollback');
+ $this->transaction = false;
+ $this->transactions = 0;
+ break;
+
+ default:
+ $result = $this->_sql_transaction($status);
+ break;
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_build_array($query, $assoc_ary = false)
+ {
+ if (!is_array($assoc_ary))
+ {
+ return false;
+ }
+
+ $fields = $values = array();
+
+ if ($query == 'INSERT' || $query == 'INSERT_SELECT')
+ {
+ foreach ($assoc_ary as $key => $var)
+ {
+ $fields[] = $key;
+
+ if (is_array($var) && is_string($var[0]))
+ {
+ // This is used for INSERT_SELECT(s)
+ $values[] = $var[0];
+ }
+ else
+ {
+ $values[] = $this->_sql_validate_value($var);
+ }
+ }
+
+ $query = ($query == 'INSERT') ? ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')' : ' (' . implode(', ', $fields) . ') SELECT ' . implode(', ', $values) . ' ';
+ }
+ else if ($query == 'MULTI_INSERT')
+ {
+ trigger_error('The MULTI_INSERT query value is no longer supported. Please use sql_multi_insert() instead.', E_USER_ERROR);
+ }
+ else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
+ {
+ $values = array();
+ foreach ($assoc_ary as $key => $var)
+ {
+ $values[] = "$key = " . $this->_sql_validate_value($var);
+ }
+ $query = implode(($query == 'UPDATE') ? ', ' : ' AND ', $values);
+ }
+
+ return $query;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
+ {
+ if (!sizeof($array))
+ {
+ if (!$allow_empty_set)
+ {
+ // Print the backtrace to help identifying the location of the problematic code
+ $this->sql_error('No values specified for SQL IN comparison');
+ }
+ else
+ {
+ // NOT IN () actually means everything so use a tautology
+ if ($negate)
+ {
+ return '1=1';
+ }
+ // IN () actually means nothing so use a contradiction
+ else
+ {
+ return '1=0';
+ }
+ }
+ }
+
+ if (!is_array($array))
+ {
+ $array = array($array);
+ }
+
+ if (sizeof($array) == 1)
+ {
+ @reset($array);
+ $var = current($array);
+
+ return $field . ($negate ? ' <> ' : ' = ') . $this->_sql_validate_value($var);
+ }
+ else
+ {
+ return $field . ($negate ? ' NOT IN ' : ' IN ') . '(' . implode(', ', array_map(array($this, '_sql_validate_value'), $array)) . ')';
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_bit_and($column_name, $bit, $compare = '')
+ {
+ if (method_exists($this, '_sql_bit_and'))
+ {
+ return $this->_sql_bit_and($column_name, $bit, $compare);
+ }
+
+ return $column_name . ' & ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_bit_or($column_name, $bit, $compare = '')
+ {
+ if (method_exists($this, '_sql_bit_or'))
+ {
+ return $this->_sql_bit_or($column_name, $bit, $compare);
+ }
+
+ return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function cast_expr_to_bigint($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function cast_expr_to_string($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_lower_text($column_name)
+ {
+ return "LOWER($column_name)";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_multi_insert($table, $sql_ary)
+ {
+ if (!sizeof($sql_ary))
+ {
+ return false;
+ }
+
+ if ($this->multi_insert)
+ {
+ $ary = array();
+ foreach ($sql_ary as $id => $_sql_ary)
+ {
+ // If by accident the sql array is only one-dimensional we build a normal insert statement
+ if (!is_array($_sql_ary))
+ {
+ return $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $sql_ary));
+ }
+
+ $values = array();
+ foreach ($_sql_ary as $key => $var)
+ {
+ $values[] = $this->_sql_validate_value($var);
+ }
+ $ary[] = '(' . implode(', ', $values) . ')';
+ }
+
+ return $this->sql_query('INSERT INTO ' . $table . ' ' . ' (' . implode(', ', array_keys($sql_ary[0])) . ') VALUES ' . implode(', ', $ary));
+ }
+ else
+ {
+ foreach ($sql_ary as $ary)
+ {
+ if (!is_array($ary))
+ {
+ return false;
+ }
+
+ $result = $this->sql_query('INSERT INTO ' . $table . ' ' . $this->sql_build_array('INSERT', $ary));
+
+ if (!$result)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Function for validating values
+ * @access private
+ */
+ function _sql_validate_value($var)
+ {
+ if (is_null($var))
+ {
+ return 'NULL';
+ }
+ else if (is_string($var))
+ {
+ return "'" . $this->sql_escape($var) . "'";
+ }
+ else
+ {
+ return (is_bool($var)) ? intval($var) : $var;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_build_query($query, $array)
+ {
+ $sql = '';
+ switch ($query)
+ {
+ case 'SELECT':
+ case 'SELECT_DISTINCT';
+
+ $sql = str_replace('_', ' ', $query) . ' ' . $array['SELECT'] . ' FROM ';
+
+ // Build table array. We also build an alias array for later checks.
+ $table_array = $aliases = array();
+ $used_multi_alias = false;
+
+ foreach ($array['FROM'] as $table_name => $alias)
+ {
+ if (is_array($alias))
+ {
+ $used_multi_alias = true;
+
+ foreach ($alias as $multi_alias)
+ {
+ $table_array[] = $table_name . ' ' . $multi_alias;
+ $aliases[] = $multi_alias;
+ }
+ }
+ else
+ {
+ $table_array[] = $table_name . ' ' . $alias;
+ $aliases[] = $alias;
+ }
+ }
+
+ // We run the following code to determine if we need to re-order the table array. ;)
+ // The reason for this is that for multi-aliased tables (two equal tables) in the FROM statement the last table need to match the first comparison.
+ // DBMS who rely on this: Oracle, PostgreSQL and MSSQL. For all other DBMS it makes absolutely no difference in which order the table is.
+ if (!empty($array['LEFT_JOIN']) && sizeof($array['FROM']) > 1 && $used_multi_alias !== false)
+ {
+ // Take first LEFT JOIN
+ $join = current($array['LEFT_JOIN']);
+
+ // Determine the table used there (even if there are more than one used, we only want to have one
+ preg_match('/(' . implode('|', $aliases) . ')\.[^\s]+/U', str_replace(array('(', ')', 'AND', 'OR', ' '), '', $join['ON']), $matches);
+
+ // If there is a first join match, we need to make sure the table order is correct
+ if (!empty($matches[1]))
+ {
+ $first_join_match = trim($matches[1]);
+ $table_array = $last = array();
+
+ foreach ($array['FROM'] as $table_name => $alias)
+ {
+ if (is_array($alias))
+ {
+ foreach ($alias as $multi_alias)
+ {
+ ($multi_alias === $first_join_match) ? $last[] = $table_name . ' ' . $multi_alias : $table_array[] = $table_name . ' ' . $multi_alias;
+ }
+ }
+ else
+ {
+ ($alias === $first_join_match) ? $last[] = $table_name . ' ' . $alias : $table_array[] = $table_name . ' ' . $alias;
+ }
+ }
+
+ $table_array = array_merge($table_array, $last);
+ }
+ }
+
+ $sql .= $this->_sql_custom_build('FROM', implode(' CROSS JOIN ', $table_array));
+
+ if (!empty($array['LEFT_JOIN']))
+ {
+ foreach ($array['LEFT_JOIN'] as $join)
+ {
+ $sql .= ' LEFT JOIN ' . key($join['FROM']) . ' ' . current($join['FROM']) . ' ON (' . $join['ON'] . ')';
+ }
+ }
+
+ if (!empty($array['WHERE']))
+ {
+ $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']);
+ }
+
+ if (!empty($array['GROUP_BY']))
+ {
+ $sql .= ' GROUP BY ' . $array['GROUP_BY'];
+ }
+
+ if (!empty($array['ORDER_BY']))
+ {
+ $sql .= ' ORDER BY ' . $array['ORDER_BY'];
+ }
+
+ break;
+ }
+
+ return $sql;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_error($sql = '')
+ {
+ global $auth, $user, $config;
+
+ // Set var to retrieve errored status
+ $this->sql_error_triggered = true;
+ $this->sql_error_sql = $sql;
+
+ $this->sql_error_returned = $this->_sql_error();
+
+ if (!$this->return_on_error)
+ {
+ $message = 'SQL ERROR [ ' . $this->sql_layer . ' ]<br /><br />' . $this->sql_error_returned['message'] . ' [' . $this->sql_error_returned['code'] . ']';
+
+ // Show complete SQL error and path to administrators only
+ // Additionally show complete error on installation or if extended debug mode is enabled
+ // The DEBUG constant is for development only!
+ if ((isset($auth) && $auth->acl_get('a_')) || defined('IN_INSTALL') || defined('DEBUG'))
+ {
+ $message .= ($sql) ? '<br /><br />SQL<br /><br />' . htmlspecialchars($sql) : '';
+ }
+ else
+ {
+ // If error occurs in initiating the session we need to use a pre-defined language string
+ // This could happen if the connection could not be established for example (then we are not able to grab the default language)
+ if (!isset($user->lang['SQL_ERROR_OCCURRED']))
+ {
+ $message .= '<br /><br />An sql error occurred while fetching this page. Please contact an administrator if this problem persists.';
+ }
+ else
+ {
+ if (!empty($config['board_contact']))
+ {
+ $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
+ }
+ else
+ {
+ $message .= '<br /><br />' . sprintf($user->lang['SQL_ERROR_OCCURRED'], '', '');
+ }
+ }
+ }
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ if (strlen($message) > 1024)
+ {
+ // We need to define $msg_long_text here to circumvent text stripping.
+ global $msg_long_text;
+ $msg_long_text = $message;
+
+ trigger_error(false, E_USER_ERROR);
+ }
+
+ trigger_error($message, E_USER_ERROR);
+ }
+
+ if ($this->transaction)
+ {
+ $this->sql_transaction('rollback');
+ }
+
+ return $this->sql_error_returned;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_report($mode, $query = '')
+ {
+ global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper, $user;
+ global $request;
+
+ if (is_object($request) && !$request->variable('explain', false))
+ {
+ return false;
+ }
+
+ if (!$query && $this->query_hold != '')
+ {
+ $query = $this->query_hold;
+ }
+
+ switch ($mode)
+ {
+ case 'display':
+ if (!empty($cache))
+ {
+ $cache->unload();
+ }
+ $this->sql_close();
+
+ $mtime = explode(' ', microtime());
+ $totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ echo '<!DOCTYPE html>
+ <html dir="ltr">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>SQL Report</title>
+ <link href="' . htmlspecialchars($phpbb_path_helper->update_web_root_path($phpbb_root_path) . $phpbb_path_helper->get_adm_relative_path()) . 'style/admin.css" rel="stylesheet" type="text/css" media="screen" />
+ </head>
+ <body id="errorpage">
+ <div id="wrap">
+ <div id="page-header">
+ <a href="' . build_url('explain') . '">Return to previous page</a>
+ </div>
+ <div id="page-body">
+ <div id="acp">
+ <div class="panel">
+ <span class="corners-top"><span></span></span>
+ <div id="content">
+ <h1>SQL Report</h1>
+ <br />
+ <p><b>Page generated in ' . round($totaltime, 4) . " seconds with {$this->num_queries['normal']} queries" . (($this->num_queries['cached']) ? " + {$this->num_queries['cached']} " . (($this->num_queries['cached'] == 1) ? 'query' : 'queries') . ' returning data from cache' : '') . '</b></p>
+
+ <p>Time spent on ' . $this->sql_layer . ' queries: <b>' . round($this->sql_time, 5) . 's</b> | Time spent on PHP: <b>' . round($totaltime - $this->sql_time, 5) . 's</b></p>
+
+ <br /><br />
+ ' . $this->sql_report . '
+ </div>
+ <span class="corners-bottom"><span></span></span>
+ </div>
+ </div>
+ </div>
+ <div id="page-footer">
+ Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
+ </div>
+ </div>
+ </body>
+ </html>';
+
+ exit_handler();
+
+ break;
+
+ case 'stop':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $this->sql_report .= '
+
+ <table cellspacing="1">
+ <thead>
+ <tr>
+ <th>Query #' . $this->num_queries['total'] . '</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td>
+ </tr>
+ </tbody>
+ </table>
+
+ ' . $this->html_hold . '
+
+ <p style="text-align: center;">
+ ';
+
+ if ($this->query_result)
+ {
+ if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
+ {
+ $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
+ }
+ $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>';
+ }
+ else
+ {
+ $error = $this->sql_error();
+ $this->sql_report .= '<b style="color: red">FAILED</b> - ' . $this->sql_layer . ' Error ' . $error['code'] . ': ' . htmlspecialchars($error['message']);
+ }
+
+ $this->sql_report .= '</p><br /><br />';
+
+ $this->sql_time += $endtime - $this->curtime;
+ break;
+
+ case 'start':
+ $this->query_hold = $query;
+ $this->html_hold = '';
+
+ $this->_sql_report($mode, $query);
+
+ $this->curtime = explode(' ', microtime());
+ $this->curtime = $this->curtime[0] + $this->curtime[1];
+
+ break;
+
+ case 'add_select_row':
+
+ $html_table = func_get_arg(2);
+ $row = func_get_arg(3);
+
+ if (!$html_table && sizeof($row))
+ {
+ $html_table = true;
+ $this->html_hold .= '<table cellspacing="1"><tr>';
+
+ foreach (array_keys($row) as $val)
+ {
+ $this->html_hold .= '<th>' . (($val) ? ucwords(str_replace('_', ' ', $val)) : '&nbsp;') . '</th>';
+ }
+ $this->html_hold .= '</tr>';
+ }
+ $this->html_hold .= '<tr>';
+
+ $class = 'row1';
+ foreach (array_values($row) as $val)
+ {
+ $class = ($class == 'row1') ? 'row2' : 'row1';
+ $this->html_hold .= '<td class="' . $class . '">' . (($val) ? $val : '&nbsp;') . '</td>';
+ }
+ $this->html_hold .= '</tr>';
+
+ return $html_table;
+
+ break;
+
+ case 'fromcache':
+
+ $this->_sql_report($mode, $query);
+
+ break;
+
+ case 'record_fromcache':
+
+ $endtime = func_get_arg(2);
+ $splittime = func_get_arg(3);
+
+ $time_cache = $endtime - $this->curtime;
+ $time_db = $splittime - $endtime;
+ $color = ($time_db > $time_cache) ? 'green' : 'red';
+
+ $this->sql_report .= '<table cellspacing="1"><thead><tr><th>Query results obtained from the cache</th></tr></thead><tbody><tr>';
+ $this->sql_report .= '<td class="row3"><textarea style="font-family:\'Courier New\',monospace;width:99%" rows="5" cols="10">' . preg_replace('/\t(AND|OR)(\W)/', "\$1\$2", htmlspecialchars(preg_replace('/[\s]*[\n\r\t]+[\n\r\s\t]*/', "\n", $query))) . '</textarea></td></tr></tbody></table>';
+ $this->sql_report .= '<p style="text-align: center;">';
+ $this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed [cache]: <b style="color: ' . $color . '">' . sprintf('%.5f', ($time_cache)) . 's</b> | Elapsed [db]: <b>' . sprintf('%.5f', $time_db) . 's</b></p><br /><br />';
+
+ // Pad the start time to not interfere with page timing
+ $starttime += $time_db;
+
+ break;
+
+ default:
+
+ $this->_sql_report($mode, $query);
+
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function get_estimated_row_count($table_name)
+ {
+ return $this->get_row_count($table_name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function get_row_count($table_name)
+ {
+ $sql = 'SELECT COUNT(*) AS rows_total
+ FROM ' . $this->sql_escape($table_name);
+ $result = $this->sql_query($sql);
+ $rows_total = $this->sql_fetchfield('rows_total');
+ $this->sql_freeresult($result);
+
+ return $rows_total;
+ }
+}
diff --git a/phpBB/phpbb/db/driver/driver_interface.php b/phpBB/phpbb/db/driver/driver_interface.php
new file mode 100644
index 0000000000..8b487c5d42
--- /dev/null
+++ b/phpBB/phpbb/db/driver/driver_interface.php
@@ -0,0 +1,453 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+interface driver_interface
+{
+ /**
+ * Gets the name of the sql layer.
+ *
+ * @return string
+ */
+ public function get_sql_layer();
+
+ /**
+ * Gets the name of the database.
+ *
+ * @return string
+ */
+ public function get_db_name();
+
+ /**
+ * Wildcards for matching any (%) character within LIKE expressions
+ *
+ * @return string
+ */
+ public function get_any_char();
+
+ /**
+ * Wildcards for matching exactly one (_) character within LIKE expressions
+ *
+ * @return string
+ */
+ public function get_one_char();
+
+ /**
+ * Gets the time spent into the queries
+ *
+ * @return int
+ */
+ public function get_sql_time();
+
+ /**
+ * Gets the connect ID.
+ *
+ * @return mixed
+ */
+ public function get_db_connect_id();
+
+ /**
+ * Indicates if an error was triggered.
+ *
+ * @return bool
+ */
+ public function get_sql_error_triggered();
+
+ /**
+ * Gets the last faulty query
+ *
+ * @return string
+ */
+ public function get_sql_error_sql();
+
+ /**
+ * Indicates if we are in a transaction.
+ *
+ * @return bool
+ */
+ public function get_transaction();
+
+ /**
+ * Gets the returned error.
+ *
+ * @return array
+ */
+ public function get_sql_error_returned();
+
+ /**
+ * Indicates if multiple insertion can be used
+ *
+ * @return bool
+ */
+ public function get_multi_insert();
+
+ /**
+ * Set if multiple insertion can be used
+ *
+ * @param bool $multi_insert
+ */
+ public function set_multi_insert($multi_insert);
+
+ /**
+ * Gets the exact number of rows in a specified table.
+ *
+ * @param string $table_name Table name
+ * @return string Exact number of rows in $table_name.
+ */
+ public function get_row_count($table_name);
+
+ /**
+ * Gets the estimated number of rows in a specified table.
+ *
+ * @param string $table_name Table name
+ * @return string Number of rows in $table_name.
+ * Prefixed with ~ if estimated (otherwise exact).
+ */
+ public function get_estimated_row_count($table_name);
+
+ /**
+ * Run LOWER() on DB column of type text (i.e. neither varchar nor char).
+ *
+ * @param string $column_name The column name to use
+ * @return string A SQL statement like "LOWER($column_name)"
+ */
+ public function sql_lower_text($column_name);
+
+ /**
+ * Display sql error page
+ *
+ * @param string $sql The SQL query causing the error
+ * @return mixed Returns the full error message, if $this->return_on_error
+ * is set, null otherwise
+ */
+ public function sql_error($sql = '');
+
+ /**
+ * Returns whether results of a query need to be buffered to run a
+ * transaction while iterating over them.
+ *
+ * @return bool Whether buffering is required.
+ */
+ public function sql_buffer_nested_transactions();
+
+ /**
+ * Run binary OR operator on DB column.
+ *
+ * @param string $column_name The column name to use
+ * @param int $bit The value to use for the OR operator,
+ * will be converted to (1 << $bit). Is used by options,
+ * using the number schema... 0, 1, 2...29
+ * @param string $compare Any custom SQL code after the check (e.g. "= 0")
+ * @return string A SQL statement like "$column | (1 << $bit) {$compare}"
+ */
+ public function sql_bit_or($column_name, $bit, $compare = '');
+
+ /**
+ * Version information about used database
+ *
+ * @param bool $raw Only return the fetched sql_server_version
+ * @param bool $use_cache Is it safe to retrieve the value from the cache
+ * @return string sql server version
+ */
+ public function sql_server_info($raw = false, $use_cache = true);
+
+ /**
+ * Return on error or display error message
+ *
+ * @param bool $fail Should we return on errors, or stop
+ * @return null
+ */
+ public function sql_return_on_error($fail = false);
+
+ /**
+ * Build sql statement from an array
+ *
+ * @param string $query Should be on of the following strings:
+ * INSERT, INSERT_SELECT, UPDATE, SELECT, DELETE
+ * @param array $assoc_ary Array with "column => value" pairs
+ * @return string A SQL statement like "c1 = 'a' AND c2 = 'b'"
+ */
+ public function sql_build_array($query, $assoc_ary = array());
+
+ /**
+ * Fetch all rows
+ *
+ * @param mixed $query_id Already executed query to get the rows from,
+ * if false, the last query will be used.
+ * @return mixed Nested array if the query had rows, false otherwise
+ */
+ public function sql_fetchrowset($query_id = false);
+
+ /**
+ * SQL Transaction
+ *
+ * @param string $status Should be one of the following strings:
+ * begin, commit, rollback
+ * @return mixed Buffered, seekable result handle, false on error
+ */
+ public function sql_transaction($status = 'begin');
+
+ /**
+ * Build a concatenated expression
+ *
+ * @param string $expr1 Base SQL expression where we append the second one
+ * @param string $expr2 SQL expression that is appended to the first expression
+ * @return string Concatenated string
+ */
+ public function sql_concatenate($expr1, $expr2);
+
+ /**
+ * Build a case expression
+ *
+ * Note: The two statements action_true and action_false must have the same
+ * data type (int, vchar, ...) in the database!
+ *
+ * @param string $condition The condition which must be true,
+ * to use action_true rather then action_else
+ * @param string $action_true SQL expression that is used, if the condition is true
+ * @param mixed $action_false SQL expression that is used, if the condition is false
+ * @return string CASE expression including the condition and statements
+ */
+ public function sql_case($condition, $action_true, $action_false = false);
+
+ /**
+ * Build sql statement from array for select and select distinct statements
+ *
+ * Possible query values: SELECT, SELECT_DISTINCT
+ *
+ * @param string $query Should be one of: SELECT, SELECT_DISTINCT
+ * @param array $array Array with the query data:
+ * SELECT A comma imploded list of columns to select
+ * FROM Array with "table => alias" pairs,
+ * (alias can also be an array)
+ * Optional: LEFT_JOIN Array of join entries:
+ * FROM Table that should be joined
+ * ON Condition for the join
+ * Optional: WHERE Where SQL statement
+ * Optional: GROUP_BY Group by SQL statement
+ * Optional: ORDER_BY Order by SQL statement
+ * @return string A SQL statement ready for execution
+ */
+ public function sql_build_query($query, $array);
+
+ /**
+ * Fetch field
+ * if rownum is false, the current row is used, else it is pointing to the row (zero-based)
+ *
+ * @param string $field Name of the column
+ * @param mixed $rownum Row number, if false the current row will be used
+ * and the row curser will point to the next row
+ * Note: $rownum is 0 based
+ * @param mixed $query_id Already executed query to get the rows from,
+ * if false, the last query will be used.
+ * @return mixed String value of the field in the selected row,
+ * false, if the row does not exist
+ */
+ public function sql_fetchfield($field, $rownum = false, $query_id = false);
+
+ /**
+ * Fetch current row
+ *
+ * @param mixed $query_id Already executed query to get the rows from,
+ * if false, the last query will be used.
+ * @return mixed Array with the current row,
+ * false, if the row does not exist
+ */
+ public function sql_fetchrow($query_id = false);
+
+ /**
+ * Returns SQL string to cast a string expression to an int.
+ *
+ * @param string $expression An expression evaluating to string
+ * @return string Expression returning an int
+ */
+ public function cast_expr_to_bigint($expression);
+
+ /**
+ * Get last inserted id after insert statement
+ *
+ * @return string Autoincrement value of the last inserted row
+ */
+ public function sql_nextid();
+
+ /**
+ * Add to query count
+ *
+ * @param bool $cached Is this query cached?
+ * @return null
+ */
+ public function sql_add_num_queries($cached = false);
+
+ /**
+ * Build LIMIT query
+ *
+ * @param string $query The SQL query to execute
+ * @param int $total The number of rows to select
+ * @param int $offset
+ * @param int $cache_ttl Either 0 to avoid caching or
+ * the time in seconds which the result shall be kept in cache
+ * @return mixed Buffered, seekable result handle, false on error
+ */
+ public function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0);
+
+ /**
+ * Base query method
+ *
+ * @param string $query The SQL query to execute
+ * @param int $cache_ttl Either 0 to avoid caching or
+ * the time in seconds which the result shall be kept in cache
+ * @return mixed Buffered, seekable result handle, false on error
+ */
+ public function sql_query($query = '', $cache_ttl = 0);
+
+ /**
+ * Returns SQL string to cast an integer expression to a string.
+ *
+ * @param string $expression An expression evaluating to int
+ * @return string Expression returning a string
+ */
+ public function cast_expr_to_string($expression);
+
+ /**
+ * Connect to server
+ *
+ * @param string $sqlserver Address of the database server
+ * @param string $sqluser User name of the SQL user
+ * @param string $sqlpassword Password of the SQL user
+ * @param string $database Name of the database
+ * @param mixed $port Port of the database server
+ * @param bool $persistency
+ * @param bool $new_link Should a new connection be established
+ * @return mixed Connection ID on success, string error message otherwise
+ */
+ public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false);
+
+ /**
+ * Run binary AND operator on DB column.
+ * Results in sql statement: "{$column_name} & (1 << {$bit}) {$compare}"
+ *
+ * @param string $column_name The column name to use
+ * @param int $bit The value to use for the AND operator,
+ * will be converted to (1 << $bit). Is used by
+ * options, using the number schema: 0, 1, 2...29
+ * @param string $compare Any custom SQL code after the check (for example "= 0")
+ * @return string A SQL statement like: "{$column} & (1 << {$bit}) {$compare}"
+ */
+ public function sql_bit_and($column_name, $bit, $compare = '');
+
+ /**
+ * Free sql result
+ *
+ * @param mixed $query_id Already executed query result,
+ * if false, the last query will be used.
+ * @return null
+ */
+ public function sql_freeresult($query_id = false);
+
+ /**
+ * Return number of sql queries and cached sql queries used
+ *
+ * @param bool $cached Should we return the number of cached or normal queries?
+ * @return int Number of queries that have been executed
+ */
+ public function sql_num_queries($cached = false);
+
+ /**
+ * Run more than one insert statement.
+ *
+ * @param string $table Table name to run the statements on
+ * @param array $sql_ary Multi-dimensional array holding the statement data
+ * @return bool false if no statements were executed.
+ */
+ public function sql_multi_insert($table, $sql_ary);
+
+ /**
+ * Return number of affected rows
+ *
+ * @return mixed Number of the affected rows by the last query
+ * false if no query has been run before
+ */
+ public function sql_affectedrows();
+
+ /**
+ * DBAL garbage collection, close SQL connection
+ *
+ * @return mixed False if no connection was opened before,
+ * Server response otherwise
+ */
+ public function sql_close();
+
+ /**
+ * Seek to given row number
+ *
+ * @param mixed $rownum Row number the curser should point to
+ * Note: $rownum is 0 based
+ * @param mixed $query_id ID of the query to set the row cursor on
+ * if false, the last query will be used.
+ * $query_id will then be set correctly
+ * @return bool False if something went wrong
+ */
+ public function sql_rowseek($rownum, &$query_id);
+
+ /**
+ * Escape string used in sql query
+ *
+ * @param string $msg String to be escaped
+ * @return string Escaped version of $msg
+ */
+ public function sql_escape($msg);
+
+ /**
+ * Correctly adjust LIKE expression for special characters
+ * Some DBMS are handling them in a different way
+ *
+ * @param string $expression The expression to use. Every wildcard is
+ * escaped, except $this->any_char and $this->one_char
+ * @return string A SQL statement like: "LIKE 'bertie_%'"
+ */
+ public function sql_like_expression($expression);
+
+ /**
+ * Correctly adjust NOT LIKE expression for special characters
+ * Some DBMS are handling them in a different way
+ *
+ * @param string $expression The expression to use. Every wildcard is
+ * escaped, except $this->any_char and $this->one_char
+ * @return string A SQL statement like: "NOT LIKE 'bertie_%'"
+ */
+ public function sql_not_like_expression($expression);
+
+ /**
+ * Explain queries
+ *
+ * @param string $mode Available modes: display, start, stop,
+ * add_select_row, fromcache, record_fromcache
+ * @param string $query The Query that should be explained
+ * @return mixed Either a full HTML page, boolean or null
+ */
+ public function sql_report($mode, $query = '');
+
+ /**
+ * Build IN or NOT IN sql comparison string, uses <> or = on single element
+ * arrays to improve comparison speed
+ *
+ * @param string $field Name of the sql column that shall be compared
+ * @param array $array Array of values that are (not) allowed
+ * @param bool $negate true for NOT IN (), false for IN ()
+ * @param bool $allow_empty_set If true, allow $array to be empty,
+ * this function will return 1=1 or 1=0 then.
+ * @return string A SQL statement like: "IN (1, 2, 3, 4)" or "= 1"
+ */
+ public function sql_in_set($field, $array, $negate = false, $allow_empty_set = false);
+}
diff --git a/phpBB/phpbb/db/driver/factory.php b/phpBB/phpbb/db/driver/factory.php
new file mode 100644
index 0000000000..fb3a826254
--- /dev/null
+++ b/phpBB/phpbb/db/driver/factory.php
@@ -0,0 +1,443 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+use \Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+* Database Abstraction Layer
+*/
+class factory implements driver_interface
+{
+ /**
+ * @var driver_interface
+ */
+ protected $driver = null;
+
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * Constructor.
+ *
+ * @param ContainerInterface $container A ContainerInterface instance
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ /**
+ * Return the current driver (and retrieved it from the container if necessary)
+ *
+ * @return driver_interface
+ */
+ protected function get_driver()
+ {
+ if ($this->driver === null)
+ {
+ $this->driver = $this->container->get('dbal.conn.driver');
+ }
+
+ return $this->driver;
+ }
+
+ /**
+ * Set the current driver
+ *
+ * @param driver_interface $driver
+ */
+ public function set_driver(driver_interface $driver)
+ {
+ $this->driver = $driver;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_layer()
+ {
+ return $this->get_driver()->get_sql_layer();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_name()
+ {
+ return $this->get_driver()->get_db_name();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_any_char()
+ {
+ return $this->get_driver()->get_any_char();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_one_char()
+ {
+ return $this->get_driver()->get_one_char();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_db_connect_id()
+ {
+ return $this->get_driver()->get_db_connect_id();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_triggered()
+ {
+ return $this->get_driver()->get_sql_error_triggered();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_sql()
+ {
+ return $this->get_driver()->get_sql_error_sql();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_transaction()
+ {
+ return $this->get_driver()->get_transaction();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_time()
+ {
+ return $this->get_driver()->get_sql_time();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql_error_returned()
+ {
+ return $this->get_driver()->get_sql_error_returned();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_multi_insert()
+ {
+ return $this->get_driver()->get_multi_insert();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_multi_insert($multi_insert)
+ {
+ $this->get_driver()->set_multi_insert($multi_insert);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_row_count($table_name)
+ {
+ return $this->get_driver()->get_row_count($table_name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_estimated_row_count($table_name)
+ {
+ return $this->get_driver()->get_estimated_row_count($table_name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_lower_text($column_name)
+ {
+ return $this->get_driver()->sql_lower_text($column_name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_error($sql = '')
+ {
+ return $this->get_driver()->sql_error($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_buffer_nested_transactions()
+ {
+ return $this->get_driver()->sql_buffer_nested_transactions();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_bit_or($column_name, $bit, $compare = '')
+ {
+ return $this->get_driver()->sql_bit_or($column_name, $bit, $compare);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_server_info($raw = false, $use_cache = true)
+ {
+ return $this->get_driver()->sql_server_info($raw, $use_cache);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_return_on_error($fail = false)
+ {
+ return $this->get_driver()->sql_return_on_error($fail);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_build_array($query, $assoc_ary = array())
+ {
+ return $this->get_driver()->sql_build_array($query, $assoc_ary);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_fetchrowset($query_id = false)
+ {
+ return $this->get_driver()->sql_fetchrowset($query_id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_transaction($status = 'begin')
+ {
+ return $this->get_driver()->sql_transaction($status);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $this->get_driver()->sql_concatenate($expr1, $expr2);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_case($condition, $action_true, $action_false = false)
+ {
+ return $this->get_driver()->sql_case($condition, $action_true, $action_false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_build_query($query, $array)
+ {
+ return $this->get_driver()->sql_build_query($query, $array);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_fetchfield($field, $rownum = false, $query_id = false)
+ {
+ return $this->get_driver()->sql_fetchfield($field, $rownum, $query_id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_fetchrow($query_id = false)
+ {
+ return $this->get_driver()->sql_fetchrow($query_id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cast_expr_to_bigint($expression)
+ {
+ return $this->get_driver()->cast_expr_to_bigint($expression);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_nextid()
+ {
+ return $this->get_driver()->sql_nextid();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_add_num_queries($cached = false)
+ {
+ return $this->get_driver()->sql_add_num_queries($cached);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ return $this->get_driver()->sql_query_limit($query, $total, $offset, $cache_ttl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_query($query = '', $cache_ttl = 0)
+ {
+ return $this->get_driver()->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function cast_expr_to_string($expression)
+ {
+ return $this->get_driver()->cast_expr_to_string($expression);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ throw new \Exception('Disabled method.');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_bit_and($column_name, $bit, $compare = '')
+ {
+ return $this->get_driver()->sql_bit_and($column_name, $bit, $compare);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_freeresult($query_id = false)
+ {
+ return $this->get_driver()->sql_freeresult($query_id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_num_queries($cached = false)
+ {
+ return $this->get_driver()->sql_num_queries($cached);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_multi_insert($table, $sql_ary)
+ {
+ return $this->get_driver()->sql_multi_insert($table, $sql_ary);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_affectedrows()
+ {
+ return $this->get_driver()->sql_affectedrows();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_close()
+ {
+ return $this->get_driver()->sql_close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_rowseek($rownum, &$query_id)
+ {
+ return $this->get_driver()->sql_rowseek($rownum, $query_id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_escape($msg)
+ {
+ return $this->get_driver()->sql_escape($msg);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_like_expression($expression)
+ {
+ return $this->get_driver()->sql_like_expression($expression);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_not_like_expression($expression)
+ {
+ return $this->get_driver()->sql_not_like_expression($expression);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_report($mode, $query = '')
+ {
+ return $this->get_driver()->sql_report($mode, $query);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
+ {
+ return $this->get_driver()->sql_in_set($field, $array, $negate, $allow_empty_set);
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mssql.php b/phpBB/phpbb/db/driver/mssql.php
new file mode 100644
index 0000000000..f9ea884ce2
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mssql.php
@@ -0,0 +1,476 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* MSSQL Database Abstraction Layer
+* Minimum Requirement is MSSQL 2000+
+*/
+class mssql extends \phpbb\db\driver\driver
+{
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ if (!function_exists('mssql_connect'))
+ {
+ $this->connect_error = 'mssql_connect function does not exist, is mssql extension installed?';
+ return $this->sql_error('');
+ }
+
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->dbname = $database;
+
+ $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
+ $this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
+
+ @ini_set('mssql.charset', 'UTF-8');
+ @ini_set('mssql.textlimit', 2147483647);
+ @ini_set('mssql.textsize', 2147483647);
+
+ $this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword, $new_link) : @mssql_connect($this->server, $this->user, $sqlpassword, $new_link);
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (!@mssql_select_db($this->dbname, $this->db_connect_id))
+ {
+ @mssql_close($this->db_connect_id);
+ return false;
+ }
+ }
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
+ {
+ $result_id = @mssql_query("SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')", $this->db_connect_id);
+
+ $row = false;
+ if ($result_id)
+ {
+ $row = @mssql_fetch_assoc($result_id);
+ @mssql_free_result($result_id);
+ }
+
+ $this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mssql_version', $this->sql_server_version);
+ }
+ }
+
+ if ($raw)
+ {
+ return $this->sql_server_version;
+ }
+
+ return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' + ' . $expr2;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
+ break;
+
+ case 'commit':
+ return @mssql_query('COMMIT TRANSACTION', $this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return @mssql_query('ROLLBACK TRANSACTION', $this->db_connect_id);
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
+ if ($total)
+ {
+ // We need to grab the total number of rows + the offset number of rows to get the correct result
+ if (strpos($query, 'SELECT DISTINCT') === 0)
+ {
+ $query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
+ }
+ else
+ {
+ $query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
+ }
+ }
+
+ $result = $this->sql_query($query, $cache_ttl);
+
+ // Seek by $offset rows
+ if ($offset)
+ {
+ $this->sql_rowseek($offset, $result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ $row = @mssql_fetch_assoc($query_id);
+
+ // I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
+ if ($row)
+ {
+ foreach ($row as $key => $value)
+ {
+ $row[$key] = ($value === ' ' || $value === null) ? '' : $value;
+ }
+ }
+
+ return $row;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ return ($query_id !== false) ? @mssql_data_seek($query_id, $rownum) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ $result_id = @mssql_query('SELECT SCOPE_IDENTITY()', $this->db_connect_id);
+ if ($result_id)
+ {
+ if ($row = @mssql_fetch_assoc($result_id))
+ {
+ @mssql_free_result($result_id);
+ return $row['computed'];
+ }
+ @mssql_free_result($result_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @mssql_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return str_replace(array("'", "\0"), array("''", ''), $msg);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_lower_text($column_name)
+ {
+ return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))";
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if (function_exists('mssql_get_last_message'))
+ {
+ $error = array(
+ 'message' => @mssql_get_last_message(),
+ 'code' => '',
+ );
+
+ // Get error code number
+ $result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
+ if ($result_id)
+ {
+ $row = @mssql_fetch_assoc($result_id);
+ $error['code'] = $row['code'];
+ @mssql_free_result($result_id);
+ }
+
+ // Get full error message if possible
+ $sql = 'SELECT CAST(description as varchar(255)) as message
+ FROM master.dbo.sysmessages
+ WHERE error = ' . $error['code'];
+ $result_id = @mssql_query($sql);
+
+ if ($result_id)
+ {
+ $row = @mssql_fetch_assoc($result_id);
+ if (!empty($row['message']))
+ {
+ $error['message'] .= '<br />' . $row['message'];
+ }
+ @mssql_free_result($result_id);
+ }
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @mssql_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+ $html_table = false;
+ @mssql_query('SET SHOWPLAN_TEXT ON;', $this->db_connect_id);
+ if ($result = @mssql_query($query, $this->db_connect_id))
+ {
+ @mssql_next_result($result);
+ while ($row = @mssql_fetch_row($result))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @mssql_query('SET SHOWPLAN_TEXT OFF;', $this->db_connect_id);
+ @mssql_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @mssql_query($query, $this->db_connect_id);
+ while ($void = @mssql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @mssql_free_result($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mssql_base.php b/phpBB/phpbb/db/driver/mssql_base.php
new file mode 100644
index 0000000000..514df9eaca
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mssql_base.php
@@ -0,0 +1,71 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* MSSQL Database Base Abstraction Layer
+ */
+abstract class mssql_base extends \phpbb\db\driver\driver
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return $expr1 . ' + ' . $expr2;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return str_replace(array("'", "\0"), array("''", ''), $msg);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_lower_text($column_name)
+ {
+ return "LOWER(SUBSTRING($column_name, 1, DATALENGTH($column_name)))";
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mssql_odbc.php b/phpBB/phpbb/db/driver/mssql_odbc.php
new file mode 100644
index 0000000000..8e5d4c7a4c
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mssql_odbc.php
@@ -0,0 +1,377 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* Unified ODBC functions
+* Unified ODBC functions support any database having ODBC driver, for example Adabas D, IBM DB2, iODBC, Solid, Sybase SQL Anywhere...
+* Here we only support MSSQL Server 2000+ because of the provided schema
+*
+* @note number of bytes returned for returning data depends on odbc.defaultlrl php.ini setting.
+* If it is limited to 4K for example only 4K of data is returned max, resulting in incomplete theme data for example.
+* @note odbc.defaultbinmode may affect UTF8 characters
+*/
+class mssql_odbc extends \phpbb\db\driver\mssql_base
+{
+ var $last_query_text = '';
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->dbname = $database;
+
+ $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
+ $this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
+
+ $max_size = @ini_get('odbc.defaultlrl');
+ if (!empty($max_size))
+ {
+ $unit = strtolower(substr($max_size, -1, 1));
+ $max_size = (int) $max_size;
+
+ if ($unit == 'k')
+ {
+ $max_size = floor($max_size / 1024);
+ }
+ else if ($unit == 'g')
+ {
+ $max_size *= 1024;
+ }
+ else if (is_numeric($unit))
+ {
+ $max_size = floor((int) ($max_size . $unit) / 1048576);
+ }
+ $max_size = max(8, $max_size) . 'M';
+
+ @ini_set('odbc.defaultlrl', $max_size);
+ }
+
+ if ($this->persistency)
+ {
+ if (!function_exists('odbc_pconnect'))
+ {
+ $this->connect_error = 'odbc_pconnect function does not exist, is odbc extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @odbc_pconnect($this->server, $this->user, $sqlpassword);
+ }
+ else
+ {
+ if (!function_exists('odbc_connect'))
+ {
+ $this->connect_error = 'odbc_connect function does not exist, is odbc extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @odbc_connect($this->server, $this->user, $sqlpassword);
+ }
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssqlodbc_version')) === false)
+ {
+ $result_id = @odbc_exec($this->db_connect_id, "SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')");
+
+ $row = false;
+ if ($result_id)
+ {
+ $row = @odbc_fetch_array($result_id);
+ @odbc_free_result($result_id);
+ }
+
+ $this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mssqlodbc_version', $this->sql_server_version);
+ }
+ }
+
+ if ($raw)
+ {
+ return $this->sql_server_version;
+ }
+
+ return ($this->sql_server_version) ? 'MSSQL (ODBC)<br />' . $this->sql_server_version : 'MSSQL (ODBC)';
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @odbc_exec($this->db_connect_id, 'BEGIN TRANSACTION');
+ break;
+
+ case 'commit':
+ return @odbc_exec($this->db_connect_id, 'COMMIT TRANSACTION');
+ break;
+
+ case 'rollback':
+ return @odbc_exec($this->db_connect_id, 'ROLLBACK TRANSACTION');
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @odbc_exec($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
+ if ($total)
+ {
+ // We need to grab the total number of rows + the offset number of rows to get the correct result
+ if (strpos($query, 'SELECT DISTINCT') === 0)
+ {
+ $query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
+ }
+ else
+ {
+ $query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
+ }
+ }
+
+ $result = $this->sql_query($query, $cache_ttl);
+
+ // Seek by $offset rows
+ if ($offset)
+ {
+ $this->sql_rowseek($offset, $result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @odbc_num_rows($this->query_result) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id !== false) ? @odbc_fetch_array($query_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ $result_id = @odbc_exec($this->db_connect_id, 'SELECT @@IDENTITY');
+
+ if ($result_id)
+ {
+ if (@odbc_fetch_array($result_id))
+ {
+ $id = @odbc_result($result_id, 1);
+ @odbc_free_result($result_id);
+ return $id;
+ }
+ @odbc_free_result($result_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @odbc_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if (function_exists('odbc_errormsg'))
+ {
+ $error = array(
+ 'message' => @odbc_errormsg(),
+ 'code' => @odbc_error(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @odbc_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @odbc_exec($this->db_connect_id, $query);
+ while ($void = @odbc_fetch_array($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @odbc_free_result($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mssqlnative.php b/phpBB/phpbb/db/driver/mssqlnative.php
new file mode 100644
index 0000000000..46a9b3a477
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mssqlnative.php
@@ -0,0 +1,442 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+/**
+* This is the MS SQL Server Native database abstraction layer.
+* PHP mssql native driver required.
+* @author Chris Pucci
+*
+*/
+
+namespace phpbb\db\driver;
+
+class mssqlnative extends \phpbb\db\driver\mssql_base
+{
+ var $m_insert_id = null;
+ var $last_query_text = '';
+ var $query_options = array();
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ // Test for driver support, to avoid suppressed fatal error
+ if (!function_exists('sqlsrv_connect'))
+ {
+ $this->connect_error = 'Native MS SQL Server driver for PHP is missing or needs to be updated. Version 1.1 or later is required to install phpBB3. You can download the driver from: http://www.microsoft.com/sqlserver/2005/en/us/PHP-Driver.aspx';
+ return $this->sql_error('');
+ }
+
+ //set up connection variables
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->dbname = $database;
+ $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
+ $this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
+
+ //connect to database
+ $this->db_connect_id = sqlsrv_connect($this->server, array(
+ 'Database' => $this->dbname,
+ 'UID' => $this->user,
+ 'PWD' => $sqlpassword
+ ));
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
+ {
+ $arr_server_info = sqlsrv_server_info($this->db_connect_id);
+ $this->sql_server_version = $arr_server_info['SQLServerVersion'];
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mssql_version', $this->sql_server_version);
+ }
+ }
+
+ if ($raw)
+ {
+ return $this->sql_server_version;
+ }
+
+ return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_buffer_nested_transactions()
+ {
+ return true;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return sqlsrv_begin_transaction($this->db_connect_id);
+ break;
+
+ case 'commit':
+ return sqlsrv_commit($this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return sqlsrv_rollback($this->db_connect_id);
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @sqlsrv_query($this->db_connect_id, $query, array(), $this->query_options)) === false)
+ {
+ $this->sql_error($query);
+ }
+ // reset options for next query
+ $this->query_options = array();
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // total == 0 means all results - not zero results
+ if ($offset == 0 && $total !== 0)
+ {
+ if (strpos($query, "SELECT") === false)
+ {
+ $query = "TOP {$total} " . $query;
+ }
+ else
+ {
+ $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP '.$total, $query);
+ }
+ }
+ else if ($offset > 0)
+ {
+ $query = preg_replace('/SELECT(\s*DISTINCT)?/Dsi', 'SELECT$1 TOP(10000000) ', $query);
+ $query = 'SELECT *
+ FROM (SELECT sub2.*, ROW_NUMBER() OVER(ORDER BY sub2.line2) AS line3
+ FROM (SELECT 1 AS line2, sub1.* FROM (' . $query . ') AS sub1) as sub2) AS sub3';
+
+ if ($total > 0)
+ {
+ $query .= ' WHERE line3 BETWEEN ' . ($offset+1) . ' AND ' . ($offset + $total);
+ }
+ else
+ {
+ $query .= ' WHERE line3 > ' . $offset;
+ }
+ }
+
+ $result = $this->sql_query($query, $cache_ttl);
+
+ return $result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @sqlsrv_rows_affected($this->query_result) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ $row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
+
+ if ($row)
+ {
+ foreach ($row as $key => $value)
+ {
+ $row[$key] = ($value === ' ' || $value === null) ? '' : $value;
+ }
+
+ // remove helper values from LIMIT queries
+ if (isset($row['line2']))
+ {
+ unset($row['line2'], $row['line3']);
+ }
+ }
+ return (sizeof($row)) ? $row : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ $result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY');
+
+ if ($result_id !== false)
+ {
+ $row = @sqlsrv_fetch_array($result_id);
+ $id = $row[0];
+ @sqlsrv_free_stmt($result_id);
+ return $id;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @sqlsrv_free_stmt($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if (function_exists('sqlsrv_errors'))
+ {
+ $errors = @sqlsrv_errors(SQLSRV_ERR_ERRORS);
+ $error_message = '';
+ $code = 0;
+
+ if ($errors != null)
+ {
+ foreach ($errors as $error)
+ {
+ $error_message .= "SQLSTATE: " . $error['SQLSTATE'] . "\n";
+ $error_message .= "code: " . $error['code'] . "\n";
+ $code = $error['code'];
+ $error_message .= "message: " . $error['message'] . "\n";
+ }
+ $this->last_error_result = $error_message;
+ $error = $this->last_error_result;
+ }
+ else
+ {
+ $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ }
+
+ $error = array(
+ 'message' => $error,
+ 'code' => $code,
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @sqlsrv_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+ $html_table = false;
+ @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT ON;');
+ if ($result = @sqlsrv_query($this->db_connect_id, $query))
+ {
+ @sqlsrv_next_result($result);
+ while ($row = @sqlsrv_fetch_array($result))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;');
+ @sqlsrv_free_stmt($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @sqlsrv_query($this->db_connect_id, $query);
+ while ($void = @sqlsrv_fetch_array($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @sqlsrv_free_stmt($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+
+ /**
+ * Utility method used to retrieve number of rows
+ * Emulates mysql_num_rows
+ * Used in acp_database.php -> write_data_mssqlnative()
+ * Requires a static or keyset cursor to be definde via
+ * mssqlnative_set_query_options()
+ */
+ function mssqlnative_num_rows($res)
+ {
+ if ($res !== false)
+ {
+ return sqlsrv_num_rows($res);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Allows setting mssqlnative specific query options passed to sqlsrv_query as 4th parameter.
+ */
+ function mssqlnative_set_query_options($options)
+ {
+ $this->query_options = $options;
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mysql.php b/phpBB/phpbb/db/driver/mysql.php
new file mode 100644
index 0000000000..e93c7239e8
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mysql.php
@@ -0,0 +1,485 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* MySQL4 Database Abstraction Layer
+* Compatible with:
+* MySQL 3.23+
+* MySQL 4.0+
+* MySQL 4.1+
+* MySQL 5.0+
+*/
+class mysql extends \phpbb\db\driver\mysql_base
+{
+ var $multi_insert = true;
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $this->sql_layer = 'mysql4';
+
+ if ($this->persistency)
+ {
+ if (!function_exists('mysql_pconnect'))
+ {
+ $this->connect_error = 'mysql_pconnect function does not exist, is mysql extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @mysql_pconnect($this->server, $this->user, $sqlpassword);
+ }
+ else
+ {
+ if (!function_exists('mysql_connect'))
+ {
+ $this->connect_error = 'mysql_connect function does not exist, is mysql extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @mysql_connect($this->server, $this->user, $sqlpassword, $new_link);
+ }
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ if (@mysql_select_db($this->dbname, $this->db_connect_id))
+ {
+ // Determine what version we are using and if it natively supports UNICODE
+ if (version_compare($this->sql_server_info(true), '4.1.0', '>='))
+ {
+ @mysql_query("SET NAMES 'utf8'", $this->db_connect_id);
+
+ // enforce strict mode on databases that support it
+ if (version_compare($this->sql_server_info(true), '5.0.2', '>='))
+ {
+ $result = @mysql_query('SELECT @@session.sql_mode AS sql_mode', $this->db_connect_id);
+ $row = @mysql_fetch_assoc($result);
+ @mysql_free_result($result);
+ $modes = array_map('trim', explode(',', $row['sql_mode']));
+
+ // TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
+ if (!in_array('TRADITIONAL', $modes))
+ {
+ if (!in_array('STRICT_ALL_TABLES', $modes))
+ {
+ $modes[] = 'STRICT_ALL_TABLES';
+ }
+
+ if (!in_array('STRICT_TRANS_TABLES', $modes))
+ {
+ $modes[] = 'STRICT_TRANS_TABLES';
+ }
+ }
+
+ $mode = implode(',', $modes);
+ @mysql_query("SET SESSION sql_mode='{$mode}'", $this->db_connect_id);
+ }
+ }
+ else if (version_compare($this->sql_server_info(true), '4.0.0', '<'))
+ {
+ $this->sql_layer = 'mysql';
+ }
+
+ return $this->db_connect_id;
+ }
+ }
+
+ return $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysql_version')) === false)
+ {
+ $result = @mysql_query('SELECT VERSION() AS version', $this->db_connect_id);
+ $row = @mysql_fetch_assoc($result);
+ @mysql_free_result($result);
+
+ $this->sql_server_version = $row['version'];
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mysql_version', $this->sql_server_version);
+ }
+ }
+
+ return ($raw) ? $this->sql_server_version : 'MySQL ' . $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @mysql_query('BEGIN', $this->db_connect_id);
+ break;
+
+ case 'commit':
+ return @mysql_query('COMMIT', $this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return @mysql_query('ROLLBACK', $this->db_connect_id);
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ if ($this->db_connect_id)
+ {
+ // We always want the number of matched rows
+ // instead of changed rows, when running an update.
+ // So when mysql_info() returns the number of matched rows
+ // we return that one instead of mysql_affected_rows()
+ $mysql_info = @mysql_info($this->db_connect_id);
+ if ($mysql_info !== false)
+ {
+ $match = array();
+ preg_match('#^Rows matched: (\d)+ Changed: (\d)+ Warnings: (\d)+$#', $mysql_info, $match);
+ if (isset($match[1]))
+ {
+ return $match[1];
+ }
+ }
+
+ return @mysql_affected_rows($this->db_connect_id);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id !== false) ? @mysql_fetch_assoc($query_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ return ($query_id !== false) ? @mysql_data_seek($query_id, $rownum) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @mysql_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ if (!$this->db_connect_id)
+ {
+ return @mysql_real_escape_string($msg);
+ }
+
+ return @mysql_real_escape_string($msg, $this->db_connect_id);
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if ($this->db_connect_id)
+ {
+ $error = array(
+ 'message' => @mysql_error($this->db_connect_id),
+ 'code' => @mysql_errno($this->db_connect_id),
+ );
+ }
+ else if (function_exists('mysql_error'))
+ {
+ $error = array(
+ 'message' => @mysql_error(),
+ 'code' => @mysql_errno(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @mysql_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ static $test_prof;
+
+ // current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
+ if ($test_prof === null)
+ {
+ $test_prof = false;
+ if (version_compare($this->sql_server_info(true), '5.0.37', '>=') && version_compare($this->sql_server_info(true), '5.1', '<'))
+ {
+ $test_prof = true;
+ }
+ }
+
+ switch ($mode)
+ {
+ case 'start':
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = false;
+
+ // begin profiling
+ if ($test_prof)
+ {
+ @mysql_query('SET profiling = 1;', $this->db_connect_id);
+ }
+
+ if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
+ {
+ while ($row = @mysql_fetch_assoc($result))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @mysql_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+
+ if ($test_prof)
+ {
+ $html_table = false;
+
+ // get the last profile
+ if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id))
+ {
+ $this->html_hold .= '<br />';
+ while ($row = @mysql_fetch_assoc($result))
+ {
+ // make <unknown> HTML safe
+ if (!empty($row['Source_function']))
+ {
+ $row['Source_function'] = str_replace(array('<', '>'), array('&lt;', '&gt;'), $row['Source_function']);
+ }
+
+ // remove unsupported features
+ foreach ($row as $key => $val)
+ {
+ if ($val === null)
+ {
+ unset($row[$key]);
+ }
+ }
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @mysql_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+
+ @mysql_query('SET profiling = 0;', $this->db_connect_id);
+ }
+ }
+
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @mysql_query($query, $this->db_connect_id);
+ while ($void = @mysql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @mysql_free_result($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mysql_base.php b/phpBB/phpbb/db/driver/mysql_base.php
new file mode 100644
index 0000000000..5e0b359134
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mysql_base.php
@@ -0,0 +1,138 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* Abstract MySQL Database Base Abstraction Layer
+*/
+abstract class mysql_base extends \phpbb\db\driver\driver
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_concatenate($expr1, $expr2)
+ {
+ return 'CONCAT(' . $expr1 . ', ' . $expr2 . ')';
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ // MySQL 4.1+ no longer supports -1 in limit queries
+ $total = '18446744073709551615';
+ }
+
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function get_estimated_row_count($table_name)
+ {
+ $table_status = $this->get_table_status($table_name);
+
+ if (isset($table_status['Engine']))
+ {
+ if ($table_status['Engine'] === 'MyISAM')
+ {
+ return $table_status['Rows'];
+ }
+ else if ($table_status['Engine'] === 'InnoDB' && $table_status['Rows'] > 100000)
+ {
+ return '~' . $table_status['Rows'];
+ }
+ }
+
+ return parent::get_row_count($table_name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function get_row_count($table_name)
+ {
+ $table_status = $this->get_table_status($table_name);
+
+ if (isset($table_status['Engine']) && $table_status['Engine'] === 'MyISAM')
+ {
+ return $table_status['Rows'];
+ }
+
+ return parent::get_row_count($table_name);
+ }
+
+ /**
+ * Gets some information about the specified table.
+ *
+ * @param string $table_name Table name
+ *
+ * @return array
+ *
+ * @access protected
+ */
+ function get_table_status($table_name)
+ {
+ $sql = "SHOW TABLE STATUS
+ LIKE '" . $this->sql_escape($table_name) . "'";
+ $result = $this->sql_query($sql);
+ $table_status = $this->sql_fetchrow($result);
+ $this->sql_freeresult($result);
+
+ return $table_status;
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ switch ($stage)
+ {
+ case 'FROM':
+ $data = '(' . $data . ')';
+ break;
+ }
+
+ return $data;
+ }
+}
diff --git a/phpBB/phpbb/db/driver/mysqli.php b/phpBB/phpbb/db/driver/mysqli.php
new file mode 100644
index 0000000000..c0ddfbf76c
--- /dev/null
+++ b/phpBB/phpbb/db/driver/mysqli.php
@@ -0,0 +1,475 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* MySQLi Database Abstraction Layer
+* mysqli-extension has to be compiled with:
+* MySQL 4.1+ or MySQL 5.0+
+*/
+class mysqli extends \phpbb\db\driver\mysql_base
+{
+ var $multi_insert = true;
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ if (!function_exists('mysqli_connect'))
+ {
+ $this->connect_error = 'mysqli_connect function does not exist, is mysqli extension installed?';
+ return $this->sql_error('');
+ }
+
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+
+ // If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix
+ $this->server = ($this->persistency) ? 'p:' . (($sqlserver) ? $sqlserver : 'localhost') : $sqlserver;
+
+ $this->dbname = $database;
+ $port = (!$port) ? null : $port;
+
+ // If port is set and it is not numeric, most likely mysqli socket is set.
+ // Try to map it to the $socket parameter.
+ $socket = null;
+ if ($port)
+ {
+ if (is_numeric($port))
+ {
+ $port = (int) $port;
+ }
+ else
+ {
+ $socket = $port;
+ $port = null;
+ }
+ }
+
+ $this->db_connect_id = mysqli_init();
+
+ if (!@mysqli_real_connect($this->db_connect_id, $this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket, MYSQLI_CLIENT_FOUND_ROWS))
+ {
+ $this->db_connect_id = '';
+ }
+
+ if ($this->db_connect_id && $this->dbname != '')
+ {
+ @mysqli_query($this->db_connect_id, "SET NAMES 'utf8'");
+
+ // enforce strict mode on databases that support it
+ if (version_compare($this->sql_server_info(true), '5.0.2', '>='))
+ {
+ $result = @mysqli_query($this->db_connect_id, 'SELECT @@session.sql_mode AS sql_mode');
+ if ($result !== null)
+ {
+ $row = @mysqli_fetch_assoc($result);
+
+ $modes = array_map('trim', explode(',', $row['sql_mode']));
+ }
+ else
+ {
+ $modes = array();
+ }
+ @mysqli_free_result($result);
+
+ // TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
+ if (!in_array('TRADITIONAL', $modes))
+ {
+ if (!in_array('STRICT_ALL_TABLES', $modes))
+ {
+ $modes[] = 'STRICT_ALL_TABLES';
+ }
+
+ if (!in_array('STRICT_TRANS_TABLES', $modes))
+ {
+ $modes[] = 'STRICT_TRANS_TABLES';
+ }
+ }
+
+ $mode = implode(',', $modes);
+ @mysqli_query($this->db_connect_id, "SET SESSION sql_mode='{$mode}'");
+ }
+ return $this->db_connect_id;
+ }
+
+ return $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysqli_version')) === false)
+ {
+ $result = @mysqli_query($this->db_connect_id, 'SELECT VERSION() AS version');
+ if ($result !== null)
+ {
+ $row = @mysqli_fetch_assoc($result);
+
+ $this->sql_server_version = $row['version'];
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mysqli_version', $this->sql_server_version);
+ }
+ }
+ @mysqli_free_result($result);
+ }
+
+ return ($raw) ? $this->sql_server_version : 'MySQL(i) ' . $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @mysqli_autocommit($this->db_connect_id, false);
+ break;
+
+ case 'commit':
+ $result = @mysqli_commit($this->db_connect_id);
+ @mysqli_autocommit($this->db_connect_id, true);
+ return $result;
+ break;
+
+ case 'rollback':
+ $result = @mysqli_rollback($this->db_connect_id);
+ @mysqli_autocommit($this->db_connect_id, true);
+ return $result;
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @mysqli_query($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @mysqli_affected_rows($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ if ($query_id !== false && $query_id !== null)
+ {
+ $result = @mysqli_fetch_assoc($query_id);
+ return $result !== null ? $result : false;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ return ($query_id !== false) ? @mysqli_data_seek($query_id, $rownum) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @mysqli_insert_id($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ return @mysqli_free_result($query_id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return @mysqli_real_escape_string($this->db_connect_id, $msg);
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if ($this->db_connect_id)
+ {
+ $error = array(
+ 'message' => @mysqli_error($this->db_connect_id),
+ 'code' => @mysqli_errno($this->db_connect_id)
+ );
+ }
+ else if (function_exists('mysqli_connect_error'))
+ {
+ $error = array(
+ 'message' => @mysqli_connect_error(),
+ 'code' => @mysqli_connect_errno(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @mysqli_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ static $test_prof;
+
+ // current detection method, might just switch to see the existance of INFORMATION_SCHEMA.PROFILING
+ if ($test_prof === null)
+ {
+ $test_prof = false;
+ if (strpos(mysqli_get_server_info($this->db_connect_id), 'community') !== false)
+ {
+ $ver = mysqli_get_server_version($this->db_connect_id);
+ if ($ver >= 50037 && $ver < 50100)
+ {
+ $test_prof = true;
+ }
+ }
+ }
+
+ switch ($mode)
+ {
+ case 'start':
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = false;
+
+ // begin profiling
+ if ($test_prof)
+ {
+ @mysqli_query($this->db_connect_id, 'SET profiling = 1;');
+ }
+
+ if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
+ {
+ while ($row = @mysqli_fetch_assoc($result))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @mysqli_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+
+ if ($test_prof)
+ {
+ $html_table = false;
+
+ // get the last profile
+ if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;'))
+ {
+ $this->html_hold .= '<br />';
+ while ($row = @mysqli_fetch_assoc($result))
+ {
+ // make <unknown> HTML safe
+ if (!empty($row['Source_function']))
+ {
+ $row['Source_function'] = str_replace(array('<', '>'), array('&lt;', '&gt;'), $row['Source_function']);
+ }
+
+ // remove unsupported features
+ foreach ($row as $key => $val)
+ {
+ if ($val === null)
+ {
+ unset($row[$key]);
+ }
+ }
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @mysqli_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+
+ @mysqli_query($this->db_connect_id, 'SET profiling = 0;');
+ }
+ }
+
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @mysqli_query($this->db_connect_id, $query);
+ if ($result !== null)
+ {
+ while ($void = @mysqli_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ }
+ @mysqli_free_result($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/oracle.php b/phpBB/phpbb/db/driver/oracle.php
new file mode 100644
index 0000000000..6dcab5dd7d
--- /dev/null
+++ b/phpBB/phpbb/db/driver/oracle.php
@@ -0,0 +1,807 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* Oracle Database Abstraction Layer
+*/
+class oracle extends \phpbb\db\driver\driver
+{
+ var $last_query_text = '';
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $connect = $database;
+
+ // support for "easy connect naming"
+ if ($sqlserver !== '' && $sqlserver !== '/')
+ {
+ if (substr($sqlserver, -1, 1) == '/')
+ {
+ $sqlserver == substr($sqlserver, 0, -1);
+ }
+ $connect = $sqlserver . (($port) ? ':' . $port : '') . '/' . $database;
+ }
+
+ if ($new_link)
+ {
+ if (!function_exists('ocinlogon'))
+ {
+ $this->connect_error = 'ocinlogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ocinlogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
+ else if ($this->persistency)
+ {
+ if (!function_exists('ociplogon'))
+ {
+ $this->connect_error = 'ociplogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ociplogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
+ else
+ {
+ if (!function_exists('ocilogon'))
+ {
+ $this->connect_error = 'ocilogon function does not exist, is oci extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @ocilogon($this->user, $sqlpassword, $connect, 'UTF8');
+ }
+
+ return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ /**
+ * force $use_cache false. I didn't research why the caching code below is commented out
+ * but I assume its because the Oracle extension provides a direct method to access it
+ * without a query.
+ */
+
+ $use_cache = false;
+/*
+ global $cache;
+
+ if (empty($cache) || ($this->sql_server_version = $cache->get('oracle_version')) === false)
+ {
+ $result = @ociparse($this->db_connect_id, 'SELECT * FROM v$version WHERE banner LIKE \'Oracle%\'');
+ @ociexecute($result, OCI_DEFAULT);
+ @ocicommit($this->db_connect_id);
+
+ $row = array();
+ @ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS);
+ @ocifreestatement($result);
+ $this->sql_server_version = trim($row['BANNER']);
+
+ $cache->put('oracle_version', $this->sql_server_version);
+ }
+*/
+ $this->sql_server_version = @ociserverversion($this->db_connect_id);
+
+ return $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return true;
+ break;
+
+ case 'commit':
+ return @ocicommit($this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return @ocirollback($this->db_connect_id);
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * Oracle specific code to handle the fact that it does not compare columns properly
+ * @access private
+ */
+ function _rewrite_col_compare($args)
+ {
+ if (sizeof($args) == 4)
+ {
+ if ($args[2] == '=')
+ {
+ return '(' . $args[0] . ' OR (' . $args[1] . ' is NULL AND ' . $args[3] . ' is NULL))';
+ }
+ else if ($args[2] == '<>')
+ {
+ // really just a fancy way of saying foo <> bar or (foo is NULL XOR bar is NULL) but SQL has no XOR :P
+ return '(' . $args[0] . ' OR ((' . $args[1] . ' is NULL AND ' . $args[3] . ' is NOT NULL) OR (' . $args[1] . ' is NOT NULL AND ' . $args[3] . ' is NULL)))';
+ }
+ }
+ else
+ {
+ return $this->_rewrite_where($args[0]);
+ }
+ }
+
+ /**
+ * Oracle specific code to handle it's lack of sanity
+ * @access private
+ */
+ function _rewrite_where($where_clause)
+ {
+ preg_match_all('/\s*(AND|OR)?\s*([\w_.()]++)\s*(?:(=|<[=>]?|>=?|LIKE)\s*((?>\'(?>[^\']++|\'\')*+\'|[\d-.()]+))|((NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))/', $where_clause, $result, PREG_SET_ORDER);
+ $out = '';
+ foreach ($result as $val)
+ {
+ if (!isset($val[5]))
+ {
+ if ($val[4] !== "''")
+ {
+ $out .= $val[0];
+ }
+ else
+ {
+ $out .= ' ' . $val[1] . ' ' . $val[2];
+ if ($val[3] == '=')
+ {
+ $out .= ' is NULL';
+ }
+ else if ($val[3] == '<>')
+ {
+ $out .= ' is NOT NULL';
+ }
+ }
+ }
+ else
+ {
+ $in_clause = array();
+ $sub_exp = substr($val[5], strpos($val[5], '(') + 1, -1);
+ $extra = false;
+ preg_match_all('/\'(?>[^\']++|\'\')*+\'|[\d-.]++/', $sub_exp, $sub_vals, PREG_PATTERN_ORDER);
+ $i = 0;
+ foreach ($sub_vals[0] as $sub_val)
+ {
+ // two things:
+ // 1) This determines if an empty string was in the IN clausing, making us turn it into a NULL comparison
+ // 2) This fixes the 1000 list limit that Oracle has (ORA-01795)
+ if ($sub_val !== "''")
+ {
+ $in_clause[(int) $i++/1000][] = $sub_val;
+ }
+ else
+ {
+ $extra = true;
+ }
+ }
+ if (!$extra && $i < 1000)
+ {
+ $out .= $val[0];
+ }
+ else
+ {
+ $out .= ' ' . $val[1] . '(';
+ $in_array = array();
+
+ // constuct each IN() clause
+ foreach ($in_clause as $in_values)
+ {
+ $in_array[] = $val[2] . ' ' . (isset($val[6]) ? $val[6] : '') . 'IN(' . implode(', ', $in_values) . ')';
+ }
+
+ // Join the IN() clauses against a few ORs (IN is just a nicer OR anyway)
+ $out .= implode(' OR ', $in_array);
+
+ // handle the empty string case
+ if ($extra)
+ {
+ $out .= ' OR ' . $val[2] . ' is ' . (isset($val[6]) ? $val[6] : '') . 'NULL';
+ }
+ $out .= ')';
+
+ unset($in_array, $in_clause);
+ }
+ }
+ }
+
+ return $out;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ $in_transaction = false;
+ if (!$this->transaction)
+ {
+ $this->sql_transaction('begin');
+ }
+ else
+ {
+ $in_transaction = true;
+ }
+
+ $array = array();
+
+ // We overcome Oracle's 4000 char limit by binding vars
+ if (strlen($query) > 4000)
+ {
+ if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/sU', $query, $regs))
+ {
+ if (strlen($regs[3]) > 4000)
+ {
+ $cols = explode(', ', $regs[2]);
+
+ preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
+
+/* The code inside this comment block breaks clob handling, but does allow the
+ database restore script to work. If you want to allow no posts longer than 4KB
+ and/or need the db restore script, uncomment this.
+
+
+ if (sizeof($cols) !== sizeof($vals))
+ {
+ // Try to replace some common data we know is from our restore script or from other sources
+ $regs[3] = str_replace("'||chr(47)||'", '/', $regs[3]);
+ $_vals = explode(', ', $regs[3]);
+
+ $vals = array();
+ $is_in_val = false;
+ $i = 0;
+ $string = '';
+
+ foreach ($_vals as $value)
+ {
+ if (strpos($value, "'") === false && !$is_in_val)
+ {
+ $vals[$i++] = $value;
+ continue;
+ }
+
+ if (substr($value, -1) === "'")
+ {
+ $vals[$i] = $string . (($is_in_val) ? ', ' : '') . $value;
+ $string = '';
+ $is_in_val = false;
+
+ if ($vals[$i][0] !== "'")
+ {
+ $vals[$i] = "''" . $vals[$i];
+ }
+ $i++;
+ continue;
+ }
+ else
+ {
+ $string .= (($is_in_val) ? ', ' : '') . $value;
+ $is_in_val = true;
+ }
+ }
+
+ if ($string)
+ {
+ // New value if cols != value
+ $vals[(sizeof($cols) !== sizeof($vals)) ? $i : $i - 1] .= $string;
+ }
+
+ $vals = array(0 => $vals);
+ }
+*/
+
+ $inserts = $vals[0];
+ unset($vals);
+
+ foreach ($inserts as $key => $value)
+ {
+ if (!empty($value) && $value[0] === "'" && strlen($value) > 4002) // check to see if this thing is greater than the max + 'x2
+ {
+ $inserts[$key] = ':' . strtoupper($cols[$key]);
+ $array[$inserts[$key]] = str_replace("''", "'", substr($value, 1, -1));
+ }
+ }
+
+ $query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
+ }
+ }
+ else if (preg_match_all('/^(UPDATE [\\w_]++\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data, PREG_SET_ORDER))
+ {
+ if (strlen($data[0][2]) > 4000)
+ {
+ $update = $data[0][1];
+ $where = $data[0][3];
+ preg_match_all('/([\\w_]++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[0][2], $temp, PREG_SET_ORDER);
+ unset($data);
+
+ $cols = array();
+ foreach ($temp as $value)
+ {
+ if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 4002) // check to see if this thing is greater than the max + 'x2
+ {
+ $cols[] = $value[1] . '=:' . strtoupper($value[1]);
+ $array[$value[1]] = str_replace("''", "'", substr($value[2], 1, -1));
+ }
+ else
+ {
+ $cols[] = $value[1] . '=' . $value[2];
+ }
+ }
+
+ $query = $update . implode(', ', $cols) . ' ' . $where;
+ unset($cols);
+ }
+ }
+ }
+
+ switch (substr($query, 0, 6))
+ {
+ case 'DELETE':
+ if (preg_match('/^(DELETE FROM [\w_]++ WHERE)((?:\s*(?:AND|OR)?\s*[\w_]+\s*(?:(?:=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]+)|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]+,? ?)*+\)))*+)$/', $query, $regs))
+ {
+ $query = $regs[1] . $this->_rewrite_where($regs[2]);
+ unset($regs);
+ }
+ break;
+
+ case 'UPDATE':
+ if (preg_match('/^(UPDATE [\\w_]++\\s+SET [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++)(?:, [\\w_]+\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]++|:\w++))*+\\s+WHERE)(.*)$/s', $query, $regs))
+ {
+ $query = $regs[1] . $this->_rewrite_where($regs[2]);
+ unset($regs);
+ }
+ break;
+
+ case 'SELECT':
+ $query = preg_replace_callback('/([\w_.]++)\s*(?:(=|<>)\s*(?>\'(?>[^\']++|\'\')*+\'|[\d-.]++|([\w_.]++))|(?:NOT )?IN\s*\((?>\'(?>[^\']++|\'\')*+\',? ?|[\d-.]++,? ?)*+\))/', array($this, '_rewrite_col_compare'), $query);
+ break;
+ }
+
+ $this->query_result = @ociparse($this->db_connect_id, $query);
+
+ foreach ($array as $key => $value)
+ {
+ @ocibindbyname($this->query_result, $key, $array[$key], -1);
+ }
+
+ $success = @ociexecute($this->query_result, OCI_DEFAULT);
+
+ if (!$success)
+ {
+ $this->sql_error($query);
+ $this->query_result = false;
+ }
+ else
+ {
+ if (!$in_transaction)
+ {
+ $this->sql_transaction('commit');
+ }
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ $query = 'SELECT * FROM (SELECT /*+ FIRST_ROWS */ rownum AS xrownum, a.* FROM (' . $query . ') a WHERE rownum <= ' . ($offset + $total) . ') WHERE xrownum >= ' . $offset;
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->query_result) ? @ocirowcount($this->query_result) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ if ($query_id !== false)
+ {
+ $row = array();
+ $result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
+
+ if (!$result || !$row)
+ {
+ return false;
+ }
+
+ $result_row = array();
+ foreach ($row as $key => $value)
+ {
+ // Oracle treats empty strings as null
+ if (is_null($value))
+ {
+ $value = '';
+ }
+
+ // OCI->CLOB?
+ if (is_object($value))
+ {
+ $value = $value->load();
+ }
+
+ $result_row[strtolower($key)] = $value;
+ }
+
+ return $result_row;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ if ($query_id === false)
+ {
+ return false;
+ }
+
+ // Reset internal pointer
+ @ociexecute($query_id, OCI_DEFAULT);
+
+ // We do not fetch the row for rownum == 0 because then the next resultset would be the second row
+ for ($i = 0; $i < $rownum; $i++)
+ {
+ if (!$this->sql_fetchrow($query_id))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ $query_id = $this->query_result;
+
+ if ($query_id !== false && $this->last_query_text != '')
+ {
+ if (preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#is', $this->last_query_text, $tablename))
+ {
+ $query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL';
+ $stmt = @ociparse($this->db_connect_id, $query);
+ @ociexecute($stmt, OCI_DEFAULT);
+
+ $temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
+ @ocifreestatement($stmt);
+
+ if ($temp_result)
+ {
+ return $temp_array['CURRVAL'];
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @ocifreestatement($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return str_replace(array("'", "\0"), array("''", ''), $msg);
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ function _sql_bit_and($column_name, $bit, $compare = '')
+ {
+ return 'BITAND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
+ }
+
+ function _sql_bit_or($column_name, $bit, $compare = '')
+ {
+ return 'BITOR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if (function_exists('ocierror'))
+ {
+ $error = @ocierror();
+ $error = (!$error) ? @ocierror($this->query_result) : $error;
+ $error = (!$error) ? @ocierror($this->db_connect_id) : $error;
+
+ if ($error)
+ {
+ $this->last_error_result = $error;
+ }
+ else
+ {
+ $error = (isset($this->last_error_result) && $this->last_error_result) ? $this->last_error_result : array();
+ }
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @ocilogoff($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+
+ $html_table = false;
+
+ // Grab a plan table, any will do
+ $sql = "SELECT table_name
+ FROM USER_TABLES
+ WHERE table_name LIKE '%PLAN_TABLE%'";
+ $stmt = ociparse($this->db_connect_id, $sql);
+ ociexecute($stmt);
+ $result = array();
+
+ if (ocifetchinto($stmt, $result, OCI_ASSOC + OCI_RETURN_NULLS))
+ {
+ $table = $result['TABLE_NAME'];
+
+ // This is the statement_id that will allow us to track the plan
+ $statement_id = substr(md5($query), 0, 30);
+
+ // Remove any stale plans
+ $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
+ ociexecute($stmt2);
+ ocifreestatement($stmt2);
+
+ // Explain the plan
+ $sql = "EXPLAIN PLAN
+ SET STATEMENT_ID = '$statement_id'
+ FOR $query";
+ $stmt2 = ociparse($this->db_connect_id, $sql);
+ ociexecute($stmt2);
+ ocifreestatement($stmt2);
+
+ // Get the data from the plan
+ $sql = "SELECT operation, options, object_name, object_type, cardinality, cost
+ FROM plan_table
+ START WITH id = 0 AND statement_id = '$statement_id'
+ CONNECT BY PRIOR id = parent_id
+ AND statement_id = '$statement_id'";
+ $stmt2 = ociparse($this->db_connect_id, $sql);
+ ociexecute($stmt2);
+
+ $row = array();
+ while (ocifetchinto($stmt2, $row, OCI_ASSOC + OCI_RETURN_NULLS))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+
+ ocifreestatement($stmt2);
+
+ // Remove the plan we just made, we delete them on request anyway
+ $stmt2 = ociparse($this->db_connect_id, "DELETE FROM $table WHERE statement_id='$statement_id'");
+ ociexecute($stmt2);
+ ocifreestatement($stmt2);
+ }
+
+ ocifreestatement($stmt);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @ociparse($this->db_connect_id, $query);
+ $success = @ociexecute($result, OCI_DEFAULT);
+ $row = array();
+
+ while (@ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @ocifreestatement($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/postgres.php b/phpBB/phpbb/db/driver/postgres.php
new file mode 100644
index 0000000000..a3b9aa4c6b
--- /dev/null
+++ b/phpBB/phpbb/db/driver/postgres.php
@@ -0,0 +1,490 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* PostgreSQL Database Abstraction Layer
+* Minimum Requirement is Version 8.3+
+*/
+class postgres extends \phpbb\db\driver\driver
+{
+ var $multi_insert = true;
+ var $last_query_text = '';
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $connect_string = '';
+
+ if ($sqluser)
+ {
+ $connect_string .= "user=$sqluser ";
+ }
+
+ if ($sqlpassword)
+ {
+ $connect_string .= "password=$sqlpassword ";
+ }
+
+ if ($sqlserver)
+ {
+ // $sqlserver can carry a port separated by : for compatibility reasons
+ // If $sqlserver has more than one : it's probably an IPv6 address.
+ // In this case we only allow passing a port via the $port variable.
+ if (substr_count($sqlserver, ':') === 1)
+ {
+ list($sqlserver, $port) = explode(':', $sqlserver);
+ }
+
+ if ($sqlserver !== 'localhost')
+ {
+ $connect_string .= "host=$sqlserver ";
+ }
+
+ if ($port)
+ {
+ $connect_string .= "port=$port ";
+ }
+ }
+
+ $schema = '';
+
+ if ($database)
+ {
+ $this->dbname = $database;
+ if (strpos($database, '.') !== false)
+ {
+ list($database, $schema) = explode('.', $database);
+ }
+ $connect_string .= "dbname=$database";
+ }
+
+ $this->persistency = $persistency;
+
+ if ($this->persistency)
+ {
+ if (!function_exists('pg_pconnect'))
+ {
+ $this->connect_error = 'pg_pconnect function does not exist, is pgsql extension installed?';
+ return $this->sql_error('');
+ }
+ $collector = new \phpbb\error_collector;
+ $collector->install();
+ $this->db_connect_id = (!$new_link) ? @pg_pconnect($connect_string) : @pg_pconnect($connect_string, PGSQL_CONNECT_FORCE_NEW);
+ }
+ else
+ {
+ if (!function_exists('pg_connect'))
+ {
+ $this->connect_error = 'pg_connect function does not exist, is pgsql extension installed?';
+ return $this->sql_error('');
+ }
+ $collector = new \phpbb\error_collector;
+ $collector->install();
+ $this->db_connect_id = (!$new_link) ? @pg_connect($connect_string) : @pg_connect($connect_string, PGSQL_CONNECT_FORCE_NEW);
+ }
+
+ $collector->uninstall();
+
+ if ($this->db_connect_id)
+ {
+ if ($schema !== '')
+ {
+ @pg_query($this->db_connect_id, 'SET search_path TO ' . $schema);
+ }
+ return $this->db_connect_id;
+ }
+
+ $this->connect_error = $collector->format_errors();
+ return $this->sql_error('');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('pgsql_version')) === false)
+ {
+ $query_id = @pg_query($this->db_connect_id, 'SELECT VERSION() AS version');
+ $row = @pg_fetch_assoc($query_id, null);
+ @pg_free_result($query_id);
+
+ $this->sql_server_version = (!empty($row['version'])) ? trim(substr($row['version'], 10)) : 0;
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('pgsql_version', $this->sql_server_version);
+ }
+ }
+
+ return ($raw) ? $this->sql_server_version : 'PostgreSQL ' . $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @pg_query($this->db_connect_id, 'BEGIN');
+ break;
+
+ case 'commit':
+ return @pg_query($this->db_connect_id, 'COMMIT');
+ break;
+
+ case 'rollback':
+ return @pg_query($this->db_connect_id, 'ROLLBACK');
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @pg_query($this->db_connect_id, $query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = 'ALL';
+ }
+
+ $query .= "\n LIMIT $total OFFSET $offset";
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id !== false) ? @pg_fetch_assoc($query_id, null) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ return ($query_id !== false) ? @pg_result_seek($query_id, $rownum) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ $query_id = $this->query_result;
+
+ if ($query_id !== false && $this->last_query_text != '')
+ {
+ if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename))
+ {
+ $query = "SELECT currval('" . $tablename[1] . "_seq') AS last_value";
+ $temp_q_id = @pg_query($this->db_connect_id, $query);
+
+ if (!$temp_q_id)
+ {
+ return false;
+ }
+
+ $temp_result = @pg_fetch_assoc($temp_q_id, null);
+ @pg_free_result($query_id);
+
+ return ($temp_result) ? $temp_result['last_value'] : false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if (isset($this->open_queries[(int) $query_id]))
+ {
+ unset($this->open_queries[(int) $query_id]);
+ return @pg_free_result($query_id);
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return @pg_escape_string($msg);
+ }
+
+ /**
+ * Build LIKE expression
+ * @access private
+ */
+ function _sql_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function cast_expr_to_bigint($expression)
+ {
+ return 'CAST(' . $expression . ' as DECIMAL(255, 0))';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function cast_expr_to_string($expression)
+ {
+ return 'CAST(' . $expression . ' as VARCHAR(255))';
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ // pg_last_error only works when there is an established connection.
+ // Connection errors have to be tracked by us manually.
+ if ($this->db_connect_id)
+ {
+ $message = @pg_last_error($this->db_connect_id);
+ }
+ else
+ {
+ $message = $this->connect_error;
+ }
+
+ return array(
+ 'message' => $message,
+ 'code' => ''
+ );
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @pg_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = false;
+
+ if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query"))
+ {
+ while ($row = @pg_fetch_assoc($result, null))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+ @pg_free_result($result);
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+ }
+
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @pg_query($this->db_connect_id, $query);
+ while ($void = @pg_fetch_assoc($result, null))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @pg_free_result($result);
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php
new file mode 100644
index 0000000000..d5da0e2438
--- /dev/null
+++ b/phpBB/phpbb/db/driver/sqlite.php
@@ -0,0 +1,378 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* Sqlite Database Abstraction Layer
+* Minimum Requirement: 2.8.2+
+*/
+class sqlite extends \phpbb\db\driver\driver
+{
+ var $connect_error = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $this->persistency = $persistency;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ $error = '';
+ if ($this->persistency)
+ {
+ if (!function_exists('sqlite_popen'))
+ {
+ $this->connect_error = 'sqlite_popen function does not exist, is sqlite extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @sqlite_popen($this->server, 0666, $error);
+ }
+ else
+ {
+ if (!function_exists('sqlite_open'))
+ {
+ $this->connect_error = 'sqlite_open function does not exist, is sqlite extension installed?';
+ return $this->sql_error('');
+ }
+ $this->db_connect_id = @sqlite_open($this->server, 0666, $error);
+ }
+
+ if ($this->db_connect_id)
+ {
+ @sqlite_query('PRAGMA short_column_names = 1', $this->db_connect_id);
+// @sqlite_query('PRAGMA encoding = "UTF-8"', $this->db_connect_id);
+ }
+
+ return ($this->db_connect_id) ? true : array('message' => $error);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false)
+ {
+ $result = @sqlite_query('SELECT sqlite_version() AS version', $this->db_connect_id);
+ $row = @sqlite_fetch_array($result, SQLITE_ASSOC);
+
+ $this->sql_server_version = (!empty($row['version'])) ? $row['version'] : 0;
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('sqlite_version', $this->sql_server_version);
+ }
+ }
+
+ return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ * @access private
+ */
+ function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return @sqlite_query('BEGIN', $this->db_connect_id);
+ break;
+
+ case 'commit':
+ return @sqlite_query('COMMIT', $this->db_connect_id);
+ break;
+
+ case 'rollback':
+ return @sqlite_query('ROLLBACK', $this->db_connect_id);
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @sqlite_query($query, $this->db_connect_id)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+ {
+ $this->open_queries[(int) $this->query_result] = $this->query_result;
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ */
+ function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = -1;
+ }
+
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? @sqlite_changes($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return ($query_id !== false) ? @sqlite_fetch_array($query_id, SQLITE_ASSOC) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, &$query_id)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_rowseek($rownum, $query_id);
+ }
+
+ return ($query_id !== false) ? @sqlite_seek($query_id, $rownum) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_nextid()
+ {
+ return ($this->db_connect_id) ? @sqlite_last_insert_rowid($this->db_connect_id) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_escape($msg)
+ {
+ return @sqlite_escape_string($msg);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For SQLite an underscore is a not-known character... this may change with SQLite3
+ */
+ function sql_like_expression($expression)
+ {
+ // Unlike LIKE, GLOB is unfortunately case sensitive.
+ // We only catch * and ? here, not the character map possible on file globbing.
+ $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
+
+ $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
+ $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
+
+ return 'GLOB \'' . $this->sql_escape($expression) . '\'';
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For SQLite an underscore is a not-known character...
+ */
+ function sql_not_like_expression($expression)
+ {
+ // Unlike NOT LIKE, NOT GLOB is unfortunately case sensitive.
+ // We only catch * and ? here, not the character map possible on file globbing.
+ $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
+
+ $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
+ $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
+
+ return 'NOT GLOB \'' . $this->sql_escape($expression) . '\'';
+ }
+
+ /**
+ * return sql error array
+ * @access private
+ */
+ function _sql_error()
+ {
+ if (function_exists('sqlite_error_string'))
+ {
+ $error = array(
+ 'message' => @sqlite_error_string(@sqlite_last_error($this->db_connect_id)),
+ 'code' => @sqlite_last_error($this->db_connect_id),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Build db-specific query data
+ * @access private
+ */
+ function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ /**
+ * Close sql connection
+ * @access private
+ */
+ function _sql_close()
+ {
+ return @sqlite_close($this->db_connect_id);
+ }
+
+ /**
+ * Build db-specific report
+ * @access private
+ */
+ function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = @sqlite_query($query, $this->db_connect_id);
+ while ($void = @sqlite_fetch_array($result, SQLITE_ASSOC))
+ {
+ // Take the time spent on parsing rows into account
+ }
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php
new file mode 100644
index 0000000000..cc3352af34
--- /dev/null
+++ b/phpBB/phpbb/db/driver/sqlite3.php
@@ -0,0 +1,405 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\driver;
+
+/**
+* SQLite3 Database Abstraction Layer
+* Minimum Requirement: 3.6.15+
+*/
+class sqlite3 extends \phpbb\db\driver\driver
+{
+ /**
+ * @var string Stores errors during connection setup in case the driver is not available
+ */
+ protected $connect_error = '';
+
+ /**
+ * @var \SQLite3 The SQLite3 database object to operate against
+ */
+ protected $dbo = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
+ {
+ $this->persistency = false;
+ $this->user = $sqluser;
+ $this->server = $sqlserver . (($port) ? ':' . $port : '');
+ $this->dbname = $database;
+
+ if (!class_exists('SQLite3', false))
+ {
+ $this->connect_error = 'SQLite3 not found, is the extension installed?';
+ return $this->sql_error('');
+ }
+
+ try
+ {
+ $this->dbo = new \SQLite3($this->server, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE);
+ $this->dbo->busyTimeout(60000);
+ $this->db_connect_id = true;
+ }
+ catch (\Exception $e)
+ {
+ $this->connect_error = $e->getMessage();
+ return array('message' => $this->connect_error);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_server_info($raw = false, $use_cache = true)
+ {
+ global $cache;
+
+ if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('sqlite_version')) === false)
+ {
+ $version = \SQLite3::version();
+
+ $this->sql_server_version = $version['versionString'];
+
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('sqlite_version', $this->sql_server_version);
+ }
+ }
+
+ return ($raw) ? $this->sql_server_version : 'SQLite ' . $this->sql_server_version;
+ }
+
+ /**
+ * SQL Transaction
+ *
+ * @param string $status Should be one of the following strings:
+ * begin, commit, rollback
+ * @return bool Success/failure of the transaction query
+ */
+ protected function _sql_transaction($status = 'begin')
+ {
+ switch ($status)
+ {
+ case 'begin':
+ return $this->dbo->exec('BEGIN IMMEDIATE');
+ break;
+
+ case 'commit':
+ return $this->dbo->exec('COMMIT');
+ break;
+
+ case 'rollback':
+ return $this->dbo->exec('ROLLBACK');
+ break;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_query($query = '', $cache_ttl = 0)
+ {
+ if ($query != '')
+ {
+ global $cache;
+
+ // EXPLAIN only in extra debug mode
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('start', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->curtime = microtime(true);
+ }
+
+ $this->last_query_text = $query;
+ $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false;
+ $this->sql_add_num_queries($this->query_result);
+
+ if ($this->query_result === false)
+ {
+ if (($this->query_result = @$this->dbo->query($query)) === false)
+ {
+ $this->sql_error($query);
+ }
+
+ if (defined('DEBUG'))
+ {
+ $this->sql_report('stop', $query);
+ }
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if ($cache && $cache_ttl)
+ {
+ $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ }
+ }
+ else if (defined('DEBUG'))
+ {
+ $this->sql_report('fromcache', $query);
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return $this->query_result;
+ }
+
+ /**
+ * Build LIMIT query
+ *
+ * @param string $query The SQL query to execute
+ * @param int $total The number of rows to select
+ * @param int $offset
+ * @param int $cache_ttl Either 0 to avoid caching or
+ * the time in seconds which the result shall be kept in cache
+ * @return mixed Buffered, seekable result handle, false on error
+ */
+ protected function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
+ {
+ $this->query_result = false;
+
+ // if $total is set to 0 we do not want to limit the number of rows
+ if ($total == 0)
+ {
+ $total = -1;
+ }
+
+ $query .= "\n LIMIT " . ((!empty($offset)) ? $offset . ', ' . $total : $total);
+
+ return $this->sql_query($query, $cache_ttl);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_affectedrows()
+ {
+ return ($this->db_connect_id) ? $this->dbo->changes() : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_fetchrow($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_fetchrow($query_id);
+ }
+
+ return is_object($query_id) ? $query_id->fetchArray(SQLITE3_ASSOC) : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_nextid()
+ {
+ return ($this->db_connect_id) ? $this->dbo->lastInsertRowID() : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_freeresult($query_id = false)
+ {
+ global $cache;
+
+ if ($query_id === false)
+ {
+ $query_id = $this->query_result;
+ }
+
+ if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
+ {
+ return $cache->sql_freeresult($query_id);
+ }
+
+ if ($query_id)
+ {
+ return @$query_id->finalize();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function sql_escape($msg)
+ {
+ return \SQLite3::escapeString($msg);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For SQLite an underscore is an unknown character.
+ */
+ public function sql_like_expression($expression)
+ {
+ // Unlike LIKE, GLOB is unfortunately case sensitive.
+ // We only catch * and ? here, not the character map possible on file globbing.
+ $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
+
+ $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
+ $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
+
+ return 'GLOB \'' . $this->sql_escape($expression) . '\'';
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For SQLite an underscore is an unknown character.
+ */
+ public function sql_not_like_expression($expression)
+ {
+ // Unlike NOT LIKE, NOT GLOB is unfortunately case sensitive
+ // We only catch * and ? here, not the character map possible on file globbing.
+ $expression = str_replace(array(chr(0) . '_', chr(0) . '%'), array(chr(0) . '?', chr(0) . '*'), $expression);
+
+ $expression = str_replace(array('?', '*'), array("\?", "\*"), $expression);
+ $expression = str_replace(array(chr(0) . "\?", chr(0) . "\*"), array('?', '*'), $expression);
+
+ return 'NOT GLOB \'' . $this->sql_escape($expression) . '\'';
+ }
+
+ /**
+ * return sql error array
+ *
+ * @return array
+ */
+ protected function _sql_error()
+ {
+ if (class_exists('SQLite3', false) && isset($this->dbo))
+ {
+ $error = array(
+ 'message' => $this->dbo->lastErrorMsg(),
+ 'code' => $this->dbo->lastErrorCode(),
+ );
+ }
+ else
+ {
+ $error = array(
+ 'message' => $this->connect_error,
+ 'code' => '',
+ );
+ }
+
+ return $error;
+ }
+
+ /**
+ * Build db-specific query data
+ *
+ * @param string $stage Available stages: FROM, WHERE
+ * @param mixed $data A string containing the CROSS JOIN query or an array of WHERE clauses
+ *
+ * @return string The db-specific query fragment
+ */
+ protected function _sql_custom_build($stage, $data)
+ {
+ return $data;
+ }
+
+ /**
+ * Close sql connection
+ *
+ * @return bool False if failure
+ */
+ protected function _sql_close()
+ {
+ return $this->dbo->close();
+ }
+
+ /**
+ * Build db-specific report
+ *
+ * @param string $mode Available modes: display, start, stop,
+ * add_select_row, fromcache, record_fromcache
+ * @param string $query The Query that should be explained
+ * @return mixed Either a full HTML page, boolean or null
+ */
+ protected function _sql_report($mode, $query = '')
+ {
+ switch ($mode)
+ {
+ case 'start':
+
+ $explain_query = $query;
+ if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+ else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
+ {
+ $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
+ }
+
+ if (preg_match('/^SELECT/', $explain_query))
+ {
+ $html_table = false;
+
+ if ($result = $this->dbo->query("EXPLAIN QUERY PLAN $explain_query"))
+ {
+ while ($row = $result->fetchArray(SQLITE3_ASSOC))
+ {
+ $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
+ }
+ }
+
+ if ($html_table)
+ {
+ $this->html_hold .= '</table>';
+ }
+ }
+
+ break;
+
+ case 'fromcache':
+ $endtime = explode(' ', microtime());
+ $endtime = $endtime[0] + $endtime[1];
+
+ $result = $this->dbo->query($query);
+ while ($void = $result->fetchArray(SQLITE3_ASSOC))
+ {
+ // Take the time spent on parsing rows into account
+ }
+
+ $splittime = explode(' ', microtime());
+ $splittime = $splittime[0] + $splittime[1];
+
+ $this->sql_report('record_fromcache', $query, $endtime, $splittime);
+
+ break;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/html_migrator_output_handler.php b/phpBB/phpbb/db/html_migrator_output_handler.php
new file mode 100644
index 0000000000..e37c667463
--- /dev/null
+++ b/phpBB/phpbb/db/html_migrator_output_handler.php
@@ -0,0 +1,48 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+use phpbb\user;
+
+class html_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * User object.
+ *
+ * @var user
+ */
+ private $user;
+
+ /**
+ * Constructor
+ *
+ * @param user $user User object
+ */
+ public function __construct(user $user)
+ {
+ $this->user = $user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ if ($verbosity <= migrator_output_handler_interface::VERBOSITY_VERBOSE)
+ {
+ $final_message = call_user_func_array(array($this->user, 'lang'), $message);
+ echo $final_message . "<br />\n";
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php b/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php
new file mode 100644
index 0000000000..94c293dc45
--- /dev/null
+++ b/phpBB/phpbb/db/log_wrapper_migrator_output_handler.php
@@ -0,0 +1,95 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+use phpbb\user;
+
+class log_wrapper_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * User object.
+ *
+ * @var user
+ */
+ protected $user;
+
+ /**
+ * A migrator output handler
+ *
+ * @var migrator_output_handler_interface
+ */
+ protected $migrator;
+
+ /**
+ * Log file handle
+ * @var resource
+ */
+ protected $file_handle = false;
+
+ /**
+ * Constructor
+ *
+ * @param user $user User object
+ * @param migrator_output_handler_interface $migrator Migrator output handler
+ * @param string $log_file File to log to
+ */
+ public function __construct(user $user, migrator_output_handler_interface $migrator, $log_file)
+ {
+ $this->user = $user;
+ $this->migrator = $migrator;
+ $this->file_open($log_file);
+ }
+
+ /**
+ * Open file for logging
+ *
+ * @param string $file File to open
+ */
+ protected function file_open($file)
+ {
+ if (phpbb_is_writable(dirname($file)))
+ {
+ $this->file_handle = fopen($file, 'w');
+ }
+ else
+ {
+ throw new \RuntimeException('Unable to write to migrator log file');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ $this->migrator->write($message, $verbosity);
+
+ if ($this->file_handle !== false)
+ {
+ $translated_message = call_user_func_array(array($this->user, 'lang'), $message) . "\n";
+
+ if ($verbosity <= migrator_output_handler_interface::VERBOSITY_NORMAL)
+ {
+ $translated_message = '[INFO] ' . $translated_message;
+ }
+ else
+ {
+ $translated_message = '[DEBUG] ' . $translated_message;
+ }
+
+ fwrite($this->file_handle, $translated_message);
+ fflush($this->file_handle);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/container_aware_migration.php b/phpBB/phpbb/db/migration/container_aware_migration.php
new file mode 100644
index 0000000000..3b4b49b04b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/container_aware_migration.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+* Abstract base class for container aware database migrations.
+*/
+abstract class container_aware_migration extends migration implements ContainerAwareInterface
+{
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setContainer(ContainerInterface $container = null)
+ {
+ $this->container = $container;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/.htaccess b/phpBB/phpbb/db/migration/data/v30x/.htaccess
new file mode 100644
index 0000000000..44242b5418
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/.htaccess
@@ -0,0 +1,33 @@
+# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from
+# module mod_authz_host to a new module called mod_access_compat (which may be
+# disabled) and a new "Require" syntax has been introduced to mod_authz_host.
+# We could just conditionally provide both versions, but unfortunately Apache
+# does not explicitly tell us its version if the module mod_version is not
+# available. In this case, we check for the availability of module
+# mod_authz_core (which should be on 2.4 or higher only) as a best guess.
+<IfModule mod_version.c>
+ <IfVersion < 2.4>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfVersion>
+ <IfVersion >= 2.4>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfVersion>
+</IfModule>
+<IfModule !mod_version.c>
+ <IfModule !mod_authz_core.c>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfModule>
+ <IfModule mod_authz_core.c>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfModule>
+</IfModule>
diff --git a/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php
new file mode 100644
index 0000000000..648ae9ce96
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php
@@ -0,0 +1,70 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class local_url_bbcode extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_local_url_bbcode'))),
+ );
+ }
+
+ /**
+ * Update BBCodes that currently use the LOCAL_URL tag
+ *
+ * To fix http://tracker.phpbb.com/browse/PHPBB3-8319 we changed
+ * the second_pass_replace value, so that needs updating for existing ones
+ */
+ public function update_local_url_bbcode()
+ {
+ $sql = 'SELECT *
+ FROM ' . BBCODES_TABLE . '
+ WHERE bbcode_match ' . $this->db->sql_like_expression($this->db->get_any_char() . 'LOCAL_URL' . $this->db->get_any_char());
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!class_exists('acp_bbcodes'))
+ {
+ if (function_exists('phpbb_require_updated'))
+ {
+ phpbb_require_updated('includes/acp/acp_bbcodes.' . $this->php_ext);
+ }
+ else
+ {
+ require($this->phpbb_root_path . 'includes/acp/acp_bbcodes.' . $this->php_ext);
+ }
+ }
+
+ $bbcode_match = $row['bbcode_match'];
+ $bbcode_tpl = $row['bbcode_tpl'];
+
+ $acp_bbcodes = new \acp_bbcodes();
+ $sql_ary = $acp_bbcodes->build_regexp($bbcode_match, $bbcode_tpl);
+
+ $sql = 'UPDATE ' . BBCODES_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE bbcode_id = ' . (int) $row['bbcode_id'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php
new file mode 100644
index 0000000000..26937d6d80
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_0.php
@@ -0,0 +1,1181 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_0 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.0', '>=');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'attachments' => array(
+ 'COLUMNS' => array(
+ 'attach_id' => array('UINT', NULL, 'auto_increment'),
+ 'post_msg_id' => array('UINT', 0),
+ 'topic_id' => array('UINT', 0),
+ 'in_message' => array('BOOL', 0),
+ 'poster_id' => array('UINT', 0),
+ 'is_orphan' => array('BOOL', 1),
+ 'physical_filename' => array('VCHAR', ''),
+ 'real_filename' => array('VCHAR', ''),
+ 'download_count' => array('UINT', 0),
+ 'attach_comment' => array('TEXT_UNI', ''),
+ 'extension' => array('VCHAR:100', ''),
+ 'mimetype' => array('VCHAR:100', ''),
+ 'filesize' => array('UINT:20', 0),
+ 'filetime' => array('TIMESTAMP', 0),
+ 'thumbnail' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'attach_id',
+ 'KEYS' => array(
+ 'filetime' => array('INDEX', 'filetime'),
+ 'post_msg_id' => array('INDEX', 'post_msg_id'),
+ 'topic_id' => array('INDEX', 'topic_id'),
+ 'poster_id' => array('INDEX', 'poster_id'),
+ 'is_orphan' => array('INDEX', 'is_orphan'),
+ ),
+ ),
+
+ $this->table_prefix . 'acl_groups' => array(
+ 'COLUMNS' => array(
+ 'group_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'auth_option_id' => array('UINT', 0),
+ 'auth_role_id' => array('UINT', 0),
+ 'auth_setting' => array('TINT:2', 0),
+ ),
+ 'KEYS' => array(
+ 'group_id' => array('INDEX', 'group_id'),
+ 'auth_opt_id' => array('INDEX', 'auth_option_id'),
+ 'auth_role_id' => array('INDEX', 'auth_role_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'acl_options' => array(
+ 'COLUMNS' => array(
+ 'auth_option_id' => array('UINT', NULL, 'auto_increment'),
+ 'auth_option' => array('VCHAR:50', ''),
+ 'is_global' => array('BOOL', 0),
+ 'is_local' => array('BOOL', 0),
+ 'founder_only' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'auth_option_id',
+ 'KEYS' => array(
+ 'auth_option' => array('INDEX', 'auth_option'),
+ ),
+ ),
+
+ $this->table_prefix . 'acl_roles' => array(
+ 'COLUMNS' => array(
+ 'role_id' => array('UINT', NULL, 'auto_increment'),
+ 'role_name' => array('VCHAR_UNI', ''),
+ 'role_description' => array('TEXT_UNI', ''),
+ 'role_type' => array('VCHAR:10', ''),
+ 'role_order' => array('USINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'role_id',
+ 'KEYS' => array(
+ 'role_type' => array('INDEX', 'role_type'),
+ 'role_order' => array('INDEX', 'role_order'),
+ ),
+ ),
+
+ $this->table_prefix . 'acl_roles_data' => array(
+ 'COLUMNS' => array(
+ 'role_id' => array('UINT', 0),
+ 'auth_option_id' => array('UINT', 0),
+ 'auth_setting' => array('TINT:2', 0),
+ ),
+ 'PRIMARY_KEY' => array('role_id', 'auth_option_id'),
+ 'KEYS' => array(
+ 'ath_op_id' => array('INDEX', 'auth_option_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'acl_users' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'auth_option_id' => array('UINT', 0),
+ 'auth_role_id' => array('UINT', 0),
+ 'auth_setting' => array('TINT:2', 0),
+ ),
+ 'KEYS' => array(
+ 'user_id' => array('INDEX', 'user_id'),
+ 'auth_option_id' => array('INDEX', 'auth_option_id'),
+ 'auth_role_id' => array('INDEX', 'auth_role_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'banlist' => array(
+ 'COLUMNS' => array(
+ 'ban_id' => array('UINT', NULL, 'auto_increment'),
+ 'ban_userid' => array('UINT', 0),
+ 'ban_ip' => array('VCHAR:40', ''),
+ 'ban_email' => array('VCHAR_UNI:100', ''),
+ 'ban_start' => array('TIMESTAMP', 0),
+ 'ban_end' => array('TIMESTAMP', 0),
+ 'ban_exclude' => array('BOOL', 0),
+ 'ban_reason' => array('VCHAR_UNI', ''),
+ 'ban_give_reason' => array('VCHAR_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'ban_id',
+ 'KEYS' => array(
+ 'ban_end' => array('INDEX', 'ban_end'),
+ 'ban_user' => array('INDEX', array('ban_userid', 'ban_exclude')),
+ 'ban_email' => array('INDEX', array('ban_email', 'ban_exclude')),
+ 'ban_ip' => array('INDEX', array('ban_ip', 'ban_exclude')),
+ ),
+ ),
+
+ $this->table_prefix . 'bbcodes' => array(
+ 'COLUMNS' => array(
+ 'bbcode_id' => array('TINT:3', 0),
+ 'bbcode_tag' => array('VCHAR:16', ''),
+ 'bbcode_helpline' => array('VCHAR_UNI', ''),
+ 'display_on_posting' => array('BOOL', 0),
+ 'bbcode_match' => array('TEXT_UNI', ''),
+ 'bbcode_tpl' => array('MTEXT_UNI', ''),
+ 'first_pass_match' => array('MTEXT_UNI', ''),
+ 'first_pass_replace' => array('MTEXT_UNI', ''),
+ 'second_pass_match' => array('MTEXT_UNI', ''),
+ 'second_pass_replace' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'bbcode_id',
+ 'KEYS' => array(
+ 'display_on_post' => array('INDEX', 'display_on_posting'),
+ ),
+ ),
+
+ $this->table_prefix . 'bookmarks' => array(
+ 'COLUMNS' => array(
+ 'topic_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => array('topic_id', 'user_id'),
+ ),
+
+ $this->table_prefix . 'bots' => array(
+ 'COLUMNS' => array(
+ 'bot_id' => array('UINT', NULL, 'auto_increment'),
+ 'bot_active' => array('BOOL', 1),
+ 'bot_name' => array('STEXT_UNI', ''),
+ 'user_id' => array('UINT', 0),
+ 'bot_agent' => array('VCHAR', ''),
+ 'bot_ip' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'bot_id',
+ 'KEYS' => array(
+ 'bot_active' => array('INDEX', 'bot_active'),
+ ),
+ ),
+
+ $this->table_prefix . 'config' => array(
+ 'COLUMNS' => array(
+ 'config_name' => array('VCHAR', ''),
+ 'config_value' => array('VCHAR_UNI', ''),
+ 'is_dynamic' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'config_name',
+ 'KEYS' => array(
+ 'is_dynamic' => array('INDEX', 'is_dynamic'),
+ ),
+ ),
+
+ $this->table_prefix . 'confirm' => array(
+ 'COLUMNS' => array(
+ 'confirm_id' => array('CHAR:32', ''),
+ 'session_id' => array('CHAR:32', ''),
+ 'confirm_type' => array('TINT:3', 0),
+ 'code' => array('VCHAR:8', ''),
+ 'seed' => array('UINT:10', 0),
+ ),
+ 'PRIMARY_KEY' => array('session_id', 'confirm_id'),
+ 'KEYS' => array(
+ 'confirm_type' => array('INDEX', 'confirm_type'),
+ ),
+ ),
+
+ $this->table_prefix . 'disallow' => array(
+ 'COLUMNS' => array(
+ 'disallow_id' => array('UINT', NULL, 'auto_increment'),
+ 'disallow_username' => array('VCHAR_UNI:255', ''),
+ ),
+ 'PRIMARY_KEY' => 'disallow_id',
+ ),
+
+ $this->table_prefix . 'drafts' => array(
+ 'COLUMNS' => array(
+ 'draft_id' => array('UINT', NULL, 'auto_increment'),
+ 'user_id' => array('UINT', 0),
+ 'topic_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'save_time' => array('TIMESTAMP', 0),
+ 'draft_subject' => array('XSTEXT_UNI', ''),
+ 'draft_message' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'draft_id',
+ 'KEYS' => array(
+ 'save_time' => array('INDEX', 'save_time'),
+ ),
+ ),
+
+ $this->table_prefix . 'extensions' => array(
+ 'COLUMNS' => array(
+ 'extension_id' => array('UINT', NULL, 'auto_increment'),
+ 'group_id' => array('UINT', 0),
+ 'extension' => array('VCHAR:100', ''),
+ ),
+ 'PRIMARY_KEY' => 'extension_id',
+ ),
+
+ $this->table_prefix . 'extension_groups' => array(
+ 'COLUMNS' => array(
+ 'group_id' => array('UINT', NULL, 'auto_increment'),
+ 'group_name' => array('VCHAR_UNI', ''),
+ 'cat_id' => array('TINT:2', 0),
+ 'allow_group' => array('BOOL', 0),
+ 'download_mode' => array('BOOL', 1),
+ 'upload_icon' => array('VCHAR', ''),
+ 'max_filesize' => array('UINT:20', 0),
+ 'allowed_forums' => array('TEXT', ''),
+ 'allow_in_pm' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'group_id',
+ ),
+
+ $this->table_prefix . 'forums' => array(
+ 'COLUMNS' => array(
+ 'forum_id' => array('UINT', NULL, 'auto_increment'),
+ 'parent_id' => array('UINT', 0),
+ 'left_id' => array('UINT', 0),
+ 'right_id' => array('UINT', 0),
+ 'forum_parents' => array('MTEXT', ''),
+ 'forum_name' => array('STEXT_UNI', ''),
+ 'forum_desc' => array('TEXT_UNI', ''),
+ 'forum_desc_bitfield' => array('VCHAR:255', ''),
+ 'forum_desc_options' => array('UINT:11', 7),
+ 'forum_desc_uid' => array('VCHAR:8', ''),
+ 'forum_link' => array('VCHAR_UNI', ''),
+ 'forum_password' => array('VCHAR_UNI:40', ''),
+ 'forum_style' => array('USINT', 0),
+ 'forum_image' => array('VCHAR', ''),
+ 'forum_rules' => array('TEXT_UNI', ''),
+ 'forum_rules_link' => array('VCHAR_UNI', ''),
+ 'forum_rules_bitfield' => array('VCHAR:255', ''),
+ 'forum_rules_options' => array('UINT:11', 7),
+ 'forum_rules_uid' => array('VCHAR:8', ''),
+ 'forum_topics_per_page' => array('TINT:4', 0),
+ 'forum_type' => array('TINT:4', 0),
+ 'forum_status' => array('TINT:4', 0),
+ 'forum_posts' => array('UINT', 0),
+ 'forum_topics' => array('UINT', 0),
+ 'forum_topics_real' => array('UINT', 0),
+ 'forum_last_post_id' => array('UINT', 0),
+ 'forum_last_poster_id' => array('UINT', 0),
+ 'forum_last_post_subject' => array('XSTEXT_UNI', ''),
+ 'forum_last_post_time' => array('TIMESTAMP', 0),
+ 'forum_last_poster_name'=> array('VCHAR_UNI', ''),
+ 'forum_last_poster_colour'=> array('VCHAR:6', ''),
+ 'forum_flags' => array('TINT:4', 32),
+ 'display_on_index' => array('BOOL', 1),
+ 'enable_indexing' => array('BOOL', 1),
+ 'enable_icons' => array('BOOL', 1),
+ 'enable_prune' => array('BOOL', 0),
+ 'prune_next' => array('TIMESTAMP', 0),
+ 'prune_days' => array('UINT', 0),
+ 'prune_viewed' => array('UINT', 0),
+ 'prune_freq' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'forum_id',
+ 'KEYS' => array(
+ 'left_right_id' => array('INDEX', array('left_id', 'right_id')),
+ 'forum_lastpost_id' => array('INDEX', 'forum_last_post_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'forums_access' => array(
+ 'COLUMNS' => array(
+ 'forum_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'session_id' => array('CHAR:32', ''),
+ ),
+ 'PRIMARY_KEY' => array('forum_id', 'user_id', 'session_id'),
+ ),
+
+ $this->table_prefix . 'forums_track' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'mark_time' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => array('user_id', 'forum_id'),
+ ),
+
+ $this->table_prefix . 'forums_watch' => array(
+ 'COLUMNS' => array(
+ 'forum_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notify_status' => array('BOOL', 0),
+ ),
+ 'KEYS' => array(
+ 'forum_id' => array('INDEX', 'forum_id'),
+ 'user_id' => array('INDEX', 'user_id'),
+ 'notify_stat' => array('INDEX', 'notify_status'),
+ ),
+ ),
+
+ $this->table_prefix . 'groups' => array(
+ 'COLUMNS' => array(
+ 'group_id' => array('UINT', NULL, 'auto_increment'),
+ 'group_type' => array('TINT:4', 1),
+ 'group_founder_manage' => array('BOOL', 0),
+ 'group_name' => array('VCHAR_CI', ''),
+ 'group_desc' => array('TEXT_UNI', ''),
+ 'group_desc_bitfield' => array('VCHAR:255', ''),
+ 'group_desc_options' => array('UINT:11', 7),
+ 'group_desc_uid' => array('VCHAR:8', ''),
+ 'group_display' => array('BOOL', 0),
+ 'group_avatar' => array('VCHAR', ''),
+ 'group_avatar_type' => array('TINT:2', 0),
+ 'group_avatar_width' => array('USINT', 0),
+ 'group_avatar_height' => array('USINT', 0),
+ 'group_rank' => array('UINT', 0),
+ 'group_colour' => array('VCHAR:6', ''),
+ 'group_sig_chars' => array('UINT', 0),
+ 'group_receive_pm' => array('BOOL', 0),
+ 'group_message_limit' => array('UINT', 0),
+ 'group_legend' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => 'group_id',
+ 'KEYS' => array(
+ 'group_legend' => array('INDEX', 'group_legend'),
+ ),
+ ),
+
+ $this->table_prefix . 'icons' => array(
+ 'COLUMNS' => array(
+ 'icons_id' => array('UINT', NULL, 'auto_increment'),
+ 'icons_url' => array('VCHAR', ''),
+ 'icons_width' => array('TINT:4', 0),
+ 'icons_height' => array('TINT:4', 0),
+ 'icons_order' => array('UINT', 0),
+ 'display_on_posting' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => 'icons_id',
+ 'KEYS' => array(
+ 'display_on_posting' => array('INDEX', 'display_on_posting'),
+ ),
+ ),
+
+ $this->table_prefix . 'lang' => array(
+ 'COLUMNS' => array(
+ 'lang_id' => array('TINT:4', NULL, 'auto_increment'),
+ 'lang_iso' => array('VCHAR:30', ''),
+ 'lang_dir' => array('VCHAR:30', ''),
+ 'lang_english_name' => array('VCHAR_UNI:100', ''),
+ 'lang_local_name' => array('VCHAR_UNI:255', ''),
+ 'lang_author' => array('VCHAR_UNI:255', ''),
+ ),
+ 'PRIMARY_KEY' => 'lang_id',
+ 'KEYS' => array(
+ 'lang_iso' => array('INDEX', 'lang_iso'),
+ ),
+ ),
+
+ $this->table_prefix . 'log' => array(
+ 'COLUMNS' => array(
+ 'log_id' => array('UINT', NULL, 'auto_increment'),
+ 'log_type' => array('TINT:4', 0),
+ 'user_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'topic_id' => array('UINT', 0),
+ 'reportee_id' => array('UINT', 0),
+ 'log_ip' => array('VCHAR:40', ''),
+ 'log_time' => array('TIMESTAMP', 0),
+ 'log_operation' => array('TEXT_UNI', ''),
+ 'log_data' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'log_id',
+ 'KEYS' => array(
+ 'log_type' => array('INDEX', 'log_type'),
+ 'forum_id' => array('INDEX', 'forum_id'),
+ 'topic_id' => array('INDEX', 'topic_id'),
+ 'reportee_id' => array('INDEX', 'reportee_id'),
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'moderator_cache' => array(
+ 'COLUMNS' => array(
+ 'forum_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'username' => array('VCHAR_UNI:255', ''),
+ 'group_id' => array('UINT', 0),
+ 'group_name' => array('VCHAR_UNI', ''),
+ 'display_on_index' => array('BOOL', 1),
+ ),
+ 'KEYS' => array(
+ 'disp_idx' => array('INDEX', 'display_on_index'),
+ 'forum_id' => array('INDEX', 'forum_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'modules' => array(
+ 'COLUMNS' => array(
+ 'module_id' => array('UINT', NULL, 'auto_increment'),
+ 'module_enabled' => array('BOOL', 1),
+ 'module_display' => array('BOOL', 1),
+ 'module_basename' => array('VCHAR', ''),
+ 'module_class' => array('VCHAR:10', ''),
+ 'parent_id' => array('UINT', 0),
+ 'left_id' => array('UINT', 0),
+ 'right_id' => array('UINT', 0),
+ 'module_langname' => array('VCHAR', ''),
+ 'module_mode' => array('VCHAR', ''),
+ 'module_auth' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'module_id',
+ 'KEYS' => array(
+ 'left_right_id' => array('INDEX', array('left_id', 'right_id')),
+ 'module_enabled' => array('INDEX', 'module_enabled'),
+ 'class_left_id' => array('INDEX', array('module_class', 'left_id')),
+ ),
+ ),
+
+ $this->table_prefix . 'poll_options' => array(
+ 'COLUMNS' => array(
+ 'poll_option_id' => array('TINT:4', 0),
+ 'topic_id' => array('UINT', 0),
+ 'poll_option_text' => array('TEXT_UNI', ''),
+ 'poll_option_total' => array('UINT', 0),
+ ),
+ 'KEYS' => array(
+ 'poll_opt_id' => array('INDEX', 'poll_option_id'),
+ 'topic_id' => array('INDEX', 'topic_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'poll_votes' => array(
+ 'COLUMNS' => array(
+ 'topic_id' => array('UINT', 0),
+ 'poll_option_id' => array('TINT:4', 0),
+ 'vote_user_id' => array('UINT', 0),
+ 'vote_user_ip' => array('VCHAR:40', ''),
+ ),
+ 'KEYS' => array(
+ 'topic_id' => array('INDEX', 'topic_id'),
+ 'vote_user_id' => array('INDEX', 'vote_user_id'),
+ 'vote_user_ip' => array('INDEX', 'vote_user_ip'),
+ ),
+ ),
+
+ $this->table_prefix . 'posts' => array(
+ 'COLUMNS' => array(
+ 'post_id' => array('UINT', NULL, 'auto_increment'),
+ 'topic_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'poster_id' => array('UINT', 0),
+ 'icon_id' => array('UINT', 0),
+ 'poster_ip' => array('VCHAR:40', ''),
+ 'post_time' => array('TIMESTAMP', 0),
+ 'post_approved' => array('BOOL', 1),
+ 'post_reported' => array('BOOL', 0),
+ 'enable_bbcode' => array('BOOL', 1),
+ 'enable_smilies' => array('BOOL', 1),
+ 'enable_magic_url' => array('BOOL', 1),
+ 'enable_sig' => array('BOOL', 1),
+ 'post_username' => array('VCHAR_UNI:255', ''),
+ 'post_subject' => array('XSTEXT_UNI', '', 'true_sort'),
+ 'post_text' => array('MTEXT_UNI', ''),
+ 'post_checksum' => array('VCHAR:32', ''),
+ 'post_attachment' => array('BOOL', 0),
+ 'bbcode_bitfield' => array('VCHAR:255', ''),
+ 'bbcode_uid' => array('VCHAR:8', ''),
+ 'post_postcount' => array('BOOL', 1),
+ 'post_edit_time' => array('TIMESTAMP', 0),
+ 'post_edit_reason' => array('STEXT_UNI', ''),
+ 'post_edit_user' => array('UINT', 0),
+ 'post_edit_count' => array('USINT', 0),
+ 'post_edit_locked' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'post_id',
+ 'KEYS' => array(
+ 'forum_id' => array('INDEX', 'forum_id'),
+ 'topic_id' => array('INDEX', 'topic_id'),
+ 'poster_ip' => array('INDEX', 'poster_ip'),
+ 'poster_id' => array('INDEX', 'poster_id'),
+ 'post_approved' => array('INDEX', 'post_approved'),
+ 'tid_post_time' => array('INDEX', array('topic_id', 'post_time')),
+ ),
+ ),
+
+ $this->table_prefix . 'privmsgs' => array(
+ 'COLUMNS' => array(
+ 'msg_id' => array('UINT', NULL, 'auto_increment'),
+ 'root_level' => array('UINT', 0),
+ 'author_id' => array('UINT', 0),
+ 'icon_id' => array('UINT', 0),
+ 'author_ip' => array('VCHAR:40', ''),
+ 'message_time' => array('TIMESTAMP', 0),
+ 'enable_bbcode' => array('BOOL', 1),
+ 'enable_smilies' => array('BOOL', 1),
+ 'enable_magic_url' => array('BOOL', 1),
+ 'enable_sig' => array('BOOL', 1),
+ 'message_subject' => array('XSTEXT_UNI', ''),
+ 'message_text' => array('MTEXT_UNI', ''),
+ 'message_edit_reason' => array('STEXT_UNI', ''),
+ 'message_edit_user' => array('UINT', 0),
+ 'message_attachment' => array('BOOL', 0),
+ 'bbcode_bitfield' => array('VCHAR:255', ''),
+ 'bbcode_uid' => array('VCHAR:8', ''),
+ 'message_edit_time' => array('TIMESTAMP', 0),
+ 'message_edit_count' => array('USINT', 0),
+ 'to_address' => array('TEXT_UNI', ''),
+ 'bcc_address' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'msg_id',
+ 'KEYS' => array(
+ 'author_ip' => array('INDEX', 'author_ip'),
+ 'message_time' => array('INDEX', 'message_time'),
+ 'author_id' => array('INDEX', 'author_id'),
+ 'root_level' => array('INDEX', 'root_level'),
+ ),
+ ),
+
+ $this->table_prefix . 'privmsgs_folder' => array(
+ 'COLUMNS' => array(
+ 'folder_id' => array('UINT', NULL, 'auto_increment'),
+ 'user_id' => array('UINT', 0),
+ 'folder_name' => array('VCHAR_UNI', ''),
+ 'pm_count' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'folder_id',
+ 'KEYS' => array(
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'privmsgs_rules' => array(
+ 'COLUMNS' => array(
+ 'rule_id' => array('UINT', NULL, 'auto_increment'),
+ 'user_id' => array('UINT', 0),
+ 'rule_check' => array('UINT', 0),
+ 'rule_connection' => array('UINT', 0),
+ 'rule_string' => array('VCHAR_UNI', ''),
+ 'rule_user_id' => array('UINT', 0),
+ 'rule_group_id' => array('UINT', 0),
+ 'rule_action' => array('UINT', 0),
+ 'rule_folder_id' => array('INT:11', 0),
+ ),
+ 'PRIMARY_KEY' => 'rule_id',
+ 'KEYS' => array(
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'privmsgs_to' => array(
+ 'COLUMNS' => array(
+ 'msg_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'author_id' => array('UINT', 0),
+ 'pm_deleted' => array('BOOL', 0),
+ 'pm_new' => array('BOOL', 1),
+ 'pm_unread' => array('BOOL', 1),
+ 'pm_replied' => array('BOOL', 0),
+ 'pm_marked' => array('BOOL', 0),
+ 'pm_forwarded' => array('BOOL', 0),
+ 'folder_id' => array('INT:11', 0),
+ ),
+ 'KEYS' => array(
+ 'msg_id' => array('INDEX', 'msg_id'),
+ 'author_id' => array('INDEX', 'author_id'),
+ 'usr_flder_id' => array('INDEX', array('user_id', 'folder_id')),
+ ),
+ ),
+
+ $this->table_prefix . 'profile_fields' => array(
+ 'COLUMNS' => array(
+ 'field_id' => array('UINT', NULL, 'auto_increment'),
+ 'field_name' => array('VCHAR_UNI', ''),
+ 'field_type' => array('TINT:4', 0),
+ 'field_ident' => array('VCHAR:20', ''),
+ 'field_length' => array('VCHAR:20', ''),
+ 'field_minlen' => array('VCHAR', ''),
+ 'field_maxlen' => array('VCHAR', ''),
+ 'field_novalue' => array('VCHAR_UNI', ''),
+ 'field_default_value' => array('VCHAR_UNI', ''),
+ 'field_validation' => array('VCHAR_UNI:20', ''),
+ 'field_required' => array('BOOL', 0),
+ 'field_show_on_reg' => array('BOOL', 0),
+ 'field_hide' => array('BOOL', 0),
+ 'field_no_view' => array('BOOL', 0),
+ 'field_active' => array('BOOL', 0),
+ 'field_order' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'field_id',
+ 'KEYS' => array(
+ 'fld_type' => array('INDEX', 'field_type'),
+ 'fld_ordr' => array('INDEX', 'field_order'),
+ ),
+ ),
+
+ $this->table_prefix . 'profile_fields_data' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'user_id',
+ ),
+
+ $this->table_prefix . 'profile_fields_lang' => array(
+ 'COLUMNS' => array(
+ 'field_id' => array('UINT', 0),
+ 'lang_id' => array('UINT', 0),
+ 'option_id' => array('UINT', 0),
+ 'field_type' => array('TINT:4', 0),
+ 'lang_value' => array('VCHAR_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => array('field_id', 'lang_id', 'option_id'),
+ ),
+
+ $this->table_prefix . 'profile_lang' => array(
+ 'COLUMNS' => array(
+ 'field_id' => array('UINT', 0),
+ 'lang_id' => array('UINT', 0),
+ 'lang_name' => array('VCHAR_UNI', ''),
+ 'lang_explain' => array('TEXT_UNI', ''),
+ 'lang_default_value' => array('VCHAR_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => array('field_id', 'lang_id'),
+ ),
+
+ $this->table_prefix . 'ranks' => array(
+ 'COLUMNS' => array(
+ 'rank_id' => array('UINT', NULL, 'auto_increment'),
+ 'rank_title' => array('VCHAR_UNI', ''),
+ 'rank_min' => array('UINT', 0),
+ 'rank_special' => array('BOOL', 0),
+ 'rank_image' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'rank_id',
+ ),
+
+ $this->table_prefix . 'reports' => array(
+ 'COLUMNS' => array(
+ 'report_id' => array('UINT', NULL, 'auto_increment'),
+ 'reason_id' => array('USINT', 0),
+ 'post_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'user_notify' => array('BOOL', 0),
+ 'report_closed' => array('BOOL', 0),
+ 'report_time' => array('TIMESTAMP', 0),
+ 'report_text' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'report_id',
+ ),
+
+ $this->table_prefix . 'reports_reasons' => array(
+ 'COLUMNS' => array(
+ 'reason_id' => array('USINT', NULL, 'auto_increment'),
+ 'reason_title' => array('VCHAR_UNI', ''),
+ 'reason_description' => array('MTEXT_UNI', ''),
+ 'reason_order' => array('USINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'reason_id',
+ ),
+
+ $this->table_prefix . 'search_results' => array(
+ 'COLUMNS' => array(
+ 'search_key' => array('VCHAR:32', ''),
+ 'search_time' => array('TIMESTAMP', 0),
+ 'search_keywords' => array('MTEXT_UNI', ''),
+ 'search_authors' => array('MTEXT', ''),
+ ),
+ 'PRIMARY_KEY' => 'search_key',
+ ),
+
+ $this->table_prefix . 'search_wordlist' => array(
+ 'COLUMNS' => array(
+ 'word_id' => array('UINT', NULL, 'auto_increment'),
+ 'word_text' => array('VCHAR_UNI', ''),
+ 'word_common' => array('BOOL', 0),
+ 'word_count' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'word_id',
+ 'KEYS' => array(
+ 'wrd_txt' => array('UNIQUE', 'word_text'),
+ 'wrd_cnt' => array('INDEX', 'word_count'),
+ ),
+ ),
+
+ $this->table_prefix . 'search_wordmatch' => array(
+ 'COLUMNS' => array(
+ 'post_id' => array('UINT', 0),
+ 'word_id' => array('UINT', 0),
+ 'title_match' => array('BOOL', 0),
+ ),
+ 'KEYS' => array(
+ 'unq_mtch' => array('UNIQUE', array('word_id', 'post_id', 'title_match')),
+ 'word_id' => array('INDEX', 'word_id'),
+ 'post_id' => array('INDEX', 'post_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'sessions' => array(
+ 'COLUMNS' => array(
+ 'session_id' => array('CHAR:32', ''),
+ 'session_user_id' => array('UINT', 0),
+ 'session_last_visit' => array('TIMESTAMP', 0),
+ 'session_start' => array('TIMESTAMP', 0),
+ 'session_time' => array('TIMESTAMP', 0),
+ 'session_ip' => array('VCHAR:40', ''),
+ 'session_browser' => array('VCHAR:150', ''),
+ 'session_forwarded_for' => array('VCHAR:255', ''),
+ 'session_page' => array('VCHAR_UNI', ''),
+ 'session_viewonline' => array('BOOL', 1),
+ 'session_autologin' => array('BOOL', 0),
+ 'session_admin' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'session_id',
+ 'KEYS' => array(
+ 'session_time' => array('INDEX', 'session_time'),
+ 'session_user_id' => array('INDEX', 'session_user_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'sessions_keys' => array(
+ 'COLUMNS' => array(
+ 'key_id' => array('CHAR:32', ''),
+ 'user_id' => array('UINT', 0),
+ 'last_ip' => array('VCHAR:40', ''),
+ 'last_login' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => array('key_id', 'user_id'),
+ 'KEYS' => array(
+ 'last_login' => array('INDEX', 'last_login'),
+ ),
+ ),
+
+ $this->table_prefix . 'sitelist' => array(
+ 'COLUMNS' => array(
+ 'site_id' => array('UINT', NULL, 'auto_increment'),
+ 'site_ip' => array('VCHAR:40', ''),
+ 'site_hostname' => array('VCHAR', ''),
+ 'ip_exclude' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'site_id',
+ ),
+
+ $this->table_prefix . 'smilies' => array(
+ 'COLUMNS' => array(
+ 'smiley_id' => array('UINT', NULL, 'auto_increment'),
+// We may want to set 'code' to VCHAR:50 or check if unicode support is possible... at the moment only ASCII characters are allowed.
+ 'code' => array('VCHAR_UNI:50', ''),
+ 'emotion' => array('VCHAR_UNI:50', ''),
+ 'smiley_url' => array('VCHAR:50', ''),
+ 'smiley_width' => array('USINT', 0),
+ 'smiley_height' => array('USINT', 0),
+ 'smiley_order' => array('UINT', 0),
+ 'display_on_posting'=> array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => 'smiley_id',
+ 'KEYS' => array(
+ 'display_on_post' => array('INDEX', 'display_on_posting'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles' => array(
+ 'COLUMNS' => array(
+ 'style_id' => array('USINT', NULL, 'auto_increment'),
+ 'style_name' => array('VCHAR_UNI:255', ''),
+ 'style_copyright' => array('VCHAR_UNI', ''),
+ 'style_active' => array('BOOL', 1),
+ 'template_id' => array('USINT', 0),
+ 'theme_id' => array('USINT', 0),
+ 'imageset_id' => array('USINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'style_id',
+ 'KEYS' => array(
+ 'style_name' => array('UNIQUE', 'style_name'),
+ 'template_id' => array('INDEX', 'template_id'),
+ 'theme_id' => array('INDEX', 'theme_id'),
+ 'imageset_id' => array('INDEX', 'imageset_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles_template' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('USINT', NULL, 'auto_increment'),
+ 'template_name' => array('VCHAR_UNI:255', ''),
+ 'template_copyright' => array('VCHAR_UNI', ''),
+ 'template_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'template_storedb' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'template_id',
+ 'KEYS' => array(
+ 'tmplte_nm' => array('UNIQUE', 'template_name'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles_template_data' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('USINT', 0),
+ 'template_filename' => array('VCHAR:100', ''),
+ 'template_included' => array('TEXT', ''),
+ 'template_mtime' => array('TIMESTAMP', 0),
+ 'template_data' => array('MTEXT_UNI', ''),
+ ),
+ 'KEYS' => array(
+ 'tid' => array('INDEX', 'template_id'),
+ 'tfn' => array('INDEX', 'template_filename'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles_theme' => array(
+ 'COLUMNS' => array(
+ 'theme_id' => array('USINT', NULL, 'auto_increment'),
+ 'theme_name' => array('VCHAR_UNI:255', ''),
+ 'theme_copyright' => array('VCHAR_UNI', ''),
+ 'theme_path' => array('VCHAR:100', ''),
+ 'theme_storedb' => array('BOOL', 0),
+ 'theme_mtime' => array('TIMESTAMP', 0),
+ 'theme_data' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'theme_id',
+ 'KEYS' => array(
+ 'theme_name' => array('UNIQUE', 'theme_name'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles_imageset' => array(
+ 'COLUMNS' => array(
+ 'imageset_id' => array('USINT', NULL, 'auto_increment'),
+ 'imageset_name' => array('VCHAR_UNI:255', ''),
+ 'imageset_copyright' => array('VCHAR_UNI', ''),
+ 'imageset_path' => array('VCHAR:100', ''),
+ ),
+ 'PRIMARY_KEY' => 'imageset_id',
+ 'KEYS' => array(
+ 'imgset_nm' => array('UNIQUE', 'imageset_name'),
+ ),
+ ),
+
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'COLUMNS' => array(
+ 'image_id' => array('USINT', NULL, 'auto_increment'),
+ 'image_name' => array('VCHAR:200', ''),
+ 'image_filename' => array('VCHAR:200', ''),
+ 'image_lang' => array('VCHAR:30', ''),
+ 'image_height' => array('USINT', 0),
+ 'image_width' => array('USINT', 0),
+ 'imageset_id' => array('USINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'image_id',
+ 'KEYS' => array(
+ 'i_d' => array('INDEX', 'imageset_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'topics' => array(
+ 'COLUMNS' => array(
+ 'topic_id' => array('UINT', NULL, 'auto_increment'),
+ 'forum_id' => array('UINT', 0),
+ 'icon_id' => array('UINT', 0),
+ 'topic_attachment' => array('BOOL', 0),
+ 'topic_approved' => array('BOOL', 1),
+ 'topic_reported' => array('BOOL', 0),
+ 'topic_title' => array('XSTEXT_UNI', '', 'true_sort'),
+ 'topic_poster' => array('UINT', 0),
+ 'topic_time' => array('TIMESTAMP', 0),
+ 'topic_time_limit' => array('TIMESTAMP', 0),
+ 'topic_views' => array('UINT', 0),
+ 'topic_replies' => array('UINT', 0),
+ 'topic_replies_real' => array('UINT', 0),
+ 'topic_status' => array('TINT:3', 0),
+ 'topic_type' => array('TINT:3', 0),
+ 'topic_first_post_id' => array('UINT', 0),
+ 'topic_first_poster_name' => array('VCHAR_UNI', ''),
+ 'topic_first_poster_colour' => array('VCHAR:6', ''),
+ 'topic_last_post_id' => array('UINT', 0),
+ 'topic_last_poster_id' => array('UINT', 0),
+ 'topic_last_poster_name' => array('VCHAR_UNI', ''),
+ 'topic_last_poster_colour' => array('VCHAR:6', ''),
+ 'topic_last_post_subject' => array('XSTEXT_UNI', ''),
+ 'topic_last_post_time' => array('TIMESTAMP', 0),
+ 'topic_last_view_time' => array('TIMESTAMP', 0),
+ 'topic_moved_id' => array('UINT', 0),
+ 'topic_bumped' => array('BOOL', 0),
+ 'topic_bumper' => array('UINT', 0),
+ 'poll_title' => array('STEXT_UNI', ''),
+ 'poll_start' => array('TIMESTAMP', 0),
+ 'poll_length' => array('TIMESTAMP', 0),
+ 'poll_max_options' => array('TINT:4', 1),
+ 'poll_last_vote' => array('TIMESTAMP', 0),
+ 'poll_vote_change' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => 'topic_id',
+ 'KEYS' => array(
+ 'forum_id' => array('INDEX', 'forum_id'),
+ 'forum_id_type' => array('INDEX', array('forum_id', 'topic_type')),
+ 'last_post_time' => array('INDEX', 'topic_last_post_time'),
+ 'topic_approved' => array('INDEX', 'topic_approved'),
+ 'forum_appr_last' => array('INDEX', array('forum_id', 'topic_approved', 'topic_last_post_id')),
+ 'fid_time_moved' => array('INDEX', array('forum_id', 'topic_last_post_time', 'topic_moved_id')),
+ ),
+ ),
+
+ $this->table_prefix . 'topics_track' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'topic_id' => array('UINT', 0),
+ 'forum_id' => array('UINT', 0),
+ 'mark_time' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => array('user_id', 'topic_id'),
+ 'KEYS' => array(
+ 'forum_id' => array('INDEX', 'forum_id'),
+ ),
+ ),
+
+ $this->table_prefix . 'topics_posted' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'topic_id' => array('UINT', 0),
+ 'topic_posted' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => array('user_id', 'topic_id'),
+ ),
+
+ $this->table_prefix . 'topics_watch' => array(
+ 'COLUMNS' => array(
+ 'topic_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notify_status' => array('BOOL', 0),
+ ),
+ 'KEYS' => array(
+ 'topic_id' => array('INDEX', 'topic_id'),
+ 'user_id' => array('INDEX', 'user_id'),
+ 'notify_stat' => array('INDEX', 'notify_status'),
+ ),
+ ),
+
+ $this->table_prefix . 'user_group' => array(
+ 'COLUMNS' => array(
+ 'group_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'group_leader' => array('BOOL', 0),
+ 'user_pending' => array('BOOL', 1),
+ ),
+ 'KEYS' => array(
+ 'group_id' => array('INDEX', 'group_id'),
+ 'user_id' => array('INDEX', 'user_id'),
+ 'group_leader' => array('INDEX', 'group_leader'),
+ ),
+ ),
+
+ $this->table_prefix . 'users' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', NULL, 'auto_increment'),
+ 'user_type' => array('TINT:2', 0),
+ 'group_id' => array('UINT', 3),
+ 'user_permissions' => array('MTEXT', ''),
+ 'user_perm_from' => array('UINT', 0),
+ 'user_ip' => array('VCHAR:40', ''),
+ 'user_regdate' => array('TIMESTAMP', 0),
+ 'username' => array('VCHAR_CI', ''),
+ 'username_clean' => array('VCHAR_CI', ''),
+ 'user_password' => array('VCHAR_UNI:40', ''),
+ 'user_passchg' => array('TIMESTAMP', 0),
+ 'user_pass_convert' => array('BOOL', 0),
+ 'user_email' => array('VCHAR_UNI:100', ''),
+ 'user_email_hash' => array('BINT', 0),
+ 'user_birthday' => array('VCHAR:10', ''),
+ 'user_lastvisit' => array('TIMESTAMP', 0),
+ 'user_lastmark' => array('TIMESTAMP', 0),
+ 'user_lastpost_time' => array('TIMESTAMP', 0),
+ 'user_lastpage' => array('VCHAR_UNI:200', ''),
+ 'user_last_confirm_key' => array('VCHAR:10', ''),
+ 'user_last_search' => array('TIMESTAMP', 0),
+ 'user_warnings' => array('TINT:4', 0),
+ 'user_last_warning' => array('TIMESTAMP', 0),
+ 'user_login_attempts' => array('TINT:4', 0),
+ 'user_inactive_reason' => array('TINT:2', 0),
+ 'user_inactive_time' => array('TIMESTAMP', 0),
+ 'user_posts' => array('UINT', 0),
+ 'user_lang' => array('VCHAR:30', ''),
+ 'user_timezone' => array('DECIMAL', 0),
+ 'user_dst' => array('BOOL', 0),
+ 'user_dateformat' => array('VCHAR_UNI:30', 'd M Y H:i'),
+ 'user_style' => array('USINT', 0),
+ 'user_rank' => array('UINT', 0),
+ 'user_colour' => array('VCHAR:6', ''),
+ 'user_new_privmsg' => array('INT:4', 0),
+ 'user_unread_privmsg' => array('INT:4', 0),
+ 'user_last_privmsg' => array('TIMESTAMP', 0),
+ 'user_message_rules' => array('BOOL', 0),
+ 'user_full_folder' => array('INT:11', -3),
+ 'user_emailtime' => array('TIMESTAMP', 0),
+ 'user_topic_show_days' => array('USINT', 0),
+ 'user_topic_sortby_type' => array('VCHAR:1', 't'),
+ 'user_topic_sortby_dir' => array('VCHAR:1', 'd'),
+ 'user_post_show_days' => array('USINT', 0),
+ 'user_post_sortby_type' => array('VCHAR:1', 't'),
+ 'user_post_sortby_dir' => array('VCHAR:1', 'a'),
+ 'user_notify' => array('BOOL', 0),
+ 'user_notify_pm' => array('BOOL', 1),
+ 'user_notify_type' => array('TINT:4', 0),
+ 'user_allow_pm' => array('BOOL', 1),
+ 'user_allow_viewonline' => array('BOOL', 1),
+ 'user_allow_viewemail' => array('BOOL', 1),
+ 'user_allow_massemail' => array('BOOL', 1),
+ 'user_options' => array('UINT:11', 895),
+ 'user_avatar' => array('VCHAR', ''),
+ 'user_avatar_type' => array('TINT:2', 0),
+ 'user_avatar_width' => array('USINT', 0),
+ 'user_avatar_height' => array('USINT', 0),
+ 'user_sig' => array('MTEXT_UNI', ''),
+ 'user_sig_bbcode_uid' => array('VCHAR:8', ''),
+ 'user_sig_bbcode_bitfield' => array('VCHAR:255', ''),
+ 'user_from' => array('VCHAR_UNI:100', ''),
+ 'user_icq' => array('VCHAR:15', ''),
+ 'user_aim' => array('VCHAR_UNI', ''),
+ 'user_yim' => array('VCHAR_UNI', ''),
+ 'user_msnm' => array('VCHAR_UNI', ''),
+ 'user_jabber' => array('VCHAR_UNI', ''),
+ 'user_website' => array('VCHAR_UNI:200', ''),
+ 'user_occ' => array('TEXT_UNI', ''),
+ 'user_interests' => array('TEXT_UNI', ''),
+ 'user_actkey' => array('VCHAR:32', ''),
+ 'user_newpasswd' => array('VCHAR_UNI:40', ''),
+ 'user_form_salt' => array('VCHAR_UNI:32', ''),
+
+ ),
+ 'PRIMARY_KEY' => 'user_id',
+ 'KEYS' => array(
+ 'user_birthday' => array('INDEX', 'user_birthday'),
+ 'user_email_hash' => array('INDEX', 'user_email_hash'),
+ 'user_type' => array('INDEX', 'user_type'),
+ 'username_clean' => array('UNIQUE', 'username_clean'),
+ ),
+ ),
+
+ $this->table_prefix . 'warnings' => array(
+ 'COLUMNS' => array(
+ 'warning_id' => array('UINT', NULL, 'auto_increment'),
+ 'user_id' => array('UINT', 0),
+ 'post_id' => array('UINT', 0),
+ 'log_id' => array('UINT', 0),
+ 'warning_time' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => 'warning_id',
+ ),
+
+ $this->table_prefix . 'words' => array(
+ 'COLUMNS' => array(
+ 'word_id' => array('UINT', NULL, 'auto_increment'),
+ 'word' => array('VCHAR_UNI', ''),
+ 'replacement' => array('VCHAR_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'word_id',
+ ),
+
+ $this->table_prefix . 'zebra' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'zebra_id' => array('UINT', 0),
+ 'friend' => array('BOOL', 0),
+ 'foe' => array('BOOL', 0),
+ ),
+ 'PRIMARY_KEY' => array('user_id', 'zebra_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'attachments',
+ $this->table_prefix . 'acl_groups',
+ $this->table_prefix . 'acl_options',
+ $this->table_prefix . 'acl_roles',
+ $this->table_prefix . 'acl_roles_data',
+ $this->table_prefix . 'acl_users',
+ $this->table_prefix . 'banlist',
+ $this->table_prefix . 'bbcodes',
+ $this->table_prefix . 'bookmarks',
+ $this->table_prefix . 'bots',
+ $this->table_prefix . 'config',
+ $this->table_prefix . 'confirm',
+ $this->table_prefix . 'disallow',
+ $this->table_prefix . 'drafts',
+ $this->table_prefix . 'extensions',
+ $this->table_prefix . 'extension_groups',
+ $this->table_prefix . 'forums',
+ $this->table_prefix . 'forums_access',
+ $this->table_prefix . 'forums_track',
+ $this->table_prefix . 'forums_watch',
+ $this->table_prefix . 'groups',
+ $this->table_prefix . 'icons',
+ $this->table_prefix . 'lang',
+ $this->table_prefix . 'log',
+ $this->table_prefix . 'moderator_cache',
+ $this->table_prefix . 'modules',
+ $this->table_prefix . 'poll_options',
+ $this->table_prefix . 'poll_votes',
+ $this->table_prefix . 'posts',
+ $this->table_prefix . 'privmsgs',
+ $this->table_prefix . 'privmsgs_folder',
+ $this->table_prefix . 'privmsgs_rules',
+ $this->table_prefix . 'privmsgs_to',
+ $this->table_prefix . 'profile_fields',
+ $this->table_prefix . 'profile_fields_data',
+ $this->table_prefix . 'profile_fields_lang',
+ $this->table_prefix . 'profile_lang',
+ $this->table_prefix . 'ranks',
+ $this->table_prefix . 'reports',
+ $this->table_prefix . 'reports_reasons',
+ $this->table_prefix . 'search_results',
+ $this->table_prefix . 'search_wordlist',
+ $this->table_prefix . 'search_wordmatch',
+ $this->table_prefix . 'sessions',
+ $this->table_prefix . 'sessions_keys',
+ $this->table_prefix . 'sitelist',
+ $this->table_prefix . 'smilies',
+ $this->table_prefix . 'styles',
+ $this->table_prefix . 'styles_template',
+ $this->table_prefix . 'styles_template_data',
+ $this->table_prefix . 'styles_theme',
+ $this->table_prefix . 'styles_imageset',
+ $this->table_prefix . 'styles_imageset_data',
+ $this->table_prefix . 'topics',
+ $this->table_prefix . 'topics_track',
+ $this->table_prefix . 'topics_posted',
+ $this->table_prefix . 'topics_watch',
+ $this->table_prefix . 'user_group',
+ $this->table_prefix . 'users',
+ $this->table_prefix . 'warnings',
+ $this->table_prefix . 'words',
+ $this->table_prefix . 'zebra',
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php
new file mode 100644
index 0000000000..f5c7e56a81
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_1_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php
new file mode 100644
index 0000000000..0d3a1ca80b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_10 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.10', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php
new file mode 100644
index 0000000000..293c46cb06
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_10_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.10-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_9');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('email_max_chunk_size', 50)),
+
+ array('config.update', array('version', '3.0.10-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php
new file mode 100644
index 0000000000..f2889120e6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_10_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.10-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php
new file mode 100644
index 0000000000..9d6697ab7f
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_10_rc3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.10-RC3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_10_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-RC3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php
new file mode 100644
index 0000000000..e77b54a1b5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_11 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.11', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php
new file mode 100644
index 0000000000..ed2dabf51c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php
@@ -0,0 +1,101 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_11_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.11-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_10');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'cleanup_deactivated_styles'))),
+ array('custom', array(array(&$this, 'delete_orphan_private_messages'))),
+
+ array('config.update', array('version', '3.0.11-RC1')),
+ );
+ }
+
+ public function cleanup_deactivated_styles()
+ {
+ // Updates users having current style a deactivated one
+ $sql = 'SELECT style_id
+ FROM ' . STYLES_TABLE . '
+ WHERE style_active = 0';
+ $result = $this->sql_query($sql);
+
+ $deactivated_style_ids = array();
+ while ($style_id = $this->db->sql_fetchfield('style_id', false, $result))
+ {
+ $deactivated_style_ids[] = (int) $style_id;
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($deactivated_style_ids))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_style = ' . (int) $this->config['default_style'] .'
+ WHERE ' . $this->db->sql_in_set('user_style', $deactivated_style_ids);
+ $this->sql_query($sql);
+ }
+ }
+
+ public function delete_orphan_private_messages()
+ {
+ // Delete orphan private messages
+ $batch_size = 500;
+
+ $sql_array = array(
+ 'SELECT' => 'p.msg_id',
+ 'FROM' => array(
+ PRIVMSGS_TABLE => 'p',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(PRIVMSGS_TO_TABLE => 't'),
+ 'ON' => 'p.msg_id = t.msg_id',
+ ),
+ ),
+ 'WHERE' => 't.user_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $delete_pms = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $delete_pms[] = (int) $row['msg_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($delete_pms))
+ {
+ $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('msg_id', $delete_pms);
+ $this->sql_query($sql);
+
+ // Return false to have the Migrator call this function again
+ return false;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php
new file mode 100644
index 0000000000..45d88708bd
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php
@@ -0,0 +1,56 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_11_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.11-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php
new file mode 100644
index 0000000000..c489c0c0e8
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_12 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.12', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.12', '<'),
+ array('config.update', array('version', '3.0.12')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php
new file mode 100644
index 0000000000..f9f6d9f7f7
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php
@@ -0,0 +1,72 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+/** @todo DROP LOGIN_ATTEMPT_TABLE.attempt_id in 3.0.12-RC1 **/
+
+class release_3_0_12_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.12-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, '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);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php
new file mode 100644
index 0000000000..8fac273073
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_12_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.12-RC2', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.12-RC2', '<'),
+ array('config.update', array('version', '3.0.12-RC2')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php
new file mode 100644
index 0000000000..fb1b8014a4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_12_rc3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.12-RC3', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_12_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.12-RC3', '<'),
+ array('config.update', array('version', '3.0.12-RC3')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_13.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13.php
new file mode 100644
index 0000000000..310fcc70fc
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_13 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.13', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_13_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.13', '<'),
+ array('config.update', array('version', '3.0.13')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_pl1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_pl1.php
new file mode 100644
index 0000000000..b12a96a7fb
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_pl1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_13_pl1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.13-PL1', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_13');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.13-PL1', '<'),
+ array('config.update', array('version', '3.0.13-PL1')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_rc1.php
new file mode 100644
index 0000000000..9ea68fa862
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_13_rc1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_13_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.13-RC1', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_12');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.13-RC1', '<'),
+ array('config.update', array('version', '3.0.13-RC1')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php
new file mode 100644
index 0000000000..51475f5a05
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_14 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.14', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_14_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.14', '<'),
+ array('config.update', array('version', '3.0.14')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php
new file mode 100644
index 0000000000..421ef06dd3
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_14_rc1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_14_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.14-RC1', '>=') && phpbb_version_compare($this->config['version'], '3.1.0-dev', '<');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_13');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ phpbb_version_compare($this->config['version'], '3.0.14-RC1', '<'),
+ array('config.update', array('version', '3.0.14-RC1')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php
new file mode 100644
index 0000000000..d1ae0b9cbc
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1_rc1.php
@@ -0,0 +1,119 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_1_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.1-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_0');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list' => array('BOOL', 1),
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('UINT', 0),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('session_forum_id'),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name' => array('group_legend', 'group_name'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list',
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('group_legend'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'fix_unset_last_view_time'))),
+ array('custom', array(array(&$this, 'reset_smiley_size'))),
+
+ array('config.update', array('version', '3.0.1-RC1')),
+ );
+ }
+
+ public function fix_unset_last_view_time()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "topics
+ SET topic_last_view_time = topic_last_post_time
+ WHERE topic_last_view_time = 0";
+ $this->sql_query($sql);
+ }
+
+ public function reset_smiley_size()
+ {
+ // Update smiley sizes
+ $smileys = array('icon_e_surprised.gif', 'icon_eek.gif', 'icon_cool.gif', 'icon_lol.gif', 'icon_mad.gif', 'icon_razz.gif', 'icon_redface.gif', 'icon_cry.gif', 'icon_evil.gif', 'icon_twisted.gif', 'icon_rolleyes.gif', 'icon_exclaim.gif', 'icon_question.gif', 'icon_idea.gif', 'icon_arrow.gif', 'icon_neutral.gif', 'icon_mrgreen.gif', 'icon_e_ugeek.gif');
+
+ foreach ($smileys as $smiley)
+ {
+ if (file_exists($this->phpbb_root_path . 'images/smilies/' . $smiley))
+ {
+ list($width, $height) = getimagesize($this->phpbb_root_path . 'images/smilies/' . $smiley);
+
+ $sql = 'UPDATE ' . SMILIES_TABLE . '
+ SET smiley_width = ' . $width . ', smiley_height = ' . $height . "
+ WHERE smiley_url = '" . $this->db->sql_escape($smiley) . "'";
+
+ $this->sql_query($sql);
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php
new file mode 100644
index 0000000000..c08f01dbeb
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_2_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php
new file mode 100644
index 0000000000..2e7f141a9b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_2_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.2-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('referer_validation', '1')),
+ array('config.add', array('check_attachment_content', '1')),
+ array('config.add', array('mime_triggers', 'body|head|html|img|plaintext|a href|pre|script|table|title')),
+
+ array('config.update', array('version', '3.0.2-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php
new file mode 100644
index 0000000000..bde5febc59
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php
@@ -0,0 +1,86 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_2_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.2-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_2_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'drafts' => array(
+ 'draft_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_subject' => array('STEXT_UNI', '', 'true_sort'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_title' => array('STEXT_UNI', '', 'true_sort'),
+ 'topic_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid' => array('session_forum_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array(
+ 'session_forum_id',
+ ),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php
new file mode 100644
index 0000000000..c277da224c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_3_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php
new file mode 100644
index 0000000000..530eaf47e6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php
@@ -0,0 +1,89 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_3_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.3-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_2');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id',
+ 'template_inherit_path',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('enable_queue_trigger', '0')),
+ array('config.add', array('queue_trigger_posts', '3')),
+ array('config.add', array('pm_max_recipients', '0')),
+ array('custom', array(array(&$this, 'set_group_default_max_recipients'))),
+ array('config.add', array('dbms_version', $this->db->sql_server_info(true))),
+ array('permission.add', array('u_masspm_group', true, 'u_masspm')),
+ array('custom', array(array(&$this, 'correct_acp_email_permissions'))),
+
+ array('config.update', array('version', '3.0.3-RC1')),
+ );
+ }
+
+ public function correct_acp_email_permissions()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'modules
+ SET module_auth = \'acl_a_email && cfg_email_enable\'
+ WHERE module_class = \'acp\'
+ AND module_basename = \'email\'';
+ $this->sql_query($sql);
+ }
+
+ public function set_group_default_max_recipients()
+ {
+ // Set maximum number of recipients for the registered users, bots, guests group
+ $sql = 'UPDATE ' . GROUPS_TABLE . ' SET group_max_recipients = 5
+ WHERE ' . $this->db->sql_in_set('group_name', array('GUESTS', 'REGISTERED', 'REGISTERED_COPPA', 'BOTS'));
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php
new file mode 100644
index 0000000000..9b08da0125
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_4 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_4_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'rename_log_delete_topic'))),
+
+ array('config.update', array('version', '3.0.4')),
+ );
+ }
+
+ public function rename_log_delete_topic()
+ {
+ if ($this->db->get_sql_layer() == 'oracle')
+ {
+ // log_operation is CLOB - but we can change this later
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation LIKE 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation = 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php
new file mode 100644
index 0000000000..10343438b3
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php
@@ -0,0 +1,129 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_4_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.4-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_3');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile' => array('BOOL', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'style_id' => array('UINT', NULL, 'auto_increment'),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_imageset' => array(
+ 'imageset_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'image_id' => array('UINT', NULL, 'auto_increment'),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'theme_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'template_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'template_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_custom_profile_fields'))),
+
+ array('config.update', array('version', '3.0.4-RC1')),
+ );
+ }
+
+ public function update_custom_profile_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new \format
+ $sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide
+ FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'field_required' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_hide' => 0,
+ 'field_show_profile'=> 0,
+ );
+
+ if ($row['field_required'])
+ {
+ $sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_show_on_reg'])
+ {
+ $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_hide'])
+ {
+ // Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module
+ $sql_ary['field_hide'] = 1;
+ }
+ else
+ {
+ // equivelant to "none", which is the "Display in user control panel" option
+ $sql_ary['field_show_profile'] = 1;
+ }
+
+ $this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary);
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php
new file mode 100644
index 0000000000..09c2bfea50
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_5 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.5', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_5_rc1part2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php
new file mode 100644
index 0000000000..003ccf8f18
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php
@@ -0,0 +1,133 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+use phpbb\db\migration\container_aware_migration;
+
+class release_3_0_5_rc1 extends container_aware_migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.5-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_4');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ $search_indexing_state = $this->config['search_indexing_state'];
+
+ return array(
+ array('config.add', array('captcha_gd_wave', 0)),
+ array('config.add', array('captcha_gd_3d_noise', 1)),
+ array('config.add', array('captcha_gd_fonts', 1)),
+ array('config.add', array('confirm_refresh', 1)),
+ array('config.add', array('max_num_search_keywords', 10)),
+ array('config.remove', array('search_indexing_state')),
+ array('config.add', array('search_indexing_state', $search_indexing_state, true)),
+ array('custom', array(array(&$this, 'hash_old_passwords'))),
+ array('custom', array(array(&$this, 'update_ichiro_bot'))),
+ );
+ }
+
+ public function hash_old_passwords()
+ {
+ $passwords_manager = $this->container->get('passwords.manager');
+ $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' => '$CP$' . $passwords_manager->hash($row['user_password'], 'passwords.driver.salted_md5'),
+ );
+
+ $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 = '" . $this->db->sql_escape($option) . "'
+ ORDER BY auth_option_id DESC";
+ // sql_query_limit not possible here, due to bug in postgresql layer
+ $result = $this->db->sql_query($sql);
+
+ // Skip first row, this is our original auth option we want to preserve
+ $row = $this->db->sql_fetchrow($result);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Ok, remove this auth option...
+ $this->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php
new file mode 100644
index 0000000000..a9041ef354
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php
@@ -0,0 +1,48 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_5_rc1part2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.5-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_5_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'acl_options' => array('auth_option'),
+ ),
+ 'add_unique_index' => array(
+ $this->table_prefix . 'acl_options' => array(
+ 'auth_option' => array('auth_option'),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php
new file mode 100644
index 0000000000..74c338a9c5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_6 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.6', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php
new file mode 100644
index 0000000000..faef68121d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php
@@ -0,0 +1,330 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_6_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.6-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_5');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new' => array('BOOL', 1),
+ 'user_reminded' => array('TINT:4', 0),
+ 'user_reminded_time' => array('TIMESTAMP', 0),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth' => array('BOOL', 0, 'after' => 'group_founder_manage'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options' => array('UINT:20', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_options' => array('UINT:11', 230271),
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id' => array('post_id'),
+ 'pm_id' => array('pm_id'),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username' => array('post_username:255'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts',
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new',
+ 'user_reminded',
+ 'user_reminded_time',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth',
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt',
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options',
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id',
+ 'pm_id',
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('captcha_plugin', 'phpbb_captcha_nogd')),
+ array('if', array(
+ ($this->config['captcha_gd']),
+ array('config.update', array('captcha_plugin', 'phpbb_captcha_gd')),
+ )),
+
+ array('config.add', array('feed_enable', 0)),
+ array('config.add', array('feed_limit', 10)),
+ array('config.add', array('feed_overall_forums', 1)),
+ array('config.add', array('feed_overall_forums_limit', 15)),
+ array('config.add', array('feed_overall_topics', 0)),
+ array('config.add', array('feed_overall_topics_limit', 15)),
+ array('config.add', array('feed_forum', 1)),
+ array('config.add', array('feed_topic', 1)),
+ array('config.add', array('feed_item_statistics', 1)),
+
+ array('config.add', array('smilies_per_page', 50)),
+ array('config.add', array('allow_pm_report', 1)),
+ array('config.add', array('min_post_chars', 1)),
+ array('config.add', array('allow_quick_reply', 1)),
+ array('config.add', array('new_member_post_limit', 0)),
+ array('config.add', array('new_member_group_default', 0)),
+ array('config.add', array('delete_time', $this->config['edit_time'])),
+
+ array('config.add', array('allow_avatar', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_upload'] || $this->config['allow_avatar_local'] || $this->config['allow_avatar_remote']),
+ array('config.update', array('allow_avatar', 1)),
+ )),
+ array('config.add', array('allow_avatar_remote_upload', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_remote'] && $this->config['allow_avatar_upload']),
+ array('config.update', array('allow_avatar_remote_upload', 1)),
+ )),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_BOARD_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_board',
+ 'modes' => array('feed'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_USERS',
+ array(
+ 'module_basename' => 'acp_users',
+ 'modes' => array('warnings'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_SERVER_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_send_statistics',
+ 'modes' => array('send_statistics'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_FORUM_BASED_PERMISSIONS',
+ array(
+ 'module_basename' => 'acp_permissions',
+ 'modes' => array('setting_forum_copy'),
+ ),
+ )),
+ array('module.add', array(
+ 'mcp',
+ 'MCP_REPORTS',
+ array(
+ 'module_basename' => 'mcp_pm_reports',
+ 'modes' => array('pm_reports','pm_reports_closed','pm_report_details'),
+ ),
+ )),
+ array('custom', array(array(&$this, 'add_newly_registered_group'))),
+ array('custom', array(array(&$this, 'set_user_options_default'))),
+
+ array('config.update', array('version', '3.0.6-RC1')),
+ );
+ }
+
+ public function set_user_options_default()
+ {
+ // 229376 is the added value to enable all three signature options
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_options = user_options + 229376';
+ $this->sql_query($sql);
+ }
+
+ public function add_newly_registered_group()
+ {
+ // Add newly_registered group... but check if it already exists (we always supported running the updater on any schema)
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'NEWLY_REGISTERED'";
+ $result = $this->db->sql_query($sql);
+ $group_id = (int) $this->db->sql_fetchfield('group_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$group_id)
+ {
+ $sql = 'INSERT INTO ' . GROUPS_TABLE . " (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('NEWLY_REGISTERED', 3, 0, '', 0, '', '', '', 5)";
+ $this->sql_query($sql);
+
+ $group_id = $this->db->sql_nextid();
+ }
+
+ // Insert new user role... at the end of the chain
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_USER_NEW_MEMBER'
+ AND role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $u_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$u_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_USER_NEW_MEMBER', 'ROLE_DESCRIPTION_USER_NEW_MEMBER', 'u_', $next_order_id)";
+ $this->sql_query($sql);
+ $u_role = $this->db->sql_nextid();
+
+ // Now add the correct data to the roles...
+ // The standard role says that new users are not able to send a PM, Mass PM, are not able to PM groups
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $u_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'u_%' AND auth_option IN ('u_sendpm', 'u_masspm', 'u_masspm_group')";
+ $this->sql_query($sql);
+
+ // Add user role to group
+ $sql = 'INSERT INTO ' . ACL_GROUPS_TABLE . " (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES ($group_id, 0, 0, $u_role, 0)";
+ $this->sql_query($sql);
+ }
+
+ // Insert new forum role
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_FORUM_NEW_MEMBER'
+ AND role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $f_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$f_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_FORUM_NEW_MEMBER', 'ROLE_DESCRIPTION_FORUM_NEW_MEMBER', 'f_', $next_order_id)";
+ $this->sql_query($sql);
+ $f_role = $this->db->sql_nextid();
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $f_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'f_%' AND auth_option IN ('f_noapprove')";
+ $this->sql_query($sql);
+ }
+
+ // Set every members user_new column to 0 (old users) only if there is no one yet (this makes sure we do not execute this more than once)
+ $sql = 'SELECT 1
+ FROM ' . USERS_TABLE . '
+ WHERE user_new = 0';
+ $result = $this->db->sql_query_limit($sql, 1);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_new = 0';
+ $this->sql_query($sql);
+ }
+
+ // To mimick the old "feature" we will assign the forum role to every forum, regardless of the setting (this makes sure there are no "this does not work!!!! YUO!!!" posts...
+ // Check if the role is already assigned...
+ $sql = 'SELECT forum_id
+ FROM ' . ACL_GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id . '
+ AND auth_role_id = ' . $f_role;
+ $result = $this->db->sql_query($sql);
+ $is_options = (int) $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ // Not assigned at all... :/
+ if (!$is_options)
+ {
+ // Get postable forums
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type != ' . FORUM_LINK;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->sql_query('INSERT INTO ' . ACL_GROUPS_TABLE . ' (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (' . $group_id . ', ' . (int) $row['forum_id'] . ', 0, ' . $f_role . ', 0)');
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ // Clear permissions...
+ include_once($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
+ $auth_admin = new \auth_admin();
+ $auth_admin->acl_clear_prefetch();
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php
new file mode 100644
index 0000000000..c52b71d8fa
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_6_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.6-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php
new file mode 100644
index 0000000000..2db3341b0a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_6_rc3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.6-RC3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_cp_fields'))),
+
+ array('config.update', array('version', '3.0.6-RC3')),
+ );
+ }
+
+ public function update_cp_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new \format
+ $sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . '
+ SET field_show_on_vt = 1
+ WHERE field_hide = 0
+ AND (field_required = 1 OR field_show_on_reg = 1 OR field_show_profile = 1)';
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php
new file mode 100644
index 0000000000..5734db25e6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_6_rc4 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.6-RC4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_6_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-RC4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php
new file mode 100644
index 0000000000..d1d602819d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_7 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.7', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_7_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php
new file mode 100644
index 0000000000..784e810f14
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_7_pl1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.7-pl1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_7');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7-pl1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php
new file mode 100644
index 0000000000..1843c3f262
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php
@@ -0,0 +1,82 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_7_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.7-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_6');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id' => array('topic_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time' => array('log_time'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('feed_overall', 1)),
+ array('config.add', array('feed_http_auth', 0)),
+ array('config.add', array('feed_limit_post', $this->config['feed_limit'])),
+ array('config.add', array('feed_limit_topic', $this->config['feed_overall_topics_limit'])),
+ array('config.add', array('feed_topics_new', $this->config['feed_overall_topics'])),
+ array('config.add', array('feed_topics_active', $this->config['feed_overall_topics'])),
+ array('custom', array(array(&$this, 'delete_text_templates'))),
+
+ array('config.update', array('version', '3.0.7-RC1')),
+ );
+ }
+
+ public function delete_text_templates()
+ {
+ // Delete all text-templates from the template_data
+ $sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . '
+ WHERE template_filename ' . $this->db->sql_like_expression($this->db->get_any_char() . '.txt');
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php
new file mode 100644
index 0000000000..e497a38765
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php
@@ -0,0 +1,79 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_7_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.7-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_7_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_email_hash'))),
+
+ array('config.update', array('version', '3.0.7-RC2')),
+ );
+ }
+
+ public function update_email_hash($start = 0)
+ {
+ $limit = 1000;
+
+ $sql = 'SELECT user_id, user_email, user_email_hash
+ FROM ' . USERS_TABLE . '
+ WHERE user_type <> ' . USER_IGNORE . "
+ AND user_email <> ''";
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $i = 0;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $i++;
+
+ // Snapshot of the phpbb_email_hash() function
+ // We cannot call it directly because the auto updater updates the DB first. :/
+ $user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']);
+
+ if ($user_email_hash != $row['user_email_hash'])
+ {
+ $sql_ary = array(
+ 'user_email_hash' => $user_email_hash,
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . (int) $row['user_id'];
+ $this->sql_query($sql);
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($i < $limit)
+ {
+ // Completed
+ return;
+ }
+
+ // Return the next start, will be sent to $start when this function is called again
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php
new file mode 100644
index 0000000000..04b5bd4a13
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_8 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.8', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_8_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.8')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php
new file mode 100644
index 0000000000..22fd51543b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php
@@ -0,0 +1,162 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_8_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.8-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_7_pl1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, '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 delete_orphan_shadow_topics()
+ {
+ // Delete shadow topics pointing to not existing topics
+ $batch_size = 500;
+
+ // Set of affected forums we have to resync
+ $sync_forum_ids = array();
+
+ $sql_array = array(
+ 'SELECT' => 't1.topic_id, t1.forum_id',
+ 'FROM' => array(
+ TOPICS_TABLE => 't1',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(TOPICS_TABLE => 't2'),
+ 'ON' => 't1.topic_moved_id = t2.topic_id',
+ ),
+ ),
+ 'WHERE' => 't1.topic_moved_id <> 0
+ AND t2.topic_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $topic_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $topic_ids[] = (int) $row['topic_id'];
+
+ $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($topic_ids))
+ {
+ $sql = 'DELETE FROM ' . TOPICS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('topic_id', $topic_ids);
+ $this->db->sql_query($sql);
+
+ // Sync the forums we have deleted shadow topics from.
+ sync('forum', 'forum_id', $sync_forum_ids, true, true);
+
+ return false;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php
new file mode 100644
index 0000000000..e69134c538
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_9 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.9', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php
new file mode 100644
index 0000000000..06e46d522f
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php
@@ -0,0 +1,130 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_9_rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.9-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_8');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'login_attempts' => array(
+ 'COLUMNS' => array(
+ // this column was removed from the database updater
+ // after 3.0.9-RC3 was released. It might still exist
+ // in 3.0.9-RCX installations and has to be dropped as
+ // soon as the db_tools class is capable of properly
+ // removing a primary key.
+ // 'attempt_id' => array('UINT', NULL, 'auto_increment'),
+ 'attempt_ip' => array('VCHAR:40', ''),
+ 'attempt_browser' => array('VCHAR:150', ''),
+ 'attempt_forwarded_for' => array('VCHAR:255', ''),
+ 'attempt_time' => array('TIMESTAMP', 0),
+ 'user_id' => array('UINT', 0),
+ 'username' => array('VCHAR_UNI:255', 0),
+ 'username_clean' => array('VCHAR_CI', 0),
+ ),
+ //'PRIMARY_KEY' => 'attempt_id',
+ 'KEYS' => array(
+ 'att_ip' => array('INDEX', array('attempt_ip', 'attempt_time')),
+ 'att_for' => array('INDEX', array('attempt_forwarded_for', 'attempt_time')),
+ 'att_time' => array('INDEX', array('attempt_time')),
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'bbcodes' => array(
+ 'bbcode_id' => array('USINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'login_attempts',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('ip_login_limit_max', 50)),
+ array('config.add', array('ip_login_limit_time', 21600)),
+ array('config.add', array('ip_login_limit_use_forwarded', 0)),
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'fix_firebird_qa_captcha'))),
+
+ array('config.update', array('version', '3.0.9-RC1')),
+ );
+ }
+
+ public function update_file_extension_group_names()
+ {
+ // Update file extension group names to use language strings, again.
+ $sql = 'SELECT group_id, group_name
+ FROM ' . EXTENSION_GROUPS_TABLE . '
+ WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->get_any_char());
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'group_name' => substr($row['group_name'], 10), // Strip off 'EXT_GROUP_'
+ );
+
+ $sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE group_id = ' . $row['group_id'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function fix_firebird_qa_captcha()
+ {
+ // Recover from potentially broken Q&A CAPTCHA table on firebird
+ // Q&A CAPTCHA was uninstallable, so it's safe to remove these
+ // without data loss
+ if ($this->db_tools->sql_layer == 'firebird')
+ {
+ $tables = array(
+ $this->table_prefix . 'captcha_questions',
+ $this->table_prefix . 'captcha_answers',
+ $this->table_prefix . 'qa_confirm',
+ );
+ foreach ($tables as $table)
+ {
+ if ($this->db_tools->sql_table_exists($table))
+ {
+ $this->db_tools->sql_table_drop($table);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php
new file mode 100644
index 0000000000..46fd51e7fe
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_9_rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.9-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php
new file mode 100644
index 0000000000..1696060735
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_9_rc3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.9-RC3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-RC3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php
new file mode 100644
index 0000000000..fdc92b52f9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v30x;
+
+class release_3_0_9_rc4 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.0.9-RC4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_9_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-RC4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/.htaccess b/phpBB/phpbb/db/migration/data/v310/.htaccess
new file mode 100644
index 0000000000..44242b5418
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/.htaccess
@@ -0,0 +1,33 @@
+# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from
+# module mod_authz_host to a new module called mod_access_compat (which may be
+# disabled) and a new "Require" syntax has been introduced to mod_authz_host.
+# We could just conditionally provide both versions, but unfortunately Apache
+# does not explicitly tell us its version if the module mod_version is not
+# available. In this case, we check for the availability of module
+# mod_authz_core (which should be on 2.4 or higher only) as a best guess.
+<IfModule mod_version.c>
+ <IfVersion < 2.4>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfVersion>
+ <IfVersion >= 2.4>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfVersion>
+</IfModule>
+<IfModule !mod_version.c>
+ <IfModule !mod_authz_core.c>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfModule>
+ <IfModule mod_authz_core.c>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfModule>
+</IfModule>
diff --git a/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php
new file mode 100644
index 0000000000..0ca4f2f19c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php
@@ -0,0 +1,81 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class acp_prune_users_module extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_langname = 'ACP_CAT_USERS'";
+ $result = $this->db->sql_query($sql);
+ $acp_cat_users_id = (int) $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'SELECT parent_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_basename = 'acp_prune'
+ AND module_mode = 'users'";
+ $result = $this->db->sql_query($sql);
+ $acp_prune_users_parent = (int) $this->db->sql_fetchfield('parent_id');
+ $this->db->sql_freeresult($result);
+
+ // Skip migration if "Users" category has been deleted
+ // or the module has already been moved to that category
+ return !$acp_cat_users_id || $acp_cat_users_id === $acp_prune_users_parent;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\beta1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'move_prune_users_module'))),
+ );
+ }
+
+ public function move_prune_users_module()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_basename = 'acp_prune'
+ AND module_mode = 'users'";
+ $result = $this->db->sql_query($sql);
+ $acp_prune_users_id = (int) $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_langname = 'ACP_CAT_USERS'";
+ $result = $this->db->sql_query($sql);
+ $acp_cat_users_id = (int) $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ if (!class_exists('\acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ }
+ $module_manager = new \acp_modules();
+ $module_manager->module_class = 'acp';
+ $module_manager->move_module($acp_prune_users_id, $acp_cat_users_id);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php
new file mode 100644
index 0000000000..4bd29f87d7
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class acp_style_components_module extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_langname = 'ACP_STYLE_COMPONENTS'";
+ $result = $this->db->sql_query($sql);
+ $module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ return $module_id == false;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_STYLE_COMPONENTS',
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/allow_cdn.php b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php
new file mode 100644
index 0000000000..286d20e20b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class allow_cdn extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['allow_cdn']);
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\jquery_update',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allow_cdn', (int) $this->config['load_jquery_cdn'])),
+ array('config.remove', array('load_jquery_cdn')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha1.php b/phpBB/phpbb/db/migration/data/v310/alpha1.php
new file mode 100644
index 0000000000..4a48d2830a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/alpha1.php
@@ -0,0 +1,53 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class alpha1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-a1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\local_url_bbcode',
+ '\phpbb\db\migration\data\v30x\release_3_0_12',
+ '\phpbb\db\migration\data\v310\acp_style_components_module',
+ '\phpbb\db\migration\data\v310\allow_cdn',
+ '\phpbb\db\migration\data\v310\auth_provider_oauth',
+ '\phpbb\db\migration\data\v310\avatars',
+ '\phpbb\db\migration\data\v310\boardindex',
+ '\phpbb\db\migration\data\v310\config_db_text',
+ '\phpbb\db\migration\data\v310\forgot_password',
+ '\phpbb\db\migration\data\v310\mod_rewrite',
+ '\phpbb\db\migration\data\v310\mysql_fulltext_drop',
+ '\phpbb\db\migration\data\v310\namespaces',
+ '\phpbb\db\migration\data\v310\notifications_cron',
+ '\phpbb\db\migration\data\v310\notification_options_reconvert',
+ '\phpbb\db\migration\data\v310\plupload',
+ '\phpbb\db\migration\data\v310\signature_module_auth',
+ '\phpbb\db\migration\data\v310\softdelete_mcp_modules',
+ '\phpbb\db\migration\data\v310\teampage',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-a1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha2.php b/phpBB/phpbb/db/migration/data/v310/alpha2.php
new file mode 100644
index 0000000000..bfbcc4f6f5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/alpha2.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class alpha2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-a2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha1',
+ '\phpbb\db\migration\data\v310\notifications_cron_p2',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-a2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha3.php b/phpBB/phpbb/db/migration/data/v310/alpha3.php
new file mode 100644
index 0000000000..bb0f904fd4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/alpha3.php
@@ -0,0 +1,39 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class alpha3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-a3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha2',
+ '\phpbb\db\migration\data\v310\avatar_types',
+ '\phpbb\db\migration\data\v310\passwords',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-a3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php
new file mode 100644
index 0000000000..2d51bd53e4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php
@@ -0,0 +1,77 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class auth_provider_oauth extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'auth_provider_oauth');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'oauth_tokens' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0), // phpbb_users.user_id
+ 'session_id' => array('CHAR:32', ''), // phpbb_sessions.session_id used only when user_id not set
+ 'provider' => array('VCHAR', ''), // Name of the OAuth provider
+ 'oauth_token' => array('MTEXT', ''), // Serialized token
+ ),
+ 'KEYS' => array(
+ 'user_id' => array('INDEX', 'user_id'),
+ 'provider' => array('INDEX', 'provider'),
+ ),
+ ),
+ $this->table_prefix . 'oauth_accounts' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'provider' => array('VCHAR', ''),
+ 'oauth_provider_id' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => array(
+ 'user_id',
+ 'provider',
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'oauth_tokens',
+ $this->table_prefix . 'oauth_accounts',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PROFILE',
+ array(
+ 'module_basename' => 'ucp_auth_link',
+ 'modes' => array('auth_link'),
+ ),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php
new file mode 100644
index 0000000000..e9e726ae20
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php
@@ -0,0 +1,44 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class auth_provider_oauth2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\auth_provider_oauth',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(
+ array($this, 'update_auth_link_module_auth'),
+ )),
+ );
+ }
+
+ public function update_auth_link_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'authmethod_oauth'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_auth_link'
+ AND module_mode = 'auth_link'
+ AND module_auth = ''";
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/avatar_types.php b/phpBB/phpbb/db/migration/data/v310/avatar_types.php
new file mode 100644
index 0000000000..117e93239d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/avatar_types.php
@@ -0,0 +1,64 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class avatar_types extends \phpbb\db\migration\migration
+{
+ /**
+ * @var avatar type map
+ */
+ protected $avatar_type_map = array(
+ AVATAR_UPLOAD => 'avatar.driver.upload',
+ AVATAR_REMOTE => 'avatar.driver.remote',
+ AVATAR_GALLERY => 'avatar.driver.local',
+ );
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ '\phpbb\db\migration\data\v310\avatars',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_user_avatar_type'))),
+ array('custom', array(array($this, 'update_group_avatar_type'))),
+ );
+ }
+
+ public function update_user_avatar_type()
+ {
+ foreach ($this->avatar_type_map as $old => $new)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_avatar_type = '$new'
+ WHERE user_avatar_type = '$old'";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ public function update_group_avatar_type()
+ {
+ foreach ($this->avatar_type_map as $old => $new)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "groups
+ SET group_avatar_type = '$new'
+ WHERE group_avatar_type = '$old'";
+ $this->db->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/avatars.php b/phpBB/phpbb/db/migration/data/v310/avatars.php
new file mode 100644
index 0000000000..9b03a8fa94
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/avatars.php
@@ -0,0 +1,95 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class avatars extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ // Get current avatar type of guest user
+ $sql = 'SELECT user_avatar_type
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_id = ' . ANONYMOUS;
+ $result = $this->db->sql_query($sql);
+ $backup_type = $this->db->sql_fetchfield('user_avatar_type');
+ $this->db->sql_freeresult($result);
+
+ // Try to set avatar type to string
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_avatar_type = 'avatar.driver.upload'
+ WHERE user_id = " . ANONYMOUS;
+ $this->db->sql_return_on_error(true);
+ $effectively_installed = $this->db->sql_query($sql);
+ $this->db->sql_return_on_error();
+
+ // Restore avatar type of guest user to previous state
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_avatar_type = '{$backup_type}'
+ WHERE user_id = " . ANONYMOUS;
+ $this->db->sql_query($sql);
+
+ return $effectively_installed !== false;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_avatar_type' => array('VCHAR:255', ''),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_avatar_type' => array('VCHAR:255', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_avatar_type' => array('TINT:2', ''),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_avatar_type' => array('TINT:2', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allow_avatar_gravatar', 0)),
+ array('custom', array(array($this, 'update_module_auth'))),
+ );
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "modules
+ SET module_auth = 'cfg_allow_avatar'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'avatar'";
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta1.php b/phpBB/phpbb/db/migration/data/v310/beta1.php
new file mode 100644
index 0000000000..9feba5235d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta1.php
@@ -0,0 +1,42 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-b1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha3',
+ '\phpbb\db\migration\data\v310\passwords_p2',
+ '\phpbb\db\migration\data\v310\postgres_fulltext_drop',
+ '\phpbb\db\migration\data\v310\profilefield_change_load_settings',
+ '\phpbb\db\migration\data\v310\profilefield_location',
+ '\phpbb\db\migration\data\v310\soft_delete_mod_convert2',
+ '\phpbb\db\migration\data\v310\ucp_popuppm_module',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta2.php b/phpBB/phpbb/db/migration/data/v310/beta2.php
new file mode 100644
index 0000000000..d5e31ce4cb
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta2.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-b2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta1',
+ '\phpbb\db\migration\data\v310\acp_prune_users_module',
+ '\phpbb\db\migration\data\v310\profilefield_location_cleanup',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta3.php b/phpBB/phpbb/db/migration/data/v310/beta3.php
new file mode 100644
index 0000000000..78c61e8e90
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta3.php
@@ -0,0 +1,41 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-b3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta2',
+ '\phpbb\db\migration\data\v310\auth_provider_oauth2',
+ '\phpbb\db\migration\data\v310\board_contact_name',
+ '\phpbb\db\migration\data\v310\jquery_update2',
+ '\phpbb\db\migration\data\v310\live_searches_config',
+ '\phpbb\db\migration\data\v310\prune_shadow_topics',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta4.php b/phpBB/phpbb/db/migration/data/v310/beta4.php
new file mode 100644
index 0000000000..e634785c38
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta4.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta4 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-b4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta3',
+ '\phpbb\db\migration\data\v310\extensions_version_check_force_unstable',
+ '\phpbb\db\migration\data\v310\reset_missing_captcha_plugin',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/board_contact_name.php b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php
new file mode 100644
index 0000000000..6f5188720b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class board_contact_name extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['board_contact_name']);
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\beta2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('board_contact_name', '')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/boardindex.php b/phpBB/phpbb/db/migration/data/v310/boardindex.php
new file mode 100644
index 0000000000..77a8558f21
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/boardindex.php
@@ -0,0 +1,29 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class boardindex extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['board_index_text']);
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('board_index_text', '')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/bot_update.php b/phpBB/phpbb/db/migration/data/v310/bot_update.php
new file mode 100644
index 0000000000..39b16c68f8
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/bot_update.php
@@ -0,0 +1,150 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class bot_update extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\rc6');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_bing_bot'))),
+ array('custom', array(array(&$this, 'update_bots'))),
+ );
+ }
+
+ public function update_bing_bot()
+ {
+ $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 update_bots()
+ {
+ // Update bots
+ if (!function_exists('user_delete'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $bots_updates = array(
+ // Bot Deletions
+ 'NG-Search [Bot]' => false,
+ 'Nutch/CVS [Bot]' => false,
+ 'OmniExplorer [Bot]' => false,
+ 'Seekport [Bot]' => false,
+ 'Synoo [Bot]' => false,
+ 'WiseNut [Bot]' => false,
+
+ // Bot Updates
+ // Bot name to bot user agent map
+ 'Baidu [Spider]' => 'Baiduspider',
+ 'Exabot [Bot]' => 'Exabot',
+ 'Voyager [Bot]' => 'voyager/',
+ 'W3C [Validator]' => 'W3C_Validator',
+ );
+
+ foreach ($bots_updates as $bot_name => $bot_agent)
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE user_type = ' . USER_IGNORE . "
+ AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'";
+ $result = $this->db->sql_query($sql);
+ $bot_user_id = (int) $this->db->sql_fetchfield('user_id');
+ $this->db->sql_freeresult($result);
+
+ if ($bot_user_id)
+ {
+ if ($bot_agent === false)
+ {
+ $sql = 'DELETE FROM ' . BOTS_TABLE . "
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+
+ user_delete('retain', $bot_user_id);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . BOTS_TABLE . "
+ SET bot_agent = '" . $this->db->sql_escape($bot_agent) . "'
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/captcha_plugins.php b/phpBB/phpbb/db/migration/data/v310/captcha_plugins.php
new file mode 100644
index 0000000000..328c08f1ec
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/captcha_plugins.php
@@ -0,0 +1,48 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class captcha_plugins extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc2',
+ );
+ }
+
+ public function update_data()
+ {
+ $captcha_plugin = $this->config['captcha_plugin'];
+ if (strpos($captcha_plugin, 'phpbb_captcha_') === 0)
+ {
+ $captcha_plugin = substr($captcha_plugin, strlen('phpbb_captcha_'));
+ }
+ else if (strpos($captcha_plugin, 'phpbb_') === 0)
+ {
+ $captcha_plugin = substr($captcha_plugin, strlen('phpbb_'));
+ }
+
+ return array(
+ array('if', array(
+ (is_file($this->phpbb_root_path . 'phpbb/captcha/plugins/' . $captcha_plugin . '.' . $this->php_ext)),
+ array('config.update', array('captcha_plugin', 'core.captcha.plugins.' . $captcha_plugin)),
+ )),
+ array('if', array(
+ (!is_file($this->phpbb_root_path . 'phpbb/captcha/plugins/' . $captcha_plugin . '.' . $this->php_ext)),
+ array('config.update', array('captcha_plugin', 'core.captcha.plugins.nogd')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/config_db_text.php b/phpBB/phpbb/db/migration/data/v310/config_db_text.php
new file mode 100644
index 0000000000..438883c438
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/config_db_text.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class config_db_text extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'config_text');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'config_text' => array(
+ 'COLUMNS' => array(
+ 'config_name' => array('VCHAR', ''),
+ 'config_value' => array('MTEXT', ''),
+ ),
+ 'PRIMARY_KEY' => 'config_name',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'config_text',
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php b/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php
new file mode 100644
index 0000000000..20bd547ac3
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/contact_admin_acp_module.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class contact_admin_acp_module extends \phpbb\db\migration\migration
+{
+ public function update_data()
+ {
+ return array(
+ array('module.add', array(
+ 'acp',
+ 'ACP_BOARD_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_contact',
+ 'modes' => array('contact'),
+ ),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php b/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php
new file mode 100644
index 0000000000..5736369f1a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/contact_admin_form.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class contact_admin_form extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['contact_admin_form_enable']);
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\config_db_text');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('contact_admin_form_enable', 1)),
+ array('custom', array(array($this, 'contact_admin_info'))),
+ );
+ }
+
+ public function contact_admin_info()
+ {
+ $text_config = new \phpbb\config\db_text($this->db, $this->table_prefix . 'config_text');
+ $text_config->set_array(array(
+ 'contact_admin_info' => '',
+ 'contact_admin_info_uid' => '',
+ 'contact_admin_info_bitfield' => '',
+ 'contact_admin_info_flags' => OPTION_FLAG_BBCODE + OPTION_FLAG_SMILIES + OPTION_FLAG_LINKS,
+ ));
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/dev.php b/phpBB/phpbb/db/migration/data/v310/dev.php
new file mode 100644
index 0000000000..f037191c2a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/dev.php
@@ -0,0 +1,415 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class dev extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return version_compare($this->config['version'], '3.1.0-dev', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\extensions',
+ '\phpbb\db\migration\data\v310\style_update_p2',
+ '\phpbb\db\migration\data\v310\timezone_p2',
+ '\phpbb\db\migration\data\v310\reported_posts_display',
+ '\phpbb\db\migration\data\v310\migrations_table',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'style_parent_id' => array('UINT:4', 0),
+ 'style_parent_tree' => array('TEXT', ''),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text' => array('MTEXT_UNI', ''),
+ 'reported_post_uid' => array('VCHAR:8', ''),
+ 'reported_post_bitfield' => array('VCHAR:255', ''),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm',
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path',
+ 'bbcode_bitfield',
+ 'style_parent_id',
+ 'style_parent_tree',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text',
+ 'reported_post_uid',
+ 'reported_post_bitfield',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ (strpos('phpbb_search_', $this->config['search_type']) !== 0),
+ array('config.update', array('search_type', 'phpbb_search_' . $this->config['search_type'])),
+ )),
+
+ array('config.add', array('fulltext_postgres_ts_name', 'simple')),
+ array('config.add', array('fulltext_postgres_min_word_len', 4)),
+ array('config.add', array('fulltext_postgres_max_word_len', 254)),
+ array('config.add', array('fulltext_sphinx_stopwords', 0)),
+ array('config.add', array('fulltext_sphinx_indexer_mem_limit', 512)),
+
+ array('config.add', array('load_jquery_cdn', 0)),
+ array('config.add', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')),
+
+ array('config.add', array('use_system_cron', 0)),
+
+ array('config.add', array('legend_sort_groupname', 0)),
+ array('config.add', array('teampage_forums', 1)),
+ array('config.add', array('teampage_memberships', 1)),
+
+ array('config.add', array('load_cpf_pm', 0)),
+
+ array('config.add', array('display_last_subject', 1)),
+
+ array('config.add', array('assets_version', 1)),
+
+ array('config.add', array('site_home_url', '')),
+ array('config.add', array('site_home_text', '')),
+
+ array('permission.add', array('u_chgprofileinfo', true, 'u_sig')),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_GROUPS',
+ array(
+ 'module_basename' => 'acp_groups',
+ 'modes' => array('position'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_ATTACHMENTS',
+ array(
+ 'module_basename' => 'acp_attachments',
+ 'modes' => array('manage'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_STYLE_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_styles',
+ 'modes' => array('install', 'cache'),
+ ),
+ )),
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PROFILE',
+ array(
+ 'module_basename' => 'ucp_profile',
+ 'modes' => array('autologin_keys'),
+ ),
+ )),
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_LANGUAGE'
+ )),
+
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_TEMPLATES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_THEMES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_IMAGESETS',
+ )),
+
+ array('custom', array(array($this, 'rename_module_basenames'))),
+ array('custom', array(array($this, 'rename_styles_module'))),
+ array('custom', array(array($this, 'add_group_teampage'))),
+ array('custom', array(array($this, 'update_group_legend'))),
+ array('custom', array(array($this, 'localise_global_announcements'))),
+ array('custom', array(array($this, 'update_ucp_pm_basename'))),
+ array('custom', array(array($this, 'update_ucp_profile_auth'))),
+ array('custom', array(array($this, 'move_customise_modules'))),
+
+ array('config.update', array('version', '3.1.0-dev')),
+ );
+ }
+
+ public function move_customise_modules()
+ {
+ // Move language management to new location in the Customise tab
+ // First get language module id
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_language'";
+ $result = $this->db->sql_query($sql);
+ $language_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+ // Next get language management module id of the one just created
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_langname = 'ACP_LANGUAGE'";
+ $result = $this->db->sql_query($sql);
+ $language_management_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ if (!class_exists('acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ }
+ // acp_modules calls adm_back_link, which is undefined at this point
+ if (!function_exists('adm_back_link'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext);
+ }
+ $module_manager = new \acp_modules();
+ $module_manager->module_class = 'acp';
+ $module_manager->move_module($language_module_id, $language_management_module_id);
+ }
+
+ public function update_ucp_pm_basename()
+ {
+ $sql = 'SELECT module_id, module_basename
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename <> 'ucp_pm' AND
+ module_langname='UCP_PM'";
+ $result = $this->db->sql_query_limit($sql, 1);
+
+ if ($row = $this->db->sql_fetchrow($result))
+ {
+ // This update is still not applied. Applying it
+
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_basename = 'ucp_pm'
+ WHERE module_id = " . (int) $row['module_id'];
+
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_ucp_profile_auth()
+ {
+ // Update the auth setting for the module
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'acl_u_chgprofileinfo'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'profile_info'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_styles_module()
+ {
+ // Rename styles module to Customise
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_langname = 'ACP_CAT_CUSTOMISE'
+ WHERE module_langname = 'ACP_CAT_STYLES'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_module_basenames()
+ {
+ // rename all module basenames to full classname
+ $sql = 'SELECT module_id, module_basename, module_class
+ FROM ' . MODULES_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $module_id = (int) $row['module_id'];
+ unset($row['module_id']);
+
+ if (!empty($row['module_basename']) && !empty($row['module_class']))
+ {
+ // all the class names start with class name or with phpbb_ for auto loading
+ if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 &&
+ strpos($row['module_basename'], 'phpbb_') !== 0)
+ {
+ $row['module_basename'] = $row['module_class'] . '_' . $row['module_basename'];
+
+ $sql_update = $this->db->sql_build_array('UPDATE', $row);
+
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET ' . $sql_update . '
+ WHERE module_id = ' . $module_id;
+ $this->sql_query($sql);
+ }
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+
+ public function add_group_teampage()
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 1
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'ADMINISTRATORS'";
+ $this->sql_query($sql);
+
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 2
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'GLOBAL_MODERATORS'";
+ $this->sql_query($sql);
+ }
+
+ public function update_group_legend()
+ {
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_legend = 1
+ ORDER BY group_name ASC';
+ $result = $this->db->sql_query($sql);
+
+ $next_legend = 1;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_legend = ' . $next_legend . '
+ WHERE group_id = ' . (int) $row['group_id'];
+ $this->sql_query($sql);
+
+ $next_legend++;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function localise_global_announcements()
+ {
+ // Localise Global Announcements
+ $sql = 'SELECT topic_id, topic_approved, (topic_replies + 1) AS topic_posts, topic_last_post_id, topic_last_post_subject, topic_last_post_time, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour
+ FROM ' . TOPICS_TABLE . '
+ WHERE forum_id = 0
+ AND topic_type = ' . POST_GLOBAL;
+ $result = $this->db->sql_query($sql);
+
+ $global_announcements = $update_lastpost_data = array();
+ $update_lastpost_data['forum_last_post_time'] = 0;
+ $update_forum_data = array(
+ 'forum_posts' => 0,
+ 'forum_topics' => 0,
+ 'forum_topics_real' => 0,
+ );
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $global_announcements[] = (int) $row['topic_id'];
+
+ $update_forum_data['forum_posts'] += (int) $row['topic_posts'];
+ $update_forum_data['forum_topics_real']++;
+ if ($row['topic_approved'])
+ {
+ $update_forum_data['forum_topics']++;
+ }
+
+ if ($update_lastpost_data['forum_last_post_time'] < $row['topic_last_post_time'])
+ {
+ $update_lastpost_data = array(
+ 'forum_last_post_id' => (int) $row['topic_last_post_id'],
+ 'forum_last_post_subject' => $row['topic_last_post_subject'],
+ 'forum_last_post_time' => (int) $row['topic_last_post_time'],
+ 'forum_last_poster_id' => (int) $row['topic_last_poster_id'],
+ 'forum_last_poster_name' => $row['topic_last_poster_name'],
+ 'forum_last_poster_colour' => $row['topic_last_poster_colour'],
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($global_announcements))
+ {
+ // Update the post/topic-count for the forum and the last-post if needed
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type = ' . FORUM_POST;
+ $result = $this->db->sql_query_limit($sql, 1);
+ $ga_forum_id = $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'SELECT forum_last_post_time
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $result = $this->db->sql_query($sql);
+ $lastpost = (int) $this->db->sql_fetchfield('forum_last_post_time');
+ $this->db->sql_freeresult($result);
+
+ $sql_update = 'forum_posts = forum_posts + ' . $update_forum_data['forum_posts'] . ', ';
+ $sql_update .= 'forum_topics_real = forum_topics_real + ' . $update_forum_data['forum_topics_real'] . ', ';
+ $sql_update .= 'forum_topics = forum_topics + ' . $update_forum_data['forum_topics'];
+ if ($lastpost < $update_lastpost_data['forum_last_post_time'])
+ {
+ $sql_update .= ', ' . $this->db->sql_build_array('UPDATE', $update_lastpost_data);
+ }
+
+ $sql = 'UPDATE ' . FORUMS_TABLE . '
+ SET ' . $sql_update . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $this->sql_query($sql);
+
+ // Update some forum_ids
+ $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
+ foreach ($table_ary as $table)
+ {
+ $sql = "UPDATE $table
+ SET forum_id = $ga_forum_id
+ WHERE " . $this->db->sql_in_set('topic_id', $global_announcements);
+ $this->sql_query($sql);
+ }
+ unset($table_ary);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/extensions.php b/phpBB/phpbb/db/migration/data/v310/extensions.php
new file mode 100644
index 0000000000..3171435482
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/extensions.php
@@ -0,0 +1,75 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class extensions extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'ext');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'ext' => array(
+ 'COLUMNS' => array(
+ 'ext_name' => array('VCHAR', ''),
+ 'ext_active' => array('BOOL', 0),
+ 'ext_state' => array('TEXT', ''),
+ ),
+ 'KEYS' => array(
+ 'ext_name' => array('UNIQUE', 'ext_name'),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'ext',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_EXTENSION_MANAGEMENT'
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_EXTENSION_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_extensions',
+ 'modes' => array('main'),
+ ),
+ )),
+ array('permission.add', array('a_extensions', true, 'a_styles')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php b/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php
new file mode 100644
index 0000000000..1d6276f484
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/extensions_version_check_force_unstable.php
@@ -0,0 +1,29 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class extensions_version_check_force_unstable extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('extension_force_unstable', false)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/forgot_password.php b/phpBB/phpbb/db/migration/data/v310/forgot_password.php
new file mode 100644
index 0000000000..362457cf23
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/forgot_password.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class forgot_password extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['allow_password_reset']);
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allow_password_reset', 1)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/gold.php b/phpBB/phpbb/db/migration/data/v310/gold.php
new file mode 100644
index 0000000000..188851f87d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/gold.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class gold extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc6',
+ '\phpbb\db\migration\data\v310\bot_update',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/jquery_update.php b/phpBB/phpbb/db/migration/data/v310/jquery_update.php
new file mode 100644
index 0000000000..8011331e80
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/jquery_update.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class jquery_update extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->config['load_jquery_url'] !== '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js';
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js')),
+ );
+ }
+
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/jquery_update2.php b/phpBB/phpbb/db/migration/data/v310/jquery_update2.php
new file mode 100644
index 0000000000..4061be5940
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/jquery_update2.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class jquery_update2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->config['load_jquery_url'] !== '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js';
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\jquery_update',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js')),
+ );
+ }
+
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/live_searches_config.php b/phpBB/phpbb/db/migration/data/v310/live_searches_config.php
new file mode 100644
index 0000000000..3d87e04ac2
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/live_searches_config.php
@@ -0,0 +1,29 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class live_searches_config extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['allow_live_searches']);
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allow_live_searches', '1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/migrations_table.php b/phpBB/phpbb/db/migration/data/v310/migrations_table.php
new file mode 100644
index 0000000000..48508b05c2
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/migrations_table.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class migrations_table extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'migrations');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'migrations' => array(
+ 'COLUMNS' => array(
+ 'migration_name' => array('VCHAR', ''),
+ 'migration_depends_on' => array('TEXT', ''),
+ 'migration_schema_done' => array('BOOL', 0),
+ 'migration_data_done' => array('BOOL', 0),
+ 'migration_data_state' => array('TEXT', ''),
+ 'migration_start_time' => array('TIMESTAMP', 0),
+ 'migration_end_time' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => 'migration_name',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'migrations',
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php b/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php
new file mode 100644
index 0000000000..85e479db59
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class mod_rewrite extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('enable_mod_rewrite', '0')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php
new file mode 100644
index 0000000000..e04a705c91
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php
@@ -0,0 +1,80 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class mysql_fulltext_drop extends \phpbb\db\migration\migration
+{
+ protected $indexes;
+
+ public function effectively_installed()
+ {
+ // This migration is irrelevant for all non-MySQL DBMSes.
+ if (strpos($this->db->get_sql_layer(), 'mysql') === false)
+ {
+ return true;
+ }
+
+ $this->find_indexes_to_drop();
+ return empty($this->indexes);
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_schema()
+ {
+ if (empty($this->indexes))
+ {
+ return array();
+ }
+
+ /*
+ * Drop FULLTEXT indexes related to MySQL fulltext search.
+ * Doing so is equivalent to dropping the search index from the ACP.
+ * Possibly time-consuming recreation of the search index (i.e.
+ * FULLTEXT indexes) is left as a task to the admin to not
+ * unnecessarily stall the upgrade process. The new search index will
+ * then require about 40% less table space (also see PHPBB3-11621).
+ */
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'posts' => $this->indexes,
+ ),
+ );
+ }
+
+ public function find_indexes_to_drop()
+ {
+ if ($this->indexes !== null)
+ {
+ return $this->indexes;
+ }
+
+ $this->indexes = array();
+ $potential_keys = array('post_subject', 'post_text', 'post_content');
+ foreach ($potential_keys as $key)
+ {
+ if ($this->db_tools->sql_index_exists($this->table_prefix . 'posts', $key))
+ {
+ $this->indexes[] = $key;
+ }
+ }
+
+ return $this->indexes;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/namespaces.php b/phpBB/phpbb/db/migration/data/v310/namespaces.php
new file mode 100644
index 0000000000..2a4935395e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/namespaces.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class namespaces extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ (preg_match('#^phpbb_search_#', $this->config['search_type'])),
+ array('config.update', array('search_type', str_replace('phpbb_search_', '\\phpbb\\search\\', $this->config['search_type']))),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php
new file mode 100644
index 0000000000..d43d432dd9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php
@@ -0,0 +1,142 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notification_options_reconvert extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\notifications_schema_fix');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'purge_notifications'))),
+ array('custom', array(array($this, 'convert_notifications'))),
+ );
+ }
+
+ public function purge_notifications()
+ {
+ $sql = 'DELETE FROM ' . $this->table_prefix . 'user_notifications';
+ $this->sql_query($sql);
+ }
+
+ public function convert_notifications($start)
+ {
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->table_prefix . 'user_notifications');
+
+ return $this->perform_conversion($insert_buffer, $start);
+ }
+
+ /**
+ * Perform the conversion (separate for testability)
+ *
+ * @param \phpbb\db\sql_insert_buffer $insert_buffer
+ * @param int $start Start of staggering step
+ * @return mixed int start of the next step, null if the end was reached
+ */
+ public function perform_conversion(\phpbb\db\sql_insert_buffer $insert_buffer, $start)
+ {
+ $limit = 250;
+ $converted_users = 0;
+ $start = $start ?: 0;
+
+ $sql = 'SELECT user_id, user_notify_type, user_notify_pm
+ FROM ' . $this->table_prefix . 'users
+ ORDER BY user_id';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted_users++;
+ $notification_methods = array();
+
+ // In-board notification
+ $notification_methods[] = '';
+
+ if ($row['user_notify_type'] == NOTIFY_EMAIL || $row['user_notify_type'] == NOTIFY_BOTH)
+ {
+ $notification_methods[] = 'email';
+ }
+
+ if ($row['user_notify_type'] == NOTIFY_IM || $row['user_notify_type'] == NOTIFY_BOTH)
+ {
+ $notification_methods[] = 'jabber';
+ }
+
+ // Notifications for posts
+ foreach (array('post', 'topic') as $item_type)
+ {
+ $this->add_method_rows(
+ $insert_buffer,
+ $item_type,
+ 0,
+ $row['user_id'],
+ $notification_methods
+ );
+ }
+
+ if ($row['user_notify_pm'])
+ {
+ // Notifications for private messages
+ // User either gets all methods or no method
+ $this->add_method_rows(
+ $insert_buffer,
+ 'pm',
+ 0,
+ $row['user_id'],
+ $notification_methods
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+
+ if ($converted_users < $limit)
+ {
+ // No more users left, we are done...
+ return;
+ }
+
+ return $start + $limit;
+ }
+
+ /**
+ * Insert method rows to DB
+ *
+ * @param \phpbb\db\sql_insert_buffer $insert_buffer
+ * @param string $item_type
+ * @param int $item_id
+ * @param int $user_id
+ * @param string $methods
+ */
+ protected function add_method_rows(\phpbb\db\sql_insert_buffer $insert_buffer, $item_type, $item_id, $user_id, array $methods)
+ {
+ $row_base = array(
+ 'item_type' => $item_type,
+ 'item_id' => (int) $item_id,
+ 'user_id' => (int) $user_id,
+ 'notify' => 1
+ );
+
+ foreach ($methods as $method)
+ {
+ $row_base['method'] = $method;
+ $insert_buffer->insert($row_base);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications.php b/phpBB/phpbb/db/migration/data/v310/notifications.php
new file mode 100644
index 0000000000..f4d012b5ac
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notifications.php
@@ -0,0 +1,102 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notifications extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'notifications');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'notification_types' => array(
+ 'COLUMNS' => array(
+ 'notification_type' => array('VCHAR:255', ''),
+ 'notification_type_enabled' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => array('notification_type', 'notification_type_enabled'),
+ ),
+ $this->table_prefix . 'notifications' => array(
+ 'COLUMNS' => array(
+ 'notification_id' => array('UINT', null, 'auto_increment'),
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'item_parent_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notification_read' => array('BOOL', 0),
+ 'notification_time' => array('TIMESTAMP', 1),
+ 'notification_data' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'notification_id',
+ 'KEYS' => array(
+ 'item_ident' => array('INDEX', array('item_type', 'item_id')),
+ 'user' => array('INDEX', array('user_id', 'notification_read')),
+ ),
+ ),
+ $this->table_prefix . 'user_notifications' => array(
+ 'COLUMNS' => array(
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'method' => array('VCHAR:255', ''),
+ 'notify' => array('BOOL', 1),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'notification_types',
+ $this->table_prefix . 'notifications',
+ $this->table_prefix . 'user_notifications',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.add', array(
+ 'ucp',
+ 'UCP_MAIN',
+ array(
+ 'module_basename' => 'ucp_notifications',
+ 'modes' => array('notification_list'),
+ ),
+ )),
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PREFS',
+ array(
+ 'module_basename' => 'ucp_notifications',
+ 'modes' => array('notification_options'),
+ ),
+ )),
+ array('config.add', array('load_notifications', 1)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php
new file mode 100644
index 0000000000..ba600f7bf5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notifications_cron extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\notifications');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('read_notification_expire_days', 30)),
+ array('config.add', array('read_notification_last_gc', 0)), // last run
+ array('config.add', array('read_notification_gc', (60 * 60 * 24))), // seconds between run; 1 day
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php
new file mode 100644
index 0000000000..263584b343
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notifications_cron_p2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\notifications_cron');
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Make read_notification_last_gc dynamic.
+ array('config.remove', array('read_notification_last_gc')),
+ array('config.add', array('read_notification_last_gc', 0, 1)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php
new file mode 100644
index 0000000000..21a39a7c91
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php
@@ -0,0 +1,98 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notifications_schema_fix extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\notifications');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'notification_types',
+ $this->table_prefix . 'notifications',
+ ),
+ 'add_tables' => array(
+ $this->table_prefix . 'notification_types' => array(
+ 'COLUMNS' => array(
+ 'notification_type_id' => array('USINT', null, 'auto_increment'),
+ 'notification_type_name' => array('VCHAR:255', ''),
+ 'notification_type_enabled' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => array('notification_type_id'),
+ 'KEYS' => array(
+ 'type' => array('UNIQUE', array('notification_type_name')),
+ ),
+ ),
+ $this->table_prefix . 'notifications' => array(
+ 'COLUMNS' => array(
+ 'notification_id' => array('UINT:10', null, 'auto_increment'),
+ 'notification_type_id' => array('USINT', 0),
+ 'item_id' => array('UINT', 0),
+ 'item_parent_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notification_read' => array('BOOL', 0),
+ 'notification_time' => array('TIMESTAMP', 1),
+ 'notification_data' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'notification_id',
+ 'KEYS' => array(
+ 'item_ident' => array('INDEX', array('notification_type_id', 'item_id')),
+ 'user' => array('INDEX', array('user_id', 'notification_read')),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'notification_types',
+ $this->table_prefix . 'notifications',
+ ),
+ 'add_tables' => array(
+ $this->table_prefix . 'notification_types' => array(
+ 'COLUMNS' => array(
+ 'notification_type' => array('VCHAR:255', ''),
+ 'notification_type_enabled' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => array('notification_type', 'notification_type_enabled'),
+ ),
+ $this->table_prefix . 'notifications' => array(
+ 'COLUMNS' => array(
+ 'notification_id' => array('UINT', null, 'auto_increment'),
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'item_parent_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notification_read' => array('BOOL', 0),
+ 'notification_time' => array('TIMESTAMP', 1),
+ 'notification_data' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'notification_id',
+ 'KEYS' => array(
+ 'item_ident' => array('INDEX', array('item_type', 'item_id')),
+ 'user' => array('INDEX', array('user_id', 'notification_read')),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_use_full_name.php b/phpBB/phpbb/db/migration/data/v310/notifications_use_full_name.php
new file mode 100644
index 0000000000..112c1e85e8
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_use_full_name.php
@@ -0,0 +1,184 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class notifications_use_full_name extends \phpbb\db\migration\migration
+{
+ protected $notification_types = array(
+ 'admin_activate_user',
+ 'approve_post',
+ 'approve_topic',
+ 'bookmark',
+ 'disapprove_post',
+ 'disapprove_topic',
+ 'group_request',
+ 'group_request_approved',
+ 'pm',
+ 'post',
+ 'post_in_queue',
+ 'quote',
+ 'report_pm',
+ 'report_pm_closed',
+ 'report_post',
+ 'report_post_closed',
+ 'topic',
+ 'topic_in_queue');
+
+ protected $notification_methods = array(
+ 'email',
+ 'jabber',
+ );
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_notifications_name'))),
+ array('custom', array(array($this, 'update_notifications_method_name'))),
+ );
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('custom', array(array($this, 'revert_notifications_name'))),
+ array('custom', array(array($this, 'revert_notifications_method_name'))),
+ );
+ }
+
+ public function update_notifications_method_name()
+ {
+ foreach ($this->notification_methods as $notification_method)
+ {
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET method = 'notification.method.{$notification_method}'
+ WHERE method = '{$notification_method}'";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ public function revert_notifications_method_name()
+ {
+ foreach ($this->notification_methods as $notification_method)
+ {
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET method = '{$notification_method}'
+ WHERE method = 'notification.method.{$notification_method}'";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ public function update_notifications_name()
+ {
+ $sql = 'UPDATE ' . NOTIFICATION_TYPES_TABLE . '
+ SET notification_type_enabled = 0
+ WHERE ' . $this->db->sql_in_set('notification_type_name', $this->notification_types, true);
+ $this->db->sql_query($sql);
+
+ foreach ($this->notification_types as $notification_type)
+ {
+ $sql = 'SELECT notification_type_id
+ FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = 'notification.type.{$notification_type}'";
+ $result = $this->db->sql_query($sql);
+ $new_type_id = (int) $this->db->sql_fetchfield('notification_type_id');
+ $this->db->sql_freeresult($result);
+
+ if ($new_type_id)
+ {
+ // New type name already exists,
+ // so we delete the old type and update the type id of existing entries.
+ $sql = 'SELECT notification_type_id
+ FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = '{$notification_type}'";
+ $result = $this->db->sql_query($sql);
+ $old_type_id = (int) $this->db->sql_fetchfield('notification_type_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'UPDATE ' . NOTIFICATIONS_TABLE . '
+ SET notification_type_id = ' . (int) $new_type_id . '
+ WHERE notification_type_id = ' . (int) $old_type_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = '{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ // Otherwise we just update the name
+ $sql = 'UPDATE ' . NOTIFICATION_TYPES_TABLE . "
+ SET notification_type_name = 'notification.type.{$notification_type}'
+ WHERE notification_type_name = '{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET item_type = 'notification.type.{$notification_type}'
+ WHERE item_type = '{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ public function revert_notifications_name()
+ {
+ foreach ($this->notification_types as $notification_type)
+ {
+ $sql = 'SELECT notification_type_id
+ FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = '{$notification_type}'";
+ $result = $this->db->sql_query($sql);
+ $new_type_id = (int) $this->db->sql_fetchfield('notification_type_id');
+ $this->db->sql_freeresult($result);
+
+ if ($new_type_id)
+ {
+ // New type name already exists,
+ // so we delete the old type and update the type id of existing entries.
+ $sql = 'SELECT notification_type_id
+ FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = 'notification.type.{$notification_type}'";
+ $result = $this->db->sql_query($sql);
+ $old_type_id = (int) $this->db->sql_fetchfield('notification_type_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'UPDATE ' . NOTIFICATIONS_TABLE . '
+ SET notification_type_id = ' . (int) $new_type_id . '
+ WHERE notification_type_id = ' . (int) $old_type_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . NOTIFICATION_TYPES_TABLE . "
+ WHERE notification_type_name = 'notification.type.{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ // Otherwise we just update the name
+ $sql = 'UPDATE ' . NOTIFICATION_TYPES_TABLE . "
+ SET notification_type_name = '{$notification_type}'
+ WHERE notification_type_name = 'notification.type.{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET item_type = '{$notification_type}'
+ WHERE item_type = 'notification.type.{$notification_type}'";
+ $this->db->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords.php b/phpBB/phpbb/db/migration/data/v310/passwords.php
new file mode 100644
index 0000000000..adee44147f
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords.php
@@ -0,0 +1,50 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_password' => array('VCHAR:255', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_password' => array('VCHAR:255', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_password' => array('VCHAR:40', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_password' => array('VCHAR:40', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php
new file mode 100644
index 0000000000..295f2d2a14
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php
@@ -0,0 +1,81 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords_convert_p1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\passwords_p2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_passwords'))),
+ );
+ }
+
+ public function update_passwords($start)
+ {
+ // Nothing to do if user_pass_convert column doesn't exist
+ if (!$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert'))
+ {
+ return;
+ }
+
+ $start = (int) $start;
+ $limit = 1000;
+ $converted_users = 0;
+
+ $sql = 'SELECT user_password, user_id
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_pass_convert = 1
+ ORDER BY user_id';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $update_users = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted_users++;
+
+ $user_id = (int) $row['user_id'];
+ // Only prefix passwords without proper prefix
+ if (!isset($update_users[$user_id]) && !preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $row['user_password']))
+ {
+ // Use $CP$ prefix for passwords that need to
+ // be converted and set pass convert to false.
+ $update_users[$user_id] = '$CP$' . $row['user_password'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($update_users as $user_id => $user_password)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_password = '" . $this->db->sql_escape($user_password) . "'
+ WHERE user_id = $user_id";
+ $this->sql_query($sql);
+ }
+
+ if ($converted_users < $limit)
+ {
+ // There are no more users to be converted
+ return;
+ }
+
+ // There are still more users to query, return the next start value
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php
new file mode 100644
index 0000000000..26a99184a6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords_convert_p2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\passwords_convert_p1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_pass_convert',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_pass_convert' => array('BOOL', 0, 'after' => 'user_passchg'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php
new file mode 100644
index 0000000000..afc7ba2813
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php
@@ -0,0 +1,44 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords_p2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\passwords');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_newpasswd' => array('VCHAR:255', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_newpasswd' => array('VCHAR:40', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/plupload.php b/phpBB/phpbb/db/migration/data/v310/plupload.php
new file mode 100644
index 0000000000..69367f86a9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/plupload.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class plupload extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['plupload_last_gc']) &&
+ isset($this->config['plupload_salt']);
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('plupload_last_gc', 0)),
+ array('config.add', array('plupload_salt', unique_id())),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php b/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php
new file mode 100644
index 0000000000..3457c19478
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/postgres_fulltext_drop.php
@@ -0,0 +1,80 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class postgres_fulltext_drop extends \phpbb\db\migration\migration
+{
+ protected $indexes;
+
+ public function effectively_installed()
+ {
+ // This migration is irrelevant for all non-PostgreSQL DBMSes.
+ if (strpos($this->db->get_sql_layer(), 'postgres') === false)
+ {
+ return true;
+ }
+
+ $this->find_indexes_to_drop();
+ return empty($this->indexes);
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_schema()
+ {
+ if (empty($this->indexes))
+ {
+ return array();
+ }
+
+ /*
+ * Drop FULLTEXT indexes related to PostgreSQL fulltext search.
+ * Doing so is equivalent to dropping the search index from the ACP.
+ * Possibly time-consuming recreation of the search index (i.e.
+ * FULLTEXT indexes) is left as a task to the admin to not
+ * unnecessarily stall the upgrade process. The new search index will
+ * then require about 40% less table space (also see PHPBB3-11040).
+ */
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'posts' => $this->indexes,
+ ),
+ );
+ }
+
+ public function find_indexes_to_drop()
+ {
+ if ($this->indexes !== null)
+ {
+ return $this->indexes;
+ }
+
+ $this->indexes = array();
+ $potential_keys = array('post_subject', 'post_text', 'post_content');
+ foreach ($potential_keys as $key)
+ {
+ if ($this->db_tools->sql_index_exists($this->table_prefix . 'posts', $key))
+ {
+ $this->indexes[] = $key;
+ }
+ }
+
+ return $this->indexes;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php b/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php
new file mode 100644
index 0000000000..65d4fe1078
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_aol.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_aol extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_yahoo_cleanup',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_aol';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_aol',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_aol',
+ 'field_length' => '40',
+ 'field_minlen' => '5',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_on_ml' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => '',
+ 'field_contact_url' => '',
+ );
+
+ protected $user_column_name = 'user_aim';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php
new file mode 100644
index 0000000000..f884d83d26
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_aol_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_aol_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_aim');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_aol',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_aim',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_aim' => array('VCHAR_UNI', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php b/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php
new file mode 100644
index 0000000000..7cc4fd8daa
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_change_load_settings.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_change_load_settings extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_aol_cleanup',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('load_cpf_memberlist', '1')),
+ array('config.update', array('load_cpf_pm', '1')),
+ array('config.update', array('load_cpf_viewprofile', '1')),
+ array('config.update', array('load_cpf_viewtopic', '1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php
new file mode 100644
index 0000000000..c44167dbfe
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_occ') &&
+ !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_interests');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_interests',
+ '\phpbb\db\migration\data\v310\profilefield_occupation',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_occ',
+ 'user_interests',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_occ' => array('MTEXT', ''),
+ 'user_interests' => array('MTEXT', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php b/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php
new file mode 100644
index 0000000000..02cd420c0f
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_contact_field.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_contact_field extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_is_contact');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_on_memberlist',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_is_contact' => array('BOOL', 0),
+ 'field_contact_desc' => array('VCHAR', ''),
+ 'field_contact_url' => array('VCHAR', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_is_contact',
+ 'field_contact_desc',
+ 'field_contact_url',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php b/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php
new file mode 100644
index 0000000000..7324b893cc
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_facebook.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_facebook extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_facebook';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_facebook',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_facebook',
+ 'field_length' => '20',
+ 'field_minlen' => '5',
+ 'field_maxlen' => '50',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[\w.]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VIEW_FACEBOOK_PROFILE',
+ 'field_contact_url' => 'http://facebook.com/%s/',
+ );
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_field_validation_length.php b/phpBB/phpbb/db/migration/data/v310/profilefield_field_validation_length.php
new file mode 100644
index 0000000000..c7d8b2dc91
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_field_validation_length.php
@@ -0,0 +1,90 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_field_validation_length extends \phpbb\db\migration\migration
+{
+ protected $validation_options_old = array(
+ 'ALPHA_SPACERS' => '[\w_\+\. \-\[\]]+',
+ );
+
+ protected $validation_options_new = array(
+ 'ALPHA_SPACERS' => '[\w\x20_+\-\[\]]+',
+ );
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc3',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_validation' => array('VCHAR_UNI:64', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_validation' => array('VCHAR_UNI:20', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_profile_fields_validation'))),
+ );
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('custom', array(array($this, 'revert_profile_fields_validation'))),
+ );
+ }
+
+ public function update_profile_fields_validation()
+ {
+ foreach ($this->validation_options_new as $validation_type => $regex)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields
+ SET field_validation = '" . $this->db->sql_escape($this->validation_options_new[$validation_type]) . "'
+ WHERE field_validation = '" . $this->db->sql_escape($this->validation_options_old[$validation_type]) . "'";
+ $this->sql_query($sql);
+ }
+ }
+
+ public function revert_profile_fields_validation()
+ {
+ foreach ($this->validation_options_new as $validation_type => $regex)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields
+ SET field_validation = '" . $this->db->sql_escape($this->validation_options_old[$validation_type]) . "'
+ WHERE field_validation = '" . $this->db->sql_escape($this->validation_options_new[$validation_type]) . "'";
+ $this->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php b/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php
new file mode 100644
index 0000000000..3b0963fc19
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_googleplus.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_googleplus extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_googleplus';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_googleplus',
+ 'field_type' => 'profilefields.type.googleplus',
+ 'field_ident' => 'phpbb_googleplus',
+ 'field_length' => '20',
+ 'field_minlen' => '3',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[\w]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VIEW_GOOGLEPLUS_PROFILE',
+ 'field_contact_url' => 'http://plus.google.com/%s',
+ );
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php b/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php
new file mode 100644
index 0000000000..e61653f3db
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_icq.php
@@ -0,0 +1,54 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_icq extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_icq';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_icq',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_icq',
+ 'field_length' => '20',
+ 'field_minlen' => '3',
+ 'field_maxlen' => '15',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[0-9]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'SEND_ICQ_MESSAGE',
+ 'field_contact_url' => 'https://www.icq.com/people/%s/',
+ );
+
+ protected $user_column_name = 'user_icq';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php
new file mode 100644
index 0000000000..516c690093
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_icq_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_icq_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_icq');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_icq',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_icq',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_icq' => array('VCHAR:20', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php b/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php
new file mode 100644
index 0000000000..33a5ba15ae
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php
@@ -0,0 +1,52 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_interests extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_interests';
+
+ protected $profilefield_database_type = array('MTEXT', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_interests',
+ 'field_type' => 'profilefields.type.text',
+ 'field_ident' => 'phpbb_interests',
+ 'field_length' => '3|30',
+ 'field_minlen' => '2',
+ 'field_maxlen' => '500',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 0,
+ 'field_show_on_vt' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ );
+
+ protected $user_column_name = 'user_interests';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_location.php b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php
new file mode 100644
index 0000000000..2d27c09e68
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php
@@ -0,0 +1,52 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_location extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ '\phpbb\db\migration\data\v310\profilefield_on_memberlist',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_location';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_location',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_location',
+ 'field_length' => '20',
+ 'field_minlen' => '2',
+ 'field_maxlen' => '100',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ );
+
+ protected $user_column_name = 'user_from';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php
new file mode 100644
index 0000000000..b824e3406a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_location_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_from');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_location',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_from',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_from' => array('VCHAR_UNI:100', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php b/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php
new file mode 100644
index 0000000000..75df2bcdee
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_occupation extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_interests',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_occupation';
+
+ protected $profilefield_database_type = array('MTEXT', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_occupation',
+ 'field_type' => 'profilefields.type.text',
+ 'field_ident' => 'phpbb_occupation',
+ 'field_length' => '3|30',
+ 'field_minlen' => '2',
+ 'field_maxlen' => '500',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 0,
+ 'field_show_on_vt' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ );
+
+ protected $user_column_name = 'user_occ';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php
new file mode 100644
index 0000000000..7ce5de0f00
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_on_memberlist extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_show_on_ml');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_cleanup',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_ml' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_ml',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php b/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php
new file mode 100644
index 0000000000..5fc88b6809
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_show_novalue.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_show_novalue extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields', 'field_show_novalue');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\profilefield_types');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php b/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php
new file mode 100644
index 0000000000..0dbe9041bb
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_skype.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_skype extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_skype';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_skype',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_skype',
+ 'field_length' => '20',
+ 'field_minlen' => '6',
+ 'field_maxlen' => '32',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[a-zA-Z][\w\.,\-_]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VIEW_SKYPE_PROFILE',
+ 'field_contact_url' => 'skype:%s?userinfo',
+ );
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php b/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php
new file mode 100644
index 0000000000..850e096439
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_twitter.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_twitter extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_twitter';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_twitter',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_twitter',
+ 'field_length' => '20',
+ 'field_minlen' => '1',
+ 'field_maxlen' => '15',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[\w_]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VIEW_TWITTER_PROFILE',
+ 'field_contact_url' => 'http://twitter.com/%s',
+ );
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_types.php b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
new file mode 100644
index 0000000000..5045eb8807
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
@@ -0,0 +1,110 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_types extends \phpbb\db\migration\migration
+{
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha2',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_type' => array('VCHAR:100', ''),
+ ),
+ $this->table_prefix . 'profile_fields_lang' => array(
+ 'field_type' => array('VCHAR:100', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_profile_fields_type'))),
+ array('custom', array(array($this, 'update_profile_fields_lang_type'))),
+ );
+ }
+
+ public function update_profile_fields_type()
+ {
+ // Update profile field types
+ $sql = 'SELECT field_type
+ FROM ' . $this->table_prefix . 'profile_fields
+ GROUP BY field_type';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields
+ SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "'
+ WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'";
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_profile_fields_lang_type()
+ {
+ // Update profile field language types
+ $sql = 'SELECT field_type
+ FROM ' . $this->table_prefix . 'profile_fields_lang
+ GROUP BY field_type';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields_lang
+ SET field_type = '" . $this->db->sql_escape($this->convert_phpbb30_field_type($row['field_type'])) . "'
+ WHERE field_type = '" . $this->db->sql_escape($row['field_type']) . "'";
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Determine the new field type for a given phpBB 3.0 field type
+ *
+ * @param $field_type string Field type in 3.0
+ * @return string Field new type which is used since 3.1
+ */
+ public function convert_phpbb30_field_type($field_type)
+ {
+ switch ($field_type)
+ {
+ case FIELD_INT:
+ return 'profilefields.type.int';
+ case FIELD_STRING:
+ return 'profilefields.type.string';
+ case FIELD_TEXT:
+ return 'profilefields.type.text';
+ case FIELD_BOOL:
+ return 'profilefields.type.bool';
+ case FIELD_DROPDOWN:
+ return 'profilefields.type.dropdown';
+ case FIELD_DATE:
+ return 'profilefields.type.date';
+ default:
+ return $field_type;
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_website.php b/phpBB/phpbb/db/migration/data/v310/profilefield_website.php
new file mode 100644
index 0000000000..e1e10f09f4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_website.php
@@ -0,0 +1,56 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_website extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_on_memberlist',
+ '\phpbb\db\migration\data\v310\profilefield_icq_cleanup',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_website';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_website',
+ 'field_type' => 'profilefields.type.url',
+ 'field_ident' => 'phpbb_website',
+ 'field_length' => '40',
+ 'field_minlen' => '12',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_on_ml' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VISIT_WEBSITE',
+ 'field_contact_url' => '%s',
+ );
+
+ protected $user_column_name = 'user_website';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php
new file mode 100644
index 0000000000..94442f0497
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_website_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_website_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_website');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_website',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_website',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_website' => array('VCHAR_UNI:200', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php
new file mode 100644
index 0000000000..2cd333fcbd
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_wlm extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_website_cleanup',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_wlm';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_wlm',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_wlm',
+ 'field_length' => '40',
+ 'field_minlen' => '5',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_on_ml' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => '',
+ 'field_contact_url' => '',
+ );
+
+ protected $user_column_name = 'user_msnm';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php
new file mode 100644
index 0000000000..7ef9e44020
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_wlm_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_wlm_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_msnm');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_wlm',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_msnm',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_msnm' => array('VCHAR_UNI', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php
new file mode 100644
index 0000000000..e269f88420
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo.php
@@ -0,0 +1,55 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_yahoo extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_wlm_cleanup',
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_yahoo';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_yahoo',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_yahoo',
+ 'field_length' => '40',
+ 'field_minlen' => '5',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_on_ml' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'SEND_YIM_MESSAGE',
+ 'field_contact_url' => 'http://edit.yahoo.com/config/send_webmesg?.target=%s&amp;.src=pg',
+ );
+
+ protected $user_column_name = 'user_yim';
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php
new file mode 100644
index 0000000000..bd724ff7db
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_yahoo_cleanup.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_yahoo_cleanup extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_yim');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_yahoo',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_yim',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_yim' => array('VCHAR_UNI', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php b/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php
new file mode 100644
index 0000000000..40a569d2a2
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_youtube.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class profilefield_youtube extends \phpbb\db\migration\profilefield_base_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\profilefield_contact_field',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
+ '\phpbb\db\migration\data\v310\profilefield_types',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ protected $profilefield_name = 'phpbb_youtube';
+
+ protected $profilefield_database_type = array('VCHAR', '');
+
+ protected $profilefield_data = array(
+ 'field_name' => 'phpbb_youtube',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_youtube',
+ 'field_length' => '20',
+ 'field_minlen' => '3',
+ 'field_maxlen' => '60',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '[a-zA-Z][\w\.,\-_]+',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => 'VIEW_YOUTUBE_CHANNEL',
+ 'field_contact_url' => 'http://youtube.com/user/%s',
+ );
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php b/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php
new file mode 100644
index 0000000000..f6d27d385e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/prune_shadow_topics.php
@@ -0,0 +1,50 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class prune_shadow_topics extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'enable_shadow_prune' => array('BOOL', 0),
+ 'prune_shadow_days' => array('UINT', 7),
+ 'prune_shadow_freq' => array('UINT', 1),
+ 'prune_shadow_next' => array('INT:11', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'enable_shadow_prune',
+ 'prune_shadow_days',
+ 'prune_shadow_freq',
+ 'prune_shadow_next',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc1.php b/phpBB/phpbb/db/migration/data/v310/rc1.php
new file mode 100644
index 0000000000..751208c6ca
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc1.php
@@ -0,0 +1,44 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta4',
+ '\phpbb\db\migration\data\v310\contact_admin_acp_module',
+ '\phpbb\db\migration\data\v310\contact_admin_form',
+ '\phpbb\db\migration\data\v310\passwords_convert_p2',
+ '\phpbb\db\migration\data\v310\profilefield_facebook',
+ '\phpbb\db\migration\data\v310\profilefield_googleplus',
+ '\phpbb\db\migration\data\v310\profilefield_skype',
+ '\phpbb\db\migration\data\v310\profilefield_twitter',
+ '\phpbb\db\migration\data\v310\profilefield_youtube',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc2.php b/phpBB/phpbb/db/migration/data/v310/rc2.php
new file mode 100644
index 0000000000..5cd0393a13
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc2.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc3.php b/phpBB/phpbb/db/migration/data/v310/rc3.php
new file mode 100644
index 0000000000..9fb483ef6a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc3.php
@@ -0,0 +1,40 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc3 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc2',
+ '\phpbb\db\migration\data\v310\captcha_plugins',
+ '\phpbb\db\migration\data\v310\rename_too_long_indexes',
+ '\phpbb\db\migration\data\v310\search_type',
+ '\phpbb\db\migration\data\v310\topic_sort_username',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc4.php b/phpBB/phpbb/db/migration/data/v310/rc4.php
new file mode 100644
index 0000000000..0d756c762c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc4.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc4 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc3',
+ '\phpbb\db\migration\data\v310\notifications_use_full_name',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc5.php b/phpBB/phpbb/db/migration/data/v310/rc5.php
new file mode 100644
index 0000000000..d92537d877
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc5.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc5 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC5', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc4',
+ '\phpbb\db\migration\data\v310\profilefield_field_validation_length',
+ '\phpbb\db\migration\data\v310\remove_acp_styles_cache',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC5')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc6.php b/phpBB/phpbb/db/migration/data/v310/rc6.php
new file mode 100644
index 0000000000..1df502a1e4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc6.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc6 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.0-RC6', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\rc5',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC6')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/remove_acp_styles_cache.php b/phpBB/phpbb/db/migration/data/v310/remove_acp_styles_cache.php
new file mode 100644
index 0000000000..7b84539814
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/remove_acp_styles_cache.php
@@ -0,0 +1,51 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class remove_acp_styles_cache extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'acp'
+ AND module_langname = 'ACP_STYLES_CACHE'";
+ $result = $this->db->sql_query($sql);
+ $module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ return !$module_id;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.remove', array(
+ 'acp',
+ 'ACP_STYLE_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_styles',
+ 'module_langname' => 'ACP_STYLES_CACHE',
+ 'module_mode' => 'cache',
+ 'module_auth' => 'acl_a_styles',
+ ),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rename_too_long_indexes.php b/phpBB/phpbb/db/migration/data/v310/rename_too_long_indexes.php
new file mode 100644
index 0000000000..8d2a15d8ea
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rename_too_long_indexes.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rename_too_long_indexes extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_0');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'search_wordmatch' => array(
+ 'unq_mtch',
+ ),
+ ),
+ 'add_unique_index' => array(
+ $this->table_prefix . 'search_wordmatch' => array(
+ 'un_mtch' => array('word_id', 'post_id', 'title_match'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php b/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php
new file mode 100644
index 0000000000..575a65d9dd
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php
@@ -0,0 +1,53 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class reported_posts_display extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'reports', 'reported_post_enable_bbcode');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode' => array('BOOL', 1),
+ 'reported_post_enable_smilies' => array('BOOL', 1),
+ 'reported_post_enable_magic_url' => array('BOOL', 1),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode',
+ 'reported_post_enable_smilies',
+ 'reported_post_enable_magic_url',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php b/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php
new file mode 100644
index 0000000000..8211457dc6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/reset_missing_captcha_plugin.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+/**
+* Class captcha_plugin
+*
+* Reset the captcha setting to the default plugin if the defined 'captcha_plugin' is missing.
+*/
+class reset_missing_captcha_plugin extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ (is_dir($this->phpbb_root_path . 'includes/captcha/plugins/') &&
+ !is_file($this->phpbb_root_path . "includes/captcha/plugins/{$this->config['captcha_plugin']}_plugin." . $this->php_ext)),
+ array('config.update', array('captcha_plugin', 'phpbb_captcha_nogd')),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/search_type.php b/phpBB/phpbb/db/migration/data/v310/search_type.php
new file mode 100644
index 0000000000..f89456ae19
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/search_type.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class search_type extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('if', array(
+ (is_file($this->phpbb_root_path . 'phpbb/search/' . $this->config['search_type'] . $this->php_ext)),
+ array('config.update', array('search_type', '\\phpbb\\search\\' . $this->config['search_type'])),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php b/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php
new file mode 100644
index 0000000000..e50f5e53a0
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php
@@ -0,0 +1,57 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class signature_module_auth extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_auth
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'signature'";
+ $result = $this->db->sql_query($sql);
+ $module_auth = $this->db->sql_fetchfield('module_auth');
+ $this->db->sql_freeresult($result);
+
+ return $module_auth === 'acl_u_sig' || $module_auth === false;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(
+ array($this, 'update_signature_module_auth'),
+ ),
+ ),
+ );
+ }
+
+ public function update_signature_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'acl_u_sig'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'signature'
+ AND module_auth = ''";
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php
new file mode 100644
index 0000000000..85b90da5fa
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert.php
@@ -0,0 +1,127 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+use phpbb\db\migration\container_aware_migration;
+
+/**
+ * Migration to convert the Soft Delete MOD for 3.0
+ *
+ * https://www.phpbb.com/customise/db/mod/soft_delete/
+ */
+class soft_delete_mod_convert extends container_aware_migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha3',
+ );
+ }
+
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_deleted');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('permission.remove', array('m_harddelete', true)),
+ array('permission.remove', array('m_harddelete', false)),
+
+ array('custom', array(array($this, 'convert_posts'))),
+ array('custom', array(array($this, 'convert_topics'))),
+ );
+ }
+
+ public function convert_posts($start)
+ {
+ $content_visibility = $this->get_content_visibility();
+
+ $limit = 250;
+ $i = 0;
+
+ $sql = 'SELECT p.*, t.topic_first_post_id, t.topic_last_post_id
+ FROM ' . $this->table_prefix . 'posts p, ' . $this->table_prefix . 'topics t
+ WHERE p.post_deleted > 0
+ AND t.topic_id = p.topic_id';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $content_visibility->set_post_visibility(
+ ITEM_DELETED,
+ $row['post_id'],
+ $row['topic_id'],
+ $row['forum_id'],
+ $row['post_deleted'],
+ $row['post_deleted_time'],
+ '',
+ ($row['post_id'] == $row['topic_first_post_id']) ? true : false,
+ ($row['post_id'] == $row['topic_last_post_id']) ? true : false
+ );
+
+ $i++;
+ }
+
+ $this->db->sql_freeresult($result);
+
+ if ($i == $limit)
+ {
+ return $start + $i;
+ }
+ }
+
+ public function convert_topics($start)
+ {
+ $content_visibility = $this->get_content_visibility();
+
+ $limit = 100;
+ $i = 0;
+
+ $sql = 'SELECT *
+ FROM ' . $this->table_prefix . 'topics
+ WHERE topic_deleted > 0';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $content_visibility->set_topic_visibility(
+ ITEM_DELETED,
+ $row['topic_id'],
+ $row['forum_id'],
+ $row['topic_deleted'],
+ $row['topic_deleted_time'],
+ ''
+ );
+
+ $i++;
+ }
+
+ $this->db->sql_freeresult($result);
+
+ if ($i == $limit)
+ {
+ return $start + $i;
+ }
+ }
+
+ /**
+ * @return \phpbb\content_visibility
+ */
+ protected function get_content_visibility()
+ {
+ return $this->container->get('content.visibility');
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php
new file mode 100644
index 0000000000..246a267a8c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/soft_delete_mod_convert2.php
@@ -0,0 +1,66 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+/**
+ * Migration to convert the Soft Delete MOD for 3.0
+ *
+ * https://www.phpbb.com/customise/db/mod/soft_delete/
+ */
+class soft_delete_mod_convert2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\soft_delete_mod_convert',
+ );
+ }
+
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_deleted');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array('forum_deleted_topic_count', 'forum_deleted_reply_count'),
+ $this->table_prefix . 'posts' => array('post_deleted', 'post_deleted_time'),
+ $this->table_prefix . 'topics' => array('topic_deleted', 'topic_deleted_time', 'topic_deleted_reply_count'),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_deleted_topic_count' => array('UINT', 0),
+ 'forum_deleted_reply_count' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_deleted' => array('UINT', 0),
+ 'post_deleted_time' => array('TIMESTAMP', 0),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_deleted' => array('UINT', 0),
+ 'topic_deleted_time' => array('TIMESTAMP', 0),
+ 'topic_deleted_reply_count' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php b/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php
new file mode 100644
index 0000000000..5e68db5889
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php
@@ -0,0 +1,61 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class softdelete_mcp_modules extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'mcp'
+ AND module_basename = 'mcp_queue'
+ AND module_mode = 'deleted_topics'";
+ $result = $this->db->sql_query($sql);
+ $module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ return $module_id !== false;
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ '\phpbb\db\migration\data\v310\softdelete_p2',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.add', array(
+ 'mcp',
+ 'MCP_QUEUE',
+ array(
+ 'module_basename' => 'mcp_queue',
+ 'modes' => array('deleted_topics'),
+ ),
+ )),
+ array('module.add', array(
+ 'mcp',
+ 'MCP_QUEUE',
+ array(
+ 'module_basename' => 'mcp_queue',
+ 'modes' => array('deleted_posts'),
+ ),
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php
new file mode 100644
index 0000000000..b1e7486e24
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php
@@ -0,0 +1,211 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class softdelete_p1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_visibility');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_posts_approved' => array('UINT', 0),
+ 'forum_posts_unapproved' => array('UINT', 0),
+ 'forum_posts_softdeleted' => array('UINT', 0),
+ 'forum_topics_approved' => array('UINT', 0),
+ 'forum_topics_unapproved' => array('UINT', 0),
+ 'forum_topics_softdeleted' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_visibility' => array('TINT:3', 0),
+ 'post_delete_time' => array('TIMESTAMP', 0),
+ 'post_delete_reason' => array('STEXT_UNI', ''),
+ 'post_delete_user' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_visibility' => array('TINT:3', 0),
+ 'topic_delete_time' => array('TIMESTAMP', 0),
+ 'topic_delete_reason' => array('STEXT_UNI', ''),
+ 'topic_delete_user' => array('UINT', 0),
+ 'topic_posts_approved' => array('UINT', 0),
+ 'topic_posts_unapproved' => array('UINT', 0),
+ 'topic_posts_softdeleted' => array('UINT', 0),
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'posts' => array(
+ 'post_visibility' => array('post_visibility'),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_visibility' => array('topic_visibility'),
+ 'forum_vis_last' => array('forum_id', 'topic_visibility', 'topic_last_post_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_posts_approved',
+ 'forum_posts_unapproved',
+ 'forum_posts_softdeleted',
+ 'forum_topics_approved',
+ 'forum_topics_unapproved',
+ 'forum_topics_softdeleted',
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_visibility',
+ 'post_delete_time',
+ 'post_delete_reason',
+ 'post_delete_user',
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_visibility',
+ 'topic_delete_time',
+ 'topic_delete_reason',
+ 'topic_delete_user',
+ 'topic_posts_approved',
+ 'topic_posts_unapproved',
+ 'topic_posts_softdeleted',
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'posts' => array('post_visibility'),
+ $this->table_prefix . 'topics' => array('topic_visibility', 'forum_vis_last'),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_post_visibility'))),
+ array('custom', array(array($this, 'update_topic_visibility'))),
+ array('custom', array(array($this, 'update_topics_post_counts'))),
+ array('custom', array(array($this, 'update_forums_topic_and_post_counts'))),
+
+ array('permission.add', array('f_softdelete', false)),
+ array('permission.add', array('m_softdelete', false)),
+ );
+ }
+
+ public function update_post_visibility()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'posts
+ SET post_visibility = post_approved';
+ $this->sql_query($sql);
+ }
+
+ public function update_topic_visibility()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'topics
+ SET topic_visibility = topic_approved';
+ $this->sql_query($sql);
+ }
+
+ public function update_topics_post_counts()
+ {
+ /*
+ * Using sql_case here to avoid "BIGINT UNSIGNED value is out of range" errors.
+ * As we update all topics in 2 queries, one broken topic would stop the conversion
+ * for all topics and the surpressed error will cause the admin to not even notice it.
+ */
+ $sql = 'UPDATE ' . $this->table_prefix . 'topics
+ SET topic_posts_approved = topic_replies + 1,
+ topic_posts_unapproved = ' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . '
+ WHERE topic_visibility = ' . ITEM_APPROVED;
+ $this->sql_query($sql);
+
+ $sql = 'UPDATE ' . $this->table_prefix . 'topics
+ SET topic_posts_approved = 0,
+ topic_posts_unapproved = (' . $this->db->sql_case('topic_replies_real > topic_replies', 'topic_replies_real - topic_replies', '0') . ') + 1
+ WHERE topic_visibility = ' . ITEM_UNAPPROVED;
+ $this->sql_query($sql);
+ }
+
+ public function update_forums_topic_and_post_counts($start)
+ {
+ $start = (int) $start;
+ $limit = 10;
+ $converted_forums = 0;
+
+ if (!$start)
+ {
+ // Preserve the forum_posts value for link forums as it represents redirects.
+ $sql = 'UPDATE ' . $this->table_prefix . 'forums
+ SET forum_posts_approved = forum_posts
+ WHERE forum_type = ' . FORUM_LINK;
+ $this->db->sql_query($sql);
+ }
+
+ $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS sum_topics, SUM(topic_posts_approved) AS sum_posts_approved, SUM(topic_posts_unapproved) AS sum_posts_unapproved
+ FROM ' . $this->table_prefix . 'topics
+ GROUP BY forum_id, topic_visibility
+ ORDER BY forum_id, topic_visibility';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $update_forums = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted_forums++;
+
+ $forum_id = (int) $row['forum_id'];
+ if (!isset($update_forums[$forum_id]))
+ {
+ $update_forums[$forum_id] = array(
+ 'forum_posts_approved' => 0,
+ 'forum_posts_unapproved' => 0,
+ 'forum_topics_approved' => 0,
+ 'forum_topics_unapproved' => 0,
+ );
+ }
+
+ $update_forums[$forum_id]['forum_posts_approved'] += (int) $row['sum_posts_approved'];
+ $update_forums[$forum_id]['forum_posts_unapproved'] += (int) $row['sum_posts_unapproved'];
+
+ $update_forums[$forum_id][(($row['topic_visibility'] == ITEM_APPROVED) ? 'forum_topics_approved' : 'forum_topics_unapproved')] += (int) $row['sum_topics'];
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($update_forums as $forum_id => $forum_data)
+ {
+ $sql = 'UPDATE ' . FORUMS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $forum_data) . '
+ WHERE forum_id = ' . $forum_id;
+ $this->sql_query($sql);
+ }
+
+ if ($converted_forums < $limit)
+ {
+ // There are no more topics, we are done
+ return;
+ }
+
+ // There are still more topics to query, return the next start value
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php
new file mode 100644
index 0000000000..849a996c1b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php
@@ -0,0 +1,78 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class softdelete_p2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'posts', 'post_approved');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\dev',
+ '\phpbb\db\migration\data\v310\softdelete_p1',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array('forum_posts', 'forum_topics', 'forum_topics_real'),
+ $this->table_prefix . 'posts' => array('post_approved'),
+ $this->table_prefix . 'topics' => array('topic_approved', 'topic_replies', 'topic_replies_real'),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'posts' => array('post_approved'),
+ $this->table_prefix . 'topics' => array(
+ 'forum_appr_last',
+ 'topic_approved',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_posts' => array('UINT', 0),
+ 'forum_topics' => array('UINT', 0),
+ 'forum_topics_real' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_approved' => array('BOOL', 1),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_approved' => array('BOOL', 1),
+ 'topic_replies' => array('UINT', 0),
+ 'topic_replies_real' => array('UINT', 0),
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'posts' => array(
+ 'post_approved' => array('post_approved'),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'forum_appr_last' => array('forum_id', 'topic_approved', 'topic_last_post_id'),
+ 'topic_approved' => array('topic_approved'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php
new file mode 100644
index 0000000000..2c7b7edf2e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php
@@ -0,0 +1,191 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class style_update_p1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'style_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'style_parent_id' => array('UINT', 0),
+ 'style_parent_tree' => array('TEXT', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'style_path',
+ 'bbcode_bitfield',
+ 'style_parent_id',
+ 'style_parent_tree',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'styles_update'))),
+ );
+ }
+
+ public function styles_update()
+ {
+ // Get list of valid 3.1 styles
+ $available_styles = array('prosilver');
+
+ $iterator = new \DirectoryIterator($this->phpbb_root_path . 'styles');
+ $skip_dirs = array('.', '..', 'prosilver');
+ foreach ($iterator as $fileinfo)
+ {
+ if ($fileinfo->isDir() && !in_array($fileinfo->getFilename(), $skip_dirs) && file_exists($fileinfo->getPathname() . '/style.cfg'))
+ {
+ $style_cfg = parse_cfg_file($fileinfo->getPathname() . '/style.cfg');
+ if (isset($style_cfg['phpbb_version']) && version_compare($style_cfg['phpbb_version'], '3.1.0-dev', '>='))
+ {
+ // 3.1 style
+ $available_styles[] = $fileinfo->getFilename();
+ }
+ }
+ }
+
+ // Get all installed styles
+ if ($this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'))
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id, i.imageset_path
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . 'styles_theme c, ' . $this->table_prefix . "styles_imageset i
+ WHERE t.template_id = s.template_id
+ AND c.theme_id = s.theme_id
+ AND i.imageset_id = s.imageset_id";
+ }
+ else
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "styles_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);
+
+ $sql_ary = array(
+ 'style_name' => 'prosilver',
+ 'style_copyright' => '&copy; phpBB Limited',
+ 'style_active' => 1,
+ 'style_path' => 'prosilver',
+ 'bbcode_bitfield' => 'lNg=',
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+
+ // Will be removed in the next step
+ 'imageset_id' => 0,
+ 'template_id' => 0,
+ 'theme_id' => 0,
+ );
+
+ $sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->sql_query($sql);
+
+ $sql = 'SELECT style_id
+ FROM ' . STYLES_TABLE . "
+ WHERE style_name = 'prosilver'";
+ $result = $this->sql_query($sql);
+ $default_style = $this->db->sql_fetchfield('style_id');
+ $this->db->sql_freeresult($result);
+
+ $this->config->set('default_style', $default_style);
+
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0';
+ $this->sql_query($sql);
+ }
+ else
+ {
+ // There are valid styles in styles table. Remove styles that are outdated
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE . '
+ WHERE ' . $this->db->sql_in_set('style_id', $valid_styles, true));
+
+ // Change default style
+ if (!in_array($this->config['default_style'], $valid_styles))
+ {
+ $this->sql_query('UPDATE ' . CONFIG_TABLE . "
+ SET config_value = '" . $valid_styles[0] . "'
+ WHERE config_name = 'default_style'");
+ }
+
+ // Reset styles for users
+ $this->sql_query('UPDATE ' . USERS_TABLE . '
+ SET user_style = 0
+ WHERE ' . $this->db->sql_in_set('user_style', $valid_styles, true));
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p2.php b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php
new file mode 100644
index 0000000000..52c8ffb2e2
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php
@@ -0,0 +1,151 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class style_update_p2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\style_update_p1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id',
+ 'template_id',
+ 'theme_id',
+ ),
+ ),
+
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id',
+ 'template_id',
+ 'theme_id',
+ ),
+ ),
+
+ 'drop_tables' => array(
+ $this->table_prefix . 'styles_imageset',
+ $this->table_prefix . 'styles_imageset_data',
+ $this->table_prefix . 'styles_template',
+ $this->table_prefix . 'styles_template_data',
+ $this->table_prefix . 'styles_theme',
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id' => array('UINT', 0),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ ),
+ ),
+
+ 'add_index' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id' => array('imageset_id'),
+ 'template_id' => array('template_id'),
+ 'theme_id' => array('theme_id'),
+ ),
+ ),
+
+ 'add_tables' => array(
+ $this->table_prefix . 'styles_imageset' => array(
+ 'COLUMNS' => array(
+ 'imageset_id' => array('UINT', null, 'auto_increment'),
+ 'imageset_name' => array('VCHAR_UNI:255', ''),
+ 'imageset_copyright' => array('VCHAR_UNI', ''),
+ 'imageset_path' => array('VCHAR:100', ''),
+ ),
+ 'PRIMARY_KEY' => 'imageset_id',
+ 'KEYS' => array(
+ 'imgset_nm' => array('UNIQUE', 'imageset_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'COLUMNS' => array(
+ 'image_id' => array('UINT', null, 'auto_increment'),
+ 'image_name' => array('VCHAR:200', ''),
+ 'image_filename' => array('VCHAR:200', ''),
+ 'image_lang' => array('VCHAR:30', ''),
+ 'image_height' => array('USINT', 0),
+ 'image_width' => array('USINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'image_id',
+ 'KEYS' => array(
+ 'i_d' => array('INDEX', 'imageset_id'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', null, 'auto_increment'),
+ 'template_name' => array('VCHAR_UNI:255', ''),
+ 'template_copyright' => array('VCHAR_UNI', ''),
+ 'template_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'template_storedb' => array('BOOL', 0),
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'template_id',
+ 'KEYS' => array(
+ 'tmplte_nm' => array('UNIQUE', 'template_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', 0),
+ 'template_filename' => array('VCHAR:100', ''),
+ 'template_included' => array('TEXT', ''),
+ 'template_mtime' => array('TIMESTAMP', 0),
+ 'template_data' => array('MTEXT_UNI', ''),
+ ),
+ 'KEYS' => array(
+ 'tid' => array('INDEX', 'template_id'),
+ 'tfn' => array('INDEX', 'template_filename'),
+ ),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'COLUMNS' => array(
+ 'theme_id' => array('UINT', null, 'auto_increment'),
+ 'theme_name' => array('VCHAR_UNI:255', ''),
+ 'theme_copyright' => array('VCHAR_UNI', ''),
+ 'theme_path' => array('VCHAR:100', ''),
+ 'theme_storedb' => array('BOOL', 0),
+ 'theme_mtime' => array('TIMESTAMP', 0),
+ 'theme_data' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'theme_id',
+ 'KEYS' => array(
+ 'theme_name' => array('UNIQUE', 'theme_name'),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/teampage.php b/phpBB/phpbb/db/migration/data/v310/teampage.php
new file mode 100644
index 0000000000..f8edbc3492
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/teampage.php
@@ -0,0 +1,110 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class teampage extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'teampage');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'teampage' => array(
+ 'COLUMNS' => array(
+ 'teampage_id' => array('UINT', null, 'auto_increment'),
+ 'group_id' => array('UINT', 0),
+ 'teampage_name' => array('VCHAR_UNI:255', ''),
+ 'teampage_position' => array('UINT', 0),
+ 'teampage_parent' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'teampage_id',
+ ),
+ ),
+ 'drop_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'teampage',
+ ),
+ 'add_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'add_groups_teampage'))),
+ );
+ }
+
+ public function add_groups_teampage()
+ {
+ $sql = 'SELECT teampage_id
+ FROM ' . TEAMPAGE_TABLE;
+ $result = $this->db->sql_query_limit($sql, 1);
+ $added_groups_teampage = (bool) $this->db->sql_fetchfield('teampage_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$added_groups_teampage)
+ {
+ $sql = 'SELECT *
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND (group_name = 'ADMINISTRATORS'
+ OR group_name = 'GLOBAL_MODERATORS')
+ ORDER BY group_name ASC";
+ $result = $this->db->sql_query($sql);
+
+ $teampage_entries = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $teampage_entries[] = array(
+ 'group_id' => (int) $row['group_id'],
+ 'teampage_name' => '',
+ 'teampage_position' => sizeof($teampage_entries) + 1,
+ 'teampage_parent' => 0,
+ );
+ }
+ $this->db->sql_freeresult($result);
+
+ if (sizeof($teampage_entries))
+ {
+ $this->db->sql_multi_insert(TEAMPAGE_TABLE, $teampage_entries);
+ }
+ unset($teampage_entries);
+ }
+
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/timezone.php b/phpBB/phpbb/db/migration/data/v310/timezone.php
new file mode 100644
index 0000000000..1f6b47ad50
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/timezone.php
@@ -0,0 +1,194 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class timezone extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v30x\release_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_timezone' => array('VCHAR:100', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_timezones'))),
+ );
+ }
+
+ public function update_timezones($start)
+ {
+ $start = (int) $start;
+ $limit = 500;
+ $converted = 0;
+
+ $update_blocks = array();
+
+ $sql = 'SELECT user_id, user_timezone, user_dst
+ FROM ' . $this->table_prefix . 'users
+ ORDER BY user_id ASC';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted++;
+
+ // In case this is somehow run twice on a row.
+ // Otherwise it would just end up as UTC on the second run
+ if (is_numeric($row['user_timezone']))
+ {
+ $update_blocks[$row['user_timezone'] . ':' . $row['user_dst']][] = (int) $row['user_id'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ // Update blocks of users who share the same timezone/dst
+ foreach ($update_blocks as $timezone => $user_ids)
+ {
+ $timezone = explode(':', $timezone);
+ $converted_timezone = $this->convert_phpbb30_timezone($timezone[0], $timezone[1]);
+
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_timezone = '" . $this->db->sql_escape($converted_timezone) . "'
+ WHERE " . $this->db->sql_in_set('user_id', $user_ids);
+ $this->sql_query($sql);
+ }
+
+ if ($converted == $limit)
+ {
+ // There are still more to convert
+ return $start + $limit;
+ }
+
+ // Update board default timezone
+ $sql = 'UPDATE ' . $this->table_prefix . "config
+ SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "'
+ WHERE config_name = 'board_timezone'";
+ $this->sql_query($sql);
+ }
+
+ /**
+ * Determine the new timezone for a given phpBB 3.0 timezone and
+ * "Daylight Saving Time" option
+ *
+ * @param $timezone float Users timezone in 3.0
+ * @param $dst int Users daylight saving time
+ * @return string Users new php Timezone which is used since 3.1
+ */
+ public function convert_phpbb30_timezone($timezone, $dst)
+ {
+ $offset = $timezone + $dst;
+
+ switch ($timezone)
+ {
+ case '-12':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 12] Baker Island Time'
+ case '-11':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 11] Niue Time, Samoa Standard Time'
+ case '-10':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 10] Hawaii-Aleutian Standard Time, Cook Island Time'
+ case '-9.5':
+ return 'Pacific/Marquesas'; //'[UTC - 9:30] Marquesas Islands Time'
+ case '-9':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 9] Alaska Standard Time, Gambier Island Time'
+ case '-8':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 8] Pacific Standard Time'
+ case '-7':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 7] Mountain Standard Time'
+ case '-6':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 6] Central Standard Time'
+ case '-5':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 5] Eastern Standard Time'
+ case '-4.5':
+ return 'America/Caracas'; //'[UTC - 4:30] Venezuelan Standard Time'
+ case '-4':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 4] Atlantic Standard Time'
+ case '-3.5':
+ return 'America/St_Johns'; //'[UTC - 3:30] Newfoundland Standard Time'
+ case '-3':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 3] Amazon Standard Time, Central Greenland Time'
+ case '-2':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 2] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time'
+ case '-1':
+ return 'Etc/GMT+' . abs($offset); //'[UTC - 1] Azores Standard Time, Cape Verde Time, Eastern Greenland Time'
+ case '0':
+ return (!$dst) ? 'UTC' : 'Etc/GMT-1'; //'[UTC] Western European Time, Greenwich Mean Time'
+ case '1':
+ return 'Etc/GMT-' . $offset; //'[UTC + 1] Central European Time, West African Time'
+ case '2':
+ return 'Etc/GMT-' . $offset; //'[UTC + 2] Eastern European Time, Central African Time'
+ case '3':
+ return 'Etc/GMT-' . $offset; //'[UTC + 3] Moscow Standard Time, Eastern African Time'
+ case '3.5':
+ return 'Asia/Tehran'; //'[UTC + 3:30] Iran Standard Time'
+ case '4':
+ return 'Etc/GMT-' . $offset; //'[UTC + 4] Gulf Standard Time, Samara Standard Time'
+ case '4.5':
+ return 'Asia/Kabul'; //'[UTC + 4:30] Afghanistan Time'
+ case '5':
+ return 'Etc/GMT-' . $offset; //'[UTC + 5] Pakistan Standard Time, Yekaterinburg Standard Time'
+ case '5.5':
+ return 'Asia/Kolkata'; //'[UTC + 5:30] Indian Standard Time, Sri Lanka Time'
+ case '5.75':
+ return 'Asia/Kathmandu'; //'[UTC + 5:45] Nepal Time'
+ case '6':
+ return 'Etc/GMT-' . $offset; //'[UTC + 6] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time'
+ case '6.5':
+ return 'Indian/Cocos'; //'[UTC + 6:30] Cocos Islands Time, Myanmar Time'
+ case '7':
+ return 'Etc/GMT-' . $offset; //'[UTC + 7] Indochina Time, Krasnoyarsk Standard Time'
+ case '8':
+ return 'Etc/GMT-' . $offset; //'[UTC + 8] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time'
+ case '8.75':
+ return 'Australia/Eucla'; //'[UTC + 8:45] Southeastern Western Australia Standard Time'
+ case '9':
+ return 'Etc/GMT-' . $offset; //'[UTC + 9] Japan Standard Time, Korea Standard Time, Chita Standard Time'
+ case '9.5':
+ return 'Australia/ACT'; //'[UTC + 9:30] Australian Central Standard Time'
+ case '10':
+ return 'Etc/GMT-' . $offset; //'[UTC + 10] Australian Eastern Standard Time, Vladivostok Standard Time'
+ case '10.5':
+ return 'Australia/Lord_Howe'; //'[UTC + 10:30] Lord Howe Standard Time'
+ case '11':
+ return 'Etc/GMT-' . $offset; //'[UTC + 11] Solomon Island Time, Magadan Standard Time'
+ case '11.5':
+ return 'Pacific/Norfolk'; //'[UTC + 11:30] Norfolk Island Time'
+ case '12':
+ return 'Etc/GMT-12'; //'[UTC + 12] New Zealand Time, Fiji Time, Kamchatka Standard Time'
+ case '12.75':
+ return 'Pacific/Chatham'; //'[UTC + 12:45] Chatham Islands Time'
+ case '13':
+ return 'Pacific/Tongatapu'; //'[UTC + 13] Tonga Time, Phoenix Islands Time'
+ case '14':
+ return 'Pacific/Kiritimati'; //'[UTC + 14] Line Island Time'
+ default:
+ return 'UTC';
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/timezone_p2.php b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php
new file mode 100644
index 0000000000..3ac7ab3c51
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class timezone_p2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\timezone');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/topic_sort_username.php b/phpBB/phpbb/db/migration/data/v310/topic_sort_username.php
new file mode 100644
index 0000000000..527da20590
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/topic_sort_username.php
@@ -0,0 +1,44 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class topic_sort_username extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'topics' => array(
+ 'topic_first_poster_name' => array('VCHAR_UNI:255', '', 'true_sort'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'topics' => array(
+ 'topic_first_poster_name' => array('VCHAR_UNI:255', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php b/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php
new file mode 100644
index 0000000000..8600f6ee27
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class ucp_popuppm_module extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT module_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_class = 'ucp'
+ AND module_langname = 'UCP_PM_POPUP_TITLE'";
+ $result = $this->db->sql_query($sql);
+ $module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ return $module_id == false;
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\dev');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.remove', array(
+ 'ucp',
+ 'UCP_PM',
+ 'UCP_PM_POPUP_TITLE',
+ )),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/.htaccess b/phpBB/phpbb/db/migration/data/v31x/.htaccess
new file mode 100644
index 0000000000..44242b5418
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/.htaccess
@@ -0,0 +1,33 @@
+# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from
+# module mod_authz_host to a new module called mod_access_compat (which may be
+# disabled) and a new "Require" syntax has been introduced to mod_authz_host.
+# We could just conditionally provide both versions, but unfortunately Apache
+# does not explicitly tell us its version if the module mod_version is not
+# available. In this case, we check for the availability of module
+# mod_authz_core (which should be on 2.4 or higher only) as a best guess.
+<IfModule mod_version.c>
+ <IfVersion < 2.4>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfVersion>
+ <IfVersion >= 2.4>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfVersion>
+</IfModule>
+<IfModule !mod_version.c>
+ <IfModule !mod_authz_core.c>
+ <Files "*">
+ Order Allow,Deny
+ Deny from All
+ </Files>
+ </IfModule>
+ <IfModule mod_authz_core.c>
+ <Files "*">
+ Require all denied
+ </Files>
+ </IfModule>
+</IfModule>
diff --git a/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php b/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php
new file mode 100644
index 0000000000..9f416fe069
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class add_jabber_ssl_context_config_options extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v3110');
+ }
+
+ public function update_data()
+ {
+ return array(
+ // See http://php.net/manual/en/context.ssl.php
+ array('config.add', array('jab_verify_peer', 1)),
+ array('config.add', array('jab_verify_peer_name', 1)),
+ array('config.add', array('jab_allow_self_signed', 0)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php b/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php
new file mode 100644
index 0000000000..fa2899e348
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\migration\data\v31x;
+
+class add_latest_topics_index extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3110',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'topics' => array(
+ 'latest_topics' => array(
+ 'forum_id',
+ 'topic_last_post_time',
+ 'topic_last_post_id',
+ 'topic_moved_id',
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'topics' => array(
+ 'latest_topics',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/add_log_time_index.php b/phpBB/phpbb/db/migration/data/v31x/add_log_time_index.php
new file mode 100644
index 0000000000..f53eedcd49
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/add_log_time_index.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class add_log_time_index extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v319',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time' => array('log_time'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php b/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php
new file mode 100644
index 0000000000..92051dc3ca
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class add_smtp_ssl_context_config_options extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v3110');
+ }
+
+ public function update_data()
+ {
+ return array(
+ // See http://php.net/manual/en/context.ssl.php
+ array('config.add', array('smtp_verify_peer', 1)),
+ array('config.add', array('smtp_verify_peer_name', 1)),
+ array('config.add', array('smtp_allow_self_signed', 0)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/increase_size_of_dateformat.php b/phpBB/phpbb/db/migration/data/v31x/increase_size_of_dateformat.php
new file mode 100644
index 0000000000..bdf83f3d62
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/increase_size_of_dateformat.php
@@ -0,0 +1,35 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class increase_size_of_dateformat extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v317',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dateformat' => array('VCHAR_UNI:64', 'd M Y H:i'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/increase_size_of_emotion.php b/phpBB/phpbb/db/migration/data/v31x/increase_size_of_emotion.php
new file mode 100644
index 0000000000..7e486aca7c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/increase_size_of_emotion.php
@@ -0,0 +1,46 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class increase_size_of_emotion extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3110',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'smilies' => array(
+ 'emotion' => array('VCHAR_UNI', ''),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'smilies' => array(
+ 'emotion' => array('VCHAR_UNI:50', ''),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/m_pm_report.php b/phpBB/phpbb/db/migration/data/v31x/m_pm_report.php
new file mode 100644
index 0000000000..9b5710c639
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/m_pm_report.php
@@ -0,0 +1,64 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class m_pm_report extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v316rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('permission.add', array('m_pm_report', true, 'm_report')),
+ array('custom', array(
+ array($this, 'update_module_auth'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('permission.remove', array('m_pm_report')),
+ array('custom', array(
+ array($this, 'revert_module_auth'),
+ ),
+ ),
+ );
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'acl_m_pm_report'
+ WHERE module_class = 'mcp'
+ AND module_basename = 'mcp_pm_reports'
+ AND module_auth = 'aclf_m_report'";
+ $this->db->sql_query($sql);
+ }
+
+ public function revert_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'aclf_m_report'
+ WHERE module_class = 'mcp'
+ AND module_basename = 'mcp_pm_reports'
+ AND module_auth = 'acl_m_pm_report'";
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/m_softdelete_global.php b/phpBB/phpbb/db/migration/data/v31x/m_softdelete_global.php
new file mode 100644
index 0000000000..dd7e20e762
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/m_softdelete_global.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class m_softdelete_global extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v311');
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Make m_softdelete global. The add method will take care of updating
+ // it if it already exists.
+ array('permission.add', array('m_softdelete', true)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/plupload_last_gc_dynamic.php b/phpBB/phpbb/db/migration/data/v31x/plupload_last_gc_dynamic.php
new file mode 100644
index 0000000000..0783d707c5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/plupload_last_gc_dynamic.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class plupload_last_gc_dynamic extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v312');
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Make plupload_last_gc dynamic.
+ array('config.remove', array('plupload_last_gc')),
+ array('config.add', array('plupload_last_gc', 0, 1)),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/profilefield_remove_underscore_from_alpha.php b/phpBB/phpbb/db/migration/data/v31x/profilefield_remove_underscore_from_alpha.php
new file mode 100644
index 0000000000..60491f8de8
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/profilefield_remove_underscore_from_alpha.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\migration\data\v31x;
+
+class profilefield_remove_underscore_from_alpha extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v311');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'remove_underscore_from_alpha_validations'))),
+ );
+ }
+
+ public function remove_underscore_from_alpha_validations()
+ {
+ $this->update_validation_rule('[\w]+', '[a-zA-Z0-9]+');
+ $this->update_validation_rule('[\w_]+', '[\w]+');
+ $this->update_validation_rule('[\w.]+', '[a-zA-Z0-9.]+');
+ $this->update_validation_rule('[\w\x20_+\-\[\]]+', '[\w\x20+\-\[\]]+');
+ $this->update_validation_rule('[a-zA-Z][\w\.,\-_]+', '[a-zA-Z][\w\.,\-]+');
+ }
+
+ public function update_validation_rule($old_validation, $new_validation)
+ {
+ $sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . "
+ SET field_validation = '" . $this->db->sql_escape($new_validation) . "'
+ WHERE field_validation = '" . $this->db->sql_escape($old_validation) . "'";
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/profilefield_yahoo_update_url.php b/phpBB/phpbb/db/migration/data/v31x/profilefield_yahoo_update_url.php
new file mode 100644
index 0000000000..4df9083bdf
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/profilefield_yahoo_update_url.php
@@ -0,0 +1,38 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class profilefield_yahoo_update_url extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v312');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_contact_url'))),
+ );
+ }
+
+ public function update_contact_url()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "profile_fields
+ SET field_contact_url = 'ymsgr:sendim?%s'
+ WHERE field_name = 'phpbb_yahoo'
+ AND field_contact_url = 'http://edit.yahoo.com/config/send_webmesg?.target=%s&amp;.src=pg'";
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/remove_duplicate_migrations.php b/phpBB/phpbb/db/migration/data/v31x/remove_duplicate_migrations.php
new file mode 100644
index 0000000000..417d569a09
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/remove_duplicate_migrations.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\migration\data\v31x;
+
+class remove_duplicate_migrations extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v31x\v3110');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'deduplicate_entries'))),
+ );
+ }
+
+ public function deduplicate_entries()
+ {
+ $migration_state = array();
+ $duplicate_migrations = array();
+
+ $sql = "SELECT *
+ FROM " . $this->table_prefix . 'migrations';
+ $result = $this->db->sql_query($sql);
+
+ if (!$this->db->get_sql_error_triggered())
+ {
+ while ($migration = $this->db->sql_fetchrow($result))
+ {
+ $migration_state[$migration['migration_name']] = $migration;
+
+ $migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+
+ foreach ($migration_state as $name => $migration)
+ {
+ $prepended_name = ($name[0] == '\\' ? '' : '\\') . $name;
+ $prefixless_name = $name[0] == '\\' ? substr($name, 1) : $name;
+
+ if ($prepended_name != $name && isset($migration_state[$prepended_name]) && $migration_state[$prepended_name]['migration_depends_on'] == $migration_state[$name]['migration_depends_on'])
+ {
+ $duplicate_migrations[] = $name;
+ unset($migration_state[$prepended_name]);
+ }
+ else if ($prefixless_name != $name && isset($migration_state[$prefixless_name]) && $migration_state[$prefixless_name]['migration_depends_on'] == $migration_state[$name]['migration_depends_on'])
+ {
+ $duplicate_migrations[] = $prefixless_name;
+ unset($migration_state[$prefixless_name]);
+ }
+ }
+
+ if (count($duplicate_migrations))
+ {
+ $sql = 'DELETE
+ FROM ' . $this->table_prefix . 'migrations
+ WHERE ' . $this->db->sql_in_set('migration_name', $duplicate_migrations);
+ $this->db->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/style_update.php b/phpBB/phpbb/db/migration/data/v31x/style_update.php
new file mode 100644
index 0000000000..bb030bbe6d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/style_update.php
@@ -0,0 +1,136 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class style_update extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\gold');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_installed_styles'))),
+ );
+ }
+
+ public function update_installed_styles()
+ {
+ // Get all currently available styles
+ $styles = $this->find_style_dirs();
+ $style_paths = $style_ids = array();
+
+ $sql = 'SELECT style_path, style_id
+ FROM ' . $this->table_prefix . 'styles';
+ $result = $this->db->sql_query($sql);
+ while ($styles_row = $this->db->sql_fetchrow())
+ {
+ if (in_array($styles_row['style_path'], $styles))
+ {
+ $style_paths[] = $styles_row['style_path'];
+ $style_ids[] = $styles_row['style_id'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ // Install prosilver if no style is available and prosilver can be installed
+ if (empty($style_paths) && in_array('prosilver', $styles))
+ {
+ // Try to parse config file
+ $cfg = parse_cfg_file($this->phpbb_root_path . 'styles/prosilver/style.cfg');
+
+ // Stop running this if prosilver cfg file can't be read
+ if (empty($cfg))
+ {
+ throw new \RuntimeException('No styles available and could not fall back to prosilver.');
+ }
+
+ $style = array(
+ 'style_name' => 'prosilver',
+ 'style_copyright' => '&copy; phpBB Limited',
+ 'style_active' => 1,
+ 'style_path' => 'prosilver',
+ 'bbcode_bitfield' => 'kNg=',
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+ );
+
+ // Add to database
+ $this->db->sql_transaction('begin');
+
+ $sql = 'INSERT INTO ' . $this->table_prefix . 'styles
+ ' . $this->db->sql_build_array('INSERT', $style);
+ $this->db->sql_query($sql);
+
+ $style_id = $this->db->sql_nextid();
+ $style_ids[] = $style_id;
+
+ $this->db->sql_transaction('commit');
+
+ // Set prosilver to default style
+ $this->config->set('default_style', $style_id);
+ }
+ else if (empty($styles) && empty($available_styles))
+ {
+ throw new \RuntimeException('No valid styles available');
+ }
+
+ // Make sure default style is available
+ if (!in_array($this->config['default_style'], $style_ids))
+ {
+ $this->config->set('default_style', array_pop($style_ids));
+ }
+
+ // Reset users to default style if their user_style is nonexistent
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_style = {$this->config['default_style']}
+ WHERE " . $this->db->sql_in_set('user_style', $style_ids, true, true);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Find all directories that have styles
+ * Copied from acp_styles
+ *
+ * @return array Directory names
+ */
+ protected function find_style_dirs()
+ {
+ $styles = array();
+ $styles_path = $this->phpbb_root_path . 'styles/';
+
+ $dp = @opendir($styles_path);
+ if ($dp)
+ {
+ while (($file = readdir($dp)) !== false)
+ {
+ $dir = $styles_path . $file;
+ if ($file[0] == '.' || !is_dir($dir))
+ {
+ continue;
+ }
+
+ if (file_exists("{$dir}/style.cfg"))
+ {
+ $styles[] = $file;
+ }
+ }
+ closedir($dp);
+ }
+
+ return $styles;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php b/phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php
new file mode 100644
index 0000000000..854ed1f568
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php
@@ -0,0 +1,70 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class update_custom_bbcodes_with_idn extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v312',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_bbcodes_table'))),
+ );
+ }
+
+ public function update_bbcodes_table()
+ {
+ if (!class_exists('acp_bbcodes'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_bbcodes.' . $this->php_ext);
+ }
+
+ $bbcodes = new \acp_bbcodes();
+
+ $sql = 'SELECT bbcode_id, bbcode_match, bbcode_tpl
+ FROM ' . BBCODES_TABLE;
+ $result = $this->sql_query($sql);
+
+ $sql_ary = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $data = array();
+ if (preg_match('/(URL|LOCAL_URL|RELATIVE_URL)/', $row['bbcode_match']))
+ {
+ $data = $bbcodes->build_regexp($row['bbcode_match'], $row['bbcode_tpl']);
+ $sql_ary[$row['bbcode_id']] = array(
+ 'first_pass_match' => $data['first_pass_match'],
+ 'first_pass_replace' => $data['first_pass_replace'],
+ 'second_pass_match' => $data['second_pass_match'],
+ 'second_pass_replace' => $data['second_pass_replace']
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($sql_ary as $bbcode_id => $bbcode_data)
+ {
+ $sql = 'UPDATE ' . BBCODES_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $bbcode_data) . '
+ WHERE bbcode_id = ' . (int) $bbcode_id;
+ $this->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/update_hashes.php b/phpBB/phpbb/db/migration/data/v31x/update_hashes.php
new file mode 100644
index 0000000000..aa83c3ffbf
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/update_hashes.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\migration\data\v31x;
+
+class update_hashes extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3110',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('enable_update_hashes', '1')),
+ array('config.add', array('update_hashes_lock', '')),
+ array('config.add', array('update_hashes_last_cron', '0'))
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v311.php b/phpBB/phpbb/db/migration/data/v31x/v311.php
new file mode 100644
index 0000000000..b9d6ed3053
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v311.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v311 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\gold',
+ '\phpbb\db\migration\data\v31x\style_update',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v3110.php b/phpBB/phpbb/db/migration/data/v31x/v3110.php
new file mode 100644
index 0000000000..b89b4cc6e6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v3110.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v3110 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.10', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3110rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.10')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v3110rc1.php b/phpBB/phpbb/db/migration/data/v31x/v3110rc1.php
new file mode 100644
index 0000000000..da69f2384e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v3110rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v3110rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.10-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v319',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.10-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v3111.php b/phpBB/phpbb/db/migration/data/v31x/v3111.php
new file mode 100644
index 0000000000..f01bbc2bff
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v3111.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v3111 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.11', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3111rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.11')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v3111rc1.php b/phpBB/phpbb/db/migration/data/v31x/v3111rc1.php
new file mode 100644
index 0000000000..259656283f
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v3111rc1.php
@@ -0,0 +1,43 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v3111rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.11-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v3110',
+ '\phpbb\db\migration\data\v31x\add_log_time_index',
+ '\phpbb\db\migration\data\v31x\increase_size_of_emotion',
+ '\phpbb\db\migration\data\v31x\add_jabber_ssl_context_config_options',
+ '\phpbb\db\migration\data\v31x\add_smtp_ssl_context_config_options',
+ '\phpbb\db\migration\data\v31x\update_hashes',
+ '\phpbb\db\migration\data\v31x\remove_duplicate_migrations',
+ '\phpbb\db\migration\data\v31x\add_latest_topics_index',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.11-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v312.php b/phpBB/phpbb/db/migration/data/v31x/v312.php
new file mode 100644
index 0000000000..114c2b959b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v312.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v312 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v312rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v312rc1.php b/phpBB/phpbb/db/migration/data/v31x/v312rc1.php
new file mode 100644
index 0000000000..e2408d432b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v312rc1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v312rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.2-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v311',
+ '\phpbb\db\migration\data\v31x\m_softdelete_global',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.2-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v313.php b/phpBB/phpbb/db/migration/data/v31x/v313.php
new file mode 100644
index 0000000000..b86788da16
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v313 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v313rc2',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v313rc1.php b/phpBB/phpbb/db/migration/data/v31x/v313rc1.php
new file mode 100644
index 0000000000..b1dcc03364
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313rc1.php
@@ -0,0 +1,40 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v313rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.3-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_13_rc1',
+ '\phpbb\db\migration\data\v31x\plupload_last_gc_dynamic',
+ '\phpbb\db\migration\data\v31x\profilefield_remove_underscore_from_alpha',
+ '\phpbb\db\migration\data\v31x\profilefield_yahoo_update_url',
+ '\phpbb\db\migration\data\v31x\update_custom_bbcodes_with_idn',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.3-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v313rc2.php b/phpBB/phpbb/db/migration/data/v31x/v313rc2.php
new file mode 100644
index 0000000000..b701dca5ed
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313rc2.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v313rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.3-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_13_pl1',
+ '\phpbb\db\migration\data\v31x\v313rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.3-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v314.php b/phpBB/phpbb/db/migration/data/v31x/v314.php
new file mode 100644
index 0000000000..82dbbf29c9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_14',
+ '\phpbb\db\migration\data\v31x\v314rc2',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc1.php b/phpBB/phpbb/db/migration/data/v31x/v314rc1.php
new file mode 100644
index 0000000000..e7baf0c2ce
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.4-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v313',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc2.php b/phpBB/phpbb/db/migration/data/v31x/v314rc2.php
new file mode 100644
index 0000000000..3fc5bf2ad5
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314rc2.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314rc2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.4-RC2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_14_rc1',
+ '\phpbb\db\migration\data\v31x\v314rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v315.php b/phpBB/phpbb/db/migration/data/v31x/v315.php
new file mode 100644
index 0000000000..d5eacf8dd3
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v315.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v315 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.5', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v315rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.5')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v315rc1.php b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php
new file mode 100644
index 0000000000..a58b6a0f2a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v315rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.5-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v314',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.5-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v316.php b/phpBB/phpbb/db/migration/data/v31x/v316.php
new file mode 100644
index 0000000000..b3e0060ced
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v316.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v316 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.6', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v316rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.6')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v316rc1.php b/phpBB/phpbb/db/migration/data/v31x/v316rc1.php
new file mode 100644
index 0000000000..6badfb68d4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v316rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v316rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.6-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v315',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.6-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v317.php b/phpBB/phpbb/db/migration/data/v31x/v317.php
new file mode 100644
index 0000000000..d95be06ba6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v317 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.7', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v317rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.7')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v317pl1.php b/phpBB/phpbb/db/migration/data/v31x/v317pl1.php
new file mode 100644
index 0000000000..1cb39b03f0
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317pl1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v317pl1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.7-pl1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v317',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.7-pl1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v317rc1.php b/phpBB/phpbb/db/migration/data/v31x/v317rc1.php
new file mode 100644
index 0000000000..77759daa66
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317rc1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v317rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.7-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\m_pm_report',
+ '\phpbb\db\migration\data\v31x\v316',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.7-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v318.php b/phpBB/phpbb/db/migration/data/v31x/v318.php
new file mode 100644
index 0000000000..7663529d3a
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v318.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v318 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.8', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v318rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.8')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v318rc1.php b/phpBB/phpbb/db/migration/data/v31x/v318rc1.php
new file mode 100644
index 0000000000..2cab5c96d4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v318rc1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v318rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.8-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\increase_size_of_dateformat',
+ '\phpbb\db\migration\data\v31x\v317pl1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.8-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v319.php b/phpBB/phpbb/db/migration/data/v31x/v319.php
new file mode 100644
index 0000000000..f773814028
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v319.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v319 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.9', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v319rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.9')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v319rc1.php b/phpBB/phpbb/db/migration/data/v31x/v319rc1.php
new file mode 100644
index 0000000000..9805b45572
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v319rc1.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v319rc1 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return phpbb_version_compare($this->config['version'], '3.1.9-RC1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v318',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.9-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/exception.php b/phpBB/phpbb/db/migration/exception.php
new file mode 100644
index 0000000000..7990e85f2d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/exception.php
@@ -0,0 +1,75 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+/**
+* The migrator is responsible for applying new migrations in the correct order.
+*/
+class exception extends \Exception
+{
+ /**
+ * Extra parameters sent to exception to aid in debugging
+ * @var array
+ */
+ protected $parameters;
+
+ /**
+ * Throw an exception.
+ *
+ * First argument is the error message.
+ * Additional arguments will be output with the error message.
+ */
+ public function __construct()
+ {
+ $parameters = func_get_args();
+ $message = array_shift($parameters);
+ parent::__construct($message);
+
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Output the error as a string
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->message . ': ' . var_export($this->parameters, true);
+ }
+
+ /**
+ * Get the parameters
+ *
+ * @return array
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Get localised message (with $user->lang())
+ *
+ * @param \phpbb\user $user
+ * @return string
+ */
+ public function getLocalisedMessage(\phpbb\user $user)
+ {
+ $parameters = $this->getParameters();
+ array_unshift($parameters, $this->getMessage());
+
+ return call_user_func_array(array($user, 'lang'), $parameters);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/helper.php b/phpBB/phpbb/db/migration/helper.php
new file mode 100644
index 0000000000..bce2efff51
--- /dev/null
+++ b/phpBB/phpbb/db/migration/helper.php
@@ -0,0 +1,116 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+/**
+* The migrator is responsible for applying new migrations in the correct order.
+*/
+class helper
+{
+ /**
+ * Get the schema steps from an array of schema changes
+ *
+ * This splits up $schema_changes into individual changes so that the
+ * changes can be chunked
+ *
+ * @param array $schema_changes from migration
+ * @return array
+ */
+ public function get_schema_steps($schema_changes)
+ {
+ $steps = array();
+
+ // Nested level of data (only supports 1/2 currently)
+ $nested_level = array(
+ 'drop_tables' => 1,
+ 'add_tables' => 1,
+ 'change_columns' => 2,
+ 'add_columns' => 2,
+ 'drop_keys' => 2,
+ 'drop_columns' => 2,
+ 'add_primary_keys' => 2, // perform_schema_changes only uses one level, but second is in the function
+ 'add_unique_index' => 2,
+ 'add_index' => 2,
+ );
+
+ foreach ($nested_level as $change_type => $data_depth)
+ {
+ if (!empty($schema_changes[$change_type]))
+ {
+ foreach ($schema_changes[$change_type] as $key => $value)
+ {
+ if ($data_depth === 1)
+ {
+ $steps[] = array(
+ 'dbtools.perform_schema_changes', array(array(
+ $change_type => array(
+ (!is_int($key)) ? $key : 0 => $value,
+ ),
+ )),
+ );
+ }
+ else if ($data_depth === 2)
+ {
+ foreach ($value as $key2 => $value2)
+ {
+ $steps[] = array(
+ 'dbtools.perform_schema_changes', array(array(
+ $change_type => array(
+ $key => array(
+ $key2 => $value2,
+ ),
+ ),
+ )),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ return $steps;
+ }
+
+ /**
+ * Reverse the update steps from an array of data changes
+ *
+ * 'If' statements and custom methods will be skipped, for all
+ * other calls the reverse method of the tool class will be called
+ *
+ * @param array $steps Update changes from migration
+ *
+ * @return array
+ */
+ public function reverse_update_data($steps)
+ {
+ $reversed_array = array();
+
+ foreach ($steps as $step)
+ {
+ $parts = explode('.', $step[0]);
+ $parameters = $step[1];
+
+ $class = $parts[0];
+ $method = isset($parts[1]) ? $parts[1] : false;
+
+ if ($class !== 'if' && $class !== 'custom')
+ {
+ array_unshift($parameters, $method);
+ $reversed_array[] = array($class . '.reverse', $parameters);
+ }
+ }
+
+ return array_reverse($reversed_array);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/migration.php b/phpBB/phpbb/db/migration/migration.php
new file mode 100644
index 0000000000..5f120333e1
--- /dev/null
+++ b/phpBB/phpbb/db/migration/migration.php
@@ -0,0 +1,186 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+/**
+* Abstract base class for database migrations
+*
+* Each migration consists of a set of schema and data changes to be implemented
+* in a subclass. This class provides various utility methods to simplify editing
+* a phpBB.
+*/
+abstract class migration
+{
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\db\tools */
+ protected $db_tools;
+
+ /** @var string */
+ protected $table_prefix;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /** @var array Errors, if any occurred */
+ protected $errors;
+
+ /** @var array List of queries executed through $this->sql_query() */
+ protected $queries = array();
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\config $config
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\db\tools $db_tools
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ * @param string $table_prefix
+ */
+ public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix)
+ {
+ $this->config = $config;
+ $this->db = $db;
+ $this->db_tools = $db_tools;
+ $this->table_prefix = $table_prefix;
+
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ $this->errors = array();
+ }
+
+ /**
+ * Defines other migrations to be applied first
+ *
+ * @return array An array of migration class names
+ */
+ static public function depends_on()
+ {
+ return array();
+ }
+
+ /**
+ * Allows you to check if the migration is effectively installed (entirely optional)
+ *
+ * This is checked when a migration is installed. If true is returned, the migration will be set as
+ * installed without performing the database changes.
+ * This function is intended to help moving to migrations from a previous database updater, where some
+ * migrations may have been installed already even though they are not yet listed in the migrations table.
+ *
+ * @return bool True if this migration is installed, False if this migration is not installed (checked on install)
+ */
+ public function effectively_installed()
+ {
+ return false;
+ }
+
+ /**
+ * Updates the database schema by providing a set of change instructions
+ *
+ * @return array Array of schema changes (compatible with db_tools->perform_schema_changes())
+ */
+ public function update_schema()
+ {
+ return array();
+ }
+
+ /**
+ * Reverts the database schema by providing a set of change instructions
+ *
+ * @return array Array of schema changes (compatible with db_tools->perform_schema_changes())
+ */
+ public function revert_schema()
+ {
+ return array();
+ }
+
+ /**
+ * Updates data by returning a list of instructions to be executed
+ *
+ * @return array Array of data update instructions
+ */
+ public function update_data()
+ {
+ return array();
+ }
+
+ /**
+ * Reverts data by returning a list of instructions to be executed
+ *
+ * @return array Array of data instructions that will be performed on revert
+ * NOTE: calls to tools (such as config.add) are automatically reverted when
+ * possible, so you should not attempt to revert those, this is mostly for
+ * otherwise unrevertable calls (custom functions for example)
+ */
+ public function revert_data()
+ {
+ return array();
+ }
+
+ /**
+ * Wrapper for running queries to generate user feedback on updates
+ *
+ * @param string $sql SQL query to run on the database
+ * @return mixed Query result from db->sql_query()
+ */
+ protected function sql_query($sql)
+ {
+ $this->queries[] = $sql;
+
+ $this->db->sql_return_on_error(true);
+
+ if ($sql === 'begin')
+ {
+ $result = $this->db->sql_transaction('begin');
+ }
+ else if ($sql === 'commit')
+ {
+ $result = $this->db->sql_transaction('commit');
+ }
+ else
+ {
+ $result = $this->db->sql_query($sql);
+ if ($this->db->get_sql_error_triggered())
+ {
+ $this->errors[] = array(
+ 'sql' => $this->db->get_sql_error_sql(),
+ 'code' => $this->db->get_sql_error_returned(),
+ );
+ }
+ }
+
+ $this->db->sql_return_on_error(false);
+
+ return $result;
+ }
+
+ /**
+ * Get the list of queries run
+ *
+ * @return array
+ */
+ public function get_queries()
+ {
+ return $this->queries;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/profilefield_base_migration.php b/phpBB/phpbb/db/migration/profilefield_base_migration.php
new file mode 100644
index 0000000000..b20ca874be
--- /dev/null
+++ b/phpBB/phpbb/db/migration/profilefield_base_migration.php
@@ -0,0 +1,247 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+abstract class profilefield_base_migration extends container_aware_migration
+{
+ protected $profilefield_name;
+
+ protected $profilefield_database_type;
+
+ protected $profilefield_data;
+
+ /**
+ * Language data should be in array -> each language_data in separate key
+ * array(
+ * array(
+ * 'option_id' => value,
+ * 'field_type' => value,
+ * 'lang_value' => value,
+ * ),
+ * array(
+ * 'option_id' => value,
+ * 'field_type' => value,
+ * 'lang_value' => value,
+ * ),
+ * )
+ */
+ protected $profilefield_language_data;
+
+ protected $user_column_name;
+
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_column_exists($this->table_prefix . 'profile_fields_data', 'pf_' . $this->profilefield_name);
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields_data' => array(
+ 'pf_' . $this->profilefield_name => $this->profilefield_database_type,
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields_data' => array(
+ 'pf_' . $this->profilefield_name,
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ array('custom', array(array($this, 'convert_user_field_to_custom_field'))),
+ );
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('custom', array(array($this, 'delete_custom_profile_field_data'))),
+ );
+ }
+
+ public function create_custom_field()
+ {
+ $sql = 'SELECT MAX(field_order) as max_field_order
+ FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+ $max_field_order = (int) $this->db->sql_fetchfield('max_field_order');
+ $this->db->sql_freeresult($result);
+
+ $sql_ary = array_merge($this->profilefield_data, array(
+ 'field_order' => $max_field_order + 1,
+ ));
+
+ $sql = 'INSERT INTO ' . PROFILE_FIELDS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+ $field_id = (int) $this->db->sql_nextid();
+
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, PROFILE_LANG_TABLE);
+
+ $sql = 'SELECT lang_id
+ FROM ' . LANG_TABLE;
+ $result = $this->db->sql_query($sql);
+ $lang_name = (strpos($this->profilefield_name, 'phpbb_') === 0) ? strtoupper(substr($this->profilefield_name, 6)) : strtoupper($this->profilefield_name);
+ while ($lang_id = (int) $this->db->sql_fetchfield('lang_id'))
+ {
+ $insert_buffer->insert(array(
+ 'field_id' => (int) $field_id,
+ 'lang_id' => (int) $lang_id,
+ 'lang_name' => $lang_name,
+ 'lang_explain' => '',
+ 'lang_default_value' => '',
+ ));
+ }
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+ }
+
+ /**
+ * Create Custom profile fields languguage entries
+ */
+ public function create_language_entries()
+ {
+ $field_id = $this->get_custom_profile_field_id();
+
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, PROFILE_FIELDS_LANG_TABLE);
+
+ $sql = 'SELECT lang_id
+ FROM ' . LANG_TABLE;
+ $result = $this->db->sql_query($sql);
+ while ($lang_id = (int) $this->db->sql_fetchfield('lang_id'))
+ {
+ foreach ($this->profilefield_language_data as $language_data)
+ {
+ $insert_buffer->insert(array_merge(array(
+ 'field_id' => (int) $field_id,
+ 'lang_id' => (int) $lang_id,
+ ), $language_data));
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+ }
+
+ /**
+ * Clean database when reverting the migration
+ */
+ public function delete_custom_profile_field_data()
+ {
+ $field_id = $this->get_custom_profile_field_id();
+
+ $sql = 'DELETE FROM ' . PROFILE_FIELDS_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . PROFILE_LANG_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . PROFILE_FIELDS_LANG_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Get custom profile field id
+ * @return int custom profile filed id
+ */
+ public function get_custom_profile_field_id()
+ {
+ $sql = 'SELECT field_id
+ FROM ' . PROFILE_FIELDS_TABLE . "
+ WHERE field_name = '" . $this->profilefield_name . "'";
+ $result = $this->db->sql_query($sql);
+ $field_id = (int) $this->db->sql_fetchfield('field_id');
+ $this->db->sql_freeresult($result);
+
+ return $field_id;
+ }
+
+ /**
+ * @param int $start Start of staggering step
+ * @return mixed int start of the next step, null if the end was reached
+ */
+ public function convert_user_field_to_custom_field($start)
+ {
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->table_prefix . 'profile_fields_data');
+ $limit = 250;
+ $converted_users = 0;
+ $start = $start ?: 0;
+
+ $sql = 'SELECT user_id, ' . $this->user_column_name . '
+ FROM ' . $this->table_prefix . 'users
+ WHERE ' . $this->user_column_name . " <> ''
+ ORDER BY user_id";
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted_users++;
+
+ $cp_data = array(
+ 'pf_' . $this->profilefield_name => $row[$this->user_column_name],
+ );
+
+ $sql = 'UPDATE ' . $this->table_prefix . 'profile_fields_data
+ SET ' . $this->db->sql_build_array('UPDATE', $cp_data) . '
+ WHERE user_id = ' . (int) $row['user_id'];
+ $this->db->sql_query($sql);
+
+ if (!$this->db->sql_affectedrows())
+ {
+ $cp_data['user_id'] = (int) $row['user_id'];
+ $cp_data = array_merge($this->get_insert_sql_array(), $cp_data);
+ $insert_buffer->insert($cp_data);
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+
+ if ($converted_users < $limit)
+ {
+ // No more users left, we are done...
+ return;
+ }
+
+ return $start + $limit;
+ }
+
+ protected function get_insert_sql_array()
+ {
+ static $profile_row;
+
+ if ($profile_row === null)
+ {
+ $manager = $this->container->get('profilefields.manager');
+ $profile_row = $manager->build_insert_sql_array(array());
+ }
+
+ return $profile_row;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/schema_generator.php b/phpBB/phpbb/db/migration/schema_generator.php
new file mode 100644
index 0000000000..91d8307d91
--- /dev/null
+++ b/phpBB/phpbb/db/migration/schema_generator.php
@@ -0,0 +1,235 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration;
+
+/**
+* The schema generator generates the schema based on the existing migrations
+*/
+class schema_generator
+{
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\db\tools */
+ protected $db_tools;
+
+ /** @var array */
+ protected $class_names;
+
+ /** @var string */
+ protected $table_prefix;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /** @var array */
+ protected $tables;
+
+ /** @var array */
+ protected $dependencies = array();
+
+ /**
+ * Constructor
+ */
+ public function __construct(array $class_names, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix)
+ {
+ $this->config = $config;
+ $this->db = $db;
+ $this->db_tools = $db_tools;
+ $this->class_names = $class_names;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->table_prefix = $table_prefix;
+ }
+
+ /**
+ * Loads all migrations and their application state from the database.
+ *
+ * @return array
+ */
+ public function get_schema()
+ {
+ if (!empty($this->tables))
+ {
+ return $this->tables;
+ }
+
+ $migrations = $this->class_names;
+
+ $tree = array();
+ $check_dependencies = true;
+ while (!empty($migrations))
+ {
+ foreach ($migrations as $migration_class)
+ {
+ $open_dependencies = array_diff($migration_class::depends_on(), $tree);
+
+ if (empty($open_dependencies))
+ {
+ $migration = new $migration_class($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
+ $tree[] = $migration_class;
+ $migration_key = array_search($migration_class, $migrations);
+
+ foreach ($migration->update_schema() as $change_type => $data)
+ {
+ if ($change_type === 'add_tables')
+ {
+ foreach ($data as $table => $table_data)
+ {
+ $this->tables[$table] = $table_data;
+ }
+ }
+ else if ($change_type === 'drop_tables')
+ {
+ foreach ($data as $table)
+ {
+ unset($this->tables[$table]);
+ }
+ }
+ else if ($change_type === 'add_columns')
+ {
+ foreach ($data as $table => $add_columns)
+ {
+ foreach ($add_columns as $column => $column_data)
+ {
+ if (isset($column_data['after']))
+ {
+ $columns = $this->tables[$table]['COLUMNS'];
+ $offset = array_search($column_data['after'], array_keys($columns));
+ unset($column_data['after']);
+
+ if ($offset === false)
+ {
+ $this->tables[$table]['COLUMNS'][$column] = array_values($column_data);
+ }
+ else
+ {
+ $this->tables[$table]['COLUMNS'] = array_merge(array_slice($columns, 0, $offset + 1, true), array($column => array_values($column_data)), array_slice($columns, $offset));
+ }
+ }
+ else
+ {
+ $this->tables[$table]['COLUMNS'][$column] = $column_data;
+ }
+ }
+ }
+ }
+ else if ($change_type === 'change_columns')
+ {
+ foreach ($data as $table => $change_columns)
+ {
+ foreach ($change_columns as $column => $column_data)
+ {
+ $this->tables[$table]['COLUMNS'][$column] = $column_data;
+ }
+ }
+ }
+ else if ($change_type === 'drop_columns')
+ {
+ foreach ($data as $table => $drop_columns)
+ {
+ if (is_array($drop_columns))
+ {
+ foreach ($drop_columns as $column)
+ {
+ unset($this->tables[$table]['COLUMNS'][$column]);
+ }
+ }
+ else
+ {
+ unset($this->tables[$table]['COLUMNS'][$drop_columns]);
+ }
+ }
+ }
+ else if ($change_type === 'add_unique_index')
+ {
+ foreach ($data as $table => $add_index)
+ {
+ foreach ($add_index as $key => $index_data)
+ {
+ $this->tables[$table]['KEYS'][$key] = array('UNIQUE', $index_data);
+ }
+ }
+ }
+ else if ($change_type === 'add_index')
+ {
+ foreach ($data as $table => $add_index)
+ {
+ foreach ($add_index as $key => $index_data)
+ {
+ $this->tables[$table]['KEYS'][$key] = array('INDEX', $index_data);
+ }
+ }
+ }
+ else if ($change_type === 'drop_keys')
+ {
+ foreach ($data as $table => $drop_keys)
+ {
+ foreach ($drop_keys as $key)
+ {
+ unset($this->tables[$table]['KEYS'][$key]);
+ }
+ }
+ }
+ else
+ {
+ var_dump($change_type);
+ }
+ }
+ unset($migrations[$migration_key]);
+ }
+ else if ($check_dependencies)
+ {
+ $this->dependencies = array_merge($this->dependencies, $open_dependencies);
+ }
+ }
+
+ // Only run this check after the first run
+ if ($check_dependencies)
+ {
+ $this->check_dependencies();
+ $check_dependencies = false;
+ }
+ }
+
+ ksort($this->tables);
+ return $this->tables;
+ }
+
+ /**
+ * Check if one of the migrations files' dependencies can't be resolved
+ * by the supplied list of migrations
+ *
+ * @throws \UnexpectedValueException If a dependency can't be resolved
+ */
+ protected function check_dependencies()
+ {
+ // Strip duplicate values from array
+ $this->dependencies = array_unique($this->dependencies);
+
+ foreach ($this->dependencies as $dependency)
+ {
+ if (!in_array($dependency, $this->class_names))
+ {
+ throw new \UnexpectedValueException("Unable to resolve the dependency '$dependency'");
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/tool/config.php b/phpBB/phpbb/db/migration/tool/config.php
new file mode 100644
index 0000000000..33aa8ff026
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/config.php
@@ -0,0 +1,165 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\tool;
+
+/**
+* Migration config tool
+*/
+class config implements \phpbb\db\migration\tool\tool_interface
+{
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\config $config
+ */
+ public function __construct(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_name()
+ {
+ return 'config';
+ }
+
+ /**
+ * Add a config setting.
+ *
+ * @param string $config_name The name of the config setting
+ * you would like to add
+ * @param mixed $config_value The value of the config setting
+ * @param bool $is_dynamic True if it is dynamic (changes very often)
+ * and should not be stored in the cache, false if not.
+ * @return null
+ */
+ public function add($config_name, $config_value, $is_dynamic = false)
+ {
+ if (isset($this->config[$config_name]))
+ {
+ return;
+ }
+
+ $this->config->set($config_name, $config_value, !$is_dynamic);
+ }
+
+ /**
+ * Update an existing config setting.
+ *
+ * @param string $config_name The name of the config setting you would
+ * like to update
+ * @param mixed $config_value The value of the config setting
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function update($config_name, $config_value)
+ {
+ if (!isset($this->config[$config_name]))
+ {
+ throw new \phpbb\db\migration\exception('CONFIG_NOT_EXIST', $config_name);
+ }
+
+ $this->config->set($config_name, $config_value);
+ }
+
+ /**
+ * Update a config setting if the first argument equal to the
+ * current config value
+ *
+ * @param string $compare If equal to the current config value, will be
+ * updated to the new config value, otherwise not
+ * @param string $config_name The name of the config setting you would
+ * like to update
+ * @param mixed $config_value The value of the config setting
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function update_if_equals($compare, $config_name, $config_value)
+ {
+ if (!isset($this->config[$config_name]))
+ {
+ throw new \phpbb\db\migration\exception('CONFIG_NOT_EXIST', $config_name);
+ }
+
+ $this->config->set_atomic($config_name, $compare, $config_value);
+ }
+
+ /**
+ * Remove an existing config setting.
+ *
+ * @param string $config_name The name of the config setting you would
+ * like to remove
+ * @return null
+ */
+ public function remove($config_name)
+ {
+ if (!isset($this->config[$config_name]))
+ {
+ return;
+ }
+
+ $this->config->delete($config_name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reverse()
+ {
+ $arguments = func_get_args();
+ $original_call = array_shift($arguments);
+
+ $call = false;
+ switch ($original_call)
+ {
+ case 'add':
+ $call = 'remove';
+ break;
+
+ case 'remove':
+ $call = 'add';
+ if (sizeof($arguments) == 1)
+ {
+ $arguments[] = '';
+ }
+ break;
+
+ case 'update_if_equals':
+ $call = 'update_if_equals';
+
+ // Set to the original value if the current value is what we compared to originally
+ $arguments = array(
+ $arguments[2],
+ $arguments[1],
+ $arguments[0],
+ );
+ break;
+
+ case 'reverse':
+ // Reversing a reverse is just the call itself
+ $call = array_shift($arguments);
+ break;
+ }
+
+ if ($call)
+ {
+ return call_user_func_array(array(&$this, $call), $arguments);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/tool/config_text.php b/phpBB/phpbb/db/migration/tool/config_text.php
new file mode 100644
index 0000000000..54b45f6f6d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/config_text.php
@@ -0,0 +1,130 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\tool;
+
+/**
+* Migration config_text tool
+*/
+class config_text implements \phpbb\db\migration\tool\tool_interface
+{
+ /** @var \phpbb\config\db_text */
+ protected $config_text;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\db_text $config_text
+ */
+ public function __construct(\phpbb\config\db_text $config_text)
+ {
+ $this->config_text = $config_text;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_name()
+ {
+ return 'config_text';
+ }
+
+ /**
+ * Add a config_text setting.
+ *
+ * @param string $config_name The name of the config_text setting
+ * you would like to add
+ * @param mixed $config_value The value of the config_text setting
+ * @return null
+ */
+ public function add($config_name, $config_value)
+ {
+ if (!is_null($this->config_text->get($config_name)))
+ {
+ return;
+ }
+
+ $this->config_text->set($config_name, $config_value);
+ }
+
+ /**
+ * Update an existing config_text setting.
+ *
+ * @param string $config_name The name of the config_text setting you would
+ * like to update
+ * @param mixed $config_value The value of the config_text setting
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function update($config_name, $config_value)
+ {
+ if (is_null($this->config_text->get($config_name)))
+ {
+ throw new \phpbb\db\migration\exception('CONFIG_NOT_EXIST', $config_name);
+ }
+
+ $this->config_text->set($config_name, $config_value);
+ }
+
+ /**
+ * Remove an existing config_text setting.
+ *
+ * @param string $config_name The name of the config_text setting you would
+ * like to remove
+ * @return null
+ */
+ public function remove($config_name)
+ {
+ if (is_null($this->config_text->get($config_name)))
+ {
+ return;
+ }
+
+ $this->config_text->delete($config_name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reverse()
+ {
+ $arguments = func_get_args();
+ $original_call = array_shift($arguments);
+
+ $call = false;
+ switch ($original_call)
+ {
+ case 'add':
+ $call = 'remove';
+ break;
+
+ case 'remove':
+ $call = 'add';
+ if (sizeof($arguments) == 1)
+ {
+ $arguments[] = '';
+ }
+ break;
+
+ case 'reverse':
+ // Reversing a reverse is just the call itself
+ $call = array_shift($arguments);
+ break;
+ }
+
+ if ($call)
+ {
+ return call_user_func_array(array(&$this, $call), $arguments);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/tool/module.php b/phpBB/phpbb/db/migration/tool/module.php
new file mode 100644
index 0000000000..7ea7d1dac1
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/module.php
@@ -0,0 +1,563 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\tool;
+
+/**
+* Migration module management tool
+*/
+class module implements \phpbb\db\migration\tool\tool_interface
+{
+ /** @var \phpbb\cache\service */
+ protected $cache;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /** @var string */
+ protected $modules_table;
+
+ /** @var array */
+ protected $module_categories = array();
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\cache\service $cache
+ * @param \phpbb\user $user
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ * @param string $modules_table
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, $phpbb_root_path, $php_ext, $modules_table)
+ {
+ $this->db = $db;
+ $this->cache = $cache;
+ $this->user = $user;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->modules_table = $modules_table;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_name()
+ {
+ return 'module';
+ }
+
+ /**
+ * Module Exists
+ *
+ * Check if a module exists
+ *
+ * @param string $class The module class(acp|mcp|ucp)
+ * @param int|string|bool $parent The parent module_id|module_langname (0 for no parent).
+ * Use false to ignore the parent check and check class wide.
+ * @param int|string $module The module_id|module_langname you would like to
+ * check for to see if it exists
+ * @return bool true/false if module exists
+ */
+ public function exists($class, $parent, $module)
+ {
+ // the main root directory should return true
+ if (!$module)
+ {
+ return true;
+ }
+
+ $parent_sql = '';
+ if ($parent !== false)
+ {
+ $parent = $this->get_parent_module_id($parent, $module, false);
+ if ($parent === false)
+ {
+ return false;
+ }
+
+ $parent_sql = 'AND parent_id = ' . (int) $parent;
+ }
+
+ $sql = 'SELECT module_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ $parent_sql
+ AND " . ((is_numeric($module)) ? 'module_id = ' . (int) $module : "module_langname = '" . $this->db->sql_escape($module) . "'");
+ $result = $this->db->sql_query($sql);
+ $module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ if ($module_id)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Module Add
+ *
+ * Add a new module
+ *
+ * @param string $class The module class(acp|mcp|ucp)
+ * @param int|string $parent The parent module_id|module_langname (0 for no parent)
+ * @param array $data an array of the data on the new \module.
+ * This can be setup in two different ways.
+ * 1. The "manual" way. For inserting a category or one at a time.
+ * It will be merged with the base array shown a bit below,
+ * but at the least requires 'module_langname' to be sent, and,
+ * if you want to create a module (instead of just a category) you must
+ * send module_basename and module_mode.
+ * array(
+ * 'module_enabled' => 1,
+ * 'module_display' => 1,
+ * 'module_basename' => '',
+ * 'module_class' => $class,
+ * 'parent_id' => (int) $parent,
+ * 'module_langname' => '',
+ * 'module_mode' => '',
+ * 'module_auth' => '',
+ * )
+ * 2. The "automatic" way. For inserting multiple at a time based on the
+ * specs in the info file for the module(s). For this to work the
+ * modules must be correctly setup in the info file.
+ * An example follows (this would insert the settings, log, and flag
+ * modes from the includes/acp/info/acp_asacp.php file):
+ * array(
+ * 'module_basename' => 'asacp',
+ * 'modes' => array('settings', 'log', 'flag'),
+ * )
+ * Optionally you may not send 'modes' and it will insert all of the
+ * modules in that info file.
+ * path, specify that here
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function add($class, $parent = 0, $data = array())
+ {
+ // allow sending the name as a string in $data to create a category
+ if (!is_array($data))
+ {
+ $data = array('module_langname' => $data);
+ }
+
+ $parent = $data['parent_id'] = $this->get_parent_module_id($parent, $data);
+
+ if (!isset($data['module_langname']))
+ {
+ // The "automatic" way
+ $basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
+ $module = $this->get_module_info($class, $basename);
+
+ $result = '';
+ foreach ($module['modes'] as $mode => $module_info)
+ {
+ if (!isset($data['modes']) || in_array($mode, $data['modes']))
+ {
+ $new_module = array(
+ 'module_basename' => $basename,
+ 'module_langname' => $module_info['title'],
+ 'module_mode' => $mode,
+ 'module_auth' => $module_info['auth'],
+ 'module_display' => (isset($module_info['display'])) ? $module_info['display'] : true,
+ 'before' => (isset($module_info['before'])) ? $module_info['before'] : false,
+ 'after' => (isset($module_info['after'])) ? $module_info['after'] : false,
+ );
+
+ // Run the "manual" way with the data we've collected.
+ $this->add($class, $parent, $new_module);
+ }
+ }
+
+ return;
+ }
+
+ // The "manual" way
+ if (!$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_EXISTS', $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
+ $module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']);
+ add_log('admin', 'LOG_MODULE_ADD', $module_log_name);
+
+ // Move the module if requested above/below an existing one
+ if (isset($data['before']) && $data['before'])
+ {
+ $sql = 'SELECT left_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND parent_id = " . (int) $parent . "
+ AND module_langname = '" . $this->db->sql_escape($data['before']) . "'";
+ $this->db->sql_query($sql);
+ $to_left = (int) $this->db->sql_fetchfield('left_id');
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id + 2, right_id = right_id + 2
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND left_id >= $to_left
+ AND left_id < {$module_data['left_id']}";
+ $this->db->sql_query($sql);
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = $to_left, right_id = " . ($to_left + 1) . "
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND module_id = {$module_data['module_id']}";
+ $this->db->sql_query($sql);
+ }
+ else if (isset($data['after']) && $data['after'])
+ {
+ $sql = 'SELECT right_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND parent_id = " . (int) $parent . "
+ AND module_langname = '" . $this->db->sql_escape($data['after']) . "'";
+ $this->db->sql_query($sql);
+ $to_right = (int) $this->db->sql_fetchfield('right_id');
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id + 2, right_id = right_id + 2
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND left_id >= $to_right
+ AND left_id < {$module_data['left_id']}";
+ $this->db->sql_query($sql);
+
+ $sql = 'UPDATE ' . $this->modules_table . '
+ SET left_id = ' . ($to_right + 1) . ', right_id = ' . ($to_right + 2) . "
+ WHERE module_class = '" . $this->db->sql_escape($class) . "'
+ AND module_id = {$module_data['module_id']}";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ // Clear the Modules Cache
+ $this->cache->destroy("_modules_$class");
+ }
+
+ /**
+ * Module Remove
+ *
+ * Remove a module
+ *
+ * @param string $class The module class(acp|mcp|ucp)
+ * @param int|string|bool $parent The parent module_id|module_langname(0 for no parent).
+ * Use false to ignore the parent check and check class wide.
+ * @param int|string $module The module id|module_langname
+ * specify that here
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function remove($class, $parent = 0, $module = '')
+ {
+ // Imitation of module_add's "automatic" and "manual" method so the uninstaller works from the same set of instructions for umil_auto
+ if (is_array($module))
+ {
+ if (isset($module['module_langname']))
+ {
+ // Manual Method
+ return $this->remove($class, $parent, $module['module_langname']);
+ }
+
+ // Failed.
+ if (!isset($module['module_basename']))
+ {
+ throw new \phpbb\db\migration\exception('MODULE_NOT_EXIST');
+ }
+
+ // Automatic method
+ $basename = $module['module_basename'];
+ $module_info = $this->get_module_info($class, $basename);
+
+ foreach ($module_info['modes'] as $mode => $info)
+ {
+ if (!isset($module['modes']) || in_array($mode, $module['modes']))
+ {
+ $this->remove($class, $parent, $info['title']);
+ }
+ }
+ }
+ else
+ {
+ if (!$this->exists($class, $parent, $module))
+ {
+ return;
+ }
+
+ $parent_sql = '';
+ if ($parent !== false)
+ {
+ $parent = $this->get_parent_module_id($parent, $module);
+ $parent_sql = 'AND parent_id = ' . (int) $parent;
+ }
+
+ $module_ids = array();
+ if (!is_numeric($module))
+ {
+ $sql = 'SELECT module_id
+ FROM ' . $this->modules_table . "
+ WHERE module_langname = '" . $this->db->sql_escape($module) . "'
+ AND module_class = '" . $this->db->sql_escape($class) . "'
+ $parent_sql";
+ $result = $this->db->sql_query($sql);
+ while ($module_id = $this->db->sql_fetchfield('module_id'))
+ {
+ $module_ids[] = (int) $module_id;
+ }
+ $this->db->sql_freeresult($result);
+ }
+ else
+ {
+ $module_ids[] = (int) $module;
+ }
+
+ if (!class_exists('acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ $this->user->add_lang('acp/modules');
+ }
+ $acp_modules = new \acp_modules();
+ $acp_modules->module_class = $class;
+
+ foreach ($module_ids as $module_id)
+ {
+ $result = $acp_modules->delete_module($module_id);
+ if (!empty($result))
+ {
+ return;
+ }
+ }
+
+ $this->cache->destroy("_modules_$class");
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reverse()
+ {
+ $arguments = func_get_args();
+ $original_call = array_shift($arguments);
+
+ $call = false;
+ switch ($original_call)
+ {
+ case 'add':
+ $call = 'remove';
+ break;
+
+ case 'remove':
+ $call = 'add';
+ break;
+
+ case 'reverse':
+ // Reversing a reverse is just the call itself
+ $call = array_shift($arguments);
+ break;
+ }
+
+ if ($call)
+ {
+ return call_user_func_array(array(&$this, $call), $arguments);
+ }
+ }
+
+ /**
+ * Wrapper for \acp_modules::get_module_infos()
+ *
+ * @param string $class Module Class
+ * @param string $basename Module Basename
+ * @return array Module Information
+ * @throws \phpbb\db\migration\exception
+ */
+ protected function get_module_info($class, $basename)
+ {
+ if (!class_exists('acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ $this->user->add_lang('acp/modules');
+ }
+ $acp_modules = new \acp_modules();
+ $module = $acp_modules->get_module_infos($basename, $class, true);
+
+ if (empty($module))
+ {
+ throw new \phpbb\db\migration\exception('MODULE_INFO_FILE_NOT_EXIST', $class, $basename);
+ }
+
+ return array_pop($module);
+ }
+
+ /**
+ * Get the list of installed module categories
+ * key - module_id
+ * value - module_langname
+ *
+ * @return null
+ */
+ protected function get_categories_list()
+ {
+ // Select the top level categories
+ // and 2nd level [sub]categories
+ $sql = 'SELECT m2.module_id, m2.module_langname
+ FROM ' . $this->modules_table . ' m1, ' . $this->modules_table . " m2
+ WHERE m1.parent_id = 0
+ AND (m1.module_id = m2.module_id OR m2.parent_id = m1.module_id)
+ ORDER BY m1.module_id, m2.module_id ASC";
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->module_categories[(int) $row['module_id']] = $row['module_langname'];
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Get parent module id
+ *
+ * @param string|int $parent_id The parent module_id|module_langname
+ * @param int|string|array $data The module_id, module_langname for existance checking or module data array for adding
+ * @param bool $throw_exception The flag indicating if exception should be thrown on error
+ * @return mixed The int parent module_id or false
+ * @throws \phpbb\db\migration\exception
+ */
+ public function get_parent_module_id($parent_id, $data = '', $throw_exception = true)
+ {
+ // Initialize exception object placeholder
+ $exception = false;
+
+ // Allow '' to be sent as 0
+ $parent_id = $parent_id ?: 0;
+
+ // If automatic adding is in action, convert array back to string to simplify things
+ if (is_array($data) && sizeof($data) == 1)
+ {
+ $data = $data['module_langname'];
+ }
+
+ if (!is_numeric($parent_id))
+ {
+ // Refresh the $module_categories array
+ $this->get_categories_list();
+
+ // Search for the parent module_langname
+ $ids = array_keys($this->module_categories, $parent_id);
+
+ switch (sizeof($ids))
+ {
+ // No parent with the given module_langname exist
+ case 0:
+ $exception = new \phpbb\db\migration\exception('MODULE_NOT_EXIST', $parent_id);
+ break;
+
+ // Return the module id
+ case 1:
+ $parent_id = (int) $ids[0];
+ break;
+
+ // Several modules with the given module_langname were found
+ // Try to determine the parent_id by the neighbour module parent
+ default:
+ if (is_array($data) && (isset($data['before']) || isset($data['after'])))
+ {
+ $neighbour_module_langname = isset($data['before']) ? $data['before'] : $data['after'];
+ $sql = 'SELECT parent_id
+ FROM ' . $this->modules_table . "
+ WHERE module_langname = '" . $this->db->sql_escape($neighbour_module_langname) . "'
+ AND " . $this->db->sql_in_set('parent_id', $ids);
+ $result = $this->db->sql_query($sql);
+ $parent_id = (int) $this->db->sql_fetchfield('parent_id');
+ if (!$parent_id)
+ {
+ $exception = new \phpbb\db\migration\exception('PARENT_MODULE_FIND_ERROR', $data['parent_id']);
+ }
+ }
+ else if (!empty($data) && !is_array($data))
+ {
+ // The module_langname is set, checking for the module existance
+ // As more than 1 parents were found already, there's no way for null parent_id here
+ $sql = 'SELECT m2.module_id as module_parent_id
+ FROM ' . $this->modules_table . ' m1, ' . $this->modules_table . " m2
+ WHERE " . ((is_numeric($data)) ? 'm1.module_id = ' . (int) $data : "m1.module_langname = '" . $this->db->sql_escape($data)) . "'
+ AND m2.module_id = m1.parent_id
+ AND " . $this->db->sql_in_set('m2.module_id', $ids);
+ $result = $this->db->sql_query($sql);
+ $parent_id = (int) $this->db->sql_fetchfield('module_parent_id');
+ }
+ else
+ {
+ //Unable to get the parent module id, throwing an exception
+ $exception = new \phpbb\db\migration\exception('MODULE_EXIST_MULTIPLE', $parent_id);
+ }
+ break;
+ }
+ }
+
+ if ($exception !== false)
+ {
+ if ($throw_exception)
+ {
+ throw $exception;
+ }
+ return false;
+ }
+
+ return $parent_id;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php
new file mode 100644
index 0000000000..9688420025
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/permission.php
@@ -0,0 +1,652 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\tool;
+
+/**
+* Migration permission management tool
+*/
+class permission implements \phpbb\db\migration\tool\tool_interface
+{
+ /** @var \phpbb\auth\auth */
+ protected $auth;
+
+ /** @var \phpbb\cache\service */
+ protected $cache;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\cache\service $cache
+ * @param \phpbb\auth\auth $auth
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\auth\auth $auth, $phpbb_root_path, $php_ext)
+ {
+ $this->db = $db;
+ $this->cache = $cache;
+ $this->auth = $auth;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_name()
+ {
+ return 'permission';
+ }
+
+ /**
+ * Permission Exists
+ *
+ * Check if a permission (auth) setting exists
+ *
+ * @param string $auth_option The name of the permission (auth) option
+ * @param bool $global True for checking a global permission setting,
+ * False for a local permission setting
+ * @return bool true if it exists, false if not
+ */
+ public function exists($auth_option, $global = true)
+ {
+ if ($global)
+ {
+ $type_sql = ' AND is_global = 1';
+ }
+ else
+ {
+ $type_sql = ' AND is_local = 1';
+ }
+
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . "
+ WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
+ . $type_sql;
+ $result = $this->db->sql_query($sql);
+
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Permission Add
+ *
+ * Add a permission (auth) option
+ *
+ * @param string $auth_option The name of the permission (auth) option
+ * @param bool $global True for checking a global permission setting,
+ * False for a local permission setting
+ * @param int|false $copy_from If set, contains the id of the permission from which to copy the new one.
+ * @return null
+ */
+ public function add($auth_option, $global = true, $copy_from = false)
+ {
+ if ($this->exists($auth_option, $global))
+ {
+ return;
+ }
+
+ // We've added permissions, so set to true to notify the user.
+ $this->permissions_added = true;
+
+ if (!class_exists('auth_admin'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
+ }
+ $auth_admin = new \auth_admin();
+
+ // We have to add a check to see if the !$global (if global, local, and if local, global) permission already exists. If it does, acl_add_option currently has a bug which would break the ACL system, so we are having a work-around here.
+ if ($this->exists($auth_option, !$global))
+ {
+ $sql_ary = array(
+ 'is_global' => 1,
+ 'is_local' => 1,
+ );
+ $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
+ WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'";
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ if ($global)
+ {
+ $auth_admin->acl_add_option(array('global' => array($auth_option)));
+ }
+ else
+ {
+ $auth_admin->acl_add_option(array('local' => array($auth_option)));
+ }
+ }
+
+ // The permission has been added, now we can copy it if needed
+ if ($copy_from && isset($auth_admin->acl_options['id'][$copy_from]))
+ {
+ $old_id = $auth_admin->acl_options['id'][$copy_from];
+ $new_id = $auth_admin->acl_options['id'][$auth_option];
+
+ $tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE);
+
+ foreach ($tables as $table)
+ {
+ $sql = 'SELECT *
+ FROM ' . $table . '
+ WHERE auth_option_id = ' . $old_id;
+ $result = $this->db->sql_query($sql);
+
+ $sql_ary = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $row['auth_option_id'] = $new_id;
+ $sql_ary[] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($sql_ary))
+ {
+ $this->db->sql_multi_insert($table, $sql_ary);
+ }
+ }
+
+ $auth_admin->acl_clear_prefetch();
+ }
+ }
+
+ /**
+ * Permission Remove
+ *
+ * Remove a permission (auth) option
+ *
+ * @param string $auth_option The name of the permission (auth) option
+ * @param bool $global True for checking a global permission setting,
+ * False for a local permission setting
+ * @return null
+ */
+ public function remove($auth_option, $global = true)
+ {
+ if (!$this->exists($auth_option, $global))
+ {
+ return;
+ }
+
+ if ($global)
+ {
+ $type_sql = ' AND is_global = 1';
+ }
+ else
+ {
+ $type_sql = ' AND is_local = 1';
+ }
+ $sql = 'SELECT auth_option_id, is_global, is_local
+ FROM ' . ACL_OPTIONS_TABLE . "
+ WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
+ $type_sql;
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $id = (int) $row['auth_option_id'];
+
+ // If it is a local and global permission, do not remove the row! :P
+ if ($row['is_global'] && $row['is_local'])
+ {
+ $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
+ SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
+ WHERE auth_option_id = ' . $id;
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ // Delete time
+ $tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE, ACL_OPTIONS_TABLE);
+ foreach ($tables as $table)
+ {
+ $this->db->sql_query('DELETE FROM ' . $table . '
+ WHERE auth_option_id = ' . $id);
+ }
+ }
+
+ // Purge the auth cache
+ $this->cache->destroy('_acl_options');
+ $this->auth->acl_clear_prefetch();
+ }
+
+ /**
+ * Add a new permission role
+ *
+ * @param string $role_name The new role name
+ * @param string $role_type The type (u_, m_, a_)
+ * @param string $role_description Description of the new role
+ *
+ * @return null
+ */
+ public function role_add($role_name, $role_type, $role_description = '')
+ {
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if ($role_id)
+ {
+ return;
+ }
+
+ $sql = 'SELECT MAX(role_order) AS max_role_order
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = '" . $this->db->sql_escape($role_type) . "'";
+ $this->db->sql_query($sql);
+ $role_order = (int) $this->db->sql_fetchfield('max_role_order');
+ $role_order = (!$role_order) ? 1 : $role_order + 1;
+
+ $sql_ary = array(
+ 'role_name' => $role_name,
+ 'role_description' => $role_description,
+ 'role_type' => $role_type,
+ 'role_order' => $role_order,
+ );
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Update the name on a permission role
+ *
+ * @param string $old_role_name The old role name
+ * @param string $new_role_name The new role name
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function role_update($old_role_name, $new_role_name)
+ {
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $old_role_name);
+ }
+
+ $sql = 'UPDATE ' . ACL_ROLES_TABLE . "
+ SET role_name = '" . $this->db->sql_escape($new_role_name) . "'
+ WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Remove a permission role
+ *
+ * @param string $role_name The role name to remove
+ * @return null
+ */
+ public function role_remove($role_name)
+ {
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ return;
+ }
+
+ $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
+ WHERE role_id = ' . $role_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . ACL_ROLES_TABLE . '
+ WHERE role_id = ' . $role_id;
+ $this->db->sql_query($sql);
+
+ $this->auth->acl_clear_prefetch();
+ }
+
+ /**
+ * Permission Set
+ *
+ * Allows you to set permissions for a certain group/role
+ *
+ * @param string $name The name of the role/group
+ * @param string|array $auth_option The auth_option or array of
+ * auth_options you would like to set
+ * @param string $type The type (role|group)
+ * @param bool $has_permission True if you want to give them permission,
+ * false if you want to deny them permission
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function permission_set($name, $auth_option, $type = 'role', $has_permission = true)
+ {
+ if (!is_array($auth_option))
+ {
+ $auth_option = array($auth_option);
+ }
+
+ $new_auth = array();
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $new_auth[] = (int) $row['auth_option_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($new_auth))
+ {
+ return;
+ }
+
+ $current_auth = array();
+
+ $type = (string) $type; // Prevent PHP bug.
+
+ switch ($type)
+ {
+ case 'role':
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name);
+ }
+
+ $sql = 'SELECT auth_option_id, auth_setting
+ FROM ' . ACL_ROLES_DATA_TABLE . '
+ WHERE role_id = ' . $role_id;
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $current_auth[$row['auth_option_id']] = $row['auth_setting'];
+ }
+ $this->db->sql_freeresult($result);
+ break;
+
+ case 'group':
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+ $group_id = (int) $this->db->sql_fetchfield('group_id');
+
+ if (!$group_id)
+ {
+ throw new \phpbb\db\migration\exception('GROUP_NOT_EXIST', $name);
+ }
+
+ // If the group has a role set for them we will add the requested permissions to that role.
+ $sql = 'SELECT auth_role_id
+ FROM ' . ACL_GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id . '
+ AND auth_role_id <> 0
+ AND forum_id = 0';
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('auth_role_id');
+ if ($role_id)
+ {
+ $sql = 'SELECT role_name, role_type
+ FROM ' . ACL_ROLES_TABLE . '
+ WHERE role_id = ' . $role_id;
+ $this->db->sql_query($sql);
+ $role_data = $this->db->sql_fetchrow();
+ $role_name = $role_data['role_name'];
+ $role_type = $role_data['role_type'];
+
+ // Filter new auth options to match the role type: a_ | f_ | m_ | u_
+ // Set new auth options to the role only if options matching the role type were found
+ $auth_option = array_filter($auth_option,
+ function ($option) use ($role_type)
+ {
+ return strpos($option, $role_type) === 0;
+ }
+ );
+
+ if (sizeof($auth_option))
+ {
+ return $this->permission_set($role_name, $auth_option, 'role', $has_permission);
+ }
+ }
+
+ $sql = 'SELECT auth_option_id, auth_setting
+ FROM ' . ACL_GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id;
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $current_auth[$row['auth_option_id']] = $row['auth_setting'];
+ }
+ $this->db->sql_freeresult($result);
+ break;
+ }
+
+ $sql_ary = array();
+ switch ($type)
+ {
+ case 'role':
+ foreach ($new_auth as $auth_option_id)
+ {
+ if (!isset($current_auth[$auth_option_id]))
+ {
+ $sql_ary[] = array(
+ 'role_id' => $role_id,
+ 'auth_option_id' => $auth_option_id,
+ 'auth_setting' => $has_permission,
+ );
+ }
+ }
+
+ $this->db->sql_multi_insert(ACL_ROLES_DATA_TABLE, $sql_ary);
+ break;
+
+ case 'group':
+ foreach ($new_auth as $auth_option_id)
+ {
+ if (!isset($current_auth[$auth_option_id]))
+ {
+ $sql_ary[] = array(
+ 'group_id' => $group_id,
+ 'auth_option_id' => $auth_option_id,
+ 'auth_setting' => $has_permission,
+ );
+ }
+ }
+
+ $this->db->sql_multi_insert(ACL_GROUPS_TABLE, $sql_ary);
+ break;
+ }
+
+ $this->auth->acl_clear_prefetch();
+ }
+
+ /**
+ * Permission Unset
+ *
+ * Allows you to unset (remove) permissions for a certain group/role
+ *
+ * @param string $name The name of the role/group
+ * @param string|array $auth_option The auth_option or array of
+ * auth_options you would like to set
+ * @param string $type The type (role|group)
+ * @return null
+ * @throws \phpbb\db\migration\exception
+ */
+ public function permission_unset($name, $auth_option, $type = 'role')
+ {
+ if (!is_array($auth_option))
+ {
+ $auth_option = array($auth_option);
+ }
+
+ $to_remove = array();
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $to_remove[] = (int) $row['auth_option_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($to_remove))
+ {
+ return;
+ }
+
+ $type = (string) $type; // Prevent PHP bug.
+
+ switch ($type)
+ {
+ case 'role':
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+ $role_id = (int) $this->db->sql_fetchfield('role_id');
+
+ if (!$role_id)
+ {
+ throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name);
+ }
+
+ $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
+ WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove) . '
+ AND role_id = ' . (int) $role_id;
+ $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;
+
+ case 'reverse':
+ // Reversing a reverse is just the call itself
+ $call = array_shift($arguments);
+ break;
+ }
+
+ if ($call)
+ {
+ return call_user_func_array(array(&$this, $call), $arguments);
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migration/tool/tool_interface.php b/phpBB/phpbb/db/migration/tool/tool_interface.php
new file mode 100644
index 0000000000..07cd2435e4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/tool_interface.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\tool;
+
+/**
+* Migration tool interface
+*/
+interface tool_interface
+{
+ /**
+ * Retrieve a short name used for commands in migrations.
+ *
+ * @return string short name
+ */
+ public function get_name();
+
+ /**
+ * Reverse an original install action
+ *
+ * First argument is the original call to the class (e.g. add, remove)
+ * After the first argument, send the original arguments to the function in the original call
+ *
+ * @return null
+ */
+ public function reverse();
+}
diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php
new file mode 100644
index 0000000000..45a333ac94
--- /dev/null
+++ b/phpBB/phpbb/db/migrator.php
@@ -0,0 +1,924 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+* The migrator is responsible for applying new migrations in the correct order.
+*/
+class migrator
+{
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\db\tools */
+ protected $db_tools;
+
+ /** @var \phpbb\db\migration\helper */
+ protected $helper;
+
+ /** @var string */
+ protected $table_prefix;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /** @var string */
+ protected $migrations_table;
+
+ /**
+ * State of all migrations
+ *
+ * (SELECT * FROM migrations table)
+ *
+ * @var array
+ */
+ protected $migration_state = array();
+
+ /**
+ * Array of all migrations available to be run
+ *
+ * @var array
+ */
+ protected $migrations = array();
+
+ /**
+ * Array of migrations that have been determined to be fulfillable
+ *
+ * @var array
+ */
+ protected $fulfillable_migrations = array();
+
+ /**
+ * 'name,' 'class,' and 'state' of the last migration run
+ *
+ * 'effectively_installed' set and set to true if the migration was effectively_installed
+ *
+ * @var array
+ */
+ protected $last_run_migration = false;
+
+ /**
+ * The output handler. A null handler is configured by default.
+ *
+ * @var migrator_output_handler_interface
+ */
+ protected $output_handler;
+
+ /**
+ * Constructor of the database migrator
+ */
+ public function __construct(ContainerInterface $container, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper)
+ {
+ $this->container = $container;
+ $this->config = $config;
+ $this->db = $db;
+ $this->db_tools = $db_tools;
+ $this->helper = $helper;
+
+ $this->migrations_table = $migrations_table;
+
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ $this->table_prefix = $table_prefix;
+
+ $this->output_handler = new null_migrator_output_handler();
+
+ foreach ($tools as $tool)
+ {
+ $this->tools[$tool->get_name()] = $tool;
+ }
+
+ $this->tools['dbtools'] = $this->db_tools;
+
+ $this->load_migration_state();
+ }
+
+ /**
+ * Set the output handler.
+ *
+ * @param migrator_output_handler $handler The output handler
+ */
+ public function set_output_handler(migrator_output_handler_interface $handler)
+ {
+ $this->output_handler = $handler;
+ }
+
+ /**
+ * 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->get_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->migration_state[$migration['migration_name']]['migration_data_state'] = !empty($migration['migration_data_state']) ? unserialize($migration['migration_data_state']) : '';
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+
+ $this->db->sql_return_on_error(false);
+ }
+
+ /**
+ * Get an array with information about the last migration run.
+ *
+ * The array contains 'name', 'class' and 'state'. 'effectively_installed' is set
+ * and set to true if the last migration was effectively_installed.
+ *
+ * @return array
+ */
+ public function get_last_run_migration()
+ {
+ return $this->last_run_migration;
+ }
+
+ /**
+ * Sets the list of available migration class names to the given array.
+ *
+ * @param array $class_names An array of migration class names
+ * @return null
+ */
+ public function set_migrations($class_names)
+ {
+ $this->migrations = $class_names;
+ }
+
+ /**
+ * Runs a single update step from the next migration to be applied.
+ *
+ * The update step can either be a schema or a (partial) data update. To
+ * check if update() needs to be called again use the finished() method.
+ *
+ * @return null
+ */
+ public function update()
+ {
+ $this->container->get('dispatcher')->disable();
+ $this->update_do();
+ $this->container->get('dispatcher')->enable();
+ }
+
+ /**
+ * Get a valid migration name from the migration state array in case the
+ * supplied name is not in the migration state list.
+ *
+ * @param string $name Migration name
+ * @return string Migration name
+ */
+ protected function get_valid_name($name)
+ {
+ // Try falling back to a valid migration name with or without leading backslash
+ if (!isset($this->migration_state[$name]))
+ {
+ $prepended_name = ($name[0] == '\\' ? '' : '\\') . $name;
+ $prefixless_name = $name[0] == '\\' ? substr($name, 1) : $name;
+
+ if (isset($this->migration_state[$prepended_name]))
+ {
+ $name = $prepended_name;
+ }
+ else if (isset($this->migration_state[$prefixless_name]))
+ {
+ $name = $prefixless_name;
+ }
+ }
+
+ return $name;
+ }
+
+ /**
+ * Effectively runs a single update step from the next migration to be applied.
+ *
+ * @return null
+ */
+ protected function update_do()
+ {
+ foreach ($this->migrations as $name)
+ {
+ $name = $this->get_valid_name($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;
+ }
+ }
+ else
+ {
+ $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
+ }
+ }
+ }
+
+ /**
+ * Attempts to apply a step of the given migration or one of its dependencies
+ *
+ * @param string $name The class name of the migration
+ * @return bool Whether any update step was successfully run
+ * @throws \phpbb\db\migration\exception
+ */
+ protected function try_apply($name)
+ {
+ if (!class_exists($name))
+ {
+ $this->output_handler->write(array('MIGRATION_NOT_VALID', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
+ 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,
+ );
+
+ if (!empty($state['migration_depends_on']))
+ {
+ $this->output_handler->write(array('MIGRATION_APPLY_DEPENDENCIES', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
+ }
+
+ foreach ($state['migration_depends_on'] as $depend)
+ {
+ $depend = $this->get_valid_name($depend);
+
+ // Test all possible namings before throwing exception
+ if ($this->unfulfillable($depend) !== false)
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend);
+ }
+
+ if (!isset($this->migration_state[$depend]) ||
+ !$this->migration_state[$depend]['migration_schema_done'] ||
+ !$this->migration_state[$depend]['migration_data_done'])
+ {
+ return $this->try_apply($depend);
+ }
+ }
+
+ $this->last_run_migration = array(
+ 'name' => $name,
+ 'class' => $migration,
+ 'state' => $state,
+ 'task' => '',
+ );
+
+ if (!isset($this->migration_state[$name]))
+ {
+ if ($state['migration_start_time'] == 0 && $migration->effectively_installed())
+ {
+ $state = array(
+ 'migration_depends_on' => $migration->depends_on(),
+ 'migration_schema_done' => true,
+ 'migration_data_done' => true,
+ 'migration_data_state' => '',
+ 'migration_start_time' => 0,
+ 'migration_end_time' => 0,
+ );
+
+ $this->last_run_migration['effectively_installed'] = true;
+
+ $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
+ }
+ else
+ {
+ $state['migration_start_time'] = time();
+ }
+ }
+
+ $this->set_migration_state($name, $state);
+
+ if (!$state['migration_schema_done'])
+ {
+ $verbosity = empty($state['migration_data_state']) ?
+ migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG;
+ $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), $verbosity);
+
+ $this->last_run_migration['task'] = 'process_schema_step';
+
+ $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ?
+ $state['migration_data_state']['_total_time'] : 0.0;
+ $elapsed_time = microtime(true);
+
+ $steps = $this->helper->get_schema_steps($migration->update_schema());
+ $result = $this->process_data_step($steps, $state['migration_data_state']);
+
+ $elapsed_time = microtime(true) - $elapsed_time;
+ $total_time += $elapsed_time;
+
+ if (is_array($result))
+ {
+ $result['_total_time'] = $total_time;
+ }
+
+ $state['migration_data_state'] = ($result === true) ? '' : $result;
+ $state['migration_schema_done'] = ($result === true);
+
+ if ($state['migration_schema_done'])
+ {
+ $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
+ }
+ else
+ {
+ $this->output_handler->write(array('MIGRATION_SCHEMA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
+ }
+ }
+ else if (!$state['migration_data_done'])
+ {
+ try
+ {
+ $verbosity = empty($state['migration_data_state']) ?
+ migrator_output_handler_interface::VERBOSITY_VERBOSE : migrator_output_handler_interface::VERBOSITY_DEBUG;
+ $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), $verbosity);
+
+ $this->last_run_migration['task'] = 'process_data_step';
+
+ $total_time = (is_array($state['migration_data_state']) && isset($state['migration_data_state']['_total_time'])) ?
+ $state['migration_data_state']['_total_time'] : 0.0;
+ $elapsed_time = microtime(true);
+
+ $result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
+
+ $elapsed_time = microtime(true) - $elapsed_time;
+ $total_time += $elapsed_time;
+
+ if (is_array($result))
+ {
+ $result['_total_time'] = $total_time;
+ }
+
+ $state['migration_data_state'] = ($result === true) ? '' : $result;
+ $state['migration_data_done'] = ($result === true);
+ $state['migration_end_time'] = ($result === true) ? time() : 0;
+
+ if ($state['migration_data_done'])
+ {
+ $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $total_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
+ }
+ else
+ {
+ $this->output_handler->write(array('MIGRATION_DATA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
+ }
+ }
+ catch (\phpbb\db\migration\exception $e)
+ {
+ // Reset data state and revert the schema changes
+ $state['migration_data_state'] = '';
+ $this->set_migration_state($name, $state);
+
+ $this->revert_do($name);
+
+ throw $e;
+ }
+ }
+
+ $this->set_migration_state($name, $state);
+
+ return true;
+ }
+
+ /**
+ * Runs a single revert step from the last migration installed
+ *
+ * YOU MUST ADD/SET ALL MIGRATIONS THAT COULD BE DEPENDENT ON THE MIGRATION TO REVERT TO BEFORE CALLING THIS METHOD!
+ * The revert step can either be a schema or a (partial) data revert. To
+ * check if revert() needs to be called again use the migration_state() method.
+ *
+ * @param string $migration String migration name to revert (including any that depend on this migration)
+ */
+ public function revert($migration)
+ {
+ $this->container->get('dispatcher')->disable();
+ $this->revert_do($migration);
+ $this->container->get('dispatcher')->enable();
+ }
+
+ /**
+ * Effectively runs a single revert step from the last migration installed
+ *
+ * @param string $migration String migration name to revert (including any that depend on this migration)
+ * @return null
+ */
+ protected function revert_do($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_do($name);
+ }
+ }
+
+ $this->try_revert($migration);
+ }
+
+ /**
+ * Attempts to revert a step of the given migration or one of its dependencies
+ *
+ * @param string $name The class name of the migration
+ * @return bool Whether any update step was successfully run
+ */
+ protected function try_revert($name)
+ {
+ if (!class_exists($name))
+ {
+ return false;
+ }
+
+ $migration = $this->get_migration($name);
+
+ $state = $this->migration_state[$name];
+
+ $this->last_run_migration = array(
+ 'name' => $name,
+ 'class' => $migration,
+ 'task' => '',
+ );
+
+ if ($state['migration_data_done'])
+ {
+ $steps = array_merge($this->helper->reverse_update_data($migration->update_data()), $migration->revert_data());
+ $result = $this->process_data_step($steps, $state['migration_data_state']);
+
+ $state['migration_data_state'] = ($result === true) ? '' : $result;
+ $state['migration_data_done'] = ($result === true) ? false : true;
+
+ $this->set_migration_state($name, $state);
+ }
+ else if ($state['migration_schema_done'])
+ {
+ $steps = $this->helper->get_schema_steps($migration->revert_schema());
+ $result = $this->process_data_step($steps, $state['migration_data_state']);
+
+ $state['migration_data_state'] = ($result === true) ? '' : $result;
+ $state['migration_schema_done'] = ($result === true) ? false : true;
+
+ if (!$state['migration_schema_done'])
+ {
+ $sql = 'DELETE FROM ' . $this->migrations_table . "
+ WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ $this->last_run_migration = false;
+ unset($this->migration_state[$name]);
+ }
+ else
+ {
+ $this->set_migration_state($name, $state);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Process the data step of the migration
+ *
+ * @param array $steps The steps to run
+ * @param bool|string $state Current state of the migration
+ * @param bool $revert true to revert a data step
+ * @return bool|string migration state. True if completed, serialized array if not finished
+ * @throws \phpbb\db\migration\exception
+ */
+ protected function process_data_step($steps, $state, $revert = false)
+ {
+ if (sizeof($steps) === 0)
+ {
+ return true;
+ }
+
+ $state = is_array($state) ? $state : false;
+
+ // reverse order of steps if reverting
+ if ($revert === true)
+ {
+ $steps = array_reverse($steps);
+ }
+
+ $step = $last_result = 0;
+ if ($state)
+ {
+ $step = $state['step'];
+
+ // We send the result from last time to the callable function
+ $last_result = $state['result'];
+ }
+
+ try
+ {
+ // Result will be null or true if everything completed correctly
+ // Stop after each update step, to let the updater control the script runtime
+ $result = $this->run_step($steps[$step], $last_result, $revert);
+ if (($result !== null && $result !== true) || $step + 1 < sizeof($steps))
+ {
+ return array(
+ 'result' => $result,
+ // Move on if the last call finished
+ 'step' => ($result !== null && $result !== true) ? $step : $step + 1,
+ );
+ }
+ }
+ 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)
+ {
+ break;
+ }
+
+ // Reverse the step that was run
+ $result = $this->run_step($reverse_step, false, !$revert);
+ }
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * Run a single step
+ *
+ * An exception should be thrown if an error occurs
+ *
+ * @param mixed $step Data step from migration
+ * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
+ * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
+ * @return null
+ */
+ protected function run_step($step, $last_result = 0, $reverse = false)
+ {
+ $callable_and_parameters = $this->get_callable_from_step($step, $last_result, $reverse);
+
+ if ($callable_and_parameters === false)
+ {
+ return;
+ }
+
+ $callable = $callable_and_parameters[0];
+ $parameters = $callable_and_parameters[1];
+
+ return call_user_func_array($callable, $parameters);
+ }
+
+ /**
+ * Get a callable statement from a data step
+ *
+ * @param array $step Data step from migration
+ * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
+ * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
+ * @return array Array with parameters for call_user_func_array(), 0 is the callable, 1 is parameters
+ * @throws \phpbb\db\migration\exception
+ */
+ protected function get_callable_from_step(array $step, $last_result = 0, $reverse = false)
+ {
+ $type = $step[0];
+ $parameters = $step[1];
+
+ $parts = explode('.', $type);
+
+ $class = $parts[0];
+ $method = false;
+
+ if (isset($parts[1]))
+ {
+ $method = $parts[1];
+ }
+
+ switch ($class)
+ {
+ case 'if':
+ if (!isset($parameters[0]))
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_CONDITION', $step);
+ }
+
+ if (!isset($parameters[1]))
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
+ }
+
+ if ($reverse)
+ {
+ // We might get unexpected results when trying
+ // to revert this, so just avoid it
+ return false;
+ }
+
+ $condition = $parameters[0];
+
+ if (!$condition)
+ {
+ return false;
+ }
+
+ $step = $parameters[1];
+
+ return $this->get_callable_from_step($step);
+ break;
+
+ case 'custom':
+ if (!is_callable($parameters[0]))
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step);
+ }
+
+ if ($reverse)
+ {
+ return false;
+ }
+ else
+ {
+ return array(
+ $parameters[0],
+ array($last_result),
+ );
+ }
+ break;
+
+ default:
+ if (!$method)
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNKNOWN_TYPE', $step);
+ }
+
+ if (!isset($this->tools[$class]))
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_TOOL', $step);
+ }
+
+ if (!method_exists(get_class($this->tools[$class]), $method))
+ {
+ throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_METHOD', $step);
+ }
+
+ // Attempt to reverse operations
+ if ($reverse)
+ {
+ array_unshift($parameters, $method);
+
+ return array(
+ array($this->tools[$class], 'reverse'),
+ $parameters,
+ );
+ }
+
+ return array(
+ array($this->tools[$class], $method),
+ $parameters,
+ );
+ break;
+ }
+ }
+
+ /**
+ * Insert/Update migration row into the database
+ *
+ * @param string $name Name of the migration
+ * @param array $state
+ * @return null
+ */
+ protected function set_migration_state($name, $state)
+ {
+ $migration_row = $state;
+ $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
+ $migration_row['migration_data_state'] = !empty($state['migration_data_state']) ? serialize($state['migration_data_state']) : '';
+
+ if (isset($this->migration_state[$name]))
+ {
+ $sql = 'UPDATE ' . $this->migrations_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . "
+ WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ $migration_row['migration_name'] = $name;
+ $sql = 'INSERT INTO ' . $this->migrations_table . '
+ ' . $this->db->sql_build_array('INSERT', $migration_row);
+ $this->db->sql_query($sql);
+ }
+
+ $this->migration_state[$name] = $state;
+
+ $this->last_run_migration['state'] = $state;
+ }
+
+ /**
+ * Checks if a migration's dependencies can even theoretically be satisfied.
+ *
+ * @param string $name The class name of the migration
+ * @return bool|string False if fulfillable, string of missing migration name if unfulfillable
+ */
+ public function unfulfillable($name)
+ {
+ $name = $this->get_valid_name($name);
+
+ if (isset($this->migration_state[$name]) || isset($this->fulfillable_migrations[$name]))
+ {
+ return false;
+ }
+
+ if (!class_exists($name))
+ {
+ return $name;
+ }
+
+ $migration = $this->get_migration($name);
+ $depends = $migration->depends_on();
+
+ foreach ($depends as $depend)
+ {
+ $depend = $this->get_valid_name($depend);
+ $unfulfillable = $this->unfulfillable($depend);
+ if ($unfulfillable !== false)
+ {
+ return $unfulfillable;
+ }
+ }
+ $this->fulfillable_migrations[$name] = true;
+
+ return false;
+ }
+
+ /**
+ * Checks whether all available, fulfillable migrations have been applied.
+ *
+ * @return bool Whether the migrations have been applied
+ */
+ public function finished()
+ {
+ foreach ($this->migrations as $name)
+ {
+ if (!isset($this->migration_state[$name]))
+ {
+ // skip unfulfillable migrations, but fulfillables mean we
+ // are not finished yet
+ if ($this->unfulfillable($name) !== false)
+ {
+ continue;
+ }
+
+ return false;
+ }
+
+ $migration = $this->migration_state[$name];
+
+ if (!$migration['migration_schema_done'] || !$migration['migration_data_done'])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets a migration state (whether it is installed and to what extent)
+ *
+ * @param string $migration String migration name to check if it is installed
+ * @return bool|array False if the migration has not at all been installed, array
+ */
+ public function migration_state($migration)
+ {
+ if (!isset($this->migration_state[$migration]))
+ {
+ return false;
+ }
+
+ return $this->migration_state[$migration];
+ }
+
+ /**
+ * Helper to get a migration
+ *
+ * @param string $name Name of the migration
+ * @return \phpbb\db\migration\migration
+ */
+ protected function get_migration($name)
+ {
+ $migration = new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
+
+ if ($migration instanceof ContainerAwareInterface)
+ {
+ $migration->setContainer($this->container);
+ }
+
+ return $migration;
+ }
+
+ /**
+ * This function adds all migrations sent to it to the migrations table
+ *
+ * THIS SHOULD NOT GENERALLY BE USED! THIS IS FOR THE PHPBB INSTALLER.
+ * THIS WILL THROW ERRORS IF MIGRATIONS ALREADY EXIST IN THE TABLE, DO NOT CALL MORE THAN ONCE!
+ *
+ * @param array $migrations Array of migrations (names) to add to the migrations table
+ * @return null
+ */
+ public function populate_migrations($migrations)
+ {
+ foreach ($migrations as $name)
+ {
+ if ($this->migration_state($name) === false)
+ {
+ $state = array(
+ 'migration_depends_on' => $name::depends_on(),
+ 'migration_schema_done' => true,
+ 'migration_data_done' => true,
+ 'migration_data_state' => '',
+ 'migration_start_time' => time(),
+ 'migration_end_time' => time(),
+ );
+ $this->set_migration_state($name, $state);
+ }
+ }
+ }
+
+ /**
+ * Creates the migrations table if it does not exist.
+ * @return null
+ */
+ public function create_migrations_table()
+ {
+ // Make sure migrations have been installed.
+ if (!$this->db_tools->sql_table_exists($this->table_prefix . 'migrations'))
+ {
+ $this->db_tools->sql_create_table($this->table_prefix . 'migrations', array(
+ 'COLUMNS' => array(
+ 'migration_name' => array('VCHAR', ''),
+ 'migration_depends_on' => array('TEXT', ''),
+ 'migration_schema_done' => array('BOOL', 0),
+ 'migration_data_done' => array('BOOL', 0),
+ 'migration_data_state' => array('TEXT', ''),
+ 'migration_start_time' => array('TIMESTAMP', 0),
+ 'migration_end_time' => array('TIMESTAMP', 0),
+ ),
+ 'PRIMARY_KEY' => 'migration_name',
+ ));
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/migrator_output_handler_interface.php b/phpBB/phpbb/db/migrator_output_handler_interface.php
new file mode 100644
index 0000000000..9947b51dcc
--- /dev/null
+++ b/phpBB/phpbb/db/migrator_output_handler_interface.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+interface migrator_output_handler_interface
+{
+ const VERBOSITY_QUIET = 16;
+ const VERBOSITY_NORMAL = 32;
+ const VERBOSITY_VERBOSE = 64;
+ const VERBOSITY_VERY_VERBOSE = 128;
+ const VERBOSITY_DEBUG = 256;
+
+ /**
+ * Write output using the configured closure.
+ *
+ * @param string|array $message The message to write or an array containing the language key and all of its parameters.
+ * @param int $verbosity The verbosity of the message.
+ */
+ public function write($message, $verbosity);
+}
diff --git a/phpBB/phpbb/db/null_migrator_output_handler.php b/phpBB/phpbb/db/null_migrator_output_handler.php
new file mode 100644
index 0000000000..0e8cfbb049
--- /dev/null
+++ b/phpBB/phpbb/db/null_migrator_output_handler.php
@@ -0,0 +1,24 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+class null_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ }
+}
diff --git a/phpBB/phpbb/db/sql_insert_buffer.php b/phpBB/phpbb/db/sql_insert_buffer.php
new file mode 100644
index 0000000000..18e4814a77
--- /dev/null
+++ b/phpBB/phpbb/db/sql_insert_buffer.php
@@ -0,0 +1,146 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+/**
+* Collects rows for insert into a database until the buffer size is reached.
+* Then flushes the buffer to the database and starts over again.
+*
+* Benefits over collecting a (possibly huge) insert array and then using
+* $db->sql_multi_insert() include:
+*
+* - Going over max packet size of the database connection is usually prevented
+* because the data is submitted in batches.
+*
+* - Reaching database connection timeout is usually prevented because
+* submission of batches talks to the database every now and then.
+*
+* - Usage of less PHP memory because data no longer needed is discarded on
+* buffer flush.
+*
+* Attention:
+* Please note that users of this class have to call flush() to flush the
+* remaining rows to the database after their batch insert operation is
+* finished.
+*
+* Usage:
+* <code>
+* $buffer = new \phpbb\db\sql_insert_buffer($db, 'test_table', 1234);
+*
+* while (do_stuff())
+* {
+* $buffer->insert(array(
+* 'column1' => 'value1',
+* 'column2' => 'value2',
+* ));
+* }
+*
+* $buffer->flush();
+* </code>
+*/
+class sql_insert_buffer
+{
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var string */
+ protected $table_name;
+
+ /** @var int */
+ protected $max_buffered_rows;
+
+ /** @var array */
+ protected $buffer = array();
+
+ /**
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param string $table_name
+ * @param int $max_buffered_rows
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, $table_name, $max_buffered_rows = 500)
+ {
+ $this->db = $db;
+ $this->table_name = $table_name;
+ $this->max_buffered_rows = $max_buffered_rows;
+ }
+
+ /**
+ * Inserts a single row into the buffer if multi insert is supported by the
+ * database (otherwise an insert query is sent immediately). Then flushes
+ * the buffer if the number of rows in the buffer is now greater than or
+ * equal to $max_buffered_rows.
+ *
+ * @param array $row
+ *
+ * @return bool True when some data was flushed to the database.
+ * False otherwise.
+ */
+ public function insert(array $row)
+ {
+ $this->buffer[] = $row;
+
+ // Flush buffer if it is full or when DB does not support multi inserts.
+ // In the later case, the buffer will always only contain one row.
+ if (!$this->db->get_multi_insert() || sizeof($this->buffer) >= $this->max_buffered_rows)
+ {
+ return $this->flush();
+ }
+
+ return false;
+ }
+
+ /**
+ * Inserts a row set, i.e. an array of rows, by calling insert().
+ *
+ * Please note that it is in most cases better to use insert() instead of
+ * first building a huge rowset. Or at least sizeof($rows) should be kept
+ * small.
+ *
+ * @param array $rows
+ *
+ * @return bool True when some data was flushed to the database.
+ * False otherwise.
+ */
+ public function insert_all(array $rows)
+ {
+ // Using bitwise |= because PHP does not have logical ||=
+ $result = 0;
+
+ foreach ($rows as $row)
+ {
+ $result |= (int) $this->insert($row);
+ }
+
+ return (bool) $result;
+ }
+
+ /**
+ * Flushes the buffer content to the DB and clears the buffer.
+ *
+ * @return bool True when some data was flushed to the database.
+ * False otherwise.
+ */
+ public function flush()
+ {
+ if (!empty($this->buffer))
+ {
+ $this->db->sql_multi_insert($this->table_name, $this->buffer);
+ $this->buffer = array();
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/db/tools.php b/phpBB/phpbb/db/tools.php
new file mode 100644
index 0000000000..832a0c510c
--- /dev/null
+++ b/phpBB/phpbb/db/tools.php
@@ -0,0 +1,2840 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db;
+
+/**
+* Database Tools for handling cross-db actions such as altering columns, etc.
+* Currently not supported is returning SQL for creating tables.
+*/
+class tools
+{
+ /**
+ * Current sql layer
+ */
+ var $sql_layer = '';
+
+ /**
+ * @var object DB object
+ */
+ var $db = null;
+
+ /**
+ * The Column types for every database we support
+ * @var array
+ */
+ var $dbms_type_map = array();
+
+ /**
+ * Is the used MS SQL Server a SQL Server 2000?
+ * @var bool
+ */
+ protected $is_sql_server_2000;
+
+ /**
+ * Get the column types for every database we support
+ *
+ * @return array
+ */
+ public static function get_dbms_type_map()
+ {
+ return array(
+ 'mysql_41' => array(
+ 'INT:' => 'int(%d)',
+ 'BINT' => 'bigint(20)',
+ 'UINT' => 'mediumint(8) UNSIGNED',
+ 'UINT:' => 'int(%d) UNSIGNED',
+ 'TINT:' => 'tinyint(%d)',
+ 'USINT' => 'smallint(4) UNSIGNED',
+ 'BOOL' => 'tinyint(1) UNSIGNED',
+ 'VCHAR' => 'varchar(255)',
+ 'VCHAR:' => 'varchar(%d)',
+ 'CHAR:' => 'char(%d)',
+ 'XSTEXT' => 'text',
+ 'XSTEXT_UNI'=> 'varchar(100)',
+ 'STEXT' => 'text',
+ 'STEXT_UNI' => 'varchar(255)',
+ 'TEXT' => 'text',
+ 'TEXT_UNI' => 'text',
+ 'MTEXT' => 'mediumtext',
+ 'MTEXT_UNI' => 'mediumtext',
+ 'TIMESTAMP' => 'int(11) UNSIGNED',
+ 'DECIMAL' => 'decimal(5,2)',
+ 'DECIMAL:' => 'decimal(%d,2)',
+ 'PDECIMAL' => 'decimal(6,3)',
+ 'PDECIMAL:' => 'decimal(%d,3)',
+ 'VCHAR_UNI' => 'varchar(255)',
+ 'VCHAR_UNI:'=> 'varchar(%d)',
+ 'VCHAR_CI' => 'varchar(255)',
+ 'VARBINARY' => 'varbinary(255)',
+ ),
+
+ 'mysql_40' => array(
+ 'INT:' => 'int(%d)',
+ 'BINT' => 'bigint(20)',
+ 'UINT' => 'mediumint(8) UNSIGNED',
+ 'UINT:' => 'int(%d) UNSIGNED',
+ 'TINT:' => 'tinyint(%d)',
+ 'USINT' => 'smallint(4) UNSIGNED',
+ 'BOOL' => 'tinyint(1) UNSIGNED',
+ 'VCHAR' => 'varbinary(255)',
+ 'VCHAR:' => 'varbinary(%d)',
+ 'CHAR:' => 'binary(%d)',
+ 'XSTEXT' => 'blob',
+ 'XSTEXT_UNI'=> 'blob',
+ 'STEXT' => 'blob',
+ 'STEXT_UNI' => 'blob',
+ 'TEXT' => 'blob',
+ 'TEXT_UNI' => 'blob',
+ 'MTEXT' => 'mediumblob',
+ 'MTEXT_UNI' => 'mediumblob',
+ 'TIMESTAMP' => 'int(11) UNSIGNED',
+ 'DECIMAL' => 'decimal(5,2)',
+ 'DECIMAL:' => 'decimal(%d,2)',
+ 'PDECIMAL' => 'decimal(6,3)',
+ 'PDECIMAL:' => 'decimal(%d,3)',
+ 'VCHAR_UNI' => 'blob',
+ 'VCHAR_UNI:'=> array('varbinary(%d)', 'limit' => array('mult', 3, 255, 'blob')),
+ 'VCHAR_CI' => 'blob',
+ 'VARBINARY' => 'varbinary(255)',
+ ),
+
+ 'mssql' => array(
+ 'INT:' => '[int]',
+ 'BINT' => '[float]',
+ 'UINT' => '[int]',
+ 'UINT:' => '[int]',
+ 'TINT:' => '[int]',
+ 'USINT' => '[int]',
+ 'BOOL' => '[int]',
+ 'VCHAR' => '[varchar] (255)',
+ 'VCHAR:' => '[varchar] (%d)',
+ 'CHAR:' => '[char] (%d)',
+ 'XSTEXT' => '[varchar] (1000)',
+ 'STEXT' => '[varchar] (3000)',
+ 'TEXT' => '[varchar] (8000)',
+ 'MTEXT' => '[text]',
+ 'XSTEXT_UNI'=> '[varchar] (100)',
+ 'STEXT_UNI' => '[varchar] (255)',
+ 'TEXT_UNI' => '[varchar] (4000)',
+ 'MTEXT_UNI' => '[text]',
+ 'TIMESTAMP' => '[int]',
+ 'DECIMAL' => '[float]',
+ 'DECIMAL:' => '[float]',
+ 'PDECIMAL' => '[float]',
+ 'PDECIMAL:' => '[float]',
+ 'VCHAR_UNI' => '[varchar] (255)',
+ 'VCHAR_UNI:'=> '[varchar] (%d)',
+ 'VCHAR_CI' => '[varchar] (255)',
+ 'VARBINARY' => '[varchar] (255)',
+ ),
+
+ 'mssqlnative' => array(
+ 'INT:' => '[int]',
+ 'BINT' => '[float]',
+ 'UINT' => '[int]',
+ 'UINT:' => '[int]',
+ 'TINT:' => '[int]',
+ 'USINT' => '[int]',
+ 'BOOL' => '[int]',
+ 'VCHAR' => '[varchar] (255)',
+ 'VCHAR:' => '[varchar] (%d)',
+ 'CHAR:' => '[char] (%d)',
+ 'XSTEXT' => '[varchar] (1000)',
+ 'STEXT' => '[varchar] (3000)',
+ 'TEXT' => '[varchar] (8000)',
+ 'MTEXT' => '[text]',
+ 'XSTEXT_UNI'=> '[varchar] (100)',
+ 'STEXT_UNI' => '[varchar] (255)',
+ 'TEXT_UNI' => '[varchar] (4000)',
+ 'MTEXT_UNI' => '[text]',
+ 'TIMESTAMP' => '[int]',
+ 'DECIMAL' => '[float]',
+ 'DECIMAL:' => '[float]',
+ 'PDECIMAL' => '[float]',
+ 'PDECIMAL:' => '[float]',
+ 'VCHAR_UNI' => '[varchar] (255)',
+ 'VCHAR_UNI:'=> '[varchar] (%d)',
+ 'VCHAR_CI' => '[varchar] (255)',
+ 'VARBINARY' => '[varchar] (255)',
+ ),
+
+ 'oracle' => array(
+ 'INT:' => 'number(%d)',
+ 'BINT' => 'number(20)',
+ 'UINT' => 'number(8)',
+ 'UINT:' => 'number(%d)',
+ 'TINT:' => 'number(%d)',
+ 'USINT' => 'number(4)',
+ 'BOOL' => 'number(1)',
+ 'VCHAR' => 'varchar2(255)',
+ 'VCHAR:' => 'varchar2(%d)',
+ 'CHAR:' => 'char(%d)',
+ 'XSTEXT' => 'varchar2(1000)',
+ 'STEXT' => 'varchar2(3000)',
+ 'TEXT' => 'clob',
+ 'MTEXT' => 'clob',
+ 'XSTEXT_UNI'=> 'varchar2(300)',
+ 'STEXT_UNI' => 'varchar2(765)',
+ 'TEXT_UNI' => 'clob',
+ 'MTEXT_UNI' => 'clob',
+ 'TIMESTAMP' => 'number(11)',
+ 'DECIMAL' => 'number(5, 2)',
+ 'DECIMAL:' => 'number(%d, 2)',
+ 'PDECIMAL' => 'number(6, 3)',
+ 'PDECIMAL:' => 'number(%d, 3)',
+ 'VCHAR_UNI' => 'varchar2(765)',
+ 'VCHAR_UNI:'=> array('varchar2(%d)', 'limit' => array('mult', 3, 765, 'clob')),
+ 'VCHAR_CI' => 'varchar2(255)',
+ 'VARBINARY' => 'raw(255)',
+ ),
+
+ 'sqlite' => array(
+ 'INT:' => 'int(%d)',
+ 'BINT' => 'bigint(20)',
+ 'UINT' => 'INTEGER UNSIGNED', //'mediumint(8) UNSIGNED',
+ 'UINT:' => 'INTEGER UNSIGNED', // 'int(%d) UNSIGNED',
+ 'TINT:' => 'tinyint(%d)',
+ 'USINT' => 'INTEGER UNSIGNED', //'mediumint(4) UNSIGNED',
+ 'BOOL' => 'INTEGER UNSIGNED', //'tinyint(1) UNSIGNED',
+ 'VCHAR' => 'varchar(255)',
+ 'VCHAR:' => 'varchar(%d)',
+ 'CHAR:' => 'char(%d)',
+ 'XSTEXT' => 'text(65535)',
+ 'STEXT' => 'text(65535)',
+ 'TEXT' => 'text(65535)',
+ 'MTEXT' => 'mediumtext(16777215)',
+ 'XSTEXT_UNI'=> 'text(65535)',
+ 'STEXT_UNI' => 'text(65535)',
+ 'TEXT_UNI' => 'text(65535)',
+ 'MTEXT_UNI' => 'mediumtext(16777215)',
+ 'TIMESTAMP' => 'INTEGER UNSIGNED', //'int(11) UNSIGNED',
+ 'DECIMAL' => 'decimal(5,2)',
+ 'DECIMAL:' => 'decimal(%d,2)',
+ 'PDECIMAL' => 'decimal(6,3)',
+ 'PDECIMAL:' => 'decimal(%d,3)',
+ 'VCHAR_UNI' => 'varchar(255)',
+ 'VCHAR_UNI:'=> 'varchar(%d)',
+ 'VCHAR_CI' => 'varchar(255)',
+ 'VARBINARY' => 'blob',
+ ),
+
+ 'sqlite3' => array(
+ 'INT:' => 'INT(%d)',
+ 'BINT' => 'BIGINT(20)',
+ 'UINT' => 'INTEGER UNSIGNED',
+ 'UINT:' => 'INTEGER UNSIGNED',
+ 'TINT:' => 'TINYINT(%d)',
+ 'USINT' => 'INTEGER UNSIGNED',
+ 'BOOL' => 'INTEGER UNSIGNED',
+ 'VCHAR' => 'VARCHAR(255)',
+ 'VCHAR:' => 'VARCHAR(%d)',
+ 'CHAR:' => 'CHAR(%d)',
+ 'XSTEXT' => 'TEXT(65535)',
+ 'STEXT' => 'TEXT(65535)',
+ 'TEXT' => 'TEXT(65535)',
+ 'MTEXT' => 'MEDIUMTEXT(16777215)',
+ 'XSTEXT_UNI'=> 'TEXT(65535)',
+ 'STEXT_UNI' => 'TEXT(65535)',
+ 'TEXT_UNI' => 'TEXT(65535)',
+ 'MTEXT_UNI' => 'MEDIUMTEXT(16777215)',
+ 'TIMESTAMP' => 'INTEGER UNSIGNED', //'int(11) UNSIGNED',
+ 'DECIMAL' => 'DECIMAL(5,2)',
+ 'DECIMAL:' => 'DECIMAL(%d,2)',
+ 'PDECIMAL' => 'DECIMAL(6,3)',
+ 'PDECIMAL:' => 'DECIMAL(%d,3)',
+ 'VCHAR_UNI' => 'VARCHAR(255)',
+ 'VCHAR_UNI:'=> 'VARCHAR(%d)',
+ 'VCHAR_CI' => 'VARCHAR(255)',
+ 'VARBINARY' => 'BLOB',
+ ),
+
+ 'postgres' => array(
+ 'INT:' => 'INT4',
+ 'BINT' => 'INT8',
+ 'UINT' => 'INT4', // unsigned
+ 'UINT:' => 'INT4', // unsigned
+ 'USINT' => 'INT2', // unsigned
+ 'BOOL' => 'INT2', // unsigned
+ 'TINT:' => 'INT2',
+ 'VCHAR' => 'varchar(255)',
+ 'VCHAR:' => 'varchar(%d)',
+ 'CHAR:' => 'char(%d)',
+ 'XSTEXT' => 'varchar(1000)',
+ 'STEXT' => 'varchar(3000)',
+ 'TEXT' => 'varchar(8000)',
+ 'MTEXT' => 'TEXT',
+ 'XSTEXT_UNI'=> 'varchar(100)',
+ 'STEXT_UNI' => 'varchar(255)',
+ 'TEXT_UNI' => 'varchar(4000)',
+ 'MTEXT_UNI' => 'TEXT',
+ 'TIMESTAMP' => 'INT4', // unsigned
+ 'DECIMAL' => 'decimal(5,2)',
+ 'DECIMAL:' => 'decimal(%d,2)',
+ 'PDECIMAL' => 'decimal(6,3)',
+ 'PDECIMAL:' => 'decimal(%d,3)',
+ 'VCHAR_UNI' => 'varchar(255)',
+ 'VCHAR_UNI:'=> 'varchar(%d)',
+ 'VCHAR_CI' => 'varchar_ci',
+ 'VARBINARY' => 'bytea',
+ ),
+ );
+ }
+
+ /**
+ * A list of types being unsigned for better reference in some db's
+ * @var array
+ */
+ var $unsigned_types = array('UINT', 'UINT:', 'USINT', 'BOOL', 'TIMESTAMP');
+
+ /**
+ * A list of supported DBMS. We change this class to support more DBMS, the DBMS itself only need to follow some rules.
+ * @var array
+ */
+ var $supported_dbms = array('mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite', 'sqlite3');
+
+ /**
+ * This is set to true if user only wants to return the 'to-be-executed' SQL statement(s) (as an array).
+ * This mode has no effect on some methods (inserting of data for example). This is expressed within the methods command.
+ */
+ var $return_statements = false;
+
+ /**
+ * Constructor. Set DB Object and set {@link $return_statements return_statements}.
+ *
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param bool $return_statements True if only statements should be returned and no SQL being executed
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
+ {
+ $this->db = $db;
+ $this->return_statements = $return_statements;
+
+ $this->dbms_type_map = self::get_dbms_type_map();
+
+ // Determine mapping database type
+ switch ($this->db->get_sql_layer())
+ {
+ case 'mysql':
+ $this->sql_layer = 'mysql_40';
+ break;
+
+ case 'mysql4':
+ if (version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
+ {
+ $this->sql_layer = 'mysql_41';
+ }
+ else
+ {
+ $this->sql_layer = 'mysql_40';
+ }
+ break;
+
+ case 'mysqli':
+ $this->sql_layer = 'mysql_41';
+ break;
+
+ case 'mssql':
+ case 'mssql_odbc':
+ $this->sql_layer = 'mssql';
+ break;
+
+ case 'mssqlnative':
+ $this->sql_layer = 'mssqlnative';
+ break;
+
+ default:
+ $this->sql_layer = $this->db->get_sql_layer();
+ break;
+ }
+ }
+
+ /**
+ * Setter for {@link $return_statements return_statements}.
+ *
+ * @param bool $return_statements True if SQL should not be executed but returned as strings
+ * @return null
+ */
+ public function set_return_statements($return_statements)
+ {
+ $this->return_statements = $return_statements;
+ }
+
+ /**
+ * Gets a list of tables in the database.
+ *
+ * @return array Array of table names (all lower case)
+ */
+ function sql_list_tables()
+ {
+ switch ($this->db->get_sql_layer())
+ {
+ case 'mysql':
+ case 'mysql4':
+ case 'mysqli':
+ $sql = 'SHOW TABLES';
+ break;
+
+ case 'sqlite':
+ $sql = 'SELECT name
+ FROM sqlite_master
+ WHERE type = "table"';
+ break;
+
+ case 'sqlite3':
+ $sql = 'SELECT name
+ FROM sqlite_master
+ WHERE type = "table"
+ AND name <> "sqlite_sequence"';
+ break;
+
+ case 'mssql':
+ case 'mssql_odbc':
+ case 'mssqlnative':
+ $sql = "SELECT name
+ FROM sysobjects
+ WHERE type='U'";
+ break;
+
+ case 'postgres':
+ $sql = 'SELECT relname
+ FROM pg_stat_user_tables';
+ break;
+
+ case 'oracle':
+ $sql = 'SELECT table_name
+ FROM USER_TABLES';
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+
+ $tables = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $name = current($row);
+ $tables[$name] = $name;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $tables;
+ }
+
+ /**
+ * Check if table exists
+ *
+ *
+ * @param string $table_name The table name to check for
+ * @return bool true if table exists, else false
+ */
+ function sql_table_exists($table_name)
+ {
+ $this->db->sql_return_on_error(true);
+ $result = $this->db->sql_query_limit('SELECT * FROM ' . $table_name, 1);
+ $this->db->sql_return_on_error(false);
+
+ if ($result)
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Create SQL Table
+ *
+ * @param string $table_name The table name to create
+ * @param array $table_data Array containing table data.
+ * @return array Statements if $return_statements is true.
+ */
+ function sql_create_table($table_name, $table_data)
+ {
+ // holds the DDL for a column
+ $columns = $statements = array();
+
+ if ($this->sql_table_exists($table_name))
+ {
+ return $this->_sql_run_sql($statements);
+ }
+
+ // Begin transaction
+ $statements[] = 'begin';
+
+ // Determine if we have created a PRIMARY KEY in the earliest
+ $primary_key_gen = false;
+
+ // Determine if the table requires a sequence
+ $create_sequence = false;
+
+ // Begin table sql statement
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
+ break;
+
+ default:
+ $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
+ break;
+ }
+
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
+ {
+ if (!isset($table_data['PRIMARY_KEY']))
+ {
+ $table_data['COLUMNS']['mssqlindex'] = array('UINT', null, 'auto_increment');
+ $table_data['PRIMARY_KEY'] = 'mssqlindex';
+ }
+ }
+
+ // Iterate through the columns to create a table
+ foreach ($table_data['COLUMNS'] as $column_name => $column_data)
+ {
+ // here lies an array, filled with information compiled on the column's data
+ $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
+
+ if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen"
+ {
+ trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR);
+ }
+
+ // here we add the definition of the new column to the list of columns
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default'];
+ break;
+
+ default:
+ $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
+ break;
+ }
+
+ // see if we have found a primary key set due to a column definition if we have found it, we can stop looking
+ if (!$primary_key_gen)
+ {
+ $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
+ }
+
+ // create sequence DDL based off of the existance of auto incrementing columns
+ if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
+ {
+ $create_sequence = $column_name;
+ }
+ }
+
+ // this makes up all the columns in the create table statement
+ $table_sql .= implode(",\n", $columns);
+
+ // Close the table for two DBMS and add to the statements
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ $table_sql .= "\n);";
+ $statements[] = $table_sql;
+ break;
+ }
+
+ // we have yet to create a primary key for this table,
+ // this means that we can add the one we really wanted instead
+ if (!$primary_key_gen)
+ {
+ // Write primary key
+ if (isset($table_data['PRIMARY_KEY']))
+ {
+ if (!is_array($table_data['PRIMARY_KEY']))
+ {
+ $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'mysql_40':
+ case 'mysql_41':
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
+ break;
+
+ case 'mssql':
+ case 'mssqlnative':
+ // We need the data here
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
+
+ $primary_key_stmts = $this->sql_create_primary_key($table_name, $table_data['PRIMARY_KEY']);
+ foreach ($primary_key_stmts as $pk_stmt)
+ {
+ $statements[] = $pk_stmt;
+ }
+
+ $this->return_statements = $old_return_statements;
+ break;
+
+ case 'oracle':
+ $table_sql .= ",\n\t CONSTRAINT pk_{$table_name} PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
+ break;
+ }
+ }
+ }
+
+ // close the table
+ switch ($this->sql_layer)
+ {
+ case 'mysql_41':
+ // make sure the table is in UTF-8 mode
+ $table_sql .= "\n) CHARACTER SET `utf8` COLLATE `utf8_bin`;";
+ $statements[] = $table_sql;
+ break;
+
+ case 'mysql_40':
+ case 'sqlite':
+ case 'sqlite3':
+ $table_sql .= "\n);";
+ $statements[] = $table_sql;
+ break;
+
+ case 'postgres':
+ // do we need to add a sequence for auto incrementing columns?
+ if ($create_sequence)
+ {
+ $statements[] = "CREATE SEQUENCE {$table_name}_seq;";
+ }
+
+ $table_sql .= "\n);";
+ $statements[] = $table_sql;
+ break;
+
+ case 'oracle':
+ $table_sql .= "\n)";
+ $statements[] = $table_sql;
+
+ // do we need to add a sequence and a tigger for auto incrementing columns?
+ if ($create_sequence)
+ {
+ // create the actual sequence
+ $statements[] = "CREATE SEQUENCE {$table_name}_seq";
+
+ // the trigger is the mechanism by which we increment the counter
+ $trigger = "CREATE OR REPLACE TRIGGER t_{$table_name}\n";
+ $trigger .= "BEFORE INSERT ON {$table_name}\n";
+ $trigger .= "FOR EACH ROW WHEN (\n";
+ $trigger .= "\tnew.{$create_sequence} IS NULL OR new.{$create_sequence} = 0\n";
+ $trigger .= ")\n";
+ $trigger .= "BEGIN\n";
+ $trigger .= "\tSELECT {$table_name}_seq.nextval\n";
+ $trigger .= "\tINTO :new.{$create_sequence}\n";
+ $trigger .= "\tFROM dual;\n";
+ $trigger .= "END;";
+
+ $statements[] = $trigger;
+ }
+ break;
+ }
+
+ // Write Keys
+ if (isset($table_data['KEYS']))
+ {
+ foreach ($table_data['KEYS'] as $key_name => $key_data)
+ {
+ if (!is_array($key_data[1]))
+ {
+ $key_data[1] = array($key_data[1]);
+ }
+
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
+
+ $key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]);
+
+ foreach ($key_stmts as $key_stmt)
+ {
+ $statements[] = $key_stmt;
+ }
+
+ $this->return_statements = $old_return_statements;
+ }
+ }
+
+ // Commit Transaction
+ $statements[] = 'commit';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Handle passed database update array.
+ * Expected structure...
+ * Key being one of the following
+ * drop_tables: Drop tables
+ * add_tables: Add tables
+ * change_columns: Column changes (only type, not name)
+ * add_columns: Add columns to a table
+ * drop_keys: Dropping keys
+ * drop_columns: Removing/Dropping columns
+ * add_primary_keys: adding primary keys
+ * add_unique_index: adding an unique index
+ * add_index: adding an index (can be column:index_size if you need to provide size)
+ *
+ * The values are in this format:
+ * {TABLE NAME} => array(
+ * {COLUMN NAME} => array({COLUMN TYPE}, {DEFAULT VALUE}, {OPTIONAL VARIABLES}),
+ * {KEY/INDEX NAME} => array({COLUMN NAMES}),
+ * )
+ *
+ * For more information have a look at /develop/create_schema_files.php (only available through SVN)
+ */
+ function perform_schema_changes($schema_changes)
+ {
+ if (empty($schema_changes))
+ {
+ return;
+ }
+
+ $statements = array();
+ $sqlite = false;
+
+ // For SQLite we need to perform the schema changes in a much more different way
+ if (($this->db->get_sql_layer() == 'sqlite' || $this->db->get_sql_layer() == 'sqlite3') && $this->return_statements)
+ {
+ $sqlite_data = array();
+ $sqlite = true;
+ }
+
+ // Drop tables?
+ if (!empty($schema_changes['drop_tables']))
+ {
+ foreach ($schema_changes['drop_tables'] as $table)
+ {
+ // only drop table if it exists
+ if ($this->sql_table_exists($table))
+ {
+ $result = $this->sql_table_drop($table);
+ if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ // Add tables?
+ if (!empty($schema_changes['add_tables']))
+ {
+ foreach ($schema_changes['add_tables'] as $table => $table_data)
+ {
+ $result = $this->sql_create_table($table, $table_data);
+ if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+
+ // Change columns?
+ if (!empty($schema_changes['change_columns']))
+ {
+ foreach ($schema_changes['change_columns'] as $table => $columns)
+ {
+ foreach ($columns as $column_name => $column_data)
+ {
+ // If the column exists we change it, else we add it ;)
+ if ($column_exists = $this->sql_column_exists($table, $column_name))
+ {
+ $result = $this->sql_column_change($table, $column_name, $column_data, true);
+ }
+ else
+ {
+ $result = $this->sql_column_add($table, $column_name, $column_data, true);
+ }
+
+ if ($sqlite)
+ {
+ if ($column_exists)
+ {
+ $sqlite_data[$table]['change_columns'][] = $result;
+ }
+ else
+ {
+ $sqlite_data[$table]['add_columns'][] = $result;
+ }
+ }
+ else if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ // Add columns?
+ if (!empty($schema_changes['add_columns']))
+ {
+ foreach ($schema_changes['add_columns'] as $table => $columns)
+ {
+ foreach ($columns as $column_name => $column_data)
+ {
+ // Only add the column if it does not exist yet
+ if ($column_exists = $this->sql_column_exists($table, $column_name))
+ {
+ continue;
+ // This is commented out here because it can take tremendous time on updates
+// $result = $this->sql_column_change($table, $column_name, $column_data, true);
+ }
+ else
+ {
+ $result = $this->sql_column_add($table, $column_name, $column_data, true);
+ }
+
+ if ($sqlite)
+ {
+ if ($column_exists)
+ {
+ continue;
+// $sqlite_data[$table]['change_columns'][] = $result;
+ }
+ else
+ {
+ $sqlite_data[$table]['add_columns'][] = $result;
+ }
+ }
+ else if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ // Remove keys?
+ if (!empty($schema_changes['drop_keys']))
+ {
+ foreach ($schema_changes['drop_keys'] as $table => $indexes)
+ {
+ foreach ($indexes as $index_name)
+ {
+ if (!$this->sql_index_exists($table, $index_name))
+ {
+ continue;
+ }
+
+ $result = $this->sql_index_drop($table, $index_name);
+
+ if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ // Drop columns?
+ if (!empty($schema_changes['drop_columns']))
+ {
+ foreach ($schema_changes['drop_columns'] as $table => $columns)
+ {
+ foreach ($columns as $column)
+ {
+ // Only remove the column if it exists...
+ if ($this->sql_column_exists($table, $column))
+ {
+ $result = $this->sql_column_remove($table, $column, true);
+
+ if ($sqlite)
+ {
+ $sqlite_data[$table]['drop_columns'][] = $result;
+ }
+ else if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+ }
+
+ // Add primary keys?
+ if (!empty($schema_changes['add_primary_keys']))
+ {
+ foreach ($schema_changes['add_primary_keys'] as $table => $columns)
+ {
+ $result = $this->sql_create_primary_key($table, $columns, true);
+
+ if ($sqlite)
+ {
+ $sqlite_data[$table]['primary_key'] = $result;
+ }
+ else if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+
+ // Add unique indexes?
+ if (!empty($schema_changes['add_unique_index']))
+ {
+ foreach ($schema_changes['add_unique_index'] as $table => $index_array)
+ {
+ foreach ($index_array as $index_name => $column)
+ {
+ if ($this->sql_unique_index_exists($table, $index_name))
+ {
+ continue;
+ }
+
+ $result = $this->sql_create_unique_index($table, $index_name, $column);
+
+ if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ // Add indexes?
+ if (!empty($schema_changes['add_index']))
+ {
+ foreach ($schema_changes['add_index'] as $table => $index_array)
+ {
+ foreach ($index_array as $index_name => $column)
+ {
+ if ($this->sql_index_exists($table, $index_name))
+ {
+ continue;
+ }
+
+ $result = $this->sql_create_index($table, $index_name, $column);
+
+ if ($this->return_statements)
+ {
+ $statements = array_merge($statements, $result);
+ }
+ }
+ }
+ }
+
+ if ($sqlite)
+ {
+ foreach ($sqlite_data as $table_name => $sql_schema_changes)
+ {
+ // Create temporary table with original data
+ $statements[] = 'begin';
+
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'table'
+ AND name = '{$table_name}'
+ ORDER BY type DESC, name;";
+ $result = $this->db->sql_query($sql);
+
+ if (!$result)
+ {
+ continue;
+ }
+
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ // Create a backup table and populate it, destroy the existing one
+ $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $row['sql']);
+ $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ // Get the columns...
+ preg_match('#\((.*)\)#s', $row['sql'], $matches);
+
+ $plain_table_cols = trim($matches[1]);
+ $new_table_cols = preg_split('/,(?![\s\w]+\))/m', $plain_table_cols);
+ $column_list = array();
+
+ foreach ($new_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
+ {
+ continue;
+ }
+ $column_list[] = $entities[0];
+ }
+
+ // note down the primary key notation because sqlite only supports adding it to the end for the new table
+ $primary_key = false;
+ $_new_cols = array();
+
+ foreach ($new_table_cols as $key => $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
+ {
+ $primary_key = $declaration;
+ continue;
+ }
+ $_new_cols[] = $declaration;
+ }
+
+ $new_table_cols = $_new_cols;
+
+ // First of all... change columns
+ if (!empty($sql_schema_changes['change_columns']))
+ {
+ foreach ($sql_schema_changes['change_columns'] as $column_sql)
+ {
+ foreach ($new_table_cols as $key => $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if (strpos($column_sql, $entities[0] . ' ') === 0)
+ {
+ $new_table_cols[$key] = $column_sql;
+ }
+ }
+ }
+ }
+
+ if (!empty($sql_schema_changes['add_columns']))
+ {
+ foreach ($sql_schema_changes['add_columns'] as $column_sql)
+ {
+ $new_table_cols[] = $column_sql;
+ }
+ }
+
+ // Now drop them...
+ if (!empty($sql_schema_changes['drop_columns']))
+ {
+ foreach ($sql_schema_changes['drop_columns'] as $column_name)
+ {
+ // Remove from column list...
+ $new_column_list = array();
+ foreach ($column_list as $key => $value)
+ {
+ if ($value === $column_name)
+ {
+ continue;
+ }
+
+ $new_column_list[] = $value;
+ }
+
+ $column_list = $new_column_list;
+
+ // Remove from table...
+ $_new_cols = array();
+ foreach ($new_table_cols as $key => $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if (strpos($column_name . ' ', $entities[0] . ' ') === 0)
+ {
+ continue;
+ }
+ $_new_cols[] = $declaration;
+ }
+ $new_table_cols = $_new_cols;
+ }
+ }
+
+ // Primary key...
+ if (!empty($sql_schema_changes['primary_key']))
+ {
+ $new_table_cols[] = 'PRIMARY KEY (' . implode(', ', $sql_schema_changes['primary_key']) . ')';
+ }
+ // Add a new one or the old primary key
+ else if ($primary_key !== false)
+ {
+ $new_table_cols[] = $primary_key;
+ }
+
+ $columns = implode(',', $column_list);
+
+ // create a new table and fill it up. destroy the temp one
+ $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $new_table_cols) . ');';
+ $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
+ $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+
+ $statements[] = 'commit';
+ }
+ }
+
+ if ($this->return_statements)
+ {
+ return $statements;
+ }
+ }
+
+ /**
+ * Gets a list of columns of a table.
+ *
+ * @param string $table Table name
+ *
+ * @return array Array of column names (all lower case)
+ */
+ function sql_list_columns($table)
+ {
+ $columns = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql = "SHOW COLUMNS FROM $table";
+ break;
+
+ // PostgreSQL has a way of doing this in a much simpler way but would
+ // not allow us to support all versions of PostgreSQL
+ case 'postgres':
+ $sql = "SELECT a.attname
+ FROM pg_class c, pg_attribute a
+ WHERE c.relname = '{$table}'
+ AND a.attnum > 0
+ AND a.attrelid = c.oid";
+ break;
+
+ // same deal with PostgreSQL, we must perform more complex operations than
+ // we technically could
+ case 'mssql':
+ case 'mssqlnative':
+ $sql = "SELECT c.name
+ FROM syscolumns c
+ LEFT JOIN sysobjects o ON c.id = o.id
+ WHERE o.name = '{$table}'";
+ break;
+
+ case 'oracle':
+ $sql = "SELECT column_name
+ FROM user_tab_columns
+ WHERE LOWER(table_name) = '" . strtolower($table) . "'";
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'table'
+ AND name = '{$table}'";
+
+ $result = $this->db->sql_query($sql);
+
+ if (!$result)
+ {
+ return false;
+ }
+
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ preg_match('#\((.*)\)#s', $row['sql'], $matches);
+
+ $cols = trim($matches[1]);
+ $col_array = preg_split('/,(?![\s\w]+\))/m', $cols);
+
+ foreach ($col_array as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
+ {
+ continue;
+ }
+
+ $column = strtolower($entities[0]);
+ $columns[$column] = $column;
+ }
+
+ return $columns;
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $column = strtolower(current($row));
+ $columns[$column] = $column;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $columns;
+ }
+
+ /**
+ * Check whether a specified column exist in a table
+ *
+ * @param string $table Table to check
+ * @param string $column_name Column to check
+ *
+ * @return bool True if column exists, false otherwise
+ */
+ function sql_column_exists($table, $column_name)
+ {
+ $columns = $this->sql_list_columns($table);
+
+ return isset($columns[$column_name]);
+ }
+
+ /**
+ * Check if a specified index exists in table. Does not return PRIMARY KEY and UNIQUE indexes.
+ *
+ * @param string $table_name Table to check the index at
+ * @param string $index_name The index name to check
+ *
+ * @return bool True if index exists, else false
+ */
+ function sql_index_exists($table_name, $index_name)
+ {
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
+ {
+ $sql = "EXEC sp_statistics '$table_name'";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['TYPE'] == 3)
+ {
+ if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return false;
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ $sql = "SELECT ic.relname as index_name
+ FROM pg_class bc, pg_class ic, pg_index i
+ WHERE (bc.oid = i.indrelid)
+ AND (ic.oid = i.indexrelid)
+ AND (bc.relname = '" . $table_name . "')
+ AND (i.indisunique != 't')
+ AND (i.indisprimary != 't')";
+ $col = 'index_name';
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql = 'SHOW KEYS
+ FROM ' . $table_name;
+ $col = 'Key_name';
+ break;
+
+ case 'oracle':
+ $sql = "SELECT index_name
+ FROM user_indexes
+ WHERE table_name = '" . strtoupper($table_name) . "'
+ AND generated = 'N'
+ AND uniqueness = 'NONUNIQUE'";
+ $col = 'index_name';
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+ $sql = "PRAGMA index_list('" . $table_name . "');";
+ $col = 'name';
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique'])
+ {
+ continue;
+ }
+
+ // These DBMS prefix index name with the table name
+ switch ($this->sql_layer)
+ {
+ case 'oracle':
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ $row[$col] = substr($row[$col], strlen($table_name) + 1);
+ break;
+ }
+
+ if (strtolower($row[$col]) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return false;
+ }
+
+ /**
+ * Check if a specified index exists in table. Does not return PRIMARY KEY indexes.
+ *
+ * @param string $table_name Table to check the index at
+ * @param string $index_name The index name to check
+ *
+ * @return bool True if index exists, else false
+ */
+ function sql_unique_index_exists($table_name, $index_name)
+ {
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
+ {
+ $sql = "EXEC sp_statistics '$table_name'";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Usually NON_UNIQUE is the column we want to check, but we allow for both
+ if ($row['TYPE'] == 3)
+ {
+ if (strtolower($row['INDEX_NAME']) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ }
+ $this->db->sql_freeresult($result);
+ return false;
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ $sql = "SELECT ic.relname as index_name, i.indisunique
+ FROM pg_class bc, pg_class ic, pg_index i
+ WHERE (bc.oid = i.indrelid)
+ AND (ic.oid = i.indexrelid)
+ AND (bc.relname = '" . $table_name . "')
+ AND (i.indisprimary != 't')";
+ $col = 'index_name';
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql = 'SHOW KEYS
+ FROM ' . $table_name;
+ $col = 'Key_name';
+ break;
+
+ case 'oracle':
+ $sql = "SELECT index_name, table_owner
+ FROM user_indexes
+ WHERE table_name = '" . strtoupper($table_name) . "'
+ AND generated = 'N'
+ AND uniqueness = 'UNIQUE'";
+ $col = 'index_name';
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+ $sql = "PRAGMA index_list('" . $table_name . "');";
+ $col = 'name';
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && ($row['Non_unique'] || $row[$col] == 'PRIMARY'))
+ {
+ continue;
+ }
+
+ if (($this->sql_layer == 'sqlite' || $this->sql_layer == 'sqlite3') && !$row['unique'])
+ {
+ continue;
+ }
+
+ if ($this->sql_layer == 'postgres' && $row['indisunique'] != 't')
+ {
+ continue;
+ }
+
+ // These DBMS prefix index name with the table name
+ switch ($this->sql_layer)
+ {
+ case 'oracle':
+ // Two cases here... prefixed with U_[table_owner] and not prefixed with table_name
+ if (strpos($row[$col], 'U_') === 0)
+ {
+ $row[$col] = substr($row[$col], strlen('U_' . $row['table_owner']) + 1);
+ }
+ else if (strpos($row[$col], strtoupper($table_name)) === 0)
+ {
+ $row[$col] = substr($row[$col], strlen($table_name) + 1);
+ }
+ break;
+
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ $row[$col] = substr($row[$col], strlen($table_name) + 1);
+ break;
+ }
+
+ if (strtolower($row[$col]) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return false;
+ }
+
+ /**
+ * Private method for performing sql statements (either execute them or return them)
+ * @access private
+ */
+ function _sql_run_sql($statements)
+ {
+ if ($this->return_statements)
+ {
+ return $statements;
+ }
+
+ // We could add error handling here...
+ foreach ($statements as $sql)
+ {
+ if ($sql === 'begin')
+ {
+ $this->db->sql_transaction('begin');
+ }
+ else if ($sql === 'commit')
+ {
+ $this->db->sql_transaction('commit');
+ }
+ else
+ {
+ $this->db->sql_query($sql);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Function to prepare some column information for better usage
+ * @access private
+ */
+ function sql_prepare_column_data($table_name, $column_name, $column_data)
+ {
+ if (strlen($column_name) > 30)
+ {
+ trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
+ }
+
+ // Get type
+ list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
+
+ // Adjust default value if db-dependent specified
+ if (is_array($column_data[1]))
+ {
+ $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
+ }
+
+ $sql = '';
+
+ $return_array = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ $sql .= " {$column_type} ";
+ $sql_default = " {$column_type} ";
+
+ // For adding columns we need the default definition
+ if (!is_null($column_data[1]))
+ {
+ // For hexadecimal values do not use single quotes
+ if (strpos($column_data[1], '0x') === 0)
+ {
+ $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') ';
+ $sql_default .= $return_array['default'];
+ }
+ else
+ {
+ $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
+ $sql_default .= $return_array['default'];
+ }
+ }
+
+ if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
+ {
+// $sql .= 'IDENTITY (1, 1) ';
+ $sql_default .= 'IDENTITY (1, 1) ';
+ }
+
+ $return_array['textimage'] = $column_type === '[text]';
+
+ if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
+ {
+ $sql .= 'NOT NULL';
+ $sql_default .= 'NOT NULL';
+ }
+ else
+ {
+ $sql .= 'NULL';
+ $sql_default .= 'NULL';
+ }
+
+ $return_array['column_type_sql_default'] = $sql_default;
+
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql .= " {$column_type} ";
+
+ // For hexadecimal values do not use single quotes
+ if (!is_null($column_data[1]) && substr($column_type, -4) !== 'text' && substr($column_type, -4) !== 'blob')
+ {
+ $sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' ";
+ }
+
+ if (!is_null($column_data[1]) || (isset($column_data[2]) && $column_data[2] == 'auto_increment'))
+ {
+ $sql .= 'NOT NULL';
+ }
+ else
+ {
+ $sql .= 'NULL';
+ }
+
+ if (isset($column_data[2]))
+ {
+ if ($column_data[2] == 'auto_increment')
+ {
+ $sql .= ' auto_increment';
+ }
+ else if ($this->sql_layer === 'mysql_41' && $column_data[2] == 'true_sort')
+ {
+ $sql .= ' COLLATE utf8_unicode_ci';
+ }
+ }
+
+ if (isset($column_data['after']))
+ {
+ $return_array['after'] = $column_data['after'];
+ }
+
+ break;
+
+ case 'oracle':
+ $sql .= " {$column_type} ";
+ $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}' " : '';
+
+ // In Oracle empty strings ('') are treated as NULL.
+ // Therefore in oracle we allow NULL's for all DEFAULT '' entries
+ // Oracle does not like setting NOT NULL on a column that is already NOT NULL (this happens only on number fields)
+ if (!preg_match('/number/i', $column_type))
+ {
+ $sql .= ($column_data[1] === '' || $column_data[1] === null) ? '' : 'NOT NULL';
+ }
+
+ $return_array['auto_increment'] = false;
+ if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
+ {
+ $return_array['auto_increment'] = true;
+ }
+
+ break;
+
+ case 'postgres':
+ $return_array['column_type'] = $column_type;
+
+ $sql .= " {$column_type} ";
+
+ $return_array['auto_increment'] = false;
+ if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
+ {
+ $default_val = "nextval('{$table_name}_seq')";
+ $return_array['auto_increment'] = true;
+ }
+ else if (!is_null($column_data[1]))
+ {
+ $default_val = "'" . $column_data[1] . "'";
+ $return_array['null'] = 'NOT NULL';
+ $sql .= 'NOT NULL ';
+ }
+ else
+ {
+ // Integers need to have 0 instead of empty string as default
+ if (strpos($column_type, 'INT') === 0)
+ {
+ $default_val = '0';
+ }
+ else
+ {
+ $default_val = "'" . $column_data[1] . "'";
+ }
+ $return_array['null'] = 'NULL';
+ $sql .= 'NULL ';
+ }
+
+ $return_array['default'] = $default_val;
+
+ $sql .= "DEFAULT {$default_val}";
+
+ // Unsigned? Then add a CHECK contraint
+ if (in_array($orig_column_type, $this->unsigned_types))
+ {
+ $return_array['constraint'] = "CHECK ({$column_name} >= 0)";
+ $sql .= " CHECK ({$column_name} >= 0)";
+ }
+
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+ $return_array['primary_key_set'] = false;
+ if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
+ {
+ $sql .= ' INTEGER PRIMARY KEY';
+ $return_array['primary_key_set'] = true;
+
+ if ($this->sql_layer === 'sqlite3')
+ {
+ $sql .= ' AUTOINCREMENT';
+ }
+ }
+ else
+ {
+ $sql .= ' ' . $column_type;
+ }
+
+ if (!is_null($column_data[1]))
+ {
+ $sql .= ' NOT NULL ';
+ $sql .= "DEFAULT '{$column_data[1]}'";
+ }
+
+ break;
+ }
+
+ $return_array['column_type_sql'] = $sql;
+
+ return $return_array;
+ }
+
+ /**
+ * Get the column's database type from the type map
+ *
+ * @param string $column_map_type
+ * @return array column type for this database
+ * and map type without length
+ */
+ function get_column_type($column_map_type)
+ {
+ if (strpos($column_map_type, ':') !== false)
+ {
+ list($orig_column_type, $column_length) = explode(':', $column_map_type);
+ if (!is_array($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']))
+ {
+ $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'], $column_length);
+ }
+ else
+ {
+ if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule']))
+ {
+ switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][0])
+ {
+ case 'div':
+ $column_length /= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['rule'][1];
+ $column_length = ceil($column_length);
+ $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length);
+ break;
+ }
+ }
+
+ if (isset($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit']))
+ {
+ switch ($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][0])
+ {
+ case 'mult':
+ $column_length *= $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][1];
+ if ($column_length > $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][2])
+ {
+ $column_type = $this->dbms_type_map[$this->sql_layer][$orig_column_type . ':']['limit'][3];
+ }
+ else
+ {
+ $column_type = sprintf($this->dbms_type_map[$this->sql_layer][$orig_column_type . ':'][0], $column_length);
+ }
+ break;
+ }
+ }
+ }
+ $orig_column_type .= ':';
+ }
+ else
+ {
+ $orig_column_type = $column_map_type;
+ $column_type = $this->dbms_type_map[$this->sql_layer][$column_map_type];
+ }
+
+ return array($column_type, $orig_column_type);
+ }
+
+ /**
+ * Add new column
+ */
+ function sql_column_add($table_name, $column_name, $column_data, $inline = false)
+ {
+ $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
+ $statements = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ // Does not support AFTER, only through temporary table
+ $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : '';
+ $statements[] = 'ALTER TABLE `' . $table_name . '` ADD COLUMN `' . $column_name . '` ' . $column_data['column_type_sql'] . $after;
+ break;
+
+ case 'oracle':
+ // Does not support AFTER, only through temporary table
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql'];
+ break;
+
+ case 'postgres':
+ // Does not support AFTER, only through temporary table
+ if (version_compare($this->db->sql_server_info(true), '8.0', '>='))
+ {
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql'];
+ }
+ else
+ {
+ // old versions cannot add columns with default and null information
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint'];
+
+ if (isset($column_data['null']))
+ {
+ if ($column_data['null'] == 'NOT NULL')
+ {
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL';
+ }
+ }
+
+ if (isset($column_data['default']))
+ {
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
+ }
+ }
+
+ break;
+
+ case 'sqlite':
+ if ($inline && $this->return_statements)
+ {
+ return $column_name . ' ' . $column_data['column_type_sql'];
+ }
+
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
+ {
+ break;
+ }
+
+ $statements[] = 'begin';
+
+ $sql_create_table = array_shift($recreate_queries);
+
+ // Create a backup table and populate it, destroy the existing one
+ $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
+ $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
+
+ $new_table_cols = trim($matches[1]);
+ $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
+ $column_list = array();
+
+ foreach ($old_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
+ {
+ continue;
+ }
+ $column_list[] = $entities[0];
+ }
+
+ $columns = implode(',', $column_list);
+
+ $new_table_cols = $column_name . ' ' . $column_data['column_type_sql'] . ',' . $new_table_cols;
+
+ // create a new table and fill it up. destroy the temp one
+ $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');';
+ $statements = array_merge($statements, $recreate_queries);
+
+ $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
+ $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+
+ $statements[] = 'commit';
+ break;
+
+ case 'sqlite3':
+ if ($inline && $this->return_statements)
+ {
+ return $column_name . ' ' . $column_data['column_type_sql'];
+ }
+
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql'];
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Drop column
+ */
+ function sql_column_remove($table_name, $column_name, $inline = false)
+ {
+ $statements = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ // We need the data here
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
+
+ $indexes = $this->get_existing_indexes($table_name, $column_name);
+ $indexes = array_merge($indexes, $this->get_existing_indexes($table_name, $column_name, true));
+
+ // Drop any indexes
+ $recreate_indexes = array();
+ if (!empty($indexes))
+ {
+ foreach ($indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_index_drop($table_name, $index_name);
+ $statements = array_merge($statements, $result);
+ if (sizeof($index_data) > 1)
+ {
+ // Remove this column from the index and recreate it
+ $recreate_indexes[$index_name] = array_diff($index_data, array($column_name));
+ }
+ }
+ }
+
+ // Drop default value constraint
+ $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
+ $statements = array_merge($statements, $result);
+
+ // Remove the column
+ $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
+
+ if (!empty($recreate_indexes))
+ {
+ // Recreate indexes after we removed the column
+ foreach ($recreate_indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_index($table_name, $index_name, $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ $this->return_statements = $old_return_statements;
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`';
+ break;
+
+ case 'oracle':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name;
+ break;
+
+ case 'postgres':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+
+ if ($inline && $this->return_statements)
+ {
+ return $column_name;
+ }
+
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name, $column_name);
+ if (empty($recreate_queries))
+ {
+ break;
+ }
+
+ $statements[] = 'begin';
+
+ $sql_create_table = array_shift($recreate_queries);
+
+ // Create a backup table and populate it, destroy the existing one
+ $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
+ $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
+
+ $new_table_cols = trim($matches[1]);
+ $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
+ $column_list = array();
+
+ foreach ($old_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name)
+ {
+ continue;
+ }
+ $column_list[] = $entities[0];
+ }
+
+ $columns = implode(',', $column_list);
+
+ $new_table_cols = trim(preg_replace('/' . $column_name . '\b[^,]+(?:,|$)/m', '', $new_table_cols));
+ if (substr($new_table_cols, -1) === ',')
+ {
+ // Remove the comma from the last entry again
+ $new_table_cols = substr($new_table_cols, 0, -1);
+ }
+
+ // create a new table and fill it up. destroy the temp one
+ $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');';
+ $statements = array_merge($statements, $recreate_queries);
+
+ $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
+ $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+
+ $statements[] = 'commit';
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Drop Index
+ */
+ function sql_index_drop($table_name, $index_name)
+ {
+ $statements = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name;
+ break;
+
+ case 'oracle':
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Drop Table
+ */
+ function sql_table_drop($table_name)
+ {
+ $statements = array();
+
+ if (!$this->sql_table_exists($table_name))
+ {
+ return $this->_sql_run_sql($statements);
+ }
+
+ // the most basic operation, get rid of the table
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ switch ($this->sql_layer)
+ {
+ case 'oracle':
+ $sql = 'SELECT A.REFERENCED_NAME
+ FROM USER_DEPENDENCIES A, USER_TRIGGERS B
+ WHERE A.REFERENCED_TYPE = \'SEQUENCE\'
+ AND A.NAME = B.TRIGGER_NAME
+ AND B.TABLE_NAME = \'' . strtoupper($table_name) . "'";
+ $result = $this->db->sql_query($sql);
+
+ // any sequences ref'd to this table's triggers?
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $statements[] = "DROP SEQUENCE {$row['referenced_name']}";
+ }
+ $this->db->sql_freeresult($result);
+ break;
+
+ case 'postgres':
+ // PGSQL does not "tightly" bind sequences and tables, we must guess...
+ $sql = "SELECT relname
+ FROM pg_class
+ WHERE relkind = 'S'
+ AND relname = '{$table_name}_seq'";
+ $result = $this->db->sql_query($sql);
+
+ // We don't even care about storing the results. We already know the answer if we get rows back.
+ if ($this->db->sql_fetchrow($result))
+ {
+ $statements[] = "DROP SEQUENCE {$table_name}_seq;\n";
+ }
+ $this->db->sql_freeresult($result);
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Add primary key
+ */
+ function sql_create_primary_key($table_name, $column, $inline = false)
+ {
+ $statements = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ case 'mysql_40':
+ case 'mysql_41':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
+ break;
+
+ case 'mssql':
+ case 'mssqlnative':
+ $sql = "ALTER TABLE [{$table_name}] WITH NOCHECK ADD ";
+ $sql .= "CONSTRAINT [PK_{$table_name}] PRIMARY KEY CLUSTERED (";
+ $sql .= '[' . implode("],\n\t\t[", $column) . ']';
+ $sql .= ')';
+
+ $statements[] = $sql;
+ break;
+
+ case 'oracle':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')';
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+
+ if ($inline && $this->return_statements)
+ {
+ return $column;
+ }
+
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
+ {
+ break;
+ }
+
+ $statements[] = 'begin';
+
+ $sql_create_table = array_shift($recreate_queries);
+
+ // Create a backup table and populate it, destroy the existing one
+ $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
+ $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
+
+ $new_table_cols = trim($matches[1]);
+ $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
+ $column_list = array();
+
+ foreach ($old_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
+ {
+ continue;
+ }
+ $column_list[] = $entities[0];
+ }
+
+ $columns = implode(',', $column_list);
+
+ // create a new table and fill it up. destroy the temp one
+ $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ', PRIMARY KEY (' . implode(', ', $column) . '));';
+ $statements = array_merge($statements, $recreate_queries);
+
+ $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
+ $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+
+ $statements[] = 'commit';
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Add unique index
+ */
+ function sql_create_unique_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config)
+ if (strlen($table_name . '_' . $index_name) - strlen($table_prefix) > 24)
+ {
+ $max_length = strlen($table_prefix) + 24;
+ trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR);
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ case 'oracle':
+ case 'sqlite':
+ case 'sqlite3':
+ $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')';
+ break;
+
+ case 'mssql':
+ case 'mssqlnative':
+ $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Add index
+ */
+ function sql_create_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config)
+ if (strlen($table_name . $index_name) - strlen($table_prefix) > 24)
+ {
+ $max_length = strlen($table_prefix) + 24;
+ trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR);
+ }
+
+ // remove index length unless MySQL4
+ if ('mysql_40' != $this->sql_layer)
+ {
+ $column = preg_replace('#:.*$#', '', $column);
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ case 'oracle':
+ case 'sqlite':
+ case 'sqlite3':
+ $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
+ break;
+
+ case 'mysql_40':
+ // add index size to definition as required by MySQL4
+ foreach ($column as $i => $col)
+ {
+ if (false !== strpos($col, ':'))
+ {
+ list($col, $index_size) = explode(':', $col);
+ $column[$i] = "$col($index_size)";
+ }
+ }
+ // no break
+ case 'mysql_41':
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . ' (' . implode(', ', $column) . ')';
+ break;
+
+ case 'mssql':
+ case 'mssqlnative':
+ $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * List all of the indices that belong to a table,
+ * does not count:
+ * * UNIQUE indices
+ * * PRIMARY keys
+ */
+ function sql_list_index($table_name)
+ {
+ $index_array = array();
+
+ if ($this->sql_layer == 'mssql' || $this->sql_layer == 'mssqlnative')
+ {
+ $sql = "EXEC sp_statistics '$table_name'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['TYPE'] == 3)
+ {
+ $index_array[] = $row['INDEX_NAME'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+ else
+ {
+ switch ($this->sql_layer)
+ {
+ case 'postgres':
+ $sql = "SELECT ic.relname as index_name
+ FROM pg_class bc, pg_class ic, pg_index i
+ WHERE (bc.oid = i.indrelid)
+ AND (ic.oid = i.indexrelid)
+ AND (bc.relname = '" . $table_name . "')
+ AND (i.indisunique != 't')
+ AND (i.indisprimary != 't')";
+ $col = 'index_name';
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql = 'SHOW KEYS
+ FROM ' . $table_name;
+ $col = 'Key_name';
+ break;
+
+ case 'oracle':
+ $sql = "SELECT index_name
+ FROM user_indexes
+ WHERE table_name = '" . strtoupper($table_name) . "'
+ AND generated = 'N'
+ AND uniqueness = 'NONUNIQUE'";
+ $col = 'index_name';
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+ $sql = "PRAGMA index_info('" . $table_name . "');";
+ $col = 'name';
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique'])
+ {
+ continue;
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'oracle':
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ $row[$col] = substr($row[$col], strlen($table_name) + 1);
+ break;
+ }
+
+ $index_array[] = $row[$col];
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ return array_map('strtolower', $index_array);
+ }
+
+ /**
+ * Removes table_name from the index_name if it is at the beginning
+ *
+ * @param $table_name
+ * @param $index_name
+ * @return string
+ */
+ protected function strip_table_name_from_index_name($table_name, $index_name)
+ {
+ return (strpos(strtoupper($index_name), strtoupper($table_name)) === 0) ? substr($index_name, strlen($table_name) + 1) : $index_name;
+ }
+
+ /**
+ * Change column type (not name!)
+ */
+ function sql_column_change($table_name, $column_name, $column_data, $inline = false)
+ {
+ $original_column_data = $column_data;
+ $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
+ $statements = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ // We need the data here
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
+
+ $indexes = $this->get_existing_indexes($table_name, $column_name);
+ $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
+
+ // Drop any indexes
+ if (!empty($indexes) || !empty($unique_indexes))
+ {
+ $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
+ foreach ($drop_indexes as $index_name)
+ {
+ $result = $this->sql_index_drop($table_name, $index_name);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ // Drop default value constraint
+ $result = $this->mssql_get_drop_default_constraints_queries($table_name, $column_name);
+ $statements = array_merge($statements, $result);
+
+ // Change the column
+ $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
+
+ if (!empty($column_data['default']))
+ {
+ // Add new default value constraint
+ $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $column_data['default'] . ' FOR [' . $column_name . ']';
+ }
+
+ if (!empty($indexes))
+ {
+ // Recreate indexes after we changed the column
+ foreach ($indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_index($table_name, $index_name, $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ if (!empty($unique_indexes))
+ {
+ // Recreate unique indexes after we changed the column
+ foreach ($unique_indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_unique_index($table_name, $index_name, $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ $this->return_statements = $old_return_statements;
+ break;
+
+ case 'mysql_40':
+ case 'mysql_41':
+ $statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql'];
+ break;
+
+ case 'oracle':
+ // We need the data here
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
+
+ // Get list of existing indexes
+ $indexes = $this->get_existing_indexes($table_name, $column_name);
+ $unique_indexes = $this->get_existing_indexes($table_name, $column_name, true);
+
+ // Drop any indexes
+ if (!empty($indexes) || !empty($unique_indexes))
+ {
+ $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
+ foreach ($drop_indexes as $index_name)
+ {
+ $result = $this->sql_index_drop($table_name, $this->strip_table_name_from_index_name($table_name, $index_name));
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ $temp_column_name = 'temp_' . substr(md5($column_name), 0, 25);
+ // Add a temporary table with the new type
+ $result = $this->sql_column_add($table_name, $temp_column_name, $original_column_data);
+ $statements = array_merge($statements, $result);
+
+ // Copy the data to the new column
+ $statements[] = 'UPDATE ' . $table_name . ' SET ' . $temp_column_name . ' = ' . $column_name;
+
+ // Drop the original column
+ $result = $this->sql_column_remove($table_name, $column_name);
+ $statements = array_merge($statements, $result);
+
+ // Recreate the original column with the new type
+ $result = $this->sql_column_add($table_name, $column_name, $original_column_data);
+ $statements = array_merge($statements, $result);
+
+ if (!empty($indexes))
+ {
+ // Recreate indexes after we changed the column
+ foreach ($indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_index($table_name, $this->strip_table_name_from_index_name($table_name, $index_name), $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ if (!empty($unique_indexes))
+ {
+ // Recreate unique indexes after we changed the column
+ foreach ($unique_indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_unique_index($table_name, $this->strip_table_name_from_index_name($table_name, $index_name), $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ // Copy the data to the original column
+ $statements[] = 'UPDATE ' . $table_name . ' SET ' . $column_name . ' = ' . $temp_column_name;
+
+ // Drop the temporary column again
+ $result = $this->sql_column_remove($table_name, $temp_column_name);
+ $statements = array_merge($statements, $result);
+
+ $this->return_statements = $old_return_statements;
+ break;
+
+ case 'postgres':
+ $sql = 'ALTER TABLE ' . $table_name . ' ';
+
+ $sql_array = array();
+ $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
+
+ if (isset($column_data['null']))
+ {
+ if ($column_data['null'] == 'NOT NULL')
+ {
+ $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
+ }
+ else if ($column_data['null'] == 'NULL')
+ {
+ $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
+ }
+ }
+
+ if (isset($column_data['default']))
+ {
+ $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
+ }
+
+ // we don't want to double up on constraints if we change different number data types
+ if (isset($column_data['constraint']))
+ {
+ $constraint_sql = "SELECT consrc as constraint_data
+ FROM pg_constraint, pg_class bc
+ WHERE conrelid = bc.oid
+ AND bc.relname = '{$table_name}'
+ AND NOT EXISTS (
+ SELECT *
+ FROM pg_constraint as c, pg_inherits as i
+ WHERE i.inhrelid = pg_constraint.conrelid
+ AND c.conname = pg_constraint.conname
+ AND c.consrc = pg_constraint.consrc
+ AND c.conrelid = i.inhparent
+ )";
+
+ $constraint_exists = false;
+
+ $result = $this->db->sql_query($constraint_sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (trim($row['constraint_data']) == trim($column_data['constraint']))
+ {
+ $constraint_exists = true;
+ break;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!$constraint_exists)
+ {
+ $sql_array[] = 'ADD ' . $column_data['constraint'];
+ }
+ }
+
+ $sql .= implode(', ', $sql_array);
+
+ $statements[] = $sql;
+ break;
+
+ case 'sqlite':
+ case 'sqlite3':
+
+ if ($inline && $this->return_statements)
+ {
+ return $column_name . ' ' . $column_data['column_type_sql'];
+ }
+
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
+ {
+ break;
+ }
+
+ $statements[] = 'begin';
+
+ $sql_create_table = array_shift($recreate_queries);
+
+ // Create a temp table and populate it, destroy the existing one
+ $statements[] = preg_replace('#CREATE\s+TABLE\s+"?' . $table_name . '"?#i', 'CREATE TEMPORARY TABLE ' . $table_name . '_temp', $sql_create_table);
+ $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
+ $statements[] = 'DROP TABLE ' . $table_name;
+
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
+
+ $new_table_cols = trim($matches[1]);
+ $old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
+ $column_list = array();
+
+ foreach ($old_table_cols as $key => $declaration)
+ {
+ $declaration = trim($declaration);
+
+ // Check for the beginning of the constraint section and stop
+ if (preg_match('/[^\(]*\s*PRIMARY KEY\s+\(/', $declaration) ||
+ preg_match('/[^\(]*\s*UNIQUE\s+\(/', $declaration) ||
+ preg_match('/[^\(]*\s*FOREIGN KEY\s+\(/', $declaration) ||
+ preg_match('/[^\(]*\s*CHECK\s+\(/', $declaration))
+ {
+ break;
+ }
+
+ $entities = preg_split('#\s+#', $declaration);
+ $column_list[] = $entities[0];
+ if ($entities[0] == $column_name)
+ {
+ $old_table_cols[$key] = $column_name . ' ' . $column_data['column_type_sql'];
+ }
+ }
+
+ $columns = implode(',', $column_list);
+
+ // Create a new table and fill it up. destroy the temp one
+ $statements[] = 'CREATE TABLE ' . $table_name . ' (' . implode(',', $old_table_cols) . ');';
+ $statements = array_merge($statements, $recreate_queries);
+
+ $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
+ $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+
+ $statements[] = 'commit';
+
+ break;
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * Get queries to drop the default constraints of a column
+ *
+ * We need to drop the default constraints of a column,
+ * before being able to change their type or deleting them.
+ *
+ * @param string $table_name
+ * @param string $column_name
+ * @return array Array with SQL statements
+ */
+ protected function mssql_get_drop_default_constraints_queries($table_name, $column_name)
+ {
+ $statements = array();
+ if ($this->mssql_is_sql_server_2000())
+ {
+ // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
+ // Deprecated in SQL Server 2005
+ $sql = "SELECT so.name AS def_name
+ FROM sysobjects so
+ JOIN sysconstraints sc ON so.id = sc.constid
+ WHERE object_name(so.parent_obj) = '{$table_name}'
+ AND so.xtype = 'D'
+ AND sc.colid = (SELECT colid FROM syscolumns
+ WHERE id = object_id('{$table_name}')
+ AND name = '{$column_name}')";
+ }
+ else
+ {
+ $sql = "SELECT dobj.name AS def_name
+ FROM sys.columns col
+ LEFT OUTER JOIN sys.objects dobj ON (dobj.object_id = col.default_object_id AND dobj.type = 'D')
+ WHERE col.object_id = object_id('{$table_name}')
+ AND col.name = '{$column_name}'
+ AND dobj.name IS NOT NULL";
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $statements[] = 'ALTER TABLE [' . $table_name . '] DROP CONSTRAINT [' . $row['def_name'] . ']';
+ }
+ $this->db->sql_freeresult($result);
+
+ return $statements;
+ }
+
+ /**
+ * Get a list with existing indexes for the column
+ *
+ * @param string $table_name
+ * @param string $column_name
+ * @param bool $unique Should we get unique indexes or normal ones
+ * @return array Array with Index name => columns
+ */
+ public function get_existing_indexes($table_name, $column_name, $unique = false)
+ {
+ switch ($this->sql_layer)
+ {
+ case 'mysql_40':
+ case 'mysql_41':
+ case 'postgres':
+ case 'sqlite':
+ case 'sqlite3':
+ // Not supported
+ throw new \Exception('DBMS is not supported');
+ break;
+ }
+
+ $sql = '';
+ $existing_indexes = array();
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ if ($this->mssql_is_sql_server_2000())
+ {
+ // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
+ // Deprecated in SQL Server 2005
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
+ FROM sysindexes ix
+ INNER JOIN sysindexkeys ixc
+ ON ixc.id = ix.id
+ AND ixc.indid = ix.indid
+ INNER JOIN syscolumns cols
+ ON cols.colid = ixc.colid
+ AND cols.id = ix.id
+ WHERE ix.id = object_id('{$table_name}')
+ AND cols.name = '{$column_name}'
+ AND INDEXPROPERTY(ix.id, ix.name, 'IsUnique') = " . ($unique ? '1' : '0');
+ }
+ else
+ {
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
+ FROM sys.indexes ix
+ INNER JOIN sys.index_columns ixc
+ ON ixc.object_id = ix.object_id
+ AND ixc.index_id = ix.index_id
+ INNER JOIN sys.columns cols
+ ON cols.column_id = ixc.column_id
+ AND cols.object_id = ix.object_id
+ WHERE ix.object_id = object_id('{$table_name}')
+ AND cols.name = '{$column_name}'
+ AND ix.is_unique = " . ($unique ? '1' : '0');
+ }
+ break;
+
+ case 'oracle':
+ $sql = "SELECT ix.index_name AS phpbb_index_name, ix.uniqueness AS is_unique
+ FROM all_ind_columns ixc, all_indexes ix
+ WHERE ix.index_name = ixc.index_name
+ AND ixc.table_name = '" . strtoupper($table_name) . "'
+ AND ixc.column_name = '" . strtoupper($column_name) . "'";
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!isset($row['is_unique']) || ($unique && $row['is_unique'] == 'UNIQUE') || (!$unique && $row['is_unique'] == 'NONUNIQUE'))
+ {
+ $existing_indexes[$row['phpbb_index_name']] = array();
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($existing_indexes))
+ {
+ return array();
+ }
+
+ switch ($this->sql_layer)
+ {
+ case 'mssql':
+ case 'mssqlnative':
+ if ($this->mssql_is_sql_server_2000())
+ {
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
+ FROM sysindexes ix
+ INNER JOIN sysindexkeys ixc
+ ON ixc.id = ix.id
+ AND ixc.indid = ix.indid
+ INNER JOIN syscolumns cols
+ ON cols.colid = ixc.colid
+ AND cols.id = ix.id
+ WHERE ix.id = object_id('{$table_name}')
+ AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
+ }
+ else
+ {
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
+ FROM sys.indexes ix
+ INNER JOIN sys.index_columns ixc
+ ON ixc.object_id = ix.object_id
+ AND ixc.index_id = ix.index_id
+ INNER JOIN sys.columns cols
+ ON cols.column_id = ixc.column_id
+ AND cols.object_id = ix.object_id
+ WHERE ix.object_id = object_id('{$table_name}')
+ AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
+ }
+ break;
+
+ case 'oracle':
+ $sql = "SELECT index_name AS phpbb_index_name, column_name AS phpbb_column_name
+ FROM all_ind_columns
+ WHERE table_name = '" . strtoupper($table_name) . "'
+ AND " . $this->db->sql_in_set('index_name', array_keys($existing_indexes));
+ break;
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name'];
+ }
+ $this->db->sql_freeresult($result);
+
+ return $existing_indexes;
+ }
+
+ /**
+ * Is the used MS SQL Server a SQL Server 2000?
+ *
+ * @return bool
+ */
+ protected function mssql_is_sql_server_2000()
+ {
+ if ($this->is_sql_server_2000 === null)
+ {
+ $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version";
+ $result = $this->db->sql_query($sql);
+ $properties = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+ $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8';
+ }
+
+ return $this->is_sql_server_2000;
+ }
+
+ /**
+ * Returns the Queries which are required to recreate a table including indexes
+ *
+ * @param string $table_name
+ * @param string $remove_column When we drop a column, we remove the column
+ * from all indexes. If the index has no other
+ * column, we drop it completly.
+ * @return array
+ */
+ protected function sqlite_get_recreate_table_queries($table_name, $remove_column = '')
+ {
+ $queries = array();
+
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'table'
+ AND name = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+ $sql_create_table = $this->db->sql_fetchfield('sql');
+ $this->db->sql_freeresult($result);
+
+ if (!$sql_create_table)
+ {
+ return array();
+ }
+ $queries[] = $sql_create_table;
+
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'index'
+ AND tbl_name = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+ while ($sql_create_index = $this->db->sql_fetchfield('sql'))
+ {
+ if ($remove_column)
+ {
+ $match = array();
+ preg_match('#(?:[\w ]+)\((.*)\)#', $sql_create_index, $match);
+ if (!isset($match[1]))
+ {
+ continue;
+ }
+
+ // Find and remove $remove_column from the index
+ $columns = explode(', ', $match[1]);
+ $found_column = array_search($remove_column, $columns);
+ if ($found_column !== false)
+ {
+ unset($columns[$found_column]);
+
+ // If the column list is not empty add the index to the list
+ if (!empty($columns))
+ {
+ $queries[] = str_replace($match[1], implode(', ', $columns), $sql_create_index);
+ }
+ }
+ else
+ {
+ $queries[] = $sql_create_index;
+ }
+ }
+ else
+ {
+ $queries[] = $sql_create_index;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return $queries;
+ }
+}