aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/phpbb
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/phpbb')
-rw-r--r--phpBB/phpbb/attachment/delete.php432
-rw-r--r--phpBB/phpbb/attachment/manager.php99
-rw-r--r--phpBB/phpbb/attachment/resync.php124
-rw-r--r--phpBB/phpbb/attachment/upload.php334
-rw-r--r--phpBB/phpbb/auth/auth.php48
-rw-r--r--phpBB/phpbb/auth/provider/apache.php36
-rw-r--r--phpBB/phpbb/auth/provider/base.php14
-rw-r--r--phpBB/phpbb/auth/provider/db.php111
-rw-r--r--phpBB/phpbb/auth/provider/ldap.php28
-rw-r--r--phpBB/phpbb/auth/provider/oauth/oauth.php71
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/base.php12
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/bitly.php12
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/exception.php12
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/facebook.php20
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/google.php20
-rw-r--r--phpBB/phpbb/auth/provider/oauth/service/service_interface.php14
-rw-r--r--phpBB/phpbb/auth/provider/oauth/token_storage.php260
-rw-r--r--phpBB/phpbb/auth/provider/provider_interface.php24
-rw-r--r--phpBB/phpbb/auth/provider_collection.php67
-rw-r--r--phpBB/phpbb/avatar/driver/driver.php45
-rw-r--r--phpBB/phpbb/avatar/driver/driver_interface.php25
-rw-r--r--phpBB/phpbb/avatar/driver/gravatar.php56
-rw-r--r--phpBB/phpbb/avatar/driver/local.php50
-rw-r--r--phpBB/phpbb/avatar/driver/remote.php82
-rw-r--r--phpBB/phpbb/avatar/driver/upload.php168
-rw-r--r--phpBB/phpbb/avatar/manager.php89
-rw-r--r--phpBB/phpbb/cache/driver/apc.php15
-rw-r--r--phpBB/phpbb/cache/driver/base.php223
-rw-r--r--phpBB/phpbb/cache/driver/driver_interface.php45
-rw-r--r--phpBB/phpbb/cache/driver/dummy.php (renamed from phpBB/phpbb/cache/driver/null.php)51
-rw-r--r--phpBB/phpbb/cache/driver/eaccelerator.php25
-rw-r--r--phpBB/phpbb/cache/driver/file.php278
-rw-r--r--phpBB/phpbb/cache/driver/memcache.php21
-rw-r--r--phpBB/phpbb/cache/driver/memory.php222
-rw-r--r--phpBB/phpbb/cache/driver/redis.php19
-rw-r--r--phpBB/phpbb/cache/driver/wincache.php15
-rw-r--r--phpBB/phpbb/cache/driver/xcache.php15
-rw-r--r--phpBB/phpbb/cache/service.php43
-rw-r--r--phpBB/phpbb/captcha/char_cube3d.php277
-rw-r--r--phpBB/phpbb/captcha/colour_manager.php527
-rw-r--r--phpBB/phpbb/captcha/factory.php88
-rw-r--r--phpBB/phpbb/captcha/gd.php1844
-rw-r--r--phpBB/phpbb/captcha/gd_wave.php842
-rw-r--r--phpBB/phpbb/captcha/non_gd.php386
-rw-r--r--phpBB/phpbb/captcha/plugins/captcha_abstract.php390
-rw-r--r--phpBB/phpbb/captcha/plugins/gd.php123
-rw-r--r--phpBB/phpbb/captcha/plugins/gd_wave.php42
-rw-r--r--phpBB/phpbb/captcha/plugins/nogd.php42
-rw-r--r--phpBB/phpbb/captcha/plugins/qa.php1011
-rw-r--r--phpBB/phpbb/captcha/plugins/recaptcha.php225
-rw-r--r--phpBB/phpbb/class_loader.php20
-rw-r--r--phpBB/phpbb/composer.json32
-rw-r--r--phpBB/phpbb/config/config.php13
-rw-r--r--phpBB/phpbb/config/db.php21
-rw-r--r--phpBB/phpbb/config/db_text.php28
-rw-r--r--phpBB/phpbb/config_php_file.php160
-rw-r--r--phpBB/phpbb/console/application.php144
-rw-r--r--phpBB/phpbb/console/command/cache/purge.php89
-rw-r--r--phpBB/phpbb/console/command/command.php23
-rw-r--r--phpBB/phpbb/console/command/config/command.php14
-rw-r--r--phpBB/phpbb/console/command/config/delete.php33
-rw-r--r--phpBB/phpbb/console/command/config/get.php34
-rw-r--r--phpBB/phpbb/console/command/config/increment.php34
-rw-r--r--phpBB/phpbb/console/command/config/set.php34
-rw-r--r--phpBB/phpbb/console/command/config/set_atomic.php39
-rw-r--r--phpBB/phpbb/console/command/cron/cron_list.php111
-rw-r--r--phpBB/phpbb/console/command/cron/run.php171
-rw-r--r--phpBB/phpbb/console/command/db/console_migrator_output_handler.php69
-rw-r--r--phpBB/phpbb/console/command/db/list_command.php73
-rw-r--r--phpBB/phpbb/console/command/db/migrate.php83
-rw-r--r--phpBB/phpbb/console/command/db/migration_command.php56
-rw-r--r--phpBB/phpbb/console/command/db/revert.php88
-rw-r--r--phpBB/phpbb/console/command/dev/migration_tips.php64
-rw-r--r--phpBB/phpbb/console/command/extension/command.php18
-rw-r--r--phpBB/phpbb/console/command/extension/disable.php21
-rw-r--r--phpBB/phpbb/console/command/extension/enable.php21
-rw-r--r--phpBB/phpbb/console/command/extension/purge.php21
-rw-r--r--phpBB/phpbb/console/command/extension/show.php22
-rw-r--r--phpBB/phpbb/console/command/fixup/recalculate_email_hash.php20
-rw-r--r--phpBB/phpbb/console/command/reparser/list_all.php69
-rw-r--r--phpBB/phpbb/console/command/reparser/reparse.php284
-rw-r--r--phpBB/phpbb/console/command/thumbnail/delete.php178
-rw-r--r--phpBB/phpbb/console/command/thumbnail/generate.php204
-rw-r--r--phpBB/phpbb/console/command/thumbnail/recreate.php72
-rw-r--r--phpBB/phpbb/console/exception_subscriber.php74
-rw-r--r--phpBB/phpbb/content_visibility.php359
-rw-r--r--phpBB/phpbb/controller/exception.php13
-rw-r--r--phpBB/phpbb/controller/helper.php155
-rw-r--r--phpBB/phpbb/controller/provider.php75
-rw-r--r--phpBB/phpbb/controller/resolver.php65
-rw-r--r--phpBB/phpbb/cron/manager.php29
-rw-r--r--phpBB/phpbb/cron/task/base.php12
-rw-r--r--phpBB/phpbb/cron/task/core/prune_all_forums.php18
-rw-r--r--phpBB/phpbb/cron/task/core/prune_forum.php20
-rw-r--r--phpBB/phpbb/cron/task/core/prune_notifications.php12
-rw-r--r--phpBB/phpbb/cron/task/core/prune_shadow_topics.php200
-rw-r--r--phpBB/phpbb/cron/task/core/queue.php16
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_cache.php12
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_database.php14
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_plupload.php20
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_search.php73
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_sessions.php12
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_warnings.php14
-rw-r--r--phpBB/phpbb/cron/task/parametrized.php12
-rw-r--r--phpBB/phpbb/cron/task/task.php11
-rw-r--r--phpBB/phpbb/cron/task/text_reparser/reparser.php168
-rw-r--r--phpBB/phpbb/cron/task/wrapper.php14
-rw-r--r--phpBB/phpbb/datetime.php56
-rw-r--r--phpBB/phpbb/db/driver/driver.php419
-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/firebird.php532
-rw-r--r--phpBB/phpbb/db/driver/mssql.php112
-rw-r--r--phpBB/phpbb/db/driver/mssql_base.php22
-rw-r--r--phpBB/phpbb/db/driver/mssql_odbc.php76
-rw-r--r--phpBB/phpbb/db/driver/mssqlnative.php87
-rw-r--r--phpBB/phpbb/db/driver/mysql.php125
-rw-r--r--phpBB/phpbb/db/driver/mysql_base.php37
-rw-r--r--phpBB/phpbb/db/driver/mysqli.php125
-rw-r--r--phpBB/phpbb/db/driver/oracle.php117
-rw-r--r--phpBB/phpbb/db/driver/postgres.php114
-rw-r--r--phpBB/phpbb/db/driver/sqlite.php103
-rw-r--r--phpBB/phpbb/db/driver/sqlite3.php431
-rw-r--r--phpBB/phpbb/db/extractor/base_extractor.php252
-rw-r--r--phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php24
-rw-r--r--phpBB/phpbb/db/extractor/exception/invalid_format_exception.php22
-rw-r--r--phpBB/phpbb/db/extractor/extractor_interface.php80
-rw-r--r--phpBB/phpbb/db/extractor/factory.php79
-rw-r--r--phpBB/phpbb/db/extractor/mssql_extractor.php524
-rw-r--r--phpBB/phpbb/db/extractor/mysql_extractor.php403
-rw-r--r--phpBB/phpbb/db/extractor/oracle_extractor.php263
-rw-r--r--phpBB/phpbb/db/extractor/postgres_extractor.php339
-rw-r--r--phpBB/phpbb/db/extractor/sqlite3_extractor.php151
-rw-r--r--phpBB/phpbb/db/extractor/sqlite_extractor.php149
-rw-r--r--phpBB/phpbb/db/migration/container_aware_migration.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php23
-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.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_10_rc3.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_11_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc1.php67
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc3.php10
-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.php15
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_2_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_3_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php12
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_4_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1.php23
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_5_rc1part2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc3.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_6_rc4.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_pl1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc1.php12
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_7_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_8_rc1.php75
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc1.php14
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc3.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v30x/release_3_0_9_rc4.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php76
-rw-r--r--phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/allow_cdn.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha1.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/alpha3.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/auth_provider_oauth2.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v310/avatar_types.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/avatars.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta1.php37
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta2.php33
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta3.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v310/beta4.php33
-rw-r--r--phpBB/phpbb/db/migration/data/v310/board_contact_name.php34
-rw-r--r--phpBB/phpbb/db/migration/data/v310/boardindex.php10
-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.php10
-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.php22
-rw-r--r--phpBB/phpbb/db/migration/data/v310/extensions.php10
-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.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/gold.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v310/jquery_update.php10
-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.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/namespaces.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_cron.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/notifications_use_full_name.php184
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php84
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php49
-rw-r--r--phpBB/phpbb/db/migration/data/v310/passwords_p2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/plupload.php10
-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.php10
-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.php11
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_location.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_location_cleanup.php51
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php10
-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.php10
-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.php39
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc2.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc3.php35
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc4.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc5.php33
-rw-r--r--phpBB/phpbb/db/migration/data/v310/rc6.php31
-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.php10
-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.php10
-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.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/softdelete_p1.php19
-rw-r--r--phpBB/phpbb/db/migration/data/v310/softdelete_p2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/style_update_p1.php22
-rw-r--r--phpBB/phpbb/db/migration/data/v310/style_update_p2.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/teampage.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v310/timezone.php55
-rw-r--r--phpBB/phpbb/db/migration/data/v310/timezone_p2.php10
-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.php10
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/increase_size_of_dateformat.php35
-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/style_update.php136
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php69
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v311.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v312.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v312rc1.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313rc1.php35
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v313rc2.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314rc1.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v314rc2.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315rc1.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v316.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v316rc1.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317pl1.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v317rc1.php32
-rw-r--r--phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v320/announce_global_permission.php43
-rw-r--r--phpBB/phpbb/db/migration/data/v320/dev.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v320/font_awesome_update.php36
-rw-r--r--phpBB/phpbb/db/migration/data/v320/icons_alt.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v320/log_post_id.php46
-rw-r--r--phpBB/phpbb/db/migration/data/v320/notifications_board.php75
-rw-r--r--phpBB/phpbb/db/migration/data/v320/oauth_states.php56
-rw-r--r--phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php90
-rw-r--r--phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php152
-rw-r--r--phpBB/phpbb/db/migration/data/v320/text_reparser.php101
-rw-r--r--phpBB/phpbb/db/migration/data/v320/v320a1.php44
-rw-r--r--phpBB/phpbb/db/migration/data/v320/v320a2.php38
-rw-r--r--phpBB/phpbb/db/migration/exception.php12
-rw-r--r--phpBB/phpbb/db/migration/helper.php12
-rw-r--r--phpBB/phpbb/db/migration/migration.php28
-rw-r--r--phpBB/phpbb/db/migration/profilefield_base_migration.php110
-rw-r--r--phpBB/phpbb/db/migration/schema_generator.php235
-rw-r--r--phpBB/phpbb/db/migration/tool/config.php14
-rw-r--r--phpBB/phpbb/db/migration/tool/config_text.php125
-rw-r--r--phpBB/phpbb/db/migration/tool/module.php101
-rw-r--r--phpBB/phpbb/db/migration/tool/permission.php53
-rw-r--r--phpBB/phpbb/db/migration/tool/tool_interface.php12
-rw-r--r--phpBB/phpbb/db/migrator.php237
-rw-r--r--phpBB/phpbb/db/output_handler/html_migrator_output_handler.php46
-rw-r--r--phpBB/phpbb/db/output_handler/installer_migrator_output_handler.php46
-rw-r--r--phpBB/phpbb/db/output_handler/log_wrapper_migrator_output_handler.php101
-rw-r--r--phpBB/phpbb/db/output_handler/migrator_output_handler_interface.php31
-rw-r--r--phpBB/phpbb/db/output_handler/null_migrator_output_handler.php24
-rw-r--r--phpBB/phpbb/db/sql_insert_buffer.php22
-rw-r--r--phpBB/phpbb/db/tools/factory.php43
-rw-r--r--phpBB/phpbb/db/tools/mssql.php795
-rw-r--r--phpBB/phpbb/db/tools/postgres.php614
-rw-r--r--phpBB/phpbb/db/tools/tools.php (renamed from phpBB/phpbb/db/tools.php)1457
-rw-r--r--phpBB/phpbb/db/tools/tools_interface.php202
-rw-r--r--phpBB/phpbb/debug/debug.php80
-rw-r--r--phpBB/phpbb/debug/error_handler.php31
-rw-r--r--phpBB/phpbb/di/container_builder.php551
-rw-r--r--phpBB/phpbb/di/extension/config.php51
-rw-r--r--phpBB/phpbb/di/extension/container_configuration.php52
-rw-r--r--phpBB/phpbb/di/extension/core.php115
-rw-r--r--phpBB/phpbb/di/extension/ext.php63
-rw-r--r--phpBB/phpbb/di/ordered_service_collection.php117
-rw-r--r--phpBB/phpbb/di/pass/collection_pass.php32
-rw-r--r--phpBB/phpbb/di/pass/kernel_pass.php62
-rw-r--r--phpBB/phpbb/di/service_collection.php77
-rw-r--r--phpBB/phpbb/di/service_collection_iterator.php46
-rw-r--r--phpBB/phpbb/error_collector.php27
-rw-r--r--phpBB/phpbb/event/data.php10
-rw-r--r--phpBB/phpbb/event/dispatcher.php50
-rw-r--r--phpBB/phpbb/event/dispatcher_interface.php50
-rw-r--r--phpBB/phpbb/event/extension_subscriber_loader.php35
-rw-r--r--phpBB/phpbb/event/kernel_exception_subscriber.php87
-rw-r--r--phpBB/phpbb/event/kernel_request_subscriber.php77
-rw-r--r--phpBB/phpbb/event/kernel_terminate_subscriber.php14
-rw-r--r--phpBB/phpbb/event/md_exporter.php560
-rw-r--r--phpBB/phpbb/event/php_exporter.php718
-rw-r--r--phpBB/phpbb/event/recursive_event_filter_iterator.php71
-rw-r--r--phpBB/phpbb/exception/exception_interface.php29
-rw-r--r--phpBB/phpbb/exception/http_exception.php70
-rw-r--r--phpBB/phpbb/exception/runtime_exception.php52
-rw-r--r--phpBB/phpbb/extension/base.php37
-rw-r--r--phpBB/phpbb/extension/di/extension_base.php138
-rw-r--r--phpBB/phpbb/extension/exception.php16
-rw-r--r--phpBB/phpbb/extension/extension_interface.php19
-rw-r--r--phpBB/phpbb/extension/manager.php138
-rw-r--r--phpBB/phpbb/extension/metadata_manager.php168
-rw-r--r--phpBB/phpbb/extension/provider.php14
-rw-r--r--phpBB/phpbb/feed/attachments_base.php85
-rw-r--r--phpBB/phpbb/feed/base.php217
-rw-r--r--phpBB/phpbb/feed/controller/feed.php389
-rw-r--r--phpBB/phpbb/feed/exception/feed_exception.php21
-rw-r--r--phpBB/phpbb/feed/exception/feed_unavailable_exception.php19
-rw-r--r--phpBB/phpbb/feed/exception/no_feed_exception.php22
-rw-r--r--phpBB/phpbb/feed/exception/no_forum_exception.php22
-rw-r--r--phpBB/phpbb/feed/exception/no_topic_exception.php22
-rw-r--r--phpBB/phpbb/feed/exception/unauthorized_exception.php19
-rw-r--r--phpBB/phpbb/feed/exception/unauthorized_forum_exception.php22
-rw-r--r--phpBB/phpbb/feed/exception/unauthorized_topic_exception.php22
-rw-r--r--phpBB/phpbb/feed/factory.php123
-rw-r--r--phpBB/phpbb/feed/feed_interface.php67
-rw-r--r--phpBB/phpbb/feed/forum.php89
-rw-r--r--phpBB/phpbb/feed/forums.php47
-rw-r--r--phpBB/phpbb/feed/helper.php88
-rw-r--r--phpBB/phpbb/feed/news.php50
-rw-r--r--phpBB/phpbb/feed/overall.php48
-rw-r--r--phpBB/phpbb/feed/post_base.php39
-rw-r--r--phpBB/phpbb/feed/topic.php92
-rw-r--r--phpBB/phpbb/feed/topic_base.php57
-rw-r--r--phpBB/phpbb/feed/topics.php49
-rw-r--r--phpBB/phpbb/feed/topics_active.php67
-rw-r--r--phpBB/phpbb/file_downloader.php120
-rw-r--r--phpBB/phpbb/files/factory.php58
-rw-r--r--phpBB/phpbb/files/filespec.php584
-rw-r--r--phpBB/phpbb/files/types/base.php65
-rw-r--r--phpBB/phpbb/files/types/form.php138
-rw-r--r--phpBB/phpbb/files/types/local.php136
-rw-r--r--phpBB/phpbb/files/types/remote.php258
-rw-r--r--phpBB/phpbb/files/types/type_interface.php38
-rw-r--r--phpBB/phpbb/files/upload.php390
-rw-r--r--phpBB/phpbb/filesystem.php46
-rw-r--r--phpBB/phpbb/filesystem/exception/filesystem_exception.php42
-rw-r--r--phpBB/phpbb/filesystem/filesystem.php916
-rw-r--r--phpBB/phpbb/filesystem/filesystem_interface.php284
-rw-r--r--phpBB/phpbb/finder.php (renamed from phpBB/phpbb/extension/finder.php)182
-rw-r--r--phpBB/phpbb/group/helper.php40
-rw-r--r--phpBB/phpbb/groupposition/exception.php13
-rw-r--r--phpBB/phpbb/groupposition/groupposition_interface.php12
-rw-r--r--phpBB/phpbb/groupposition/legend.php36
-rw-r--r--phpBB/phpbb/groupposition/teampage.php45
-rw-r--r--phpBB/phpbb/help/controller/bbcode.php85
-rw-r--r--phpBB/phpbb/help/controller/controller.php76
-rw-r--r--phpBB/phpbb/help/controller/faq.php165
-rw-r--r--phpBB/phpbb/help/controller/help.php160
-rw-r--r--phpBB/phpbb/help/manager.php137
-rw-r--r--phpBB/phpbb/hook/finder.php25
-rw-r--r--phpBB/phpbb/install/console/command/install/config/show.php131
-rw-r--r--phpBB/phpbb/install/console/command/install/config/validate.php132
-rw-r--r--phpBB/phpbb/install/console/command/install/install.php206
-rw-r--r--phpBB/phpbb/install/controller/archive_download.php93
-rw-r--r--phpBB/phpbb/install/controller/helper.php417
-rw-r--r--phpBB/phpbb/install/controller/install.php173
-rw-r--r--phpBB/phpbb/install/controller/installer_index.php81
-rw-r--r--phpBB/phpbb/install/controller/update.php167
-rw-r--r--phpBB/phpbb/install/event/kernel_exception_subscriber.php125
-rw-r--r--phpBB/phpbb/install/exception/cannot_build_container_exception.php22
-rw-r--r--phpBB/phpbb/install/exception/file_updater_failure_exception.php22
-rw-r--r--phpBB/phpbb/install/exception/installer_config_not_writable_exception.php22
-rw-r--r--phpBB/phpbb/install/exception/installer_exception.php24
-rw-r--r--phpBB/phpbb/install/exception/invalid_dbms_exception.php22
-rw-r--r--phpBB/phpbb/install/exception/jump_to_restart_point_exception.php44
-rw-r--r--phpBB/phpbb/install/exception/resource_limit_reached_exception.php22
-rw-r--r--phpBB/phpbb/install/exception/user_interaction_required_exception.php25
-rw-r--r--phpBB/phpbb/install/helper/config.php438
-rw-r--r--phpBB/phpbb/install/helper/container_factory.php194
-rw-r--r--phpBB/phpbb/install/helper/database.php456
-rw-r--r--phpBB/phpbb/install/helper/file_updater/compression_file_updater.php133
-rw-r--r--phpBB/phpbb/install/helper/file_updater/factory.php69
-rw-r--r--phpBB/phpbb/install/helper/file_updater/file_updater.php202
-rw-r--r--phpBB/phpbb/install/helper/file_updater/file_updater_interface.php49
-rw-r--r--phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php136
-rw-r--r--phpBB/phpbb/install/helper/install_helper.php60
-rw-r--r--phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php390
-rw-r--r--phpBB/phpbb/install/helper/iohandler/cli_iohandler.php292
-rw-r--r--phpBB/phpbb/install/helper/iohandler/exception/iohandler_not_implemented_exception.php19
-rw-r--r--phpBB/phpbb/install/helper/iohandler/factory.php81
-rw-r--r--phpBB/phpbb/install/helper/iohandler/iohandler_base.php196
-rw-r--r--phpBB/phpbb/install/helper/iohandler/iohandler_interface.php191
-rw-r--r--phpBB/phpbb/install/helper/navigation/install_navigation.php75
-rw-r--r--phpBB/phpbb/install/helper/navigation/main_navigation.php48
-rw-r--r--phpBB/phpbb/install/helper/navigation/navigation_interface.php43
-rw-r--r--phpBB/phpbb/install/helper/navigation/navigation_provider.php121
-rw-r--r--phpBB/phpbb/install/helper/navigation/update_navigation.php80
-rw-r--r--phpBB/phpbb/install/helper/update_helper.php113
-rw-r--r--phpBB/phpbb/install/installer.php275
-rw-r--r--phpBB/phpbb/install/installer_configuration.php140
-rw-r--r--phpBB/phpbb/install/module/install_data/module.php28
-rw-r--r--phpBB/phpbb/install/module/install_data/task/add_bots.php242
-rw-r--r--phpBB/phpbb/install/module/install_data/task/add_languages.php121
-rw-r--r--phpBB/phpbb/install/module/install_data/task/add_modules.php462
-rw-r--r--phpBB/phpbb/install/module/install_database/module.php28
-rw-r--r--phpBB/phpbb/install/module/install_database/task/add_config_settings.php341
-rw-r--r--phpBB/phpbb/install/module/install_database/task/add_default_data.php163
-rw-r--r--phpBB/phpbb/install/module/install_database/task/create_schema.php221
-rw-r--r--phpBB/phpbb/install/module/install_filesystem/module.php28
-rw-r--r--phpBB/phpbb/install/module/install_filesystem/task/create_config_file.php246
-rw-r--r--phpBB/phpbb/install/module/install_finish/module.php28
-rw-r--r--phpBB/phpbb/install/module/install_finish/task/notify_user.php170
-rw-r--r--phpBB/phpbb/install/module/install_finish/task/populate_migrations.php72
-rw-r--r--phpBB/phpbb/install/module/obtain_data/install_module.php33
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_admin_data.php219
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_board_data.php186
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_database_data.php271
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php167
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_file_updater_method.php168
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_imagick_path.php89
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_server_data.php203
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_update_files.php113
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_update_ftp_data.php164
-rw-r--r--phpBB/phpbb/install/module/obtain_data/task/obtain_update_settings.php103
-rw-r--r--phpBB/phpbb/install/module/obtain_data/update_module.php33
-rw-r--r--phpBB/phpbb/install/module/requirements/abstract_requirements_module.php106
-rw-r--r--phpBB/phpbb/install/module/requirements/install_module.php25
-rw-r--r--phpBB/phpbb/install/module/requirements/task/check_filesystem.php275
-rw-r--r--phpBB/phpbb/install/module/requirements/task/check_server_environment.php190
-rw-r--r--phpBB/phpbb/install/module/requirements/task/check_update.php185
-rw-r--r--phpBB/phpbb/install/module/requirements/update_module.php25
-rw-r--r--phpBB/phpbb/install/module/update_database/module.php33
-rw-r--r--phpBB/phpbb/install/module/update_database/task/update.php212
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/module.php33
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/task/diff_files.php205
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php124
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/task/file_check.php186
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php168
-rw-r--r--phpBB/phpbb/install/module/update_filesystem/task/update_files.php294
-rw-r--r--phpBB/phpbb/install/module_base.php218
-rw-r--r--phpBB/phpbb/install/module_interface.php63
-rw-r--r--phpBB/phpbb/install/task_base.php53
-rw-r--r--phpBB/phpbb/install/task_interface.php61
-rw-r--r--phpBB/phpbb/json_response.php11
-rw-r--r--phpBB/phpbb/language/exception/invalid_plural_rule_exception.php22
-rw-r--r--phpBB/phpbb/language/exception/language_exception.php22
-rw-r--r--phpBB/phpbb/language/exception/language_file_not_found.php22
-rw-r--r--phpBB/phpbb/language/language.php653
-rw-r--r--phpBB/phpbb/language/language_file_helper.php71
-rw-r--r--phpBB/phpbb/language/language_file_loader.php206
-rw-r--r--phpBB/phpbb/lock/db.php21
-rw-r--r--phpBB/phpbb/lock/flock.php11
-rw-r--r--phpBB/phpbb/log/dummy.php (renamed from phpBB/phpbb/log/null.php)23
-rw-r--r--phpBB/phpbb/log/log.php378
-rw-r--r--phpBB/phpbb/log/log_interface.php40
-rw-r--r--phpBB/phpbb/message/admin_form.php192
-rw-r--r--phpBB/phpbb/message/form.php175
-rw-r--r--phpBB/phpbb/message/message.php282
-rw-r--r--phpBB/phpbb/message/topic_form.php158
-rw-r--r--phpBB/phpbb/message/user_form.php136
-rw-r--r--phpBB/phpbb/mimetype/content_guesser.php20
-rw-r--r--phpBB/phpbb/mimetype/extension_guesser.php18
-rw-r--r--phpBB/phpbb/mimetype/guesser.php56
-rw-r--r--phpBB/phpbb/mimetype/guesser_base.php18
-rw-r--r--phpBB/phpbb/mimetype/guesser_interface.php15
-rw-r--r--phpBB/phpbb/module/exception/module_exception.php19
-rw-r--r--phpBB/phpbb/module/exception/module_not_found_exception.php19
-rw-r--r--phpBB/phpbb/module/module_manager.php564
-rw-r--r--phpBB/phpbb/notification/exception.php19
-rw-r--r--phpBB/phpbb/notification/manager.php732
-rw-r--r--phpBB/phpbb/notification/method/base.php145
-rw-r--r--phpBB/phpbb/notification/method/board.php399
-rw-r--r--phpBB/phpbb/notification/method/email.php38
-rw-r--r--phpBB/phpbb/notification/method/jabber.php40
-rw-r--r--phpBB/phpbb/notification/method/messenger_base.php39
-rw-r--r--phpBB/phpbb/notification/method/method_interface.php115
-rw-r--r--phpBB/phpbb/notification/type/admin_activate_user.php49
-rw-r--r--phpBB/phpbb/notification/type/approve_post.php62
-rw-r--r--phpBB/phpbb/notification/type/approve_topic.php55
-rw-r--r--phpBB/phpbb/notification/type/base.php239
-rw-r--r--phpBB/phpbb/notification/type/bookmark.php72
-rw-r--r--phpBB/phpbb/notification/type/disapprove_post.php76
-rw-r--r--phpBB/phpbb/notification/type/disapprove_topic.php76
-rw-r--r--phpBB/phpbb/notification/type/group_request.php32
-rw-r--r--phpBB/phpbb/notification/type/group_request_approved.php22
-rw-r--r--phpBB/phpbb/notification/type/pm.php67
-rw-r--r--phpBB/phpbb/notification/type/post.php174
-rw-r--r--phpBB/phpbb/notification/type/post_in_queue.php51
-rw-r--r--phpBB/phpbb/notification/type/quote.php130
-rw-r--r--phpBB/phpbb/notification/type/report_pm.php105
-rw-r--r--phpBB/phpbb/notification/type/report_pm_closed.php59
-rw-r--r--phpBB/phpbb/notification/type/report_post.php91
-rw-r--r--phpBB/phpbb/notification/type/report_post_closed.php59
-rw-r--r--phpBB/phpbb/notification/type/topic.php99
-rw-r--r--phpBB/phpbb/notification/type/topic_in_queue.php43
-rw-r--r--phpBB/phpbb/notification/type/type_interface.php55
-rw-r--r--phpBB/phpbb/pagination.php86
-rw-r--r--phpBB/phpbb/passwords/driver/base.php35
-rw-r--r--phpBB/phpbb/passwords/driver/bcrypt.php25
-rw-r--r--phpBB/phpbb/passwords/driver/bcrypt_2y.php17
-rw-r--r--phpBB/phpbb/passwords/driver/bcrypt_wcf2.php84
-rw-r--r--phpBB/phpbb/passwords/driver/convert_password.php43
-rw-r--r--phpBB/phpbb/passwords/driver/driver_interface.php23
-rw-r--r--phpBB/phpbb/passwords/driver/helper.php49
-rw-r--r--phpBB/phpbb/passwords/driver/md5_mybb.php60
-rw-r--r--phpBB/phpbb/passwords/driver/md5_phpbb2.php123
-rw-r--r--phpBB/phpbb/passwords/driver/md5_vb.php60
-rw-r--r--phpBB/phpbb/passwords/driver/phpass.php15
-rw-r--r--phpBB/phpbb/passwords/driver/salted_md5.php35
-rw-r--r--phpBB/phpbb/passwords/driver/sha1.php52
-rw-r--r--phpBB/phpbb/passwords/driver/sha1_smf.php51
-rw-r--r--phpBB/phpbb/passwords/driver/sha1_wcf1.php60
-rw-r--r--phpBB/phpbb/passwords/driver/sha_xf1.php68
-rw-r--r--phpBB/phpbb/passwords/helper.php15
-rw-r--r--phpBB/phpbb/passwords/manager.php97
-rw-r--r--phpBB/phpbb/path_helper.php335
-rw-r--r--phpBB/phpbb/permissions.php50
-rw-r--r--phpBB/phpbb/php/ini.php12
-rw-r--r--phpBB/phpbb/plupload/plupload.php73
-rw-r--r--phpBB/phpbb/profilefields/lang_helper.php76
-rw-r--r--phpBB/phpbb/profilefields/manager.php262
-rw-r--r--phpBB/phpbb/profilefields/type/type_base.php35
-rw-r--r--phpBB/phpbb/profilefields/type/type_bool.php82
-rw-r--r--phpBB/phpbb/profilefields/type/type_date.php27
-rw-r--r--phpBB/phpbb/profilefields/type/type_dropdown.php42
-rw-r--r--phpBB/phpbb/profilefields/type/type_googleplus.php66
-rw-r--r--phpBB/phpbb/profilefields/type/type_int.php25
-rw-r--r--phpBB/phpbb/profilefields/type/type_interface.php32
-rw-r--r--phpBB/phpbb/profilefields/type/type_string.php13
-rw-r--r--phpBB/phpbb/profilefields/type/type_string_common.php71
-rw-r--r--phpBB/phpbb/profilefields/type/type_text.php11
-rw-r--r--phpBB/phpbb/profilefields/type/type_url.php74
-rw-r--r--phpBB/phpbb/recursive_dot_prefix_filter_iterator.php30
-rw-r--r--phpBB/phpbb/report/controller/report.php319
-rw-r--r--phpBB/phpbb/report/exception/already_reported_exception.php19
-rw-r--r--phpBB/phpbb/report/exception/empty_report_exception.php22
-rw-r--r--phpBB/phpbb/report/exception/entity_not_found_exception.php19
-rw-r--r--phpBB/phpbb/report/exception/factory_invalid_argument_exception.php21
-rw-r--r--phpBB/phpbb/report/exception/invalid_report_exception.php21
-rw-r--r--phpBB/phpbb/report/exception/pm_reporting_disabled_exception.php22
-rw-r--r--phpBB/phpbb/report/exception/report_permission_denied_exception.php19
-rw-r--r--phpBB/phpbb/report/handler_factory.php56
-rw-r--r--phpBB/phpbb/report/report_handler.php104
-rw-r--r--phpBB/phpbb/report/report_handler_interface.php43
-rw-r--r--phpBB/phpbb/report/report_handler_pm.php137
-rw-r--r--phpBB/phpbb/report/report_handler_post.php175
-rw-r--r--phpBB/phpbb/report/report_reason_list_provider.php78
-rw-r--r--phpBB/phpbb/request/deactivated_super_global.php14
-rw-r--r--phpBB/phpbb/request/request.php37
-rw-r--r--phpBB/phpbb/request/request_interface.php22
-rw-r--r--phpBB/phpbb/request/type_cast_helper.php13
-rw-r--r--phpBB/phpbb/request/type_cast_helper_interface.php12
-rw-r--r--phpBB/phpbb/routing/file_locator.php33
-rw-r--r--phpBB/phpbb/routing/helper.php153
-rw-r--r--phpBB/phpbb/routing/loader_resolver.php50
-rw-r--r--phpBB/phpbb/routing/resources_locator/chained_resources_locator.php47
-rw-r--r--phpBB/phpbb/routing/resources_locator/default_resources_locator.php105
-rw-r--r--phpBB/phpbb/routing/resources_locator/installer_resources_locator.php78
-rw-r--r--phpBB/phpbb/routing/resources_locator/resources_locator_interface.php27
-rw-r--r--phpBB/phpbb/routing/router.php406
-rw-r--r--phpBB/phpbb/search/base.php34
-rw-r--r--phpBB/phpbb/search/fulltext_mysql.php218
-rw-r--r--phpBB/phpbb/search/fulltext_native.php339
-rw-r--r--phpBB/phpbb/search/fulltext_postgres.php271
-rw-r--r--phpBB/phpbb/search/fulltext_sphinx.php129
-rw-r--r--phpBB/phpbb/search/sphinx/config.php12
-rw-r--r--phpBB/phpbb/search/sphinx/config_comment.php10
-rw-r--r--phpBB/phpbb/search/sphinx/config_section.php10
-rw-r--r--phpBB/phpbb/search/sphinx/config_variable.php10
-rw-r--r--phpBB/phpbb/session.php309
-rw-r--r--phpBB/phpbb/symfony_request.php25
-rw-r--r--phpBB/phpbb/template/asset.php37
-rw-r--r--phpBB/phpbb/template/base.php26
-rw-r--r--phpBB/phpbb/template/context.php107
-rw-r--r--phpBB/phpbb/template/exception/user_object_not_available.php22
-rw-r--r--phpBB/phpbb/template/template.php19
-rw-r--r--phpBB/phpbb/template/twig/definition.php12
-rw-r--r--phpBB/phpbb/template/twig/environment.php100
-rw-r--r--phpBB/phpbb/template/twig/extension.php28
-rw-r--r--phpBB/phpbb/template/twig/extension/routing.php43
-rw-r--r--phpBB/phpbb/template/twig/lexer.php38
-rw-r--r--phpBB/phpbb/template/twig/loader.php42
-rw-r--r--phpBB/phpbb/template/twig/node/definenode.php23
-rw-r--r--phpBB/phpbb/template/twig/node/event.php33
-rw-r--r--phpBB/phpbb/template/twig/node/expression/binary/equalequal.php11
-rw-r--r--phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php11
-rw-r--r--phpBB/phpbb/template/twig/node/includeasset.php24
-rw-r--r--phpBB/phpbb/template/twig/node/includecss.php12
-rw-r--r--phpBB/phpbb/template/twig/node/includejs.php12
-rw-r--r--phpBB/phpbb/template/twig/node/includenode.php19
-rw-r--r--phpBB/phpbb/template/twig/node/includephp.php28
-rw-r--r--phpBB/phpbb/template/twig/node/php.php21
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/defineparser.php41
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/event.php31
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includecss.php30
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includejs.php31
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includeparser.php32
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includephp.php35
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/php.php31
-rw-r--r--phpBB/phpbb/template/twig/twig.php147
-rw-r--r--phpBB/phpbb/textformatter/cache_interface.php31
-rw-r--r--phpBB/phpbb/textformatter/data_access.php228
-rw-r--r--phpBB/phpbb/textformatter/parser_interface.php112
-rw-r--r--phpBB/phpbb/textformatter/renderer_interface.php92
-rw-r--r--phpBB/phpbb/textformatter/s9e/factory.php617
-rw-r--r--phpBB/phpbb/textformatter/s9e/link_helper.php118
-rw-r--r--phpBB/phpbb/textformatter/s9e/parser.php397
-rw-r--r--phpBB/phpbb/textformatter/s9e/quote_helper.php81
-rw-r--r--phpBB/phpbb/textformatter/s9e/renderer.php315
-rw-r--r--phpBB/phpbb/textformatter/s9e/utils.php139
-rw-r--r--phpBB/phpbb/textformatter/utils_interface.php71
-rw-r--r--phpBB/phpbb/textreparser/base.php243
-rw-r--r--phpBB/phpbb/textreparser/manager.php128
-rw-r--r--phpBB/phpbb/textreparser/plugins/contact_admin_info.php69
-rw-r--r--phpBB/phpbb/textreparser/plugins/forum_description.php30
-rw-r--r--phpBB/phpbb/textreparser/plugins/forum_rules.php30
-rw-r--r--phpBB/phpbb/textreparser/plugins/group_description.php30
-rw-r--r--phpBB/phpbb/textreparser/plugins/pm_text.php32
-rw-r--r--phpBB/phpbb/textreparser/plugins/poll_option.php74
-rw-r--r--phpBB/phpbb/textreparser/plugins/poll_title.php42
-rw-r--r--phpBB/phpbb/textreparser/plugins/post_text.php32
-rw-r--r--phpBB/phpbb/textreparser/plugins/user_signature.php65
-rw-r--r--phpBB/phpbb/textreparser/reparser_interface.php32
-rw-r--r--phpBB/phpbb/textreparser/row_based_plugin.php117
-rw-r--r--phpBB/phpbb/tree/nestedset.php73
-rw-r--r--phpBB/phpbb/tree/nestedset_forum.php14
-rw-r--r--phpBB/phpbb/tree/tree_interface.php12
-rw-r--r--phpBB/phpbb/user.php426
-rw-r--r--phpBB/phpbb/user_loader.php40
-rw-r--r--phpBB/phpbb/version_helper.php306
-rw-r--r--phpBB/phpbb/viewonline_helper.php54
688 files changed, 60545 insertions, 7672 deletions
diff --git a/phpBB/phpbb/attachment/delete.php b/phpBB/phpbb/attachment/delete.php
new file mode 100644
index 0000000000..e093da8865
--- /dev/null
+++ b/phpBB/phpbb/attachment/delete.php
@@ -0,0 +1,432 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\attachment;
+
+use \phpbb\config\config;
+use \phpbb\db\driver\driver_interface;
+use \phpbb\event\dispatcher;
+use \phpbb\filesystem\filesystem;
+
+/**
+ * Attachment delete class
+ */
+class delete
+{
+ /** @var config */
+ protected $config;
+
+ /** @var driver_interface */
+ protected $db;
+
+ /** @var \phpbb\event\dispatcher */
+ protected $dispatcher;
+
+ /** @var filesystem */
+ protected $filesystem;
+
+ /** @var resync */
+ protected $resync;
+
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /** @var array Attachement IDs */
+ protected $ids;
+
+ /** @var string SQL ID string */
+ private $sql_id;
+
+ /** @var string SQL where string */
+ private $sql_where = '';
+
+ /** @var int Number of deleted items */
+ private $num_deleted;
+
+ /** @var array Post IDs */
+ private $post_ids = array();
+
+ /** @var array Message IDs */
+ private $message_ids = array();
+
+ /** @var array Topic IDs */
+ private $topic_ids = array();
+
+ /** @var array Info of physical file */
+ private $physical = array();
+
+ /**
+ * Attachment delete class constructor
+ *
+ * @param config $config
+ * @param driver_interface $db
+ * @param dispatcher $dispatcher
+ * @param filesystem $filesystem
+ * @param resync $resync
+ * @param string $phpbb_root_path
+ */
+ public function __construct(config $config, driver_interface $db, dispatcher $dispatcher, filesystem $filesystem, resync $resync, $phpbb_root_path)
+ {
+ $this->config = $config;
+ $this->db = $db;
+ $this->dispatcher = $dispatcher;
+ $this->filesystem = $filesystem;
+ $this->resync = $resync;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * Delete Attachments
+ *
+ * @param string $mode can be: post|message|topic|attach|user
+ * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
+ * @param bool $resync set this to false if you are deleting posts or topics
+ *
+ * @return int|bool Number of deleted attachments or false if something
+ * went wrong during attachment deletion
+ */
+ public function delete($mode, $ids, $resync = true)
+ {
+ if (!$this->set_attachment_ids($ids))
+ {
+ return false;
+ }
+
+ $this->set_sql_constraints($mode);
+
+ /**
+ * Perform additional actions before collecting data for attachment(s) deletion
+ *
+ * @event core.delete_attachments_collect_data_before
+ * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
+ * @var mixed ids Array or comma separated list of ids corresponding to the mode
+ * @var bool resync Flag indicating if posts/messages/topics should be synchronized
+ * @var string sql_id The field name to collect/delete data for depending on the mode
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'mode',
+ 'ids',
+ 'resync',
+ 'sql_id',
+ );
+ extract($this->dispatcher->trigger_event('core.delete_attachments_collect_data_before', compact($vars)));
+
+ // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
+ $this->collect_attachment_info($resync);
+
+ // Delete attachments from database
+ $this->delete_attachments_from_db();
+
+ /**
+ * Perform additional actions after attachment(s) deletion from the database
+ *
+ * @event core.delete_attachments_from_database_after
+ * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
+ * @var mixed ids Array or comma separated list of ids corresponding to the mode
+ * @var bool resync Flag indicating if posts/messages/topics should be synchronized
+ * @var string sql_id The field name to collect/delete data for depending on the mode
+ * @var array post_ids Array with post ids for deleted attachment(s)
+ * @var array topic_ids Array with topic ids for deleted attachment(s)
+ * @var array message_ids Array with private message ids for deleted attachment(s)
+ * @var array physical Array with deleted attachment(s) physical file(s) data
+ * @var int num_deleted The number of deleted attachment(s) from the database
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'mode',
+ 'ids',
+ 'resync',
+ 'sql_id',
+ 'post_ids',
+ 'topic_ids',
+ 'message_ids',
+ 'physical',
+ 'num_deleted',
+ );
+ extract($this->dispatcher->trigger_event('core.delete_attachments_from_database_after', compact($vars)));
+
+ if (!$this->num_deleted)
+ {
+ return 0;
+ }
+
+ // Delete attachments from filesystem
+ $this->remove_from_filesystem();
+
+ // If we do not resync, we do not need to adjust any message, post, topic or user entries
+ if (!$resync)
+ {
+ return $this->num_deleted;
+ }
+
+ // No more use for the original ids
+ unset($ids);
+
+ // Update post indicators for posts now no longer having attachments
+ $this->resync->resync('post', $this->post_ids);
+
+ // Update message table if messages are affected
+ $this->resync->resync('message', $this->message_ids);
+
+ // Now update the topics. This is a bit trickier, because there could be posts still having attachments within the topic
+ $this->resync->resync('topic', $this->topic_ids);
+
+ return $this->num_deleted;
+ }
+
+ /**
+ * Set attachment IDs
+ *
+ * @param mixed $ids ID or array of IDs
+ *
+ * @return bool True if attachment IDs were set, false if not
+ */
+ protected function set_attachment_ids($ids)
+ {
+ // 0 is as bad as an empty array
+ if (empty($ids))
+ {
+ return false;
+ }
+
+ if (is_array($ids))
+ {
+ $ids = array_unique($ids);
+ $this->ids = array_map('intval', $ids);
+ }
+ else
+ {
+ $this->ids = array((int) $ids);
+ }
+
+ return true;
+ }
+
+ /**
+ * Set SQL constraints based on mode
+ *
+ * @param string $mode Delete mode; can be: post|message|topic|attach|user
+ */
+ private function set_sql_constraints($mode)
+ {
+ switch ($mode)
+ {
+ case 'post':
+ case 'message':
+ $this->sql_id = 'post_msg_id';
+ $this->sql_where = ' AND in_message = ' . ($mode == 'message' ? 1 : 0);
+ break;
+
+ case 'topic':
+ $this->sql_id = 'topic_id';
+ break;
+
+ case 'user':
+ $this->sql_id = 'poster_id';
+ break;
+
+ case 'attach':
+ default:
+ $this->sql_id = 'attach_id';
+ break;
+ }
+ }
+
+ /**
+ * Collect info about attachment IDs
+ *
+ * @param bool $resync Whether topics/posts should be resynced after delete
+ */
+ protected function collect_attachment_info($resync)
+ {
+ // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
+ $sql = 'SELECT post_msg_id, topic_id, in_message, physical_filename, thumbnail, filesize, is_orphan
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE ' . $this->db->sql_in_set($this->sql_id, $this->ids);
+
+ $sql .= $this->sql_where;
+
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // We only need to store post/message/topic ids if resync is enabled and the file is not orphaned
+ if ($resync && !$row['is_orphan'])
+ {
+ if (!$row['in_message'])
+ {
+ $this->post_ids[] = $row['post_msg_id'];
+ $this->topic_ids[] = $row['topic_id'];
+ }
+ else
+ {
+ $this->message_ids[] = $row['post_msg_id'];
+ }
+ }
+
+ $this->physical[] = array('filename' => $row['physical_filename'], 'thumbnail' => $row['thumbnail'], 'filesize' => $row['filesize'], 'is_orphan' => $row['is_orphan']);
+ }
+ $this->db->sql_freeresult($result);
+
+ // IDs should be unique
+ $this->post_ids = array_unique($this->post_ids);
+ $this->message_ids = array_unique($this->message_ids);
+ $this->topic_ids = array_unique($this->topic_ids);
+ }
+
+ /**
+ * Delete attachments from database table
+ */
+ protected function delete_attachments_from_db()
+ {
+ /**
+ * Perform additional actions before attachment(s) deletion
+ *
+ * @event core.delete_attachments_before
+ * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
+ * @var mixed ids Array or comma separated list of ids corresponding to the mode
+ * @var bool resync Flag indicating if posts/messages/topics should be synchronized
+ * @var string sql_id The field name to collect/delete data for depending on the mode
+ * @var array post_ids Array with post ids for deleted attachment(s)
+ * @var array topic_ids Array with topic ids for deleted attachment(s)
+ * @var array message_ids Array with private message ids for deleted attachment(s)
+ * @var array physical Array with deleted attachment(s) physical file(s) data
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'mode',
+ 'ids',
+ 'resync',
+ 'sql_id',
+ 'post_ids',
+ 'topic_ids',
+ 'message_ids',
+ 'physical',
+ );
+ extract($this->dispatcher->trigger_event('core.delete_attachments_before', compact($vars)));
+
+ // Delete attachments
+ $sql = 'DELETE FROM ' . ATTACHMENTS_TABLE . '
+ WHERE ' . $this->db->sql_in_set($this->sql_id, $this->ids);
+
+ $sql .= $this->sql_where;
+
+ $this->db->sql_query($sql);
+ $this->num_deleted = $this->db->sql_affectedrows();
+ }
+
+ /**
+ * Delete attachments from filesystem
+ */
+ protected function remove_from_filesystem()
+ {
+ $space_removed = $files_removed = 0;
+
+ foreach ($this->physical as $file_ary)
+ {
+ if ($this->unlink_attachment($file_ary['filename'], 'file', true) && !$file_ary['is_orphan'])
+ {
+ // Only non-orphaned files count to the file size
+ $space_removed += $file_ary['filesize'];
+ $files_removed++;
+ }
+
+ if ($file_ary['thumbnail'])
+ {
+ $this->unlink_attachment($file_ary['filename'], 'thumbnail', true);
+ }
+ }
+
+ /**
+ * Perform additional actions after attachment(s) deletion from the filesystem
+ *
+ * @event core.delete_attachments_from_filesystem_after
+ * @var string mode Variable containing attachments deletion mode, can be: post|message|topic|attach|user
+ * @var mixed ids Array or comma separated list of ids corresponding to the mode
+ * @var bool resync Flag indicating if posts/messages/topics should be synchronized
+ * @var string sql_id The field name to collect/delete data for depending on the mode
+ * @var array post_ids Array with post ids for deleted attachment(s)
+ * @var array topic_ids Array with topic ids for deleted attachment(s)
+ * @var array message_ids Array with private message ids for deleted attachment(s)
+ * @var array physical Array with deleted attachment(s) physical file(s) data
+ * @var int num_deleted The number of deleted attachment(s) from the database
+ * @var int space_removed The size of deleted files(s) from the filesystem
+ * @var int files_removed The number of deleted file(s) from the filesystem
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'mode',
+ 'ids',
+ 'resync',
+ 'sql_id',
+ 'post_ids',
+ 'topic_ids',
+ 'message_ids',
+ 'physical',
+ 'num_deleted',
+ 'space_removed',
+ 'files_removed',
+ );
+ extract($this->dispatcher->trigger_event('core.delete_attachments_from_filesystem_after', compact($vars)));
+
+ if ($space_removed || $files_removed)
+ {
+ $this->config->increment('upload_dir_size', $space_removed * (-1), false);
+ $this->config->increment('num_files', $files_removed * (-1), false);
+ }
+ }
+
+ /**
+ * Delete attachment from filesystem
+ *
+ * @param string $filename Filename of attachment
+ * @param string $mode Delete mode
+ * @param bool $entry_removed Whether entry was removed. Defaults to false
+ * @return bool True if file was removed, false if not
+ */
+ public function unlink_attachment($filename, $mode = 'file', $entry_removed = false)
+ {
+ // Because of copying topics or modifications a physical filename could be assigned more than once. If so, do not remove the file itself.
+ $sql = 'SELECT COUNT(attach_id) AS num_entries
+ FROM ' . ATTACHMENTS_TABLE . "
+ WHERE physical_filename = '" . $this->db->sql_escape(utf8_basename($filename)) . "'";
+ $result = $this->db->sql_query($sql);
+ $num_entries = (int) $this->db->sql_fetchfield('num_entries');
+ $this->db->sql_freeresult($result);
+
+ // Do not remove file if at least one additional entry with the same name exist.
+ if (($entry_removed && $num_entries > 0) || (!$entry_removed && $num_entries > 1))
+ {
+ return false;
+ }
+
+ $filename = ($mode == 'thumbnail') ? 'thumb_' . utf8_basename($filename) : utf8_basename($filename);
+ $filepath = $this->phpbb_root_path . $this->config['upload_path'] . '/' . $filename;
+
+ try
+ {
+ if ($this->filesystem->exists($filepath))
+ {
+ $this->filesystem->remove($this->phpbb_root_path . $this->config['upload_path'] . '/' . $filename);
+ return true;
+ }
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $exception)
+ {
+ // Fail is covered by return statement below
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/attachment/manager.php b/phpBB/phpbb/attachment/manager.php
new file mode 100644
index 0000000000..3c47171b2f
--- /dev/null
+++ b/phpBB/phpbb/attachment/manager.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\attachment;
+
+/**
+ * Attachment manager
+ */
+class manager
+{
+ /** @var delete Attachment delete class */
+ protected $delete;
+
+ /** @var resync Attachment resync class */
+ protected $resync;
+
+ /** @var upload Attachment upload class */
+ protected $upload;
+
+ /**
+ * Constructor for attachment manager
+ *
+ * @param delete $delete Attachment delete class
+ * @param resync $resync Attachment resync class
+ * @param upload $upload Attachment upload class
+ */
+ public function __construct(delete $delete, resync $resync, upload $upload)
+ {
+ $this->delete = $delete;
+ $this->resync = $resync;
+ $this->upload = $upload;
+ }
+
+ /**
+ * Wrapper method for deleting attachments
+ *
+ * @param string $mode can be: post|message|topic|attach|user
+ * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
+ * @param bool $resync set this to false if you are deleting posts or topics
+ *
+ * @return int|bool Number of deleted attachments or false if something
+ * went wrong during attachment deletion
+ */
+ public function delete($mode, $ids, $resync = true)
+ {
+ return $this->delete->delete($mode, $ids, $resync);
+ }
+
+ /**
+ * Wrapper method for deleting attachments from filesystem
+ *
+ * @param string $filename Filename of attachment
+ * @param string $mode Delete mode
+ * @param bool $entry_removed Whether entry was removed. Defaults to false
+ * @return bool True if file was removed, false if not
+ */
+ public function unlink($filename, $mode = 'file', $entry_removed = false)
+ {
+ return $this->delete->unlink_attachment($filename, $mode, $entry_removed);
+ }
+
+ /**
+ * Wrapper method for resyncing specified type
+ *
+ * @param string $type Type of resync
+ * @param array $ids IDs to resync
+ */
+ public function resync($type, $ids)
+ {
+ $this->resync->resync($type, $ids);
+ }
+
+ /**
+ * Wrapper method for uploading attachment
+ *
+ * @param string $form_name The form name of the file upload input
+ * @param int $forum_id The id of the forum
+ * @param bool $local Whether the file is local or not
+ * @param string $local_storage The path to the local file
+ * @param bool $is_message Whether it is a PM or not
+ * @param array $local_filedata An file data object created for the local file
+ *
+ * @return array File data array
+ */
+ public function upload($form_name, $forum_id, $local = false, $local_storage = '', $is_message = false, $local_filedata = [])
+ {
+ return $this->upload->upload($form_name, $forum_id, $local, $local_storage, $is_message, $local_filedata);
+ }
+}
diff --git a/phpBB/phpbb/attachment/resync.php b/phpBB/phpbb/attachment/resync.php
new file mode 100644
index 0000000000..6c2e0a8b0d
--- /dev/null
+++ b/phpBB/phpbb/attachment/resync.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\attachment;
+
+use \phpbb\db\driver\driver_interface;
+
+/**
+ * Attachment resync class
+ */
+class resync
+{
+ /** @var driver_interface */
+ protected $db;
+
+ /** @var string Attachment table SQL ID */
+ private $attach_sql_id;
+
+ /** @var string Resync table SQL ID */
+ private $resync_sql_id;
+
+ /** @var string Resync SQL table */
+ private $resync_table;
+
+ /** @var string SQL where statement */
+ private $sql_where;
+
+ /**
+ * Constructor for attachment resync class
+ *
+ * @param driver_interface $db Database driver
+ */
+ public function __construct(driver_interface $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * Set type constraints for attachment resync
+ *
+ * @param string $type Type of resync; can be: message|post|topic
+ */
+ protected function set_type_constraints($type)
+ {
+ switch ($type)
+ {
+ case 'message':
+ $this->attach_sql_id = 'post_msg_id';
+ $this->sql_where = ' AND in_message = 1
+ AND is_orphan = 0';
+ $this->resync_table = PRIVMSGS_TABLE;
+ $this->resync_sql_id = 'msg_id';
+ break;
+
+ case 'post':
+ $this->attach_sql_id = 'post_msg_id';
+ $this->sql_where = ' AND in_message = 0
+ AND is_orphan = 0';
+ $this->resync_table = POSTS_TABLE;
+ $this->resync_sql_id = 'post_id';
+ break;
+
+ case 'topic':
+ $this->attach_sql_id = 'topic_id';
+ $this->sql_where = ' AND is_orphan = 0';
+ $this->resync_table = TOPICS_TABLE;
+ $this->resync_sql_id = 'topic_id';
+ break;
+ }
+ }
+
+ /**
+ * Resync specified type
+ *
+ * @param string $type Type of resync
+ * @param array $ids IDs to resync
+ */
+ public function resync($type, $ids)
+ {
+ if (empty($type) || !is_array($ids) || !sizeof($ids) || !in_array($type, array('post', 'topic', 'message')))
+ {
+ return;
+ }
+
+ $this->set_type_constraints($type);
+
+ // Just check which elements are still having an assigned attachment
+ // not orphaned by querying the attachments table
+ $sql = 'SELECT ' . $this->attach_sql_id . '
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE ' . $this->db->sql_in_set($this->attach_sql_id, $ids)
+ . $this->sql_where;
+ $result = $this->db->sql_query($sql);
+
+ $remaining_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $remaining_ids[] = $row[$this->attach_sql_id];
+ }
+ $this->db->sql_freeresult($result);
+
+ // Now only unset those ids remaining
+ $ids = array_diff($ids, $remaining_ids);
+
+ if (sizeof($ids))
+ {
+ $sql = 'UPDATE ' . $this->resync_table . '
+ SET ' . $type . '_attachment = 0
+ WHERE ' . $this->db->sql_in_set($this->resync_sql_id, $ids);
+ $this->db->sql_query($sql);
+ }
+ }
+
+}
diff --git a/phpBB/phpbb/attachment/upload.php b/phpBB/phpbb/attachment/upload.php
new file mode 100644
index 0000000000..957558768b
--- /dev/null
+++ b/phpBB/phpbb/attachment/upload.php
@@ -0,0 +1,334 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\attachment;
+
+use phpbb\auth\auth;
+use \phpbb\cache\service;
+use \phpbb\config\config;
+use \phpbb\event\dispatcher;
+use \phpbb\language\language;
+use \phpbb\mimetype\guesser;
+use \phpbb\plupload\plupload;
+use \phpbb\user;
+
+/**
+ * Attachment upload class
+ */
+class upload
+{
+ /** @var auth */
+ protected $auth;
+
+ /** @var service */
+ protected $cache;
+
+ /** @var config */
+ protected $config;
+
+ /** @var \phpbb\files\upload Upload class */
+ protected $files_upload;
+
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ /** @var guesser Mimetype guesser */
+ protected $mimetype_guesser;
+
+ /** @var dispatcher */
+ protected $phpbb_dispatcher;
+
+ /** @var plupload Plupload */
+ protected $plupload;
+
+ /** @var user */
+ protected $user;
+
+ /** @var \phpbb\files\filespec Current filespec instance */
+ private $file;
+
+ /** @var array File data */
+ private $file_data = array(
+ 'error' => array()
+ );
+
+ /** @var array Extensions array */
+ private $extensions;
+
+ /**
+ * Constructor for attachments upload class
+ *
+ * @param auth $auth
+ * @param service $cache
+ * @param config $config
+ * @param \phpbb\files\upload $files_upload
+ * @param language $language
+ * @param guesser $mimetype_guesser
+ * @param dispatcher $phpbb_dispatcher
+ * @param plupload $plupload
+ * @param user $user
+ * @param $phpbb_root_path
+ */
+ public function __construct(auth $auth, service $cache, config $config, \phpbb\files\upload $files_upload, language $language, guesser $mimetype_guesser, dispatcher $phpbb_dispatcher, plupload $plupload, user $user, $phpbb_root_path)
+ {
+ $this->auth = $auth;
+ $this->cache = $cache;
+ $this->config = $config;
+ $this->files_upload = $files_upload;
+ $this->language = $language;
+ $this->mimetype_guesser = $mimetype_guesser;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
+ $this->plupload = $plupload;
+ $this->user = $user;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * Upload Attachment - filedata is generated here
+ * Uses upload class
+ *
+ * @param string $form_name The form name of the file upload input
+ * @param int $forum_id The id of the forum
+ * @param bool $local Whether the file is local or not
+ * @param string $local_storage The path to the local file
+ * @param bool $is_message Whether it is a PM or not
+ * @param array $local_filedata An file data object created for the local file
+ *
+ * @return array File data array
+ */
+ public function upload($form_name, $forum_id, $local = false, $local_storage = '', $is_message = false, $local_filedata = array())
+ {
+ $this->init_files_upload($forum_id, $is_message);
+
+ $this->file_data['post_attach'] = $local || $this->files_upload->is_valid($form_name);
+
+ if (!$this->file_data['post_attach'])
+ {
+ $this->file_data['error'][] = $this->language->lang('NO_UPLOAD_FORM_FOUND');
+ return $this->file_data;
+ }
+
+ $this->file = ($local) ? $this->files_upload->handle_upload('files.types.local', $local_storage, $local_filedata) : $this->files_upload->handle_upload('files.types.form', $form_name);
+
+ if ($this->file->init_error())
+ {
+ $this->file_data['post_attach'] = false;
+ return $this->file_data;
+ }
+
+ // Whether the uploaded file is in the image category
+ $is_image = (isset($this->extensions[$this->file->get('extension')]['display_cat'])) ? $this->extensions[$this->file->get('extension')]['display_cat'] == ATTACHMENT_CATEGORY_IMAGE : false;
+
+ if (!$this->auth->acl_get('a_') && !$this->auth->acl_get('m_', $forum_id))
+ {
+ // Check Image Size, if it is an image
+ if ($is_image)
+ {
+ $this->file->upload->set_allowed_dimensions(0, 0, $this->config['img_max_width'], $this->config['img_max_height']);
+ }
+
+ // Admins and mods are allowed to exceed the allowed filesize
+ if (!empty($this->extensions[$this->file->get('extension')]['max_filesize']))
+ {
+ $allowed_filesize = $this->extensions[$this->file->get('extension')]['max_filesize'];
+ }
+ else
+ {
+ $allowed_filesize = ($is_message) ? $this->config['max_filesize_pm'] : $this->config['max_filesize'];
+ }
+
+ $this->file->upload->set_max_filesize($allowed_filesize);
+ }
+
+ $this->file->clean_filename('unique', $this->user->data['user_id'] . '_');
+
+ // Are we uploading an image *and* this image being within the image category?
+ // Only then perform additional image checks.
+ $this->file->move_file($this->config['upload_path'], false, !$is_image);
+
+ // Do we have to create a thumbnail?
+ $this->file_data['thumbnail'] = ($is_image && $this->config['img_create_thumbnail']) ? 1 : 0;
+
+ // Make sure the image category only holds valid images...
+ $this->check_image($is_image);
+
+ if (sizeof($this->file->error))
+ {
+ $this->file->remove();
+ $this->file_data['error'] = array_merge($this->file_data['error'], $this->file->error);
+ $this->file_data['post_attach'] = false;
+
+ return $this->file_data;
+ }
+
+ $this->fill_file_data();
+
+ $filedata = $this->file_data;
+
+ /**
+ * Event to modify uploaded file before submit to the post
+ *
+ * @event core.modify_uploaded_file
+ * @var array filedata Array containing uploaded file data
+ * @var bool is_image Flag indicating if the file is an image
+ * @since 3.1.0-RC3
+ */
+ $vars = array(
+ 'filedata',
+ 'is_image',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.modify_uploaded_file', compact($vars)));
+ $this->file_data = $filedata;
+ unset($filedata);
+
+ // Check for attachment quota and free space
+ if (!$this->check_attach_quota() || !$this->check_disk_space())
+ {
+ return $this->file_data;
+ }
+
+ // Create Thumbnail
+ $this->create_thumbnail();
+
+ return $this->file_data;
+ }
+
+ /**
+ * Create thumbnail for file if necessary
+ *
+ * @return array Updated $filedata
+ */
+ protected function create_thumbnail()
+ {
+ if ($this->file_data['thumbnail'])
+ {
+ $source = $this->file->get('destination_file');
+ $destination = $this->file->get('destination_path') . '/thumb_' . $this->file->get('realname');
+
+ if (!create_thumbnail($source, $destination, $this->file->get('mimetype')))
+ {
+ $this->file_data['thumbnail'] = 0;
+ }
+ }
+ }
+
+ /**
+ * Init files upload class
+ *
+ * @param int $forum_id Forum ID
+ * @param bool $is_message Whether attachment is inside PM or not
+ */
+ protected function init_files_upload($forum_id, $is_message)
+ {
+ if ($this->config['check_attachment_content'] && isset($this->config['mime_triggers']))
+ {
+ $this->files_upload->set_disallowed_content(explode('|', $this->config['mime_triggers']));
+ }
+ else if (!$this->config['check_attachment_content'])
+ {
+ $this->files_upload->set_disallowed_content(array());
+ }
+
+ $this->extensions = $this->cache->obtain_attach_extensions((($is_message) ? false : (int) $forum_id));
+ $this->files_upload->set_allowed_extensions(array_keys($this->extensions['_allowed_']));
+ }
+
+ /**
+ * Check if uploaded file is really an image
+ *
+ * @param bool $is_image Whether file is image
+ */
+ protected function check_image($is_image)
+ {
+ // Make sure the image category only holds valid images...
+ if ($is_image && !$this->file->is_image())
+ {
+ $this->file->remove();
+
+ if ($this->plupload && $this->plupload->is_active())
+ {
+ $this->plupload->emit_error(104, 'ATTACHED_IMAGE_NOT_IMAGE');
+ }
+
+ // If this error occurs a user tried to exploit an IE Bug by renaming extensions
+ // Since the image category is displaying content inline we need to catch this.
+ $this->file->set_error($this->language->lang('ATTACHED_IMAGE_NOT_IMAGE'));
+ }
+ }
+
+ /**
+ * Check if attachment quota was reached
+ *
+ * @return bool False if attachment quota was reached, true if not
+ */
+ protected function check_attach_quota()
+ {
+ if ($this->config['attachment_quota'])
+ {
+ if (intval($this->config['upload_dir_size']) + $this->file->get('filesize') > $this->config['attachment_quota'])
+ {
+ $this->file_data['error'][] = $this->language->lang('ATTACH_QUOTA_REACHED');
+ $this->file_data['post_attach'] = false;
+
+ $this->file->remove();
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if there is enough free space available on disk
+ *
+ * @return bool True if disk space is available, false if not
+ */
+ protected function check_disk_space()
+ {
+ if ($free_space = @disk_free_space($this->phpbb_root_path . $this->config['upload_path']))
+ {
+ if ($free_space <= $this->file->get('filesize'))
+ {
+ if ($this->auth->acl_get('a_'))
+ {
+ $this->file_data['error'][] = $this->language->lang('ATTACH_DISK_FULL');
+ }
+ else
+ {
+ $this->file_data['error'][] = $this->language->lang('ATTACH_QUOTA_REACHED');
+ }
+ $this->file_data['post_attach'] = false;
+
+ $this->file->remove();
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Fills file data with file information and current time as filetime
+ */
+ protected function fill_file_data()
+ {
+ $this->file_data['filesize'] = $this->file->get('filesize');
+ $this->file_data['mimetype'] = $this->file->get('mimetype');
+ $this->file_data['extension'] = $this->file->get('extension');
+ $this->file_data['physical_filename'] = $this->file->get('realname');
+ $this->file_data['real_filename'] = $this->file->get('uploadname');
+ $this->file_data['filetime'] = time();
+ }
+}
diff --git a/phpBB/phpbb/auth/auth.php b/phpBB/phpbb/auth/auth.php
index 81676e75fc..fc7cc1a0b1 100644
--- a/phpBB/phpbb/auth/auth.php
+++ b/phpBB/phpbb/auth/auth.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\auth;
/**
* Permission/Auth class
-* @package phpBB3
*/
class auth
{
@@ -205,9 +208,12 @@ class auth
/**
* Get forums with the specified permission setting
- * if the option is prefixed with !, then the result becomes negated
*
- * @param bool $clean set to true if only values needs to be returned which are set/unset
+ * @param string $opt The permission name to lookup. If prefixed with !, the result is negated.
+ * @param bool $clean set to true if only values needs to be returned which are set/unset
+ *
+ * @return array Contains the forum ids with the specified permission set to true.
+ This is a nested array: array => forum_id => permission => true
*/
function acl_getf($opt, $clean = false)
{
@@ -921,11 +927,13 @@ class auth
*/
function login($username, $password, $autologin = false, $viewonline = 1, $admin = 0)
{
- global $config, $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;
+ global $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;
+ global $phpbb_dispatcher;
- $method = trim(basename($config['auth_method']));
+ /* @var $provider_collection \phpbb\auth\provider_collection */
+ $provider_collection = $phpbb_container->get('auth.provider_collection');
- $provider = $phpbb_container->get('auth.provider.' . $method);
+ $provider = $provider_collection->get_provider();
if ($provider)
{
$login = $provider->login($username, $password);
@@ -976,6 +984,24 @@ class auth
redirect($url);
}
+ /**
+ * Event is triggered after checking for valid username and password, and before the actual session creation.
+ *
+ * @event core.auth_login_session_create_before
+ * @var array login Variable containing login array
+ * @var bool admin Boolean variable whether user is logging into the ACP
+ * @var string username Username of user to log in
+ * @var bool autologin Boolean variable signaling whether login is triggered via auto login
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'login',
+ 'admin',
+ 'username',
+ 'autologin',
+ );
+ extract($phpbb_dispatcher->trigger_event('core.auth_login_session_create_before', compact($vars)));
+
// If login succeeded, we will log the user in... else we pass the login array through...
if ($login['status'] == LOGIN_SUCCESS)
{
@@ -1040,7 +1066,7 @@ class auth
{
if (strpos($auth_options, '%') !== false)
{
- $sql_opts = "AND $key " . $db->sql_like_expression(str_replace('%', $db->any_char, $auth_options));
+ $sql_opts = "AND $key " . $db->sql_like_expression(str_replace('%', $db->get_any_char(), $auth_options));
}
else
{
@@ -1071,7 +1097,7 @@ class auth
{
if (strpos($option, '%') !== false)
{
- $sql[] = $key . ' ' . $db->sql_like_expression(str_replace('%', $db->any_char, $option));
+ $sql[] = $key . ' ' . $db->sql_like_expression(str_replace('%', $db->get_any_char(), $option));
}
else
{
diff --git a/phpBB/phpbb/auth/provider/apache.php b/phpBB/phpbb/auth/provider/apache.php
index 23cdc89829..aa5bf64335 100644
--- a/phpBB/phpbb/auth/provider/apache.php
+++ b/phpBB/phpbb/auth/provider/apache.php
@@ -1,19 +1,21 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\auth\provider;
/**
- * Apache authentication provider for phpBB3
- *
- * @package auth
- */
+* Apache authentication provider for phpBB3
+*/
class apache extends \phpbb\auth\provider\base
{
/**
@@ -26,15 +28,15 @@ class apache extends \phpbb\auth\provider\base
/**
* Apache Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\config\config $config
- * @param \phpbb\passwords\manager $passwords_manager
- * @param \phpbb\request\request $request
- * @param \phpbb\user $user
- * @param string $phpbb_root_path
- * @param string $php_ext
+ * @param \phpbb\db\driver\driver_interface $db Database object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\passwords\manager $passwords_manager Passwords Manager object
+ * @param \phpbb\request\request $request Request object
+ * @param \phpbb\user $user User object
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $php_ext PHP file extension
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
@@ -135,7 +137,7 @@ class apache extends \phpbb\auth\provider\base
return array(
'status' => LOGIN_SUCCESS_CREATE_PROFILE,
'error_msg' => false,
- 'user_row' => user_row_apache($php_auth_user, $php_auth_pw),
+ 'user_row' => $this->user_row($php_auth_user, $php_auth_pw),
);
}
@@ -183,7 +185,7 @@ class apache extends \phpbb\auth\provider\base
}
// create the user if he does not exist yet
- user_add(user_row_apache($php_auth_user, $php_auth_pw));
+ user_add($this->user_row($php_auth_user, $php_auth_pw));
$sql = 'SELECT *
FROM ' . USERS_TABLE . "
diff --git a/phpBB/phpbb/auth/provider/base.php b/phpBB/phpbb/auth/provider/base.php
index 78a3289356..dea27ccc25 100644
--- a/phpBB/phpbb/auth/provider/base.php
+++ b/phpBB/phpbb/auth/provider/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\auth\provider;
/**
* Base authentication provider class that all other providers should implement
-*
-* @package auth
*/
abstract class base implements \phpbb\auth\provider\provider_interface
{
@@ -59,7 +61,7 @@ abstract class base implements \phpbb\auth\provider\provider_interface
/**
* {@inheritdoc}
*/
- public function get_auth_link_data()
+ public function get_auth_link_data($user_id = 0)
{
return;
}
diff --git a/phpBB/phpbb/auth/provider/db.php b/phpBB/phpbb/auth/provider/db.php
index 6bbbc0be16..1adf85ee05 100644
--- a/phpBB/phpbb/auth/provider/db.php
+++ b/phpBB/phpbb/auth/provider/db.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,10 +15,7 @@ namespace phpbb\auth\provider;
/**
* Database authentication provider for phpBB3
- *
* This is for authentication via the integrated user table
- *
- * @package auth
*/
class db extends \phpbb\auth\provider\base
{
@@ -26,17 +27,25 @@ class db extends \phpbb\auth\provider\base
protected $passwords_manager;
/**
+ * DI container
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $phpbb_container;
+
+ /**
* Database Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\db\driver\driver_interface $db
* @param \phpbb\config\config $config
* @param \phpbb\passwords\manager $passwords_manager
* @param \phpbb\request\request $request
* @param \phpbb\user $user
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container DI container
* @param string $phpbb_root_path
* @param string $php_ext
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request $request, \phpbb\user $user, \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
@@ -45,6 +54,7 @@ class db extends \phpbb\auth\provider\base
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
+ $this->phpbb_container = $phpbb_container;
}
/**
@@ -77,7 +87,7 @@ class db extends \phpbb\auth\provider\base
$username_clean = utf8_clean_string($username);
- $sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts
+ $sql = 'SELECT *
FROM ' . USERS_TABLE . "
WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
$result = $this->db->sql_query($sql);
@@ -113,7 +123,7 @@ class db extends \phpbb\auth\provider\base
'username_clean' => $username_clean,
);
$sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE . $this->db->sql_build_array('INSERT', $attempt_data);
- $result = $this->db->sql_query($sql);
+ $this->db->sql_query($sql);
}
else
{
@@ -145,13 +155,9 @@ class db extends \phpbb\auth\provider\base
// Every auth module is able to define what to do by itself...
if ($show_captcha)
{
- // Visual Confirmation handling
- if (!class_exists('phpbb_captcha_factory', false))
- {
- include ($this->phpbb_root_path . 'includes/captcha/captcha_factory.' . $this->php_ext);
- }
-
- $captcha = \phpbb_captcha_factory::get_instance($this->config['captcha_plugin']);
+ /* @var $captcha_factory \phpbb\captcha\factory */
+ $captcha_factory = $this->phpbb_container->get('captcha.factory');
+ $captcha = $captcha_factory->get_instance($this->config['captcha_plugin']);
$captcha->init(CONFIRM_LOGIN);
$vc_response = $captcha->validate($row);
if ($vc_response)
@@ -169,72 +175,8 @@ class db extends \phpbb\auth\provider\base
}
- // If the password convert flag is set we need to convert it
- if ($row['user_pass_convert'])
- {
- // enable super globals to get literal value
- // this is needed to prevent unicode normalization
- $super_globals_disabled = $this->request->super_globals_disabled();
- if ($super_globals_disabled)
- {
- $this->request->enable_super_globals();
- }
-
- // in phpBB2 passwords were used exactly as they were sent, with addslashes applied
- $password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : '';
- $password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format;
- $password_new_format = $this->request->variable('password', '', true);
-
- if ($super_globals_disabled)
- {
- $this->request->disable_super_globals();
- }
-
- if ($password == $password_new_format)
- {
- if (!function_exists('utf8_to_cp1252'))
- {
- include($this->phpbb_root_path . 'includes/utf/data/recode_basic.' . $this->php_ext);
- }
-
- // cp1252 is phpBB2's default encoding, characters outside ASCII range might work when converted into that encoding
- // plain md5 support left in for conversions from other systems.
- if ((strlen($row['user_password']) == 34 && ($this->passwords_manager->check(md5($password_old_format), $row['user_password']) || $this->passwords_manager->check(md5(utf8_to_cp1252($password_old_format)), $row['user_password'])))
- || (strlen($row['user_password']) == 32 && (md5($password_old_format) == $row['user_password'] || md5(utf8_to_cp1252($password_old_format)) == $row['user_password'])))
- {
- $hash = $this->passwords_manager->hash($password_new_format);
-
- // Update the password in the users table to the new format and remove user_pass_convert flag
- $sql = 'UPDATE ' . USERS_TABLE . '
- SET user_password = \'' . $this->db->sql_escape($hash) . '\',
- user_pass_convert = 0
- WHERE user_id = ' . $row['user_id'];
- $this->db->sql_query($sql);
-
- $row['user_pass_convert'] = 0;
- $row['user_password'] = $hash;
- }
- else
- {
- // Although we weren't able to convert this password we have to
- // increase login attempt count to make sure this cannot be exploited
- $sql = 'UPDATE ' . USERS_TABLE . '
- SET user_login_attempts = user_login_attempts + 1
- WHERE user_id = ' . (int) $row['user_id'] . '
- AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX;
- $this->db->sql_query($sql);
-
- return array(
- 'status' => LOGIN_ERROR_PASSWORD_CONVERT,
- 'error_msg' => 'LOGIN_ERROR_PASSWORD_CONVERT',
- 'user_row' => $row,
- );
- }
- }
- }
-
// Check password ...
- if (!$row['user_pass_convert'] && $this->passwords_manager->check($password, $row['user_password']))
+ if ($this->passwords_manager->check($password, $row['user_password'], $row))
{
// Check for old password hash...
if ($this->passwords_manager->convert_flag || strlen($row['user_password']) == 32)
@@ -243,8 +185,7 @@ class db extends \phpbb\auth\provider\base
// Update the password in the users table to the new format
$sql = 'UPDATE ' . USERS_TABLE . "
- SET user_password = '" . $this->db->sql_escape($hash) . "',
- user_pass_convert = 0
+ SET user_password = '" . $this->db->sql_escape($hash) . "'
WHERE user_id = {$row['user_id']}";
$this->db->sql_query($sql);
@@ -292,7 +233,7 @@ class db extends \phpbb\auth\provider\base
// Give status about wrong password...
return array(
'status' => ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD,
- 'error_msg' => ($show_captcha) ? 'LOGIN_ERROR_ATTEMPTS' : 'LOGIN_ERROR_PASSWORD',
+ 'error_msg' => 'LOGIN_ERROR_PASSWORD',
'user_row' => $row,
);
}
diff --git a/phpBB/phpbb/auth/provider/ldap.php b/phpBB/phpbb/auth/provider/ldap.php
index e92a227e16..c48b771ab0 100644
--- a/phpBB/phpbb/auth/provider/ldap.php
+++ b/phpBB/phpbb/auth/provider/ldap.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,10 +15,7 @@ namespace phpbb\auth\provider;
/**
* Database authentication provider for phpBB3
- *
* This is for authentication via the integrated user table
- *
- * @package auth
*/
class ldap extends \phpbb\auth\provider\base
{
@@ -28,12 +29,12 @@ class ldap extends \phpbb\auth\provider\base
/**
* LDAP Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\config\config $config
- * @param \phpbb\passwords\manager $passwords_manager
- * @param \phpbb\user $user
+ * @param \phpbb\db\driver\driver_interface $db Database object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\passwords\manager $passwords_manager Passwords manager object
+ * @param \phpbb\user $user User object
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\user $user)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\user $user)
{
$this->db = $db;
$this->config = $config;
@@ -288,7 +289,6 @@ class ldap extends \phpbb\auth\provider\base
/**
* {@inheritdoc}
*/
-
public function acp()
{
// These are fields required in the config table
@@ -305,9 +305,9 @@ class ldap extends \phpbb\auth\provider\base
return array(
'TEMPLATE_FILE' => 'auth_provider_ldap.html',
'TEMPLATE_VARS' => array(
- 'AUTH_LDAP_DN' => $new_config['ldap_base_dn'],
+ 'AUTH_LDAP_BASE_DN' => $new_config['ldap_base_dn'],
'AUTH_LDAP_EMAIL' => $new_config['ldap_email'],
- 'AUTH_LDAP_PASSORD' => $new_config['ldap_password'],
+ 'AUTH_LDAP_PASSORD' => $new_config['ldap_password'] !== '' ? '********' : '',
'AUTH_LDAP_PORT' => $new_config['ldap_port'],
'AUTH_LDAP_SERVER' => $new_config['ldap_server'],
'AUTH_LDAP_UID' => $new_config['ldap_uid'],
diff --git a/phpBB/phpbb/auth/provider/oauth/oauth.php b/phpBB/phpbb/auth/provider/oauth/oauth.php
index 0128c89248..bfeac2dd32 100644
--- a/phpBB/phpbb/auth/provider/oauth/oauth.php
+++ b/phpBB/phpbb/auth/provider/oauth/oauth.php
@@ -1,28 +1,29 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\auth\provider\oauth;
use OAuth\Common\Consumer\Credentials;
-use OAuth\Common\Http\Uri\Uri;
/**
* OAuth authentication provider for phpBB3
-*
-* @package auth
*/
class oauth extends \phpbb\auth\provider\base
{
/**
* Database driver
*
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -62,6 +63,13 @@ class oauth extends \phpbb\auth\provider\base
protected $auth_provider_oauth_token_storage_table;
/**
+ * OAuth state table
+ *
+ * @var string
+ */
+ protected $auth_provider_oauth_state_table;
+
+ /**
* OAuth account association table
*
* @var string
@@ -90,6 +98,13 @@ class oauth extends \phpbb\auth\provider\base
protected $current_uri;
/**
+ * DI container
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $phpbb_container;
+
+ /**
* phpBB root path
*
* @var string
@@ -97,7 +112,7 @@ class oauth extends \phpbb\auth\provider\base
protected $phpbb_root_path;
/**
- * PHP extenstion
+ * PHP file extension
*
* @var string
*/
@@ -106,19 +121,21 @@ class oauth extends \phpbb\auth\provider\base
/**
* OAuth Authentication Constructor
*
- * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\db\driver\driver_interface $db
* @param \phpbb\config\config $config
* @param \phpbb\passwords\manager $passwords_manager
* @param \phpbb\request\request_interface $request
* @param \phpbb\user $user
* @param string $auth_provider_oauth_token_storage_table
+ * @param string $auth_provider_oauth_state_table
* @param string $auth_provider_oauth_token_account_assoc
* @param \phpbb\di\service_collection $service_providers Contains \phpbb\auth\provider\oauth\service_interface
* @param string $users_table
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container DI container
* @param string $phpbb_root_path
* @param string $php_ext
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_state_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container, $phpbb_root_path, $php_ext)
{
$this->db = $db;
$this->config = $config;
@@ -126,9 +143,11 @@ class oauth extends \phpbb\auth\provider\base
$this->request = $request;
$this->user = $user;
$this->auth_provider_oauth_token_storage_table = $auth_provider_oauth_token_storage_table;
+ $this->auth_provider_oauth_state_table = $auth_provider_oauth_state_table;
$this->auth_provider_oauth_token_account_assoc = $auth_provider_oauth_token_account_assoc;
$this->service_providers = $service_providers;
$this->users_table = $users_table;
+ $this->phpbb_container = $phpbb_container;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
}
@@ -159,7 +178,7 @@ class oauth extends \phpbb\auth\provider\base
// Temporary workaround for only having one authentication provider available
if (!$this->request->is_set('oauth_service'))
{
- $provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->passwords_manager, $this->request, $this->user, $this->phpbb_root_path, $this->php_ext);
+ $provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->passwords_manager, $this->request, $this->user, $this->phpbb_container, $this->phpbb_root_path, $this->php_ext);
return $provider->login($username, $password);
}
@@ -178,7 +197,7 @@ class oauth extends \phpbb\auth\provider\base
// Get the service credentials for the given service
$service_credentials = $this->service_providers[$service_name]->get_service_credentials();
- $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table);
+ $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
$query = 'mode=login&login=external&oauth_service=' . $service_name_original;
$service = $this->get_service($service_name_original, $storage, $service_credentials, $query, $this->service_providers[$service_name]->get_auth_scope());
@@ -213,7 +232,7 @@ class oauth extends \phpbb\auth\provider\base
}
// Retrieve the user's account
- $sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts
+ $sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type, user_login_attempts
FROM ' . $this->users_table . '
WHERE user_id = ' . (int) $row['user_id'];
$result = $this->db->sql_query($sql);
@@ -222,7 +241,7 @@ class oauth extends \phpbb\auth\provider\base
if (!$row)
{
- throw new Exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY');
+ throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY');
}
// Update token storage to store the user_id
@@ -276,9 +295,10 @@ class oauth extends \phpbb\auth\provider\base
* @param array $service_credentials {@see \phpbb\auth\provider\oauth\oauth::get_service_credentials}
* @param string $query The query string of the
* current_uri used in redirection
- * @param array $scope The scope of the request against
+ * @param array $scopes The scope of the request against
* the api.
* @return \OAuth\Common\Service\ServiceInterface
+ * @throws \Exception
*/
protected function get_service($service_name, \phpbb\auth\provider\oauth\token_storage $storage, array $service_credentials, $query, array $scopes = array())
{
@@ -296,7 +316,7 @@ class oauth extends \phpbb\auth\provider\base
if (!$service)
{
- throw new Exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');
+ throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');
}
return $service;
@@ -445,7 +465,7 @@ class oauth extends \phpbb\auth\provider\base
*/
protected function link_account_login_link(array $link_data, $service_name)
{
- $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table);
+ $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
// Check for an access token, they should have one
if (!$storage->has_access_token_by_session($service_name))
@@ -488,7 +508,7 @@ class oauth extends \phpbb\auth\provider\base
*/
protected function link_account_auth_link(array $link_data, $service_name)
{
- $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table);
+ $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
$query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . strtolower($link_data['oauth_service']);
$service_credentials = $this->service_providers[$service_name]->get_service_credentials();
$scopes = $this->service_providers[$service_name]->get_auth_scope();
@@ -533,7 +553,7 @@ class oauth extends \phpbb\auth\provider\base
public function logout($data, $new_session)
{
// Clear all tokens belonging to the user
- $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table);
+ $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
$storage->clearAllTokens();
return;
@@ -542,13 +562,13 @@ class oauth extends \phpbb\auth\provider\base
/**
* {@inheritdoc}
*/
- public function get_auth_link_data()
+ public function get_auth_link_data($user_id = 0)
{
$block_vars = array();
// Get all external accounts tied to the current user
$data = array(
- 'user_id' => (int) $this->user->data['user_id'],
+ 'user_id' => ($user_id <= 0) ? (int) $this->user->data['user_id'] : (int) $user_id,
);
$sql = 'SELECT oauth_provider_id, provider FROM ' . $this->auth_provider_oauth_token_account_assoc . '
WHERE ' . $this->db->sql_build_array('SELECT', $data);
@@ -605,17 +625,18 @@ class oauth extends \phpbb\auth\provider\base
return 'LOGIN_LINK_MISSING_DATA';
}
+ // Remove user specified in $link_data if possible
+ $user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id'];
+
// Remove the link
$sql = 'DELETE FROM ' . $this->auth_provider_oauth_token_account_assoc . "
WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "'
- AND user_id = " . (int) $this->user->data['user_id'];
+ AND user_id = " . (int) $user_id;
$this->db->sql_query($sql);
// Clear all tokens belonging to the user on this servce
$service_name = 'auth.provider.oauth.service.' . strtolower($link_data['oauth_service']);
- $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table);
+ $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
$storage->clearToken($service_name);
-
- return;
}
}
diff --git a/phpBB/phpbb/auth/provider/oauth/service/base.php b/phpBB/phpbb/auth/provider/oauth/service/base.php
index 7a144d2f51..6adf64aa30 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/base.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\auth\provider\oauth\service;
/**
* Base OAuth abstract class that all OAuth services should implement
-*
-* @package auth
*/
abstract class base implements \phpbb\auth\provider\oauth\service\service_interface
{
diff --git a/phpBB/phpbb/auth/provider/oauth/service/bitly.php b/phpBB/phpbb/auth/provider/oauth/service/bitly.php
index b4050033a6..25e731a02c 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/bitly.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/bitly.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\auth\provider\oauth\service;
/**
* Bitly OAuth service
-*
-* @package auth
*/
class bitly extends \phpbb\auth\provider\oauth\service\base
{
diff --git a/phpBB/phpbb/auth/provider/oauth/service/exception.php b/phpBB/phpbb/auth/provider/oauth/service/exception.php
index 3bc93be01e..d3e95bef0d 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/exception.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/exception.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\auth\provider\oauth\service;
/**
* OAuth service exception class
-*
-* @package auth
*/
class exception extends \RuntimeException
{
diff --git a/phpBB/phpbb/auth/provider/oauth/service/facebook.php b/phpBB/phpbb/auth/provider/oauth/service/facebook.php
index 2698be8b18..bb98835e07 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/facebook.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/facebook.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,30 +15,28 @@ namespace phpbb\auth\provider\oauth\service;
/**
* Facebook OAuth service
-*
-* @package auth
*/
class facebook extends base
{
/**
* phpBB config
*
- * @var phpbb\config\config
+ * @var \phpbb\config\config
*/
protected $config;
/**
* phpBB request
*
- * @var phpbb\request\request_interface
+ * @var \phpbb\request\request_interface
*/
protected $request;
/**
* Constructor
*
- * @param phpbb\config\config $config
- * @param phpbb\request\request_interface $request
+ * @param \phpbb\config\config $config
+ * @param \phpbb\request\request_interface $request
*/
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
{
diff --git a/phpBB/phpbb/auth/provider/oauth/service/google.php b/phpBB/phpbb/auth/provider/oauth/service/google.php
index 08cb025c2d..cb9f83a94f 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/google.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/google.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,30 +15,28 @@ namespace phpbb\auth\provider\oauth\service;
/**
* Google OAuth service
-*
-* @package auth
*/
class google extends base
{
/**
* phpBB config
*
- * @var phpbb\config\config
+ * @var \phpbb\config\config
*/
protected $config;
/**
* phpBB request
*
- * @var phpbb\request\request_interface
+ * @var \phpbb\request\request_interface
*/
protected $request;
/**
* Constructor
*
- * @param phpbb\config\config $config
- * @param phpbb\request\request_interface $request
+ * @param \phpbb\config\config $config
+ * @param \phpbb\request\request_interface $request
*/
public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
{
diff --git a/phpBB/phpbb/auth/provider/oauth/service/service_interface.php b/phpBB/phpbb/auth/provider/oauth/service/service_interface.php
index eee3a51cac..e84eb247b6 100644
--- a/phpBB/phpbb/auth/provider/oauth/service/service_interface.php
+++ b/phpBB/phpbb/auth/provider/oauth/service/service_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\auth\provider\oauth\service;
/**
* OAuth service interface
-*
-* @package auth
*/
interface service_interface
{
@@ -65,7 +67,7 @@ interface service_interface
/**
* Sets the external library service provider
*
- * @param \OAuth\Common\Service\ServiceInterface $service
+ * @param \OAuth\Common\Service\ServiceInterface $service_provider
*/
public function set_external_service_provider(\OAuth\Common\Service\ServiceInterface $service_provider);
}
diff --git a/phpBB/phpbb/auth/provider/oauth/token_storage.php b/phpBB/phpbb/auth/provider/oauth/token_storage.php
index 43574288dc..e922342ef6 100644
--- a/phpBB/phpbb/auth/provider/oauth/token_storage.php
+++ b/phpBB/phpbb/auth/provider/oauth/token_storage.php
@@ -1,32 +1,33 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\auth\provider\oauth;
-
use OAuth\OAuth1\Token\StdOAuth1Token;
use OAuth\Common\Token\TokenInterface;
use OAuth\Common\Storage\TokenStorageInterface;
-use OAuth\Common\Storage\Exception\StorageException;
use OAuth\Common\Storage\Exception\TokenNotFoundException;
+use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException;
/**
* OAuth storage wrapper for phpbb's cache
-*
-* @package auth
*/
class token_storage implements TokenStorageInterface
{
/**
* Cache driver.
*
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -42,7 +43,14 @@ class token_storage implements TokenStorageInterface
*
* @var string
*/
- protected $auth_provider_oauth_table;
+ protected $oauth_token_table;
+
+ /**
+ * OAuth state table
+ *
+ * @var string
+ */
+ protected $oauth_state_table;
/**
* @var object|TokenInterface
@@ -50,17 +58,24 @@ class token_storage implements TokenStorageInterface
protected $cachedToken;
/**
+ * @var string
+ */
+ protected $cachedState;
+
+ /**
* Creates token storage for phpBB.
*
- * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\db\driver\driver_interface $db
* @param \phpbb\user $user
- * @param string $auth_provider_oauth_table
+ * @param string $oauth_token_table
+ * @param string $oauth_state_table
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\user $user, $auth_provider_oauth_table)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $oauth_token_table, $oauth_state_table)
{
$this->db = $db;
$this->user = $user;
- $this->auth_provider_oauth_table = $auth_provider_oauth_table;
+ $this->oauth_token_table = $oauth_token_table;
+ $this->oauth_state_table = $oauth_state_table;
}
/**
@@ -104,9 +119,11 @@ class token_storage implements TokenStorageInterface
'session_id' => $this->user->data['session_id'],
);
- $sql = 'INSERT INTO ' . $this->auth_provider_oauth_table . '
+ $sql = 'INSERT INTO ' . $this->oauth_token_table . '
' . $this->db->sql_build_array('INSERT', $data);
$this->db->sql_query($sql);
+
+ return $this;
}
/**
@@ -116,7 +133,8 @@ class token_storage implements TokenStorageInterface
{
$service = $this->get_service_name_for_db($service);
- if ($this->cachedToken) {
+ if ($this->cachedToken)
+ {
return true;
}
@@ -142,7 +160,7 @@ class token_storage implements TokenStorageInterface
$this->cachedToken = null;
- $sql = 'DELETE FROM ' . $this->auth_provider_oauth_table . '
+ $sql = 'DELETE FROM ' . $this->oauth_token_table . '
WHERE user_id = ' . (int) $this->user->data['user_id'] . "
AND provider = '" . $this->db->sql_escape($service) . "'";
@@ -152,6 +170,8 @@ class token_storage implements TokenStorageInterface
}
$this->db->sql_query($sql);
+
+ return $this;
}
/**
@@ -161,7 +181,7 @@ class token_storage implements TokenStorageInterface
{
$this->cachedToken = null;
- $sql = 'DELETE FROM ' . $this->auth_provider_oauth_table . '
+ $sql = 'DELETE FROM ' . $this->oauth_token_table . '
WHERE user_id = ' . (int) $this->user->data['user_id'];
if ((int) $this->user->data['user_id'] === ANONYMOUS)
@@ -170,6 +190,124 @@ class token_storage implements TokenStorageInterface
}
$this->db->sql_query($sql);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function storeAuthorizationState($service, $state)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ $this->cachedState = $state;
+
+ $data = array(
+ 'user_id' => (int) $this->user->data['user_id'],
+ 'provider' => $service,
+ 'oauth_state' => $state,
+ 'session_id' => $this->user->data['session_id'],
+ );
+
+ $sql = 'INSERT INTO ' . $this->oauth_state_table . '
+ ' . $this->db->sql_build_array('INSERT', $data);
+ $this->db->sql_query($sql);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasAuthorizationState($service)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ if ($this->cachedState)
+ {
+ return true;
+ }
+
+ $data = array(
+ 'user_id' => (int) $this->user->data['user_id'],
+ 'provider' => $service,
+ );
+
+ if ((int) $this->user->data['user_id'] === ANONYMOUS)
+ {
+ $data['session_id'] = $this->user->data['session_id'];
+ }
+
+ return (bool) $this->get_state_row($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function retrieveAuthorizationState($service)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ if ($this->cachedState)
+ {
+ return $this->cachedState;
+ }
+
+ $data = array(
+ 'user_id' => (int) $this->user->data['user_id'],
+ 'provider' => $service,
+ );
+
+ if ((int) $this->user->data['user_id'] === ANONYMOUS)
+ {
+ $data['session_id'] = $this->user->data['session_id'];
+ }
+
+ return $this->get_state_row($data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clearAuthorizationState($service)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ $this->cachedState = null;
+
+ $sql = 'DELETE FROM ' . $this->oauth_state_table . '
+ WHERE user_id = ' . (int) $this->user->data['user_id'] . "
+ AND provider = '" . $this->db->sql_escape($service) . "'";
+
+ if ((int) $this->user->data['user_id'] === ANONYMOUS)
+ {
+ $sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
+ }
+
+ $this->db->sql_query($sql);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clearAllAuthorizationStates()
+ {
+ $this->cachedState = null;
+
+ $sql = 'DELETE FROM ' . $this->oauth_state_table . '
+ WHERE user_id = ' . (int) $this->user->data['user_id'];
+
+ if ((int) $this->user->data['user_id'] === ANONYMOUS)
+ {
+ $sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'";
+ }
+
+ $this->db->sql_query($sql);
+
+ return $this;
}
/**
@@ -184,7 +322,7 @@ class token_storage implements TokenStorageInterface
return;
}
- $sql = 'UPDATE ' . $this->auth_provider_oauth_table . '
+ $sql = 'UPDATE ' . $this->oauth_token_table . '
SET ' . $this->db->sql_build_array('UPDATE', array(
'user_id' => (int) $user_id
)) . '
@@ -196,6 +334,7 @@ class token_storage implements TokenStorageInterface
/**
* Checks to see if an access token exists solely by the session_id of the user
*
+ * @param string $service The name of the OAuth service
* @return bool true if they have token, false if they don't
*/
public function has_access_token_by_session($service)
@@ -216,6 +355,29 @@ class token_storage implements TokenStorageInterface
}
/**
+ * Checks to see if a state exists solely by the session_id of the user
+ *
+ * @param string $service The name of the OAuth service
+ * @return bool true if they have state, false if they don't
+ */
+ public function has_state_by_session($service)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ if ($this->cachedState)
+ {
+ return true;
+ }
+
+ $data = array(
+ 'session_id' => $this->user->data['session_id'],
+ 'provider' => $service,
+ );
+
+ return (bool) $this->get_state_row($data);
+ }
+
+ /**
* A helper function that performs the query for has access token functions
*
* @param array $data
@@ -230,7 +392,8 @@ class token_storage implements TokenStorageInterface
{
$service = $this->get_service_name_for_db($service);
- if ($this->cachedToken instanceof TokenInterface) {
+ if ($this->cachedToken instanceof TokenInterface)
+ {
return $this->cachedToken;
}
@@ -242,12 +405,30 @@ class token_storage implements TokenStorageInterface
return $this->_retrieve_access_token($data);
}
+ public function retrieve_state_by_session($service)
+ {
+ $service = $this->get_service_name_for_db($service);
+
+ if ($this->cachedState)
+ {
+ return $this->cachedState;
+ }
+
+ $data = array(
+ 'session_id' => $this->user->data['session_id'],
+ 'provider' => $service,
+ );
+
+ return $this->_retrieve_state($data);
+ }
+
/**
* A helper function that performs the query for retrieve access token functions
* Also checks if the token is a valid token
*
* @param array $data
* @return mixed
+ * @throws \OAuth\Common\Storage\Exception\TokenNotFoundException
*/
protected function _retrieve_access_token($data)
{
@@ -263,7 +444,7 @@ class token_storage implements TokenStorageInterface
// Ensure that the token was serialized/unserialized correctly
if (!($token instanceof TokenInterface))
{
- $this->clearToken();
+ $this->clearToken($data['provider']);
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED');
}
@@ -272,6 +453,26 @@ class token_storage implements TokenStorageInterface
}
/**
+ * A helper function that performs the query for retrieve state functions
+ *
+ * @param array $data
+ * @return mixed
+ * @throws \OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException
+ */
+ protected function _retrieve_state($data)
+ {
+ $row = $this->get_state_row($data);
+
+ if (!$row)
+ {
+ throw new AuthorizationStateNotFoundException();
+ }
+
+ $this->cachedState = $row['oauth_state'];
+ return $this->cachedState;
+ }
+
+ /**
* A helper function that performs the query for retrieving an access token
*
* @param array $data
@@ -279,7 +480,24 @@ class token_storage implements TokenStorageInterface
*/
protected function get_access_token_row($data)
{
- $sql = 'SELECT oauth_token FROM ' . $this->auth_provider_oauth_table . '
+ $sql = 'SELECT oauth_token FROM ' . $this->oauth_token_table . '
+ WHERE ' . $this->db->sql_build_array('SELECT', $data);
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ return $row;
+ }
+
+ /**
+ * A helper function that performs the query for retrieving a state
+ *
+ * @param array $data
+ * @return mixed
+ */
+ protected function get_state_row($data)
+ {
+ $sql = 'SELECT oauth_state FROM ' . $this->oauth_state_table . '
WHERE ' . $this->db->sql_build_array('SELECT', $data);
$result = $this->db->sql_query($sql);
$row = $this->db->sql_fetchrow($result);
diff --git a/phpBB/phpbb/auth/provider/provider_interface.php b/phpBB/phpbb/auth/provider/provider_interface.php
index 946731f52d..35e0f559a1 100644
--- a/phpBB/phpbb/auth/provider/provider_interface.php
+++ b/phpBB/phpbb/auth/provider/provider_interface.php
@@ -1,19 +1,21 @@
<?php
/**
*
-* @package auth
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\auth\provider;
/**
- * The interface authentication provider classes have to implement.
- *
- * @package auth
- */
+* The interface authentication provider classes have to implement.
+*/
interface provider_interface
{
/**
@@ -146,7 +148,7 @@ interface provider_interface
* user_id of an account needed to successfully link an external account to
* a forum account.
*
- * @param array $link_data Any data needed to link a phpBB account to
+ * @param array $login_link_data Any data needed to link a phpBB account to
* an external account.
* @return string|null Returns a string with a language constant if there
* is data missing or null if there is no error.
@@ -164,6 +166,10 @@ interface provider_interface
/**
* Returns an array of data necessary to build the ucp_auth_link page
*
+ * @param int $user_id User ID for whom the data should be retrieved.
+ * defaults to 0, which is not a valid ID. The method
+ * should fall back to the current user's ID in this
+ * case.
* @return array|null If this function is not implemented on an auth
* provider then it returns null. If it is implemented
* it will return an array of up to four elements of
@@ -179,7 +185,7 @@ interface provider_interface
* 'VARS' => array(...),
* )
*/
- public function get_auth_link_data();
+ public function get_auth_link_data($user_id = 0);
/**
* Unlinks an external account from a phpBB account.
diff --git a/phpBB/phpbb/auth/provider_collection.php b/phpBB/phpbb/auth/provider_collection.php
new file mode 100644
index 0000000000..8e7e9e2cc1
--- /dev/null
+++ b/phpBB/phpbb/auth/provider_collection.php
@@ -0,0 +1,67 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\auth;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+* Collection of auth providers to be configured at container compile time.
+*/
+class provider_collection extends \phpbb\di\service_collection
+{
+ /** @var \phpbb\config\config phpBB Config */
+ protected $config;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Container object
+ * @param \phpbb\config\config $config phpBB config
+ */
+ public function __construct(ContainerInterface $container, \phpbb\config\config $config)
+ {
+ $this->container = $container;
+ $this->config = $config;
+ }
+
+ /**
+ * Get an auth provider.
+ *
+ * @param string $provider_name The name of the auth provider
+ * @return object Default auth provider selected in config if it
+ * does exist. Otherwise the standard db auth
+ * provider.
+ * @throws \RuntimeException If neither the auth provider that
+ * is specified by the phpBB config nor the db
+ * auth provider exist. The db auth provider
+ * should always exist in a phpBB installation.
+ */
+ public function get_provider($provider_name = '')
+ {
+ $provider_name = ($provider_name !== '') ? $provider_name : basename(trim($this->config['auth_method']));
+ if ($this->offsetExists('auth.provider.' . $provider_name))
+ {
+ return $this->offsetGet('auth.provider.' . $provider_name);
+ }
+ // Revert to db auth provider if selected method does not exist
+ else if ($this->offsetExists('auth.provider.db'))
+ {
+ return $this->offsetGet('auth.provider.db');
+ }
+ else
+ {
+ throw new \RuntimeException(sprintf('The authentication provider for the authentication method "%1$s" does not exist. It was not possible to recover from this by reverting to the database authentication provider.', $this->config['auth_method']));
+ }
+ }
+}
diff --git a/phpBB/phpbb/avatar/driver/driver.php b/phpBB/phpbb/avatar/driver/driver.php
index dd55f09119..45681f3e59 100644
--- a/phpBB/phpbb/avatar/driver/driver.php
+++ b/phpBB/phpbb/avatar/driver/driver.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\avatar\driver;
/**
* Base class for avatar drivers
-* @package phpBB3
*/
abstract class driver implements \phpbb\avatar\driver\driver_interface
{
@@ -27,6 +30,9 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
*/
protected $config;
+ /** @var \FastImageSize\FastImageSize */
+ protected $imagesize;
+
/**
* Current $phpbb_root_path
* @var string
@@ -70,15 +76,16 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
* Construct a driver object
*
* @param \phpbb\config\config $config phpBB configuration
- * @param \phpbb\request\request $request Request object
+ * @param \FastImageSize\FastImageSize $imagesize FastImageSize class
* @param string $phpbb_root_path Path to the phpBB root
* @param string $php_ext PHP file extension
- * @param \phpbb_path_helper $path_helper phpBB path helper
+ * @param \phpbb\path_helper $path_helper phpBB path helper
* @param \phpbb\cache\driver\driver_interface $cache Cache driver
*/
- public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\path_helper $path_helper, \phpbb\cache\driver\driver_interface $cache = null)
+ public function __construct(\phpbb\config\config $config, \FastImageSize\FastImageSize $imagesize, $phpbb_root_path, $php_ext, \phpbb\path_helper $path_helper, \phpbb\cache\driver\driver_interface $cache = null)
{
$this->config = $config;
+ $this->imagesize = $imagesize;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->path_helper = $path_helper;
@@ -86,7 +93,7 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_custom_html($user, $row, $alt = '')
{
@@ -94,7 +101,7 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form_acp($user)
{
@@ -102,7 +109,7 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function delete($row)
{
@@ -110,7 +117,7 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_name()
{
@@ -118,6 +125,22 @@ abstract class driver implements \phpbb\avatar\driver\driver_interface
}
/**
+ * {@inheritdoc}
+ */
+ public function get_config_name()
+ {
+ return preg_replace('#^phpbb\\\\avatar\\\\driver\\\\#', '', get_class($this));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_acp_template_name()
+ {
+ return 'acp_avatar_options_' . $this->get_config_name() . '.html';
+ }
+
+ /**
* Sets the name of the driver.
*
* @param string $name Driver name
diff --git a/phpBB/phpbb/avatar/driver/driver_interface.php b/phpBB/phpbb/avatar/driver/driver_interface.php
index 7f049469a2..7d6c2cff8a 100644
--- a/phpBB/phpbb/avatar/driver/driver_interface.php
+++ b/phpBB/phpbb/avatar/driver/driver_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\avatar\driver;
/**
* Interface for avatar drivers
-* @package phpBB3
*/
interface driver_interface
{
@@ -23,6 +26,13 @@ interface driver_interface
public function get_name();
/**
+ * Returns the config name of the driver. To be used in accessing the CONFIG variables.
+ *
+ * @return string Config name of driver.
+ */
+ public function get_config_name();
+
+ /**
* Get the avatar url and dimensions
*
* @param array $row User data or group data that has been cleaned with
@@ -107,4 +117,11 @@ interface driver_interface
* @return string Avatar driver's template name
*/
public function get_template_name();
+
+ /**
+ * Get the avatar driver's template name (ACP)
+ *
+ * @return string Avatar driver's template name
+ */
+ public function get_acp_template_name();
}
diff --git a/phpBB/phpbb/avatar/driver/gravatar.php b/phpBB/phpbb/avatar/driver/gravatar.php
index 9f14b7f468..b8cf84424a 100644
--- a/phpBB/phpbb/avatar/driver/gravatar.php
+++ b/phpBB/phpbb/avatar/driver/gravatar.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\avatar\driver;
/**
* Handles avatars hosted at gravatar.com
-* @package phpBB3
*/
class gravatar extends \phpbb\avatar\driver\driver
{
@@ -21,7 +24,7 @@ class gravatar extends \phpbb\avatar\driver\driver
const GRAVATAR_URL = '//secure.gravatar.com/avatar/';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_data($row)
{
@@ -33,7 +36,7 @@ class gravatar extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_custom_html($user, $row, $alt = '')
{
@@ -44,7 +47,7 @@ class gravatar extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form($request, $template, $user, $row, &$error)
{
@@ -58,7 +61,7 @@ class gravatar extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function process_form($request, $template, $user, $row, &$error)
{
@@ -66,6 +69,11 @@ class gravatar extends \phpbb\avatar\driver\driver
$row['avatar_width'] = $request->variable('avatar_gravatar_width', 0);
$row['avatar_height'] = $request->variable('avatar_gravatar_height', 0);
+ if (empty($row['avatar']))
+ {
+ return false;
+ }
+
if (!function_exists('validate_data'))
{
require($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
@@ -78,7 +86,8 @@ class gravatar extends \phpbb\avatar\driver\driver
array(
'email' => array(
array('string', false, 6, 60),
- array('email'))
+ array('email'),
+ ),
)
);
@@ -89,8 +98,8 @@ class gravatar extends \phpbb\avatar\driver\driver
return false;
}
- // Make sure getimagesize works...
- if (function_exists('getimagesize') && ($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0))
+ // Get image dimensions if they are not set
+ if ($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0)
{
/**
* default to the minimum of the maximum allowed avatar size if the size
@@ -99,20 +108,20 @@ class gravatar extends \phpbb\avatar\driver\driver
$row['avatar_width'] = $row['avatar_height'] = min($this->config['avatar_max_width'], $this->config['avatar_max_height']);
$url = $this->get_gravatar_url($row);
- if (($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0) && (($image_data = getimagesize($url)) === false))
+ if (($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0) && (($image_data = $this->imagesize->getImageSize($url)) === false))
{
$error[] = 'UNABLE_GET_IMAGE_SIZE';
return false;
}
- if (!empty($image_data) && ($image_data[0] <= 0 || $image_data[1] <= 0))
+ if (!empty($image_data) && ($image_data['width'] <= 0 || $image_data['width'] <= 0))
{
$error[] = 'AVATAR_NO_SIZE';
return false;
}
- $row['avatar_width'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_width'] : $image_data[0];
- $row['avatar_height'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_height'] : $image_data[1];
+ $row['avatar_width'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_width'] : $image_data['width'];
+ $row['avatar_height'] = ($row['avatar_width'] && $row['avatar_height']) ? $row['avatar_height'] : $image_data['height'];
}
if ($row['avatar_width'] <= 0 || $row['avatar_height'] <= 0)
@@ -147,7 +156,7 @@ class gravatar extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_template_name()
{
@@ -157,10 +166,14 @@ class gravatar extends \phpbb\avatar\driver\driver
/**
* Build gravatar URL for output on page
*
+ * @param array $row User data or group data that has been cleaned with
+ * \phpbb\avatar\manager::clean_row
* @return string Gravatar URL
*/
protected function get_gravatar_url($row)
{
+ global $phpbb_dispatcher;
+
$url = self::GRAVATAR_URL;
$url .= md5(strtolower(trim($row['avatar'])));
@@ -169,6 +182,17 @@ class gravatar extends \phpbb\avatar\driver\driver
$url .= '?s=' . max($row['avatar_width'], $row['avatar_height']);
}
+ /**
+ * Modify gravatar url
+ *
+ * @event core.get_gravatar_url_after
+ * @var string row User data or group data
+ * @var string url Gravatar URL
+ * @since 3.1.7-RC1
+ */
+ $vars = array('row', 'url');
+ extract($phpbb_dispatcher->trigger_event('core.get_gravatar_url_after', compact($vars)));
+
return $url;
}
}
diff --git a/phpBB/phpbb/avatar/driver/local.php b/phpBB/phpbb/avatar/driver/local.php
index 611a44cb3d..f5547c4bc6 100644
--- a/phpBB/phpbb/avatar/driver/local.php
+++ b/phpBB/phpbb/avatar/driver/local.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,29 +15,30 @@ namespace phpbb\avatar\driver;
/**
* Handles avatars selected from the board gallery
-* @package phpBB3
*/
class local extends \phpbb\avatar\driver\driver
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_data($row)
{
+ $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path();
+
return array(
- 'src' => $this->path_helper->get_web_root_path() . $this->config['avatar_gallery_path'] . '/' . $row['avatar'],
+ 'src' => $root_path . $this->config['avatar_gallery_path'] . '/' . $row['avatar'],
'width' => $row['avatar_width'],
'height' => $row['avatar_height'],
);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form($request, $template, $user, $row, &$error)
{
$avatar_list = $this->get_avatar_list($user);
- $category = $request->variable('avatar_local_cat', '');
+ $category = $request->variable('avatar_local_cat', key($avatar_list));
foreach ($avatar_list as $cat => $null)
{
@@ -79,11 +84,13 @@ class local extends \phpbb\avatar\driver\driver
'AVATAR_IMAGE' => $this->phpbb_root_path . $this->config['avatar_gallery_path'] . '/' . $img['file'],
'AVATAR_NAME' => $img['name'],
'AVATAR_FILE' => $img['filename'],
+ 'CHECKED' => $img['file'] === $row['avatar'],
));
$template->assign_block_vars('avatar_local_row.avatar_local_option', array(
'AVATAR_FILE' => $img['filename'],
- 'S_OPTIONS_AVATAR' => $img['filename']
+ 'S_OPTIONS_AVATAR' => $img['filename'],
+ 'CHECKED' => $img['file'] === $row['avatar'],
));
$col_count = ($col_count + 1) % $table_cols;
@@ -96,7 +103,7 @@ class local extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form_acp($user)
{
@@ -106,7 +113,7 @@ class local extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function process_form($request, $template, $user, $row, &$error)
{
@@ -117,7 +124,6 @@ class local extends \phpbb\avatar\driver\driver
if (empty($category) || empty($file))
{
- $error[] = 'NO_AVATAR_SELECTED';
return false;
}
@@ -128,14 +134,14 @@ class local extends \phpbb\avatar\driver\driver
}
return array(
- 'avatar' => ($category != $user->lang['MAIN']) ? $category . '/' . $file : $file,
+ 'avatar' => ($category != $user->lang['NO_AVATAR_CATEGORY']) ? $category . '/' . $file : $file,
'avatar_width' => $avatar_list[$category][urldecode($file)]['width'],
'avatar_height' => $avatar_list[$category][urldecode($file)]['height'],
);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_template_name()
{
@@ -152,7 +158,7 @@ class local extends \phpbb\avatar\driver\driver
*/
protected function get_avatar_list($user)
{
- $avatar_list = ($this->cache == null) ? false : $this->cache->get('avatar_local_list');
+ $avatar_list = ($this->cache == null) ? false : $this->cache->get('_avatar_local_list');
if ($avatar_list === false)
{
@@ -168,17 +174,19 @@ class local extends \phpbb\avatar\driver\driver
// Match all images in the gallery folder
if (preg_match('#^[^&\'"<>]+\.(?:' . implode('|', $this->allowed_extensions) . ')$#i', $image) && is_file($file_path . '/' . $image))
{
- if (function_exists('getimagesize'))
+ $dims = $this->imagesize->getImageSize($file_path . '/' . $image);
+
+ if ($dims === false)
{
- $dims = getimagesize($file_path . '/' . $image);
+ $dims = array(0, 0);
}
else
{
- $dims = array(0, 0);
+ $dims = array($dims['width'], $dims['height']);
}
- $cat = ($path == $file_path) ? $user->lang['MAIN'] : str_replace("$path/", '', $file_path);
+ $cat = ($path == $file_path) ? $user->lang['NO_AVATAR_CATEGORY'] : str_replace("$path/", '', $file_path);
$avatar_list[$cat][$image] = array(
- 'file' => ($cat != $user->lang['MAIN']) ? rawurlencode($cat) . '/' . rawurlencode($image) : rawurlencode($image),
+ 'file' => ($cat != $user->lang['NO_AVATAR_CATEGORY']) ? str_replace('%2F', '/', rawurlencode($cat)) . '/' . rawurlencode($image) : rawurlencode($image),
'filename' => rawurlencode($image),
'name' => ucfirst(str_replace('_', ' ', preg_replace('#^(.*)\..*$#', '\1', $image))),
'width' => $dims[0],
@@ -190,7 +198,7 @@ class local extends \phpbb\avatar\driver\driver
if ($this->cache != null)
{
- $this->cache->put('avatar_local_list', $avatar_list, 86400);
+ $this->cache->put('_avatar_local_list', $avatar_list, 86400);
}
}
diff --git a/phpBB/phpbb/avatar/driver/remote.php b/phpBB/phpbb/avatar/driver/remote.php
index 36623942df..0526b9184e 100644
--- a/phpBB/phpbb/avatar/driver/remote.php
+++ b/phpBB/phpbb/avatar/driver/remote.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,12 +15,11 @@ namespace phpbb\avatar\driver;
/**
* Handles avatars hosted remotely
-* @package phpBB3
*/
class remote extends \phpbb\avatar\driver\driver
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_data($row)
{
@@ -28,7 +31,7 @@ class remote extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form($request, $template, $user, $row, &$error)
{
@@ -42,7 +45,7 @@ class remote extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function process_form($request, $template, $user, $row, &$error)
{
@@ -50,6 +53,11 @@ class remote extends \phpbb\avatar\driver\driver
$width = $request->variable('avatar_remote_width', 0);
$height = $request->variable('avatar_remote_height', 0);
+ if (empty($url))
+ {
+ return false;
+ }
+
if (!preg_match('#^(http|https|ftp)://#i', $url))
{
$url = 'http://' . $url;
@@ -84,46 +92,54 @@ class remote extends \phpbb\avatar\driver\driver
return false;
}
- // Make sure getimagesize works...
- if (function_exists('getimagesize'))
+ // Get image dimensions
+ if (($width <= 0 || $height <= 0) && (($image_data = $this->imagesize->getImageSize($url)) === false))
{
- if (($width <= 0 || $height <= 0) && (($image_data = @getimagesize($url)) === false))
- {
- $error[] = 'UNABLE_GET_IMAGE_SIZE';
- return false;
- }
-
- if (!empty($image_data) && ($image_data[0] <= 0 || $image_data[1] <= 0))
- {
- $error[] = 'AVATAR_NO_SIZE';
- return false;
- }
-
- $width = ($width && $height) ? $width : $image_data[0];
- $height = ($width && $height) ? $height : $image_data[1];
+ $error[] = 'UNABLE_GET_IMAGE_SIZE';
+ return false;
}
- if ($width <= 0 || $height <= 0)
+ if (!empty($image_data) && ($image_data['width'] <= 0 || $image_data['height'] <= 0))
{
$error[] = 'AVATAR_NO_SIZE';
return false;
}
- if (!class_exists('fileupload'))
+ $width = ($width && $height) ? $width : $image_data['width'];
+ $height = ($width && $height) ? $height : $image_data['height'];
+
+ if ($width <= 0 || $height <= 0)
{
- include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext);
+ $error[] = 'AVATAR_NO_SIZE';
+ return false;
}
- $types = \fileupload::image_types();
- $extension = strtolower(\filespec::get_extension($url));
+ $types = \phpbb\files\upload::image_types();
+ $extension = strtolower(\phpbb\files\filespec::get_extension($url));
// Check if this is actually an image
if ($file_stream = @fopen($url, 'r'))
{
// Timeout after 1 second
stream_set_timeout($file_stream, 1);
+ // read some data to ensure headers are present
+ fread($file_stream, 1024);
$meta = stream_get_meta_data($file_stream);
- foreach ($meta['wrapper_data'] as $header)
+
+ if (isset($meta['wrapper_data']['headers']) && is_array($meta['wrapper_data']['headers']))
+ {
+ $headers = $meta['wrapper_data']['headers'];
+ }
+ else if (isset($meta['wrapper_data']) && is_array($meta['wrapper_data']))
+ {
+ $headers = $meta['wrapper_data'];
+ }
+ else
+ {
+ $headers = array();
+ }
+
+ foreach ($headers as $header)
{
$header = preg_split('/ /', $header, 2);
if (strtr(strtolower(trim($header[0], ':')), '_', '-') === 'content-type')
@@ -148,15 +164,15 @@ class remote extends \phpbb\avatar\driver\driver
return false;
}
- if (!empty($image_data) && (!isset($types[$image_data[2]]) || !in_array($extension, $types[$image_data[2]])))
+ if (!empty($image_data) && (!isset($types[$image_data['type']]) || !in_array($extension, $types[$image_data['type']])))
{
- if (!isset($types[$image_data[2]]))
+ if (!isset($types[$image_data['type']]))
{
$error[] = 'UNABLE_GET_IMAGE_SIZE';
}
else
{
- $error[] = array('IMAGE_FILETYPE_MISMATCH', $types[$image_data[2]][0], $extension);
+ $error[] = array('IMAGE_FILETYPE_MISMATCH', $types[$image_data['type']][0], $extension);
}
return false;
@@ -188,7 +204,7 @@ class remote extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_template_name()
{
diff --git a/phpBB/phpbb/avatar/driver/upload.php b/phpBB/phpbb/avatar/driver/upload.php
index 1e50e135e4..a0c23cb624 100644
--- a/phpBB/phpbb/avatar/driver/upload.php
+++ b/phpBB/phpbb/avatar/driver/upload.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,24 +15,64 @@ namespace phpbb\avatar\driver;
/**
* Handles avatars uploaded to the board
-* @package phpBB3
*/
class upload extends \phpbb\avatar\driver\driver
{
/**
- * @inheritdoc
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
+ * @var \phpbb\files\factory
+ */
+ protected $files_factory;
+
+ /**
+ * Construct a driver object
+ *
+ * @param \phpbb\config\config $config phpBB configuration
+ * @param string $phpbb_root_path Path to the phpBB root
+ * @param string $php_ext PHP file extension
+ * @param \phpbb\filesystem\filesystem_interface $filesystem phpBB filesystem helper
+ * @param \phpbb\path_helper $path_helper phpBB path helper
+ * @param \phpbb\event\dispatcher_interface $dispatcher phpBB Event dispatcher object
+ * @param \phpbb\files\factory $files_factory File classes factory
+ * @param \phpbb\cache\driver\driver_interface $cache Cache driver
+ */
+ public function __construct(\phpbb\config\config $config, $phpbb_root_path, $php_ext, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\path_helper $path_helper, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\files\factory $files_factory, \phpbb\cache\driver\driver_interface $cache = null)
+ {
+ $this->config = $config;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->filesystem = $filesystem;
+ $this->path_helper = $path_helper;
+ $this->dispatcher = $dispatcher;
+ $this->files_factory = $files_factory;
+ $this->cache = $cache;
+ }
+
+ /**
+ * {@inheritdoc}
*/
- public function get_data($row, $ignore_config = false)
+ public function get_data($row)
{
+ $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $this->path_helper->get_web_root_path();
+
return array(
- 'src' => $this->path_helper->get_web_root_path() . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'],
+ 'src' => $root_path . 'download/file.' . $this->php_ext . '?avatar=' . $row['avatar'],
'width' => $row['avatar_width'],
'height' => $row['avatar_height'],
);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form($request, $template, $user, $row, &$error)
{
@@ -46,7 +90,7 @@ class upload extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function process_form($request, $template, $user, $row, &$error)
{
@@ -55,21 +99,26 @@ class upload extends \phpbb\avatar\driver\driver
return false;
}
- if (!class_exists('fileupload'))
- {
- include($this->phpbb_root_path . 'includes/functions_upload.' . $this->php_ext);
- }
-
- $upload = new \fileupload('AVATAR_', $this->allowed_extensions, $this->config['avatar_filesize'], $this->config['avatar_min_width'], $this->config['avatar_min_height'], $this->config['avatar_max_width'], $this->config['avatar_max_height'], (isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false));
+ /** @var \phpbb\files\upload $upload */
+ $upload = $this->files_factory->get('upload')
+ ->set_error_prefix('AVATAR_')
+ ->set_allowed_extensions($this->allowed_extensions)
+ ->set_max_filesize($this->config['avatar_filesize'])
+ ->set_allowed_dimensions(
+ $this->config['avatar_min_width'],
+ $this->config['avatar_min_height'],
+ $this->config['avatar_max_width'],
+ $this->config['avatar_max_height'])
+ ->set_disallowed_content((isset($this->config['mime_triggers']) ? explode('|', $this->config['mime_triggers']) : false));
$url = $request->variable('avatar_upload_url', '');
$upload_file = $request->file('avatar_upload_file');
if (!empty($upload_file['name']))
{
- $file = $upload->form_upload('avatar_upload_file');
+ $file = $upload->handle_upload('files.types.form', 'avatar_upload_file');
}
- elseif (!empty($this->config['allow_avatar_remote_upload']) && !empty($url))
+ else if (!empty($this->config['allow_avatar_remote_upload']) && !empty($url))
{
if (!preg_match('#^(http|https|ftp)://#i', $url))
{
@@ -97,17 +146,25 @@ class upload extends \phpbb\avatar\driver\driver
return false;
}
- $file = $upload->remote_upload($url);
+ $file = $upload->handle_upload('files.types.remote', $url);
}
else
{
- $error[] = 'NO_AVATAR_SELECTED';
return false;
}
$prefix = $this->config['avatar_salt'] . '_';
$file->clean_filename('avatar', $prefix, $row['id']);
+ // If there was an error during upload, then abort operation
+ if (sizeof($file->error))
+ {
+ $file->remove();
+ $error = $file->error;
+ return false;
+ }
+
+ // Calculate new destination
$destination = $this->config['avatar_path'];
// Adjust destination path (no trailing slash)
@@ -122,16 +179,45 @@ class upload extends \phpbb\avatar\driver\driver
$destination = '';
}
- // Move file and overwrite any existing image
- $file->move_file($destination, true);
+ /**
+ * Before moving new file in place (and eventually overwriting the existing avatar with the newly uploaded avatar)
+ *
+ * @event core.avatar_driver_upload_move_file_before
+ * @var string destination Destination directory where the file is going to be moved
+ * @var string prefix Prefix for the avatar filename
+ * @var array row Array with avatar row data
+ * @var array error Array of errors, if filled in by this event file will not be moved
+ * @since 3.1.6-RC1
+ */
+ $vars = array(
+ 'destination',
+ 'prefix',
+ 'row',
+ 'error',
+ );
+ extract($this->dispatcher->trigger_event('core.avatar_driver_upload_move_file_before', compact($vars)));
- if (sizeof($file->error))
+ if (!sizeof($error))
+ {
+ // Move file and overwrite any existing image
+ $file->move_file($destination, true);
+ }
+
+ // If there was an error during move, then clean up leftovers
+ $error = array_merge($error, $file->error);
+ if (sizeof($error))
{
$file->remove();
- $error = array_merge($error, $file->error);
return false;
}
+ // Delete current avatar if not overwritten
+ $ext = substr(strrchr($row['avatar'], '.'), 1);
+ if ($ext && $ext !== $file->get('extension'))
+ {
+ $this->delete($row);
+ }
+
return array(
'avatar' => $row['id'] . '_' . time() . '.' . $file->get('extension'),
'avatar_width' => $file->get('width'),
@@ -140,26 +226,48 @@ class upload extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function prepare_form_acp($user)
{
return array(
'allow_avatar_remote_upload'=> array('lang' => 'ALLOW_REMOTE_UPLOAD', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
'avatar_filesize' => array('lang' => 'MAX_FILESIZE', 'validate' => 'int:0', 'type' => 'number:0', 'explain' => true, 'append' => ' ' . $user->lang['BYTES']),
- 'avatar_path' => array('lang' => 'AVATAR_STORAGE_PATH', 'validate' => 'rwpath', 'type' => 'text:20:255', 'explain' => true),
+ 'avatar_path' => array('lang' => 'AVATAR_STORAGE_PATH', 'validate' => 'rpath', 'type' => 'text:20:255', 'explain' => true),
);
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function delete($row)
{
+
+ $error = array();
+ $destination = $this->config['avatar_path'];
+ $prefix = $this->config['avatar_salt'] . '_';
$ext = substr(strrchr($row['avatar'], '.'), 1);
- $filename = $this->phpbb_root_path . $this->config['avatar_path'] . '/' . $this->config['avatar_salt'] . '_' . $row['id'] . '.' . $ext;
+ $filename = $this->phpbb_root_path . $destination . '/' . $prefix . $row['id'] . '.' . $ext;
+
+ /**
+ * Before deleting an existing avatar
+ *
+ * @event core.avatar_driver_upload_delete_before
+ * @var string destination Destination directory where the file is going to be deleted
+ * @var string prefix Prefix for the avatar filename
+ * @var array row Array with avatar row data
+ * @var array error Array of errors, if filled in by this event file will not be deleted
+ * @since 3.1.6-RC1
+ */
+ $vars = array(
+ 'destination',
+ 'prefix',
+ 'row',
+ 'error',
+ );
+ extract($this->dispatcher->trigger_event('core.avatar_driver_upload_delete_before', compact($vars)));
- if (file_exists($filename))
+ if (!sizeof($error) && file_exists($filename))
{
@unlink($filename);
}
@@ -168,7 +276,7 @@ class upload extends \phpbb\avatar\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_template_name()
{
@@ -182,6 +290,6 @@ class upload extends \phpbb\avatar\driver\driver
*/
protected function can_upload()
{
- return (file_exists($this->phpbb_root_path . $this->config['avatar_path']) && phpbb_is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on'));
+ return (file_exists($this->phpbb_root_path . $this->config['avatar_path']) && $this->filesystem->is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on'));
}
}
diff --git a/phpBB/phpbb/avatar/manager.php b/phpBB/phpbb/avatar/manager.php
index 6ce924d2eb..26eb17c265 100644
--- a/phpBB/phpbb/avatar/manager.php
+++ b/phpBB/phpbb/avatar/manager.php
@@ -1,17 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\avatar;
-/**
-* @package avatar
-*/
class manager
{
/**
@@ -40,8 +41,8 @@ class manager
static protected $default_row = array(
'avatar' => '',
'avatar_type' => '',
- 'avatar_width' => '',
- 'avatar_height' => '',
+ 'avatar_width' => 0,
+ 'avatar_height' => 0,
);
/**
@@ -245,7 +246,7 @@ class manager
*/
public function is_enabled($driver)
{
- $config_name = $this->get_driver_config_name($driver);
+ $config_name = $driver->get_config_name();
return $this->config["allow_avatar_{$config_name}"];
}
@@ -259,7 +260,7 @@ class manager
*/
public function get_avatar_settings($driver)
{
- $config_name = $this->get_driver_config_name($driver);
+ $config_name = $driver->get_config_name();
return array(
'allow_avatar_' . $config_name => array('lang' => 'ALLOW_' . strtoupper(str_replace('\\', '_', $config_name)), 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => false),
@@ -267,18 +268,6 @@ class manager
}
/**
- * Get the config name of an avatar driver
- *
- * @param object $driver Avatar driver object
- *
- * @return string Avatar driver config name
- */
- public function get_driver_config_name($driver)
- {
- return preg_replace('#^phpbb\\\\avatar\\\\driver\\\\#', '', get_class($driver));
- }
-
- /**
* Replace "error" strings with their real, localized form
*
* @param \phpbb\user phpBB User object
@@ -306,4 +295,60 @@ class manager
return $error;
}
+
+ /**
+ * Handle deleting avatars
+ *
+ * @param \phpbb\db\driver\driver_interface $db phpBB dbal
+ * @param \phpbb\user $user phpBB user object
+ * @param array $avatar_data Cleaned user data containing the user's
+ * avatar data
+ * @param string $table Database table from which the avatar should be deleted
+ * @param string $prefix Prefix of user data columns in database
+ * @return null
+ */
+ public function handle_avatar_delete(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $avatar_data, $table, $prefix)
+ {
+ if ($driver = $this->get_driver($avatar_data['avatar_type']))
+ {
+ $driver->delete($avatar_data);
+ }
+
+ $result = $this->prefix_avatar_columns($prefix, self::$default_row);
+
+ $sql = 'UPDATE ' . $table . '
+ SET ' . $db->sql_build_array('UPDATE', $result) . '
+ WHERE ' . $prefix . 'id = ' . (int) $avatar_data['id'];
+ $db->sql_query($sql);
+
+ // Make sure we also delete this avatar from the users
+ if ($prefix === 'group_')
+ {
+ $result = $this->prefix_avatar_columns('user_', self::$default_row);
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $db->sql_build_array('UPDATE', $result) . "
+ WHERE user_avatar = '" . $db->sql_escape($avatar_data['avatar']) . "'";
+ $db->sql_query($sql);
+ }
+ }
+
+ /**
+ * Prefix avatar columns
+ *
+ * @param string $prefix Column prefix
+ * @param array $data Column data
+ *
+ * @return array Column data with prefixed column names
+ */
+ public function prefix_avatar_columns($prefix, $data)
+ {
+ foreach ($data as $key => $value)
+ {
+ $data[$prefix . $key] = $value;
+ unset($data[$key]);
+ }
+
+ return $data;
+ }
}
diff --git a/phpBB/phpbb/cache/driver/apc.php b/phpBB/phpbb/cache/driver/apc.php
index a28d91c00a..521d5d41ea 100644
--- a/phpBB/phpbb/cache/driver/apc.php
+++ b/phpBB/phpbb/cache/driver/apc.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,16 +15,13 @@ namespace phpbb\cache\driver;
/**
* ACM for APC
-* @package acm
*/
class apc extends \phpbb\cache\driver\memory
{
var $extension = 'apc';
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
diff --git a/phpBB/phpbb/cache/driver/base.php b/phpBB/phpbb/cache/driver/base.php
index feaca25a5b..55cd4668de 100644
--- a/phpBB/phpbb/cache/driver/base.php
+++ b/phpBB/phpbb/cache/driver/base.php
@@ -1,17 +1,228 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\cache\driver;
-/**
-* @package acm
-*/
abstract class base implements \phpbb\cache\driver\driver_interface
{
+ var $vars = array();
+ var $is_modified = false;
+
+ var $sql_rowset = array();
+ var $sql_row_pointer = array();
+ var $cache_dir = '';
+
+ /**
+ * {@inheritDoc}
+ */
+ function purge()
+ {
+ // Purge all phpbb cache files
+ try
+ {
+ $iterator = new \DirectoryIterator($this->cache_dir);
+ }
+ catch (\Exception $e)
+ {
+ return;
+ }
+
+ foreach ($iterator as $fileInfo)
+ {
+ if ($fileInfo->isDot())
+ {
+ continue;
+ }
+ $filename = $fileInfo->getFilename();
+ if ($fileInfo->isDir())
+ {
+ $this->remove_dir($fileInfo->getPathname());
+ }
+ else if (strpos($filename, 'container_') === 0 ||
+ strpos($filename, 'url_matcher') === 0 ||
+ strpos($filename, 'url_generator') === 0 ||
+ strpos($filename, 'sql_') === 0 ||
+ strpos($filename, 'data_') === 0)
+ {
+ $this->remove_file($fileInfo->getPathname());
+ }
+ }
+
+ unset($this->vars);
+ unset($this->sql_rowset);
+ unset($this->sql_row_pointer);
+
+ $this->vars = array();
+ $this->sql_rowset = array();
+ $this->sql_row_pointer = array();
+
+ $this->is_modified = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function unload()
+ {
+ $this->save();
+ unset($this->vars);
+ unset($this->sql_rowset);
+ unset($this->sql_row_pointer);
+
+ $this->vars = array();
+ $this->sql_rowset = array();
+ $this->sql_row_pointer = array();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_load($query)
+ {
+ // Remove extra spaces and tabs
+ $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
+ $query_id = md5($query);
+
+ if (($result = $this->_read('sql_' . $query_id)) === false)
+ {
+ return false;
+ }
+
+ $this->sql_rowset[$query_id] = $result;
+ $this->sql_row_pointer[$query_id] = 0;
+
+ return $query_id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_exists($query_id)
+ {
+ return isset($this->sql_rowset[$query_id]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchrow($query_id)
+ {
+ if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
+ {
+ return $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++];
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_fetchfield($query_id, $field)
+ {
+ if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
+ {
+ return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_rowseek($rownum, $query_id)
+ {
+ if ($rownum >= sizeof($this->sql_rowset[$query_id]))
+ {
+ return false;
+ }
+
+ $this->sql_row_pointer[$query_id] = $rownum;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_freeresult($query_id)
+ {
+ if (!isset($this->sql_rowset[$query_id]))
+ {
+ return false;
+ }
+
+ unset($this->sql_rowset[$query_id]);
+ unset($this->sql_row_pointer[$query_id]);
+
+ return true;
+ }
+
+ /**
+ * Removes/unlinks file
+ *
+ * @param string $filename Filename to remove
+ * @param bool $check Check file permissions
+ * @return bool True if the file was successfully removed, otherwise false
+ */
+ function remove_file($filename, $check = false)
+ {
+ global $phpbb_filesystem;
+
+ if ($check && !$phpbb_filesystem->is_writable($this->cache_dir))
+ {
+ // E_USER_ERROR - not using language entry - intended.
+ trigger_error('Unable to remove files within ' . $this->cache_dir . '. Please check directory permissions.', E_USER_ERROR);
+ }
+
+ return @unlink($filename);
+ }
+
+ /**
+ * Remove directory
+ *
+ * @param string $dir Directory to remove
+ *
+ * @return null
+ */
+ protected function remove_dir($dir)
+ {
+ try
+ {
+ $iterator = new \DirectoryIterator($dir);
+ }
+ catch (\Exception $e)
+ {
+ return;
+ }
+
+ foreach ($iterator as $fileInfo)
+ {
+ if ($fileInfo->isDot())
+ {
+ continue;
+ }
+
+ if ($fileInfo->isDir())
+ {
+ $this->remove_dir($fileInfo->getPathname());
+ }
+ else
+ {
+ $this->remove_file($fileInfo->getPathname());
+ }
+ }
+
+ @rmdir($dir);
+ }
}
diff --git a/phpBB/phpbb/cache/driver/driver_interface.php b/phpBB/phpbb/cache/driver/driver_interface.php
index 0715a4b934..9ac9ca0c59 100644
--- a/phpBB/phpbb/cache/driver/driver_interface.php
+++ b/phpBB/phpbb/cache/driver/driver_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,53 +15,78 @@ namespace phpbb\cache\driver;
/**
* An interface that all cache drivers must implement
-*
-* @package acm
*/
interface driver_interface
{
/**
* Load global cache
+ *
+ * @return mixed False if an error was encountered, otherwise the data type of the cached data
*/
public function load();
/**
* Unload cache object
+ *
+ * @return null
*/
public function unload();
/**
* Save modified objects
+ *
+ * @return null
*/
public function save();
/**
* Tidy cache
+ *
+ * @return null
*/
public function tidy();
/**
* Get saved cache object
+ *
+ * @param string $var_name Cache key
+ * @return mixed False if an error was encountered, otherwise the saved cached object
*/
public function get($var_name);
/**
* Put data into cache
+ *
+ * @param string $var_name Cache key
+ * @param mixed $var Cached data to store
+ * @param int $ttl Time-to-live of cached data
+ * @return null
*/
public function put($var_name, $var, $ttl = 0);
/**
* Purge cache data
+ *
+ * @return null
*/
public function purge();
/**
* Destroy cache data
+ *
+ * @param string $var_name Cache key
+ * @param string $table Table name
+ * @return null
*/
public function destroy($var_name, $table = '');
/**
* Check if a given cache entry exists
+ *
+ * @param string $var_name Cache key
+ *
+ * @return bool True if cache file exists and has not expired.
+ * False otherwise.
*/
public function _exists($var_name);
@@ -79,7 +108,7 @@ interface driver_interface
* result to persistent storage. In other words, there is no need
* to call save() afterwards.
*
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param string $query SQL query, should be used for generating storage key
* @param mixed $query_result The result from \dbal::sql_query, to be passed to
* \dbal::sql_fetchrow to get all rows and store them
@@ -90,7 +119,7 @@ interface driver_interface
* representing the query should be returned. Otherwise
* the original $query_result should be returned.
*/
- public function sql_save(\phpbb\db\driver\driver $db, $query, $query_result, $ttl);
+ public function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl);
/**
* Check if result for a given SQL query exists in cache.
@@ -113,7 +142,7 @@ interface driver_interface
* Fetch a field from the current row of a cached database result (database)
*
* @param int $query_id
- * @param $field The name of the column.
+ * @param string $field The name of the column.
* @return string|bool The field of the query result if found in the cache,
* otherwise false.
*/
diff --git a/phpBB/phpbb/cache/driver/null.php b/phpBB/phpbb/cache/driver/dummy.php
index ea535ca1e1..1f74f6dd77 100644
--- a/phpBB/phpbb/cache/driver/null.php
+++ b/phpBB/phpbb/cache/driver/dummy.php
@@ -1,19 +1,22 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\cache\driver;
/**
-* ACM Null Caching
-* @package acm
+* ACM dummy Caching
*/
-class null extends \phpbb\cache\driver\base
+class dummy extends \phpbb\cache\driver\base
{
/**
* Set cache path
@@ -23,7 +26,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Load global cache
+ * {@inheritDoc}
*/
function load()
{
@@ -31,30 +34,32 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Unload cache object
+ * {@inheritDoc}
*/
function unload()
{
}
/**
- * Save modified objects
+ * {@inheritDoc}
*/
function save()
{
}
/**
- * Tidy cache
+ * {@inheritDoc}
*/
function tidy()
{
+ global $config;
+
// This cache always has a tidy room.
- set_config('cache_last_gc', time(), true);
+ $config->set('cache_last_gc', time(), false);
}
/**
- * Get saved cache object
+ * {@inheritDoc}
*/
function get($var_name)
{
@@ -62,28 +67,28 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Put data into cache
+ * {@inheritDoc}
*/
function put($var_name, $var, $ttl = 0)
{
}
/**
- * Purge cache data
+ * {@inheritDoc}
*/
function purge()
{
}
/**
- * Destroy cache data
+ * {@inheritDoc}
*/
function destroy($var_name, $table = '')
{
}
/**
- * Check if a given cache entry exist
+ * {@inheritDoc}
*/
function _exists($var_name)
{
@@ -91,7 +96,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Load cached sql query
+ * {@inheritDoc}
*/
function sql_load($query)
{
@@ -101,13 +106,13 @@ class null extends \phpbb\cache\driver\base
/**
* {@inheritDoc}
*/
- function sql_save(\phpbb\db\driver\driver $db, $query, $query_result, $ttl)
+ function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
{
return $query_result;
}
/**
- * Ceck if a given sql query exist in cache
+ * {@inheritDoc}
*/
function sql_exists($query_id)
{
@@ -115,7 +120,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Fetch row from cache (database)
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id)
{
@@ -123,7 +128,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Fetch a field from the current row of a cached database result (database)
+ * {@inheritDoc}
*/
function sql_fetchfield($query_id, $field)
{
@@ -131,7 +136,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Seek a specific row in an a cached database result (database)
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, $query_id)
{
@@ -139,7 +144,7 @@ class null extends \phpbb\cache\driver\base
}
/**
- * Free memory used for a cached database result (database)
+ * {@inheritDoc}
*/
function sql_freeresult($query_id)
{
diff --git a/phpBB/phpbb/cache/driver/eaccelerator.php b/phpBB/phpbb/cache/driver/eaccelerator.php
index 2629cb53e5..740855144f 100644
--- a/phpBB/phpbb/cache/driver/eaccelerator.php
+++ b/phpBB/phpbb/cache/driver/eaccelerator.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\cache\driver;
/**
* ACM for eAccelerator
-* @package acm
* @todo Missing locks from destroy() talk with David
*/
class eaccelerator extends \phpbb\cache\driver\memory
@@ -22,9 +25,7 @@ class eaccelerator extends \phpbb\cache\driver\memory
var $serialize_header = '#phpbb-serialized#';
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
@@ -39,15 +40,15 @@ class eaccelerator extends \phpbb\cache\driver\memory
}
/**
- * Perform cache garbage collection
- *
- * @return null
- */
+ * {@inheritDoc}
+ */
function tidy()
{
+ global $config;
+
eaccelerator_gc();
- set_config('cache_last_gc', time(), true);
+ $config->set('cache_last_gc', time(), false);
}
/**
diff --git a/phpBB/phpbb/cache/driver/file.php b/phpBB/phpbb/cache/driver/file.php
index 6686da6953..bb055d3acf 100644
--- a/phpBB/phpbb/cache/driver/file.php
+++ b/phpBB/phpbb/cache/driver/file.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,29 +15,36 @@ namespace phpbb\cache\driver;
/**
* ACM File Based Caching
-* @package acm
*/
class file extends \phpbb\cache\driver\base
{
- var $vars = array();
var $var_expires = array();
- var $is_modified = false;
- var $sql_rowset = array();
- var $sql_row_pointer = array();
- var $cache_dir = '';
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
/**
* Set cache path
+ *
+ * @param string $cache_dir Define the path to the cache directory (default: $phpbb_root_path . 'cache/')
*/
function __construct($cache_dir = null)
{
- global $phpbb_root_path;
- $this->cache_dir = !is_null($cache_dir) ? $cache_dir : $phpbb_root_path . 'cache/';
+ global $phpbb_root_path, $phpbb_container;
+
+ $this->cache_dir = !is_null($cache_dir) ? $cache_dir : $phpbb_root_path . 'cache/' . $phpbb_container->getParameter('core.environment') . '/';
+ $this->filesystem = new \phpbb\filesystem\filesystem();
+
+ if (!is_dir($this->cache_dir))
+ {
+ @mkdir($this->cache_dir, 0777, true);
+ }
}
/**
- * Load global cache
+ * {@inheritDoc}
*/
function load()
{
@@ -41,24 +52,17 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Unload cache object
+ * {@inheritDoc}
*/
function unload()
{
- $this->save();
- unset($this->vars);
+ parent::unload();
unset($this->var_expires);
- unset($this->sql_rowset);
- unset($this->sql_row_pointer);
-
- $this->vars = array();
$this->var_expires = array();
- $this->sql_rowset = array();
- $this->sql_row_pointer = array();
}
/**
- * Save modified objects
+ * {@inheritDoc}
*/
function save()
{
@@ -71,14 +75,8 @@ class file extends \phpbb\cache\driver\base
if (!$this->_write('data_global'))
{
- if (!function_exists('phpbb_is_writable'))
- {
- global $phpbb_root_path;
- include($phpbb_root_path . 'includes/functions.' . $phpEx);
- }
-
// Now, this occurred how often? ... phew, just tell the user then...
- if (!phpbb_is_writable($this->cache_dir))
+ if (!$this->filesystem->is_writable($this->cache_dir))
{
// We need to use die() here, because else we may encounter an infinite loop (the message handler calls $cache->unload())
die('Fatal: ' . $this->cache_dir . ' is NOT writable.');
@@ -93,11 +91,11 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Tidy cache
+ * {@inheritDoc}
*/
function tidy()
{
- global $phpEx;
+ global $config, $phpEx;
$dir = @opendir($this->cache_dir);
@@ -151,18 +149,16 @@ class file extends \phpbb\cache\driver\base
}
}
- set_config('cache_last_gc', time(), true);
+ $config->set('cache_last_gc', time(), false);
}
/**
- * Get saved cache object
+ * {@inheritDoc}
*/
function get($var_name)
{
if ($var_name[0] == '_')
{
- global $phpEx;
-
if (!$this->_exists($var_name))
{
return false;
@@ -177,7 +173,7 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Put data into cache
+ * {@inheritDoc}
*/
function put($var_name, $var, $ttl = 31536000)
{
@@ -194,93 +190,16 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Purge cache data
+ * {@inheritDoc}
*/
function purge()
{
- // Purge all phpbb cache files
- try
- {
- $iterator = new \DirectoryIterator($this->cache_dir);
- }
- catch (Exception $e)
- {
- return;
- }
-
- foreach ($iterator as $fileInfo)
- {
- if ($fileInfo->isDot())
- {
- continue;
- }
- $filename = $fileInfo->getFilename();
- if ($fileInfo->isDir())
- {
- $this->remove_dir($fileInfo->getPathname());
- }
- elseif (strpos($filename, 'container_') === 0 ||
- strpos($filename, 'url_matcher') === 0 ||
- strpos($filename, 'sql_') === 0 ||
- strpos($filename, 'data_') === 0)
- {
- $this->remove_file($fileInfo->getPathname());
- }
- }
-
- unset($this->vars);
- unset($this->var_expires);
- unset($this->sql_rowset);
- unset($this->sql_row_pointer);
-
- $this->vars = array();
+ parent::purge();
$this->var_expires = array();
- $this->sql_rowset = array();
- $this->sql_row_pointer = array();
-
- $this->is_modified = false;
- }
-
- /**
- * Remove directory
- *
- * @param string $dir Directory to remove
- *
- * @return null
- */
- protected function remove_dir($dir)
- {
- try
- {
- $iterator = new \DirectoryIterator($dir);
- }
- catch (Exception $e)
- {
- return;
- }
-
- foreach ($iterator as $fileInfo)
- {
- if ($fileInfo->isDot())
- {
- continue;
- }
-
- if ($fileInfo->isDir())
- {
- $this->remove_dir($fileInfo->getPathname());
- }
- else
- {
- $this->remove_file($fileInfo->getPathname());
- }
- }
-
- @rmdir($dir);
}
/**
- * Destroy cache data
+ * {@inheritDoc}
*/
function destroy($var_name, $table = '')
{
@@ -359,13 +278,14 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Check if a given cache entry exist
+ * {@inheritDoc}
*/
function _exists($var_name)
{
if ($var_name[0] == '_')
{
global $phpEx;
+ $var_name = $this->clean_varname($var_name);
return file_exists($this->cache_dir . 'data' . $var_name . ".$phpEx");
}
else
@@ -385,34 +305,14 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Load cached sql query
- */
- function sql_load($query)
- {
- // Remove extra spaces and tabs
- $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
-
- if (($rowset = $this->_read('sql_' . md5($query))) === false)
- {
- return false;
- }
-
- $query_id = sizeof($this->sql_rowset);
- $this->sql_rowset[$query_id] = $rowset;
- $this->sql_row_pointer[$query_id] = 0;
-
- return $query_id;
- }
-
- /**
* {@inheritDoc}
*/
- function sql_save(\phpbb\db\driver\driver $db, $query, $query_result, $ttl)
+ function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
{
// Remove extra spaces and tabs
$query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
- $query_id = sizeof($this->sql_rowset);
+ $query_id = md5($query);
$this->sql_rowset[$query_id] = array();
$this->sql_row_pointer[$query_id] = 0;
@@ -422,7 +322,7 @@ class file extends \phpbb\cache\driver\base
}
$db->sql_freeresult($query_result);
- if ($this->_write('sql_' . md5($query), $this->sql_rowset[$query_id], $ttl + time(), $query))
+ if ($this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl + time(), $query))
{
return $query_id;
}
@@ -431,70 +331,6 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Ceck if a given sql query exist in cache
- */
- function sql_exists($query_id)
- {
- return isset($this->sql_rowset[$query_id]);
- }
-
- /**
- * Fetch row from cache (database)
- */
- function sql_fetchrow($query_id)
- {
- if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
- {
- return $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++];
- }
-
- return false;
- }
-
- /**
- * Fetch a field from the current row of a cached database result (database)
- */
- function sql_fetchfield($query_id, $field)
- {
- if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
- {
- return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false;
- }
-
- return false;
- }
-
- /**
- * Seek a specific row in an a cached database result (database)
- */
- function sql_rowseek($rownum, $query_id)
- {
- if ($rownum >= sizeof($this->sql_rowset[$query_id]))
- {
- return false;
- }
-
- $this->sql_row_pointer[$query_id] = $rownum;
- return true;
- }
-
- /**
- * Free memory used for a cached database result (database)
- */
- function sql_freeresult($query_id)
- {
- if (!isset($this->sql_rowset[$query_id]))
- {
- return false;
- }
-
- unset($this->sql_rowset[$query_id]);
- unset($this->sql_row_pointer[$query_id]);
-
- return true;
- }
-
- /**
* Read cached data from a specified file
*
* @access private
@@ -505,6 +341,7 @@ class file extends \phpbb\cache\driver\base
{
global $phpEx;
+ $filename = $this->clean_varname($filename);
$file = "{$this->cache_dir}$filename.$phpEx";
$type = substr($filename, 0, strpos($filename, '_'));
@@ -687,6 +524,7 @@ class file extends \phpbb\cache\driver\base
{
global $phpEx;
+ $filename = $this->clean_varname($filename);
$file = "{$this->cache_dir}$filename.$phpEx";
$lock = new \phpbb\lock\flock($file);
@@ -736,13 +574,14 @@ class file extends \phpbb\cache\driver\base
fclose($handle);
- if (!function_exists('phpbb_chmod'))
+ try
{
- global $phpbb_root_path;
- include($phpbb_root_path . 'includes/functions.' . $phpEx);
+ $this->filesystem->phpbb_chmod($file, CHMOD_READ | CHMOD_WRITE);
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing
}
-
- phpbb_chmod($file, CHMOD_READ | CHMOD_WRITE);
$return_value = true;
}
@@ -757,22 +596,13 @@ class file extends \phpbb\cache\driver\base
}
/**
- * Removes/unlinks file
+ * Replace slashes in the file name
+ *
+ * @param string $varname name of a cache variable
+ * @return string $varname name that is safe to use as a filename
*/
- function remove_file($filename, $check = false)
+ protected function clean_varname($varname)
{
- if (!function_exists('phpbb_is_writable'))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/functions.' . $phpEx);
- }
-
- if ($check && !phpbb_is_writable($this->cache_dir))
- {
- // E_USER_ERROR - not using language entry - intended.
- trigger_error('Unable to remove files within ' . $this->cache_dir . '. Please check directory permissions.', E_USER_ERROR);
- }
-
- return @unlink($filename);
+ return str_replace('/', '-', $varname);
}
}
diff --git a/phpBB/phpbb/cache/driver/memcache.php b/phpBB/phpbb/cache/driver/memcache.php
index c725ec0fb0..caa82fb0b1 100644
--- a/phpBB/phpbb/cache/driver/memcache.php
+++ b/phpBB/phpbb/cache/driver/memcache.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -32,7 +36,6 @@ if (!defined('PHPBB_ACM_MEMCACHE'))
/**
* ACM for Memcached
-* @package acm
*/
class memcache extends \phpbb\cache\driver\memory
{
@@ -47,7 +50,7 @@ class memcache extends \phpbb\cache\driver\memory
parent::__construct();
$this->memcache = new \Memcache;
- foreach(explode(',', PHPBB_ACM_MEMCACHE) as $u)
+ foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u)
{
$parts = explode('/', $u);
$this->memcache->addServer(trim($parts[0]), trim($parts[1]));
@@ -56,9 +59,7 @@ class memcache extends \phpbb\cache\driver\memory
}
/**
- * Unload the cache resources
- *
- * @return null
+ * {@inheritDoc}
*/
function unload()
{
@@ -68,9 +69,7 @@ class memcache extends \phpbb\cache\driver\memory
}
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
diff --git a/phpBB/phpbb/cache/driver/memory.php b/phpBB/phpbb/cache/driver/memory.php
index 292024212b..baae22d809 100644
--- a/phpBB/phpbb/cache/driver/memory.php
+++ b/phpBB/phpbb/cache/driver/memory.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,19 +15,11 @@ namespace phpbb\cache\driver;
/**
* ACM Abstract Memory Class
-* @package acm
*/
abstract class memory extends \phpbb\cache\driver\base
{
var $key_prefix;
- var $vars = array();
- var $is_modified = false;
-
- var $sql_rowset = array();
- var $sql_row_pointer = array();
- var $cache_dir = '';
-
/**
* Set cache path
*/
@@ -50,7 +46,7 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Load global cache
+ * {@inheritDoc}
*/
function load()
{
@@ -66,22 +62,7 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Unload cache object
- */
- function unload()
- {
- $this->save();
- unset($this->vars);
- unset($this->sql_rowset);
- unset($this->sql_row_pointer);
-
- $this->vars = array();
- $this->sql_rowset = array();
- $this->sql_row_pointer = array();
- }
-
- /**
- * Save modified objects
+ * {@inheritDoc}
*/
function save()
{
@@ -96,17 +77,18 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Tidy cache
+ * {@inheritDoc}
*/
function tidy()
{
- // cache has auto GC, no need to have any code here :)
+ global $config;
- set_config('cache_last_gc', time(), true);
+ // cache has auto GC, no need to have any code here :)
+ $config->set('cache_last_gc', time(), false);
}
/**
- * Get saved cache object
+ * {@inheritDoc}
*/
function get($var_name)
{
@@ -126,7 +108,7 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Put data into cache
+ * {@inheritDoc}
*/
function put($var_name, $var, $ttl = 2592000)
{
@@ -142,48 +124,7 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Purge cache data
- */
- function purge()
- {
- // Purge all phpbb cache files
- $dir = @opendir($this->cache_dir);
-
- if (!$dir)
- {
- return;
- }
-
- while (($entry = readdir($dir)) !== false)
- {
- if (strpos($entry, 'container_') !== 0 &&
- strpos($entry, 'url_matcher') !== 0 &&
- strpos($entry, 'sql_') !== 0 &&
- strpos($entry, 'data_') !== 0 &&
- strpos($entry, 'ctpl_') !== 0 &&
- strpos($entry, 'tpl_') !== 0)
- {
- continue;
- }
-
- $this->remove_file($this->cache_dir . $entry);
- }
- closedir($dir);
-
- unset($this->vars);
- unset($this->sql_rowset);
- unset($this->sql_row_pointer);
-
- $this->vars = array();
- $this->sql_rowset = array();
- $this->sql_row_pointer = array();
-
- $this->is_modified = false;
- }
-
-
- /**
- * Destroy cache data
+ * {@inheritDoc}
*/
function destroy($var_name, $table = '')
{
@@ -237,7 +178,7 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Check if a given cache entry exist
+ * {@inheritDoc}
*/
function _exists($var_name)
{
@@ -257,43 +198,35 @@ abstract class memory extends \phpbb\cache\driver\base
}
/**
- * Load cached sql query
- */
- function sql_load($query)
- {
- // Remove extra spaces and tabs
- $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
- $query_id = sizeof($this->sql_rowset);
-
- if (($result = $this->_read('sql_' . md5($query))) === false)
- {
- return false;
- }
-
- $this->sql_rowset[$query_id] = $result;
- $this->sql_row_pointer[$query_id] = 0;
-
- return $query_id;
- }
-
- /**
* {@inheritDoc}
*/
- function sql_save(\phpbb\db\driver\driver $db, $query, $query_result, $ttl)
+ function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
{
// Remove extra spaces and tabs
$query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
- $hash = md5($query);
+ $query_id = md5($query);
// determine which tables this query belongs to
// Some queries use backticks, namely the get_database_size() query
// don't check for conformity, the SQL would error and not reach here.
- if (!preg_match('/FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?/', $query, $regs))
+ if (!preg_match_all('/(?:FROM \\(?(`?\\w+`?(?: \\w+)?(?:, ?`?\\w+`?(?: \\w+)?)*)\\)?)|(?:JOIN (`?\\w+`?(?: \\w+)?))/', $query, $regs, PREG_SET_ORDER))
{
// Bail out if the match fails.
return $query_result;
}
- $tables = array_map('trim', explode(',', $regs[1]));
+
+ $tables = array();
+ foreach ($regs as $match)
+ {
+ if ($match[0][0] == 'F')
+ {
+ $tables = array_merge($tables, array_map('trim', explode(',', $match[1])));
+ }
+ else
+ {
+ $tables[] = $match[2];
+ }
+ }
foreach ($tables as $table_name)
{
@@ -312,14 +245,13 @@ abstract class memory extends \phpbb\cache\driver\base
$temp = array();
}
- $temp[$hash] = true;
+ $temp[$query_id] = true;
// This must never expire
$this->_write('sql_' . $table_name, $temp, 0);
}
// store them in the right place
- $query_id = sizeof($this->sql_rowset);
$this->sql_rowset[$query_id] = array();
$this->sql_row_pointer[$query_id] = 0;
@@ -329,96 +261,12 @@ abstract class memory extends \phpbb\cache\driver\base
}
$db->sql_freeresult($query_result);
- $this->_write('sql_' . $hash, $this->sql_rowset[$query_id], $ttl);
+ $this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl);
return $query_id;
}
/**
- * Ceck if a given sql query exist in cache
- */
- function sql_exists($query_id)
- {
- return isset($this->sql_rowset[$query_id]);
- }
-
- /**
- * Fetch row from cache (database)
- */
- function sql_fetchrow($query_id)
- {
- if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
- {
- return $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++];
- }
-
- return false;
- }
-
- /**
- * Fetch a field from the current row of a cached database result (database)
- */
- function sql_fetchfield($query_id, $field)
- {
- if ($this->sql_row_pointer[$query_id] < sizeof($this->sql_rowset[$query_id]))
- {
- return (isset($this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]][$field])) ? $this->sql_rowset[$query_id][$this->sql_row_pointer[$query_id]++][$field] : false;
- }
-
- return false;
- }
-
- /**
- * Seek a specific row in an a cached database result (database)
- */
- function sql_rowseek($rownum, $query_id)
- {
- if ($rownum >= sizeof($this->sql_rowset[$query_id]))
- {
- return false;
- }
-
- $this->sql_row_pointer[$query_id] = $rownum;
- return true;
- }
-
- /**
- * Free memory used for a cached database result (database)
- */
- function sql_freeresult($query_id)
- {
- if (!isset($this->sql_rowset[$query_id]))
- {
- return false;
- }
-
- unset($this->sql_rowset[$query_id]);
- unset($this->sql_row_pointer[$query_id]);
-
- return true;
- }
-
- /**
- * Removes/unlinks file
- */
- function remove_file($filename, $check = false)
- {
- if (!function_exists('phpbb_is_writable'))
- {
- global $phpbb_root_path, $phpEx;
- include($phpbb_root_path . 'includes/functions.' . $phpEx);
- }
-
- if ($check && !phpbb_is_writable($this->cache_dir))
- {
- // E_USER_ERROR - not using language entry - intended.
- trigger_error('Unable to remove files within ' . $this->cache_dir . '. Please check directory permissions.', E_USER_ERROR);
- }
-
- return @unlink($filename);
- }
-
- /**
* Check if a cache var exists
*
* @access protected
diff --git a/phpBB/phpbb/cache/driver/redis.php b/phpBB/phpbb/cache/driver/redis.php
index 2b6f9bf36d..eda774491c 100644
--- a/phpBB/phpbb/cache/driver/redis.php
+++ b/phpBB/phpbb/cache/driver/redis.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -25,7 +29,6 @@ if (!defined('PHPBB_ACM_REDIS_HOST'))
* Compatible with the php extension phpredis available
* at https://github.com/nicolasff/phpredis
*
-* @package acm
*/
class redis extends \phpbb\cache\driver\memory
{
@@ -92,9 +95,7 @@ class redis extends \phpbb\cache\driver\memory
}
/**
- * Unload the cache resources
- *
- * @return null
+ * {@inheritDoc}
*/
function unload()
{
@@ -104,9 +105,7 @@ class redis extends \phpbb\cache\driver\memory
}
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
diff --git a/phpBB/phpbb/cache/driver/wincache.php b/phpBB/phpbb/cache/driver/wincache.php
index 1f040e9ab2..632b534362 100644
--- a/phpBB/phpbb/cache/driver/wincache.php
+++ b/phpBB/phpbb/cache/driver/wincache.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,16 +15,13 @@ namespace phpbb\cache\driver;
/**
* ACM for WinCache
-* @package acm
*/
class wincache extends \phpbb\cache\driver\memory
{
var $extension = 'wincache';
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
diff --git a/phpBB/phpbb/cache/driver/xcache.php b/phpBB/phpbb/cache/driver/xcache.php
index 4d0d683b3d..0c845a6a8d 100644
--- a/phpBB/phpbb/cache/driver/xcache.php
+++ b/phpBB/phpbb/cache/driver/xcache.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005, 2009 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\cache\driver;
/**
* ACM for XCache
-* @package acm
*
* To use this module you need ini_get() enabled and the following INI settings configured as follows:
* - xcache.var_size > 0
@@ -33,9 +36,7 @@ class xcache extends \phpbb\cache\driver\memory
}
/**
- * Purge cache data
- *
- * @return null
+ * {@inheritDoc}
*/
function purge()
{
diff --git a/phpBB/phpbb/cache/service.php b/phpBB/phpbb/cache/service.php
index ebbcfb8cdb..a022c00bc6 100644
--- a/phpBB/phpbb/cache/service.php
+++ b/phpBB/phpbb/cache/service.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package acm
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\cache;
/**
* Class for grabbing/handling cached entries
-* @package acm
*/
class service
{
@@ -32,7 +35,7 @@ class service
/**
* Database connection.
*
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -44,7 +47,7 @@ class service
protected $phpbb_root_path;
/**
- * PHP extension.
+ * PHP file extension.
*
* @var string
*/
@@ -55,11 +58,11 @@ class service
*
* @param \phpbb\cache\driver\driver_interface $driver The cache driver
* @param \phpbb\config\config $config The config
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param string $phpbb_root_path Root path
- * @param string $php_ext PHP extension
+ * @param string $php_ext PHP file extension
*/
- public function __construct(\phpbb\cache\driver\driver_interface $driver, \phpbb\config\config $config, \phpbb\db\driver\driver $db, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\cache\driver\driver_interface $driver, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, $phpbb_root_path, $php_ext)
{
$this->set_driver($driver);
$this->config = $config;
@@ -138,6 +141,7 @@ class service
$icons[$row['icons_id']]['img'] = $row['icons_url'];
$icons[$row['icons_id']]['width'] = (int) $row['icons_width'];
$icons[$row['icons_id']]['height'] = (int) $row['icons_height'];
+ $icons[$row['icons_id']]['alt'] = ($row['icons_alt']) ? $row['icons_alt'] : '';
$icons[$row['icons_id']]['display'] = (bool) $row['display_on_posting'];
}
$this->db->sql_freeresult($result);
@@ -165,18 +169,12 @@ class service
{
if ($row['rank_special'])
{
- $ranks['special'][$row['rank_id']] = array(
- 'rank_title' => $row['rank_title'],
- 'rank_image' => $row['rank_image']
- );
+ unset($row['rank_min']);
+ $ranks['special'][$row['rank_id']] = $row;
}
else
{
- $ranks['normal'][] = array(
- 'rank_title' => $row['rank_title'],
- 'rank_min' => $row['rank_min'],
- 'rank_image' => $row['rank_image']
- );
+ $ranks['normal'][$row['rank_id']] = $row;
}
}
$this->db->sql_freeresult($result);
@@ -302,7 +300,7 @@ class service
{
if (($bots = $this->driver->get('_bots')) === false)
{
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'mssql':
case 'mssql_odbc':
@@ -313,13 +311,6 @@ class service
ORDER BY LEN(bot_agent) DESC';
break;
- case 'firebird':
- $sql = 'SELECT user_id, bot_agent, bot_ip
- FROM ' . BOTS_TABLE . '
- WHERE bot_active = 1
- ORDER BY CHAR_LENGTH(bot_agent) DESC';
- break;
-
// LENGTH supported by MySQL, IBM DB2 and Oracle for sure...
default:
$sql = 'SELECT user_id, bot_agent, bot_ip
diff --git a/phpBB/phpbb/captcha/char_cube3d.php b/phpBB/phpbb/captcha/char_cube3d.php
new file mode 100644
index 0000000000..a712b16dce
--- /dev/null
+++ b/phpBB/phpbb/captcha/char_cube3d.php
@@ -0,0 +1,277 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+class char_cube3d
+{
+ var $bitmap;
+ var $bitmap_width;
+ var $bitmap_height;
+
+ var $basis_matrix = array(array(1, 0, 0), array(0, 1, 0), array(0, 0, 1));
+ var $abs_x = array(1, 0);
+ var $abs_y = array(0, 1);
+ var $x = 0;
+ var $y = 1;
+ var $z = 2;
+ var $letter = '';
+
+ /**
+ */
+ function __construct(&$bitmaps, $letter)
+ {
+ $this->bitmap = $bitmaps['data'][$letter];
+ $this->bitmap_width = $bitmaps['width'];
+ $this->bitmap_height = $bitmaps['height'];
+
+ $this->basis_matrix[0][0] = mt_rand(-600, 600);
+ $this->basis_matrix[0][1] = mt_rand(-600, 600);
+ $this->basis_matrix[0][2] = (mt_rand(0, 1) * 2000) - 1000;
+ $this->basis_matrix[1][0] = mt_rand(-1000, 1000);
+ $this->basis_matrix[1][1] = mt_rand(-1000, 1000);
+ $this->basis_matrix[1][2] = mt_rand(-1000, 1000);
+
+ $this->normalize($this->basis_matrix[0]);
+ $this->normalize($this->basis_matrix[1]);
+ $this->basis_matrix[2] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[1]);
+ $this->normalize($this->basis_matrix[2]);
+
+ // $this->basis_matrix[1] might not be (probably isn't) orthogonal to $basis_matrix[0]
+ $this->basis_matrix[1] = $this->cross_product($this->basis_matrix[0], $this->basis_matrix[2]);
+ $this->normalize($this->basis_matrix[1]);
+
+ // Make sure our cube is facing into the canvas (assuming +z == in)
+ for ($i = 0; $i < 3; ++$i)
+ {
+ if ($this->basis_matrix[$i][2] < 0)
+ {
+ $this->basis_matrix[$i][0] *= -1;
+ $this->basis_matrix[$i][1] *= -1;
+ $this->basis_matrix[$i][2] *= -1;
+ }
+ }
+
+ // Force our "z" basis vector to be the one with greatest absolute z value
+ $this->x = 0;
+ $this->y = 1;
+ $this->z = 2;
+
+ // Swap "y" with "z"
+ if ($this->basis_matrix[1][2] > $this->basis_matrix[2][2])
+ {
+ $this->z = 1;
+ $this->y = 2;
+ }
+
+ // Swap "x" with "z"
+ if ($this->basis_matrix[0][2] > $this->basis_matrix[$this->z][2])
+ {
+ $this->x = $this->z;
+ $this->z = 0;
+ }
+
+ // Still need to determine which of $x,$y are which.
+ // wrong orientation if y's y-component is less than it's x-component
+ // likewise if x's x-component is less than it's y-component
+ // if they disagree, go with the one with the greater weight difference.
+ // rotate if positive
+ $weight = (abs($this->basis_matrix[$this->x][1]) - abs($this->basis_matrix[$this->x][0])) + (abs($this->basis_matrix[$this->y][0]) - abs($this->basis_matrix[$this->y][1]));
+
+ // Swap "x" with "y"
+ if ($weight > 0)
+ {
+ list($this->x, $this->y) = array($this->y, $this->x);
+ }
+
+ $this->abs_x = array($this->basis_matrix[$this->x][0], $this->basis_matrix[$this->x][1]);
+ $this->abs_y = array($this->basis_matrix[$this->y][0], $this->basis_matrix[$this->y][1]);
+
+ if ($this->abs_x[0] < 0)
+ {
+ $this->abs_x[0] *= -1;
+ $this->abs_x[1] *= -1;
+ }
+
+ if ($this->abs_y[1] > 0)
+ {
+ $this->abs_y[0] *= -1;
+ $this->abs_y[1] *= -1;
+ }
+
+ $this->letter = $letter;
+ }
+
+ /**
+ * Draw a character
+ */
+ function drawchar($scale, $xoff, $yoff, $img, $background, $colours)
+ {
+ $width = $this->bitmap_width;
+ $height = $this->bitmap_height;
+ $bitmap = $this->bitmap;
+
+ $colour1 = $colours[array_rand($colours)];
+ $colour2 = $colours[array_rand($colours)];
+
+ $swapx = ($this->basis_matrix[$this->x][0] > 0);
+ $swapy = ($this->basis_matrix[$this->y][1] < 0);
+
+ for ($y = 0; $y < $height; ++$y)
+ {
+ for ($x = 0; $x < $width; ++$x)
+ {
+ $xp = ($swapx) ? ($width - $x - 1) : $x;
+ $yp = ($swapy) ? ($height - $y - 1) : $y;
+
+ if ($bitmap[$height - $yp - 1][$xp])
+ {
+ $dx = $this->scale($this->abs_x, ($xp - ($swapx ? ($width / 2) : ($width / 2) - 1)) * $scale);
+ $dy = $this->scale($this->abs_y, ($yp - ($swapy ? ($height / 2) : ($height / 2) - 1)) * $scale);
+ $xo = $xoff + $dx[0] + $dy[0];
+ $yo = $yoff + $dx[1] + $dy[1];
+
+ $origin = array(0, 0, 0);
+ $xvec = $this->scale($this->basis_matrix[$this->x], $scale);
+ $yvec = $this->scale($this->basis_matrix[$this->y], $scale);
+ $face_corner = $this->sum2($xvec, $yvec);
+
+ $zvec = $this->scale($this->basis_matrix[$this->z], $scale);
+ $x_corner = $this->sum2($xvec, $zvec);
+ $y_corner = $this->sum2($yvec, $zvec);
+
+ imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $xvec, $x_corner,$zvec), 4, $colour1);
+ imagefilledpolygon($img, $this->gen_poly($xo, $yo, $origin, $yvec, $y_corner,$zvec), 4, $colour2);
+
+ $face = $this->gen_poly($xo, $yo, $origin, $xvec, $face_corner, $yvec);
+
+ imagefilledpolygon($img, $face, 4, $background);
+ imagepolygon($img, $face, 4, $colour1);
+ }
+ }
+ }
+ }
+
+ /*
+ * return a roughly acceptable range of sizes for rendering with this texttype
+ */
+ function range()
+ {
+ return array(3, 4);
+ }
+
+ /**
+ * Vector length
+ */
+ function vectorlen($vector)
+ {
+ return sqrt(pow($vector[0], 2) + pow($vector[1], 2) + pow($vector[2], 2));
+ }
+
+ /**
+ * Normalize
+ */
+ function normalize(&$vector, $length = 1)
+ {
+ $length = (( $length < 1) ? 1 : $length);
+ $length /= $this->vectorlen($vector);
+ $vector[0] *= $length;
+ $vector[1] *= $length;
+ $vector[2] *= $length;
+ }
+
+ /**
+ */
+ function cross_product($vector1, $vector2)
+ {
+ $retval = array(0, 0, 0);
+ $retval[0] = (($vector1[1] * $vector2[2]) - ($vector1[2] * $vector2[1]));
+ $retval[1] = -(($vector1[0] * $vector2[2]) - ($vector1[2] * $vector2[0]));
+ $retval[2] = (($vector1[0] * $vector2[1]) - ($vector1[1] * $vector2[0]));
+
+ return $retval;
+ }
+
+ /**
+ */
+ function sum($vector1, $vector2)
+ {
+ return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1], $vector1[2] + $vector2[2]);
+ }
+
+ /**
+ */
+ function sum2($vector1, $vector2)
+ {
+ return array($vector1[0] + $vector2[0], $vector1[1] + $vector2[1]);
+ }
+
+ /**
+ */
+ function scale($vector, $length)
+ {
+ if (sizeof($vector) == 2)
+ {
+ return array($vector[0] * $length, $vector[1] * $length);
+ }
+
+ return array($vector[0] * $length, $vector[1] * $length, $vector[2] * $length);
+ }
+
+ /**
+ */
+ function gen_poly($xoff, $yoff, &$vec1, &$vec2, &$vec3, &$vec4)
+ {
+ $poly = array();
+ $poly[0] = $xoff + $vec1[0];
+ $poly[1] = $yoff + $vec1[1];
+ $poly[2] = $xoff + $vec2[0];
+ $poly[3] = $yoff + $vec2[1];
+ $poly[4] = $xoff + $vec3[0];
+ $poly[5] = $yoff + $vec3[1];
+ $poly[6] = $xoff + $vec4[0];
+ $poly[7] = $yoff + $vec4[1];
+
+ return $poly;
+ }
+
+ /**
+ * dimensions
+ */
+ function dimensions($size)
+ {
+ $xn = $this->scale($this->basis_matrix[$this->x], -($this->bitmap_width / 2) * $size);
+ $xp = $this->scale($this->basis_matrix[$this->x], ($this->bitmap_width / 2) * $size);
+ $yn = $this->scale($this->basis_matrix[$this->y], -($this->bitmap_height / 2) * $size);
+ $yp = $this->scale($this->basis_matrix[$this->y], ($this->bitmap_height / 2) * $size);
+
+ $p = array();
+ $p[0] = $this->sum2($xn, $yn);
+ $p[1] = $this->sum2($xp, $yn);
+ $p[2] = $this->sum2($xp, $yp);
+ $p[3] = $this->sum2($xn, $yp);
+
+ $min_x = $max_x = $p[0][0];
+ $min_y = $max_y = $p[0][1];
+
+ for ($i = 1; $i < 4; ++$i)
+ {
+ $min_x = ($min_x > $p[$i][0]) ? $p[$i][0] : $min_x;
+ $min_y = ($min_y > $p[$i][1]) ? $p[$i][1] : $min_y;
+ $max_x = ($max_x < $p[$i][0]) ? $p[$i][0] : $max_x;
+ $max_y = ($max_y < $p[$i][1]) ? $p[$i][1] : $max_y;
+ }
+
+ return array($min_x, $min_y, $max_x, $max_y);
+ }
+}
diff --git a/phpBB/phpbb/captcha/colour_manager.php b/phpBB/phpbb/captcha/colour_manager.php
new file mode 100644
index 0000000000..6ca3c3fd2c
--- /dev/null
+++ b/phpBB/phpbb/captcha/colour_manager.php
@@ -0,0 +1,527 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+class colour_manager
+{
+ var $img;
+ var $mode;
+ var $colours;
+ var $named_colours;
+
+ /**
+ * Create the colour manager, link it to the image resource
+ */
+ function __construct($img, $background = false, $mode = 'ahsv')
+ {
+ $this->img = $img;
+ $this->mode = $mode;
+ $this->colours = array();
+ $this->named_colours = array();
+
+ if ($background !== false)
+ {
+ $bg = $this->allocate_named('background', $background);
+ imagefill($this->img, 0, 0, $bg);
+ }
+ }
+
+ /**
+ * Lookup a named colour resource
+ */
+ function get_resource($named_colour)
+ {
+ if (isset($this->named_colours[$named_colour]))
+ {
+ return $this->named_colours[$named_colour];
+ }
+
+ if (isset($this->named_rgb[$named_colour]))
+ {
+ return $this->allocate_named($named_colour, $this->named_rgb[$named_colour], 'rgb');
+ }
+
+ return false;
+ }
+
+ /**
+ * Assign a name to a colour resource
+ */
+ function name_colour($name, $resource)
+ {
+ $this->named_colours[$name] = $resource;
+ }
+
+ /**
+ * names and allocates a colour resource
+ */
+ function allocate_named($name, $colour, $mode = false)
+ {
+ $resource = $this->allocate($colour, $mode);
+
+ if ($resource !== false)
+ {
+ $this->name_colour($name, $resource);
+ }
+ return $resource;
+ }
+
+ /**
+ * allocates a specified colour into the image
+ */
+ function allocate($colour, $mode = false)
+ {
+ if ($mode === false)
+ {
+ $mode = $this->mode;
+ }
+
+ if (!is_array($colour))
+ {
+ if (isset($this->named_rgb[$colour]))
+ {
+ return $this->allocate_named($colour, $this->named_rgb[$colour], 'rgb');
+ }
+
+ if (!is_int($colour))
+ {
+ return false;
+ }
+
+ $mode = 'rgb';
+ $colour = array(255 & ($colour >> 16), 255 & ($colour >> 8), 255 & $colour);
+ }
+
+ if (isset($colour['mode']))
+ {
+ $mode = $colour['mode'];
+ unset($colour['mode']);
+ }
+
+ if (isset($colour['random']))
+ {
+ unset($colour['random']);
+ // everything else is params
+ return $this->random_colour($colour, $mode);
+ }
+
+ $rgb = $this->model_convert($colour, $mode, 'rgb');
+ $store = ($this->mode == 'rgb') ? $rgb : $this->model_convert($colour, $mode, $this->mode);
+ $resource = imagecolorallocate($this->img, $rgb[0], $rgb[1], $rgb[2]);
+ $this->colours[$resource] = $store;
+
+ return $resource;
+ }
+
+ /**
+ * randomly generates a colour, with optional params
+ */
+ function random_colour($params = array(), $mode = false)
+ {
+ if ($mode === false)
+ {
+ $mode = $this->mode;
+ }
+
+ switch ($mode)
+ {
+ case 'rgb':
+ // @TODO random rgb generation. do we intend to do this, or is it just too tedious?
+ break;
+
+ case 'ahsv':
+ case 'hsv':
+ default:
+
+ $default_params = array(
+ 'hue_bias' => false, // degree / 'r'/'g'/'b'/'c'/'m'/'y' /'o'
+ 'hue_range' => false, // if hue bias, then difference range +/- from bias
+ 'min_saturation' => 30, // 0 - 100
+ 'max_saturation' => 80, // 0 - 100
+ 'min_value' => 30, // 0 - 100
+ 'max_value' => 80, // 0 - 100
+ );
+
+ $alt = ($mode == 'ahsv') ? true : false;
+ $params = array_merge($default_params, $params);
+
+ $min_hue = 0;
+ $max_hue = 359;
+ $min_saturation = max(0, $params['min_saturation']);
+ $max_saturation = min(100, $params['max_saturation']);
+ $min_value = max(0, $params['min_value']);
+ $max_value = min(100, $params['max_value']);
+
+ if ($params['hue_bias'] !== false)
+ {
+ if (is_numeric($params['hue_bias']))
+ {
+ $h = intval($params['hue_bias']) % 360;
+ }
+ else
+ {
+ switch ($params['hue_bias'])
+ {
+ case 'o':
+ $h = $alt ? 60 : 30;
+ break;
+
+ case 'y':
+ $h = $alt ? 120 : 60;
+ break;
+
+ case 'g':
+ $h = $alt ? 180 : 120;
+ break;
+
+ case 'c':
+ $h = $alt ? 210 : 180;
+ break;
+
+ case 'b':
+ $h = 240;
+ break;
+
+ case 'm':
+ $h = 300;
+ break;
+
+ case 'r':
+ default:
+ $h = 0;
+ break;
+ }
+ }
+
+ $min_hue = $h + 360;
+ $max_hue = $h + 360;
+
+ if ($params['hue_range'])
+ {
+ $min_hue -= min(180, $params['hue_range']);
+ $max_hue += min(180, $params['hue_range']);
+ }
+ }
+
+ $h = mt_rand($min_hue, $max_hue);
+ $s = mt_rand($min_saturation, $max_saturation);
+ $v = mt_rand($min_value, $max_value);
+
+ return $this->allocate(array($h, $s, $v), $mode);
+
+ break;
+ }
+ }
+
+ /**
+ */
+ function colour_scheme($resource, $include_original = true)
+ {
+ $mode = 'hsv';
+
+ if (($pre = $this->get_resource($resource)) !== false)
+ {
+ $resource = $pre;
+ }
+
+ $colour = $this->model_convert($this->colours[$resource], $this->mode, $mode);
+ $results = ($include_original) ? array($resource) : array();
+ $colour2 = $colour3 = $colour4 = $colour;
+ $colour2[0] += 150;
+ $colour3[0] += 180;
+ $colour4[0] += 210;
+
+ $results[] = $this->allocate($colour2, $mode);
+ $results[] = $this->allocate($colour3, $mode);
+ $results[] = $this->allocate($colour4, $mode);
+
+ return $results;
+ }
+
+ /**
+ */
+ function mono_range($resource, $count = 5, $include_original = true)
+ {
+ if (is_array($resource))
+ {
+ $results = array();
+ for ($i = 0, $size = sizeof($resource); $i < $size; ++$i)
+ {
+ $results = array_merge($results, $this->mono_range($resource[$i], $count, $include_original));
+ }
+ return $results;
+ }
+
+ $mode = (in_array($this->mode, array('hsv', 'ahsv'), true) ? $this->mode : 'ahsv');
+ if (($pre = $this->get_resource($resource)) !== false)
+ {
+ $resource = $pre;
+ }
+
+ $colour = $this->model_convert($this->colours[$resource], $this->mode, $mode);
+
+ $results = array();
+ if ($include_original)
+ {
+ $results[] = $resource;
+ $count--;
+ }
+
+ // This is a hard problem. I chicken out and try to maintain readability at the cost of less randomness.
+
+ while ($count > 0)
+ {
+ $colour[1] = ($colour[1] + mt_rand(40,60)) % 99;
+ $colour[2] = ($colour[2] + mt_rand(40,60));
+ $results[] = $this->allocate($colour, $mode);
+ $count--;
+ }
+ return $results;
+ }
+
+ /**
+ * Convert from one colour model to another
+ */
+ function model_convert($colour, $from_model, $to_model)
+ {
+ if ($from_model == $to_model)
+ {
+ return $colour;
+ }
+
+ switch ($to_model)
+ {
+ case 'hsv':
+
+ switch ($from_model)
+ {
+ case 'ahsv':
+ return $this->ah2h($colour);
+ break;
+
+ case 'rgb':
+ return $this->rgb2hsv($colour);
+ break;
+ }
+ break;
+
+ case 'ahsv':
+
+ switch ($from_model)
+ {
+ case 'hsv':
+ return $this->h2ah($colour);
+ break;
+
+ case 'rgb':
+ return $this->h2ah($this->rgb2hsv($colour));
+ break;
+ }
+ break;
+
+ case 'rgb':
+ switch ($from_model)
+ {
+ case 'hsv':
+ return $this->hsv2rgb($colour);
+ break;
+
+ case 'ahsv':
+ return $this->hsv2rgb($this->ah2h($colour));
+ break;
+ }
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Slightly altered from wikipedia's algorithm
+ */
+ function hsv2rgb($hsv)
+ {
+ $this->normalize_hue($hsv[0]);
+
+ $h = $hsv[0];
+ $s = min(1, max(0, $hsv[1] / 100));
+ $v = min(1, max(0, $hsv[2] / 100));
+
+ // calculate hue sector
+ $hi = floor($hsv[0] / 60);
+
+ // calculate opposite colour
+ $p = $v * (1 - $s);
+
+ // calculate distance between hex vertices
+ $f = ($h / 60) - $hi;
+
+ // coming in or going out?
+ if (!($hi & 1))
+ {
+ $f = 1 - $f;
+ }
+
+ // calculate adjacent colour
+ $q = $v * (1 - ($f * $s));
+
+ switch ($hi)
+ {
+ case 0:
+ $rgb = array($v, $q, $p);
+ break;
+
+ case 1:
+ $rgb = array($q, $v, $p);
+ break;
+
+ case 2:
+ $rgb = array($p, $v, $q);
+ break;
+
+ case 3:
+ $rgb = array($p, $q, $v);
+ break;
+
+ case 4:
+ $rgb = array($q, $p, $v);
+ break;
+
+ case 5:
+ $rgb = array($v, $p, $q);
+ break;
+
+ default:
+ return array(0, 0, 0);
+ break;
+ }
+
+ return array(255 * $rgb[0], 255 * $rgb[1], 255 * $rgb[2]);
+ }
+
+ /**
+ * (more than) Slightly altered from wikipedia's algorithm
+ */
+ function rgb2hsv($rgb)
+ {
+ $r = min(255, max(0, $rgb[0]));
+ $g = min(255, max(0, $rgb[1]));
+ $b = min(255, max(0, $rgb[2]));
+ $max = max($r, $g, $b);
+ $min = min($r, $g, $b);
+
+ $v = $max / 255;
+ $s = (!$max) ? 0 : 1 - ($min / $max);
+
+ // if max - min is 0, we want hue to be 0 anyway.
+ $h = $max - $min;
+
+ if ($h)
+ {
+ switch ($max)
+ {
+ case $g:
+ $h = 120 + (60 * ($b - $r) / $h);
+ break;
+
+ case $b:
+ $h = 240 + (60 * ($r - $g) / $h);
+ break;
+
+ case $r:
+ $h = 360 + (60 * ($g - $b) / $h);
+ break;
+ }
+ }
+ $this->normalize_hue($h);
+
+ return array($h, $s * 100, $v * 100);
+ }
+
+ /**
+ */
+ function normalize_hue(&$hue)
+ {
+ $hue %= 360;
+
+ if ($hue < 0)
+ {
+ $hue += 360;
+ }
+ }
+
+ /**
+ * Alternate hue to hue
+ */
+ function ah2h($ahue)
+ {
+ if (is_array($ahue))
+ {
+ $ahue[0] = $this->ah2h($ahue[0]);
+ return $ahue;
+ }
+ $this->normalize_hue($ahue);
+
+ // blue through red is already ok
+ if ($ahue >= 240)
+ {
+ return $ahue;
+ }
+
+ // ahue green is at 180
+ if ($ahue >= 180)
+ {
+ // return (240 - (2 * (240 - $ahue)));
+ return (2 * $ahue) - 240; // equivalent
+ }
+
+ // ahue yellow is at 120 (RYB rather than RGB)
+ if ($ahue >= 120)
+ {
+ return $ahue - 60;
+ }
+
+ return $ahue / 2;
+ }
+
+ /**
+ * hue to Alternate hue
+ */
+ function h2ah($hue)
+ {
+ if (is_array($hue))
+ {
+ $hue[0] = $this->h2ah($hue[0]);
+ return $hue;
+ }
+ $this->normalize_hue($hue);
+
+ // blue through red is already ok
+ if ($hue >= 240)
+ {
+ return $hue;
+ }
+ else if ($hue <= 60)
+ {
+ return $hue * 2;
+ }
+ else if ($hue <= 120)
+ {
+ return $hue + 60;
+ }
+ else
+ {
+ return ($hue + 240) / 2;
+ }
+ }
+}
diff --git a/phpBB/phpbb/captcha/factory.php b/phpBB/phpbb/captcha/factory.php
new file mode 100644
index 0000000000..dd44aca8bb
--- /dev/null
+++ b/phpBB/phpbb/captcha/factory.php
@@ -0,0 +1,88 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+class factory
+{
+ /**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ private $container;
+
+ /**
+ * @var \phpbb\di\service_collection
+ */
+ private $plugins;
+
+ /**
+ * Constructor
+ *
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+ * @param \phpbb\di\service_collection $plugins
+ */
+ public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, \phpbb\di\service_collection $plugins)
+ {
+ $this->container = $container;
+ $this->plugins = $plugins;
+ }
+
+ /**
+ * Return a new instance of a given plugin
+ *
+ * @param $name
+ * @return object
+ */
+ public function get_instance($name)
+ {
+ return $this->container->get($name);
+ }
+
+ /**
+ * Call the garbage collector
+ *
+ * @param string $name The name to the captcha service.
+ */
+ function garbage_collect($name)
+ {
+ $captcha = $this->get_instance($name);
+ $captcha->garbage_collect(0);
+ }
+
+ /**
+ * Return a list of all registered CAPTCHA plugins
+ *
+ * @returns array
+ */
+ function get_captcha_types()
+ {
+ $captchas = array(
+ 'available' => array(),
+ 'unavailable' => array(),
+ );
+
+ foreach ($this->plugins as $plugin => $plugin_instance)
+ {
+ if ($plugin_instance->is_available())
+ {
+ $captchas['available'][$plugin] = $plugin_instance->get_name();
+ }
+ else
+ {
+ $captchas['unavailable'][$plugin] = $plugin_instance->get_name();
+ }
+ }
+
+ return $captchas;
+ }
+}
diff --git a/phpBB/phpbb/captcha/gd.php b/phpBB/phpbb/captcha/gd.php
new file mode 100644
index 0000000000..e9538439c6
--- /dev/null
+++ b/phpBB/phpbb/captcha/gd.php
@@ -0,0 +1,1844 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+class gd
+{
+ var $width = 360;
+ var $height = 96;
+
+ /**
+ * Create the image containing $code with a seed of $seed
+ */
+ function execute($code, $seed)
+ {
+ global $config;
+
+ mt_srand($seed);
+
+ // Create image
+ $img = imagecreatetruecolor($this->width, $this->height);
+
+ // Generate colours
+ $colour = new colour_manager($img, array(
+ 'random' => true,
+ 'min_value' => 60,
+ ), 'hsv');
+
+ $scheme = $colour->colour_scheme('background', false);
+ $scheme = $colour->mono_range($scheme, 10, false);
+ shuffle($scheme);
+
+ $bg_colours = array_splice($scheme, mt_rand(6, 12));
+
+ // Generate code characters
+ $characters = $sizes = $bounding_boxes = $noise = array();
+ $width_avail = $this->width - 15;
+ $code_len = strlen($code);
+ $captcha_bitmaps = $this->captcha_bitmaps();
+
+ for ($i = 0; $i < $code_len; ++$i)
+ {
+ $characters[$i] = new char_cube3d($captcha_bitmaps, $code[$i]);
+
+ list($min, $max) = $characters[$i]->range();
+ $sizes[$i] = mt_rand($min, $max);
+
+ $box = $characters[$i]->dimensions($sizes[$i]);
+ $width_avail -= ($box[2] - $box[0]);
+ $bounding_boxes[$i] = $box;
+ }
+
+ // Redistribute leftover x-space
+ $offset = array();
+ for ($i = 0; $i < $code_len; ++$i)
+ {
+ $denom = ($code_len - $i);
+ $denom = max(1.3, $denom);
+ $offset[$i] = phpbb_mt_rand(0, (int) round((1.5 * $width_avail) / $denom));
+ $width_avail -= $offset[$i];
+ }
+
+ if ($config['captcha_gd_x_grid'])
+ {
+ $grid = (int) $config['captcha_gd_x_grid'];
+ for ($y = 0; $y < $this->height; $y += mt_rand($grid - 2, $grid + 2))
+ {
+ $current_colour = $scheme[array_rand($scheme)];
+ imageline($img, mt_rand(0,4), mt_rand($y - 3, $y), mt_rand($this->width - 5, $this->width), mt_rand($y - 3, $y), $current_colour);
+ }
+ }
+
+ if ($config['captcha_gd_y_grid'])
+ {
+ $grid = (int) $config['captcha_gd_y_grid'];
+ for ($x = 0; $x < $this->width; $x += mt_rand($grid - 2, $grid + 2))
+ {
+ $current_colour = $scheme[array_rand($scheme)];
+ imagedashedline($img, mt_rand($x -3, $x + 3), mt_rand(0, 4), mt_rand($x -3, $x + 3), mt_rand($this->height - 5, $this->height), $current_colour);
+ }
+ }
+
+ if ($config['captcha_gd_wave'] && ($config['captcha_gd_y_grid'] || $config['captcha_gd_y_grid']))
+ {
+ $this->wave($img);
+ }
+
+ if ($config['captcha_gd_3d_noise'])
+ {
+ $noise_bitmaps = $this->captcha_noise_bg_bitmaps();
+ for ($i = 0; $i < $code_len; ++$i)
+ {
+ $noise[$i] = new char_cube3d($noise_bitmaps, mt_rand(1, sizeof($noise_bitmaps['data'])));
+
+ $noise[$i]->range();
+ //$box = $noise[$i]->dimensions($sizes[$i]);
+ }
+ $xoffset = 0;
+ for ($i = 0; $i < $code_len; ++$i)
+ {
+ $dimm = $bounding_boxes[$i];
+ $xoffset += ($offset[$i] - $dimm[0]);
+ $yoffset = mt_rand(-$dimm[1], $this->height - $dimm[3]);
+
+ $noise[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $colour->get_resource('background'), $scheme);
+ $xoffset += $dimm[2];
+ }
+ }
+
+ $xoffset = 5;
+ for ($i = 0; $i < $code_len; ++$i)
+ {
+ $dimm = $bounding_boxes[$i];
+ $xoffset += ($offset[$i] - $dimm[0]);
+ $yoffset = mt_rand(-$dimm[1], $this->height - $dimm[3]);
+
+ $characters[$i]->drawchar($sizes[$i], $xoffset, $yoffset, $img, $colour->get_resource('background'), $scheme);
+ $xoffset += $dimm[2];
+ }
+
+ if ($config['captcha_gd_wave'])
+ {
+ $this->wave($img);
+ }
+
+ if ($config['captcha_gd_foreground_noise'])
+ {
+ $this->noise_line($img, 0, 0, $this->width, $this->height, $colour->get_resource('background'), $scheme, $bg_colours);
+ }
+
+ // Send image
+ header('Content-Type: image/png');
+ header('Cache-control: no-cache, no-store');
+ imagepng($img);
+ imagedestroy($img);
+ }
+
+ /**
+ * Sinus
+ */
+ function wave($img)
+ {
+ $period_x = mt_rand(12,18);
+ $period_y = mt_rand(7,14);
+ $amp_x = mt_rand(5,10);
+ $amp_y = mt_rand(2,4);
+ $socket = mt_rand(0,100);
+
+ $dampen_x = mt_rand($this->width/5, $this->width/2);
+ $dampen_y = mt_rand($this->height/5, $this->height/2);
+ $direction_x = (mt_rand (0, 1));
+ $direction_y = (mt_rand (0, 1));
+
+ for ($i = 0; $i < $this->width; $i++)
+ {
+ $dir = ($direction_x) ? $i : ($this->width - $i);
+ imagecopy($img, $img, $i-1, sin($socket+ $i/($period_x + $dir/$dampen_x)) * $amp_x, $i, 0, 1, $this->height);
+ }
+ $socket = mt_rand(0,100);
+ for ($i = 0; $i < $this->height; $i++)
+ {
+ $dir = ($direction_y) ? $i : ($this->height - $i);
+ imagecopy($img, $img ,sin($socket + $i/($period_y + ($dir)/$dampen_y)) * $amp_y, $i-1, 0, $i, $this->width, 1);
+ }
+ return $img;
+ }
+
+ /**
+ * Noise line
+ */
+ function noise_line($img, $min_x, $min_y, $max_x, $max_y, $bg, $font, $non_font)
+ {
+ imagesetthickness($img, 2);
+
+ $x1 = $min_x;
+ $x2 = $max_x;
+ $y1 = $min_y;
+ $y2 = $min_y;
+
+ do
+ {
+ $line = array_merge(
+ array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
+ array_fill(0, mt_rand(30, 60), $bg)
+ );
+
+ imagesetstyle($img, $line);
+ imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
+
+ $y1 += mt_rand(12, 35);
+ $y2 += mt_rand(12, 35);
+ }
+ while ($y1 < $max_y && $y2 < $max_y);
+
+ $x1 = $min_x;
+ $x2 = $min_x;
+ $y1 = $min_y;
+ $y2 = $max_y;
+
+ do
+ {
+ $line = array_merge(
+ array_fill(0, mt_rand(30, 60), $non_font[array_rand($non_font)]),
+ array_fill(0, mt_rand(30, 60), $bg)
+ );
+
+ imagesetstyle($img, $line);
+ imageline($img, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
+
+ $x1 += mt_rand(20, 35);
+ $x2 += mt_rand(20, 35);
+ }
+ while ($x1 < $max_x && $x2 < $max_x);
+ imagesetthickness($img, 1);
+ }
+
+ function captcha_noise_bg_bitmaps()
+ {
+ return array(
+ 'width' => 15,
+ 'height' => 5,
+ 'data' => array(
+
+ 1 => array(
+ array(1,0,0,0,1,0,0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0,0,0,1,0,0,0),
+ ),
+ 2 => array(
+ array(1,1,mt_rand(0,1),1,0,1,1,1,1,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,1,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,1,1,0,1,1,1),
+ ),
+ 3 => array(
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,1,0,0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,0,1),
+ ),
+ 4 => array(
+ array(1,0,1,0,1,0,0,1,1,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,1,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0,0,0,0,0,0,0),
+ ),
+ 5 => array(
+ array(1,1,1,1,0,0,0,1,1,1,0,0,1,0,1),
+ array(0,0,0,0,0,0,0,1,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0,0,0,0,0,0,0),
+ ),
+ 6 => array(
+ array(mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),0,mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),mt_rand(0,1),0,mt_rand(0,1),mt_rand(0,1),mt_rand(0,1)),
+ array(0,0,0,0,0,0,0,mt_rand(0,1),0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(mt_rand(0,1),0,mt_rand(0,1),0,0,0,0,0,0,0,0,0,0,0,0),
+ ),
+ 7 => array(
+ array(0,0,0,0,0,0,0,0,0,0,1,1,0,1,1),
+ array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ array(0,0,1,1,0,0,0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,1,0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ ),
+ ));
+ }
+
+ /**
+ * Return bitmaps
+ */
+ function captcha_bitmaps()
+ {
+ global $config;
+
+ $chars = array(
+ 'A' => array(
+ array(
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,1,1,0,1,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,1),
+ array(0,0,0,0,0,1,1,1,1),
+ array(0,0,0,1,1,1,0,0,1),
+ array(0,1,1,1,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,0,0,0,0,1,1,1),
+ array(0,1,1,1,1,1,1,0,1),
+ ),
+ ),
+ 'B' => array(
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,0,0),
+ ),
+ ),
+ 'C' => array(
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(0,0,1,1,1,1,1,0,1),
+ ),
+ ),
+ 'D' => array(
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,1,0,0,0,1,1,1),
+ array(0,0,1,1,1,1,1,0,1),
+ ),
+ ),
+ 'E' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(0,1,1,1,1,1,1,1,0),
+ ),
+ ),
+ 'F' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ ),
+ array(
+ array(0,1,1,1,1,1,1,1,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,1,1,0,0,0,0),
+ array(0,0,1,1,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,1,1,1,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'G' => array(
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,1,1,1,1,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(0,0,1,1,1,1,1,0,1),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,1,1,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,1,0,0,0,0,0,1),
+ array(0,0,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,1),
+ array(1,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'H' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,1,1,1,0,0,0),
+ array(1,1,1,1,0,1,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ ),
+ ),
+ 'I' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ ),
+ ),
+ 'J' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,0,1,1,0,0,0,0,0),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,1,0,0,1,0,0,0,0),
+ array(1,0,1,1,0,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,0,1,1,0,0,0,0,0),
+ ),
+ ),
+ 'K' => array(
+ array( // New 'K', supplied by NeoThermic
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0),
+ array(1,1,0,0,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,1,0,0,0,1,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,1,0,1,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,0,1,0,0,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,1,0,0,0,1,0,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,1,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,1,0,1,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,0,1,0,0,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,1,0,0,0,1,0,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ ),
+ ),
+ 'L' => array(
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,0,1,1,1,0,0,0,0),
+ ),
+ ),
+ 'M' => array(
+ array(
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,1,1,0,1,1,1,0),
+ array(1,1,0,1,1,1,0,1,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ ),
+ ),
+ 'N' => array(
+ array(
+ array(1,1,0,0,0,0,0,0,1),
+ array(1,1,0,0,0,0,0,0,1),
+ array(1,0,1,0,0,0,0,0,1),
+ array(1,0,1,0,0,0,0,0,1),
+ array(1,0,0,1,0,0,0,0,1),
+ array(1,0,0,1,0,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,0,1,0,0,1),
+ array(1,0,0,0,0,1,0,0,1),
+ array(1,0,0,0,0,0,1,0,1),
+ array(1,0,0,0,0,0,1,0,1),
+ array(1,0,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,0,1,0),
+ array(0,1,0,1,0,0,0,1,0),
+ array(0,1,0,1,0,0,0,1,0),
+ array(0,1,0,1,0,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,1,1,0,1,0),
+ array(0,1,0,0,0,1,0,1,0),
+ array(0,1,0,0,0,1,1,1,0),
+ array(0,1,0,0,0,0,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,0,1,1,1,1,0,0,0),
+ array(1,1,1,0,0,1,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ ),
+ ),
+ 'O' => array(
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,0,0,0),
+ array(1,1,1,0,0,1,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,0,0,0,1,1,0,0),
+ array(0,1,1,1,1,1,0,0,0),
+ ),
+ ),
+ 'P' => array(
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,1,1,0,0,0,0,0),
+ array(1,1,0,1,1,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,1,1,0,0,0,0),
+ array(1,1,1,1,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'Q' => array(
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,1,0,0,1),
+ array(1,0,0,0,0,0,1,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,1),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,1,0,0,1,1,0,1,1),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,0,0,1,1,0),
+ array(0,0,0,0,0,0,0,1,1),
+ array(0,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,1,1,1,1),
+ array(0,0,0,0,1,1,0,0,1),
+ array(0,0,0,0,1,0,0,0,1),
+ array(0,0,0,0,1,0,0,0,1),
+ array(0,0,0,0,1,1,0,1,1),
+ array(0,0,0,0,0,1,1,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ ),
+ ),
+ 'R' => array(
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,1,1,0,0,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,1,1,0,0,0,0,0,0),
+ array(0,1,1,1,0,0,0,0,0),
+ array(0,1,0,1,1,0,0,0,0),
+ array(0,1,0,0,1,1,0,0,0),
+ array(0,1,0,0,0,1,1,0,0),
+ array(0,1,0,0,0,0,1,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,0,0,0,0),
+ array(1,1,0,0,1,1,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'S' => array(
+ array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,0,0,0,0,0,1,0),
+ array(1,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,0,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,1,1,0,0,0),
+ array(0,1,1,1,1,0,0,0,0),
+ ),
+ ),
+ 'T' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,1,1,1,1,1,1,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,1,0,0,0),
+ array(0,0,0,0,0,1,1,1,0),
+ ),
+ ),
+ 'U' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,1,1),
+ array(0,0,1,1,0,0,1,1,1),
+ array(0,0,0,1,1,1,1,0,1),
+ ),
+ ),
+ 'V' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ ),
+ ),
+ 'W' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,1,0,0,0,0,0,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,1,1,1,0,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,1,1,0,1,1,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,1,1,1,0,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,1,1,0,1,1,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'X' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,0,1,1,0,1,1,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,1,1,0,1,1,0,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'Y' => array(
+ array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(1,1,1,0,0,0,1,1,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,1),
+ array(0,0,0,1,1,0,0,0,1),
+ array(0,0,0,0,1,0,0,1,1),
+ array(0,0,0,0,1,1,0,1,0),
+ array(0,0,0,0,0,1,1,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,1,0,0,0),
+ array(0,0,1,1,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ ),
+ 'Z' => array(
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,1),
+ ),
+ array(
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,0,1,1,0,0),
+ array(0,0,0,0,1,1,0,0,0),
+ array(0,0,0,1,1,0,0,0,0),
+ array(0,0,1,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,0),
+ ),
+ ),
+ );
+ return array(
+ 'width' => 9,
+ 'height' => 15,
+ 'data' => array(
+
+ 'A' => $chars['A'][mt_rand(0, min(sizeof($chars['A']), $config['captcha_gd_fonts']) -1)],
+ 'B' => $chars['B'][mt_rand(0, min(sizeof($chars['B']), $config['captcha_gd_fonts']) -1)],
+ 'C' => $chars['C'][mt_rand(0, min(sizeof($chars['C']), $config['captcha_gd_fonts']) -1)],
+ 'D' => $chars['D'][mt_rand(0, min(sizeof($chars['D']), $config['captcha_gd_fonts']) -1)],
+ 'E' => $chars['E'][mt_rand(0, min(sizeof($chars['E']), $config['captcha_gd_fonts']) -1)],
+ 'F' => $chars['F'][mt_rand(0, min(sizeof($chars['F']), $config['captcha_gd_fonts']) -1)],
+ 'G' => $chars['G'][mt_rand(0, min(sizeof($chars['G']), $config['captcha_gd_fonts']) -1)],
+ 'H' => $chars['H'][mt_rand(0, min(sizeof($chars['H']), $config['captcha_gd_fonts']) -1)],
+ 'I' => $chars['I'][mt_rand(0, min(sizeof($chars['I']), $config['captcha_gd_fonts']) -1)],
+ 'J' => $chars['J'][mt_rand(0, min(sizeof($chars['J']), $config['captcha_gd_fonts']) -1)],
+ 'K' => $chars['K'][mt_rand(0, min(sizeof($chars['K']), $config['captcha_gd_fonts']) -1)],
+ 'L' => $chars['L'][mt_rand(0, min(sizeof($chars['L']), $config['captcha_gd_fonts']) -1)],
+ 'M' => $chars['M'][mt_rand(0, min(sizeof($chars['M']), $config['captcha_gd_fonts']) -1)],
+ 'N' => $chars['N'][mt_rand(0, min(sizeof($chars['N']), $config['captcha_gd_fonts']) -1)],
+ 'O' => $chars['O'][mt_rand(0, min(sizeof($chars['O']), $config['captcha_gd_fonts']) -1)],
+ 'P' => $chars['P'][mt_rand(0, min(sizeof($chars['P']), $config['captcha_gd_fonts']) -1)],
+ 'Q' => $chars['Q'][mt_rand(0, min(sizeof($chars['Q']), $config['captcha_gd_fonts']) -1)],
+ 'R' => $chars['R'][mt_rand(0, min(sizeof($chars['R']), $config['captcha_gd_fonts']) -1)],
+ 'S' => $chars['S'][mt_rand(0, min(sizeof($chars['S']), $config['captcha_gd_fonts']) -1)],
+ 'T' => $chars['T'][mt_rand(0, min(sizeof($chars['T']), $config['captcha_gd_fonts']) -1)],
+ 'U' => $chars['U'][mt_rand(0, min(sizeof($chars['U']), $config['captcha_gd_fonts']) -1)],
+ 'V' => $chars['V'][mt_rand(0, min(sizeof($chars['V']), $config['captcha_gd_fonts']) -1)],
+ 'W' => $chars['W'][mt_rand(0, min(sizeof($chars['W']), $config['captcha_gd_fonts']) -1)],
+ 'X' => $chars['X'][mt_rand(0, min(sizeof($chars['X']), $config['captcha_gd_fonts']) -1)],
+ 'Y' => $chars['Y'][mt_rand(0, min(sizeof($chars['Y']), $config['captcha_gd_fonts']) -1)],
+ 'Z' => $chars['Z'][mt_rand(0, min(sizeof($chars['Z']), $config['captcha_gd_fonts']) -1)],
+
+ '1' => array(
+ array(0,0,0,1,1,0,0,0,0),
+ array(0,0,1,0,1,0,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,0),
+ ),
+ '2' => array( // New '2' supplied by Anon
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,1,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '3' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ '4' => array(
+ array(0,0,0,0,0,0,1,1,0),
+ array(0,0,0,0,0,1,0,1,0),
+ array(0,0,0,0,1,0,0,1,0),
+ array(0,0,0,1,0,0,0,1,0),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ ),
+ '5' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ '6' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,1,1,1,1,0,0),
+ array(1,0,1,0,0,0,0,1,0),
+ array(1,1,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ '7' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ ),
+ '8' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ '9' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,1),
+ array(0,1,0,0,0,0,1,0,1),
+ array(0,0,1,1,1,1,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ ),
+ )
+ );
+ }
+}
diff --git a/phpBB/phpbb/captcha/gd_wave.php b/phpBB/phpbb/captcha/gd_wave.php
new file mode 100644
index 0000000000..f2ec4137d2
--- /dev/null
+++ b/phpBB/phpbb/captcha/gd_wave.php
@@ -0,0 +1,842 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+/**
+* Wave3D CAPTCHA
+*/
+class gd_wave
+{
+ var $width = 360;
+ var $height = 96;
+
+ function execute($code, $seed)
+ {
+ // seed the random generator
+ mt_srand($seed);
+
+ // set height and width
+ $img_x = $this->width;
+ $img_y = $this->height;
+
+ // Generate image
+ $img = imagecreatetruecolor($img_x, $img_y);
+ $x_grid = mt_rand(6, 10);
+ $y_grid = mt_rand(6, 10);
+
+ // Ok, so lets cut to the chase. We could accurately represent this in 3d and
+ // do all the appropriate linear transforms. my questions is... why bother?
+ // The computational overhead is unnecessary when you consider the simple fact:
+ // we're not here to accurately represent a model, but to just show off some random-ish
+ // polygons
+
+ // Conceive of 3 spaces.
+ // 1) planar-space (discrete "pixel" grid)
+ // 2) 3-space. (planar-space with z/height aspect)
+ // 3) image space (pixels on the screen)
+ // resolution of the planar-space we're embedding the text code in
+ $plane_x = 100;
+ $plane_y = 30;
+
+ $subdivision_factor = 3;
+
+ // $box is the 4 points in img_space that correspond to the corners of the plane in 3-space
+ $box = array(
+ 'upper_left' => array(
+ 'x' => mt_rand(5, 15),
+ 'y' => mt_rand(10, 15)
+ ),
+ 'upper_right' => array(
+ 'x' => mt_rand($img_x - 35, $img_x - 19),
+ 'y' => mt_rand(10, 17)
+ ),
+ 'lower_left' => array(
+ 'x' => mt_rand($img_x - 45, $img_x - 5),
+ 'y' => mt_rand($img_y - 15, $img_y - 0),
+ ),
+ );
+
+ $box['lower_right'] = array(
+ 'x' => $box['lower_left']['x'] + $box['upper_left']['x'] - $box['upper_right']['x'],
+ 'y' => $box['lower_left']['y'] + $box['upper_left']['y'] - $box['upper_right']['y'],
+ );
+
+ // TODO
+ $background = imagecolorallocate($img, mt_rand(155, 255), mt_rand(155, 255), mt_rand(155, 255));
+ imagefill($img, 0, 0, $background);
+
+ $random = array();
+ $fontcolors = array();
+
+ for ($i = 0; $i < 15; ++$i)
+ {
+ $random[$i] = imagecolorallocate($img, mt_rand(120, 255), mt_rand(120, 255), mt_rand(120, 255));
+ }
+
+ $fontcolors[0] = imagecolorallocate($img, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120));
+
+ $colors = array();
+
+ $minr = mt_rand(20, 30);
+ $ming = mt_rand(20, 30);
+ $minb = mt_rand(20, 30);
+
+ $maxr = mt_rand(150, 230);
+ $maxg = mt_rand(150, 230);
+ $maxb = mt_rand(150, 230);
+
+ for ($i = -30; $i <= 30; ++$i)
+ {
+ $coeff1 = ($i + 12) / 45;
+ $coeff2 = 1 - $coeff1;
+ $colors[$i] = imagecolorallocate($img, ($coeff2 * $maxr) + ($coeff1 * $minr), ($coeff2 * $maxg) + ($coeff1 * $ming), ($coeff2 * $maxb) + ($coeff1 * $minb));
+ }
+
+ // $img_buffer is the last row of 3-space positions (converted to img-space), cached
+ // (using this means we don't need to recalculate all 4 positions for each new polygon,
+ // merely the newest point that we're adding, which is then cached.
+ $img_buffer = array(array(), array());
+
+ // In image-space, the x- and y-offset necessary to move one unit in the x-direction in planar-space
+ $dxx = ($box['upper_right']['x'] - $box['upper_left']['x']) / ($subdivision_factor * $plane_x);
+ $dxy = ($box['upper_right']['y'] - $box['upper_left']['y']) / ($subdivision_factor * $plane_x);
+
+ // In image-space, the x- and y-offset necessary to move one unit in the y-direction in planar-space
+ $dyx = ($box['lower_right']['x'] - $box['upper_left']['x']) / ($subdivision_factor * $plane_y);
+ $dyy = ($box['lower_right']['y'] - $box['upper_left']['y']) / ($subdivision_factor * $plane_y);
+
+ // Initial captcha-letter offset in planar-space
+ $plane_offset_x = mt_rand(3, 8);
+ $plane_offset_y = mt_rand( 12, 15);
+
+ // character map
+ $map = $this->captcha_bitmaps();
+
+ // matrix
+ $plane = array();
+
+ // for each character, we'll silkscreen it into our boolean pixel plane
+ for ($c = 0, $code_num = strlen($code); $c < $code_num; ++$c)
+ {
+ $letter = $code[$c];
+
+ for ($x = $map['width'] - 1; $x >= 0; --$x)
+ {
+ for ($y = $map['height'] - 1; $y >= 0; --$y)
+ {
+ if ($map['data'][$letter][$y][$x])
+ {
+ $plane[$y + $plane_offset_y + (($c & 1) ? 1 : -1)][$x + $plane_offset_x] = true;
+ }
+ }
+ }
+ $plane_offset_x += 11;
+ }
+
+ // calculate our first buffer, we can't actually draw polys with these yet
+ // img_pos_prev == screen x,y location to our immediate left.
+ // img_pos_cur == current screen x,y location
+ // we calculate screen position of our
+ // current cell based on the difference from the previous cell
+ // rather than recalculating from absolute coordinates
+ // What we cache into the $img_buffer contains the raised text coordinates.
+ $img_pos_prev = $img_buffer[0][0] = array($box['upper_left']['x'], $box['upper_left']['y']);
+ $prev_height = $this->wave_height(0, 0, $subdivision_factor);
+ $full_x = $plane_x * $subdivision_factor;
+ $full_y = $plane_y * $subdivision_factor;
+
+ for ($x = 1; $x <= $full_x; ++$x)
+ {
+ $cur_height = $this->wave_height($x, 0, $subdivision_factor);
+ $offset = $cur_height - $prev_height;
+ $img_pos_cur = array($img_pos_prev[0] + $dxx, $img_pos_prev[1] + $dxy + $offset);
+
+ $img_buffer[0][$x] = $img_pos_cur;
+ $img_pos_prev = $img_pos_cur;
+ $prev_height = $cur_height;
+ }
+
+ for ($y = 1; $y <= $full_y; ++$y)
+ {
+ // swap buffers
+ $buffer_cur = $y & 1;
+ $buffer_prev = 1 - $buffer_cur;
+
+ $prev_height = $this->wave_height(0, $y, $subdivision_factor);
+ $offset = $prev_height - $this->wave_height(0, $y - 1, $subdivision_factor);
+ $img_pos_cur = array($img_buffer[$buffer_prev][0][0] + $dyx, min($img_buffer[$buffer_prev][0][1] + $dyy + $offset, $img_y - 1));
+
+ // make sure we don't try to write off the page
+ $img_pos_prev = $img_pos_cur;
+
+ $img_buffer[$buffer_cur][0] = $img_pos_cur;
+
+ for ($x = 1; $x <= $full_x; ++$x)
+ {
+ $cur_height = $this->wave_height($x, $y, $subdivision_factor) + $this->grid_height($x, $y, $x_grid, $y_grid, 1);
+
+ // height is a z-factor, not a y-factor
+ $offset = $cur_height - $prev_height;
+ $img_pos_cur = array($img_pos_prev[0] + $dxx, $img_pos_prev[1] + $dxy + $offset);
+
+ // height is float, index it to an int, get closest color
+ $color = $colors[intval($cur_height)];
+ $img_pos_prev = $img_pos_cur;
+ $prev_height = $cur_height;
+
+ $y_index_old = intval(($y - 1) / $subdivision_factor);
+ $y_index_new = intval($y / $subdivision_factor);
+ $x_index_old = intval(($x - 1) / $subdivision_factor);
+ $x_index_new = intval($x / $subdivision_factor);
+
+ if (!empty($plane[$y_index_new][$x_index_new]))
+ {
+ $img_pos_cur[1] += $this->wave_height($x, $y, $subdivision_factor, 1) - 30 - $cur_height;
+ $color = $colors[20];
+ }
+ $img_pos_cur[1] = min($img_pos_cur[1], $img_y - 1);
+ $img_buffer[$buffer_cur][$x] = $img_pos_cur;
+
+ // Smooth the edges as much as possible by having not more than one low<->high traingle per square
+ // Otherwise, just
+ $diag_down = (empty($plane[$y_index_old][$x_index_old]) == empty($plane[$y_index_new][$x_index_new]));
+ $diag_up = (empty($plane[$y_index_old][$x_index_new]) == empty($plane[$y_index_new][$x_index_old]));
+
+ // natural switching
+ $mode = ($x + $y) & 1;
+
+ // override if it requires it
+ if ($diag_down != $diag_up)
+ {
+ $mode = $diag_up;
+ }
+
+ if ($mode)
+ {
+ // +-/ /
+ // 1 |/ 2 /|
+ // / /-+
+ $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x]);
+ $poly2 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_cur][$x], $img_buffer[$buffer_prev][$x]);
+ }
+ else
+ {
+ // \ \-+
+ // 1 |\ 2 \|
+ // +-\ \
+ $poly1 = array_merge($img_buffer[$buffer_cur][$x - 1], $img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_cur][$x]);
+ $poly2 = array_merge($img_buffer[$buffer_prev][$x - 1], $img_buffer[$buffer_prev][$x], $img_buffer[$buffer_cur][$x]);
+ }
+
+ imagefilledpolygon($img, $poly1, 3, $color);
+ imagefilledpolygon($img, $poly2, 3, $color);
+ }
+ }
+
+ // Output image
+ header('Content-Type: image/png');
+ header('Cache-control: no-cache, no-store');
+ //$mtime = explode(' ', microtime());
+ //$totaltime = $mtime[0] + $mtime[1] - $starttime;
+
+ //echo $totaltime . "<br />\n";
+ //echo memory_get_usage() - $tmp;
+ imagepng($img);
+ imagedestroy($img);
+ }
+
+ function wave_height($x, $y, $factor = 1, $tweak = 0.7)
+ {
+ // stretch the wave. TODO: pretty it up
+ $x = $x/5 + 180;
+ $y = $y/4;
+ return ((sin($x / (3 * $factor)) + sin($y / (3 * $factor))) * 10 * $tweak);
+ }
+
+ function grid_height($x, $y, $x_grid, $y_grid, $factor = 1)
+ {
+ return ((!($x % ($x_grid * $factor)) || !($y % ($y_grid * $factor))) ? 3 : 0);
+ }
+
+ function captcha_bitmaps()
+ {
+ return array(
+ 'width' => 9,
+ 'height' => 13,
+ 'data' => array(
+ 'A' => array(
+ array(0,0,1,1,1,1,0,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'B' => array(
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,1,1,1,1,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'C' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'D' => array(
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'E' => array(
+ array(0,0,1,1,1,1,1,1,1),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'F' => array(
+ array(0,0,1,1,1,1,1,1,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'G' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'H' => array(
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'I' => array(
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'J' => array(
+ array(0,0,0,0,0,0,1,1,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,0,0,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'K' => array(
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0),
+ array(1,1,0,0,0,0,0,0,0),
+ array(1,0,1,0,0,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'L' => array(
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'M' => array(
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,1,0,0,0,1,1,0),
+ array(0,1,0,1,0,1,0,1,0),
+ array(0,1,0,0,1,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'N' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,0,0,0,0,0,0,1),
+ array(1,0,1,0,0,0,0,0,1),
+ array(1,0,0,1,0,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,0,0,1,0,0,1),
+ array(1,0,0,0,0,0,1,0,1),
+ array(1,0,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'O' => array(
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'P' => array(
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'Q' => array(
+ array(0,0,1,1,1,1,0,0,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,1,0,0,1,0),
+ array(1,0,0,0,0,1,0,1,0),
+ array(0,1,0,0,0,0,1,0,0),
+ array(0,0,1,1,1,1,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'R' => array(
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(1,1,1,1,1,1,0,0,0),
+ array(1,0,1,0,0,0,0,0,0),
+ array(1,0,0,1,0,0,0,0,0),
+ array(1,0,0,0,1,0,0,0,0),
+ array(1,0,0,0,0,1,0,0,0),
+ array(1,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'S' => array(
+ array(0,0,1,1,1,1,1,1,1),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(1,1,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'T' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'U' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'V' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'W' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,1,0,0,0,1),
+ array(1,0,0,1,0,1,0,0,1),
+ array(1,0,1,0,0,0,1,0,1),
+ array(1,1,0,0,0,0,0,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'X' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'Y' => array(
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,0,0,1,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ 'Z' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '1' => array(
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,1,0,0,0,0),
+ array(0,0,1,0,1,0,0,0,0),
+ array(0,1,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '2' => array(
+ array(0,0,0,1,1,1,0,0,0),
+ array(0,0,1,0,0,0,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,1,0,0,0,0,0),
+ array(0,0,1,0,0,0,0,0,0),
+ array(0,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '3' => array(
+ array(0,0,0,1,1,1,1,0,0),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,0,0,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '4' => array(
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,1,0),
+ array(0,0,0,0,0,1,0,1,0),
+ array(0,0,0,0,1,0,0,1,0),
+ array(0,0,0,1,0,0,0,1,0),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,1,1,1,1,1,1,1,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '5' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(0,1,0,0,0,0,0,0,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '6' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,0,0,0,0,0,0),
+ array(1,0,0,1,1,1,1,0,0),
+ array(1,0,1,0,0,0,0,1,0),
+ array(1,1,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '7' => array(
+ array(1,1,1,1,1,1,1,1,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,1,0),
+ array(0,0,0,0,0,0,1,0,0),
+ array(0,0,0,0,0,1,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,1,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '8' => array(
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,1,0,0,0,0,0,1,0),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(1,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,0),
+ array(0,0,1,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ '9' => array(
+ array(0,0,0,1,1,1,1,0,0),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,1,1),
+ array(0,0,1,1,1,1,1,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,0,0,0,0,0,0,0,1),
+ array(0,1,0,0,0,0,0,0,1),
+ array(0,0,1,0,0,0,0,1,0),
+ array(0,0,0,1,1,1,1,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ array(0,0,0,0,0,0,0,0,0),
+ ),
+ )
+ );
+ }
+}
diff --git a/phpBB/phpbb/captcha/non_gd.php b/phpBB/phpbb/captcha/non_gd.php
new file mode 100644
index 0000000000..3818672f17
--- /dev/null
+++ b/phpBB/phpbb/captcha/non_gd.php
@@ -0,0 +1,386 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha;
+
+/**
+* Main non-gd captcha class
+* @ignore
+*/
+class non_gd
+{
+ var $filtered_pngs;
+ var $width = 320;
+ var $height = 50;
+
+ /**
+ * Define filtered pngs on init
+ */
+ function __construct()
+ {
+ // If we can we will generate a single filtered png, we avoid nastiness via emulation of some Zlib stuff
+ $this->define_filtered_pngs();
+ }
+
+ /**
+ * Create the image containing $code with a seed of $seed
+ */
+ function execute($code, $seed)
+ {
+ $img_height = $this->height - 10;
+ $img_width = 0;
+
+ mt_srand($seed);
+
+ $char_widths = $hold_chars = array();
+ $code_len = strlen($code);
+
+ for ($i = 0; $i < $code_len; $i++)
+ {
+ $char = $code[$i];
+
+ $width = mt_rand(0, 4);
+ $raw_width = $this->filtered_pngs[$char]['width'];
+ $char_widths[$i] = $width;
+ $img_width += $raw_width - $width;
+
+ // Split the char into chunks of $raw_width + 1 length
+ if (empty($hold_chars[$char]))
+ {
+ $hold_chars[$char] = str_split(base64_decode($this->filtered_pngs[$char]['data']), $raw_width + 1);
+ }
+ }
+
+ $offset_x = mt_rand(0, $this->width - $img_width);
+ $offset_y = mt_rand(0, $this->height - $img_height);
+
+ $image = '';
+ for ($i = 0; $i < $this->height; $i++)
+ {
+ $image .= chr(0);
+
+ if ($i > $offset_y && $i < $offset_y + $img_height)
+ {
+ for ($j = 0; $j < $offset_x; $j++)
+ {
+ $image .= chr(mt_rand(140, 255));
+ }
+
+ for ($j = 0; $j < $code_len; $j++)
+ {
+ $image .= $this->randomise(substr($hold_chars[$code{$j}][$i - $offset_y - 1], 1), $char_widths[$j]);
+ }
+
+ for ($j = $offset_x + $img_width; $j < $this->width; $j++)
+ {
+ $image .= chr(mt_rand(140, 255));
+ }
+ }
+ else
+ {
+ for ($j = 0; $j < $this->width; $j++)
+ {
+ $image .= chr(mt_rand(140, 255));
+ }
+ }
+ }
+ unset($hold_chars);
+
+ $image = $this->create_png($image, $this->width, $this->height);
+
+ // Output image
+ header('Content-Type: image/png');
+ header('Cache-control: no-cache, no-store');
+ echo $image;
+ exit;
+ }
+
+ /**
+ * This is designed to randomise the pixels of the image data within
+ * certain limits so as to keep it readable. It also varies the image
+ * width a little
+ */
+ function randomise($scanline, $width)
+ {
+ $new_line = '';
+
+ $end = strlen($scanline) - ceil($width/2);
+ for ($i = (int) floor($width / 2); $i < $end; $i++)
+ {
+ $pixel = ord($scanline{$i});
+
+ if ($pixel < 190)
+ {
+ $new_line .= chr(mt_rand(0, 205));
+ }
+ else if ($pixel > 190)
+ {
+ $new_line .= chr(mt_rand(145, 255));
+ }
+ else
+ {
+ $new_line .= $scanline{$i};
+ }
+ }
+
+ return $new_line;
+ }
+
+ /**
+ * This creates a chunk of the given type, with the given data
+ * of the given length adding the relevant crc
+ */
+ function png_chunk($length, $type, $data)
+ {
+ $raw = $type . $data;
+
+ return pack('N', $length) . $raw . pack('N', crc32($raw));
+ }
+
+ /**
+ * Creates greyscale 8bit png - The PNG spec can be found at
+ * http://www.libpng.org/pub/png/spec/PNG-Contents.html we use
+ * png because it's a fully recognised open standard and supported
+ * by practically all modern browsers and OSs
+ */
+ function create_png($raw_image, $width, $height)
+ {
+ // SIG
+ $image = pack('C8', 137, 80, 78, 71, 13, 10, 26, 10);
+
+ // IHDR
+ $raw = pack('N2', $width, $height);
+ $raw .= pack('C5', 8, 0, 0, 0, 0);
+ $image .= $this->png_chunk(13, 'IHDR', $raw);
+
+ // IDAT
+ if (@extension_loaded('zlib'))
+ {
+ $raw_image = gzcompress($raw_image);
+ $length = strlen($raw_image);
+ }
+ else
+ {
+ // The total length of this image, uncompressed, is just a calculation of pixels
+ $length = ($width + 1) * $height;
+
+ // Adler-32 hash generation
+ // Note: The hash is _backwards_ so we must reverse it
+
+ if (@extension_loaded('hash'))
+ {
+ $adler_hash = strrev(hash('adler32', $raw_image, true));
+ }
+ else if (@extension_loaded('mhash'))
+ {
+ $adler_hash = strrev(mhash(MHASH_ADLER32, $raw_image));
+ }
+ else
+ {
+ // Optimized Adler-32 loop ported from the GNU Classpath project
+ $temp_length = $length;
+ $s1 = 1;
+ $s2 = $index = 0;
+
+ while ($temp_length > 0)
+ {
+ // We can defer the modulo operation:
+ // s1 maximally grows from 65521 to 65521 + 255 * 3800
+ // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31
+ $substract_value = ($temp_length < 3800) ? $temp_length : 3800;
+ $temp_length -= $substract_value;
+
+ while (--$substract_value >= 0)
+ {
+ $s1 += ord($raw_image[$index]);
+ $s2 += $s1;
+
+ $index++;
+ }
+
+ $s1 %= 65521;
+ $s2 %= 65521;
+ }
+ $adler_hash = pack('N', ($s2 << 16) | $s1);
+ }
+
+ // This is the same thing as gzcompress($raw_image, 0) but does not need zlib
+ $raw_image = pack('C3v2', 0x78, 0x01, 0x01, $length, ~$length) . $raw_image . $adler_hash;
+
+ // The Zlib header + Adler hash make us add on 11
+ $length += 11;
+ }
+
+ // IDAT
+ $image .= $this->png_chunk($length, 'IDAT', $raw_image);
+
+ // IEND
+ $image .= $this->png_chunk(0, 'IEND', '');
+
+ return $image;
+ }
+
+ /**
+ * png image data
+ * Each 'data' element is base64_encoded uncompressed IDAT
+ */
+ function define_filtered_pngs()
+ {
+ $this->filtered_pngs = array(
+ '0' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////olFAkBAAAGDyA4P///M31/////////////wD////////////////0dAgAAAAAAAAAAAAEcPipFGHn////////////AP//////////////6DAAAAAAAAAAAAAAAAAALSEAN+T///////////8A//////////////xAAAAAAAAAAAAAAAAAAAAAACPA/////////////wD/////////////oAAAAAAAAAAAAAAAAAAAAAAAev//////////////AP////////////8oAAAAAAAAPNj/zDAAAAAAAABD//////////////8A////////////1AAAAAAAABjw////5BAAAAAAAADo/////////////wD///////////+QAAAAAAAAbP//////QgAAAAAAAKj/////////////AP///////////1wAAAAAAACs/////8AXAAAAAAAAcP////////////8A////////////OAAAAAAAAND////dNwAAAAAAAABI/////////////wD///////////8gAAAAAAAA4P//7koACwAAAAAAACT/////////////AP///////////wgAAAAAAAD///VqAwaPAAAAAAAAEP////////////8A////////////AAAAAAAAAP/8kQYDavUAAAAAAAAA/////////////wD///////////8AAAAAAAAA/6kNAEru/wAAAAAAAAD/////////////AP///////////wAAAAAAAADAIwA33f//AAAAAAAAAP////////////8A////////////FAAAAAAAADYAI8D///8AAAAAAAAQ/////////////wD///////////8kAAAAAAAAAA2p////5AAAAAAAACD/////////////AP///////////0gAAAAAAAAFkfz////UAAAAAAAAQP////////////8A////////////cAAAAAAAAET1/////7AAAAAAAABo/////////////wD///////////+oAAAAAAAAXfX/////sAAAAAAAAGj/////////////AAAAALgAAAAAAAAwAAAAAAAAAAAAAAD////////////oAAAAAAAACOT////oEAAAAAAAAOD/////////////AP////////////8+AAAAAAAAKMz/zDQAAAAAAAA0//////////////8A////////////7jgAAAAAAAAAAAAAAAAAAAAAAKT//////////////wD///////////VqAwIAAAAAAAAAAAAAAAAAAAA8////////////////AP//////////rQcDaVEAAAAAAAAAAAAAAAAAKOj///////////////8A///////////nblnu/IAIAAAAAAAAAAAAAFzw/////////////////wD////////////79////+iITCAAAAAgSITg////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////w==',
+ 'width' => 40
+ ),
+ '1' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////8BAAAAAAAP//////////////////AP////////////////////////9sAAAAAAAA//////////////////8A////////////////////////pAAAAAAAAAD//////////////////wD//////////////////////6wEAAAAAAAAAP//////////////////AP////////////////////h4AAAAAAAAAAAA//////////////////8A//////////////////ygJAAAAAAAAAAAAAD//////////////////wD//////////////9x8HAAAAAAAAAAAAAAAAP//////////////////AP//////////////AAAAAAAAAAAAAAAAAAAA//////////////////8A//////////////8AAAAAAAAAAAAAAAAAAAD//////////////////wD//////////////wAAAAAAAAR4AAAAAAAAAP//////////////////AP//////////////AAAAAAA4zP8AAAAAAAAA//////////////////8A//////////////8AAAA4sP///wAAAAAAAAD//////////////////wD//////////////yR80P//////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////AAAAAAAAAP//////////////////AP////////////////////////8AAAAAAAAA//////////////////8A/////////////////////////wAAAAAAAAD//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '2' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////okFAkCAAABCBIfNT///////////////////8A///////////////8hAgAAAAAAAAAAAAAAFTo/////////////////wD//////////////1QAAAAAAAAAAAAAAAAAACjo////////////////AP////////////+MAAAAAAAAAAAAAAAAAAAAADj///////////////8A////////////9BAAAAAAAAAAAAAAAAAAAAAAALD//////////////wD///////////+gAAAAAAAAAHjs+KwMAAAAAAAAVP//////////////AP///////////1gAAAAAAABM/////6QAAAAAAAAU//////////////8A////////////KAAAAAAAALj/////+AAAAAAAAAD//////////////wD///////////+MfGBMOCAI8P/////wAAAAAAAACP//////////////AP///////////////////////////5wAAAAAAAAw//////////////8A///////////////////////////oFAAAAAAAAHz//////////////wD/////////////////////////6CgAAAAAAAAE3P//////////////AP///////////////////////9ggAAAAAAAAAHT///////////////8A//////////////////////+0DAAAAAAAAAA8+P///////////////wD/////////////////////gAAAAAAAAAAAKOj/////////////////AP//////////////////9FAAAAAAAAAAADzw//////////////////8A/////////////////+g4AAAAAAAAAABk/P///////////////////wD////////////////oKAAAAAAAAAAMqP//////////////////////AP//////////////6CgAAAAAAAAAMNz///////////////////////8A//////////////g4AAAAAAAAAFT0/////////////////////////wD/////////////bAAAAAAAAABU/P//////////////////////////AP///////////8wAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A////////////SAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////9AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////xAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '3' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////8sGg0FAAAACA4cLz8////////////////////AP//////////////rBgAAAAAAAAAAAAAACTA//////////////////8A/////////////3QAAAAAAAAAAAAAAAAAAASs/////////////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAjc////////////////AP//////////6AwAAAAAAAAAAAAAAAAAAAAAAGT///////////////8A//////////94AAAAAAAABJDw/8g4AAAAAAAAHP///////////////wD//////////yAAAAAAAACE/////9gAAAAAAAAA////////////////AP///////////NSwiGQ4FOT//////AAAAAAAABD///////////////8A//////////////////////////+YAAAAAAAAVP///////////////wD//////////////////////P/ggAQAAAAAAATM////////////////AP////////////////////9gAAAAAAAAAAAElP////////////////8A/////////////////////0AAAAAAAAAAHLj//////////////////wD/////////////////////OAAAAAAAAAAwkPj/////////////////AP////////////////////8gAAAAAAAAAAAAINj///////////////8A/////////////////////xAAAAAAAAAAAAAAIPD//////////////wD/////////////////////uOz/4HgEAAAAAAAAhP//////////////AP///////////////////////////3wAAAAAAAAw//////////////8A////////////////////////////6AAAAAAAAAj//////////////wD/////////////////////////////AAAAAAAAAP//////////////AP//////////tJh8YEQoDNz//////+AAAAAAAAAY//////////////8A//////////88AAAAAAAAaP//////dAAAAAAAAEz//////////////wD//////////6QAAAAAAAAAdOD/5HQAAAAAAAAApP//////////////AP///////////CgAAAAAAAAAAAAAAAAAAAAAACD4//////////////8A////////////yAQAAAAAAAAAAAAAAAAAAAAEuP///////////////wD/////////////rAQAAAAAAAAAAAAAAAAABJD/////////////////AP//////////////zDQAAAAAAAAAAAAAACTA//////////////////8A/////////////////8BwOCAAAAAUNGi0/P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '4' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////////////nAAAAAAAAAD///////////////8A/////////////////////////8AEAAAAAAAAAP///////////////wD////////////////////////gGAAAAAAAAAAA////////////////AP//////////////////////9DAAAAAAAAAAAAD///////////////8A//////////////////////9UAAAAAAAAAAAAAP///////////////wD/////////////////////hAAAAAAAAAAAAAAA////////////////AP///////////////////7QAAAAAAAAAAAAAAAD///////////////8A///////////////////UDAAAAAAUAAAAAAAAAP///////////////wD/////////////////7CQAAAAABMAAAAAAAAAA////////////////AP////////////////xEAAAAAACU/wAAAAAAAAD///////////////8A////////////////cAAAAAAAZP//AAAAAAAAAP///////////////wD//////////////6AAAAAAADz8//8AAAAAAAAA////////////////AP/////////////IBAAAAAAc6P///wAAAAAAAAD///////////////8A////////////5BgAAAAADMz/////AAAAAAAAAP///////////////wD///////////g0AAAAAACk//////8AAAAAAAAA////////////////AP//////////XAAAAAAAfP///////wAAAAAAAAD///////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP///////////////////////////wAAAAAAAAD///////////////8A////////////////////////////AAAAAAAAAP///////////////wD///////////////////////////8AAAAAAAAA////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '5' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////8AAAAAAAAAAAAAAAAAAAAAAA//////////////8A///////////////MAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////6wAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////iAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////////9kAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////////0QAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////////IAAAAAAAYP////////////////////////////8A//////////////wAAAAAAAB8/////////////////////////////wD/////////////3AAAAAAAAIj/////////////////////////////AP////////////+4AAAAAAAAoLRYHAAEKGTE//////////////////8A/////////////5QAAAAAAAAQAAAAAAAAAABY9P///////////////wD/////////////dAAAAAAAAAAAAAAAAAAAAAA89P//////////////AP////////////9QAAAAAAAAAAAAAAAAAAAAAABg//////////////8A/////////////zAAAAAAAAAAAAAAAAAAAAAAAADQ/////////////wD/////////////IAAAAAAAAGjY/+h4BAAAAAAAAGz/////////////AP//////////////9NS0lHSc//////90AAAAAAAALP////////////8A/////////////////////////////9QAAAAAAAAE/////////////wD//////////////////////////////wAAAAAAAAD/////////////AP/////////////////////////////8AAAAAAAAEP////////////8A////////////pIRwWEAgDOD//////8wAAAAAAAA8/////////////wD///////////9EAAAAAAAAaP//////ZAAAAAAAAHz/////////////AP///////////6QAAAAAAAAAaOD/4GQAAAAAAAAE4P////////////8A/////////////CQAAAAAAAAAAAAAAAAAAAAAAGD//////////////wD/////////////yAQAAAAAAAAAAAAAAAAAAAAc7P//////////////AP//////////////rAwAAAAAAAAAAAAAAAAAGNj///////////////8A////////////////0EAAAAAAAAAAAAAAAFTo/////////////////wD//////////////////8h4QCAAAAAcQHzU////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '6' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////+0ZCwMAAAUNGjI////////////////////AP/////////////////EMAAAAAAAAAAAAABM6P////////////////8A////////////////lAQAAAAAAAAAAAAAAAAo6P///////////////wD//////////////6wAAAAAAAAAAAAAAAAAAABI////////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAACw//////////////8A/////////////3AAAAAAAAAoxP/YPAAAAAAAAEj//////////////wD////////////4EAAAAAAACOD////YDCBAVGiAoP//////////////AP///////////7gAAAAAAABY//////////////////////////////8A////////////eAAAAAAAAJT//////////////////////////////wD///////////9MAAAAAAAAvP/IXBgABCx03P//////////////////AP///////////ygAAAAAAADcdAAAAAAAAAAEiP////////////////8A////////////FAAAAAAAAFAAAAAAAAAAAAAAcP///////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAlP//////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAQ8P////////////8A////////////AAAAAAAAAABAyP/kZAAAAAAAAACQ/////////////wD///////////8MAAAAAAAALPj/////WAAAAAAAAET/////////////AP///////////yQAAAAAAACY///////MAAAAAAAAFP////////////8A////////////SAAAAAAAAMD///////wAAAAAAAAA/////////////wD///////////9wAAAAAAAAvP///////wAAAAAAAAD/////////////AP///////////7QAAAAAAACI///////UAAAAAAAAJP////////////8A////////////+AwAAAAAACDw/////2wAAAAAAABY/////////////wD/////////////cAAAAAAAADC8/Ox4AAAAAAAAAKj/////////////AP/////////////oEAAAAAAAAAAAAAAAAAAAAAAk/P////////////8A//////////////+oAAAAAAAAAAAAAAAAAAAABLj//////////////wD///////////////+QAAAAAAAAAAAAAAAAAACQ////////////////AP////////////////+0JAAAAAAAAAAAAAAkuP////////////////8A///////////////////8sGg0FAAADCxgqPz//////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '7' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAABP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAy4/////////////wD//////////////////////////+QUAAAAAAAEuP//////////////AP/////////////////////////8QAAAAAAAAKT///////////////8A/////////////////////////4wAAAAAAAB0/////////////////wD////////////////////////cCAAAAAAANPz/////////////////AP///////////////////////0QAAAAAAATY//////////////////8A//////////////////////+0AAAAAAAAeP///////////////////wD//////////////////////CQAAAAAABTw////////////////////AP////////////////////+gAAAAAAAAkP////////////////////8A/////////////////////ywAAAAAABDw/////////////////////wD///////////////////+4AAAAAAAAbP//////////////////////AP///////////////////1wAAAAAAADQ//////////////////////8A///////////////////4DAAAAAAAMP///////////////////////wD//////////////////7QAAAAAAAB8////////////////////////AP//////////////////aAAAAAAAAMj///////////////////////8A//////////////////8oAAAAAAAM/P///////////////////////wD/////////////////8AAAAAAAAET/////////////////////////AP////////////////+0AAAAAAAAcP////////////////////////8A/////////////////4wAAAAAAACY/////////////////////////wD/////////////////WAAAAAAAAMD/////////////////////////AP////////////////80AAAAAAAA4P////////////////////////8A/////////////////xAAAAAAAAD4/////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '8' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD////////////////////IdDQUAAAEIEiA1P//////////////////AP/////////////////gRAAAAAAAAAAAAAAAROD///////////////8A////////////////0BgAAAAAAAAAAAAAAAAAEMj//////////////wD///////////////AcAAAAAAAAAAAAAAAAAAAAHPD/////////////AP//////////////hAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A//////////////8sAAAAAAAAKMz/zCgAAAAAAAAs/////////////wD//////////////wAAAAAAAADM////zAAAAAAAAAD/////////////AP//////////////BAAAAAAAAP//////AAAAAAAABP////////////8A//////////////8sAAAAAAAAzP///9QAAAAAAAAw/////////////wD//////////////3wAAAAAAAAoyP/YNAAAAAAAAIT/////////////AP//////////////7BgAAAAAAAAAAAAAAAAAAAAc8P////////////8A////////////////xBgAAAAAAAAAAAAAAAAAGNj//////////////wD/////////////////tAQAAAAAAAAAAAAAAACo////////////////AP///////////////HAAAAAAAAAAAAAAAAAAAAB8//////////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB8/////////////wD/////////////wAAAAAAAAABk4P/UWAAAAAAAAATQ////////////AP////////////9UAAAAAAAAaP//////XAAAAAAAAGT///////////8A/////////////xgAAAAAAADg///////cAAAAAAAAJP///////////wD/////////////AAAAAAAAAP////////8AAAAAAAAA////////////AP////////////8AAAAAAAAA4P//////3AAAAAAAAAT///////////8A/////////////ygAAAAAAABg//////9cAAAAAAAALP///////////wD/////////////ZAAAAAAAAABY1P/cXAAAAAAAAABw////////////AP/////////////QAAAAAAAAAAAAAAAAAAAAAAAABNz///////////8A//////////////9gAAAAAAAAAAAAAAAAAAAAAAB0/////////////wD///////////////Q8AAAAAAAAAAAAAAAAAAAAUPz/////////////AP////////////////x4CAAAAAAAAAAAAAAAEIT8//////////////8A///////////////////smFQwGAAAABg0ZKT0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ '9' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////////////ysYCwMAAAUNGiw/P//////////////////AP////////////////+4JAAAAAAAAAAAAAAkuP////////////////8A////////////////lAQAAAAAAAAAAAAAAAAAkP///////////////wD//////////////8AEAAAAAAAAAAAAAAAAAAAAqP//////////////AP/////////////8JAAAAAAAAAAAAAAAAAAAAAAQ7P////////////8A/////////////6wAAAAAAAAAfOz8vCwAAAAAAABw/////////////wD/////////////WAAAAAAAAHD/////7BgAAAAAAAz4////////////AP////////////8kAAAAAAAA1P//////hAAAAAAAALT///////////8A/////////////wAAAAAAAAD///////+4AAAAAAAAcP///////////wD/////////////AAAAAAAAAPz//////8AAAAAAAABI////////////AP////////////8UAAAAAAAAzP//////lAAAAAAAACT///////////8A/////////////0QAAAAAAABY//////gsAAAAAAAADP///////////wD/////////////kAAAAAAAAABw5P/IPAAAAAAAAAAA////////////AP/////////////wEAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A//////////////+UAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////9wAAAAAAAAAAAAAFAAAAAAAAAU////////////AP////////////////+IBAAAAAAAAABw3AAAAAAAACj///////////8A///////////////////cdCwEABhcxP+8AAAAAAAATP///////////wD//////////////////////////////5AAAAAAAAB4////////////AP//////////////////////////////UAAAAAAAALj///////////8A//////////////+kgGxUQCAM2P///+AIAAAAAAAQ+P///////////wD//////////////0gAAAAAAAA42P/EKAAAAAAAAHD/////////////AP//////////////sAAAAAAAAAAAAAAAAAAAAAAQ6P////////////8A////////////////TAAAAAAAAAAAAAAAAAAAAKz//////////////wD////////////////oKAAAAAAAAAAAAAAAAASU////////////////AP/////////////////sUAAAAAAAAAAAAAAwxP////////////////8A////////////////////yHA0FAAADCxktP///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'A' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////+QAAAAAAAAAAAAAAOT/////////////////AP//////////////////kAAAAAAAAAAAAAAAkP////////////////8A//////////////////88AAAAAAAAAAAAAAA8/////////////////wD/////////////////5AAAAAAAAAAAAAAAAADk////////////////AP////////////////+QAAAAAAAAAAAAAAAAAJD///////////////8A/////////////////zwAAAAAAAAAAAAAAAAAPP///////////////wD////////////////kAAAAAAAAAAgAAAAAAAAA5P//////////////AP///////////////5AAAAAAAAAAgAAAAAAAAACQ//////////////8A////////////////PAAAAAAAAAz8HAAAAAAAADz//////////////wD//////////////+QAAAAAAAAAWP9kAAAAAAAAANz/////////////AP//////////////kAAAAAAAAACk/7wAAAAAAAAAhP////////////8A//////////////88AAAAAAAABOz//BQAAAAAAAAw/////////////wD/////////////4AAAAAAAAAA8////ZAAAAAAAAADc////////////AP////////////+EAAAAAAAAAIj///+8AAAAAAAAAIT///////////8A/////////////zAAAAAAAAAA2P////wQAAAAAAAAMP///////////wD////////////cAAAAAAAAACT//////1wAAAAAAAAA3P//////////AP///////////4QAAAAAAAAAAAAAAAAAAAAAAAAAAACE//////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAAAAAADD//////////wD//////////9wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANz/////////AP//////////hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhP////////8A//////////8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw/////////wD/////////3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc////////AP////////+EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIT///////8A/////////zAAAAAAAAAAhP///////////2QAAAAAAAAAMP///////wD////////cAAAAAAAAAADM////////////vAAAAAAAAAAA3P//////AP///////4QAAAAAAAAAHP/////////////4DAAAAAAAAACE//////8A////////MAAAAAAAAABk//////////////9cAAAAAAAAADD//////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'B' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAEDh83P///////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAEhP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAeP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAABY////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAABT///////////8A//////////8AAAAAAAAAAP/////4zEwAAAAAAAAAAP///////////wD//////////wAAAAAAAAAA////////7AAAAAAAAAAQ////////////AP//////////AAAAAAAAAAD////////sAAAAAAAAAEj///////////8A//////////8AAAAAAAAAAP/////4zEQAAAAAAAAAtP///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAFz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAiA/P////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAIjPj//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAGKz/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJT///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABNz//////////wD//////////wAAAAAAAAAA///////sqCAAAAAAAAAAbP//////////AP//////////AAAAAAAAAAD/////////yAAAAAAAAAAs//////////8A//////////8AAAAAAAAAAP//////////AAAAAAAAAAT//////////wD//////////wAAAAAAAAAA/////////7wAAAAAAAAAAP//////////AP//////////AAAAAAAAAAD//////+ikGAAAAAAAAAAY//////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFT//////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsP//////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAADj///////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAc6P///////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAATOj/////////////AP//////////AAAAAAAAAAAAAAAAAAAEIEBkkNj///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'C' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////////////5JRULBAAAAgkTIDQ//////////////////8A////////////////1FAAAAAAAAAAAAAAAABAyP///////////////wD//////////////4gEAAAAAAAAAAAAAAAAAAAElP//////////////AP////////////9wAAAAAAAAAAAAAAAAAAAAAAAAlP////////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAEyP///////////wD//////////9wIAAAAAAAAAAAAAAAAAAAAAAAAAAAw////////////AP//////////WAAAAAAAAAAAWMz/8JwQAAAAAAAAAACw//////////8A/////////+wEAAAAAAAAAID//////9QMAAAAAAAAAET//////////wD/////////nAAAAAAAAAAo/P///////3wAAAAABDBspP//////////AP////////9gAAAAAAAAAIz/////////3BxQjMT0//////////////8A/////////zQAAAAAAAAAzP///////////////////////////////wD/////////GAAAAAAAAADo////////////////////////////////AP////////8AAAAAAAAAAP////////////////////////////////8A/////////wAAAAAAAAAA/////////////////////////////////wD/////////AAAAAAAAAAD/////////////////////////////////AP////////8cAAAAAAAAAOj///////////////////////////////8A/////////zgAAAAAAAAA0P/////////kIGio7P///////////////wD/////////bAAAAAAAAACg/////////5wAAAAAMHS49P//////////AP////////+oAAAAAAAAAEz/////////PAAAAAAAAAAc//////////8A//////////QIAAAAAAAAALz//////6QAAAAAAAAAAGT//////////wD//////////3AAAAAAAAAADIzo/+SEBAAAAAAAAAAAyP//////////AP//////////7BAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////rAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD/////////////fAAAAAAAAAAAAAAAAAAAAAAAAJz/////////////AP//////////////iAQAAAAAAAAAAAAAAAAAAASY//////////////8A////////////////yEAAAAAAAAAAAAAAAAA8yP///////////////wD//////////////////9yIUCwQAAAAIEB4yP//////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'D' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////////8AAAAAAAAAAAAAAAAADChQkOT/////////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAABGjw//////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAACDY/////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAABjk////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKj//////////wD///////////8AAAAAAAAAAP///+isSAAAAAAAAAAANP//////////AP///////////wAAAAAAAAAA////////hAAAAAAAAAAA2P////////8A////////////AAAAAAAAAAD/////////MAAAAAAAAACQ/////////wD///////////8AAAAAAAAAAP////////+MAAAAAAAAAFj/////////AP///////////wAAAAAAAAAA/////////8gAAAAAAAAAMP////////8A////////////AAAAAAAAAAD/////////5AAAAAAAAAAY/////////wD///////////8AAAAAAAAAAP//////////AAAAAAAAAAD/////////AP///////////wAAAAAAAAAA//////////8AAAAAAAAAAP////////8A////////////AAAAAAAAAAD//////////wAAAAAAAAAA/////////wD///////////8AAAAAAAAAAP/////////wAAAAAAAAABD/////////AP///////////wAAAAAAAAAA/////////9QAAAAAAAAAJP////////8A////////////AAAAAAAAAAD/////////qAAAAAAAAABI/////////wD///////////8AAAAAAAAAAP////////9QAAAAAAAAAHj/////////AP///////////wAAAAAAAAAA////////uAAAAAAAAAAAvP////////8A////////////AAAAAAAAAAD////w0HwEAAAAAAAAACT8/////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAADz8//////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAY6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAKNz/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAACHT0//////////////8A////////////AAAAAAAAAAAAAAAAABg4bKj0/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'E' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAD///////////////////////////////8A//////////8AAAAAAAAAAP///////////////////////////////wD//////////wAAAAAAAAAA////////////////////////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'F' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'G' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////////////////MB8TCgQAAAACCA4YJzs////////////////AP///////////////JQcAAAAAAAAAAAAAAAAAAhw8P////////////8A/////////////9gwAAAAAAAAAAAAAAAAAAAAAAAk2P///////////wD////////////EDAAAAAAAAAAAAAAAAAAAAAAAAAAc7P//////////AP//////////2AwAAAAAAAAAAAAAAAAAAAAAAAAAAABY//////////8A//////////wwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQ/////////wD/////////kAAAAAAAAAAAEHzQ/P/gmCAAAAAAAAAAAFz/////////AP////////wcAAAAAAAAACjg////////8CwAAAAAAAAgWP////////8A////////vAAAAAAAAAAI2P//////////yBRAcJjI8P///////////wD///////94AAAAAAAAAGD/////////////////////////////////AP///////0AAAAAAAAAAsP////////////////////////////////8A////////IAAAAAAAAADc/////////////////////////////////wD///////8AAAAAAAAAAP///////wAAAAAAAAAAAAAAAAD/////////AP///////wAAAAAAAAAA////////AAAAAAAAAAAAAAAAAP////////8A////////AAAAAAAAAAD///////8AAAAAAAAAAAAAAAAA/////////wD///////8gAAAAAAAAAOD//////wAAAAAAAAAAAAAAAAD/////////AP///////0AAAAAAAAAAtP//////AAAAAAAAAAAAAAAAAP////////8A////////cAAAAAAAAABw//////8AAAAAAAAAAAAAAAAA/////////wD///////+8AAAAAAAAABDs////////////AAAAAAAAAAD/////////AP////////wYAAAAAAAAADz0//////////AAAAAAAAAAAP////////8A/////////5AAAAAAAAAAACCY4P//3KhcCAAAAAAAAAAA/////////wD/////////+CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////AP//////////xAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIP////////8A////////////rAQAAAAAAAAAAAAAAAAAAAAAAAAAAGTw/////////wD/////////////vBQAAAAAAAAAAAAAAAAAAAAAADjI////////////AP//////////////8HAQAAAAAAAAAAAAAAAAAEiw//////////////8A//////////////////iwcEAgBAAABCA4aKDk/////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'H' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'I' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAP///////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAA////////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAD///////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'J' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAD//////////////wD///////////////////////////8AAAAAAAAAAP//////////////AP///////////////////////////wAAAAAAAAAA//////////////8A////////////////////////////AAAAAAAAAAj//////////////wD//////////+zMrIxwUDAQ//////wAAAAAAAAAIP//////////////AP//////////DAAAAAAAAADo////2AAAAAAAAAA0//////////////8A//////////8wAAAAAAAAAKj///+YAAAAAAAAAFj//////////////wD//////////2gAAAAAAAAAIND/yBgAAAAAAAAAkP//////////////AP//////////vAAAAAAAAAAAAAAAAAAAAAAAAADc//////////////8A////////////MAAAAAAAAAAAAAAAAAAAAAAAUP///////////////wD////////////EBAAAAAAAAAAAAAAAAAAAABjk////////////////AP////////////+sBAAAAAAAAAAAAAAAAAAY2P////////////////8A///////////////EMAAAAAAAAAAAAAAAVOj//////////////////wD/////////////////vHBAIAAAABg8fNT/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'K' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////8AAAAAAAAAAP//////////wAQAAAAAAAAAAABw////////AP///////wAAAAAAAAAA/////////9AMAAAAAAAAAAAAcP////////8A////////AAAAAAAAAAD////////cGAAAAAAAAAAAAHD//////////wD///////8AAAAAAAAAAP//////6CgAAAAAAAAAAABs////////////AP///////wAAAAAAAAAA//////Q0AAAAAAAAAAAAVPz///////////8A////////AAAAAAAAAAD////8RAAAAAAAAAAAAFT8/////////////wD///////8AAAAAAAAAAP///1gAAAAAAAAAAABU/P//////////////AP///////wAAAAAAAAAA//9wAAAAAAAAAAAASPz///////////////8A////////AAAAAAAAAAD/jAAAAAAAAAAAADz0/////////////////wD///////8AAAAAAAAAAKQAAAAAAAAAAAA89P//////////////////AP///////wAAAAAAAAAABAAAAAAAAAAAFPT///////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAApP///////////////////wD///////8AAAAAAAAAAAAAAAAAAAAAAAAU8P//////////////////AP///////wAAAAAAAAAAAAAAAAAAAAAAAABk//////////////////8A////////AAAAAAAAAAAAAAAAAAAAAAAAAADE/////////////////wD///////8AAAAAAAAAAAAAAAAoEAAAAAAAACz8////////////////AP///////wAAAAAAAAAAAAAAGNiAAAAAAAAAAIj///////////////8A////////AAAAAAAAAAAAABjY//gYAAAAAAAACOD//////////////wD///////8AAAAAAAAAAAAY2P///5wAAAAAAAAASP//////////////AP///////wAAAAAAAAAAGNj//////CgAAAAAAAAAqP////////////8A////////AAAAAAAAAADI////////sAAAAAAAAAAc8P///////////wD///////8AAAAAAAAAAP//////////QAAAAAAAAABs////////////AP///////wAAAAAAAAAA///////////IAAAAAAAAAATI//////////8A////////AAAAAAAAAAD///////////9YAAAAAAAAADD8/////////wD///////8AAAAAAAAAAP///////////9wEAAAAAAAAAJD/////////AP///////wAAAAAAAAAA/////////////3AAAAAAAAAADOT///////8A////////AAAAAAAAAAD/////////////7BAAAAAAAAAAUP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'L' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAD/////////////////////////////AP////////////8AAAAAAAAAAP////////////////////////////8A/////////////wAAAAAAAAAA/////////////////////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////wAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////////AAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'M' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////8AAAAAAAAAAAAAAHz//////3wAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAATP//////UAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAc//////8cAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAADw////8AAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAAAAAAALz////AAAAAAAAAAAAAAAAA////////AP//////AAAAAAAAAAAAAAAAkP///5AAAAAAAAAAAAAAAAD///////8A//////8AAAAAAAAAAAAAAABc////ZAAAAAAAAAAAAAAAAP///////wD//////wAAAAAAAAAoAAAAADD///8wAAAAACQAAAAAAAAA////////AP//////AAAAAAAAAFwAAAAABPz//AgAAAAAXAAAAAAAAAD///////8A//////8AAAAAAAAAkAAAAAAA0P/UAAAAAACQAAAAAAAAAP///////wD//////wAAAAAAAADMAAAAAACg/6gAAAAAAMQAAAAAAAAA////////AP//////AAAAAAAAAPgEAAAAAHD/dAAAAAAE+AAAAAAAAAD///////8A//////8AAAAAAAAA/zQAAAAAQP9IAAAAADD/AAAAAAAAAP///////wD//////wAAAAAAAAD/bAAAAAAQ/xQAAAAAaP8AAAAAAAAA////////AP//////AAAAAAAAAP+gAAAAAADQAAAAAACc/wAAAAAAAAD///////8A//////8AAAAAAAAA/9QAAAAAAGgAAAAAAND/AAAAAAAAAP///////wD//////wAAAAAAAAD//wwAAAAAFAAAAAAM/P8AAAAAAAAA////////AP//////AAAAAAAAAP//RAAAAAAAAAAAADz//wAAAAAAAAD///////8A//////8AAAAAAAAA//94AAAAAAAAAAAAcP//AAAAAAAAAP///////wD//////wAAAAAAAAD//7AAAAAAAAAAAACo//8AAAAAAAAA////////AP//////AAAAAAAAAP//5AAAAAAAAAAAANz//wAAAAAAAAD///////8A//////8AAAAAAAAA////HAAAAAAAAAAQ////AAAAAAAAAP///////wD//////wAAAAAAAAD///9QAAAAAAAAAEz///8AAAAAAAAA////////AP//////AAAAAAAAAP///4gAAAAAAAAAfP///wAAAAAAAAD///////8A//////8AAAAAAAAA////vAAAAAAAAACw////AAAAAAAAAP///////wD//////wAAAAAAAAD////wAAAAAAAAAOz///8AAAAAAAAA////////AP//////AAAAAAAAAP////8sAAAAAAAc/////wAAAAAAAAD///////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'N' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAALD/////////////AAAAAAAAAP//////////AP////////8AAAAAAAAAFOj///////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAASP///////////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAkP//////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAI1P////////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAw+P///////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAABw////////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAC8//////8AAAAAAAAA//////////8A/////////wAAAAAAAAAAAAAAABzs/////wAAAAAAAAD//////////wD/////////AAAAAAAAAAAAAAAAAFD/////AAAAAAAAAP//////////AP////////8AAAAAAAAAAAAAAAAAAJz///8AAAAAAAAA//////////8A/////////wAAAAAAAAAUAAAAAAAADNz//wAAAAAAAAD//////////wD/////////AAAAAAAAALQAAAAAAAAANPz/AAAAAAAAAP//////////AP////////8AAAAAAAAA/2wAAAAAAAAAfP8AAAAAAAAA//////////8A/////////wAAAAAAAAD/+CwAAAAAAAAExAAAAAAAAAD//////////wD/////////AAAAAAAAAP//0AQAAAAAAAAgAAAAAAAAAP//////////AP////////8AAAAAAAAA////jAAAAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////RAAAAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP/////kFAAAAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA//////+sAAAAAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD///////9kAAAAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP////////QkAAAAAAAAAAAAAP//////////AP////////8AAAAAAAAA/////////8wEAAAAAAAAAAAA//////////8A/////////wAAAAAAAAD//////////4QAAAAAAAAAAAD//////////wD/////////AAAAAAAAAP///////////DwAAAAAAAAAAP//////////AP////////8AAAAAAAAA////////////4BAAAAAAAAAA//////////8A/////////wAAAAAAAAD/////////////qAAAAAAAAAD//////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'O' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A///////////////////0qGw4HAAAABw4aKT0/////////////////wD////////////////wcAwAAAAAAAAAAAAAAAho6P//////////////AP//////////////uBQAAAAAAAAAAAAAAAAAAAAMoP////////////8A/////////////6AEAAAAAAAAAAAAAAAAAAAAAAAAkP///////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP//////////8BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAM5P////////8A//////////9wAAAAAAAAAAAsrPD/7KQsAAAAAAAAAABg/////////wD/////////+BAAAAAAAAAAUPj///////hQAAAAAAAAAAjs////////AP////////+sAAAAAAAAABDw//////////AYAAAAAAAAAKD///////8A/////////2wAAAAAAAAAdP///////////3wAAAAAAAAAYP///////wD/////////OAAAAAAAAAC4////////////xAAAAAAAAAAw////////AP////////8cAAAAAAAAAOD////////////oAAAAAAAAABT///////8A/////////wAAAAAAAAAA//////////////8AAAAAAAAAAP///////wD/////////AAAAAAAAAAD//////////////wAAAAAAAAAA////////AP////////8AAAAAAAAAAP/////////////8AAAAAAAAAAD///////8A/////////xwAAAAAAAAA5P///////////+AAAAAAAAAAHP///////wD/////////NAAAAAAAAAC8////////////uAAAAAAAAAA4////////AP////////9oAAAAAAAAAHj///////////98AAAAAAAAAGT///////8A/////////6gAAAAAAAAAGPD/////////+BgAAAAAAAAApP///////wD/////////9AwAAAAAAAAAUPz///////xcAAAAAAAAAAjs////////AP//////////cAAAAAAAAAAALKjs//CwOAAAAAAAAAAAYP////////8A///////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzk/////////wD///////////+4BAAAAAAAAAAAAAAAAAAAAAAAAAAAoP//////////AP////////////+QAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A//////////////+sEAAAAAAAAAAAAAAAAAAAAAyg/////////////wD////////////////oZAgAAAAAAAAAAAAAAARg4P//////////////AP//////////////////9KhsOCAAAAAUMFyc7P////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'P' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////////wAAAAAAAAAAAAAAAAAACCxguP////////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAOOD//////////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAGOD/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAARP////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAxP///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAABo////////////AP///////////wAAAAAAAAAA////6JwMAAAAAAAAADD///////////8A////////////AAAAAAAAAAD//////6AAAAAAAAAADP///////////wD///////////8AAAAAAAAAAP//////9AAAAAAAAAAA////////////AP///////////wAAAAAAAAAA///////0AAAAAAAAAAD///////////8A////////////AAAAAAAAAAD//////5gAAAAAAAAAHP///////////wD///////////8AAAAAAAAAAP///9iICAAAAAAAAABI////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAJD///////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////8AAAAAAAAAAAAAAAAAAAAAAAAAAIT/////////////AP///////////wAAAAAAAAAAAAAAAAAAAAAAAABU/P////////////8A////////////AAAAAAAAAAAAAAAAAAAAAAAIhPz//////////////wD///////////8AAAAAAAAAAAAAAAAABCRMkOz/////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP///////////wAAAAAAAAAA//////////////////////////////8A////////////AAAAAAAAAAD//////////////////////////////wD///////////8AAAAAAAAAAP//////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'Q' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////SoaDQcAAAAHDhoqPT///////////////////8A//////////////BwDAAAAAAAAAAAAAAACHDo/////////////////wD///////////+4FAAAAAAAAAAAAAAAAAAAABCo////////////////AP//////////nAQAAAAAAAAAAAAAAAAAAAAAAACQ//////////////8A/////////7gEAAAAAAAAAAAAAAAAAAAAAAAAAACg/////////////wD////////wFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzo////////////AP///////3AAAAAAAAAAACyo8P/sqCwAAAAAAAAAAGT///////////8A///////4EAAAAAAAAABM+P///////FQAAAAAAAAACPT//////////wD//////7AAAAAAAAAAFPD/////////9BgAAAAAAAAApP//////////AP//////bAAAAAAAAAB4////////////fAAAAAAAAABk//////////8A//////84AAAAAAAAALz///////////+8AAAAAAAAADT//////////wD//////xwAAAAAAAAA6P///////////+QAAAAAAAAAHP//////////AP//////AAAAAAAAAAD//////////////wAAAAAAAAAA//////////8A//////8AAAAAAAAAAP//////////////AAAAAAAAAAD//////////wD//////wAAAAAAAAAA/P////////////8AAAAAAAAAAP//////////AP//////GAAAAAAAAADg////////////4AAAAAAAAAAc//////////8A//////84AAAAAAAAALT////MJHTo//+8AAAAAAAAADT//////////wD//////2wAAAAAAAAAdP///2AAABCg/3wAAAAAAAAAZP//////////AP//////rAAAAAAAAAAY9P/sCAAAAABMGAAAAAAAAACk//////////8A///////4EAAAAAAAAABU/P+0OAAAAAAAAAAAAAAACPT//////////wD///////94AAAAAAAAAAA4sPD/gAAAAAAAAAAAAABk////////////AP////////AcAAAAAAAAAAAAAAAAAAAAAAAAAAAADOT///////////8A/////////7wEAAAAAAAAAAAAAAAAAAAAAAAAAACQ/////////////wD//////////6wEAAAAAAAAAAAAAAAAAAAAAAAAABSs////////////AP///////////7gUAAAAAAAAAAAAAAAAAAAAAAAAAABAwP////////8A//////////////BwDAAAAAAAAAAAAAAABAgAAAAAAAA8/////////wD////////////////0qGg0GAAAABgwXJjkxBgAAAAAALD/////////AP//////////////////////////////////5DQAAAAk/P////////8A////////////////////////////////////+GwAAJD//////////wD//////////////////////////////////////8A49P//////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'R' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////wAAAAAAAAAAAAAAAAAAAAQgOGSk+P///////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAcuP//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAEsP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ6P///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD///////////8A/////////wAAAAAAAAAA///////svDgAAAAAAAAACP///////////wD/////////AAAAAAAAAAD/////////7AAAAAAAAAAA////////////AP////////8AAAAAAAAAAP/////////cAAAAAAAAABD///////////8A/////////wAAAAAAAAAA//////DQoCQAAAAAAAAAQP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACU////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPj///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAzU/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAA02P//////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAxctPz///////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAEDY/////////////////wD/////////AAAAAAAAAAD/9LAsAAAAAAAAAAzc////////////////AP////////8AAAAAAAAAAP///+wkAAAAAAAAADD8//////////////8A/////////wAAAAAAAAAA/////8QAAAAAAAAAAJD//////////////wD/////////AAAAAAAAAAD//////1QAAAAAAAAAFPD/////////////AP////////8AAAAAAAAAAP//////3AQAAAAAAAAAgP////////////8A/////////wAAAAAAAAAA////////aAAAAAAAAAAM6P///////////wD/////////AAAAAAAAAAD////////oCAAAAAAAAABs////////////AP////////8AAAAAAAAAAP////////+AAAAAAAAAAATc//////////8A/////////wAAAAAAAAAA//////////AUAAAAAAAAAFj//////////wD/////////AAAAAAAAAAD//////////5AAAAAAAAAAAND/////////AP////////8AAAAAAAAAAP//////////+CQAAAAAAAAAQP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'S' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP/////////////////8vHBEIAgAAAQgQHC8/P////////////////8A////////////////pCQAAAAAAAAAAAAAAAAcoP///////////////wD//////////////FwAAAAAAAAAAAAAAAAAAAAAXP//////////////AP////////////9oAAAAAAAAAAAAAAAAAAAAAAAAhP////////////8A////////////zAAAAAAAAAAAAAAAAAAAAAAAAAAI6P///////////wD///////////9cAAAAAAAAAAAAAAAAAAAAAAAAAACA////////////AP///////////xgAAAAAAAAAUOD/8KwkAAAAAAAAADj///////////8A////////////AAAAAAAAAAD0/////8wABCAgICxASP///////////wD///////////8MAAAAAAAAAMz/////////////////////////////AP///////////0AAAAAAAAAACFiQxPT///////////////////////8A////////////oAAAAAAAAAAAAAAAADBwtPT//////////////////wD////////////8QAAAAAAAAAAAAAAAAAAACFTA////////////////AP/////////////oOAAAAAAAAAAAAAAAAAAAAABM6P////////////8A///////////////4fAgAAAAAAAAAAAAAAAAAAAAY2P///////////wD/////////////////7IwwAAAAAAAAAAAAAAAAAAAo+P//////////AP/////////////////////koGw0BAAAAAAAAAAAAACU//////////8A///////////////////////////4uFgAAAAAAAAAADz//////////wD//////////2BgSEA0IBwA6P///////5QAAAAAAAAADP//////////AP//////////JAAAAAAAAACc/////////AAAAAAAAAAA//////////8A//////////9YAAAAAAAAACDo///////AAAAAAAAAABT//////////wD//////////6QAAAAAAAAAACCk7P/snBQAAAAAAAAAUP//////////AP//////////+BAAAAAAAAAAAAAAAAAAAAAAAAAAAACs//////////8A////////////kAAAAAAAAAAAAAAAAAAAAAAAAAAAOP///////////wD////////////8RAAAAAAAAAAAAAAAAAAAAAAAABjc////////////AP/////////////0PAAAAAAAAAAAAAAAAAAAAAAg2P////////////8A///////////////8hBQAAAAAAAAAAAAAAAAMdPT//////////////wD/////////////////+LRwSCAMAAAAHDhoqPT/////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'T' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD///////////////////8AAAAAAAAAAP//////////////////////AP///////////////////wAAAAAAAAAA//////////////////////8A////////////////////AAAAAAAAAAD//////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'U' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////AAAAAAAAAAD///////////8AAAAAAAAAAP//////////AP////////8AAAAAAAAAAP///////////wAAAAAAAAAA//////////8A/////////wAAAAAAAAAA////////////AAAAAAAAAAD//////////wD/////////JAAAAAAAAADk/////////+gAAAAAAAAAHP//////////AP////////9MAAAAAAAAAJz/////////nAAAAAAAAABE//////////8A/////////4gAAAAAAAAAHOj//////+ggAAAAAAAAAHz//////////wD/////////0AAAAAAAAAAAIJzs/+ykIAAAAAAAAAAA0P//////////AP//////////QAAAAAAAAAAAAAAAAAAAAAAAAAAAAED///////////8A///////////IBAAAAAAAAAAAAAAAAAAAAAAAAAAE0P///////////wD///////////+YAAAAAAAAAAAAAAAAAAAAAAAAAJj/////////////AP////////////+UBAAAAAAAAAAAAAAAAAAAAASU//////////////8A///////////////IPAAAAAAAAAAAAAAAAAAwyP///////////////wD/////////////////0IxYOCAIAAAEIEiAyP//////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'V' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD//////zAAAAAAAAAAYP//////////////ZAAAAAAAAAAw////////AP//////kAAAAAAAAAAU/P////////////8UAAAAAAAAAJD///////8A///////oBAAAAAAAAADE////////////xAAAAAAAAAAE7P///////wD///////9MAAAAAAAAAHD///////////94AAAAAAAAAEz/////////AP///////6gAAAAAAAAAJP///////////yQAAAAAAAAArP////////8A////////+BAAAAAAAAAA1P/////////YAAAAAAAAABT4/////////wD/////////aAAAAAAAAACE/////////4QAAAAAAAAAbP//////////AP/////////EAAAAAAAAADT/////////OAAAAAAAAADM//////////8A//////////8kAAAAAAAAAOT//////+QAAAAAAAAAKP///////////wD//////////4QAAAAAAAAAmP//////nAAAAAAAAACI////////////AP//////////5AAAAAAAAABE//////9EAAAAAAAABOT///////////8A////////////QAAAAAAAAAT0////9AgAAAAAAABI/////////////wD///////////+gAAAAAAAAAKT///+kAAAAAAAAAKj/////////////AP////////////QIAAAAAAAAXP///1wAAAAAAAAM+P////////////8A/////////////1wAAAAAAAAM+P/8DAAAAAAAAGT//////////////wD/////////////vAAAAAAAAAC8/7wAAAAAAAAAxP//////////////AP//////////////HAAAAAAAAGj/aAAAAAAAACT///////////////8A//////////////94AAAAAAAAHP8cAAAAAAAAhP///////////////wD//////////////9gAAAAAAAAAkAAAAAAAAADk////////////////AP///////////////zgAAAAAAAAQAAAAAAAAQP////////////////8A////////////////lAAAAAAAAAAAAAAAAACg/////////////////wD////////////////sCAAAAAAAAAAAAAAADPT/////////////////AP////////////////9QAAAAAAAAAAAAAABg//////////////////8A/////////////////7AAAAAAAAAAAAAAAMD//////////////////wD//////////////////BQAAAAAAAAAAAAc////////////////////AP//////////////////cAAAAAAAAAAAAHz///////////////////8A///////////////////MAAAAAAAAAAAA3P///////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'W' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//8cAAAAAAAAALz/////4AAAAAAAAAAA6P////+8AAAAAAAAABz//wD//1QAAAAAAAAAjP////+gAAAAAAAAAACo/////4wAAAAAAAAAUP//AP//jAAAAAAAAABU/////2AAAAAAAAAAAGj/////VAAAAAAAAACM//8A///EAAAAAAAAACT/////IAAAAAAAAAAAKP////8kAAAAAAAAAMT//wD///gEAAAAAAAAAPD//+AAAAAAAAAAAAAA6P//8AAAAAAAAAAE9P//AP///zAAAAAAAAAAvP//oAAAAAAAAAAAAACo//+8AAAAAAAAADD///8A////bAAAAAAAAACM//9gAAAAAAAAAAAAAGT//4wAAAAAAAAAaP///wD///+kAAAAAAAAAFT//yAAAAAAAAAAAAAAIP//VAAAAAAAAACc////AP///9gAAAAAAAAAJP/gAAAAAAAAAAAAAAAA4P8kAAAAAAAAANT///8A/////xAAAAAAAAAA8KAAAAAAAAAAAAAAAACg8AAAAAAAAAAQ/////wD/////TAAAAAAAAAC8YAAAAAAAAAAAAAAAAGC8AAAAAAAAAET/////AP////+AAAAAAAAAAIwgAAAAAAAAAAAAAAAAIIwAAAAAAAAAfP////8A/////7gAAAAAAAAANAAAAAAAACwwAAAAAAAANAAAAAAAAACw/////wD/////8AAAAAAAAAAAAAAAAAAAdHgAAAAAAAAAAAAAAAAAAOz/////AP//////KAAAAAAAAAAAAAAAAAC4vAAAAAAAAAAAAAAAAAAg//////8A//////9gAAAAAAAAAAAAAAAACPj4CAAAAAAAAAAAAAAAAFj//////wD//////5QAAAAAAAAAAAAAAABE//9IAAAAAAAAAAAAAAAAkP//////AP//////0AAAAAAAAAAAAAAAAIj//4wAAAAAAAAAAAAAAADI//////8A///////8DAAAAAAAAAAAAAAAzP//1AAAAAAAAAAAAAAABPj//////wD///////88AAAAAAAAAAAAABT/////GAAAAAAAAAAAAAA0////////AP///////3QAAAAAAAAAAAAAWP////9gAAAAAAAAAAAAAHD///////8A////////sAAAAAAAAAAAAACg/////6QAAAAAAAAAAAAApP///////wD////////kAAAAAAAAAAAAAOT/////6AAAAAAAAAAAAADc////////AP////////8cAAAAAAAAAAAo////////MAAAAAAAAAAAEP////////8A/////////1QAAAAAAAAAAHD///////94AAAAAAAAAABM/////////wD/////////jAAAAAAAAAAAtP///////7wAAAAAAAAAAID/////////AP/////////EAAAAAAAAAAT0////////+AgAAAAAAAAAuP////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'X' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD///////9UAAAAAAAAAKz///////////+sAAAAAAAAAFD/////////AP///////+QQAAAAAAAAFOT/////////8BwAAAAAAAAM5P////////8A/////////5gAAAAAAAAATP////////9kAAAAAAAAAJD//////////wD//////////0AAAAAAAAAAoP//////wAAAAAAAAAA0/P//////////AP//////////2AgAAAAAAAAQ4P////gkAAAAAAAABMz///////////8A////////////iAAAAAAAAABA////dAAAAAAAAABw/////////////wD////////////8MAAAAAAAAACU/9AEAAAAAAAAHPD/////////////AP/////////////IBAAAAAAAAAzYMAAAAAAAAACs//////////////8A//////////////90AAAAAAAAABAAAAAAAAAATP///////////////wD///////////////QgAAAAAAAAAAAAAAAAAAzg////////////////AP///////////////7wAAAAAAAAAAAAAAAAAjP////////////////8A/////////////////2AAAAAAAAAAAAAAADD8/////////////////wD/////////////////7BQAAAAAAAAAAAAEyP//////////////////AP/////////////////gDAAAAAAAAAAAAAjY//////////////////8A/////////////////0AAAAAAAAAAAAAAADj8/////////////////wD///////////////+UAAAAAAAAAAAAAAAAAJD/////////////////AP//////////////4AwAAAAAAAAAAAAAAAAADOD///////////////8A//////////////9AAAAAAAAAAAAAAAAAAAAAQP///////////////wD/////////////nAAAAAAAAAAAWAAAAAAAAAAAlP//////////////AP///////////+QQAAAAAAAAAGD/YAAAAAAAAAAM4P////////////8A////////////TAAAAAAAAAAs9P/0LAAAAAAAAABM/////////////wD//////////6AAAAAAAAAADNT////UDAAAAAAAAACg////////////AP/////////kEAAAAAAAAACg//////+gAAAAAAAAABDk//////////8A/////////0wAAAAAAAAAYP////////9gAAAAAAAAAEz//////////wD///////+oAAAAAAAAACz0//////////QsAAAAAAAAAKT/////////AP//////7BQAAAAAAAAM1P///////////9QMAAAAAAAAFOz///////8A//////9UAAAAAAAAAKD//////////////6AAAAAAAAAAVP///////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'Y' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP///////1QAAAAAAAAAAGj//////////2gAAAAAAAAAAFT///////8A////////5BAAAAAAAAAAAMT////////EAAAAAAAAAAAQ5P///////wD/////////mAAAAAAAAAAAKPj/////+CgAAAAAAAAAAJj/////////AP//////////PAAAAAAAAAAAgP////+AAAAAAAAAAAA8//////////8A///////////YCAAAAAAAAAAE2P//2AQAAAAAAAAACNj//////////wD///////////+AAAAAAAAAAAA4//84AAAAAAAAAACA////////////AP////////////woAAAAAAAAAACUlAAAAAAAAAAAKPz///////////8A/////////////8gAAAAAAAAAABAQAAAAAAAAAADI/////////////wD//////////////2wAAAAAAAAAAAAAAAAAAAAAbP//////////////AP//////////////8BwAAAAAAAAAAAAAAAAAABzw//////////////8A////////////////tAAAAAAAAAAAAAAAAAAAtP///////////////wD/////////////////VAAAAAAAAAAAAAAAAFT/////////////////AP/////////////////oEAAAAAAAAAAAAAAQ6P////////////////8A//////////////////+cAAAAAAAAAAAAAJz//////////////////wD///////////////////9AAAAAAAAAAABA////////////////////AP///////////////////9gAAAAAAAAAANj///////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////8AAAAAAAAAAP////////////////////8A/////////////////////wAAAAAAAAAA/////////////////////wD/////////////////////AAAAAAAAAAD/////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ 'Z' => array(
+ 'data' => 'AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////8A//////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////wD//////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////AP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAQ//////////////8A/////////////////////////1AAAAAAAAAABLz//////////////wD///////////////////////98AAAAAAAAAACY////////////////AP//////////////////////pAAAAAAAAAAAaP////////////////8A/////////////////////8QIAAAAAAAAAET8/////////////////wD////////////////////gGAAAAAAAAAAo9P//////////////////AP//////////////////9CwAAAAAAAAAFNz///////////////////8A//////////////////xMAAAAAAAAAATA/////////////////////wD/////////////////eAAAAAAAAAAAnP//////////////////////AP///////////////5wAAAAAAAAAAHT///////////////////////8A///////////////ABAAAAAAAAABM/P///////////////////////wD/////////////3BQAAAAAAAAALPT/////////////////////////AP////////////QoAAAAAAAAABjg//////////////////////////8A///////////8SAAAAAAAAAAExP///////////////////////////wD//////////2wAAAAAAAAAAKD/////////////////////////////AP////////+YAAAAAAAAAAB8//////////////////////////////8A/////////wQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////////////wD/////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////AP////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8A/////////////////////////////////////////////////////wD/////////////////////////////////////////////////////AP////////////////////////////////////////////////////8=',
+ 'width' => 40
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/captcha/plugins/captcha_abstract.php b/phpBB/phpbb/captcha/plugins/captcha_abstract.php
new file mode 100644
index 0000000000..82b08704ff
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/captcha_abstract.php
@@ -0,0 +1,390 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha\plugins;
+
+/**
+* This class holds the code shared by the two default 3.0.x CAPTCHAs.
+*/
+abstract class captcha_abstract
+{
+ var $confirm_id;
+ var $confirm_code;
+ var $code;
+ var $seed;
+ var $attempts = 0;
+ var $type;
+ var $solved = 0;
+ var $captcha_vars = false;
+
+ /**
+ * @var string name of the service.
+ */
+ protected $service_name;
+
+ function init($type)
+ {
+ global $config, $request;
+
+ // read input
+ $this->confirm_id = $request->variable('confirm_id', '');
+ $this->confirm_code = $request->variable('confirm_code', '');
+ $refresh = $request->variable('refresh_vc', false) && $config['confirm_refresh'];
+
+ $this->type = (int) $type;
+
+ if (!strlen($this->confirm_id) || !$this->load_code())
+ {
+ // we have no confirm ID, better get ready to display something
+ $this->generate_code();
+ }
+ else if ($refresh)
+ {
+ $this->regenerate_code();
+ }
+ }
+
+ function execute_demo()
+ {
+ $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS));
+ $this->seed = hexdec(substr(unique_id(), 4, 10));
+
+ // compute $seed % 0x7fffffff
+ $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff);
+
+ $generator = $this->get_generator_class();
+ $captcha = new $generator();
+ define('IMAGE_OUTPUT', 1);
+ $captcha->execute($this->code, $this->seed);
+ }
+
+ function execute()
+ {
+ if (empty($this->code))
+ {
+ if (!$this->load_code())
+ {
+ // invalid request, bail out
+ return false;
+ }
+ }
+ $generator = $this->get_generator_class();
+ $captcha = new $generator();
+ define('IMAGE_OUTPUT', 1);
+ $captcha->execute($this->code, $this->seed);
+ }
+
+ function get_template()
+ {
+ global $config, $user, $template, $phpEx, $phpbb_root_path;
+
+ if ($this->is_solved())
+ {
+ return false;
+ }
+ else
+ {
+ $link = append_sid($phpbb_root_path . 'ucp.' . $phpEx, 'mode=confirm&amp;confirm_id=' . $this->confirm_id . '&amp;type=' . $this->type);
+ $contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx);
+ $explain = $user->lang(($this->type != CONFIRM_POST) ? 'CONFIRM_EXPLAIN' : 'POST_CONFIRM_EXPLAIN', '<a href="' . $contact_link . '">', '</a>');
+
+ $template->assign_vars(array(
+ 'CONFIRM_IMAGE_LINK' => $link,
+ 'CONFIRM_IMAGE' => '<img src="' . $link . '" />',
+ 'CONFIRM_IMG' => '<img src="' . $link . '" />',
+ 'CONFIRM_ID' => $this->confirm_id,
+ 'S_CONFIRM_CODE' => true,
+ 'S_TYPE' => $this->type,
+ 'S_CONFIRM_REFRESH' => ($config['enable_confirm'] && $config['confirm_refresh'] && $this->type == CONFIRM_REG) ? true : false,
+ 'L_CONFIRM_EXPLAIN' => $explain,
+ ));
+
+ return 'captcha_default.html';
+ }
+ }
+
+ function get_demo_template($id)
+ {
+ global $config, $template, $request, $phpbb_admin_path, $phpEx;
+
+ $variables = '';
+
+ if (is_array($this->captcha_vars))
+ {
+ foreach ($this->captcha_vars as $captcha_var => $template_var)
+ {
+ $variables .= '&amp;' . rawurlencode($captcha_var) . '=' . $request->variable($captcha_var, (int) $config[$captcha_var]);
+ }
+ }
+
+ // acp_captcha has a delivery function; let's use it
+ $template->assign_vars(array(
+ 'CONFIRM_IMAGE' => append_sid($phpbb_admin_path . 'index.' . $phpEx, 'captcha_demo=1&amp;mode=visual&amp;i=' . $id . '&amp;select_captcha=' . $this->get_service_name()) . $variables,
+ 'CONFIRM_ID' => $this->confirm_id,
+ ));
+
+ return 'captcha_default_acp_demo.html';
+ }
+
+ function get_hidden_fields()
+ {
+ $hidden_fields = array();
+
+ // this is required for posting.php - otherwise we would forget about the captcha being already solved
+ if ($this->solved)
+ {
+ $hidden_fields['confirm_code'] = $this->confirm_code;
+ }
+ $hidden_fields['confirm_id'] = $this->confirm_id;
+ return $hidden_fields;
+ }
+
+ function garbage_collect($type)
+ {
+ global $db;
+
+ $sql = 'SELECT DISTINCT c.session_id
+ FROM ' . CONFIRM_TABLE . ' c
+ LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id)
+ WHERE s.session_id IS NULL' .
+ ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type);
+ $result = $db->sql_query($sql);
+
+ if ($row = $db->sql_fetchrow($result))
+ {
+ $sql_in = array();
+ do
+ {
+ $sql_in[] = (string) $row['session_id'];
+ }
+ while ($row = $db->sql_fetchrow($result));
+
+ if (sizeof($sql_in))
+ {
+ $sql = 'DELETE FROM ' . CONFIRM_TABLE . '
+ WHERE ' . $db->sql_in_set('session_id', $sql_in);
+ $db->sql_query($sql);
+ }
+ }
+ $db->sql_freeresult($result);
+ }
+
+ function uninstall()
+ {
+ $this->garbage_collect(0);
+ }
+
+ function install()
+ {
+ return;
+ }
+
+ function validate()
+ {
+ global $user;
+
+ if (!$user->is_setup())
+ {
+ $user->setup();
+ }
+
+ $error = '';
+ if (!$this->confirm_id)
+ {
+ $error = $user->lang['CONFIRM_CODE_WRONG'];
+ }
+ else
+ {
+ if ($this->check_code())
+ {
+ $this->solved = true;
+ }
+ else
+ {
+ $error = $user->lang['CONFIRM_CODE_WRONG'];
+ }
+ }
+
+ if (strlen($error))
+ {
+ // okay, incorrect answer. Let's ask a new question.
+ $this->new_attempt();
+ return $error;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * The old way to generate code, suitable for GD and non-GD. Resets the internal state.
+ */
+ function generate_code()
+ {
+ global $db, $user;
+
+ $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS));
+ $this->confirm_id = md5(unique_id($user->ip));
+ $this->seed = hexdec(substr(unique_id(), 4, 10));
+ $this->solved = 0;
+ // compute $seed % 0x7fffffff
+ $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff);
+
+ $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
+ 'confirm_id' => (string) $this->confirm_id,
+ 'session_id' => (string) $user->session_id,
+ 'confirm_type' => (int) $this->type,
+ 'code' => (string) $this->code,
+ 'seed' => (int) $this->seed)
+ );
+ $db->sql_query($sql);
+ }
+
+ /**
+ * New Question, if desired.
+ */
+ function regenerate_code()
+ {
+ global $db, $user;
+
+ $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS));
+ $this->seed = hexdec(substr(unique_id(), 4, 10));
+ $this->solved = 0;
+ // compute $seed % 0x7fffffff
+ $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff);
+
+ $sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array(
+ 'code' => (string) $this->code,
+ 'seed' => (int) $this->seed)) . '
+ WHERE
+ confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\'
+ AND session_id = \'' . $db->sql_escape($user->session_id) . '\'';
+ $db->sql_query($sql);
+ }
+
+ /**
+ * New Question, if desired.
+ */
+ function new_attempt()
+ {
+ global $db, $user;
+
+ $this->code = gen_rand_string_friendly(mt_rand(CAPTCHA_MIN_CHARS, CAPTCHA_MAX_CHARS));
+ $this->seed = hexdec(substr(unique_id(), 4, 10));
+ $this->solved = 0;
+ // compute $seed % 0x7fffffff
+ $this->seed -= 0x7fffffff * floor($this->seed / 0x7fffffff);
+
+ $sql = 'UPDATE ' . CONFIRM_TABLE . ' SET ' . $db->sql_build_array('UPDATE', array(
+ 'code' => (string) $this->code,
+ 'seed' => (int) $this->seed)) . '
+ , attempts = attempts + 1
+ WHERE
+ confirm_id = \'' . $db->sql_escape($this->confirm_id) . '\'
+ AND session_id = \'' . $db->sql_escape($user->session_id) . '\'';
+ $db->sql_query($sql);
+ }
+
+ /**
+ * Look up everything we need for painting&checking.
+ */
+ function load_code()
+ {
+ global $db, $user;
+
+ $sql = 'SELECT code, seed, attempts
+ FROM ' . CONFIRM_TABLE . "
+ WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
+ AND session_id = '" . $db->sql_escape($user->session_id) . "'
+ AND confirm_type = " . $this->type;
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $this->code = $row['code'];
+ $this->seed = $row['seed'];
+ $this->attempts = $row['attempts'];
+ return true;
+ }
+
+ return false;
+ }
+
+ function check_code()
+ {
+ return (strcasecmp($this->code, $this->confirm_code) === 0);
+ }
+
+ function get_attempt_count()
+ {
+ return $this->attempts;
+ }
+
+ function reset()
+ {
+ global $db, $user;
+
+ $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
+ WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
+ AND confirm_type = " . (int) $this->type;
+ $db->sql_query($sql);
+
+ // we leave the class usable by generating a new question
+ $this->generate_code();
+ }
+
+ function is_solved()
+ {
+ global $request;
+
+ if ($request->variable('confirm_code', false) && $this->solved === 0)
+ {
+ $this->validate();
+ }
+ return (bool) $this->solved;
+ }
+
+ /**
+ * API function
+ */
+ function has_config()
+ {
+ return false;
+ }
+
+ /**
+ * @return string the name of the service corresponding to the plugin
+ */
+ function get_service_name()
+ {
+ return $this->service_name;
+ }
+
+ /**
+ * Set the name of the plugin
+ *
+ * @param string $name
+ */
+ public function set_name($name)
+ {
+ $this->service_name = $name;
+ }
+
+ /**
+ * @return string the name of the class used to generate the captcha
+ */
+ abstract function get_generator_class();
+}
diff --git a/phpBB/phpbb/captcha/plugins/gd.php b/phpBB/phpbb/captcha/plugins/gd.php
new file mode 100644
index 0000000000..831e5bcfdf
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/gd.php
@@ -0,0 +1,123 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha\plugins;
+
+class gd extends captcha_abstract
+{
+ var $captcha_vars = array(
+ 'captcha_gd_x_grid' => 'CAPTCHA_GD_X_GRID',
+ 'captcha_gd_y_grid' => 'CAPTCHA_GD_Y_GRID',
+ 'captcha_gd_foreground_noise' => 'CAPTCHA_GD_FOREGROUND_NOISE',
+// 'captcha_gd' => 'CAPTCHA_GD_PREVIEWED',
+ 'captcha_gd_wave' => 'CAPTCHA_GD_WAVE',
+ 'captcha_gd_3d_noise' => 'CAPTCHA_GD_3D_NOISE',
+ 'captcha_gd_fonts' => 'CAPTCHA_GD_FONTS',
+ );
+
+ public function is_available()
+ {
+ return @extension_loaded('gd');
+ }
+
+ /**
+ * @return string the name of the class used to generate the captcha
+ */
+ function get_generator_class()
+ {
+ return '\\phpbb\\captcha\\gd';
+ }
+
+ /**
+ * API function
+ */
+ function has_config()
+ {
+ return true;
+ }
+
+ public function get_name()
+ {
+ return 'CAPTCHA_GD';
+ }
+
+ function acp_page($id, &$module)
+ {
+ global $user, $template, $phpbb_log, $request;
+ global $config;
+
+ $user->add_lang('acp/board');
+
+ $module->tpl_name = 'captcha_gd_acp';
+ $module->page_title = 'ACP_VC_SETTINGS';
+ $form_key = 'acp_captcha';
+ add_form_key($form_key);
+
+ $submit = $request->variable('submit', '');
+
+ if ($submit && check_form_key($form_key))
+ {
+ $captcha_vars = array_keys($this->captcha_vars);
+ foreach ($captcha_vars as $captcha_var)
+ {
+ $value = $request->variable($captcha_var, 0);
+ if ($value >= 0)
+ {
+ $config->set($captcha_var, $value);
+ }
+ }
+
+ $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
+ trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($module->u_action));
+ }
+ else if ($submit)
+ {
+ trigger_error($user->lang['FORM_INVALID'] . adm_back_link($module->u_action));
+ }
+ else
+ {
+ foreach ($this->captcha_vars as $captcha_var => $template_var)
+ {
+ $var = (isset($_REQUEST[$captcha_var])) ? $request->variable($captcha_var, 0) : $config[$captcha_var];
+ $template->assign_var($template_var, $var);
+ }
+
+ $template->assign_vars(array(
+ 'CAPTCHA_PREVIEW' => $this->get_demo_template($id),
+ 'CAPTCHA_NAME' => $this->get_service_name(),
+ 'U_ACTION' => $module->u_action,
+ ));
+ }
+ }
+
+ function execute_demo()
+ {
+ global $config, $request;
+
+ $config_old = $config;
+
+ $config = new \phpbb\config\config(array());
+ foreach ($config_old as $key => $value)
+ {
+ $config->set($key, $value);
+ }
+
+ foreach ($this->captcha_vars as $captcha_var => $template_var)
+ {
+ $config->set($captcha_var, $request->variable($captcha_var, (int) $config[$captcha_var]));
+ }
+ parent::execute_demo();
+ $config = $config_old;
+ }
+
+}
diff --git a/phpBB/phpbb/captcha/plugins/gd_wave.php b/phpBB/phpbb/captcha/plugins/gd_wave.php
new file mode 100644
index 0000000000..bde46f8815
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/gd_wave.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\captcha\plugins;
+
+class gd_wave extends captcha_abstract
+{
+ public function is_available()
+ {
+ return @extension_loaded('gd');
+ }
+
+ public function get_name()
+ {
+ return 'CAPTCHA_GD_3D';
+ }
+
+ /**
+ * @return string the name of the class used to generate the captcha
+ */
+ function get_generator_class()
+ {
+ return '\\phpbb\\captcha\\gd_wave';
+ }
+
+ function acp_page($id, &$module)
+ {
+ global $user;
+
+ trigger_error($user->lang['CAPTCHA_NO_OPTIONS'] . adm_back_link($module->u_action));
+ }
+}
diff --git a/phpBB/phpbb/captcha/plugins/nogd.php b/phpBB/phpbb/captcha/plugins/nogd.php
new file mode 100644
index 0000000000..6845e5935c
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/nogd.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\captcha\plugins;
+
+class nogd extends captcha_abstract
+{
+ public function is_available()
+ {
+ return true;
+ }
+
+ public function get_name()
+ {
+ return 'CAPTCHA_NO_GD';
+ }
+
+ /**
+ * @return string the name of the class used to generate the captcha
+ */
+ function get_generator_class()
+ {
+ return '\\phpbb\\captcha\\non_gd';
+ }
+
+ function acp_page($id, &$module)
+ {
+ global $user;
+
+ trigger_error($user->lang['CAPTCHA_NO_OPTIONS'] . adm_back_link($module->u_action));
+ }
+}
diff --git a/phpBB/phpbb/captcha/plugins/qa.php b/phpBB/phpbb/captcha/plugins/qa.php
new file mode 100644
index 0000000000..4df8a86432
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/qa.php
@@ -0,0 +1,1011 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha\plugins;
+
+/**
+* And now to something completely different. Let's make a captcha without extending the abstract class.
+* QA CAPTCHA sample implementation
+*/
+class qa
+{
+ var $confirm_id;
+ var $answer;
+ var $question_ids;
+ var $question_text;
+ var $question_lang;
+ var $question_strict;
+ var $attempts = 0;
+ var $type;
+ // dirty trick: 0 is false, but can still encode that the captcha is not yet validated
+ var $solved = 0;
+
+ protected $table_captcha_questions;
+ protected $table_captcha_answers;
+ protected $table_qa_confirm;
+
+ /**
+ * @var string name of the service.
+ */
+ protected $service_name;
+
+ /**
+ * Constructor
+ *
+ * @param string $table_captcha_questions
+ * @param string $table_captcha_answers
+ * @param string $table_qa_confirm
+ */
+ function __construct($table_captcha_questions, $table_captcha_answers, $table_qa_confirm)
+ {
+ $this->table_captcha_questions = $table_captcha_questions;
+ $this->table_captcha_answers = $table_captcha_answers;
+ $this->table_qa_confirm = $table_qa_confirm;
+ }
+
+ /**
+ * @param int $type as per the CAPTCHA API docs, the type
+ */
+ function init($type)
+ {
+ global $config, $db, $user, $request;
+
+ // load our language file
+ $user->add_lang('captcha_qa');
+
+ // read input
+ $this->confirm_id = $request->variable('qa_confirm_id', '');
+ $this->answer = $request->variable('qa_answer', '', true);
+
+ $this->type = (int) $type;
+ $this->question_lang = $user->lang_name;
+
+ // we need all defined questions - shouldn't be too many, so we can just grab them
+ // try the user's lang first
+ $sql = 'SELECT question_id
+ FROM ' . $this->table_captcha_questions . "
+ WHERE lang_iso = '" . $db->sql_escape($user->lang_name) . "'";
+ $result = $db->sql_query($sql, 3600);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $this->question_ids[$row['question_id']] = $row['question_id'];
+ }
+ $db->sql_freeresult($result);
+
+ // fallback to the board default lang
+ if (!sizeof($this->question_ids))
+ {
+ $this->question_lang = $config['default_lang'];
+
+ $sql = 'SELECT question_id
+ FROM ' . $this->table_captcha_questions . "
+ WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
+ $result = $db->sql_query($sql, 7200);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $this->question_ids[$row['question_id']] = $row['question_id'];
+ }
+ $db->sql_freeresult($result);
+ }
+
+ // okay, if there is a confirm_id, we try to load that confirm's state. If not, we try to find one
+ if (!$this->load_answer() && (!$this->load_confirm_id() || !$this->load_answer()))
+ {
+ // we have no valid confirm ID, better get ready to ask something
+ $this->select_question();
+ }
+ }
+
+ /**
+ * See if the captcha has created its tables.
+ */
+ public function is_installed()
+ {
+ global $phpbb_container;
+
+ $db_tool = $phpbb_container->get('dbal.tools');
+
+ return $db_tool->sql_table_exists($this->table_captcha_questions);
+ }
+
+ /**
+ * API function - for the captcha to be available, it must have installed itself and there has to be at least one question in the board's default lang
+ */
+ public function is_available()
+ {
+ global $config, $db, $user;
+
+ // load language file for pretty display in the ACP dropdown
+ $user->add_lang('captcha_qa');
+
+ if (!$this->is_installed())
+ {
+ return false;
+ }
+
+ $sql = 'SELECT COUNT(question_id) AS question_count
+ FROM ' . $this->table_captcha_questions . "
+ WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ return ((bool) $row['question_count']);
+ }
+
+ /**
+ * API function
+ */
+ function has_config()
+ {
+ return true;
+ }
+
+ /**
+ * API function
+ */
+ static public function get_name()
+ {
+ return 'CAPTCHA_QA';
+ }
+
+ /**
+ * @return string the name of the service corresponding to the plugin
+ */
+ function get_service_name()
+ {
+ return $this->service_name;
+ }
+
+ /**
+ * Set the name of the plugin
+ *
+ * @param string $name
+ */
+ public function set_name($name)
+ {
+ $this->service_name = $name;
+ }
+
+ /**
+ * API function - not needed as we don't display an image
+ */
+ function execute_demo()
+ {
+ }
+
+ /**
+ * API function - not needed as we don't display an image
+ */
+ function execute()
+ {
+ }
+
+ /**
+ * API function - send the question to the template
+ */
+ function get_template()
+ {
+ global $template;
+
+ if ($this->is_solved())
+ {
+ return false;
+ }
+ else
+ {
+ $template->assign_vars(array(
+ 'QA_CONFIRM_QUESTION' => $this->question_text,
+ 'QA_CONFIRM_ID' => $this->confirm_id,
+ 'S_CONFIRM_CODE' => true,
+ 'S_TYPE' => $this->type,
+ ));
+
+ return 'captcha_qa.html';
+ }
+ }
+
+ /**
+ * API function - we just display a mockup so that the captcha doesn't need to be installed
+ */
+ function get_demo_template()
+ {
+ global $config, $db, $template;
+
+ if ($this->is_available())
+ {
+ $sql = 'SELECT question_text
+ FROM ' . $this->table_captcha_questions . "
+ WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'";
+ $result = $db->sql_query_limit($sql, 1);
+ if ($row = $db->sql_fetchrow($result))
+ {
+ $template->assign_vars(array(
+ 'QA_CONFIRM_QUESTION' => $row['question_text'],
+ ));
+ }
+ $db->sql_freeresult($result);
+ }
+ return 'captcha_qa_acp_demo.html';
+ }
+
+ /**
+ * API function
+ */
+ function get_hidden_fields()
+ {
+ $hidden_fields = array();
+
+ // this is required - otherwise we would forget about the captcha being already solved
+ if ($this->solved)
+ {
+ $hidden_fields['qa_answer'] = $this->answer;
+ }
+ $hidden_fields['qa_confirm_id'] = $this->confirm_id;
+
+ return $hidden_fields;
+ }
+
+ /**
+ * API function
+ */
+ function garbage_collect($type = 0)
+ {
+ global $db;
+
+ $sql = 'SELECT c.confirm_id
+ FROM ' . $this->table_qa_confirm . ' c
+ LEFT JOIN ' . SESSIONS_TABLE . ' s
+ ON (c.session_id = s.session_id)
+ WHERE s.session_id IS NULL' .
+ ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type);
+ $result = $db->sql_query($sql);
+
+ if ($row = $db->sql_fetchrow($result))
+ {
+ $sql_in = array();
+
+ do
+ {
+ $sql_in[] = (string) $row['confirm_id'];
+ }
+ while ($row = $db->sql_fetchrow($result));
+
+ if (sizeof($sql_in))
+ {
+ $sql = 'DELETE FROM ' . $this->table_qa_confirm . '
+ WHERE ' . $db->sql_in_set('confirm_id', $sql_in);
+ $db->sql_query($sql);
+ }
+ }
+ $db->sql_freeresult($result);
+ }
+
+ /**
+ * API function - we don't drop the tables here, as that would cause the loss of all entered questions.
+ */
+ function uninstall()
+ {
+ $this->garbage_collect(0);
+ }
+
+ /**
+ * API function - set up shop
+ */
+ function install()
+ {
+ global $phpbb_container;
+
+ $db_tool = $phpbb_container->get('dbal.tools');
+ $schemas = array(
+ $this->table_captcha_questions => array (
+ 'COLUMNS' => array(
+ 'question_id' => array('UINT', null, 'auto_increment'),
+ 'strict' => array('BOOL', 0),
+ 'lang_id' => array('UINT', 0),
+ 'lang_iso' => array('VCHAR:30', ''),
+ 'question_text' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'question_id',
+ 'KEYS' => array(
+ 'lang' => array('INDEX', 'lang_iso'),
+ ),
+ ),
+ $this->table_captcha_answers => array (
+ 'COLUMNS' => array(
+ 'question_id' => array('UINT', 0),
+ 'answer_text' => array('STEXT_UNI', ''),
+ ),
+ 'KEYS' => array(
+ 'qid' => array('INDEX', 'question_id'),
+ ),
+ ),
+ $this->table_qa_confirm => array (
+ 'COLUMNS' => array(
+ 'session_id' => array('CHAR:32', ''),
+ 'confirm_id' => array('CHAR:32', ''),
+ 'lang_iso' => array('VCHAR:30', ''),
+ 'question_id' => array('UINT', 0),
+ 'attempts' => array('UINT', 0),
+ 'confirm_type' => array('USINT', 0),
+ ),
+ 'KEYS' => array(
+ 'session_id' => array('INDEX', 'session_id'),
+ 'lookup' => array('INDEX', array('confirm_id', 'session_id', 'lang_iso')),
+ ),
+ 'PRIMARY_KEY' => 'confirm_id',
+ ),
+ );
+
+ foreach ($schemas as $table => $schema)
+ {
+ if (!$db_tool->sql_table_exists($table))
+ {
+ $db_tool->sql_create_table($table, $schema);
+ }
+ }
+ }
+
+ /**
+ * API function - see what has to be done to validate
+ */
+ function validate()
+ {
+ global $user;
+
+ $error = '';
+
+ if (!sizeof($this->question_ids))
+ {
+ return false;
+ }
+
+ if (!$this->confirm_id)
+ {
+ $error = $user->lang['CONFIRM_QUESTION_WRONG'];
+ }
+ else
+ {
+ if ($this->check_answer())
+ {
+ $this->solved = true;
+ }
+ else
+ {
+ $error = $user->lang['CONFIRM_QUESTION_WRONG'];
+ }
+ }
+
+ if (strlen($error))
+ {
+ // okay, incorrect answer. Let's ask a new question.
+ $this->new_attempt();
+ $this->solved = false;
+
+ return $error;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Select a question
+ */
+ function select_question()
+ {
+ global $db, $user;
+
+ if (!sizeof($this->question_ids))
+ {
+ return;
+ }
+ $this->confirm_id = md5(unique_id($user->ip));
+ $this->question = (int) array_rand($this->question_ids);
+
+ $sql = 'INSERT INTO ' . $this->table_qa_confirm . ' ' . $db->sql_build_array('INSERT', array(
+ 'confirm_id' => (string) $this->confirm_id,
+ 'session_id' => (string) $user->session_id,
+ 'lang_iso' => (string) $this->question_lang,
+ 'confirm_type' => (int) $this->type,
+ 'question_id' => (int) $this->question,
+ ));
+ $db->sql_query($sql);
+
+ $this->load_answer();
+ }
+
+ /**
+ * New Question, if desired.
+ */
+ function reselect_question()
+ {
+ global $db, $user;
+
+ if (!sizeof($this->question_ids))
+ {
+ return;
+ }
+
+ $this->question = (int) array_rand($this->question_ids);
+ $this->solved = 0;
+
+ $sql = 'UPDATE ' . $this->table_qa_confirm . '
+ SET question_id = ' . (int) $this->question . "
+ WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
+ AND session_id = '" . $db->sql_escape($user->session_id) . "'";
+ $db->sql_query($sql);
+
+ $this->load_answer();
+ }
+
+ /**
+ * Wrong answer, so we increase the attempts and use a different question.
+ */
+ function new_attempt()
+ {
+ global $db, $user;
+
+ // yah, I would prefer a stronger rand, but this should work
+ $this->question = (int) array_rand($this->question_ids);
+ $this->solved = 0;
+
+ $sql = 'UPDATE ' . $this->table_qa_confirm . '
+ SET question_id = ' . (int) $this->question . ",
+ attempts = attempts + 1
+ WHERE confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
+ AND session_id = '" . $db->sql_escape($user->session_id) . "'";
+ $db->sql_query($sql);
+
+ $this->load_answer();
+ }
+
+
+ /**
+ * See if there is already an entry for the current session.
+ */
+ function load_confirm_id()
+ {
+ global $db, $user;
+
+ $sql = 'SELECT confirm_id
+ FROM ' . $this->table_qa_confirm . "
+ WHERE
+ session_id = '" . $db->sql_escape($user->session_id) . "'
+ AND lang_iso = '" . $db->sql_escape($this->question_lang) . "'
+ AND confirm_type = " . $this->type;
+ $result = $db->sql_query_limit($sql, 1);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $this->confirm_id = $row['confirm_id'];
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Look up everything we need and populate the instance variables.
+ */
+ function load_answer()
+ {
+ global $db, $user;
+
+ if (!strlen($this->confirm_id) || !sizeof($this->question_ids))
+ {
+ return false;
+ }
+
+ $sql = 'SELECT con.question_id, attempts, question_text, strict
+ FROM ' . $this->table_qa_confirm . ' con, ' . $this->table_captcha_questions . " qes
+ WHERE con.question_id = qes.question_id
+ AND confirm_id = '" . $db->sql_escape($this->confirm_id) . "'
+ AND session_id = '" . $db->sql_escape($user->session_id) . "'
+ AND qes.lang_iso = '" . $db->sql_escape($this->question_lang) . "'
+ AND confirm_type = " . $this->type;
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $this->question = $row['question_id'];
+
+ $this->attempts = $row['attempts'];
+ $this->question_strict = $row['strict'];
+ $this->question_text = $row['question_text'];
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * The actual validation
+ */
+ function check_answer()
+ {
+ global $db, $request;
+
+ $answer = ($this->question_strict) ? $request->variable('qa_answer', '', true) : utf8_clean_string($request->variable('qa_answer', '', true));
+
+ $sql = 'SELECT answer_text
+ FROM ' . $this->table_captcha_answers . '
+ WHERE question_id = ' . (int) $this->question;
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $solution = ($this->question_strict) ? $row['answer_text'] : utf8_clean_string($row['answer_text']);
+
+ if ($solution === $answer)
+ {
+ $this->solved = true;
+
+ break;
+ }
+ }
+ $db->sql_freeresult($result);
+
+ return $this->solved;
+ }
+
+ /**
+ * API function
+ */
+ function get_attempt_count()
+ {
+ return $this->attempts;
+ }
+
+ /**
+ * API function
+ */
+ function reset()
+ {
+ global $db, $user;
+
+ $sql = 'DELETE FROM ' . $this->table_qa_confirm . "
+ WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
+ AND confirm_type = " . (int) $this->type;
+ $db->sql_query($sql);
+
+ // we leave the class usable by generating a new question
+ $this->select_question();
+ }
+
+ /**
+ * API function
+ */
+ function is_solved()
+ {
+ global $request;
+
+ if ($request->variable('qa_answer', false) && $this->solved === 0)
+ {
+ $this->validate();
+ }
+
+ return (bool) $this->solved;
+ }
+
+ /**
+ * API function - The ACP backend, this marks the end of the easy methods
+ */
+ function acp_page($id, &$module)
+ {
+ global $config, $request, $phpbb_log, $template, $user;
+
+ $user->add_lang('acp/board');
+ $user->add_lang('captcha_qa');
+
+ if (!self::is_installed())
+ {
+ $this->install();
+ }
+
+ $module->tpl_name = 'captcha_qa_acp';
+ $module->page_title = 'ACP_VC_SETTINGS';
+ $form_key = 'acp_captcha';
+ add_form_key($form_key);
+
+ $submit = $request->variable('submit', false);
+ $question_id = $request->variable('question_id', 0);
+ $action = $request->variable('action', '');
+
+ // we have two pages, so users might want to navigate from one to the other
+ $list_url = $module->u_action . "&amp;configure=1&amp;select_captcha=" . $this->get_service_name();
+
+ $template->assign_vars(array(
+ 'U_ACTION' => $module->u_action,
+ 'QUESTION_ID' => $question_id ,
+ 'CLASS' => $this->get_service_name(),
+ ));
+
+ // show the list?
+ if (!$question_id && $action != 'add')
+ {
+ $this->acp_question_list($module);
+ }
+ else if ($question_id && $action == 'delete')
+ {
+ if ($this->get_service_name() !== $config['captcha_plugin'] || !$this->acp_is_last($question_id))
+ {
+ if (confirm_box(true))
+ {
+ $this->acp_delete_question($question_id);
+
+ trigger_error($user->lang['QUESTION_DELETED'] . adm_back_link($list_url));
+ }
+ else
+ {
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'question_id' => $question_id,
+ 'action' => $action,
+ 'configure' => 1,
+ 'select_captcha' => $this->get_service_name(),
+ ))
+ );
+ }
+ }
+ else
+ {
+ trigger_error($user->lang['QA_LAST_QUESTION'] . adm_back_link($list_url), E_USER_WARNING);
+ }
+ }
+ else
+ {
+ // okay, show the editor
+ $question_input = $this->acp_get_question_input();
+ $langs = $this->get_languages();
+
+ foreach ($langs as $lang => $entry)
+ {
+ $template->assign_block_vars('langs', array(
+ 'ISO' => $lang,
+ 'NAME' => $entry['name'],
+ ));
+ }
+
+ $template->assign_vars(array(
+ 'U_LIST' => $list_url,
+ ));
+
+ if ($question_id)
+ {
+ if ($question = $this->acp_get_question_data($question_id))
+ {
+ $template->assign_vars(array(
+ 'QUESTION_TEXT' => ($question_input['question_text']) ? $question_input['question_text'] : $question['question_text'],
+ 'LANG_ISO' => ($question_input['lang_iso']) ? $question_input['lang_iso'] : $question['lang_iso'],
+ 'STRICT' => (isset($_REQUEST['strict'])) ? $question_input['strict'] : $question['strict'],
+ 'ANSWERS' => implode("\n", $question['answers']),
+ ));
+ }
+ else
+ {
+ trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url));
+ }
+ }
+ else
+ {
+ $template->assign_vars(array(
+ 'QUESTION_TEXT' => $question_input['question_text'],
+ 'LANG_ISO' => $question_input['lang_iso'],
+ 'STRICT' => $question_input['strict'],
+ 'ANSWERS' => (is_array($question_input['answers'])) ? implode("\n", $question_input['answers']) : '',
+ ));
+ }
+
+ if ($submit && check_form_key($form_key))
+ {
+ if (!$this->validate_input($question_input))
+ {
+ $template->assign_vars(array(
+ 'S_ERROR' => true,
+ ));
+ }
+ else
+ {
+ if ($question_id)
+ {
+ $this->acp_update_question($question_input, $question_id);
+ }
+ else
+ {
+ $this->acp_add_question($question_input);
+ }
+
+ $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
+ trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($list_url));
+ }
+ }
+ else if ($submit)
+ {
+ trigger_error($user->lang['FORM_INVALID'] . adm_back_link($list_url), E_USER_WARNING);
+ }
+ }
+ }
+
+ /**
+ * This handles the list overview
+ */
+ function acp_question_list(&$module)
+ {
+ global $db, $template;
+
+ $sql = 'SELECT *
+ FROM ' . $this->table_captcha_questions;
+ $result = $db->sql_query($sql);
+
+ $template->assign_vars(array(
+ 'S_LIST' => true,
+ ));
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $url = $module->u_action . "&amp;question_id={$row['question_id']}&amp;configure=1&amp;select_captcha=" . $this->get_service_name() . '&amp;';
+
+ $template->assign_block_vars('questions', array(
+ 'QUESTION_TEXT' => $row['question_text'],
+ 'QUESTION_ID' => $row['question_id'],
+ 'QUESTION_LANG' => $row['lang_iso'],
+ 'U_DELETE' => "{$url}action=delete",
+ 'U_EDIT' => "{$url}action=edit",
+ ));
+ }
+ $db->sql_freeresult($result);
+ }
+
+ /**
+ * Grab a question and bring it into a format the editor understands
+ */
+ function acp_get_question_data($question_id)
+ {
+ global $db;
+
+ if ($question_id)
+ {
+ $sql = 'SELECT *
+ FROM ' . $this->table_captcha_questions . '
+ WHERE question_id = ' . $question_id;
+ $result = $db->sql_query($sql);
+ $question = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$question)
+ {
+ return false;
+ }
+
+ $question['answers'] = array();
+
+ $sql = 'SELECT *
+ FROM ' . $this->table_captcha_answers . '
+ WHERE question_id = ' . $question_id;
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $question['answers'][] = $row['answer_text'];
+ }
+ $db->sql_freeresult($result);
+
+ return $question;
+ }
+
+ return false;
+ }
+
+ /**
+ * Grab a question from input and bring it into a format the editor understands
+ */
+ function acp_get_question_input()
+ {
+ global $request;
+
+ $answers = $request->variable('answers', '', true);
+
+ // Convert answers into array and filter if answers are set
+ if (strlen($answers))
+ {
+ $answers = array_filter(array_map('trim', explode("\n", $answers)), function ($value) {
+ return $value !== '';
+ });
+ }
+
+ $question = array(
+ 'question_text' => $request->variable('question_text', '', true),
+ 'strict' => $request->variable('strict', false),
+ 'lang_iso' => $request->variable('lang_iso', ''),
+ 'answers' => $answers,
+ );
+ return $question;
+ }
+
+ /**
+ * Update a question.
+ * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
+ */
+ function acp_update_question($data, $question_id)
+ {
+ global $db, $cache;
+
+ // easier to delete all answers than to figure out which to update
+ $sql = 'DELETE FROM ' . $this->table_captcha_answers . " WHERE question_id = $question_id";
+ $db->sql_query($sql);
+
+ $langs = $this->get_languages();
+ $question_ary = $data;
+ $question_ary['lang_id'] = $langs[$question_ary['lang_iso']]['id'];
+ unset($question_ary['answers']);
+
+ $sql = 'UPDATE ' . $this->table_captcha_questions . '
+ SET ' . $db->sql_build_array('UPDATE', $question_ary) . "
+ WHERE question_id = $question_id";
+ $db->sql_query($sql);
+
+ $this->acp_insert_answers($data, $question_id);
+
+ $cache->destroy('sql', $this->table_captcha_questions);
+ }
+
+ /**
+ * Insert a question.
+ * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
+ */
+ function acp_add_question($data)
+ {
+ global $db, $cache;
+
+ $langs = $this->get_languages();
+ $question_ary = $data;
+
+ $question_ary['lang_id'] = $langs[$data['lang_iso']]['id'];
+ unset($question_ary['answers']);
+
+ $sql = 'INSERT INTO ' . $this->table_captcha_questions . ' ' . $db->sql_build_array('INSERT', $question_ary);
+ $db->sql_query($sql);
+
+ $question_id = $db->sql_nextid();
+
+ $this->acp_insert_answers($data, $question_id);
+
+ $cache->destroy('sql', $this->table_captcha_questions);
+ }
+
+ /**
+ * Insert the answers.
+ * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
+ */
+ function acp_insert_answers($data, $question_id)
+ {
+ global $db, $cache;
+
+ foreach ($data['answers'] as $answer)
+ {
+ $answer_ary = array(
+ 'question_id' => $question_id,
+ 'answer_text' => $answer,
+ );
+
+ $sql = 'INSERT INTO ' . $this->table_captcha_answers . ' ' . $db->sql_build_array('INSERT', $answer_ary);
+ $db->sql_query($sql);
+ }
+
+ $cache->destroy('sql', $this->table_captcha_answers);
+ }
+
+ /**
+ * Delete a question.
+ */
+ function acp_delete_question($question_id)
+ {
+ global $db, $cache;
+
+ $tables = array($this->table_captcha_questions, $this->table_captcha_answers);
+
+ foreach ($tables as $table)
+ {
+ $sql = "DELETE FROM $table
+ WHERE question_id = $question_id";
+ $db->sql_query($sql);
+ }
+
+ $cache->destroy('sql', $tables);
+ }
+
+ /**
+ * Check if the entered data can be inserted/used
+ * param mixed $data : an array as created from acp_get_question_input or acp_get_question_data
+ */
+ function validate_input($question_data)
+ {
+ $langs = $this->get_languages();
+
+ if (!isset($question_data['lang_iso']) ||
+ !isset($question_data['question_text']) ||
+ !isset($question_data['strict']) ||
+ !isset($question_data['answers']))
+ {
+ return false;
+ }
+
+ if (!isset($langs[$question_data['lang_iso']]) ||
+ !strlen($question_data['question_text']) ||
+ !sizeof($question_data['answers']) ||
+ !is_array($question_data['answers']))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * List the installed language packs
+ */
+ function get_languages()
+ {
+ global $db;
+
+ $sql = 'SELECT *
+ FROM ' . LANG_TABLE;
+ $result = $db->sql_query($sql);
+
+ $langs = array();
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $langs[$row['lang_iso']] = array(
+ 'name' => $row['lang_local_name'],
+ 'id' => (int) $row['lang_id'],
+ );
+ }
+ $db->sql_freeresult($result);
+
+ return $langs;
+ }
+
+
+
+ /**
+ * See if there is a question other than the one we have
+ */
+ function acp_is_last($question_id)
+ {
+ global $config, $db;
+
+ if ($question_id)
+ {
+ $sql = 'SELECT question_id
+ FROM ' . $this->table_captcha_questions . "
+ WHERE lang_iso = '" . $db->sql_escape($config['default_lang']) . "'
+ AND question_id <> " . (int) $question_id;
+ $result = $db->sql_query_limit($sql, 1);
+ $question = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if (!$question)
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/phpBB/phpbb/captcha/plugins/recaptcha.php b/phpBB/phpbb/captcha/plugins/recaptcha.php
new file mode 100644
index 0000000000..152709a9ea
--- /dev/null
+++ b/phpBB/phpbb/captcha/plugins/recaptcha.php
@@ -0,0 +1,225 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\captcha\plugins;
+
+class recaptcha extends captcha_abstract
+{
+ var $recaptcha_server = 'http://www.google.com/recaptcha/api';
+ var $recaptcha_server_secure = 'https://www.google.com/recaptcha/api'; // class constants :(
+
+ var $response;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ global $request;
+ $this->recaptcha_server = $request->is_secure() ? $this->recaptcha_server_secure : $this->recaptcha_server;
+ }
+
+ function init($type)
+ {
+ global $user, $request;
+
+ $user->add_lang('captcha_recaptcha');
+ parent::init($type);
+ $this->response = $request->variable('g-recaptcha-response', '');
+ }
+
+ public function is_available()
+ {
+ global $config, $user;
+ $user->add_lang('captcha_recaptcha');
+ return (isset($config['recaptcha_pubkey']) && !empty($config['recaptcha_pubkey']));
+ }
+
+ /**
+ * API function
+ */
+ function has_config()
+ {
+ return true;
+ }
+
+ static public function get_name()
+ {
+ return 'CAPTCHA_RECAPTCHA';
+ }
+
+ /**
+ * This function is implemented because required by the upper class, but is never used for reCaptcha.
+ */
+ function get_generator_class()
+ {
+ throw new \Exception('No generator class given.');
+ }
+
+ function acp_page($id, &$module)
+ {
+ global $config, $template, $user, $phpbb_log, $request;
+
+ $captcha_vars = array(
+ 'recaptcha_pubkey' => 'RECAPTCHA_PUBKEY',
+ 'recaptcha_privkey' => 'RECAPTCHA_PRIVKEY',
+ );
+
+ $module->tpl_name = 'captcha_recaptcha_acp';
+ $module->page_title = 'ACP_VC_SETTINGS';
+ $form_key = 'acp_captcha';
+ add_form_key($form_key);
+
+ $submit = $request->variable('submit', '');
+
+ if ($submit && check_form_key($form_key))
+ {
+ $captcha_vars = array_keys($captcha_vars);
+ foreach ($captcha_vars as $captcha_var)
+ {
+ $value = $request->variable($captcha_var, '');
+ if ($value)
+ {
+ $config->set($captcha_var, $value);
+ }
+ }
+
+ $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_CONFIG_VISUAL');
+ trigger_error($user->lang['CONFIG_UPDATED'] . adm_back_link($module->u_action));
+ }
+ else if ($submit)
+ {
+ trigger_error($user->lang['FORM_INVALID'] . adm_back_link($module->u_action));
+ }
+ else
+ {
+ foreach ($captcha_vars as $captcha_var => $template_var)
+ {
+ $var = (isset($_REQUEST[$captcha_var])) ? $request->variable($captcha_var, '') : ((isset($config[$captcha_var])) ? $config[$captcha_var] : '');
+ $template->assign_var($template_var, $var);
+ }
+
+ $template->assign_vars(array(
+ 'CAPTCHA_PREVIEW' => $this->get_demo_template($id),
+ 'CAPTCHA_NAME' => $this->get_service_name(),
+ 'U_ACTION' => $module->u_action,
+ ));
+
+ }
+ }
+
+ // not needed
+ function execute_demo()
+ {
+ }
+
+ // not needed
+ function execute()
+ {
+ }
+
+ function get_template()
+ {
+ global $config, $user, $template, $phpbb_root_path, $phpEx;
+
+ if ($this->is_solved())
+ {
+ return false;
+ }
+ else
+ {
+ $contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx);
+ $explain = $user->lang(($this->type != CONFIRM_POST) ? 'CONFIRM_EXPLAIN' : 'POST_CONFIRM_EXPLAIN', '<a href="' . $contact_link . '">', '</a>');
+
+ $template->assign_vars(array(
+ 'RECAPTCHA_SERVER' => $this->recaptcha_server,
+ 'RECAPTCHA_PUBKEY' => isset($config['recaptcha_pubkey']) ? $config['recaptcha_pubkey'] : '',
+ 'S_RECAPTCHA_AVAILABLE' => self::is_available(),
+ 'S_CONFIRM_CODE' => true,
+ 'S_TYPE' => $this->type,
+ 'L_CONFIRM_EXPLAIN' => $explain,
+ ));
+
+ return 'captcha_recaptcha.html';
+ }
+ }
+
+ function get_demo_template($id)
+ {
+ return $this->get_template();
+ }
+
+ function get_hidden_fields()
+ {
+ $hidden_fields = array();
+
+ // this is required for posting.php - otherwise we would forget about the captcha being already solved
+ if ($this->solved)
+ {
+ $hidden_fields['confirm_code'] = $this->code;
+ }
+ $hidden_fields['confirm_id'] = $this->confirm_id;
+ return $hidden_fields;
+ }
+
+ function uninstall()
+ {
+ $this->garbage_collect(0);
+ }
+
+ function install()
+ {
+ return;
+ }
+
+ function validate()
+ {
+ if (!parent::validate())
+ {
+ return false;
+ }
+ else
+ {
+ return $this->recaptcha_check_answer();
+ }
+ }
+
+ /**
+ * Calls an HTTP POST function to verify if the user's guess was correct
+ *
+ * @return bool|string Returns false on success or error string on failure.
+ */
+ function recaptcha_check_answer()
+ {
+ global $config, $user;
+
+ //discard spam submissions
+ if ($this->response == null || strlen($this->response) == 0)
+ {
+ return $user->lang['RECAPTCHA_INCORRECT'];
+ }
+
+ $recaptcha = new \ReCaptcha\ReCaptcha($config['recaptcha_privkey']);
+ $result = $recaptcha->verify($this->response, $user->ip);
+
+ if ($result->isSuccess())
+ {
+ $this->solved = true;
+ return false;
+ }
+ else
+ {
+ return $user->lang['RECAPTCHA_INCORRECT'];
+ }
+ }
+}
diff --git a/phpBB/phpbb/class_loader.php b/phpBB/phpbb/class_loader.php
index 37b62fff24..cfdcc2af0b 100644
--- a/phpBB/phpbb/class_loader.php
+++ b/phpBB/phpbb/class_loader.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -19,8 +23,6 @@ namespace phpbb;
*
* If every part of the class name is a directory, the last directory name is
* also used as the filename, e.g. phpbb_dir would resolve to dir/dir.php.
-*
-* @package phpBB3
*/
class class_loader
{
@@ -142,7 +144,13 @@ class class_loader
*/
public function load_class($class)
{
- $class = '\\' . $class;
+ // In general $class is not supposed to contain a leading backslash,
+ // but sometimes it does. See tickets PHP-50731 and HHVM-1840.
+ if ($class[0] !== '\\')
+ {
+ $class = '\\' . $class;
+ }
+
if (substr($class, 0, strlen($this->namespace)) === $this->namespace)
{
$path = $this->resolve_path($class);
diff --git a/phpBB/phpbb/composer.json b/phpBB/phpbb/composer.json
new file mode 100644
index 0000000000..758125234f
--- /dev/null
+++ b/phpBB/phpbb/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "phpbb/phpbb-core",
+ "description": "Collection of core phpBB libraries",
+ "type": "library",
+ "keywords": ["phpbb", "forum"],
+ "homepage": "https://www.phpbb.com",
+ "license": "GPL-2.0",
+ "authors": [
+ {
+ "name": "phpBB Limited",
+ "email": "operations@phpbb.com",
+ "homepage": "https://www.phpbb.com/go/authors"
+ }
+ ],
+ "support": {
+ "issues": "https://tracker.phpbb.com",
+ "forum": "https://www.phpbb.com/community/",
+ "wiki": "https://wiki.phpbb.com",
+ "irc": "irc://irc.freenode.org/phpbb"
+ },
+ "autoload": {
+ "classmap": [""]
+ },
+ "require": {
+ "php": ">=5.4"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2.x-dev"
+ }
+ }
+}
diff --git a/phpBB/phpbb/config/config.php b/phpBB/phpbb/config/config.php
index d37922acf1..aaad333006 100644
--- a/phpBB/phpbb/config/config.php
+++ b/phpBB/phpbb/config/config.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\config;
/**
* Configuration container class
-* @package phpBB3
*/
class config implements \ArrayAccess, \IteratorAggregate, \Countable
{
@@ -34,7 +37,7 @@ class config implements \ArrayAccess, \IteratorAggregate, \Countable
/**
* Retrieves an ArrayIterator over the configuration values.
*
- * @return ArrayIterator An iterator over all config data
+ * @return \ArrayIterator An iterator over all config data
*/
public function getIterator()
{
diff --git a/phpBB/phpbb/config/db.php b/phpBB/phpbb/config/db.php
index c1a3630a14..26489bdd34 100644
--- a/phpBB/phpbb/config/db.php
+++ b/phpBB/phpbb/config/db.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\config;
/**
* Configuration container class
-* @package phpBB3
*/
class db extends \phpbb\config\config
{
@@ -23,7 +26,7 @@ class db extends \phpbb\config\config
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -36,11 +39,11 @@ class db extends \phpbb\config\config
/**
* Creates a configuration container with a default set of values
*
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param \phpbb\cache\driver\driver_interface $cache Cache instance
* @param string $table Configuration table name
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\cache\driver\driver_interface $cache, $table)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache, $table)
{
$this->db = $db;
$this->cache = $cache;
@@ -142,9 +145,9 @@ class db extends \phpbb\config\config
$sql .= " AND config_value = '" . $this->db->sql_escape($old_value) . "'";
}
- $result = $this->db->sql_query($sql);
+ $this->db->sql_query($sql);
- if (!$this->db->sql_affectedrows($result) && isset($this->config[$key]))
+ if (!$this->db->sql_affectedrows() && isset($this->config[$key]))
{
return false;
}
diff --git a/phpBB/phpbb/config/db_text.php b/phpBB/phpbb/config/db_text.php
index b1ea112b53..818f6bdcc9 100644
--- a/phpBB/phpbb/config/db_text.php
+++ b/phpBB/phpbb/config/db_text.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,14 +17,12 @@ namespace phpbb\config;
* Manages configuration options with an arbitrary length value stored in a TEXT
* column. In constrast to class \phpbb\config\db, values are never cached and
* prefetched, but every get operation sends a query to the database.
-*
-* @package phpBB3
*/
class db_text
{
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -31,10 +33,10 @@ class db_text
protected $table;
/**
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param string $table Table name
*/
- public function __construct(\phpbb\db\driver\driver $db, $table)
+ public function __construct(\phpbb\db\driver\driver_interface $db, $table)
{
$this->db = $db;
$this->table = $this->db->sql_escape($table);
@@ -98,13 +100,13 @@ class db_text
$sql = 'UPDATE ' . $this->table . "
SET config_value = '" . $this->db->sql_escape($value) . "'
WHERE config_name = '" . $this->db->sql_escape($key) . "'";
- $result = $this->db->sql_query($sql);
+ $this->db->sql_query($sql);
- if (!$this->db->sql_affectedrows($result))
+ if (!$this->db->sql_affectedrows())
{
$sql = 'INSERT INTO ' . $this->table . ' ' . $this->db->sql_build_array('INSERT', array(
- 'config_name' => $key,
- 'config_value' => $value,
+ 'config_name' => (string) $key,
+ 'config_value' => (string) $value,
));
$this->db->sql_query($sql);
}
@@ -152,6 +154,6 @@ class db_text
$sql = 'DELETE
FROM ' . $this->table . '
WHERE ' . $this->db->sql_in_set('config_name', $keys, false, true);
- $result = $this->db->sql_query($sql);
+ $this->db->sql_query($sql);
}
}
diff --git a/phpBB/phpbb/config_php_file.php b/phpBB/phpbb/config_php_file.php
new file mode 100644
index 0000000000..7445e7df22
--- /dev/null
+++ b/phpBB/phpbb/config_php_file.php
@@ -0,0 +1,160 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb;
+
+class config_php_file
+{
+ /** @var string phpBB Root Path */
+ protected $phpbb_root_path;
+
+ /** @var string php file extension */
+ protected $php_ext;
+
+ /**
+ * Indicates whether the php config file has been loaded.
+ *
+ * @var bool
+ */
+ protected $config_loaded = false;
+
+ /**
+ * The content of the php config file
+ *
+ * @var array
+ */
+ protected $config_data = array();
+
+ /**
+ * The path to the config file. (Default: $phpbb_root_path . 'config.' . $php_ext)
+ *
+ * @var string
+ */
+ protected $config_file;
+
+ private $defined_vars;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path phpBB Root Path
+ * @param string $php_ext php file extension
+ */
+ function __construct($phpbb_root_path, $php_ext)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->config_file = $this->phpbb_root_path . 'config.' . $this->php_ext;
+ }
+
+ /**
+ * Set the path to the config file.
+ *
+ * @param string $config_file
+ */
+ public function set_config_file($config_file)
+ {
+ $this->config_file = $config_file;
+ $this->config_loaded = false;
+ }
+
+ /**
+ * Returns an associative array containing the variables defined by the config file.
+ *
+ * @return array Return the content of the config file or an empty array if the file does not exists.
+ */
+ public function get_all()
+ {
+ $this->load_config_file();
+
+ return $this->config_data;
+ }
+
+ /**
+ * Return the value of a variable defined into the config.php file or null if the variable does not exist.
+ *
+ * @param string $variable The name of the variable
+ * @return mixed Value of the variable or null if the variable is not defined.
+ */
+ public function get($variable)
+ {
+ $this->load_config_file();
+
+ return isset($this->config_data[$variable]) ? $this->config_data[$variable] : null;
+ }
+
+ /**
+ * Load the config file and store the information.
+ *
+ * @return null
+ */
+ protected function load_config_file()
+ {
+ if (!$this->config_loaded && file_exists($this->config_file))
+ {
+ $this->defined_vars = get_defined_vars();
+
+ require($this->config_file);
+ $this->config_data = array_diff_key(get_defined_vars(), $this->defined_vars);
+
+ $this->config_loaded = true;
+ }
+ }
+
+ /**
+ * Convert either 3.0 dbms or 3.1 db driver class name to 3.1 db driver class name.
+ *
+ * If $dbms is a valid 3.1 db driver class name, returns it unchanged.
+ * Otherwise prepends phpbb\db\driver\ to the dbms to convert a 3.0 dbms
+ * to 3.1 db driver class name.
+ *
+ * @param string $dbms dbms parameter
+ * @return string driver class
+ * @throws \RuntimeException
+ */
+ public function convert_30_dbms_to_31($dbms)
+ {
+ // Note: this check is done first because mysqli extension
+ // supplies a mysqli class, and class_exists($dbms) would return
+ // true for mysqli class.
+ // However, per the docblock any valid 3.1 driver name should be
+ // recognized by this function, and have priority over 3.0 dbms.
+ if (strpos($dbms, 'phpbb\db\driver') === false && class_exists('phpbb\db\driver\\' . $dbms))
+ {
+ return 'phpbb\db\driver\\' . $dbms;
+ }
+
+ if (class_exists($dbms))
+ {
+ // Additionally we could check that $dbms extends phpbb\db\driver\driver.
+ // http://php.net/manual/en/class.reflectionclass.php
+ // Beware of possible performance issues:
+ // http://stackoverflow.com/questions/294582/php-5-reflection-api-performance
+ // We could check for interface implementation in all paths or
+ // only when we do not prepend phpbb\db\driver\.
+
+ /*
+ $reflection = new \ReflectionClass($dbms);
+
+ if ($reflection->isSubclassOf('phpbb\db\driver\driver'))
+ {
+ return $dbms;
+ }
+ */
+
+ return $dbms;
+ }
+
+ throw new \RuntimeException("You have specified an invalid dbms driver: $dbms");
+ }
+}
diff --git a/phpBB/phpbb/console/application.php b/phpBB/phpbb/console/application.php
index fdcd9d42f6..dc9b8016b2 100644
--- a/phpBB/phpbb/console/application.php
+++ b/phpBB/phpbb/console/application.php
@@ -1,23 +1,153 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console;
-use Symfony\Component\DependencyInjection\TaggedContainerInterface;
+use Symfony\Component\Console\Input\InputDefinition;
+use Symfony\Component\Console\Shell;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
class application extends \Symfony\Component\Console\Application
{
- function register_container_commands(TaggedContainerInterface $container, $tag = 'console.command')
+ /**
+ * @var bool Indicates whether or not we are in a shell
+ */
+ protected $in_shell = false;
+
+ /**
+ * @var \phpbb\language\language User object
+ */
+ protected $language;
+
+ /**
+ * @param string $name The name of the application
+ * @param string $version The version of the application
+ * @param \phpbb\language\language $language The user which runs the application (used for translation)
+ */
+ public function __construct($name, $version, \phpbb\language\language $language)
+ {
+ $this->language = $language;
+
+ parent::__construct($name, $version);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getDefaultInputDefinition()
+ {
+ $input_definition = parent::getDefaultInputDefinition();
+
+ $this->register_global_options($input_definition);
+
+ return $input_definition;
+ }
+
+ /**
+ * Gets the help message.
+ *
+ * It's a hack of the default help message to display the --shell
+ * option only for the application and not for all the commands.
+ *
+ * @return string A help message.
+ */
+ public function getHelp()
{
- foreach($container->findTaggedServiceIds($tag) as $id => $void)
+ // If we are already in a shell
+ // we do not want to have the --shell option available
+ if ($this->in_shell)
+ {
+ return parent::getHelp();
+ }
+
+ try
+ {
+ $definition = $this->getDefinition();
+ $definition->addOption(new InputOption(
+ '--shell',
+ '-s',
+ InputOption::VALUE_NONE,
+ $this->language->lang('CLI_DESCRIPTION_OPTION_SHELL')
+ ));
+ }
+ catch (\LogicException $e)
+ {
+ // Do nothing
+ }
+
+ return parent::getHelp();
+ }
+
+ /**
+ * Register a set of commands from the container
+ *
+ * @param \phpbb\di\service_collection $command_collection The console service collection
+ */
+ public function register_container_commands(\phpbb\di\service_collection $command_collection)
+ {
+ foreach ($command_collection as $service_command)
+ {
+ $this->add($service_command);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function doRun(InputInterface $input, OutputInterface $output)
+ {
+ // Run a shell if the --shell (or -s) option is set and if no command name is specified
+ // Also, we do not want to have the --shell option available if we are already in a shell
+ if (!$this->in_shell && $this->getCommandName($input) === null && $input->hasParameterOption(array('--shell', '-s')))
+ {
+ $shell = new Shell($this);
+ $this->in_shell = true;
+ $shell->run();
+
+ return 0;
+ }
+
+ return parent::doRun($input, $output);
+ }
+
+ /**
+ * Register global options
+ *
+ * @param InputDefinition $definition An InputDefinition instance
+ */
+ protected function register_global_options(InputDefinition $definition)
+ {
+ try
+ {
+ $definition->addOption(new InputOption(
+ 'safe-mode',
+ null,
+ InputOption::VALUE_NONE,
+ $this->language->lang('CLI_DESCRIPTION_OPTION_SAFE_MODE')
+ ));
+
+ $definition->addOption(new InputOption(
+ 'env',
+ 'e',
+ InputOption::VALUE_REQUIRED,
+ $this->language->lang('CLI_DESCRIPTION_OPTION_ENV')
+ ));
+ }
+ catch (\LogicException $e)
{
- $this->add($container->get($id));
+ // Do nothing
}
}
}
diff --git a/phpBB/phpbb/console/command/cache/purge.php b/phpBB/phpbb/console/command/cache/purge.php
new file mode 100644
index 0000000000..d0c2ef6f72
--- /dev/null
+++ b/phpBB/phpbb/console/command/cache/purge.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\console\command\cache;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class purge extends \phpbb\console\command\command
+{
+ /** @var \phpbb\cache\driver\driver_interface */
+ protected $cache;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\auth\auth */
+ protected $auth;
+
+ /** @var \phpbb\log\log_interface */
+ protected $log;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user User instance
+ * @param \phpbb\cache\driver\driver_interface $cache Cache instance
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param \phpbb\auth\auth $auth Auth instance
+ * @param \phpbb\log\log $log Logger instance
+ * @param \phpbb\config\config $config Config instance
+ */
+ public function __construct(\phpbb\user $user, \phpbb\cache\driver\driver_interface $cache, \phpbb\db\driver\driver_interface $db, \phpbb\auth\auth $auth, \phpbb\log\log_interface $log, \phpbb\config\config $config)
+ {
+ $this->cache = $cache;
+ $this->db = $db;
+ $this->auth = $auth;
+ $this->log = $log;
+ $this->config = $config;
+ parent::__construct($user);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('cache:purge')
+ ->setDescription($this->user->lang('PURGE_CACHE'))
+ ;
+ }
+
+ /**
+ * Executes the command cache:purge.
+ *
+ * Purge the cache (including permissions) and increment the asset_version number
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->config->increment('assets_version', 1);
+ $this->cache->purge();
+
+ // Clear permissions
+ $this->auth->acl_clear_prefetch();
+ phpbb_cache_moderators($this->db, $this->cache, $this->auth);
+
+ $this->log->add('admin', ANONYMOUS, '', 'LOG_PURGE_CACHE', time(), array());
+
+ $output->writeln($this->user->lang('PURGE_CACHE_SUCCESS'));
+ }
+}
diff --git a/phpBB/phpbb/console/command/command.php b/phpBB/phpbb/console/command/command.php
index 6abbdd203c..638c989da2 100644
--- a/phpBB/phpbb/console/command/command.php
+++ b/phpBB/phpbb/console/command/command.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,4 +15,17 @@ namespace phpbb\console\command;
abstract class command extends \Symfony\Component\Console\Command\Command
{
+ /** @var \phpbb\user */
+ protected $user;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user User instance (mostly for translation)
+ */
+ public function __construct(\phpbb\user $user)
+ {
+ $this->user = $user;
+ parent::__construct();
+ }
}
diff --git a/phpBB/phpbb/console/command/config/command.php b/phpBB/phpbb/console/command/config/command.php
index b105bc826d..f0ad5d4d19 100644
--- a/phpBB/phpbb/console/command/config/command.php
+++ b/phpBB/phpbb/console/command/config/command.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
@@ -13,10 +17,10 @@ abstract class command extends \phpbb\console\command\command
/** @var \phpbb\config\config */
protected $config;
- function __construct(\phpbb\config\config $config)
+ function __construct(\phpbb\user $user, \phpbb\config\config $config)
{
$this->config = $config;
- parent::__construct();
+ parent::__construct($user);
}
}
diff --git a/phpBB/phpbb/console/command/config/delete.php b/phpBB/phpbb/console/command/config/delete.php
index 9a2d00561d..efd276d7e3 100644
--- a/phpBB/phpbb/console/command/config/delete.php
+++ b/phpBB/phpbb/console/command/config/delete.php
@@ -1,33 +1,50 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class delete extends command
{
+ /**
+ * {@inheritdoc}
+ */
protected function configure()
{
$this
->setName('config:delete')
- ->setDescription('Deletes a configuration option')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_DELETE_CONFIG'))
->addArgument(
'key',
InputArgument::REQUIRED,
- "The configuration option's name"
+ $this->user->lang('CLI_CONFIG_OPTION_NAME')
)
;
}
+ /**
+ * Executes the command config:delete.
+ *
+ * Removes a configuration option
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ * @see \phpbb\config\config::delete()
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$key = $input->getArgument('key');
@@ -36,11 +53,11 @@ class delete extends command
{
$this->config->delete($key);
- $output->writeln("<info>Successfully deleted config $key</info>");
+ $output->writeln('<info>' . $this->user->lang('CLI_CONFIG_DELETE_SUCCESS', $key) . '</info>');
}
else
{
- $output->writeln("<error>Config $key does not exist</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_CONFIG_NOT_EXISTS', $key) . '</error>');
}
}
}
diff --git a/phpBB/phpbb/console/command/config/get.php b/phpBB/phpbb/console/command/config/get.php
index 275c82b53f..9c03b49a3d 100644
--- a/phpBB/phpbb/console/command/config/get.php
+++ b/phpBB/phpbb/console/command/config/get.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
@@ -15,25 +19,39 @@ use Symfony\Component\Console\Output\OutputInterface;
class get extends command
{
+ /**
+ * {@inheritdoc}
+ */
protected function configure()
{
$this
->setName('config:get')
- ->setDescription("Gets a configuration option's value")
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_GET_CONFIG'))
->addArgument(
'key',
InputArgument::REQUIRED,
- "The configuration option's name"
+ $this->user->lang('CLI_CONFIG_OPTION_NAME')
)
->addOption(
'no-newline',
null,
InputOption::VALUE_NONE,
- 'Set this option if the value should be printed without a new line at the end.'
+ $this->user->lang('CLI_CONFIG_PRINT_WITHOUT_NEWLINE')
)
;
}
+ /**
+ * Executes the command config:get.
+ *
+ * Retrieves a configuration value.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ * @see \phpbb\config\config::offsetGet()
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$key = $input->getArgument('key');
@@ -42,13 +60,13 @@ class get extends command
{
$output->write($this->config[$key]);
}
- elseif (isset($this->config[$key]))
+ else if (isset($this->config[$key]))
{
$output->writeln($this->config[$key]);
}
else
{
- $output->writeln("<error>Could not get config $key</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_CONFIG_NOT_EXISTS', $key) . '</error>');
}
}
}
diff --git a/phpBB/phpbb/console/command/config/increment.php b/phpBB/phpbb/console/command/config/increment.php
index bc6b63c6ff..b4d7438b66 100644
--- a/phpBB/phpbb/console/command/config/increment.php
+++ b/phpBB/phpbb/console/command/config/increment.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
@@ -15,30 +19,44 @@ use Symfony\Component\Console\Output\OutputInterface;
class increment extends command
{
+ /**
+ * {@inheritdoc}
+ */
protected function configure()
{
$this
->setName('config:increment')
- ->setDescription("Increments a configuration option's value")
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_INCREMENT_CONFIG'))
->addArgument(
'key',
InputArgument::REQUIRED,
- "The configuration option's name"
+ $this->user->lang('CLI_CONFIG_OPTION_NAME')
)
->addArgument(
'increment',
InputArgument::REQUIRED,
- 'Amount to increment by'
+ $this->user->lang('CLI_CONFIG_INCREMENT_BY')
)
->addOption(
'dynamic',
'd',
InputOption::VALUE_NONE,
- 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ $this->user->lang('CLI_CONFIG_CANNOT_CACHED')
)
;
}
+ /**
+ * Executes the command config:increment.
+ *
+ * Increments an integer configuration value.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ * @see \phpbb\config\config::increment()
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$key = $input->getArgument('key');
@@ -47,6 +65,6 @@ class increment extends command
$this->config->increment($key, $increment, $use_cache);
- $output->writeln("<info>Successfully incremented config $key</info>");
+ $output->writeln('<info>' . $this->user->lang('CLI_CONFIG_INCREMENT_SUCCESS', $key) . '</info>');
}
}
diff --git a/phpBB/phpbb/console/command/config/set.php b/phpBB/phpbb/console/command/config/set.php
index 9d471a96ad..695de31013 100644
--- a/phpBB/phpbb/console/command/config/set.php
+++ b/phpBB/phpbb/console/command/config/set.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
@@ -15,30 +19,44 @@ use Symfony\Component\Console\Output\OutputInterface;
class set extends command
{
+ /**
+ * {@inheritdoc}
+ */
protected function configure()
{
$this
->setName('config:set')
- ->setDescription("Sets a configuration option's value")
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_SET_CONFIG'))
->addArgument(
'key',
InputArgument::REQUIRED,
- "The configuration option's name"
+ $this->user->lang('CLI_CONFIG_OPTION_NAME')
)
->addArgument(
'value',
InputArgument::REQUIRED,
- 'New configuration value, use 0 and 1 to specify boolean values'
+ $this->user->lang('CLI_CONFIG_NEW')
)
->addOption(
'dynamic',
'd',
InputOption::VALUE_NONE,
- 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ $this->user->lang('CLI_CONFIG_CANNOT_CACHED')
)
;
}
+ /**
+ * Executes the command config:set.
+ *
+ * Sets a configuration option's value.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ * @see \phpbb\config\config::set()
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$key = $input->getArgument('key');
@@ -47,6 +65,6 @@ class set extends command
$this->config->set($key, $value, $use_cache);
- $output->writeln("<info>Successfully set config $key</info>");
+ $output->writeln('<info>' . $this->user->lang('CLI_CONFIG_SET_SUCCESS', $key) . '</info>');
}
}
diff --git a/phpBB/phpbb/console/command/config/set_atomic.php b/phpBB/phpbb/console/command/config/set_atomic.php
index 03e7a60210..e8c69a0885 100644
--- a/phpBB/phpbb/console/command/config/set_atomic.php
+++ b/phpBB/phpbb/console/command/config/set_atomic.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\config;
@@ -15,35 +19,50 @@ use Symfony\Component\Console\Output\OutputInterface;
class set_atomic extends command
{
+ /**
+ * {@inheritdoc}
+ */
protected function configure()
{
$this
->setName('config:set-atomic')
- ->setDescription("Sets a configuration option's value only if the old matches the current value.")
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_SET_ATOMIC_CONFIG'))
->addArgument(
'key',
InputArgument::REQUIRED,
- "The configuration option's name"
+ $this->user->lang('CLI_CONFIG_OPTION_NAME')
)
->addArgument(
'old',
InputArgument::REQUIRED,
- 'Current configuration value, use 0 and 1 to specify boolean values'
+ $this->user->lang('CLI_CONFIG_CURRENT')
)
->addArgument(
'new',
InputArgument::REQUIRED,
- 'New configuration value, use 0 and 1 to specify boolean values'
+ $this->user->lang('CLI_CONFIG_NEW')
)
->addOption(
'dynamic',
'd',
InputOption::VALUE_NONE,
- 'Set this option if the configuration option changes too frequently to be efficiently cached.'
+ $this->user->lang('CLI_CONFIG_CANNOT_CACHED')
)
;
}
+ /**
+ * Executes the command config:set-atomic.
+ *
+ * Sets a configuration option's value only if the old_value matches the
+ * current configuration value or the configuration value does not exist yet.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return bool True if the value was changed, false otherwise.
+ * @see \phpbb\config\config::set_atomic()
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$key = $input->getArgument('key');
@@ -53,12 +72,12 @@ class set_atomic extends command
if ($this->config->set_atomic($key, $old_value, $new_value, $use_cache))
{
- $output->writeln("<info>Successfully set config $key</info>");
+ $output->writeln('<info>' . $this->user->lang('CLI_CONFIG_SET_SUCCESS', $key) . '</info>');
return 0;
}
else
{
- $output->writeln("<error>Could not set config $key</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_CONFIG_SET_FAILURE', $key) . '</error>');
return 1;
}
}
diff --git a/phpBB/phpbb/console/command/cron/cron_list.php b/phpBB/phpbb/console/command/cron/cron_list.php
new file mode 100644
index 0000000000..c515fd9e80
--- /dev/null
+++ b/phpBB/phpbb/console/command/cron/cron_list.php
@@ -0,0 +1,111 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+namespace phpbb\console\command\cron;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class cron_list extends \phpbb\console\command\command
+{
+ /** @var \phpbb\cron\manager */
+ protected $cron_manager;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user User instance
+ * @param \phpbb\cron\manager $cron_manager Cron manager
+ */
+ public function __construct(\phpbb\user $user, \phpbb\cron\manager $cron_manager)
+ {
+ $this->cron_manager = $cron_manager;
+ parent::__construct($user);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('cron:list')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_CRON_LIST'))
+ ;
+ }
+
+ /**
+ * Executes the command cron:list.
+ *
+ * Prints a list of ready and unready cron jobs.
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $tasks = $this->cron_manager->get_tasks();
+
+ if (empty($tasks))
+ {
+ $output->writeln($this->user->lang('CRON_NO_TASKS'));
+ return;
+ }
+
+ $ready_tasks = array();
+ $not_ready_tasks = array();
+ foreach ($tasks as $task)
+ {
+ if ($task->is_ready())
+ {
+ $ready_tasks[] = $task;
+ }
+ else
+ {
+ $not_ready_tasks[] = $task;
+ }
+ }
+
+ if (!empty($ready_tasks))
+ {
+ $output->writeln('<info>' . $this->user->lang('TASKS_READY') . '</info>');
+ $this->print_tasks_names($ready_tasks, $output);
+ }
+
+ if (!empty($ready_tasks) && !empty($not_ready_tasks))
+ {
+ $output->writeln('');
+ }
+
+ if (!empty($not_ready_tasks))
+ {
+ $output->writeln('<info>' . $this->user->lang('TASKS_NOT_READY') . '</info>');
+ $this->print_tasks_names($not_ready_tasks, $output);
+ }
+ }
+
+ /**
+ * Print a list of cron jobs
+ *
+ * @param array $tasks A list of task to display
+ * @param OutputInterface $output An OutputInterface instance
+ */
+ protected function print_tasks_names(array $tasks, OutputInterface $output)
+ {
+ foreach ($tasks as $task)
+ {
+ $output->writeln($task->get_name());
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/cron/run.php b/phpBB/phpbb/console/command/cron/run.php
new file mode 100644
index 0000000000..dea6493007
--- /dev/null
+++ b/phpBB/phpbb/console/command/cron/run.php
@@ -0,0 +1,171 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\console\command\cron;
+
+use phpbb\exception\runtime_exception;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class run extends \phpbb\console\command\command
+{
+ /** @var \phpbb\cron\manager */
+ protected $cron_manager;
+
+ /** @var \phpbb\lock\db */
+ protected $lock_db;
+
+ /**
+ * Construct method
+ *
+ * @param \phpbb\user $user The user object (used to get language information)
+ * @param \phpbb\cron\manager $cron_manager The cron manager containing
+ * the cron tasks to be executed.
+ * @param \phpbb\lock\db $lock_db The lock for accessing database.
+ */
+ public function __construct(\phpbb\user $user, \phpbb\cron\manager $cron_manager, \phpbb\lock\db $lock_db)
+ {
+ $this->cron_manager = $cron_manager;
+ $this->lock_db = $lock_db;
+ parent::__construct($user);
+ }
+
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('cron:run')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_CRON_RUN'))
+ ->setHelp($this->user->lang('CLI_HELP_CRON_RUN'))
+ ->addArgument('name', InputArgument::OPTIONAL, $this->user->lang('CLI_DESCRIPTION_CRON_RUN_ARGUMENT_1'))
+ ;
+ }
+
+ /**
+ * Executes the command cron:run.
+ *
+ * Tries to acquire the cron lock, then if no argument has been given runs all ready cron tasks.
+ * If the cron lock can not be obtained, an error message is printed
+ * and the exit status is set to 1.
+ * If the verbose option is specified, each start of a task is printed.
+ * Otherwise there is no output.
+ * If an argument is given to the command, only the task whose name matches the
+ * argument will be started. If verbose option is specified,
+ * an info message containing the name of the task is printed.
+ * If no task matches the argument given, an error message is printed
+ * and the exit status is set to 2.
+ *
+ * @param InputInterface $input The input stream used to get the argument and verboe option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ *
+ * @return int 0 if all is ok, 1 if a lock error occured and 2 if no task matching the argument was found.
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ if ($this->lock_db->acquire())
+ {
+ $task_name = $input->getArgument('name');
+ if ($task_name)
+ {
+ $exit_status = $this->run_one($input, $output, $task_name);
+ }
+ else
+ {
+ $exit_status = $this->run_all($input, $output);
+ }
+
+ $this->lock_db->release();
+ return $exit_status;
+ }
+ else
+ {
+ throw new runtime_exception('CRON_LOCK_ERROR', array(), null, 1);
+ }
+ }
+
+ /**
+ * Executes all ready cron tasks.
+ *
+ * If verbose mode is set, an info message will be printed if there is no task to
+ * be run, or else for each starting task.
+ *
+ * @see execute
+ * @param InputInterface $input The input stream used to get the argument and verbose option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ * @return int 0
+ */
+ protected function run_all(InputInterface $input, OutputInterface $output)
+ {
+ $run_tasks = $this->cron_manager->find_all_ready_tasks();
+
+ if ($run_tasks)
+ {
+ foreach ($run_tasks as $task)
+ {
+ if ($input->getOption('verbose'))
+ {
+ $output->writeln('<info>' . $this->user->lang('RUNNING_TASK', $task->get_name()) . '</info>');
+ }
+
+ $task->run();
+ }
+ }
+ else
+ {
+ if ($input->getOption('verbose'))
+ {
+ $output->writeln('<info>' . $this->user->lang('CRON_NO_TASK') . '</info>');
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Executes a given cron task, if it is ready.
+ *
+ * If there is a task whose name matches $task_name, it is run and 0 is returned.
+ * and if verbose mode is set, print an info message with the name of the task.
+ * If there is no task matching $task_name, the function prints an error message
+ * and returns with status 2.
+ *
+ * @see execute
+ * @param string $task_name The name of the task that should be run.
+ * @param InputInterface $input The input stream used to get the argument and verbose option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ * @return int 0 if all is well, 2 if no task matches $task_name.
+ */
+ protected function run_one(InputInterface $input, OutputInterface $output, $task_name)
+ {
+ $task = $this->cron_manager->find_task($task_name);
+ if ($task)
+ {
+ if ($input->getOption('verbose'))
+ {
+ $output->writeln('<info>' . $this->user->lang('RUNNING_TASK', $task_name) . '</info>');
+ }
+
+ $task->run();
+ return 0;
+ }
+ else
+ {
+ throw new runtime_exception('CRON_NO_SUCH_TASK', array( $task_name), null, 2);
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/db/console_migrator_output_handler.php b/phpBB/phpbb/console/command/db/console_migrator_output_handler.php
new file mode 100644
index 0000000000..568b2646d4
--- /dev/null
+++ b/phpBB/phpbb/console/command/db/console_migrator_output_handler.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\console\command\db;
+
+use phpbb\db\output_handler\migrator_output_handler_interface;
+use phpbb\user;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class console_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * User object.
+ *
+ * @var user
+ */
+ private $user;
+
+ /**
+ * Console output object.
+ *
+ * @var OutputInterface
+ */
+ private $output;
+
+ /**
+ * Constructor
+ *
+ * @param user $user User object
+ * @param OutputInterface $output Console output object
+ */
+ public function __construct(user $user, OutputInterface $output)
+ {
+ $this->user = $user;
+ $this->output = $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ if ($verbosity <= $this->output->getVerbosity())
+ {
+ $translated_message = call_user_func_array(array($this->user, 'lang'), $message);
+
+ if ($verbosity === migrator_output_handler_interface::VERBOSITY_NORMAL)
+ {
+ $translated_message = '<info>' . $translated_message . '</info>';
+ }
+ else if ($verbosity === migrator_output_handler_interface::VERBOSITY_VERBOSE)
+ {
+ $translated_message = '<comment>' . $translated_message . '</comment>';
+ }
+
+ $this->output->writeln($translated_message);
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/db/list_command.php b/phpBB/phpbb/console/command/db/list_command.php
new file mode 100644
index 0000000000..708107b592
--- /dev/null
+++ b/phpBB/phpbb/console/command/db/list_command.php
@@ -0,0 +1,73 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+namespace phpbb\console\command\db;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class list_command extends \phpbb\console\command\db\migration_command
+{
+ protected function configure()
+ {
+ $this
+ ->setName('db:list')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_DB_LIST'))
+ ->addOption(
+ 'available',
+ 'u',
+ InputOption::VALUE_NONE,
+ $this->user->lang('CLI_MIGRATIONS_ONLY_AVAILABLE')
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $show_installed = !$input->getOption('available');
+ $installed = $available = array();
+
+ foreach ($this->load_migrations() as $name)
+ {
+ if ($this->migrator->migration_state($name) !== false)
+ {
+ $installed[] = $name;
+ }
+ else
+ {
+ $available[] = $name;
+ }
+ }
+
+ if ($show_installed)
+ {
+ $output->writeln('<info>' . $this->user->lang('CLI_MIGRATIONS_INSTALLED') . $this->user->lang('COLON') . '</info>');
+ $output->writeln($installed);
+
+ if (empty($installed))
+ {
+ $output->writeln($this->user->lang('CLI_MIGRATIONS_EMPTY'));
+ }
+
+ $output->writeln('');
+ }
+
+ $output->writeln('<info>' . $this->user->lang('CLI_MIGRATIONS_AVAILABLE') . $this->user->lang('COLON') . '</info>');
+ $output->writeln($available);
+
+ if (empty($available))
+ {
+ $output->writeln($this->user->lang('CLI_MIGRATIONS_EMPTY'));
+ }
+ }
+}
diff --git a/phpBB/phpbb/console/command/db/migrate.php b/phpBB/phpbb/console/command/db/migrate.php
new file mode 100644
index 0000000000..ae4211f7be
--- /dev/null
+++ b/phpBB/phpbb/console/command/db/migrate.php
@@ -0,0 +1,83 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+namespace phpbb\console\command\db;
+
+use phpbb\db\output_handler\log_wrapper_migrator_output_handler;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class migrate extends \phpbb\console\command\db\migration_command
+{
+ /** @var \phpbb\log\log */
+ protected $log;
+
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /** @var \phpbb\filesystem\filesystem_interface */
+ protected $filesystem;
+
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ function __construct(\phpbb\user $user, \phpbb\language\language $language, \phpbb\db\migrator $migrator, \phpbb\extension\manager $extension_manager, \phpbb\config\config $config, \phpbb\cache\service $cache, \phpbb\log\log $log, \phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path)
+ {
+ $this->language = $language;
+ $this->log = $log;
+ $this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
+ parent::__construct($user, $migrator, $extension_manager, $config, $cache);
+ $this->user->add_lang(array('common', 'install', 'migrator'));
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setName('db:migrate')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_DB_MIGRATE'))
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->migrator->set_output_handler(new log_wrapper_migrator_output_handler($this->language, new console_migrator_output_handler($this->user, $output), $this->phpbb_root_path . 'store/migrations_' . time() . '.log', $this->filesystem));
+
+ $this->migrator->create_migrations_table();
+
+ $this->cache->purge();
+
+ $this->load_migrations();
+ $orig_version = $this->config['version'];
+ while (!$this->migrator->finished())
+ {
+ try
+ {
+ $this->migrator->update();
+ }
+ catch (\phpbb\db\migration\exception $e)
+ {
+ $output->writeln('<error>' . $e->getLocalisedMessage($this->user) . '</error>');
+ $this->finalise_update();
+ return 1;
+ }
+ }
+
+ if ($orig_version != $this->config['version'])
+ {
+ $this->log->add('admin', ANONYMOUS, '', 'LOG_UPDATE_DATABASE', time(), array($orig_version, $this->config['version']));
+ }
+
+ $this->finalise_update();
+ $output->writeln($this->user->lang['DATABASE_UPDATE_COMPLETE']);
+ }
+}
diff --git a/phpBB/phpbb/console/command/db/migration_command.php b/phpBB/phpbb/console/command/db/migration_command.php
new file mode 100644
index 0000000000..d44ef8c5cb
--- /dev/null
+++ b/phpBB/phpbb/console/command/db/migration_command.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\console\command\db;
+
+abstract class migration_command extends \phpbb\console\command\command
+{
+ /** @var \phpbb\db\migrator */
+ protected $migrator;
+
+ /** @var \phpbb\extension\manager */
+ protected $extension_manager;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var \phpbb\cache\service */
+ protected $cache;
+
+ function __construct(\phpbb\user $user, \phpbb\db\migrator $migrator, \phpbb\extension\manager $extension_manager, \phpbb\config\config $config, \phpbb\cache\service $cache)
+ {
+ $this->migrator = $migrator;
+ $this->extension_manager = $extension_manager;
+ $this->config = $config;
+ $this->cache = $cache;
+ parent::__construct($user);
+ }
+
+ protected function load_migrations()
+ {
+ $migrations = $this->extension_manager
+ ->get_finder()
+ ->core_path('phpbb/db/migration/data/')
+ ->extension_directory('/migrations')
+ ->get_classes();
+
+ $this->migrator->set_migrations($migrations);
+
+ return $migrations;
+ }
+
+ protected function finalise_update()
+ {
+ $this->cache->purge();
+ $this->config->increment('assets_version', 1);
+ }
+}
diff --git a/phpBB/phpbb/console/command/db/revert.php b/phpBB/phpbb/console/command/db/revert.php
new file mode 100644
index 0000000000..3fa2e17515
--- /dev/null
+++ b/phpBB/phpbb/console/command/db/revert.php
@@ -0,0 +1,88 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+namespace phpbb\console\command\db;
+
+use phpbb\db\output_handler\log_wrapper_migrator_output_handler;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class revert extends \phpbb\console\command\db\migration_command
+{
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /** @var \phpbb\filesystem\filesystem_interface */
+ protected $filesystem;
+
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ function __construct(\phpbb\user $user, \phpbb\language\language $language, \phpbb\db\migrator $migrator, \phpbb\extension\manager $extension_manager, \phpbb\config\config $config, \phpbb\cache\service $cache, \phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->language = $language;
+ $this->phpbb_root_path = $phpbb_root_path;
+ parent::__construct($user, $migrator, $extension_manager, $config, $cache);
+ $this->user->add_lang(array('common', 'migrator'));
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setName('db:revert')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_DB_REVERT'))
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ $this->user->lang('CLI_MIGRATION_NAME')
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $name = str_replace('/', '\\', $input->getArgument('name'));
+
+ $this->migrator->set_output_handler(new log_wrapper_migrator_output_handler($this->language, new console_migrator_output_handler($this->user, $output), $this->phpbb_root_path . 'store/migrations_' . time() . '.log', $this->filesystem));
+
+ $this->cache->purge();
+
+ if (!in_array($name, $this->load_migrations()))
+ {
+ $output->writeln('<error>' . $this->user->lang('MIGRATION_NOT_VALID', $name) . '</error>');
+ return 1;
+ }
+ else if ($this->migrator->migration_state($name) === false)
+ {
+ $output->writeln('<error>' . $this->user->lang('MIGRATION_NOT_INSTALLED', $name) . '</error>');
+ return 1;
+ }
+
+ try
+ {
+ while ($this->migrator->migration_state($name) !== false)
+ {
+ $this->migrator->revert($name);
+ }
+ }
+ catch (\phpbb\db\migration\exception $e)
+ {
+ $output->writeln('<error>' . $e->getLocalisedMessage($this->user) . '</error>');
+ $this->finalise_update();
+ return 1;
+ }
+
+ $this->finalise_update();
+ }
+}
diff --git a/phpBB/phpbb/console/command/dev/migration_tips.php b/phpBB/phpbb/console/command/dev/migration_tips.php
new file mode 100644
index 0000000000..f9047bdac8
--- /dev/null
+++ b/phpBB/phpbb/console/command/dev/migration_tips.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\console\command\dev;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class migration_tips extends \phpbb\console\command\command
+{
+ /** @var \phpbb\extension\manager */
+ protected $extension_manager;
+
+ function __construct(\phpbb\user $user, \phpbb\extension\manager $extension_manager)
+ {
+ $this->extension_manager = $extension_manager;
+ parent::__construct($user);
+ }
+
+ protected function configure()
+ {
+ $this
+ ->setName('dev:migration-tips')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_FIND_MIGRATIONS'))
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $migrations = $this->extension_manager->get_finder()
+ ->set_extensions(array())
+ ->core_path('phpbb/db/migration/data/')
+ ->get_classes();
+ $tips = $migrations;
+
+ foreach ($migrations as $migration_class)
+ {
+ foreach ($migration_class::depends_on() as $dependency)
+ {
+ $tips_key = array_search($dependency, $tips);
+ if ($tips_key !== false)
+ {
+ unset($tips[$tips_key]);
+ }
+ }
+ }
+
+ $output->writeln("\t\tarray(");
+ foreach ($tips as $migration)
+ {
+ $output->writeln("\t\t\t'{$migration}',");
+ }
+ $output->writeln("\t\t);");
+ }
+}
diff --git a/phpBB/phpbb/console/command/extension/command.php b/phpBB/phpbb/console/command/extension/command.php
index edde7ce2e2..364d954082 100644
--- a/phpBB/phpbb/console/command/extension/command.php
+++ b/phpBB/phpbb/console/command/extension/command.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\extension;
@@ -13,10 +17,14 @@ abstract class command extends \phpbb\console\command\command
/** @var \phpbb\extension\manager */
protected $manager;
- function __construct(\phpbb\extension\manager $manager)
+ /** @var \phpbb\log\log */
+ protected $log;
+
+ public function __construct(\phpbb\user $user, \phpbb\extension\manager $manager, \phpbb\log\log $log)
{
$this->manager = $manager;
+ $this->log = $log;
- parent::__construct();
+ parent::__construct($user);
}
}
diff --git a/phpBB/phpbb/console/command/extension/disable.php b/phpBB/phpbb/console/command/extension/disable.php
index e4de70ca34..1eee16cbd9 100644
--- a/phpBB/phpbb/console/command/extension/disable.php
+++ b/phpBB/phpbb/console/command/extension/disable.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\extension;
@@ -18,11 +22,11 @@ class disable extends command
{
$this
->setName('extension:disable')
- ->setDescription('Disables the specified extension.')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_DISABLE_EXTENSION'))
->addArgument(
'extension-name',
InputArgument::REQUIRED,
- 'Name of the extension'
+ $this->user->lang('CLI_EXTENSION_NAME')
)
;
}
@@ -33,14 +37,15 @@ class disable extends command
$this->manager->disable($name);
$this->manager->load_extensions();
- if ($this->manager->enabled($name))
+ if ($this->manager->is_enabled($name))
{
- $output->writeln("<error>Could not disable extension $name</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_EXTENSION_DISABLE_FAILURE', $name) . '</error>');
return 1;
}
else
{
- $output->writeln("<info>Successfully disabled extension $name</info>");
+ $this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_DISABLE', time(), array($name));
+ $output->writeln('<info>' . $this->user->lang('CLI_EXTENSION_DISABLE_SUCCESS', $name) . '</info>');
return 0;
}
}
diff --git a/phpBB/phpbb/console/command/extension/enable.php b/phpBB/phpbb/console/command/extension/enable.php
index ee7dae76aa..59ff11e9b7 100644
--- a/phpBB/phpbb/console/command/extension/enable.php
+++ b/phpBB/phpbb/console/command/extension/enable.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\extension;
@@ -18,11 +22,11 @@ class enable extends command
{
$this
->setName('extension:enable')
- ->setDescription('Enables the specified extension.')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_ENABLE_EXTENSION'))
->addArgument(
'extension-name',
InputArgument::REQUIRED,
- 'Name of the extension'
+ $this->user->lang('CLI_EXTENSION_NAME')
)
;
}
@@ -33,14 +37,15 @@ class enable extends command
$this->manager->enable($name);
$this->manager->load_extensions();
- if ($this->manager->enabled($name))
+ if ($this->manager->is_enabled($name))
{
- $output->writeln("<info>Successfully enabled extension $name</info>");
+ $this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_ENABLE', time(), array($name));
+ $output->writeln('<info>' . $this->user->lang('CLI_EXTENSION_ENABLE_SUCCESS', $name) . '</info>');
return 0;
}
else
{
- $output->writeln("<error>Could not enable extension $name</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_EXTENSION_ENABLE_FAILURE', $name) . '</error>');
return 1;
}
}
diff --git a/phpBB/phpbb/console/command/extension/purge.php b/phpBB/phpbb/console/command/extension/purge.php
index c2e1d2928c..517e9a74c9 100644
--- a/phpBB/phpbb/console/command/extension/purge.php
+++ b/phpBB/phpbb/console/command/extension/purge.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\extension;
@@ -18,11 +22,11 @@ class purge extends command
{
$this
->setName('extension:purge')
- ->setDescription('Purges the specified extension.')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_PURGE_EXTENSION'))
->addArgument(
'extension-name',
InputArgument::REQUIRED,
- 'Name of the extension'
+ $this->user->lang('CLI_EXTENSION_NAME')
)
;
}
@@ -33,14 +37,15 @@ class purge extends command
$this->manager->purge($name);
$this->manager->load_extensions();
- if ($this->manager->enabled($name))
+ if ($this->manager->is_enabled($name))
{
- $output->writeln("<error>Could not purge extension $name</error>");
+ $output->writeln('<error>' . $this->user->lang('CLI_EXTENSION_PURGE_FAILURE', $name) . '</error>');
return 1;
}
else
{
- $output->writeln("<info>Successfully purge extension $name</info>");
+ $this->log->add('admin', ANONYMOUS, '', 'LOG_EXT_PURGE', time(), array($name));
+ $output->writeln('<info>' . $this->user->lang('CLI_EXTENSION_PURGE_SUCCESS', $name) . '</info>');
return 0;
}
}
diff --git a/phpBB/phpbb/console/command/extension/show.php b/phpBB/phpbb/console/command/extension/show.php
index 0f48ac2379..f9322034d7 100644
--- a/phpBB/phpbb/console/command/extension/show.php
+++ b/phpBB/phpbb/console/command/extension/show.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\extension;
@@ -17,7 +21,7 @@ class show extends command
{
$this
->setName('extension:show')
- ->setDescription('Lists all extensions in the database and on the filesystem.')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_LIST_EXTENSIONS'))
;
}
@@ -28,27 +32,27 @@ class show extends command
if (empty($all))
{
- $output->writeln('<comment>No extensions were found.</comment>');
+ $output->writeln('<comment>' . $this->user->lang('CLI_EXTENSION_NOT_FOUND') . '</comment>');
return 3;
}
$enabled = array_keys($this->manager->all_enabled());
- $this->print_extension_list($output, 'Enabled', $enabled);
+ $this->print_extension_list($output, $this->user->lang('CLI_EXTENSIONS_ENABLED') . $this->user->lang('COLON'), $enabled);
$output->writeln('');
$disabled = array_keys($this->manager->all_disabled());
- $this->print_extension_list($output, 'Disabled', $disabled);
+ $this->print_extension_list($output, $this->user->lang('CLI_EXTENSIONS_DISABLED') . $this->user->lang('COLON'), $disabled);
$output->writeln('');
$purged = array_diff($all, $enabled, $disabled);
- $this->print_extension_list($output, 'Available', $purged);
+ $this->print_extension_list($output, $this->user->lang('CLI_EXTENSIONS_AVAILABLE') . $this->user->lang('COLON'), $purged);
}
protected function print_extension_list(OutputInterface $output, $type, array $extensions)
{
- $output->writeln("<info>$type:</info>");
+ $output->writeln("<info>$type</info>");
foreach ($extensions as $extension)
{
diff --git a/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php b/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php
index 04db880091..ec4e1b0ee7 100644
--- a/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php
+++ b/phpBB/phpbb/console/command/fixup/recalculate_email_hash.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\console\command\fixup;
@@ -13,21 +17,21 @@ use Symfony\Component\Console\Output\OutputInterface;
class recalculate_email_hash extends \phpbb\console\command\command
{
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
- function __construct(\phpbb\db\driver\driver $db)
+ function __construct(\phpbb\user $user, \phpbb\db\driver\driver_interface $db)
{
$this->db = $db;
- parent::__construct();
+ parent::__construct($user);
}
protected function configure()
{
$this
->setName('fixup:recalculate-email-hash')
- ->setDescription('Recalculates the user_email_hash column of the users table.')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_RECALCULATE_EMAIL_HASH'))
;
}
@@ -66,6 +70,6 @@ class recalculate_email_hash extends \phpbb\console\command\command
}
$this->db->sql_freeresult($result);
- $output->writeln('<info>Successfully recalculated all email hashes.</info>');
+ $output->writeln('<info>' . $this->user->lang('CLI_FIXUP_RECALCULATE_EMAIL_HASH_SUCCESS') . '</info>');
}
}
diff --git a/phpBB/phpbb/console/command/reparser/list_all.php b/phpBB/phpbb/console/command/reparser/list_all.php
new file mode 100644
index 0000000000..e42c3ac782
--- /dev/null
+++ b/phpBB/phpbb/console/command/reparser/list_all.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\console\command\reparser;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class list_all extends \phpbb\console\command\command
+{
+ /**
+ * @var string[] Names of the reparser services
+ */
+ protected $reparser_names;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user
+ * @param \phpbb\di\service_collection $reparsers
+ */
+ public function __construct(\phpbb\user $user, \phpbb\di\service_collection $reparsers)
+ {
+ parent::__construct($user);
+ $this->reparser_names = array();
+ foreach ($reparsers as $name => $reparser)
+ {
+ // Store the names without the "text_reparser." prefix
+ $this->reparser_names[] = preg_replace('(^text_reparser\\.)', '', $name);
+ }
+ }
+
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('reparser:list')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_REPARSER_LIST'))
+ ;
+ }
+
+ /**
+ * Executes the command reparser:reparse
+ *
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return integer
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $output->writeln('<info>' . implode(', ', $this->reparser_names) . '</info>');
+
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/console/command/reparser/reparse.php b/phpBB/phpbb/console/command/reparser/reparse.php
new file mode 100644
index 0000000000..ddc97a1d1d
--- /dev/null
+++ b/phpBB/phpbb/console/command/reparser/reparse.php
@@ -0,0 +1,284 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\console\command\reparser;
+
+use phpbb\exception\runtime_exception;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class reparse extends \phpbb\console\command\command
+{
+ /**
+ * @var InputInterface
+ */
+ protected $input;
+
+ /**
+ * @var SymfonyStyle
+ */
+ protected $io;
+
+ /**
+ * @var OutputInterface
+ */
+ protected $output;
+
+ /**
+ * @var \phpbb\lock\db
+ */
+ protected $reparse_lock;
+
+ /**
+ * @var \phpbb\textreparser\manager
+ */
+ protected $reparser_manager;
+
+ /**
+ * @var \phpbb\di\service_collection
+ */
+ protected $reparsers;
+
+ /**
+ * @var array The reparser's last $current ID as values
+ */
+ protected $resume_data;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user
+ * @param \phpbb\lock\db $reparse_lock
+ * @param \phpbb\textreparser\manager $reparser_manager
+ * @param \phpbb\di\service_collection $reparsers
+ */
+ public function __construct(\phpbb\user $user, \phpbb\lock\db $reparse_lock, \phpbb\textreparser\manager $reparser_manager, \phpbb\di\service_collection $reparsers)
+ {
+ require_once __DIR__ . '/../../../../includes/functions_content.php';
+
+ $this->reparse_lock = $reparse_lock;
+ $this->reparser_manager = $reparser_manager;
+ $this->reparsers = $reparsers;
+ parent::__construct($user);
+ }
+
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('reparser:reparse')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE'))
+ ->addArgument('reparser-name', InputArgument::OPTIONAL, $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_ARG_1'))
+ ->addOption(
+ 'dry-run',
+ null,
+ InputOption::VALUE_NONE,
+ $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_DRY_RUN')
+ )
+ ->addOption(
+ 'resume',
+ null,
+ InputOption::VALUE_NONE,
+ $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RESUME')
+ )
+ ->addOption(
+ 'range-min',
+ null,
+ InputOption::VALUE_REQUIRED,
+ $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_MIN'),
+ 1
+ )
+ ->addOption(
+ 'range-max',
+ null,
+ InputOption::VALUE_REQUIRED,
+ $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_MAX')
+ )
+ ->addOption(
+ 'range-size',
+ null,
+ InputOption::VALUE_REQUIRED,
+ $this->user->lang('CLI_DESCRIPTION_REPARSER_REPARSE_OPT_RANGE_SIZE'),
+ 100
+ );
+ ;
+ }
+
+ /**
+ * Create a styled progress bar
+ *
+ * @param integer $max Max value for the progress bar
+ * @return \Symfony\Component\Console\Helper\ProgressBar
+ */
+ protected function create_progress_bar($max)
+ {
+ $progress = $this->io->createProgressBar($max);
+ if ($this->output->getVerbosity() === OutputInterface::VERBOSITY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%percent:3s%%]</info> %message%');
+ $progress->setOverwrite(false);
+ }
+ else if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%current:s%/%max:s%]</info><comment>[%elapsed%/%estimated%][%memory%]</comment> %message%');
+ $progress->setOverwrite(false);
+ }
+ else
+ {
+ $this->io->newLine(2);
+ $progress->setFormat(
+ " %current:s%/%max:s% %bar% %percent:3s%%\n" .
+ " %message% %elapsed:6s%/%estimated:-6s% %memory:6s%\n");
+ $progress->setBarWidth(60);
+ }
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD'))
+ {
+ $progress->setEmptyBarCharacter('â–‘'); // light shade character \u2591
+ $progress->setProgressCharacter('');
+ $progress->setBarCharacter('â–“'); // dark shade character \u2593
+ }
+
+ return $progress;
+ }
+
+ /**
+ * Executes the command reparser:reparse
+ *
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return integer
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->input = $input;
+ $this->output = $output;
+ $this->io = new SymfonyStyle($input, $output);
+
+ if (!$this->reparse_lock->acquire())
+ {
+ throw new runtime_exception('REPARSE_LOCK_ERROR', array(), null, 1);
+ }
+
+ $name = $input->getArgument('reparser-name');
+ if (isset($name))
+ {
+ // Allow "post_text" to be an alias for "text_reparser.post_text"
+ if (!isset($this->reparsers[$name]))
+ {
+ $name = 'text_reparser.' . $name;
+ }
+ $this->reparse($name);
+ }
+ else
+ {
+ foreach ($this->reparsers as $name => $service)
+ {
+ $this->reparse($name);
+ }
+ }
+
+ $this->io->success($this->user->lang('CLI_REPARSER_REPARSE_SUCCESS'));
+
+ $this->reparse_lock->release();
+
+ return 0;
+ }
+
+ /**
+ * Get an option value, adjusted for given reparser
+ *
+ * Will use the last saved value if --resume is set and the option was not specified
+ * on the command line
+ *
+ * @param string $option_name Option name
+ * @return integer
+ */
+ protected function get_option($option_name)
+ {
+ // Return the option from the resume_data if applicable
+ if ($this->input->getOption('resume') && isset($this->resume_data[$option_name]) && !$this->input->hasParameterOption('--' . $option_name))
+ {
+ return $this->resume_data[$option_name];
+ }
+
+ return $this->input->getOption($option_name);
+ }
+
+ /**
+ * Reparse all text handled by given reparser within given range
+ *
+ * @param string $name Reparser name
+ */
+ protected function reparse($name)
+ {
+ $reparser = $this->reparsers[$name];
+ $this->resume_data = $this->reparser_manager->get_resume_data($name);
+ if ($this->input->getOption('dry-run'))
+ {
+ $reparser->disable_save();
+ }
+ else
+ {
+ $reparser->enable_save();
+ }
+
+ // Start at range-max if specified or at the highest ID otherwise
+ $max = $this->get_option('range-max');
+ $min = $this->get_option('range-min');
+ $size = $this->get_option('range-size');
+
+ // range-max has no default value, it must be computed for each reparser
+ if ($max == null)
+ {
+ $max = $reparser->get_max_id();
+ }
+
+ if ($max < $min)
+ {
+ return;
+ }
+
+ $this->io->section($this->user->lang('CLI_REPARSER_REPARSE_REPARSING', preg_replace('(^text_reparser\\.)', '', $name), $min, $max));
+
+ $progress = $this->create_progress_bar($max);
+ $progress->setMessage($this->user->lang('CLI_REPARSER_REPARSE_REPARSING_START', preg_replace('(^text_reparser\\.)', '', $name)));
+ $progress->start();
+
+ // Start from $max and decrement $current by $size until we reach $min
+ $current = $max;
+ while ($current >= $min)
+ {
+ $start = max($min, $current + 1 - $size);
+ $end = max($min, $current);
+
+ $progress->setMessage($this->user->lang('CLI_REPARSER_REPARSE_REPARSING', preg_replace('(^text_reparser\\.)', '', $name), $start, $end));
+ $reparser->reparse_range($start, $end);
+
+ $current = $start - 1;
+ $progress->setProgress($max + 1 - $start);
+
+ $this->reparser_manager->update_resume_data($name, $min, $current, $size, !$this->input->getOption('dry-run'));
+ }
+ $progress->finish();
+
+ $this->io->newLine(2);
+ }
+}
diff --git a/phpBB/phpbb/console/command/thumbnail/delete.php b/phpBB/phpbb/console/command/thumbnail/delete.php
new file mode 100644
index 0000000000..e8e4cf568e
--- /dev/null
+++ b/phpBB/phpbb/console/command/thumbnail/delete.php
@@ -0,0 +1,178 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+namespace phpbb\console\command\thumbnail;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class delete extends \phpbb\console\command\command
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user The user object (used to get language information)
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param string $phpbb_root_path Root path
+ */
+ public function __construct(\phpbb\user $user, \phpbb\db\driver\driver_interface $db, $phpbb_root_path)
+ {
+ $this->db = $db;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ parent::__construct($user);
+ }
+
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('thumbnail:delete')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_THUMBNAIL_DELETE'))
+ ;
+ }
+
+ /**
+ * Executes the command thumbnail:delete.
+ *
+ * Deletes all existing thumbnails and updates the database accordingly.
+ *
+ * @param InputInterface $input The input stream used to get the argument and verbose option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ *
+ * @return int 0 if all is ok, 1 if a thumbnail couldn't be deleted.
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $io->section($this->user->lang('CLI_THUMBNAIL_DELETING'));
+
+ $sql = 'SELECT COUNT(*) AS nb_missing_thumbnails
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE thumbnail = 1';
+ $result = $this->db->sql_query($sql);
+ $nb_missing_thumbnails = (int) $this->db->sql_fetchfield('nb_missing_thumbnails');
+ $this->db->sql_freeresult($result);
+
+ if ($nb_missing_thumbnails === 0)
+ {
+ $io->warning($this->user->lang('CLI_THUMBNAIL_NOTHING_TO_DELETE'));
+ return 0;
+ }
+
+ $sql = 'SELECT attach_id, physical_filename, extension, real_filename, mimetype
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE thumbnail = 1';
+ $result = $this->db->sql_query($sql);
+
+ $progress = $io->createProgressBar($nb_missing_thumbnails);
+ if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%percent:3s%%]</info> %message%');
+ $progress->setOverwrite(false);
+ }
+ else if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%current:s%/%max:s%]</info><comment>[%elapsed%/%estimated%][%memory%]</comment> %message%');
+ $progress->setOverwrite(false);
+ }
+ else
+ {
+ $io->newLine(2);
+ $progress->setFormat(
+ " %current:s%/%max:s% %bar% %percent:3s%%\n" .
+ " %elapsed:6s%/%estimated:-6s% %memory:6s%\n");
+ $progress->setBarWidth(60);
+ }
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD'))
+ {
+ $progress->setEmptyBarCharacter('â–‘'); // light shade character \u2591
+ $progress->setProgressCharacter('');
+ $progress->setBarCharacter('â–“'); // dark shade character \u2593
+ }
+
+ $progress->setMessage($this->user->lang('CLI_THUMBNAIL_DELETING'));
+
+ $progress->start();
+
+ $thumbnail_deleted = array();
+ $return = 0;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $thumbnail_path = $this->phpbb_root_path . 'files/thumb_' . $row['physical_filename'];
+
+ if (@unlink($thumbnail_path))
+ {
+ $thumbnail_deleted[] = $row['attach_id'];
+
+ if (sizeof($thumbnail_deleted) === 250)
+ {
+ $this->commit_changes($thumbnail_deleted);
+ $thumbnail_deleted = array();
+ }
+
+ $progress->setMessage($this->user->lang('CLI_THUMBNAIL_DELETED', $row['real_filename'], $row['physical_filename']));
+ }
+ else
+ {
+ $return = 1;
+ $progress->setMessage('<error>' . $this->user->lang('CLI_THUMBNAIL_SKIPPED', $row['real_filename'], $row['physical_filename']) . '</error>');
+ }
+
+ $progress->advance();
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($thumbnail_deleted))
+ {
+ $this->commit_changes($thumbnail_deleted);
+ }
+
+ $progress->finish();
+
+ $io->newLine(2);
+ $io->success($this->user->lang('CLI_THUMBNAIL_DELETING_DONE'));
+
+ return $return;
+ }
+
+ /**
+ * Commits the changes to the database
+ *
+ * @param array $thumbnail_deleted
+ */
+ protected function commit_changes(array $thumbnail_deleted)
+ {
+ $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
+ SET thumbnail = 0
+ WHERE ' . $this->db->sql_in_set('attach_id', $thumbnail_deleted);
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/console/command/thumbnail/generate.php b/phpBB/phpbb/console/command/thumbnail/generate.php
new file mode 100644
index 0000000000..e677db3a97
--- /dev/null
+++ b/phpBB/phpbb/console/command/thumbnail/generate.php
@@ -0,0 +1,204 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\console\command\thumbnail;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+class generate extends \phpbb\console\command\command
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\cache\service
+ */
+ protected $cache;
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP extension.
+ *
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user The user object (used to get language information)
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param \phpbb\cache\service $cache The cache service
+ * @param string $phpbb_root_path Root path
+ * @param string $php_ext PHP extension
+ */
+ public function __construct(\phpbb\user $user, \phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, $phpbb_root_path, $php_ext)
+ {
+ $this->db = $db;
+ $this->cache = $cache;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ parent::__construct($user);
+ }
+
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('thumbnail:generate')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_THUMBNAIL_GENERATE'))
+ ;
+ }
+
+ /**
+ * Executes the command thumbnail:generate.
+ *
+ * Generate a thumbnail for all attachments which need one and don't have it yet.
+ *
+ * @param InputInterface $input The input stream used to get the argument and verboe option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ *
+ * @return int 0.
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $io->section($this->user->lang('CLI_THUMBNAIL_GENERATING'));
+
+ $sql = 'SELECT COUNT(*) AS nb_missing_thumbnails
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE thumbnail = 0';
+ $result = $this->db->sql_query($sql);
+ $nb_missing_thumbnails = (int) $this->db->sql_fetchfield('nb_missing_thumbnails');
+ $this->db->sql_freeresult($result);
+
+ if ($nb_missing_thumbnails === 0)
+ {
+ $io->warning($this->user->lang('CLI_THUMBNAIL_NOTHING_TO_GENERATE'));
+ return 0;
+ }
+
+ $extensions = $this->cache->obtain_attach_extensions(true);
+
+ $sql = 'SELECT attach_id, physical_filename, extension, real_filename, mimetype
+ FROM ' . ATTACHMENTS_TABLE . '
+ WHERE thumbnail = 0';
+ $result = $this->db->sql_query($sql);
+
+ if (!function_exists('create_thumbnail'))
+ {
+ require($this->phpbb_root_path . 'includes/functions_posting.' . $this->php_ext);
+ }
+
+ $progress = $io->createProgressBar($nb_missing_thumbnails);
+ if ($output->getVerbosity() === OutputInterface::VERBOSITY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%percent:3s%%]</info> %message%');
+ $progress->setOverwrite(false);
+ }
+ else if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE)
+ {
+ $progress->setFormat('<info>[%current:s%/%max:s%]</info><comment>[%elapsed%/%estimated%][%memory%]</comment> %message%');
+ $progress->setOverwrite(false);
+ }
+ else
+ {
+ $io->newLine(2);
+ $progress->setFormat(
+ " %current:s%/%max:s% %bar% %percent:3s%%\n" .
+ " %elapsed:6s%/%estimated:-6s% %memory:6s%\n");
+ $progress->setBarWidth(60);
+ }
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD'))
+ {
+ $progress->setEmptyBarCharacter('â–‘'); // light shade character \u2591
+ $progress->setProgressCharacter('');
+ $progress->setBarCharacter('â–“'); // dark shade character \u2593
+ }
+
+ $progress->setMessage($this->user->lang('CLI_THUMBNAIL_GENERATING'));
+
+ $progress->start();
+
+ $thumbnail_created = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (isset($extensions[$row['extension']]['display_cat']) && $extensions[$row['extension']]['display_cat'] == ATTACHMENT_CATEGORY_IMAGE)
+ {
+ $source = $this->phpbb_root_path . 'files/' . $row['physical_filename'];
+ $destination = $this->phpbb_root_path . 'files/thumb_' . $row['physical_filename'];
+
+ if (create_thumbnail($source, $destination, $row['mimetype']))
+ {
+ $thumbnail_created[] = (int) $row['attach_id'];
+
+ if (count($thumbnail_created) === 250)
+ {
+ $this->commit_changes($thumbnail_created);
+ $thumbnail_created = array();
+ }
+
+ $progress->setMessage($this->user->lang('CLI_THUMBNAIL_GENERATED', $row['real_filename'], $row['physical_filename']));
+ }
+ else
+ {
+ $progress->setMessage('<info>' . $this->user->lang('CLI_THUMBNAIL_SKIPPED', $row['real_filename'], $row['physical_filename']) . '</info>');
+ }
+ }
+
+ $progress->advance();
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($thumbnail_created))
+ {
+ $this->commit_changes($thumbnail_created);
+ }
+
+ $progress->finish();
+
+ $io->newLine(2);
+ $io->success($this->user->lang('CLI_THUMBNAIL_GENERATING_DONE'));
+
+ return 0;
+ }
+
+ /**
+ * Commits the changes to the database
+ *
+ * @param array $thumbnail_created
+ */
+ protected function commit_changes(array $thumbnail_created)
+ {
+ $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
+ SET thumbnail = 1
+ WHERE ' . $this->db->sql_in_set('attach_id', $thumbnail_created);
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/console/command/thumbnail/recreate.php b/phpBB/phpbb/console/command/thumbnail/recreate.php
new file mode 100644
index 0000000000..382da290bf
--- /dev/null
+++ b/phpBB/phpbb/console/command/thumbnail/recreate.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\console\command\thumbnail;
+
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class recreate extends \phpbb\console\command\command
+{
+ /**
+ * Sets the command name and description
+ *
+ * @return null
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('thumbnail:recreate')
+ ->setDescription($this->user->lang('CLI_DESCRIPTION_THUMBNAIL_RECREATE'))
+ ;
+ }
+
+ /**
+ * Executes the command thumbnail:recreate.
+ *
+ * This command is a "macro" to execute thumbnail:delete and then thumbnail:generate.
+ *
+ * @param InputInterface $input The input stream used to get the argument and verboe option.
+ * @param OutputInterface $output The output stream, used for printing verbose-mode and error information.
+ *
+ * @return int 0 if all is ok, 1 if a thumbnail couldn't be deleted.
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $parameters = array(
+ 'command' => 'thumbnail:delete'
+ );
+
+ if ($input->getOption('verbose'))
+ {
+ $parameters['-' . str_repeat('v', $output->getVerbosity() - 1)] = true;
+ }
+
+ $this->getApplication()->setAutoExit(false);
+
+ $input_delete = new ArrayInput($parameters);
+ $return = $this->getApplication()->run($input_delete, $output);
+
+ if ($return === 0)
+ {
+ $parameters['command'] = 'thumbnail:generate';
+
+ $input_create = new ArrayInput($parameters);
+ $return = $this->getApplication()->run($input_create, $output);
+ }
+
+ $this->getApplication()->setAutoExit(true);
+
+ return $return;
+ }
+}
diff --git a/phpBB/phpbb/console/exception_subscriber.php b/phpBB/phpbb/console/exception_subscriber.php
new file mode 100644
index 0000000000..b920d4abae
--- /dev/null
+++ b/phpBB/phpbb/console/exception_subscriber.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\console;
+
+use phpbb\exception\exception_interface;
+use Symfony\Component\Console\ConsoleEvents;
+use Symfony\Component\Console\Event\ConsoleExceptionEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class exception_subscriber implements EventSubscriberInterface
+{
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * Construct method
+ *
+ * @param \phpbb\language\language $language Language object
+ * @param bool $debug Debug mode
+ */
+ public function __construct(\phpbb\language\language $language, $debug = false)
+ {
+ $this->language = $language;
+ $this->debug = $debug;
+ }
+
+ /**
+ * This listener is run when the ConsoleEvents::EXCEPTION event is triggered.
+ * It translate the exception message. If din debug mode the original exception is embedded.
+ *
+ * @param ConsoleExceptionEvent $event
+ */
+ public function on_exception(ConsoleExceptionEvent $event)
+ {
+ $original_exception = $event->getException();
+
+ if ($original_exception instanceof exception_interface)
+ {
+ $parameters = array_merge(array($original_exception->getMessage()), $original_exception->get_parameters());
+ $message = call_user_func_array(array($this->language, 'lang'), $parameters);
+
+ if ($this->debug)
+ {
+ $exception = new \RuntimeException($message , $original_exception->getCode(), $original_exception);
+ }
+ else
+ {
+ $exception = new \RuntimeException($message , $original_exception->getCode());
+ }
+
+ $event->setException($exception);
+ }
+ }
+
+ static public function getSubscribedEvents()
+ {
+ return array(
+ ConsoleEvents::EXCEPTION => 'on_exception',
+ );
+ }
+}
diff --git a/phpBB/phpbb/content_visibility.php b/phpBB/phpbb/content_visibility.php
index 874889015a..0ba0489cb7 100644
--- a/phpBB/phpbb/content_visibility.php
+++ b/phpBB/phpbb/content_visibility.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpbb
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,13 +16,12 @@ namespace phpbb;
/**
* phpbb_visibility
* Handle fetching and setting the visibility for topics and posts
-* @package phpbb
*/
class content_visibility
{
/**
* Database object
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -35,6 +38,18 @@ class content_visibility
protected $auth;
/**
+ * config object
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * Event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* phpBB root path
* @var string
*/
@@ -50,15 +65,22 @@ class content_visibility
* Constructor
*
* @param \phpbb\auth\auth $auth Auth object
- * @param \phpbb\db\driver\driver $db Database object
- * @param \phpbb\user $user User object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
+ * @param \phpbb\db\driver\driver_interface $db Database object
+ * @param \phpbb\user $user User object
* @param string $phpbb_root_path Root path
* @param string $php_ext PHP Extension
- * @return null
+ * @param string $forums_table Forums table name
+ * @param string $posts_table Posts table name
+ * @param string $topics_table Topics table name
+ * @param string $users_table Users table name
*/
- public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver $db, \phpbb\user $user, $phpbb_root_path, $php_ext, $forums_table, $posts_table, $topics_table, $users_table)
+ public function __construct(\phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, $phpbb_root_path, $php_ext, $forums_table, $posts_table, $topics_table, $users_table)
{
$this->auth = $auth;
+ $this->config = $config;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->db = $db;
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
@@ -121,12 +143,43 @@ class content_visibility
*/
public function get_visibility_sql($mode, $forum_id, $table_alias = '')
{
+ $where_sql = '';
+
+ $get_visibility_sql_overwrite = false;
+
+ /**
+ * Allow changing the result of calling get_visibility_sql
+ *
+ * @event core.phpbb_content_visibility_get_visibility_sql_before
+ * @var string where_sql Extra visibility conditions. It must end with either an SQL "AND" or an "OR"
+ * @var string mode Either "topic" or "post" depending on the query this is being used in
+ * @var array forum_id The forum id in which the search is made.
+ * @var string table_alias Table alias to prefix in SQL queries
+ * @var mixed get_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
+ * If false, get_visibility_sql continues normally
+ * It must be either boolean or string
+ * @since 3.1.4-RC1
+ */
+ $vars = array(
+ 'where_sql',
+ 'mode',
+ 'forum_id',
+ 'table_alias',
+ 'get_visibility_sql_overwrite',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_visibility_sql_before', compact($vars)));
+
+ if ($get_visibility_sql_overwrite !== false)
+ {
+ return $get_visibility_sql_overwrite;
+ }
+
if ($this->auth->acl_get('m_approve', $forum_id))
{
- return '1 = 1';
+ return $where_sql . '1 = 1';
}
- return $table_alias . $mode . '_visibility = ' . ITEM_APPROVED;
+ return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED;
}
/**
@@ -146,6 +199,36 @@ class content_visibility
$approve_forums = array_intersect($forum_ids, array_keys($this->auth->acl_getf('m_approve', true)));
+ $get_forums_visibility_sql_overwrite = false;
+ /**
+ * Allow changing the result of calling get_forums_visibility_sql
+ *
+ * @event core.phpbb_content_visibility_get_forums_visibility_before
+ * @var string where_sql The action the user tried to execute
+ * @var string mode Either "topic" or "post" depending on the query this is being used in
+ * @var array forum_ids Array of forum ids which the posts/topics are limited to
+ * @var string table_alias Table alias to prefix in SQL queries
+ * @var array approve_forums Array of forums where the user has m_approve permissions
+ * @var mixed get_forums_visibility_sql_overwrite If a string, forces the function to return get_forums_visibility_sql_overwrite after executing the event
+ * If false, get_forums_visibility_sql continues normally
+ * It must be either boolean or string
+ * @since 3.1.3-RC1
+ */
+ $vars = array(
+ 'where_sql',
+ 'mode',
+ 'forum_ids',
+ 'table_alias',
+ 'approve_forums',
+ 'get_forums_visibility_sql_overwrite',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_forums_visibility_before', compact($vars)));
+
+ if ($get_forums_visibility_sql_overwrite !== false)
+ {
+ return $get_forums_visibility_sql_overwrite;
+ }
+
if (sizeof($approve_forums))
{
// Remove moderator forums from the rest
@@ -154,7 +237,7 @@ class content_visibility
if (!sizeof($forum_ids))
{
// The user can see all posts/topics in all specified forums
- return $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums);
+ return $where_sql . $this->db->sql_in_set($table_alias . 'forum_id', $approve_forums) . ')';
}
else
{
@@ -165,8 +248,8 @@ class content_visibility
else
{
// The user is just a normal user
- return $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
- AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true);
+ return $where_sql . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
+ AND ' . $this->db->sql_in_set($table_alias . 'forum_id', $forum_ids, false, true) . ')';
}
$where_sql .= '(' . $table_alias . $mode . '_visibility = ' . ITEM_APPROVED . '
@@ -192,6 +275,35 @@ class content_visibility
$approve_forums = array_diff(array_keys($this->auth->acl_getf('m_approve', true)), $exclude_forum_ids);
+ $visibility_sql_overwrite = null;
+
+ /**
+ * Allow changing the result of calling get_global_visibility_sql
+ *
+ * @event core.phpbb_content_visibility_get_global_visibility_before
+ * @var array where_sqls The action the user tried to execute
+ * @var string mode Either "topic" or "post" depending on the query this is being used in
+ * @var array exclude_forum_ids Array of forum ids the current user doesn't have access to
+ * @var string table_alias Table alias to prefix in SQL queries
+ * @var array approve_forums Array of forums where the user has m_approve permissions
+ * @var string visibility_sql_overwrite Forces the function to return an implosion of where_sqls (joined by "OR")
+ * @since 3.1.3-RC1
+ */
+ $vars = array(
+ 'where_sqls',
+ 'mode',
+ 'exclude_forum_ids',
+ 'table_alias',
+ 'approve_forums',
+ 'visibility_sql_overwrite',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.phpbb_content_visibility_get_global_visibility_before', compact($vars)));
+
+ if ($visibility_sql_overwrite)
+ {
+ return $visibility_sql_overwrite;
+ }
+
if (sizeof($exclude_forum_ids))
{
$where_sqls[] = '(' . $this->db->sql_in_set($table_alias . 'forum_id', $exclude_forum_ids, true) . '
@@ -215,23 +327,23 @@ class content_visibility
/**
* Change visibility status of one post or all posts of a topic
*
- * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED}
+ * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
* @param $post_id mixed Post ID or array of post IDs to act on,
* if it is empty, all posts of topic_id will be modified
* @param $topic_id int Topic where $post_id is found
* @param $forum_id int Forum where $topic_id is found
* @param $user_id int User performing the action
* @param $time int Timestamp when the action is performed
- * @param $reason string Reason why the visibilty was changed.
+ * @param $reason string Reason why the visibility was changed.
* @param $is_starter bool Is this the first post of the topic changed?
* @param $is_latest bool Is this the last post of the topic changed?
* @param $limit_visibility mixed Limit updating per topic_id to a certain visibility
* @param $limit_delete_time mixed Limit updating per topic_id to a certain deletion time
- * @return array Changed post data, empty array if an error occured.
+ * @return array Changed post data, empty array if an error occurred.
*/
public function set_post_visibility($visibility, $post_id, $topic_id, $forum_id, $user_id, $time, $reason, $is_starter, $is_latest, $limit_visibility = false, $limit_delete_time = false)
{
- if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED)))
+ if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
{
return array();
}
@@ -326,7 +438,7 @@ class content_visibility
// Update users postcounts
foreach ($postcounts as $num_posts => $poster_ids)
{
- if ($visibility == ITEM_DELETED)
+ if (in_array($visibility, array(ITEM_REAPPROVE, ITEM_DELETED)))
{
$sql = 'UPDATE ' . $this->users_table . '
SET user_posts = 0
@@ -384,75 +496,52 @@ class content_visibility
$update_topic_postcount = false;
}
+ $topic_update_array = array();
// Update the topic's reply count and the forum's post count
if ($update_topic_postcount)
{
- $cur_posts = $cur_unapproved_posts = $cur_softdeleted_posts = 0;
+ $field_alias = array(
+ ITEM_APPROVED => 'posts_approved',
+ ITEM_UNAPPROVED => 'posts_unapproved',
+ ITEM_DELETED => 'posts_softdeleted',
+ ITEM_REAPPROVE => 'posts_unapproved',
+ );
+ $cur_posts = array_fill_keys($field_alias, 0);
+
foreach ($postcount_visibility as $post_visibility => $visibility_posts)
{
- // We need to substract the posts from the counters ...
- if ($post_visibility == ITEM_APPROVED)
- {
- $cur_posts += $visibility_posts;
- }
- else if ($post_visibility == ITEM_UNAPPROVED)
- {
- $cur_unapproved_posts += $visibility_posts;
- }
- else if ($post_visibility == ITEM_DELETED)
- {
- $cur_softdeleted_posts += $visibility_posts;
- }
+ $cur_posts[$field_alias[(int) $post_visibility]] += $visibility_posts;
}
$sql_ary = array();
- if ($visibility == ITEM_DELETED)
+ $recipient_field = $field_alias[$visibility];
+
+ foreach ($cur_posts as $field => $count)
{
- if ($cur_posts)
+ // Decrease the count for the old statuses.
+ if ($count && $field != $recipient_field)
{
- $sql_ary['posts_approved'] = ' - ' . $cur_posts;
- }
- if ($cur_unapproved_posts)
- {
- $sql_ary['posts_unapproved'] = ' - ' . $cur_unapproved_posts;
- }
- if ($cur_posts + $cur_unapproved_posts)
- {
- $sql_ary['posts_softdeleted'] = ' + ' . ($cur_posts + $cur_unapproved_posts);
+ $sql_ary[$field] = " - $count";
}
}
- else
+ // Add up the count from all statuses excluding the recipient status.
+ $count_increase = array_sum(array_diff($cur_posts, array($recipient_field)));
+
+ if ($count_increase)
{
- if ($cur_unapproved_posts)
- {
- $sql_ary['posts_unapproved'] = ' - ' . $cur_unapproved_posts;
- }
- if ($cur_softdeleted_posts)
- {
- $sql_ary['posts_softdeleted'] = ' - ' . $cur_softdeleted_posts;
- }
- if ($cur_softdeleted_posts + $cur_unapproved_posts)
- {
- $sql_ary['posts_approved'] = ' + ' . ($cur_softdeleted_posts + $cur_unapproved_posts);
- }
+ $sql_ary[$recipient_field] = " + $count_increase";
}
if (sizeof($sql_ary))
{
- $topic_sql = $forum_sql = array();
+ $forum_sql = array();
foreach ($sql_ary as $field => $value_change)
{
- $topic_sql[] = 'topic_' . $field . ' = topic_' . $field . $value_change;
+ $topic_update_array[] = 'topic_' . $field . ' = topic_' . $field . $value_change;
$forum_sql[] = 'forum_' . $field . ' = forum_' . $field . $value_change;
}
- // Update the number for replies and posts
- $sql = 'UPDATE ' . $this->topics_table . '
- SET ' . implode(', ', $topic_sql) . '
- WHERE topic_id = ' . (int) $topic_id;
- $this->db->sql_query($sql);
-
$sql = 'UPDATE ' . $this->forums_table . '
SET ' . implode(', ', $forum_sql) . '
WHERE forum_id = ' . (int) $forum_id;
@@ -460,6 +549,38 @@ class content_visibility
}
}
+ if ($post_id)
+ {
+ $sql = 'SELECT 1 AS has_attachments
+ FROM ' . POSTS_TABLE . '
+ WHERE topic_id = ' . (int) $topic_id . '
+ AND post_attachment = 1
+ AND post_visibility = ' . ITEM_APPROVED . '
+ AND ' . $this->db->sql_in_set('post_id', $post_id, true);
+ $result = $this->db->sql_query_limit($sql, 1);
+
+ $has_attachment = (bool) $this->db->sql_fetchfield('has_attachments');
+ $this->db->sql_freeresult($result);
+
+ if ($has_attachment && $visibility == ITEM_APPROVED)
+ {
+ $topic_update_array[] = 'topic_attachment = 1';
+ }
+ else if (!$has_attachment && $visibility != ITEM_APPROVED)
+ {
+ $topic_update_array[] = 'topic_attachment = 0';
+ }
+ }
+
+ if (!empty($topic_update_array))
+ {
+ // Update the number for replies and posts, and update the attachments flag
+ $sql = 'UPDATE ' . $this->topics_table . '
+ SET ' . implode(', ', $topic_update_array) . '
+ WHERE topic_id = ' . (int) $topic_id;
+ $this->db->sql_query($sql);
+ }
+
return $data;
}
@@ -475,7 +596,7 @@ class content_visibility
* as soft deleted.
* If you want to update all posts, use the force option.
*
- * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED}
+ * @param $visibility int Element of {ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE}
* @param $topic_id mixed Topic ID to act on
* @param $forum_id int Forum where $topic_id is found
* @param $user_id int User performing the action
@@ -486,7 +607,7 @@ class content_visibility
*/
public function set_topic_visibility($visibility, $topic_id, $forum_id, $user_id, $time, $reason, $force_update_all = false)
{
- if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED)))
+ if (!in_array($visibility, array(ITEM_APPROVED, ITEM_DELETED, ITEM_REAPPROVE)))
{
return array();
}
@@ -528,16 +649,16 @@ class content_visibility
if (!$force_update_all && $original_topic_data['topic_delete_time'] && $original_topic_data['topic_visibility'] == ITEM_DELETED && $visibility == ITEM_APPROVED)
{
// If we're restoring a topic we only restore posts, that were soft deleted through the topic soft deletion.
- self::set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility'], $original_topic_data['topic_delete_time']);
+ $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility'], $original_topic_data['topic_delete_time']);
}
else if (!$force_update_all && $original_topic_data['topic_visibility'] == ITEM_APPROVED && $visibility == ITEM_DELETED)
{
- // If we're soft deleting a topic we only approved posts are soft deleted.
- self::set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility']);
+ // If we're soft deleting a topic we only mark approved posts as soft deleted.
+ $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true, $original_topic_data['topic_visibility']);
}
else
{
- self::set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true);
+ $this->set_post_visibility($visibility, false, $topic_id, $forum_id, $user_id, $time, '', true, true);
}
return $data;
@@ -547,7 +668,7 @@ class content_visibility
* Add post to topic and forum statistics
*
* @param $data array Contains information from the topics table about given topic
- * @param $sql_data array Populated with the SQL changes, may be empty at call time
+ * @param &$sql_data array Populated with the SQL changes, may be empty at call time
* @return null
*/
public function add_post_to_statistic($data, &$sql_data)
@@ -561,90 +682,68 @@ class content_visibility
$sql_data[$this->users_table] = (($sql_data[$this->users_table]) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts + 1';
}
- set_config_count('num_posts', 1, true);
+ $this->config->increment('num_posts', 1, false);
}
/**
* Remove post from topic and forum statistics
*
* @param $data array Contains information from the topics table about given topic
- * @param $sql_data array Populated with the SQL changes, may be empty at call time
+ * @param &$sql_data array Populated with the SQL changes, may be empty at call time
* @return null
*/
public function remove_post_from_statistic($data, &$sql_data)
{
- $sql_data[$this->topics_table] = ((!empty($sql_data[$this->topics_table])) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved - 1';
- $sql_data[$this->forums_table] = ((!empty($sql_data[$this->forums_table])) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved - 1';
+ if ($data['post_visibility'] == ITEM_APPROVED)
+ {
+ $sql_data[$this->topics_table] = ((!empty($sql_data[$this->topics_table])) ? $sql_data[$this->topics_table] . ', ' : '') . 'topic_posts_approved = topic_posts_approved - 1';
+ $sql_data[$this->forums_table] = ((!empty($sql_data[$this->forums_table])) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_posts_approved = forum_posts_approved - 1';
- if ($data['post_postcount'])
+ if ($data['post_postcount'])
+ {
+ $sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
+ }
+
+ $this->config->increment('num_posts', -1, false);
+ }
+ else if ($data['post_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
{
- $sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
+ $sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_unapproved = forum_posts_unapproved - 1';
+ $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_unapproved = topic_posts_unapproved - 1';
+ }
+ else if ($data['post_visibility'] == ITEM_DELETED)
+ {
+ $sql_data[FORUMS_TABLE] = (($sql_data[FORUMS_TABLE]) ? $sql_data[FORUMS_TABLE] . ', ' : '') . 'forum_posts_softdeleted = forum_posts_softdeleted - 1';
+ $sql_data[TOPICS_TABLE] = (($sql_data[TOPICS_TABLE]) ? $sql_data[TOPICS_TABLE] . ', ' : '') . 'topic_posts_softdeleted = topic_posts_softdeleted - 1';
}
-
- set_config_count('num_posts', -1, true);
}
/**
* Remove topic from forum statistics
*
- * @param $topic_id int The topic to act on
- * @param $forum_id int Forum where the topic is found
- * @param $topic_row array Contains information from the topic, may be empty at call time
- * @param $sql_data array Populated with the SQL changes, may be empty at call time
+ * @param $data array Post and topic data
+ * @param &$sql_data array Populated with the SQL changes, may be empty at call time
* @return null
*/
- public function remove_topic_from_statistic($topic_id, $forum_id, &$topic_row, &$sql_data)
+ public function remove_topic_from_statistic($data, &$sql_data)
{
- // Do we need to grab some topic informations?
- if (!sizeof($topic_row))
+ if ($data['topic_visibility'] == ITEM_APPROVED)
{
- $sql = 'SELECT topic_type, topic_posts_approved, topic_posts_unapproved, topic_posts_softdeleted, topic_visibility
- FROM ' . $this->topics_table . '
- WHERE topic_id = ' . (int) $topic_id;
- $result = $this->db->sql_query($sql);
- $topic_row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
- }
-
- // If this is an edited topic or the first post the topic gets completely disapproved later on...
- $sql_data[$this->forums_table] = (($sql_data[$this->forums_table]) ? $sql_data[$this->forums_table] . ', ' : '') . 'forum_topics_approved = forum_topics_approved - 1';
- $sql_data[$this->forums_table] .= ', forum_posts_approved = forum_posts_approved - ' . $topic_row['topic_posts_approved'];
- $sql_data[$this->forums_table] .= ', forum_posts_unapproved = forum_posts_unapproved - ' . $topic_row['topic_posts_unapproved'];
- $sql_data[$this->forums_table] .= ', forum_posts_softdeleted = forum_posts_softdeleted - ' . $topic_row['topic_posts_softdeleted'];
-
- set_config_count('num_topics', -1, true);
- set_config_count('num_posts', $topic_row['topic_posts_approved'] * (-1), true);
-
- // Get user post count information
- $sql = 'SELECT poster_id, COUNT(post_id) AS num_posts
- FROM ' . $this->posts_table . '
- WHERE topic_id = ' . (int) $topic_id . '
- AND post_postcount = 1
- AND post_visibility = ' . ITEM_APPROVED . '
- GROUP BY poster_id';
- $result = $this->db->sql_query($sql);
+ $sql_data[FORUMS_TABLE] .= 'forum_posts_approved = forum_posts_approved - 1, forum_topics_approved = forum_topics_approved - 1';
- $postcounts = array();
- while ($row = $this->db->sql_fetchrow($result))
+ if ($data['post_postcount'])
+ {
+ $sql_data[$this->users_table] = ((!empty($sql_data[$this->users_table])) ? $sql_data[$this->users_table] . ', ' : '') . 'user_posts = user_posts - 1';
+ }
+ }
+ else if ($data['topic_visibility'] == ITEM_UNAPPROVED || $data['post_visibility'] == ITEM_REAPPROVE)
{
- $postcounts[(int) $row['num_posts']][] = (int) $row['poster_id'];
+ $sql_data[FORUMS_TABLE] .= 'forum_posts_unapproved = forum_posts_unapproved - 1, forum_topics_unapproved = forum_topics_unapproved - 1';
}
- $this->db->sql_freeresult($result);
-
- // Decrement users post count
- foreach ($postcounts as $num_posts => $poster_ids)
+ else if ($data['topic_visibility'] == ITEM_DELETED)
{
- $sql = 'UPDATE ' . $this->users_table . '
- SET user_posts = 0
- WHERE user_posts < ' . $num_posts . '
- AND ' . $this->db->sql_in_set('user_id', $poster_ids);
- $this->db->sql_query($sql);
-
- $sql = 'UPDATE ' . $this->users_table . '
- SET user_posts = user_posts - ' . $num_posts . '
- WHERE user_posts >= ' . $num_posts . '
- AND ' . $this->db->sql_in_set('user_id', $poster_ids);
- $this->db->sql_query($sql);
+ $sql_data[FORUMS_TABLE] .= 'forum_posts_softdeleted = forum_posts_softdeleted - 1, forum_topics_softdeleted = forum_topics_softdeleted - 1';
}
+
}
}
diff --git a/phpBB/phpbb/controller/exception.php b/phpBB/phpbb/controller/exception.php
index 06ece8d1d5..e227c7c37b 100644
--- a/phpBB/phpbb/controller/exception.php
+++ b/phpBB/phpbb/controller/exception.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package controller
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,7 @@ namespace phpbb\controller;
/**
* Controller exception class
-* @package phpBB3
*/
-class exception extends \RuntimeException
+class exception extends \phpbb\exception\runtime_exception
{
}
diff --git a/phpBB/phpbb/controller/helper.php b/phpBB/phpbb/controller/helper.php
index 05a05d1e57..e98de0e771 100644
--- a/phpBB/phpbb/controller/helper.php
+++ b/phpBB/phpbb/controller/helper.php
@@ -1,19 +1,24 @@
<?php
/**
*
-* @package controller
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\controller;
+use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Controller helper class, contains methods that do things for controllers
-* @package phpBB3
*/
class helper
{
@@ -35,47 +40,52 @@ class helper
*/
protected $config;
- /**
- * phpBB root path
- * @var string
- */
- protected $phpbb_root_path;
+ /* @var \phpbb\symfony_request */
+ protected $symfony_request;
+
+ /* @var \phpbb\request\request_interface */
+ protected $request;
/**
- * PHP extension
- * @var string
- */
- protected $php_ext;
+ * @var \phpbb\routing\helper
+ */
+ protected $routing_helper;
/**
* Constructor
*
* @param \phpbb\template\template $template Template object
- * @param \phpbb\user $user User object
- * @param \phpbb\config\config $config Config object
- * @param string $phpbb_root_path phpBB root path
- * @param string $php_ext PHP extension
+ * @param \phpbb\user $user User object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\symfony_request $symfony_request Symfony Request object
+ * @param \phpbb\request\request_interface $request phpBB request object
+ * @param \phpbb\routing\helper $routing_helper Helper to generate the routes
*/
- public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, $phpbb_root_path, $php_ext)
+ public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, \phpbb\symfony_request $symfony_request, \phpbb\request\request_interface $request, \phpbb\routing\helper $routing_helper)
{
$this->template = $template;
$this->user = $user;
$this->config = $config;
- $this->phpbb_root_path = $phpbb_root_path;
- $this->php_ext = $php_ext;
+ $this->symfony_request = $symfony_request;
+ $this->request = $request;
+ $this->routing_helper = $routing_helper;
}
/**
* Automate setting up the page and creating the response object.
*
- * @param string $handle The template handle to render
+ * @param string $template_file The template handle to render
* @param string $page_title The title of the page to output
* @param int $status_code The status code to be sent to the page header
+ * @param bool $display_online_list Do we display online users list
+ * @param int $item_id Restrict online users to item id
+ * @param string $item Restrict online users to a certain session item, e.g. forum for session_forum_id
+ *
* @return Response object containing rendered page
*/
- public function render($template_file, $page_title = '', $status_code = 200)
+ public function render($template_file, $page_title = '', $status_code = 200, $display_online_list = false, $item_id = 0, $item = 'forum')
{
- page_header($page_title);
+ page_header($page_title, $display_online_list, $item_id, $item);
$this->template->set_filenames(array(
'body' => $template_file,
@@ -87,47 +97,96 @@ class helper
}
/**
- * Generate a URL
+ * Generate a URL to a route
*
- * @param string $route The route to travel
- * @param mixed $params String or array of additional url parameters
+ * @param string $route Name of the route to travel
+ * @param array $params String or array of additional url parameters
* @param bool $is_amp Is url using &amp; (true) or & (false)
- * @param string $session_id Possibility to use a custom session id instead of the global one
+ * @param string|bool $session_id Possibility to use a custom session id instead of the global one
+ * @param bool|string $reference_type The type of reference to be generated (one of the constants)
* @return string The URL already passed through append_sid()
*/
- public function url($route, $params = false, $is_amp = true, $session_id = false)
+ public function route($route, array $params = array(), $is_amp = true, $session_id = false, $reference_type = UrlGeneratorInterface::ABSOLUTE_PATH)
{
- $route_params = '';
- if (($route_delim = strpos($route, '?')) !== false)
- {
- $route_params = substr($route, $route_delim);
- $route = substr($route, 0, $route_delim);
- }
-
- // If enable_mod_rewrite is false, we need to include app.php
- $route_prefix = $this->phpbb_root_path;
- if (empty($this->config['enable_mod_rewrite']))
- {
- $route_prefix .= 'app.' . $this->php_ext . '/';
- }
-
- return append_sid($route_prefix . "$route" . $route_params, $params, $is_amp, $session_id);
+ return $this->routing_helper->route($route, $params, $is_amp, $session_id, $reference_type);
}
/**
* Output an error, effectively the same thing as trigger_error
*
* @param string $message The error message
- * @param string $code The error code (e.g. 404, 500, 503, etc.)
- * @return Response A Reponse instance
+ * @param int $code The error code (e.g. 404, 500, 503, etc.)
+ * @return Response A Response instance
+ *
+ * @deprecated 3.1.3 (To be removed: 3.3.0) Use exceptions instead.
*/
public function error($message, $code = 500)
{
+ return $this->message($message, array(), 'INFORMATION', $code);
+ }
+
+ /**
+ * Output a message
+ *
+ * In case of an error, please throw an exception instead
+ *
+ * @param string $message The message to display (must be a language variable)
+ * @param array $parameters The parameters to use with the language var
+ * @param string $title Title for the message (must be a language variable)
+ * @param int $code The HTTP status code (e.g. 404, 500, 503, etc.)
+ * @return Response A Response instance
+ */
+ public function message($message, array $parameters = array(), $title = 'INFORMATION', $code = 200)
+ {
+ array_unshift($parameters, $message);
+ $message_text = call_user_func_array(array($this->user, 'lang'), $parameters);
+ $message_title = $this->user->lang($title);
+
+ if ($this->request->is_ajax())
+ {
+ global $refresh_data;
+
+ return new JsonResponse(
+ array(
+ 'MESSAGE_TITLE' => $message_title,
+ 'MESSAGE_TEXT' => $message_text,
+ 'S_USER_WARNING' => false,
+ 'S_USER_NOTICE' => false,
+ 'REFRESH_DATA' => (!empty($refresh_data)) ? $refresh_data : null
+ ),
+ $code
+ );
+ }
+
$this->template->assign_vars(array(
- 'MESSAGE_TEXT' => $message,
- 'MESSAGE_TITLE' => $this->user->lang('INFORMATION'),
+ 'MESSAGE_TEXT' => $message_text,
+ 'MESSAGE_TITLE' => $message_title,
));
- return $this->render('message_body.html', $this->user->lang('INFORMATION'), $code);
+ return $this->render('message_body.html', $message_title, $code);
+ }
+
+ /**
+ * Assigns automatic refresh time meta tag in template
+ *
+ * @param int $time time in seconds, when redirection should occur
+ * @param string $url the URL where the user should be redirected
+ * @return null
+ */
+ public function assign_meta_refresh_var($time, $url)
+ {
+ $this->template->assign_vars(array(
+ 'META' => '<meta http-equiv="refresh" content="' . $time . '; url=' . $url . '" />',
+ ));
+ }
+
+ /**
+ * Return the current url
+ *
+ * @return string
+ */
+ public function get_current_url()
+ {
+ return generate_board_url(true) . $this->request->escape($this->symfony_request->getRequestUri(), true);
}
}
diff --git a/phpBB/phpbb/controller/provider.php b/phpBB/phpbb/controller/provider.php
deleted file mode 100644
index fde51696e8..0000000000
--- a/phpBB/phpbb/controller/provider.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-/**
-*
-* @package controller
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\controller;
-
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Loader\YamlFileLoader;
-use Symfony\Component\Config\FileLocator;
-
-/**
-* Controller interface
-* @package phpBB3
-*/
-class provider
-{
- /**
- * YAML file(s) containing route information
- * @var array
- */
- protected $routing_files;
-
- /**
- * Construct method
- *
- * @param array() $routing_files Array of strings containing paths
- * to YAML files holding route information
- */
- public function __construct($routing_files = array())
- {
- $this->routing_files = $routing_files;
- }
-
- /**
- * Locate paths containing routing files
- * This sets an internal property but does not return the paths.
- *
- * @return The current instance of this object for method chaining
- */
- public function import_paths_from_finder(\phpbb\extension\finder $finder)
- {
- // We hardcode the path to the core config directory
- // because the finder cannot find it
- $this->routing_files = array_merge(array('config/routing.yml'), array_keys($finder
- ->directory('config')
- ->suffix('routing.yml')
- ->find()
- ));
-
- return $this;
- }
-
- /**
- * Get a list of controllers and return it
- *
- * @param string $base_path Base path to prepend to file paths
- * @return array Array of controllers and their route information
- */
- public function find($base_path = '')
- {
- $routes = new RouteCollection;
- foreach ($this->routing_files as $file_path)
- {
- $loader = new YamlFileLoader(new FileLocator($base_path));
- $routes->addCollection($loader->load($file_path));
- }
-
- return $routes;
- }
-}
diff --git a/phpBB/phpbb/controller/resolver.php b/phpBB/phpbb/controller/resolver.php
index 233179e343..4f432c3323 100644
--- a/phpBB/phpbb/controller/resolver.php
+++ b/phpBB/phpbb/controller/resolver.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package controller
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -15,17 +19,10 @@ use Symfony\Component\HttpFoundation\Request;
/**
* Controller manager class
-* @package phpBB3
*/
class resolver implements ControllerResolverInterface
{
/**
- * User object
- * @var \phpbb\user
- */
- protected $user;
-
- /**
* ContainerInterface object
* @var ContainerInterface
*/
@@ -33,28 +30,41 @@ class resolver implements ControllerResolverInterface
/**
* phpbb\template\template object
- * @var phpbb\template\template
+ * @var \phpbb\template\template
*/
protected $template;
/**
+ * Request type cast helper object
+ * @var \phpbb\request\type_cast_helper
+ */
+ protected $type_cast_helper;
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
* Construct method
*
- * @param \phpbb\user $user User Object
* @param ContainerInterface $container ContainerInterface object
+ * @param string $phpbb_root_path Relative path to phpBB root
* @param \phpbb\template\template $template
*/
- public function __construct(\phpbb\user $user, ContainerInterface $container, \phpbb\template\template $template = null)
+ public function __construct(ContainerInterface $container, $phpbb_root_path, \phpbb\template\template $template = null)
{
- $this->user = $user;
$this->container = $container;
$this->template = $template;
+ $this->type_cast_helper = new \phpbb\request\type_cast_helper();
+ $this->phpbb_root_path = $phpbb_root_path;
}
/**
* Load a controller callable
*
- * @param Symfony\Component\HttpFoundation\Request $request Symfony Request object
+ * @param \Symfony\Component\HttpFoundation\Request $request Symfony Request object
* @return bool|Callable Callable or false
* @throws \phpbb\controller\exception
*/
@@ -64,20 +74,20 @@ class resolver implements ControllerResolverInterface
if (!$controller)
{
- throw new \phpbb\controller\exception($this->user->lang['CONTROLLER_NOT_SPECIFIED']);
+ throw new \phpbb\controller\exception('CONTROLLER_NOT_SPECIFIED');
}
// Require a method name along with the service name
if (stripos($controller, ':') === false)
{
- throw new \phpbb\controller\exception($this->user->lang['CONTROLLER_METHOD_NOT_SPECIFIED']);
+ throw new \phpbb\controller\exception('CONTROLLER_METHOD_NOT_SPECIFIED');
}
list($service, $method) = explode(':', $controller);
if (!$this->container->has($service))
{
- throw new \phpbb\controller\exception($this->user->lang('CONTROLLER_SERVICE_UNDEFINED', $service));
+ throw new \phpbb\controller\exception('CONTROLLER_SERVICE_UNDEFINED', array($service));
}
$controller_object = $this->container->get($service);
@@ -94,7 +104,7 @@ class resolver implements ControllerResolverInterface
{
$controller_style_dir = 'ext/' . $controller_dir[0] . '/' . $controller_dir[1] . '/styles';
- if (is_dir($controller_style_dir))
+ if (is_dir($this->phpbb_root_path . $controller_style_dir))
{
$this->template->set_style(array($controller_style_dir, 'styles'));
}
@@ -109,9 +119,9 @@ class resolver implements ControllerResolverInterface
* and should match the parameters of the method you are using as your
* controller.
*
- * @param Symfony\Component\HttpFoundation\Request $request Symfony Request object
+ * @param \Symfony\Component\HttpFoundation\Request $request Symfony Request object
* @param mixed $controller A callable (controller class, method)
- * @return bool False
+ * @return array An array of arguments to pass to the controller
* @throws \phpbb\controller\exception
*/
public function getArguments(Request $request, $controller)
@@ -127,7 +137,16 @@ class resolver implements ControllerResolverInterface
{
if (array_key_exists($param->name, $attributes))
{
- $arguments[] = $attributes[$param->name];
+ if (is_string($attributes[$param->name]))
+ {
+ $value = $attributes[$param->name];
+ $this->type_cast_helper->set_var($value, $attributes[$param->name], 'string', true, false);
+ $arguments[] = $value;
+ }
+ else
+ {
+ $arguments[] = $attributes[$param->name];
+ }
}
else if ($param->getClass() && $param->getClass()->isInstance($request))
{
@@ -139,7 +158,7 @@ class resolver implements ControllerResolverInterface
}
else
{
- throw new \phpbb\controller\exception($this->user->lang('CONTROLLER_ARGUMENT_VALUE_MISSING', $param->getPosition() + 1, get_class($object) . ':' . $method, $param->name));
+ throw new \phpbb\controller\exception('CONTROLLER_ARGUMENT_VALUE_MISSING', array($param->getPosition() + 1, get_class($object) . ':' . $method, $param->name));
}
}
diff --git a/phpBB/phpbb/cron/manager.php b/phpBB/phpbb/cron/manager.php
index b6af07aff7..079ce8107e 100644
--- a/phpBB/phpbb/cron/manager.php
+++ b/phpBB/phpbb/cron/manager.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ namespace phpbb\cron;
* Cron manager class.
*
* Finds installed cron tasks, stores task objects, provides task selection.
-*
-* @package phpBB3
*/
class manager
{
@@ -32,7 +34,9 @@ class manager
/**
* Constructor. Loads all available tasks.
*
- * @param array|Traversable $tasks Provides an iterable set of task names
+ * @param array|\Traversable $tasks Provides an iterable set of task names
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $php_ext PHP file extension
*/
public function __construct($tasks, $phpbb_root_path, $php_ext)
{
@@ -46,7 +50,7 @@ class manager
* Loads tasks given by name, wraps them
* and puts them into $this->tasks.
*
- * @param array|Traversable $tasks Array of instances of \phpbb\cron\task\task
+ * @param array|\Traversable $tasks Array of instances of \phpbb\cron\task\task
*
* @return null
*/
@@ -69,6 +73,7 @@ class manager
*/
public function find_one_ready_task()
{
+ shuffle($this->tasks);
foreach ($this->tasks as $task)
{
if ($task->is_ready())
@@ -120,6 +125,16 @@ class manager
}
/**
+ * Find all tasks and return them.
+ *
+ * @return array List of all tasks.
+ */
+ public function get_tasks()
+ {
+ return $this->tasks;
+ }
+
+ /**
* Wraps a task inside an instance of \phpbb\cron\task\wrapper.
*
* @param \phpbb\cron\task\task $task The task.
diff --git a/phpBB/phpbb/cron/task/base.php b/phpBB/phpbb/cron/task/base.php
index 63f0407bcd..57c9912d31 100644
--- a/phpBB/phpbb/cron/task/base.php
+++ b/phpBB/phpbb/cron/task/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -17,8 +21,6 @@ namespace phpbb\cron\task;
*
* Cron tasks need not inherit from this base class. If desired,
* they may implement cron task interface directly.
-*
-* @package phpBB3
*/
abstract class base implements \phpbb\cron\task\task
{
diff --git a/phpBB/phpbb/cron/task/core/prune_all_forums.php b/phpBB/phpbb/cron/task/core/prune_all_forums.php
index 90b9a5914b..b47939ccbe 100644
--- a/phpBB/phpbb/cron/task/core/prune_all_forums.php
+++ b/phpBB/phpbb/cron/task/core/prune_all_forums.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -15,8 +19,6 @@ namespace phpbb\cron\task\core;
* It is intended to be invoked from system cron.
* This task will find all forums for which pruning is enabled, and will
* prune all forums as necessary.
-*
-* @package phpBB3
*/
class prune_all_forums extends \phpbb\cron\task\base
{
@@ -29,11 +31,11 @@ class prune_all_forums extends \phpbb\cron\task\base
* Constructor.
*
* @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
+ * @param string $php_ext The PHP file extension
* @param \phpbb\config\config $config The config
- * @param \phpbb\db\driver\driver $db The db connection
+ * @param \phpbb\db\driver\driver_interface $db The db connection
*/
- public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config, \phpbb\db\driver\driver $db)
+ public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
diff --git a/phpBB/phpbb/cron/task/core/prune_forum.php b/phpBB/phpbb/cron/task/core/prune_forum.php
index e0d8b067c5..abf91aee19 100644
--- a/phpBB/phpbb/cron/task/core/prune_forum.php
+++ b/phpBB/phpbb/cron/task/core/prune_forum.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -15,8 +19,6 @@ namespace phpbb\cron\task\core;
* It is intended to be used when cron is invoked via web.
* This task can decide whether it should be run using data obtained by viewforum
* code, without making additional database queries.
-*
-* @package phpBB3
*/
class prune_forum extends \phpbb\cron\task\base implements \phpbb\cron\task\parametrized
{
@@ -29,7 +31,7 @@ class prune_forum extends \phpbb\cron\task\base implements \phpbb\cron\task\para
* If $forum_data is given, it is assumed to contain necessary information
* about a single forum that is to be pruned.
*
- * If $forum_data is not given, forum id will be retrieved via request_var
+ * If $forum_data is not given, forum id will be retrieved via $request->variable()
* and a database query will be performed to load the necessary information
* about the forum.
*/
@@ -39,11 +41,11 @@ class prune_forum extends \phpbb\cron\task\base implements \phpbb\cron\task\para
* Constructor.
*
* @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
+ * @param string $php_ext PHP file extension
* @param \phpbb\config\config $config The config
- * @param \phpbb\db\driver\driver $db The db connection
+ * @param \phpbb\db\driver\driver_interface $db The db connection
*/
- public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config, \phpbb\db\driver\driver $db)
+ public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
diff --git a/phpBB/phpbb/cron/task/core/prune_notifications.php b/phpBB/phpbb/cron/task/core/prune_notifications.php
index 9f67c54e1c..ffa7e17970 100644
--- a/phpBB/phpbb/cron/task/core/prune_notifications.php
+++ b/phpBB/phpbb/cron/task/core/prune_notifications.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Prune notifications cron task.
-*
-* @package phpBB3
*/
class prune_notifications extends \phpbb\cron\task\base
{
diff --git a/phpBB/phpbb/cron/task/core/prune_shadow_topics.php b/phpBB/phpbb/cron/task/core/prune_shadow_topics.php
new file mode 100644
index 0000000000..0ab59f9ed5
--- /dev/null
+++ b/phpBB/phpbb/cron/task/core/prune_shadow_topics.php
@@ -0,0 +1,200 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\cron\task\core;
+
+/**
+* Prune one forum of its shadow topics cron task.
+*
+* It is intended to be used when cron is invoked via web.
+* This task can decide whether it should be run using data obtained by viewforum
+* code, without making additional database queries.
+*/
+class prune_shadow_topics extends \phpbb\cron\task\base implements \phpbb\cron\task\parametrized
+{
+ protected $phpbb_root_path;
+ protected $php_ext;
+ protected $config;
+ protected $db;
+ protected $log;
+ protected $user;
+
+ /**
+ * If $forum_data is given, it is assumed to contain necessary information
+ * about a single forum that is to be pruned.
+ *
+ * If $forum_data is not given, forum id will be retrieved via $request->variable()
+ * and a database query will be performed to load the necessary information
+ * about the forum.
+ */
+ protected $forum_data;
+
+ /**
+ * Constructor.
+ *
+ * @param string $phpbb_root_path The root path
+ * @param string $php_ext PHP file extension
+ * @param \phpbb\config\config $config The config
+ * @param \phpbb\db\driver\driver_interface $db The db connection
+ * @param \phpbb\log\log $log The phpBB log system
+ * @param \phpbb\user $user The phpBB user object
+ */
+ public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\log\log $log, \phpbb\user $user)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->config = $config;
+ $this->db = $db;
+ $this->log = $log;
+ $this->user = $user;
+ }
+
+ /**
+ * Manually set forum data.
+ *
+ * @param array $forum_data Information about a forum to be pruned.
+ */
+ public function set_forum_data($forum_data)
+ {
+ $this->forum_data = $forum_data;
+ }
+
+ /**
+ * Runs this cron task.
+ *
+ * @return null
+ */
+ public function run()
+ {
+ if (!function_exists('auto_prune'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_admin.' . $this->php_ext);
+ }
+
+ if ($this->forum_data['prune_shadow_days'])
+ {
+ $this->auto_prune_shadow_topics($this->forum_data['forum_id'], 'shadow', $this->forum_data['forum_flags'], $this->forum_data['prune_shadow_days'], $this->forum_data['prune_shadow_freq']);
+ }
+ }
+
+ /**
+ * Returns whether this cron task can run, given current board configuration.
+ *
+ * This cron task will not run when system cron is utilised, as in
+ * such cases prune_all_forums task would run instead.
+ *
+ * Additionally, this task must be given the forum data, either via
+ * the constructor or parse_parameters method.
+ *
+ * @return bool
+ */
+ public function is_runnable()
+ {
+ return !$this->config['use_system_cron'] && $this->forum_data;
+ }
+
+ /**
+ * Returns whether this cron task should run now, because enough time
+ * has passed since it was last run.
+ *
+ * Forum pruning interval is specified in the forum data.
+ *
+ * @return bool
+ */
+ public function should_run()
+ {
+ return $this->forum_data['enable_shadow_prune'] && $this->forum_data['prune_shadow_next'] < time();
+ }
+
+ /**
+ * Returns parameters of this cron task as an array.
+ * The array has one key, f, whose value is id of the forum to be pruned.
+ *
+ * @return array
+ */
+ public function get_parameters()
+ {
+ return array('f' => $this->forum_data['forum_id']);
+ }
+
+ /**
+ * Parses parameters found in $request, which is an instance of
+ * \phpbb\request\request_interface.
+ *
+ * It is expected to have a key f whose value is id of the forum to be pruned.
+ *
+ * @param \phpbb\request\request_interface $request Request object.
+ *
+ * @return null
+ */
+ public function parse_parameters(\phpbb\request\request_interface $request)
+ {
+ $this->forum_data = null;
+ if ($request->is_set('f'))
+ {
+ $forum_id = $request->variable('f', 0);
+
+ $sql = 'SELECT forum_id, prune_shadow_next, enable_shadow_prune, prune_shadow_days, forum_flags, prune_shadow_freq
+ FROM ' . FORUMS_TABLE . "
+ WHERE forum_id = $forum_id";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $this->forum_data = $row;
+ }
+ }
+ }
+
+ /**
+ * Automatically prune shadow topics
+ * Based on fuunction auto_prune()
+ * @param int $forum_id Forum ID of forum that should be pruned
+ * @param string $prune_mode Prune mode
+ * @param int $prune_flags Prune flags
+ * @param int $prune_days Prune date in days
+ * @param int $prune_freq Prune frequency
+ * @return null
+ */
+ protected function auto_prune_shadow_topics($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq)
+ {
+ $sql = 'SELECT forum_name
+ FROM ' . FORUMS_TABLE . "
+ WHERE forum_id = $forum_id";
+ $result = $this->db->sql_query($sql, 3600);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $prune_date = time() - ($prune_days * 86400);
+ $next_prune = time() + ($prune_freq * 86400);
+
+ prune($forum_id, $prune_mode, $prune_date, $prune_flags, true);
+
+ $sql = 'UPDATE ' . FORUMS_TABLE . "
+ SET prune_shadow_next = $next_prune
+ WHERE forum_id = $forum_id";
+ $this->db->sql_query($sql);
+
+ $user_id = (empty($this->user->data)) ? ANONYMOUS : $this->user->data['user_id'];
+ $user_ip = (empty($this->user->ip)) ? '' : $this->user->ip;
+
+ $this->log->add('admin', $user_id, $user_ip, 'LOG_PRUNE_SHADOW', false, array($row['forum_name']));
+ }
+
+ return;
+ }
+}
diff --git a/phpBB/phpbb/cron/task/core/queue.php b/phpBB/phpbb/cron/task/core/queue.php
index cd799b8024..a9345a44df 100644
--- a/phpBB/phpbb/cron/task/core/queue.php
+++ b/phpBB/phpbb/cron/task/core/queue.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Queue cron task. Sends email and jabber messages queued by other scripts.
-*
-* @package phpBB3
*/
class queue extends \phpbb\cron\task\base
{
@@ -24,7 +26,7 @@ class queue extends \phpbb\cron\task\base
* Constructor.
*
* @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
+ * @param string $php_ext PHP file extension
* @param \phpbb\config\config $config The config
*/
public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config)
@@ -71,6 +73,6 @@ class queue extends \phpbb\cron\task\base
*/
public function should_run()
{
- return $this->config['last_queue_run'] < time() - $this->config['queue_interval_config'];
+ return $this->config['last_queue_run'] < time() - $this->config['queue_interval'];
}
}
diff --git a/phpBB/phpbb/cron/task/core/tidy_cache.php b/phpBB/phpbb/cron/task/core/tidy_cache.php
index a94a85db53..506a245f0f 100644
--- a/phpBB/phpbb/cron/task/core/tidy_cache.php
+++ b/phpBB/phpbb/cron/task/core/tidy_cache.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Tidy cache cron task.
-*
-* @package phpBB3
*/
class tidy_cache extends \phpbb\cron\task\base
{
diff --git a/phpBB/phpbb/cron/task/core/tidy_database.php b/phpBB/phpbb/cron/task/core/tidy_database.php
index f712a5047c..949bba8012 100644
--- a/phpBB/phpbb/cron/task/core/tidy_database.php
+++ b/phpBB/phpbb/cron/task/core/tidy_database.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Tidy database cron task.
-*
-* @package phpBB3
*/
class tidy_database extends \phpbb\cron\task\base
{
@@ -24,7 +26,7 @@ class tidy_database extends \phpbb\cron\task\base
* Constructor.
*
* @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
+ * @param string $php_ext The PHP file extension
* @param \phpbb\config\config $config The config
*/
public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config)
diff --git a/phpBB/phpbb/cron/task/core/tidy_plupload.php b/phpBB/phpbb/cron/task/core/tidy_plupload.php
index 5a98e0bd7b..d7364374af 100644
--- a/phpBB/phpbb/cron/task/core/tidy_plupload.php
+++ b/phpBB/phpbb/cron/task/core/tidy_plupload.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Cron task for cleaning plupload's temporary upload directory.
-*
-* @package phpBB3
*/
class tidy_plupload extends \phpbb\cron\task\base
{
@@ -65,6 +67,8 @@ class tidy_plupload extends \phpbb\cron\task\base
*/
public function run()
{
+ global $user, $phpbb_log;
+
// Remove old temporary file (perhaps failed uploads?)
$last_valid_timestamp = time() - $this->max_file_age;
try
@@ -86,13 +90,11 @@ class tidy_plupload extends \phpbb\cron\task\base
}
catch (\UnexpectedValueException $e)
{
- add_log(
- 'critical',
- 'LOG_PLUPLOAD_TIDY_FAILED',
+ $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_PLUPLOAD_TIDY_FAILED', false, array(
$this->plupload_upload_path,
$e->getMessage(),
$e->getTraceAsString()
- );
+ ));
}
$this->config->set('plupload_last_gc', time(), true);
diff --git a/phpBB/phpbb/cron/task/core/tidy_search.php b/phpBB/phpbb/cron/task/core/tidy_search.php
index 42f7df308f..eb3970254f 100644
--- a/phpBB/phpbb/cron/task/core/tidy_search.php
+++ b/phpBB/phpbb/cron/task/core/tidy_search.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,29 +17,63 @@ namespace phpbb\cron\task\core;
* Tidy search cron task.
*
* Will only run when the currently selected search backend supports tidying.
-*
-* @package phpBB3
*/
class tidy_search extends \phpbb\cron\task\base
{
+ /**
+ * phpBB root path
+ * @var string
+ */
protected $phpbb_root_path;
+
+ /**
+ * PHP file extension
+ * @var string
+ */
protected $php_ext;
+
+ /**
+ * Auth object
+ * @var \phpbb\auth\auth
+ */
protected $auth;
+
+ /**
+ * Config object
+ * @var \phpbb\config\config
+ */
protected $config;
+
+ /**
+ * Database object
+ * @var \phpbb\db\driver\driver_interface
+ */
protected $db;
+
+ /**
+ * User object
+ * @var \phpbb\user
+ */
protected $user;
/**
+ * Event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* Constructor.
*
- * @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
- * @param \phpbb\auth\auth $auth The auth
- * @param \phpbb\config\config $config The config
- * @param \phpbb\db\driver\driver $db The db connection
- * @param \phpbb\user $user The user
+ * @param string $phpbb_root_path The phpBB root path
+ * @param string $php_ext The PHP file extension
+ * @param \phpbb\auth\auth $auth The auth object
+ * @param \phpbb\config\config $config The config object
+ * @param \phpbb\db\driver\driver_interface $db The database object
+ * @param \phpbb\user $user The user object
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher The event dispatcher object
*/
- public function __construct($phpbb_root_path, $php_ext, \phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\db\driver\driver $db, \phpbb\user $user)
+ public function __construct($phpbb_root_path, $php_ext, \phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, \phpbb\event\dispatcher_interface $phpbb_dispatcher)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
@@ -43,6 +81,7 @@ class tidy_search extends \phpbb\cron\task\base
$this->config = $config;
$this->db = $db;
$this->user = $user;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
}
/**
@@ -52,12 +91,11 @@ class tidy_search extends \phpbb\cron\task\base
*/
public function run()
{
- // Select the search method
- $search_type = basename($this->config['search_type']);
+ $search_type = $this->config['search_type'];
// We do some additional checks in the module to ensure it can actually be utilised
$error = false;
- $search = new $search_type($error, $this->phpbb_root_path, $this->php_ext, $this->auth, $this->config, $this->db, $this->user);
+ $search = new $search_type($error, $this->phpbb_root_path, $this->php_ext, $this->auth, $this->config, $this->db, $this->user, $this->phpbb_dispatcher);
if (!$error)
{
@@ -76,10 +114,7 @@ class tidy_search extends \phpbb\cron\task\base
*/
public function is_runnable()
{
- // Select the search method
- $search_type = basename($this->config['search_type']);
-
- return class_exists($search_type);
+ return class_exists($this->config['search_type']);
}
/**
diff --git a/phpBB/phpbb/cron/task/core/tidy_sessions.php b/phpBB/phpbb/cron/task/core/tidy_sessions.php
index 68094af1f7..5e6dabdabf 100644
--- a/phpBB/phpbb/cron/task/core/tidy_sessions.php
+++ b/phpBB/phpbb/cron/task/core/tidy_sessions.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\cron\task\core;
/**
* Tidy sessions cron task.
-*
-* @package phpBB3
*/
class tidy_sessions extends \phpbb\cron\task\base
{
diff --git a/phpBB/phpbb/cron/task/core/tidy_warnings.php b/phpBB/phpbb/cron/task/core/tidy_warnings.php
index a0ff23fc57..7b67eae6ef 100644
--- a/phpBB/phpbb/cron/task/core/tidy_warnings.php
+++ b/phpBB/phpbb/cron/task/core/tidy_warnings.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ namespace phpbb\cron\task\core;
* Tidy warnings cron task.
*
* Will only run when warnings are configured to expire.
-*
-* @package phpBB3
*/
class tidy_warnings extends \phpbb\cron\task\base
{
@@ -26,7 +28,7 @@ class tidy_warnings extends \phpbb\cron\task\base
* Constructor.
*
* @param string $phpbb_root_path The root path
- * @param string $php_ext The PHP extension
+ * @param string $php_ext PHP file extension
* @param \phpbb\config\config $config The config
*/
public function __construct($phpbb_root_path, $php_ext, \phpbb\config\config $config)
diff --git a/phpBB/phpbb/cron/task/parametrized.php b/phpBB/phpbb/cron/task/parametrized.php
index 1aeead0399..7e190b9b86 100644
--- a/phpBB/phpbb/cron/task/parametrized.php
+++ b/phpBB/phpbb/cron/task/parametrized.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -17,8 +21,6 @@ namespace phpbb\cron\task;
* parametrized cron tasks perform actions on a particular object (or objects).
* Parametrized cron tasks do not make sense and are not usable without
* specifying these objects.
-*
-* @package phpBB3
*/
interface parametrized extends \phpbb\cron\task\task
{
diff --git a/phpBB/phpbb/cron/task/task.php b/phpBB/phpbb/cron/task/task.php
index 3ce3de9598..6d5a383d2d 100644
--- a/phpBB/phpbb/cron/task/task.php
+++ b/phpBB/phpbb/cron/task/task.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\cron\task;
/**
* Cron task interface
-* @package phpBB3
*/
interface task
{
diff --git a/phpBB/phpbb/cron/task/text_reparser/reparser.php b/phpBB/phpbb/cron/task/text_reparser/reparser.php
new file mode 100644
index 0000000000..aa644de827
--- /dev/null
+++ b/phpBB/phpbb/cron/task/text_reparser/reparser.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\cron\task\text_reparser;
+
+/**
+ * Reparse text cron task
+ */
+class reparser extends \phpbb\cron\task\base
+{
+ const MIN = 1;
+ const SIZE = 100;
+
+ /**
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\config\db_text
+ */
+ protected $config_text;
+
+ /**
+ * @var \phpbb\lock\db
+ */
+ protected $reparse_lock;
+
+ /**
+ * @var \phpbb\textreparser\manager
+ */
+ protected $reparser_manager;
+
+ /**
+ * @var string
+ */
+ protected $reparser_name;
+
+ /**
+ * @var \phpbb\di\service_collection
+ */
+ protected $reparsers;
+
+ /**
+ * @var array
+ */
+ protected $resume_data;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\config $config
+ * @param \phpbb\config\db_text $config_text
+ * @param \phpbb\lock\db $reparse_lock
+ * @param \phpbb\textreparser\manager $reparser_manager
+ * @param \phpbb\di\service_collection $reparsers
+ */
+ public function __construct(\phpbb\config\config $config, \phpbb\config\db_text $config_text, \phpbb\lock\db $reparse_lock, \phpbb\textreparser\manager $reparser_manager, \phpbb\di\service_collection $reparsers)
+ {
+ $this->config = $config;
+ $this->config_text = $config_text;
+ $this->reparse_lock = $reparse_lock;
+ $this->reparser_manager = $reparser_manager;
+ $this->reparsers = $reparsers;
+ }
+
+ /**
+ * Sets the reparser for this cron task
+ *
+ * @param string $reparser
+ */
+ public function set_reparser($reparser)
+ {
+ $this->reparser_name = (!isset($this->reparsers[$reparser]) ? 'text_reparser.' : '') . $reparser;
+
+ if ($this->resume_data === null)
+ {
+ $this->reparser_manager->get_resume_data($this->reparser_name);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_runnable()
+ {
+ if ($this->resume_data === null)
+ {
+ $this->reparser_manager->get_resume_data($this->reparser_name);
+ }
+
+ if (empty($this->resume_data['range-max']) || $this->resume_data['range-max'] >= $this->resume_data['range-min'])
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function should_run()
+ {
+ if (!empty($this->config['reparse_lock']))
+ {
+ $last_run = explode(' ', $this->config['reparse_lock']);
+
+ if ($last_run[0] + 3600 >= time())
+ {
+ return false;
+ }
+ }
+
+ if ($this->config[$this->reparser_name . '_cron_interval'])
+ {
+ return $this->config[$this->reparser_name . '_last_cron'] < time() - $this->config[$this->reparser_name . '_cron_interval'];
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ if ($this->reparse_lock->acquire())
+ {
+ if ($this->resume_data === null)
+ {
+ $this->resume_data = $this->reparser_manager->get_resume_data($this->reparser_name);
+ }
+
+ /**
+ * @var \phpbb\textreparser\reparser_interface $reparser
+ */
+ $reparser = $this->reparsers[$this->reparser_name];
+
+ $min = !empty($this->resume_data['range-min']) ? $this->resume_data['range-min'] : self::MIN;
+ $current = !empty($this->resume_data['range-max']) ? $this->resume_data['range-max'] : $reparser->get_max_id();
+ $size = !empty($this->resume_data['range-size']) ? $this->resume_data['range-size'] : self::SIZE;
+
+ if ($current >= $min)
+ {
+ $start = max($min, $current + 1 - $size);
+ $end = max($min, $current);
+
+ $reparser->reparse_range($start, $end);
+
+ $this->reparser_manager->update_resume_data($this->reparser_name, $min, $start - 1, $size);
+ }
+
+ $this->config->set($this->reparser_name . '_last_cron', time());
+ $this->reparse_lock->release();
+ }
+ }
+}
diff --git a/phpBB/phpbb/cron/task/wrapper.php b/phpBB/phpbb/cron/task/wrapper.php
index fc3f897206..8a4a8b1f0c 100644
--- a/phpBB/phpbb/cron/task/wrapper.php
+++ b/phpBB/phpbb/cron/task/wrapper.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,8 +16,6 @@ namespace phpbb\cron\task;
/**
* Cron task wrapper class.
* Enhances cron tasks with convenience methods that work identically for all tasks.
-*
-* @package phpBB3
*/
class wrapper
{
@@ -27,6 +29,8 @@ class wrapper
* Wraps a task $task, which must implement cron_task interface.
*
* @param \phpbb\cron\task\task $task The cron task to wrap.
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $php_ext PHP file extension
*/
public function __construct(\phpbb\cron\task\task $task, $phpbb_root_path, $php_ext)
{
diff --git a/phpBB/phpbb/datetime.php b/phpBB/phpbb/datetime.php
index dfa21976e0..63cdba90fd 100644
--- a/phpBB/phpbb/datetime.php
+++ b/phpBB/phpbb/datetime.php
@@ -1,9 +1,14 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
*/
namespace phpbb;
@@ -33,9 +38,9 @@ class datetime extends \DateTime
* Constructs a new instance of \phpbb\datetime, expanded to include an argument to inject
* the user context and modify the timezone to the users selected timezone if one is not set.
*
+ * @param user $user object for context.
* @param string $time String in a format accepted by strtotime().
- * @param DateTimeZone $timezone Time zone of the time.
- * @param user User object for context.
+ * @param \DateTimeZone $timezone Time zone of the time.
*/
public function __construct($user, $time = 'now', \DateTimeZone $timezone = null)
{
@@ -86,25 +91,28 @@ class datetime extends \DateTime
$midnight = $midnight->getTimestamp();
- $day = false;
-
- if ($timestamp > $midnight + 86400)
- {
- $day = 'TOMORROW';
- }
- else if ($timestamp > $midnight)
- {
- $day = 'TODAY';
- }
- else if ($timestamp > $midnight - 86400)
- {
- $day = 'YESTERDAY';
- }
-
- if ($day !== false)
+ if ($timestamp <= $midnight + 2 * 86400)
{
- // Format using the short formatting and finally swap out the relative token placeholder with the correct value
- return str_replace(self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER, $this->user->lang['datetime'][$day], strtr(parent::format($format['format_short']), $format['lang']));
+ $day = false;
+
+ if ($timestamp > $midnight + 86400)
+ {
+ $day = 'TOMORROW';
+ }
+ else if ($timestamp > $midnight)
+ {
+ $day = 'TODAY';
+ }
+ else if ($timestamp > $midnight - 86400)
+ {
+ $day = 'YESTERDAY';
+ }
+
+ if ($day !== false)
+ {
+ // Format using the short formatting and finally swap out the relative token placeholder with the correct value
+ return str_replace(self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER, $this->user->lang['datetime'][$day], strtr(parent::format($format['format_short']), $format['lang']));
+ }
}
}
}
@@ -115,7 +123,7 @@ class datetime extends \DateTime
/**
* Magic method to convert DateTime object to string
*
- * @return Formatted date time, according to the users default settings.
+ * @return string Formatted date time, according to the users default settings.
*/
public function __toString()
{
diff --git a/phpBB/phpbb/db/driver/driver.php b/phpBB/phpbb/db/driver/driver.php
index d721ed2eb7..30cb667344 100644
--- a/phpBB/phpbb/db/driver/driver.php
+++ b/phpBB/phpbb/db/driver/driver.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,9 +15,8 @@ namespace phpbb\db\driver;
/**
* Database Abstraction Layer
-* @package dbal
*/
-class driver
+abstract class driver implements driver_interface
{
var $db_connect_id;
var $query_result;
@@ -63,6 +66,15 @@ class driver
*/
var $sql_server_version = false;
+ const LOGICAL_OP = 0;
+ const STATEMENTS = 1;
+ const LEFT_STMT = 0;
+ const COMPARE_OP = 1;
+ const RIGHT_STMT = 2;
+ const SUBQUERY_OP = 3;
+ const SUBQUERY_SELECT_TYPE = 4;
+ const SUBQUERY_BUILD = 5;
+
/**
* Constructor
*/
@@ -84,7 +96,103 @@ class driver
}
/**
- * return on error or display error message
+ * {@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)
{
@@ -95,7 +203,7 @@ class driver
}
/**
- * Return number of sql queries and cached sql queries used
+ * {@inheritDoc}
*/
function sql_num_queries($cached = false)
{
@@ -103,7 +211,7 @@ class driver
}
/**
- * Add to query count
+ * {@inheritDoc}
*/
function sql_add_num_queries($cached = false)
{
@@ -113,7 +221,7 @@ class driver
}
/**
- * DBAL garbage collection, close sql connection
+ * {@inheritDoc}
*/
function sql_close()
{
@@ -146,8 +254,7 @@ class driver
}
/**
- * Build LIMIT query
- * Doing some validation here.
+ * {@inheritDoc}
*/
function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
{
@@ -164,7 +271,7 @@ class driver
}
/**
- * Fetch all rows
+ * {@inheritDoc}
*/
function sql_fetchrowset($query_id = false)
{
@@ -173,7 +280,7 @@ class driver
$query_id = $this->query_result;
}
- if ($query_id !== false)
+ if ($query_id)
{
$result = array();
while ($row = $this->sql_fetchrow($query_id))
@@ -188,8 +295,7 @@ class driver
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -205,7 +311,7 @@ class driver
return $cache->sql_rowseek($rownum, $query_id);
}
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
@@ -213,7 +319,7 @@ class driver
$this->sql_freeresult($query_id);
$query_id = $this->sql_query($this->last_query_text);
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
@@ -231,8 +337,7 @@ class driver
}
/**
- * Fetch field
- * if rownum is false, the current row is used, else it is pointing to the row (zero-based)
+ * {@inheritDoc}
*/
function sql_fetchfield($field, $rownum = false, $query_id = false)
{
@@ -243,7 +348,7 @@ class driver
$query_id = $this->query_result;
}
- if ($query_id !== false)
+ if ($query_id)
{
if ($rownum !== false)
{
@@ -263,29 +368,29 @@ class driver
}
/**
- * 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 LIKE expression including the keyword!
+ * {@inheritDoc}
*/
function sql_like_expression($expression)
{
- $expression = utf8_str_replace(array('_', '%'), array("\_", "\%"), $expression);
- $expression = utf8_str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
+ $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = str_replace(array(chr(0) . "\_", chr(0) . "\%"), array('_', '%'), $expression);
return $this->_sql_like_expression('LIKE \'' . $this->sql_escape($expression) . '\'');
}
/**
- * 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 string $action_else SQL expression that is used, if the condition is false, optional
- * @return string CASE expression including the condition and statements
+ * {@inheritDoc}
+ */
+ function sql_not_like_expression($expression)
+ {
+ $expression = str_replace(array('_', '%'), array("\_", "\%"), $expression);
+ $expression = 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)
{
@@ -297,11 +402,7 @@ class driver
}
/**
- * 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
+ * {@inheritDoc}
*/
public function sql_concatenate($expr1, $expr2)
{
@@ -309,9 +410,7 @@ class driver
}
/**
- * Returns whether results of a query need to be buffered to run a transaction while iterating over them.
- *
- * @return bool Whether buffering is required.
+ * {@inheritDoc}
*/
function sql_buffer_nested_transactions()
{
@@ -319,15 +418,14 @@ class driver
}
/**
- * SQL Transaction
- * @access private
+ * {@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 (prevening auto commit)
+ // 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++;
@@ -345,14 +443,16 @@ class driver
break;
case 'commit':
- // If there was a previously opened transaction we do not commit yet... but count back the number of inner transactions
+ // 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)
+ // 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)
{
@@ -385,11 +485,7 @@ class driver
}
/**
- * Build sql statement from array for insert/update/select statements
- *
- * Idea for this from Ikonboard
- * Possible query values: INSERT, INSERT_SELECT, UPDATE, SELECT
- *
+ * {@inheritDoc}
*/
function sql_build_array($query, $assoc_ary = false)
{
@@ -423,7 +519,7 @@ class driver
{
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')
+ else if ($query == 'UPDATE' || $query == 'SELECT' || $query == 'DELETE')
{
$values = array();
foreach ($assoc_ary as $key => $var)
@@ -437,14 +533,7 @@ class driver
}
/**
- * Build IN or NOT IN sql comparison string, uses <> or = on single element
- * arrays to improve comparison speed
- *
- * @access public
- * @param string $field name of the sql column that shall be compared
- * @param array $array array of values that are allowed (IN) or not allowed (NOT IN)
- * @param bool $negate true for NOT IN (), false for IN () (default)
- * @param bool $allow_empty_set If true, allow $array to be empty, this function will return 1=1 or 1=0 then. Default to false.
+ * {@inheritDoc}
*/
function sql_in_set($field, $array, $negate = false, $allow_empty_set = false)
{
@@ -489,12 +578,7 @@ class driver
}
/**
- * 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")
+ * {@inheritDoc}
*/
function sql_bit_and($column_name, $bit, $compare = '')
{
@@ -507,12 +591,7 @@ class driver
}
/**
- * Run binary OR 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 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 (for example "= 0")
+ * {@inheritDoc}
*/
function sql_bit_or($column_name, $bit, $compare = '')
{
@@ -525,10 +604,7 @@ class driver
}
/**
- * 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
+ * {@inheritDoc}
*/
function cast_expr_to_bigint($expression)
{
@@ -536,10 +612,7 @@ class driver
}
/**
- * 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
+ * {@inheritDoc}
*/
function cast_expr_to_string($expression)
{
@@ -547,11 +620,7 @@ class driver
}
/**
- * 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)"
+ * {@inheritDoc}
*/
function sql_lower_text($column_name)
{
@@ -559,13 +628,7 @@ class driver
}
/**
- * 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.
- * @access public
+ * {@inheritDoc}
*/
function sql_multi_insert($table, $sql_ary)
{
@@ -637,9 +700,7 @@ class driver
}
/**
- * Build sql statement from array for select and select distinct statements
- *
- * Possible query values: SELECT, SELECT_DISTINCT
+ * {@inheritDoc}
*/
function sql_build_query($query, $array)
{
@@ -722,7 +783,18 @@ class driver
if (!empty($array['WHERE']))
{
- $sql .= ' WHERE ' . $this->_sql_custom_build('WHERE', $array['WHERE']);
+ $sql .= ' WHERE ';
+
+ if (is_array($array['WHERE']))
+ {
+ $sql_where = $this->_process_boolean_tree_first($array['WHERE']);
+ }
+ else
+ {
+ $sql_where = $array['WHERE'];
+ }
+
+ $sql .= $this->_sql_custom_build('WHERE', $sql_where);
}
if (!empty($array['GROUP_BY']))
@@ -741,8 +813,132 @@ class driver
return $sql;
}
+
+ protected function _process_boolean_tree_first($operations_ary)
+ {
+ // In cases where an array exists but there is no head condition,
+ // it should be because there's only 1 WHERE clause. This seems the best way to deal with it.
+ if ($operations_ary[self::LOGICAL_OP] !== 'AND' &&
+ $operations_ary[self::LOGICAL_OP] !== 'OR')
+ {
+ $operations_ary = array('AND', array($operations_ary));
+ }
+ return $this->_process_boolean_tree($operations_ary) . "\n";
+ }
+
+ protected function _process_boolean_tree($operations_ary)
+ {
+ $operation = $operations_ary[self::LOGICAL_OP];
+
+ foreach ($operations_ary[self::STATEMENTS] as &$condition)
+ {
+ switch ($condition[self::LOGICAL_OP])
+ {
+ case 'AND':
+ case 'OR':
+
+ $condition = ' ( ' . $this->_process_boolean_tree($condition) . ') ';
+
+ break;
+ case 'NOT':
+
+ $condition = ' NOT (' . $this->_process_boolean_tree($condition) . ') ';
+
+ break;
+
+ default:
+
+ switch (sizeof($condition))
+ {
+ case 3:
+
+ // Typical 3 element clause with {left hand} {operator} {right hand}
+ switch ($condition[self::COMPARE_OP])
+ {
+ case 'IN':
+ case 'NOT_IN':
+
+ // As this is used with an IN, assume it is a set of elements for sql_in_set()
+ $condition = $this->sql_in_set($condition[self::LEFT_STMT], $condition[self::RIGHT_STMT], $condition[self::COMPARE_OP] === 'NOT_IN', true);
+
+ break;
+
+ case 'LIKE':
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_like_expression($condition[self::RIGHT_STMT]) . ' ';
+
+ break;
+
+ case 'NOT_LIKE':
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $this->sql_not_like_expression($condition[self::RIGHT_STMT]) . ' ';
+
+ break;
+
+ case 'IS_NOT':
+
+ $condition[self::COMPARE_OP] = 'IS NOT';
+
+ // no break
+ case 'IS':
+
+ // If the value is NULL, the string of it is the empty string ('') which is not the intended result.
+ // this should solve that
+ if ($condition[self::RIGHT_STMT] === null)
+ {
+ $condition[self::RIGHT_STMT] = 'NULL';
+ }
+
+ $condition = implode(' ', $condition);
+
+ break;
+
+ default:
+
+ $condition = implode(' ', $condition);
+
+ break;
+ }
+
+ break;
+
+ case 5:
+
+ // Subquery with {left hand} {operator} {compare kind} {SELECT Kind } {Sub Query}
+
+ $condition = $condition[self::LEFT_STMT] . ' ' . $condition[self::COMPARE_OP] . ' ' . $condition[self::SUBQUERY_OP] . ' ( ';
+ $condition .= $this->sql_build_query($condition[self::SUBQUERY_SELECT_TYPE], $condition[self::SUBQUERY_BUILD]);
+ $condition .= ' )';
+
+ break;
+
+ default:
+ // This is an unpredicted clause setup. Just join all elements.
+ $condition = implode(' ', $condition);
+
+ break;
+ }
+
+ break;
+ }
+
+ }
+
+ if ($operation === 'NOT')
+ {
+ $operations_ary = implode("", $operations_ary[self::STATEMENTS]);
+ }
+ else
+ {
+ $operations_ary = implode(" \n $operation ", $operations_ary[self::STATEMENTS]);
+ }
+
+ return $operations_ary;
+ }
+
+
/**
- * display sql error page
+ * {@inheritDoc}
*/
function sql_error($sql = '')
{
@@ -812,11 +1008,11 @@ class driver
}
/**
- * Explain queries
+ * {@inheritDoc}
*/
function sql_report($mode, $query = '')
{
- global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper, $user;
+ global $cache, $starttime, $phpbb_root_path, $phpbb_path_helper;
global $request;
if (is_object($request) && !$request->variable('explain', false))
@@ -872,7 +1068,7 @@ class driver
</div>
</div>
<div id="page-footer">
- Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Group
+ Powered by <a href="https://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Limited
</div>
</div>
</body>
@@ -910,7 +1106,7 @@ class driver
{
if (preg_match('/^(UPDATE|DELETE|REPLACE)/', $query))
{
- $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows($this->query_result) . '</b> | ';
+ $this->sql_report .= 'Affected rows: <b>' . $this->sql_affectedrows() . '</b> | ';
}
$this->sql_report .= 'Before: ' . sprintf('%.5f', $this->curtime - $starttime) . 's | After: ' . sprintf('%.5f', $endtime - $starttime) . 's | Elapsed: <b>' . sprintf('%.5f', $endtime - $this->curtime) . 's</b>';
}
@@ -1002,14 +1198,7 @@ class driver
}
/**
- * 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).
- *
- * @access public
+ * {@inheritDoc}
*/
function get_estimated_row_count($table_name)
{
@@ -1017,13 +1206,7 @@ class driver
}
/**
- * 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.
- *
- * @access public
+ * {@inheritDoc}
*/
function get_row_count($table_name)
{
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/firebird.php b/phpBB/phpbb/db/driver/firebird.php
deleted file mode 100644
index ed56a5d154..0000000000
--- a/phpBB/phpbb/db/driver/firebird.php
+++ /dev/null
@@ -1,532 +0,0 @@
-<?php
-/**
-*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\db\driver;
-
-/**
-* Firebird/Interbase Database Abstraction Layer
-* Minimum Requirement is Firebird 2.1
-* @package dbal
-*/
-class firebird extends \phpbb\db\driver\driver
-{
- var $last_query_text = '';
- var $service_handle = false;
- var $affected_rows = 0;
- var $connect_error = '';
-
- /**
- * Connect to server
- */
- 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 = str_replace('\\', '/', $database);
-
- // There are three possibilities to connect to an interbase db
- if (!$this->server)
- {
- $use_database = $this->dbname;
- }
- else if (strpos($this->server, '//') === 0)
- {
- $use_database = $this->server . $this->dbname;
- }
- else
- {
- $use_database = $this->server . ':' . $this->dbname;
- }
-
- if ($this->persistency)
- {
- if (!function_exists('ibase_pconnect'))
- {
- $this->connect_error = 'ibase_pconnect function does not exist, is interbase extension installed?';
- return $this->sql_error('');
- }
- $this->db_connect_id = @ibase_pconnect($use_database, $this->user, $sqlpassword, false, false, 3);
- }
- else
- {
- if (!function_exists('ibase_connect'))
- {
- $this->connect_error = 'ibase_connect function does not exist, is interbase extension installed?';
- return $this->sql_error('');
- }
- $this->db_connect_id = @ibase_connect($use_database, $this->user, $sqlpassword, false, false, 3);
- }
-
- // Do not call ibase_service_attach if connection failed,
- // otherwise error message from ibase_(p)connect call will be clobbered.
- if ($this->db_connect_id && function_exists('ibase_service_attach') && $this->server)
- {
- $this->service_handle = @ibase_service_attach($this->server, $this->user, $sqlpassword);
- }
- else
- {
- $this->service_handle = false;
- }
-
- return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
- }
-
- /**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache forced to false for Interbase
- * @return string sql server version
- */
- function sql_server_info($raw = false, $use_cache = true)
- {
- /**
- * force $use_cache false. I didn't research why the caching code there is no caching code
- * but I assume its because the IB extension provides a direct method to access it
- * without a query.
- */
-
- $use_cache = false;
-
- if ($this->service_handle !== false && function_exists('ibase_server_info'))
- {
- return @ibase_server_info($this->service_handle, IBASE_SVC_SERVER_VERSION);
- }
-
- return ($raw) ? '2.1' : 'Firebird/Interbase';
- }
-
- /**
- * SQL Transaction
- * @access private
- */
- function _sql_transaction($status = 'begin')
- {
- switch ($status)
- {
- case 'begin':
- return true;
- break;
-
- case 'commit':
- return @ibase_commit();
- break;
-
- case 'rollback':
- return @ibase_rollback();
- break;
- }
-
- return true;
- }
-
- /**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access 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);
- }
-
- $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)
- {
- $array = array();
- // We overcome Firebird's 32767 char limit by binding vars
- if (strlen($query) > 32767)
- {
- if (preg_match('/^(INSERT INTO[^(]++)\\(([^()]+)\\) VALUES[^(]++\\((.*?)\\)$/s', $query, $regs))
- {
- if (strlen($regs[3]) > 32767)
- {
- preg_match_all('/\'(?:[^\']++|\'\')*+\'|[\d-.]+/', $regs[3], $vals, PREG_PATTERN_ORDER);
-
- $inserts = $vals[0];
- unset($vals);
-
- foreach ($inserts as $key => $value)
- {
- if (!empty($value) && $value[0] === "'" && strlen($value) > 32769) // check to see if this thing is greater than the max + 'x2
- {
- $inserts[$key] = '?';
- $array[] = str_replace("''", "'", substr($value, 1, -1));
- }
- }
-
- $query = $regs[1] . '(' . $regs[2] . ') VALUES (' . implode(', ', $inserts) . ')';
- }
- }
- else if (preg_match('/^(UPDATE ([\\w_]++)\\s+SET )([\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|\\d+)(?:,\\s*[\\w_]++\\s*=\\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+)\\s+(WHERE.*)$/s', $query, $data))
- {
- if (strlen($data[3]) > 32767)
- {
- $update = $data[1];
- $where = $data[4];
- preg_match_all('/(\\w++)\\s*=\\s*(\'(?:[^\']++|\'\')*+\'|[\d-.]++)/', $data[3], $temp, PREG_SET_ORDER);
- unset($data);
-
- $cols = array();
- foreach ($temp as $value)
- {
- if (!empty($value[2]) && $value[2][0] === "'" && strlen($value[2]) > 32769) // check to see if this thing is greater than the max + 'x2
- {
- $array[] = str_replace("''", "'", substr($value[2], 1, -1));
- $cols[] = $value[1] . '=?';
- }
- else
- {
- $cols[] = $value[1] . '=' . $value[2];
- }
- }
-
- $query = $update . implode(', ', $cols) . ' ' . $where;
- unset($cols);
- }
- }
- }
-
- if (!function_exists('ibase_affected_rows') && (preg_match('/^UPDATE ([\w_]++)\s+SET [\w_]++\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+)(?:,\s*[\w_]++\s*=\s*(?:\'(?:[^\']++|\'\')*+\'|[\d-.]+))*+\s+(WHERE.*)?$/s', $query, $regs) || preg_match('/^DELETE FROM ([\w_]++)\s*(WHERE\s*.*)?$/s', $query, $regs)))
- {
- $affected_sql = 'SELECT COUNT(*) as num_rows_affected FROM ' . $regs[1];
- if (!empty($regs[2]))
- {
- $affected_sql .= ' ' . $regs[2];
- }
-
- if (!($temp_q_id = @ibase_query($this->db_connect_id, $affected_sql)))
- {
- return false;
- }
-
- $temp_result = @ibase_fetch_assoc($temp_q_id);
- @ibase_free_result($temp_q_id);
-
- $this->affected_rows = ($temp_result) ? $temp_result['NUM_ROWS_AFFECTED'] : false;
- }
-
- if (sizeof($array))
- {
- $p_query = @ibase_prepare($this->db_connect_id, $query);
- array_unshift($array, $p_query);
- $this->query_result = call_user_func_array('ibase_execute', $array);
- unset($array);
-
- if ($this->query_result === false)
- {
- $this->sql_error($query);
- }
- }
- else if (($this->query_result = @ibase_query($this->db_connect_id, $query)) === false)
- {
- $this->sql_error($query);
- }
-
- if (defined('DEBUG'))
- {
- $this->sql_report('stop', $query);
- }
-
- if (!$this->transaction)
- {
- if (function_exists('ibase_commit_ret'))
- {
- @ibase_commit_ret();
- }
- else
- {
- // way cooler than ibase_commit_ret :D
- @ibase_query('COMMIT RETAIN;');
- }
- }
-
- 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 FIRST ' . $total . ((!empty($offset)) ? ' SKIP ' . $offset : '') . substr($query, 6);
-
- return $this->sql_query($query, $cache_ttl);
- }
-
- /**
- * Return number of affected rows
- */
- function sql_affectedrows()
- {
- // PHP 5+ function
- if (function_exists('ibase_affected_rows'))
- {
- return ($this->db_connect_id) ? @ibase_affected_rows($this->db_connect_id) : false;
- }
- else
- {
- return $this->affected_rows;
- }
- }
-
- /**
- * Fetch current row
- */
- 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 = array();
- $cur_row = @ibase_fetch_object($query_id, IBASE_TEXT);
-
- if (!$cur_row)
- {
- return false;
- }
-
- foreach (get_object_vars($cur_row) as $key => $value)
- {
- $row[strtolower($key)] = (is_string($value)) ? trim(str_replace(array("\\0", "\\n"), array("\0", "\n"), $value)) : $value;
- }
-
- return (sizeof($row)) ? $row : false;
- }
-
- /**
- * Get last inserted id after insert statement
- */
- function sql_nextid()
- {
- $query_id = $this->query_result;
-
- if ($query_id !== false && $this->last_query_text != '')
- {
- if ($this->query_result && preg_match('#^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)#i', $this->last_query_text, $tablename))
- {
- $sql = 'SELECT GEN_ID(' . $tablename[1] . '_gen, 0) AS new_id FROM RDB$DATABASE';
-
- if (!($temp_q_id = @ibase_query($this->db_connect_id, $sql)))
- {
- return false;
- }
-
- $temp_result = @ibase_fetch_assoc($temp_q_id);
- @ibase_free_result($temp_q_id);
-
- return ($temp_result) ? $temp_result['NEW_ID'] : false;
- }
- }
-
- return false;
- }
-
- /**
- * Free sql result
- */
- 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 @ibase_free_result($query_id);
- }
-
- return false;
- }
-
- /**
- * Escape string used in sql query
- */
- 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 db-specific query data
- * @access private
- */
- function _sql_custom_build($stage, $data)
- {
- return $data;
- }
-
- function _sql_bit_and($column_name, $bit, $compare = '')
- {
- return 'BIN_AND(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
- }
-
- function _sql_bit_or($column_name, $bit, $compare = '')
- {
- return 'BIN_OR(' . $column_name . ', ' . (1 << $bit) . ')' . (($compare) ? ' ' . $compare : '');
- }
-
- /**
- * @inheritdoc
- */
- function cast_expr_to_bigint($expression)
- {
- // Precision must be from 1 to 18
- return 'CAST(' . $expression . ' as DECIMAL(18, 0))';
- }
-
- /**
- * @inheritdoc
- */
- function cast_expr_to_string($expression)
- {
- return 'CAST(' . $expression . ' as VARCHAR(255))';
- }
-
- /**
- * return sql error array
- * @access private
- */
- function _sql_error()
- {
- // Need special handling here because ibase_errmsg returns
- // connection errors, however if the interbase extension
- // is not installed then ibase_errmsg does not exist and
- // we cannot call it.
- if (function_exists('ibase_errmsg'))
- {
- $msg = @ibase_errmsg();
- if (!$msg)
- {
- $msg = $this->connect_error;
- }
- }
- else
- {
- $msg = $this->connect_error;
- }
- return array(
- 'message' => $msg,
- 'code' => (@function_exists('ibase_errcode') ? @ibase_errcode() : '')
- );
- }
-
- /**
- * Close sql connection
- * @access private
- */
- function _sql_close()
- {
- if ($this->service_handle !== false)
- {
- @ibase_service_detach($this->service_handle);
- }
-
- return @ibase_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 = @ibase_query($this->db_connect_id, $query);
- while ($void = @ibase_fetch_object($result, IBASE_TEXT))
- {
- // Take the time spent on parsing rows into account
- }
- @ibase_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.php b/phpBB/phpbb/db/driver/mssql.php
index 6ebc891673..dfdbfe15e6 100644
--- a/phpBB/phpbb/db/driver/mssql.php
+++ b/phpBB/phpbb/db/driver/mssql.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,14 +16,13 @@ namespace phpbb\db\driver;
/**
* MSSQL Database Abstraction Layer
* Minimum Requirement is MSSQL 2000+
-* @package dbal
*/
class mssql extends \phpbb\db\driver\driver
{
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -55,10 +58,7 @@ class mssql extends \phpbb\db\driver\driver
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -71,8 +71,8 @@ class mssql extends \phpbb\db\driver\driver
$row = false;
if ($result_id)
{
- $row = @mssql_fetch_assoc($result_id);
- @mssql_free_result($result_id);
+ $row = mssql_fetch_assoc($result_id);
+ mssql_free_result($result_id);
}
$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
@@ -124,13 +124,7 @@ class mssql extends \phpbb\db\driver\driver
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -143,6 +137,10 @@ class mssql extends \phpbb\db\driver\driver
{
$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);
@@ -158,13 +156,22 @@ class mssql extends \phpbb\db\driver\driver
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0 && $this->query_result !== true)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -215,7 +222,7 @@ class mssql extends \phpbb\db\driver\driver
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -223,7 +230,7 @@ class mssql extends \phpbb\db\driver\driver
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -239,12 +246,12 @@ class mssql extends \phpbb\db\driver\driver
return $cache->sql_fetchrow($query_id);
}
- if ($query_id === false)
+ if (!$query_id || $query_id === true)
{
return false;
}
- $row = @mssql_fetch_assoc($query_id);
+ $row = mssql_fetch_assoc($query_id);
// I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
if ($row)
@@ -259,8 +266,7 @@ class mssql extends \phpbb\db\driver\driver
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -271,35 +277,40 @@ class mssql extends \phpbb\db\driver\driver
$query_id = $this->query_result;
}
+ if ($query_id === true)
+ {
+ return false;
+ }
+
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;
+ return ($query_id) ? @mssql_data_seek($query_id, $rownum) : false;
}
/**
- * Get last inserted id after insert statement
+ * {@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))
+ if ($row = mssql_fetch_assoc($result_id))
{
- @mssql_free_result($result_id);
+ mssql_free_result($result_id);
return $row['computed'];
}
- @mssql_free_result($result_id);
+ mssql_free_result($result_id);
}
return false;
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -310,6 +321,11 @@ class mssql extends \phpbb\db\driver\driver
$query_id = $this->query_result;
}
+ if ($query_id === true)
+ {
+ return false;
+ }
+
if ($cache && !is_object($query_id) && $cache->sql_exists($query_id))
{
return $cache->sql_freeresult($query_id);
@@ -318,14 +334,14 @@ class mssql extends \phpbb\db\driver\driver
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @mssql_free_result($query_id);
+ return mssql_free_result($query_id);
}
return false;
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -350,6 +366,15 @@ class mssql extends \phpbb\db\driver\driver
}
/**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
* return sql error array
* @access private
*/
@@ -366,9 +391,9 @@ class mssql extends \phpbb\db\driver\driver
$result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
if ($result_id)
{
- $row = @mssql_fetch_assoc($result_id);
+ $row = mssql_fetch_assoc($result_id);
$error['code'] = $row['code'];
- @mssql_free_result($result_id);
+ mssql_free_result($result_id);
}
// Get full error message if possible
@@ -379,12 +404,12 @@ class mssql extends \phpbb\db\driver\driver
if ($result_id)
{
- $row = @mssql_fetch_assoc($result_id);
+ $row = mssql_fetch_assoc($result_id);
if (!empty($row['message']))
{
$error['message'] .= '<br />' . $row['message'];
}
- @mssql_free_result($result_id);
+ mssql_free_result($result_id);
}
}
else
@@ -430,13 +455,13 @@ class mssql extends \phpbb\db\driver\driver
if ($result = @mssql_query($query, $this->db_connect_id))
{
@mssql_next_result($result);
- while ($row = @mssql_fetch_row($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);
+ mssql_free_result($result);
if ($html_table)
{
@@ -449,11 +474,14 @@ class mssql extends \phpbb\db\driver\driver
$endtime = $endtime[0] + $endtime[1];
$result = @mssql_query($query, $this->db_connect_id);
- while ($void = @mssql_fetch_assoc($result))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = mssql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ mssql_free_result($result);
}
- @mssql_free_result($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/mssql_base.php b/phpBB/phpbb/db/driver/mssql_base.php
index 113f1c6902..514df9eaca 100644
--- a/phpBB/phpbb/db/driver/mssql_base.php
+++ b/phpBB/phpbb/db/driver/mssql_base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\db\driver;
/**
* MSSQL Database Base Abstraction Layer
-* @package dbal
*/
abstract class mssql_base extends \phpbb\db\driver\driver
{
@@ -24,7 +27,7 @@ abstract class mssql_base extends \phpbb\db\driver\driver
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -49,6 +52,15 @@ abstract class mssql_base extends \phpbb\db\driver\driver
}
/**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression . " ESCAPE '\\'";
+ }
+
+ /**
* Build db-specific query data
* @access private
*/
diff --git a/phpBB/phpbb/db/driver/mssql_odbc.php b/phpBB/phpbb/db/driver/mssql_odbc.php
index f8c70f1cd7..9d9ad603e0 100644
--- a/phpBB/phpbb/db/driver/mssql_odbc.php
+++ b/phpBB/phpbb/db/driver/mssql_odbc.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -17,8 +21,6 @@ namespace phpbb\db\driver;
* @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
-*
-* @package dbal
*/
class mssql_odbc extends \phpbb\db\driver\mssql_base
{
@@ -26,7 +28,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -83,10 +85,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -99,8 +98,8 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
$row = false;
if ($result_id)
{
- $row = @odbc_fetch_array($result_id);
- @odbc_free_result($result_id);
+ $row = odbc_fetch_array($result_id);
+ odbc_free_result($result_id);
}
$this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
@@ -144,13 +143,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -163,6 +156,10 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
{
$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;
@@ -179,13 +176,22 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -236,7 +242,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -244,8 +250,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
}
/**
- * Fetch current row
- * @note number of bytes returned depends on odbc.defaultlrl php.ini setting. If it is limited to 4K for example only 4K of data is returned max.
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -261,11 +266,11 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
return $cache->sql_fetchrow($query_id);
}
- return ($query_id !== false) ? @odbc_fetch_array($query_id) : false;
+ return ($query_id) ? odbc_fetch_array($query_id) : false;
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -273,20 +278,20 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
if ($result_id)
{
- if (@odbc_fetch_array($result_id))
+ if (odbc_fetch_array($result_id))
{
- $id = @odbc_result($result_id, 1);
- @odbc_free_result($result_id);
+ $id = odbc_result($result_id, 1);
+ odbc_free_result($result_id);
return $id;
}
- @odbc_free_result($result_id);
+ odbc_free_result($result_id);
}
return false;
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -305,7 +310,7 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @odbc_free_result($query_id);
+ return odbc_free_result($query_id);
}
return false;
@@ -360,11 +365,14 @@ class mssql_odbc extends \phpbb\db\driver\mssql_base
$endtime = $endtime[0] + $endtime[1];
$result = @odbc_exec($this->db_connect_id, $query);
- while ($void = @odbc_fetch_array($result))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = odbc_fetch_array($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ odbc_free_result($result);
}
- @odbc_free_result($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/mssqlnative.php b/phpBB/phpbb/db/driver/mssqlnative.php
index 125db9c8d4..50dce35baa 100644
--- a/phpBB/phpbb/db/driver/mssqlnative.php
+++ b/phpBB/phpbb/db/driver/mssqlnative.php
@@ -1,10 +1,17 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
*
+* @copyright (c) phpBB Limited <https://www.phpbb.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
@@ -13,9 +20,6 @@
namespace phpbb\db\driver;
-/**
-* @package dbal
-*/
class mssqlnative extends \phpbb\db\driver\mssql_base
{
var $m_insert_id = null;
@@ -24,7 +28,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -53,10 +57,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -113,13 +114,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -132,6 +127,10 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
{
$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;
@@ -150,13 +149,22 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -215,7 +223,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -223,7 +231,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -239,12 +247,12 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
return $cache->sql_fetchrow($query_id);
}
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
- $row = @sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
+ $row = sqlsrv_fetch_array($query_id, SQLSRV_FETCH_ASSOC);
if ($row)
{
@@ -263,17 +271,17 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
$result_id = @sqlsrv_query($this->db_connect_id, 'SELECT @@IDENTITY');
- if ($result_id !== false)
+ if ($result_id)
{
- $row = @sqlsrv_fetch_array($result_id);
+ $row = sqlsrv_fetch_array($result_id);
$id = $row[0];
- @sqlsrv_free_stmt($result_id);
+ sqlsrv_free_stmt($result_id);
return $id;
}
else
@@ -283,7 +291,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -302,7 +310,7 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @sqlsrv_free_stmt($query_id);
+ return sqlsrv_free_stmt($query_id);
}
return false;
@@ -324,10 +332,10 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
{
foreach ($errors as $error)
{
- $error_message .= "SQLSTATE: " . $error[ 'SQLSTATE'] . "\n";
- $error_message .= "code: " . $error[ 'code'] . "\n";
+ $error_message .= "SQLSTATE: " . $error['SQLSTATE'] . "\n";
+ $error_message .= "code: " . $error['code'] . "\n";
$code = $error['code'];
- $error_message .= "message: " . $error[ 'message'] . "\n";
+ $error_message .= "message: " . $error['message'] . "\n";
}
$this->last_error_result = $error_message;
$error = $this->last_error_result;
@@ -375,14 +383,14 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
@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))
+ sqlsrv_next_result($result);
+ while ($row = sqlsrv_fetch_array($result))
{
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ sqlsrv_free_stmt($result);
}
@sqlsrv_query($this->db_connect_id, 'SET SHOWPLAN_TEXT OFF;');
- @sqlsrv_free_stmt($result);
if ($html_table)
{
@@ -395,11 +403,14 @@ class mssqlnative extends \phpbb\db\driver\mssql_base
$endtime = $endtime[0] + $endtime[1];
$result = @sqlsrv_query($this->db_connect_id, $query);
- while ($void = @sqlsrv_fetch_array($result))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = sqlsrv_fetch_array($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ sqlsrv_free_stmt($result);
}
- @sqlsrv_free_stmt($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/mysql.php b/phpBB/phpbb/db/driver/mysql.php
index e311f0dd74..a94e88b331 100644
--- a/phpBB/phpbb/db/driver/mysql.php
+++ b/phpBB/phpbb/db/driver/mysql.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -16,7 +20,6 @@ namespace phpbb\db\driver;
* MySQL 4.0+
* MySQL 4.1+
* MySQL 5.0+
-* @package dbal
*/
class mysql extends \phpbb\db\driver\mysql_base
{
@@ -24,8 +27,7 @@ class mysql extends \phpbb\db\driver\mysql_base
var $connect_error = '';
/**
- * Connect to server
- * @access public
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -68,9 +70,16 @@ class mysql extends \phpbb\db\driver\mysql_base
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']));
+ if ($result)
+ {
+ $row = mysql_fetch_assoc($result);
+ mysql_free_result($result);
+ $modes = array_map('trim', explode(',', $row['sql_mode']));
+ }
+ else
+ {
+ $modes = array();
+ }
// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
if (!in_array('TRADITIONAL', $modes))
@@ -103,10 +112,7 @@ class mysql extends \phpbb\db\driver\mysql_base
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -115,14 +121,17 @@ class mysql extends \phpbb\db\driver\mysql_base
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);
+ if ($result)
+ {
+ $row = mysql_fetch_assoc($result);
+ mysql_free_result($result);
- $this->sql_server_version = $row['version'];
+ $this->sql_server_version = $row['version'];
- if (!empty($cache) && $use_cache)
- {
- $cache->put('mysql_version', $this->sql_server_version);
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mysql_version', $this->sql_server_version);
+ }
}
}
@@ -154,13 +163,7 @@ class mysql extends \phpbb\db\driver\mysql_base
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -173,6 +176,10 @@ class mysql extends \phpbb\db\driver\mysql_base
{
$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);
@@ -188,13 +195,22 @@ class mysql extends \phpbb\db\driver\mysql_base
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -213,15 +229,34 @@ class mysql extends \phpbb\db\driver\mysql_base
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
- return ($this->db_connect_id) ? @mysql_affected_rows($this->db_connect_id) : false;
+ 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;
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -237,12 +272,11 @@ class mysql extends \phpbb\db\driver\mysql_base
return $cache->sql_fetchrow($query_id);
}
- return ($query_id !== false) ? @mysql_fetch_assoc($query_id) : false;
+ return ($query_id) ? mysql_fetch_assoc($query_id) : false;
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -262,7 +296,7 @@ class mysql extends \phpbb\db\driver\mysql_base
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -270,7 +304,7 @@ class mysql extends \phpbb\db\driver\mysql_base
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -289,14 +323,14 @@ class mysql extends \phpbb\db\driver\mysql_base
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @mysql_free_result($query_id);
+ return mysql_free_result($query_id);
}
return false;
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -392,12 +426,12 @@ class mysql extends \phpbb\db\driver\mysql_base
if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id))
{
- while ($row = @mysql_fetch_assoc($result))
+ while ($row = mysql_fetch_assoc($result))
{
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ mysql_free_result($result);
}
- @mysql_free_result($result);
if ($html_table)
{
@@ -412,7 +446,7 @@ class mysql extends \phpbb\db\driver\mysql_base
if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id))
{
$this->html_hold .= '<br />';
- while ($row = @mysql_fetch_assoc($result))
+ while ($row = mysql_fetch_assoc($result))
{
// make <unknown> HTML safe
if (!empty($row['Source_function']))
@@ -430,8 +464,8 @@ class mysql extends \phpbb\db\driver\mysql_base
}
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ mysql_free_result($result);
}
- @mysql_free_result($result);
if ($html_table)
{
@@ -449,11 +483,14 @@ class mysql extends \phpbb\db\driver\mysql_base
$endtime = $endtime[0] + $endtime[1];
$result = @mysql_query($query, $this->db_connect_id);
- while ($void = @mysql_fetch_assoc($result))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = mysql_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ mysql_free_result($result);
}
- @mysql_free_result($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/mysql_base.php b/phpBB/phpbb/db/driver/mysql_base.php
index 87b6d153a9..5e0b359134 100644
--- a/phpBB/phpbb/db/driver/mysql_base.php
+++ b/phpBB/phpbb/db/driver/mysql_base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\db\driver;
/**
* Abstract MySQL Database Base Abstraction Layer
-* @package dbal
*/
abstract class mysql_base extends \phpbb\db\driver\driver
{
@@ -43,14 +46,7 @@ abstract class mysql_base extends \phpbb\db\driver\driver
}
/**
- * 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).
- *
- * @access public
+ * {@inheritDoc}
*/
function get_estimated_row_count($table_name)
{
@@ -72,13 +68,7 @@ abstract class mysql_base extends \phpbb\db\driver\driver
}
/**
- * 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.
- *
- * @access public
+ * {@inheritDoc}
*/
function get_row_count($table_name)
{
@@ -122,6 +112,15 @@ abstract class mysql_base extends \phpbb\db\driver\driver
}
/**
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
* Build db-specific query data
* @access private
*/
diff --git a/phpBB/phpbb/db/driver/mysqli.php b/phpBB/phpbb/db/driver/mysqli.php
index adc8f96302..d43e201526 100644
--- a/phpBB/phpbb/db/driver/mysqli.php
+++ b/phpBB/phpbb/db/driver/mysqli.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,7 +17,6 @@ namespace phpbb\db\driver;
* MySQLi Database Abstraction Layer
* mysqli-extension has to be compiled with:
* MySQL 4.1+ or MySQL 5.0+
-* @package dbal
*/
class mysqli extends \phpbb\db\driver\mysql_base
{
@@ -21,7 +24,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -31,8 +34,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
return $this->sql_error('');
}
- // Mysqli extension supports persistent connection since PHP 5.3.0
- $this->persistency = (version_compare(PHP_VERSION, '5.3.0', '>=')) ? $persistency : false;
+ $this->persistency = $persistency;
$this->user = $sqluser;
// If persistent connection, set dbhost to localhost when empty and prepend it with 'p:' prefix
@@ -57,7 +59,12 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
}
- $this->db_connect_id = @mysqli_connect($this->server, $this->user, $sqlpassword, $this->dbname, $port, $socket);
+ $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 != '')
{
@@ -67,10 +74,17 @@ class mysqli extends \phpbb\db\driver\mysql_base
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');
- $row = @mysqli_fetch_assoc($result);
- @mysqli_free_result($result);
+ if ($result)
+ {
+ $row = mysqli_fetch_assoc($result);
+ mysqli_free_result($result);
- $modes = array_map('trim', explode(',', $row['sql_mode']));
+ $modes = array_map('trim', explode(',', $row['sql_mode']));
+ }
+ else
+ {
+ $modes = array();
+ }
// TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES
if (!in_array('TRADITIONAL', $modes))
@@ -96,10 +110,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -108,14 +119,17 @@ class mysqli extends \phpbb\db\driver\mysql_base
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');
- $row = @mysqli_fetch_assoc($result);
- @mysqli_free_result($result);
+ if ($result)
+ {
+ $row = mysqli_fetch_assoc($result);
+ mysqli_free_result($result);
- $this->sql_server_version = $row['version'];
+ $this->sql_server_version = $row['version'];
- if (!empty($cache) && $use_cache)
- {
- $cache->put('mysqli_version', $this->sql_server_version);
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('mysqli_version', $this->sql_server_version);
+ }
}
}
@@ -151,13 +165,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -170,6 +178,10 @@ class mysqli extends \phpbb\db\driver\mysql_base
{
$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);
@@ -185,6 +197,15 @@ class mysqli extends \phpbb\db\driver\mysql_base
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
if ($cache && $cache_ttl)
{
@@ -205,7 +226,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -213,7 +234,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -229,9 +250,9 @@ class mysqli extends \phpbb\db\driver\mysql_base
return $cache->sql_fetchrow($query_id);
}
- if ($query_id !== false)
+ if ($query_id)
{
- $result = @mysqli_fetch_assoc($query_id);
+ $result = mysqli_fetch_assoc($query_id);
return $result !== null ? $result : false;
}
@@ -239,8 +260,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -256,11 +276,11 @@ class mysqli extends \phpbb\db\driver\mysql_base
return $cache->sql_rowseek($rownum, $query_id);
}
- return ($query_id !== false) ? @mysqli_data_seek($query_id, $rownum) : false;
+ return ($query_id) ? @mysqli_data_seek($query_id, $rownum) : false;
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -268,7 +288,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -284,11 +304,21 @@ class mysqli extends \phpbb\db\driver\mysql_base
return $cache->sql_freeresult($query_id);
}
- return @mysqli_free_result($query_id);
+ if (!$query_id)
+ {
+ return false;
+ }
+
+ if ($query_id === true)
+ {
+ return true;
+ }
+
+ return mysqli_free_result($query_id);
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -383,12 +413,12 @@ class mysqli extends \phpbb\db\driver\mysql_base
if ($result = @mysqli_query($this->db_connect_id, "EXPLAIN $explain_query"))
{
- while ($row = @mysqli_fetch_assoc($result))
+ while ($row = mysqli_fetch_assoc($result))
{
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ mysqli_free_result($result);
}
- @mysqli_free_result($result);
if ($html_table)
{
@@ -403,7 +433,7 @@ class mysqli extends \phpbb\db\driver\mysql_base
if ($result = @mysqli_query($this->db_connect_id, 'SHOW PROFILE ALL;'))
{
$this->html_hold .= '<br />';
- while ($row = @mysqli_fetch_assoc($result))
+ while ($row = mysqli_fetch_assoc($result))
{
// make <unknown> HTML safe
if (!empty($row['Source_function']))
@@ -421,8 +451,8 @@ class mysqli extends \phpbb\db\driver\mysql_base
}
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ mysqli_free_result($result);
}
- @mysqli_free_result($result);
if ($html_table)
{
@@ -440,11 +470,14 @@ class mysqli extends \phpbb\db\driver\mysql_base
$endtime = $endtime[0] + $endtime[1];
$result = @mysqli_query($this->db_connect_id, $query);
- while ($void = @mysqli_fetch_assoc($result))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = mysqli_fetch_assoc($result))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ mysqli_free_result($result);
}
- @mysqli_free_result($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/oracle.php b/phpBB/phpbb/db/driver/oracle.php
index 36ed43d4a7..54238a15ef 100644
--- a/phpBB/phpbb/db/driver/oracle.php
+++ b/phpBB/phpbb/db/driver/oracle.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\db\driver;
/**
* Oracle Database Abstraction Layer
-* @package dbal
*/
class oracle extends \phpbb\db\driver\driver
{
@@ -19,7 +22,7 @@ class oracle extends \phpbb\db\driver\driver
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -72,10 +75,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache forced to false for Oracle
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -84,8 +84,6 @@ class oracle extends \phpbb\db\driver\driver
* but I assume its because the Oracle extension provides a direct method to access it
* without a query.
*/
-
- $use_cache = false;
/*
global $cache;
@@ -240,13 +238,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -259,6 +251,10 @@ class oracle extends \phpbb\db\driver\driver
{
$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;
@@ -436,13 +432,22 @@ class oracle extends \phpbb\db\driver\driver
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -473,7 +478,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -481,7 +486,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -497,10 +502,10 @@ class oracle extends \phpbb\db\driver\driver
return $cache->sql_fetchrow($query_id);
}
- if ($query_id !== false)
+ if ($query_id)
{
$row = array();
- $result = @ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
+ $result = ocifetchinto($query_id, $row, OCI_ASSOC + OCI_RETURN_NULLS);
if (!$result || !$row)
{
@@ -532,8 +537,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -549,7 +553,7 @@ class oracle extends \phpbb\db\driver\driver
return $cache->sql_rowseek($rownum, $query_id);
}
- if ($query_id === false)
+ if (!$query_id)
{
return false;
}
@@ -570,7 +574,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -582,18 +586,24 @@ class oracle extends \phpbb\db\driver\driver
{
$query = 'SELECT ' . $tablename[1] . '_seq.currval FROM DUAL';
$stmt = @ociparse($this->db_connect_id, $query);
- @ociexecute($stmt, OCI_DEFAULT);
+ if ($stmt)
+ {
+ $success = @ociexecute($stmt, OCI_DEFAULT);
- $temp_result = @ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
- @ocifreestatement($stmt);
+ if ($success)
+ {
+ $temp_result = ocifetchinto($stmt, $temp_array, OCI_ASSOC + OCI_RETURN_NULLS);
+ ocifreestatement($stmt);
- if ($temp_result)
- {
- return $temp_array['CURRVAL'];
- }
- else
- {
- return false;
+ if ($temp_result)
+ {
+ return $temp_array['CURRVAL'];
+ }
+ else
+ {
+ return false;
+ }
+ }
}
}
}
@@ -602,7 +612,7 @@ class oracle extends \phpbb\db\driver\driver
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -621,14 +631,14 @@ class oracle extends \phpbb\db\driver\driver
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @ocifreestatement($query_id);
+ return ocifreestatement($query_id);
}
return false;
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -644,6 +654,15 @@ class oracle extends \phpbb\db\driver\driver
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;
@@ -777,14 +796,20 @@ class oracle extends \phpbb\db\driver\driver
$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))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ $success = @ociexecute($result, OCI_DEFAULT);
+ if ($success)
+ {
+ $row = array();
+
+ while (ocifetchinto($result, $row, OCI_ASSOC + OCI_RETURN_NULLS))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ @ocifreestatement($result);
+ }
}
- @ocifreestatement($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/postgres.php b/phpBB/phpbb/db/driver/postgres.php
index 5dbd1ca74f..44476612c3 100644
--- a/phpBB/phpbb/db/driver/postgres.php
+++ b/phpBB/phpbb/db/driver/postgres.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,16 +15,16 @@ namespace phpbb\db\driver;
/**
* PostgreSQL Database Abstraction Layer
-* Minimum Requirement is Version 7.3+
-* @package dbal
+* Minimum Requirement is Version 8.3+
*/
class postgres extends \phpbb\db\driver\driver
{
+ var $multi_insert = true;
var $last_query_text = '';
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -98,11 +102,6 @@ class postgres extends \phpbb\db\driver\driver
if ($this->db_connect_id)
{
- if (version_compare($this->sql_server_info(true), '8.2', '>='))
- {
- $this->multi_insert = true;
- }
-
if ($schema !== '')
{
@pg_query($this->db_connect_id, 'SET search_path TO ' . $schema);
@@ -115,10 +114,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache If true, it is safe to retrieve the value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -127,14 +123,17 @@ class postgres extends \phpbb\db\driver\driver
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);
+ if ($query_id)
+ {
+ $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;
+ $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);
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('pgsql_version', $this->sql_server_version);
+ }
}
}
@@ -166,13 +165,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -185,6 +178,10 @@ class postgres extends \phpbb\db\driver\driver
{
$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;
@@ -201,13 +198,22 @@ class postgres extends \phpbb\db\driver\driver
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
+
+ if (!$this->query_result)
+ {
+ return false;
+ }
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)
+ else if (strpos($query, 'SELECT') === 0)
{
$this->open_queries[(int) $this->query_result] = $this->query_result;
}
@@ -253,7 +259,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -261,7 +267,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -277,12 +283,11 @@ class postgres extends \phpbb\db\driver\driver
return $cache->sql_fetchrow($query_id);
}
- return ($query_id !== false) ? @pg_fetch_assoc($query_id, null) : false;
+ return ($query_id) ? pg_fetch_assoc($query_id, null) : false;
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -298,11 +303,11 @@ class postgres extends \phpbb\db\driver\driver
return $cache->sql_rowseek($rownum, $query_id);
}
- return ($query_id !== false) ? @pg_result_seek($query_id, $rownum) : false;
+ return ($query_id) ? @pg_result_seek($query_id, $rownum) : false;
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -320,8 +325,8 @@ class postgres extends \phpbb\db\driver\driver
return false;
}
- $temp_result = @pg_fetch_assoc($temp_q_id, null);
- @pg_free_result($query_id);
+ $temp_result = pg_fetch_assoc($temp_q_id, null);
+ pg_free_result($query_id);
return ($temp_result) ? $temp_result['last_value'] : false;
}
@@ -331,7 +336,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -350,15 +355,14 @@ class postgres extends \phpbb\db\driver\driver
if (isset($this->open_queries[(int) $query_id]))
{
unset($this->open_queries[(int) $query_id]);
- return @pg_free_result($query_id);
+ return pg_free_result($query_id);
}
return false;
}
/**
- * Escape string used in sql query
- * Note: Do not use for bytea values if we may use them at a later stage
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -375,7 +379,16 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * @inheritdoc
+ * Build NOT LIKE expression
+ * @access private
+ */
+ function _sql_not_like_expression($expression)
+ {
+ return $expression;
+ }
+
+ /**
+ * {@inheritDoc}
*/
function cast_expr_to_bigint($expression)
{
@@ -383,7 +396,7 @@ class postgres extends \phpbb\db\driver\driver
}
/**
- * @inheritdoc
+ * {@inheritDoc}
*/
function cast_expr_to_string($expression)
{
@@ -448,12 +461,12 @@ class postgres extends \phpbb\db\driver\driver
if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query"))
{
- while ($row = @pg_fetch_assoc($result, null))
+ while ($row = pg_fetch_assoc($result, null))
{
$html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
}
+ pg_free_result($result);
}
- @pg_free_result($result);
if ($html_table)
{
@@ -468,11 +481,14 @@ class postgres extends \phpbb\db\driver\driver
$endtime = $endtime[0] + $endtime[1];
$result = @pg_query($this->db_connect_id, $query);
- while ($void = @pg_fetch_assoc($result, null))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = pg_fetch_assoc($result, null))
+ {
+ // Take the time spent on parsing rows into account
+ }
+ pg_free_result($result);
}
- @pg_free_result($result);
$splittime = explode(' ', microtime());
$splittime = $splittime[0] + $splittime[1];
diff --git a/phpBB/phpbb/db/driver/sqlite.php b/phpBB/phpbb/db/driver/sqlite.php
index 59ec895c0f..8e205ebb81 100644
--- a/phpBB/phpbb/db/driver/sqlite.php
+++ b/phpBB/phpbb/db/driver/sqlite.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,14 +16,13 @@ namespace phpbb\db\driver;
/**
* Sqlite Database Abstraction Layer
* Minimum Requirement: 2.8.2+
-* @package dbal
*/
class sqlite extends \phpbb\db\driver\driver
{
var $connect_error = '';
/**
- * Connect to server
+ * {@inheritDoc}
*/
function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
{
@@ -58,10 +61,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Version information about used database
- * @param bool $raw if true, only return the fetched sql_server_version
- * @param bool $use_cache if true, it is safe to retrieve the stored value from the cache
- * @return string sql server version
+ * {@inheritDoc}
*/
function sql_server_info($raw = false, $use_cache = true)
{
@@ -70,13 +70,16 @@ class sqlite extends \phpbb\db\driver\driver
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);
+ if ($result)
+ {
+ $row = sqlite_fetch_array($result, SQLITE_ASSOC);
- $this->sql_server_version = (!empty($row['version'])) ? $row['version'] : 0;
+ $this->sql_server_version = (!empty($row['version'])) ? $row['version'] : 0;
- if (!empty($cache) && $use_cache)
- {
- $cache->put('sqlite_version', $this->sql_server_version);
+ if (!empty($cache) && $use_cache)
+ {
+ $cache->put('sqlite_version', $this->sql_server_version);
+ }
}
}
@@ -108,13 +111,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Base query method
- *
- * @param string $query Contains the SQL query which shall be executed
- * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
- * @return mixed When casted to bool the returned value returns true on success and false on failure
- *
- * @access public
+ * {@inheritDoc}
*/
function sql_query($query = '', $cache_ttl = 0)
{
@@ -127,6 +124,10 @@ class sqlite extends \phpbb\db\driver\driver
{
$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);
@@ -142,15 +143,19 @@ class sqlite extends \phpbb\db\driver\driver
{
$this->sql_report('stop', $query);
}
+ else if (defined('PHPBB_DISPLAY_LOAD_TIME'))
+ {
+ $this->sql_time += microtime(true) - $this->curtime;
+ }
- if ($cache && $cache_ttl)
+ if (!$this->query_result)
{
- $this->open_queries[(int) $this->query_result] = $this->query_result;
- $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl);
+ return false;
}
- else if (strpos($query, 'SELECT') === 0 && $this->query_result)
+
+ 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 (defined('DEBUG'))
@@ -185,7 +190,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Return number of affected rows
+ * {@inheritDoc}
*/
function sql_affectedrows()
{
@@ -193,7 +198,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Fetch current row
+ * {@inheritDoc}
*/
function sql_fetchrow($query_id = false)
{
@@ -209,12 +214,11 @@ class sqlite extends \phpbb\db\driver\driver
return $cache->sql_fetchrow($query_id);
}
- return ($query_id !== false) ? @sqlite_fetch_array($query_id, SQLITE_ASSOC) : false;
+ return ($query_id) ? sqlite_fetch_array($query_id, SQLITE_ASSOC) : false;
}
/**
- * Seek to given row number
- * rownum is zero-based
+ * {@inheritDoc}
*/
function sql_rowseek($rownum, &$query_id)
{
@@ -230,11 +234,11 @@ class sqlite extends \phpbb\db\driver\driver
return $cache->sql_rowseek($rownum, $query_id);
}
- return ($query_id !== false) ? @sqlite_seek($query_id, $rownum) : false;
+ return ($query_id) ? @sqlite_seek($query_id, $rownum) : false;
}
/**
- * Get last inserted id after insert statement
+ * {@inheritDoc}
*/
function sql_nextid()
{
@@ -242,7 +246,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Free sql result
+ * {@inheritDoc}
*/
function sql_freeresult($query_id = false)
{
@@ -262,7 +266,7 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Escape string used in sql query
+ * {@inheritDoc}
*/
function sql_escape($msg)
{
@@ -270,12 +274,13 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
- * Correctly adjust LIKE expression for special characters
+ * {@inheritDoc}
+ *
* For SQLite an underscore is a not-known character... this may change with SQLite3
*/
function sql_like_expression($expression)
{
- // Unlike LIKE, GLOB is case sensitive (unfortunatly). SQLite users need to live with it!
+ // 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);
@@ -286,6 +291,23 @@ class sqlite extends \phpbb\db\driver\driver
}
/**
+ * {@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
*/
@@ -343,9 +365,12 @@ class sqlite extends \phpbb\db\driver\driver
$endtime = $endtime[0] + $endtime[1];
$result = @sqlite_query($query, $this->db_connect_id);
- while ($void = @sqlite_fetch_array($result, SQLITE_ASSOC))
+ if ($result)
{
- // Take the time spent on parsing rows into account
+ while ($void = sqlite_fetch_array($result, SQLITE_ASSOC))
+ {
+ // Take the time spent on parsing rows into account
+ }
}
$splittime = explode(' ', microtime());
diff --git a/phpBB/phpbb/db/driver/sqlite3.php b/phpBB/phpbb/db/driver/sqlite3.php
new file mode 100644
index 0000000000..0508500c52
--- /dev/null
+++ b/phpBB/phpbb/db/driver/sqlite3.php
@@ -0,0 +1,431 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU 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->transaction === true && strpos($query, 'INSERT') === 0)
+ {
+ $query = preg_replace('/^INSERT INTO/', 'INSERT OR ROLLBACK INTO', $query);
+ }
+
+ if (($this->query_result = @$this->dbo->query($query)) === false)
+ {
+ // Try to recover a lost database connection
+ if ($this->dbo && !@$this->dbo->lastErrorMsg())
+ {
+ if ($this->sql_connect($this->server, $this->user, '', $this->dbname))
+ {
+ $this->query_result = @$this->dbo->query($query);
+ }
+ }
+
+ if ($this->query_result === 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 (!$this->query_result)
+ {
+ return false;
+ }
+
+ 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)
+ {
+ /** @var \SQLite3Result $query_id */
+ $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);
+ if ($result)
+ {
+ 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/extractor/base_extractor.php b/phpBB/phpbb/db/extractor/base_extractor.php
new file mode 100644
index 0000000000..547c85f066
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/base_extractor.php
@@ -0,0 +1,252 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\invalid_format_exception;
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+/**
+ * Abstract base class for database extraction
+ */
+abstract class base_extractor implements extractor_interface
+{
+ /**
+ * @var string phpBB root path
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var \phpbb\request\request_interface
+ */
+ protected $request;
+
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var bool
+ */
+ protected $download;
+
+ /**
+ * @var bool
+ */
+ protected $store;
+
+ /**
+ * @var int
+ */
+ protected $time;
+
+ /**
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * @var resource
+ */
+ protected $fp;
+
+ /**
+ * @var string
+ */
+ protected $write;
+
+ /**
+ * @var string
+ */
+ protected $close;
+
+ /**
+ * @var bool
+ */
+ protected $run_comp;
+
+ /**
+ * @var bool
+ */
+ protected $is_initialized;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path
+ * @param \phpbb\request\request_interface $request
+ * @param \phpbb\db\driver\driver_interface $db
+ */
+ public function __construct($phpbb_root_path, \phpbb\request\request_interface $request, \phpbb\db\driver\driver_interface $db)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->request = $request;
+ $this->db = $db;
+ $this->fp = null;
+
+ $this->is_initialized = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function init_extractor($format, $filename, $time, $download = false, $store = false)
+ {
+ $this->download = $download;
+ $this->store = $store;
+ $this->time = $time;
+ $this->format = $format;
+
+ switch ($format)
+ {
+ case 'text':
+ $ext = '.sql';
+ $open = 'fopen';
+ $this->write = 'fwrite';
+ $this->close = 'fclose';
+ $mimetype = 'text/x-sql';
+ break;
+ case 'bzip2':
+ $ext = '.sql.bz2';
+ $open = 'bzopen';
+ $this->write = 'bzwrite';
+ $this->close = 'bzclose';
+ $mimetype = 'application/x-bzip2';
+ break;
+ case 'gzip':
+ $ext = '.sql.gz';
+ $open = 'gzopen';
+ $this->write = 'gzwrite';
+ $this->close = 'gzclose';
+ $mimetype = 'application/x-gzip';
+ break;
+ default:
+ throw new invalid_format_exception();
+ break;
+ }
+
+ if ($download === true)
+ {
+ $name = $filename . $ext;
+ header('Cache-Control: private, no-cache');
+ header("Content-Type: $mimetype; name=\"$name\"");
+ header("Content-disposition: attachment; filename=$name");
+
+ switch ($format)
+ {
+ case 'bzip2':
+ ob_start();
+ break;
+
+ case 'gzip':
+ if (strpos($this->request->header('Accept-Encoding'), 'gzip') !== false && strpos(strtolower($this->request->header('User-Agent')), 'msie') === false)
+ {
+ ob_start('ob_gzhandler');
+ }
+ else
+ {
+ $this->run_comp = true;
+ }
+ break;
+ }
+ }
+
+ if ($store === true)
+ {
+ $file = $this->phpbb_root_path . 'store/' . $filename . $ext;
+
+ $this->fp = $open($file, 'w');
+
+ if (!$this->fp)
+ {
+ trigger_error('FILE_WRITE_FAIL', E_USER_ERROR);
+ }
+ }
+
+ $this->is_initialized = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_end()
+ {
+ static $close;
+
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ if ($this->store)
+ {
+ if ($close === null)
+ {
+ $close = $this->close;
+ }
+ $close($this->fp);
+ }
+
+ // bzip2 must be written all the way at the end
+ if ($this->download && $this->format === 'bzip2')
+ {
+ $c = ob_get_clean();
+ echo bzcompress($c);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function flush($data)
+ {
+ static $write;
+
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ if ($this->store === true)
+ {
+ if ($write === null)
+ {
+ $write = $this->write;
+ }
+ $write($this->fp, $data);
+ }
+
+ if ($this->download === true)
+ {
+ if ($this->format === 'bzip2' || $this->format === 'text' || ($this->format === 'gzip' && !$this->run_comp))
+ {
+ echo $data;
+ }
+
+ // we can write the gzip data as soon as we get it
+ if ($this->format === 'gzip')
+ {
+ if ($this->run_comp)
+ {
+ echo gzencode($data);
+ }
+ else
+ {
+ ob_flush();
+ flush();
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php b/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php
new file mode 100644
index 0000000000..62eb434be1
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/exception/extractor_not_initialized_exception.php
@@ -0,0 +1,24 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor\exception;
+
+use phpbb\exception\runtime_exception;
+
+/**
+* This exception is thrown when invalid format is given to the extractor
+*/
+class extractor_not_initialized_exception extends runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php b/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php
new file mode 100644
index 0000000000..6be24cb5dc
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/exception/invalid_format_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor\exception;
+
+/**
+* This exception is thrown when invalid format is given to the extractor
+*/
+class invalid_format_exception extends \InvalidArgumentException
+{
+
+}
diff --git a/phpBB/phpbb/db/extractor/extractor_interface.php b/phpBB/phpbb/db/extractor/extractor_interface.php
new file mode 100644
index 0000000000..ff45df9bb7
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/extractor_interface.php
@@ -0,0 +1,80 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+/**
+* Database extractor interface
+*/
+interface extractor_interface
+{
+ /**
+ * Start the extraction of the database
+ *
+ * This function initialize the database extraction. It is required to call this
+ * function before calling any other extractor functions.
+ *
+ * @param string $format
+ * @param string $filename
+ * @param int $time
+ * @param bool $download
+ * @param bool $store
+ * @return null
+ * @throws \phpbb\db\extractor\exception\invalid_format_exception when $format is invalid
+ */
+ public function init_extractor($format, $filename, $time, $download = false, $store = false);
+
+ /**
+ * Writes header comments to the database backup
+ *
+ * @param string $table_prefix prefix of phpBB database tables
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_start($table_prefix);
+
+ /**
+ * Closes file and/or dumps download data
+ *
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_end();
+
+ /**
+ * Extracts database table structure
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_table($table_name);
+
+ /**
+ * Extracts data from database table
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_data($table_name);
+
+ /**
+ * Writes data to file/download content
+ *
+ * @param string $data
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function flush($data);
+}
diff --git a/phpBB/phpbb/db/extractor/factory.php b/phpBB/phpbb/db/extractor/factory.php
new file mode 100644
index 0000000000..a1ffb65595
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/factory.php
@@ -0,0 +1,79 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+/**
+* A factory which serves the suitable extractor instance for the given dbal
+*/
+class factory
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * Extractor factory constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \Symfony\Component\DependencyInjection\ContainerInterface $container)
+ {
+ $this->db = $db;
+ $this->container = $container;
+ }
+
+ /**
+ * DB extractor factory getter
+ *
+ * @return \phpbb\db\extractor\extractor_interface an appropriate instance of the database extractor for the used database driver
+ * @throws \InvalidArgumentException when the database driver is unknown
+ */
+ public function get()
+ {
+ // Return the appropriate DB extractor
+ if ($this->db instanceof \phpbb\db\driver\mssql || $this->db instanceof \phpbb\db\driver\mssql_base)
+ {
+ return $this->container->get('dbal.extractor.extractors.mssql_extractor');
+ }
+ else if ($this->db instanceof \phpbb\db\driver\mysql_base)
+ {
+ return $this->container->get('dbal.extractor.extractors.mysql_extractor');
+ }
+ else if ($this->db instanceof \phpbb\db\driver\oracle)
+ {
+ return $this->container->get('dbal.extractor.extractors.oracle_extractor');
+ }
+ else if ($this->db instanceof \phpbb\db\driver\postgres)
+ {
+ return $this->container->get('dbal.extractor.extractors.postgres_extractor');
+ }
+ else if ($this->db instanceof \phpbb\db\driver\sqlite)
+ {
+ return $this->container->get('dbal.extractor.extractors.sqlite_extractor');
+ }
+ else if ($this->db instanceof \phpbb\db\driver\sqlite3)
+ {
+ return $this->container->get('dbal.extractor.extractors.sqlite3_extractor');
+ }
+
+ throw new \InvalidArgumentException('Invalid database driver given');
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/mssql_extractor.php b/phpBB/phpbb/db/extractor/mssql_extractor.php
new file mode 100644
index 0000000000..fc30f4789d
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/mssql_extractor.php
@@ -0,0 +1,524 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class mssql_extractor extends base_extractor
+{
+ /**
+ * Writes closing line(s) to database backup
+ *
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_end()
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $this->flush("COMMIT\nGO\n");
+ parent::write_end();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "--\n";
+ $sql_data .= "-- phpBB Backup Script\n";
+ $sql_data .= "-- Dump of tables for $table_prefix\n";
+ $sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "--\n";
+ $sql_data .= "BEGIN TRANSACTION\n";
+ $sql_data .= "GO\n";
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = '-- Table: ' . $table_name . "\n";
+ $sql_data .= "IF OBJECT_ID(N'$table_name', N'U') IS NOT NULL\n";
+ $sql_data .= "DROP TABLE $table_name;\n";
+ $sql_data .= "GO\n";
+ $sql_data .= "\nCREATE TABLE [$table_name] (\n";
+ $rows = array();
+
+ $text_flag = false;
+
+ $sql = "SELECT COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity') as IS_IDENTITY
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_NAME = '$table_name'";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $line = "\t[{$row['COLUMN_NAME']}] [{$row['DATA_TYPE']}]";
+
+ if ($row['DATA_TYPE'] == 'text')
+ {
+ $text_flag = true;
+ }
+
+ if ($row['IS_IDENTITY'])
+ {
+ $line .= ' IDENTITY (1 , 1)';
+ }
+
+ if ($row['CHARACTER_MAXIMUM_LENGTH'] && $row['DATA_TYPE'] !== 'text')
+ {
+ $line .= ' (' . $row['CHARACTER_MAXIMUM_LENGTH'] . ')';
+ }
+
+ if ($row['IS_NULLABLE'] == 'YES')
+ {
+ $line .= ' NULL';
+ }
+ else
+ {
+ $line .= ' NOT NULL';
+ }
+
+ if ($row['COLUMN_DEFAULT'])
+ {
+ $line .= ' DEFAULT ' . $row['COLUMN_DEFAULT'];
+ }
+
+ $rows[] = $line;
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql_data .= implode(",\n", $rows);
+ $sql_data .= "\n) ON [PRIMARY]";
+
+ if ($text_flag)
+ {
+ $sql_data .= " TEXTIMAGE_ON [PRIMARY]";
+ }
+
+ $sql_data .= "\nGO\n\n";
+ $rows = array();
+
+ $sql = "SELECT CONSTRAINT_NAME, COLUMN_NAME
+ FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
+ WHERE TABLE_NAME = '$table_name'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!sizeof($rows))
+ {
+ $sql_data .= "ALTER TABLE [$table_name] WITH NOCHECK ADD\n";
+ $sql_data .= "\tCONSTRAINT [{$row['CONSTRAINT_NAME']}] PRIMARY KEY CLUSTERED \n\t(\n";
+ }
+ $rows[] = "\t\t[{$row['COLUMN_NAME']}]";
+ }
+ if (sizeof($rows))
+ {
+ $sql_data .= implode(",\n", $rows);
+ $sql_data .= "\n\t) ON [PRIMARY] \nGO\n";
+ }
+ $this->db->sql_freeresult($result);
+
+ $index = array();
+ $sql = "EXEC sp_statistics '$table_name'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['TYPE'] == 3)
+ {
+ $index[$row['INDEX_NAME']][] = '[' . $row['COLUMN_NAME'] . ']';
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($index as $index_name => $column_name)
+ {
+ $index[$index_name] = implode(', ', $column_name);
+ }
+
+ foreach ($index as $index_name => $columns)
+ {
+ $sql_data .= "\nCREATE INDEX [$index_name] ON [$table_name]($columns) ON [PRIMARY]\nGO\n";
+ }
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ if ($this->db->get_sql_layer() === 'mssql')
+ {
+ $this->write_data_mssql($table_name);
+ }
+ else if ($this->db->get_sql_layer() === 'mssqlnative')
+ {
+ $this->write_data_mssqlnative($table_name);
+ }
+ else
+ {
+ $this->write_data_odbc($table_name);
+ }
+ }
+
+ /**
+ * Extracts data from database table (for MSSQL driver)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function write_data_mssql($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $ary_type = $ary_name = array();
+ $ident_set = false;
+ $sql_data = '';
+
+ // Grab all of the data from current table.
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ $retrieved_data = mssql_num_rows($result);
+
+ $i_num_fields = mssql_num_fields($result);
+
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $ary_type[$i] = mssql_field_type($result, $i);
+ $ary_name[$i] = mssql_field_name($result, $i);
+ }
+
+ if ($retrieved_data)
+ {
+ $sql = "SELECT 1 as has_identity
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
+ $result2 = $this->db->sql_query($sql);
+ $row2 = $this->db->sql_fetchrow($result2);
+ if (!empty($row2['has_identity']))
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
+ $ident_set = true;
+ }
+ $this->db->sql_freeresult($result2);
+ }
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $schema_vals = $schema_fields = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $str_val = $row[$ary_name[$i]];
+
+ if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
+ {
+ $str_quote = '';
+ $str_empty = "''";
+ $str_val = sanitize_data_mssql(str_replace("'", "''", $str_val));
+ }
+ else if (preg_match('#date|timestamp#i', $ary_type[$i]))
+ {
+ if (empty($str_val))
+ {
+ $str_quote = '';
+ }
+ else
+ {
+ $str_quote = "'";
+ }
+ }
+ else
+ {
+ $str_quote = '';
+ $str_empty = 'NULL';
+ }
+
+ if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val)))
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[$i] = $str_quote . $str_val . $str_quote;
+ $schema_fields[$i] = $ary_name[$i];
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n";
+
+ $this->flush($sql_data);
+ $sql_data = '';
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($retrieved_data && $ident_set)
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n";
+ }
+ $this->flush($sql_data);
+ }
+
+ /**
+ * Extracts data from database table (for MSSQL Native driver)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function write_data_mssqlnative($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $ary_type = $ary_name = array();
+ $ident_set = false;
+ $sql_data = '';
+
+ // Grab all of the data from current table.
+ $sql = "SELECT * FROM $table_name";
+ $this->db->mssqlnative_set_query_options(array('Scrollable' => SQLSRV_CURSOR_STATIC));
+ $result = $this->db->sql_query($sql);
+
+ $retrieved_data = $this->db->mssqlnative_num_rows($result);
+
+ if (!$retrieved_data)
+ {
+ $this->db->sql_freeresult($result);
+ return;
+ }
+
+ $sql = "SELECT COLUMN_NAME, DATA_TYPE
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE INFORMATION_SCHEMA.COLUMNS.TABLE_NAME = '" . $this->db->sql_escape($table_name) . "'";
+ $result_fields = $this->db->sql_query($sql);
+
+ $i_num_fields = 0;
+ while ($row = $this->db->sql_fetchrow($result_fields))
+ {
+ $ary_type[$i_num_fields] = $row['DATA_TYPE'];
+ $ary_name[$i_num_fields] = $row['COLUMN_NAME'];
+ $i_num_fields++;
+ }
+ $this->db->sql_freeresult($result_fields);
+
+ $sql = "SELECT 1 as has_identity
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
+ $result2 = $this->db->sql_query($sql);
+ $row2 = $this->db->sql_fetchrow($result2);
+
+ if (!empty($row2['has_identity']))
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
+ $ident_set = true;
+ }
+ $this->db->sql_freeresult($result2);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $schema_vals = $schema_fields = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $str_val = $row[$ary_name[$i]];
+
+ // defaults to type number - better quote just to be safe, so check for is_int too
+ if (is_int($ary_type[$i]) || preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
+ {
+ $str_quote = '';
+ $str_empty = "''";
+ $str_val = sanitize_data_mssql(str_replace("'", "''", $str_val));
+ }
+ else if (preg_match('#date|timestamp#i', $ary_type[$i]))
+ {
+ if (empty($str_val))
+ {
+ $str_quote = '';
+ }
+ else
+ {
+ $str_quote = "'";
+ }
+ }
+ else
+ {
+ $str_quote = '';
+ $str_empty = 'NULL';
+ }
+
+ if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val)))
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[$i] = $str_quote . $str_val . $str_quote;
+ $schema_fields[$i] = $ary_name[$i];
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n";
+
+ $this->flush($sql_data);
+ $sql_data = '';
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($ident_set)
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n";
+ }
+ $this->flush($sql_data);
+ }
+
+ /**
+ * Extracts data from database table (for ODBC driver)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function write_data_odbc($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $ary_type = $ary_name = array();
+ $ident_set = false;
+ $sql_data = '';
+
+ // Grab all of the data from current table.
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ $retrieved_data = odbc_num_rows($result);
+
+ if ($retrieved_data)
+ {
+ $sql = "SELECT 1 as has_identity
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE COLUMNPROPERTY(object_id('$table_name'), COLUMN_NAME, 'IsIdentity') = 1";
+ $result2 = $this->db->sql_query($sql);
+ $row2 = $this->db->sql_fetchrow($result2);
+ if (!empty($row2['has_identity']))
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name ON\nGO\n";
+ $ident_set = true;
+ }
+ $this->db->sql_freeresult($result2);
+ }
+
+ $i_num_fields = odbc_num_fields($result);
+
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $ary_type[$i] = odbc_field_type($result, $i + 1);
+ $ary_name[$i] = odbc_field_name($result, $i + 1);
+ }
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $schema_vals = $schema_fields = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $str_val = $row[$ary_name[$i]];
+
+ if (preg_match('#char|text|bool|varbinary#i', $ary_type[$i]))
+ {
+ $str_quote = '';
+ $str_empty = "''";
+ $str_val = sanitize_data_mssql(str_replace("'", "''", $str_val));
+ }
+ else if (preg_match('#date|timestamp#i', $ary_type[$i]))
+ {
+ if (empty($str_val))
+ {
+ $str_quote = '';
+ }
+ else
+ {
+ $str_quote = "'";
+ }
+ }
+ else
+ {
+ $str_quote = '';
+ $str_empty = 'NULL';
+ }
+
+ if (empty($str_val) && $str_val !== '0' && !(is_int($str_val) || is_float($str_val)))
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[$i] = $str_quote . $str_val . $str_quote;
+ $schema_fields[$i] = $ary_name[$i];
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $sql_data .= "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ");\nGO\n";
+
+ $this->flush($sql_data);
+
+ $sql_data = '';
+
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($retrieved_data && $ident_set)
+ {
+ $sql_data .= "\nSET IDENTITY_INSERT $table_name OFF\nGO\n";
+ }
+ $this->flush($sql_data);
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/mysql_extractor.php b/phpBB/phpbb/db/extractor/mysql_extractor.php
new file mode 100644
index 0000000000..34e309c19e
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/mysql_extractor.php
@@ -0,0 +1,403 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class mysql_extractor extends base_extractor
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "#\n";
+ $sql_data .= "# phpBB Backup Script\n";
+ $sql_data .= "# Dump of tables for $table_prefix\n";
+ $sql_data .= "# DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "#\n";
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ static $new_extract;
+
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ if ($new_extract === null)
+ {
+ if ($this->db->get_sql_layer() === 'mysqli' || version_compare($this->db->sql_server_info(true), '3.23.20', '>='))
+ {
+ $new_extract = true;
+ }
+ else
+ {
+ $new_extract = false;
+ }
+ }
+
+ if ($new_extract)
+ {
+ $this->new_write_table($table_name);
+ }
+ else
+ {
+ $this->old_write_table($table_name);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ if ($this->db->get_sql_layer() === 'mysqli')
+ {
+ $this->write_data_mysqli($table_name);
+ }
+ else
+ {
+ $this->write_data_mysql($table_name);
+ }
+ }
+
+ /**
+ * Extracts data from database table (for MySQLi driver)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function write_data_mysqli($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = mysqli_query($this->db->get_db_connect_id(), $sql, MYSQLI_USE_RESULT);
+ if ($result != false)
+ {
+ $fields_cnt = mysqli_num_fields($result);
+
+ // Get field information
+ $field = mysqli_fetch_fields($result);
+ $field_set = array();
+
+ for ($j = 0; $j < $fields_cnt; $j++)
+ {
+ $field_set[] = $field[$j]->name;
+ }
+
+ $search = array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"');
+ $replace = array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"');
+ $fields = implode(', ', $field_set);
+ $sql_data = 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES ';
+ $first_set = true;
+ $query_len = 0;
+ $max_len = get_usable_memory();
+
+ while ($row = mysqli_fetch_row($result))
+ {
+ $values = array();
+ if ($first_set)
+ {
+ $query = $sql_data . '(';
+ }
+ else
+ {
+ $query .= ',(';
+ }
+
+ for ($j = 0; $j < $fields_cnt; $j++)
+ {
+ if (!isset($row[$j]) || is_null($row[$j]))
+ {
+ $values[$j] = 'NULL';
+ }
+ else if (($field[$j]->flags & 32768) && !($field[$j]->flags & 1024))
+ {
+ $values[$j] = $row[$j];
+ }
+ else
+ {
+ $values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'";
+ }
+ }
+ $query .= implode(', ', $values) . ')';
+
+ $query_len += strlen($query);
+ if ($query_len > $max_len)
+ {
+ $this->flush($query . ";\n\n");
+ $query = '';
+ $query_len = 0;
+ $first_set = true;
+ }
+ else
+ {
+ $first_set = false;
+ }
+ }
+ mysqli_free_result($result);
+
+ // check to make sure we have nothing left to flush
+ if (!$first_set && $query)
+ {
+ $this->flush($query . ";\n\n");
+ }
+ }
+ }
+
+ /**
+ * Extracts data from database table (for MySQL driver)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function write_data_mysql($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = mysql_unbuffered_query($sql, $this->db->get_db_connect_id());
+
+ if ($result != false)
+ {
+ $fields_cnt = mysql_num_fields($result);
+
+ // Get field information
+ $field = array();
+ for ($i = 0; $i < $fields_cnt; $i++)
+ {
+ $field[] = mysql_fetch_field($result, $i);
+ }
+ $field_set = array();
+
+ for ($j = 0; $j < $fields_cnt; $j++)
+ {
+ $field_set[] = $field[$j]->name;
+ }
+
+ $search = array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"');
+ $replace = array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"');
+ $fields = implode(', ', $field_set);
+ $sql_data = 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES ';
+ $first_set = true;
+ $query_len = 0;
+ $max_len = get_usable_memory();
+
+ while ($row = mysql_fetch_row($result))
+ {
+ $values = array();
+ if ($first_set)
+ {
+ $query = $sql_data . '(';
+ }
+ else
+ {
+ $query .= ',(';
+ }
+
+ for ($j = 0; $j < $fields_cnt; $j++)
+ {
+ if (!isset($row[$j]) || is_null($row[$j]))
+ {
+ $values[$j] = 'NULL';
+ }
+ else if ($field[$j]->numeric && ($field[$j]->type !== 'timestamp'))
+ {
+ $values[$j] = $row[$j];
+ }
+ else
+ {
+ $values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'";
+ }
+ }
+ $query .= implode(', ', $values) . ')';
+
+ $query_len += strlen($query);
+ if ($query_len > $max_len)
+ {
+ $this->flush($query . ";\n\n");
+ $query = '';
+ $query_len = 0;
+ $first_set = true;
+ }
+ else
+ {
+ $first_set = false;
+ }
+ }
+ mysql_free_result($result);
+
+ // check to make sure we have nothing left to flush
+ if (!$first_set && $query)
+ {
+ $this->flush($query . ";\n\n");
+ }
+ }
+ }
+
+ /**
+ * Extracts database table structure (for MySQLi or MySQL 3.23.20+)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function new_write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql = 'SHOW CREATE TABLE ' . $table_name;
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+
+ $sql_data = '# Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE IF EXISTS $table_name;\n";
+ $this->flush($sql_data . $row['Create Table'] . ";\n\n");
+
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * Extracts database table structure (for MySQL verisons older than 3.23.20)
+ *
+ * @param string $table_name name of the database table
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ protected function old_write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = '# Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE IF EXISTS $table_name;\n";
+ $sql_data .= "CREATE TABLE $table_name(\n";
+ $rows = array();
+
+ $sql = "SHOW FIELDS
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $line = ' ' . $row['Field'] . ' ' . $row['Type'];
+
+ if (!is_null($row['Default']))
+ {
+ $line .= " DEFAULT '{$row['Default']}'";
+ }
+
+ if ($row['Null'] != 'YES')
+ {
+ $line .= ' NOT NULL';
+ }
+
+ if ($row['Extra'] != '')
+ {
+ $line .= ' ' . $row['Extra'];
+ }
+
+ $rows[] = $line;
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql = "SHOW KEYS
+ FROM $table_name";
+
+ $result = $this->db->sql_query($sql);
+
+ $index = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $kname = $row['Key_name'];
+
+ if ($kname != 'PRIMARY')
+ {
+ if ($row['Non_unique'] == 0)
+ {
+ $kname = "UNIQUE|$kname";
+ }
+ }
+
+ if ($row['Sub_part'])
+ {
+ $row['Column_name'] .= '(' . $row['Sub_part'] . ')';
+ }
+ $index[$kname][] = $row['Column_name'];
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($index as $key => $columns)
+ {
+ $line = ' ';
+
+ if ($key == 'PRIMARY')
+ {
+ $line .= 'PRIMARY KEY (' . implode(', ', $columns) . ')';
+ }
+ else if (strpos($key, 'UNIQUE') === 0)
+ {
+ $line .= 'UNIQUE ' . substr($key, 7) . ' (' . implode(', ', $columns) . ')';
+ }
+ else if (strpos($key, 'FULLTEXT') === 0)
+ {
+ $line .= 'FULLTEXT ' . substr($key, 9) . ' (' . implode(', ', $columns) . ')';
+ }
+ else
+ {
+ $line .= "KEY $key (" . implode(', ', $columns) . ')';
+ }
+
+ $rows[] = $line;
+ }
+
+ $sql_data .= implode(",\n", $rows);
+ $sql_data .= "\n);\n\n";
+
+ $this->flush($sql_data);
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/oracle_extractor.php b/phpBB/phpbb/db/extractor/oracle_extractor.php
new file mode 100644
index 0000000000..79a991889b
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/oracle_extractor.php
@@ -0,0 +1,263 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class oracle_extractor extends base_extractor
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = '-- Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE $table_name\n/\n";
+ $sql_data .= "\nCREATE TABLE $table_name (\n";
+
+ $sql = "SELECT COLUMN_NAME, DATA_TYPE, DATA_PRECISION, DATA_LENGTH, NULLABLE, DATA_DEFAULT
+ FROM ALL_TAB_COLS
+ WHERE table_name = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+
+ $rows = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $line = ' "' . $row['column_name'] . '" ' . $row['data_type'];
+
+ if ($row['data_type'] !== 'CLOB')
+ {
+ if ($row['data_type'] !== 'VARCHAR2' && $row['data_type'] !== 'CHAR')
+ {
+ $line .= '(' . $row['data_precision'] . ')';
+ }
+ else
+ {
+ $line .= '(' . $row['data_length'] . ')';
+ }
+ }
+
+ if (!empty($row['data_default']))
+ {
+ $line .= ' DEFAULT ' . $row['data_default'];
+ }
+
+ if ($row['nullable'] == 'N')
+ {
+ $line .= ' NOT NULL';
+ }
+ $rows[] = $line;
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql = "SELECT A.CONSTRAINT_NAME, A.COLUMN_NAME
+ FROM USER_CONS_COLUMNS A, USER_CONSTRAINTS B
+ WHERE A.CONSTRAINT_NAME = B.CONSTRAINT_NAME
+ AND B.CONSTRAINT_TYPE = 'P'
+ AND A.TABLE_NAME = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+
+ $primary_key = array();
+ $constraint_name = '';
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $constraint_name = '"' . $row['constraint_name'] . '"';
+ $primary_key[] = '"' . $row['column_name'] . '"';
+ }
+ $this->db->sql_freeresult($result);
+
+ if (sizeof($primary_key))
+ {
+ $rows[] = " CONSTRAINT {$constraint_name} PRIMARY KEY (" . implode(', ', $primary_key) . ')';
+ }
+
+ $sql = "SELECT A.CONSTRAINT_NAME, A.COLUMN_NAME
+ FROM USER_CONS_COLUMNS A, USER_CONSTRAINTS B
+ WHERE A.CONSTRAINT_NAME = B.CONSTRAINT_NAME
+ AND B.CONSTRAINT_TYPE = 'U'
+ AND A.TABLE_NAME = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+
+ $unique = array();
+ $constraint_name = '';
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $constraint_name = '"' . $row['constraint_name'] . '"';
+ $unique[] = '"' . $row['column_name'] . '"';
+ }
+ $this->db->sql_freeresult($result);
+
+ if (sizeof($unique))
+ {
+ $rows[] = " CONSTRAINT {$constraint_name} UNIQUE (" . implode(', ', $unique) . ')';
+ }
+
+ $sql_data .= implode(",\n", $rows);
+ $sql_data .= "\n)\n/\n";
+
+ $sql = "SELECT A.REFERENCED_NAME, C.*
+ FROM USER_DEPENDENCIES A, USER_TRIGGERS B, USER_SEQUENCES C
+ WHERE A.REFERENCED_TYPE = 'SEQUENCE'
+ AND A.NAME = B.TRIGGER_NAME
+ AND B.TABLE_NAME = '{$table_name}'
+ AND C.SEQUENCE_NAME = A.REFERENCED_NAME";
+ $result = $this->db->sql_query($sql);
+
+ $type = $this->request->variable('type', '');
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_data .= "\nDROP SEQUENCE \"{$row['referenced_name']}\"\n/\n";
+ $sql_data .= "\nCREATE SEQUENCE \"{$row['referenced_name']}\"";
+
+ if ($type == 'full')
+ {
+ $sql_data .= ' START WITH ' . $row['last_number'];
+ }
+
+ $sql_data .= "\n/\n";
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql = "SELECT DESCRIPTION, WHEN_CLAUSE, TRIGGER_BODY
+ FROM USER_TRIGGERS
+ WHERE TABLE_NAME = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_data .= "\nCREATE OR REPLACE TRIGGER {$row['description']}WHEN ({$row['when_clause']})\n{$row['trigger_body']}\n/\n";
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql = "SELECT A.INDEX_NAME, B.COLUMN_NAME
+ FROM USER_INDEXES A, USER_IND_COLUMNS B
+ WHERE A.UNIQUENESS = 'NONUNIQUE'
+ AND A.INDEX_NAME = B.INDEX_NAME
+ AND B.TABLE_NAME = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+
+ $index = array();
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $index[$row['index_name']][] = $row['column_name'];
+ }
+
+ foreach ($index as $index_name => $column_names)
+ {
+ $sql_data .= "\nCREATE INDEX $index_name ON $table_name(" . implode(', ', $column_names) . ")\n/\n";
+ }
+ $this->db->sql_freeresult($result);
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $ary_type = $ary_name = array();
+
+ // Grab all of the data from current table.
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ $i_num_fields = ocinumcols($result);
+
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $ary_type[$i] = ocicolumntype($result, $i + 1);
+ $ary_name[$i] = ocicolumnname($result, $i + 1);
+ }
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $schema_vals = $schema_fields = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ // Oracle uses uppercase - we use lowercase
+ $str_val = $row[strtolower($ary_name[$i])];
+
+ if (preg_match('#char|text|bool|raw|clob#i', $ary_type[$i]))
+ {
+ $str_quote = '';
+ $str_empty = "''";
+ $str_val = sanitize_data_oracle($str_val);
+ }
+ else if (preg_match('#date|timestamp#i', $ary_type[$i]))
+ {
+ if (empty($str_val))
+ {
+ $str_quote = '';
+ }
+ else
+ {
+ $str_quote = "'";
+ }
+ }
+ else
+ {
+ $str_quote = '';
+ $str_empty = 'NULL';
+ }
+
+ if (empty($str_val) && $str_val !== '0')
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[$i] = $str_quote . $str_val . $str_quote;
+ $schema_fields[$i] = '"' . $ary_name[$i] . '"';
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $sql_data = "INSERT INTO $table_name (" . implode(', ', $schema_fields) . ') VALUES (' . implode(', ', $schema_vals) . ")\n/\n";
+
+ $this->flush($sql_data);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "--\n";
+ $sql_data .= "-- phpBB Backup Script\n";
+ $sql_data .= "-- Dump of tables for $table_prefix\n";
+ $sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "--\n";
+ $this->flush($sql_data);
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/postgres_extractor.php b/phpBB/phpbb/db/extractor/postgres_extractor.php
new file mode 100644
index 0000000000..a98e39621c
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/postgres_extractor.php
@@ -0,0 +1,339 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class postgres_extractor extends base_extractor
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "--\n";
+ $sql_data .= "-- phpBB Backup Script\n";
+ $sql_data .= "-- Dump of tables for $table_prefix\n";
+ $sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "--\n";
+ $sql_data .= "BEGIN TRANSACTION;\n";
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ static $domains_created = array();
+
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql = "SELECT a.domain_name, a.data_type, a.character_maximum_length, a.domain_default
+ FROM INFORMATION_SCHEMA.domains a, INFORMATION_SCHEMA.column_domain_usage b
+ WHERE a.domain_name = b.domain_name
+ AND b.table_name = '{$table_name}'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (empty($domains_created[$row['domain_name']]))
+ {
+ $domains_created[$row['domain_name']] = true;
+ //$sql_data = "DROP DOMAIN {$row['domain_name']};\n";
+ $sql_data = "CREATE DOMAIN {$row['domain_name']} as {$row['data_type']}";
+ if (!empty($row['character_maximum_length']))
+ {
+ $sql_data .= '(' . $row['character_maximum_length'] . ')';
+ }
+ $sql_data .= ' NOT NULL';
+ if (!empty($row['domain_default']))
+ {
+ $sql_data .= ' DEFAULT ' . $row['domain_default'];
+ }
+ $this->flush($sql_data . ";\n");
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql_data = '-- Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE $table_name;\n";
+ // PGSQL does not "tightly" bind sequences and tables, we must guess...
+ $sql = "SELECT relname
+ FROM pg_class
+ WHERE relkind = 'S'
+ AND relname = '{$table_name}_seq'";
+ $result = $this->db->sql_query($sql);
+ // We don't even care about storing the results. We already know the answer if we get rows back.
+ if ($this->db->sql_fetchrow($result))
+ {
+ $sql_data .= "DROP SEQUENCE {$table_name}_seq;\n";
+ $sql_data .= "CREATE SEQUENCE {$table_name}_seq;\n";
+ }
+ $this->db->sql_freeresult($result);
+
+ $field_query = "SELECT a.attnum, a.attname as field, t.typname as type, a.attlen as length, a.atttypmod as lengthvar, a.attnotnull as notnull
+ FROM pg_class c, pg_attribute a, pg_type t
+ WHERE c.relname = '" . $this->db->sql_escape($table_name) . "'
+ AND a.attnum > 0
+ AND a.attrelid = c.oid
+ AND a.atttypid = t.oid
+ ORDER BY a.attnum";
+ $result = $this->db->sql_query($field_query);
+
+ $sql_data .= "CREATE TABLE $table_name(\n";
+ $lines = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Get the data from the table
+ $sql_get_default = "SELECT pg_get_expr(d.adbin, d.adrelid) as rowdefault
+ FROM pg_attrdef d, pg_class c
+ WHERE (c.relname = '" . $this->db->sql_escape($table_name) . "')
+ AND (c.oid = d.adrelid)
+ AND d.adnum = " . $row['attnum'];
+ $def_res = $this->db->sql_query($sql_get_default);
+ $def_row = $this->db->sql_fetchrow($def_res);
+ $this->db->sql_freeresult($def_res);
+
+ if (empty($def_row))
+ {
+ unset($row['rowdefault']);
+ }
+ else
+ {
+ $row['rowdefault'] = $def_row['rowdefault'];
+ }
+
+ if ($row['type'] == 'bpchar')
+ {
+ // Internally stored as bpchar, but isn't accepted in a CREATE TABLE statement.
+ $row['type'] = 'char';
+ }
+
+ $line = ' ' . $row['field'] . ' ' . $row['type'];
+
+ if (strpos($row['type'], 'char') !== false)
+ {
+ if ($row['lengthvar'] > 0)
+ {
+ $line .= '(' . ($row['lengthvar'] - 4) . ')';
+ }
+ }
+
+ if (strpos($row['type'], 'numeric') !== false)
+ {
+ $line .= '(';
+ $line .= sprintf("%s,%s", (($row['lengthvar'] >> 16) & 0xffff), (($row['lengthvar'] - 4) & 0xffff));
+ $line .= ')';
+ }
+
+ if (isset($row['rowdefault']))
+ {
+ $line .= ' DEFAULT ' . $row['rowdefault'];
+ }
+
+ if ($row['notnull'] == 't')
+ {
+ $line .= ' NOT NULL';
+ }
+
+ $lines[] = $line;
+ }
+ $this->db->sql_freeresult($result);
+
+ // Get the listing of primary keys.
+ $sql_pri_keys = "SELECT ic.relname as index_name, bc.relname as tab_name, ta.attname as column_name, i.indisunique as unique_key, i.indisprimary as primary_key
+ FROM pg_class bc, pg_class ic, pg_index i, pg_attribute ta, pg_attribute ia
+ WHERE (bc.oid = i.indrelid)
+ AND (ic.oid = i.indexrelid)
+ AND (ia.attrelid = i.indexrelid)
+ AND (ta.attrelid = bc.oid)
+ AND (bc.relname = '" . $this->db->sql_escape($table_name) . "')
+ AND (ta.attrelid = i.indrelid)
+ AND (ta.attnum = i.indkey[ia.attnum-1])
+ ORDER BY index_name, tab_name, column_name";
+
+ $result = $this->db->sql_query($sql_pri_keys);
+
+ $index_create = $index_rows = $primary_key = array();
+
+ // We do this in two steps. It makes placing the comma easier
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['primary_key'] == 't')
+ {
+ $primary_key[] = $row['column_name'];
+ $primary_key_name = $row['index_name'];
+ }
+ else
+ {
+ // We have to store this all this info because it is possible to have a multi-column key...
+ // we can loop through it again and build the statement
+ $index_rows[$row['index_name']]['table'] = $table_name;
+ $index_rows[$row['index_name']]['unique'] = ($row['unique_key'] == 't') ? true : false;
+ $index_rows[$row['index_name']]['column_names'][] = $row['column_name'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($index_rows))
+ {
+ foreach ($index_rows as $idx_name => $props)
+ {
+ $index_create[] = 'CREATE ' . ($props['unique'] ? 'UNIQUE ' : '') . "INDEX $idx_name ON $table_name (" . implode(', ', $props['column_names']) . ");";
+ }
+ }
+
+ if (!empty($primary_key))
+ {
+ $lines[] = " CONSTRAINT $primary_key_name PRIMARY KEY (" . implode(', ', $primary_key) . ")";
+ }
+
+ // Generate constraint clauses for CHECK constraints
+ $sql_checks = "SELECT conname as index_name, consrc
+ FROM pg_constraint, pg_class bc
+ WHERE conrelid = bc.oid
+ AND bc.relname = '" . $this->db->sql_escape($table_name) . "'
+ AND NOT EXISTS (
+ SELECT *
+ FROM pg_constraint as c, pg_inherits as i
+ WHERE i.inhrelid = pg_constraint.conrelid
+ AND c.conname = pg_constraint.conname
+ AND c.consrc = pg_constraint.consrc
+ AND c.conrelid = i.inhparent
+ )";
+ $result = $this->db->sql_query($sql_checks);
+
+ // Add the constraints to the sql file.
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!is_null($row['consrc']))
+ {
+ $lines[] = ' CONSTRAINT ' . $row['index_name'] . ' CHECK ' . $row['consrc'];
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql_data .= implode(", \n", $lines);
+ $sql_data .= "\n);\n";
+
+ if (!empty($index_create))
+ {
+ $sql_data .= implode("\n", $index_create) . "\n\n";
+ }
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ // Grab all of the data from current table.
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ $i_num_fields = pg_num_fields($result);
+ $seq = '';
+
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $ary_type[] = pg_field_type($result, $i);
+ $ary_name[] = pg_field_name($result, $i);
+
+ $sql = "SELECT pg_get_expr(d.adbin, d.adrelid) as rowdefault
+ FROM pg_attrdef d, pg_class c
+ WHERE (c.relname = '{$table_name}')
+ AND (c.oid = d.adrelid)
+ AND d.adnum = " . strval($i + 1);
+ $result2 = $this->db->sql_query($sql);
+ if ($row = $this->db->sql_fetchrow($result2))
+ {
+ // Determine if we must reset the sequences
+ if (strpos($row['rowdefault'], "nextval('") === 0)
+ {
+ $seq .= "SELECT SETVAL('{$table_name}_seq',(select case when max({$ary_name[$i]})>0 then max({$ary_name[$i]})+1 else 1 end FROM {$table_name}));\n";
+ }
+ }
+ }
+
+ $this->flush("COPY $table_name (" . implode(', ', $ary_name) . ') FROM stdin;' . "\n");
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $schema_vals = array();
+
+ // Build the SQL statement to recreate the data.
+ for ($i = 0; $i < $i_num_fields; $i++)
+ {
+ $str_val = $row[$ary_name[$i]];
+
+ if (preg_match('#char|text|bool|bytea#i', $ary_type[$i]))
+ {
+ $str_val = str_replace(array("\n", "\t", "\r", "\b", "\f", "\v"), array('\n', '\t', '\r', '\b', '\f', '\v'), addslashes($str_val));
+ $str_empty = '';
+ }
+ else
+ {
+ $str_empty = '\N';
+ }
+
+ if (empty($str_val) && $str_val !== '0')
+ {
+ $str_val = $str_empty;
+ }
+
+ $schema_vals[] = $str_val;
+ }
+
+ // Take the ordered fields and their associated data and build it
+ // into a valid sql statement to recreate that field in the data.
+ $this->flush(implode("\t", $schema_vals) . "\n");
+ }
+ $this->db->sql_freeresult($result);
+ $this->flush("\\.\n");
+
+ // Write out the sequence statements
+ $this->flush($seq);
+ }
+
+ /**
+ * Writes closing line(s) to database backup
+ *
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_end()
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $this->flush("COMMIT;\n");
+ parent::write_end();
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/sqlite3_extractor.php b/phpBB/phpbb/db/extractor/sqlite3_extractor.php
new file mode 100644
index 0000000000..ce8da6a652
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/sqlite3_extractor.php
@@ -0,0 +1,151 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class sqlite3_extractor extends base_extractor
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "--\n";
+ $sql_data .= "-- phpBB Backup Script\n";
+ $sql_data .= "-- Dump of tables for $table_prefix\n";
+ $sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "--\n";
+ $sql_data .= "BEGIN TRANSACTION;\n";
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = '-- Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE $table_name;\n";
+
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'table'
+ AND name = '" . $this->db->sql_escape($table_name) . "'
+ ORDER BY name ASC;";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ // Create Table
+ $sql_data .= $row['sql'] . ";\n";
+
+ $result = $this->db->sql_query("PRAGMA index_list('" . $this->db->sql_escape($table_name) . "');");
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (strpos($row['name'], 'autoindex') !== false)
+ {
+ continue;
+ }
+
+ $result2 = $this->db->sql_query("PRAGMA index_info('" . $this->db->sql_escape($row['name']) . "');");
+
+ $fields = array();
+ while ($row2 = $this->db->sql_fetchrow($result2))
+ {
+ $fields[] = $row2['name'];
+ }
+ $this->db->sql_freeresult($result2);
+
+ $sql_data .= 'CREATE ' . ($row['unique'] ? 'UNIQUE ' : '') . 'INDEX ' . $row['name'] . ' ON ' . $table_name . ' (' . implode(', ', $fields) . ");\n";
+ }
+ $this->db->sql_freeresult($result);
+
+ $this->flush($sql_data . "\n");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $result = $this->db->sql_query("PRAGMA table_info('" . $this->db->sql_escape($table_name) . "');");
+
+ $col_types = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $col_types[$row['name']] = $row['type'];
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql_insert = 'INSERT INTO ' . $table_name . ' (' . implode(', ', array_keys($col_types)) . ') VALUES (';
+
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ foreach ($row as $column_name => $column_data)
+ {
+ if (is_null($column_data))
+ {
+ $row[$column_name] = 'NULL';
+ }
+ else if ($column_data === '')
+ {
+ $row[$column_name] = "''";
+ }
+ else if (stripos($col_types[$column_name], 'text') !== false || stripos($col_types[$column_name], 'char') !== false || stripos($col_types[$column_name], 'blob') !== false)
+ {
+ $row[$column_name] = sanitize_data_generic(str_replace("'", "''", $column_data));
+ }
+ }
+ $this->flush($sql_insert . implode(', ', $row) . ");\n");
+ }
+ }
+
+ /**
+ * Writes closing line(s) to database backup
+ *
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_end()
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $this->flush("COMMIT;\n");
+ parent::write_end();
+ }
+}
diff --git a/phpBB/phpbb/db/extractor/sqlite_extractor.php b/phpBB/phpbb/db/extractor/sqlite_extractor.php
new file mode 100644
index 0000000000..2734e23235
--- /dev/null
+++ b/phpBB/phpbb/db/extractor/sqlite_extractor.php
@@ -0,0 +1,149 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\extractor;
+
+use phpbb\db\extractor\exception\extractor_not_initialized_exception;
+
+class sqlite_extractor extends base_extractor
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function write_start($table_prefix)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = "--\n";
+ $sql_data .= "-- phpBB Backup Script\n";
+ $sql_data .= "-- Dump of tables for $table_prefix\n";
+ $sql_data .= "-- DATE : " . gmdate("d-m-Y H:i:s", $this->time) . " GMT\n";
+ $sql_data .= "--\n";
+ $sql_data .= "BEGIN TRANSACTION;\n";
+ $this->flush($sql_data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_table($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $sql_data = '-- Table: ' . $table_name . "\n";
+ $sql_data .= "DROP TABLE $table_name;\n";
+
+ $sql = "SELECT sql
+ FROM sqlite_master
+ WHERE type = 'table'
+ AND name = '" . $this->db->sql_escape($table_name) . "'
+ ORDER BY type DESC, name;";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ // Create Table
+ $sql_data .= $row['sql'] . ";\n";
+
+ $result = $this->db->sql_query("PRAGMA index_list('" . $this->db->sql_escape($table_name) . "');");
+
+ $ar = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $ar[] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($ar as $value)
+ {
+ if (strpos($value['name'], 'autoindex') !== false)
+ {
+ continue;
+ }
+
+ $result = $this->db->sql_query("PRAGMA index_info('" . $this->db->sql_escape($value['name']) . "');");
+
+ $fields = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $fields[] = $row['name'];
+ }
+ $this->db->sql_freeresult($result);
+
+ $sql_data .= 'CREATE ' . ($value['unique'] ? 'UNIQUE ' : '') . 'INDEX ' . $value['name'] . ' on ' . $table_name . ' (' . implode(', ', $fields) . ");\n";
+ }
+
+ $this->flush($sql_data . "\n");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write_data($table_name)
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $col_types = sqlite_fetch_column_types($this->db->get_db_connect_id(), $table_name);
+
+ $sql = "SELECT *
+ FROM $table_name";
+ $result = sqlite_unbuffered_query($this->db->get_db_connect_id(), $sql);
+ $rows = sqlite_fetch_all($result, SQLITE_ASSOC);
+ $sql_insert = 'INSERT INTO ' . $table_name . ' (' . implode(', ', array_keys($col_types)) . ') VALUES (';
+ foreach ($rows as $row)
+ {
+ foreach ($row as $column_name => $column_data)
+ {
+ if (is_null($column_data))
+ {
+ $row[$column_name] = 'NULL';
+ }
+ else if ($column_data == '')
+ {
+ $row[$column_name] = "''";
+ }
+ else if (strpos($col_types[$column_name], 'text') !== false || strpos($col_types[$column_name], 'char') !== false || strpos($col_types[$column_name], 'blob') !== false)
+ {
+ $row[$column_name] = sanitize_data_generic(str_replace("'", "''", $column_data));
+ }
+ }
+ $this->flush($sql_insert . implode(', ', $row) . ");\n");
+ }
+ }
+
+ /**
+ * Writes closing line(s) to database backup
+ *
+ * @return null
+ * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor()
+ */
+ public function write_end()
+ {
+ if (!$this->is_initialized)
+ {
+ throw new extractor_not_initialized_exception();
+ }
+
+ $this->flush("COMMIT;\n");
+ parent::write_end();
+ }
+}
diff --git a/phpBB/phpbb/db/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/local_url_bbcode.php b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php
index 5e6ba90336..648ae9ce96 100644
--- a/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php
+++ b/phpBB/phpbb/db/migration/data/v30x/local_url_bbcode.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -33,16 +37,23 @@ class local_url_bbcode extends \phpbb\db\migration\migration
{
$sql = 'SELECT *
FROM ' . BBCODES_TABLE . '
- WHERE bbcode_match ' . $this->db->sql_like_expression($this->db->any_char . 'LOCAL_URL' . $this->db->any_char);
+ 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'))
{
- global $phpEx;
- phpbb_require_updated('includes/acp/acp_bbcodes.' . $phpEx);
+ 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'];
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
index aed0f2784b..f5c7e56a81 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_1.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 305309c3bd..0d3a1ca80b 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_10.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index fb50d67fb5..293c46cb06 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 63ba1e8fc2..f2889120e6 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7055063032..9d6697ab7f 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 1246597efb..e77b54a1b5 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_11.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7e284235e1..ed2dabf51c 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 017038855d..45d88708bd 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 5a2d569724..c489c0c0e8 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 35a3015959..f9f6d9f7f7 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -27,7 +31,6 @@ class release_3_0_12_rc1 extends \phpbb\db\migration\migration
{
return array(
array('custom', array(array(&$this, 'update_module_auth'))),
- array('custom', array(array(&$this, 'update_bots'))),
array('custom', array(array(&$this, 'disable_bots_from_receiving_pms'))),
array('config.update', array('version', '3.0.12-RC1')),
@@ -66,60 +69,4 @@ class release_3_0_12_rc1 extends \phpbb\db\migration\migration
AND module_mode = \'signature\'';
$this->sql_query($sql);
}
-
- public function update_bots()
- {
- // Update bots
- if (!function_exists('user_delete'))
- {
- include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
- }
-
- $bots_updates = array(
- // Bot Deletions
- 'NG-Search [Bot]' => false,
- 'Nutch/CVS [Bot]' => false,
- 'OmniExplorer [Bot]' => false,
- 'Seekport [Bot]' => false,
- 'Synoo [Bot]' => false,
- 'WiseNut [Bot]' => false,
-
- // Bot Updates
- // Bot name to bot user agent map
- 'Baidu [Spider]' => 'Baiduspider',
- 'Exabot [Bot]' => 'Exabot',
- 'Voyager [Bot]' => 'voyager/',
- 'W3C [Validator]' => 'W3C_Validator',
- );
-
- foreach ($bots_updates as $bot_name => $bot_agent)
- {
- $sql = 'SELECT user_id
- FROM ' . USERS_TABLE . '
- WHERE user_type = ' . USER_IGNORE . "
- AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'";
- $result = $this->db->sql_query($sql);
- $bot_user_id = (int) $this->db->sql_fetchfield('user_id');
- $this->db->sql_freeresult($result);
-
- if ($bot_user_id)
- {
- if ($bot_agent === false)
- {
- $sql = 'DELETE FROM ' . BOTS_TABLE . "
- WHERE user_id = $bot_user_id";
- $this->sql_query($sql);
-
- user_delete('retain', $bot_user_id);
- }
- else
- {
- $sql = 'UPDATE ' . BOTS_TABLE . "
- SET bot_agent = '" . $this->db->sql_escape($bot_agent) . "'
- WHERE user_id = $bot_user_id";
- $this->sql_query($sql);
- }
- }
- }
- }
}
diff --git a/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php b/phpBB/phpbb/db/migration/data/v30x/release_3_0_12_rc2.php
index 3edb578fc8..8fac273073 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 510693a5b7..fb1b8014a4 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 862276528d..d1ae0b9cbc 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -16,6 +20,11 @@ class release_3_0_1_rc1 extends \phpbb\db\migration\migration
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(
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
index 7e2a08590e..c08f01dbeb 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7a856383e2..2e7f141a9b 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 61562575eb..bde5febc59 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index b2adbeaa43..c277da224c 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_3.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 57bd59bba3..530eaf47e6 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 5d6140393b..9b08da0125 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_4.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -32,7 +36,7 @@ class release_3_0_4 extends \phpbb\db\migration\migration
public function rename_log_delete_topic()
{
- if ($this->db->sql_layer == 'oracle')
+ if ($this->db->get_sql_layer() == 'oracle')
{
// log_operation is CLOB - but we can change this later
$sql = 'UPDATE ' . $this->table_prefix . "log
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
index a8af4dd76c..10343438b3 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7bbe7ffed9..09c2bfea50 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_5.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index ffe2c6a44d..084d00a13a 100644
--- 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
@@ -1,15 +1,21 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\db\migration\data\v30x;
-class release_3_0_5_rc1 extends \phpbb\db\migration\migration
+use phpbb\db\migration\container_aware_migration;
+
+class release_3_0_5_rc1 extends container_aware_migration
{
public function effectively_installed()
{
@@ -51,6 +57,9 @@ class release_3_0_5_rc1 extends \phpbb\db\migration\migration
public function hash_old_passwords()
{
+ /* @var $passwords_manager \phpbb\passwords\manager */
+ $passwords_manager = $this->container->get('passwords.manager');
+
$sql = 'SELECT user_id, user_password
FROM ' . $this->table_prefix . 'users
WHERE user_pass_convert = 1';
@@ -61,7 +70,7 @@ class release_3_0_5_rc1 extends \phpbb\db\migration\migration
if (strlen($row['user_password']) == 32)
{
$sql_ary = array(
- 'user_password' => phpbb_hash($row['user_password']),
+ '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']);
@@ -103,13 +112,13 @@ class release_3_0_5_rc1 extends \phpbb\db\migration\migration
// Select auth_option_ids... the largest id will be preserved
$sql = 'SELECT auth_option_id
FROM ' . ACL_OPTIONS_TABLE . "
- WHERE auth_option = '" . $db->sql_escape($option) . "'
+ 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);
+ $this->db->sql_fetchrow($result);
while ($row = $this->db->sql_fetchrow($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
index 04b14b5189..a9041ef354 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 85ea2e9d20..74c338a9c5 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_6.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 87d5e490f8..faef68121d 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7a0ef28601..c52b71d8fa 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 73a1fe9e6a..2db3341b0a 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index b6e5be2c2f..5734db25e6 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 2b0da30bc6..d1d602819d 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_7.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 3547ee77e1..784e810f14 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index de4d772808..1843c3f262 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -72,7 +76,7 @@ class release_3_0_7_rc1 extends \phpbb\db\migration\migration
{
// Delete all text-templates from the template_data
$sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . '
- WHERE template_filename ' . $this->db->sql_like_expression($this->db->any_char . '.txt');
+ 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
index 800803a753..e497a38765 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 6c8b1df6fc..04b5bd4a13 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_8.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 1a14e5c961..22fd51543b 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -26,7 +30,6 @@ class release_3_0_8_rc1 extends \phpbb\db\migration\migration
return array(
array('custom', array(array(&$this, 'update_file_extension_group_names'))),
array('custom', array(array(&$this, 'update_module_auth'))),
- array('custom', array(array(&$this, 'update_bots'))),
array('custom', array(array(&$this, 'delete_orphan_shadow_topics'))),
array('module.add', array(
'acp',
@@ -110,70 +113,6 @@ class release_3_0_8_rc1 extends \phpbb\db\migration\migration
$this->sql_query($sql);
}
- public function update_bots()
- {
- $bot_name = 'Bing [Bot]';
- $bot_name_clean = utf8_clean_string($bot_name);
-
- $sql = 'SELECT user_id
- FROM ' . USERS_TABLE . "
- WHERE username_clean = '" . $this->db->sql_escape($bot_name_clean) . "'";
- $result = $this->db->sql_query($sql);
- $bing_already_added = (bool) $this->db->sql_fetchfield('user_id');
- $this->db->sql_freeresult($result);
-
- if (!$bing_already_added)
- {
- $bot_agent = 'bingbot/';
- $bot_ip = '';
- $sql = 'SELECT group_id, group_colour
- FROM ' . GROUPS_TABLE . "
- WHERE group_name = 'BOTS'";
- $result = $this->db->sql_query($sql);
- $group_row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
-
- if (!$group_row)
- {
- // default fallback, should never get here
- $group_row['group_id'] = 6;
- $group_row['group_colour'] = '9E8DA7';
- }
-
- if (!function_exists('user_add'))
- {
- include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
- }
-
- $user_row = array(
- 'user_type' => USER_IGNORE,
- 'group_id' => $group_row['group_id'],
- 'username' => $bot_name,
- 'user_regdate' => time(),
- 'user_password' => '',
- 'user_colour' => $group_row['group_colour'],
- 'user_email' => '',
- 'user_lang' => $this->config['default_lang'],
- 'user_style' => $this->config['default_style'],
- 'user_timezone' => 0,
- 'user_dateformat' => $this->config['default_dateformat'],
- 'user_allow_massemail' => 0,
- );
-
- $user_id = user_add($user_row);
-
- $sql = 'INSERT INTO ' . BOTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', array(
- 'bot_active' => 1,
- 'bot_name' => (string) $bot_name,
- 'user_id' => (int) $user_id,
- 'bot_agent' => (string) $bot_agent,
- 'bot_ip' => (string) $bot_ip,
- ));
-
- $this->sql_query($sql);
- }
- }
-
public function delete_orphan_shadow_topics()
{
// Delete shadow topics pointing to not existing topics
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
index 9af2fce971..e69134c538 100644
--- a/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php
+++ b/phpBB/phpbb/db/migration/data/v30x/release_3_0_9.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 3fb790bc0d..5f928df47c 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -30,7 +34,7 @@ class release_3_0_9_rc1 extends \phpbb\db\migration\migration
// 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
+ // soon as the \phpbb\db\tools\tools class is capable of properly
// removing a primary key.
// 'attempt_id' => array('UINT', NULL, 'auto_increment'),
'attempt_ip' => array('VCHAR:40', ''),
@@ -85,7 +89,7 @@ class release_3_0_9_rc1 extends \phpbb\db\migration\migration
// Update file extension group names to use language strings, again.
$sql = 'SELECT group_id, group_name
FROM ' . EXTENSION_GROUPS_TABLE . '
- WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->any_char);
+ 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))
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
index cd79d24ade..46fd51e7fe 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 7e59b8f9e8..1696060735 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index e71d9defa6..fdc92b52f9 100644
--- 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
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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..725c57ca86
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php
@@ -0,0 +1,76 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public 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\container_aware_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);
+
+ $module_manager = $this->container->get('module.manager');
+ $module_manager->move_module($acp_prune_users_id, $acp_cat_users_id, 'acp');
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php
index 9f168f4fd6..4bd29f87d7 100644
--- a/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php
+++ b/phpBB/phpbb/db/migration/data/v310/acp_style_components_module.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/allow_cdn.php b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php
index aa471df6e7..286d20e20b 100644
--- a/phpBB/phpbb/db/migration/data/v310/allow_cdn.php
+++ b/phpBB/phpbb/db/migration/data/v310/allow_cdn.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha1.php b/phpBB/phpbb/db/migration/data/v310/alpha1.php
index 403e301e64..1df85bc64c 100644
--- a/phpBB/phpbb/db/migration/data/v310/alpha1.php
+++ b/phpBB/phpbb/db/migration/data/v310/alpha1.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha2.php b/phpBB/phpbb/db/migration/data/v310/alpha2.php
index 3c0853f924..78bc755ec9 100644
--- a/phpBB/phpbb/db/migration/data/v310/alpha2.php
+++ b/phpBB/phpbb/db/migration/data/v310/alpha2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/alpha3.php b/phpBB/phpbb/db/migration/data/v310/alpha3.php
index 4bd2231bb9..574d19d2f4 100644
--- a/phpBB/phpbb/db/migration/data/v310/alpha3.php
+++ b/phpBB/phpbb/db/migration/data/v310/alpha3.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php
index 971a7e8504..2d51bd53e4 100644
--- a/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php
+++ b/phpBB/phpbb/db/migration/data/v310/auth_provider_oauth.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index bdbdccf0c5..117e93239d 100644
--- a/phpBB/phpbb/db/migration/data/v310/avatar_types.php
+++ b/phpBB/phpbb/db/migration/data/v310/avatar_types.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/avatars.php b/phpBB/phpbb/db/migration/data/v310/avatars.php
index 80ce606f29..9b03a8fa94 100644
--- a/phpBB/phpbb/db/migration/data/v310/avatars.php
+++ b/phpBB/phpbb/db/migration/data/v310/avatars.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,7 +17,29 @@ class avatars extends \phpbb\db\migration\migration
{
public function effectively_installed()
{
- return isset($this->config['allow_avatar_gravatar']);
+ // Get current avatar type of guest user
+ $sql = 'SELECT user_avatar_type
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_id = ' . ANONYMOUS;
+ $result = $this->db->sql_query($sql);
+ $backup_type = $this->db->sql_fetchfield('user_avatar_type');
+ $this->db->sql_freeresult($result);
+
+ // Try to set avatar type to string
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_avatar_type = 'avatar.driver.upload'
+ WHERE user_id = " . ANONYMOUS;
+ $this->db->sql_return_on_error(true);
+ $effectively_installed = $this->db->sql_query($sql);
+ $this->db->sql_return_on_error();
+
+ // Restore avatar type of guest user to previous state
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_avatar_type = '{$backup_type}'
+ WHERE user_id = " . ANONYMOUS;
+ $this->db->sql_query($sql);
+
+ return $effectively_installed !== false;
}
static public function depends_on()
diff --git a/phpBB/phpbb/db/migration/data/v310/beta1.php b/phpBB/phpbb/db/migration/data/v310/beta1.php
new file mode 100644
index 0000000000..84887bd58e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta1.php
@@ -0,0 +1,37 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\alpha3',
+ '\phpbb\db\migration\data\v310\passwords_p2',
+ '\phpbb\db\migration\data\v310\postgres_fulltext_drop',
+ '\phpbb\db\migration\data\v310\profilefield_change_load_settings',
+ '\phpbb\db\migration\data\v310\profilefield_location',
+ '\phpbb\db\migration\data\v310\soft_delete_mod_convert2',
+ '\phpbb\db\migration\data\v310\ucp_popuppm_module',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta2.php b/phpBB/phpbb/db/migration/data/v310/beta2.php
new file mode 100644
index 0000000000..458e305c7b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta2.php
@@ -0,0 +1,33 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta1',
+ '\phpbb\db\migration\data\v310\acp_prune_users_module',
+ '\phpbb\db\migration\data\v310\profilefield_location_cleanup',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta3.php b/phpBB/phpbb/db/migration/data/v310/beta3.php
new file mode 100644
index 0000000000..a6c62bf936
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta3.php
@@ -0,0 +1,36 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta3 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta2',
+ '\phpbb\db\migration\data\v310\auth_provider_oauth2',
+ '\phpbb\db\migration\data\v310\board_contact_name',
+ '\phpbb\db\migration\data\v310\jquery_update2',
+ '\phpbb\db\migration\data\v310\live_searches_config',
+ '\phpbb\db\migration\data\v310\prune_shadow_topics',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b3')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/beta4.php b/phpBB/phpbb/db/migration/data/v310/beta4.php
new file mode 100644
index 0000000000..3e91d95178
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/beta4.php
@@ -0,0 +1,33 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class beta4 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta3',
+ '\phpbb\db\migration\data\v310\extensions_version_check_force_unstable',
+ '\phpbb\db\migration\data\v310\reset_missing_captcha_plugin',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-b4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/board_contact_name.php b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php
new file mode 100644
index 0000000000..6f5188720b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/board_contact_name.php
@@ -0,0 +1,34 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class board_contact_name extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return isset($this->config['board_contact_name']);
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\beta2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('board_contact_name', '')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/boardindex.php b/phpBB/phpbb/db/migration/data/v310/boardindex.php
index 27492f2d0d..77a8558f21 100644
--- a/phpBB/phpbb/db/migration/data/v310/boardindex.php
+++ b/phpBB/phpbb/db/migration/data/v310/boardindex.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 1a7ee7a9a6..438883c438 100644
--- a/phpBB/phpbb/db/migration/data/v310/config_db_text.php
+++ b/phpBB/phpbb/db/migration/data/v310/config_db_text.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index c1db883616..250258eea7 100644
--- a/phpBB/phpbb/db/migration/data/v310/dev.php
+++ b/phpBB/phpbb/db/migration/data/v310/dev.php
@@ -1,15 +1,19 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public 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
+class dev extends \phpbb\db\migration\container_aware_migration
{
public function effectively_installed()
{
@@ -23,6 +27,7 @@ class dev extends \phpbb\db\migration\migration
'\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',
);
}
@@ -199,18 +204,13 @@ class dev extends \phpbb\db\migration\migration
$language_management_module_id = $this->db->sql_fetchfield('module_id');
$this->db->sql_freeresult($result);
- if (!class_exists('acp_modules'))
- {
- include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
- }
// acp_modules calls adm_back_link, which is undefined at this point
if (!function_exists('adm_back_link'))
{
include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext);
}
- $module_manager = new \acp_modules();
- $module_manager->module_class = 'acp';
- $module_manager->move_module($language_module_id, $language_management_module_id);
+ $module_manager = $this->container->get('module.manager');
+ $module_manager->move_module($language_module_id, $language_management_module_id, 'acp');
}
public function update_ucp_pm_basename()
diff --git a/phpBB/phpbb/db/migration/data/v310/extensions.php b/phpBB/phpbb/db/migration/data/v310/extensions.php
index d8b38dbc9e..3171435482 100644
--- a/phpBB/phpbb/db/migration/data/v310/extensions.php
+++ b/phpBB/phpbb/db/migration/data/v310/extensions.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 814093caa9..362457cf23 100644
--- a/phpBB/phpbb/db/migration/data/v310/forgot_password.php
+++ b/phpBB/phpbb/db/migration/data/v310/forgot_password.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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..e84c7ee951
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/gold.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class gold extends \phpbb\db\migration\migration
+{
+ 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
index bd2de2b4d4..8011331e80 100644
--- a/phpBB/phpbb/db/migration/data/v310/jquery_update.php
+++ b/phpBB/phpbb/db/migration/data/v310/jquery_update.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index ffb790b135..85e479db59 100644
--- a/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php
+++ b/phpBB/phpbb/db/migration/data/v310/mod_rewrite.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php
index 97d174d4bc..e04a705c91 100644
--- a/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php
+++ b/phpBB/phpbb/db/migration/data/v310/mysql_fulltext_drop.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,10 +15,18 @@ 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.
- return strpos($this->db->sql_layer, 'mysql') === false;
+ 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()
@@ -26,6 +38,11 @@ class mysql_fulltext_drop extends \phpbb\db\migration\migration
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.
@@ -36,12 +53,28 @@ class mysql_fulltext_drop extends \phpbb\db\migration\migration
*/
return array(
'drop_keys' => array(
- $this->table_prefix . 'posts' => array(
- 'post_subject',
- 'post_text',
- 'post_content',
- ),
+ $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
index f74ecbd874..2a4935395e 100644
--- a/phpBB/phpbb/db/migration/data/v310/namespaces.php
+++ b/phpBB/phpbb/db/migration/data/v310/namespaces.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php
index 4195623618..2d4d26ae61 100644
--- a/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php
+++ b/phpBB/phpbb/db/migration/data/v310/notification_options_reconvert.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications.php b/phpBB/phpbb/db/migration/data/v310/notifications.php
index 61be25bb5f..f4d012b5ac 100644
--- a/phpBB/phpbb/db/migration/data/v310/notifications.php
+++ b/phpBB/phpbb/db/migration/data/v310/notifications.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php
index d5fa9c58a5..ba600f7bf5 100644
--- a/phpBB/phpbb/db/migration/data/v310/notifications_cron.php
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php
index 050e679cc0..263584b343 100644
--- a/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_cron_p2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php
index eb2eb361ee..21a39a7c91 100644
--- a/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php
+++ b/phpBB/phpbb/db/migration/data/v310/notifications_schema_fix.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 3422f75917..adee44147f 100644
--- a/phpBB/phpbb/db/migration/data/v310/passwords.php
+++ b/phpBB/phpbb/db/migration/data/v310/passwords.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php
new file mode 100644
index 0000000000..aad8e44681
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p1.php
@@ -0,0 +1,84 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords_convert_p1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\passwords_p2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_passwords'))),
+ );
+ }
+
+ public function update_passwords($start)
+ {
+ // Nothing to do if user_pass_convert column doesn't exist
+ if (!$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert'))
+ {
+ return;
+ }
+
+ $start = (int) $start;
+ $limit = 1000;
+ $converted_users = 0;
+
+ $sql = 'SELECT user_password, user_id
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_pass_convert = 1
+ ORDER BY user_id';
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $update_users = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $converted_users++;
+
+ $user_id = (int) $row['user_id'];
+ // Only prefix passwords without proper prefix
+ if (!isset($update_users[$user_id]) && !preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $row['user_password']))
+ {
+ // Use $CP$ prefix for passwords that need to
+ // be converted and set pass convert to false.
+ $update_users[$user_id] = array(
+ 'user_password' => '$CP$' . $row['user_password'],
+ 'user_pass_convert' => 0,
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ foreach ($update_users as $user_id => $user_data)
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'users
+ SET ' . $this->db->sql_build_array('UPDATE', $user_data) . '
+ WHERE user_id = ' . $user_id;
+ $this->sql_query($sql);
+ }
+
+ if ($converted_users < $limit)
+ {
+ // There are no more users to be converted
+ return;
+ }
+
+ // There are still more users to query, return the next start value
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php
new file mode 100644
index 0000000000..26a99184a6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_convert_p2.php
@@ -0,0 +1,49 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class passwords_convert_p2 extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ return !$this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_pass_convert');
+ }
+
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\passwords_convert_p1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_pass_convert',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_pass_convert' => array('BOOL', 0, 'after' => 'user_passchg'),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/passwords_p2.php b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php
index 553e79403d..afc7ba2813 100644
--- a/phpBB/phpbb/db/migration/data/v310/passwords_p2.php
+++ b/phpBB/phpbb/db/migration/data/v310/passwords_p2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/plupload.php b/phpBB/phpbb/db/migration/data/v310/plupload.php
index 7cdba507a2..69367f86a9 100644
--- a/phpBB/phpbb/db/migration/data/v310/plupload.php
+++ b/phpBB/phpbb/db/migration/data/v310/plupload.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 9e7ba68327..c44167dbfe 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_cleanup.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index d73bc78edb..33a5ba15ae 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_interests.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -15,6 +19,7 @@ class profilefield_interests extends \phpbb\db\migration\profilefield_base_migra
{
return array(
'\phpbb\db\migration\data\v310\profilefield_types',
+ '\phpbb\db\migration\data\v310\profilefield_show_novalue',
);
}
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_location.php b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php
index 4e62eab2d7..2d27c09e68 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_location.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_location.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index b0ea961c08..75df2bcdee 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_occupation.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php
index ce51944c3e..7ce5de0f00 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_on_memberlist.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 2152aaee20..5045eb8807 100644
--- a/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
+++ b/phpBB/phpbb/db/migration/data/v310/profilefield_types.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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..10ba7fefff
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc1.php
@@ -0,0 +1,39 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v310\beta4',
+ '\phpbb\db\migration\data\v310\contact_admin_acp_module',
+ '\phpbb\db\migration\data\v310\contact_admin_form',
+ '\phpbb\db\migration\data\v310\passwords_convert_p2',
+ '\phpbb\db\migration\data\v310\profilefield_facebook',
+ '\phpbb\db\migration\data\v310\profilefield_googleplus',
+ '\phpbb\db\migration\data\v310\profilefield_skype',
+ '\phpbb\db\migration\data\v310\profilefield_twitter',
+ '\phpbb\db\migration\data\v310\profilefield_youtube',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.0-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v310/rc2.php b/phpBB/phpbb/db/migration/data/v310/rc2.php
new file mode 100644
index 0000000000..e1323659da
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc2.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 rc2 extends \phpbb\db\migration\migration
+{
+ 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..0e6a452251
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc3.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\v310;
+
+class rc3 extends \phpbb\db\migration\migration
+{
+ 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..47de8291c1
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc4.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc4 extends \phpbb\db\migration\migration
+{
+ 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..5b6f70e32e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc5.php
@@ -0,0 +1,33 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v310;
+
+class rc5 extends \phpbb\db\migration\migration
+{
+ 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..b84f2edcc9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v310/rc6.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 rc6 extends \phpbb\db\migration\migration
+{
+ 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
index 56b7a0916c..575a65d9dd 100644
--- a/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php
+++ b/phpBB/phpbb/db/migration/data/v310/reported_posts_display.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index 6da1cb8009..e50f5e53a0 100644
--- a/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php
+++ b/phpBB/phpbb/db/migration/data/v310/signature_module_auth.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index d1a31815b2..5e68db5889 100644
--- a/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_mcp_modules.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php
index f080c78c50..b1e7486e24 100644
--- a/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p1.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -149,6 +153,15 @@ class softdelete_p1 extends \phpbb\db\migration\migration
$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
diff --git a/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php
index 38b190c766..849a996c1b 100644
--- a/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php
+++ b/phpBB/phpbb/db/migration/data/v310/softdelete_p2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php
index 26f1046287..2c7b7edf2e 100644
--- a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php
+++ b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -88,7 +92,7 @@ class style_update_p1 extends \phpbb\db\migration\migration
else
{
$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id
- FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "stles_theme c
+ 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";
}
@@ -132,11 +136,11 @@ class style_update_p1 extends \phpbb\db\migration\migration
if (!sizeof($valid_styles))
{
// No valid styles: remove everything and add prosilver
- $this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary);
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE);
$sql_ary = array(
'style_name' => 'prosilver',
- 'style_copyright' => '&copy; phpBB Group',
+ 'style_copyright' => '&copy; phpBB Limited',
'style_active' => 1,
'style_path' => 'prosilver',
'bbcode_bitfield' => 'lNg=',
@@ -153,13 +157,13 @@ class style_update_p1 extends \phpbb\db\migration\migration
$this->sql_query($sql);
$sql = 'SELECT style_id
- FROM ' . $table . "
+ FROM ' . STYLES_TABLE . "
WHERE style_name = 'prosilver'";
$result = $this->sql_query($sql);
- $default_style = $this->db->sql_fetchfield($result);
+ $default_style = $this->db->sql_fetchfield('style_id');
$this->db->sql_freeresult($result);
- set_config('default_style', $default_style);
+ $this->config->set('default_style', $default_style);
$sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0';
$this->sql_query($sql);
diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p2.php b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php
index 40d6a4dbbd..52c8ffb2e2 100644
--- a/phpBB/phpbb/db/migration/data/v310/style_update_p2.php
+++ b/phpBB/phpbb/db/migration/data/v310/style_update_p2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/teampage.php b/phpBB/phpbb/db/migration/data/v310/teampage.php
index 172435c672..f8edbc3492 100644
--- a/phpBB/phpbb/db/migration/data/v310/teampage.php
+++ b/phpBB/phpbb/db/migration/data/v310/teampage.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/db/migration/data/v310/timezone.php b/phpBB/phpbb/db/migration/data/v310/timezone.php
index dd0c6a2093..1f6b47ad50 100644
--- a/phpBB/phpbb/db/migration/data/v310/timezone.php
+++ b/phpBB/phpbb/db/migration/data/v310/timezone.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -39,23 +43,48 @@ class timezone extends \phpbb\db\migration\migration
);
}
- public function update_timezones()
+ public function update_timezones($start)
{
- // Update user timezones
- $sql = 'SELECT user_dst, user_timezone
- FROM ' . $this->table_prefix . 'users
- GROUP BY user_timezone, user_dst';
- $result = $this->db->sql_query($sql);
+ $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($this->convert_phpbb30_timezone($row['user_timezone'], $row['user_dst'])) . "'
- WHERE user_timezone = '" . $this->db->sql_escape($row['user_timezone']) . "'
- AND user_dst = " . (int) $row['user_dst'];
+ SET user_timezone = '" . $this->db->sql_escape($converted_timezone) . "'
+ WHERE " . $this->db->sql_in_set('user_id', $user_ids);
$this->sql_query($sql);
}
- $this->db->sql_freeresult($result);
+
+ if ($converted == $limit)
+ {
+ // There are still more to convert
+ return $start + $limit;
+ }
// Update board default timezone
$sql = 'UPDATE ' . $this->table_prefix . "config
diff --git a/phpBB/phpbb/db/migration/data/v310/timezone_p2.php b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php
index 1066ab8571..3ac7ab3c51 100644
--- a/phpBB/phpbb/db/migration/data/v310/timezone_p2.php
+++ b/phpBB/phpbb/db/migration/data/v310/timezone_p2.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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
index f8ada6c6f5..8600f6ee27 100644
--- a/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php
+++ b/phpBB/phpbb/db/migration/data/v310/ucp_popuppm_module.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
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/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/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..14b7b7b0f6
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/update_custom_bbcodes_with_idn.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public 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))
+ {
+ 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/v311.php b/phpBB/phpbb/db/migration/data/v31x/v311.php
new file mode 100644
index 0000000000..00844dd4c0
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v311.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 v311 extends \phpbb\db\migration\migration
+{
+ 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/v312.php b/phpBB/phpbb/db/migration/data/v31x/v312.php
new file mode 100644
index 0000000000..bf49935f4d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v312.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 v312 extends \phpbb\db\migration\migration
+{
+ 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..d4b133fc01
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v312rc1.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 v312rc1 extends \phpbb\db\migration\migration
+{
+ 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..5a4e21a9b7
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313.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 v313 extends \phpbb\db\migration\migration
+{
+ 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..e50754f805
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313rc1.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 v313rc1 extends \phpbb\db\migration\migration
+{
+ 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..d832d6f502
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v313rc2.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 v313rc2 extends \phpbb\db\migration\migration
+{
+ 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..b7793ca569
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_14',
+ '\phpbb\db\migration\data\v31x\v314rc2',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc1.php b/phpBB/phpbb/db/migration/data/v31x/v314rc1.php
new file mode 100644
index 0000000000..10cdbe3f9c
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314rc1.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314rc1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v313',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v314rc2.php b/phpBB/phpbb/db/migration/data/v31x/v314rc2.php
new file mode 100644
index 0000000000..b75b7a9be8
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v314rc2.php
@@ -0,0 +1,32 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v314rc2 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v30x\release_3_0_14_rc1',
+ '\phpbb\db\migration\data\v31x\v314rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.4-RC2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v315.php b/phpBB/phpbb/db/migration/data/v31x/v315.php
new file mode 100644
index 0000000000..778cdf717e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v315.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v315 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v315rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.5')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v315rc1.php b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php
new file mode 100644
index 0000000000..4cf4472aa7
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v315rc1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v314',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.5-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v316.php b/phpBB/phpbb/db/migration/data/v31x/v316.php
new file mode 100644
index 0000000000..cec113eff2
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v316.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v316 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v316rc1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.6')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v316rc1.php b/phpBB/phpbb/db/migration/data/v31x/v316rc1.php
new file mode 100644
index 0000000000..487cd05e5d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v316rc1.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v31x;
+
+class v316rc1 extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v315',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.1.6-RC1')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v31x/v317.php b/phpBB/phpbb/db/migration/data/v31x/v317.php
new file mode 100644
index 0000000000..15ba2a1feb
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317.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 v317 extends \phpbb\db\migration\migration
+{
+ 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..2e1b0e9b9d
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317pl1.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 v317pl1 extends \phpbb\db\migration\migration
+{
+ 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..fa24819094
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v31x/v317rc1.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 v317rc1 extends \phpbb\db\migration\migration
+{
+ 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/v320/allowed_schemes_links.php b/phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.php
new file mode 100644
index 0000000000..726822bc71
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/allowed_schemes_links.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\v320;
+
+class allowed_schemes_links extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allowed_schemes_links', 'http,https,ftp')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/announce_global_permission.php b/phpBB/phpbb/db/migration/data/v320/announce_global_permission.php
new file mode 100644
index 0000000000..7afecb884b
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/announce_global_permission.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\v320;
+
+class announce_global_permission extends \phpbb\db\migration\migration
+{
+ public function effectively_installed()
+ {
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . "
+ WHERE auth_option = 'f_announce_global'";
+ $result = $this->db->sql_query($sql);
+ $auth_option_id = $this->db->sql_fetchfield('auth_option_id');
+ $this->db->sql_freeresult($result);
+
+ return $auth_option_id !== false;
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('permission.add', array('f_announce_global', false, 'f_announce')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/dev.php b/phpBB/phpbb/db/migration/data/v320/dev.php
new file mode 100644
index 0000000000..ad2da3c1f4
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/dev.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\v320;
+
+class dev extends \phpbb\db\migration\container_aware_migration
+{
+ public function effectively_installed()
+ {
+ return version_compare($this->config['version'], '3.2.0-dev', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v316',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.2.0-dev')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/font_awesome_update.php b/phpBB/phpbb/db/migration/data/v320/font_awesome_update.php
new file mode 100644
index 0000000000..817b638037
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/font_awesome_update.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\v320;
+
+class font_awesome_update extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function effectively_installed()
+ {
+ return isset($this->config['load_font_awesome_url']);
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('load_font_awesome_url', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/icons_alt.php b/phpBB/phpbb/db/migration/data/v320/icons_alt.php
new file mode 100644
index 0000000000..80132e579e
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/icons_alt.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\v320;
+
+class icons_alt extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'icons' => array(
+ 'icons_alt' => array('VCHAR', '', 'after' => 'icons_height'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'icons' => array(
+ 'icons_alt',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/log_post_id.php b/phpBB/phpbb/db/migration/data/v320/log_post_id.php
new file mode 100644
index 0000000000..ead53c8138
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/log_post_id.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\v320;
+
+class log_post_id extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'log' => array(
+ 'post_id' => array('UINT', 0, 'after' => 'topic_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'log' => array(
+ 'post_id',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/notifications_board.php b/phpBB/phpbb/db/migration/data/v320/notifications_board.php
new file mode 100644
index 0000000000..8a76ebab58
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/notifications_board.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\v320;
+
+class notifications_board extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('allow_board_notifications', 1)),
+ array('custom', array(array($this, 'update_user_subscriptions'))),
+ array('custom', array(array($this, 'update_module'))),
+ );
+ }
+
+ public function update_module()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET auth = 'cfg_allow_board_notifications'
+ WHERE module_basename = 'ucp_notifications'
+ AND module_mode = 'notification_list'";
+ $this->sql_query($sql);
+ }
+
+ public function update_user_subscriptions()
+ {
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET method = 'notification.method.board'
+ WHERE method = ''";
+ $this->sql_query($sql);
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('custom', array(array($this, 'revert_user_subscriptions'))),
+ array('custom', array(array($this, 'revert_module'))),
+ );
+ }
+
+ public function revert_user_subscriptions()
+ {
+ $sql = 'UPDATE ' . USER_NOTIFICATIONS_TABLE . "
+ SET method = ''
+ WHERE method = 'notification.method.board'";
+ $this->sql_query($sql);
+ }
+
+ public function revert_module()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET auth = ''
+ WHERE module_basename = 'ucp_notifications'
+ AND module_mode = 'notification_list'";
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/oauth_states.php b/phpBB/phpbb/db/migration/data/v320/oauth_states.php
new file mode 100644
index 0000000000..22ab2dabb3
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/oauth_states.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\v320;
+
+class oauth_states extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\auth_provider_oauth');
+ }
+
+ public function effectively_installed()
+ {
+ return $this->db_tools->sql_table_exists($this->table_prefix . 'oauth_states');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'oauth_states' => array(
+ 'COLUMNS' => array(
+ 'user_id' => array('UINT', 0),
+ 'session_id' => array('CHAR:32', ''),
+ 'provider' => array('VCHAR', ''),
+ 'oauth_state' => array('VCHAR', ''),
+ ),
+ 'KEYS' => array(
+ 'user_id' => array('INDEX', 'user_id'),
+ 'provider' => array('INDEX', 'provider'),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'oauth_states',
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php b/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.php
new file mode 100644
index 0000000000..c14d31f1c0
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/remove_outdated_media.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\v320;
+
+class remove_outdated_media extends \phpbb\db\migration\migration
+{
+ protected $cat_id = array(
+ ATTACHMENT_CATEGORY_WM,
+ ATTACHMENT_CATEGORY_RM,
+ ATTACHMENT_CATEGORY_QUICKTIME,
+ );
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'change_extension_group'))),
+ );
+ }
+
+ public function change_extension_group()
+ {
+ // select group ids of outdated media
+ $sql = 'SELECT group_id
+ FROM ' . EXTENSION_GROUPS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('cat_id', $this->cat_id);
+ $result = $this->db->sql_query($sql);
+
+ $group_ids = array();
+ while ($group_id = (int) $this->db->sql_fetchfield('group_id'))
+ {
+ $group_ids[] = $group_id;
+ }
+ $this->db->sql_freeresult($result);
+
+ // nothing to do, admin has removed all the outdated media extension groups
+ if (empty($group_ids))
+ {
+ return true;
+ }
+
+ // get the group id of downloadable files
+ $sql = 'SELECT group_id
+ FROM ' . EXTENSION_GROUPS_TABLE . "
+ WHERE group_name = 'DOWNLOADABLE_FILES'";
+ $result = $this->db->sql_query($sql);
+ $download_id = (int) $this->db->sql_fetchfield('group_id');
+ $this->db->sql_freeresult($result);
+
+ if (empty($download_id))
+ {
+ $sql = 'UPDATE ' . EXTENSIONS_TABLE . '
+ SET group_id = 0
+ WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
+ }
+ else
+ {
+ // move outdated media extensions to downloadable files
+ $sql = 'UPDATE ' . EXTENSIONS_TABLE . "
+ SET group_id = $download_id" . '
+ WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
+ }
+
+ $result = $this->db->sql_query($sql);
+ $this->db->sql_freeresult($result);
+
+ // delete the now empty, outdated media extension groups
+ $sql = 'DELETE FROM ' . EXTENSION_GROUPS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('group_id', $group_ids);
+ $result = $this->db->sql_query($sql);
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php b/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php
new file mode 100644
index 0000000000..1cb9070bf9
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/remove_profilefield_wlm.php
@@ -0,0 +1,152 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v320;
+
+class remove_profilefield_wlm extends \phpbb\db\migration\migration
+{
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields_data' => array(
+ 'pf_phpbb_wlm',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields_data' => array(
+ 'pf_phpbb_wlm' => array('VCHAR', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'delete_custom_profile_field_data'))),
+ );
+ }
+
+ public function revert_data()
+ {
+ return array(
+ array('custom', array(array($this, 'create_custom_field'))),
+ );
+ }
+
+ public function delete_custom_profile_field_data()
+ {
+ $field_id = $this->get_custom_profile_field_id();
+
+ $sql = 'DELETE FROM ' . PROFILE_FIELDS_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . PROFILE_LANG_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . PROFILE_FIELDS_LANG_TABLE . '
+ WHERE field_id = ' . (int) $field_id;
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Get custom profile field id
+ * @return int custom profile filed id
+ */
+ public function get_custom_profile_field_id()
+ {
+ $sql = 'SELECT field_id
+ FROM ' . PROFILE_FIELDS_TABLE . "
+ WHERE field_name = 'phpbb_wlm'";
+ $result = $this->db->sql_query($sql);
+ $field_id = (int) $this->db->sql_fetchfield('field_id');
+ $this->db->sql_freeresult($result);
+
+ return $field_id;
+ }
+
+ public function create_custom_field()
+ {
+ $sql = 'SELECT MAX(field_order) as max_field_order
+ FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+ $max_field_order = (int) $this->db->sql_fetchfield('max_field_order');
+ $this->db->sql_freeresult($result);
+
+ $sql_ary = array(
+ 'field_name' => 'phpbb_wlm',
+ 'field_type' => 'profilefields.type.string',
+ 'field_ident' => 'phpbb_wlm',
+ 'field_length' => '40',
+ 'field_minlen' => '5',
+ 'field_maxlen' => '255',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ 'field_validation' => '.*',
+ 'field_required' => 0,
+ 'field_show_novalue' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_show_on_pm' => 1,
+ 'field_show_on_vt' => 1,
+ 'field_show_on_ml' => 0,
+ 'field_show_profile' => 1,
+ 'field_hide' => 0,
+ 'field_no_view' => 0,
+ 'field_active' => 1,
+ 'field_is_contact' => 1,
+ 'field_contact_desc' => '',
+ 'field_contact_url' => '',
+ 'field_order' => $max_field_order + 1,
+ );
+
+ $sql = 'INSERT INTO ' . PROFILE_FIELDS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+ $field_id = (int) $this->db->sql_nextid();
+
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, PROFILE_LANG_TABLE);
+
+ $sql = 'SELECT lang_id
+ FROM ' . LANG_TABLE;
+ $result = $this->db->sql_query($sql);
+ $lang_name = 'WLM';
+ while ($lang_id = (int) $this->db->sql_fetchfield('lang_id'))
+ {
+ $insert_buffer->insert(array(
+ 'field_id' => (int) $field_id,
+ 'lang_id' => (int) $lang_id,
+ 'lang_name' => $lang_name,
+ 'lang_explain' => '',
+ 'lang_default_value' => '',
+ ));
+ }
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/text_reparser.php b/phpBB/phpbb/db/migration/data/v320/text_reparser.php
new file mode 100644
index 0000000000..1d73b74a76
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/text_reparser.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\v320;
+
+class text_reparser extends \phpbb\db\migration\container_aware_migration
+{
+ static public function depends_on()
+ {
+ return array('\phpbb\db\migration\data\v310\contact_admin_form');
+ }
+
+ public function effectively_installed()
+ {
+ return isset($this->config['reparse_lock']);
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('reparse_lock', 0, true)),
+ array('config.add', array('text_reparser.pm_text_cron_interval', 10)),
+ array('config.add', array('text_reparser.pm_text_last_cron', 0)),
+ array('config.add', array('text_reparser.poll_option_cron_interval', 10)),
+ array('config.add', array('text_reparser.poll_option_last_cron', 0)),
+ array('config.add', array('text_reparser.poll_title_cron_interval', 10)),
+ array('config.add', array('text_reparser.poll_title_last_cron', 0)),
+ array('config.add', array('text_reparser.post_text_cron_interval', 10)),
+ array('config.add', array('text_reparser.post_text_last_cron', 0)),
+ array('config.add', array('text_reparser.user_signature_cron_interval', 10)),
+ array('config.add', array('text_reparser.user_signature_last_cron', 0)),
+ array('custom', array(array($this, 'reparse'))),
+ );
+ }
+
+ public function reparse($resume_data)
+ {
+ // Somtimes a cron job is too much
+ $limit = 100;
+ $fast_reparsers = array(
+ 'text_reparser.contact_admin_info',
+ 'text_reparser.forum_description',
+ 'text_reparser.forum_rules',
+ 'text_reparser.group_description',
+ );
+
+ if (!is_array($resume_data))
+ {
+ $resume_data = array(
+ 'reparser' => 0,
+ 'current' => $this->container->get($fast_reparsers[0])->get_max_id(),
+ );
+ }
+
+ $fast_reparsers_size = sizeof($fast_reparsers);
+ $processed_records = 0;
+ while ($processed_records < $limit && $resume_data['reparser'] < $fast_reparsers_size)
+ {
+ $reparser = $this->container->get($fast_reparsers[$resume_data['reparser']]);
+
+ // New reparser
+ if ($resume_data['current'] === 0)
+ {
+ $resume_data['current'] = $reparser->get_max_id();
+ }
+
+ $start = max(1, $resume_data['current'] + 1 - ($limit - $processed_records));
+ $end = max(1, $resume_data['current']);
+ $reparser->reparse_range($start, $end);
+
+ $processed_records = $end - $start + 1;
+ $resume_data['current'] = $start - 1;
+
+ if ($start === 1)
+ {
+ // Prevent CLI command from running these reparsers again
+ $reparser_manager = $this->container->get('text_reparser.manager');
+ $reparser_manager->update_resume_data($fast_reparsers[$resume_data['reparser']], 1, 0, $limit);
+
+ $resume_data['reparser']++;
+ }
+ }
+
+ if ($resume_data['reparser'] === $fast_reparsers_size)
+ {
+ return true;
+ }
+
+ return $resume_data;
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/v320a1.php b/phpBB/phpbb/db/migration/data/v320/v320a1.php
new file mode 100644
index 0000000000..d7ecb36f90
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/v320a1.php
@@ -0,0 +1,44 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\migration\data\v320;
+
+class v320a1 extends \phpbb\db\migration\container_aware_migration
+{
+ public function effectively_installed()
+ {
+ return version_compare($this->config['version'], '3.2.0-a1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v320\dev',
+ '\phpbb\db\migration\data\v320\allowed_schemes_links',
+ '\phpbb\db\migration\data\v320\announce_global_permission',
+ '\phpbb\db\migration\data\v320\remove_profilefield_wlm',
+ '\phpbb\db\migration\data\v320\font_awesome_update',
+ '\phpbb\db\migration\data\v320\icons_alt',
+ '\phpbb\db\migration\data\v320\log_post_id',
+ '\phpbb\db\migration\data\v320\remove_outdated_media',
+ '\phpbb\db\migration\data\v320\notifications_board',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.2.0-dev')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/data/v320/v320a2.php b/phpBB/phpbb/db/migration/data/v320/v320a2.php
new file mode 100644
index 0000000000..ae53a73210
--- /dev/null
+++ b/phpBB/phpbb/db/migration/data/v320/v320a2.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\v320;
+
+class v320a2 extends \phpbb\db\migration\container_aware_migration
+{
+ public function effectively_installed()
+ {
+ return version_compare($this->config['version'], '3.2.0-a2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ '\phpbb\db\migration\data\v31x\v317rc1',
+ '\phpbb\db\migration\data\v320\text_reparser',
+ '\phpbb\db\migration\data\v320\v320a1',
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.2.0-a2')),
+ );
+ }
+}
diff --git a/phpBB/phpbb/db/migration/exception.php b/phpBB/phpbb/db/migration/exception.php
index cfe546d1ab..7990e85f2d 100644
--- a/phpBB/phpbb/db/migration/exception.php
+++ b/phpBB/phpbb/db/migration/exception.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package db
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\db\migration;
/**
* The migrator is responsible for applying new migrations in the correct order.
-*
-* @package db
*/
class exception extends \Exception
{
diff --git a/phpBB/phpbb/db/migration/helper.php b/phpBB/phpbb/db/migration/helper.php
index 009ad1da9f..e40deeb37b 100644
--- a/phpBB/phpbb/db/migration/helper.php
+++ b/phpBB/phpbb/db/migration/helper.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package db
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\db\migration;
/**
* The migrator is responsible for applying new migrations in the correct order.
-*
-* @package db
*/
class helper
{
diff --git a/phpBB/phpbb/db/migration/migration.php b/phpBB/phpbb/db/migration/migration.php
index b32de00871..2304c8e44c 100644
--- a/phpBB/phpbb/db/migration/migration.php
+++ b/phpBB/phpbb/db/migration/migration.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package db
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -15,18 +19,16 @@ namespace phpbb\db\migration;
* Each migration consists of a set of schema and data changes to be implemented
* in a subclass. This class provides various utility methods to simplify editing
* a phpBB.
-*
-* @package db
*/
abstract class migration
{
/** @var \phpbb\config\config */
protected $config;
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
- /** @var \phpbb\db\tools */
+ /** @var \phpbb\db\tools\tools_interface */
protected $db_tools;
/** @var string */
@@ -48,13 +50,13 @@ abstract class migration
* Constructor
*
* @param \phpbb\config\config $config
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\db\tools $db_tools
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\db\tools\tools_interface $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 $db, \phpbb\db\tools $db_tools, $phpbb_root_path, $php_ext, $table_prefix)
+ public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools\tools_interface $db_tools, $phpbb_root_path, $php_ext, $table_prefix)
{
$this->config = $config;
$this->db = $db;
@@ -158,11 +160,11 @@ abstract class migration
else
{
$result = $this->db->sql_query($sql);
- if ($this->db->sql_error_triggered)
+ if ($this->db->get_sql_error_triggered())
{
$this->errors[] = array(
- 'sql' => $this->db->sql_error_sql,
- 'code' => $this->db->sql_error_returned,
+ 'sql' => $this->db->get_sql_error_sql(),
+ 'code' => $this->db->get_sql_error_returned(),
);
}
}
diff --git a/phpBB/phpbb/db/migration/profilefield_base_migration.php b/phpBB/phpbb/db/migration/profilefield_base_migration.php
index dec7a4c2bb..3f26a4998c 100644
--- a/phpBB/phpbb/db/migration/profilefield_base_migration.php
+++ b/phpBB/phpbb/db/migration/profilefield_base_migration.php
@@ -1,15 +1,19 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\db\migration;
-abstract class profilefield_base_migration extends \phpbb\db\migration\migration
+abstract class profilefield_base_migration extends container_aware_migration
{
protected $profilefield_name;
@@ -17,6 +21,23 @@ abstract class profilefield_base_migration extends \phpbb\db\migration\migration
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()
@@ -54,6 +75,13 @@ abstract class profilefield_base_migration extends \phpbb\db\migration\migration
);
}
+ 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
@@ -75,12 +103,13 @@ abstract class profilefield_base_migration extends \phpbb\db\migration\migration
$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' => $field_id,
- 'lang_id' => $lang_id,
- 'lang_name' => strtoupper(substr($this->profilefield_name, 6)),// Remove phpbb_ from field name
+ 'field_id' => (int) $field_id,
+ 'lang_id' => (int) $lang_id,
+ 'lang_name' => $lang_name,
'lang_explain' => '',
'lang_default_value' => '',
));
@@ -91,6 +120,69 @@ abstract class profilefield_base_migration extends \phpbb\db\migration\migration
}
/**
+ * 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
*/
@@ -145,8 +237,8 @@ abstract class profilefield_base_migration extends \phpbb\db\migration\migration
if ($profile_row === null)
{
- global $phpbb_container;
- $manager = $phpbb_container->get('profilefields.manager');
+ /* @var $manager \phpbb\profilefields\manager */
+ $manager = $this->container->get('profilefields.manager');
$profile_row = $manager->build_insert_sql_array(array());
}
diff --git a/phpBB/phpbb/db/migration/schema_generator.php b/phpBB/phpbb/db/migration/schema_generator.php
new file mode 100644
index 0000000000..7003844bc4
--- /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\tools_interface */
+ 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\tools_interface $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
index 36a1931f4e..f93e7118c4 100644
--- a/phpBB/phpbb/db/migration/tool/config.php
+++ b/phpBB/phpbb/db/migration/tool/config.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\db\migration\tool;
/**
* Migration config tool
-*
-* @package db
*/
class config implements \phpbb\db\migration\tool\tool_interface
{
@@ -64,6 +66,7 @@ class config implements \phpbb\db\migration\tool\tool_interface
* 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)
{
@@ -85,6 +88,7 @@ class config implements \phpbb\db\migration\tool\tool_interface
* 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)
{
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..bf8ac55023
--- /dev/null
+++ b/phpBB/phpbb/db/migration/tool/config_text.php
@@ -0,0 +1,125 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General 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;
+ }
+
+ 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
index 3e39d87c04..a5ed62fd65 100644
--- a/phpBB/phpbb/db/migration/tool/module.php
+++ b/phpBB/phpbb/db/migration/tool/module.php
@@ -1,30 +1,37 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General 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;
+use phpbb\module\exception\module_exception;
+
/**
* Migration module management tool
-*
-* @package db
*/
class module implements \phpbb\db\migration\tool\tool_interface
{
/** @var \phpbb\cache\service */
protected $cache;
- /** @var dbal */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var \phpbb\user */
protected $user;
+ /** @var \phpbb\module\module_manager */
+ protected $module_manager;
+
/** @var string */
protected $phpbb_root_path;
@@ -37,18 +44,20 @@ class module implements \phpbb\db\migration\tool\tool_interface
/**
* Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param mixed $cache
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\cache\service $cache
* @param \phpbb\user $user
+ * @param \phpbb\module\module_manager $module_manager
* @param string $phpbb_root_path
* @param string $php_ext
* @param string $modules_table
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\cache\service $cache, \phpbb\user $user, $phpbb_root_path, $php_ext, $modules_table)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, \phpbb\module\module_manager $module_manager, $phpbb_root_path, $php_ext, $modules_table)
{
$this->db = $db;
$this->cache = $cache;
$this->user = $user;
+ $this->module_manager = $module_manager;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->modules_table = $modules_table;
@@ -163,12 +172,14 @@ class module implements \phpbb\db\migration\tool\tool_interface
* )
* Optionally you may not send 'modes' and it will insert all of the
* modules in that info file.
- * @param string|bool $include_path If you would like to use a custom include
* path, specify that here
* @return null
+ * @throws \phpbb\db\migration\exception
*/
- public function add($class, $parent = 0, $data = array(), $include_path = false)
+ public function add($class, $parent = 0, $data = array())
{
+ global $user, $phpbb_log;
+
// Allows '' to be sent as 0
$parent = $parent ?: 0;
@@ -184,7 +195,6 @@ class module implements \phpbb\db\migration\tool\tool_interface
$basename = (isset($data['module_basename'])) ? $data['module_basename'] : '';
$module = $this->get_module_info($class, $basename);
- $result = '';
foreach ($module['modes'] as $mode => $module_info)
{
if (!isset($data['modes']) || in_array($mode, $data['modes']))
@@ -235,13 +245,6 @@ class module implements \phpbb\db\migration\tool\tool_interface
return;
}
- if (!class_exists('acp_modules'))
- {
- include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
- $this->user->add_lang('acp/modules');
- }
- $acp_modules = new \acp_modules();
-
$module_data = array(
'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1,
'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1,
@@ -252,19 +255,14 @@ class module implements \phpbb\db\migration\tool\tool_interface
'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '',
'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '',
);
- $result = $acp_modules->update_module_data($module_data, true);
- // update_module_data can either return a string or an empty array...
- if (is_string($result))
- {
- // Error
- throw new \phpbb\db\migration\exception('MODULE_ERROR', $result);
- }
- else
+ try
{
+ $this->module_manager->update_module_data($module_data);
+
// Success
$module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']);
- add_log('admin', 'LOG_MODULE_ADD', $module_log_name);
+ $phpbb_log->add('admin', (isset($user->data['user_id'])) ? $user->data['user_id'] : ANONYMOUS, $user->ip, 'LOG_MODULE_ADD', false, array($module_log_name));
// Move the module if requested above/below an existing one
if (isset($data['before']) && $data['before'])
@@ -314,6 +312,11 @@ class module implements \phpbb\db\migration\tool\tool_interface
$this->db->sql_query($sql);
}
}
+ catch (module_exception $e)
+ {
+ // Error
+ throw new \phpbb\db\migration\exception('MODULE_ERROR', $e->getMessage());
+ }
// Clear the Modules Cache
$this->cache->destroy("_modules_$class");
@@ -328,11 +331,11 @@ class module implements \phpbb\db\migration\tool\tool_interface
* @param int|string|bool $parent The parent module_id|module_langname(0 for no parent).
* Use false to ignore the parent check and check class wide.
* @param int|string $module The module id|module_langname
- * @param string|bool $include_path If you would like to use a custom include path,
* specify that here
* @return null
+ * @throws \phpbb\db\migration\exception
*/
- public function remove($class, $parent = 0, $module = '', $include_path = false)
+ 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))
@@ -340,7 +343,7 @@ class module implements \phpbb\db\migration\tool\tool_interface
if (isset($module['module_langname']))
{
// Manual Method
- return $this->remove($class, $parent, $module['module_langname'], $include_path);
+ return $this->remove($class, $parent, $module['module_langname']);
}
// Failed.
@@ -407,39 +410,15 @@ class module implements \phpbb\db\migration\tool\tool_interface
$module_ids[] = (int) $module_id;
}
$this->db->sql_freeresult($result);
-
- $module_name = $module;
}
else
{
- $module = (int) $module;
- $sql = 'SELECT module_langname
- FROM ' . $this->modules_table . "
- WHERE module_id = $module
- AND module_class = '" . $this->db->sql_escape($class) . "'
- $parent_sql";
- $result = $this->db->sql_query($sql);
- $module_name = $this->db->sql_fetchfield('module_id');
- $this->db->sql_freeresult($result);
-
- $module_ids[] = $module;
+ $module_ids[] = (int) $module;
}
- if (!class_exists('acp_modules'))
- {
- include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
- $this->user->add_lang('acp/modules');
- }
- $acp_modules = new \acp_modules();
- $acp_modules->module_class = $class;
-
foreach ($module_ids as $module_id)
{
- $result = $acp_modules->delete_module($module_id);
- if (!empty($result))
- {
- return;
- }
+ $this->module_manager->delete_module($module_id, $class);
}
$this->cache->destroy("_modules_$class");
@@ -478,15 +457,11 @@ class module implements \phpbb\db\migration\tool\tool_interface
* @param string $class Module Class
* @param string $basename Module Basename
* @return array Module Information
+ * @throws \phpbb\db\migration\exception
*/
protected function get_module_info($class, $basename)
{
- if (!class_exists('acp_modules'))
- {
- include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
- }
- $acp_modules = new \acp_modules();
- $module = $acp_modules->get_module_infos($basename, $class, true);
+ $module = $this->module_manager->get_module_infos($class, $basename, true);
if (empty($module))
{
diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php
index fd2de9c8fb..ceff6d7d5a 100644
--- a/phpBB/phpbb/db/migration/tool/permission.php
+++ b/phpBB/phpbb/db/migration/tool/permission.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\db\migration\tool;
/**
* Migration permission management tool
-*
-* @package db
*/
class permission implements \phpbb\db\migration\tool\tool_interface
{
@@ -22,7 +24,7 @@ class permission implements \phpbb\db\migration\tool\tool_interface
/** @var \phpbb\cache\service */
protected $cache;
- /** @var dbal */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var string */
@@ -34,13 +36,13 @@ class permission implements \phpbb\db\migration\tool\tool_interface
/**
* Constructor
*
- * @param \phpbb\db\driver\driver $db
- * @param mixed $cache
+ * @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 $db, \phpbb\cache\service $cache, \phpbb\auth\auth $auth, $phpbb_root_path, $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;
@@ -103,6 +105,7 @@ class permission implements \phpbb\db\migration\tool\tool_interface
* @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)
@@ -241,7 +244,9 @@ class permission implements \phpbb\db\migration\tool\tool_interface
* Add a new permission role
*
* @param string $role_name The new role name
- * @param sting $role_type The type (u_, m_, a_)
+ * @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 = '')
@@ -281,6 +286,7 @@ class permission implements \phpbb\db\migration\tool\tool_interface
* @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)
{
@@ -343,6 +349,7 @@ class permission implements \phpbb\db\migration\tool\tool_interface
* @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)
{
@@ -418,13 +425,27 @@ class permission implements \phpbb\db\migration\tool\tool_interface
$role_id = (int) $this->db->sql_fetchfield('auth_role_id');
if ($role_id)
{
- $sql = 'SELECT role_name
+ $sql = 'SELECT role_name, role_type
FROM ' . ACL_ROLES_TABLE . '
WHERE role_id = ' . $role_id;
$this->db->sql_query($sql);
- $role_name = $this->db->sql_fetchfield('role_name');
-
- return $this->permission_set($role_name, $auth_option, 'role', $has_permission);
+ $role_data = $this->db->sql_fetchrow();
+ $role_name = $role_data['role_name'];
+ $role_type = $role_data['role_type'];
+
+ // Filter new auth options to match the role type: a_ | f_ | m_ | u_
+ // Set new auth options to the role only if options matching the role type were found
+ $auth_option = array_filter($auth_option,
+ function ($option) use ($role_type)
+ {
+ return strpos($option, $role_type) === 0;
+ }
+ );
+
+ if (sizeof($auth_option))
+ {
+ return $this->permission_set($role_name, $auth_option, 'role', $has_permission);
+ }
}
$sql = 'SELECT auth_option_id, auth_setting
@@ -488,6 +509,7 @@ class permission implements \phpbb\db\migration\tool\tool_interface
* 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')
{
@@ -529,7 +551,8 @@ class permission implements \phpbb\db\migration\tool\tool_interface
}
$sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
- WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
+ WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove) . '
+ AND role_id = ' . (int) $role_id;
$this->db->sql_query($sql);
break;
diff --git a/phpBB/phpbb/db/migration/tool/tool_interface.php b/phpBB/phpbb/db/migration/tool/tool_interface.php
index e7b89d8858..07cd2435e4 100644
--- a/phpBB/phpbb/db/migration/tool/tool_interface.php
+++ b/phpBB/phpbb/db/migration/tool/tool_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package migration
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\db\migration\tool;
/**
* Migration tool interface
-*
-* @package db
*/
interface tool_interface
{
diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php
index 4fb3f1a241..d91860949a 100644
--- a/phpBB/phpbb/db/migrator.php
+++ b/phpBB/phpbb/db/migrator.php
@@ -1,28 +1,40 @@
<?php
/**
*
-* @package db
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU 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\db\output_handler\migrator_output_handler_interface;
+use phpbb\db\output_handler\null_migrator_output_handler;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* The migrator is responsible for applying new migrations in the correct order.
-*
-* @package db
*/
class migrator
{
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
/** @var \phpbb\config\config */
protected $config;
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
- /** @var \phpbb\db\tools */
+ /** @var \phpbb\db\tools\tools_interface */
protected $db_tools;
/** @var \phpbb\db\migration\helper */
@@ -57,6 +69,13 @@ class migrator
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
@@ -66,10 +85,18 @@ class migrator
public $last_run_migration = false;
/**
+ * The output handler. A null handler is configured by default.
+ *
+ * @var migrator_output_handler_interface
+ */
+ public $output_handler;
+
+ /**
* Constructor of the database migrator
*/
- public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper)
+ public function __construct(ContainerInterface $container, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools\tools_interface $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;
@@ -82,6 +109,8 @@ class migrator
$this->table_prefix = $table_prefix;
+ $this->output_handler = new null_migrator_output_handler();
+
foreach ($tools as $tool)
{
$this->tools[$tool->get_name()] = $tool;
@@ -93,6 +122,16 @@ class migrator
}
/**
+ * Set the output handler.
+ *
+ * @param migrator_output_handler_interface $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
@@ -108,7 +147,7 @@ class migrator
FROM " . $this->migrations_table;
$result = $this->db->sql_query($sql);
- if (!$this->db->sql_error_triggered)
+ if (!$this->db->get_sql_error_triggered())
{
while ($migration = $this->db->sql_fetchrow($result))
{
@@ -144,6 +183,18 @@ class migrator
*/
public function update()
{
+ $this->container->get('dispatcher')->disable();
+ $this->update_do();
+ $this->container->get('dispatcher')->enable();
+ }
+
+ /**
+ * Effectively runs a single update step from the next migration to be applied.
+ *
+ * @return null
+ */
+ protected function update_do()
+ {
foreach ($this->migrations as $name)
{
if (!isset($this->migration_state[$name]) ||
@@ -159,19 +210,25 @@ class migrator
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 The class name of the migration
+ * @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;
}
@@ -188,6 +245,11 @@ class migrator
'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)
{
if ($this->unfulfillable($depend) !== false)
@@ -224,6 +286,8 @@ class migrator
);
$this->last_run_migration['effectively_installed'] = true;
+
+ $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
}
else
{
@@ -235,28 +299,48 @@ class migrator
if (!$state['migration_schema_done'])
{
+ $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
+
$this->last_run_migration['task'] = 'process_schema_step';
+ $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;
$state['migration_data_state'] = ($result === true) ? '' : $result;
$state['migration_schema_done'] = ($result === true);
+
+ $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
}
else if (!$state['migration_data_done'])
{
try
{
+ $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
+
$this->last_run_migration['task'] = 'process_data_step';
+
+ $elapsed_time = microtime(true);
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
+ $elapsed_time = microtime(true) - $elapsed_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_schema_done'])
+ {
+ $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $elapsed_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)
{
// Revert the schema changes
- $this->revert($name);
+ $this->revert_do($name);
// Rethrow exception
throw $e;
@@ -276,10 +360,22 @@ class migrator
* check if revert() needs to be called again use the migration_state() method.
*
* @param string $migration String migration name to revert (including any that depend on this migration)
- * @return null
*/
public function revert($migration)
{
+ $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
@@ -290,7 +386,7 @@ class migrator
{
if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on']))
{
- $this->revert($name);
+ $this->revert_do($name);
}
}
@@ -300,7 +396,7 @@ class migrator
/**
* Attempts to revert a step of the given migration or one of its dependencies
*
- * @param string The class name of the migration
+ * @param string $name The class name of the migration
* @return bool Whether any update step was successfully run
*/
protected function try_revert($name)
@@ -322,6 +418,9 @@ class migrator
if ($state['migration_data_done'])
{
+ $this->output_handler->write(array('MIGRATION_REVERT_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
+ $elapsed_time = microtime(true);
+
if ($state['migration_data_state'] !== 'revert_data')
{
$result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true);
@@ -337,9 +436,22 @@ class migrator
}
$this->set_migration_state($name, $state);
+
+ $elapsed_time = microtime(true) - $elapsed_time;
+ if ($state['migration_data_done'])
+ {
+ $this->output_handler->write(array('MIGRATION_REVERT_DATA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
+ }
+ else
+ {
+ $this->output_handler->write(array('MIGRATION_REVERT_DATA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
+ }
}
else if ($state['migration_schema_done'])
{
+ $this->output_handler->write(array('MIGRATION_REVERT_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
+ $elapsed_time = microtime(true);
+
$steps = $this->helper->get_schema_steps($migration->revert_schema());
$result = $this->process_data_step($steps, $state['migration_data_state']);
@@ -354,6 +466,9 @@ class migrator
unset($this->migration_state[$name]);
}
+
+ $elapsed_time = microtime(true) - $elapsed_time;
+ $this->output_handler->write(array('MIGRATION_REVERT_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
}
return true;
@@ -366,6 +481,7 @@ class migrator
* @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)
{
@@ -419,7 +535,7 @@ class migrator
}
// Reverse the step that was run
- $result = $this->run_step($reverse_step, false, !$revert);
+ $this->run_step($reverse_step, false, !$revert);
}
// rethrow the exception
@@ -462,6 +578,7 @@ class migrator
* @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)
{
@@ -509,10 +626,17 @@ class migrator
throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step);
}
- return array(
- $parameters[0],
- array($last_result),
- );
+ if ($reverse)
+ {
+ return false;
+ }
+ else
+ {
+ return array(
+ $parameters[0],
+ array($last_result),
+ );
+ }
break;
default:
@@ -590,7 +714,7 @@ class migrator
*/
public function unfulfillable($name)
{
- if (isset($this->migration_state[$name]))
+ if (isset($this->migration_state[$name]) || isset($this->fulfillable_migrations[$name]))
{
return false;
}
@@ -611,6 +735,7 @@ class migrator
return $unfulfillable;
}
}
+ $this->fulfillable_migrations[$name] = true;
return false;
}
@@ -671,7 +796,14 @@ class migrator
*/
protected function get_migration($name)
{
- return new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
+ $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;
}
/**
@@ -703,55 +835,26 @@ class migrator
}
/**
- * Load migration data files from a directory
- *
- * @param \phpbb\extension\finder $finder
- * @param string $path Path to migration data files
- * @param bool $check_fulfillable If TRUE (default), we will check
- * if all of the migrations are fulfillable after loading them.
- * If FALSE, we will not check. You SHOULD check at least once
- * to prevent errors (if including multiple directories, check
- * with the last call to prevent throwing errors unnecessarily).
- * @return array Array of migration names
+ * Creates the migrations table if it does not exist.
+ * @return null
*/
- public function load_migrations(\phpbb\extension\finder $finder, $path, $check_fulfillable = true)
+ public function create_migrations_table()
{
- if (!is_dir($path))
- {
- throw new \phpbb\db\migration\exception('DIRECTORY INVALID', $path);
- }
-
- $migrations = array();
-
- $files = $finder
- ->extension_directory("/")
- ->find_from_paths(array('/' => $path));
- foreach ($files as $file)
- {
- $migrations[$file['path'] . $file['filename']] = '';
- }
- $migrations = $finder->get_classes_from_files($migrations);
-
- foreach ($migrations as $migration)
+ // Make sure migrations have been installed.
+ if (!$this->db_tools->sql_table_exists($this->table_prefix . 'migrations'))
{
- if (!in_array($migration, $this->migrations))
- {
- $this->migrations[] = $migration;
- }
+ $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',
+ ));
}
-
- if ($check_fulfillable)
- {
- foreach ($this->migrations as $name)
- {
- $unfulfillable = $this->unfulfillable($name);
- if ($unfulfillable !== false)
- {
- throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable);
- }
- }
- }
-
- return $this->migrations;
}
}
diff --git a/phpBB/phpbb/db/output_handler/html_migrator_output_handler.php b/phpBB/phpbb/db/output_handler/html_migrator_output_handler.php
new file mode 100644
index 0000000000..67309649c9
--- /dev/null
+++ b/phpBB/phpbb/db/output_handler/html_migrator_output_handler.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\output_handler;
+
+class html_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * Language object.
+ *
+ * @var \phpbb\language\language
+ */
+ private $language;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\language\language $language Language object
+ */
+ public function __construct(\phpbb\language\language $language)
+ {
+ $this->language = $language;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ if ($verbosity <= migrator_output_handler_interface::VERBOSITY_VERBOSE)
+ {
+ $final_message = $this->language->lang_array(array_shift($message), $message);
+ echo $final_message . "<br />\n";
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/output_handler/installer_migrator_output_handler.php b/phpBB/phpbb/db/output_handler/installer_migrator_output_handler.php
new file mode 100644
index 0000000000..56d5cf49a1
--- /dev/null
+++ b/phpBB/phpbb/db/output_handler/installer_migrator_output_handler.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\output_handler;
+
+use phpbb\install\helper\iohandler\iohandler_interface;
+
+class installer_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * Constructor
+ *
+ * @param iohandler_interface $iohandler Installer's IO-handler
+ */
+ public function __construct(iohandler_interface $iohandler)
+ {
+ $this->iohandler = $iohandler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function write($message, $verbosity)
+ {
+ if ($verbosity <= migrator_output_handler_interface::VERBOSITY_VERBOSE)
+ {
+ $this->iohandler->add_log_message($message);
+ $this->iohandler->send_response();
+ }
+ }
+}
diff --git a/phpBB/phpbb/db/output_handler/log_wrapper_migrator_output_handler.php b/phpBB/phpbb/db/output_handler/log_wrapper_migrator_output_handler.php
new file mode 100644
index 0000000000..20991746ac
--- /dev/null
+++ b/phpBB/phpbb/db/output_handler/log_wrapper_migrator_output_handler.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\output_handler;
+
+class log_wrapper_migrator_output_handler implements migrator_output_handler_interface
+{
+ /**
+ * Language object.
+ *
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * A migrator output handler
+ *
+ * @var migrator_output_handler_interface
+ */
+ protected $migrator;
+
+ /**
+ * Log file handle
+ * @var resource
+ */
+ protected $file_handle = false;
+
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\language\language $language Language object
+ * @param migrator_output_handler_interface $migrator Migrator output handler
+ * @param string $log_file File to log to
+ * @param \phpbb\filesystem\filesystem_interface $filesystem phpBB filesystem object
+ */
+ public function __construct(\phpbb\language\language $language, migrator_output_handler_interface $migrator, $log_file, \phpbb\filesystem\filesystem_interface $filesystem)
+ {
+ $this->language = $language;
+ $this->migrator = $migrator;
+ $this->filesystem = $filesystem;
+ $this->file_open($log_file);
+ }
+
+ /**
+ * Open file for logging
+ *
+ * @param string $file File to open
+ */
+ protected function file_open($file)
+ {
+ if ($this->filesystem->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 = $this->language->lang_array(array_shift($message), $message);
+
+ 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/output_handler/migrator_output_handler_interface.php b/phpBB/phpbb/db/output_handler/migrator_output_handler_interface.php
new file mode 100644
index 0000000000..7bb5c73fec
--- /dev/null
+++ b/phpBB/phpbb/db/output_handler/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\output_handler;
+
+interface migrator_output_handler_interface
+{
+ const VERBOSITY_QUIET = 0;
+ const VERBOSITY_NORMAL = 1;
+ const VERBOSITY_VERBOSE = 2;
+ const VERBOSITY_VERY_VERBOSE = 3;
+ const VERBOSITY_DEBUG = 4;
+
+ /**
+ * 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/output_handler/null_migrator_output_handler.php b/phpBB/phpbb/db/output_handler/null_migrator_output_handler.php
new file mode 100644
index 0000000000..5fc2a52577
--- /dev/null
+++ b/phpBB/phpbb/db/output_handler/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\output_handler;
+
+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
index 41026ad425..18e4814a77 100644
--- a/phpBB/phpbb/db/sql_insert_buffer.php
+++ b/phpBB/phpbb/db/sql_insert_buffer.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -44,12 +48,10 @@ namespace phpbb\db;
*
* $buffer->flush();
* </code>
-*
-* @package dbal
*/
class sql_insert_buffer
{
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var string */
@@ -62,11 +64,11 @@ class sql_insert_buffer
protected $buffer = array();
/**
- * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\db\driver\driver_interface $db
* @param string $table_name
* @param int $max_buffered_rows
*/
- public function __construct(\phpbb\db\driver\driver $db, $table_name, $max_buffered_rows = 500)
+ public function __construct(\phpbb\db\driver\driver_interface $db, $table_name, $max_buffered_rows = 500)
{
$this->db = $db;
$this->table_name = $table_name;
@@ -90,7 +92,7 @@ class sql_insert_buffer
// Flush buffer if it is full or when DB does not support multi inserts.
// In the later case, the buffer will always only contain one row.
- if (!$this->db->multi_insert || sizeof($this->buffer) >= $this->max_buffered_rows)
+ if (!$this->db->get_multi_insert() || sizeof($this->buffer) >= $this->max_buffered_rows)
{
return $this->flush();
}
@@ -105,7 +107,7 @@ class sql_insert_buffer
* first building a huge rowset. Or at least sizeof($rows) should be kept
* small.
*
- * @param array $rows
+ * @param array $rows
*
* @return bool True when some data was flushed to the database.
* False otherwise.
diff --git a/phpBB/phpbb/db/tools/factory.php b/phpBB/phpbb/db/tools/factory.php
new file mode 100644
index 0000000000..d204451a63
--- /dev/null
+++ b/phpBB/phpbb/db/tools/factory.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\tools;
+
+/**
+ * A factory which serves the suitable tools instance for the given dbal
+ */
+class factory
+{
+ /**
+ * @param mixed $db_driver
+ * @param bool $return_statements
+ * @return \phpbb\db\tools\tools_interface
+ */
+ public function get($db_driver, $return_statements = false)
+ {
+ if ($db_driver instanceof \phpbb\db\driver\mssql || $db_driver instanceof \phpbb\db\driver\mssql_base)
+ {
+ return new \phpbb\db\tools\mssql($db_driver, $return_statements);
+ }
+ else if ($db_driver instanceof \phpbb\db\driver\postgres)
+ {
+ return new \phpbb\db\tools\postgres($db_driver, $return_statements);
+ }
+ else if ($db_driver instanceof \phpbb\db\driver\driver_interface)
+ {
+ return new \phpbb\db\tools\tools($db_driver, $return_statements);
+ }
+
+ throw new \InvalidArgumentException('Invalid database driver given');
+ }
+}
diff --git a/phpBB/phpbb/db/tools/mssql.php b/phpBB/phpbb/db/tools/mssql.php
new file mode 100644
index 0000000000..a90a85bbb2
--- /dev/null
+++ b/phpBB/phpbb/db/tools/mssql.php
@@ -0,0 +1,795 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\tools;
+
+/**
+ * Database Tools for handling cross-db actions such as altering columns, etc.
+ * Currently not supported is returning SQL for creating tables.
+ */
+class mssql extends tools
+{
+ /**
+ * Is the used MS SQL Server a SQL Server 2000?
+ * @var bool
+ */
+ protected $is_sql_server_2000;
+
+ /**
+ * Get the column types for mssql based databases
+ *
+ * @return array
+ */
+ public static function get_dbms_type_map()
+ {
+ return array(
+ 'mssql' => array(
+ 'INT:' => '[int]',
+ 'BINT' => '[float]',
+ 'ULINT' => '[int]',
+ '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]',
+ 'ULINT' => '[int]',
+ '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)',
+ ),
+ );
+ }
+
+ /**
+ * 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)
+ {
+ parent::__construct($db, $return_statements);
+
+ // Determine mapping database type
+ switch ($this->db->get_sql_layer())
+ {
+ case 'mssql':
+ case 'mssql_odbc':
+ $this->sql_layer = 'mssql';
+ break;
+
+ case 'mssqlnative':
+ $this->sql_layer = 'mssqlnative';
+ break;
+ }
+
+ $this->dbms_type_map = self::get_dbms_type_map();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_tables()
+ {
+ $sql = "SELECT name
+ FROM sysobjects
+ WHERE type='U'";
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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
+ $table_sql = 'CREATE TABLE [' . $table_name . '] (' . "\n";
+
+ 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
+ $columns[] = "\t [{$column_name}] " . $prepared_column['column_type_sql_default'];
+
+ // 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
+ $table_sql .= "\n);";
+ $statements[] = $table_sql;
+
+ // 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']);
+ }
+
+ // 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;
+ }
+ }
+
+ // 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);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_columns($table_name)
+ {
+ $columns = array();
+
+ $sql = "SELECT c.name
+ FROM syscolumns c
+ LEFT JOIN sysobjects o ON c.id = o.id
+ WHERE o.name = '{$table_name}'";
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_index_exists($table_name, $index_name)
+ {
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_unique_index_exists($table_name, $index_name)
+ {
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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, ) = $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();
+
+ $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;
+
+ $return_array['column_type_sql'] = $sql;
+
+ return $return_array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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();
+
+ // Does not support AFTER, only through temporary table
+ $statements[] = 'ALTER TABLE [' . $table_name . '] ADD [' . $column_name . '] ' . $column_data['column_type_sql_default'];
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_column_remove($table_name, $column_name, $inline = false)
+ {
+ $statements = array();
+
+ // 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;
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_index_drop($table_name, $index_name)
+ {
+ $statements = array();
+
+ $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name;
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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;
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_primary_key($table_name, $column, $inline = false)
+ {
+ $statements = array();
+
+ $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;
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_unique_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $this->check_index_name_length($table_name, $index_name);
+
+ $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $this->check_index_name_length($table_name, $index_name);
+
+ // remove index length
+ $column = preg_replace('#:.*$#', '', $column);
+
+ $statements[] = 'CREATE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_index($table_name)
+ {
+ $index_array = array();
+ $sql = "EXEC sp_statistics '$table_name'";
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['TYPE'] == 3)
+ {
+ $index_array[] = strtolower($row['INDEX_NAME']);
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return $index_array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_column_change($table_name, $column_name, $column_data, $inline = false)
+ {
+ $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
+ $statements = array();
+
+ // 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] ' . $this->db->sql_escape($column_data['default']) . ' FOR [' . $column_name . ']';
+ }
+
+ if (!empty($indexes))
+ {
+ // Recreate indexes after we changed the column
+ foreach ($indexes as $index_name => $index_data)
+ {
+ $result = $this->sql_create_index($table_name, $index_name, $index_data);
+ $statements = array_merge($statements, $result);
+ }
+ }
+
+ 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;
+
+ 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)
+ {
+ $existing_indexes = array();
+ if ($this->mssql_is_sql_server_2000())
+ {
+ // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
+ // Deprecated in SQL Server 2005
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name
+ FROM sysindexes ix
+ INNER JOIN sysindexkeys ixc
+ ON ixc.id = ix.id
+ AND ixc.indid = ix.indid
+ INNER JOIN syscolumns cols
+ ON cols.colid = ixc.colid
+ AND cols.id = ix.id
+ WHERE ix.id = object_id('{$table_name}')
+ AND cols.name = '{$column_name}'
+ 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');
+ }
+
+ $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();
+ }
+
+ if ($this->mssql_is_sql_server_2000())
+ {
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
+ FROM sysindexes ix
+ INNER JOIN sysindexkeys ixc
+ ON ixc.id = ix.id
+ AND ixc.indid = ix.indid
+ INNER JOIN syscolumns cols
+ ON cols.colid = ixc.colid
+ AND cols.id = ix.id
+ WHERE ix.id = object_id('{$table_name}')
+ AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
+ }
+ else
+ {
+ $sql = "SELECT DISTINCT ix.name AS phpbb_index_name, cols.name AS phpbb_column_name
+ FROM sys.indexes ix
+ INNER JOIN sys.index_columns ixc
+ ON ixc.object_id = ix.object_id
+ AND ixc.index_id = ix.index_id
+ INNER JOIN sys.columns cols
+ ON cols.column_id = ixc.column_id
+ AND cols.object_id = ix.object_id
+ WHERE ix.object_id = object_id('{$table_name}')
+ AND " . $this->db->sql_in_set('ix.name', array_keys($existing_indexes));
+ }
+
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $existing_indexes[$row['phpbb_index_name']][] = $row['phpbb_column_name'];
+ }
+ $this->db->sql_freeresult($result);
+
+ return $existing_indexes;
+ }
+
+ /**
+ * Is the used MS SQL Server a SQL Server 2000?
+ *
+ * @return bool
+ */
+ protected function mssql_is_sql_server_2000()
+ {
+ if ($this->is_sql_server_2000 === null)
+ {
+ $sql = "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(25)) AS mssql_version";
+ $result = $this->db->sql_query($sql);
+ $properties = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+ $this->is_sql_server_2000 = $properties['mssql_version'][0] == '8';
+ }
+
+ return $this->is_sql_server_2000;
+ }
+
+}
diff --git a/phpBB/phpbb/db/tools/postgres.php b/phpBB/phpbb/db/tools/postgres.php
new file mode 100644
index 0000000000..e2a4e668a6
--- /dev/null
+++ b/phpBB/phpbb/db/tools/postgres.php
@@ -0,0 +1,614 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\db\tools;
+
+/**
+ * Database Tools for handling cross-db actions such as altering columns, etc.
+ * Currently not supported is returning SQL for creating tables.
+ */
+class postgres extends tools
+{
+ /**
+ * Get the column types for postgres only
+ *
+ * @return array
+ */
+ public static function get_dbms_type_map()
+ {
+ return array(
+ 'postgres' => array(
+ 'INT:' => 'INT4',
+ 'BINT' => 'INT8',
+ 'ULINT' => 'INT4', // unsigned
+ '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',
+ ),
+ );
+ }
+
+ /**
+ * 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)
+ {
+ parent::__construct($db, $return_statements);
+
+ // Determine mapping database type
+ $this->sql_layer = 'postgres';
+
+ $this->dbms_type_map = self::get_dbms_type_map();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_tables()
+ {
+ $sql = 'SELECT relname
+ FROM pg_stat_user_tables';
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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
+ $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
+
+ // 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
+ $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
+
+ // 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);
+
+ // 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']);
+ }
+
+ $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
+ }
+ }
+
+ // do we need to add a sequence for auto incrementing columns?
+ if ($create_sequence)
+ {
+ $statements[] = "CREATE SEQUENCE {$table_name}_seq;";
+ }
+
+ // close the table
+ $table_sql .= "\n);";
+ $statements[] = $table_sql;
+
+ // 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);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_columns($table_name)
+ {
+ $columns = array();
+
+ $sql = "SELECT a.attname
+ FROM pg_class c, pg_attribute a
+ WHERE c.relname = '{$table_name}'
+ AND a.attnum > 0
+ AND a.attrelid = c.oid";
+ $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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_index_exists($table_name, $index_name)
+ {
+ $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')";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // This DBMS prefixes index names with the table name
+ $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
+
+ if (strtolower($row['index_name']) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_unique_index_exists($table_name, $index_name)
+ {
+ $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')";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if ($row['indisunique'] != 't')
+ {
+ continue;
+ }
+
+ // This DBMS prefixes index names with the table name
+ $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
+
+ if (strtolower($row['index_name']) == strtolower($index_name))
+ {
+ $this->db->sql_freeresult($result);
+ return true;
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ return false;
+ }
+
+ /**
+ * 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 = " {$column_type} ";
+
+ $return_array = array(
+ 'column_type' => $column_type,
+ '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)";
+ }
+
+ $return_array['column_type_sql'] = $sql;
+
+ return $return_array;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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();
+
+ // 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'];
+ }
+ }
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_column_remove($table_name, $column_name, $inline = false)
+ {
+ $statements = array();
+
+ $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_index_drop($table_name, $index_name)
+ {
+ $statements = array();
+
+ $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ 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;
+
+ // 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);
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_primary_key($table_name, $column, $inline = false)
+ {
+ $statements = array();
+
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_unique_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $this->check_index_name_length($table_name, $index_name);
+
+ $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_create_index($table_name, $index_name, $column)
+ {
+ $statements = array();
+
+ $this->check_index_name_length($table_name, $index_name);
+
+ // remove index length
+ $column = preg_replace('#:.*$#', '', $column);
+
+ $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
+
+ return $this->_sql_run_sql($statements);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_list_index($table_name)
+ {
+ $index_array = array();
+
+ $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')";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
+
+ $index_array[] = $row['index_name'];
+ }
+ $this->db->sql_freeresult($result);
+
+ return array_map('strtolower', $index_array);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ function sql_column_change($table_name, $column_name, $column_data, $inline = false)
+ {
+ $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
+ $statements = array();
+
+ $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;
+
+ return $this->_sql_run_sql($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)
+ {
+ // Not supported
+ throw new \Exception('DBMS is not supported');
+ }
+}
diff --git a/phpBB/phpbb/db/tools.php b/phpBB/phpbb/db/tools/tools.php
index 18e30d309a..e00683dd46 100644
--- a/phpBB/phpbb/db/tools.php
+++ b/phpBB/phpbb/db/tools/tools.php
@@ -1,21 +1,23 @@
<?php
/**
*
-* @package dbal
-* @copyright (c) 2007 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
-namespace phpbb\db;
+namespace phpbb\db\tools;
/**
* Database Tools for handling cross-db actions such as altering columns, etc.
* Currently not supported is returning SQL for creating tables.
-*
-* @package dbal
*/
-class tools
+class tools implements tools_interface
{
/**
* Current sql layer
@@ -38,7 +40,7 @@ class tools
*
* @return array
*/
- public static function get_dbms_type_map()
+ static public function get_dbms_type_map()
{
return array(
'mysql_41' => array(
@@ -103,99 +105,6 @@ class tools
'VARBINARY' => 'varbinary(255)',
),
- 'firebird' => array(
- 'INT:' => 'INTEGER',
- 'BINT' => 'DOUBLE PRECISION',
- 'ULINT' => 'INTEGER',
- 'UINT' => 'INTEGER',
- 'UINT:' => 'INTEGER',
- 'TINT:' => 'INTEGER',
- 'USINT' => 'INTEGER',
- 'BOOL' => 'INTEGER',
- 'VCHAR' => 'VARCHAR(255) CHARACTER SET NONE',
- 'VCHAR:' => 'VARCHAR(%d) CHARACTER SET NONE',
- 'CHAR:' => 'CHAR(%d) CHARACTER SET NONE',
- 'XSTEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE',
- 'STEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE',
- 'TEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE',
- 'MTEXT' => 'BLOB SUB_TYPE TEXT CHARACTER SET NONE',
- 'XSTEXT_UNI'=> 'VARCHAR(100) CHARACTER SET UTF8',
- 'STEXT_UNI' => 'VARCHAR(255) CHARACTER SET UTF8',
- 'TEXT_UNI' => 'BLOB SUB_TYPE TEXT CHARACTER SET UTF8',
- 'MTEXT_UNI' => 'BLOB SUB_TYPE TEXT CHARACTER SET UTF8',
- 'TIMESTAMP' => 'INTEGER',
- 'DECIMAL' => 'DOUBLE PRECISION',
- 'DECIMAL:' => 'DOUBLE PRECISION',
- 'PDECIMAL' => 'DOUBLE PRECISION',
- 'PDECIMAL:' => 'DOUBLE PRECISION',
- 'VCHAR_UNI' => 'VARCHAR(255) CHARACTER SET UTF8',
- 'VCHAR_UNI:'=> 'VARCHAR(%d) CHARACTER SET UTF8',
- 'VCHAR_CI' => 'VARCHAR(255) CHARACTER SET UTF8',
- 'VARBINARY' => 'CHAR(255) CHARACTER SET NONE',
- ),
-
- 'mssql' => array(
- 'INT:' => '[int]',
- 'BINT' => '[float]',
- 'ULINT' => '[int]',
- '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]',
- 'ULINT' => '[int]',
- '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)',
@@ -258,35 +167,35 @@ class tools
'VARBINARY' => 'blob',
),
- 'postgres' => array(
- 'INT:' => 'INT4',
- 'BINT' => 'INT8',
- 'ULINT' => 'INT4', // unsigned
- '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',
+ 'sqlite3' => array(
+ 'INT:' => 'INT(%d)',
+ 'BINT' => 'BIGINT(20)',
+ 'ULINT' => 'INTEGER UNSIGNED',
+ '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',
),
);
}
@@ -298,12 +207,6 @@ class tools
var $unsigned_types = array('ULINT', '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('firebird', 'mssql', 'mssqlnative', 'mysql_40', 'mysql_41', 'oracle', 'postgres', 'sqlite');
-
- /**
* 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.
*/
@@ -312,10 +215,10 @@ class tools
/**
* Constructor. Set DB Object and set {@link $return_statements return_statements}.
*
- * @param \phpbb\db\driver\driver $db Database connection
+ * @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 $db, $return_statements = false)
+ public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
{
$this->db = $db;
$this->return_statements = $return_statements;
@@ -323,7 +226,7 @@ class tools
$this->dbms_type_map = self::get_dbms_type_map();
// Determine mapping database type
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'mysql':
$this->sql_layer = 'mysql_40';
@@ -344,17 +247,8 @@ class tools
$this->sql_layer = 'mysql_41';
break;
- case 'mssql':
- case 'mssql_odbc':
- $this->sql_layer = 'mssql';
- break;
-
- case 'mssqlnative':
- $this->sql_layer = 'mssqlnative';
- break;
-
default:
- $this->sql_layer = $this->db->sql_layer;
+ $this->sql_layer = $this->db->get_sql_layer();
break;
}
}
@@ -371,13 +265,11 @@ class tools
}
/**
- * Gets a list of tables in the database.
- *
- * @return array Array of table names (all lower case)
- */
+ * {@inheritDoc}
+ */
function sql_list_tables()
{
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'mysql':
case 'mysql4':
@@ -391,24 +283,11 @@ class tools
WHERE type = "table"';
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 'firebird':
- $sql = 'SELECT rdb$relation_name
- FROM rdb$relations
- WHERE rdb$view_source is null
- AND rdb$system_flag = 0';
+ case 'sqlite3':
+ $sql = 'SELECT name
+ FROM sqlite_master
+ WHERE type = "table"
+ AND name <> "sqlite_sequence"';
break;
case 'oracle':
@@ -431,12 +310,8 @@ class tools
}
/**
- * Check if table exists
- *
- *
- * @param string $table_name The table name to check for
- * @return bool true if table exists, else false
- */
+ * {@inheritDoc}
+ */
function sql_table_exists($table_name)
{
$this->db->sql_return_on_error(true);
@@ -453,12 +328,8 @@ class tools
}
/**
- * 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.
- */
+ * {@inheritDoc}
+ */
function sql_create_table($table_name, $table_data)
{
// holds the DDL for a column
@@ -475,24 +346,11 @@ class tools
// Determine if we have created a PRIMARY KEY in the earliest
$primary_key_gen = false;
- // Determine if the table must be created with TEXTIMAGE
- $create_textimage = 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;
- }
+ $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
// Iterate through the columns to create a table
foreach ($table_data['COLUMNS'] as $column_name => $column_data)
@@ -506,17 +364,7 @@ class tools
}
// 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;
- }
+ $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
// 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)
@@ -524,12 +372,6 @@ class tools
$primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
}
- // create textimage DDL based off of the existance of certain column types
- if (!$create_textimage)
- {
- $create_textimage = isset($prepared_column['textimage']) && $prepared_column['textimage'];
- }
-
// create sequence DDL based off of the existance of auto incrementing columns
if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
{
@@ -540,21 +382,6 @@ class tools
// 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 'firebird':
- $table_sql .= "\n);";
- $statements[] = $table_sql;
- break;
-
- case 'mssql':
- case 'mssqlnative':
- $table_sql .= "\n) ON [PRIMARY]" . (($create_textimage) ? ' TEXTIMAGE_ON [PRIMARY]' : '');
- $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)
@@ -571,27 +398,11 @@ class tools
{
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 'firebird':
- 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;
@@ -610,17 +421,7 @@ class tools
case 'mysql_40':
case 'sqlite':
- $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;";
- }
-
+ case 'sqlite3':
$table_sql .= "\n);";
$statements[] = $table_sql;
break;
@@ -650,19 +451,6 @@ class tools
$statements[] = $trigger;
}
break;
-
- case 'firebird':
- if ($create_sequence)
- {
- $statements[] = "CREATE GENERATOR {$table_name}_gen;";
- $statements[] = "SET GENERATOR {$table_name}_gen TO 0;";
-
- $trigger = "CREATE TRIGGER t_$table_name FOR $table_name\n";
- $trigger .= "BEFORE INSERT\nAS\nBEGIN\n";
- $trigger .= "\tNEW.{$create_sequence} = GEN_ID({$table_name}_gen, 1);\nEND;";
- $statements[] = $trigger;
- }
- break;
}
// Write Keys
@@ -696,27 +484,8 @@ class tools
}
/**
- * Handle passed database update array.
- * Expected structure...
- * Key being one of the following
- * drop_tables: Drop tables
- * add_tables: Add tables
- * change_columns: Column changes (only type, not name)
- * add_columns: Add columns to a table
- * drop_keys: Dropping keys
- * 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)
- */
+ * {@inheritDoc}
+ */
function perform_schema_changes($schema_changes)
{
if (empty($schema_changes))
@@ -728,7 +497,7 @@ class tools
$sqlite = false;
// For SQLite we need to perform the schema changes in a much more different way
- if ($this->db->sql_layer == 'sqlite' && $this->return_statements)
+ if (($this->db->get_sql_layer() == 'sqlite' || $this->db->get_sql_layer() == 'sqlite3') && $this->return_statements)
{
$sqlite_data = array();
$sqlite = true;
@@ -904,7 +673,7 @@ class tools
}
}
- // Add unqiue indexes?
+ // Add unique indexes?
if (!empty($schema_changes['add_unique_index']))
{
foreach ($schema_changes['add_unique_index'] as $table => $index_array)
@@ -1096,13 +865,9 @@ class tools
}
/**
- * 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)
+ * {@inheritDoc}
+ */
+ function sql_list_columns($table_name)
{
$columns = array();
@@ -1110,46 +875,21 @@ class tools
{
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}'";
+ $sql = "SHOW COLUMNS FROM $table_name";
break;
case 'oracle':
$sql = "SELECT column_name
FROM user_tab_columns
- WHERE LOWER(table_name) = '" . strtolower($table) . "'";
- break;
-
- case 'firebird':
- $sql = "SELECT RDB\$FIELD_NAME as FNAME
- FROM RDB\$RELATION_FIELDS
- WHERE RDB\$RELATION_NAME = '" . strtoupper($table) . "'";
+ WHERE LOWER(table_name) = '" . strtolower($table_name) . "'";
break;
case 'sqlite':
+ case 'sqlite3':
$sql = "SELECT sql
FROM sqlite_master
WHERE type = 'table'
- AND name = '{$table}'";
+ AND name = '{$table_name}'";
$result = $this->db->sql_query($sql);
@@ -1195,73 +935,22 @@ class tools
}
/**
- * 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)
+ * {@inheritDoc}
+ */
+ function sql_column_exists($table_name, $column_name)
{
- $columns = $this->sql_list_columns($table);
+ $columns = $this->sql_list_columns($table_name);
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
- */
+ * {@inheritDoc}
+ */
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 'firebird':
- $sql = "SELECT LOWER(RDB\$INDEX_NAME) as index_name
- FROM RDB\$INDICES
- WHERE RDB\$RELATION_NAME = '" . strtoupper($table_name) . "'
- AND RDB\$UNIQUE_FLAG IS NULL
- AND RDB\$FOREIGN_KEY IS NULL";
- $col = 'index_name';
- break;
-
- 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
@@ -1279,6 +968,7 @@ class tools
break;
case 'sqlite':
+ case 'sqlite3':
$sql = "PRAGMA index_list('" . $table_name . "');";
$col = 'name';
break;
@@ -1295,10 +985,9 @@ class tools
// These DBMS prefix index name with the table name
switch ($this->sql_layer)
{
- case 'firebird':
case 'oracle':
- case 'postgres':
case 'sqlite':
+ case 'sqlite3':
$row[$col] = substr($row[$col], strlen($table_name) + 1);
break;
}
@@ -1315,57 +1004,12 @@ class tools
}
/**
- * 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
- */
+ * {@inheritDoc}
+ */
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 'firebird':
- $sql = "SELECT LOWER(RDB\$INDEX_NAME) as index_name
- FROM RDB\$INDICES
- WHERE RDB\$RELATION_NAME = '" . strtoupper($table_name) . "'
- AND RDB\$UNIQUE_FLAG IS NOT NULL
- AND RDB\$FOREIGN_KEY IS NULL";
- $col = 'index_name';
- break;
-
- 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
@@ -1383,6 +1027,7 @@ class tools
break;
case 'sqlite':
+ case 'sqlite3':
$sql = "PRAGMA index_list('" . $table_name . "');";
$col = 'name';
break;
@@ -1396,12 +1041,7 @@ class tools
continue;
}
- if ($this->sql_layer == 'sqlite' && !$row['unique'])
- {
- continue;
- }
-
- if ($this->sql_layer == 'postgres' && $row['indisunique'] != 't')
+ if (($this->sql_layer == 'sqlite' || $this->sql_layer == 'sqlite3') && !$row['unique'])
{
continue;
}
@@ -1421,9 +1061,8 @@ class tools
}
break;
- case 'firebird':
- case 'postgres':
case 'sqlite':
+ case 'sqlite3':
$row[$col] = substr($row[$col], strlen($table_name) + 1);
break;
}
@@ -1482,7 +1121,7 @@ class tools
}
// Get type
- list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
+ list($column_type) = $this->get_column_type($column_data[0]);
// Adjust default value if db-dependent specified
if (is_array($column_data[1]))
@@ -1496,68 +1135,6 @@ class tools
switch ($this->sql_layer)
{
- case 'firebird':
- $sql .= " {$column_type} ";
- $return_array['column_type_sql_type'] = " {$column_type} ";
-
- if (!is_null($column_data[1]))
- {
- $sql .= 'DEFAULT ' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ' ';
- $return_array['column_type_sql_default'] = ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ' ';
- }
-
- $sql .= 'NOT NULL';
-
- // This is a UNICODE column and thus should be given it's fair share
- if (preg_match('/^X?STEXT_UNI|VCHAR_(CI|UNI:?)/', $column_data[0]))
- {
- $sql .= ' COLLATE UNICODE';
- }
-
- $return_array['auto_increment'] = false;
- if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
- {
- $return_array['auto_increment'] = true;
- }
-
- break;
-
- case 'mssql':
- case 'mssqlnative':
- $sql .= " {$column_type} ";
- $sql_default = " {$column_type} ";
-
- // For adding columns we need the default definition
- if (!is_null($column_data[1]))
- {
- // For hexadecimal values do not use single quotes
- if (strpos($column_data[1], '0x') === 0)
- {
- $return_array['default'] = 'DEFAULT (' . $column_data[1] . ') ';
- $sql_default .= $return_array['default'];
- }
- else
- {
- $return_array['default'] = 'DEFAULT (' . ((is_numeric($column_data[1])) ? $column_data[1] : "'{$column_data[1]}'") . ') ';
- $sql_default .= $return_array['default'];
- }
- }
-
- if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
- {
-// $sql .= 'IDENTITY (1, 1) ';
- $sql_default .= 'IDENTITY (1, 1) ';
- }
-
- $return_array['textimage'] = $column_type === '[text]';
-
- $sql .= 'NOT NULL';
- $sql_default .= 'NOT NULL';
-
- $return_array['column_type_sql_default'] = $sql_default;
-
- break;
-
case 'mysql_40':
case 'mysql_41':
$sql .= " {$column_type} ";
@@ -1567,7 +1144,15 @@ class tools
{
$sql .= (strpos($column_data[1], '0x') === 0) ? "DEFAULT {$column_data[1]} " : "DEFAULT '{$column_data[1]}' ";
}
- $sql .= 'NOT NULL';
+
+ 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]))
{
@@ -1592,7 +1177,7 @@ class tools
// Oracle does not like setting NOT NULL on a column that is already NOT NULL (this happens only on number fields)
if (!preg_match('/number/i', $column_type))
{
- $sql .= ($column_data[1] === '') ? '' : 'NOT NULL';
+ $sql .= ($column_data[1] === '' || $column_data[1] === null) ? '' : 'NOT NULL';
}
$return_array['auto_increment'] = false;
@@ -1603,51 +1188,29 @@ class tools
break;
- case 'postgres':
- $return_array['column_type'] = $column_type;
-
- $sql .= " {$column_type} ";
-
- $return_array['auto_increment'] = false;
- if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
- {
- $default_val = "nextval('{$table_name}_seq')";
- $return_array['auto_increment'] = true;
- }
- else if (!is_null($column_data[1]))
- {
- $default_val = "'" . $column_data[1] . "'";
- $return_array['null'] = 'NOT NULL';
- $sql .= 'NOT NULL ';
- }
-
- $return_array['default'] = $default_val;
-
- $sql .= "DEFAULT {$default_val}";
-
- // Unsigned? Then add a CHECK contraint
- if (in_array($orig_column_type, $this->unsigned_types))
- {
- $return_array['constraint'] = "CHECK ({$column_name} >= 0)";
- $sql .= " CHECK ({$column_name} >= 0)";
- }
-
- break;
-
case 'sqlite':
+ case 'sqlite3':
$return_array['primary_key_set'] = false;
if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
{
$sql .= ' INTEGER PRIMARY KEY';
$return_array['primary_key_set'] = true;
+
+ if ($this->sql_layer === 'sqlite3')
+ {
+ $sql .= ' AUTOINCREMENT';
+ }
}
else
{
$sql .= ' ' . $column_type;
}
- $sql .= ' NOT NULL ';
- $sql .= (!is_null($column_data[1])) ? "DEFAULT '{$column_data[1]}'" : '';
+ if (!is_null($column_data[1]))
+ {
+ $sql .= ' NOT NULL ';
+ $sql .= "DEFAULT '{$column_data[1]}'";
+ }
break;
}
@@ -1666,6 +1229,7 @@ class tools
*/
function get_column_type($column_map_type)
{
+ $column_type = '';
if (strpos($column_map_type, ':') !== false)
{
list($orig_column_type, $column_length) = explode(':', $column_map_type);
@@ -1717,8 +1281,8 @@ class tools
}
/**
- * Add new column
- */
+ * {@inheritDoc}
+ */
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);
@@ -1726,17 +1290,6 @@ class tools
switch ($this->sql_layer)
{
- case 'firebird':
- // Does not support AFTER statement, only POSITION (and there you need the column position)
- $statements[] = 'ALTER TABLE ' . $table_name . ' ADD "' . strtoupper($column_name) . '" ' . $column_data['column_type_sql'];
- break;
-
- 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'] : '';
@@ -1748,95 +1301,64 @@ class tools
$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'];
}
- if (version_compare(sqlite_libversion(), '3.0') == -1)
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
{
- $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)
- {
- break;
- }
+ break;
+ }
- $row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
+ $statements[] = 'begin';
- $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', $row['sql']);
- $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
- $statements[] = 'DROP TABLE ' . $table_name;
+ // 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', $row['sql'], $matches);
+ 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();
+ $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)
+ foreach ($old_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY')
{
- $entities = preg_split('#\s+#', trim($declaration));
- if ($entities[0] == 'PRIMARY')
- {
- continue;
- }
- $column_list[] = $entities[0];
+ continue;
}
+ $column_list[] = $entities[0];
+ }
- $columns = implode(',', $column_list);
+ $columns = implode(',', $column_list);
- $new_table_cols = $column_name . ' ' . $column_data['column_type_sql'] . ',' . $new_table_cols;
+ $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[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
- $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+ // 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[] = 'commit';
- }
- else
+ $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)
{
- $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' [' . $column_data['column_type_sql'] . ']';
+ return $column_name . ' ' . $column_data['column_type_sql'];
}
+
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD ' . $column_name . ' ' . $column_data['column_type_sql'];
break;
}
@@ -1844,39 +1366,14 @@ class tools
}
/**
- * Drop column
- */
+ * {@inheritDoc}
+ */
function sql_column_remove($table_name, $column_name, $inline = false)
{
$statements = array();
switch ($this->sql_layer)
{
- case 'firebird':
- $statements[] = 'ALTER TABLE ' . $table_name . ' DROP "' . strtoupper($column_name) . '"';
- break;
-
- case 'mssql':
- case 'mssqlnative':
- // remove default cosntraints first
- // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx
- $statements[] = "DECLARE @drop_default_name VARCHAR(100), @cmd VARCHAR(1000)
- SET @drop_default_name =
- (SELECT so.name FROM sysobjects so
- JOIN sysconstraints sc ON so.id = sc.constid
- WHERE object_name(so.parent_obj) = '{$table_name}'
- AND so.xtype = 'D'
- AND sc.colid = (SELECT colid FROM syscolumns
- WHERE id = object_id('{$table_name}')
- AND name = '{$column_name}'))
- IF @drop_default_name <> ''
- BEGIN
- SET @cmd = 'ALTER TABLE [{$table_name}] DROP CONSTRAINT [' + @drop_default_name + ']'
- EXEC(@cmd)
- END";
- $statements[] = 'ALTER TABLE [' . $table_name . '] DROP COLUMN [' . $column_name . ']';
- break;
-
case 'mysql_40':
case 'mysql_41':
$statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`';
@@ -1886,72 +1383,62 @@ class tools
$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;
}
- if (version_compare(sqlite_libversion(), '3.0') == -1)
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name, $column_name);
+ if (empty($recreate_queries))
{
- $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)
- {
- break;
- }
+ break;
+ }
- $row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
+ $statements[] = 'begin';
- $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', $row['sql']);
- $statements[] = 'INSERT INTO ' . $table_name . '_temp SELECT * FROM ' . $table_name;
- $statements[] = 'DROP TABLE ' . $table_name;
+ // 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', $row['sql'], $matches);
+ 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();
+ $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)
+ foreach ($old_table_cols as $declaration)
+ {
+ $entities = preg_split('#\s+#', trim($declaration));
+ if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name)
{
- $entities = preg_split('#\s+#', trim($declaration));
- if ($entities[0] == 'PRIMARY' || $entities[0] === $column_name)
- {
- continue;
- }
- $column_list[] = $entities[0];
+ continue;
}
+ $column_list[] = $entities[0];
+ }
- $columns = implode(',', $column_list);
-
- $new_table_cols = preg_replace('/' . $column_name . '[^,]+(?:,|$)/m', '', $new_table_cols);
-
- // create a new table and fill it up. destroy the temp one
- $statements[] = 'CREATE TABLE ' . $table_name . ' (' . $new_table_cols . ');';
- $statements[] = 'INSERT INTO ' . $table_name . ' (' . $columns . ') SELECT ' . $columns . ' FROM ' . $table_name . '_temp;';
- $statements[] = 'DROP TABLE ' . $table_name . '_temp';
+ $columns = implode(',', $column_list);
- $statements[] = 'commit';
- }
- else
+ $new_table_cols = trim(preg_replace('/' . $column_name . '\b[^,]+(?:,|$)/m', '', $new_table_cols));
+ if (substr($new_table_cols, -1) === ',')
{
- $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN ' . $column_name;
+ // 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;
}
@@ -1959,28 +1446,22 @@ class tools
}
/**
- * Drop Index
- */
+ * {@inheritDoc}
+ */
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 'firebird':
case 'oracle':
- case 'postgres':
case 'sqlite':
+ case 'sqlite3':
$statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
break;
}
@@ -1989,8 +1470,8 @@ class tools
}
/**
- * Drop Table
- */
+ * {@inheritDoc}
+ */
function sql_table_drop($table_name)
{
$statements = array();
@@ -2005,21 +1486,6 @@ class tools
switch ($this->sql_layer)
{
- case 'firebird':
- $sql = 'SELECT RDB$GENERATOR_NAME as gen
- FROM RDB$GENERATORS
- WHERE RDB$SYSTEM_FLAG = 0
- AND RDB$GENERATOR_NAME = \'' . strtoupper($table_name) . "_GEN'";
- $result = $this->db->sql_query($sql);
-
- // does a generator exist?
- if ($row = $this->db->sql_fetchrow($result))
- {
- $statements[] = "DROP GENERATOR {$row['gen']};";
- }
- $this->db->sql_freeresult($result);
- break;
-
case 'oracle':
$sql = 'SELECT A.REFERENCED_NAME
FROM USER_DEPENDENCIES A, USER_TRIGGERS B
@@ -2035,87 +1501,53 @@ class tools
}
$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
- */
+ * {@inheritDoc}
+ */
function sql_create_primary_key($table_name, $column, $inline = false)
{
$statements = array();
switch ($this->sql_layer)
{
- case 'firebird':
- 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 .= ') ON [PRIMARY]';
-
- $statements[] = $sql;
- break;
-
case 'oracle':
- $statements[] = 'ALTER TABLE ' . $table_name . 'add CONSTRAINT pk_' . $table_name . ' PRIMARY KEY (' . implode(', ', $column) . ')';
+ $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;
}
- $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)
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
{
break;
}
- $row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
-
$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', $row['sql']);
+ $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', $row['sql'], $matches);
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
$new_table_cols = trim($matches[1]);
$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
@@ -2135,6 +1567,8 @@ class tools
// 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';
@@ -2146,25 +1580,19 @@ class tools
}
/**
- * Add unique index
- */
+ * {@inheritDoc}
+ */
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);
- }
+ $this->check_index_name_length($table_name, $index_name);
switch ($this->sql_layer)
{
- case 'firebird':
- case 'postgres':
case 'oracle':
case 'sqlite':
+ case 'sqlite3':
$statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
break;
@@ -2172,29 +1600,19 @@ class tools
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) . ') ON [PRIMARY]';
- break;
}
return $this->_sql_run_sql($statements);
}
/**
- * Add index
- */
+ * {@inheritDoc}
+ */
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);
- }
+ $this->check_index_name_length($table_name, $index_name);
// remove index length unless MySQL4
if ('mysql_40' != $this->sql_layer)
@@ -2204,10 +1622,9 @@ class tools
switch ($this->sql_layer)
{
- case 'firebird':
- case 'postgres':
case 'oracle':
case 'sqlite':
+ case 'sqlite3':
$statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
break;
@@ -2223,12 +1640,7 @@ class tools
}
// 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) . ') ON [PRIMARY]';
+ $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . ' (' . implode(', ', $column) . ')';
break;
}
@@ -2236,252 +1648,195 @@ class tools
}
/**
- * List all of the indices that belong to a table,
- * does not count:
- * * UNIQUE indices
- * * PRIMARY keys
- */
+ * Check whether the index name is too long
+ *
+ * @param string $table_name
+ * @param string $index_name
+ */
+ protected function check_index_name_length($table_name, $index_name)
+ {
+ $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);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
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)
{
- switch ($this->sql_layer)
- {
- case 'firebird':
- $sql = "SELECT LOWER(RDB\$INDEX_NAME) as index_name
- FROM RDB\$INDICES
- WHERE RDB\$RELATION_NAME = '" . strtoupper($table_name) . "'
- AND RDB\$UNIQUE_FLAG IS NULL
- AND RDB\$FOREIGN_KEY IS NULL";
- $col = 'index_name';
- break;
-
- 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';
+ case 'mysql_40':
+ case 'mysql_41':
+ $sql = 'SHOW KEYS
+ FROM ' . $table_name;
+ $col = 'Key_name';
break;
- case 'mysql_40':
- case 'mysql_41':
- $sql = 'SHOW KEYS
- FROM ' . $table_name;
- $col = 'Key_name';
+ 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 'oracle':
- $sql = "SELECT index_name
- FROM user_indexes
- WHERE table_name = '" . strtoupper($table_name) . "'
- AND generated = 'N'
- AND uniqueness = 'NONUNIQUE'";
- $col = 'index_name';
+ case 'sqlite':
+ case 'sqlite3':
+ $sql = "PRAGMA index_info('" . $table_name . "');";
+ $col = 'name';
break;
+ }
- case 'sqlite':
- $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;
}
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
+ switch ($this->sql_layer)
{
- if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique'])
- {
- continue;
- }
-
- switch ($this->sql_layer)
- {
- case 'firebird':
- case 'oracle':
- case 'postgres':
- case 'sqlite':
- $row[$col] = substr($row[$col], strlen($table_name) + 1);
+ case 'oracle':
+ case 'sqlite':
+ case 'sqlite3':
+ $row[$col] = substr($row[$col], strlen($table_name) + 1);
break;
- }
-
- $index_array[] = $row[$col];
}
- $this->db->sql_freeresult($result);
+
+ $index_array[] = $row[$col];
}
+ $this->db->sql_freeresult($result);
return array_map('strtolower', $index_array);
}
/**
- * Change column type (not name!)
- */
+ * 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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
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 'firebird':
- // Change type...
- if (!empty($column_data['column_type_sql_default']))
- {
- $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" TYPE ' . ' ' . $column_data['column_type_sql_type'];
- $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" SET DEFAULT ' . ' ' . $column_data['column_type_sql_default'];
- }
- else
- {
- // TODO: try to change pkey without removing trigger, generator or constraints. ATM this query may fail.
- $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN "' . strtoupper($column_name) . '" TYPE ' . ' ' . $column_data['column_type_sql_type'];
- }
- break;
-
- case 'mssql':
- case 'mssqlnative':
- $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql'];
-
- if (!empty($column_data['default']))
- {
- // Using TRANSACT-SQL for this statement because we do not want to have colliding data if statements are executed at a later stage
- $statements[] = "DECLARE @drop_default_name VARCHAR(100), @cmd VARCHAR(1000)
- SET @drop_default_name =
- (SELECT so.name FROM sysobjects so
- JOIN sysconstraints sc ON so.id = sc.constid
- WHERE object_name(so.parent_obj) = '{$table_name}'
- AND so.xtype = 'D'
- AND sc.colid = (SELECT colid FROM syscolumns
- WHERE id = object_id('{$table_name}')
- AND name = '{$column_name}'))
- IF @drop_default_name <> ''
- BEGIN
- SET @cmd = 'ALTER TABLE [{$table_name}] DROP CONSTRAINT [' + @drop_default_name + ']'
- EXEC(@cmd)
- END
- SET @cmd = 'ALTER TABLE [{$table_name}] ADD CONSTRAINT [DF_{$table_name}_{$column_name}_1] {$column_data['default']} FOR [{$column_name}]'
- EXEC(@cmd)";
- }
- break;
-
case 'mysql_40':
case 'mysql_41':
$statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql'];
break;
case 'oracle':
- $statements[] = 'ALTER TABLE ' . $table_name . ' MODIFY ' . $column_name . ' ' . $column_data['column_type_sql'];
- break;
-
- case 'postgres':
- $sql = 'ALTER TABLE ' . $table_name . ' ';
+ // We need the data here
+ $old_return_statements = $this->return_statements;
+ $this->return_statements = true;
- $sql_array = array();
- $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
+ // 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);
- if (isset($column_data['null']))
+ // Drop any indexes
+ if (!empty($indexes) || !empty($unique_indexes))
{
- if ($column_data['null'] == 'NOT NULL')
- {
- $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
- }
- else if ($column_data['null'] == 'NULL')
+ $drop_indexes = array_merge(array_keys($indexes), array_keys($unique_indexes));
+ foreach ($drop_indexes as $index_name)
{
- $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
+ $result = $this->sql_index_drop($table_name, $this->strip_table_name_from_index_name($table_name, $index_name));
+ $statements = array_merge($statements, $result);
}
}
- if (isset($column_data['default']))
- {
- $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
- }
+ $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);
- // we don't want to double up on constraints if we change different number data types
- if (isset($column_data['constraint']))
+ if (!empty($indexes))
{
- $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))
+ // Recreate indexes after we changed the column
+ foreach ($indexes as $index_name => $index_data)
{
- if (trim($row['constraint_data']) == trim($column_data['constraint']))
- {
- $constraint_exists = true;
- break;
- }
+ $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);
}
- $this->db->sql_freeresult($result);
+ }
- if (!$constraint_exists)
+ if (!empty($unique_indexes))
+ {
+ // Recreate unique indexes after we changed the column
+ foreach ($unique_indexes as $index_name => $index_data)
{
- $sql_array[] = 'ADD ' . $column_data['constraint'];
+ $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);
}
}
- $sql .= implode(', ', $sql_array);
+ // 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);
- $statements[] = $sql;
+ $this->return_statements = $old_return_statements;
break;
case 'sqlite':
+ case 'sqlite3':
if ($inline && $this->return_statements)
{
return $column_name . ' ' . $column_data['column_type_sql'];
}
- $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)
+ $recreate_queries = $this->sqlite_get_recreate_table_queries($table_name);
+ if (empty($recreate_queries))
{
break;
}
- $row = $this->db->sql_fetchrow($result);
- $this->db->sql_freeresult($result);
-
$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', $row['sql']);
+ $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', $row['sql'], $matches);
+ preg_match('#\((.*)\)#s', $sql_create_table, $matches);
$new_table_cols = trim($matches[1]);
$old_table_cols = preg_split('/,(?![\s\w]+\))/m', $new_table_cols);
@@ -2489,7 +1844,18 @@ class tools
foreach ($old_table_cols as $key => $declaration)
{
- $entities = preg_split('#\s+#', trim($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)
{
@@ -2499,8 +1865,10 @@ class tools
$columns = implode(',', $column_list);
- // create a new table and fill it up. destroy the temp one
+ // 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';
@@ -2511,4 +1879,145 @@ class tools
return $this->_sql_run_sql($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 'sqlite':
+ case 'sqlite3':
+ // Not supported
+ throw new \Exception('DBMS is not supported');
+ break;
+ }
+
+ $sql = '';
+ $existing_indexes = array();
+
+ switch ($this->sql_layer)
+ {
+ 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 '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;
+ }
+
+ /**
+ * 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;
+ }
}
diff --git a/phpBB/phpbb/db/tools/tools_interface.php b/phpBB/phpbb/db/tools/tools_interface.php
new file mode 100644
index 0000000000..f153f73a54
--- /dev/null
+++ b/phpBB/phpbb/db/tools/tools_interface.php
@@ -0,0 +1,202 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\db\tools;
+
+/**
+ * Interface for a Database Tools for handling cross-db actions such as altering columns, etc.
+ */
+interface tools_interface
+{
+ /**
+ * 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}),
+ * )
+ *
+ *
+ * @param array $schema_changes
+ * @return null
+ */
+ public function perform_schema_changes($schema_changes);
+
+ /**
+ * Gets a list of tables in the database.
+ *
+ * @return array Array of table names (all lower case)
+ */
+ public function sql_list_tables();
+
+ /**
+ * Check if table exists
+ *
+ * @param string $table_name The table name to check for
+ * @return bool true if table exists, else false
+ */
+ public function sql_table_exists($table_name);
+
+ /**
+ * Create SQL Table
+ *
+ * @param string $table_name The table name to create
+ * @param array $table_data Array containing table data.
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_create_table($table_name, $table_data);
+
+ /**
+ * Drop Table
+ *
+ * @param string $table_name The table name to drop
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_table_drop($table_name);
+
+ /**
+ * Gets a list of columns of a table.
+ *
+ * @param string $table_name Table name
+ * @return array Array of column names (all lower case)
+ */
+ public function sql_list_columns($table_name);
+
+ /**
+ * Check whether a specified column exist in a table
+ *
+ * @param string $table_name Table to check
+ * @param string $column_name Column to check
+ * @return bool True if column exists, false otherwise
+ */
+ public function sql_column_exists($table_name, $column_name);
+
+ /**
+ * Add new column
+ *
+ * @param string $table_name Table to modify
+ * @param string $column_name Name of the column to add
+ * @param array $column_data Column data
+ * @param bool $inline Whether the query should actually be run,
+ * or return a string for adding the column
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_column_add($table_name, $column_name, $column_data, $inline = false);
+
+ /**
+ * Change column type (not name!)
+ *
+ * @param string $table_name Table to modify
+ * @param string $column_name Name of the column to modify
+ * @param array $column_data Column data
+ * @param bool $inline Whether the query should actually be run,
+ * or return a string for modifying the column
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_column_change($table_name, $column_name, $column_data, $inline = false);
+
+ /**
+ * Drop column
+ *
+ * @param string $table_name Table to modify
+ * @param string $column_name Name of the column to drop
+ * @param bool $inline Whether the query should actually be run,
+ * or return a string for deleting the column
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_column_remove($table_name, $column_name, $inline = false);
+
+ /**
+ * List all of the indices that belong to a table
+ *
+ * NOTE: does not list
+ * - UNIQUE indices
+ * - PRIMARY keys
+ *
+ * @param string $table_name Table to check
+ * @return array Array with index names
+ */
+ public function sql_list_index($table_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
+ */
+ public function sql_index_exists($table_name, $index_name);
+
+ /**
+ * Add index
+ *
+ * @param string $table_name Table to modify
+ * @param string $index_name Name of the index to create
+ * @param string|array $column Either a string with a column name, or an array with columns
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_create_index($table_name, $index_name, $column);
+
+ /**
+ * Drop Index
+ *
+ * @param string $table_name Table to modify
+ * @param string $index_name Name of the index to delete
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_index_drop($table_name, $index_name);
+
+ /**
+ * Check if a specified index exists in table.
+ *
+ * NOTE: Does not return normal and 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
+ */
+ public function sql_unique_index_exists($table_name, $index_name);
+
+ /**
+ * Add unique index
+ *
+ * @param string $table_name Table to modify
+ * @param string $index_name Name of the unique index to create
+ * @param string|array $column Either a string with a column name, or an array with columns
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_create_unique_index($table_name, $index_name, $column);
+
+ /**
+ * Add primary key
+ *
+ * @param string $table_name Table to modify
+ * @param string|array $column Either a string with a column name, or an array with columns
+ * @param bool $inline Whether the query should actually be run,
+ * or return a string for creating the key
+ * @return array|true Statements to run, or true if the statements have been executed
+ */
+ public function sql_create_primary_key($table_name, $column, $inline = false);
+}
diff --git a/phpBB/phpbb/debug/debug.php b/phpBB/phpbb/debug/debug.php
new file mode 100644
index 0000000000..c5ffada2e5
--- /dev/null
+++ b/phpBB/phpbb/debug/debug.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\debug;
+
+use Symfony\Component\Debug\BufferingLogger;
+use Symfony\Component\Debug\DebugClassLoader;
+use Symfony\Component\Debug\ExceptionHandler;
+
+/**
+ * Registers all the debug tools.
+
+ * @see Symfony\Component\Debug\Debug
+ */
+class debug
+{
+ private static $enabled = false;
+
+ /**
+ * Enables the debug tools.
+ *
+ * This method registers an error handler and an exception handler.
+ *
+ * If the Symfony ClassLoader component is available, a special
+ * class loader is also registered.
+ *
+ * @param int $errorReportingLevel The level of error reporting you want
+ * @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
+ */
+ public static function enable($errorReportingLevel = null, $displayErrors = true)
+ {
+ if (static::$enabled)
+ {
+ return;
+ }
+
+ static::$enabled = true;
+
+ if ($errorReportingLevel !== null)
+ {
+ error_reporting($errorReportingLevel);
+ }
+ else
+ {
+ error_reporting(-1);
+ }
+
+ if ('cli' !== php_sapi_name())
+ {
+ ini_set('display_errors', 0);
+ ExceptionHandler::register();
+ }
+ else if ($displayErrors && (!ini_get('log_errors') || ini_get('error_log')))
+ {
+ // CLI - display errors only if they're not already logged to STDERR
+ ini_set('display_errors', 1);
+ }
+
+ if ($displayErrors)
+ {
+ error_handler::register(new error_handler(new BufferingLogger()));
+ }
+ else
+ {
+ error_handler::register()->throwAt(0, true);
+ }
+
+ DebugClassLoader::enable();
+ }
+}
diff --git a/phpBB/phpbb/debug/error_handler.php b/phpBB/phpbb/debug/error_handler.php
new file mode 100644
index 0000000000..246e724f56
--- /dev/null
+++ b/phpBB/phpbb/debug/error_handler.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\debug;
+
+use Symfony\Component\Debug\ErrorHandler;
+
+class error_handler extends ErrorHandler
+{
+ public function handleError($type, $message, $file, $line, array $context, array $backtrace = null)
+ {
+ if ($type === E_USER_WARNING || $type === E_USER_NOTICE)
+ {
+ $handler = defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler';
+
+ $handler($type, $message, $file, $line);
+ }
+
+ return parent::handleError($type, $message, $file, $line, $context, $backtrace);
+ }
+}
diff --git a/phpBB/phpbb/di/container_builder.php b/phpBB/phpbb/di/container_builder.php
new file mode 100644
index 0000000000..8f175c966c
--- /dev/null
+++ b/phpBB/phpbb/di/container_builder.php
@@ -0,0 +1,551 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\di;
+
+use phpbb\filesystem\filesystem;
+use Symfony\Component\Config\ConfigCache;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
+use Symfony\Component\Filesystem\Exception\IOException;
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
+
+class container_builder
+{
+ /**
+ * @var string The environment to use.
+ */
+ protected $environment;
+
+ /**
+ * @var string phpBB Root Path
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string php file extension
+ */
+ protected $php_ext;
+
+ /**
+ * The container under construction
+ *
+ * @var ContainerBuilder
+ */
+ protected $container;
+
+ /**
+ * Indicates whether extensions should be used (default to true).
+ *
+ * @var bool
+ */
+ protected $use_extensions = true;
+
+ /**
+ * Defines a custom path to find the configuration of the container (default to $this->phpbb_root_path . 'config')
+ *
+ * @var string
+ */
+ protected $config_path = null;
+
+ /**
+ * Indicates whether the container should be dumped to the filesystem (default to true).
+ *
+ * If DEBUG_CONTAINER is set this option is ignored and a new container is build.
+ *
+ * @var bool
+ */
+ protected $use_cache = true;
+
+ /**
+ * Indicates if the container should be compiled automatically (default to true).
+ *
+ * @var bool
+ */
+ protected $compile_container = true;
+
+ /**
+ * Custom parameters to inject into the container.
+ *
+ * Default to:
+ * array(
+ * 'core.root_path', $this->phpbb_root_path,
+ * 'core.php_ext', $this->php_ext,
+ * );
+ *
+ * @var array
+ */
+ protected $custom_parameters = null;
+
+ /**
+ * @var \phpbb\config_php_file
+ */
+ protected $config_php_file;
+
+ /**
+ * @var string
+ */
+ protected $cache_dir;
+
+ /**
+ * @var array
+ */
+ private $container_extensions;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path Path to the phpbb includes directory.
+ * @param string $php_ext php file extension
+ */
+ function __construct($phpbb_root_path, $php_ext)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Build and return a new Container respecting the current configuration
+ *
+ * @return \phpbb_cache_container|ContainerBuilder
+ */
+ public function get_container()
+ {
+ $container_filename = $this->get_container_filename();
+ $config_cache = new ConfigCache($container_filename, defined('DEBUG'));
+ if ($this->use_cache && $config_cache->isFresh())
+ {
+ require($config_cache->getPath());
+ $this->container = new \phpbb_cache_container();
+ }
+ else
+ {
+ $this->container_extensions = array(new extension\core($this->get_config_path()));
+
+ if ($this->use_extensions)
+ {
+ $this->load_extensions();
+ }
+
+ // Inject the config
+ if ($this->config_php_file)
+ {
+ $this->container_extensions[] = new extension\config($this->config_php_file);
+ }
+
+ $this->container = $this->create_container($this->container_extensions);
+
+ // Easy collections through tags
+ $this->container->addCompilerPass(new pass\collection_pass());
+
+ // Event listeners "phpBB style"
+ $this->container->addCompilerPass(new RegisterListenersPass('dispatcher', 'event.listener_listener', 'event.listener'));
+
+ // Event listeners "Symfony style"
+ $this->container->addCompilerPass(new RegisterListenersPass('dispatcher'));
+
+ if ($this->use_extensions)
+ {
+ $this->register_ext_compiler_pass();
+ }
+
+ $filesystem = new filesystem();
+ $loader = new YamlFileLoader($this->container, new FileLocator($filesystem->realpath($this->get_config_path())));
+ $loader->load($this->container->getParameter('core.environment') . '/config.yml');
+
+ $this->inject_custom_parameters();
+
+ if ($this->compile_container)
+ {
+ $this->container->compile();
+
+ if ($this->use_cache)
+ {
+ $this->dump_container($config_cache);
+ }
+ }
+ }
+
+ if ($this->compile_container && $this->config_php_file)
+ {
+ $this->container->set('config.php', $this->config_php_file);
+ }
+
+ return $this->container;
+ }
+
+ /**
+ * Enable the extensions.
+ *
+ * @param string $environment The environment to use
+ * @return $this
+ */
+ public function with_environment($environment)
+ {
+ $this->environment = $environment;
+
+ return $this;
+ }
+
+ /**
+ * Enable the extensions.
+ *
+ * @return $this
+ */
+ public function with_extensions()
+ {
+ $this->use_extensions = true;
+
+ return $this;
+ }
+
+ /**
+ * Disable the extensions.
+ *
+ * @return $this
+ */
+ public function without_extensions()
+ {
+ $this->use_extensions = false;
+
+ return $this;
+ }
+
+ /**
+ * Enable the caching of the container.
+ *
+ * If DEBUG_CONTAINER is set this option is ignored and a new container is build.
+ *
+ * @return $this
+ */
+ public function with_cache()
+ {
+ $this->use_cache = true;
+
+ return $this;
+ }
+
+ /**
+ * Disable the caching of the container.
+ *
+ * @return $this
+ */
+ public function without_cache()
+ {
+ $this->use_cache = false;
+
+ return $this;
+ }
+
+ /**
+ * Set the cache directory.
+ *
+ * @param string $cache_dir The cache directory.
+ * @return $this
+ */
+ public function with_cache_dir($cache_dir)
+ {
+ $this->cache_dir = $cache_dir;
+
+ return $this;
+ }
+
+ /**
+ * Enable the compilation of the container.
+ *
+ * @return $this
+ */
+ public function with_compiled_container()
+ {
+ $this->compile_container = true;
+
+ return $this;
+ }
+
+ /**
+ * Disable the compilation of the container.
+ *
+ * @return $this
+ */
+ public function without_compiled_container()
+ {
+ $this->compile_container = false;
+
+ return $this;
+ }
+
+ /**
+ * Set a custom path to find the configuration of the container.
+ *
+ * @param string $config_path
+ * @return $this
+ */
+ public function with_config_path($config_path)
+ {
+ $this->config_path = $config_path;
+
+ return $this;
+ }
+
+ /**
+ * Set custom parameters to inject into the container.
+ *
+ * @param array $custom_parameters
+ * @return $this
+ */
+ public function with_custom_parameters($custom_parameters)
+ {
+ $this->custom_parameters = $custom_parameters;
+
+ return $this;
+ }
+
+ /**
+ * Set custom parameters to inject into the container.
+ *
+ * @param \phpbb\config_php_file $config_php_file
+ * @return $this
+ */
+ public function with_config(\phpbb\config_php_file $config_php_file)
+ {
+ $this->config_php_file = $config_php_file;
+
+ return $this;
+ }
+
+ /**
+ * Returns the path to the container configuration (default: root_path/config)
+ *
+ * @return string
+ */
+ protected function get_config_path()
+ {
+ return $this->config_path ?: $this->phpbb_root_path . 'config';
+ }
+
+ /**
+ * Returns the path to the cache directory (default: root_path/cache/environment).
+ *
+ * @return string Path to the cache directory.
+ */
+ protected function get_cache_dir()
+ {
+ return $this->cache_dir ?: $this->phpbb_root_path . 'cache/' . $this->get_environment() . '/';
+ }
+
+ /**
+ * Load the enabled extensions.
+ */
+ protected function load_extensions()
+ {
+ if ($this->config_php_file !== null)
+ {
+ // Build an intermediate container to load the ext list from the database
+ $container_builder = new container_builder($this->phpbb_root_path, $this->php_ext);
+ $ext_container = $container_builder
+ ->without_cache()
+ ->without_extensions()
+ ->with_config($this->config_php_file)
+ ->with_config_path($this->get_config_path())
+ ->with_environment('production')
+ ->without_compiled_container()
+ ->get_container()
+ ;
+
+ $ext_container->register('cache.driver', '\\phpbb\\cache\\driver\\dummy');
+ $ext_container->compile();
+
+ $extensions = $ext_container->get('ext.manager')->all_enabled();
+
+ // Load each extension found
+ foreach ($extensions as $ext_name => $path)
+ {
+ $extension_class = '\\' . str_replace('/', '\\', $ext_name) . '\\di\\extension';
+
+ if (!class_exists($extension_class))
+ {
+ $extension_class = '\\phpbb\\extension\\di\\extension_base';
+ }
+
+ $this->container_extensions[] = new $extension_class($ext_name, $path);
+
+ // Load extension autoloader
+ $filename = $path . 'vendor/autoload.php';
+ if (file_exists($filename))
+ {
+ require $filename;
+ }
+ }
+ }
+ else
+ {
+ // To load the extensions we need the database credentials.
+ // Automatically disable the extensions if we don't have them.
+ $this->use_extensions = false;
+ }
+ }
+
+ /**
+ * Dump the container to the disk.
+ *
+ * @param ConfigCache $cache The config cache
+ */
+ protected function dump_container($cache)
+ {
+ try
+ {
+ $dumper = new PhpDumper($this->container);
+ $cached_container_dump = $dumper->dump(array(
+ 'class' => 'phpbb_cache_container',
+ 'base_class' => 'Symfony\\Component\\DependencyInjection\\ContainerBuilder',
+ ));
+
+ $cache->write($cached_container_dump, $this->container->getResources());
+ }
+ catch (IOException $e)
+ {
+ // Don't fail if the cache isn't writeable
+ }
+ }
+
+ /**
+ * Create the ContainerBuilder object
+ *
+ * @param array $extensions Array of Container extension objects
+ * @return ContainerBuilder object
+ */
+ protected function create_container(array $extensions)
+ {
+ $container = new ContainerBuilder(new ParameterBag($this->get_core_parameters()));
+
+ $extensions_alias = array();
+
+ foreach ($extensions as $extension)
+ {
+ $container->registerExtension($extension);
+ $extensions_alias[] = $extension->getAlias();
+ }
+
+ $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions_alias));
+
+ return $container;
+ }
+
+ /**
+ * Inject the customs parameters into the container
+ */
+ protected function inject_custom_parameters()
+ {
+ if ($this->custom_parameters !== null)
+ {
+ foreach ($this->custom_parameters as $key => $value)
+ {
+ $this->container->setParameter($key, $value);
+ }
+ }
+ }
+
+ /**
+ * Returns the core parameters.
+ *
+ * @return array An array of core parameters
+ */
+ protected function get_core_parameters()
+ {
+ return array_merge(
+ array(
+ 'core.root_path' => $this->phpbb_root_path,
+ 'core.php_ext' => $this->php_ext,
+ 'core.environment' => $this->get_environment(),
+ 'core.debug' => defined('DEBUG') ? DEBUG : false,
+ ),
+ $this->get_env_parameters()
+ );
+ }
+
+ /**
+ * Gets the environment parameters.
+ *
+ * Only the parameters starting with "PHPBB__" are considered.
+ *
+ * @return array An array of parameters
+ */
+ protected function get_env_parameters()
+ {
+ $parameters = array();
+ foreach ($_SERVER as $key => $value)
+ {
+ if (0 === strpos($key, 'PHPBB__'))
+ {
+ $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
+ }
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Get the filename under which the dumped container will be stored.
+ *
+ * @return string Path for dumped container
+ */
+ protected function get_container_filename()
+ {
+ return $this->get_cache_dir() . 'container_' . md5($this->phpbb_root_path) . '.' . $this->php_ext;
+ }
+
+ /**
+ * Return the name of the current environment.
+ *
+ * @return string
+ */
+ protected function get_environment()
+ {
+ return $this->environment ?: PHPBB_ENVIRONMENT;
+ }
+
+ private function register_ext_compiler_pass()
+ {
+ $finder = new Finder();
+ $finder
+ ->name('*_pass.php')
+ ->path('di/pass')
+ ->files()
+ ->ignoreDotFiles(true)
+ ->ignoreUnreadableDirs(true)
+ ->ignoreVCS(true)
+ ->followLinks()
+ ->in($this->phpbb_root_path . 'ext/')
+ ;
+
+ /** @var \SplFileInfo $pass */
+ foreach ($finder as $pass)
+ {
+ $filename = $pass->getPathname();
+ $filename = substr($filename, 0, -strlen('.' . $pass->getExtension()));
+ $filename = str_replace(DIRECTORY_SEPARATOR, '/', $filename);
+ $className = preg_replace('#^.*ext/#', '', $filename);
+ $className = '\\' . str_replace('/', '\\', $className);
+
+ if (class_exists($className) && in_array('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface', class_implements($className), true))
+ {
+ $this->container->addCompilerPass(new $className());
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/di/extension/config.php b/phpBB/phpbb/di/extension/config.php
index 2603e7b358..7984a783df 100644
--- a/phpBB/phpbb/di/extension/config.php
+++ b/phpBB/phpbb/di/extension/config.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,17 +15,18 @@ namespace phpbb\di\extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
-use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
-use Symfony\Component\Config\FileLocator;
/**
* Container config extension
*/
class config extends Extension
{
- public function __construct($config_file)
+ /** @var array */
+ protected $config_php;
+
+ public function __construct(\phpbb\config_php_file $config_php)
{
- $this->config_file = $config_file;
+ $this->config_php = $config_php;
}
/**
@@ -30,22 +35,28 @@ class config extends Extension
* @param array $config An array of configuration values
* @param ContainerBuilder $container A ContainerBuilder instance
*
- * @throws InvalidArgumentException When provided tag is not defined in this extension
+ * @throws \InvalidArgumentException When provided tag is not defined in this extension
*/
public function load(array $config, ContainerBuilder $container)
{
- require($this->config_file);
+ $parameters = array(
+ 'core.adm_relative_path' => $this->config_php->get('phpbb_adm_relative_path') ? $this->config_php->get('phpbb_adm_relative_path') : 'adm/',
+ 'core.table_prefix' => $this->config_php->get('table_prefix'),
+ 'cache.driver.class' => $this->convert_30_acm_type($this->config_php->get('acm_type')),
+ 'dbal.driver.class' => $this->config_php->convert_30_dbms_to_31($this->config_php->get('dbms')),
+ 'dbal.dbhost' => $this->config_php->get('dbhost'),
+ 'dbal.dbuser' => $this->config_php->get('dbuser'),
+ 'dbal.dbpasswd' => $this->config_php->get('dbpasswd'),
+ 'dbal.dbname' => $this->config_php->get('dbname'),
+ 'dbal.dbport' => $this->config_php->get('dbport'),
+ 'dbal.new_link' => defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK,
+ );
+ $parameter_bag = $container->getParameterBag();
- $container->setParameter('core.adm_relative_path', (isset($phpbb_adm_relative_path) ? $phpbb_adm_relative_path : 'adm/'));
- $container->setParameter('core.table_prefix', $table_prefix);
- $container->setParameter('cache.driver.class', $this->convert_30_acm_type($acm_type));
- $container->setParameter('dbal.driver.class', phpbb_convert_30_dbms_to_31($dbms));
- $container->setParameter('dbal.dbhost', $dbhost);
- $container->setParameter('dbal.dbuser', $dbuser);
- $container->setParameter('dbal.dbpasswd', $dbpasswd);
- $container->setParameter('dbal.dbname', $dbname);
- $container->setParameter('dbal.dbport', $dbport);
- $container->setParameter('dbal.new_link', defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK);
+ foreach ($parameters as $parameter => $value)
+ {
+ $container->setParameter($parameter, $parameter_bag->escapeValue($value));
+ }
}
/**
@@ -64,7 +75,7 @@ class config extends Extension
* Convert 3.0 ACM type to 3.1 cache driver class name
*
* @param string $acm_type ACM type
- * @return cache driver class
+ * @return string cache driver class
*/
protected function convert_30_acm_type($acm_type)
{
diff --git a/phpBB/phpbb/di/extension/container_configuration.php b/phpBB/phpbb/di/extension/container_configuration.php
new file mode 100644
index 0000000000..4585d6509e
--- /dev/null
+++ b/phpBB/phpbb/di/extension/container_configuration.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\di\extension;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class container_configuration implements ConfigurationInterface
+{
+
+ /**
+ * Generates the configuration tree builder.
+ *
+ * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
+ */
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('core');
+ $rootNode
+ ->children()
+ ->booleanNode('require_dev_dependencies')->defaultValue(false)->end()
+ ->arrayNode('debug')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('exceptions')->defaultValue(false)->end()
+ ->end()
+ ->end()
+ ->arrayNode('twig')
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('debug')->defaultValue(null)->end()
+ ->booleanNode('auto_reload')->defaultValue(null)->end()
+ ->booleanNode('enable_debug_extension')->defaultValue(false)->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+ return $treeBuilder;
+ }
+}
diff --git a/phpBB/phpbb/di/extension/core.php b/phpBB/phpbb/di/extension/core.php
index 455dfa7ecd..c48a80a558 100644
--- a/phpBB/phpbb/di/extension/core.php
+++ b/phpBB/phpbb/di/extension/core.php
@@ -1,18 +1,23 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\di\extension;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
-use Symfony\Component\Config\FileLocator;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* Container core extension
@@ -20,42 +25,96 @@ use Symfony\Component\Config\FileLocator;
class core extends Extension
{
/**
- * Config path
- * @var string
- */
+ * Config path
+ * @var string
+ */
protected $config_path;
/**
- * Constructor
- *
- * @param string $config_path Config path
- */
+ * Constructor
+ *
+ * @param string $config_path Config path
+ */
public function __construct($config_path)
{
$this->config_path = $config_path;
}
/**
- * Loads a specific configuration.
- *
- * @param array $config An array of configuration values
- * @param ContainerBuilder $container A ContainerBuilder instance
- *
- * @throws InvalidArgumentException When provided tag is not defined in this extension
- */
- public function load(array $config, ContainerBuilder $container)
+ * Loads a specific configuration.
+ *
+ * @param array $configs An array of configuration values
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ *
+ * @throws \InvalidArgumentException When provided tag is not defined in this extension
+ */
+ public function load(array $configs, ContainerBuilder $container)
{
- $loader = new YamlFileLoader($container, new FileLocator(phpbb_realpath($this->config_path)));
- $loader->load('services.yml');
+ $filesystem = new \phpbb\filesystem\filesystem();
+ $loader = new YamlFileLoader($container, new FileLocator($filesystem->realpath($this->config_path)));
+ $loader->load($container->getParameter('core.environment') . '/container/environment.yml');
+
+ $config = $this->getConfiguration($configs, $container);
+ $config = $this->processConfiguration($config, $configs);
+
+ if ($config['require_dev_dependencies'])
+ {
+ if (!class_exists('Goutte\Client', true))
+ {
+ trigger_error(
+ 'Composer development dependencies have not been set up for the ' . $container->getParameter('core.environment') . ' environment yet, run ' .
+ "'php ../composer.phar install --dev' from the phpBB directory to do so.",
+ E_USER_ERROR
+ );
+ }
+ }
+
+ // Set the Twig options if defined in the environment
+ $definition = $container->getDefinition('template.twig.environment');
+ $twig_environment_options = $definition->getArgument(7);
+ if ($config['twig']['debug'])
+ {
+ $twig_environment_options['debug'] = true;
+ }
+ if ($config['twig']['auto_reload'])
+ {
+ $twig_environment_options['auto_reload'] = true;
+ }
+
+ // Replace the 8th argument, the options passed to the environment
+ $definition->replaceArgument(7, $twig_environment_options);
+
+ if ($config['twig']['enable_debug_extension'])
+ {
+ $definition = $container->getDefinition('template.twig.extensions.debug');
+ $definition->addTag('twig.extension');
+ }
+
+ // Set the debug options
+ foreach ($config['debug'] as $name => $value)
+ {
+ $container->setParameter('debug.' . $name, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfiguration(array $config, ContainerBuilder $container)
+ {
+ $r = new \ReflectionClass('\phpbb\di\extension\container_configuration');
+ $container->addResource(new FileResource($r->getFileName()));
+
+ return new container_configuration();
}
/**
- * Returns the recommended alias to use in XML.
- *
- * This alias is also the mandatory prefix to use when using YAML.
- *
- * @return string The alias
- */
+ * Returns the recommended alias to use in XML.
+ *
+ * This alias is also the mandatory prefix to use when using YAML.
+ *
+ * @return string The alias
+ */
public function getAlias()
{
return 'core';
diff --git a/phpBB/phpbb/di/extension/ext.php b/phpBB/phpbb/di/extension/ext.php
deleted file mode 100644
index 4f2f24cb1a..0000000000
--- a/phpBB/phpbb/di/extension/ext.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\di\extension;
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\HttpKernel\DependencyInjection\Extension;
-use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
-use Symfony\Component\Config\FileLocator;
-
-/**
-* Container ext extension
-*/
-class ext extends Extension
-{
- protected $paths = array();
-
- public function __construct($enabled_extensions)
- {
- foreach ($enabled_extensions as $ext => $path)
- {
- $this->paths[] = $path;
- }
- }
-
- /**
- * Loads a specific configuration.
- *
- * @param array $config An array of configuration values
- * @param ContainerBuilder $container A ContainerBuilder instance
- *
- * @throws InvalidArgumentException When provided tag is not defined in this extension
- */
- public function load(array $config, ContainerBuilder $container)
- {
- foreach ($this->paths as $path)
- {
- if (file_exists($path . '/config/services.yml'))
- {
- $loader = new YamlFileLoader($container, new FileLocator(phpbb_realpath($path . '/config')));
- $loader->load('services.yml');
- }
- }
- }
-
- /**
- * Returns the recommended alias to use in XML.
- *
- * This alias is also the mandatory prefix to use when using YAML.
- *
- * @return string The alias
- */
- public function getAlias()
- {
- return 'ext';
- }
-}
diff --git a/phpBB/phpbb/di/ordered_service_collection.php b/phpBB/phpbb/di/ordered_service_collection.php
new file mode 100644
index 0000000000..046012ae5b
--- /dev/null
+++ b/phpBB/phpbb/di/ordered_service_collection.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\di;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Collection of services in a specified order
+ */
+class ordered_service_collection extends service_collection
+{
+ /**
+ * @var bool
+ */
+ protected $is_ordered;
+
+ /**
+ * @var array
+ */
+ protected $service_ids;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Container object
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->is_ordered = false;
+ $this->service_ids = array();
+
+ parent::__construct($container);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIterator()
+ {
+ if (!$this->is_ordered)
+ {
+ $this->sort_services();
+ }
+
+ return new service_collection_iterator($this);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetExists($index)
+ {
+ if (!$this->is_ordered)
+ {
+ $this->sort_services();
+ }
+
+ return parent::offsetExists($index);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetGet($index)
+ {
+ if (!$this->is_ordered)
+ {
+ $this->sort_services();
+ }
+
+ return parent::offsetGet($index);
+ }
+
+ /**
+ * Adds a service ID to the collection
+ *
+ * @param string $service_id
+ * @param int $order
+ */
+ public function add($service_id, $order = 0)
+ {
+ $order = (int) $order;
+ $this->service_ids[$order][] = $service_id;
+ $this->is_ordered = false;
+ }
+
+ protected function sort_services()
+ {
+ if ($this->is_ordered)
+ {
+ return;
+ }
+
+ $this->exchangeArray(array());
+ ksort($this->service_ids);
+ foreach ($this->service_ids as $service_order_group)
+ {
+ foreach ($service_order_group as $service_id)
+ {
+ $this->offsetSet($service_id, null);
+ }
+ }
+
+ $this->is_ordered = true;
+ }
+}
diff --git a/phpBB/phpbb/di/pass/collection_pass.php b/phpBB/phpbb/di/pass/collection_pass.php
index 507271de3e..341f88518d 100644
--- a/phpBB/phpbb/di/pass/collection_pass.php
+++ b/phpBB/phpbb/di/pass/collection_pass.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -30,10 +34,30 @@ class collection_pass implements CompilerPassInterface
foreach ($container->findTaggedServiceIds('service_collection') as $id => $data)
{
$definition = $container->getDefinition($id);
+ $is_ordered_collection = (substr($definition->getClass(), -strlen('ordered_service_collection')) === 'ordered_service_collection');
+ $is_class_name_aware = (isset($data[0]['class_name_aware']) && $data[0]['class_name_aware']);
foreach ($container->findTaggedServiceIds($data[0]['tag']) as $service_id => $service_data)
{
- $definition->addMethodCall('add', array($service_id));
+ if ($is_ordered_collection)
+ {
+ $arguments = array($service_id, $service_data[0]['order']);
+ }
+ else
+ {
+ $arguments = array($service_id);
+ }
+
+ if ($is_class_name_aware)
+ {
+ $service_definition = $container->getDefinition($service_id);
+ $definition->addMethodCall('add_service_class', array(
+ $service_id,
+ $service_definition->getClass()
+ ));
+ }
+
+ $definition->addMethodCall('add', $arguments);
}
}
}
diff --git a/phpBB/phpbb/di/pass/kernel_pass.php b/phpBB/phpbb/di/pass/kernel_pass.php
deleted file mode 100644
index 9c2b193361..0000000000
--- a/phpBB/phpbb/di/pass/kernel_pass.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\di\pass;
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
-
-class kernel_pass implements CompilerPassInterface
-{
- /**
- * Modify the container before it is passed to the rest of the code
- *
- * @param ContainerBuilder $container ContainerBuilder object
- * @return null
- */
- public function process(ContainerBuilder $container)
- {
- $definition = $container->getDefinition('dispatcher');
-
- foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $events)
- {
- foreach ($events as $event)
- {
- $priority = isset($event['priority']) ? $event['priority'] : 0;
-
- if (!isset($event['event']))
- {
- throw new \InvalidArgumentException(sprintf('Service "%1$s" must define the "event" attribute on "kernel.event_listener" tags.', $id));
- }
-
- if (!isset($event['method']))
- {
- throw new \InvalidArgumentException(sprintf('Service "%1$s" must define the "method" attribute on "kernel.event_listener" tags.', $id));
- }
-
- $definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
- }
- }
-
- foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes)
- {
- // We must assume that the class value has been correctly filled, even if the service is created by a factory
- $class = $container->getDefinition($id)->getClass();
-
- $refClass = new \ReflectionClass($class);
- $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
- if (!$refClass->implementsInterface($interface))
- {
- throw new \InvalidArgumentException(sprintf('Service "%1$s" must implement interface "%2$s".', $id, $interface));
- }
-
- $definition->addMethodCall('addSubscriberService', array($id, $class));
- }
- }
-}
diff --git a/phpBB/phpbb/di/service_collection.php b/phpBB/phpbb/di/service_collection.php
index 65df9ab1d1..8e9175e204 100644
--- a/phpBB/phpbb/di/service_collection.php
+++ b/phpBB/phpbb/di/service_collection.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,12 +17,20 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Collection of services to be configured at container compile time.
-*
-* @package phpBB3
*/
class service_collection extends \ArrayObject
{
/**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * @var array
+ */
+ protected $service_classes;
+
+ /**
* Constructor
*
* @param ContainerInterface $container Container object
@@ -26,6 +38,38 @@ class service_collection extends \ArrayObject
public function __construct(ContainerInterface $container)
{
$this->container = $container;
+ $this->service_classes = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIterator()
+ {
+ return new service_collection_iterator($this);
+ }
+
+ // Because of a PHP issue we have to redefine offsetExists
+ // (even with a call to the parent):
+ // https://bugs.php.net/bug.php?id=66834
+ // https://bugs.php.net/bug.php?id=67067
+ // But it triggers a sniffer issue that we have to skip
+ // @codingStandardsIgnoreStart
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetExists($index)
+ {
+ return parent::offsetExists($index);
+ }
+ // @codingStandardsIgnoreEnd
+
+ /**
+ * {@inheritdoc}
+ */
+ public function offsetGet($index)
+ {
+ return $this->container->get($index);
}
/**
@@ -36,8 +80,27 @@ class service_collection extends \ArrayObject
*/
public function add($name)
{
- $task = $this->container->get($name);
+ $this->offsetSet($name, null);
+ }
- $this->offsetSet($name, $task);
+ /**
+ * Add a service's class to the collection
+ *
+ * @param string $service_id
+ * @param string $class
+ */
+ public function add_service_class($service_id, $class)
+ {
+ $this->service_classes[$service_id] = $class;
+ }
+
+ /**
+ * Get services' classes
+ *
+ * @return array
+ */
+ public function get_service_classes()
+ {
+ return $this->service_classes;
}
}
diff --git a/phpBB/phpbb/di/service_collection_iterator.php b/phpBB/phpbb/di/service_collection_iterator.php
new file mode 100644
index 0000000000..31bc156e99
--- /dev/null
+++ b/phpBB/phpbb/di/service_collection_iterator.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\di;
+
+/**
+* Iterator which loads the services when they are requested
+*/
+class service_collection_iterator extends \ArrayIterator
+{
+ /**
+ * @var \phpbb\di\service_collection
+ */
+ protected $collection;
+
+ /**
+ * Construct an ArrayIterator for service_collection
+ *
+ * @param \phpbb\di\service_collection $collection The collection to iterate over
+ * @param int $flags Flags to control the behaviour of the ArrayObject object.
+ * @see ArrayObject::setFlags()
+ */
+ public function __construct(service_collection $collection, $flags = 0)
+ {
+ parent::__construct($collection->getArrayCopy(), $flags);
+ $this->collection = $collection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function current()
+ {
+ return $this->collection->offsetGet($this->key());
+ }
+}
diff --git a/phpBB/phpbb/error_collector.php b/phpBB/phpbb/error_collector.php
index 297972c6b8..bf8efd1065 100644
--- a/phpBB/phpbb/error_collector.php
+++ b/phpBB/phpbb/error_collector.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,15 +16,28 @@ namespace phpbb;
class error_collector
{
var $errors;
+ var $error_types;
- function __construct()
+ /**
+ * Constructor.
+ *
+ * The variable $error_types may be set to a mask of PHP error types that
+ * the collector should keep, e.g. `E_ALL`. If unset, the current value of
+ * the error_reporting() function will be used to determine which errors
+ * the collector will keep.
+ *
+ * @see PHPBB3-13306
+ * @param int|null $error_types
+ */
+ function __construct($error_types = null)
{
$this->errors = array();
+ $this->error_types = $error_types;
}
function install()
{
- set_error_handler(array(&$this, 'error_handler'));
+ set_error_handler(array(&$this, 'error_handler'), ($this->error_types !== null) ? $this->error_types : error_reporting());
}
function uninstall()
diff --git a/phpBB/phpbb/event/data.php b/phpBB/phpbb/event/data.php
index fbb16574ed..c7365aee35 100644
--- a/phpBB/phpbb/event/data.php
+++ b/phpBB/phpbb/event/data.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/event/dispatcher.php b/phpBB/phpbb/event/dispatcher.php
index 74b35eb78d..1c4abeb108 100644
--- a/phpBB/phpbb/event/dispatcher.php
+++ b/phpBB/phpbb/event/dispatcher.php
@@ -1,15 +1,20 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\event;
use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
/**
* Extension of the Symfony2 EventDispatcher
@@ -25,12 +30,49 @@ use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
* extract($phpbb_dispatcher->trigger_event('core.index', compact($vars)));
*
*/
-class dispatcher extends ContainerAwareEventDispatcher
+class dispatcher extends ContainerAwareEventDispatcher implements dispatcher_interface
{
+ /**
+ * @var bool
+ */
+ protected $disabled = false;
+
+ /**
+ * {@inheritdoc}
+ */
public function trigger_event($eventName, $data = array())
{
$event = new \phpbb\event\data($data);
$this->dispatch($eventName, $event);
return $event->get_data_filtered(array_keys($data));
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dispatch($eventName, Event $event = null)
+ {
+ if ($this->disabled)
+ {
+ return $event;
+ }
+
+ return parent::dispatch($eventName, $event);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable()
+ {
+ $this->disabled = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable()
+ {
+ $this->disabled = false;
+ }
}
diff --git a/phpBB/phpbb/event/dispatcher_interface.php b/phpBB/phpbb/event/dispatcher_interface.php
new file mode 100644
index 0000000000..c66aa98260
--- /dev/null
+++ b/phpBB/phpbb/event/dispatcher_interface.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\event;
+
+/**
+* Extension of the Symfony2 EventDispatcher
+*
+* It provides an additional `trigger_event` method, which
+* gives some syntactic sugar for dispatching events. Instead
+* of creating the event object, the method will do that for
+* you.
+*
+* Example:
+*
+* $vars = array('page_title');
+* extract($phpbb_dispatcher->trigger_event('core.index', compact($vars)));
+*
+*/
+interface dispatcher_interface extends \Symfony\Component\EventDispatcher\EventDispatcherInterface
+{
+ /**
+ * Construct and dispatch an event
+ *
+ * @param string $eventName The event name
+ * @param array $data An array containing the variables sending with the event
+ * @return mixed
+ */
+ public function trigger_event($eventName, $data = array());
+
+ /**
+ * Disable the event dispatcher.
+ */
+ public function disable();
+
+ /**
+ * Enable the event dispatcher.
+ */
+ public function enable();
+}
diff --git a/phpBB/phpbb/event/extension_subscriber_loader.php b/phpBB/phpbb/event/extension_subscriber_loader.php
deleted file mode 100644
index 6408f93e2a..0000000000
--- a/phpBB/phpbb/event/extension_subscriber_loader.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\event;
-
-use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-
-class extension_subscriber_loader
-{
- private $dispatcher;
- private $listener_collection;
-
- public function __construct(EventDispatcherInterface $dispatcher, \phpbb\di\service_collection $listener_collection)
- {
- $this->dispatcher = $dispatcher;
- $this->listener_collection = $listener_collection;
- }
-
- public function load()
- {
- if (!empty($this->listener_collection))
- {
- foreach ($this->listener_collection as $listener)
- {
- $this->dispatcher->addSubscriber($listener);
- }
- }
- }
-}
diff --git a/phpBB/phpbb/event/kernel_exception_subscriber.php b/phpBB/phpbb/event/kernel_exception_subscriber.php
index 8a4de1fbad..e427abf5e3 100644
--- a/phpBB/phpbb/event/kernel_exception_subscriber.php
+++ b/phpBB/phpbb/event/kernel_exception_subscriber.php
@@ -1,44 +1,51 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
-use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\Response;
class kernel_exception_subscriber implements EventSubscriberInterface
{
/**
* Template object
+ *
* @var \phpbb\template\template
*/
protected $template;
/**
- * User object
- * @var \phpbb\user
+ * Language object
+ *
+ * @var \phpbb\language\language
*/
- protected $user;
+ protected $language;
/**
* Construct method
*
- * @param \phpbb\template\template $template Template object
- * @param \phpbb\user $user User object
+ * @param \phpbb\template\template $template Template object
+ * @param \phpbb\language\language $language Language object
*/
- public function __construct(\phpbb\template\template $template, \phpbb\user $user)
+ public function __construct(\phpbb\template\template $template, \phpbb\language\language $language)
{
$this->template = $template;
- $this->user = $user;
+ $this->language = $language;
}
/**
@@ -49,27 +56,59 @@ class kernel_exception_subscriber implements EventSubscriberInterface
*/
public function on_kernel_exception(GetResponseForExceptionEvent $event)
{
- page_header($this->user->lang('INFORMATION'));
-
$exception = $event->getException();
- $this->template->assign_vars(array(
- 'MESSAGE_TITLE' => $this->user->lang('INFORMATION'),
- 'MESSAGE_TEXT' => $exception->getMessage(),
- ));
+ $message = $exception->getMessage();
+
+ if ($exception instanceof \phpbb\exception\exception_interface)
+ {
+ $message = $this->language->lang_array($message, $exception->get_parameters());
+ }
+
+ if (!$event->getRequest()->isXmlHttpRequest())
+ {
+ page_header($this->language->lang('INFORMATION'));
+
+ $this->template->assign_vars(array(
+ 'MESSAGE_TITLE' => $this->language->lang('INFORMATION'),
+ 'MESSAGE_TEXT' => $message,
+ ));
+
+ $this->template->set_filenames(array(
+ 'body' => 'message_body.html',
+ ));
+
+ page_footer(true, false, false);
+
+ $response = new Response($this->template->assign_display('body'), 500);
+ }
+ else
+ {
+ $data = array();
+
+ if (!empty($message))
+ {
+ $data['message'] = $message;
+ }
+
+ if (defined('DEBUG'))
+ {
+ $data['trace'] = $exception->getTrace();
+ }
- $this->template->set_filenames(array(
- 'body' => 'message_body.html',
- ));
+ $response = new JsonResponse($data, 500);
+ }
- page_footer(true, false, false);
+ if ($exception instanceof HttpExceptionInterface)
+ {
+ $response->setStatusCode($exception->getStatusCode());
+ $response->headers->add($exception->getHeaders());
+ }
- $status_code = $exception instanceof HttpException ? $exception->getStatusCode() : 500;
- $response = new Response($this->template->assign_display('body'), $status_code);
$event->setResponse($response);
}
- public static function getSubscribedEvents()
+ static public function getSubscribedEvents()
{
return array(
KernelEvents::EXCEPTION => 'on_kernel_exception',
diff --git a/phpBB/phpbb/event/kernel_request_subscriber.php b/phpBB/phpbb/event/kernel_request_subscriber.php
deleted file mode 100644
index 7d5418498b..0000000000
--- a/phpBB/phpbb/event/kernel_request_subscriber.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\event;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\HttpKernel\EventListener\RouterListener;
-use Symfony\Component\Routing\RequestContext;
-
-class kernel_request_subscriber implements EventSubscriberInterface
-{
- /**
- * Extension finder object
- * @var \phpbb\extension\finder
- */
- protected $finder;
-
- /**
- * PHP extension
- * @var string
- */
- protected $php_ext;
-
- /**
- * Root path
- * @var string
- */
- protected $root_path;
-
- /**
- * Construct method
- *
- * @param \phpbb\extension\finder $finder Extension finder object
- * @param string $root_path Root path
- * @param string $php_ext PHP extension
- */
- public function __construct(\phpbb\extension\finder $finder, $root_path, $php_ext)
- {
- $this->finder = $finder;
- $this->root_path = $root_path;
- $this->php_ext = $php_ext;
- }
-
- /**
- * This listener is run when the KernelEvents::REQUEST event is triggered
- *
- * This is responsible for setting up the routing information
- *
- * @param GetResponseEvent $event
- * @return null
- */
- public function on_kernel_request(GetResponseEvent $event)
- {
- $request = $event->getRequest();
- $context = new RequestContext();
- $context->fromRequest($request);
-
- $matcher = phpbb_get_url_matcher($this->finder, $context, $this->root_path, $this->php_ext);
- $router_listener = new RouterListener($matcher, $context);
- $router_listener->onKernelRequest($event);
- }
-
- public static function getSubscribedEvents()
- {
- return array(
- KernelEvents::REQUEST => 'on_kernel_request',
- );
- }
-}
diff --git a/phpBB/phpbb/event/kernel_terminate_subscriber.php b/phpBB/phpbb/event/kernel_terminate_subscriber.php
index 32dba322d1..f0d0a2f595 100644
--- a/phpBB/phpbb/event/kernel_terminate_subscriber.php
+++ b/phpBB/phpbb/event/kernel_terminate_subscriber.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -28,10 +32,10 @@ class kernel_terminate_subscriber implements EventSubscriberInterface
exit_handler();
}
- public static function getSubscribedEvents()
+ static public function getSubscribedEvents()
{
return array(
- KernelEvents::TERMINATE => 'on_kernel_terminate',
+ KernelEvents::TERMINATE => array('on_kernel_terminate', ~PHP_INT_MAX),
);
}
}
diff --git a/phpBB/phpbb/event/md_exporter.php b/phpBB/phpbb/event/md_exporter.php
new file mode 100644
index 0000000000..e042d0a5d1
--- /dev/null
+++ b/phpBB/phpbb/event/md_exporter.php
@@ -0,0 +1,560 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\event;
+
+/**
+* Crawls through a markdown file and grabs all events
+*/
+class md_exporter
+{
+ /** @var string Path where we look for files*/
+ protected $path;
+
+ /** @var string phpBB Root Path */
+ protected $root_path;
+
+ /** @var string The minimum version for the events to return */
+ protected $min_version;
+
+ /** @var string The maximum version for the events to return */
+ protected $max_version;
+
+ /** @var string */
+ protected $filter;
+
+ /** @var string */
+ protected $current_event;
+
+ /** @var array */
+ protected $events;
+
+ /**
+ * @param string $phpbb_root_path
+ * @param mixed $extension String 'vendor/ext' to filter, null for phpBB core
+ * @param string $min_version
+ * @param string $max_version
+ */
+ public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null)
+ {
+ $this->root_path = $phpbb_root_path;
+ $this->path = $this->root_path;
+ if ($extension)
+ {
+ $this->path .= 'ext/' . $extension . '/';
+ }
+
+ $this->events = array();
+ $this->events_by_file = array();
+ $this->filter = $this->current_event = '';
+ $this->min_version = $min_version;
+ $this->max_version = $max_version;
+ }
+
+ /**
+ * Get the list of all events
+ *
+ * @return array Array with events: name => details
+ */
+ public function get_events()
+ {
+ return $this->events;
+ }
+
+ /**
+ * @param string $md_file Relative from phpBB root
+ * @return int Number of events found
+ * @throws \LogicException
+ */
+ public function crawl_phpbb_directory_adm($md_file)
+ {
+ $this->crawl_eventsmd($md_file, 'adm');
+
+ $file_list = $this->get_recursive_file_list($this->path . 'adm/style/');
+ foreach ($file_list as $file)
+ {
+ $file_name = 'adm/style/' . $file;
+ $this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
+ }
+
+ return sizeof($this->events);
+ }
+
+ /**
+ * @param string $md_file Relative from phpBB root
+ * @return int Number of events found
+ * @throws \LogicException
+ */
+ public function crawl_phpbb_directory_styles($md_file)
+ {
+ $this->crawl_eventsmd($md_file, 'styles');
+
+ $styles = array('prosilver');
+ foreach ($styles as $style)
+ {
+ $file_list = $this->get_recursive_file_list(
+ $this->path . 'styles/' . $style . '/template/'
+ );
+
+ foreach ($file_list as $file)
+ {
+ $file_name = 'styles/' . $style . '/template/' . $file;
+ $this->validate_events_from_file($file_name, $this->crawl_file_for_events($file_name));
+ }
+ }
+
+ return sizeof($this->events);
+ }
+
+ /**
+ * @param string $md_file Relative from phpBB root
+ * @param string $filter Should be 'styles' or 'adm'
+ * @return int Number of events found
+ * @throws \LogicException
+ */
+ public function crawl_eventsmd($md_file, $filter)
+ {
+ if (!file_exists($this->path . $md_file))
+ {
+ throw new \LogicException("The event docs file '{$md_file}' could not be found");
+ }
+
+ $file_content = file_get_contents($this->path . $md_file);
+ $this->filter = $filter;
+
+ $events = explode("\n\n", $file_content);
+ foreach ($events as $event)
+ {
+ // Last row of the file
+ if (strpos($event, "\n===\n") === false)
+ {
+ continue;
+ }
+
+ list($event_name, $details) = explode("\n===\n", $event, 2);
+ $this->validate_event_name($event_name);
+ $this->current_event = $event_name;
+
+ if (isset($this->events[$this->current_event]))
+ {
+ throw new \LogicException("The event '{$this->current_event}' is defined multiple times");
+ }
+
+ if (($this->filter == 'adm' && strpos($this->current_event, 'acp_') !== 0)
+ || ($this->filter == 'styles' && strpos($this->current_event, 'acp_') === 0))
+ {
+ continue;
+ }
+
+ list($file_details, $details) = explode("\n* Since: ", $details, 2);
+
+ $changed_versions = array();
+ if (strpos($details, "\n* Changed: ") !== false)
+ {
+ list($since, $details) = explode("\n* Changed: ", $details, 2);
+ while (strpos($details, "\n* Changed: ") !== false)
+ {
+ list($changed, $details) = explode("\n* Changed: ", $details, 2);
+ $changed_versions[] = $changed;
+ }
+ list($changed, $description) = explode("\n* Purpose: ", $details, 2);
+ $changed_versions[] = $changed;
+ }
+ else
+ {
+ list($since, $description) = explode("\n* Purpose: ", $details, 2);
+ $changed_versions = array();
+ }
+
+ $files = $this->validate_file_list($file_details);
+ $since = $this->validate_since($since);
+ $changes = array();
+ foreach ($changed_versions as $changed)
+ {
+ list($changed_version, $changed_description) = $this->validate_changed($changed);
+
+ if (isset($changes[$changed_version]))
+ {
+ throw new \LogicException("Duplicate change information found for event '{$this->current_event}'");
+ }
+
+ $changes[$changed_version] = $changed_description;
+ }
+ $description = trim($description, "\n") . "\n";
+
+ if (!$this->version_is_filtered($since))
+ {
+ $is_filtered = false;
+ foreach ($changes as $version => $null)
+ {
+ if ($this->version_is_filtered($version))
+ {
+ $is_filtered = true;
+ break;
+ }
+ }
+
+ if (!$is_filtered)
+ {
+ continue;
+ }
+ }
+
+ $this->events[$event_name] = array(
+ 'event' => $this->current_event,
+ 'files' => $files,
+ 'since' => $since,
+ 'changed' => $changes,
+ 'description' => $description,
+ );
+ }
+
+ return sizeof($this->events);
+ }
+
+ /**
+ * The version to check
+ *
+ * @param string $version
+ * @return bool
+ */
+ protected function version_is_filtered($version)
+ {
+ return (!$this->min_version || phpbb_version_compare($this->min_version, $version, '<='))
+ && (!$this->max_version || phpbb_version_compare($this->max_version, $version, '>='));
+ }
+
+ /**
+ * Format the php events as a wiki table
+ *
+ * @param string $action
+ * @return string Number of events found
+ */
+ public function export_events_for_wiki($action = '')
+ {
+ if ($this->filter === 'adm')
+ {
+ if ($action === 'diff')
+ {
+ $wiki_page = '=== ACP Template Events ===' . "\n";
+ }
+ else
+ {
+ $wiki_page = '= ACP Template Events =' . "\n";
+ }
+ $wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
+ $wiki_page .= '! Identifier !! Placement !! Added in Release !! Explanation' . "\n";
+ }
+ else
+ {
+ if ($action === 'diff')
+ {
+ $wiki_page = '=== Template Events ===' . "\n";
+ }
+ else
+ {
+ $wiki_page = '= Template Events =' . "\n";
+ }
+ $wiki_page .= '{| class="zebra sortable" cellspacing="0" cellpadding="5"' . "\n";
+ $wiki_page .= '! Identifier !! Prosilver Placement (If applicable) !! Added in Release !! Explanation' . "\n";
+ }
+
+ foreach ($this->events as $event_name => $event)
+ {
+ $wiki_page .= "|- id=\"{$event_name}\"\n";
+ $wiki_page .= "| [[#{$event_name}|{$event_name}]] || ";
+
+ if ($this->filter === 'adm')
+ {
+ $wiki_page .= implode(', ', $event['files']['adm']);
+ }
+ else
+ {
+ $wiki_page .= implode(', ', $event['files']['prosilver']);
+ }
+
+ $wiki_page .= " || {$event['since']} || " . str_replace("\n", ' ', $event['description']) . "\n";
+ }
+ $wiki_page .= '|}' . "\n";
+
+ return $wiki_page;
+ }
+
+ /**
+ * Validates a template event name
+ *
+ * @param $event_name
+ * @return null
+ * @throws \LogicException
+ */
+ public function validate_event_name($event_name)
+ {
+ if (!preg_match('#^([a-z][a-z0-9]*(?:_[a-z][a-z0-9]*)+)$#', $event_name))
+ {
+ throw new \LogicException("Invalid event name '{$event_name}'");
+ }
+ }
+
+ /**
+ * Validate "Since" Information
+ *
+ * @param string $since
+ * @return string
+ * @throws \LogicException
+ */
+ public function validate_since($since)
+ {
+ if (!$this->validate_version($since))
+ {
+ throw new \LogicException("Invalid since information found for event '{$this->current_event}'");
+ }
+
+ return $since;
+ }
+
+ /**
+ * Validate "Changed" Information
+ *
+ * @param string $changed
+ * @return string
+ * @throws \LogicException
+ */
+ public function validate_changed($changed)
+ {
+ if (strpos($changed, ' ') !== false)
+ {
+ list($version, $description) = explode(' ', $changed, 2);
+ }
+ else
+ {
+ $version = $changed;
+ $description = '';
+ }
+
+ if (!$this->validate_version($version))
+ {
+ throw new \LogicException("Invalid changed information found for event '{$this->current_event}'");
+ }
+
+ return array($version, $description);
+ }
+
+ /**
+ * Validate "version" Information
+ *
+ * @param string $version
+ * @return bool True if valid, false otherwise
+ */
+ public function validate_version($version)
+ {
+ return preg_match('#^\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?$#', $version);
+ }
+
+ /**
+ * Validate the files list
+ *
+ * @param string $file_details
+ * @return array
+ * @throws \LogicException
+ */
+ public function validate_file_list($file_details)
+ {
+ $files_list = array(
+ 'prosilver' => array(),
+ 'adm' => array(),
+ );
+
+ // Multi file list
+ if (strpos($file_details, "* Locations:\n + ") === 0)
+ {
+ $file_details = substr($file_details, strlen("* Locations:\n + "));
+ $files = explode("\n + ", $file_details);
+ foreach ($files as $file)
+ {
+ if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
+ {
+ throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 1);
+ }
+
+ if (($this->filter !== 'adm') && strpos($file, 'styles/prosilver/template/') === 0)
+ {
+ $files_list['prosilver'][] = substr($file, strlen('styles/prosilver/template/'));
+ }
+ else if (($this->filter === 'adm') && strpos($file, 'adm/style/') === 0)
+ {
+ $files_list['adm'][] = substr($file, strlen('adm/style/'));
+ }
+ else
+ {
+ throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 2);
+ }
+
+ $this->events_by_file[$file][] = $this->current_event;
+ }
+ }
+ else if ($this->filter == 'adm')
+ {
+ $file = substr($file_details, strlen('* Location: '));
+ if (!file_exists($this->path . $file) || substr($file, -5) !== '.html')
+ {
+ throw new \LogicException("Invalid file '{$file}' not found for event '{$this->current_event}'", 1);
+ }
+
+ $files_list['adm'][] = substr($file, strlen('adm/style/'));
+
+ $this->events_by_file[$file][] = $this->current_event;
+ }
+ else
+ {
+ throw new \LogicException("Invalid file list found for event '{$this->current_event}'", 2);
+ }
+
+ return $files_list;
+ }
+
+ /**
+ * Get all template events in a template file
+ *
+ * @param string $file
+ * @return array
+ * @throws \LogicException
+ */
+ public function crawl_file_for_events($file)
+ {
+ if (!file_exists($this->path . $file))
+ {
+ throw new \LogicException("File '{$file}' does not exist", 1);
+ }
+
+ $event_list = array();
+ $file_content = file_get_contents($this->path . $file);
+
+ $events = explode('<!-- EVENT ', $file_content);
+ // Remove the code before the first event
+ array_shift($events);
+ foreach ($events as $event)
+ {
+ $event = explode(' -->', $event, 2);
+ $event_list[] = array_shift($event);
+ }
+
+ return $event_list;
+ }
+
+ /**
+ * Validates whether all events from $file are in the md file and vice-versa
+ *
+ * @param string $file
+ * @param array $events
+ * @return true
+ * @throws \LogicException
+ */
+ public function validate_events_from_file($file, array $events)
+ {
+ if (empty($this->events_by_file[$file]) && empty($events))
+ {
+ return true;
+ }
+ else if (empty($this->events_by_file[$file]))
+ {
+ $event_list = implode("', '", $events);
+ throw new \LogicException("File '{$file}' should not contain events, but contains: "
+ . "'{$event_list}'", 1);
+ }
+ else if (empty($events))
+ {
+ $event_list = implode("', '", $this->events_by_file[$file]);
+ throw new \LogicException("File '{$file}' contains no events, but should contain: "
+ . "'{$event_list}'", 1);
+ }
+
+ $missing_events_from_file = array();
+ foreach ($this->events_by_file[$file] as $event)
+ {
+ if (!in_array($event, $events))
+ {
+ $missing_events_from_file[] = $event;
+ }
+ }
+
+ if (!empty($missing_events_from_file))
+ {
+ $event_list = implode("', '", $missing_events_from_file);
+ throw new \LogicException("File '{$file}' does not contain events: '{$event_list}'", 2);
+ }
+
+ $missing_events_from_md = array();
+ foreach ($events as $event)
+ {
+ if (!in_array($event, $this->events_by_file[$file]))
+ {
+ $missing_events_from_md[] = $event;
+ }
+ }
+
+ if (!empty($missing_events_from_md))
+ {
+ $event_list = implode("', '", $missing_events_from_md);
+ throw new \LogicException("File '{$file}' contains additional events: '{$event_list}'", 3);
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a list of files in $dir
+ *
+ * Works recursive with any depth
+ *
+ * @param string $dir Directory to go through
+ * @return array List of files (including directories)
+ */
+ public function get_recursive_file_list($dir)
+ {
+ try
+ {
+ $iterator = new \RecursiveIteratorIterator(
+ new \phpbb\recursive_dot_prefix_filter_iterator(
+ new \RecursiveDirectoryIterator(
+ $dir,
+ \FilesystemIterator::SKIP_DOTS
+ )
+ ),
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+ }
+ catch (\Exception $e)
+ {
+ return array();
+ }
+
+ $files = array();
+ foreach ($iterator as $file_info)
+ {
+ /** @var \RecursiveDirectoryIterator $file_info */
+ if ($file_info->isDir())
+ {
+ continue;
+ }
+
+ $relative_path = $iterator->getInnerIterator()->getSubPathname();
+
+ if (substr($relative_path, -5) == '.html')
+ {
+ $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path);
+ }
+ }
+
+ return $files;
+ }
+}
diff --git a/phpBB/phpbb/event/php_exporter.php b/phpBB/phpbb/event/php_exporter.php
new file mode 100644
index 0000000000..d2ab0595c0
--- /dev/null
+++ b/phpBB/phpbb/event/php_exporter.php
@@ -0,0 +1,718 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\event;
+
+/**
+* Class php_exporter
+* Crawls through a list of files and grabs all php-events
+*/
+class php_exporter
+{
+ /** @var string Path where we look for files*/
+ protected $path;
+
+ /** @var string phpBB Root Path */
+ protected $root_path;
+
+ /** @var string The minimum version for the events to return */
+ protected $min_version;
+
+ /** @var string The maximum version for the events to return */
+ protected $max_version;
+
+ /** @var string */
+ protected $current_file;
+
+ /** @var string */
+ protected $current_event;
+
+ /** @var int */
+ protected $current_event_line;
+
+ /** @var array */
+ protected $events;
+
+ /** @var array */
+ protected $file_lines;
+
+ /**
+ * @param string $phpbb_root_path
+ * @param mixed $extension String 'vendor/ext' to filter, null for phpBB core
+ * @param string $min_version
+ * @param string $max_version
+ */
+ public function __construct($phpbb_root_path, $extension = null, $min_version = null, $max_version = null)
+ {
+ $this->root_path = $phpbb_root_path;
+ $this->path = $phpbb_root_path;
+ $this->events = $this->file_lines = array();
+ $this->current_file = $this->current_event = '';
+ $this->current_event_line = 0;
+ $this->min_version = $min_version;
+ $this->max_version = $max_version;
+
+ $this->path = $this->root_path;
+ if ($extension)
+ {
+ $this->path .= 'ext/' . $extension . '/';
+ }
+ }
+
+ /**
+ * Get the list of all events
+ *
+ * @return array Array with events: name => details
+ */
+ public function get_events()
+ {
+ return $this->events;
+ }
+
+ /**
+ * Set current event data
+ *
+ * @param string $name Name of the current event (used for error messages)
+ * @param int $line Line where the current event is placed in
+ * @return null
+ */
+ public function set_current_event($name, $line)
+ {
+ $this->current_event = $name;
+ $this->current_event_line = $line;
+ }
+
+ /**
+ * Set the content of this file
+ *
+ * @param array $content Array with the lines of the file
+ * @return null
+ */
+ public function set_content($content)
+ {
+ $this->file_lines = $content;
+ }
+
+ /**
+ * Crawl the phpBB/ directory for php events
+ * @return int The number of events found
+ */
+ public function crawl_phpbb_directory_php()
+ {
+ $files = $this->get_recursive_file_list();
+ $this->events = array();
+ foreach ($files as $file)
+ {
+ $this->crawl_php_file($file);
+ }
+ ksort($this->events);
+
+ return sizeof($this->events);
+ }
+
+ /**
+ * Returns a list of files in $dir
+ *
+ * @return array List of files (including the path)
+ */
+ public function get_recursive_file_list()
+ {
+ try
+ {
+ $iterator = new \RecursiveIteratorIterator(
+ new \phpbb\event\recursive_event_filter_iterator(
+ new \RecursiveDirectoryIterator(
+ $this->path,
+ \FilesystemIterator::SKIP_DOTS
+ ),
+ $this->path
+ ),
+ \RecursiveIteratorIterator::LEAVES_ONLY
+ );
+ }
+ catch (\Exception $e)
+ {
+ return array();
+ }
+
+ $files = array();
+ foreach ($iterator as $file_info)
+ {
+ /** @var \RecursiveDirectoryIterator $file_info */
+ $relative_path = $iterator->getInnerIterator()->getSubPathname();
+ $files[] = str_replace(DIRECTORY_SEPARATOR, '/', $relative_path);
+ }
+
+ return $files;
+ }
+
+ /**
+ * Format the php events as a wiki table
+ *
+ * @param string $action
+ * @return string
+ */
+ public function export_events_for_wiki($action = '')
+ {
+ if ($action === 'diff')
+ {
+ $wiki_page = '=== PHP Events (Hook Locations) ===' . "\n";
+ }
+ else
+ {
+ $wiki_page = '= PHP Events (Hook Locations) =' . "\n";
+ }
+ $wiki_page .= '{| class="sortable zebra" cellspacing="0" cellpadding="5"' . "\n";
+ $wiki_page .= '! Identifier !! Placement !! Arguments !! Added in Release !! Explanation' . "\n";
+ foreach ($this->events as $event)
+ {
+ $wiki_page .= '|- id="' . $event['event'] . '"' . "\n";
+ $wiki_page .= '| [[#' . $event['event'] . '|' . $event['event'] . ']] || ' . $event['file'] . ' || ' . implode(', ', $event['arguments']) . ' || ' . $event['since'] . ' || ' . $event['description'] . "\n";
+ }
+ $wiki_page .= '|}' . "\n";
+
+ return $wiki_page;
+ }
+
+ /**
+ * @param string $file
+ * @return int Number of events found in this file
+ * @throws \LogicException
+ */
+ public function crawl_php_file($file)
+ {
+ $this->current_file = $file;
+ $this->file_lines = array();
+ $content = file_get_contents($this->path . $this->current_file);
+ $num_events_found = 0;
+
+ if (strpos($content, "dispatcher->trigger_event('") || strpos($content, "dispatcher->dispatch('"))
+ {
+ $this->set_content(explode("\n", $content));
+ for ($i = 0, $num_lines = sizeof($this->file_lines); $i < $num_lines; $i++)
+ {
+ $event_line = false;
+ $found_trigger_event = strpos($this->file_lines[$i], "dispatcher->trigger_event('");
+ $arguments = array();
+ if ($found_trigger_event !== false)
+ {
+ $event_line = $i;
+ $this->set_current_event($this->get_event_name($event_line, false), $event_line);
+
+ // Find variables of the event
+ $arguments = $this->get_vars_from_array();
+ $doc_vars = $this->get_vars_from_docblock();
+ $this->validate_vars_docblock_array($arguments, $doc_vars);
+ }
+ else
+ {
+ $found_dispatch = strpos($this->file_lines[$i], "dispatcher->dispatch('");
+ if ($found_dispatch !== false)
+ {
+ $event_line = $i;
+ $this->set_current_event($this->get_event_name($event_line, true), $event_line);
+ }
+ }
+
+ if ($event_line)
+ {
+ // Validate @event
+ $event_line_num = $this->find_event();
+ $this->validate_event($this->current_event, $this->file_lines[$event_line_num]);
+
+ // Validate @since
+ $since_line_num = $this->find_since();
+ $since = $this->validate_since($this->file_lines[$since_line_num]);
+
+ $changed_line_nums = $this->find_changed('changed');
+ if (empty($changed_line_nums))
+ {
+ $changed_line_nums = $this->find_changed('change');
+ }
+ $changed_versions = array();
+ if (!empty($changed_line_nums))
+ {
+ foreach ($changed_line_nums as $changed_line_num)
+ {
+ $changed_versions[] = $this->validate_changed($this->file_lines[$changed_line_num]);
+ }
+ }
+
+ if (!$this->version_is_filtered($since))
+ {
+ $valid_version = false;
+ foreach ($changed_versions as $changed)
+ {
+ $valid_version = $valid_version || $this->version_is_filtered($changed);
+ }
+
+ if (!$valid_version)
+ {
+ continue;
+ }
+ }
+
+ // Find event description line
+ $description_line_num = $this->find_description();
+ $description = substr(trim($this->file_lines[$description_line_num]), strlen('* '));
+
+ if (isset($this->events[$this->current_event]))
+ {
+ throw new \LogicException("The event '{$this->current_event}' from file "
+ . "'{$this->current_file}:{$event_line_num}' already exists in file "
+ . "'{$this->events[$this->current_event]['file']}'", 10);
+ }
+
+ sort($arguments);
+ $this->events[$this->current_event] = array(
+ 'event' => $this->current_event,
+ 'file' => $this->current_file,
+ 'arguments' => $arguments,
+ 'since' => $since,
+ 'description' => $description,
+ );
+ $num_events_found++;
+ }
+ }
+ }
+
+ return $num_events_found;
+ }
+
+ /**
+ * The version to check
+ *
+ * @param string $version
+ * @return bool
+ */
+ protected function version_is_filtered($version)
+ {
+ return (!$this->min_version || phpbb_version_compare($this->min_version, $version, '<='))
+ && (!$this->max_version || phpbb_version_compare($this->max_version, $version, '>='));
+ }
+
+ /**
+ * Find the name of the event inside the dispatch() line
+ *
+ * @param int $event_line
+ * @param bool $is_dispatch Do we look for dispatch() or trigger_event() ?
+ * @return string Name of the event
+ * @throws \LogicException
+ */
+ public function get_event_name($event_line, $is_dispatch)
+ {
+ $event_text_line = $this->file_lines[$event_line];
+ $event_text_line = ltrim($event_text_line, "\t ");
+
+ if ($is_dispatch)
+ {
+ $regex = '#\$([a-z](?:[a-z0-9_]|->)*)';
+ $regex .= '->dispatch\(';
+ $regex .= '\'' . $this->preg_match_event_name() . '\'';
+ $regex .= '\);#';
+ }
+ else
+ {
+ $regex = '#extract\(\$([a-z](?:[a-z0-9_]|->)*)';
+ $regex .= '->trigger_event\(';
+ $regex .= '\'' . $this->preg_match_event_name() . '\'';
+ $regex .= ', compact\(\$vars\)\)\);#';
+ }
+
+ $match = array();
+ preg_match($regex, $event_text_line, $match);
+ if (!isset($match[2]))
+ {
+ throw new \LogicException("Can not find event name in line '{$event_text_line}' "
+ . "in file '{$this->current_file}:{$event_line}'", 1);
+ }
+
+ return $match[2];
+ }
+
+ /**
+ * Returns a regex match for the event name
+ *
+ * @return string
+ */
+ protected function preg_match_event_name()
+ {
+ return '([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+)';
+ }
+
+ /**
+ * Find the $vars array
+ *
+ * @return array List of variables
+ * @throws \LogicException
+ */
+ public function get_vars_from_array()
+ {
+ $line = ltrim($this->file_lines[$this->current_event_line - 1], "\t");
+ if ($line === ');')
+ {
+ $vars_array = $this->get_vars_from_multi_line_array();
+ }
+ else
+ {
+ $vars_array = $this->get_vars_from_single_line_array($line);
+ }
+
+ foreach ($vars_array as $var)
+ {
+ if (!preg_match('#^([a-zA-Z_][a-zA-Z0-9_]*)$#', $var))
+ {
+ throw new \LogicException("Found invalid var '{$var}' in array for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3);
+ }
+ }
+
+ sort($vars_array);
+ return $vars_array;
+ }
+
+ /**
+ * Find the variables in single line array
+ *
+ * @param string $line
+ * @param bool $throw_multiline Throw an exception when there are too
+ * many arguments in one line.
+ * @return array List of variables
+ * @throws \LogicException
+ */
+ public function get_vars_from_single_line_array($line, $throw_multiline = true)
+ {
+ $match = array();
+ preg_match('#^\$vars = array\(\'([a-zA-Z0-9_\' ,]+)\'\);$#', $line, $match);
+
+ if (isset($match[1]))
+ {
+ $vars_array = explode("', '", $match[1]);
+ if ($throw_multiline && sizeof($vars_array) > 6)
+ {
+ throw new \LogicException('Should use multiple lines for $vars definition '
+ . "for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+ return $vars_array;
+ }
+ else
+ {
+ throw new \LogicException("Can not find '\$vars = array();'-line for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1);
+ }
+ }
+
+ /**
+ * Find the variables in single line array
+ *
+ * @return array List of variables
+ * @throws \LogicException
+ */
+ public function get_vars_from_multi_line_array()
+ {
+ $current_vars_line = 2;
+ $var_lines = array();
+ while (ltrim($this->file_lines[$this->current_event_line - $current_vars_line], "\t") !== '$vars = array(')
+ {
+ $var_lines[] = substr(trim($this->file_lines[$this->current_event_line - $current_vars_line]), 0, -1);
+
+ $current_vars_line++;
+ if ($current_vars_line > $this->current_event_line)
+ {
+ // Reached the start of the file
+ throw new \LogicException("Can not find end of \$vars array for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+ }
+
+ return $this->get_vars_from_single_line_array('$vars = array(' . implode(", ", $var_lines) . ');', false);
+ }
+
+ /**
+ * Find the $vars array
+ *
+ * @return array List of variables
+ * @throws \LogicException
+ */
+ public function get_vars_from_docblock()
+ {
+ $doc_vars = array();
+ $current_doc_line = 1;
+ $found_comment_end = false;
+ while (ltrim($this->file_lines[$this->current_event_line - $current_doc_line], "\t") !== '/**')
+ {
+ if (ltrim($this->file_lines[$this->current_event_line - $current_doc_line], "\t ") === '*/')
+ {
+ $found_comment_end = true;
+ }
+
+ if ($found_comment_end)
+ {
+ $var_line = trim($this->file_lines[$this->current_event_line - $current_doc_line]);
+ $var_line = preg_replace('!\s+!', ' ', $var_line);
+ if (strpos($var_line, '* @var ') === 0)
+ {
+ $doc_line = explode(' ', $var_line, 5);
+ if (sizeof($doc_line) !== 5)
+ {
+ throw new \LogicException("Found invalid line '{$this->file_lines[$this->current_event_line - $current_doc_line]}' "
+ . "for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1);
+ }
+ $doc_vars[] = $doc_line[3];
+ }
+ }
+
+ $current_doc_line++;
+ if ($current_doc_line > $this->current_event_line)
+ {
+ // Reached the start of the file
+ throw new \LogicException("Can not find end of docblock for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+ }
+
+ if (empty($doc_vars))
+ {
+ // Reached the start of the file
+ throw new \LogicException("Can not find @var lines for event '{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3);
+ }
+
+ foreach ($doc_vars as $var)
+ {
+ if (!preg_match('#^([a-zA-Z_][a-zA-Z0-9_]*)$#', $var))
+ {
+ throw new \LogicException("Found invalid @var '{$var}' in docblock for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 4);
+ }
+ }
+
+ sort($doc_vars);
+ return $doc_vars;
+ }
+
+ /**
+ * Find the "@since" Information line
+ *
+ * @return int Absolute line number
+ * @throws \LogicException
+ */
+ public function find_since()
+ {
+ return $this->find_tag('since', array('event', 'var'));
+ }
+
+ /**
+ * Find the "@changed" Information lines
+ *
+ * @param string $tag_name Should be 'changed' or 'change'
+ * @return array Absolute line numbers
+ * @throws \LogicException
+ */
+ public function find_changed($tag_name)
+ {
+ $lines = array();
+ $last_line = 0;
+ try
+ {
+ while ($line = $this->find_tag($tag_name, array('since'), $last_line))
+ {
+ $lines[] = $line;
+ $last_line = $line;
+ }
+ }
+ catch (\LogicException $e)
+ {
+ // Not changed? No problem!
+ }
+
+ return $lines;
+ }
+
+ /**
+ * Find the "@event" Information line
+ *
+ * @return int Absolute line number
+ */
+ public function find_event()
+ {
+ return $this->find_tag('event', array());
+ }
+
+ /**
+ * Find a "@*" Information line
+ *
+ * @param string $find_tag Name of the tag we are trying to find
+ * @param array $disallowed_tags List of tags that must not appear between
+ * the tag and the actual event
+ * @param int $skip_to_line Skip lines until this one
+ * @return int Absolute line number
+ * @throws \LogicException
+ */
+ public function find_tag($find_tag, $disallowed_tags, $skip_to_line = 0)
+ {
+ $find_tag_line = $skip_to_line ? $this->current_event_line - $skip_to_line + 1 : 0;
+ $found_comment_end = ($skip_to_line) ? true : false;
+ while (strpos(ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t "), '* @' . $find_tag . ' ') !== 0)
+ {
+ if ($found_comment_end && ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t") === '/**')
+ {
+ // Reached the start of this doc block
+ throw new \LogicException("Can not find '@{$find_tag}' information for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1);
+ }
+
+ foreach ($disallowed_tags as $disallowed_tag)
+ {
+ if ($found_comment_end && strpos(ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t "), '* @' . $disallowed_tag) === 0)
+ {
+ // Found @var after the @since
+ throw new \LogicException("Found '@{$disallowed_tag}' information after '@{$find_tag}' for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 3);
+ }
+ }
+
+ if (ltrim($this->file_lines[$this->current_event_line - $find_tag_line], "\t ") === '*/')
+ {
+ $found_comment_end = true;
+ }
+
+ $find_tag_line++;
+ if ($find_tag_line >= $this->current_event_line)
+ {
+ // Reached the start of the file
+ throw new \LogicException("Can not find '@{$find_tag}' information for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+ }
+
+ return $this->current_event_line - $find_tag_line;
+ }
+
+ /**
+ * Find a "@*" Information line
+ *
+ * @return int Absolute line number
+ * @throws \LogicException
+ */
+ public function find_description()
+ {
+ $find_desc_line = 0;
+ while (ltrim($this->file_lines[$this->current_event_line - $find_desc_line], "\t") !== '/**')
+ {
+ $find_desc_line++;
+ if ($find_desc_line > $this->current_event_line)
+ {
+ // Reached the start of the file
+ throw new \LogicException("Can not find a description for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1);
+ }
+ }
+
+ $find_desc_line = $this->current_event_line - $find_desc_line + 1;
+
+ $desc = trim($this->file_lines[$find_desc_line]);
+ if (strpos($desc, '* @') === 0 || $desc[0] !== '*' || substr($desc, 1) == '')
+ {
+ // First line of the doc block is a @-line, empty or only contains "*"
+ throw new \LogicException("Can not find a description for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+
+ return $find_desc_line;
+ }
+
+ /**
+ * Validate "@since" Information
+ *
+ * @param string $line
+ * @return string
+ * @throws \LogicException
+ */
+ public function validate_since($line)
+ {
+ $match = array();
+ preg_match('#^\* @since (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)$#', ltrim($line, "\t "), $match);
+ if (!isset($match[1]))
+ {
+ throw new \LogicException("Invalid '@since' information for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'");
+ }
+
+ return $match[1];
+ }
+
+ /**
+ * Validate "@changed" Information
+ *
+ * @param string $line
+ * @return string
+ * @throws \LogicException
+ */
+ public function validate_changed($line)
+ {
+ $match = array();
+ $line = str_replace("\t", ' ', ltrim($line, "\t "));
+ preg_match('#^\* @change(d)? (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)( (?:.*))?$#', $line, $match);
+ if (!isset($match[2]))
+ {
+ throw new \LogicException("Invalid '@changed' information for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'");
+ }
+
+ return $match[2];
+ }
+
+ /**
+ * Validate "@event" Information
+ *
+ * @param string $event_name
+ * @param string $line
+ * @return string
+ * @throws \LogicException
+ */
+ public function validate_event($event_name, $line)
+ {
+ $event = substr(ltrim($line, "\t "), strlen('* @event '));
+
+ if ($event !== trim($event))
+ {
+ throw new \LogicException("Invalid '@event' information for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 1);
+ }
+
+ if ($event !== $event_name)
+ {
+ throw new \LogicException("Event name does not match '@event' tag for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'", 2);
+ }
+
+ return $event;
+ }
+
+ /**
+ * Validates that two arrays contain the same strings
+ *
+ * @param array $vars_array Variables found in the array line
+ * @param array $vars_docblock Variables found in the doc block
+ * @return null
+ * @throws \LogicException
+ */
+ public function validate_vars_docblock_array($vars_array, $vars_docblock)
+ {
+ $vars_array = array_unique($vars_array);
+ $vars_docblock = array_unique($vars_docblock);
+ $sizeof_vars_array = sizeof($vars_array);
+
+ if ($sizeof_vars_array !== sizeof($vars_docblock) || $sizeof_vars_array !== sizeof(array_intersect($vars_array, $vars_docblock)))
+ {
+ throw new \LogicException("\$vars array does not match the list of '@var' tags for event "
+ . "'{$this->current_event}' in file '{$this->current_file}:{$this->current_event_line}'");
+ }
+ }
+}
diff --git a/phpBB/phpbb/event/recursive_event_filter_iterator.php b/phpBB/phpbb/event/recursive_event_filter_iterator.php
new file mode 100644
index 0000000000..64e2e56f6a
--- /dev/null
+++ b/phpBB/phpbb/event/recursive_event_filter_iterator.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\event;
+
+/**
+* This filter ignores directories and files starting with a dot.
+* It also skips some directories that do not contain events anyway,
+* such as e.g. files/, store/ and vendor/
+*/
+class recursive_event_filter_iterator extends \RecursiveFilterIterator
+{
+ protected $root_path;
+
+ /**
+ * Construct
+ *
+ * @param \RecursiveIterator $iterator
+ * @param string $root_path
+ */
+ public function __construct(\RecursiveIterator $iterator, $root_path)
+ {
+ $this->root_path = str_replace(DIRECTORY_SEPARATOR, '/', $root_path);
+ parent::__construct($iterator);
+ }
+
+ /**
+ * Return the inner iterator's children contained in a recursive_event_filter_iterator
+ *
+ * @return recursive_event_filter_iterator
+ */
+ public function getChildren()
+ {
+ return new self($this->getInnerIterator()->getChildren(), $this->root_path);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function accept()
+ {
+ $relative_path = str_replace(DIRECTORY_SEPARATOR, '/', $this->current());
+ $filename = $this->current()->getFilename();
+
+ return (substr($relative_path, -4) === '.php' || $this->current()->isDir())
+ && $filename[0] !== '.'
+ && strpos($relative_path, $this->root_path . 'cache/') !== 0
+ && strpos($relative_path, $this->root_path . 'develop/') !== 0
+ && strpos($relative_path, $this->root_path . 'docs/') !== 0
+ && strpos($relative_path, $this->root_path . 'ext/') !== 0
+ && strpos($relative_path, $this->root_path . 'files/') !== 0
+ && strpos($relative_path, $this->root_path . 'includes/utf/') !== 0
+ && strpos($relative_path, $this->root_path . 'language/') !== 0
+ && strpos($relative_path, $this->root_path . 'phpbb/db/migration/data/') !== 0
+ && strpos($relative_path, $this->root_path . 'phpbb/event/') !== 0
+ && strpos($relative_path, $this->root_path . 'store/') !== 0
+ && strpos($relative_path, $this->root_path . 'tests/') !== 0
+ && strpos($relative_path, $this->root_path . 'vendor/') !== 0
+ ;
+ }
+}
diff --git a/phpBB/phpbb/exception/exception_interface.php b/phpBB/phpbb/exception/exception_interface.php
new file mode 100644
index 0000000000..e8526a35f5
--- /dev/null
+++ b/phpBB/phpbb/exception/exception_interface.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\exception;
+
+/**
+ * Interface exception_interface
+ *
+ * Define an exception which support a language var as message.
+ */
+interface exception_interface
+{
+ /**
+ * Return the arguments associated with the message if it's a language var.
+ *
+ * @return array
+ */
+ public function get_parameters();
+}
diff --git a/phpBB/phpbb/exception/http_exception.php b/phpBB/phpbb/exception/http_exception.php
new file mode 100644
index 0000000000..0e6ffe4f59
--- /dev/null
+++ b/phpBB/phpbb/exception/http_exception.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\exception;
+
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
+
+/**
+ * Class http_exception
+ */
+class http_exception extends runtime_exception implements HttpExceptionInterface
+{
+ /**
+ * Http status code.
+ *
+ * @var integer
+ */
+ private $status_code;
+
+ /**
+ * Additional headers to set in the response.
+ *
+ * @var array
+ */
+ private $headers;
+
+ /**
+ * Constructor
+ *
+ * @param integer $status_code The http status code.
+ * @param string $message The Exception message to throw (must be a language variable).
+ * @param array $parameters The parameters to use with the language var.
+ * @param \Exception $previous The previous exception used for the exception chaining.
+ * @param array $headers Additional headers to set in the response.
+ * @param integer $code The Exception code.
+ */
+ public function __construct($status_code, $message = "", array $parameters = array(), \Exception $previous = null, array $headers = array(), $code = 0)
+ {
+ $this->status_code = $status_code;
+ $this->headers = $headers;
+
+ parent::__construct($message, $parameters, $previous, $code);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStatusCode()
+ {
+ return $this->status_code;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+}
diff --git a/phpBB/phpbb/exception/runtime_exception.php b/phpBB/phpbb/exception/runtime_exception.php
new file mode 100644
index 0000000000..6568bbf86f
--- /dev/null
+++ b/phpBB/phpbb/exception/runtime_exception.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\exception;
+
+/**
+ * Class runtime_exception
+ *
+ * Define an exception which support a language var as message.
+ */
+class runtime_exception extends \RuntimeException implements exception_interface
+{
+ /**
+ * Parameters to use with the language var.
+ *
+ * @var array
+ */
+ private $parameters;
+
+ /**
+ * Constructor
+ *
+ * @param string $message The Exception message to throw (must be a language variable).
+ * @param array $parameters The parameters to use with the language var.
+ * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining.
+ * @param integer $code The Exception code.
+ */
+ public function __construct($message = "", array $parameters = array(), \Exception $previous = null, $code = 0)
+ {
+ $this->parameters = $parameters;
+
+ parent::__construct($message, $code, $previous);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_parameters()
+ {
+ return $this->parameters;
+ }
+}
diff --git a/phpBB/phpbb/extension/base.php b/phpBB/phpbb/extension/base.php
index 1f871750e0..5bb530bad4 100644
--- a/phpBB/phpbb/extension/base.php
+++ b/phpBB/phpbb/extension/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,15 +17,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* A base class for extensions without custom enable/disable/purge code.
-*
-* @package extension
*/
class base implements \phpbb\extension\extension_interface
{
/** @var ContainerInterface */
protected $container;
- /** @var \phpbb\extension\finder */
+ /** @var \phpbb\finder */
protected $finder;
/** @var \phpbb\db\migrator */
@@ -33,15 +35,19 @@ class base implements \phpbb\extension\extension_interface
/** @var string */
protected $extension_path;
+ /** @var string[] */
+ private $migrations = false;
+
/**
* Constructor
*
* @param ContainerInterface $container Container object
- * @param \phpbb\extension\finder $extension_finder
+ * @param \phpbb\finder $extension_finder
+ * @param \phpbb\db\migrator $migrator
* @param string $extension_name Name of this extension (from ext.manager)
* @param string $extension_path Relative path to this extension
*/
- public function __construct(ContainerInterface $container, \phpbb\extension\finder $extension_finder, \phpbb\db\migrator $migrator, $extension_name, $extension_path)
+ public function __construct(ContainerInterface $container, \phpbb\finder $extension_finder, \phpbb\db\migrator $migrator, $extension_name, $extension_path)
{
$this->container = $container;
$this->extension_finder = $extension_finder;
@@ -52,6 +58,14 @@ class base implements \phpbb\extension\extension_interface
}
/**
+ * {@inheritdoc}
+ */
+ public function is_enableable()
+ {
+ return true;
+ }
+
+ /**
* Single enable step that installs any included migrations
*
* @param mixed $old_state State returned by previous call of this method
@@ -111,17 +125,16 @@ class base implements \phpbb\extension\extension_interface
*/
protected function get_migration_file_list()
{
- static $migrations = false;
-
- if ($migrations !== false)
+ if ($this->migrations !== false)
{
- return $migrations;
+ return $this->migrations;
}
// Only have the finder search in this extension path directory
$migrations = $this->extension_finder
->extension_directory('/migrations')
->find_from_extension($this->extension_name, $this->extension_path);
+
$migrations = $this->extension_finder->get_classes_from_files($migrations);
return $migrations;
diff --git a/phpBB/phpbb/extension/di/extension_base.php b/phpBB/phpbb/extension/di/extension_base.php
new file mode 100644
index 0000000000..ba74615e70
--- /dev/null
+++ b/phpBB/phpbb/extension/di/extension_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\extension\di;
+
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+
+/**
+ * Container core extension
+ */
+class extension_base extends Extension
+{
+ /**
+ * Name of the extension (vendor/name)
+ *
+ * @var string
+ */
+ protected $extension_name;
+
+ /**
+ * Path to the extension.
+ *
+ * @var string
+ */
+ protected $ext_path;
+
+ /**
+ * Constructor
+ *
+ * @param string $extension_name Name of the extension (vendor/name)
+ * @param string $ext_path Path to the extension
+ */
+ public function __construct($extension_name, $ext_path)
+ {
+ $this->extension_name = $extension_name;
+ $this->ext_path = $ext_path;
+ }
+
+ /**
+ * Loads a specific configuration.
+ *
+ * @param array $configs An array of configuration values
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ *
+ * @throws \InvalidArgumentException When provided tag is not defined in this extension
+ */
+ public function load(array $configs, ContainerBuilder $container)
+ {
+ $this->load_services($container);
+ }
+
+ /**
+ * Loads the services.yml file.
+ *
+ * @param ContainerBuilder $container A ContainerBuilder instance
+ */
+ protected function load_services(ContainerBuilder $container)
+ {
+ $services_directory = false;
+ $services_file = false;
+
+ if (file_exists($this->ext_path . 'config/' . $container->getParameter('core.environment') . '/container/environment.yml'))
+ {
+ $services_directory = $this->ext_path . 'config/' . $container->getParameter('core.environment') . '/container/';
+ $services_file = 'environment.yml';
+ }
+ else if (!is_dir($this->ext_path . 'config/' . $container->getParameter('core.environment')))
+ {
+ if (file_exists($this->ext_path . 'config/default/container/environment.yml'))
+ {
+ $services_directory = $this->ext_path . 'config/default/container/';
+ $services_file = 'environment.yml';
+ }
+ else if (!is_dir($this->ext_path . 'config/default') && file_exists($this->ext_path . '/config/services.yml'))
+ {
+ $services_directory = $this->ext_path . 'config';
+ $services_file = 'services.yml';
+ }
+ }
+
+ if ($services_directory && $services_file)
+ {
+ $filesystem = new \phpbb\filesystem\filesystem();
+ $loader = new YamlFileLoader($container, new FileLocator($filesystem->realpath($services_directory)));
+ $loader->load($services_file);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConfiguration(array $config, ContainerBuilder $container)
+ {
+ $reflected = new \ReflectionClass($this);
+ $namespace = $reflected->getNamespaceName();
+
+ $class = $namespace . '\\di\configuration';
+ if (class_exists($class))
+ {
+ $r = new \ReflectionClass($class);
+ $container->addResource(new FileResource($r->getFileName()));
+
+ if (!method_exists($class, '__construct'))
+ {
+ $configuration = new $class();
+
+ return $configuration;
+ }
+ }
+
+ }
+
+ /**
+ * Returns the recommended alias to use in XML.
+ *
+ * This alias is also the mandatory prefix to use when using YAML.
+ *
+ * @return string The alias
+ */
+ public function getAlias()
+ {
+ return str_replace('/', '_', $this->extension_name);
+ }
+}
diff --git a/phpBB/phpbb/extension/exception.php b/phpBB/phpbb/extension/exception.php
index 82327c9d5e..9050449bf1 100644
--- a/phpBB/phpbb/extension/exception.php
+++ b/phpBB/phpbb/extension/exception.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,10 +16,6 @@ namespace phpbb\extension;
/**
* Exception class for metadata
*/
-class exception extends \UnexpectedValueException
+class exception extends \phpbb\exception\runtime_exception
{
- public function __toString()
- {
- return $this->getMessage();
- }
}
diff --git a/phpBB/phpbb/extension/extension_interface.php b/phpBB/phpbb/extension/extension_interface.php
index bddff51b5a..6a6b6adb8f 100644
--- a/phpBB/phpbb/extension/extension_interface.php
+++ b/phpBB/phpbb/extension/extension_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,12 +16,17 @@ namespace phpbb\extension;
/**
* The interface extension meta classes have to implement to run custom code
* on enable/disable/purge.
-*
-* @package extension
*/
interface extension_interface
{
/**
+ * Indicate whether or not the extension can be enabled.
+ *
+ * @return bool
+ */
+ public function is_enableable();
+
+ /**
* enable_step is executed on enabling an extension until it returns false.
*
* Calls to this function can be made in subsequent requests, when the
diff --git a/phpBB/phpbb/extension/manager.php b/phpBB/phpbb/extension/manager.php
index 23b281deaa..98d2d27278 100644
--- a/phpBB/phpbb/extension/manager.php
+++ b/phpBB/phpbb/extension/manager.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The extension manager provides means to activate/deactivate extensions.
-*
-* @package extension
*/
class manager
{
@@ -34,26 +36,26 @@ class manager
* Creates a manager and loads information from database
*
* @param ContainerInterface $container A container
- * @param \phpbb\db\driver\driver $db A database connection
- * @param \phpbb\config\config $config \phpbb\config\config
- * @param \phpbb\filesystem $filesystem
+ * @param \phpbb\db\driver\driver_interface $db A database connection
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\filesystem\filesystem_interface $filesystem
* @param string $extension_table The name of the table holding extensions
* @param string $phpbb_root_path Path to the phpbb includes directory.
- * @param string $php_ext php file extension
+ * @param string $php_ext php file extension, defaults to php
* @param \phpbb\cache\driver\driver_interface $cache A cache instance or null
* @param string $cache_name The name of the cache variable, defaults to _ext
*/
- public function __construct(ContainerInterface $container, \phpbb\db\driver\driver $db, \phpbb\config\config $config, \phpbb\filesystem $filesystem, $extension_table, $phpbb_root_path, $php_ext = 'php', \phpbb\cache\driver\driver_interface $cache = null, $cache_name = '_ext')
+ public function __construct(ContainerInterface $container, \phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\filesystem\filesystem_interface $filesystem, $extension_table, $phpbb_root_path, $php_ext = 'php', \phpbb\cache\driver\driver_interface $cache = null, $cache_name = '_ext')
{
+ $this->cache = $cache;
+ $this->cache_name = $cache_name;
+ $this->config = $config;
$this->container = $container;
- $this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
- $this->config = $config;
- $this->cache = $cache;
+ $this->extension_table = $extension_table;
$this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
- $this->extension_table = $extension_table;
- $this->cache_name = $cache_name;
$this->extensions = ($this->cache) ? $this->cache->get($this->cache_name) : false;
@@ -72,11 +74,12 @@ class manager
{
$this->extensions = array();
- // Do not try to load any extensions when installing or updating
+ // Do not try to load any extensions if the extension table
+ // does not exist or when installing or updating.
// Note: database updater invokes this code, and in 3.0
// there is no extension table therefore the rest of this function
// fails
- if (defined('IN_INSTALL'))
+ if (defined('IN_INSTALL') || version_compare($this->config['version'], '3.1.0-dev', '<'))
{
return;
}
@@ -143,7 +146,7 @@ class manager
* Instantiates the metadata manager for the extension with the given name
*
* @param string $name The extension name
- * @param string $template The template manager
+ * @param \phpbb\template\template $template The template manager
* @return \phpbb\extension\metadata_manager Instance of the metadata manager
*/
public function create_extension_metadata_manager($name, \phpbb\template\template $template)
@@ -172,6 +175,12 @@ class manager
$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
$extension = $this->get_extension($name);
+
+ if (!$extension->is_enableable())
+ {
+ return false;
+ }
+
$state = $extension->enable_step($old_state);
$active = ($state === false);
@@ -212,6 +221,11 @@ class manager
$this->cache->purge();
}
+ if ($active)
+ {
+ $this->config->increment('assets_version', 1);
+ }
+
return !$active;
}
@@ -404,11 +418,16 @@ class manager
}
$iterator = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', \FilesystemIterator::NEW_CURRENT_AND_KEY | \FilesystemIterator::FOLLOW_SYMLINKS),
- \RecursiveIteratorIterator::SELF_FIRST);
+ new \phpbb\recursive_dot_prefix_filter_iterator(
+ new \RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/', \FilesystemIterator::NEW_CURRENT_AND_KEY | \FilesystemIterator::FOLLOW_SYMLINKS)
+ ),
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+ $iterator->setMaxDepth(2);
+
foreach ($iterator as $file_info)
{
- if ($file_info->isFile() && $file_info->getFilename() == 'ext.' . $this->php_ext)
+ if ($file_info->isFile() && $file_info->getFilename() == 'composer.json')
{
$ext_name = $iterator->getInnerIterator()->getSubPath();
$composer_file = $iterator->getPath() . '/composer.json';
@@ -442,15 +461,17 @@ class manager
* All enabled and disabled extensions are considered configured. A purged
* extension that is no longer in the database is not configured.
*
+ * @param bool $phpbb_relative Whether the path should be relative to phpbb root
+ *
* @return array An array with extension names as keys and and the
* database stored extension information as values
*/
- public function all_configured()
+ public function all_configured($phpbb_relative = true)
{
$configured = array();
foreach ($this->extensions as $name => $data)
{
- $data['ext_path'] = $this->phpbb_root_path . $data['ext_path'];
+ $data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
$configured[$name] = $data;
}
return $configured;
@@ -458,18 +479,19 @@ class manager
/**
* Retrieves all enabled extensions.
+ * @param bool $phpbb_relative Whether the path should be relative to phpbb root
*
* @return array An array with extension names as keys and and the
* database stored extension information as values
*/
- public function all_enabled()
+ public function all_enabled($phpbb_relative = true)
{
$enabled = array();
foreach ($this->extensions as $name => $data)
{
if ($data['ext_active'])
{
- $enabled[$name] = $this->phpbb_root_path . $data['ext_path'];
+ $enabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
}
}
return $enabled;
@@ -478,17 +500,19 @@ class manager
/**
* Retrieves all disabled extensions.
*
+ * @param bool $phpbb_relative Whether the path should be relative to phpbb root
+ *
* @return array An array with extension names as keys and and the
* database stored extension information as values
*/
- public function all_disabled()
+ public function all_disabled($phpbb_relative = true)
{
$disabled = array();
foreach ($this->extensions as $name => $data)
{
if (!$data['ext_active'])
{
- $disabled[$name] = $this->phpbb_root_path . $data['ext_path'];
+ $disabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path'];
}
}
return $disabled;
@@ -500,7 +524,7 @@ class manager
* @param string $name Extension name to check NOTE: Can be user input
* @return bool Depending on whether or not the extension is available
*/
- public function available($name)
+ public function is_available($name)
{
return file_exists($this->get_extension_path($name, true));
}
@@ -511,18 +535,66 @@ class manager
* @param string $name Extension name to check
* @return bool Depending on whether or not the extension is enabled
*/
- public function enabled($name)
+ public function is_enabled($name)
{
return isset($this->extensions[$name]) && $this->extensions[$name]['ext_active'];
}
/**
- * Instantiates a \phpbb\extension\finder.
+ * Check to see if a given extension is disabled
+ *
+ * @param string $name Extension name to check
+ * @return bool Depending on whether or not the extension is disabled
+ */
+ public function is_disabled($name)
+ {
+ return isset($this->extensions[$name]) && !$this->extensions[$name]['ext_active'];
+ }
+
+ /**
+ * Check to see if a given extension is configured
*
- * @return \phpbb\extension\finder An extension finder instance
+ * All enabled and disabled extensions are considered configured. A purged
+ * extension that is no longer in the database is not configured.
+ *
+ * @param string $name Extension name to check
+ * @return bool Depending on whether or not the extension is configured
*/
- public function get_finder()
+ public function is_configured($name)
{
- return new \phpbb\extension\finder($this, $this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
+ return isset($this->extensions[$name]);
+ }
+
+ /**
+ * Check to see if a given extension is purged
+ *
+ * An extension is purged if it is available, not enabled and not disabled.
+ *
+ * @param string $name Extension name to check
+ * @return bool Depending on whether or not the extension is purged
+ */
+ public function is_purged($name)
+ {
+ return $this->is_available($name) && !$this->is_configured($name);
+ }
+
+ /**
+ * Instantiates a \phpbb\finder.
+ *
+ * @param bool $use_all_available Should we load all extensions, or just enabled ones
+ * @return \phpbb\finder An extension finder instance
+ */
+ public function get_finder($use_all_available = false)
+ {
+ $finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
+ if ($use_all_available)
+ {
+ $finder->set_extensions(array_keys($this->all_available()));
+ }
+ else
+ {
+ $finder->set_extensions(array_keys($this->all_enabled()));
+ }
+ return $finder;
}
}
diff --git a/phpBB/phpbb/extension/metadata_manager.php b/phpBB/phpbb/extension/metadata_manager.php
index 66cdb86513..4f080647c8 100644
--- a/phpBB/phpbb/extension/metadata_manager.php
+++ b/phpBB/phpbb/extension/metadata_manager.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\extension;
/**
* The extension metadata manager validates and gets meta-data for extensions
-*
-* @package extension
*/
class metadata_manager
{
@@ -63,8 +65,8 @@ class metadata_manager
*
* @param string $ext_name Name (including vendor) of the extension
* @param \phpbb\config\config $config phpBB Config instance
- * @param \phpbb\extension\manager $extension_manager An instance of the phpBBb extension manager
- * @param \phpbb\template\template $template phpBB Template instance
+ * @param \phpbb\extension\manager $extension_manager An instance of the phpBB extension manager
+ * @param \phpbb\template\template $template phpBB Template instance
* @param string $phpbb_root_path Path to the phpbb includes directory.
*/
public function __construct($ext_name, \phpbb\config\config $config, \phpbb\extension\manager $extension_manager, \phpbb\template\template $template, $phpbb_root_path)
@@ -80,11 +82,11 @@ class metadata_manager
}
/**
- * Processes and gets the metadata requested
- *
- * @param string $element All for all metadata that it has and is valid, otherwise specify which section you want by its shorthand term.
- * @return array Contains all of the requested metadata, throws an exception on failure
- */
+ * Processes and gets the metadata requested
+ *
+ * @param string $element All for all metadata that it has and is valid, otherwise specify which section you want by its shorthand term.
+ * @return array Contains all of the requested metadata, throws an exception on failure
+ */
public function get_metadata($element = 'all')
{
$this->set_metadata_file();
@@ -126,10 +128,10 @@ class metadata_manager
}
/**
- * Sets the filepath of the metadata file
- *
- * @return boolean Set to true if it exists, throws an exception on failure
- */
+ * Sets the filepath of the metadata file
+ *
+ * @throws \phpbb\extension\exception
+ */
private function set_metadata_file()
{
$ext_filepath = $this->extension_manager->get_extension_path($this->ext_name);
@@ -139,33 +141,35 @@ class metadata_manager
if (!file_exists($this->metadata_file))
{
- throw new \phpbb\extension\exception('The required file does not exist: ' . $this->metadata_file);
+ throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file));
}
}
/**
- * Gets the contents of the composer.json file
- *
- * @return bool True if success, throws an exception on failure
- */
+ * Gets the contents of the composer.json file
+ *
+ * @return bool True if success, throws an exception on failure
+ * @throws \phpbb\extension\exception
+ */
private function fetch_metadata()
{
if (!file_exists($this->metadata_file))
{
- throw new \phpbb\extension\exception('The required file does not exist: ' . $this->metadata_file);
+ throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file));
}
else
{
if (!($file_contents = file_get_contents($this->metadata_file)))
{
- throw new \phpbb\extension\exception('file_get_contents failed on ' . $this->metadata_file);
+ throw new \phpbb\extension\exception('FILE_CONTENT_ERR', array($this->metadata_file));
}
if (($metadata = json_decode($file_contents, true)) === null)
{
- throw new \phpbb\extension\exception('json_decode failed on ' . $this->metadata_file);
+ throw new \phpbb\extension\exception('FILE_JSON_DECODE_ERR', array($this->metadata_file));
}
+ array_walk_recursive($metadata, array($this, 'sanitize_json'));
$this->metadata = $metadata;
return true;
@@ -173,10 +177,21 @@ class metadata_manager
}
/**
- * This array handles the cleaning of the array
+ * Sanitize input from JSON array using htmlspecialchars()
*
- * @return array Contains the cleaned metadata array
+ * @param mixed $value Value of array row
+ * @param string $key Key of array row
*/
+ public function sanitize_json(&$value, $key)
+ {
+ $value = htmlspecialchars($value);
+ }
+
+ /**
+ * This array handles the cleaning of the array
+ *
+ * @return array Contains the cleaned metadata array
+ */
private function clean_metadata_array()
{
return $this->metadata;
@@ -189,6 +204,7 @@ class metadata_manager
* "display" for name, type, and authors
* "name", "type")
* @return Bool True if valid, throws an exception if invalid
+ * @throws \phpbb\extension\exception
*/
public function validate($name = 'display')
{
@@ -196,7 +212,7 @@ class metadata_manager
$fields = array(
'name' => '#^[a-zA-Z0-9_\x7f-\xff]{2,}/[a-zA-Z0-9_\x7f-\xff]{2,}$#',
'type' => '#^phpbb-extension$#',
- 'licence' => '#.+#',
+ 'license' => '#.+#',
'version' => '#.+#',
);
@@ -222,12 +238,12 @@ class metadata_manager
{
if (!isset($this->metadata[$name]))
{
- throw new \phpbb\extension\exception("Required meta field '$name' has not been set.");
+ throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array($name));
}
if (!preg_match($fields[$name], $this->metadata[$name]))
{
- throw new \phpbb\extension\exception("Meta field '$name' is invalid.");
+ throw new \phpbb\extension\exception('META_FIELD_INVALID', array($name));
}
}
break;
@@ -237,22 +253,23 @@ class metadata_manager
}
/**
- * Validates the contents of the authors field
- *
- * @return boolean True when passes validation, throws exception if invalid
- */
+ * Validates the contents of the authors field
+ *
+ * @return boolean True when passes validation, throws exception if invalid
+ * @throws \phpbb\extension\exception
+ */
public function validate_authors()
{
if (empty($this->metadata['authors']))
{
- throw new \phpbb\extension\exception("Required meta field 'authors' has not been set.");
+ throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('authors'));
}
foreach ($this->metadata['authors'] as $author)
{
if (!isset($author['name']))
{
- throw new \phpbb\extension\exception("Required meta field 'author name' has not been set.");
+ throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('author name'));
}
}
@@ -260,10 +277,10 @@ class metadata_manager
}
/**
- * This array handles the verification that this extension can be enabled on this board
- *
- * @return bool True if validation succeeded, False if failed
- */
+ * This array handles the verification that this extension can be enabled on this board
+ *
+ * @return bool True if validation succeeded, False if failed
+ */
public function validate_enable()
{
// Check for valid directory & phpBB, PHP versions
@@ -276,10 +293,10 @@ class metadata_manager
}
/**
- * Validates the most basic directory structure to ensure it follows <vendor>/<ext> convention.
- *
- * @return boolean True when passes validation
- */
+ * Validates the most basic directory structure to ensure it follows <vendor>/<ext> convention.
+ *
+ * @return boolean True when passes validation
+ */
public function validate_dir()
{
return (substr_count($this->ext_name, '/') === 1 && $this->ext_name == $this->get_metadata('name'));
@@ -287,13 +304,13 @@ class metadata_manager
/**
- * Validates the contents of the phpbb requirement field
- *
- * @return boolean True when passes validation
- */
+ * Validates the contents of the phpbb requirement field
+ *
+ * @return boolean True when passes validation
+ */
public function validate_require_phpbb()
{
- if (!isset($this->metadata['require']['phpbb/phpbb']))
+ if (!isset($this->metadata['extra']['soft-require']['phpbb/phpbb']))
{
return false;
}
@@ -302,10 +319,10 @@ class metadata_manager
}
/**
- * Validates the contents of the php requirement field
- *
- * @return boolean True when passes validation
- */
+ * Validates the contents of the php requirement field
+ *
+ * @return boolean True when passes validation
+ */
public function validate_require_php()
{
if (!isset($this->metadata['require']['php']))
@@ -317,58 +334,37 @@ class metadata_manager
}
/**
- * Version validation helper
+ * Outputs the metadata into the template
*
- * @param string $string The string for comparing to a version
- * @param string $current_version The version to compare to
- * @return bool True/False if meets version requirements
+ * @return null
*/
- private function _validate_version($string, $current_version)
- {
- // Allow them to specify their own comparison operator (ex: <3.1.2, >=3.1.0)
- $comparison_matches = false;
- preg_match('#[=<>]+#', $string, $comparison_matches);
-
- if (!empty($comparison_matches))
- {
- return version_compare($current_version, str_replace(array($comparison_matches[0], ' '), '', $string), $comparison_matches[0]);
- }
-
- return version_compare($current_version, $string, '>=');
- }
-
- /**
- * Outputs the metadata into the template
- *
- * @return null
- */
public function output_template_data()
{
$this->template->assign_vars(array(
- 'META_NAME' => htmlspecialchars($this->metadata['name']),
- 'META_TYPE' => htmlspecialchars($this->metadata['type']),
- 'META_DESCRIPTION' => (isset($this->metadata['description'])) ? htmlspecialchars($this->metadata['description']) : '',
+ 'META_NAME' => $this->metadata['name'],
+ 'META_TYPE' => $this->metadata['type'],
+ 'META_DESCRIPTION' => (isset($this->metadata['description'])) ? $this->metadata['description'] : '',
'META_HOMEPAGE' => (isset($this->metadata['homepage'])) ? $this->metadata['homepage'] : '',
- 'META_VERSION' => (isset($this->metadata['version'])) ? htmlspecialchars($this->metadata['version']) : '',
- 'META_TIME' => (isset($this->metadata['time'])) ? htmlspecialchars($this->metadata['time']) : '',
- 'META_LICENCE' => htmlspecialchars($this->metadata['licence']),
+ 'META_VERSION' => (isset($this->metadata['version'])) ? $this->metadata['version'] : '',
+ 'META_TIME' => (isset($this->metadata['time'])) ? $this->metadata['time'] : '',
+ 'META_LICENSE' => $this->metadata['license'],
- 'META_REQUIRE_PHP' => (isset($this->metadata['require']['php'])) ? htmlspecialchars($this->metadata['require']['php']) : '',
+ 'META_REQUIRE_PHP' => (isset($this->metadata['require']['php'])) ? $this->metadata['require']['php'] : '',
'META_REQUIRE_PHP_FAIL' => !$this->validate_require_php(),
- 'META_REQUIRE_PHPBB' => (isset($this->metadata['require']['phpbb/phpbb'])) ? htmlspecialchars($this->metadata['require']['phpbb/phpbb']) : '',
+ 'META_REQUIRE_PHPBB' => (isset($this->metadata['extra']['soft-require']['phpbb/phpbb'])) ? $this->metadata['extra']['soft-require']['phpbb/phpbb'] : '',
'META_REQUIRE_PHPBB_FAIL' => !$this->validate_require_phpbb(),
- 'META_DISPLAY_NAME' => (isset($this->metadata['extra']['display-name'])) ? htmlspecialchars($this->metadata['extra']['display-name']) : '',
+ 'META_DISPLAY_NAME' => (isset($this->metadata['extra']['display-name'])) ? $this->metadata['extra']['display-name'] : '',
));
foreach ($this->metadata['authors'] as $author)
{
$this->template->assign_block_vars('meta_authors', array(
- 'AUTHOR_NAME' => htmlspecialchars($author['name']),
+ 'AUTHOR_NAME' => $author['name'],
'AUTHOR_EMAIL' => (isset($author['email'])) ? $author['email'] : '',
'AUTHOR_HOMEPAGE' => (isset($author['homepage'])) ? $author['homepage'] : '',
- 'AUTHOR_ROLE' => (isset($author['role'])) ? htmlspecialchars($author['role']) : '',
+ 'AUTHOR_ROLE' => (isset($author['role'])) ? $author['role'] : '',
));
}
}
diff --git a/phpBB/phpbb/extension/provider.php b/phpBB/phpbb/extension/provider.php
index bfdc2b66b9..1c42cf7b5e 100644
--- a/phpBB/phpbb/extension/provider.php
+++ b/phpBB/phpbb/extension/provider.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -19,8 +23,6 @@ namespace phpbb\extension;
*
* Items could be anything, for example template paths or cron task names.
* Derived classes completely define what the items are.
-*
-* @package extension
*/
abstract class provider implements \IteratorAggregate
{
@@ -56,7 +58,7 @@ abstract class provider implements \IteratorAggregate
/**
* Retrieve an iterator over all items
*
- * @return ArrayIterator An iterator for the array of template paths
+ * @return \ArrayIterator An iterator for the array of template paths
*/
public function getIterator()
{
diff --git a/phpBB/phpbb/feed/attachments_base.php b/phpBB/phpbb/feed/attachments_base.php
new file mode 100644
index 0000000000..b14dafe15a
--- /dev/null
+++ b/phpBB/phpbb/feed/attachments_base.php
@@ -0,0 +1,85 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\feed;
+
+/**
+* Abstract class for feeds displaying attachments
+*/
+abstract class attachments_base extends base
+{
+ /**
+ * Attachments that may be displayed
+ */
+ protected $attachments = array();
+
+ /**
+ * Retrieve the list of attachments that may be displayed
+ */
+ protected function fetch_attachments()
+ {
+ $sql_array = array(
+ 'SELECT' => 'a.*',
+ 'FROM' => array(
+ ATTACHMENTS_TABLE => 'a'
+ ),
+ 'WHERE' => 'a.in_message = 0 ',
+ 'ORDER_BY' => 'a.filetime DESC, a.post_msg_id ASC',
+ );
+
+ if (isset($this->topic_id))
+ {
+ $sql_array['WHERE'] .= 'AND a.topic_id = ' . (int) $this->topic_id;
+ }
+ else if (isset($this->forum_id))
+ {
+ $sql_array['LEFT_JOIN'] = array(
+ array(
+ 'FROM' => array(TOPICS_TABLE => 't'),
+ 'ON' => 'a.topic_id = t.topic_id',
+ )
+ );
+ $sql_array['WHERE'] .= 'AND t.forum_id = ' . (int) $this->forum_id;
+ }
+
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+ $result = $this->db->sql_query($sql);
+
+ // Set attachments in feed items
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->attachments[$row['post_msg_id']][] = $row;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function open()
+ {
+ parent::open();
+ $this->fetch_attachments();
+ }
+
+ /**
+ * Get attachments related to a given post
+ *
+ * @param $post_id int Post id
+ * @return mixed Attachments related to $post_id
+ */
+ public function get_attachments($post_id)
+ {
+ return $this->attachments[$post_id];
+ }
+}
diff --git a/phpBB/phpbb/feed/base.php b/phpBB/phpbb/feed/base.php
index e6c1e606fa..188d229515 100644
--- a/phpBB/phpbb/feed/base.php
+++ b/phpBB/phpbb/feed/base.php
@@ -1,31 +1,33 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Base class with some generic functions and settings.
-*
-* @package phpBB3
-*/
-abstract class base
+ * Base class with some generic functions and settings.
+ */
+abstract class base implements feed_interface
{
/**
- * Feed helper object
- * @var \phpbb\feed\helper
- */
+ * Feed helper object
+ * @var \phpbb\feed\helper
+ */
protected $helper;
/** @var \phpbb\config\config */
protected $config;
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var \phpbb\cache\driver\driver_interface */
@@ -41,44 +43,46 @@ abstract class base
protected $phpEx;
/**
- * SQL Query to be executed to get feed items
- */
- var $sql = array();
+ * SQL Query to be executed to get feed items
+ */
+ protected $sql = array();
/**
- * Keys specified for retrieval of title, content, etc.
- */
- var $keys = array();
+ * Keys specified for retrieval of title, content, etc.
+ */
+ protected $keys = array();
/**
- * Number of items to fetch. Usually overwritten by $config['feed_something']
- */
- var $num_items = 15;
+ * Number of items to fetch. Usually overwritten by $config['feed_something']
+ */
+ protected $num_items = 15;
/**
- * Separator for title elements to separate items (for example forum / topic)
- */
- var $separator = "\xE2\x80\xA2"; // &bull;
+ * Separator for title elements to separate items (for example forum / topic)
+ */
+ protected $separator = "\xE2\x80\xA2"; // &bull;
/**
- * Separator for the statistics row (Posted by, post date, replies, etc.)
- */
- var $separator_stats = "\xE2\x80\x94"; // &mdash;
+ * Separator for the statistics row (Posted by, post date, replies, etc.)
+ */
+ protected $separator_stats = "\xE2\x80\x94"; // &mdash;
+
+ /** @var mixed Query result handle */
+ protected $result;
/**
- * Constructor
- *
- * @param \phpbb\feed\helper $helper Feed helper
- * @param \phpbb\config\config $config Config object
- * @param \phpbb\db\driver\driver $db Database connection
- * @param \phpbb\cache\driver\driver_interface $cache Cache object
- * @param \phpbb\user $user User object
- * @param \phpbb\auth\auth $auth Auth object
- * @param \phpbb\content_visibility $content_visibility Auth object
- * @param string $phpEx php file extension
- * @return null
- */
- function __construct(\phpbb\feed\helper $helper, \phpbb\config\config $config, \phpbb\db\driver\driver $db, \phpbb\cache\driver\driver_interface $cache, \phpbb\user $user, \phpbb\auth\auth $auth, \phpbb\content_visibility $content_visibility, $phpEx)
+ * Constructor
+ *
+ * @param \phpbb\feed\helper $helper Feed helper
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param \phpbb\cache\driver\driver_interface $cache Cache object
+ * @param \phpbb\user $user User object
+ * @param \phpbb\auth\auth $auth Auth object
+ * @param \phpbb\content_visibility $content_visibility Auth object
+ * @param string $phpEx php file extension
+ */
+ public function __construct(\phpbb\feed\helper $helper, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache, \phpbb\user $user, \phpbb\auth\auth $auth, \phpbb\content_visibility $content_visibility, $phpEx)
{
$this->config = $config;
$this->helper = $helper;
@@ -105,23 +109,23 @@ abstract class base
}
/**
- * Set keys.
- */
- function set_keys()
+ * {@inheritdoc}
+ */
+ public function set_keys()
{
}
/**
- * Open feed
- */
- function open()
+ * {@inheritdoc}
+ */
+ public function open()
{
}
/**
- * Close feed
- */
- function close()
+ * {@inheritdoc}
+ */
+ public function close()
{
if (!empty($this->result))
{
@@ -130,22 +134,47 @@ abstract class base
}
/**
- * Set key
- */
- function set($key, $value)
+ * {@inheritdoc}
+ */
+ public function set($key, $value)
{
$this->keys[$key] = $value;
}
/**
- * Get key
- */
- function get($key)
+ * {@inheritdoc}
+ */
+ public function get($key)
{
return (isset($this->keys[$key])) ? $this->keys[$key] : null;
}
- function get_readable_forums()
+ /**
+ * {@inheritdoc}
+ */
+ public function get_item()
+ {
+ if (!isset($this->result))
+ {
+ if (!$this->get_sql())
+ {
+ return false;
+ }
+
+ // Query database
+ $sql = $this->db->sql_build_query('SELECT', $this->sql);
+ $this->result = $this->db->sql_query_limit($sql, $this->num_items);
+ }
+
+ return $this->db->sql_fetchrow($this->result);
+ }
+
+ /**
+ * Returns the ids of the forums readable by the current user.
+ *
+ * @return int[]
+ */
+ protected function get_readable_forums()
{
static $forum_ids;
@@ -157,7 +186,12 @@ abstract class base
return $forum_ids;
}
- function get_moderator_approve_forums()
+ /**
+ * Returns the ids of the forum for which the current user can approve the post in the moderation queue.
+ *
+ * @return int[]
+ */
+ protected function get_moderator_approve_forums()
{
static $forum_ids;
@@ -169,7 +203,13 @@ abstract class base
return $forum_ids;
}
- function is_moderator_approve_forum($forum_id)
+ /**
+ * Returns true if the current user can approve the post of the given forum
+ *
+ * @param int $forum_id Forum id to check
+ * @return bool
+ */
+ protected function is_moderator_approve_forum($forum_id)
{
static $forum_ids;
@@ -181,7 +221,12 @@ abstract class base
return (isset($forum_ids[$forum_id])) ? true : false;
}
- function get_excluded_forums()
+ /**
+ * Returns the ids of the forum excluded from the feeds
+ *
+ * @return int[]
+ */
+ protected function get_excluded_forums()
{
static $forum_ids;
@@ -208,38 +253,35 @@ abstract class base
return $forum_ids;
}
- function is_excluded_forum($forum_id)
+ /**
+ * Returns true if the given id is in the excluded forums list.
+ *
+ * @param int $forum_id Id to check
+ * @return bool
+ */
+ protected function is_excluded_forum($forum_id)
{
$forum_ids = $this->get_excluded_forums();
return isset($forum_ids[$forum_id]) ? true : false;
}
- function get_passworded_forums()
+ /**
+ * Returns all password protected forum ids the current user is currently NOT authenticated for.
+ *
+ * @return array Array of forum ids
+ */
+ protected function get_passworded_forums()
{
return $this->user->get_passworded_forums();
}
- function get_item()
- {
- static $result;
-
- if (!isset($result))
- {
- if (!$this->get_sql())
- {
- return false;
- }
-
- // Query database
- $sql = $this->db->sql_build_query('SELECT', $this->sql);
- $result = $this->db->sql_query_limit($sql, $this->num_items);
- }
-
- return $this->db->sql_fetchrow($result);
- }
-
- function user_viewprofile($row)
+ /**
+ * Returns the link to the user profile.
+ *
+ * @return string
+ */
+ protected function user_viewprofile($row)
{
$author_id = (int) $row[$this->get('author_id')];
@@ -252,4 +294,11 @@ abstract class base
return '<a href="' . $this->helper->append_sid('memberlist.' . $this->phpEx, 'mode=viewprofile&amp;u=' . $author_id) . '">' . $row[$this->get('creator')] . '</a>';
}
+
+ /**
+ * Returns the SQL query used to retrieve the posts of the feed.
+ *
+ * @return string SQL SELECT query
+ */
+ protected abstract function get_sql();
}
diff --git a/phpBB/phpbb/feed/controller/feed.php b/phpBB/phpbb/feed/controller/feed.php
new file mode 100644
index 0000000000..31476b7317
--- /dev/null
+++ b/phpBB/phpbb/feed/controller/feed.php
@@ -0,0 +1,389 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\feed\controller;
+
+use phpbb\auth\auth;
+use phpbb\config\config;
+use phpbb\db\driver\driver_interface;
+use phpbb\exception\http_exception;
+use phpbb\feed\feed_interface;
+use phpbb\feed\exception\feed_unavailable_exception;
+use phpbb\feed\exception\unauthorized_exception;
+use phpbb\feed\helper as feed_helper;
+use phpbb\controller\helper as controller_helper;
+use phpbb\symfony_request;
+use phpbb\user;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
+class feed
+{
+ /**
+ * @var \Twig_Environment
+ */
+ protected $template;
+
+ /**
+ * @var symfony_request
+ */
+ protected $request;
+
+ /**
+ * @var controller_helper
+ */
+ protected $controller_helper;
+
+ /**
+ * @var config
+ */
+ protected $config;
+
+ /**
+ * @var driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * @var feed_helper
+ */
+ protected $feed_helper;
+
+ /**
+ * @var user
+ */
+ protected $user;
+
+ /**
+ * @var auth
+ */
+ protected $auth;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \Twig_Environment $twig
+ * @param symfony_request $request
+ * @param controller_helper $controller_helper
+ * @param config $config
+ * @param driver_interface $db
+ * @param ContainerInterface $container
+ * @param feed_helper $feed_helper
+ * @param user $user
+ * @param auth $auth
+ * @param string $php_ext
+ */
+ public function __construct(\Twig_Environment $twig, symfony_request $request, controller_helper $controller_helper, config $config, driver_interface $db, ContainerInterface $container, feed_helper $feed_helper, user $user, auth $auth, $php_ext)
+ {
+ $this->request = $request;
+ $this->controller_helper = $controller_helper;
+ $this->config = $config;
+ $this->db = $db;
+ $this->container = $container;
+ $this->feed_helper = $feed_helper;
+ $this->user = $user;
+ $this->auth = $auth;
+ $this->php_ext = $php_ext;
+ $this->template = $twig;
+ }
+
+ /**
+ * Controller for /feed/forums route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function forums()
+ {
+ if (!$this->config['feed_overall_forums'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.forums'));
+ }
+
+ /**
+ * Controller for /feed/news route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function news()
+ {
+ // Get at least one news forum
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0');
+ $result = $this->db->sql_query_limit($sql, 1, 0, 600);
+ $s_feed_news = (int) $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$s_feed_news)
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.news'));
+ }
+
+ /**
+ * Controller for /feed/topics route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function topics()
+ {
+ if (!$this->config['feed_topics_new'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.topics'));
+ }
+
+ /**
+ * Controller for /feed/topics_new route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function topics_new()
+ {
+ return $this->topics();
+ }
+
+ /**
+ * Controller for /feed/topics_active route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function topics_active()
+ {
+ if (!$this->config['feed_topics_active'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.topics_active'));
+ }
+
+ /**
+ * Controller for /feed/forum/{forum_id} route
+ *
+ * @param int $forum_id
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function forum($forum_id)
+ {
+ if (!$this->config['feed_forum'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.forum')->set_forum_id($forum_id));
+ }
+
+ /**
+ * Controller for /feed/topic/{topic_id} route
+ *
+ * @param int $topic_id
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function topic($topic_id)
+ {
+ if (!$this->config['feed_topic'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.topic')->set_topic_id($topic_id));
+ }
+
+ /**
+ * Controller for /feed/{mode] route
+ *
+ * @return Response
+ *
+ * @throws http_exception when the feed is disabled
+ */
+ public function overall()
+ {
+ if (!$this->config['feed_overall'])
+ {
+ $this->send_unavailable();
+ }
+
+ return $this->send_feed($this->container->get('feed.overall'));
+ }
+
+ /**
+ * Display a given feed
+ *
+ * @param feed_interface $feed
+ *
+ * @return Response
+ */
+ protected function send_feed(feed_interface $feed)
+ {
+ try
+ {
+ return $this->send_feed_do($feed);
+ }
+ catch (feed_unavailable_exception $e)
+ {
+ throw new http_exception(Response::HTTP_NOT_FOUND, $e->getMessage(), $e->get_parameters(), $e);
+ }
+ catch (unauthorized_exception $e)
+ {
+ throw new http_exception(Response::HTTP_FORBIDDEN, $e->getMessage(), $e->get_parameters(), $e);
+ }
+ }
+
+ /**
+ * Really send the feed
+ *
+ * @param feed_interface $feed
+ *
+ * @return Response
+ *
+ * @throw exception\feed_exception
+ */
+ protected function send_feed_do(feed_interface $feed)
+ {
+ $feed_updated_time = 0;
+ $item_vars = array();
+
+ $board_url = $this->feed_helper->get_board_url();
+
+ // Open Feed
+ $feed->open();
+
+ // Iterate through items
+ while ($row = $feed->get_item())
+ {
+ // BBCode options to correctly disable urls, smilies, bbcode...
+ if ($feed->get('options') === null)
+ {
+ // Allow all combinations
+ $options = 7;
+
+ if ($feed->get('enable_bbcode') !== null && $feed->get('enable_smilies') !== null && $feed->get('enable_magic_url') !== null)
+ {
+ $options = (($row[$feed->get('enable_bbcode')]) ? OPTION_FLAG_BBCODE : 0) + (($row[$feed->get('enable_smilies')]) ? OPTION_FLAG_SMILIES : 0) + (($row[$feed->get('enable_magic_url')]) ? OPTION_FLAG_LINKS : 0);
+ }
+ }
+ else
+ {
+ $options = $row[$feed->get('options')];
+ }
+
+ $title = (isset($row[$feed->get('title')]) && $row[$feed->get('title')] !== '') ? $row[$feed->get('title')] : ((isset($row[$feed->get('title2')])) ? $row[$feed->get('title2')] : '');
+
+ $published = ($feed->get('published') !== null) ? (int) $row[$feed->get('published')] : 0;
+ $updated = ($feed->get('updated') !== null) ? (int) $row[$feed->get('updated')] : 0;
+
+ $display_attachments = ($this->auth->acl_get('u_download') && $this->auth->acl_get('f_download', $row['forum_id']) && isset($row['post_attachment']) && $row['post_attachment']) ? true : false;
+
+ $item_row = array(
+ 'author' => ($feed->get('creator') !== null) ? $row[$feed->get('creator')] : '',
+ 'published' => ($published > 0) ? $this->feed_helper->format_date($published) : '',
+ 'updated' => ($updated > 0) ? $this->feed_helper->format_date($updated) : '',
+ 'link' => '',
+ 'title' => censor_text($title),
+ 'category' => ($this->config['feed_item_statistics'] && !empty($row['forum_id'])) ? $board_url . '/viewforum.' . $this->php_ext . '?f=' . $row['forum_id'] : '',
+ 'category_name' => ($this->config['feed_item_statistics'] && isset($row['forum_name'])) ? $row['forum_name'] : '',
+ 'description' => censor_text($this->feed_helper->generate_content($row[$feed->get('text')], $row[$feed->get('bbcode_uid')], $row[$feed->get('bitfield')], $options, $row['forum_id'], ($display_attachments ? $feed->get_attachments($row['post_id']) : array()))),
+ 'statistics' => '',
+ );
+
+ // Adjust items, fill link, etc.
+ $feed->adjust_item($item_row, $row);
+
+ $item_vars[] = $item_row;
+
+ $feed_updated_time = max($feed_updated_time, $published, $updated);
+ }
+
+ // If we do not have any items at all, sending the current time is better than sending no time.
+ if (!$feed_updated_time)
+ {
+ $feed_updated_time = time();
+ }
+
+ $feed->close();
+
+ $content = $this->template->render('feed.xml.twig', array(
+ // Some default assignments
+ // FEED_IMAGE is not used (atom)
+ 'FEED_IMAGE' => '',
+ 'SELF_LINK' => $this->controller_helper->route($this->request->attributes->get('_route'), $this->request->attributes->get('_route_params'), true, '', UrlGeneratorInterface::ABSOLUTE_URL),
+ 'FEED_LINK' => $board_url . '/index.' . $this->php_ext,
+ 'FEED_TITLE' => $this->config['sitename'],
+ 'FEED_SUBTITLE' => $this->config['site_desc'],
+ 'FEED_UPDATED' => $this->feed_helper->format_date($feed_updated_time),
+ 'FEED_LANG' => $this->user->lang['USER_LANG'],
+ 'FEED_AUTHOR' => $this->config['sitename'],
+
+ // Feed entries
+ 'FEED_ROWS' => $item_vars,
+ ));
+
+ $response = new Response($content);
+ $response->headers->set('Content-Type', 'application/atom+xml');
+ $response->setCharset('UTF-8');
+ $response->setLastModified(new \DateTime('@' . $feed_updated_time));
+
+ if (!empty($this->user->data['is_bot']))
+ {
+ // Let reverse proxies know we detected a bot.
+ $response->headers->set('X-PHPBB-IS-BOT', 'yes');
+ }
+
+ return $response;
+ }
+
+ /**
+ * Throw and exception saying that the feed isn't available
+ *
+ * @throw http_exception
+ */
+ protected function send_unavailable()
+ {
+ throw new http_exception(404, 'FEATURE_NOT_AVAILABLE');
+ }
+}
diff --git a/phpBB/phpbb/feed/exception/feed_exception.php b/phpBB/phpbb/feed/exception/feed_exception.php
new file mode 100644
index 0000000000..c9c888211e
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/feed_exception.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+use phpbb\exception\runtime_exception;
+
+abstract class feed_exception extends runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/feed/exception/feed_unavailable_exception.php b/phpBB/phpbb/feed/exception/feed_unavailable_exception.php
new file mode 100644
index 0000000000..4b6605b47d
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/feed_unavailable_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+abstract class feed_unavailable_exception extends feed_exception
+{
+
+}
diff --git a/phpBB/phpbb/feed/exception/no_feed_exception.php b/phpBB/phpbb/feed/exception/no_feed_exception.php
new file mode 100644
index 0000000000..af6357b74c
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/no_feed_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+class no_feed_exception extends feed_unavailable_exception
+{
+ public function __construct(\Exception $previous = null, $code = 0)
+ {
+ parent::__construct('NO_FEED', array(), $previous, $code);
+ }
+}
diff --git a/phpBB/phpbb/feed/exception/no_forum_exception.php b/phpBB/phpbb/feed/exception/no_forum_exception.php
new file mode 100644
index 0000000000..a60832957a
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/no_forum_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+class no_forum_exception extends feed_unavailable_exception
+{
+ public function __construct($forum_id, \Exception $previous = null, $code = 0)
+ {
+ parent::__construct('NO_FORUM', array($forum_id), $previous, $code);
+ }
+}
diff --git a/phpBB/phpbb/feed/exception/no_topic_exception.php b/phpBB/phpbb/feed/exception/no_topic_exception.php
new file mode 100644
index 0000000000..b961a65d1c
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/no_topic_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+class no_topic_exception extends feed_unavailable_exception
+{
+ public function __construct($topic_id, \Exception $previous = null, $code = 0)
+ {
+ parent::__construct('NO_TOPIC', array($topic_id), $previous, $code);
+ }
+}
diff --git a/phpBB/phpbb/feed/exception/unauthorized_exception.php b/phpBB/phpbb/feed/exception/unauthorized_exception.php
new file mode 100644
index 0000000000..7868975779
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/unauthorized_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+abstract class unauthorized_exception extends feed_exception
+{
+
+}
diff --git a/phpBB/phpbb/feed/exception/unauthorized_forum_exception.php b/phpBB/phpbb/feed/exception/unauthorized_forum_exception.php
new file mode 100644
index 0000000000..4384c7b39b
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/unauthorized_forum_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+class unauthorized_forum_exception extends unauthorized_exception
+{
+ public function __construct($forum_id, \Exception $previous = null, $code = 0)
+ {
+ parent::__construct('SORRY_AUTH_READ', array($forum_id), $previous, $code);
+ }
+}
diff --git a/phpBB/phpbb/feed/exception/unauthorized_topic_exception.php b/phpBB/phpbb/feed/exception/unauthorized_topic_exception.php
new file mode 100644
index 0000000000..f49f0a0476
--- /dev/null
+++ b/phpBB/phpbb/feed/exception/unauthorized_topic_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed\exception;
+
+class unauthorized_topic_exception extends unauthorized_exception
+{
+ public function __construct($topic_id, \Exception $previous = null, $code = 0)
+ {
+ parent::__construct('SORRY_AUTH_READ_TOPIC', array($topic_id), $previous, $code);
+ }
+}
diff --git a/phpBB/phpbb/feed/factory.php b/phpBB/phpbb/feed/factory.php
deleted file mode 100644
index d370160563..0000000000
--- a/phpBB/phpbb/feed/factory.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
-
-namespace phpbb\feed;
-
-/**
-* Factory class to return correct object
-* @package phpBB3
-*/
-class factory
-{
- /**
- * Service container object
- * @var object
- */
- protected $container;
-
- /** @var \phpbb\config\config */
- protected $config;
-
- /** @var \phpbb\db\driver\driver */
- protected $db;
-
- /**
- * Constructor
- *
- * @param objec $container Container object
- * @param \phpbb\config\config $config Config object
- * @param \phpbb\db\driver\driver $db Database connection
- * @return null
- */
- public function __construct($container, \phpbb\config\config $config, \phpbb\db\driver\driver $db)
- {
- $this->container = $container;
- $this->config = $config;
- $this->db = $db;
- }
-
- /**
- * Return correct object for specified mode
- *
- * @param string $mode The feeds mode.
- * @param int $forum_id Forum id specified by the script if forum feed provided.
- * @param int $topic_id Topic id specified by the script if topic feed provided.
- *
- * @return object Returns correct feeds object for specified mode.
- */
- function get_feed($mode, $forum_id, $topic_id)
- {
- switch ($mode)
- {
- case 'forums':
- if (!$this->config['feed_overall_forums'])
- {
- return false;
- }
-
- return $this->container->get('feed.forums');
- break;
-
- case 'topics':
- case 'topics_new':
- if (!$this->config['feed_topics_new'])
- {
- return false;
- }
-
- return $this->container->get('feed.topics');
- break;
-
- case 'topics_active':
- if (!$this->config['feed_topics_active'])
- {
- return false;
- }
-
- return $this->container->get('feed.topics_active');
- break;
-
- case 'news':
- // Get at least one news forum
- $sql = 'SELECT forum_id
- FROM ' . FORUMS_TABLE . '
- WHERE ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0');
- $result = $this->db->sql_query_limit($sql, 1, 0, 600);
- $s_feed_news = (int) $this->db->sql_fetchfield('forum_id');
- $this->db->sql_freeresult($result);
-
- if (!$s_feed_news)
- {
- return false;
- }
-
- return $this->container->get('feed.news');
- break;
-
- default:
- if ($topic_id && $this->config['feed_topic'])
- {
- return $this->container->get('feed.topic')
- ->set_topic_id($topic_id);
- }
- else if ($forum_id && $this->config['feed_forum'])
- {
- return $this->container->get('feed.forum')
- ->set_forum_id($forum_id);
- }
- else if ($this->config['feed_overall'])
- {
- return $this->container->get('feed.overall');
- }
-
- return false;
- break;
- }
- }
-}
diff --git a/phpBB/phpbb/feed/feed_interface.php b/phpBB/phpbb/feed/feed_interface.php
new file mode 100644
index 0000000000..c185cd249c
--- /dev/null
+++ b/phpBB/phpbb/feed/feed_interface.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\feed;
+
+/**
+ * Interface implemented by all feeds types
+ */
+interface feed_interface
+{
+ /**
+ * Set keys.
+ */
+ public function set_keys();
+
+ /**
+ * Open feed
+ */
+ public function open();
+
+ /**
+ * Close feed
+ */
+ public function close();
+
+ /**
+ * Set key
+ *
+ * @param string $key Key
+ * @param mixed $value Value
+ */
+ public function set($key, $value);
+
+ /**
+ * Get key
+ *
+ * @param string $key Key
+ * @return mixed
+ */
+ public function get($key);
+
+ /**
+ * Get the next post in the feed
+ *
+ * @return array
+ */
+ public function get_item();
+
+ /**
+ * Adjust a feed entry
+ *
+ * @param $item_row
+ * @param $row
+ * @return array
+ */
+ public function adjust_item(&$item_row, &$row);
+}
diff --git a/phpBB/phpbb/feed/forum.php b/phpBB/phpbb/feed/forum.php
index 8026824ab7..6701c4d9e7 100644
--- a/phpBB/phpbb/feed/forum.php
+++ b/phpBB/phpbb/feed/forum.php
@@ -1,33 +1,39 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
+use phpbb\feed\exception\no_feed_exception;
+use phpbb\feed\exception\no_forum_exception;
+use phpbb\feed\exception\unauthorized_forum_exception;
+
/**
-* Forum feed
-*
-* This will give you the last {$this->num_items} posts made
-* within a specific forum.
-*
-* @package phpBB3
-*/
-class forum extends \phpbb\feed\post_base
+ * Forum feed
+ *
+ * This will give you the last {$this->num_items} posts made
+ * within a specific forum.
+ */
+class forum extends post_base
{
- var $forum_id = 0;
- var $forum_data = array();
+ protected $forum_id = 0;
+ protected $forum_data = array();
/**
- * Set the Forum ID
- *
- * @param int $forum_id Forum ID
- * @return \phpbb\feed\forum
- */
+ * Set the Forum ID
+ *
+ * @param int $forum_id Forum ID
+ * @return \phpbb\feed\forum
+ */
public function set_forum_id($forum_id)
{
$this->forum_id = (int) $forum_id;
@@ -35,7 +41,10 @@ class forum extends \phpbb\feed\post_base
return $this;
}
- function open()
+ /**
+ * {@inheritdoc}
+ */
+ public function open()
{
// Check if forum exists
$sql = 'SELECT forum_id, forum_name, forum_password, forum_type, forum_options
@@ -47,25 +56,25 @@ class forum extends \phpbb\feed\post_base
if (empty($this->forum_data))
{
- trigger_error('NO_FORUM');
+ throw new no_forum_exception($this->forum_id);
}
// Forum needs to be postable
if ($this->forum_data['forum_type'] != FORUM_POST)
{
- trigger_error('NO_FEED');
+ throw new no_feed_exception();
}
// Make sure forum is not excluded from feed
if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->forum_data['forum_options']))
{
- trigger_error('NO_FEED');
+ throw new no_feed_exception();
}
// Make sure we can read this forum
if (!$this->auth->acl_get('f_read', $this->forum_id))
{
- trigger_error('SORRY_AUTH_READ');
+ throw new unauthorized_forum_exception($this->forum_id);
}
// Make sure forum is not passworded or user is authed
@@ -75,14 +84,19 @@ class forum extends \phpbb\feed\post_base
if (isset($forum_ids_passworded[$this->forum_id]))
{
- trigger_error('SORRY_AUTH_READ');
+ throw new unauthorized_forum_exception($this->forum_id);
}
unset($forum_ids_passworded);
}
+
+ parent::open();
}
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
// Determine topics with recent activity
$sql = 'SELECT topic_id, topic_last_post_time
@@ -90,7 +104,7 @@ class forum extends \phpbb\feed\post_base
WHERE forum_id = ' . $this->forum_id . '
AND topic_moved_id = 0
AND ' . $this->content_visibility->get_visibility_sql('topic', $this->forum_id) . '
- ORDER BY topic_last_post_time DESC';
+ ORDER BY topic_last_post_time DESC, topic_last_post_id DESC';
$result = $this->db->sql_query_limit($sql, $this->num_items);
$topic_ids = array();
@@ -109,8 +123,8 @@ class forum extends \phpbb\feed\post_base
}
$this->sql = array(
- 'SELECT' => 'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' .
- 'u.username, u.user_id',
+ 'SELECT' => 'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, ' .
+ 'u.username, u.user_id',
'FROM' => array(
POSTS_TABLE => 'p',
USERS_TABLE => 'u',
@@ -119,20 +133,27 @@ class forum extends \phpbb\feed\post_base
AND ' . $this->content_visibility->get_visibility_sql('post', $this->forum_id, 'p.') . '
AND p.post_time >= ' . $min_post_time . '
AND p.poster_id = u.user_id',
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
parent::adjust_item($item_row, $row);
$item_row['title'] = (isset($row['forum_name']) && $row['forum_name'] !== '') ? $row['forum_name'] . ' ' . $this->separator . ' ' . $item_row['title'] : $item_row['title'];
+ $item_row['forum_id'] = $this->forum_id;
}
- function get_item()
+ /**
+ * {@inheritdoc}
+ */
+ public function get_item()
{
return ($row = parent::get_item()) ? array_merge($this->forum_data, $row) : $row;
}
diff --git a/phpBB/phpbb/feed/forums.php b/phpBB/phpbb/feed/forums.php
index ddbb0bf7b3..92f2b2dd4d 100644
--- a/phpBB/phpbb/feed/forums.php
+++ b/phpBB/phpbb/feed/forums.php
@@ -1,27 +1,32 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* 'All Forums' feed
-*
-* This will give you a list of all postable forums where feeds are enabled
-* including forum description, topic stats and post stats
-*
-* @package phpBB3
-*/
-class forums extends \phpbb\feed\base
+ * 'All Forums' feed
+ *
+ * This will give you a list of all postable forums where feeds are enabled
+ * including forum description, topic stats and post stats
+ */
+class forums extends base
{
- var $num_items = 0;
+ protected $num_items = 0;
- function set_keys()
+ /**
+ * {@inheritdoc}
+ */
+ public function set_keys()
{
$this->set('title', 'forum_name');
$this->set('text', 'forum_desc');
@@ -31,7 +36,10 @@ class forums extends \phpbb\feed\base
$this->set('options', 'forum_desc_options');
}
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ public function get_sql()
{
$in_fid_ary = array_diff($this->get_readable_forums(), $this->get_excluded_forums());
if (empty($in_fid_ary))
@@ -53,7 +61,10 @@ class forums extends \phpbb\feed\base
return true;
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
$item_row['link'] = $this->helper->append_sid('viewforum.' . $this->phpEx, 'f=' . $row['forum_id']);
diff --git a/phpBB/phpbb/feed/helper.php b/phpBB/phpbb/feed/helper.php
index 3f2759b85e..e15d1e131e 100644
--- a/phpBB/phpbb/feed/helper.php
+++ b/phpBB/phpbb/feed/helper.php
@@ -1,18 +1,21 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Class with some helpful functions used in feeds
-* @package phpBB3
-*/
+ * Class with some helpful functions used in feeds
+ */
class helper
{
/** @var \phpbb\config\config */
@@ -24,24 +27,28 @@ class helper
/** @var string */
protected $phpbb_root_path;
+ /** @var string */
+ protected $phpEx;
+
/**
- * Constructor
- *
- * @param \phpbb\config\config $config Config object
- * @param \phpbb\user $user User object
- * @param string $phpbb_root_path Root path
- * @return null
- */
- public function __construct(\phpbb\config\config $config, \phpbb\user $user, $phpbb_root_path)
+ * Constructor
+ *
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\user $user User object
+ * @param string $phpbb_root_path Root path
+ * @param string $phpEx PHP file extension
+ */
+ public function __construct(\phpbb\config\config $config, \phpbb\user $user, $phpbb_root_path, $phpEx)
{
$this->config = $config;
$this->user = $user;
$this->phpbb_root_path = $phpbb_root_path;
+ $this->phpEx = $phpEx;
}
/**
- * Run links through append_sid(), prepend generate_board_url() and remove session id
- */
+ * Run links through append_sid(), prepend generate_board_url() and remove session id
+ */
public function get_board_url()
{
static $board_url;
@@ -55,16 +62,16 @@ class helper
}
/**
- * Run links through append_sid(), prepend generate_board_url() and remove session id
- */
+ * Run links through append_sid(), prepend generate_board_url() and remove session id
+ */
public function append_sid($url, $params)
{
return append_sid($this->get_board_url() . '/' . $url, $params, true, '');
}
/**
- * Generate ISO 8601 date string (RFC 3339)
- */
+ * Generate ISO 8601 date string (RFC 3339)
+ */
public function format_date($time)
{
static $zone_offset;
@@ -80,9 +87,17 @@ class helper
}
/**
- * Generate text content
- */
- public function generate_content($content, $uid, $bitfield, $options)
+ * Generate text content
+ *
+ * @param string $content is feed text content
+ * @param string $uid is bbcode_uid
+ * @param string $bitfield is bbcode bitfield
+ * @param int $options bbcode flag options
+ * @param int $forum_id is the forum id
+ * @param array $post_attachments is an array containing the attachments and their respective info
+ * @return string the html content to be printed for the feed
+ */
+ public function generate_content($content, $uid, $bitfield, $options, $forum_id, $post_attachments)
{
if (empty($content))
{
@@ -107,16 +122,16 @@ class helper
// Firefox does not support CSS for feeds, though
// Remove font sizes
- // $content = preg_replace('#<span style="font-size: [0-9]+%; line-height: [0-9]+%;">([^>]+)</span>#iU', '\1', $content);
+ // $content = preg_replace('#<span style="font-size: [0-9]+%; line-height: [0-9]+%;">([^>]+)</span>#iU', '\1', $content);
// Make text strong :P
- // $content = preg_replace('#<span style="font-weight: bold?">(.*?)</span>#iU', '<strong>\1</strong>', $content);
+ // $content = preg_replace('#<span style="font-weight: bold?">(.*?)</span>#iU', '<strong>\1</strong>', $content);
// Italic
- // $content = preg_replace('#<span style="font-style: italic?">([^<]+)</span>#iU', '<em>\1</em>', $content);
+ // $content = preg_replace('#<span style="font-style: italic?">([^<]+)</span>#iU', '<em>\1</em>', $content);
// Underline
- // $content = preg_replace('#<span style="text-decoration: underline?">([^<]+)</span>#iU', '<u>\1</u>', $content);
+ // $content = preg_replace('#<span style="text-decoration: underline?">([^<]+)</span>#iU', '<u>\1</u>', $content);
// Remove embed Windows Media Streams
$content = preg_replace( '#<\!--\[if \!IE\]>-->([^[]+)<\!--<!\[endif\]-->#si', '', $content);
@@ -129,8 +144,19 @@ class helper
// Remove some specials html tag, because somewhere there are a mod to allow html tags ;)
$content = preg_replace( '#<(script|iframe)([^[]+)\1>#siU', ' <strong>$1</strong> ', $content);
+ // Parse inline images to display with the feed
+ if (!empty($post_attachments))
+ {
+ $update_count = array();
+ parse_attachments($forum_id, $content, $post_attachments, $update_count);
+ $content .= implode('<br />', $post_attachments);
+
+ // Convert attachments' relative path to absolute path
+ $content = str_replace($this->phpbb_root_path . 'download/file.' . $this->phpEx, $this->get_board_url() . '/download/file.' . $this->phpEx, $content);
+ }
+
// Remove Comments from inline attachments [ia]
- $content = preg_replace('#<div class="(inline-attachment|attachtitle)">(.*?)<!-- ia(.*?) -->(.*?)<!-- ia(.*?) -->(.*?)</div>#si','$4',$content);
+ $content = preg_replace('#<dd>(.*?)</dd>#','',$content);
// Replace some entities with their unicode counterpart
$entities = array(
diff --git a/phpBB/phpbb/feed/news.php b/phpBB/phpbb/feed/news.php
index 7888e73239..fb6fa09278 100644
--- a/phpBB/phpbb/feed/news.php
+++ b/phpBB/phpbb/feed/news.php
@@ -1,25 +1,31 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* News feed
-*
-* This will give you {$this->num_items} first posts
-* of all topics in the selected news forums.
-*
-* @package phpBB3
-*/
-class news extends \phpbb\feed\topic_base
+ * News feed
+ *
+ * This will give you {$this->num_items} first posts
+ * of all topics in the selected news forums.
+ */
+class news extends topic_base
{
- function get_news_forums()
+ /**
+ * Returns the ids of the 'news forums'
+ * @return int[]
+ */
+ private function get_news_forums()
{
static $forum_ids;
@@ -46,7 +52,10 @@ class news extends \phpbb\feed\topic_base
return $forum_ids;
}
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
// Determine forum ids
$in_fid_ary = array_intersect($this->get_news_forums(), $this->get_readable_forums());
@@ -64,9 +73,8 @@ class news extends \phpbb\feed\topic_base
// We really have to get the post ids first!
$sql = 'SELECT topic_first_post_id, topic_time
FROM ' . TOPICS_TABLE . '
- WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . '
- AND topic_moved_id = 0
- AND topic_visibility = ' . ITEM_APPROVED . '
+ WHERE topic_moved_id = 0
+ AND ' . $this->content_visibility->get_forums_visibility_sql('topic', $in_fid_ary) . '
ORDER BY topic_time DESC';
$result = $this->db->sql_query_limit($sql, $this->num_items);
@@ -85,7 +93,7 @@ class news extends \phpbb\feed\topic_base
$this->sql = array(
'SELECT' => 'f.forum_id, f.forum_name,
t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time,
- p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url',
+ p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, t.topic_visibility',
'FROM' => array(
TOPICS_TABLE => 't',
POSTS_TABLE => 'p',
@@ -98,7 +106,7 @@ class news extends \phpbb\feed\topic_base
),
'WHERE' => 'p.topic_id = t.topic_id
AND ' . $this->db->sql_in_set('p.post_id', $post_ids),
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
diff --git a/phpBB/phpbb/feed/overall.php b/phpBB/phpbb/feed/overall.php
index 4545ba5c64..40cf94ace0 100644
--- a/phpBB/phpbb/feed/overall.php
+++ b/phpBB/phpbb/feed/overall.php
@@ -1,25 +1,30 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Board wide feed (aka overall feed)
-*
-* This will give you the newest {$this->num_items} posts
-* from the whole board.
-*
-* @package phpBB3
-*/
-class overall extends \phpbb\feed\post_base
+ * Board wide feed (aka overall feed)
+ *
+ * This will give you the newest {$this->num_items} posts
+ * from the whole board.
+ */
+class overall extends post_base
{
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
$forum_ids = array_diff($this->get_readable_forums(), $this->get_excluded_forums(), $this->get_passworded_forums());
if (empty($forum_ids))
@@ -32,7 +37,7 @@ class overall extends \phpbb\feed\post_base
FROM ' . TOPICS_TABLE . '
WHERE topic_moved_id = 0
AND ' . $this->content_visibility->get_forums_visibility_sql('topic', $forum_ids) . '
- ORDER BY topic_last_post_time DESC';
+ ORDER BY topic_last_post_time DESC, topic_last_post_id DESC';
$result = $this->db->sql_query_limit($sql, $this->num_items);
$topic_ids = array();
@@ -53,8 +58,8 @@ class overall extends \phpbb\feed\post_base
// Get the actual data
$this->sql = array(
'SELECT' => 'f.forum_id, f.forum_name, ' .
- 'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' .
- 'u.username, u.user_id',
+ 'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, ' .
+ 'u.username, u.user_id',
'FROM' => array(
USERS_TABLE => 'u',
POSTS_TABLE => 'p',
@@ -69,13 +74,16 @@ class overall extends \phpbb\feed\post_base
AND ' . $this->content_visibility->get_forums_visibility_sql('post', $forum_ids, 'p.') . '
AND p.post_time >= ' . $min_post_time . '
AND u.user_id = p.poster_id',
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
parent::adjust_item($item_row, $row);
diff --git a/phpBB/phpbb/feed/post_base.php b/phpBB/phpbb/feed/post_base.php
index 42c5eea9e3..f6dc39cbec 100644
--- a/phpBB/phpbb/feed/post_base.php
+++ b/phpBB/phpbb/feed/post_base.php
@@ -1,24 +1,29 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Abstract class for post based feeds
-*
-* @package phpBB3
-*/
-abstract class post_base extends \phpbb\feed\base
+ * Abstract class for post based feeds
+ */
+abstract class post_base extends attachments_base
{
- var $num_items = 'feed_limit_post';
+ protected $num_items = 'feed_limit_post';
- function set_keys()
+ /**
+ * {@inheritdoc}
+ */
+ public function set_keys()
{
$this->set('title', 'post_subject');
$this->set('title2', 'topic_title');
@@ -37,7 +42,10 @@ abstract class post_base extends \phpbb\feed\base
$this->set('enable_magic_url', 'enable_magic_url');
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
$item_row['link'] = $this->helper->append_sid('viewtopic.' . $this->phpEx, "t={$row['topic_id']}&amp;p={$row['post_id']}#p{$row['post_id']}");
@@ -45,7 +53,8 @@ abstract class post_base extends \phpbb\feed\base
{
$item_row['statistics'] = $this->user->lang['POSTED'] . ' ' . $this->user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row)
. ' ' . $this->separator_stats . ' ' . $this->user->format_date($row[$this->get('published')])
- . (($this->is_moderator_approve_forum($row['forum_id']) && $row['post_visibility'] !== ITEM_APPROVED) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POST_UNAPPROVED'] : '');
+ . (($this->is_moderator_approve_forum($row['forum_id']) && (int) $row['post_visibility'] === ITEM_UNAPPROVED) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POST_UNAPPROVED'] : '')
+ . (($this->is_moderator_approve_forum($row['forum_id']) && (int) $row['post_visibility'] === ITEM_DELETED) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POST_DELETED'] : '');
}
}
}
diff --git a/phpBB/phpbb/feed/topic.php b/phpBB/phpbb/feed/topic.php
index 09f377dd10..f029c2b00e 100644
--- a/phpBB/phpbb/feed/topic.php
+++ b/phpBB/phpbb/feed/topic.php
@@ -1,33 +1,40 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
+use phpbb\feed\exception\no_feed_exception;
+use phpbb\feed\exception\no_topic_exception;
+use phpbb\feed\exception\unauthorized_forum_exception;
+use phpbb\feed\exception\unauthorized_topic_exception;
+
/**
-* Topic feed for a specific topic
-*
-* This will give you the last {$this->num_items} posts made within this topic.
-*
-* @package phpBB3
-*/
-class topic extends \phpbb\feed\post_base
+ * Topic feed for a specific topic
+ *
+ * This will give you the last {$this->num_items} posts made within this topic.
+ */
+class topic extends post_base
{
- var $topic_id = 0;
- var $forum_id = 0;
- var $topic_data = array();
+ protected $topic_id = 0;
+ protected $forum_id = 0;
+ protected $topic_data = array();
/**
- * Set the Topic ID
- *
- * @param int $topic_id Topic ID
- * @return \phpbb\feed\topic
- */
+ * Set the Topic ID
+ *
+ * @param int $topic_id Topic ID
+ * @return \phpbb\feed\topic
+ */
public function set_topic_id($topic_id)
{
$this->topic_id = (int) $topic_id;
@@ -35,7 +42,10 @@ class topic extends \phpbb\feed\post_base
return $this;
}
- function open()
+ /**
+ * {@inheritdoc}
+ */
+ public function open()
{
$sql = 'SELECT f.forum_options, f.forum_password, t.topic_id, t.forum_id, t.topic_visibility, t.topic_title, t.topic_time, t.topic_views, t.topic_posts_approved, t.topic_type
FROM ' . TOPICS_TABLE . ' t
@@ -48,7 +58,7 @@ class topic extends \phpbb\feed\post_base
if (empty($this->topic_data))
{
- trigger_error('NO_TOPIC');
+ throw new no_topic_exception($this->topic_id);
}
$this->forum_id = (int) $this->topic_data['forum_id'];
@@ -56,19 +66,19 @@ class topic extends \phpbb\feed\post_base
// Make sure topic is either approved or user authed
if ($this->topic_data['topic_visibility'] != ITEM_APPROVED && !$this->auth->acl_get('m_approve', $this->forum_id))
{
- trigger_error('SORRY_AUTH_READ');
+ throw new unauthorized_topic_exception($this->topic_id);
}
// Make sure forum is not excluded from feed
if (phpbb_optionget(FORUM_OPTION_FEED_EXCLUDE, $this->topic_data['forum_options']))
{
- trigger_error('NO_FEED');
+ throw new no_feed_exception();
}
// Make sure we can read this forum
if (!$this->auth->acl_get('f_read', $this->forum_id))
{
- trigger_error('SORRY_AUTH_READ');
+ throw new unauthorized_forum_exception($this->forum_id);
}
// Make sure forum is not passworded or user is authed
@@ -78,18 +88,23 @@ class topic extends \phpbb\feed\post_base
if (isset($forum_ids_passworded[$this->forum_id]))
{
- trigger_error('SORRY_AUTH_READ');
+ throw new unauthorized_forum_exception($this->forum_id);
}
unset($forum_ids_passworded);
}
+
+ parent::open();
}
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
$this->sql = array(
- 'SELECT' => 'p.post_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, ' .
- 'u.username, u.user_id',
+ 'SELECT' => 'p.post_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, ' .
+ 'u.username, u.user_id',
'FROM' => array(
POSTS_TABLE => 'p',
USERS_TABLE => 'u',
@@ -97,13 +112,26 @@ class topic extends \phpbb\feed\post_base
'WHERE' => 'p.topic_id = ' . $this->topic_id . '
AND ' . $this->content_visibility->get_visibility_sql('post', $this->forum_id, 'p.') . '
AND p.poster_id = u.user_id',
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
}
- function get_item()
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
+ {
+ parent::adjust_item($item_row, $row);
+
+ $item_row['forum_id'] = $this->forum_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_item()
{
return ($row = parent::get_item()) ? array_merge($this->topic_data, $row) : $row;
}
diff --git a/phpBB/phpbb/feed/topic_base.php b/phpBB/phpbb/feed/topic_base.php
index 7e28e67b82..0f1a9ccb70 100644
--- a/phpBB/phpbb/feed/topic_base.php
+++ b/phpBB/phpbb/feed/topic_base.php
@@ -1,24 +1,29 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Abstract class for topic based feeds
-*
-* @package phpBB3
-*/
-abstract class topic_base extends \phpbb\feed\base
+ * Abstract class for topic based feeds
+ */
+abstract class topic_base extends attachments_base
{
- var $num_items = 'feed_limit_topic';
+ protected $num_items = 'feed_limit_topic';
- function set_keys()
+ /**
+ * {@inheritdoc}
+ */
+ public function set_keys()
{
$this->set('title', 'topic_title');
$this->set('title2', 'forum_name');
@@ -37,7 +42,10 @@ abstract class topic_base extends \phpbb\feed\base
$this->set('enable_magic_url', 'enable_magic_url');
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
$item_row['link'] = $this->helper->append_sid('viewtopic.' . $this->phpEx, 't=' . $row['topic_id'] . '&amp;p=' . $row['post_id'] . '#p' . $row['post_id']);
@@ -45,9 +53,24 @@ abstract class topic_base extends \phpbb\feed\base
{
$item_row['statistics'] = $this->user->lang['POSTED'] . ' ' . $this->user->lang['POST_BY_AUTHOR'] . ' ' . $this->user_viewprofile($row)
. ' ' . $this->separator_stats . ' ' . $this->user->format_date($row[$this->get('published')])
- . ' ' . $this->separator_stats . ' ' . $this->user->lang['REPLIES'] . ' ' . $this->content_visibility->get_count('topic_posts', $row, $row['forum_id']) - 1
- . ' ' . $this->separator_stats . ' ' . $this->user->lang['VIEWS'] . ' ' . $row['topic_views']
- . (($this->is_moderator_approve_forum($row['forum_id']) && $row['topic_posts_unapproved']) ? ' ' . $this->separator_stats . ' ' . $this->user->lang['POSTS_UNAPPROVED'] : '');
+ . ' ' . $this->separator_stats . ' ' . $this->user->lang['REPLIES'] . ' ' . ($this->content_visibility->get_count('topic_posts', $row, $row['forum_id']) - 1)
+ . ' ' . $this->separator_stats . ' ' . $this->user->lang['VIEWS'] . ' ' . $row['topic_views'];
+
+ if ($this->is_moderator_approve_forum($row['forum_id']))
+ {
+ if ((int) $row['topic_visibility'] === ITEM_DELETED)
+ {
+ $item_row['statistics'] .= ' ' . $this->separator_stats . ' ' . $this->user->lang['TOPIC_DELETED'];
+ }
+ else if ((int) $row['topic_visibility'] === ITEM_UNAPPROVED)
+ {
+ $item_row['statistics'] .= ' ' . $this->separator_stats . ' ' . $this->user->lang['TOPIC_UNAPPROVED'];
+ }
+ else if ($row['topic_posts_unapproved'])
+ {
+ $item_row['statistics'] .= ' ' . $this->separator_stats . ' ' . $this->user->lang['POSTS_UNAPPROVED'];
+ }
+ }
}
}
}
diff --git a/phpBB/phpbb/feed/topics.php b/phpBB/phpbb/feed/topics.php
index bdc858e947..cf4a2e579e 100644
--- a/phpBB/phpbb/feed/topics.php
+++ b/phpBB/phpbb/feed/topics.php
@@ -1,25 +1,30 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* New Topics feed
-*
-* This will give you the last {$this->num_items} created topics
-* including the first post.
-*
-* @package phpBB3
-*/
-class topics extends \phpbb\feed\topic_base
+ * New Topics feed
+ *
+ * This will give you the last {$this->num_items} created topics
+ * including the first post.
+ */
+class topics extends topic_base
{
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
$forum_ids_read = $this->get_readable_forums();
if (empty($forum_ids_read))
@@ -36,9 +41,8 @@ class topics extends \phpbb\feed\topic_base
// We really have to get the post ids first!
$sql = 'SELECT topic_first_post_id, topic_time
FROM ' . TOPICS_TABLE . '
- WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . '
- AND topic_moved_id = 0
- AND topic_visibility = ' . ITEM_APPROVED . '
+ WHERE topic_moved_id = 0
+ AND ' . $this->content_visibility->get_forums_visibility_sql('topic', $in_fid_ary) . '
ORDER BY topic_time DESC';
$result = $this->db->sql_query_limit($sql, $this->num_items);
@@ -57,7 +61,7 @@ class topics extends \phpbb\feed\topic_base
$this->sql = array(
'SELECT' => 'f.forum_id, f.forum_name,
t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time,
- p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url',
+ p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, t.topic_visibility',
'FROM' => array(
TOPICS_TABLE => 't',
POSTS_TABLE => 'p',
@@ -70,13 +74,16 @@ class topics extends \phpbb\feed\topic_base
),
'WHERE' => 'p.topic_id = t.topic_id
AND ' . $this->db->sql_in_set('p.post_id', $post_ids),
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
parent::adjust_item($item_row, $row);
diff --git a/phpBB/phpbb/feed/topics_active.php b/phpBB/phpbb/feed/topics_active.php
index cc0adac2eb..52340dc2d5 100644
--- a/phpBB/phpbb/feed/topics_active.php
+++ b/phpBB/phpbb/feed/topics_active.php
@@ -1,28 +1,33 @@
<?php
/**
-*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
-*
-*/
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
namespace phpbb\feed;
/**
-* Active Topics feed
-*
-* This will give you the last {$this->num_items} topics
-* with replies made withing the last {$this->sort_days} days
-* including the last post.
-*
-* @package phpBB3
-*/
-class topics_active extends \phpbb\feed\topic_base
+ * Active Topics feed
+ *
+ * This will give you the last {$this->num_items} topics
+ * with replies made withing the last {$this->sort_days} days
+ * including the last post.
+ */
+class topics_active extends topic_base
{
- var $sort_days = 7;
+ protected $sort_days = 7;
- function set_keys()
+ /**
+ * {@inheritdoc}
+ */
+ public function set_keys()
{
parent::set_keys();
@@ -30,7 +35,10 @@ class topics_active extends \phpbb\feed\topic_base
$this->set('creator', 'topic_last_poster_name');
}
- function get_sql()
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_sql()
{
$forum_ids_read = $this->get_readable_forums();
if (empty($forum_ids_read))
@@ -51,11 +59,10 @@ class topics_active extends \phpbb\feed\topic_base
// We really have to get the post ids first!
$sql = 'SELECT topic_last_post_id, topic_last_post_time
FROM ' . TOPICS_TABLE . '
- WHERE ' . $this->db->sql_in_set('forum_id', $in_fid_ary) . '
- AND topic_moved_id = 0
- AND topic_visibility = ' . ITEM_APPROVED . '
+ WHERE topic_moved_id = 0
+ AND ' . $this->content_visibility->get_forums_visibility_sql('topic', $in_fid_ary) . '
' . $last_post_time_sql . '
- ORDER BY topic_last_post_time DESC';
+ ORDER BY topic_last_post_time DESC, topic_last_post_id DESC';
$result = $this->db->sql_query_limit($sql, $this->num_items);
$post_ids = array();
@@ -74,7 +81,7 @@ class topics_active extends \phpbb\feed\topic_base
'SELECT' => 'f.forum_id, f.forum_name,
t.topic_id, t.topic_title, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views,
t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_post_time,
- p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url',
+ p.post_id, p.post_time, p.post_edit_time, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, t.topic_visibility',
'FROM' => array(
TOPICS_TABLE => 't',
POSTS_TABLE => 'p',
@@ -87,13 +94,18 @@ class topics_active extends \phpbb\feed\topic_base
),
'WHERE' => 'p.topic_id = t.topic_id
AND ' . $this->db->sql_in_set('p.post_id', $post_ids),
- 'ORDER_BY' => 'p.post_time DESC',
+ 'ORDER_BY' => 'p.post_time DESC, p.post_id DESC',
);
return true;
}
- function get_forum_ids()
+ /**
+ * Returns the ids of the forums not excluded from the active list
+ *
+ * @return int[]
+ */
+ private function get_forum_ids()
{
static $forum_ids;
@@ -121,7 +133,10 @@ class topics_active extends \phpbb\feed\topic_base
return $forum_ids;
}
- function adjust_item(&$item_row, &$row)
+ /**
+ * {@inheritdoc}
+ */
+ public function adjust_item(&$item_row, &$row)
{
parent::adjust_item($item_row, $row);
diff --git a/phpBB/phpbb/file_downloader.php b/phpBB/phpbb/file_downloader.php
new file mode 100644
index 0000000000..ab9505a14c
--- /dev/null
+++ b/phpBB/phpbb/file_downloader.php
@@ -0,0 +1,120 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb;
+
+class file_downloader
+{
+ /** @var string Error string */
+ protected $error_string = '';
+
+ /** @var int Error number */
+ protected $error_number = 0;
+
+ /**
+ * Retrieve contents from remotely stored file
+ *
+ * @param string $host File host
+ * @param string $directory Directory file is in
+ * @param string $filename Filename of file to retrieve
+ * @param int $port Port to connect to; default: 80
+ * @param int $timeout Connection timeout in seconds; default: 6
+ *
+ * @return mixed File data as string if file can be read and there is no
+ * timeout, false if there were errors or the connection timed out
+ *
+ * @throws \phpbb\exception\runtime_exception If data can't be retrieved and no error
+ * message is returned
+ */
+ public function get($host, $directory, $filename, $port = 80, $timeout = 6)
+ {
+ // Set default values for error variables
+ $this->error_number = 0;
+ $this->error_string = '';
+
+ if ($socket = @fsockopen(($port == 443 ? 'tls://' : '') . $host, $port, $this->error_number, $this->error_string, $timeout))
+ {
+ @fputs($socket, "GET $directory/$filename HTTP/1.0\r\n");
+ @fputs($socket, "HOST: $host\r\n");
+ @fputs($socket, "Connection: close\r\n\r\n");
+
+ $timer_stop = time() + $timeout;
+ stream_set_timeout($socket, $timeout);
+
+ $file_info = '';
+ $get_info = false;
+
+ while (!@feof($socket))
+ {
+ if ($get_info)
+ {
+ $file_info .= @fread($socket, 1024);
+ }
+ else
+ {
+ $line = @fgets($socket, 1024);
+ if ($line == "\r\n")
+ {
+ $get_info = true;
+ }
+ else if (stripos($line, '404 not found') !== false)
+ {
+ throw new \phpbb\exception\runtime_exception('FILE_NOT_FOUND', array($filename));
+ }
+ }
+
+ $stream_meta_data = stream_get_meta_data($socket);
+
+ if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop)
+ {
+ throw new \phpbb\exception\runtime_exception('FSOCK_TIMEOUT');
+ }
+ }
+ @fclose($socket);
+ }
+ else
+ {
+ if ($this->error_string)
+ {
+ $this->error_string = utf8_convert_message($this->error_string);
+ return false;
+ }
+ else
+ {
+ throw new \phpbb\exception\runtime_exception('FSOCK_DISABLED');
+ }
+ }
+
+ return $file_info;
+ }
+
+ /**
+ * Get error string
+ *
+ * @return string Error string
+ */
+ public function get_error_string()
+ {
+ return $this->error_string;
+ }
+
+ /**
+ * Get error number
+ *
+ * @return int Error number
+ */
+ public function get_error_number()
+ {
+ return $this->error_number;
+ }
+}
diff --git a/phpBB/phpbb/files/factory.php b/phpBB/phpbb/files/factory.php
new file mode 100644
index 0000000000..84b7cc9449
--- /dev/null
+++ b/phpBB/phpbb/files/factory.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\files;
+
+class factory
+{
+ /**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ private $container;
+
+ /**
+ * Constructor
+ *
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+ */
+ public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ /**
+ * Get files service
+ *
+ * @param string $name Service name
+ *
+ * @return object|bool Requested service or false if service could not be
+ * found by the container
+ */
+ public function get($name)
+ {
+ $service = false;
+
+ $name = (strpos($name, '.') === false) ? 'files.' . $name : $name;
+
+ try
+ {
+ $service = $this->container->get($name);
+ }
+ catch (\Exception $e)
+ {
+ // do nothing
+ }
+
+ return $service;
+ }
+}
diff --git a/phpBB/phpbb/files/filespec.php b/phpBB/phpbb/files/filespec.php
new file mode 100644
index 0000000000..2ff2a92c83
--- /dev/null
+++ b/phpBB/phpbb/files/filespec.php
@@ -0,0 +1,584 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\files;
+
+use phpbb\language\language;
+
+/**
+ * Responsible for holding all file relevant information, as well as doing file-specific operations.
+ * The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on.
+ */
+class filespec
+{
+ /** @var string File name */
+ protected $filename = '';
+
+ /** @var string Real name of file */
+ protected $realname = '';
+
+ /** @var string Upload name of file */
+ protected $uploadname = '';
+
+ /** @var string Mimetype of file */
+ protected $mimetype = '';
+
+ /** @var string File extension */
+ protected $extension = '';
+
+ /** @var int File size */
+ protected $filesize = 0;
+
+ /** @var int Width of file */
+ protected $width = 0;
+
+ /** @var int Height of file */
+ protected $height = 0;
+
+ /** @var array Image info including type and size */
+ protected $image_info = array();
+
+ /** @var string Destination file name */
+ protected $destination_file = '';
+
+ /** @var string Destination file path */
+ protected $destination_path = '';
+
+ /** @var bool Whether file was moved */
+ protected $file_moved = false;
+
+ /** @var bool Whether file is local */
+ protected $local = false;
+
+ /** @var bool Class initialization flag */
+ protected $class_initialized = false;
+
+ /** @var array Error array */
+ public $error = array();
+
+ /** @var upload Instance of upload class */
+ public $upload;
+
+ /** @var \phpbb\filesystem\filesystem_interface */
+ protected $filesystem;
+
+ /** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper class */
+ protected $php_ini;
+
+ /** @var \FastImageSize\FastImageSize */
+ protected $imagesize;
+
+ /** @var language Language class */
+ protected $language;
+
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /** @var \phpbb\plupload\plupload The plupload object */
+ protected $plupload;
+
+ /** @var \phpbb\mimetype\guesser phpBB Mimetype guesser */
+ protected $mimetype_guesser;
+
+ /**
+ * File upload class
+ *
+ * @param \phpbb\filesystem\filesystem_interface $phpbb_filesystem Filesystem
+ * @param language $language Language
+ * @param \bantu\IniGetWrapper\IniGetWrapper $php_ini ini_get() wrapper
+ * @param \FastImageSize\FastImageSize $imagesize Imagesize class
+ * @param string $phpbb_root_path phpBB root path
+ * @param \phpbb\mimetype\guesser $mimetype_guesser Mime type guesser
+ * @param \phpbb\plupload\plupload $plupload Plupload
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $phpbb_filesystem, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \FastImageSize\FastImageSize $imagesize, $phpbb_root_path, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null)
+ {
+ $this->filesystem = $phpbb_filesystem;
+ $this->language = $language;
+ $this->php_ini = $php_ini;
+ $this->imagesize = $imagesize;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->plupload = $plupload;
+ $this->mimetype_guesser = $mimetype_guesser;
+ }
+
+ /**
+ * Set upload ary
+ *
+ * @param array $upload_ary Upload ary
+ *
+ * @return filespec This instance of the filespec class
+ */
+ public function set_upload_ary($upload_ary)
+ {
+ if (!isset($upload_ary) || !sizeof($upload_ary))
+ {
+ return $this;
+ }
+
+ $this->class_initialized = true;
+ $this->filename = $upload_ary['tmp_name'];
+ $this->filesize = $upload_ary['size'];
+ $name = (STRIP) ? stripslashes($upload_ary['name']) : $upload_ary['name'];
+ $name = trim(utf8_basename($name));
+ $this->realname = $this->uploadname = $name;
+ $this->mimetype = $upload_ary['type'];
+
+ // Opera adds the name to the mime type
+ $this->mimetype = (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype;
+
+ if (!$this->mimetype)
+ {
+ $this->mimetype = 'application/octet-stream';
+ }
+
+ $this->extension = strtolower(self::get_extension($this->realname));
+
+ // Try to get real filesize from temporary folder (not always working) ;)
+ $this->filesize = ($this->get_filesize($this->filename)) ?: $this->filesize;
+
+ $this->width = $this->height = 0;
+ $this->file_moved = false;
+
+ $this->local = (isset($upload_ary['local_mode'])) ? true : false;
+
+ return $this;
+ }
+
+ /**
+ * Set the upload namespace
+ *
+ * @param upload $namespace Instance of upload class
+ *
+ * @return filespec This instance of the filespec class
+ */
+ public function set_upload_namespace($namespace)
+ {
+ $this->upload = $namespace;
+
+ return $this;
+ }
+
+ /**
+ * Check if class members were not properly initialised yet
+ *
+ * @return bool True if there was an init error, false if not
+ */
+ public function init_error()
+ {
+ return !$this->class_initialized;
+ }
+
+ /**
+ * Set error in error array
+ *
+ * @param mixed $error Content for error array
+ *
+ * @return \phpbb\files\filespec This instance of the filespec class
+ */
+ public function set_error($error)
+ {
+ $this->error[] = $error;
+
+ return $this;
+ }
+
+ /**
+ * Cleans destination filename
+ *
+ * @param string $mode Either real, unique, or unique_ext. Real creates a
+ * realname, filtering some characters, lowering every
+ * character. Unique creates a unique filename.
+ * @param string $prefix Prefix applied to filename
+ * @param string $user_id The user_id is only needed for when cleaning a user's avatar
+ */
+ public function clean_filename($mode = 'unique', $prefix = '', $user_id = '')
+ {
+ if ($this->init_error())
+ {
+ return;
+ }
+
+ switch ($mode)
+ {
+ case 'real':
+ // Remove every extension from filename (to not let the mime bug being exposed)
+ if (strpos($this->realname, '.') !== false)
+ {
+ $this->realname = substr($this->realname, 0, strpos($this->realname, '.'));
+ }
+
+ // Replace any chars which may cause us problems with _
+ $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|');
+
+ $this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname)));
+ $this->realname = preg_replace("/%(\w{2})/", '_', $this->realname);
+
+ $this->realname = $prefix . $this->realname . '.' . $this->extension;
+ break;
+
+ case 'unique':
+ $this->realname = $prefix . md5(unique_id());
+ break;
+
+ case 'avatar':
+ $this->extension = strtolower($this->extension);
+ $this->realname = $prefix . $user_id . '.' . $this->extension;
+
+ break;
+
+ case 'unique_ext':
+ default:
+ $this->realname = $prefix . md5(unique_id()) . '.' . $this->extension;
+ }
+ }
+
+ /**
+ * Get property from file object
+ *
+ * @param string $property Name of property
+ *
+ * @return mixed Content of property
+ */
+ public function get($property)
+ {
+ if ($this->init_error() || !isset($this->$property))
+ {
+ return false;
+ }
+
+ return $this->$property;
+ }
+
+ /**
+ * Check if file is an image (mime type)
+ *
+ * @return bool true if it is an image, false if not
+ */
+ public function is_image()
+ {
+ return (strpos($this->mimetype, 'image/') === 0);
+ }
+
+ /**
+ * Check if the file got correctly uploaded
+ *
+ * @return bool true if it is a valid upload, false if not
+ */
+ public function is_uploaded()
+ {
+ $is_plupload = $this->plupload && $this->plupload->is_active();
+
+ if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename))
+ {
+ return false;
+ }
+
+ if (($this->local || $is_plupload) && !file_exists($this->filename))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove file
+ */
+ public function remove()
+ {
+ if ($this->file_moved)
+ {
+ @unlink($this->destination_file);
+ }
+ }
+
+ /**
+ * Get file extension
+ *
+ * @param string $filename Filename that needs to be checked
+ *
+ * @return string Extension of the supplied filename
+ */
+ static public function get_extension($filename)
+ {
+ $filename = utf8_basename($filename);
+
+ if (strpos($filename, '.') === false)
+ {
+ return '';
+ }
+
+ $filename = explode('.', $filename);
+ return array_pop($filename);
+ }
+
+ /**
+ * Get mime type
+ *
+ * @param string $filename Filename that needs to be checked
+ * @return string Mime type of supplied filename
+ */
+ public function get_mimetype($filename)
+ {
+ if ($this->mimetype_guesser !== null)
+ {
+ $mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname);
+
+ if ($mimetype !== 'application/octet-stream')
+ {
+ $this->mimetype = $mimetype;
+ }
+ }
+
+ return $this->mimetype;
+ }
+
+ /**
+ * Get file size
+ *
+ * @param string $filename File name of file to check
+ *
+ * @return int File size
+ */
+ public function get_filesize($filename)
+ {
+ return @filesize($filename);
+ }
+
+
+ /**
+ * Check the first 256 bytes for forbidden content
+ *
+ * @param array $disallowed_content Array containg disallowed content
+ *
+ * @return bool False if disallowed content found, true if not
+ */
+ public function check_content($disallowed_content)
+ {
+ if (empty($disallowed_content))
+ {
+ return true;
+ }
+
+ $fp = @fopen($this->filename, 'rb');
+
+ if ($fp !== false)
+ {
+ $ie_mime_relevant = fread($fp, 256);
+ fclose($fp);
+ foreach ($disallowed_content as $forbidden)
+ {
+ if (stripos($ie_mime_relevant, '<' . $forbidden) !== false)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Move file to destination folder
+ * The phpbb_root_path variable will be applied to the destination path
+ *
+ * @param string $destination Destination path, for example $config['avatar_path']
+ * @param bool $overwrite If set to true, an already existing file will be overwritten
+ * @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped
+ * @param string|bool $chmod Permission mask for chmodding the file after a successful move.
+ * The mode entered here reflects the mode defined by {@link phpbb_chmod()}
+ *
+ * @return bool True if file was moved, false if not
+ * @access public
+ */
+ public function move_file($destination, $overwrite = false, $skip_image_check = false, $chmod = false)
+ {
+ if (sizeof($this->error))
+ {
+ return false;
+ }
+
+ $chmod = ($chmod === false) ? CHMOD_READ | CHMOD_WRITE : $chmod;
+
+ // We need to trust the admin in specifying valid upload directories and an attacker not being able to overwrite it...
+ $this->destination_path = $this->phpbb_root_path . $destination;
+
+ // Check if the destination path exist...
+ if (!file_exists($this->destination_path))
+ {
+ @unlink($this->filename);
+ return false;
+ }
+
+ $upload_mode = ($this->php_ini->getBool('open_basedir') || $this->php_ini->getBool('safe_mode')) ? 'move' : 'copy';
+ $upload_mode = ($this->local) ? 'local' : $upload_mode;
+ $this->destination_file = $this->destination_path . '/' . utf8_basename($this->realname);
+
+ // Check if the file already exist, else there is something wrong...
+ if (file_exists($this->destination_file) && !$overwrite)
+ {
+ @unlink($this->filename);
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
+ $this->file_moved = false;
+ return false;
+ }
+ else
+ {
+ if (file_exists($this->destination_file))
+ {
+ @unlink($this->destination_file);
+ }
+
+ switch ($upload_mode)
+ {
+ case 'copy':
+
+ if (!@copy($this->filename, $this->destination_file))
+ {
+ if (!@move_uploaded_file($this->filename, $this->destination_file))
+ {
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
+ }
+ }
+
+ break;
+
+ case 'move':
+
+ if (!@move_uploaded_file($this->filename, $this->destination_file))
+ {
+ if (!@copy($this->filename, $this->destination_file))
+ {
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
+ }
+ }
+
+ break;
+
+ case 'local':
+
+ if (!@copy($this->filename, $this->destination_file))
+ {
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file);
+ }
+
+ break;
+ }
+
+ // Remove temporary filename
+ @unlink($this->filename);
+
+ if (sizeof($this->error))
+ {
+ return false;
+ }
+
+ try
+ {
+ $this->filesystem->phpbb_chmod($this->destination_file, $chmod);
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ }
+
+ // Try to get real filesize from destination folder
+ $this->filesize = ($this->get_filesize($this->destination_file)) ?: $this->filesize;
+
+ // Get mimetype of supplied file
+ $this->mimetype = $this->get_mimetype($this->destination_file);
+
+ if ($this->is_image() && !$skip_image_check)
+ {
+ $this->width = $this->height = 0;
+
+ $this->image_info = $this->imagesize->getImageSize($this->destination_file, $this->mimetype);
+
+ if ($this->image_info !== false)
+ {
+ $this->width = $this->image_info['width'];
+ $this->height = $this->image_info['height'];
+
+ // Check image type
+ $types = upload::image_types();
+
+ if (!isset($types[$this->image_info['type']]) || !in_array($this->extension, $types[$this->image_info['type']]))
+ {
+ if (!isset($types[$this->image_info['type']]))
+ {
+ $this->error[] = $this->language->lang('IMAGE_FILETYPE_INVALID', $this->image_info['type'], $this->mimetype);
+ }
+ else
+ {
+ $this->error[] = $this->language->lang('IMAGE_FILETYPE_MISMATCH', $types[$this->image_info['type']][0], $this->extension);
+ }
+ }
+
+ // Make sure the dimensions match a valid image
+ if (empty($this->width) || empty($this->height))
+ {
+ $this->error[] = $this->language->lang('ATTACHED_IMAGE_NOT_IMAGE');
+ }
+ }
+ else
+ {
+ $this->error[] = $this->language->lang('UNABLE_GET_IMAGE_SIZE');
+ }
+ }
+
+ $this->file_moved = true;
+ $this->additional_checks();
+ unset($this->upload);
+
+ return true;
+ }
+
+ /**
+ * Performing additional checks
+ *
+ * @return bool False if issue was found, true if not
+ */
+ public function additional_checks()
+ {
+ if (!$this->file_moved)
+ {
+ return false;
+ }
+
+ // Filesize is too big or it's 0 if it was larger than the maxsize in the upload form
+ if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0))
+ {
+ $max_filesize = get_formatted_filesize($this->upload->max_filesize, false);
+
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']);
+
+ return false;
+ }
+
+ if (!$this->upload->valid_dimensions($this))
+ {
+ $this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_SIZE',
+ $this->language->lang('PIXELS', (int) $this->upload->min_width),
+ $this->language->lang('PIXELS', (int) $this->upload->min_height),
+ $this->language->lang('PIXELS', (int) $this->upload->max_width),
+ $this->language->lang('PIXELS', (int) $this->upload->max_height),
+ $this->language->lang('PIXELS', (int) $this->width),
+ $this->language->lang('PIXELS', (int) $this->height));
+
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/phpBB/phpbb/files/types/base.php b/phpBB/phpbb/files/types/base.php
new file mode 100644
index 0000000000..3313ad040b
--- /dev/null
+++ b/phpBB/phpbb/files/types/base.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\files\types;
+
+abstract class base implements type_interface
+{
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ /** @var \bantu\IniGetWrapper\IniGetWrapper */
+ protected $php_ini;
+
+ /** @var \phpbb\files\upload */
+ protected $upload;
+
+ /**
+ * Check if upload exceeds maximum file size
+ *
+ * @param \phpbb\files\filespec $file Filespec object
+ *
+ * @return \phpbb\files\filespec Returns same filespec instance
+ */
+ public function check_upload_size($file)
+ {
+ // PHP Upload filesize exceeded
+ if ($file->get('filename') == 'none')
+ {
+ $max_filesize = $this->php_ini->getString('upload_max_filesize');
+ $unit = 'MB';
+
+ if (!empty($max_filesize))
+ {
+ $unit = strtolower(substr($max_filesize, -1, 1));
+ $max_filesize = (int) $max_filesize;
+
+ $unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB');
+ }
+
+ $file->error[] = (empty($max_filesize)) ? $this->language->lang($this->upload->error_prefix . 'PHP_SIZE_NA') : $this->language->lang($this->upload->error_prefix . 'PHP_SIZE_OVERRUN', $max_filesize, $this->language->lang($unit));
+ }
+
+ return $file;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_upload(\phpbb\files\upload $upload)
+ {
+ $this->upload = $upload;
+
+ return $this;
+ }
+}
diff --git a/phpBB/phpbb/files/types/form.php b/phpBB/phpbb/files/types/form.php
new file mode 100644
index 0000000000..832f090c47
--- /dev/null
+++ b/phpBB/phpbb/files/types/form.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\files\types;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use phpbb\files\factory;
+use phpbb\files\filespec;
+use phpbb\language\language;
+use phpbb\plupload\plupload;
+use phpbb\request\request_interface;
+
+class form extends base
+{
+ /** @var factory Files factory */
+ protected $factory;
+
+ /** @var language */
+ protected $language;
+
+ /** @var IniGetWrapper */
+ protected $php_ini;
+
+ /** @var plupload */
+ protected $plupload;
+
+ /** @var request_interface */
+ protected $request;
+
+ /** @var \phpbb\files\upload */
+ protected $upload;
+
+ /**
+ * Construct a form upload type
+ *
+ * @param factory $factory Files factory
+ * @param language $language Language class
+ * @param IniGetWrapper $php_ini ini_get() wrapper
+ * @param plupload $plupload Plupload
+ * @param request_interface $request Request object
+ */
+ public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, plupload $plupload, request_interface $request)
+ {
+ $this->factory = $factory;
+ $this->language = $language;
+ $this->php_ini = $php_ini;
+ $this->plupload = $plupload;
+ $this->request = $request;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function upload()
+ {
+ $args = func_get_args();
+ return $this->form_upload($args[0]);
+ }
+
+ /**
+ * Form upload method
+ * Upload file from users harddisk
+ *
+ * @param string $form_name Form name assigned to the file input field (if it is an array, the key has to be specified)
+ *
+ * @return filespec $file Object "filespec" is returned, all further operations can be done with this object
+ * @access public
+ */
+ protected function form_upload($form_name)
+ {
+ $upload = $this->request->file($form_name);
+ unset($upload['local_mode']);
+
+ $result = $this->plupload->handle_upload($form_name);
+ if (is_array($result))
+ {
+ $upload = array_merge($upload, $result);
+ }
+
+ /** @var filespec $file */
+ $file = $this->factory->get('filespec')
+ ->set_upload_ary($upload)
+ ->set_upload_namespace($this->upload);
+
+ if ($file->init_error())
+ {
+ $file->error[] = '';
+ return $file;
+ }
+
+ // Error array filled?
+ if (isset($upload['error']))
+ {
+ $error = $this->upload->assign_internal_error($upload['error']);
+
+ if ($error !== false)
+ {
+ $file->error[] = $error;
+ return $file;
+ }
+ }
+
+ // Check if empty file got uploaded (not catched by is_uploaded_file)
+ if (isset($upload['size']) && $upload['size'] == 0)
+ {
+ $file->error[] = $this->language->lang($this->upload->error_prefix . 'EMPTY_FILEUPLOAD');
+ return $file;
+ }
+
+ // PHP Upload file size check
+ $file = $this->check_upload_size($file);
+ if (sizeof($file->error))
+ {
+ return $file;
+ }
+
+ // Not correctly uploaded
+ if (!$file->is_uploaded())
+ {
+ $file->error[] = $this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED');
+ return $file;
+ }
+
+ $this->upload->common_checks($file);
+
+ return $file;
+ }
+}
diff --git a/phpBB/phpbb/files/types/local.php b/phpBB/phpbb/files/types/local.php
new file mode 100644
index 0000000000..7e9210b196
--- /dev/null
+++ b/phpBB/phpbb/files/types/local.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\files\types;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use phpbb\files\factory;
+use phpbb\files\filespec;
+use phpbb\language\language;
+use phpbb\request\request_interface;
+
+class local extends base
+{
+ /** @var factory Files factory */
+ protected $factory;
+
+ /** @var language */
+ protected $language;
+
+ /** @var IniGetWrapper */
+ protected $php_ini;
+
+ /** @var request_interface */
+ protected $request;
+
+ /** @var \phpbb\files\upload */
+ protected $upload;
+
+ /**
+ * Construct a form upload type
+ *
+ * @param factory $factory Files factory
+ * @param language $language Language class
+ * @param IniGetWrapper $php_ini ini_get() wrapper
+ * @param request_interface $request Request object
+ */
+ public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, request_interface $request)
+ {
+ $this->factory = $factory;
+ $this->language = $language;
+ $this->php_ini = $php_ini;
+ $this->request = $request;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function upload()
+ {
+ $args = func_get_args();
+ return $this->local_upload($args[0], isset($args[1]) ? $args[1] : false);
+ }
+
+ /**
+ * Move file from another location to phpBB
+ *
+ * @param string $source_file Filename of source file
+ * @param array|bool $filedata Array with filedata or false
+ *
+ * @return filespec Object "filespec" is returned, all further operations can be done with this object
+ */
+ protected function local_upload($source_file, $filedata = false)
+ {
+ $upload = $this->get_upload_ary($source_file, $filedata);
+
+ /** @var filespec $file */
+ $file = $this->factory->get('filespec')
+ ->set_upload_ary($upload)
+ ->set_upload_namespace($this->upload);
+
+ if ($file->init_error())
+ {
+ $file->error[] = '';
+ return $file;
+ }
+
+ // PHP Upload file size check
+ $file = $this->check_upload_size($file);
+ if (sizeof($file->error))
+ {
+ return $file;
+ }
+
+ // Not correctly uploaded
+ if (!$file->is_uploaded())
+ {
+ $file->error[] = $this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED');
+ return $file;
+ }
+
+ $this->upload->common_checks($file);
+ $this->request->overwrite('local', $upload, request_interface::FILES);
+
+ return $file;
+ }
+
+ /**
+ * Retrieve upload array
+ *
+ * @param string $source_file Source file name
+ * @param array $filedata File data array
+ *
+ * @return array Upload array
+ */
+ protected function get_upload_ary($source_file, $filedata)
+ {
+ $upload = array();
+
+ $upload['local_mode'] = true;
+ $upload['tmp_name'] = $source_file;
+
+ if ($filedata === false)
+ {
+ $upload['name'] = utf8_basename($source_file);
+ $upload['size'] = 0;
+ }
+ else
+ {
+ $upload['name'] = $filedata['realname'];
+ $upload['size'] = $filedata['size'];
+ $upload['type'] = $filedata['type'];
+ }
+
+ return $upload;
+ }
+}
diff --git a/phpBB/phpbb/files/types/remote.php b/phpBB/phpbb/files/types/remote.php
new file mode 100644
index 0000000000..33cbfb00ae
--- /dev/null
+++ b/phpBB/phpbb/files/types/remote.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\files\types;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use phpbb\files\factory;
+use phpbb\files\filespec;
+use phpbb\language\language;
+use phpbb\request\request_interface;
+
+class remote extends base
+{
+ /** @var factory Files factory */
+ protected $factory;
+
+ /** @var language */
+ protected $language;
+
+ /** @var IniGetWrapper */
+ protected $php_ini;
+
+ /** @var request_interface */
+ protected $request;
+
+ /** @var \phpbb\files\upload */
+ protected $upload;
+
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /**
+ * Construct a form upload type
+ *
+ * @param factory $factory Files factory
+ * @param language $language Language class
+ * @param IniGetWrapper $php_ini ini_get() wrapper
+ * @param request_interface $request Request object
+ * @param string $phpbb_root_path phpBB root path
+ */
+ public function __construct(factory $factory, language $language, IniGetWrapper $php_ini, request_interface $request, $phpbb_root_path)
+ {
+ $this->factory = $factory;
+ $this->language = $language;
+ $this->php_ini = $php_ini;
+ $this->request = $request;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function upload()
+ {
+ $args = func_get_args();
+ return $this->remote_upload($args[0]);
+ }
+
+ /**
+ * Remote upload method
+ * Uploads file from given url
+ *
+ * @param string $upload_url URL pointing to file to upload, for example http://www.foobar.com/example.gif
+ * @return filespec $file Object "filespec" is returned, all further operations can be done with this object
+ * @access public
+ */
+ protected function remote_upload($upload_url)
+ {
+ $upload_ary = array();
+ $upload_ary['local_mode'] = true;
+
+ if (!preg_match('#^(https?://).*?\.(' . implode('|', $this->upload->allowed_extensions) . ')$#i', $upload_url, $match))
+ {
+ return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'URL_INVALID'));
+ }
+
+ $url = parse_url($upload_url);
+
+ $host = $url['host'];
+ $path = $url['path'];
+ $port = (!empty($url['port'])) ? (int) $url['port'] : 80;
+
+ $upload_ary['type'] = 'application/octet-stream';
+
+ $url['path'] = explode('.', $url['path']);
+ $ext = array_pop($url['path']);
+
+ $url['path'] = implode('', $url['path']);
+ $upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : '');
+ $filesize = 0;
+
+ $remote_max_filesize = $this->get_max_file_size();
+
+ $errno = 0;
+ $errstr = '';
+
+ if (!($fsock = @fsockopen($host, $port, $errno, $errstr)))
+ {
+ return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'NOT_UPLOADED'));
+ }
+
+ // Make sure $path not beginning with /
+ if (strpos($path, '/') === 0)
+ {
+ $path = substr($path, 1);
+ }
+
+ fputs($fsock, 'GET /' . $path . " HTTP/1.1\r\n");
+ fputs($fsock, "HOST: " . $host . "\r\n");
+ fputs($fsock, "Connection: close\r\n\r\n");
+
+ // Set a proper timeout for the socket
+ socket_set_timeout($fsock, $this->upload->upload_timeout);
+
+ $get_info = false;
+ $data = '';
+ $length = false;
+ $timer_stop = time() + $this->upload->upload_timeout;
+
+ while ((!$length || $filesize < $length) && !@feof($fsock))
+ {
+ if ($get_info)
+ {
+ if ($length)
+ {
+ // Don't attempt to read past end of file if server indicated length
+ $block = @fread($fsock, min($length - $filesize, 1024));
+ }
+ else
+ {
+ $block = @fread($fsock, 1024);
+ }
+
+ $filesize += strlen($block);
+
+ if ($remote_max_filesize && $filesize > $remote_max_filesize)
+ {
+ $max_filesize = get_formatted_filesize($remote_max_filesize, false);
+
+ return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']));
+ }
+
+ $data .= $block;
+ }
+ else
+ {
+ $line = @fgets($fsock, 1024);
+
+ if ($line == "\r\n")
+ {
+ $get_info = true;
+ }
+ else
+ {
+ if (stripos($line, 'content-type: ') !== false)
+ {
+ $upload_ary['type'] = rtrim(str_replace('content-type: ', '', strtolower($line)));
+ }
+ else if ($this->upload->max_filesize && stripos($line, 'content-length: ') !== false)
+ {
+ $length = (int) str_replace('content-length: ', '', strtolower($line));
+
+ if ($remote_max_filesize && $length && $length > $remote_max_filesize)
+ {
+ $max_filesize = get_formatted_filesize($remote_max_filesize, false);
+
+ return $this->factory->get('filespec')->set_error($this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']));
+ }
+ }
+ else if (stripos($line, '404 not found') !== false)
+ {
+ return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'URL_NOT_FOUND');
+ }
+ }
+ }
+
+ $stream_meta_data = stream_get_meta_data($fsock);
+
+ // Cancel upload if we exceed timeout
+ if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop)
+ {
+ return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'REMOTE_UPLOAD_TIMEOUT');
+ }
+ }
+ @fclose($fsock);
+
+ if (empty($data))
+ {
+ return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'EMPTY_REMOTE_DATA');
+ }
+
+ $filename = tempnam(sys_get_temp_dir(), unique_id() . '-');
+
+ if (!($fp = @fopen($filename, 'wb')))
+ {
+ return $this->factory->get('filespec')->set_error($this->upload->error_prefix . 'NOT_UPLOADED');
+ }
+
+ $upload_ary['size'] = fwrite($fp, $data);
+ fclose($fp);
+ unset($data);
+
+ $upload_ary['tmp_name'] = $filename;
+
+ /** @var filespec $file */
+ $file = $this->factory->get('filespec')
+ ->set_upload_ary($upload_ary)
+ ->set_upload_namespace($this->upload);
+ $this->upload->common_checks($file);
+
+ return $file;
+ }
+
+ /**
+ * Get maximum file size for remote uploads
+ *
+ * @return int Maximum file size
+ */
+ protected function get_max_file_size()
+ {
+ $max_file_size = $this->upload->max_filesize;
+ if (!$max_file_size)
+ {
+ $max_file_size = $this->php_ini->getString('upload_max_filesize');
+
+ if (!empty($max_file_size))
+ {
+ $unit = strtolower(substr($max_file_size, -1, 1));
+ $max_file_size = (int) $max_file_size;
+
+ switch ($unit)
+ {
+ case 'g':
+ $max_file_size *= 1024;
+ // no break
+ case 'm':
+ $max_file_size *= 1024;
+ // no break
+ case 'k':
+ $max_file_size *= 1024;
+ // no break
+ }
+ }
+ }
+
+ return $max_file_size;
+ }
+}
diff --git a/phpBB/phpbb/files/types/type_interface.php b/phpBB/phpbb/files/types/type_interface.php
new file mode 100644
index 0000000000..e07078349a
--- /dev/null
+++ b/phpBB/phpbb/files/types/type_interface.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\files\types;
+
+use phpbb\files\upload;
+
+interface type_interface
+{
+ /**
+ * Handle upload for upload types. Arguments passed to this method will be
+ * handled by the upload type classes themselves.
+ *
+ * @return \phpbb\files\filespec|bool Filespec instance if upload is
+ * successful or false if not
+ */
+ public function upload();
+
+ /**
+ * Set upload instance
+ * Needs to be executed before every upload.
+ *
+ * @param upload $upload Upload instance
+ *
+ * @return type_interface Returns itself
+ */
+ public function set_upload(upload $upload);
+}
diff --git a/phpBB/phpbb/files/upload.php b/phpBB/phpbb/files/upload.php
new file mode 100644
index 0000000000..a9bf74094d
--- /dev/null
+++ b/phpBB/phpbb/files/upload.php
@@ -0,0 +1,390 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\files;
+
+use phpbb\filesystem\filesystem_interface;
+use phpbb\language\language;
+use phpbb\request\request_interface;
+
+/**
+ * File upload class
+ * Init class (all parameters optional and able to be set/overwritten separately) - scope is global and valid for all uploads
+ */
+class upload
+{
+ /** @var array Allowed file extensions */
+ public $allowed_extensions = array();
+
+ /** @var array Disallowed content */
+ protected $disallowed_content = array('body', 'head', 'html', 'img', 'plaintext', 'a href', 'pre', 'script', 'table', 'title');
+
+ /** @var int Maximum filesize */
+ public $max_filesize = 0;
+
+ /** @var int Minimum width of images */
+ public $min_width = 0;
+
+ /** @var int Minimum height of images */
+ public $min_height = 0;
+
+ /** @var int Maximum width of images */
+ public $max_width = 0;
+
+ /** @var int Maximum height of images */
+ public $max_height = 0;
+
+ /** @var string Prefix for language variables of errors */
+ public $error_prefix = '';
+
+ /** @var int Timeout for remote upload */
+ public $upload_timeout = 6;
+
+ /** @var filesystem_interface */
+ protected $filesystem;
+
+ /** @var \phpbb\files\factory Files factory */
+ protected $factory;
+
+ /** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper */
+ protected $php_ini;
+
+ /** @var \phpbb\language\language Language class */
+ protected $language;
+
+ /** @var request_interface Request class */
+ protected $request;
+
+ /**
+ * Init file upload class.
+ *
+ * @param filesystem_interface $filesystem
+ * @param factory $factory Files factory
+ * @param language $language Language class
+ * @param \bantu\IniGetWrapper\IniGetWrapper $php_ini ini_get() wrapper
+ * @param request_interface $request Request class
+ */
+ public function __construct(filesystem_interface $filesystem, factory $factory, language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, request_interface $request)
+ {
+ $this->filesystem = $filesystem;
+ $this->factory = $factory;
+ $this->language = $language;
+ $this->php_ini = $php_ini;
+ $this->request = $request;
+ }
+
+ /**
+ * Reset vars
+ */
+ public function reset_vars()
+ {
+ $this->max_filesize = 0;
+ $this->min_width = $this->min_height = $this->max_width = $this->max_height = 0;
+ $this->error_prefix = '';
+ $this->allowed_extensions = array();
+ $this->disallowed_content = array();
+ }
+
+ /**
+ * Set allowed extensions
+ *
+ * @param array $allowed_extensions Allowed file extensions
+ *
+ * @return \phpbb\files\upload This instance of upload
+ */
+ public function set_allowed_extensions($allowed_extensions)
+ {
+ if ($allowed_extensions !== false && is_array($allowed_extensions))
+ {
+ $this->allowed_extensions = $allowed_extensions;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set allowed dimensions
+ *
+ * @param int $min_width Minimum image width
+ * @param int $min_height Minimum image height
+ * @param int $max_width Maximum image width
+ * @param int $max_height Maximum image height
+ *
+ * @return \phpbb\files\upload This instance of upload
+ */
+ public function set_allowed_dimensions($min_width, $min_height, $max_width, $max_height)
+ {
+ $this->min_width = (int) $min_width;
+ $this->min_height = (int) $min_height;
+ $this->max_width = (int) $max_width;
+ $this->max_height = (int) $max_height;
+
+ return $this;
+ }
+
+ /**
+ * Set maximum allowed file size
+ *
+ * @param int $max_filesize Maximum file size
+ *
+ * @return \phpbb\files\upload This instance of upload
+ */
+ public function set_max_filesize($max_filesize)
+ {
+ if ($max_filesize !== false && (int) $max_filesize)
+ {
+ $this->max_filesize = (int) $max_filesize;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set disallowed strings
+ *
+ * @param array $disallowed_content Disallowed content
+ *
+ * @return \phpbb\files\upload This instance of upload
+ */
+ public function set_disallowed_content($disallowed_content)
+ {
+ if ($disallowed_content !== false && is_array($disallowed_content))
+ {
+ $this->disallowed_content = array_diff($disallowed_content, array(''));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set error prefix
+ *
+ * @param string $error_prefix Prefix for language variables of errors
+ *
+ * @return \phpbb\files\upload This instance of upload
+ */
+ public function set_error_prefix($error_prefix)
+ {
+ $this->error_prefix = $error_prefix;
+
+ return $this;
+ }
+
+ /**
+ * Handle upload based on type
+ *
+ * @param string $type Upload type
+ *
+ * @return \phpbb\files\filespec|bool A filespec instance if upload was
+ * successful, false if there were issues or the type is not supported
+ */
+ public function handle_upload($type)
+ {
+ $args = func_get_args();
+ array_shift($args);
+ $type_class = $this->factory->get($type)
+ ->set_upload($this);
+
+ return (is_object($type_class)) ? call_user_func_array(array($type_class, 'upload'), $args) : false;
+ }
+
+ /**
+ * Assign internal error
+ *
+ * @param string $errorcode Error code to assign
+ *
+ * @return string Error string
+ * @access public
+ */
+ public function assign_internal_error($errorcode)
+ {
+ switch ($errorcode)
+ {
+ case UPLOAD_ERR_INI_SIZE:
+ $max_filesize = $this->php_ini->getString('upload_max_filesize');
+ $unit = 'MB';
+
+ if (!empty($max_filesize))
+ {
+ $unit = strtolower(substr($max_filesize, -1, 1));
+ $max_filesize = (int) $max_filesize;
+
+ $unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB');
+ }
+
+ $error = (empty($max_filesize)) ? $this->language->lang($this->error_prefix . 'PHP_SIZE_NA') : $this->language->lang($this->error_prefix . 'PHP_SIZE_OVERRUN', $max_filesize, $this->language->lang($unit));
+ break;
+
+ case UPLOAD_ERR_FORM_SIZE:
+ $max_filesize = get_formatted_filesize($this->max_filesize, false);
+
+ $error = $this->language->lang($this->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']);
+ break;
+
+ case UPLOAD_ERR_PARTIAL:
+ $error = $this->language->lang($this->error_prefix . 'PARTIAL_UPLOAD');
+ break;
+
+ case UPLOAD_ERR_NO_FILE:
+ $error = $this->language->lang($this->error_prefix . 'NOT_UPLOADED');
+ break;
+
+ case UPLOAD_ERR_NO_TMP_DIR:
+ case UPLOAD_ERR_CANT_WRITE:
+ $error = $this->language->lang($this->error_prefix . 'NO_TEMP_DIR');
+ break;
+
+ case UPLOAD_ERR_EXTENSION:
+ $error = $this->language->lang($this->error_prefix . 'PHP_UPLOAD_STOPPED');
+ break;
+
+ default:
+ $error = false;
+ break;
+ }
+
+ return $error;
+ }
+
+ /**
+ * Perform common file checks
+ *
+ * @param filespec $file Instance of filespec class
+ */
+ public function common_checks(&$file)
+ {
+ // Filesize is too big or it's 0 if it was larger than the maxsize in the upload form
+ if ($this->max_filesize && ($file->get('filesize') > $this->max_filesize || $file->get('filesize') == 0))
+ {
+ $max_filesize = get_formatted_filesize($this->max_filesize, false);
+
+ $file->error[] = $this->language->lang($this->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']);
+ }
+
+ // check Filename
+ if (preg_match("#[\\/:*?\"<>|]#i", $file->get('realname')))
+ {
+ $file->error[] = $this->language->lang($this->error_prefix . 'INVALID_FILENAME', $file->get('realname'));
+ }
+
+ // Invalid Extension
+ if (!$this->valid_extension($file))
+ {
+ $file->error[] = $this->language->lang($this->error_prefix . 'DISALLOWED_EXTENSION', $file->get('extension'));
+ }
+
+ // MIME Sniffing
+ if (!$this->valid_content($file))
+ {
+ $file->error[] = $this->language->lang($this->error_prefix . 'DISALLOWED_CONTENT');
+ }
+ }
+
+ /**
+ * Check for allowed extension
+ *
+ * @param filespec $file Instance of filespec class
+ *
+ * @return bool True if extension is allowed, false if not
+ */
+ public function valid_extension(&$file)
+ {
+ return (in_array($file->get('extension'), $this->allowed_extensions)) ? true : false;
+ }
+
+ /**
+ * Check for allowed dimension
+ *
+ * @param filespec $file Instance of filespec class
+ *
+ * @return bool True if dimensions are valid or no constraints set, false
+ * if not
+ */
+ public function valid_dimensions(&$file)
+ {
+ if (!$this->max_width && !$this->max_height && !$this->min_width && !$this->min_height)
+ {
+ return true;
+ }
+
+ if (($file->get('width') > $this->max_width && $this->max_width) ||
+ ($file->get('height') > $this->max_height && $this->max_height) ||
+ ($file->get('width') < $this->min_width && $this->min_width) ||
+ ($file->get('height') < $this->min_height && $this->min_height))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if form upload is valid
+ *
+ * @param string $form_name Name of form
+ *
+ * @return bool True if form upload is valid, false if not
+ */
+ public function is_valid($form_name)
+ {
+ $upload = $this->request->file($form_name);
+
+ return (!empty($upload) && $upload['name'] !== 'none');
+ }
+
+
+ /**
+ * Check for bad content (IE mime-sniffing)
+ *
+ * @param filespec $file Instance of filespec class
+ *
+ * @return bool True if content is valid, false if not
+ */
+ public function valid_content(&$file)
+ {
+ return ($file->check_content($this->disallowed_content));
+ }
+
+ /**
+ * Get image type/extension mapping
+ *
+ * @return array Array containing the image types and their extensions
+ */
+ static public function image_types()
+ {
+ $result = array(
+ IMAGETYPE_GIF => array('gif'),
+ IMAGETYPE_JPEG => array('jpg', 'jpeg'),
+ IMAGETYPE_PNG => array('png'),
+ IMAGETYPE_SWF => array('swf'),
+ IMAGETYPE_PSD => array('psd'),
+ IMAGETYPE_BMP => array('bmp'),
+ IMAGETYPE_TIFF_II => array('tif', 'tiff'),
+ IMAGETYPE_TIFF_MM => array('tif', 'tiff'),
+ IMAGETYPE_JPC => array('jpg', 'jpeg'),
+ IMAGETYPE_JP2 => array('jpg', 'jpeg'),
+ IMAGETYPE_JPX => array('jpg', 'jpeg'),
+ IMAGETYPE_JB2 => array('jpg', 'jpeg'),
+ IMAGETYPE_IFF => array('iff'),
+ IMAGETYPE_WBMP => array('wbmp'),
+ IMAGETYPE_XBM => array('xbm'),
+ );
+
+ if (defined('IMAGETYPE_SWC'))
+ {
+ $result[IMAGETYPE_SWC] = array('swc');
+ }
+
+ return $result;
+ }
+}
diff --git a/phpBB/phpbb/filesystem.php b/phpBB/phpbb/filesystem.php
index 7878be0a5e..af56d78845 100644
--- a/phpBB/phpbb/filesystem.php
+++ b/phpBB/phpbb/filesystem.php
@@ -1,47 +1,21 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb;
/**
-* A class with various functions that are related to paths, files and the filesystem
-* @package phpBB3
-*/
-class filesystem
+ * @deprecated 3.2.0-dev (To be removed 3.3.0) use \phpbb\filesystem\filesystem instead
+ */
+class filesystem extends \phpbb\filesystem\filesystem
{
- /**
- * Eliminates useless . and .. components from specified path.
- *
- * @param string $path Path to clean
- * @return string Cleaned path
- */
- public function clean_path($path)
- {
- $exploded = explode('/', $path);
- $filtered = array();
- foreach ($exploded as $part)
- {
- if ($part === '.' && !empty($filtered))
- {
- continue;
- }
-
- if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '..')
- {
- array_pop($filtered);
- }
- else
- {
- $filtered[] = $part;
- }
- }
- $path = implode('/', $filtered);
- return $path;
- }
}
diff --git a/phpBB/phpbb/filesystem/exception/filesystem_exception.php b/phpBB/phpbb/filesystem/exception/filesystem_exception.php
new file mode 100644
index 0000000000..d68fa9adf3
--- /dev/null
+++ b/phpBB/phpbb/filesystem/exception/filesystem_exception.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\filesystem\exception;
+
+class filesystem_exception extends \phpbb\exception\runtime_exception
+{
+ /**
+ * Constructor
+ *
+ * @param string $message The Exception message to throw (must be a language variable).
+ * @param string $filename The file that caused the error.
+ * @param array $parameters The parameters to use with the language var.
+ * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining.
+ * @param integer $code The Exception code.
+ */
+ public function __construct($message = "", $filename = '', $parameters = array(), \Exception $previous = null, $code = 0)
+ {
+ parent::__construct($message, array_merge(array('filename' => $filename), $parameters), $previous, $code);
+ }
+
+ /**
+ * Returns the filename that triggered the error
+ *
+ * @return string
+ */
+ public function get_filename()
+ {
+ $parameters = parent::get_parameters();
+ return $parameters['filename'];
+ }
+}
diff --git a/phpBB/phpbb/filesystem/filesystem.php b/phpBB/phpbb/filesystem/filesystem.php
new file mode 100644
index 0000000000..2112882d1d
--- /dev/null
+++ b/phpBB/phpbb/filesystem/filesystem.php
@@ -0,0 +1,916 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\filesystem;
+
+use phpbb\filesystem\exception\filesystem_exception;
+
+/**
+ * A class with various functions that are related to paths, files and the filesystem
+ */
+class filesystem implements filesystem_interface
+{
+ /**
+ * Store some information about file ownership for phpBB's chmod function
+ *
+ * @var array
+ */
+ protected $chmod_info;
+
+ /**
+ * Stores current working directory
+ *
+ * @var string|bool current working directory or false if it cannot be recovered
+ */
+ protected $working_directory;
+
+ /**
+ * Symfony's Filesystem component
+ *
+ * @var \Symfony\Component\Filesystem\Filesystem
+ */
+ protected $symfony_filesystem;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->chmod_info = array();
+ $this->symfony_filesystem = new \Symfony\Component\Filesystem\Filesystem();
+ $this->working_directory = null;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function chgrp($files, $group, $recursive = false)
+ {
+ try
+ {
+ $this->symfony_filesystem->chgrp($files, $group, $recursive);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ // Try to recover filename
+ // By the time this is written that is at the end of the message
+ $error = trim($e->getMessage());
+ $file = substr($error, strrpos($error, ' '));
+
+ throw new filesystem_exception('CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
+ {
+ if (is_null($perms))
+ {
+ // Default to read permission for compatibility reasons
+ $perms = self::CHMOD_READ;
+ }
+
+ // Check if we got a permission flag
+ if ($perms > self::CHMOD_ALL)
+ {
+ $file_perm = $perms;
+
+ // Extract permissions
+ //$owner = ($file_perm >> 6) & 7; // This will be ignored
+ $group = ($file_perm >> 3) & 7;
+ $other = ($file_perm >> 0) & 7;
+
+ // Does any permissions provided? if so we add execute bit for directories
+ $group = ($group !== 0) ? ($group | self::CHMOD_EXECUTE) : $group;
+ $other = ($other !== 0) ? ($other | self::CHMOD_EXECUTE) : $other;
+
+ // Compute directory permissions
+ $dir_perm = (self::CHMOD_ALL << 6) + ($group << 3) + ($other << 3);
+ }
+ else
+ {
+ // Add execute bit to owner if execute bit is among perms
+ $owner_perm = (self::CHMOD_READ | self::CHMOD_WRITE) | ($perms & self::CHMOD_EXECUTE);
+ $file_perm = ($owner_perm << 6) + ($perms << 3) + ($perms << 0);
+
+ // Compute directory permissions
+ $perm = ($perms !== 0) ? ($perms | self::CHMOD_EXECUTE) : $perms;
+ $dir_perm = (($owner_perm | self::CHMOD_EXECUTE) << 6) + ($perm << 3) + ($perm << 0);
+ }
+
+ // Symfony's filesystem component does not support extra execution flags on directories
+ // so we need to implement it again
+ foreach ($this->to_iterator($files) as $file)
+ {
+ if ($recursive && is_dir($file) && !is_link($file))
+ {
+ $this->chmod(new \FilesystemIterator($file), $perms, true);
+ }
+
+ // Don't chmod links as mostly those require 0777 and that cannot be changed
+ if (is_dir($file) || (is_link($file) && $force_chmod_link))
+ {
+ if (true !== @chmod($file, $dir_perm))
+ {
+ throw new filesystem_exception('CANNOT_CHANGE_FILE_PERMISSIONS', $file, array());
+ }
+ }
+ else if (is_file($file))
+ {
+ if (true !== @chmod($file, $file_perm))
+ {
+ throw new filesystem_exception('CANNOT_CHANGE_FILE_PERMISSIONS', $file, array());
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function chown($files, $user, $recursive = false)
+ {
+ try
+ {
+ $this->symfony_filesystem->chown($files, $user, $recursive);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ // Try to recover filename
+ // By the time this is written that is at the end of the message
+ $error = trim($e->getMessage());
+ $file = substr($error, strrpos($error, ' '));
+
+ throw new filesystem_exception('CANNOT_CHANGE_FILE_GROUP', $file, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clean_path($path)
+ {
+ $exploded = explode('/', $path);
+ $filtered = array();
+ foreach ($exploded as $part)
+ {
+ if ($part === '.' && !empty($filtered))
+ {
+ continue;
+ }
+
+ if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '.' && $filtered[sizeof($filtered) - 1] !== '..')
+ {
+ array_pop($filtered);
+ }
+ else
+ {
+ $filtered[] = $part;
+ }
+ }
+ $path = implode('/', $filtered);
+ return $path;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function copy($origin_file, $target_file, $override = false)
+ {
+ try
+ {
+ $this->symfony_filesystem->copy($origin_file, $target_file, $override);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ throw new filesystem_exception('CANNOT_COPY_FILES', '', array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dump_file($filename, $content)
+ {
+ try
+ {
+ $this->symfony_filesystem->dumpFile($filename, $content);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ throw new filesystem_exception('CANNOT_DUMP_FILE', $filename, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function exists($files)
+ {
+ return $this->symfony_filesystem->exists($files);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_absolute_path($path)
+ {
+ return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_readable($files, $recursive = false)
+ {
+ foreach ($this->to_iterator($files) as $file)
+ {
+ if ($recursive && is_dir($file) && !is_link($file))
+ {
+ if (!$this->is_readable(new \FilesystemIterator($file), true))
+ {
+ return false;
+ }
+ }
+
+ if (!is_readable($file))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_writable($files, $recursive = false)
+ {
+ if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('is_writable'))
+ {
+ foreach ($this->to_iterator($files) as $file)
+ {
+ if ($recursive && is_dir($file) && !is_link($file))
+ {
+ if (!$this->is_writable(new \FilesystemIterator($file), true))
+ {
+ return false;
+ }
+ }
+
+ if (!$this->phpbb_is_writable($file))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // use built in is_writable
+ foreach ($this->to_iterator($files) as $file)
+ {
+ if ($recursive && is_dir($file) && !is_link($file))
+ {
+ if (!$this->is_writable(new \FilesystemIterator($file), true))
+ {
+ return false;
+ }
+ }
+
+ if (!is_writable($file))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function make_path_relative($end_path, $start_path)
+ {
+ return $this->symfony_filesystem->makePathRelative($end_path, $start_path);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array())
+ {
+ try
+ {
+ $this->symfony_filesystem->mirror($origin_dir, $target_dir, $iterator, $options);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ $msg = $e->getMessage();
+ $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
+
+ throw new filesystem_exception('CANNOT_MIRROR_DIRECTORY', $filename, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mkdir($dirs, $mode = 0777)
+ {
+ try
+ {
+ $this->symfony_filesystem->mkdir($dirs, $mode);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ $msg = $e->getMessage();
+ $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
+
+ throw new filesystem_exception('CANNOT_CREATE_DIRECTORY', $filename, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function phpbb_chmod($files, $perms = null, $recursive = false, $force_chmod_link = false)
+ {
+ if (is_null($perms))
+ {
+ // Default to read permission for compatibility reasons
+ $perms = self::CHMOD_READ;
+ }
+
+ if (empty($this->chmod_info))
+ {
+ if (!function_exists('fileowner') || !function_exists('filegroup'))
+ {
+ $this->chmod_info['process'] = false;
+ }
+ else
+ {
+ $common_php_owner = @fileowner(__FILE__);
+ $common_php_group = @filegroup(__FILE__);
+
+ // And the owner and the groups PHP is running under.
+ $php_uid = (function_exists('posic_getuid')) ? @posix_getuid() : false;
+ $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
+
+ // If we are unable to get owner/group, then do not try to set them by guessing
+ if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
+ {
+ $this->chmod_info['process'] = false;
+ }
+ else
+ {
+ $this->chmod_info = array(
+ 'process' => true,
+ 'common_owner' => $common_php_owner,
+ 'common_group' => $common_php_group,
+ 'php_uid' => $php_uid,
+ 'php_gids' => $php_gids,
+ );
+ }
+ }
+ }
+
+ if ($this->chmod_info['process'])
+ {
+ try
+ {
+ foreach ($this->to_iterator($files) as $file)
+ {
+ $file_uid = @fileowner($file);
+ $file_gid = @filegroup($file);
+
+ // Change owner
+ if ($file_uid !== $this->chmod_info['common_owner'])
+ {
+ $this->chown($file, $this->chmod_info['common_owner'], $recursive);
+ }
+
+ // Change group
+ if ($file_gid !== $this->chmod_info['common_group'])
+ {
+ $this->chgrp($file, $this->chmod_info['common_group'], $recursive);
+ }
+
+ clearstatcache();
+ $file_uid = @fileowner($file);
+ $file_gid = @filegroup($file);
+ }
+ }
+ catch (filesystem_exception $e)
+ {
+ $this->chmod_info['process'] = false;
+ }
+ }
+
+ // Still able to process?
+ if ($this->chmod_info['process'])
+ {
+ if ($file_uid === $this->chmod_info['php_uid'])
+ {
+ $php = 'owner';
+ }
+ else if (in_array($file_gid, $this->chmod_info['php_gids']))
+ {
+ $php = 'group';
+ }
+ else
+ {
+ // Since we are setting the everyone bit anyway, no need to do expensive operations
+ $this->chmod_info['process'] = false;
+ }
+ }
+
+ // We are not able to determine or change something
+ if (!$this->chmod_info['process'])
+ {
+ $php = 'other';
+ }
+
+ switch ($php)
+ {
+ case 'owner':
+ try
+ {
+ $this->chmod($files, $perms, $recursive, $force_chmod_link);
+ clearstatcache();
+ if ($this->is_readable($files) && $this->is_writable($files))
+ {
+ break;
+ }
+ }
+ catch (filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ case 'group':
+ try
+ {
+ $this->chmod($files, $perms, $recursive, $force_chmod_link);
+ clearstatcache();
+ if ((!($perms & self::CHMOD_READ) || $this->is_readable($files, $recursive)) && (!($perms & self::CHMOD_WRITE) || $this->is_writable($files, $recursive)))
+ {
+ break;
+ }
+ }
+ catch (filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ case 'other':
+ default:
+ $this->chmod($files, $perms, $recursive, $force_chmod_link);
+ break;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function realpath($path)
+ {
+ if (!function_exists('realpath'))
+ {
+ return $this->phpbb_own_realpath($path);
+ }
+
+ $realpath = realpath($path);
+
+ // Strangely there are provider not disabling realpath but returning strange values. :o
+ // We at least try to cope with them.
+ if ((!$this->is_absolute_path($path) && $realpath === $path) || $realpath === false)
+ {
+ return $this->phpbb_own_realpath($path);
+ }
+
+ // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
+ if (substr($realpath, -1) === DIRECTORY_SEPARATOR)
+ {
+ $realpath = substr($realpath, 0, -1);
+ }
+
+ return $realpath;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove($files)
+ {
+ try
+ {
+ $this->symfony_filesystem->remove($files);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ // Try to recover filename
+ // By the time this is written that is at the end of the message
+ $error = trim($e->getMessage());
+ $file = substr($error, strrpos($error, ' '));
+
+ throw new filesystem_exception('CANNOT_DELETE_FILES', $file, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function rename($origin, $target, $overwrite = false)
+ {
+ try
+ {
+ $this->symfony_filesystem->rename($origin, $target, $overwrite);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ $msg = $e->getMessage();
+ $filename = substr($msg, strpos($msg, '"'), strrpos($msg, '"'));
+
+ throw new filesystem_exception('CANNOT_RENAME_FILE', $filename, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function symlink($origin_dir, $target_dir, $copy_on_windows = false)
+ {
+ try
+ {
+ $this->symfony_filesystem->symlink($origin_dir, $target_dir, $copy_on_windows);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ throw new filesystem_exception('CANNOT_CREATE_SYMLINK', $origin_dir, array(), $e);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function touch($files, $time = null, $access_time = null)
+ {
+ try
+ {
+ $this->symfony_filesystem->touch($files, $time, $access_time);
+ }
+ catch (\Symfony\Component\Filesystem\Exception\IOException $e)
+ {
+ // Try to recover filename
+ // By the time this is written that is at the end of the message
+ $error = trim($e->getMessage());
+ $file = substr($error, strrpos($error, ' '));
+
+ throw new filesystem_exception('CANNOT_TOUCH_FILES', $file, array(), $e);
+ }
+ }
+
+ /**
+ * phpBB's implementation of is_writable
+ *
+ * @todo Investigate if is_writable is still buggy
+ *
+ * @param string $file file/directory to check if writable
+ *
+ * @return bool true if the given path is writable
+ */
+ protected function phpbb_is_writable($file)
+ {
+ if (file_exists($file))
+ {
+ // Canonicalise path to absolute path
+ $file = $this->realpath($file);
+
+ if (is_dir($file))
+ {
+ // Test directory by creating a file inside the directory
+ $result = @tempnam($file, 'i_w');
+
+ if (is_string($result) && file_exists($result))
+ {
+ unlink($result);
+
+ // Ensure the file is actually in the directory (returned realpathed)
+ return (strpos($result, $file) === 0) ? true : false;
+ }
+ }
+ else
+ {
+ $handle = @fopen($file, 'c');
+
+ if (is_resource($handle))
+ {
+ fclose($handle);
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // file does not exist test if we can write to the directory
+ $dir = dirname($file);
+
+ if (file_exists($dir) && is_dir($dir) && $this->phpbb_is_writable($dir))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Try to resolve real path when PHP's realpath failes to do so
+ *
+ * @param string $path
+ * @return bool|string
+ */
+ protected function phpbb_own_realpath($path)
+ {
+ // Replace all directory separators with '/'
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
+
+ $is_absolute_path = false;
+ $path_prefix = '';
+
+ if ($this->is_absolute_path($path))
+ {
+ $is_absolute_path = true;
+ }
+ else
+ {
+ // Resolve working directory and store it
+ if (is_null($this->working_directory))
+ {
+ if (function_exists('getcwd'))
+ {
+ $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd());
+ }
+
+ //
+ // From this point on we really just guessing
+ // If chdir were called we screwed
+ //
+ else if (function_exists('debug_backtrace'))
+ {
+ $call_stack = debug_backtrace(0);
+ $this->working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[sizeof($call_stack) - 1]['file']));
+ }
+ else
+ {
+ //
+ // Assuming that the working directory is phpBB root
+ // we could use this as a fallback, when phpBB will use controllers
+ // everywhere this will be a safe assumption
+ //
+ //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__);
+ //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\'));
+
+ //$namespace_part_count = sizeof($namespace_parts);
+
+ // Check if we still loading from root
+ //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts)
+ //{
+ // $this->working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count));
+ //}
+ //else
+ //{
+ // $this->working_directory = false;
+ //}
+
+ $this->working_directory = false;
+ }
+ }
+
+ if ($this->working_directory !== false)
+ {
+ $is_absolute_path = true;
+ $path = $this->working_directory . '/' . $path;
+ }
+ }
+
+ if ($is_absolute_path)
+ {
+ if (defined('PHP_WINDOWS_VERSION_MAJOR'))
+ {
+ $path_prefix = $path[0] . ':';
+ $path = substr($path, 2);
+ }
+ else
+ {
+ $path_prefix = '';
+ }
+ }
+
+ $resolved_path = $this->resolve_path($path, $path_prefix, $is_absolute_path);
+ if ($resolved_path === false)
+ {
+ return false;
+ }
+
+ if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path)))
+ {
+ return false;
+ }
+
+ // Return OS specific directory separators
+ $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path);
+
+ // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
+ if (substr($resolved, -1) === DIRECTORY_SEPARATOR)
+ {
+ return substr($resolved, 0, -1);
+ }
+
+ return $resolved;
+ }
+
+ /**
+ * Convert file(s) to \Traversable object
+ *
+ * This is the same function as Symfony's toIterator, but that is private
+ * so we cannot use it.
+ *
+ * @param string|array|\Traversable $files filename/list of filenames
+ * @return \Traversable
+ */
+ protected function to_iterator($files)
+ {
+ if (!$files instanceof \Traversable)
+ {
+ $files = new \ArrayObject(is_array($files) ? $files : array($files));
+ }
+
+ return $files;
+ }
+
+ /**
+ * Try to resolve symlinks in path
+ *
+ * @param string $path The path to resolve
+ * @param string $prefix The path prefix (on windows the drive letter)
+ * @param bool $absolute Whether or not the path is absolute
+ * @param bool $return_array Whether or not to return path parts
+ *
+ * @return string|array|bool returns the resolved path or an array of parts of the path if $return_array is true
+ * or false if path cannot be resolved
+ */
+ protected function resolve_path($path, $prefix = '', $absolute = false, $return_array = false)
+ {
+ if ($return_array)
+ {
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
+ }
+
+ trim ($path, '/');
+ $path_parts = explode('/', $path);
+ $resolved = array();
+ $resolved_path = $prefix;
+ $file_found = false;
+
+ foreach ($path_parts as $path_part)
+ {
+ if ($file_found)
+ {
+ return false;
+ }
+
+ if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved))))
+ {
+ continue;
+ }
+ else if ($absolute && $path_part === '..')
+ {
+ if (empty($resolved))
+ {
+ // No directories above root
+ return false;
+ }
+
+ array_pop($resolved);
+ $resolved_path = false;
+ }
+ else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[sizeof($resolved) - 1], array('.', '..')))
+ {
+ array_pop($resolved);
+ $resolved_path = false;
+ }
+ else
+ {
+ if ($resolved_path === false)
+ {
+ if (empty($resolved))
+ {
+ $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part;
+ }
+ else
+ {
+ $tmp_array = $resolved;
+ if ($absolute)
+ {
+ array_unshift($tmp_array, $prefix);
+ }
+
+ $resolved_path = implode('/', $tmp_array);
+ }
+ }
+
+ $current_path = $resolved_path . '/' . $path_part;
+
+ // Resolve symlinks
+ if (is_link($current_path))
+ {
+ if (!function_exists('readlink'))
+ {
+ return false;
+ }
+
+ $link = readlink($current_path);
+
+ // Is link has an absolute path in it?
+ if ($this->is_absolute_path($link))
+ {
+ if (defined('PHP_WINDOWS_VERSION_MAJOR'))
+ {
+ $prefix = $link[0] . ':';
+ $link = substr($link, 2);
+ }
+ else
+ {
+ $prefix = '';
+ }
+
+ $resolved = $this->resolve_path($link, $prefix, true, true);
+ $absolute = true;
+ }
+ else
+ {
+ $resolved = $this->resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true);
+ }
+
+ if (!$resolved)
+ {
+ return false;
+ }
+
+ $resolved_path = false;
+ }
+ else if (is_dir($current_path . '/'))
+ {
+ $resolved[] = $path_part;
+ $resolved_path = $current_path;
+ }
+ else if (is_file($current_path))
+ {
+ $resolved[] = $path_part;
+ $resolved_path = $current_path;
+ $file_found = true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // If at the end of the path there were a .. or .
+ // we need to build the path again.
+ // Only doing this when a string is expected in return
+ if ($resolved_path === false && $return_array === false)
+ {
+ if (empty($resolved))
+ {
+ $resolved_path = ($absolute) ? $prefix . '/' : './';
+ }
+ else
+ {
+ $tmp_array = $resolved;
+ if ($absolute)
+ {
+ array_unshift($tmp_array, $prefix);
+ }
+
+ $resolved_path = implode('/', $tmp_array);
+ }
+ }
+
+ return ($return_array) ? $resolved : $resolved_path;
+ }
+}
diff --git a/phpBB/phpbb/filesystem/filesystem_interface.php b/phpBB/phpbb/filesystem/filesystem_interface.php
new file mode 100644
index 0000000000..1093be2499
--- /dev/null
+++ b/phpBB/phpbb/filesystem/filesystem_interface.php
@@ -0,0 +1,284 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\filesystem;
+
+/**
+ * Interface for phpBB's filesystem service
+ */
+interface filesystem_interface
+{
+ /**
+ * chmod all permissions flag
+ *
+ * @var int
+ */
+ const CHMOD_ALL = 7;
+
+ /**
+ * chmod read permissions flag
+ *
+ * @var int
+ */
+ const CHMOD_READ = 4;
+
+ /**
+ * chmod write permissions flag
+ *
+ * @var int
+ */
+ const CHMOD_WRITE = 2;
+
+ /**
+ * chmod execute permissions flag
+ *
+ * @var int
+ */
+ const CHMOD_EXECUTE = 1;
+
+ /**
+ * Change owner group of files/directories
+ *
+ * @param string|array|\Traversable $files The file(s)/directorie(s) to change group
+ * @param string $group The group that should own the files/directories
+ * @param bool $recursive If the group should be changed recursively
+ * @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function chgrp($files, $group, $recursive = false);
+
+ /**
+ * Global function for chmodding directories and files for internal use
+ *
+ * The function accepts filesystem_interface::CHMOD_ flags in the permission argument
+ * or the user can specify octal values (or any integer if it makes sense). All directories will have
+ * an execution bit appended, if the user group (owner, group or other) has any bit specified.
+ *
+ * @param string|array|\Traversable $files The file/directory to be chmodded
+ * @param int $perms Permissions to set
+ * @param bool $recursive If the permissions should be changed recursively
+ * @param bool $force_chmod_link Try to apply permissions to symlinks as well
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function chmod($files, $perms = null, $recursive = false, $force_chmod_link = false);
+
+ /**
+ * Change owner group of files/directories
+ *
+ * @param string|array|\Traversable $files The file(s)/directorie(s) to change group
+ * @param string $user The owner user name
+ * @param bool $recursive Whether change the owner recursively or not
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function chown($files, $user, $recursive = false);
+
+ /**
+ * Eliminates useless . and .. components from specified path.
+ *
+ * @param string $path Path to clean
+ *
+ * @return string Cleaned path
+ */
+ public function clean_path($path);
+
+ /**
+ * Copies a file.
+ *
+ * This method only copies the file if the origin file is newer than the target file.
+ *
+ * By default, if the target already exists, it is not overridden.
+ *
+ * @param string $origin_file The original filename
+ * @param string $target_file The target filename
+ * @param bool $override Whether to override an existing file or not
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be copied
+ */
+ public function copy($origin_file, $target_file, $override = false);
+
+ /**
+ * Atomically dumps content into a file.
+ *
+ * @param string $filename The file to be written to.
+ * @param string $content The data to write into the file.
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be written
+ */
+ public function dump_file($filename, $content);
+
+ /**
+ * Checks the existence of files or directories.
+ *
+ * @param string|array|\Traversable $files files/directories to check
+ *
+ * @return bool Returns true if all files/directories exist, false otherwise
+ */
+ public function exists($files);
+
+ /**
+ * Checks if a path is absolute or not
+ *
+ * @param string $path Path to check
+ *
+ * @return bool true if the path is absolute, false otherwise
+ */
+ public function is_absolute_path($path);
+
+ /**
+ * Checks if files/directories are readable
+ *
+ * @param string|array|\Traversable $files files/directories to check
+ * @param bool $recursive Whether or not directories should be checked recursively
+ *
+ * @return bool True when the files/directories are readable, otherwise false.
+ */
+ public function is_readable($files, $recursive = false);
+
+ /**
+ * Test if a file/directory is writable
+ *
+ * @param string|array|\Traversable $files files/directories to perform write test on
+ * @param bool $recursive Whether or not directories should be checked recursively
+ *
+ * @return bool True when the files/directories are writable, otherwise false.
+ */
+ public function is_writable($files, $recursive = false);
+
+ /**
+ * Given an existing path, convert it to a path relative to a given starting path
+ *
+ * @param string $end_path Absolute path of target
+ * @param string $start_path Absolute path where traversal begins
+ *
+ * @return string Path of target relative to starting path
+ */
+ public function make_path_relative($end_path, $start_path);
+
+ /**
+ * Mirrors a directory to another.
+ *
+ * @param string $origin_dir The origin directory
+ * @param string $target_dir The target directory
+ * @param \Traversable $iterator A Traversable instance
+ * @param array $options An array of boolean options
+ * Valid options are:
+ * - $options['override'] Whether to override an existing file on copy or not (see copy())
+ * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink())
+ * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false)
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When the file cannot be copied.
+ * The filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function mirror($origin_dir, $target_dir, \Traversable $iterator = null, $options = array());
+
+ /**
+ * Creates a directory recursively.
+ *
+ * @param string|array|\Traversable $dirs The directory path
+ * @param int $mode The directory mode
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception On any directory creation failure
+ * The filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function mkdir($dirs, $mode = 0777);
+
+ /**
+ * Global function for chmodding directories and files for internal use
+ *
+ * This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
+ * The function determines owner and group from common.php file and sets the same to the provided file.
+ * The function uses bit fields to build the permissions.
+ * The function sets the appropiate execute bit on directories.
+ *
+ * Supported constants representing bit fields are:
+ *
+ * filesystem_interface::CHMOD_ALL - all permissions (7)
+ * filesystem_interface::CHMOD_READ - read permission (4)
+ * filesystem_interface::CHMOD_WRITE - write permission (2)
+ * filesystem_interface::CHMOD_EXECUTE - execute permission (1)
+ *
+ * NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
+ *
+ * @param string|array|\Traversable $file The file/directory to be chmodded
+ * @param int $perms Permissions to set
+ * @param bool $recursive If the permissions should be changed recursively
+ * @param bool $force_chmod_link Try to apply permissions to symlinks as well
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception the filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function phpbb_chmod($file, $perms = null, $recursive = false, $force_chmod_link = false);
+
+ /**
+ * A wrapper for PHP's realpath
+ *
+ * Try to resolve realpath when PHP's realpath is not available, or
+ * known to be buggy.
+ *
+ * @param string $path Path to resolve
+ *
+ * @return string Resolved path
+ */
+ public function realpath($path);
+
+ /**
+ * Removes files or directories.
+ *
+ * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When removal fails.
+ * The filename which triggered the error can be
+ * retrieved by filesystem_exception::get_filename()
+ */
+ public function remove($files);
+
+ /**
+ * Renames a file or a directory.
+ *
+ * @param string $origin The origin filename or directory
+ * @param string $target The new filename or directory
+ * @param bool $overwrite Whether to overwrite the target if it already exists
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When target file or directory already exists,
+ * or origin cannot be renamed.
+ */
+ public function rename($origin, $target, $overwrite = false);
+
+ /**
+ * Creates a symbolic link or copy a directory.
+ *
+ * @param string $origin_dir The origin directory path
+ * @param string $target_dir The symbolic link name
+ * @param bool $copy_on_windows Whether to copy files if on Windows
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When symlink fails
+ */
+ public function symlink($origin_dir, $target_dir, $copy_on_windows = false);
+
+ /**
+ * Sets access and modification time of file.
+ *
+ * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create
+ * @param int $time The touch time as a Unix timestamp
+ * @param int $access_time The access time as a Unix timestamp
+ *
+ * @throws \phpbb\filesystem\exception\filesystem_exception When touch fails
+ */
+ public function touch($files, $time = null, $access_time = null);
+}
diff --git a/phpBB/phpbb/extension/finder.php b/phpBB/phpbb/finder.php
index c9c16ae6d5..58bc27084e 100644
--- a/phpBB/phpbb/extension/finder.php
+++ b/phpBB/phpbb/finder.php
@@ -1,22 +1,24 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
-namespace phpbb\extension;
+namespace phpbb;
/**
-* The extension finder provides a simple way to locate files in active extensions
-*
-* @package extension
+* The finder provides a simple way to locate files in the core and a set of extensions
*/
class finder
{
- protected $extension_manager;
+ protected $extensions;
protected $filesystem;
protected $phpbb_root_path;
protected $cache;
@@ -46,19 +48,15 @@ class finder
/**
* Creates a new finder instance with its dependencies
*
- * @param \phpbb\extension\manager $extension_manager An extension manager
- * instance that provides the finder with a list of active
- * extensions and their locations
- * @param \phpbb\filesystem $filesystem Filesystem instance
+ * @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem instance
* @param string $phpbb_root_path Path to the phpbb root directory
* @param \phpbb\cache\driver\driver_interface $cache A cache instance or null
* @param string $php_ext php file extension
* @param string $cache_name The name of the cache variable, defaults to
* _ext_finder
*/
- public function __construct(\phpbb\extension\manager $extension_manager, \phpbb\filesystem $filesystem, $phpbb_root_path = '', \phpbb\cache\driver\driver_interface $cache = null, $php_ext = 'php', $cache_name = '_ext_finder')
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path = '', \phpbb\cache\driver\driver_interface $cache = null, $php_ext = 'php', $cache_name = '_ext_finder')
{
- $this->extension_manager = $extension_manager;
$this->filesystem = $filesystem;
$this->phpbb_root_path = $phpbb_root_path;
$this->cache = $cache;
@@ -74,15 +72,37 @@ class finder
'extension_prefix' => false,
'extension_directory' => false,
);
+ $this->extensions = array();
$this->cached_queries = ($this->cache) ? $this->cache->get($this->cache_name) : false;
}
/**
+ * Set the array of extensions
+ *
+ * @param array $extensions A list of extensions that should be searched aswell
+ * @param bool $replace_list Should the list be emptied before adding the extensions
+ * @return \phpbb\finder This object for chaining calls
+ */
+ public function set_extensions(array $extensions, $replace_list = true)
+ {
+ if ($replace_list)
+ {
+ $this->extensions = array();
+ }
+
+ foreach ($extensions as $ext_name)
+ {
+ $this->extensions[$ext_name] = $this->phpbb_root_path . 'ext/' . $ext_name . '/';
+ }
+ return $this;
+ }
+
+ /**
* Sets a core path to be searched in addition to extensions
*
* @param string $core_path The path relative to phpbb_root_path
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function core_path($core_path)
{
@@ -98,7 +118,7 @@ class finder
* file extension is automatically added to suffixes.
*
* @param string $suffix A filename suffix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function suffix($suffix)
{
@@ -115,7 +135,7 @@ class finder
* file extension is automatically added to suffixes.
*
* @param string $extension_suffix A filename suffix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function extension_suffix($extension_suffix)
{
@@ -131,7 +151,7 @@ class finder
* file extension is automatically added to suffixes.
*
* @param string $core_suffix A filename suffix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function core_suffix($core_suffix)
{
@@ -143,7 +163,7 @@ class finder
* Sets the prefix all files found in extensions and core must match
*
* @param string $prefix A filename prefix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function prefix($prefix)
{
@@ -156,7 +176,7 @@ class finder
* Sets a prefix all files found in extensions must match
*
* @param string $extension_prefix A filename prefix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function extension_prefix($extension_prefix)
{
@@ -168,7 +188,7 @@ class finder
* Sets a prefix all files found in the core path must match
*
* @param string $core_prefix A filename prefix
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function core_prefix($core_prefix)
{
@@ -183,7 +203,7 @@ class finder
* the current directory.
*
* @param string $directory
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function directory($directory)
{
@@ -196,7 +216,7 @@ class finder
* Sets a directory all files found in extensions must be contained in
*
* @param string $extension_directory
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function extension_directory($extension_directory)
{
@@ -208,7 +228,7 @@ class finder
* Sets a directory all files found in the core path must be contained in
*
* @param string $core_directory
- * @return \phpbb\extension\finder This object for chaining calls
+ * @return \phpbb\finder This object for chaining calls
*/
public function core_directory($core_directory)
{
@@ -244,16 +264,14 @@ class finder
* phpBB naming rules an incorrect class name will be returned.
*
* @param bool $cache Whether the result should be cached
- * @param bool $use_all_available Use all available instead of just all
- * enabled extensions
* @return array An array of found class names
*/
- public function get_classes($cache = true, $use_all_available = false)
+ public function get_classes($cache = true)
{
$this->query['extension_suffix'] .= '.' . $this->php_ext;
$this->query['core_suffix'] .= '.' . $this->php_ext;
- $files = $this->find($cache, false, $use_all_available);
+ $files = $this->find($cache, false);
return $this->get_classes_from_files($files);
}
@@ -288,27 +306,23 @@ class finder
* Finds all directories matching the configured options
*
* @param bool $cache Whether the result should be cached
- * @param bool $use_all_available Use all available instead of just all
- * enabled extensions
* @param bool $extension_keys Whether the result should have extension name as array key
* @return array An array of paths to found directories
*/
- public function get_directories($cache = true, $use_all_available = false, $extension_keys = false)
+ public function get_directories($cache = true, $extension_keys = false)
{
- return $this->find_with_root_path($cache, true, $use_all_available, $extension_keys);
+ return $this->find_with_root_path($cache, true, $extension_keys);
}
/**
* Finds all files matching the configured options.
*
* @param bool $cache Whether the result should be cached
- * @param bool $use_all_available Use all available instead of just all
- * enabled extensions
* @return array An array of paths to found files
*/
- public function get_files($cache = true, $use_all_available = false)
+ public function get_files($cache = true)
{
- return $this->find_with_root_path($cache, false, $use_all_available);
+ return $this->find_with_root_path($cache, false);
}
/**
@@ -316,16 +330,14 @@ class finder
*
* @param bool $cache Whether the result should be cached
* @param bool $is_dir Directories will be returned when true, only files
- * otherwise
- * @param bool $use_all_available Use all available instead of just all
- * enabled extensions
+ * otherwise
* @param bool $extension_keys If true, result will be associative array
* with extension name as key
* @return array An array of paths to found items
*/
- protected function find_with_root_path($cache = true, $is_dir = false, $use_all_available = false, $extension_keys = false)
+ protected function find_with_root_path($cache = true, $is_dir = false, $extension_keys = false)
{
- $items = $this->find($cache, $is_dir, $use_all_available);
+ $items = $this->find($cache, $is_dir);
$result = array();
foreach ($items as $item => $ext_name)
@@ -349,21 +361,11 @@ class finder
* @param bool $cache Whether the result should be cached
* @param bool $is_dir Directories will be returned when true, only files
* otherwise
- * @param bool $use_all_available Use all available instead of just all
- * enabled extensions
* @return array An array of paths to found items
*/
- public function find($cache = true, $is_dir = false, $use_all_available = false)
+ public function find($cache = true, $is_dir = false)
{
- if ($use_all_available)
- {
- $extensions = $this->extension_manager->all_available();
- }
- else
- {
- $extensions = $this->extension_manager->all_enabled();
- }
-
+ $extensions = $this->extensions;
if ($this->query['core_path'])
{
$extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];
@@ -463,6 +465,10 @@ class finder
}
else if ($directory && $directory[0] === '/')
{
+ if (!$is_dir)
+ {
+ $path .= substr($directory, 1);
+ }
$directory_pattern = '^' . preg_quote(str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR, '#');
}
else
@@ -475,40 +481,56 @@ class finder
}
$directory_pattern = '#' . $directory_pattern . '#';
- $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path), \RecursiveIteratorIterator::SELF_FIRST);
- foreach ($iterator as $file_info)
+ if (is_dir($path))
{
- $filename = $file_info->getFilename();
- if ($filename == '.' || $filename == '..')
+ $iterator = new \RecursiveIteratorIterator(
+ new \phpbb\recursive_dot_prefix_filter_iterator(
+ new \RecursiveDirectoryIterator(
+ $path,
+ \FilesystemIterator::SKIP_DOTS
+ )
+ ),
+ \RecursiveIteratorIterator::SELF_FIRST
+ );
+
+ foreach ($iterator as $file_info)
{
- continue;
- }
+ $filename = $file_info->getFilename();
- if ($file_info->isDir() == $is_dir)
- {
- if ($is_dir)
+ if ($file_info->isDir() == $is_dir)
{
- $relative_path = $iterator->getInnerIterator()->getSubPath() . DIRECTORY_SEPARATOR . basename($filename) . DIRECTORY_SEPARATOR;
- if ($relative_path[0] !== DIRECTORY_SEPARATOR)
+ if ($is_dir)
{
- $relative_path = DIRECTORY_SEPARATOR . $relative_path;
+ $relative_path = $iterator->getInnerIterator()->getSubPath() . DIRECTORY_SEPARATOR . basename($filename) . DIRECTORY_SEPARATOR;
+ if ($relative_path[0] !== DIRECTORY_SEPARATOR)
+ {
+ $relative_path = DIRECTORY_SEPARATOR . $relative_path;
+ }
+ }
+ else
+ {
+ $relative_path = $iterator->getInnerIterator()->getSubPathname();
+ if ($directory && $directory[0] === '/')
+ {
+ $relative_path = str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR . $relative_path;
+ }
+ else
+ {
+ $relative_path = DIRECTORY_SEPARATOR . $relative_path;
+ }
}
- }
- else
- {
- $relative_path = DIRECTORY_SEPARATOR . $iterator->getInnerIterator()->getSubPathname();
- }
- if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) &&
- (!$prefix || substr($filename, 0, strlen($prefix)) === $prefix) &&
- (!$directory || preg_match($directory_pattern, $relative_path)))
- {
- $files[] = array(
- 'named_path' => str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1)),
- 'ext_name' => $ext_name,
- 'path' => str_replace(array(DIRECTORY_SEPARATOR, $this->phpbb_root_path), array('/', ''), $file_info->getPath()) . '/',
- 'filename' => $filename,
- );
+ if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) &&
+ (!$prefix || substr($filename, 0, strlen($prefix)) === $prefix) &&
+ (!$directory || preg_match($directory_pattern, $relative_path)))
+ {
+ $files[] = array(
+ 'named_path' => str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1)),
+ 'ext_name' => $ext_name,
+ 'path' => str_replace(array(DIRECTORY_SEPARATOR, $this->phpbb_root_path), array('/', ''), $file_info->getPath()) . '/',
+ 'filename' => $filename,
+ );
+ }
}
}
}
diff --git a/phpBB/phpbb/group/helper.php b/phpBB/phpbb/group/helper.php
new file mode 100644
index 0000000000..5befddfc53
--- /dev/null
+++ b/phpBB/phpbb/group/helper.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\group;
+
+class helper
+{
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\language\language $language Language object
+ */
+ public function __construct(\phpbb\language\language $language)
+ {
+ $this->language = $language;
+ }
+
+ /**
+ * @param $group_name string The stored group name
+ *
+ * @return string Group name or translated group name if it exists
+ */
+ public function get_name($group_name)
+ {
+ return $this->language->is_set('G_' . utf8_strtoupper($group_name)) ? $this->language->lang('G_' . utf8_strtoupper($group_name)) : $group_name;
+ }
+}
diff --git a/phpBB/phpbb/groupposition/exception.php b/phpBB/phpbb/groupposition/exception.php
index f43502235d..956c7238f2 100644
--- a/phpBB/phpbb/groupposition/exception.php
+++ b/phpBB/phpbb/groupposition/exception.php
@@ -1,17 +1,18 @@
<?php
/**
*
-* @package groupposition
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\groupposition;
-/**
-* @package groupposition
-*/
class exception extends \Exception
{
}
diff --git a/phpBB/phpbb/groupposition/groupposition_interface.php b/phpBB/phpbb/groupposition/groupposition_interface.php
index 9785172a00..3bd3fcce90 100644
--- a/phpBB/phpbb/groupposition/groupposition_interface.php
+++ b/phpBB/phpbb/groupposition/groupposition_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ namespace phpbb\groupposition;
* Interface to manage group positions in various places of phpbb
*
* The interface provides simple methods to add, delete and move a group
-*
-* @package phpBB3
*/
interface groupposition_interface
{
diff --git a/phpBB/phpbb/groupposition/legend.php b/phpBB/phpbb/groupposition/legend.php
index 47ba06c006..efea3389d4 100644
--- a/phpBB/phpbb/groupposition/legend.php
+++ b/phpBB/phpbb/groupposition/legend.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,8 +18,6 @@ namespace phpbb\groupposition;
*
* group_legend is an ascending list 1, 2, ..., n for groups which are displayed. 1 is the first group, n the last.
* If the value is 0 (self::GROUP_DISABLED) the group is not displayed.
-*
-* @package phpBB3
*/
class legend implements \phpbb\groupposition\groupposition_interface
{
@@ -26,7 +28,7 @@ class legend implements \phpbb\groupposition\groupposition_interface
/**
* Database object
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -39,10 +41,10 @@ class legend implements \phpbb\groupposition\groupposition_interface
/**
* Constructor
*
- * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\db\driver\driver_interface $db Database object
* @param \phpbb\user $user User object
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\user $user)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user)
{
$this->db = $db;
$this->user = $user;
@@ -51,7 +53,9 @@ class legend implements \phpbb\groupposition\groupposition_interface
/**
* Returns the group_legend for a given group, if the group exists.
*
- * {@inheritDoc}
+ * @param int $group_id group_id of the group to be selected
+ * @return int position of the group
+ * @throws \phpbb\groupposition\exception
*/
public function get_group_value($group_id)
{
@@ -74,7 +78,7 @@ class legend implements \phpbb\groupposition\groupposition_interface
/**
* Get number of groups, displayed on the legend
*
- * {@inheritDoc}
+ * @return int value of the last item displayed
*/
public function get_group_count()
{
@@ -89,8 +93,6 @@ class legend implements \phpbb\groupposition\groupposition_interface
}
/**
- * Adds a group by group_id
- *
* {@inheritDoc}
*/
public function add_group($group_id)
@@ -116,7 +118,9 @@ class legend implements \phpbb\groupposition\groupposition_interface
/**
* Deletes a group by setting the field to self::GROUP_DISABLED and closing the gap in the list.
*
- * {@inheritDoc}
+ * @param int $group_id group_id of the group to be deleted
+ * @param bool $skip_group Skip setting the value for this group, to save the query, when you need to update it anyway.
+ * @return bool True if the group was deleted successfully
*/
public function delete_group($group_id, $skip_group = false)
{
@@ -148,8 +152,6 @@ class legend implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group up by group_id
- *
* {@inheritDoc}
*/
public function move_up($group_id)
@@ -158,8 +160,6 @@ class legend implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group down by group_id
- *
* {@inheritDoc}
*/
public function move_down($group_id)
@@ -168,8 +168,6 @@ class legend implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group up/down
- *
* {@inheritDoc}
*/
public function move($group_id, $delta)
diff --git a/phpBB/phpbb/groupposition/teampage.php b/phpBB/phpbb/groupposition/teampage.php
index d934571ebc..2985c51525 100644
--- a/phpBB/phpbb/groupposition/teampage.php
+++ b/phpBB/phpbb/groupposition/teampage.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ namespace phpbb\groupposition;
* Teampage group position class
*
* Teampage position is an ascending list 1, 2, ..., n for items which are displayed. 1 is the first item, n the last.
-*
-* @package phpBB3
*/
class teampage implements \phpbb\groupposition\groupposition_interface
{
@@ -30,7 +32,7 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
* Database object
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -49,11 +51,11 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
* Constructor
*
- * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\db\driver\driver_interface $db Database object
* @param \phpbb\user $user User object
* @param \phpbb\cache\driver\driver_interface $cache Cache object
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\user $user, \phpbb\cache\driver\driver_interface $cache)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, \phpbb\cache\driver\driver_interface $cache)
{
$this->db = $db;
$this->user = $user;
@@ -63,7 +65,9 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
* Returns the teampage position for a given group, if the group exists.
*
- * {@inheritDoc}
+ * @param int $group_id group_id of the group to be selected
+ * @return int position of the group
+ * @throws \phpbb\groupposition\exception
*/
public function get_group_value($group_id)
{
@@ -91,6 +95,7 @@ class teampage implements \phpbb\groupposition\groupposition_interface
*
* @param int $group_id group_id of the group to be selected
* @return array Data row of the group
+ * @throws \phpbb\groupposition\exception
*/
public function get_group_values($group_id)
{
@@ -118,6 +123,7 @@ class teampage implements \phpbb\groupposition\groupposition_interface
*
* @param int $teampage_id Teampage_id of the selected item
* @return int Teampage position of the item
+ * @throws \phpbb\groupposition\exception
*/
public function get_teampage_value($teampage_id)
{
@@ -142,6 +148,7 @@ class teampage implements \phpbb\groupposition\groupposition_interface
*
* @param int $teampage_id Teampage_id of the selected item
* @return array Teampage row of the item
+ * @throws \phpbb\groupposition\exception
*/
public function get_teampage_values($teampage_id)
{
@@ -163,8 +170,6 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
- * Get number of items displayed
- *
* {@inheritDoc}
*/
public function get_group_count()
@@ -180,8 +185,6 @@ class teampage implements \phpbb\groupposition\groupposition_interface
}
/**
- * Adds a group by group_id
- *
* {@inheritDoc}
*/
public function add_group($group_id)
@@ -286,7 +289,9 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
* Deletes a group from the list and closes the gap in the position list.
*
- * {@inheritDoc}
+ * @param int $group_id group_id of the group to be deleted
+ * @param bool $skip_group Skip setting the value for this group, to save the query, when you need to update it anyway.
+ * @return bool True if the group was deleted successfully
*/
public function delete_group($group_id, $skip_group = false)
{
@@ -345,8 +350,6 @@ class teampage implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group up by group_id
- *
* {@inheritDoc}
*/
public function move_up($group_id)
@@ -357,7 +360,7 @@ class teampage implements \phpbb\groupposition\groupposition_interface
/**
* Moves an item up by teampage_id
*
- * @param int $group_id group_id of the group to be moved
+ * @param int $teampage_id teampage_id of the item to be move
* @return bool True if the group was moved successfully
*/
public function move_up_teampage($teampage_id)
@@ -366,8 +369,6 @@ class teampage implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group down by group_id
- *
* {@inheritDoc}
*/
public function move_down($group_id)
@@ -376,9 +377,9 @@ class teampage implements \phpbb\groupposition\groupposition_interface
}
/**
- * Movesan item down by teampage_id
+ * Moves an item down by teampage_id
*
- * @param int $group_id group_id of the group to be moved
+ * @param int $teampage_id teampage_id of the item to be moved
* @return bool True if the group was moved successfully
*/
public function move_down_teampage($teampage_id)
@@ -387,8 +388,6 @@ class teampage implements \phpbb\groupposition\groupposition_interface
}
/**
- * Moves a group up/down
- *
* {@inheritDoc}
*/
public function move($group_id, $delta)
diff --git a/phpBB/phpbb/help/controller/bbcode.php b/phpBB/phpbb/help/controller/bbcode.php
new file mode 100644
index 0000000000..e16f99023d
--- /dev/null
+++ b/phpBB/phpbb/help/controller/bbcode.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\help\controller;
+
+/**
+ * BBCode help page
+ */
+class bbcode extends controller
+{
+ /**
+ * @return string The title of the page
+ */
+ public function display()
+ {
+ $this->language->add_lang('help/bbcode');
+
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_INTRO',
+ false,
+ array(
+ 'HELP_BBCODE_INTRO_BBCODE_QUESTION' => 'HELP_BBCODE_INTRO_BBCODE_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_TEXT',
+ false,
+ array(
+ 'HELP_BBCODE_TEXT_BASIC_QUESTION' => 'HELP_BBCODE_TEXT_BASIC_ANSWER',
+ 'HELP_BBCODE_TEXT_COLOR_QUESTION' => 'HELP_BBCODE_TEXT_COLOR_ANSWER',
+ 'HELP_BBCODE_TEXT_COMBINE_QUESTION' => 'HELP_BBCODE_TEXT_COMBINE_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_QUOTES',
+ false,
+ array(
+ 'HELP_BBCODE_QUOTES_TEXT_QUESTION' => 'HELP_BBCODE_QUOTES_TEXT_ANSWER',
+ 'HELP_BBCODE_QUOTES_CODE_QUESTION' => 'HELP_BBCODE_QUOTES_CODE_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_LISTS',
+ false,
+ array(
+ 'HELP_BBCODE_LISTS_UNORDERER_QUESTION' => 'HELP_BBCODE_LISTS_UNORDERER_ANSWER',
+ 'HELP_BBCODE_LISTS_ORDERER_QUESTION' => 'HELP_BBCODE_LISTS_ORDERER_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_LINKS',
+ true,
+ array(
+ 'HELP_BBCODE_LINKS_BASIC_QUESTION' => 'HELP_BBCODE_LINKS_BASIC_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_IMAGES',
+ false,
+ array(
+ 'HELP_BBCODE_IMAGES_BASIC_QUESTION' => 'HELP_BBCODE_IMAGES_BASIC_ANSWER',
+ 'HELP_BBCODE_IMAGES_ATTACHMENT_QUESTION' => 'HELP_BBCODE_IMAGES_ATTACHMENT_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_BBCODE_BLOCK_OTHERS',
+ false,
+ array(
+ 'HELP_BBCODE_OTHERS_CUSTOM_QUESTION' => 'HELP_BBCODE_OTHERS_CUSTOM_ANSWER',
+ )
+ );
+
+ return $this->language->lang('BBCODE_GUIDE');
+ }
+}
diff --git a/phpBB/phpbb/help/controller/controller.php b/phpBB/phpbb/help/controller/controller.php
new file mode 100644
index 0000000000..29494205a9
--- /dev/null
+++ b/phpBB/phpbb/help/controller/controller.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\help\controller;
+
+/**
+ * BBCode help page
+ */
+abstract class controller
+{
+ /** @var \phpbb\controller\helper */
+ protected $helper;
+
+ /** @var \phpbb\help\manager */
+ protected $manager;
+
+ /** @var \phpbb\template\template */
+ protected $template;
+
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ /** @var string */
+ protected $root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\controller\helper $helper
+ * @param \phpbb\help\manager $manager
+ * @param \phpbb\template\template $template
+ * @param \phpbb\language\language $language
+ * @param string $root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\controller\helper $helper, \phpbb\help\manager $manager, \phpbb\template\template $template, \phpbb\language\language $language, $root_path, $php_ext)
+ {
+ $this->helper = $helper;
+ $this->manager = $manager;
+ $this->template = $template;
+ $this->language = $language;
+ $this->root_path = $root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function display();
+
+ public function handle()
+ {
+ $title = $this->display();
+
+ $this->template->assign_vars(array(
+ 'L_FAQ_TITLE' => $title,
+ 'S_IN_FAQ' => true,
+ ));
+
+ make_jumpbox(append_sid("{$this->root_path}viewforum.{$this->php_ext}"));
+ return $this->helper->render('faq_body.html', $title);
+ }
+}
diff --git a/phpBB/phpbb/help/controller/faq.php b/phpBB/phpbb/help/controller/faq.php
new file mode 100644
index 0000000000..5e45cfe667
--- /dev/null
+++ b/phpBB/phpbb/help/controller/faq.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\help\controller;
+
+/**
+ * FAQ help page
+ */
+class faq extends controller
+{
+ /**
+ * @return string The title of the page
+ */
+ public function display()
+ {
+ $this->language->add_lang('help/faq');
+
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_LOGIN',
+ false,
+ array(
+ 'HELP_FAQ_LOGIN_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_ANSWER',
+ 'HELP_FAQ_LOGIN_COPPA_QUESTION' => 'HELP_FAQ_LOGIN_COPPA_ANSWER',
+ 'HELP_FAQ_LOGIN_CANNOT_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_REGISTER_ANSWER',
+ 'HELP_FAQ_LOGIN_REGISTER_CONFIRM_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_CONFIRM_ANSWER',
+ 'HELP_FAQ_LOGIN_CANNOT_LOGIN_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANSWER',
+ 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_ANSWER',
+ 'HELP_FAQ_LOGIN_LOST_PASSWORD_QUESTION' => 'HELP_FAQ_LOGIN_LOST_PASSWORD_ANSWER',
+ 'HELP_FAQ_LOGIN_AUTO_LOGOUT_QUESTION' => 'HELP_FAQ_LOGIN_AUTO_LOGOUT_ANSWER',
+ 'HELP_FAQ_LOGIN_DELETE_COOKIES_QUESTION' => 'HELP_FAQ_LOGIN_DELETE_COOKIES_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_USERSETTINGS',
+ false,
+ array(
+ 'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_QUESTION' => 'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_QUESTION' => 'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_TIMEZONE_QUESTION' => 'HELP_FAQ_USERSETTINGS_TIMEZONE_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_SERVERTIME_QUESTION' => 'HELP_FAQ_USERSETTINGS_SERVERTIME_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_LANGUAGE_QUESTION' => 'HELP_FAQ_USERSETTINGS_LANGUAGE_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_AVATAR_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_RANK_QUESTION' => 'HELP_FAQ_USERSETTINGS_RANK_ANSWER',
+ 'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_QUESTION' => 'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_POSTING',
+ false,
+ array(
+ 'HELP_FAQ_POSTING_CREATE_QUESTION' => 'HELP_FAQ_POSTING_CREATE_ANSWER',
+ 'HELP_FAQ_POSTING_EDIT_DELETE_QUESTION' => 'HELP_FAQ_POSTING_EDIT_DELETE_ANSWER',
+ 'HELP_FAQ_POSTING_SIGNATURE_QUESTION' => 'HELP_FAQ_POSTING_SIGNATURE_ANSWER',
+ 'HELP_FAQ_POSTING_POLL_CREATE_QUESTION' => 'HELP_FAQ_POSTING_POLL_CREATE_ANSWER',
+ 'HELP_FAQ_POSTING_POLL_ADD_QUESTION' => 'HELP_FAQ_POSTING_POLL_ADD_ANSWER',
+ 'HELP_FAQ_POSTING_POLL_EDIT_QUESTION' => 'HELP_FAQ_POSTING_POLL_EDIT_ANSWER',
+ 'HELP_FAQ_POSTING_FORUM_RESTRICTED_QUESTION' => 'HELP_FAQ_POSTING_FORUM_RESTRICTED_ANSWER',
+ 'HELP_FAQ_POSTING_NO_ATTACHMENTS_QUESTION' => 'HELP_FAQ_POSTING_NO_ATTACHMENTS_ANSWER',
+ 'HELP_FAQ_POSTING_WARNING_QUESTION' => 'HELP_FAQ_POSTING_WARNING_ANSWER',
+ 'HELP_FAQ_POSTING_REPORT_QUESTION' => 'HELP_FAQ_POSTING_REPORT_ANSWER',
+ 'HELP_FAQ_POSTING_DRAFT_QUESTION' => 'HELP_FAQ_POSTING_DRAFT_ANSWER',
+ 'HELP_FAQ_POSTING_QUEUE_QUESTION' => 'HELP_FAQ_POSTING_QUEUE_ANSWER',
+ 'HELP_FAQ_POSTING_BUMP_QUESTION' => 'HELP_FAQ_POSTING_BUMP_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_FORMATTING',
+ false,
+ array(
+ 'HELP_FAQ_FORMATTING_BBOCDE_QUESTION' => 'HELP_FAQ_FORMATTING_BBOCDE_ANSWER',
+ 'HELP_FAQ_FORMATTING_HTML_QUESTION' => 'HELP_FAQ_FORMATTING_HTML_ANSWER',
+ 'HELP_FAQ_FORMATTING_SMILIES_QUESTION' => 'HELP_FAQ_FORMATTING_SMILIES_ANSWER',
+ 'HELP_FAQ_FORMATTING_IMAGES_QUESTION' => 'HELP_FAQ_FORMATTING_IMAGES_ANSWER',
+ 'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_QUESTION' => 'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_ANSWER',
+ 'HELP_FAQ_FORMATTING_ANNOUNCEMENT_QUESTION' => 'HELP_FAQ_FORMATTING_ANNOUNCEMENT_ANSWER',
+ 'HELP_FAQ_FORMATTING_STICKIES_QUESTION' => 'HELP_FAQ_FORMATTING_STICKIES_ANSWER',
+ 'HELP_FAQ_FORMATTING_LOCKED_QUESTION' => 'HELP_FAQ_FORMATTING_LOCKED_ANSWER',
+ 'HELP_FAQ_FORMATTING_ICONS_QUESTION' => 'HELP_FAQ_FORMATTING_ICONS_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_GROUPS',
+ true,
+ array(
+ 'HELP_FAQ_GROUPS_ADMINISTRATORS_QUESTION' => 'HELP_FAQ_GROUPS_ADMINISTRATORS_ANSWER',
+ 'HELP_FAQ_GROUPS_MODERATORS_QUESTION' => 'HELP_FAQ_GROUPS_MODERATORS_ANSWER',
+ 'HELP_FAQ_GROUPS_USERGROUPS_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_ANSWER',
+ 'HELP_FAQ_GROUPS_USERGROUPS_JOIN_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_JOIN_ANSWER',
+ 'HELP_FAQ_GROUPS_USERGROUPS_LEAD_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_LEAD_ANSWER',
+ 'HELP_FAQ_GROUPS_COLORS_QUESTION' => 'HELP_FAQ_GROUPS_COLORS_ANSWER',
+ 'HELP_FAQ_GROUPS_DEFAULT_QUESTION' => 'HELP_FAQ_GROUPS_DEFAULT_ANSWER',
+ 'HELP_FAQ_GROUPS_TEAM_QUESTION' => 'HELP_FAQ_GROUPS_TEAM_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_PMS',
+ false,
+ array(
+ 'HELP_FAQ_PMS_CANNOT_SEND_QUESTION' => 'HELP_FAQ_PMS_CANNOT_SEND_ANSWER',
+ 'HELP_FAQ_PMS_UNWANTED_QUESTION' => 'HELP_FAQ_PMS_UNWANTED_ANSWER',
+ 'HELP_FAQ_PMS_SPAM_QUESTION' => 'HELP_FAQ_PMS_SPAM_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_FRIENDS',
+ false,
+ array(
+ 'HELP_FAQ_FRIENDS_BASIC_QUESTION' => 'HELP_FAQ_FRIENDS_BASIC_ANSWER',
+ 'HELP_FAQ_FRIENDS_MANAGE_QUESTION' => 'HELP_FAQ_FRIENDS_MANAGE_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_SEARCH',
+ false,
+ array(
+ 'HELP_FAQ_SEARCH_FORUM_QUESTION' => 'HELP_FAQ_SEARCH_FORUM_ANSWER',
+ 'HELP_FAQ_SEARCH_NO_RESULT_QUESTION' => 'HELP_FAQ_SEARCH_NO_RESULT_ANSWER',
+ 'HELP_FAQ_SEARCH_BLANK_QUESTION' => 'HELP_FAQ_SEARCH_BLANK_ANSWER',
+ 'HELP_FAQ_SEARCH_MEMBERS_QUESTION' => 'HELP_FAQ_SEARCH_MEMBERS_ANSWER',
+ 'HELP_FAQ_SEARCH_OWN_QUESTION' => 'HELP_FAQ_SEARCH_OWN_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_BOOKMARKS',
+ false,
+ array(
+ 'HELP_FAQ_BOOKMARKS_DIFFERENCE_QUESTION' => 'HELP_FAQ_BOOKMARKS_DIFFERENCE_ANSWER',
+ 'HELP_FAQ_BOOKMARKS_TOPIC_QUESTION' => 'HELP_FAQ_BOOKMARKS_TOPIC_ANSWER',
+ 'HELP_FAQ_BOOKMARKS_FORUM_QUESTION' => 'HELP_FAQ_BOOKMARKS_FORUM_ANSWER',
+ 'HELP_FAQ_BOOKMARKS_REMOVE_QUESTION' => 'HELP_FAQ_BOOKMARKS_REMOVE_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_ATTACHMENTS',
+ false,
+ array(
+ 'HELP_FAQ_ATTACHMENTS_ALLOWED_QUESTION' => 'HELP_FAQ_ATTACHMENTS_ALLOWED_ANSWER',
+ 'HELP_FAQ_ATTACHMENTS_OWN_QUESTION' => 'HELP_FAQ_ATTACHMENTS_OWN_ANSWER',
+ )
+ );
+ $this->manager->add_block(
+ 'HELP_FAQ_BLOCK_ISSUES',
+ false,
+ array(
+ 'HELP_FAQ_ISSUES_WHOIS_PHPBB_QUESTION' => 'HELP_FAQ_ISSUES_WHOIS_PHPBB_ANSWER',
+ 'HELP_FAQ_ISSUES_FEATURE_QUESTION' => 'HELP_FAQ_ISSUES_FEATURE_ANSWER',
+ 'HELP_FAQ_ISSUES_LEGAL_QUESTION' => 'HELP_FAQ_ISSUES_LEGAL_ANSWER',
+ 'HELP_FAQ_ISSUES_ADMIN_QUESTION' => 'HELP_FAQ_ISSUES_ADMIN_ANSWER',
+ )
+ );
+
+ return $this->language->lang('FAQ_EXPLAIN');
+ }
+}
diff --git a/phpBB/phpbb/help/controller/help.php b/phpBB/phpbb/help/controller/help.php
new file mode 100644
index 0000000000..9cc3b0c8b4
--- /dev/null
+++ b/phpBB/phpbb/help/controller/help.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\help\controller;
+
+use phpbb\exception\http_exception;
+
+class help
+{
+ /** @var \phpbb\controller\helper */
+ protected $helper;
+
+ /** @var \phpbb\event\dispatcher_interface */
+ protected $dispatcher;
+
+ /** @var \phpbb\template\template */
+ protected $template;
+
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var string */
+ protected $root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\controller\helper $helper
+ * @param \phpbb\event\dispatcher_interface $dispatcher
+ * @param \phpbb\template\template $template
+ * @param \phpbb\user $user
+ * @param string $root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\controller\helper $helper, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\template\template $template, \phpbb\user $user, $root_path, $php_ext)
+ {
+ $this->helper = $helper;
+ $this->dispatcher = $dispatcher;
+ $this->template = $template;
+ $this->user = $user;
+ $this->root_path = $root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Controller for /help/{mode} routes
+ *
+ * @param string $mode
+ * @return \Symfony\Component\HttpFoundation\Response A Symfony Response object
+ * @throws http_exception when the $mode is not known by any extension
+ */
+ public function handle($mode)
+ {
+ switch ($mode)
+ {
+ case 'faq':
+ case 'bbcode':
+ $page_title = ($mode === 'faq') ? $this->user->lang['FAQ_EXPLAIN'] : $this->user->lang['BBCODE_GUIDE'];
+ $this->user->add_lang($mode, false, true);
+ break;
+
+ default:
+ $page_title = $this->user->lang['FAQ_EXPLAIN'];
+ $ext_name = $lang_file = '';
+
+ /**
+ * You can use this event display a custom help page
+ *
+ * @event core.faq_mode_validation
+ * @var string page_title Title of the page
+ * @var string mode FAQ that is going to be displayed
+ * @var string lang_file Language file containing the help data
+ * @var string ext_name Vendor and extension name where the help
+ * language file can be loaded from
+ * @since 3.1.4-RC1
+ */
+ $vars = array(
+ 'page_title',
+ 'mode',
+ 'lang_file',
+ 'ext_name',
+ );
+ extract($this->dispatcher->trigger_event('core.faq_mode_validation', compact($vars)));
+
+ if ($ext_name === '' || $lang_file === '')
+ {
+ throw new http_exception(404, 'Not Found');
+ }
+
+ $this->user->add_lang($lang_file, false, true, $ext_name);
+ break;
+
+ }
+
+ $this->template->assign_vars(array(
+ 'L_FAQ_TITLE' => $page_title,
+ 'S_IN_FAQ' => true,
+ ));
+
+ $this->assign_to_template($this->user->help);
+
+ make_jumpbox(append_sid("{$this->root_path}viewforum.{$this->php_ext}"));
+ return $this->helper->render('faq_body.html', $page_title);
+ }
+
+ /**
+ * Assigns the help data to the template blocks
+ *
+ * @param array $help_data
+ * @return null
+ */
+ protected function assign_to_template(array $help_data)
+ {
+ // Pull the array data from the lang pack
+ $switch_column = $found_switch = false;
+ foreach ($help_data as $help_ary)
+ {
+ if ($help_ary[0] == '--')
+ {
+ if ($help_ary[1] == '--')
+ {
+ $switch_column = true;
+ $found_switch = true;
+ continue;
+ }
+
+ $this->template->assign_block_vars('faq_block', array(
+ 'BLOCK_TITLE' => $help_ary[1],
+ 'SWITCH_COLUMN' => $switch_column,
+ ));
+
+ if ($switch_column)
+ {
+ $switch_column = false;
+ }
+ continue;
+ }
+
+ $this->template->assign_block_vars('faq_block.faq_row', array(
+ 'FAQ_QUESTION' => $help_ary[0],
+ 'FAQ_ANSWER' => $help_ary[1],
+ ));
+ }
+
+ $this->template->assign_var('SWITCH_COLUMN_MANUALLY', !$found_switch);
+ }
+}
diff --git a/phpBB/phpbb/help/manager.php b/phpBB/phpbb/help/manager.php
new file mode 100644
index 0000000000..39f52d343b
--- /dev/null
+++ b/phpBB/phpbb/help/manager.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\help;
+
+/**
+ * Class help page manager
+ */
+class manager
+{
+ /** @var \phpbb\event\dispatcher */
+ protected $dispatcher;
+
+ /** @var \phpbb\language\language */
+ protected $language;
+
+ /** @var \phpbb\template\template */
+ protected $template;
+
+ /** @var bool */
+ protected $switched_column;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\event\dispatcher $dispatcher
+ * @param \phpbb\language\language $language
+ * @param \phpbb\template\template $template
+ */
+ public function __construct(\phpbb\event\dispatcher $dispatcher, \phpbb\language\language $language, \phpbb\template\template $template)
+ {
+ $this->dispatcher = $dispatcher;
+ $this->language = $language;
+ $this->template = $template;
+ }
+
+ /**
+ * Add a new faq block
+ *
+ * @param string $block_name Name or language key with the name of the block
+ * @param bool $switch_column Switch the column of the menu
+ * @param array $questions Array of frequently asked questions
+ */
+ public function add_block($block_name, $switch_column = false, $questions = array())
+ {
+ /**
+ * You can use this event to add a block before the current one.
+ *
+ * @event core.help_manager_add_block_before
+ * @var string block_name Language key of the block headline
+ * @var bool switch_column Should we switch the menu column before this headline
+ * @var array questions Array with questions
+ * @since 3.2.0-a1
+ */
+ $vars = array('block_name', 'switch_column', 'questions');
+ extract($this->dispatcher->trigger_event('core.help_manager_add_block_before', compact($vars)));
+
+ $this->template->assign_block_vars('faq_block', array(
+ 'BLOCK_TITLE' => $this->language->lang($block_name),
+ 'SWITCH_COLUMN' => !$this->switched_column && $switch_column,
+ ));
+
+ foreach ($questions as $question => $answer)
+ {
+ $this->add_question($question, $answer);
+ }
+
+ $this->switched_column = $this->switched_column || $switch_column;
+
+ /**
+ * You can use this event to add a block after the current one.
+ *
+ * @event core.help_manager_add_block_after
+ * @var string block_name Language key of the block headline
+ * @var bool switch_column Should we switch the menu column before this headline
+ * @var array questions Array with questions
+ * @since 3.2.0-a1
+ */
+ $vars = array('block_name', 'switch_column', 'questions');
+ extract($this->dispatcher->trigger_event('core.help_manager_add_block_after', compact($vars)));
+ }
+
+ /**
+ * Add a new faq question
+ *
+ * @param string $question Question or language key with the question of the block
+ * @param string $answer Answer or language key with the answer of the block
+ */
+ public function add_question($question, $answer)
+ {
+ /**
+ * You can use this event to add a question before the current one.
+ *
+ * @event core.help_manager_add_question_before
+ * @var string question Language key of the question
+ * @var string answer Language key of the answer
+ * @since 3.2.0-a1
+ */
+ $vars = array('question', 'answer');
+ extract($this->dispatcher->trigger_event('core.help_manager_add_question_before', compact($vars)));
+
+ $this->template->assign_block_vars('faq_block.faq_row', array(
+ 'FAQ_QUESTION' => $this->language->lang($question),
+ 'FAQ_ANSWER' => $this->language->lang($answer),
+ ));
+
+ /**
+ * You can use this event to add a question after the current one.
+ *
+ * @event core.help_manager_add_question_after
+ * @var string question Language key of the question
+ * @var string answer Language key of the answer
+ * @since 3.2.0-a1
+ */
+ $vars = array('question', 'answer');
+ extract($this->dispatcher->trigger_event('core.help_manager_add_question_after', compact($vars)));
+ }
+
+ /**
+ * Returns whether the block titles switched side
+ * @return bool
+ */
+ public function switched_column()
+ {
+ return $this->switched_column;
+ }
+}
diff --git a/phpBB/phpbb/hook/finder.php b/phpBB/phpbb/hook/finder.php
index c8f71861d9..f5a68a1370 100644
--- a/phpBB/phpbb/hook/finder.php
+++ b/phpBB/phpbb/hook/finder.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package extension
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,13 +15,22 @@ namespace phpbb\hook;
/**
* The hook finder locates installed hooks.
-*
-* @package phpBB3
*/
class finder
{
- protected $phpbb_root_path;
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
protected $cache;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
protected $php_ext;
/**
diff --git a/phpBB/phpbb/install/console/command/install/config/show.php b/phpBB/phpbb/install/console/command/install/config/show.php
new file mode 100644
index 0000000000..5d82d8d1ef
--- /dev/null
+++ b/phpBB/phpbb/install/console/command/install/config/show.php
@@ -0,0 +1,131 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\install\console\command\install\config;
+
+use phpbb\install\helper\iohandler\factory;
+use phpbb\install\installer;
+use phpbb\install\installer_configuration;
+use phpbb\language\language;
+use Symfony\Component\Config\Definition\Exception\Exception;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+
+class show extends \phpbb\console\command\command
+{
+ /**
+ * @var factory
+ */
+ protected $iohandler_factory;
+
+ /**
+ * @var installer
+ */
+ protected $installer;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * Constructor
+ *
+ * @param language $language
+ * @param factory $factory
+ * @param installer $installer
+ */
+ public function __construct(language $language, factory $factory, installer $installer)
+ {
+ $this->iohandler_factory = $factory;
+ $this->installer = $installer;
+ $this->language = $language;
+
+ parent::__construct(new \phpbb\user($language, 'datetime'));
+ }
+
+ /**
+ *
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('install:config:show')
+ ->addArgument(
+ 'config-file',
+ InputArgument::REQUIRED,
+ $this->language->lang('CLI_CONFIG_FILE'))
+ ->setDescription($this->language->lang('CLI_INSTALL_SHOW_CONFIG'))
+ ;
+ }
+
+ /**
+ * Show the validated configuration
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->iohandler_factory->set_environment('cli');
+
+ /** @var \phpbb\install\helper\iohandler\cli_iohandler $iohandler */
+ $iohandler = $this->iohandler_factory->get();
+ $style = new SymfonyStyle($input, $output);
+ $iohandler->set_style($style, $output);
+
+ $config_file = $input->getArgument('config-file');
+
+ if (!is_file($config_file))
+ {
+ $iohandler->add_error_message(array('MISSING_FILE', $config_file));
+
+ return;
+ }
+
+ try
+ {
+ $config = Yaml::parse(file_get_contents($config_file), true, false);
+ }
+ catch (ParseException $e)
+ {
+ $iohandler->add_error_message('INVALID_YAML_FILE');
+
+ return;
+ }
+
+ $processor = new Processor();
+ $configuration = new installer_configuration();
+
+ try
+ {
+ $config = $processor->processConfiguration($configuration, $config);
+ }
+ catch (Exception $e)
+ {
+ $iohandler->add_error_message('INVALID_CONFIGURATION', $e->getMessage());
+
+ return;
+ }
+
+ $iohandler->add_log_message(Yaml::dump(array('installer' => $config), 10, 4, true, false));
+ }
+}
diff --git a/phpBB/phpbb/install/console/command/install/config/validate.php b/phpBB/phpbb/install/console/command/install/config/validate.php
new file mode 100644
index 0000000000..3bbbc23e34
--- /dev/null
+++ b/phpBB/phpbb/install/console/command/install/config/validate.php
@@ -0,0 +1,132 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\install\console\command\install\config;
+
+use phpbb\install\helper\iohandler\factory;
+use phpbb\install\installer;
+use phpbb\install\installer_configuration;
+use phpbb\language\language;
+use Symfony\Component\Config\Definition\Exception\Exception;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+
+class validate extends \phpbb\console\command\command
+{
+ /**
+ * @var factory
+ */
+ protected $iohandler_factory;
+
+ /**
+ * @var installer
+ */
+ protected $installer;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * Constructor
+ *
+ * @param language $language
+ * @param factory $factory
+ * @param installer $installer
+ */
+ public function __construct(language $language, factory $factory, installer $installer)
+ {
+ $this->iohandler_factory = $factory;
+ $this->installer = $installer;
+ $this->language = $language;
+
+ parent::__construct(new \phpbb\user($language, 'datetime'));
+ }
+
+ /**
+ *
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('install:config:validate')
+ ->addArgument(
+ 'config-file',
+ InputArgument::REQUIRED,
+ $this->language->lang('CLI_CONFIG_FILE'))
+ ->setDescription($this->language->lang('CLI_INSTALL_VALIDATE_CONFIG'))
+ ;
+ }
+
+ /**
+ * Validate the configuration file
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->iohandler_factory->set_environment('cli');
+
+ /** @var \phpbb\install\helper\iohandler\cli_iohandler $iohandler */
+ $iohandler = $this->iohandler_factory->get();
+ $style = new SymfonyStyle($input, $output);
+ $iohandler->set_style($style, $output);
+
+ $config_file = $input->getArgument('config-file');
+
+ if (!is_file($config_file))
+ {
+ $iohandler->add_error_message(array('MISSING_FILE', array($config_file)));
+
+ return 1;
+ }
+
+ try
+ {
+ $config = Yaml::parse(file_get_contents($config_file), true, false);
+ }
+ catch (ParseException $e)
+ {
+ $iohandler->add_error_message('INVALID_YAML_FILE');
+
+ return 1;
+ }
+
+ $processor = new Processor();
+ $configuration = new installer_configuration();
+
+ try
+ {
+ $processor->processConfiguration($configuration, $config);
+ }
+ catch (Exception $e)
+ {
+ $iohandler->add_error_message('INVALID_CONFIGURATION', $e->getMessage());
+
+ return 1;
+ }
+
+ $iohandler->add_success_message('CONFIGURATION_VALID');
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/console/command/install/install.php b/phpBB/phpbb/install/console/command/install/install.php
new file mode 100644
index 0000000000..d76182af92
--- /dev/null
+++ b/phpBB/phpbb/install/console/command/install/install.php
@@ -0,0 +1,206 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\install\console\command\install;
+
+use phpbb\install\exception\installer_exception;
+use phpbb\install\helper\install_helper;
+use phpbb\install\helper\iohandler\cli_iohandler;
+use phpbb\install\helper\iohandler\factory;
+use phpbb\install\installer;
+use phpbb\install\installer_configuration;
+use phpbb\language\language;
+use Symfony\Component\Config\Definition\Exception\Exception;
+use Symfony\Component\Config\Definition\Processor;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+
+class install extends \phpbb\console\command\command
+{
+ /**
+ * @var factory
+ */
+ protected $iohandler_factory;
+
+ /**
+ * @var installer
+ */
+ protected $installer;
+
+ /**
+ * @var install_helper
+ */
+ protected $install_helper;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * Constructor
+ *
+ * @param language $language
+ * @param factory $factory
+ * @param installer $installer
+ * @param install_helper $install_helper
+ */
+ public function __construct(language $language, factory $factory, installer $installer, install_helper $install_helper)
+ {
+ $this->iohandler_factory = $factory;
+ $this->installer = $installer;
+ $this->language = $language;
+ $this->install_helper = $install_helper;
+
+ parent::__construct(new \phpbb\user($language, 'datetime'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure()
+ {
+ $this
+ ->setName('install')
+ ->addArgument(
+ 'config-file',
+ InputArgument::REQUIRED,
+ $this->language->lang('CLI_CONFIG_FILE'))
+ ->setDescription($this->language->lang('CLI_INSTALL_BOARD'))
+ ;
+ }
+
+ /**
+ * Executes the command install.
+ *
+ * Install the board
+ *
+ * @param InputInterface $input An InputInterface instance
+ * @param OutputInterface $output An OutputInterface instance
+ *
+ * @return null
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $this->iohandler_factory->set_environment('cli');
+
+ /** @var \phpbb\install\helper\iohandler\cli_iohandler $iohandler */
+ $iohandler = $this->iohandler_factory->get();
+ $style = new SymfonyStyle($input, $output);
+ $iohandler->set_style($style, $output);
+
+ $this->installer->set_iohandler($iohandler);
+
+ $config_file = $input->getArgument('config-file');
+
+ if ($this->install_helper->is_phpbb_installed())
+ {
+ $iohandler->add_error_message('PHPBB_ALREADY_INSTALLED');
+
+ return 1;
+ }
+
+ if (!is_file($config_file))
+ {
+ $iohandler->add_error_message(array('MISSING_FILE', $config_file));
+
+ return 1;
+ }
+
+ try
+ {
+ $config = Yaml::parse(file_get_contents($config_file), true, false);
+ }
+ catch (ParseException $e)
+ {
+ $iohandler->add_error_message(array('INVALID_YAML_FILE', $config_file));
+
+ return 1;
+ }
+
+ $processor = new Processor();
+ $configuration = new installer_configuration();
+
+ try
+ {
+ $config = $processor->processConfiguration($configuration, $config);
+ }
+ catch (Exception $e)
+ {
+ $iohandler->add_error_message('INVALID_CONFIGURATION', $e->getMessage());
+
+ return 1;
+ }
+
+ $this->register_configuration($iohandler, $config);
+
+ try
+ {
+ $this->installer->run();
+ }
+ catch (installer_exception $e)
+ {
+ $iohandler->add_error_message($e->getMessage());
+ return 1;
+ }
+ }
+
+ /**
+ * Register the configuration to simulate the forms.
+ *
+ * @param cli_iohandler $iohandler
+ * @param array $config
+ */
+ private function register_configuration(cli_iohandler $iohandler, $config)
+ {
+ $iohandler->set_input('admin_name', $config['admin']['name']);
+ $iohandler->set_input('admin_pass1', $config['admin']['password']);
+ $iohandler->set_input('admin_pass2', $config['admin']['password']);
+ $iohandler->set_input('board_email', $config['admin']['email']);
+ $iohandler->set_input('submit_admin', 'submit');
+
+ $iohandler->set_input('default_lang', $config['board']['lang']);
+ $iohandler->set_input('board_name', $config['board']['name']);
+ $iohandler->set_input('board_description', $config['board']['description']);
+ $iohandler->set_input('submit_board', 'submit');
+
+ $iohandler->set_input('dbms', $config['database']['dbms']);
+ $iohandler->set_input('dbhost', $config['database']['dbhost']);
+ $iohandler->set_input('dbport', $config['database']['dbport']);
+ $iohandler->set_input('dbuser', $config['database']['dbuser']);
+ $iohandler->set_input('dbpasswd', $config['database']['dbpasswd']);
+ $iohandler->set_input('dbname', $config['database']['dbname']);
+ $iohandler->set_input('table_prefix', $config['database']['table_prefix']);
+ $iohandler->set_input('submit_database', 'submit');
+
+ $iohandler->set_input('email_enable', $config['email']['enabled']);
+ $iohandler->set_input('smtp_delivery', $config['email']['smtp_delivery']);
+ $iohandler->set_input('smtp_host', $config['email']['smtp_host']);
+ $iohandler->set_input('smtp_auth', $config['email']['smtp_auth']);
+ $iohandler->set_input('smtp_user', $config['email']['smtp_user']);
+ $iohandler->set_input('smtp_pass', $config['email']['smtp_pass']);
+ $iohandler->set_input('submit_email', 'submit');
+
+ $iohandler->set_input('cookie_secure', $config['server']['cookie_secure']);
+ $iohandler->set_input('server_protocol', $config['server']['server_protocol']);
+ $iohandler->set_input('force_server_vars', $config['server']['force_server_vars']);
+ $iohandler->set_input('server_name', $config['server']['server_name']);
+ $iohandler->set_input('server_port', $config['server']['server_port']);
+ $iohandler->set_input('script_path', $config['server']['script_path']);
+ $iohandler->set_input('submit_server', 'submit');
+ }
+}
diff --git a/phpBB/phpbb/install/controller/archive_download.php b/phpBB/phpbb/install/controller/archive_download.php
new file mode 100644
index 0000000000..a0f0ba181d
--- /dev/null
+++ b/phpBB/phpbb/install/controller/archive_download.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\controller;
+
+use phpbb\exception\http_exception;
+use phpbb\install\helper\config;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+
+class archive_download
+{
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * Constructor
+ *
+ * @param config $config
+ */
+ public function __construct(config $config)
+ {
+ $this->installer_config = $config;
+ $this->installer_config->load_config();
+ }
+
+ /**
+ * Sends response with the merge conflict archive
+ *
+ * Merge conflicts always have to be resolved manually,
+ * so we use a different archive for that.
+ *
+ * @return BinaryFileResponse
+ */
+ public function conflict_archive()
+ {
+ $filename = $this->installer_config->get('update_file_conflict_archive', false);
+
+ if (!$filename)
+ {
+ throw new http_exception(404, 'URL_NOT_FOUND');
+ }
+
+ return $this->send_response($filename);
+ }
+
+ /**
+ * Sends response with the updated files' archive
+ *
+ * @return BinaryFileResponse
+ */
+ public function update_archive()
+ {
+ $filename = $this->installer_config->get('update_file_archive', '');
+
+ if (!$filename)
+ {
+ throw new http_exception(404, 'URL_NOT_FOUND');
+ }
+
+ return $this->send_response($filename);
+ }
+
+ /**
+ * Generates a download response
+ *
+ * @param string $filename Path to the file to download
+ *
+ * @return BinaryFileResponse Response object
+ */
+ private function send_response($filename)
+ {
+ $response = new BinaryFileResponse($filename);
+ $response->setContentDisposition(
+ ResponseHeaderBag::DISPOSITION_ATTACHMENT,
+ basename($filename)
+ );
+
+ return $response;
+ }
+}
diff --git a/phpBB/phpbb/install/controller/helper.php b/phpBB/phpbb/install/controller/helper.php
new file mode 100644
index 0000000000..2dad42b4b6
--- /dev/null
+++ b/phpBB/phpbb/install/controller/helper.php
@@ -0,0 +1,417 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\controller;
+
+use phpbb\install\helper\config;
+use phpbb\install\helper\navigation\navigation_provider;
+use phpbb\language\language;
+use phpbb\language\language_file_helper;
+use phpbb\path_helper;
+use phpbb\request\request;
+use phpbb\request\request_interface;
+use phpbb\routing\router;
+use phpbb\symfony_request;
+use phpbb\template\template;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\Cookie;
+
+/**
+ * A duplicate of \phpbb\controller\helper
+ *
+ * This class is necessary because of controller\helper's legacy function calls
+ * to page_header() page_footer() functions which has unavailable dependencies.
+ */
+class helper
+{
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var bool|string
+ */
+ protected $language_cookie;
+
+ /**
+ * @var \phpbb\language\language_file_helper
+ */
+ protected $lang_helper;
+
+ /**
+ * @var \phpbb\install\helper\navigation\navigation_provider
+ */
+ protected $navigation_provider;
+
+ /**
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * @var \phpbb\path_helper
+ */
+ protected $path_helper;
+
+ /**
+ * @var \phpbb\request\request
+ */
+ protected $phpbb_request;
+
+ /**
+ * @var \phpbb\symfony_request
+ */
+ protected $request;
+
+ /**
+ * @var \phpbb\routing\router
+ */
+ protected $router;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_admin_path;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param config $config
+ * @param language $language
+ * @param language_file_helper $lang_helper
+ * @param navigation_provider $nav
+ * @param template $template
+ * @param path_helper $path_helper
+ * @param request $phpbb_request
+ * @param symfony_request $request
+ * @param router $router
+ * @param string $phpbb_root_path
+ */
+ public function __construct(config $config, language $language, language_file_helper $lang_helper, navigation_provider $nav, template $template, path_helper $path_helper, request $phpbb_request, symfony_request $request, router $router, $phpbb_root_path)
+ {
+ $this->installer_config = $config;
+ $this->language = $language;
+ $this->language_cookie = false;
+ $this->lang_helper = $lang_helper;
+ $this->navigation_provider = $nav;
+ $this->template = $template;
+ $this->path_helper = $path_helper;
+ $this->phpbb_request = $phpbb_request;
+ $this->request = $request;
+ $this->router = $router;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->phpbb_admin_path = $phpbb_root_path . 'adm/';
+ }
+
+ /**
+ * Automate setting up the page and creating the response object.
+ *
+ * @param string $template_file The template handle to render
+ * @param string $page_title The title of the page to output
+ * @param bool $selected_language True to enable language selector it, false otherwise
+ * @param int $status_code The status code to be sent to the page header
+ *
+ * @return Response object containing rendered page
+ */
+ public function render($template_file, $page_title = '', $selected_language = false, $status_code = 200)
+ {
+ $this->page_header($page_title, $selected_language);
+
+ $this->template->set_filenames(array(
+ 'body' => $template_file,
+ ));
+
+ $response = new Response($this->template->assign_display('body'), $status_code);
+
+ // Set language cookie
+ if ($this->language_cookie !== false)
+ {
+ $cookie = new Cookie('lang', $this->language_cookie, time() + 3600);
+ $response->headers->setCookie($cookie);
+
+ $this->language_cookie = false;
+ }
+
+ return $response;
+ }
+
+ /**
+ * Returns path from route name
+ *
+ * @param string $route_name
+ *
+ * @return string
+ */
+ public function route($route_name)
+ {
+ $url = $this->router->generate($route_name);
+
+ return $url;
+ }
+
+ /**
+ * Handles language selector form
+ */
+ public function handle_language_select()
+ {
+ $lang = null;
+
+ // Check if language form has been submited
+ $submit = $this->phpbb_request->variable('change_lang', '');
+ if (!empty($submit))
+ {
+ $lang = $this->phpbb_request->variable('language', '');
+
+ if (!empty($lang))
+ {
+ $this->language_cookie = $lang;
+ }
+ }
+
+ // Retrieve language from cookie
+ $lang_cookie = $this->phpbb_request->variable('lang', '', false, request_interface::COOKIE);
+ if (empty($lang) && !empty($lang_cookie))
+ {
+ $lang = $lang_cookie;
+ $this->language_cookie = $lang;
+ }
+
+ $lang = (!empty($lang) && strpos($lang, '/') === false) ? $lang : null;
+
+ $this->render_language_select($lang);
+
+ if ($lang !== null)
+ {
+ $this->language->set_user_language($lang, true);
+ $this->installer_config->set('user_language', $lang);
+ }
+ }
+
+ /**
+ * Process navigation data to reflect active/completed stages
+ *
+ * @param \phpbb\install\helper\iohandler\iohandler_interface|null $iohandler
+ */
+ public function handle_navigation($iohandler = null)
+ {
+ $nav_data = $this->installer_config->get_navigation_data();
+
+ // Set active navigation stage
+ if (isset($nav_data['active']) && is_array($nav_data['active']))
+ {
+ if ($iohandler !== null)
+ {
+ $iohandler->set_active_stage_menu($nav_data['active']);
+ }
+
+ $this->navigation_provider->set_nav_property($nav_data['active'], array(
+ 'selected' => true,
+ 'completed' => false,
+ ));
+ }
+
+ // Set finished navigation stages
+ if (isset($nav_data['finished']) && is_array($nav_data['finished']))
+ {
+ foreach ($nav_data['finished'] as $finished_stage)
+ {
+ if ($iohandler !== null)
+ {
+ $iohandler->set_finished_stage_menu($finished_stage);
+ }
+
+ $this->navigation_provider->set_nav_property($finished_stage, array(
+ 'selected' => false,
+ 'completed' => true,
+ ));
+ }
+ }
+ }
+
+ /**
+ * Set default template variables
+ *
+ * @param string $page_title Title of the page
+ * @param bool $selected_language True to enable language selector it, false otherwise
+ */
+ protected function page_header($page_title, $selected_language = false)
+ {
+ // Path to templates
+ $paths = array($this->phpbb_root_path . 'install/update/new/adm/', $this->phpbb_admin_path);
+ $paths = array_filter($paths, 'is_dir');
+ $path = array_shift($paths);
+ $path = substr($path, strlen($this->phpbb_root_path));
+
+ $this->template->assign_vars(array(
+ 'L_CHANGE' => $this->language->lang('CHANGE'),
+ 'L_COLON' => $this->language->lang('COLON'),
+ 'L_INSTALL_PANEL' => $this->language->lang('INSTALL_PANEL'),
+ 'L_SELECT_LANG' => $this->language->lang('SELECT_LANG'),
+ 'L_SKIP' => $this->language->lang('SKIP'),
+ 'PAGE_TITLE' => $this->language->lang($page_title),
+ 'T_IMAGE_PATH' => $this->path_helper->get_web_root_path() . $path . 'images',
+ 'T_JQUERY_LINK' => $this->path_helper->get_web_root_path() . $path . '../assets/javascript/jquery.min.js',
+ 'T_TEMPLATE_PATH' => $this->path_helper->get_web_root_path() . $path . 'style',
+ 'T_ASSETS_PATH' => $this->path_helper->get_web_root_path() . $path . '../assets',
+
+ 'S_CONTENT_DIRECTION' => $this->language->lang('DIRECTION'),
+ 'S_CONTENT_FLOW_BEGIN' => ($this->language->lang('DIRECTION') === 'ltr') ? 'left' : 'right',
+ 'S_CONTENT_FLOW_END' => ($this->language->lang('DIRECTION') === 'ltr') ? 'right' : 'left',
+ 'S_CONTENT_ENCODING' => 'UTF-8',
+ 'S_LANG_SELECT' => $selected_language,
+
+ 'S_USER_LANG' => $this->language->lang('USER_LANG'),
+ ));
+
+ $this->render_navigation();
+ }
+
+ /**
+ * Render navigation
+ */
+ protected function render_navigation()
+ {
+ // Get navigation items
+ $nav_array = $this->navigation_provider->get();
+ $nav_array = $this->sort_navigation_level($nav_array);
+
+ $active_main_menu = $this->get_active_main_menu($nav_array);
+
+ // Pass navigation to template
+ foreach ($nav_array as $key => $entry)
+ {
+ $this->template->assign_block_vars('t_block1', array(
+ 'L_TITLE' => $this->language->lang($entry['label']),
+ 'S_SELECTED' => ($active_main_menu === $key),
+ 'U_TITLE' => $this->route($entry['route']),
+ ));
+
+ if (is_array($entry[0]) && $active_main_menu === $key)
+ {
+ $entry[0] = $this->sort_navigation_level($entry[0]);
+
+ foreach ($entry[0] as $name => $sub_entry)
+ {
+ if (isset($sub_entry['stage']) && $sub_entry['stage'] === true)
+ {
+ $this->template->assign_block_vars('l_block2', array(
+ 'L_TITLE' => $this->language->lang($sub_entry['label']),
+ 'S_SELECTED' => (isset($sub_entry['selected']) && $sub_entry['selected'] === true),
+ 'S_COMPLETE' => (isset($sub_entry['completed']) && $sub_entry['completed'] === true),
+ 'STAGE_NAME' => $name,
+ ));
+ }
+ else
+ {
+ $this->template->assign_block_vars('l_block1', array(
+ 'L_TITLE' => $this->language->lang($sub_entry['label']),
+ 'S_SELECTED' => (isset($sub_entry['route']) && $sub_entry['route'] === $this->request->get('_route')),
+ 'U_TITLE' => $this->route($sub_entry['route']),
+ ));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Render language select form
+ *
+ * @param string $selected_language
+ */
+ protected function render_language_select($selected_language = null)
+ {
+ $langs = $this->lang_helper->get_available_languages();
+ foreach ($langs as $lang)
+ {
+ $this->template->assign_block_vars('language_select_item', array(
+ 'VALUE' => $lang['iso'],
+ 'NAME' => $lang['local_name'],
+ 'SELECTED' => ($lang['iso'] === $selected_language),
+ ));
+ }
+ }
+
+ /**
+ * Returns the name of the active main menu item
+ *
+ * @param array $nav_array
+ *
+ * @return string|bool Returns the name of the active main menu element, if the element not found, returns false
+ */
+ protected function get_active_main_menu($nav_array)
+ {
+ $active_route = $this->request->get('_route');
+
+ foreach ($nav_array as $nav_name => $nav_options)
+ {
+ $current_menu = $nav_name;
+
+ if (isset($nav_options['route']) && $nav_options['route'] === $active_route)
+ {
+ return $nav_name;
+ }
+
+ if (is_array($nav_options[0]))
+ {
+ foreach ($nav_options[0] as $sub_menus)
+ {
+ if (isset($sub_menus['route']) && $sub_menus['route'] === $active_route)
+ {
+ return $current_menu;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sorts the top level of navigation array
+ *
+ * @param array $nav_array Navigation array
+ *
+ * @return array
+ */
+ protected function sort_navigation_level($nav_array)
+ {
+ $sorted = array();
+ foreach ($nav_array as $key => $nav)
+ {
+ $order = (isset($nav['order'])) ? $nav['order'] : 0;
+ $sorted[$order][$key] = $nav;
+ }
+
+ // Linearization of navigation array
+ $nav_array = array();
+ ksort($sorted);
+ foreach ($sorted as $nav)
+ {
+ $nav_array = array_merge($nav_array, $nav);
+ }
+
+ return $nav_array;
+ }
+}
diff --git a/phpBB/phpbb/install/controller/install.php b/phpBB/phpbb/install/controller/install.php
new file mode 100644
index 0000000000..b987d91c6a
--- /dev/null
+++ b/phpBB/phpbb/install/controller/install.php
@@ -0,0 +1,173 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\controller;
+
+use phpbb\exception\http_exception;
+use phpbb\install\helper\install_helper;
+use phpbb\install\helper\navigation\navigation_provider;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+use Symfony\Component\HttpFoundation\Response;
+use phpbb\install\helper\iohandler\factory;
+use phpbb\template\template;
+use phpbb\request\request_interface;
+use phpbb\install\installer;
+use phpbb\language\language;
+
+/**
+ * Controller for installing phpBB
+ */
+class install
+{
+ /**
+ * @var helper
+ */
+ protected $controller_helper;
+
+ /**
+ * @var factory
+ */
+ protected $iohandler_factory;
+
+ /**
+ * @var navigation_provider
+ */
+ protected $menu_provider;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * @var template
+ */
+ protected $template;
+
+ /**
+ * @var request_interface
+ */
+ protected $request;
+
+ /**
+ * @var installer
+ */
+ protected $installer;
+
+ /**
+ * @var install_helper
+ */
+ protected $install_helper;
+
+ /**
+ * Constructor
+ *
+ * @param helper $helper
+ * @param factory $factory
+ * @param navigation_provider $nav_provider
+ * @param language $language
+ * @param template $template
+ * @param request_interface $request
+ * @param installer $installer
+ * @param install_helper $install_helper
+ */
+ public function __construct(helper $helper, factory $factory, navigation_provider $nav_provider, language $language, template $template, request_interface $request, installer $installer, install_helper $install_helper)
+ {
+ $this->controller_helper = $helper;
+ $this->iohandler_factory = $factory;
+ $this->menu_provider = $nav_provider;
+ $this->language = $language;
+ $this->template = $template;
+ $this->request = $request;
+ $this->installer = $installer;
+ $this->install_helper = $install_helper;
+ }
+
+ /**
+ * Controller logic
+ *
+ * @return Response|StreamedResponse
+ *
+ * @throws http_exception When phpBB is already installed
+ */
+ public function handle()
+ {
+ if ($this->install_helper->is_phpbb_installed())
+ {
+ throw new http_exception(403, 'INSTALL_PHPBB_INSTALLED');
+ }
+
+ $this->template->assign_vars(array(
+ 'U_ACTION' => $this->controller_helper->route('phpbb_installer_install'),
+ ));
+
+ // Set up input-output handler
+ if ($this->request->is_ajax())
+ {
+ $this->iohandler_factory->set_environment('ajax');
+ }
+ else
+ {
+ $this->iohandler_factory->set_environment('nojs');
+ }
+
+ // Set the appropriate input-output handler
+ $this->installer->set_iohandler($this->iohandler_factory->get());
+
+ if ($this->request->is_ajax())
+ {
+ $installer = $this->installer;
+ $response = new StreamedResponse();
+ $response->setCallback(function() use ($installer) {
+ $installer->run();
+ });
+
+ // Try to bypass any server output buffers
+ $response->headers->set('X-Accel-Buffering', 'no');
+
+ return $response;
+ }
+ else
+ {
+ // Determine whether the installation was started or not
+ if (true)
+ {
+ $this->controller_helper->handle_language_select();
+
+ // Set active stage
+ $this->menu_provider->set_nav_property(
+ array('install', 0, 'introduction'),
+ array(
+ 'selected' => true,
+ 'completed' => false,
+ )
+ );
+
+ // If not, let's render the welcome page
+ $this->template->assign_vars(array(
+ 'SHOW_INSTALL_START_FORM' => true,
+ 'TITLE' => $this->language->lang('INSTALL_INTRO'),
+ 'CONTENT' => $this->language->lang('INSTALL_INTRO_BODY'),
+ ));
+
+ /** @var \phpbb\install\helper\iohandler\iohandler_interface $iohandler */
+ $iohandler = $this->iohandler_factory->get();
+ $this->controller_helper->handle_navigation($iohandler);
+
+ return $this->controller_helper->render('installer_install.html', 'INSTALL', true);
+ }
+
+ // @todo: implement no js controller logic
+ }
+ }
+}
diff --git a/phpBB/phpbb/install/controller/installer_index.php b/phpBB/phpbb/install/controller/installer_index.php
new file mode 100644
index 0000000000..c2d9572284
--- /dev/null
+++ b/phpBB/phpbb/install/controller/installer_index.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\install\controller;
+
+class installer_index
+{
+ /**
+ * @var helper
+ */
+ protected $helper;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param helper $helper
+ * @param \phpbb\language\language $language
+ * @param \phpbb\template\template $template
+ * @param string $phpbb_root_path
+ */
+ public function __construct(helper $helper, \phpbb\language\language $language, \phpbb\template\template $template, $phpbb_root_path)
+ {
+ $this->helper = $helper;
+ $this->language = $language;
+ $this->template = $template;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ public function handle($mode)
+ {
+ $this->helper->handle_language_select();
+
+ switch ($mode)
+ {
+ case "intro":
+ $title = $this->language->lang('INTRODUCTION_TITLE');
+ $body = $this->language->lang('INTRODUCTION_BODY');
+ break;
+ case "support":
+ $title = $this->language->lang('SUPPORT_TITLE');
+ $body = $this->language->lang('SUPPORT_BODY');
+ break;
+ case "license":
+ $title = $this->language->lang('LICENSE_TITLE');
+ $body = implode("<br/>\n", file($this->phpbb_root_path . 'docs/LICENSE.txt'));
+ break;
+ }
+
+ $this->template->assign_vars(array(
+ 'TITLE' => $title,
+ 'BODY' => $body,
+ ));
+
+ return $this->helper->render('installer_main.html', $title, true);
+ }
+}
diff --git a/phpBB/phpbb/install/controller/update.php b/phpBB/phpbb/install/controller/update.php
new file mode 100644
index 0000000000..9fff11cae8
--- /dev/null
+++ b/phpBB/phpbb/install/controller/update.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\controller;
+
+use phpbb\exception\http_exception;
+use phpbb\install\helper\install_helper;
+use phpbb\install\helper\iohandler\factory;
+use phpbb\install\helper\navigation\navigation_provider;
+use phpbb\install\installer;
+use phpbb\language\language;
+use phpbb\request\request_interface;
+use phpbb\template\template;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+
+/**
+ * Updater controller
+ */
+class update
+{
+ /**
+ * @var helper
+ */
+ protected $controller_helper;
+
+ /**
+ * @var installer
+ */
+ protected $installer;
+
+ /**
+ * @var install_helper
+ */
+ protected $install_helper;
+
+ /**
+ * @var factory
+ */
+ protected $iohandler_factory;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * @var navigation_provider
+ */
+ protected $menu_provider;
+
+ /**
+ * @var request_interface
+ */
+ protected $request;
+
+ /**
+ * @var template
+ */
+ protected $template;
+
+ /**
+ * Constructor
+ *
+ * @param helper $controller_helper
+ * @param installer $installer
+ * @param install_helper $install_helper
+ * @param factory $iohandler
+ * @param language $language
+ * @param navigation_provider $menu_provider
+ * @param request_interface $request
+ * @param template $template
+ */
+ public function __construct(helper $controller_helper, installer $installer, install_helper $install_helper, factory $iohandler, language $language, navigation_provider $menu_provider, request_interface $request, template $template)
+ {
+ $this->controller_helper = $controller_helper;
+ $this->installer = $installer;
+ $this->install_helper = $install_helper;
+ $this->iohandler_factory = $iohandler;
+ $this->language = $language;
+ $this->menu_provider = $menu_provider;
+ $this->request = $request;
+ $this->template = $template;
+ }
+
+ /**
+ * Controller entry point
+ *
+ * @return Response|StreamedResponse
+ *
+ * @throws http_exception When phpBB is not installed
+ */
+ public function handle()
+ {
+ if (!$this->install_helper->is_phpbb_installed())
+ {
+ throw new http_exception(403, 'INSTALL_PHPBB_NOT_INSTALLED');
+ }
+
+ $this->template->assign_vars(array(
+ 'U_ACTION' => $this->controller_helper->route('phpbb_installer_update'),
+ ));
+
+ // Set up input-output handler
+ if ($this->request->is_ajax())
+ {
+ $this->iohandler_factory->set_environment('ajax');
+ }
+ else
+ {
+ $this->iohandler_factory->set_environment('nojs');
+ }
+
+ // Set the appropriate input-output handler
+ $this->installer->set_iohandler($this->iohandler_factory->get());
+
+ // Render the intro page
+ if ($this->request->is_ajax())
+ {
+ $installer = $this->installer;
+ $response = new StreamedResponse();
+ $response->setCallback(function() use ($installer) {
+ $installer->run();
+ });
+
+ // Try to bypass any server output buffers
+ $response->headers->set('X-Accel-Buffering', 'no');
+ $response->headers->set('Content-type', 'application/json');
+
+ return $response;
+ }
+ else
+ {
+ $this->controller_helper->handle_language_select();
+
+ // Set active stage
+ $this->menu_provider->set_nav_property(
+ array('update', 0, 'introduction'),
+ array(
+ 'selected' => true,
+ 'completed' => false,
+ )
+ );
+
+ $this->template->assign_vars(array(
+ 'SHOW_INSTALL_START_FORM' => true,
+ 'TITLE' => $this->language->lang('UPDATE_INSTALLATION'),
+ 'CONTENT' => $this->language->lang('UPDATE_INSTALLATION_EXPLAIN'),
+ ));
+
+ /** @var \phpbb\install\helper\iohandler\iohandler_interface $iohandler */
+ $iohandler = $this->iohandler_factory->get();
+ $this->controller_helper->handle_navigation($iohandler);
+
+ return $this->controller_helper->render('installer_update.html', 'UPDATE_INSTALLATION', true);
+ }
+ }
+}
diff --git a/phpBB/phpbb/install/event/kernel_exception_subscriber.php b/phpBB/phpbb/install/event/kernel_exception_subscriber.php
new file mode 100644
index 0000000000..c2960cb13c
--- /dev/null
+++ b/phpBB/phpbb/install/event/kernel_exception_subscriber.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\event;
+
+use phpbb\exception\exception_interface;
+use phpbb\install\controller\helper;
+use phpbb\language\language;
+use phpbb\template\template;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+
+/**
+ * Exception handler for the installer
+ */
+class kernel_exception_subscriber implements EventSubscriberInterface
+{
+ /**
+ * @var helper
+ */
+ protected $controller_helper;
+
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * @var template
+ */
+ protected $template;
+
+ /**
+ * Constructor
+ *
+ * @param helper $controller_helper
+ * @param language $language
+ * @param template $template
+ */
+ public function __construct(helper $controller_helper, language $language, template $template)
+ {
+ $this->controller_helper = $controller_helper;
+ $this->language = $language;
+ $this->template = $template;
+ }
+
+ /**
+ * This listener is run when the KernelEvents::EXCEPTION event is triggered
+ *
+ * @param GetResponseForExceptionEvent $event
+ */
+ public function on_kernel_exception(GetResponseForExceptionEvent $event)
+ {
+ $exception = $event->getException();
+ $message = $exception->getMessage();
+
+ if ($exception instanceof exception_interface)
+ {
+ $message = $this->language->lang_array($message, $exception->get_parameters());
+ }
+
+ if (!$event->getRequest()->isXmlHttpRequest())
+ {
+ $this->template->assign_vars(array(
+ 'TITLE' => $this->language->lang('INFORMATION'),
+ 'BODY' => $message,
+ ));
+
+ $response = $this->controller_helper->render(
+ 'installer_main.html',
+ $this->language->lang('INFORMATION'),
+ false,
+ 500
+ );
+ }
+ else
+ {
+ $data = array();
+
+ if (!empty($message))
+ {
+ $data['message'] = $message;
+ }
+
+ if (defined('DEBUG'))
+ {
+ $data['trace'] = $exception->getTrace();
+ }
+
+ $response = new JsonResponse($data, 500);
+ }
+
+ if ($exception instanceof HttpExceptionInterface)
+ {
+ $response->setStatusCode($exception->getStatusCode());
+ $response->headers->add($exception->getHeaders());
+ }
+
+ $event->setResponse($response);
+ }
+
+ /**
+ * Returns an array of events the object is subscribed to
+ *
+ * @return array Array of events the object is subscribed to
+ */
+ static public function getSubscribedEvents()
+ {
+ return array(
+ KernelEvents::EXCEPTION => 'on_kernel_exception',
+ );
+ }
+}
diff --git a/phpBB/phpbb/install/exception/cannot_build_container_exception.php b/phpBB/phpbb/install/exception/cannot_build_container_exception.php
new file mode 100644
index 0000000000..6cf12b008b
--- /dev/null
+++ b/phpBB/phpbb/install/exception/cannot_build_container_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * Thrown when the container cannot be built
+ */
+class cannot_build_container_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/file_updater_failure_exception.php b/phpBB/phpbb/install/exception/file_updater_failure_exception.php
new file mode 100644
index 0000000000..46ba2ed32d
--- /dev/null
+++ b/phpBB/phpbb/install/exception/file_updater_failure_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * Thrown when the file updater fails
+ */
+class file_updater_failure_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/installer_config_not_writable_exception.php b/phpBB/phpbb/install/exception/installer_config_not_writable_exception.php
new file mode 100644
index 0000000000..51864c5dca
--- /dev/null
+++ b/phpBB/phpbb/install/exception/installer_config_not_writable_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * Thrown when installer config is not writable to disk
+ */
+class installer_config_not_writable_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/installer_exception.php b/phpBB/phpbb/install/exception/installer_exception.php
new file mode 100644
index 0000000000..f17dca8f17
--- /dev/null
+++ b/phpBB/phpbb/install/exception/installer_exception.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+use phpbb\exception\runtime_exception;
+
+/**
+ * Installer's base exception
+ */
+class installer_exception extends runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/invalid_dbms_exception.php b/phpBB/phpbb/install/exception/invalid_dbms_exception.php
new file mode 100644
index 0000000000..38de5f613a
--- /dev/null
+++ b/phpBB/phpbb/install/exception/invalid_dbms_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * Thrown when an unavailable DBMS has been selected
+ */
+class invalid_dbms_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/jump_to_restart_point_exception.php b/phpBB/phpbb/install/exception/jump_to_restart_point_exception.php
new file mode 100644
index 0000000000..b628c4fbe3
--- /dev/null
+++ b/phpBB/phpbb/install/exception/jump_to_restart_point_exception.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\install\exception;
+
+class jump_to_restart_point_exception extends installer_exception
+{
+ /**
+ * @var string
+ */
+ protected $restart_point_name;
+
+ /**
+ * Constructor
+ *
+ * @param string $restart_point_name
+ */
+ public function __construct($restart_point_name)
+ {
+ $this->restart_point_name = $restart_point_name;
+
+ parent::__construct();
+ }
+
+ /**
+ * Returns the restart point's name
+ *
+ * @return string
+ */
+ public function get_restart_point_name()
+ {
+ return $this->restart_point_name;
+ }
+}
diff --git a/phpBB/phpbb/install/exception/resource_limit_reached_exception.php b/phpBB/phpbb/install/exception/resource_limit_reached_exception.php
new file mode 100644
index 0000000000..025e09fbd3
--- /dev/null
+++ b/phpBB/phpbb/install/exception/resource_limit_reached_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * Thrown when the installer is out of memory or time
+ */
+class resource_limit_reached_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/exception/user_interaction_required_exception.php b/phpBB/phpbb/install/exception/user_interaction_required_exception.php
new file mode 100644
index 0000000000..d65a448841
--- /dev/null
+++ b/phpBB/phpbb/install/exception/user_interaction_required_exception.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\exception;
+
+/**
+ * This exception should be thrown when user interaction is inevitable
+ *
+ * Note: Please note that the output should already be setup for the user
+ * when you use throw this exception
+ */
+class user_interaction_required_exception extends installer_exception
+{
+
+}
diff --git a/phpBB/phpbb/install/helper/config.php b/phpBB/phpbb/install/helper/config.php
new file mode 100644
index 0000000000..0f0840f470
--- /dev/null
+++ b/phpBB/phpbb/install/helper/config.php
@@ -0,0 +1,438 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper;
+
+use phpbb\install\exception\installer_config_not_writable_exception;
+
+/**
+ * Stores common settings and installation status
+ */
+class config
+{
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * Array which contains config settings for the installer
+ *
+ * The array will also store all the user input, as well as any
+ * data that is passed to other tasks by a task.
+ *
+ * @var array
+ */
+ protected $installer_config;
+
+ /**
+ * @var string
+ */
+ protected $install_config_file;
+
+ /**
+ * @var \bantu\IniGetWrapper\IniGetWrapper
+ */
+ protected $php_ini;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Array containing progress information
+ *
+ * @var array
+ */
+ protected $progress_data;
+
+ /**
+ * Array containing system information
+ *
+ * The array contains run time and memory limitations.
+ *
+ * @var array
+ */
+ protected $system_data;
+
+ /**
+ * Array containing navigation bar information
+ *
+ * @var array
+ */
+ protected $navigation_data;
+
+ /**
+ * Flag indicating that config file should be cleaned up
+ *
+ * @var bool
+ */
+ protected $do_clean_up;
+
+ /**
+ * Constructor
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, \bantu\IniGetWrapper\IniGetWrapper $php_ini, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->php_ini = $php_ini;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->do_clean_up = false;
+
+ // Set up data arrays
+ $this->navigation_data = array();
+ $this->installer_config = array();
+ $this->system_data = array();
+ $this->progress_data = array(
+ 'last_task_module_name' => '', // Stores the service name of the latest finished module
+ 'last_task_name' => '', // Stores the service name of the latest finished task
+ 'max_task_progress' => 0,
+ 'current_task_progress' => 0,
+ '_restart_points' => array(),
+ 'use_restart_point' => false,
+ );
+
+ $this->install_config_file = $this->phpbb_root_path . 'store/install_config.php';
+
+ $this->setup_system_data();
+ }
+
+ /**
+ * Returns data for a specified parameter
+ *
+ * @param string $param_name Name of the parameter to return
+ * @param mixed $default Default value to return when the specified data
+ * does not exist.
+ *
+ * @return mixed value of the specified parameter or the default value if the data
+ * cannot be recovered.
+ */
+ public function get($param_name, $default = false)
+ {
+ return (isset($this->installer_config[$param_name])) ? $this->installer_config[$param_name] : $default;
+ }
+
+ /**
+ * Sets a parameter in installer_config
+ *
+ * @param string $param_name Name of the parameter
+ * @param mixed $value Values to set the parameter
+ */
+ public function set($param_name, $value)
+ {
+ $this->installer_config = array_merge($this->installer_config, array(
+ $param_name => $value,
+ ));
+ }
+
+ /**
+ * Returns system parameter
+ *
+ * @param string $param_name Name of the parameter
+ *
+ * @return mixed Returns system parameter if it is defined, false otherwise
+ */
+ public function system_get($param_name)
+ {
+ return (isset($this->system_data[$param_name])) ? $this->system_data[$param_name] : false;
+ }
+
+ /**
+ * Returns remaining time until the run time limit
+ *
+ * @return int Remaining time until the run time limit in seconds
+ */
+ public function get_time_remaining()
+ {
+ if ($this->system_data['max_execution_time'] <= 0)
+ {
+ return 1;
+ }
+
+ return ($this->system_data['start_time'] + $this->system_data['max_execution_time']) - time();
+ }
+
+ /**
+ * Returns remaining memory available for PHP
+ *
+ * @return int Remaining memory until reaching the limit
+ */
+ public function get_memory_remaining()
+ {
+ if ($this->system_data['memory_limit'] <= 0)
+ {
+ return 1;
+ }
+
+ if (function_exists('memory_get_usage'))
+ {
+ return ($this->system_data['memory_limit'] - memory_get_usage());
+ }
+
+ // If we cannot get the information then just return a positive number (and cross fingers)
+ return 1;
+ }
+
+ /**
+ * Saves the latest executed task
+ *
+ * @param string $task_service_name Name of the installer task service
+ */
+ public function set_finished_task($task_service_name)
+ {
+ $this->progress_data['last_task_name'] = $task_service_name;
+ }
+
+ /**
+ * Set active module
+ *
+ * @param string $module_service_name Name of the installer module service
+ */
+ public function set_active_module($module_service_name)
+ {
+ $this->progress_data['last_task_module_name'] = $module_service_name;
+ }
+
+ /**
+ * Getter for progress data
+ *
+ * @return array
+ */
+ public function get_progress_data()
+ {
+ return $this->progress_data;
+ }
+
+ /**
+ * Recovers install configuration from file
+ */
+ public function load_config()
+ {
+ if (!$this->filesystem->exists($this->install_config_file))
+ {
+ return;
+ }
+
+ $file_content = @file_get_contents($this->install_config_file);
+ $serialized_data = trim(substr($file_content, 8));
+
+ $this->installer_config = array();
+ $this->progress_data = array();
+ $this->navigation_data = array();
+
+ if (!empty($serialized_data))
+ {
+ $unserialized_data = json_decode($serialized_data, true);
+
+ $this->installer_config = (is_array($unserialized_data['installer_config'])) ? $unserialized_data['installer_config'] : array();
+ $this->progress_data = (is_array($unserialized_data['progress_data'])) ? $unserialized_data['progress_data'] : array();
+ $this->navigation_data = (is_array($unserialized_data['navigation_data'])) ? $unserialized_data['navigation_data'] : array();
+ }
+ }
+
+ /**
+ * Creates a progress restart point
+ *
+ * Restart points can be used to repeat certain tasks periodically.
+ * You need to call this method from the first task you want to repeat.
+ *
+ * @param string $name Name of the restart point
+ */
+ public function create_progress_restart_point($name)
+ {
+ $tmp_progress_data = $this->progress_data;
+ unset($tmp_progress_data['_restart_points']);
+
+ $this->progress_data['_restart_points'][$name] = $tmp_progress_data;
+ }
+
+ /**
+ * Set restart point to continue from
+ *
+ * @param string $name Name of the restart point
+ *
+ * @return bool Returns false if the restart point name does not exist, otherwise true
+ */
+ public function jump_to_restart_point($name)
+ {
+ if (!isset($this->progress_data['_restart_points'][$name]) || empty($this->progress_data['_restart_points'][$name]))
+ {
+ return false;
+ }
+
+ foreach ($this->progress_data['_restart_points'][$name] as $key => $value)
+ {
+ $this->progress_data[$key] = $value;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns whether a restart point with a given name exists or not
+ *
+ * @param string $name Name of the restart point
+ *
+ * @return bool
+ */
+ public function has_restart_point($name)
+ {
+ return isset($this->progress_data['_restart_points'][$name]);
+ }
+
+ /**
+ * Dumps install configuration to disk
+ */
+ public function save_config()
+ {
+ if ($this->do_clean_up)
+ {
+ @unlink($this->install_config_file);
+ return;
+ }
+
+ // Create array to save
+ $save_array = array(
+ 'installer_config' => $this->installer_config,
+ 'progress_data' => $this->progress_data,
+ 'navigation_data' => $this->navigation_data,
+ );
+
+ // Create file content
+ $file_content = '<?php // ';
+ $file_content .= json_encode($save_array);
+ $file_content .= "\n";
+
+ // Dump file_content to disk
+ $fp = @fopen($this->install_config_file, 'w');
+ if (!$fp)
+ {
+ throw new installer_config_not_writable_exception();
+ }
+
+ fwrite($fp, $file_content);
+ fclose($fp);
+ }
+
+ /**
+ * Increments the task progress
+ *
+ * @param int $increment_by The amount to increment by
+ */
+ public function increment_current_task_progress($increment_by = 1)
+ {
+ $this->progress_data['current_task_progress'] += $increment_by;
+
+ if ($this->progress_data['current_task_progress'] > $this->progress_data['max_task_progress'])
+ {
+ $this->progress_data['current_task_progress'] = $this->progress_data['max_task_progress'];
+ }
+ }
+
+ /**
+ * Sets the task progress to a specific number
+ *
+ * @param int $task_progress The task progress number to be set
+ */
+ public function set_current_task_progress($task_progress)
+ {
+ $this->progress_data['current_task_progress'] = $task_progress;
+ }
+
+ /**
+ * Sets the number of tasks belonging to the installer in the current mode.
+ *
+ * @param int $task_progress_count Number of tasks
+ */
+ public function set_task_progress_count($task_progress_count)
+ {
+ $this->progress_data['max_task_progress'] = $task_progress_count;
+ }
+
+ /**
+ * Returns the number of the current task being executed
+ *
+ * @return int
+ */
+ public function get_current_task_progress()
+ {
+ return $this->progress_data['current_task_progress'];
+ }
+
+ /**
+ * Returns the number of tasks belonging to the installer in the current mode.
+ *
+ * @return int
+ */
+ public function get_task_progress_count()
+ {
+ return $this->progress_data['max_task_progress'];
+ }
+
+ /**
+ * Marks stage as completed in the navigation bar
+ *
+ * @param array $nav_path Array to the navigation elem
+ */
+ public function set_finished_navigation_stage($nav_path)
+ {
+ $this->navigation_data['finished'][] = $nav_path;
+ }
+
+ /**
+ * Marks stage as active in the navigation bar
+ *
+ * @param array $nav_path Array to the navigation elem
+ */
+ public function set_active_navigation_stage($nav_path)
+ {
+ $this->navigation_data['active'] = $nav_path;
+ }
+
+ /**
+ * Returns navigation data
+ *
+ * @return array
+ */
+ public function get_navigation_data()
+ {
+ return $this->navigation_data;
+ }
+
+ /**
+ * Removes install config file
+ */
+ public function clean_up_config_file()
+ {
+ $this->do_clean_up = true;
+ @unlink($this->install_config_file);
+ }
+
+ /**
+ * Filling up system_data array
+ */
+ protected function setup_system_data()
+ {
+ // Query maximum runtime from php.ini
+ $execution_time = $this->php_ini->getNumeric('max_execution_time');
+ $execution_time = min(15, $execution_time / 2);
+ $this->system_data['max_execution_time'] = $execution_time;
+
+ // Set start time
+ $this->system_data['start_time'] = time();
+
+ // Get memory limit
+ $this->system_data['memory_limit'] = $this->php_ini->getBytes('memory_limit');
+ }
+}
diff --git a/phpBB/phpbb/install/helper/container_factory.php b/phpBB/phpbb/install/helper/container_factory.php
new file mode 100644
index 0000000000..6c1ecd2d02
--- /dev/null
+++ b/phpBB/phpbb/install/helper/container_factory.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\install\helper;
+
+use phpbb\cache\driver\dummy;
+use phpbb\install\exception\cannot_build_container_exception;
+use phpbb\language\language;
+use phpbb\request\request;
+
+class container_factory
+{
+ /**
+ * @var language
+ */
+ protected $language;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var \phpbb\request\request
+ */
+ protected $request;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * The full phpBB container
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * Constructor
+ *
+ * @param language $language Language service
+ * @param request $request Request interface
+ * @param update_helper $update_helper Update helper
+ * @param string $phpbb_root_path Path to phpBB's root
+ * @param string $php_ext Extension of PHP files
+ */
+ public function __construct(language $language, request $request, update_helper $update_helper, $phpbb_root_path, $php_ext)
+ {
+ $this->language = $language;
+ $this->request = $request;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->container = null;
+ }
+
+ /**
+ * Container getter
+ *
+ * @param null|string $service_name Name of the service to return
+ *
+ * @return \Symfony\Component\DependencyInjection\ContainerInterface|Object phpBB's dependency injection container
+ * or the service specified in $service_name
+ *
+ * @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
+ * @throws \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException If the service is not defined
+ * @throws \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException When a circular reference is detected
+ * @throws \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException When the service is not defined
+ */
+ public function get($service_name = null)
+ {
+ // Check if container was built, if not try to build it
+ if ($this->container === null)
+ {
+ $this->build_container();
+ }
+
+ return ($service_name === null) ? $this->container : $this->container->get($service_name);
+ }
+
+ /**
+ * Returns the specified parameter from the container
+ *
+ * @param string $param_name
+ *
+ * @return mixed
+ *
+ * @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
+ */
+ public function get_parameter($param_name)
+ {
+ // Check if container was built, if not try to build it
+ if ($this->container === null)
+ {
+ $this->build_container();
+ }
+
+ return $this->container->getParameter($param_name);
+ }
+
+ /**
+ * Build dependency injection container
+ *
+ * @throws \phpbb\install\exception\cannot_build_container_exception When container cannot be built
+ */
+ protected function build_container()
+ {
+ // If the container has been already built just return.
+ // Although this should never happen
+ if ($this->container instanceof \Symfony\Component\DependencyInjection\ContainerInterface)
+ {
+ return;
+ }
+
+ // Check whether container can be built
+ // We need config.php for that so let's check if it has been set up yet
+ if (!filesize($this->phpbb_root_path . 'config.' . $this->php_ext))
+ {
+ throw new cannot_build_container_exception();
+ }
+
+ $phpbb_config_php_file = new \phpbb\config_php_file($this->phpbb_root_path, $this->php_ext);
+ $phpbb_container_builder = new \phpbb\di\container_builder($this->phpbb_root_path, $this->php_ext);
+
+ // For BC with functions that we need during install
+ global $phpbb_container, $table_prefix;
+
+ $disable_super_globals = $this->request->super_globals_disabled();
+
+ // This is needed because container_builder::get_env_parameters() uses $_SERVER
+ if ($disable_super_globals)
+ {
+ $this->request->enable_super_globals();
+ }
+
+ $other_config_path = $this->phpbb_root_path . 'install/update/new/config';
+ $config_path = (is_dir($other_config_path)) ? $other_config_path : $this->phpbb_root_path . 'config';
+
+ $this->container = $phpbb_container_builder
+ ->with_environment('production')
+ ->with_config($phpbb_config_php_file)
+ ->with_config_path($config_path)
+ ->without_cache()
+ ->without_compiled_container()
+ ->get_container();
+
+ // Setting request is required for the compatibility globals as those are generated from
+ // this container
+ $this->container->register('request')->setSynthetic(true);
+ $this->container->set('request', $this->request);
+
+ $this->container->register('language')->setSynthetic(true);
+ $this->container->set('language', $this->language);
+
+ // Replace cache service, as config gets cached, and we don't want that when we are installing
+ if (!is_dir($other_config_path))
+ {
+ $this->container->register('cache.driver')->setSynthetic(true);
+ $this->container->set('cache.driver', new dummy());
+ }
+
+ $this->container->compile();
+
+ $phpbb_container = $this->container;
+ $table_prefix = $phpbb_config_php_file->get('table_prefix');
+
+ // Restore super globals to previous state
+ if ($disable_super_globals)
+ {
+ $this->request->disable_super_globals();
+ }
+
+ // Get compatibilty globals and constants
+ $this->update_helper->include_file('includes/compatibility_globals.' . $this->php_ext);
+ $this->update_helper->include_file('includes/constants.' . $this->php_ext);
+ }
+}
diff --git a/phpBB/phpbb/install/helper/database.php b/phpBB/phpbb/install/helper/database.php
new file mode 100644
index 0000000000..c4c90a01a4
--- /dev/null
+++ b/phpBB/phpbb/install/helper/database.php
@@ -0,0 +1,456 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper;
+
+use phpbb\install\exception\invalid_dbms_exception;
+
+/**
+ * Database related general functionality for installer
+ */
+class database
+{
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var array
+ */
+ protected $supported_dbms = array(
+ // Note: php 5.5 alpha 2 deprecated mysql.
+ // Keep mysqli before mysql in this list.
+ 'mysqli' => array(
+ 'LABEL' => 'MySQL with MySQLi Extension',
+ 'SCHEMA' => 'mysql_41',
+ 'MODULE' => 'mysqli',
+ 'DELIM' => ';',
+ 'DRIVER' => 'phpbb\db\driver\mysqli',
+ 'AVAILABLE' => true,
+ '2.0.x' => true,
+ ),
+ 'mysql' => array(
+ 'LABEL' => 'MySQL',
+ 'SCHEMA' => 'mysql',
+ 'MODULE' => 'mysql',
+ 'DELIM' => ';',
+ 'DRIVER' => 'phpbb\db\driver\mysql',
+ 'AVAILABLE' => true,
+ '2.0.x' => true,
+ ),
+ 'mssql' => array(
+ 'LABEL' => 'MS SQL Server 2000+',
+ 'SCHEMA' => 'mssql',
+ 'MODULE' => 'mssql',
+ 'DELIM' => 'GO',
+ 'DRIVER' => 'phpbb\db\driver\mssql',
+ 'AVAILABLE' => true,
+ '2.0.x' => true,
+ ),
+ 'mssql_odbc'=> array(
+ 'LABEL' => 'MS SQL Server [ ODBC ]',
+ 'SCHEMA' => 'mssql',
+ 'MODULE' => 'odbc',
+ 'DELIM' => 'GO',
+ 'DRIVER' => 'phpbb\db\driver\mssql_odbc',
+ 'AVAILABLE' => true,
+ '2.0.x' => true,
+ ),
+ 'mssqlnative' => array(
+ 'LABEL' => 'MS SQL Server 2005+ [ Native ]',
+ 'SCHEMA' => 'mssql',
+ 'MODULE' => 'sqlsrv',
+ 'DELIM' => 'GO',
+ 'DRIVER' => 'phpbb\db\driver\mssqlnative',
+ 'AVAILABLE' => true,
+ '2.0.x' => false,
+ ),
+ 'oracle' => array(
+ 'LABEL' => 'Oracle',
+ 'SCHEMA' => 'oracle',
+ 'MODULE' => 'oci8',
+ 'DELIM' => '/',
+ 'DRIVER' => 'phpbb\db\driver\oracle',
+ 'AVAILABLE' => true,
+ '2.0.x' => false,
+ ),
+ 'postgres' => array(
+ 'LABEL' => 'PostgreSQL 8.3+',
+ 'SCHEMA' => 'postgres',
+ 'MODULE' => 'pgsql',
+ 'DELIM' => ';',
+ 'DRIVER' => 'phpbb\db\driver\postgres',
+ 'AVAILABLE' => true,
+ '2.0.x' => true,
+ ),
+ 'sqlite' => array(
+ 'LABEL' => 'SQLite',
+ 'SCHEMA' => 'sqlite',
+ 'MODULE' => 'sqlite',
+ 'DELIM' => ';',
+ 'DRIVER' => 'phpbb\db\driver\sqlite',
+ 'AVAILABLE' => true,
+ '2.0.x' => false,
+ ),
+ 'sqlite3' => array(
+ 'LABEL' => 'SQLite3',
+ 'SCHEMA' => 'sqlite',
+ 'MODULE' => 'sqlite3',
+ 'DELIM' => ';',
+ 'DRIVER' => 'phpbb\db\driver\sqlite3',
+ 'AVAILABLE' => true,
+ '2.0.x' => false,
+ ),
+ );
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem interface
+ * @param string $phpbb_root_path Path to phpBB's root
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * Returns an array of available DBMS supported by phpBB
+ *
+ * If a DBMS is specified it will only return data for that DBMS
+ * and will load its extension if necessary.
+ *
+ * @param mixed $dbms name of the DBMS that's info is required or false for all DBMS info
+ * @param bool $return_unavailable set it to true if you expect unavailable but supported DBMS
+ * returned as well
+ * @param bool $only_20x_options set it to true if you only want to recover 2.0.x options
+ *
+ * @return array Array of available and supported DBMS
+ */
+ public function get_available_dbms($dbms = false, $return_unavailable = false, $only_20x_options = false)
+ {
+ $available_dbms = $this->supported_dbms;
+
+ if ($dbms)
+ {
+ if (isset($this->supported_dbms[$dbms]))
+ {
+ $available_dbms = array($dbms => $this->supported_dbms[$dbms]);
+ }
+ else
+ {
+ return array();
+ }
+ }
+
+ $any_dbms_available = false;
+ foreach ($available_dbms as $db_name => $db_array)
+ {
+ if ($only_20x_options && !$db_array['2.0.x'])
+ {
+ if ($return_unavailable)
+ {
+ $available_dbms[$db_name]['AVAILABLE'] = false;
+ }
+ else
+ {
+ unset($available_dbms[$db_name]);
+ }
+
+ continue;
+ }
+
+ $dll = $db_array['MODULE'];
+ if (!@extension_loaded($dll))
+ {
+ if ($return_unavailable)
+ {
+ $available_dbms[$db_name]['AVAILABLE'] = false;
+ }
+ else
+ {
+ unset($available_dbms[$db_name]);
+ }
+
+ continue;
+ }
+
+ $any_dbms_available = true;
+ }
+
+ if ($return_unavailable)
+ {
+ $available_dbms['ANY_DB_SUPPORT'] = $any_dbms_available;
+ }
+
+ return $available_dbms;
+ }
+
+ /**
+ * Removes "/* style" as well as "# style" comments from $input.
+ *
+ * @param string $sql_query Input string
+ *
+ * @return string Input string with comments removed
+ */
+ public function remove_comments($sql_query)
+ {
+ // Remove /* */ comments (http://ostermiller.org/findcomment.html)
+ $sql_query = preg_replace('#/\*(.|[\r\n])*?\*/#', "\n", $sql_query);
+
+ // Remove # style comments
+ $sql_query = preg_replace('/\n{2,}/', "\n", preg_replace('/^#.*$/m', "\n", $sql_query));
+
+ return $sql_query;
+ }
+
+ /**
+ * split_sql_file() will split an uploaded sql file into single sql statements.
+ *
+ * Note: expects trim() to have already been run on $sql.
+ *
+ * @param string $sql SQL statements
+ * @param string $delimiter Delimiter between sql statements
+ *
+ * @return array Array of sql statements
+ */
+ public function split_sql_file($sql, $delimiter)
+ {
+ $sql = str_replace("\r" , '', $sql);
+ $data = preg_split('/' . preg_quote($delimiter, '/') . '$/m', $sql);
+
+ $data = array_map('trim', $data);
+
+ // The empty case
+ $end_data = end($data);
+
+ if (empty($end_data))
+ {
+ unset($data[key($data)]);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Validates table prefix
+ *
+ * @param string $dbms The selected dbms
+ * @param string $table_prefix The table prefix to validate
+ *
+ * @return bool|array true if table prefix is valid, array of errors otherwise
+ *
+ * @throws \phpbb\install\exception\invalid_dbms_exception When $dbms is not a valid
+ */
+ public function validate_table_prefix($dbms, $table_prefix)
+ {
+ $errors = array();
+
+ if (!preg_match('#^[a-zA-Z][a-zA-Z0-9_]*$#', $table_prefix))
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_INVALID_PREFIX',
+ );
+ }
+
+ // Do dbms specific checks
+ $dbms_info = $this->get_available_dbms($dbms);
+ switch ($dbms_info[$dbms]['SCHEMA'])
+ {
+ case 'mysql':
+ case 'mysql_41':
+ $prefix_length = 36;
+ break;
+ case 'mssql':
+ $prefix_length = 90;
+ break;
+ case 'oracle':
+ $prefix_length = 6;
+ break;
+ case 'postgres':
+ $prefix_length = 36;
+ break;
+ case 'sqlite':
+ $prefix_length = 200;
+ break;
+ default:
+ throw new invalid_dbms_exception();
+ break;
+ }
+
+ // Check the prefix length to ensure that index names are not too long
+ if (strlen($table_prefix) > $prefix_length)
+ {
+ $errors[] = array(
+ 'title' => array('INST_ERR_PREFIX_TOO_LONG', $prefix_length),
+ );
+ }
+
+ return (empty($errors)) ? true : $errors;
+ }
+
+ /**
+ * Check if the user provided database parameters are correct
+ *
+ * This function checks the database connection data and also checks for
+ * any other problems that could cause an error during the installation
+ * such as if there is any database table names conflicting.
+ *
+ * Note: The function assumes that $table_prefix has been already validated
+ * with validate_table_prefix().
+ *
+ * @param string $dbms Selected database type
+ * @param string $dbhost Database host address
+ * @param int $dbport Database port number
+ * @param string $dbuser Database username
+ * @param string $dbpass Database password
+ * @param string $dbname Database name
+ * @param string $table_prefix Database table prefix
+ *
+ * @return array|bool Returns true if test is successful, array of errors otherwise
+ */
+ public function check_database_connection($dbms, $dbhost, $dbport, $dbuser, $dbpass, $dbname, $table_prefix)
+ {
+ $dbms_info = $this->get_available_dbms($dbms);
+ $dbms_info = $dbms_info[$dbms];
+ $errors = array();
+
+ // Instantiate it and set return on error true
+ /** @var \phpbb\db\driver\driver_interface $db */
+ $db = new $dbms_info['DRIVER'];
+ $db->sql_return_on_error(true);
+
+ // Check that we actually have a database name before going any further
+ if (!in_array($dbms_info['SCHEMA'], array('sqlite', 'oracle'), true) && $dbname === '')
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_NAME',
+ );
+ }
+
+ // Make sure we don't have a daft user who thinks having the SQLite database in the forum directory is a good idea
+ if ($dbms_info['SCHEMA'] === 'sqlite'
+ && stripos($this->filesystem->realpath($dbhost), $this->filesystem->realpath($this->phpbb_root_path) === 0))
+ {
+ $errors[] = array(
+ 'title' =>'INST_ERR_DB_FORUM_PATH',
+ );
+ }
+
+ // Try to connect to db
+ if (is_array($db->sql_connect($dbhost, $dbuser, $dbpass, $dbname, $dbport, false, true)))
+ {
+ $db_error = $db->sql_error();
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_CONNECT',
+ 'description' => ($db_error['message']) ? utf8_convert_message($db_error['message']) : 'INST_ERR_DB_NO_ERROR',
+ );
+ }
+ else
+ {
+ // Check if there is any table name collisions
+ $temp_prefix = strtolower($table_prefix);
+ $table_ary = array(
+ $temp_prefix . 'attachments',
+ $temp_prefix . 'config',
+ $temp_prefix . 'sessions',
+ $temp_prefix . 'topics',
+ $temp_prefix . 'users',
+ );
+
+ $db_tools_factory = new \phpbb\db\tools\factory();
+ $db_tools = $db_tools_factory->get($db);
+ $tables = $db_tools->sql_list_tables();
+ $tables = array_map('strtolower', $tables);
+ $table_intersect = array_intersect($tables, $table_ary);
+
+ if (sizeof($table_intersect))
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_PREFIX',
+ );
+ }
+
+ // Check if database version is supported
+ switch ($dbms)
+ {
+ case 'mysqli':
+ if (version_compare($db->sql_server_info(true), '4.1.3', '<'))
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_MYSQLI',
+ );
+ }
+ break;
+ case 'sqlite':
+ if (version_compare($db->sql_server_info(true), '2.8.2', '<'))
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_SQLITE',
+ );
+ }
+ break;
+ case 'sqlite3':
+ if (version_compare($db->sql_server_info(true), '3.6.15', '<'))
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_SQLITE3',
+ );
+ }
+ break;
+ case 'oracle':
+ $sql = "SELECT *
+ FROM NLS_DATABASE_PARAMETERS
+ WHERE PARAMETER = 'NLS_RDBMS_VERSION'
+ OR PARAMETER = 'NLS_CHARACTERSET'";
+ $result = $db->sql_query($sql);
+
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $stats[$row['parameter']] = $row['value'];
+ }
+ $db->sql_freeresult($result);
+
+ if (version_compare($stats['NLS_RDBMS_VERSION'], '9.2', '<') && $stats['NLS_CHARACTERSET'] !== 'UTF8')
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_ORACLE',
+ );
+ }
+ break;
+ case 'postgres':
+ $sql = "SHOW server_encoding;";
+ $result = $db->sql_query($sql);
+ $row = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+
+ if ($row['server_encoding'] !== 'UNICODE' && $row['server_encoding'] !== 'UTF8')
+ {
+ $errors[] = array(
+ 'title' => 'INST_ERR_DB_NO_POSTGRES',
+ );
+ }
+ break;
+ }
+ }
+
+ return (empty($errors)) ? true : $errors;
+ }
+}
diff --git a/phpBB/phpbb/install/helper/file_updater/compression_file_updater.php b/phpBB/phpbb/install/helper/file_updater/compression_file_updater.php
new file mode 100644
index 0000000000..ede992fb6e
--- /dev/null
+++ b/phpBB/phpbb/install/helper/file_updater/compression_file_updater.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\install\helper\file_updater;
+
+use phpbb\install\helper\update_helper;
+
+/**
+ * File updater for generating archive with updated files
+ */
+class compression_file_updater implements file_updater_interface
+{
+ /**
+ * @var \compress
+ */
+ protected $compress;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(update_helper $update_helper, $phpbb_root_path, $php_ext)
+ {
+ $this->compress = null;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Set the compression method
+ *
+ * @param string $method Compression method's file extension
+ *
+ * @return string Archive's filename
+ */
+ public function init($method)
+ {
+ $this->update_helper->include_file('includes/functions_compress.' . $this->php_ext);
+
+ $archive_filename = 'update_archive_' . time() . '_' . uniqid();
+ $path = $this->phpbb_root_path . 'store/' . $archive_filename . '' . $method;
+
+ if ($method === '.zip')
+ {
+ $this->compress = new \compress_zip('w', $path);
+ }
+ else
+ {
+ $this->compress = new \compress_tar('w', $path, $method);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Close archive writing process
+ */
+ public function close()
+ {
+ $this->compress->close();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function delete_file($path_to_file)
+ {
+ // We do absolutely nothing here, as this function is called when a file should be
+ // removed from the filesystem, but since this is an archive generator, it clearly
+ // cannot do that.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function create_new_file($path_to_file_to_create, $source, $create_from_content = false)
+ {
+ if ($create_from_content)
+ {
+ $this->compress->add_data($source, $path_to_file_to_create);
+ }
+ else
+ {
+ $this->compress->add_custom_file($source, $path_to_file_to_create);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function update_file($path_to_file_to_update, $source, $create_from_content = false)
+ {
+ // Both functions are identical here
+ $this->create_new_file($path_to_file_to_update, $source, $create_from_content);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_method_name()
+ {
+ return 'compression';
+ }
+}
diff --git a/phpBB/phpbb/install/helper/file_updater/factory.php b/phpBB/phpbb/install/helper/file_updater/factory.php
new file mode 100644
index 0000000000..d3a2f22782
--- /dev/null
+++ b/phpBB/phpbb/install/helper/file_updater/factory.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\file_updater;
+
+use phpbb\di\service_collection;
+use phpbb\install\exception\file_updater_failure_exception;
+
+/**
+ * File updater factory
+ */
+class factory
+{
+ /**
+ * @var array
+ */
+ protected $file_updaters;
+
+ /**
+ * Constructor
+ *
+ * @param service_collection $collection File updater service collection
+ */
+ public function __construct(service_collection $collection)
+ {
+ foreach ($collection as $service)
+ {
+ $this->register($service);
+ }
+ }
+
+ /**
+ * Register updater object
+ *
+ * @param file_updater_interface $updater Updater object
+ */
+ public function register(file_updater_interface $updater)
+ {
+ $name = $updater->get_method_name();
+ $this->file_updaters[$name] = $updater;
+ }
+
+ /**
+ * Returns file updater object
+ *
+ * @param string $name Name of the updater method
+ *
+ * @throws file_updater_failure_exception When the specified file updater does not exist
+ */
+ public function get($name)
+ {
+ if (!isset($this->file_updaters[$name]))
+ {
+ throw new file_updater_failure_exception();
+ }
+
+ return $this->file_updaters[$name];
+ }
+}
diff --git a/phpBB/phpbb/install/helper/file_updater/file_updater.php b/phpBB/phpbb/install/helper/file_updater/file_updater.php
new file mode 100644
index 0000000000..cc0f5c6b5f
--- /dev/null
+++ b/phpBB/phpbb/install/helper/file_updater/file_updater.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\file_updater;
+
+use phpbb\filesystem\exception\filesystem_exception;
+use phpbb\filesystem\filesystem;
+use phpbb\install\exception\file_updater_failure_exception;
+
+/**
+ * File updater for direct filesystem access
+ */
+class file_updater implements file_updater_interface
+{
+ /**
+ * @var filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param filesystem $filesystem
+ * @param string $phpbb_root_path
+ */
+ public function __construct(filesystem $filesystem, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws file_updater_failure_exception When the file is not writable
+ * @throws filesystem_exception When the filesystem class fails
+ */
+ public function delete_file($path_to_file)
+ {
+ $this->filesystem->remove($this->phpbb_root_path . $path_to_file);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws file_updater_failure_exception When the file is not writable
+ * @throws filesystem_exception When the filesystem class fails
+ */
+ public function create_new_file($path_to_file_to_create, $source, $create_from_content = false)
+ {
+ $path_to_file_to_create = $this->phpbb_root_path . $path_to_file_to_create;
+
+ $dir = dirname($path_to_file_to_create);
+ if (!$this->filesystem->exists($dir))
+ {
+ $this->make_dir($dir);
+ }
+
+ $original_dir_perms = false;
+
+ if (!$this->filesystem->is_writable($dir))
+ {
+ // Extract last 9 bits we actually need
+ $original_dir_perms = @fileperms($dir) & 511;
+ $this->filesystem->phpbb_chmod($dir, filesystem::CHMOD_ALL);
+ }
+
+ if (!$create_from_content)
+ {
+ try
+ {
+ $this->filesystem->copy($source, $path_to_file_to_create);
+ }
+ catch (filesystem_exception $e)
+ {
+ $this->write_file($path_to_file_to_create, $source, $create_from_content);
+ }
+ }
+ else
+ {
+ $this->write_file($path_to_file_to_create, $source, $create_from_content);
+ }
+
+ if ($original_dir_perms !== false)
+ {
+ $this->filesystem->phpbb_chmod($dir, $original_dir_perms);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws file_updater_failure_exception When the file is not writable
+ * @throws filesystem_exception When the filesystem class fails
+ */
+ public function update_file($path_to_file_to_update, $source, $create_from_content = false)
+ {
+ $path_to_file_to_update = $this->phpbb_root_path . $path_to_file_to_update;
+ $original_file_perms = false;
+
+ // Maybe necessary for binary files
+ $dir = dirname($path_to_file_to_update);
+ if (!$this->filesystem->exists($dir))
+ {
+ $this->make_dir($dir);
+ }
+
+ if (!$this->filesystem->is_writable($path_to_file_to_update))
+ {
+ // Extract last 9 bits we actually need
+ $original_file_perms = @fileperms($path_to_file_to_update) & 511;
+ $this->filesystem->phpbb_chmod($path_to_file_to_update, filesystem::CHMOD_WRITE);
+ }
+
+ if (!$create_from_content)
+ {
+ try
+ {
+ $this->filesystem->copy($source, $path_to_file_to_update, true);
+ }
+ catch (filesystem_exception $e)
+ {
+ $this->write_file($path_to_file_to_update, $source, $create_from_content);
+ }
+ }
+ else
+ {
+ $this->write_file($path_to_file_to_update, $source, $create_from_content);
+ }
+
+ if ($original_file_perms !== false)
+ {
+ $this->filesystem->phpbb_chmod($path_to_file_to_update, $original_file_perms);
+ }
+ }
+
+ /**
+ * Creates directory structure
+ *
+ * @param string $path Path to the directory where the file should be placed (and non-existent)
+ */
+ private function make_dir($path)
+ {
+ if (is_dir($path))
+ {
+ return;
+ }
+
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
+ $this->filesystem->mkdir($path, 493); // 493 === 0755
+ }
+
+ /**
+ * Fallback function for file writing
+ *
+ * @param string $path_to_file Path to the file's location
+ * @param string $source Path to file to copy or string with the new file's content
+ * @param bool|false $create_from_content Whether or not to use $source as the content, false by default
+ *
+ * @throws file_updater_failure_exception When the file is not writable
+ */
+ private function write_file($path_to_file, $source, $create_from_content = false)
+ {
+ if (!$create_from_content)
+ {
+ $source = @file_get_contents($source);
+ }
+
+ $file_pointer = @fopen($path_to_file, 'w');
+
+ if (!is_resource($file_pointer))
+ {
+ throw new file_updater_failure_exception();
+ }
+
+ @fwrite($file_pointer, $source);
+ @fclose($file_pointer);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_method_name()
+ {
+ return 'direct_file';
+ }
+}
diff --git a/phpBB/phpbb/install/helper/file_updater/file_updater_interface.php b/phpBB/phpbb/install/helper/file_updater/file_updater_interface.php
new file mode 100644
index 0000000000..b13d7c9fe1
--- /dev/null
+++ b/phpBB/phpbb/install/helper/file_updater/file_updater_interface.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\install\helper\file_updater;
+
+interface file_updater_interface
+{
+ /**
+ * Deletes a file
+ *
+ * @param string $path_to_file Path to the file to delete
+ */
+ public function delete_file($path_to_file);
+
+ /**
+ * Creates a new file
+ *
+ * @param string $path_to_file_to_create Path to the new file's location
+ * @param string $source Path to file to copy or string with the new file's content
+ * @param bool $create_from_content Whether or not to use $source as the content, false by default
+ */
+ public function create_new_file($path_to_file_to_create, $source, $create_from_content = false);
+
+ /**
+ * Update file
+ *
+ * @param string $path_to_file_to_update Path to the file's location
+ * @param string $source Path to file to copy or string with the new file's content
+ * @param bool $create_from_content Whether or not to use $source as the content, false by default
+ */
+ public function update_file($path_to_file_to_update, $source, $create_from_content = false);
+
+ /**
+ * Returns the name of the file updater method
+ *
+ * @return string
+ */
+ public function get_method_name();
+}
diff --git a/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php b/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php
new file mode 100644
index 0000000000..258a035768
--- /dev/null
+++ b/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.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\install\helper\file_updater;
+
+use phpbb\install\helper\update_helper;
+
+/**
+ * File updater for FTP updates
+ */
+class ftp_file_updater implements file_updater_interface
+{
+ /**
+ * @var \transfer
+ */
+ protected $transfer;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __constructor(update_helper $update_helper, $phpbb_root_path, $php_ext)
+ {
+ $this->transfer = null;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Initialize FTP connection
+ *
+ * @param string $method
+ * @param string $host
+ * @param string $user
+ * @param string $pass
+ * @param string $path
+ * @param int $port
+ * @param int $timeout
+ */
+ public function init($method, $host, $user, $pass, $path, $port, $timeout)
+ {
+ $this->update_helper->include_file('includes/functions_transfer.' . $this->php_ext);
+ $this->transfer = new $method($host, $user, $pass, $path, $port, $timeout);
+ $this->transfer->open_session();
+ }
+
+ /**
+ * Close FTP session
+ */
+ public function close()
+ {
+ $this->transfer->close_session();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function delete_file($path_to_file)
+ {
+ $this->transfer->delete_file($path_to_file);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function create_new_file($path_to_file_to_create, $source, $create_from_content = false)
+ {
+ $dirname = dirname($path_to_file_to_create);
+
+ if ($dirname && !file_exists($this->phpbb_root_path . $dirname))
+ {
+ $this->transfer->make_dir($dirname);
+ }
+
+ if ($create_from_content)
+ {
+ $this->transfer->write_file($path_to_file_to_create, $source);
+ }
+ else
+ {
+ $this->transfer->copy_file($path_to_file_to_create, $source);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function update_file($path_to_file_to_update, $source, $create_from_content = false)
+ {
+ if ($create_from_content)
+ {
+ $this->transfer->write_file($path_to_file_to_update, $source);
+ }
+ else
+ {
+ $this->transfer->copy_file($path_to_file_to_update, $source);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_method_name()
+ {
+ return 'ftp';
+ }
+}
diff --git a/phpBB/phpbb/install/helper/install_helper.php b/phpBB/phpbb/install/helper/install_helper.php
new file mode 100644
index 0000000000..ffe36cd645
--- /dev/null
+++ b/phpBB/phpbb/install/helper/install_helper.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper;
+
+/**
+ * General helper functionality for the installer
+ */
+class install_helper
+{
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path path to phpBB's root
+ * @param string $php_ext Extension of PHP files
+ */
+ public function __construct($phpbb_root_path, $php_ext)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Check whether phpBB is installed.
+ *
+ * @return bool
+ */
+ public function is_phpbb_installed()
+ {
+ $config_path = $this->phpbb_root_path . 'config.' . $this->php_ext;
+ $install_lock_path = $this->phpbb_root_path . 'cache/install_lock';
+
+ if (file_exists($config_path) && !file_exists($install_lock_path) && filesize($config_path))
+ {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php
new file mode 100644
index 0000000000..1342ffa30f
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php
@@ -0,0 +1,390 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\iohandler;
+
+use phpbb\path_helper;
+use phpbb\routing\router;
+
+/**
+ * Input-Output handler for the AJAX frontend
+ */
+class ajax_iohandler extends iohandler_base
+{
+ /**
+ * @var path_helper
+ */
+ protected $path_helper;
+
+ /**
+ * @var \phpbb\request\request_interface
+ */
+ protected $request;
+
+ /**
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * @var router
+ */
+ protected $router;
+
+ /**
+ * @var string
+ */
+ protected $file_status;
+
+ /**
+ * @var string
+ */
+ protected $form;
+
+ /**
+ * @var bool
+ */
+ protected $request_client_refresh;
+
+ /**
+ * @var array
+ */
+ protected $nav_data;
+
+ /**
+ * @var array
+ */
+ protected $cookies;
+
+ /**
+ * @var array
+ */
+ protected $download;
+
+ /**
+ * Constructor
+ *
+ * @param path_helper $path_helper
+ * @param \phpbb\request\request_interface $request HTTP request interface
+ * @param \phpbb\template\template $template Template engine
+ * @param router $router Router
+ */
+ public function __construct(path_helper $path_helper, \phpbb\request\request_interface $request, \phpbb\template\template $template, router $router)
+ {
+ $this->path_helper = $path_helper;
+ $this->request = $request;
+ $this->router = $router;
+ $this->template = $template;
+ $this->form = '';
+ $this->nav_data = array();
+ $this->cookies = array();
+ $this->download = array();
+ $this->file_status = '';
+
+ parent::__construct();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_input($name, $default, $multibyte = false)
+ {
+ return $this->request->variable($name, $default, $multibyte);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_server_variable($name, $default = '')
+ {
+ return $this->request->server($name, $default);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_header_variable($name, $default = '')
+ {
+ return $this->request->header($name, $default);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_secure()
+ {
+ return $this->request->is_secure();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_user_form_group($title, $form)
+ {
+ $this->template->assign_block_vars('options', array(
+ 'LEGEND' => $this->language->lang($title),
+ 'S_LEGEND' => true,
+ ));
+
+ $not_button_form = false;
+
+ foreach ($form as $input_name => $input_options)
+ {
+ if (!isset($input_options['type']))
+ {
+ continue;
+ }
+
+ $tpl_ary = array();
+ $not_button_form = ($input_options['type'] !== 'submit' || $not_button_form);
+
+ $tpl_ary['TYPE'] = $input_options['type'];
+ $tpl_ary['TITLE'] = $this->language->lang($input_options['label']);
+ $tpl_ary['KEY'] = $input_name;
+ $tpl_ary['S_EXPLAIN'] = false;
+
+ if (isset($input_options['default']))
+ {
+ $default = $input_options['default'];
+ $default = preg_replace_callback('#\{L_([A-Z0-9\-_]*)\}#s', array($this, 'lang_replace_callback'), $default);
+ $tpl_ary['DEFAULT'] = $default;
+ }
+
+ if (isset($input_options['description']))
+ {
+ $tpl_ary['TITLE_EXPLAIN'] = $this->language->lang($input_options['description']);
+ $tpl_ary['S_EXPLAIN'] = true;
+ }
+
+ if (in_array($input_options['type'], array('select', 'radio'), true))
+ {
+ for ($i = 0, $total = sizeof($input_options['options']); $i < $total; $i++)
+ {
+ if (isset($input_options['options'][$i]['label']))
+ {
+ $input_options['options'][$i]['label'] = $this->language->lang($input_options['options'][$i]['label']);
+ }
+ }
+
+ $tpl_ary['OPTIONS'] = $input_options['options'];
+ }
+
+ $block_name = ($input_options['type'] === 'submit') ? 'submit_buttons' : 'options';
+ $this->template->assign_block_vars($block_name, $tpl_ary);
+ }
+
+ $this->template->assign_var('S_NOT_ONLY_BUTTON_FORM', $not_button_form);
+
+ $this->template->set_filenames(array(
+ 'form_install' => 'installer_form.html',
+ ));
+
+ $this->form = $this->template->assign_display('form_install');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function send_response()
+ {
+ $json_data_array = $this->prepare_json_array();
+ $json_data = json_encode($json_data_array);
+
+ // Try to push content to the browser
+ print(str_pad(' ', 4096) . "\n");
+ print($json_data . "\n\n");
+ flush();
+ }
+
+ /**
+ * Prepares iohandler's data to be sent out to the client.
+ *
+ * @return array
+ */
+ protected function prepare_json_array()
+ {
+ $json_array = array(
+ 'errors' => $this->errors,
+ 'warnings' => $this->warnings,
+ 'logs' => $this->logs,
+ 'success' => $this->success,
+ 'download' => $this->download,
+ );
+
+ $this->errors = array();
+ $this->warnings = array();
+ $this->logs = array();
+ $this->success = array();
+ $this->download = array();
+
+ if (!empty($this->form))
+ {
+ $json_array['form'] = $this->form;
+ $this->form = '';
+ }
+
+ if (!empty($this->file_status))
+ {
+ $json_array['file_status'] = $this->file_status;
+ $this->file_status = '';
+ }
+
+ // If current task name is set, we push progress message to the client side
+ if (!empty($this->current_task_name))
+ {
+ $json_array['progress'] = array(
+ 'task_name' => $this->current_task_name,
+ 'task_num' => $this->current_task_progress,
+ 'task_count' => $this->task_progress_count,
+ );
+
+ if ($this->restart_progress_bar)
+ {
+ $json_array['progress']['restart'] = 1;
+ $this->restart_progress_bar = false;
+ }
+ }
+
+ if (!empty($this->nav_data))
+ {
+ $json_array['nav'] = $this->nav_data;
+ $this->nav_data = array();
+ }
+
+ if ($this->request_client_refresh)
+ {
+ $json_array['refresh'] = true;
+ $this->request_client_refresh = false;
+ }
+
+ if (!empty($this->cookies))
+ {
+ $json_array['cookies'] = $this->cookies;
+ $this->cookies = array();
+ }
+
+ return $json_array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_progress($task_lang_key, $task_number)
+ {
+ parent::set_progress($task_lang_key, $task_number);
+ $this->send_response();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function request_refresh()
+ {
+ $this->request_client_refresh = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_active_stage_menu($menu_path)
+ {
+ $this->nav_data['active'] = $menu_path[sizeof($menu_path) - 1];
+ $this->send_response();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_finished_stage_menu($menu_path)
+ {
+ $this->nav_data['finished'][] = $menu_path[sizeof($menu_path) - 1];
+ $this->send_response();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_cookie($cookie_name, $cookie_value)
+ {
+ $this->cookies[] = array(
+ 'name' => $cookie_name,
+ 'value' => $cookie_value
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_download_link($route, $title, $msg = null)
+ {
+ $link_properties = array(
+ 'href' => $this->router->generate($route),
+ 'title' => $this->language->lang($title),
+ 'download' => $this->language->lang('DOWNLOAD'),
+ );
+
+ if ($msg !== null)
+ {
+ $link_properties['msg'] = htmlspecialchars_decode($this->language->lang($msg));
+ }
+
+ $this->download[] = $link_properties;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render_update_file_status($status_array)
+ {
+ $this->template->assign_vars(array(
+ 'T_IMAGE_PATH' => $this->path_helper->get_web_root_path() . 'adm/images/',
+ ));
+
+ foreach ($status_array as $block => $list)
+ {
+ foreach ($list as $filename)
+ {
+ $dirname = dirname($filename);
+
+ $this->template->assign_block_vars($block, array(
+ 'STATUS' => $block,
+ 'FILENAME' => $filename,
+ 'DIR_PART' => (!empty($dirname) && $dirname !== '.') ? dirname($filename) . '/' : false,
+ 'FILE_PART' => basename($filename),
+ ));
+ }
+ }
+
+ $this->template->set_filenames(array(
+ 'file_status' => 'installer_update_file_status.html',
+ ));
+
+ $this->file_status = $this->template->assign_display('file_status');
+ }
+
+ /**
+ * Callback function for language replacing
+ *
+ * @param array $matches
+ * @return string
+ */
+ public function lang_replace_callback($matches)
+ {
+ if (!empty($matches[1]))
+ {
+ return $this->language->lang($matches[1]);
+ }
+
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php b/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php
new file mode 100644
index 0000000000..89f3594378
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php
@@ -0,0 +1,292 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\iohandler;
+
+use phpbb\install\exception\installer_exception;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\OutputStyle;
+
+/**
+ * Input-Output handler for the CLI frontend
+ */
+class cli_iohandler extends iohandler_base
+{
+ /**
+ * @var OutputInterface
+ */
+ protected $output;
+
+ /**
+ * @var OutputStyle
+ */
+ protected $io;
+
+ /**
+ * @var array
+ */
+ protected $input_values = array();
+
+ /**
+ * @var \Symfony\Component\Console\Helper\ProgressBar
+ */
+ protected $progress_bar;
+
+ /**
+ * Set the style and output used to display feedback;
+ *
+ * @param OutputStyle $style
+ * @param OutputInterface $output
+ */
+ public function set_style(OutputStyle $style, OutputInterface $output)
+ {
+ $this->io = $style;
+ $this->output = $output;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_input($name, $default, $multibyte = false)
+ {
+ $result = $default;
+
+ if (isset($this->input_values[$name]))
+ {
+ $result = $this->input_values[$name];
+ }
+
+ if ($multibyte)
+ {
+ return utf8_normalize_nfc($result);
+ }
+
+ return $result;
+ }
+
+ public function set_input($name, $value)
+ {
+ $this->input_values[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_server_variable($name, $default = '')
+ {
+ return $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_header_variable($name, $default = '')
+ {
+ return $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_secure()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_user_form_group($title, $form)
+ {
+ throw new installer_exception('MISSING_DATA');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function send_response()
+ {
+ }
+
+ /**
+ * {@inheritdoc
+ */
+ public function add_error_message($error_title, $error_description = false)
+ {
+ $this->io->newLine();
+
+ if (strpos($error_title, '<br />') !== false)
+ {
+ $error_title = strip_tags(str_replace('<br />', "\n", $error_title));
+ }
+ $message = $this->translate_message($error_title, $error_description);
+ $message_string = $message['title'] . (!empty($message['description']) ? "\n" . $message['description'] : '');
+ $this->io->error($message_string);
+
+ if ($this->progress_bar !== null)
+ {
+ $this->io->newLine(2);
+ $this->progress_bar->display();
+ }
+ }
+
+ /**
+ * {@inheritdoc
+ */
+ public function add_warning_message($warning_title, $warning_description = false)
+ {
+ $this->io->newLine();
+
+ $message = $this->translate_message($warning_title, $warning_description);
+ $message_string = $message['title'] . (!empty($message['description']) ? "\n" . $message['description'] : '');
+ $this->io->warning($message_string);
+
+ if ($this->progress_bar !== null)
+ {
+ $this->io->newLine(2);
+ $this->progress_bar->display();
+ }
+ }
+
+ /**
+ * {@inheritdoc
+ */
+ public function add_log_message($log_title, $log_description = false)
+ {
+ if ($this->output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL)
+ {
+ $message = $this->translate_message($log_title, $log_description);
+ $this->output->writeln(sprintf('[%3d/%-3d] ---- %s', $this->current_task_progress, $this->task_progress_count, $message['title']));
+ }
+ }
+
+ /**
+ * {@inheritdoc
+ */
+ public function add_success_message($error_title, $error_description = false)
+ {
+ $this->io->newLine();
+
+ $message = $this->translate_message($error_title, $error_description);
+ $message_string = $message['title'] . (!empty($message['description']) ? "\n" . $message['description'] : '');
+ $this->io->success($message_string);
+
+ if ($this->progress_bar !== null)
+ {
+ $this->io->newLine(2);
+ $this->progress_bar->display();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_task_count($task_count, $restart = false)
+ {
+ parent::set_task_count($task_count, $restart);
+
+ if ($this->output->getVerbosity() === OutputInterface::VERBOSITY_NORMAL)
+ {
+ $this->progress_bar = $this->io->createProgressBar($task_count);
+ $this->progress_bar->setFormat(
+ " %current:3s%/%max:-3s% %bar% %percent:3s%%\n" .
+ " %message%\n");
+ $this->progress_bar->setBarWidth(60);
+
+ if (!defined('PHP_WINDOWS_VERSION_BUILD'))
+ {
+ $this->progress_bar->setEmptyBarCharacter('â–‘'); // light shade character \u2591
+ $this->progress_bar->setProgressCharacter('');
+ $this->progress_bar->setBarCharacter('â–“'); // dark shade character \u2593
+ }
+
+ $this->progress_bar->setMessage('');
+ $this->io->newLine(2);
+ $this->progress_bar->start();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_progress($task_lang_key, $task_number)
+ {
+ parent::set_progress($task_lang_key, $task_number);
+
+ if ($this->progress_bar !== null)
+ {
+ $this->progress_bar->setProgress($this->current_task_progress);
+ $this->progress_bar->setMessage($this->current_task_name);
+ }
+ else
+ {
+ $this->output->writeln(sprintf('[%3d/%-3d] %s', $this->current_task_progress, $this->task_progress_count, $this->current_task_name));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function finish_progress($message_lang_key)
+ {
+ parent::finish_progress($message_lang_key);
+
+ if ($this->progress_bar !== null)
+ {
+ $this->progress_bar->finish();
+ $this->progress_bar = null;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function request_refresh()
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_active_stage_menu($menu_path)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_finished_stage_menu($menu_path)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_cookie($cookie_name, $cookie_value)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_download_link($route, $title, $msg = null)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render_update_file_status($status_array)
+ {
+ }
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/exception/iohandler_not_implemented_exception.php b/phpBB/phpbb/install/helper/iohandler/exception/iohandler_not_implemented_exception.php
new file mode 100644
index 0000000000..f2ddeda6f7
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/exception/iohandler_not_implemented_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\iohandler\exception;
+
+class iohandler_not_implemented_exception extends \Exception
+{
+
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/factory.php b/phpBB/phpbb/install/helper/iohandler/factory.php
new file mode 100644
index 0000000000..52d24e49b2
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/factory.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\install\helper\iohandler;
+
+use phpbb\install\helper\iohandler\exception\iohandler_not_implemented_exception;
+
+/**
+ * Input-output handler factory
+ */
+class factory
+{
+ /**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * @var string
+ */
+ protected $environment;
+
+ /**
+ * Constructor
+ *
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container Dependency injection container
+ */
+ public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container)
+ {
+ $this->container = $container;
+ $this->environment = null;
+ }
+
+ /**
+ * @param string $environment The name of the input-output handler to use
+ */
+ public function set_environment($environment)
+ {
+ $this->environment = $environment;
+ }
+
+ /**
+ * Factory getter for iohandler
+ *
+ * @return \phpbb\install\helper\iohandler\iohandler_interface
+ *
+ * @throws \phpbb\install\helper\iohandler\exception\iohandler_not_implemented_exception
+ * When the specified iohandler_interface does not exists
+ */
+ public function get()
+ {
+ switch ($this->environment)
+ {
+ case 'ajax':
+ return $this->container->get('installer.helper.iohandler_ajax');
+ break;
+ case 'nojs':
+ // @todo replace this
+ return $this->container->get('installer.helper.iohandler_ajax');
+ break;
+ case 'cli':
+ return $this->container->get('installer.helper.iohandler_cli');
+ break;
+ default:
+ throw new iohandler_not_implemented_exception();
+ break;
+ }
+
+ throw new iohandler_not_implemented_exception();
+ }
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/iohandler_base.php b/phpBB/phpbb/install/helper/iohandler/iohandler_base.php
new file mode 100644
index 0000000000..7271fe9bc0
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/iohandler_base.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\iohandler;
+
+/**
+ * Base class for installer input-output handlers
+ */
+abstract class iohandler_base implements iohandler_interface
+{
+ /**
+ * Array of errors
+ *
+ * Errors should be added, when the installation cannot continue without
+ * user interaction. If the aim is to notify the user about something, please
+ * use a warning instead.
+ *
+ * @var array
+ */
+ protected $errors;
+
+ /**
+ * Array of warnings
+ *
+ * @var array
+ */
+ protected $warnings;
+
+ /**
+ * Array of logs
+ *
+ * @var array
+ */
+ protected $logs;
+
+ /**
+ * Array of success messages
+ *
+ * @var array
+ */
+ protected $success;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var int
+ */
+ protected $task_progress_count;
+
+ /**
+ * @var int
+ */
+ protected $current_task_progress;
+
+ /**
+ * @var string
+ */
+ protected $current_task_name;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->errors = array();
+ $this->warnings = array();
+ $this->logs = array();
+ $this->success = array();
+
+ $this->restart_progress_bar = false;
+ $this->task_progress_count = 0;
+ $this->current_task_progress = 0;
+ $this->current_task_name = '';
+ }
+
+ /**
+ * Set language service
+ *
+ * @param \phpbb\language\language $language
+ */
+ public function set_language(\phpbb\language\language $language)
+ {
+ $this->language = $language;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_error_message($error_title, $error_description = false)
+ {
+ if (!is_array($error_title) && strpos($error_title, '<br />') !== false)
+ {
+ $error_title = strip_tags(htmlspecialchars_decode($error_title));
+ }
+ $this->errors[] = $this->translate_message($error_title, $error_description);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_warning_message($warning_title, $warning_description = false)
+ {
+ $this->warnings[] = $this->translate_message($warning_title, $warning_description);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_log_message($log_title, $log_description = false)
+ {
+ $this->logs[] = $this->translate_message($log_title, $log_description);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_success_message($success_title, $success_description = false)
+ {
+ $this->success[] = $this->translate_message($success_title, $success_description);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_task_count($task_count, $restart = false)
+ {
+ $this->task_progress_count = $task_count;
+ $this->restart_progress_bar = $restart;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_progress($task_lang_key, $task_number)
+ {
+ $this->current_task_name = '';
+
+ if (!empty($task_lang_key))
+ {
+ $this->current_task_name = $this->language->lang($task_lang_key);
+ }
+
+ $this->current_task_progress = $task_number;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function finish_progress($message_lang_key)
+ {
+ if (!empty($message_lang_key))
+ {
+ $this->current_task_name = $this->language->lang($message_lang_key);
+ }
+
+ $this->current_task_progress = $this->task_progress_count;
+ }
+
+ /**
+ * Localize message.
+ *
+ * Note: When an array is passed into the parameters below, it will be
+ * resolved as printf($param[0], $param[1], ...).
+ *
+ * @param array|string $title Title of the message
+ * @param array|string|bool $description Description of the message
+ *
+ * @return array Localized message in an array
+ */
+ protected function translate_message($title, $description)
+ {
+ $message_array = array();
+
+ $message_array['title'] = call_user_func_array(array($this->language, 'lang'), (array) $title);
+
+ if ($description !== false)
+ {
+ $message_array['description'] = call_user_func_array(array($this->language, 'lang'), (array) $description);
+ }
+
+ return $message_array;
+ }
+}
diff --git a/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php b/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php
new file mode 100644
index 0000000000..00aab3283e
--- /dev/null
+++ b/phpBB/phpbb/install/helper/iohandler/iohandler_interface.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\install\helper\iohandler;
+
+/**
+ * Input-Output handler interface for the installer
+ */
+interface iohandler_interface
+{
+ /**
+ * Renders or returns response message
+ */
+ public function send_response();
+
+ /**
+ * Returns input variable
+ *
+ * @param string $name Name of the input variable to obtain
+ * @param mixed $default A default value that is returned if the variable was not set.
+ * This function will always return a value of the same type as the default.
+ * @param bool $multibyte If $default is a string this paramater has to be true if the variable may contain any UTF-8 characters
+ * Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks
+ *
+ * @return mixed Value of the input variable
+ */
+ public function get_input($name, $default, $multibyte = false);
+
+ /**
+ * Returns server variable
+ *
+ * This function should work the same as request_interterface::server().
+ *
+ * @param string $name Name of the server variable
+ * @param mixed $default Default value to return when the requested variable does not exist
+ *
+ * @return mixed Value of the server variable
+ */
+ public function get_server_variable($name, $default = '');
+
+ /**
+ * Wrapper function for request_interterface::header()
+ *
+ * @param string $name Name of the request header variable
+ * @param mixed $default Default value to return when the requested variable does not exist
+ *
+ * @return mixed
+ */
+ public function get_header_variable($name, $default = '');
+
+ /**
+ * Returns true if the connection is encrypted
+ *
+ * @return bool
+ */
+ public function is_secure();
+
+ /**
+ * Adds an error message to the rendering queue
+ *
+ * Note: When an array is passed into the parameters below, it will be
+ * resolved as printf($param[0], $param[1], ...).
+ *
+ * @param string|array $error_title Title of the error message.
+ * @param string|bool|array $error_description Description of the error (and possibly guidelines to resolve it),
+ * or false if the error description is not available.
+ */
+ public function add_error_message($error_title, $error_description = false);
+
+ /**
+ * Adds a warning message to the rendering queue
+ *
+ * Note: When an array is passed into the parameters below, it will be
+ * resolved as printf($param[0], $param[1], ...).
+ *
+ * @param string|array $warning_title Title of the warning message
+ * @param string|bool|array $warning_description Description of the warning (and possibly guidelines to resolve it),
+ * or false if the warning description is not available
+ */
+ public function add_warning_message($warning_title, $warning_description = false);
+
+ /**
+ * Adds a log message to the rendering queue
+ *
+ * Note: When an array is passed into the parameters below, it will be
+ * resolved as printf($param[0], $param[1], ...).
+ *
+ * @param string|array $log_title Title of the log message
+ * @param string|bool|array $log_description Description of the log,
+ * or false if the log description is not available
+ */
+ public function add_log_message($log_title, $log_description = false);
+
+ /**
+ * Adds a success message to the rendering queue
+ *
+ * Note: When an array is passed into the parameters below, it will be
+ * resolved as printf($param[0], $param[1], ...).
+ *
+ * @param string|array $success_title Title of the success message
+ * @param string|bool|array $success_description Description of the success,
+ * or false if the success description is not available
+ *
+ * @return null
+ */
+ public function add_success_message($success_title, $success_description = false);
+
+ /**
+ * Adds a requested data group to the rendering queue
+ *
+ * @param string $title Language variable with the title of the form
+ * @param array $form An array describing the required data (options etc)
+ */
+ public function add_user_form_group($title, $form);
+
+ /**
+ * Sets the number of tasks belonging to the installer in the current mode.
+ *
+ * @param int $task_count Number of tasks
+ * @param bool $restart Whether or not to restart the progress bar, false by default
+ */
+ public function set_task_count($task_count, $restart = false);
+
+ /**
+ * Sets the progress information
+ *
+ * @param string $task_lang_key Language key for the name of the task
+ * @param int $task_number Position of the current task in the task queue
+ */
+ public function set_progress($task_lang_key, $task_number);
+
+ /**
+ * Sends refresh request to the client
+ */
+ public function request_refresh();
+
+ /**
+ * Marks stage as active in the navigation bar
+ *
+ * @param array $menu_path Array to the navigation elem
+ */
+ public function set_active_stage_menu($menu_path);
+
+ /**
+ * Marks stage as completed in the navigation bar
+ *
+ * @param array $menu_path Array to the navigation elem
+ */
+ public function set_finished_stage_menu($menu_path);
+
+ /**
+ * Finish the progress bar
+ *
+ * @param string $message_lang_key Language key for the message
+ */
+ public function finish_progress($message_lang_key);
+
+ /**
+ * Adds a download link
+ *
+ * @param string $route Route for the link
+ * @param string $title Language key for the title
+ * @param string|null|array $msg Language key for the message
+ */
+ public function add_download_link($route, $title, $msg = null);
+
+ /**
+ * Renders the status of update files
+ *
+ * @param array $status_array Array containing files in groups to render
+ */
+ public function render_update_file_status($status_array);
+
+ /**
+ * Sends and sets cookies
+ *
+ * @param string $cookie_name Name of the cookie to set
+ * @param string $cookie_value Value of the cookie to set
+ */
+ public function set_cookie($cookie_name, $cookie_value);
+}
diff --git a/phpBB/phpbb/install/helper/navigation/install_navigation.php b/phpBB/phpbb/install/helper/navigation/install_navigation.php
new file mode 100644
index 0000000000..f690f8de76
--- /dev/null
+++ b/phpBB/phpbb/install/helper/navigation/install_navigation.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\install\helper\navigation;
+
+use phpbb\install\helper\install_helper;
+
+class install_navigation implements navigation_interface
+{
+ /**
+ * @var install_helper
+ */
+ private $install_helper;
+
+ /**
+ * Constructor
+ *
+ * @param install_helper $install_helper
+ */
+ public function __construct(install_helper $install_helper)
+ {
+ $this->install_helper = $install_helper;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get()
+ {
+ if ($this->install_helper->is_phpbb_installed())
+ {
+ return array();
+ }
+
+ return array(
+ 'install' => array(
+ 'label' => 'INSTALL',
+ 'route' => 'phpbb_installer_install',
+ 'order' => 1,
+ array(
+ 'introduction' => array(
+ 'label' => 'INTRODUCTION_TITLE',
+ 'stage' => true,
+ 'order' => 0,
+ ),
+ 'requirements' => array(
+ 'label' => 'STAGE_REQUIREMENTS',
+ 'stage' => true,
+ 'order' => 1,
+ ),
+ 'obtain_data' => array(
+ 'label' => 'STAGE_OBTAIN_DATA',
+ 'stage' => true,
+ 'order' => 2,
+ ),
+ 'install' => array(
+ 'label' => 'STAGE_INSTALL',
+ 'stage' => true,
+ 'order' => 3,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/install/helper/navigation/main_navigation.php b/phpBB/phpbb/install/helper/navigation/main_navigation.php
new file mode 100644
index 0000000000..214bb04963
--- /dev/null
+++ b/phpBB/phpbb/install/helper/navigation/main_navigation.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\install\helper\navigation;
+
+class main_navigation implements navigation_interface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get()
+ {
+ return array(
+ 'overview' => array(
+ 'label' => 'MENU_OVERVIEW',
+ 'route' => 'phpbb_installer_index',
+ 'order' => 0,
+ array(
+ 'introduction' => array(
+ 'label' => 'MENU_INTRO',
+ 'route' => 'phpbb_installer_index',
+ 'order' => 0,
+ ),
+ 'support' => array(
+ 'label' => 'MENU_SUPPORT',
+ 'route' => 'phpbb_installer_support',
+ 'order' => 1,
+ ),
+ 'license' => array(
+ 'label' => 'MENU_LICENSE',
+ 'route' => 'phpbb_installer_license',
+ 'order' => 2,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/install/helper/navigation/navigation_interface.php b/phpBB/phpbb/install/helper/navigation/navigation_interface.php
new file mode 100644
index 0000000000..eebdbe923f
--- /dev/null
+++ b/phpBB/phpbb/install/helper/navigation/navigation_interface.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\install\helper\navigation;
+
+/**
+ * Interface for installer's navigation defining services
+ */
+interface navigation_interface
+{
+ /**
+ * Returns an array with the navigation items
+ *
+ * The returned array should have the following format:
+ * <code>
+ * array(
+ * 'parent_nav_name' => array(
+ * 'nav_name' => array(
+ * 'label' => 'MY_MENU',
+ * 'route' => 'phpbb_route_name',
+ * )
+ * )
+ * )
+ * </code>
+ *
+ * Navigation item setting options:
+ * - label: The language variable name
+ * - route: Name of the route which it is belongs to
+ *
+ * @return array
+ */
+ public function get();
+}
diff --git a/phpBB/phpbb/install/helper/navigation/navigation_provider.php b/phpBB/phpbb/install/helper/navigation/navigation_provider.php
new file mode 100644
index 0000000000..d52aec8999
--- /dev/null
+++ b/phpBB/phpbb/install/helper/navigation/navigation_provider.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper\navigation;
+
+use phpbb\di\service_collection;
+
+/**
+ * Installers navigation provider
+ */
+class navigation_provider
+{
+ /**
+ * @var array
+ */
+ private $menu_collection;
+
+ /**
+ * Constructor
+ *
+ * @param service_collection $plugins
+ */
+ public function __construct(service_collection $plugins)
+ {
+ $this->menu_collection = array();
+
+ foreach ($plugins as $plugin => $plugin_instance)
+ {
+ $this->register($plugin_instance);
+ }
+ }
+
+ /**
+ * Returns navigation array
+ *
+ * @return array
+ */
+ public function get()
+ {
+ return $this->menu_collection;
+ }
+
+ /**
+ * Registers a navigation provider's navigation items
+ *
+ * @param navigation_interface $navigation
+ */
+ public function register(navigation_interface $navigation)
+ {
+ $nav_arry = $navigation->get();
+ $this->menu_collection = $this->merge($nav_arry, $this->menu_collection);
+ }
+
+ /**
+ * Set a property in the navigation array
+ *
+ * @param array $nav_element Array to the navigation elem
+ * @param array $property_array Array with the properties to set
+ */
+ public function set_nav_property($nav_element, $property_array)
+ {
+ $array_pointer = array();
+ $array_root_pointer = &$array_pointer;
+ foreach ($nav_element as $array_path)
+ {
+ $array_pointer[$array_path] = array();
+ $array_pointer = &$array_pointer[$array_path];
+ }
+
+ $array_pointer = $property_array;
+
+ $this->menu_collection = $this->merge($array_root_pointer, $this->menu_collection);
+ }
+
+ /**
+ * Recursive array merge
+ *
+ * This function is necessary to be able to replace the options of
+ * already set navigation items.
+ *
+ * @param array $array_to_merge
+ * @param array $array_to_merge_into
+ *
+ * @return array Merged array
+ */
+ private function merge($array_to_merge, $array_to_merge_into)
+ {
+ $merged_array = $array_to_merge_into;
+
+ foreach ($array_to_merge as $key => $value)
+ {
+ if (isset($array_to_merge_into[$key]))
+ {
+ if (is_array($array_to_merge_into[$key]) && is_array($value))
+ {
+ $merged_array[$key] = $this->merge($value, $array_to_merge_into[$key]);
+ }
+ else
+ {
+ $merged_array[$key] = $value;
+ }
+ }
+ else
+ {
+ $merged_array[$key] = $value;
+ }
+ }
+
+ return $merged_array;
+ }
+}
diff --git a/phpBB/phpbb/install/helper/navigation/update_navigation.php b/phpBB/phpbb/install/helper/navigation/update_navigation.php
new file mode 100644
index 0000000000..3d239c3451
--- /dev/null
+++ b/phpBB/phpbb/install/helper/navigation/update_navigation.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\install\helper\navigation;
+
+use phpbb\install\helper\install_helper;
+
+class update_navigation implements navigation_interface
+{
+ /**
+ * @var install_helper
+ */
+ private $install_helper;
+
+ /**
+ * Constructor
+ *
+ * @param install_helper $install_helper
+ */
+ public function __construct(install_helper $install_helper)
+ {
+ $this->install_helper = $install_helper;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get()
+ {
+ if (!$this->install_helper->is_phpbb_installed())
+ {
+ return array();
+ }
+
+ return array(
+ 'update' => array(
+ 'label' => 'UPDATE',
+ 'route' => 'phpbb_installer_update',
+ 'order' => 1,
+ array(
+ 'introduction' => array(
+ 'label' => 'INTRODUCTION_TITLE',
+ 'stage' => true,
+ 'order' => 0,
+ ),
+ 'requirements' => array(
+ 'label' => 'STAGE_REQUIREMENTS',
+ 'stage' => true,
+ 'order' => 1,
+ ),
+ 'obtain_data' => array(
+ 'label' => 'STAGE_OBTAIN_DATA',
+ 'stage' => true,
+ 'order' => 2,
+ ),
+ 'update_files' => array(
+ 'label' => 'STAGE_UPDATE_FILES',
+ 'stage' => true,
+ 'order' => 3,
+ ),
+ 'update_database' => array(
+ 'label' => 'STAGE_UPDATE_DATABASE',
+ 'stage' => true,
+ 'order' => 4,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/phpbb/install/helper/update_helper.php b/phpBB/phpbb/install/helper/update_helper.php
new file mode 100644
index 0000000000..a00731d317
--- /dev/null
+++ b/phpBB/phpbb/install/helper/update_helper.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\helper;
+
+/**
+ * General helper functionality for the updater
+ */
+class update_helper
+{
+ /**
+ * @var string
+ */
+ protected $path_to_new_files;
+
+ /**
+ * @var string
+ */
+ protected $path_to_old_files;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path
+ */
+ public function __construct($phpbb_root_path)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->path_to_new_files = $phpbb_root_path . 'install/update/new/';
+ $this->path_to_old_files = $phpbb_root_path . 'install/update/old/';
+ }
+
+ /**
+ * Returns path to new update files
+ *
+ * @return string
+ */
+ public function get_path_to_new_update_files()
+ {
+ return $this->path_to_new_files;
+ }
+
+ /**
+ * Returns path to new update files
+ *
+ * @return string
+ */
+ public function get_path_to_old_update_files()
+ {
+ return $this->path_to_old_files;
+ }
+
+ /**
+ * Includes the updated file if available
+ *
+ * @param string $filename Path to the file relative to phpBB root path
+ */
+ public function include_file($filename)
+ {
+ if (is_file($this->path_to_new_files . $filename))
+ {
+ include_once($this->path_to_new_files . $filename);
+ }
+ else if (is_file($this->phpbb_root_path . $filename))
+ {
+ include_once($this->phpbb_root_path . $filename);
+ }
+ }
+
+ /**
+ * Customized version_compare()
+ *
+ * @param string $version_number1
+ * @param string $version_number2
+ * @param string|null $operator
+ * @return int|bool The returned value is identical to the PHP build-in function version_compare()
+ */
+ public function phpbb_version_compare($version_number1, $version_number2, $operator = null)
+ {
+ if ($operator === null)
+ {
+ $result = version_compare(
+ str_replace('rc', 'RC', strtolower($version_number1)),
+ str_replace('rc', 'RC', strtolower($version_number2))
+ );
+ }
+ else
+ {
+ $result = version_compare(
+ str_replace('rc', 'RC', strtolower($version_number1)),
+ str_replace('rc', 'RC', strtolower($version_number2)),
+ $operator
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/phpBB/phpbb/install/installer.php b/phpBB/phpbb/install/installer.php
new file mode 100644
index 0000000000..a41b4cd6a6
--- /dev/null
+++ b/phpBB/phpbb/install/installer.php
@@ -0,0 +1,275 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install;
+
+use phpbb\cache\driver\driver_interface;
+use phpbb\di\ordered_service_collection;
+use phpbb\install\exception\installer_config_not_writable_exception;
+use phpbb\install\exception\jump_to_restart_point_exception;
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\cli_iohandler;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\path_helper;
+
+class installer
+{
+ /**
+ * @var driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var config
+ */
+ protected $install_config;
+
+ /**
+ * @var array
+ */
+ protected $installer_modules;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var string
+ */
+ protected $web_root;
+
+ /**
+ * Stores the number of steps that a given module has
+ *
+ * @var array
+ */
+ protected $module_step_count;
+
+ /**
+ * Constructor
+ *
+ * @param driver_interface $cache Cache service
+ * @param config $config Installer config handler
+ * @param path_helper $path_helper Path helper
+ */
+ public function __construct(driver_interface $cache, config $config, path_helper $path_helper)
+ {
+ $this->cache = $cache;
+ $this->install_config = $config;
+ $this->installer_modules = null;
+ $this->web_root = $path_helper->get_web_root_path();
+ }
+
+ /**
+ * Sets modules to execute
+ *
+ * Note: The installer will run modules in the order they are set in
+ * the array.
+ *
+ * @param ordered_service_collection $modules Service collection of module service names
+ */
+ public function set_modules(ordered_service_collection $modules)
+ {
+ $this->installer_modules = $modules;
+ }
+
+ /**
+ * Sets input-output handler objects
+ *
+ * @param iohandler_interface $iohandler
+ */
+ public function set_iohandler(iohandler_interface $iohandler)
+ {
+ $this->iohandler = $iohandler;
+ }
+
+ /**
+ * Run phpBB installer
+ */
+ public function run()
+ {
+ // Load install progress
+ $this->install_config->load_config();
+
+ // Recover install progress
+ $module_name = $this->recover_progress();
+ $module_found = false;
+
+ // Variable used to check if the install process have been finished
+ $install_finished = false;
+ $fail_cleanup = false;
+ $send_refresh = false;
+
+ // We are installing something, so the introduction stage can go now...
+ $this->install_config->set_finished_navigation_stage(array('install', 0, 'introduction'));
+ $this->iohandler->set_finished_stage_menu(array('install', 0, 'introduction'));
+
+ if ($this->install_config->get_task_progress_count() === 0)
+ {
+ // Count all tasks in the current installer modules
+ $step_count = 0;
+
+ /** @var \phpbb\install\module_interface $module */
+ foreach ($this->installer_modules as $name => $module)
+ {
+ $module_step_count = $module->get_step_count();
+ $step_count += $module_step_count;
+ $this->module_step_count[$name] = $module_step_count;
+ }
+
+ // Set task count
+ $this->install_config->set_task_progress_count($step_count);
+ }
+
+ // Set up progress information
+ $this->iohandler->set_task_count(
+ $this->install_config->get_task_progress_count()
+ );
+
+ try
+ {
+ foreach ($this->installer_modules as $name => $module)
+ {
+ // Skip forward until the current task is reached
+ if (!$module_found)
+ {
+ if ($module_name === $name || empty($module_name))
+ {
+ $module_found = true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // Log progress
+ $this->install_config->set_active_module($name);
+
+ // Run until there are available resources
+ if ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0)
+ {
+ throw new resource_limit_reached_exception();
+ }
+
+ // Check if module should be executed
+ if (!$module->is_essential() && !$module->check_requirements())
+ {
+ $this->install_config->set_finished_navigation_stage($module->get_navigation_stage_path());
+ $this->iohandler->set_finished_stage_menu($module->get_navigation_stage_path());
+
+ $this->iohandler->add_log_message(array(
+ 'SKIP_MODULE',
+ $name,
+ ));
+ $this->install_config->increment_current_task_progress($this->module_step_count[$name]);
+ continue;
+ }
+
+ // Set the correct stage in the navigation bar
+ $this->install_config->set_active_navigation_stage($module->get_navigation_stage_path());
+ $this->iohandler->set_active_stage_menu($module->get_navigation_stage_path());
+
+ $module->run();
+
+ $this->install_config->set_finished_navigation_stage($module->get_navigation_stage_path());
+ $this->iohandler->set_finished_stage_menu($module->get_navigation_stage_path());
+ }
+
+ // Installation finished
+ $install_finished = true;
+
+ if ($this->iohandler instanceof cli_iohandler)
+ {
+ $this->iohandler->add_success_message('INSTALLER_FINISHED');
+ }
+ else
+ {
+ global $SID;
+ $acp_url = $this->web_root . 'adm/index.php' . $SID;
+ $this->iohandler->add_success_message('INSTALLER_FINISHED', array(
+ 'ACP_LINK',
+ $acp_url,
+ ));
+ }
+ }
+ catch (user_interaction_required_exception $e)
+ {
+ // Do nothing
+ }
+ catch (resource_limit_reached_exception $e)
+ {
+ $send_refresh = true;
+ }
+ catch (jump_to_restart_point_exception $e)
+ {
+ $this->install_config->jump_to_restart_point($e->get_restart_point_name());
+ $send_refresh = true;
+ }
+ catch (\Exception $e)
+ {
+ $this->iohandler->add_error_message($e->getMessage());
+ $this->iohandler->send_response();
+ $fail_cleanup = true;
+ }
+
+ if ($install_finished)
+ {
+ // Send install finished message
+ $this->iohandler->set_progress('INSTALLER_FINISHED', $this->install_config->get_task_progress_count());
+ }
+ else if ($send_refresh)
+ {
+ $this->iohandler->request_refresh();
+ $this->iohandler->send_response();
+ }
+
+ // Save install progress
+ try
+ {
+ if ($install_finished || $fail_cleanup)
+ {
+ $this->install_config->clean_up_config_file();
+ $this->cache->purge();
+ }
+ else
+ {
+ $this->install_config->save_config();
+ }
+ }
+ catch (installer_config_not_writable_exception $e)
+ {
+ // It is allowed to fail this test during requirements testing
+ $progress_data = $this->install_config->get_progress_data();
+
+ if ($progress_data['last_task_module_name'] !== 'installer.module.requirements_install')
+ {
+ $this->iohandler->add_error_message('INSTALLER_CONFIG_NOT_WRITABLE');
+ }
+ }
+ }
+
+ /**
+ * Recover install progress
+ *
+ * @return string Index of the next installer module to execute
+ */
+ protected function recover_progress()
+ {
+ $progress_array = $this->install_config->get_progress_data();
+ return $progress_array['last_task_module_name'];
+ }
+}
diff --git a/phpBB/phpbb/install/installer_configuration.php b/phpBB/phpbb/install/installer_configuration.php
new file mode 100644
index 0000000000..ab02da8686
--- /dev/null
+++ b/phpBB/phpbb/install/installer_configuration.php
@@ -0,0 +1,140 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\install;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+class installer_configuration implements ConfigurationInterface
+{
+
+ /**
+ * Generates the configuration tree builder.
+ *
+ * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
+ */
+ public function getConfigTreeBuilder()
+ {
+ $treeBuilder = new TreeBuilder();
+ $rootNode = $treeBuilder->root('installer');
+ $rootNode
+ ->children()
+ ->arrayNode('admin')
+ ->children()
+ ->scalarNode('name')->defaultValue('admin')->cannotBeEmpty()->end()
+ ->scalarNode('password')->defaultValue('adminadmin')->cannotBeEmpty()->end()
+ ->scalarNode('email')->defaultValue('admin@example.org')->cannotBeEmpty()->end()
+ ->end()
+ ->end()
+ ->arrayNode('board')
+ ->children()
+ ->scalarNode('lang')
+ ->defaultValue('en')
+ ->cannotBeEmpty()
+ ->end()
+ ->scalarNode('name')
+ ->defaultValue('My Board')
+ ->cannotBeEmpty()
+ ->end()
+ ->scalarNode('description')
+ ->defaultValue('My amazing new phpBB board')
+ ->cannotBeEmpty()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('database')
+ ->children()
+ ->scalarNode('dbms')
+ ->defaultValue('sqlite3')
+ ->cannotBeEmpty()
+ ->isRequired()
+ ->end()
+ ->scalarNode('dbhost')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('dbport')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('dbuser')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('dbpasswd')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('dbname')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('table_prefix')
+ ->defaultValue('phpbb_')
+ ->cannotBeEmpty()
+ ->isRequired()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('email')
+ ->canBeEnabled()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->booleanNode('smtp_delivery')
+ ->defaultValue(false)
+ ->treatNullLike(false)
+ ->end()
+ ->scalarNode('smtp_host')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('smtp_auth')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('smtp_user')
+ ->defaultValue(null)
+ ->end()
+ ->scalarNode('smtp_pass')
+ ->defaultValue(null)
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('server')
+ ->children()
+ ->booleanNode('cookie_secure')
+ ->defaultValue(false)
+ ->treatNullLike(false)
+ ->end()
+ ->scalarNode('server_protocol')
+ ->defaultValue('http://')
+ ->cannotBeEmpty()
+ ->end()
+ ->booleanNode('force_server_vars')
+ ->defaultValue(false)
+ ->treatNullLike(false)
+ ->end()
+ ->scalarNode('server_name')
+ ->defaultValue('localhost')
+ ->cannotBeEmpty()
+ ->end()
+ ->integerNode('server_port')
+ ->defaultValue(80)
+ ->min(1)
+ ->cannotBeEmpty()
+ ->end()
+ ->scalarNode('script_path')
+ ->defaultValue('/')
+ ->cannotBeEmpty()
+ ->end()
+ ->end()
+ ->end()
+ ->end()
+ ;
+ return $treeBuilder;
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_data/module.php b/phpBB/phpbb/install/module/install_data/module.php
new file mode 100644
index 0000000000..77f1f73f1f
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_data/module.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_data;
+
+/**
+ * Installer module for recovering and installing default data installation
+ */
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'install');
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_data/task/add_bots.php b/phpBB/phpbb/install/module/install_data/task/add_bots.php
new file mode 100644
index 0000000000..2ee641ff63
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_data/task/add_bots.php
@@ -0,0 +1,242 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_data\task;
+
+class add_bots extends \phpbb\install\task_base
+{
+ /**
+ * A list of the web-crawlers/bots we recognise by default
+ *
+ * Candidates but not included:
+ * 'Accoona [Bot]' 'Accoona-AI-Agent/'
+ * 'ASPseek [Crawler]' 'ASPseek/'
+ * 'Boitho [Crawler]' 'boitho.com-dc/'
+ * 'Bunnybot [Bot]' 'powered by www.buncat.de'
+ * 'Cosmix [Bot]' 'cfetch/'
+ * 'Crawler Search [Crawler]' '.Crawler-Search.de'
+ * 'Findexa [Crawler]' 'Findexa Crawler ('
+ * 'GBSpider [Spider]' 'GBSpider v'
+ * 'genie [Bot]' 'genieBot ('
+ * 'Hogsearch [Bot]' 'oegp v. 1.3.0'
+ * 'Insuranco [Bot]' 'InsurancoBot'
+ * 'IRLbot [Bot]' 'http://irl.cs.tamu.edu/crawler'
+ * 'ISC Systems [Bot]' 'ISC Systems iRc Search'
+ * 'Jyxobot [Bot]' 'Jyxobot/'
+ * 'Kraehe [Metasuche]' '-DIE-KRAEHE- META-SEARCH-ENGINE/'
+ * 'LinkWalker' 'LinkWalker'
+ * 'MMSBot [Bot]' 'http://www.mmsweb.at/bot.html'
+ * 'Naver [Bot]' 'nhnbot@naver.com)'
+ * 'NetResearchServer' 'NetResearchServer/'
+ * 'Nimble [Crawler]' 'NimbleCrawler'
+ * 'Ocelli [Bot]' 'Ocelli/'
+ * 'Onsearch [Bot]' 'onCHECK-Robot'
+ * 'Orange [Spider]' 'OrangeSpider'
+ * 'Sproose [Bot]' 'http://www.sproose.com/bot'
+ * 'Susie [Sync]' '!Susie (http://www.sync2it.com/susie)'
+ * 'Tbot [Bot]' 'Tbot/'
+ * 'Thumbshots [Capture]' 'thumbshots-de-Bot'
+ * 'Vagabondo [Crawler]' 'http://webagent.wise-guys.nl/'
+ * 'Walhello [Bot]' 'appie 1.1 (www.walhello.com)'
+ * 'WissenOnline [Bot]' 'WissenOnline-Bot'
+ * 'WWWeasel [Bot]' 'WWWeasel Robot v'
+ * 'Xaldon [Spider]' 'Xaldon WebSpider'
+ *
+ * @var array
+ */
+ protected $bot_list = array(
+ 'AdsBot [Google]' => array('AdsBot-Google', ''),
+ 'Alexa [Bot]' => array('ia_archiver', ''),
+ 'Alta Vista [Bot]' => array('Scooter/', ''),
+ 'Ask Jeeves [Bot]' => array('Ask Jeeves', ''),
+ 'Baidu [Spider]' => array('Baiduspider', ''),
+ 'Bing [Bot]' => array('bingbot/', ''),
+ 'Exabot [Bot]' => array('Exabot', ''),
+ 'FAST Enterprise [Crawler]' => array('FAST Enterprise Crawler', ''),
+ 'FAST WebCrawler [Crawler]' => array('FAST-WebCrawler/', ''),
+ 'Francis [Bot]' => array('http://www.neomo.de/', ''),
+ 'Gigabot [Bot]' => array('Gigabot/', ''),
+ 'Google Adsense [Bot]' => array('Mediapartners-Google', ''),
+ 'Google Desktop' => array('Google Desktop', ''),
+ 'Google Feedfetcher' => array('Feedfetcher-Google', ''),
+ 'Google [Bot]' => array('Googlebot', ''),
+ 'Heise IT-Markt [Crawler]' => array('heise-IT-Markt-Crawler', ''),
+ 'Heritrix [Crawler]' => array('heritrix/1.', ''),
+ 'IBM Research [Bot]' => array('ibm.com/cs/crawler', ''),
+ 'ICCrawler - ICjobs' => array('ICCrawler - ICjobs', ''),
+ 'ichiro [Crawler]' => array('ichiro/', ''),
+ 'Majestic-12 [Bot]' => array('MJ12bot/', ''),
+ 'Metager [Bot]' => array('MetagerBot/', ''),
+ 'MSN NewsBlogs' => array('msnbot-NewsBlogs/', ''),
+ 'MSN [Bot]' => array('msnbot/', ''),
+ 'MSNbot Media' => array('msnbot-media/', ''),
+ 'Nutch [Bot]' => array('http://lucene.apache.org/nutch/', ''),
+ 'Online link [Validator]' => array('online link validator', ''),
+ 'psbot [Picsearch]' => array('psbot/0', ''),
+ 'Sensis [Crawler]' => array('Sensis Web Crawler', ''),
+ 'SEO Crawler' => array('SEO search Crawler/', ''),
+ 'Seoma [Crawler]' => array('Seoma [SEO Crawler]', ''),
+ 'SEOSearch [Crawler]' => array('SEOsearch/', ''),
+ 'Snappy [Bot]' => array('Snappy/1.1 ( http://www.urltrends.com/ )', ''),
+ 'Steeler [Crawler]' => array('http://www.tkl.iis.u-tokyo.ac.jp/~crawler/', ''),
+ 'Telekom [Bot]' => array('crawleradmin.t-info@telekom.de', ''),
+ 'TurnitinBot [Bot]' => array('TurnitinBot/', ''),
+ 'Voyager [Bot]' => array('voyager/', ''),
+ 'W3 [Sitesearch]' => array('W3 SiteSearch Crawler', ''),
+ 'W3C [Linkcheck]' => array('W3C-checklink/', ''),
+ 'W3C [Validator]' => array('W3C_Validator', ''),
+ 'YaCy [Bot]' => array('yacybot', ''),
+ 'Yahoo MMCrawler [Bot]' => array('Yahoo-MMCrawler/', ''),
+ 'Yahoo Slurp [Bot]' => array('Yahoo! DE Slurp', ''),
+ 'Yahoo [Bot]' => array('Yahoo! Slurp', ''),
+ 'YahooSeeker [Bot]' => array('YahooSeeker/', ''),
+ );
+
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $install_config Installer's config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Input-output handler for the installer
+ * @param \phpbb\install\helper\container_factory $container Installer's DI container
+ * @param \phpbb\language\language $language Language provider
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $php_ext PHP extension
+ */
+ public function __construct(\phpbb\install\helper\config $install_config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\install\helper\container_factory $container,
+ \phpbb\language\language $language,
+ $phpbb_root_path,
+ $php_ext)
+ {
+ parent::__construct(true);
+
+ $this->db = $container->get('dbal.conn');
+ $this->install_config = $install_config;
+ $this->io_handler = $iohandler;
+ $this->language = $language;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'BOTS'";
+ $result = $this->db->sql_query($sql);
+ $group_id = (int) $this->db->sql_fetchfield('group_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$group_id)
+ {
+ // If we reach this point then something has gone very wrong
+ $this->io_handler->add_error_message('NO_GROUP');
+ }
+
+ foreach ($this->bot_list as $bot_name => $bot_ary)
+ {
+ $user_row = array(
+ 'user_type' => USER_IGNORE,
+ 'group_id' => $group_id,
+ 'username' => $bot_name,
+ 'user_regdate' => time(),
+ 'user_password' => '',
+ 'user_colour' => '9E8DA7',
+ 'user_email' => '',
+ 'user_lang' => $this->install_config->get('default_lang'),
+ 'user_style' => 1,
+ 'user_timezone' => 'UTC',
+ 'user_dateformat' => $this->language->lang('default_dateformat'),
+ 'user_allow_massemail' => 0,
+ 'user_allow_pm' => 0,
+ );
+
+ if (!function_exists('user_add'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $user_id = user_add($user_row);
+
+ if (!$user_id)
+ {
+ // If we can't insert this user then continue to the next one to avoid inconsistent data
+ $this->io_handler->add_error_message('CONV_ERROR_INSERT_BOT');
+
+ continue;
+ }
+
+ $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_ary[0],
+ 'bot_ip' => (string) $bot_ary[1],
+ ));
+
+ $this->db->sql_query($sql);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_ADD_BOTS';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_data/task/add_languages.php b/phpBB/phpbb/install/module/install_data/task/add_languages.php
new file mode 100644
index 0000000000..7ffdf4f276
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_data/task/add_languages.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_data\task;
+
+class add_languages extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\language\language_file_helper
+ */
+ protected $language_helper;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param \phpbb\install\helper\container_factory $container Installer's DI container
+ * @param \phpbb\language\language_file_helper $language_helper Language file helper service
+ */
+ public function __construct(\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\install\helper\container_factory $container,
+ \phpbb\language\language_file_helper $language_helper)
+ {
+ $this->db = $container->get('dbal.conn');
+ $this->iohandler = $iohandler;
+ $this->language_helper = $language_helper;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $languages = $this->language_helper->get_available_languages();
+ $installed_languages = array();
+
+ foreach ($languages as $lang_info)
+ {
+ $lang_pack = array(
+ 'lang_iso' => $lang_info['iso'],
+ 'lang_dir' => $lang_info['iso'],
+ 'lang_english_name' => htmlspecialchars($lang_info['name']),
+ 'lang_local_name' => htmlspecialchars($lang_info['local_name'], ENT_COMPAT, 'UTF-8'),
+ 'lang_author' => htmlspecialchars($lang_info['author'], ENT_COMPAT, 'UTF-8'),
+ );
+
+ $this->db->sql_query('INSERT INTO ' . LANG_TABLE . ' ' . $this->db->sql_build_array('INSERT', $lang_pack));
+
+ $installed_languages[] = (int) $this->db->sql_nextid();
+ if ($this->db->get_sql_error_triggered())
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message($error['message']);
+ }
+ }
+
+ $sql = 'SELECT * FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, PROFILE_LANG_TABLE);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ foreach ($installed_languages as $lang_id)
+ {
+ $insert_buffer->insert(array(
+ 'field_id' => $row['field_id'],
+ 'lang_id' => $lang_id,
+
+ // Remove phpbb_ from field name
+ 'lang_name' => strtoupper(substr($row['field_name'], 6)),
+ 'lang_explain' => '',
+ 'lang_default_value' => '',
+ ));
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+
+ $insert_buffer->flush();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_ADD_LANGUAGES';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_data/task/add_modules.php b/phpBB/phpbb/install/module/install_data/task/add_modules.php
new file mode 100644
index 0000000000..bfbe6282bc
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_data/task/add_modules.php
@@ -0,0 +1,462 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_data\task;
+
+class add_modules extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\extension\manager
+ */
+ protected $extension_manager;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\module\module_manager
+ */
+ protected $module_manager;
+
+ /**
+ * Define the module structure so that we can populate the database without
+ * needing to hard-code module_id values
+ *
+ * @var array
+ */
+ protected $module_categories = array(
+ 'acp' => array(
+ 'ACP_CAT_GENERAL' => array(
+ 'ACP_QUICK_ACCESS',
+ 'ACP_BOARD_CONFIGURATION',
+ 'ACP_CLIENT_COMMUNICATION',
+ 'ACP_SERVER_CONFIGURATION',
+ ),
+ 'ACP_CAT_FORUMS' => array(
+ 'ACP_MANAGE_FORUMS',
+ 'ACP_FORUM_BASED_PERMISSIONS',
+ ),
+ 'ACP_CAT_POSTING' => array(
+ 'ACP_MESSAGES',
+ 'ACP_ATTACHMENTS',
+ ),
+ 'ACP_CAT_USERGROUP' => array(
+ 'ACP_CAT_USERS',
+ 'ACP_GROUPS',
+ 'ACP_USER_SECURITY',
+ ),
+ 'ACP_CAT_PERMISSIONS' => array(
+ 'ACP_GLOBAL_PERMISSIONS',
+ 'ACP_FORUM_BASED_PERMISSIONS',
+ 'ACP_PERMISSION_ROLES',
+ 'ACP_PERMISSION_MASKS',
+ ),
+ 'ACP_CAT_CUSTOMISE' => array(
+ 'ACP_STYLE_MANAGEMENT',
+ 'ACP_EXTENSION_MANAGEMENT',
+ 'ACP_LANGUAGE',
+ ),
+ 'ACP_CAT_MAINTENANCE' => array(
+ 'ACP_FORUM_LOGS',
+ 'ACP_CAT_DATABASE',
+ ),
+ 'ACP_CAT_SYSTEM' => array(
+ 'ACP_AUTOMATION',
+ 'ACP_GENERAL_TASKS',
+ 'ACP_MODULE_MANAGEMENT',
+ ),
+ 'ACP_CAT_DOT_MODS' => null,
+ ),
+ 'mcp' => array(
+ 'MCP_MAIN' => null,
+ 'MCP_QUEUE' => null,
+ 'MCP_REPORTS' => null,
+ 'MCP_NOTES' => null,
+ 'MCP_WARN' => null,
+ 'MCP_LOGS' => null,
+ 'MCP_BAN' => null,
+ ),
+ 'ucp' => array(
+ 'UCP_MAIN' => null,
+ 'UCP_PROFILE' => null,
+ 'UCP_PREFS' => null,
+ 'UCP_PM' => null,
+ 'UCP_USERGROUPS' => null,
+ 'UCP_ZEBRA' => null,
+ ),
+ );
+
+ /**
+ * @var array
+ */
+ protected $module_categories_basenames = array(
+ 'UCP_PM' => 'ucp_pm',
+ );
+
+ /**
+ * @var array
+ */
+ protected $module_extras = array(
+ 'acp' => array(
+ 'ACP_QUICK_ACCESS' => array(
+ 'ACP_MANAGE_USERS',
+ 'ACP_GROUPS_MANAGE',
+ 'ACP_MANAGE_FORUMS',
+ 'ACP_MOD_LOGS',
+ 'ACP_BOTS',
+ 'ACP_PHP_INFO',
+ ),
+ 'ACP_FORUM_BASED_PERMISSIONS' => array(
+ 'ACP_FORUM_PERMISSIONS',
+ 'ACP_FORUM_PERMISSIONS_COPY',
+ 'ACP_FORUM_MODERATORS',
+ 'ACP_USERS_FORUM_PERMISSIONS',
+ 'ACP_GROUPS_FORUM_PERMISSIONS',
+ ),
+ ),
+ );
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param \phpbb\install\helper\container_factory $container Installer's DI container
+ */
+ public function __construct(\phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\install\helper\container_factory $container)
+ {
+ $this->db = $container->get('dbal.conn');
+ $this->extension_manager = $container->get('ext.manager');
+ $this->iohandler = $iohandler;
+ $this->module_manager = $container->get('module.manager');
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $module_classes = array('acp', 'mcp', 'ucp');
+ foreach ($module_classes as $module_class)
+ {
+ $categories = array();
+
+ foreach ($this->module_categories[$module_class] as $cat_name => $subs)
+ {
+ // Check if this sub-category has a basename. If it has, use it.
+ $basename = (isset($this->module_categories_basenames[$cat_name])) ? $this->module_categories_basenames[$cat_name] : '';
+
+ $module_data = array(
+ 'module_basename' => $basename,
+ 'module_enabled' => 1,
+ 'module_display' => 1,
+ 'parent_id' => 0,
+ 'module_class' => $module_class,
+ 'module_langname' => $cat_name,
+ 'module_mode' => '',
+ 'module_auth' => '',
+ );
+
+ $this->module_manager->update_module_data($module_data);
+
+ // Check for last sql error happened
+ if ($this->db->get_sql_error_triggered())
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+
+ $categories[$cat_name]['id'] = (int) $module_data['module_id'];
+ $categories[$cat_name]['parent_id'] = 0;
+
+ if (is_array($subs))
+ {
+ foreach ($subs as $level2_name)
+ {
+ // Check if this sub-category has a basename. If it has, use it.
+ $basename = (isset($this->module_categories_basenames[$level2_name])) ? $this->module_categories_basenames[$level2_name] : '';
+
+ $module_data = array(
+ 'module_basename' => $basename,
+ 'module_enabled' => 1,
+ 'module_display' => 1,
+ 'parent_id' => (int) $categories[$cat_name]['id'],
+ 'module_class' => $module_class,
+ 'module_langname' => $level2_name,
+ 'module_mode' => '',
+ 'module_auth' => '',
+ );
+
+ $this->module_manager->update_module_data($module_data);
+
+ // Check for last sql error happened
+ if ($this->db->get_sql_error_triggered())
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+
+ $categories[$level2_name]['id'] = (int) $module_data['module_id'];
+ $categories[$level2_name]['parent_id'] = (int) $categories[$cat_name]['id'];
+ }
+ }
+ }
+
+ // Get the modules we want to add... returned sorted by name
+ $module_info = $this->module_manager->get_module_infos($module_class);
+
+ foreach ($module_info as $module_basename => $fileinfo)
+ {
+ foreach ($fileinfo['modes'] as $module_mode => $row)
+ {
+ foreach ($row['cat'] as $cat_name)
+ {
+ if (!isset($categories[$cat_name]))
+ {
+ continue;
+ }
+
+ $module_data = array(
+ 'module_basename' => $module_basename,
+ 'module_enabled' => 1,
+ 'module_display' => (isset($row['display'])) ? (int) $row['display'] : 1,
+ 'parent_id' => (int) $categories[$cat_name]['id'],
+ 'module_class' => $module_class,
+ 'module_langname' => $row['title'],
+ 'module_mode' => $module_mode,
+ 'module_auth' => $row['auth'],
+ );
+
+ $this->module_manager->update_module_data($module_data);
+
+ // Check for last sql error happened
+ if ($this->db->get_sql_error_triggered())
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+ }
+ }
+ }
+
+ // Move some of the modules around since the code above will put them in the wrong place
+ if ($module_class === 'acp')
+ {
+ // Move main module 4 up...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_main'
+ AND module_class = 'acp'
+ AND module_mode = 'main'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'acp', 'move_up', 4);
+
+ // Move permissions intro screen module 4 up...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_permissions'
+ AND module_class = 'acp'
+ AND module_mode = 'intro'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'acp', 'move_up', 4);
+
+ // Move manage users screen module 5 up...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_users'
+ AND module_class = 'acp'
+ AND module_mode = 'overview'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'acp', 'move_up', 5);
+
+ // Move extension management module 1 up...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_langname = 'ACP_EXTENSION_MANAGEMENT'
+ AND module_class = 'acp'
+ AND module_mode = ''
+ AND module_basename = ''";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'acp', 'move_up', 1);
+ }
+
+ if ($module_class == 'mcp')
+ {
+ // Move pm report details module 3 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'mcp_pm_reports'
+ AND module_class = 'mcp'
+ AND module_mode = 'pm_report_details'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);
+
+ // Move closed pm reports module 3 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'mcp_pm_reports'
+ AND module_class = 'mcp'
+ AND module_mode = 'pm_reports_closed'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);
+
+ // Move open pm reports module 3 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'mcp_pm_reports'
+ AND module_class = 'mcp'
+ AND module_mode = 'pm_reports'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'mcp', 'move_down', 3);
+ }
+
+ if ($module_class == 'ucp')
+ {
+ // Move attachment module 4 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'ucp_attachments'
+ AND module_class = 'ucp'
+ AND module_mode = 'attachments'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'ucp', 'move_down', 4);
+
+ // Move notification options module 4 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'ucp_notifications'
+ AND module_class = 'ucp'
+ AND module_mode = 'notification_options'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'ucp', 'move_down', 4);
+
+ // Move OAuth module 5 down...
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'ucp_auth_link'
+ AND module_class = 'ucp'
+ AND module_mode = 'auth_link'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $this->module_manager->move_module_by($row, 'ucp', 'move_down', 5);
+ }
+
+ // And now for the special ones
+ // (these are modules which appear in multiple categories and thus get added manually
+ // to some for more control)
+ if (isset($this->module_extras[$module_class]))
+ {
+ foreach ($this->module_extras[$module_class] as $cat_name => $mods)
+ {
+ $sql = 'SELECT module_id, left_id, right_id
+ FROM ' . MODULES_TABLE . "
+ WHERE module_langname = '" . $this->db->sql_escape($cat_name) . "'
+ AND module_class = '" . $this->db->sql_escape($module_class) . "'";
+ $result = $this->db->sql_query_limit($sql, 1);
+ $row2 = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ foreach ($mods as $mod_name)
+ {
+ $sql = 'SELECT *
+ FROM ' . MODULES_TABLE . "
+ WHERE module_langname = '" . $this->db->sql_escape($mod_name) . "'
+ AND module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND module_basename <> ''";
+ $result = $this->db->sql_query_limit($sql, 1);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $module_data = array(
+ 'module_basename' => $row['module_basename'],
+ 'module_enabled' => (int) $row['module_enabled'],
+ 'module_display' => (int) $row['module_display'],
+ 'parent_id' => (int) $row2['module_id'],
+ 'module_class' => $row['module_class'],
+ 'module_langname' => $row['module_langname'],
+ 'module_mode' => $row['module_mode'],
+ 'module_auth' => $row['module_auth'],
+ );
+
+ $this->module_manager->update_module_data($module_data);
+
+ // Check for last sql error happened
+ if ($this->db->get_sql_error_triggered())
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+ }
+ }
+ }
+
+ $this->module_manager->remove_cache_file($module_class);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_ADD_MODULES';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_database/module.php b/phpBB/phpbb/install/module/install_database/module.php
new file mode 100644
index 0000000000..0d8b33087f
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_database/module.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_database;
+
+/**
+ * Installer module for database installation
+ */
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'install');
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_database/task/add_config_settings.php b/phpBB/phpbb/install/module/install_database/task/add_config_settings.php
new file mode 100644
index 0000000000..6fb03ff73d
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_database/task/add_config_settings.php
@@ -0,0 +1,341 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_database\task;
+
+/**
+ * Create database schema
+ */
+class add_config_settings extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var \phpbb\passwords\manager
+ */
+ protected $password_manager;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $config_table;
+
+ /**
+ * @var string
+ */
+ protected $user_table;
+
+ /**
+ * @var string
+ */
+ protected $topics_table;
+
+ /**
+ * @var string
+ */
+ protected $forums_table;
+
+ /**
+ * @var string
+ */
+ protected $posts_table;
+
+ /**
+ * @var string
+ */
+ protected $moderator_cache_table;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem service
+ * @param \phpbb\install\helper\config $install_config Installer's config helper
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param \phpbb\install\helper\container_factory $container Installer's DI container
+ * @param \phpbb\language\language $language Language service
+ * @param string $phpbb_root_path Path to phpBB's root
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem,
+ \phpbb\install\helper\config $install_config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\install\helper\container_factory $container,
+ \phpbb\language\language $language,
+ $phpbb_root_path)
+ {
+ $this->db = $container->get('dbal.conn');
+ $this->filesystem = $filesystem;
+ $this->install_config = $install_config;
+ $this->iohandler = $iohandler;
+ $this->language = $language;
+ $this->password_manager = $container->get('passwords.manager');
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ // Table names
+ $this->config_table = $container->get_parameter('tables.config');
+ $this->forums_table = $container->get_parameter('tables.forums');
+ $this->topics_table = $container->get_parameter('tables.topics');
+ $this->user_table = $container->get_parameter('tables.users');
+ $this->moderator_cache_table = $container->get_parameter('tables.moderator_cache');
+ $this->posts_table = $container->get_parameter('tables.posts');
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $server_name = $this->install_config->get('server_name');
+ $cookie_domain = $this->install_config->get('cookie_domain');
+ $current_time = time();
+ $user_ip = phpbb_ip_normalise($this->iohandler->get_server_variable('REMOTE_ADDR'));
+ $user_ip = ($user_ip === false) ? '' : $user_ip;
+ $referer = $this->iohandler->get_server_variable('REFERER');
+
+ // Set default config and post data, this applies to all DB's
+ $sql_ary = array(
+ 'INSERT INTO ' . $this->config_table . " (config_name, config_value)
+ VALUES ('board_startdate', '$current_time')",
+
+ 'INSERT INTO ' . $this->config_table . " (config_name, config_value)
+ VALUES ('default_lang', '" . $this->db->sql_escape($this->install_config->get('default_lang')) . "')",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('img_imagick')) . "'
+ WHERE config_name = 'img_imagick'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_name')) . "'
+ WHERE config_name = 'server_name'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_port')) . "'
+ WHERE config_name = 'server_port'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_email')) . "'
+ WHERE config_name = 'board_email'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_email')) . "'
+ WHERE config_name = 'board_contact'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($cookie_domain) . "'
+ WHERE config_name = 'cookie_domain'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->language->lang('default_dateformat')) . "'
+ WHERE config_name = 'default_dateformat'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('email_enable')) . "'
+ WHERE config_name = 'email_enable'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_delivery')) . "'
+ WHERE config_name = 'smtp_delivery'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_host')) . "'
+ WHERE config_name = 'smtp_host'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_auth')) . "'
+ WHERE config_name = 'smtp_auth_method'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_user')) . "'
+ WHERE config_name = 'smtp_username'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('smtp_pass')) . "'
+ WHERE config_name = 'smtp_password'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('cookie_secure')) . "'
+ WHERE config_name = 'cookie_secure'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('force_server_vars')) . "'
+ WHERE config_name = 'force_server_vars'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('script_path')) . "'
+ WHERE config_name = 'script_path'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('server_protocol')) . "'
+ WHERE config_name = 'server_protocol'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
+ WHERE config_name = 'newest_username'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . md5(mt_rand()) . "'
+ WHERE config_name = 'avatar_salt'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . md5(mt_rand()) . "'
+ WHERE config_name = 'plupload_salt'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_name')) . "'
+ WHERE config_name = 'sitename'",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->install_config->get('board_description')) . "'
+ WHERE config_name = 'site_desc'",
+
+ 'UPDATE ' . $this->user_table . "
+ SET username = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "',
+ user_password='" . $this->password_manager->hash($this->install_config->get('admin_passwd')) . "',
+ user_ip = '" . $this->db->sql_escape($user_ip) . "',
+ user_lang = '" . $this->db->sql_escape($this->install_config->get('user_language', 'en')) . "',
+ user_email='" . $this->db->sql_escape($this->install_config->get('board_email')) . "',
+ user_dateformat='" . $this->db->sql_escape($this->language->lang('default_dateformat')) . "',
+ user_email_hash = " . $this->db->sql_escape(phpbb_email_hash($this->install_config->get('board_email'))) . ",
+ username_clean = '" . $this->db->sql_escape(utf8_clean_string($this->install_config->get('admin_name'))) . "'
+ WHERE username = 'Admin'",
+
+ 'UPDATE ' . $this->moderator_cache_table . "
+ SET username = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
+ WHERE username = 'Admin'",
+
+ 'UPDATE ' . $this->forums_table . "
+ SET forum_last_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
+ WHERE forum_last_poster_name = 'Admin'",
+
+ 'UPDATE ' . $this->topics_table . "
+ SET topic_first_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "',
+ topic_last_poster_name = '" . $this->db->sql_escape($this->install_config->get('admin_name')) . "'
+ WHERE topic_first_poster_name = 'Admin'
+ OR topic_last_poster_name = 'Admin'",
+
+ 'UPDATE ' . $this->user_table . "
+ SET user_regdate = $current_time",
+
+ 'UPDATE ' . $this->posts_table . "
+ SET post_time = $current_time, poster_ip = '" . $this->db->sql_escape($user_ip) . "'",
+
+ 'UPDATE ' . $this->topics_table . "
+ SET topic_time = $current_time, topic_last_post_time = $current_time",
+
+ 'UPDATE ' . $this->forums_table . "
+ SET forum_last_post_time = $current_time",
+
+ 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($this->db->sql_server_info(true)) . "'
+ WHERE config_name = 'dbms_version'",
+ );
+
+ if (@extension_loaded('gd'))
+ {
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = 'core.captcha.plugins.gd'
+ WHERE config_name = 'captcha_plugin'";
+
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = '1'
+ WHERE config_name = 'captcha_gd'";
+ }
+
+ $ref = substr($referer, strpos($referer, '://') + 3);
+ if (!(stripos($ref, $server_name) === 0))
+ {
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = '0'
+ WHERE config_name = 'referer_validation'";
+ }
+
+ // We set a (semi-)unique cookie name to bypass login issues related to the cookie name.
+ $cookie_name = 'phpbb3_';
+ $rand_str = md5(mt_rand());
+ $rand_str = str_replace('0', 'z', base_convert($rand_str, 16, 35));
+ $rand_str = substr($rand_str, 0, 5);
+ $cookie_name .= strtolower($rand_str);
+
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = '" . $this->db->sql_escape($cookie_name) . "'
+ WHERE config_name = 'cookie_name'";
+
+ // Disable avatars if upload directory is not writable
+ if (!$this->filesystem->is_writable($this->phpbb_root_path . 'images/avatars/upload/'))
+ {
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = '0'
+ WHERE config_name = 'allow_avatar'";
+
+ $sql_ary[] = 'UPDATE ' . $this->config_table . "
+ SET config_value = '0'
+ WHERE config_name = 'allow_avatar_upload'";
+ }
+
+ foreach ($sql_ary as $sql)
+ {
+ if (!$this->db->sql_query($sql))
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_ADD_CONFIG_SETTINGS';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_database/task/add_default_data.php b/phpBB/phpbb/install/module/install_database/task/add_default_data.php
new file mode 100644
index 0000000000..3d73a74618
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_database/task/add_default_data.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_database\task;
+
+/**
+ * Create database schema
+ */
+class add_default_data extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\install\helper\database
+ */
+ protected $database_helper;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\database $db_helper Installer's database helper
+ * @param \phpbb\install\helper\config $config Installer config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param \phpbb\install\helper\container_factory $container Installer's DI container
+ * @param \phpbb\language\language $language Language service
+ * @param string $root_path Root path of phpBB
+ */
+ public function __construct(\phpbb\install\helper\database $db_helper,
+ \phpbb\install\helper\config $config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\install\helper\container_factory $container,
+ \phpbb\language\language $language,
+ $root_path)
+ {
+ $this->db = $container->get('dbal.conn.driver');
+ $this->database_helper = $db_helper;
+ $this->config = $config;
+ $this->iohandler = $iohandler;
+ $this->language = $language;
+ $this->phpbb_root_path = $root_path;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $table_prefix = $this->config->get('table_prefix');
+ $dbms = $this->config->get('dbms');
+ $dbms_info = $this->database_helper->get_available_dbms($dbms);
+
+ // Get schema data from file
+ $sql_query = @file_get_contents($this->phpbb_root_path . 'install/schemas/schema_data.sql');
+
+ // Clean up SQL
+ $sql_query = $this->replace_dbms_specific_sql($sql_query);
+ $sql_query = preg_replace('# phpbb_([^\s]*) #i', ' ' . $table_prefix . '\1 ', $sql_query);
+ $sql_query = preg_replace_callback('#\{L_([A-Z0-9\-_]*)\}#s', array($this, 'lang_replace_callback'), $sql_query);
+ $sql_query = $this->database_helper->remove_comments($sql_query);
+ $sql_query = $this->database_helper->split_sql_file($sql_query, $dbms_info[$dbms]['DELIM']);
+
+ foreach ($sql_query as $sql)
+ {
+ if (!$this->db->sql_query($sql))
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+ }
+ }
+
+ /**
+ * Process DB specific SQL
+ *
+ * @return string
+ */
+ protected function replace_dbms_specific_sql($query)
+ {
+ if ($this->db instanceof \phpbb\db\driver\mssql_base || $this->db instanceof \phpbb\db\driver\mssql)
+ {
+ $query = preg_replace('#\# MSSQL IDENTITY (phpbb_[a-z_]+) (ON|OFF) \##s', 'SET IDENTITY_INSERT \1 \2;', $query);
+ }
+ else if ($this->db instanceof \phpbb\db\driver\postgres)
+ {
+ $query = preg_replace('#\# POSTGRES (BEGIN|COMMIT) \##s', '\1; ', $query);
+ }
+ else if ($this->db instanceof \phpbb\db\driver\mysql_base)
+ {
+ $query = str_replace('\\', '\\\\', $query);
+ }
+
+ return $query;
+ }
+
+ /**
+ * Callback function for language replacing
+ *
+ * @param array $matches
+ * @return string
+ */
+ public function lang_replace_callback($matches)
+ {
+ if (!empty($matches[1]))
+ {
+ return $this->db->sql_escape($this->language->lang($matches[1]));
+ }
+
+ return '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_ADD_DEFAULT_DATA';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_database/task/create_schema.php b/phpBB/phpbb/install/module/install_database/task/create_schema.php
new file mode 100644
index 0000000000..cabb78787f
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_database/task/create_schema.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_database\task;
+
+/**
+ * Create database schema
+ */
+class create_schema extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\db\tools\tools_interface
+ */
+ protected $db_tools;
+
+ /**
+ * @var \phpbb\install\helper\database
+ */
+ protected $database_helper;
+
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $config Installer's config provider
+ * @param \phpbb\install\helper\database $db_helper Installer's database helper
+ * @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem service
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param string $phpbb_root_path Path phpBB's root
+ * @param string $php_ext Extension of PHP files
+ */
+ public function __construct(\phpbb\install\helper\config $config,
+ \phpbb\install\helper\database $db_helper,
+ \phpbb\filesystem\filesystem_interface $filesystem,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ $phpbb_root_path,
+ $php_ext)
+ {
+ $dbms = $db_helper->get_available_dbms($config->get('dbms'));
+ $dbms = $dbms[$config->get('dbms')]['DRIVER'];
+ $factory = new \phpbb\db\tools\factory();
+
+ $this->db = new $dbms();
+ $this->db->sql_connect(
+ $config->get('dbhost'),
+ $config->get('dbuser'),
+ $config->get('dbpasswd'),
+ $config->get('dbname'),
+ $config->get('dbport'),
+ false,
+ false
+ );
+
+ $this->config = $config;
+ $this->db_tools = $factory->get($this->db);
+ $this->database_helper = $db_helper;
+ $this->filesystem = $filesystem;
+ $this->iohandler = $iohandler;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->db->sql_return_on_error(true);
+
+ $dbms = $this->config->get('dbms');
+ $dbms_info = $this->database_helper->get_available_dbms($dbms);
+ $schema_name = $dbms_info[$dbms]['SCHEMA'];
+ $delimiter = $dbms_info[$dbms]['DELIM'];
+ $table_prefix = $this->config->get('table_prefix');
+
+ if ($dbms === 'mysql')
+ {
+ if (version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
+ {
+ $schema_name .= '_41';
+ }
+ else
+ {
+ $schema_name .= '_40';
+ }
+ }
+
+ $db_schema_path = $this->phpbb_root_path . 'install/schemas/' . $schema_name . '_schema.sql';
+
+ // Load database vendor specific code if there is any
+ if ($this->filesystem->exists($db_schema_path))
+ {
+ $sql_query = @file_get_contents($db_schema_path);
+ $sql_query = preg_replace('#phpbb_#i', $table_prefix, $sql_query);
+ $sql_query = $this->database_helper->remove_comments($sql_query);
+ $sql_query = $this->database_helper->split_sql_file($sql_query, $delimiter);
+
+ foreach ($sql_query as $sql)
+ {
+ if (!$this->db->sql_query($sql))
+ {
+ $error = $this->db->sql_error($this->db->get_sql_error_sql());
+ $this->iohandler->add_error_message('INST_ERR_DB', $error['message']);
+ }
+ }
+
+ unset($sql_query);
+ }
+
+ $change_prefix = false;
+
+ // Generate database schema
+ if ($this->filesystem->exists($this->phpbb_root_path . 'install/schemas/schema.json'))
+ {
+ $db_table_schema = @file_get_contents($this->phpbb_root_path . 'install/schemas/schema.json');
+ $db_table_schema = json_decode($db_table_schema, true);
+ $change_prefix = true;
+ }
+ else
+ {
+ global $table_prefix;
+
+ $table_prefix = $this->config->get('table_prefix');
+
+ if (!defined('CONFIG_TABLE'))
+ {
+ // We need to include the constants file for the table constants
+ // when we generate the schema from the migration files.
+ include ($this->phpbb_root_path . 'includes/constants.' . $this->php_ext);
+ }
+
+ $finder = new \phpbb\finder($this->filesystem, $this->phpbb_root_path, null, $this->php_ext);
+ $migrator_classes = $finder->core_path('phpbb/db/migration/data/')->get_classes();
+ $factory = new \phpbb\db\tools\factory();
+ $db_tools = $factory->get($this->db, true);
+ $schema_generator = new \phpbb\db\migration\schema_generator(
+ $migrator_classes,
+ new \phpbb\config\config(array()),
+ $this->db,
+ $db_tools,
+ $this->phpbb_root_path,
+ $this->php_ext,
+ $table_prefix
+ );
+ $db_table_schema = $schema_generator->get_schema();
+ }
+
+ if (!defined('CONFIG_TABLE'))
+ {
+ // CONFIG_TABLE is required by sql_create_index() to check the
+ // length of index names. However table_prefix is not defined
+ // here yet, so we need to create the constant ourselves.
+ define('CONFIG_TABLE', $table_prefix . 'config');
+ }
+
+ foreach ($db_table_schema as $table_name => $table_data)
+ {
+ $this->db_tools->sql_create_table(
+ ( ($change_prefix) ? ($table_prefix . substr($table_name, 6)) : $table_name ),
+ $table_data
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_CREATE_DATABASE_SCHEMA';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_filesystem/module.php b/phpBB/phpbb/install/module/install_filesystem/module.php
new file mode 100644
index 0000000000..7215449664
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_filesystem/module.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_filesystem;
+
+/**
+ * Installer module for filesystem installation
+ */
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'install');
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_filesystem/task/create_config_file.php b/phpBB/phpbb/install/module/install_filesystem/task/create_config_file.php
new file mode 100644
index 0000000000..e0890a929c
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_filesystem/task/create_config_file.php
@@ -0,0 +1,246 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_filesystem\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+/**
+ * Dumps config file
+ */
+class create_config_file extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var \phpbb\install\helper\database
+ */
+ protected $db_helper;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var array
+ */
+ protected $options;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\filesystem\filesystem_interface $filesystem
+ * @param \phpbb\install\helper\config $install_config
+ * @param \phpbb\install\helper\database $db_helper
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ * @param array $options
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem,
+ \phpbb\install\helper\config $install_config,
+ \phpbb\install\helper\database $db_helper,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ $phpbb_root_path,
+ $php_ext,
+ $options = array())
+ {
+ $this->install_config = $install_config;
+ $this->db_helper = $db_helper;
+ $this->filesystem = $filesystem;
+ $this->iohandler = $iohandler;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->options = array_merge(array(
+ 'debug' => false,
+ 'debug_container' => false,
+ 'environment' => null,
+ ), $options);
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $config_written = true;
+
+ // Create config.php
+ $path_to_config = $this->phpbb_root_path . 'config.' . $this->php_ext;
+
+ $fp = @fopen($path_to_config, 'w');
+ if (!$fp)
+ {
+ $config_written = false;
+ }
+
+ $config_content = $this->get_config_data($this->options['debug'], $this->options['debug_container'], $this->options['environment']);
+
+ if (!@fwrite($fp, $config_content))
+ {
+ $config_written = false;
+ }
+
+ @fclose($fp);
+
+ // chmod config.php to be only readable
+ if ($config_written)
+ {
+ try
+ {
+ $this->filesystem->phpbb_chmod($path_to_config, \phpbb\filesystem\filesystem_interface::CHMOD_READ);
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing, the user will get a notice later
+ }
+ }
+ else
+ {
+ $this->iohandler->add_error_message('UNABLE_TO_WRITE_CONFIG_FILE');
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+
+ // Create a lock file to indicate that there is an install in progress
+ $fp = @fopen($this->phpbb_root_path . 'cache/install_lock', 'wb');
+ if ($fp === false)
+ {
+ // We were unable to create the lock file - abort
+ $this->iohandler->add_error_message('UNABLE_TO_WRITE_LOCK');
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ @fclose($fp);
+
+ try
+ {
+ $this->filesystem->phpbb_chmod($this->phpbb_root_path . 'cache/install_lock', 0777);
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing, the user will get a notice later
+ }
+ }
+
+ /**
+ * Returns the content which should be dumped to config.php
+ *
+ * @param bool $debug If the debug constants should be enabled by default or not
+ * @param bool $debug_container If the container should be compiled on
+ * every page load or not
+ * @param string $environment The environment to use
+ *
+ * @return string content to be written to the config file
+ */
+ protected function get_config_data($debug = false, $debug_container = false, $environment = null)
+ {
+ $config_content = "<?php\n";
+ $config_content .= "// phpBB 3.2.x auto-generated configuration file\n// Do not change anything in this file!\n";
+
+ $dbms = $this->install_config->get('dbms');
+ $db_driver = $this->db_helper->get_available_dbms($dbms);
+ $db_driver = $db_driver[$dbms]['DRIVER'];
+
+ $config_data_array = array(
+ 'dbms' => $db_driver,
+ 'dbhost' => $this->install_config->get('dbhost'),
+ 'dbport' => $this->install_config->get('dbport'),
+ 'dbname' => $this->install_config->get('dbname'),
+ 'dbuser' => $this->install_config->get('dbuser'),
+ 'dbpasswd' => $this->install_config->get('dbpasswd'),
+ 'table_prefix' => $this->install_config->get('table_prefix'),
+
+ 'phpbb_adm_relative_path' => 'adm/',
+
+ 'acm_type' => 'phpbb\cache\driver\file',
+ );
+
+ foreach ($config_data_array as $key => $value)
+ {
+ $config_content .= "\${$key} = '" . str_replace("'", "\\'", str_replace('\\', '\\\\', $value)) . "';\n";
+ }
+
+ $config_content .= "\n@define('PHPBB_INSTALLED', true);\n";
+ $config_content .= "// @define('PHPBB_DISPLAY_LOAD_TIME', true);\n";
+
+ if ($environment)
+ {
+ $config_content .= "@define('PHPBB_ENVIRONMENT', 'test');\n";
+ }
+ else if ($debug)
+ {
+ $config_content .= "@define('PHPBB_ENVIRONMENT', 'development');\n";
+ }
+ else
+ {
+ $config_content .= "@define('PHPBB_ENVIRONMENT', 'production');\n";
+ }
+
+ if ($debug_container)
+ {
+ $config_content .= "@define('DEBUG_CONTAINER', true);\n";
+ }
+ else
+ {
+ $config_content .= "// @define('DEBUG_CONTAINER', true);\n";
+ }
+
+ if ($environment === 'test')
+ {
+ $config_content .= "@define('DEBUG_TEST', true);\n";
+
+ // Mandatory for the functional tests, will be removed by PHPBB3-12623
+ $config_content .= "@define('DEBUG', true);\n";
+ }
+
+ return $config_content;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_CREATE_CONFIG_FILE';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_finish/module.php b/phpBB/phpbb/install/module/install_finish/module.php
new file mode 100644
index 0000000000..3a7544b84f
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_finish/module.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_finish;
+
+/**
+ * Installer module for filesystem installation
+ */
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'install');
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_finish/task/notify_user.php b/phpBB/phpbb/install/module/install_finish/task/notify_user.php
new file mode 100644
index 0000000000..5268b85a42
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_finish/task/notify_user.php
@@ -0,0 +1,170 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\install_finish\task;
+
+use phpbb\config\db;
+
+/**
+ * Logs installation and sends an email to the admin
+ */
+class notify_user extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\auth\auth
+ */
+ protected $auth;
+
+ /**
+ * @var \phpbb\config\db
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var \phpbb\log\log_interface
+ */
+ protected $log;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\container_factory $container
+ * @param \phpbb\install\helper\config $install_config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\install\helper\container_factory $container, \phpbb\install\helper\config $install_config, \phpbb\install\helper\iohandler\iohandler_interface $iohandler, $phpbb_root_path, $php_ext)
+ {
+ $this->install_config = $install_config;
+ $this->iohandler = $iohandler;
+
+ $this->auth = $container->get('auth');
+ $this->language = $container->get('language');
+ $this->log = $container->get('log');
+ $this->user = $container->get('user');
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ // We need to reload config for cases when it doesn't have all values
+ $this->config = new db(
+ $container->get('dbal.conn'),
+ $container->get('cache.driver'),
+ $container->get_parameter('tables.config')
+ );
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->user->session_begin();
+ $this->user->setup('common');
+
+ if ($this->config['email_enable'])
+ {
+ include ($this->phpbb_root_path . 'includes/functions_messenger.' . $this->php_ext);
+
+ // functions_messenger.php uses config to determine language paths
+ // Remove when able
+ global $config;
+ $config = $this->config;
+
+ $messenger = new \messenger(false);
+ $messenger->template('installed', $this->install_config->get('user_language', 'en'));
+ $messenger->to($this->config['board_email'], $this->install_config->get('admin_name'));
+ $messenger->anti_abuse_headers($this->config, $this->user);
+ $messenger->assign_vars(array(
+ 'USERNAME' => htmlspecialchars_decode($this->install_config->get('admin_name')),
+ 'PASSWORD' => htmlspecialchars_decode($this->install_config->get('admin_passwd')))
+ );
+ $messenger->send(NOTIFY_EMAIL);
+ }
+
+ // Login admin
+ // Ugly but works
+ $this->auth->login(
+ $this->install_config->get('admin_name'),
+ $this->install_config->get('admin_passwd'),
+ false,
+ true,
+ true
+ );
+
+ $this->iohandler->set_cookie($this->config['cookie_name'] . '_sid', $this->user->session_id);
+ $this->iohandler->set_cookie($this->config['cookie_name'] . '_u', $this->user->cookie_data['u']);
+ $this->iohandler->set_cookie($this->config['cookie_name'] . '_k', $this->user->cookie_data['k']);
+
+ // Create log
+ $this->log->add(
+ 'admin',
+ $this->user->data['user_id'],
+ $this->user->ip,
+ 'LOG_INSTALL_INSTALLED',
+ false,
+ array($this->config['version'])
+ );
+
+ // Remove install_lock
+ @unlink($this->phpbb_root_path . 'cache/install_lock');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_NOTIFY_USER';
+ }
+}
diff --git a/phpBB/phpbb/install/module/install_finish/task/populate_migrations.php b/phpBB/phpbb/install/module/install_finish/task/populate_migrations.php
new file mode 100644
index 0000000000..8629d9aea3
--- /dev/null
+++ b/phpBB/phpbb/install/module/install_finish/task/populate_migrations.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\install\module\install_finish\task;
+
+/**
+ * Populates migrations
+ */
+class populate_migrations extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\extension\manager
+ */
+ protected $extension_manager;
+
+ /**
+ * @var \phpbb\db\migrator
+ */
+ protected $migrator;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\container_factory $container phpBB's DI contianer
+ */
+ public function __construct(\phpbb\install\helper\container_factory $container)
+ {
+ $this->extension_manager = $container->get('ext.manager');
+ $this->migrator = $container->get('migrator');
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $finder = $this->extension_manager->get_finder();
+
+ $migrations = $finder
+ ->core_path('phpbb/db/migration/data/')
+ ->get_classes();
+ $this->migrator->populate_migrations($migrations);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return 'TASK_POPULATE_MIGRATIONS';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/install_module.php b/phpBB/phpbb/install/module/obtain_data/install_module.php
new file mode 100644
index 0000000000..deb4be90d8
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/install_module.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\install\module\obtain_data;
+
+class install_module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'obtain_data');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_admin_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_admin_data.php
new file mode 100644
index 0000000000..ac305e8ab5
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_admin_data.php
@@ -0,0 +1,219 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+/**
+ * This class requests and validates admin account data from the user
+ */
+class obtain_admin_data extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $install_config Installer's config helper
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ */
+ public function __construct(\phpbb\install\helper\config $install_config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler)
+ {
+ $this->install_config = $install_config;
+ $this->io_handler = $iohandler;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Check if data is sent
+ if ($this->io_handler->get_input('submit_admin', false))
+ {
+ $this->process_form();
+ }
+ else
+ {
+ $this->request_form_data();
+ }
+ }
+
+ /**
+ * Process form data
+ */
+ protected function process_form()
+ {
+ // Admin data
+ $admin_name = $this->io_handler->get_input('admin_name', '', true);
+ $admin_pass1 = $this->io_handler->get_input('admin_pass1', '', true);
+ $admin_pass2 = $this->io_handler->get_input('admin_pass2', '', true);
+ $board_email = $this->io_handler->get_input('board_email', '', true);
+
+ $admin_data_valid = $this->check_admin_data($admin_name, $admin_pass1, $admin_pass2, $board_email);
+
+ if ($admin_data_valid)
+ {
+ $this->install_config->set('admin_name', $admin_name);
+ $this->install_config->set('admin_passwd', $admin_pass1);
+ $this->install_config->set('board_email', $board_email);
+ }
+ else
+ {
+ $this->request_form_data(true);
+ }
+ }
+
+ /**
+ * Request data from the user
+ *
+ * @param bool $use_request_data Whether to use submited data
+ *
+ * @throws \phpbb\install\exception\user_interaction_required_exception When the user is required to provide data
+ */
+ protected function request_form_data($use_request_data = false)
+ {
+ if ($use_request_data)
+ {
+ $admin_username = $this->io_handler->get_input('admin_name', '', true);
+ $admin_email = $this->io_handler->get_input('board_email', '', true);
+ }
+ else
+ {
+ $admin_username = '';
+ $admin_email = '';
+ }
+
+ $admin_form = array(
+ 'admin_name' => array(
+ 'label' => 'ADMIN_USERNAME',
+ 'description' => 'ADMIN_USERNAME_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $admin_username,
+ ),
+ 'board_email' => array(
+ 'label' => 'CONTACT_EMAIL',
+ 'type' => 'email',
+ 'default' => $admin_email,
+ ),
+ 'admin_pass1' => array(
+ 'label' => 'ADMIN_PASSWORD',
+ 'description' => 'ADMIN_PASSWORD_EXPLAIN',
+ 'type' => 'password',
+ ),
+ 'admin_pass2' => array(
+ 'label' => 'ADMIN_PASSWORD_CONFIRM',
+ 'type' => 'password',
+ ),
+ 'submit_admin' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ );
+
+ $this->io_handler->add_user_form_group('ADMIN_CONFIG', $admin_form);
+
+ // Require user interaction
+ $this->io_handler->send_response();
+ throw new user_interaction_required_exception();
+ }
+
+ /**
+ * Check admin data
+ *
+ * @param string $username Admin username
+ * @param string $pass1 Admin password
+ * @param string $pass2 Admin password confirmation
+ * @param string $email Admin e-mail address
+ *
+ * @return bool True if data is valid, false otherwise
+ */
+ protected function check_admin_data($username, $pass1, $pass2, $email)
+ {
+ $data_valid = true;
+
+ // Check if none of admin data is empty
+ if (in_array('', array($username, $pass1, $pass2, $email), true))
+ {
+ $this->io_handler->add_error_message('INST_ERR_MISSING_DATA');
+ $data_valid = false;
+ }
+
+ if (utf8_strlen($username) < 3)
+ {
+ $this->io_handler->add_error_message('INST_ERR_USER_TOO_SHORT');
+ $data_valid = false;
+ }
+
+ if (utf8_strlen($username) > 20)
+ {
+ $this->io_handler->add_error_message('INST_ERR_USER_TOO_LONG');
+ $data_valid = false;
+ }
+
+ if ($pass1 !== $pass2 && $pass1 !== '')
+ {
+ $this->io_handler->add_error_message('INST_ERR_PASSWORD_MISMATCH');
+ $data_valid = false;
+ }
+
+ // Test against the default password rules
+ if (utf8_strlen($pass1) < 6)
+ {
+ $this->io_handler->add_error_message('INST_ERR_PASSWORD_TOO_SHORT');
+ $data_valid = false;
+ }
+
+ if (utf8_strlen($pass1) > 30)
+ {
+ $this->io_handler->add_error_message('INST_ERR_PASSWORD_TOO_LONG');
+ $data_valid = false;
+ }
+
+ if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
+ {
+ $this->io_handler->add_error_message('INST_ERR_EMAIL_INVALID');
+ $data_valid = false;
+ }
+
+ return $data_valid;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_board_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_board_data.php
new file mode 100644
index 0000000000..6c54561d14
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_board_data.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\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+/**
+ * This class obtains default data from the user related to board (Board name, Board descritpion, etc...)
+ */
+class obtain_board_data extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * @var \phpbb\language\language_file_helper
+ */
+ protected $language_helper;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $config Installer's config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ * @param \phpbb\language\language_file_helper $lang_helper Language file helper
+ */
+ public function __construct(\phpbb\install\helper\config $config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler,
+ \phpbb\language\language_file_helper $lang_helper)
+ {
+ $this->install_config = $config;
+ $this->io_handler = $iohandler;
+ $this->language_helper = $lang_helper;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Check if data is sent
+ if ($this->io_handler->get_input('submit_board', false))
+ {
+ $this->process_form();
+ }
+ else
+ {
+ $this->request_form_data();
+ }
+ }
+
+ /**
+ * Process form data
+ */
+ protected function process_form()
+ {
+ // Board data
+ $default_lang = $this->io_handler->get_input('default_lang', '');
+ $board_name = $this->io_handler->get_input('board_name', '', true);
+ $board_desc = $this->io_handler->get_input('board_description', '', true);
+
+ // Check default lang
+ $langs = $this->language_helper->get_available_languages();
+ $lang_valid = false;
+
+ foreach ($langs as $lang)
+ {
+ if ($lang['iso'] === $default_lang)
+ {
+ $lang_valid = true;
+ break;
+ }
+ }
+
+ $this->install_config->set('board_name', $board_name);
+ $this->install_config->set('board_description', $board_desc);
+
+ if ($lang_valid)
+ {
+ $this->install_config->set('default_lang', $default_lang);
+ }
+ else
+ {
+ $this->request_form_data(true);
+ }
+ }
+
+ /**
+ * Request data from the user
+ *
+ * @param bool $use_request_data Whether to use submited data
+ *
+ * @throws \phpbb\install\exception\user_interaction_required_exception When the user is required to provide data
+ */
+ protected function request_form_data($use_request_data = false)
+ {
+ if ($use_request_data)
+ {
+ $board_name = $this->io_handler->get_input('board_name', '', true);
+ $board_desc = $this->io_handler->get_input('board_description', '', true);
+ }
+ else
+ {
+ $board_name = '{L_CONFIG_SITENAME}';
+ $board_desc = '{L_CONFIG_SITE_DESC}';
+ }
+
+ // Use language because we only check this to be valid
+ $default_lang = $this->install_config->get('user_language', 'en');
+
+ $langs = $this->language_helper->get_available_languages();
+ $lang_options = array();
+
+ foreach ($langs as $lang)
+ {
+ $lang_options[] = array(
+ 'value' => $lang['iso'],
+ 'label' => $lang['local_name'],
+ 'selected' => ($default_lang === $lang['iso']),
+ );
+ }
+
+ $board_form = array(
+ 'default_lang' => array(
+ 'label' => 'DEFAULT_LANGUAGE',
+ 'type' => 'select',
+ 'options' => $lang_options,
+ ),
+ 'board_name' => array(
+ 'label' => 'BOARD_NAME',
+ 'type' => 'text',
+ 'default' => $board_name,
+ ),
+ 'board_description' => array(
+ 'label' => 'BOARD_DESCRIPTION',
+ 'type' => 'text',
+ 'default' => $board_desc,
+ ),
+ 'submit_board' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ );
+
+ $this->io_handler->add_user_form_group('BOARD_CONFIG', $board_form);
+
+ $this->io_handler->send_response();
+ throw new user_interaction_required_exception();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_database_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_database_data.php
new file mode 100644
index 0000000000..f0e7f1f686
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_database_data.php
@@ -0,0 +1,271 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+/**
+ * This class requests and validates database information from the user
+ */
+class obtain_database_data extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\database
+ */
+ protected $database_helper;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\database $database_helper Installer's database helper
+ * @param \phpbb\install\helper\config $install_config Installer's config helper
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ */
+ public function __construct(\phpbb\install\helper\database $database_helper,
+ \phpbb\install\helper\config $install_config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler)
+ {
+ $this->database_helper = $database_helper;
+ $this->install_config = $install_config;
+ $this->io_handler = $iohandler;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Check if data is sent
+ if ($this->io_handler->get_input('submit_database', false))
+ {
+ $this->process_form();
+ }
+ else
+ {
+ $this->request_form_data();
+ }
+ }
+
+ /**
+ * Process form data
+ */
+ protected function process_form()
+ {
+ // Collect database data
+ $dbms = $this->io_handler->get_input('dbms', '');
+ $dbhost = $this->io_handler->get_input('dbhost', '');
+ $dbport = $this->io_handler->get_input('dbport', '');
+ $dbuser = $this->io_handler->get_input('dbuser', '');
+ $dbpasswd = $this->io_handler->get_input('dbpasswd', '', true);
+ $dbname = $this->io_handler->get_input('dbname', '');
+ $table_prefix = $this->io_handler->get_input('table_prefix', '');
+
+ // Check database data
+ $user_data_vaild = $this->check_database_data($dbms, $dbhost, $dbport, $dbuser, $dbpasswd, $dbname, $table_prefix);
+
+ // Save database data if it is correct
+ if ($user_data_vaild)
+ {
+ $this->install_config->set('dbms', $dbms);
+ $this->install_config->set('dbhost', $dbhost);
+ $this->install_config->set('dbport', $dbport);
+ $this->install_config->set('dbuser', $dbuser);
+ $this->install_config->set('dbpasswd', $dbpasswd);
+ $this->install_config->set('dbname', $dbname);
+ $this->install_config->set('table_prefix', $table_prefix);
+ }
+ else
+ {
+ $this->request_form_data(true);
+ }
+ }
+
+ /**
+ * Request data from the user
+ *
+ * @param bool $use_request_data Whether to use submited data
+ *
+ * @throws \phpbb\install\exception\user_interaction_required_exception When the user is required to provide data
+ */
+ protected function request_form_data($use_request_data = false)
+ {
+ if ($use_request_data)
+ {
+ $dbms = $this->io_handler->get_input('dbms', '');
+ $dbhost = $this->io_handler->get_input('dbhost', '');
+ $dbport = $this->io_handler->get_input('dbport', '');
+ $dbuser = $this->io_handler->get_input('dbuser', '');
+ $dbname = $this->io_handler->get_input('dbname', '');
+ $table_prefix = $this->io_handler->get_input('table_prefix', 'phpbb_');
+ }
+ else
+ {
+ $dbms = '';
+ $dbhost = '';
+ $dbport = '';
+ $dbuser = '';
+ $dbname = '';
+ $table_prefix = 'phpbb_';
+ }
+
+ $dbms_select = array();
+ foreach ($this->database_helper->get_available_dbms() as $dbms_key => $dbms_array)
+ {
+ $dbms_select[] = array(
+ 'value' => $dbms_key,
+ 'label' => 'DB_OPTION_' . strtoupper($dbms_key),
+ 'selected' => ($dbms_key === $dbms),
+ );
+ }
+
+ $database_form = array(
+ 'dbms' => array(
+ 'label' => 'DBMS',
+ 'type' => 'select',
+ 'options' => $dbms_select,
+ ),
+ 'dbhost' => array(
+ 'label' => 'DB_HOST',
+ 'description' => 'DB_HOST_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $dbhost,
+ ),
+ 'dbport' => array(
+ 'label' => 'DB_PORT',
+ 'description' => 'DB_PORT_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $dbport,
+ ),
+ 'dbuser' => array(
+ 'label' => 'DB_USERNAME',
+ 'type' => 'text',
+ 'default' => $dbuser,
+ ),
+ 'dbpasswd' => array(
+ 'label' => 'DB_PASSWORD',
+ 'type' => 'password',
+ ),
+ 'dbname' => array(
+ 'label' => 'DB_NAME',
+ 'type' => 'text',
+ 'default' => $dbname,
+ ),
+ 'table_prefix' => array(
+ 'label' => 'TABLE_PREFIX',
+ 'description' => 'TABLE_PREFIX_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $table_prefix,
+ ),
+ 'submit_database' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ );
+
+ $this->io_handler->add_user_form_group('DB_CONFIG', $database_form);
+
+ // Require user interaction
+ $this->io_handler->send_response();
+ throw new user_interaction_required_exception();
+ }
+
+ /**
+ * Check database data
+ *
+ * @param string $dbms Selected database type
+ * @param string $dbhost Database host address
+ * @param int $dbport Database port number
+ * @param string $dbuser Database username
+ * @param string $dbpass Database password
+ * @param string $dbname Database name
+ * @param string $table_prefix Database table prefix
+ *
+ * @return bool True if database data is correct, false otherwise
+ */
+ protected function check_database_data($dbms, $dbhost, $dbport, $dbuser, $dbpass, $dbname, $table_prefix)
+ {
+ $available_dbms = $this->database_helper->get_available_dbms();
+ $data_valid = true;
+
+ // Check if PHP has the database extensions for the specified DBMS
+ if (!isset($available_dbms[$dbms]))
+ {
+ $this->io_handler->add_error_message('INST_ERR_NO_DB');
+ $data_valid = false;
+ }
+
+ // Validate table prefix
+ $prefix_valid = $this->database_helper->validate_table_prefix($dbms, $table_prefix);
+ if (is_array($prefix_valid))
+ {
+ foreach ($prefix_valid as $error)
+ {
+ $this->io_handler->add_error_message(
+ $error['title'],
+ (isset($error['description'])) ? $error['description'] : false
+ );
+ }
+
+ $data_valid = false;
+ }
+
+ // Try to connect to database if all provided data is valid
+ if ($data_valid)
+ {
+ $connect_test = $this->database_helper->check_database_connection($dbms, $dbhost, $dbport, $dbuser, $dbpass, $dbname, $table_prefix);
+ if (is_array($connect_test))
+ {
+ foreach ($connect_test as $error)
+ {
+ $this->io_handler->add_error_message(
+ $error['title'],
+ (isset($error['description'])) ? $error['description'] : false
+ );
+ }
+
+ $data_valid = false;
+ }
+ }
+
+ return $data_valid;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php
new file mode 100644
index 0000000000..b04b8e353f
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+class obtain_email_data extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $config Installer's config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ */
+ public function __construct(\phpbb\install\helper\config $config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler)
+ {
+ $this->install_config = $config;
+ $this->io_handler = $iohandler;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // E-mail data
+ $email_enable = $this->io_handler->get_input('email_enable', true);
+ $smtp_delivery = $this->io_handler->get_input('smtp_delivery', '');
+ $smtp_host = $this->io_handler->get_input('smtp_host', '');
+ $smtp_auth = $this->io_handler->get_input('smtp_auth', '');
+ $smtp_user = $this->io_handler->get_input('smtp_user', '');
+ $smtp_passwd = $this->io_handler->get_input('smtp_pass', '');
+
+ $auth_methods = array('PLAIN', 'LOGIN', 'CRAM-MD5', 'DIGEST-MD5', 'POP-BEFORE-SMTP');
+
+ // Check if data is sent
+ if ($this->io_handler->get_input('submit_email', false))
+ {
+ $this->install_config->set('email_enable', $email_enable);
+ $this->install_config->set('smtp_delivery', $smtp_delivery);
+ $this->install_config->set('smtp_host', $smtp_host);
+ $this->install_config->set('smtp_auth', $smtp_auth);
+ $this->install_config->set('smtp_user', $smtp_user);
+ $this->install_config->set('smtp_pass', $smtp_passwd);
+ }
+ else
+ {
+ $auth_options = array();
+ foreach ($auth_methods as $method)
+ {
+ $auth_options[] = array(
+ 'value' => $method,
+ 'label' => 'SMTP_' . str_replace('-', '_', $method),
+ 'selected' => false,
+ );
+ }
+
+ $email_form = array(
+ 'email_enable' => array(
+ 'label' => 'ENABLE_EMAIL',
+ 'description' => 'COOKIE_SECURE_EXPLAIN',
+ 'type' => 'radio',
+ 'options' => array(
+ array(
+ 'value' => 1,
+ 'label' => 'ENABLE',
+ 'selected' => true,
+ ),
+ array(
+ 'value' => 0,
+ 'label' => 'DISABLE',
+ 'selected' => false,
+ ),
+ ),
+ ),
+ 'smtp_delivery' => array(
+ 'label' => 'USE_SMTP',
+ 'description' => 'USE_SMTP_EXPLAIN',
+ 'type' => 'radio',
+ 'options' => array(
+ array(
+ 'value' => 0,
+ 'label' => 'NO',
+ 'selected' => true,
+ ),
+ array(
+ 'value' => 1,
+ 'label' => 'YES',
+ 'selected' => false,
+ ),
+ ),
+ ),
+ 'smtp_host' => array(
+ 'label' => 'SMTP_SERVER',
+ 'type' => 'text',
+ 'default' => $smtp_host,
+ ),
+ 'smtp_auth' => array(
+ 'label' => 'SMTP_AUTH_METHOD',
+ 'description' => 'SMTP_AUTH_METHOD_EXPLAIN',
+ 'type' => 'select',
+ 'options' => $auth_options,
+ ),
+ 'smtp_user' => array(
+ 'label' => 'SMTP_USERNAME',
+ 'description' => 'SMTP_USERNAME_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $smtp_user,
+ ),
+ 'smtp_pass' => array(
+ 'label' => 'SMTP_PASSWORD',
+ 'description' => 'SMTP_PASSWORD_EXPLAIN',
+ 'type' => 'password',
+ ),
+ 'submit_email' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ );
+
+ $this->io_handler->add_user_form_group('EMAIL_CONFIG', $email_form);
+
+ $this->io_handler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_file_updater_method.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_file_updater_method.php
new file mode 100644
index 0000000000..9bcb73a6a9
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_file_updater_method.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\task_base;
+
+class obtain_file_updater_method extends task_base
+{
+ /**
+ * @var array Supported compression methods
+ *
+ * Note: .tar is assumed to be supported, but not in the list
+ */
+ protected $available_methods;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $installer_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * Constructor
+ *
+ * @param config $installer_config
+ * @param iohandler_interface $iohandler
+ */
+ public function __construct(config $installer_config, iohandler_interface $iohandler)
+ {
+ $this->installer_config = $installer_config;
+ $this->iohandler = $iohandler;
+
+ $this->available_methods = array('.tar.gz' => 'zlib', '.tar.bz2' => 'bz2', '.zip' => 'zlib');
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Check if data is sent
+ if ($this->iohandler->get_input('submit_update_file', false))
+ {
+ $supported_methods = array('compression', 'ftp', 'direct_file');
+ $method = $this->iohandler->get_input('method', 'compression');
+ $update_method = (in_array($method, $supported_methods, true)) ? $method : 'compression';
+ $this->installer_config->set('file_update_method', $update_method);
+
+ $compression = $this->iohandler->get_input('compression_method', '.zip');
+ $supported_methods = array_keys($this->available_methods);
+ $supported_methods[] = '.tar';
+ $compression = (in_array($compression, $supported_methods, true)) ? $compression : '.zip';
+ $this->installer_config->set('file_update_compression', $compression);
+ }
+ else
+ {
+ $this->iohandler->add_user_form_group('UPDATE_FILE_METHOD_TITLE', array(
+ 'method' => array(
+ 'label' => 'UPDATE_FILE_METHOD',
+ 'type' => 'select',
+ 'options' => array(
+ array(
+ 'value' => 'compression',
+ 'label' => 'UPDATE_FILE_METHOD_DOWNLOAD',
+ 'selected' => true,
+ ),
+ array(
+ 'value' => 'ftp',
+ 'label' => 'UPDATE_FILE_METHOD_FTP',
+ 'selected' => false,
+ ),
+ array(
+ 'value' => 'direct_file',
+ 'label' => 'UPDATE_FILE_METHOD_FILESYSTEM',
+ 'selected' => false,
+ ),
+ ),
+ ),
+ 'compression_method' => array(
+ 'label' => 'SELECT_DOWNLOAD_FORMAT',
+ 'type' => 'select',
+ 'options' => $this->get_available_compression_methods(),
+ ),
+ 'submit_update_file' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ ));
+
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * Returns form elements in an array of available compression methods
+ *
+ * @return array
+ */
+ protected function get_available_compression_methods()
+ {
+ $methods[] = array(
+ 'value' => '.tar',
+ 'label' => '.tar',
+ 'selected' => true,
+ );
+
+ foreach ($this->available_methods as $type => $module)
+ {
+ if (!@extension_loaded($module))
+ {
+ continue;
+ }
+
+ $methods[] = array(
+ 'value' => $type,
+ 'label' => $type,
+ 'selected' => false,
+ );
+ }
+
+ return $methods;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_imagick_path.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_imagick_path.php
new file mode 100644
index 0000000000..9f74b61770
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_imagick_path.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\install\module\obtain_data\task;
+
+class obtain_imagick_path extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $config;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $config Installer's config
+ */
+ public function __construct(\phpbb\install\helper\config $config)
+ {
+ $this->config = $config;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Can we find Imagemagick anywhere on the system?
+ $exe = (DIRECTORY_SEPARATOR == '\\') ? '.exe' : '';
+
+ $magic_home = getenv('MAGICK_HOME');
+ $img_imagick = '';
+ if (empty($magic_home))
+ {
+ $locations = array('C:/WINDOWS/', 'C:/WINNT/', 'C:/WINDOWS/SYSTEM/', 'C:/WINNT/SYSTEM/', 'C:/WINDOWS/SYSTEM32/', 'C:/WINNT/SYSTEM32/', '/usr/bin/', '/usr/sbin/', '/usr/local/bin/', '/usr/local/sbin/', '/opt/', '/usr/imagemagick/', '/usr/bin/imagemagick/');
+ $path_locations = str_replace('\\', '/', (explode(($exe) ? ';' : ':', getenv('PATH'))));
+
+ $locations = array_merge($path_locations, $locations);
+ foreach ($locations as $location)
+ {
+ // The path might not end properly, fudge it
+ if (substr($location, -1, 1) !== '/')
+ {
+ $location .= '/';
+ }
+
+ if (@file_exists($location) && @is_readable($location . 'mogrify' . $exe) && @filesize($location . 'mogrify' . $exe) > 3000)
+ {
+ $img_imagick = str_replace('\\', '/', $location);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ $img_imagick = str_replace('\\', '/', $magic_home);
+ }
+
+ $this->config->set('img_imagick', $img_imagick);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_server_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_server_data.php
new file mode 100644
index 0000000000..654b5534a9
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_server_data.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+
+/**
+ * This class requests and saves some information about the server
+ */
+class obtain_server_data extends \phpbb\install\task_base implements \phpbb\install\task_interface
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $install_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $io_handler;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\config $config Installer's config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler Installer's input-output handler
+ */
+ public function __construct(\phpbb\install\helper\config $config,
+ \phpbb\install\helper\iohandler\iohandler_interface $iohandler)
+ {
+ $this->install_config = $config;
+ $this->io_handler = $iohandler;
+
+ parent::__construct(true);
+ }
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $cookie_secure = $this->io_handler->is_secure();
+ $server_protocol = ($this->io_handler->is_secure()) ? 'https://' : 'http://';
+ $server_port = $this->io_handler->get_server_variable('SERVER_PORT', 0);
+
+ // HTTP_HOST is having the correct browser url in most cases...
+ $server_name = strtolower(htmlspecialchars_decode($this->io_handler->get_header_variable(
+ 'Host',
+ $this->io_handler->get_server_variable('SERVER_NAME')
+ )));
+
+ // HTTP HOST can carry a port number...
+ if (strpos($server_name, ':') !== false)
+ {
+ $server_name = substr($server_name, 0, strpos($server_name, ':'));
+ }
+
+ $script_path = htmlspecialchars_decode($this->io_handler->get_server_variable('PHP_SELF'));
+
+ if (!$script_path)
+ {
+ $script_path = htmlspecialchars_decode($this->io_handler->get_server_variable('REQUEST_URI'));
+ }
+
+ $script_path = str_replace(array('\\', '//'), '/', $script_path);
+ $script_path = trim(dirname(dirname(dirname($script_path)))); // Because we are in install/app.php/route_name
+
+ // Server data
+ $cookie_secure = $this->io_handler->get_input('cookie_secure', $cookie_secure);
+ $server_protocol = $this->io_handler->get_input('server_protocol', $server_protocol);
+ $force_server_vars = $this->io_handler->get_input('force_server_vars', 0);
+ $server_name = $this->io_handler->get_input('server_name', $server_name);
+ $server_port = $this->io_handler->get_input('server_port', $server_port);
+ $script_path = $this->io_handler->get_input('script_path', $script_path);
+
+ // Clean up script path
+ if ($script_path !== '/')
+ {
+ // Adjust destination path (no trailing slash)
+ if (substr($script_path, -1) === '/')
+ {
+ $script_path = substr($script_path, 0, -1);
+ }
+
+ $script_path = str_replace(array('../', './'), '', $script_path);
+
+ if ($script_path[0] !== '/')
+ {
+ $script_path = '/' . $script_path;
+ }
+ }
+
+ // Check if data is sent
+ if ($this->io_handler->get_input('submit_server', false))
+ {
+ $this->install_config->set('cookie_secure', $cookie_secure);
+ $this->install_config->set('server_protocol', $server_protocol);
+ $this->install_config->set('force_server_vars', $force_server_vars);
+ $this->install_config->set('server_name', $server_name);
+ $this->install_config->set('server_port', $server_port);
+ $this->install_config->set('script_path', $script_path);
+ }
+ else
+ {
+ // Render form
+ $server_form = array(
+ 'cookie_secure' => array(
+ 'label' => 'COOKIE_SECURE',
+ 'description' => 'COOKIE_SECURE_EXPLAIN',
+ 'type' => 'radio',
+ 'options' => array(
+ array(
+ 'value' => 0,
+ 'label' => 'NO',
+ 'selected' => (!$cookie_secure),
+ ),
+ array(
+ 'value' => 1,
+ 'label' => 'YES',
+ 'selected' => ($cookie_secure),
+ ),
+ ),
+ ),
+ 'force_server_vars' => array(
+ 'label' => 'FORCE_SERVER_VARS',
+ 'description' => 'FORCE_SERVER_VARS_EXPLAIN',
+ 'type' => 'radio',
+ 'options' => array(
+ array(
+ 'value' => 0,
+ 'label' => 'NO',
+ 'selected' => true,
+ ),
+ array(
+ 'value' => 1,
+ 'label' => 'YES',
+ 'selected' => false,
+ ),
+ ),
+ ),
+ 'server_protocol' => array(
+ 'label' => 'SERVER_PROTOCOL',
+ 'description' => 'SERVER_PROTOCOL_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $server_protocol,
+ ),
+ 'server_name' => array(
+ 'label' => 'SERVER_NAME',
+ 'description' => 'SERVER_NAME_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $server_name,
+ ),
+ 'server_port' => array(
+ 'label' => 'SERVER_PORT',
+ 'description' => 'SERVER_PORT_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $server_port,
+ ),
+ 'script_path' => array(
+ 'label' => 'SCRIPT_PATH',
+ 'description' => 'SCRIPT_PATH_EXPLAIN',
+ 'type' => 'text',
+ 'default' => $script_path,
+ ),
+ 'submit_server' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ )
+ );
+
+ $this->io_handler->add_user_form_group('SERVER_CONFIG', $server_form);
+
+ $this->io_handler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_update_files.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_files.php
new file mode 100644
index 0000000000..0cb809154e
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_files.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\task_base;
+
+class obtain_update_files extends task_base
+{
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(config $config, iohandler_interface $iohandler, $phpbb_root_path, $php_ext)
+ {
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Load update info file
+ // The file should be checked in the requirements, so we assume that it exists
+ $update_info_file = $this->phpbb_root_path . 'install/update/index.' . $this->php_ext;
+ include($update_info_file);
+ $info = (empty($update_info) || !is_array($update_info)) ? false : $update_info;
+
+ // If the file is invalid, abort mission
+ if (!$info)
+ {
+ $this->iohandler->add_error_message('WRONG_INFO_FILE_FORMAT');
+ throw new user_interaction_required_exception();
+ }
+
+ // Replace .php with $this->php_ext if needed
+ if ($this->php_ext !== 'php')
+ {
+ $custom_extension = '.' . $this->php_ext;
+ $info['files'] = preg_replace('#\.php$#i', $custom_extension, $info['files']);
+ }
+
+ // Save update info
+ $this->installer_config->set('update_info_unprocessed', $info);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_update_ftp_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_ftp_data.php
new file mode 100644
index 0000000000..a4d362a0f1
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_ftp_data.php
@@ -0,0 +1,164 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\helper\update_helper;
+use phpbb\install\task_base;
+
+class obtain_update_ftp_data extends task_base
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $installer_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param config $installer_config
+ * @param iohandler_interface $iohandler
+ * @param update_helper $update_helper
+ * @param string $php_ext
+ */
+ public function __construct(config $installer_config, iohandler_interface $iohandler, update_helper $update_helper, $php_ext)
+ {
+ $this->installer_config = $installer_config;
+ $this->iohandler = $iohandler;
+ $this->update_helper = $update_helper;
+ $this->php_ext = $php_ext;
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return ($this->installer_config->get('do_update_files', false) &&
+ ($this->installer_config->get('file_update_method', '') === 'ftp')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ if ($this->iohandler->get_input('submit_ftp', false))
+ {
+ $this->update_helper->include_file('includes/functions_transfer.' . $this->php_ext);
+
+ $method = 'ftp';
+ $methods = \transfer::methods();
+ if (!in_array($method, $methods, true))
+ {
+ $method = $methods[0];
+ }
+
+ $ftp_host = $this->iohandler->get_input('ftp_host', '');
+ $ftp_user = $this->iohandler->get_input('ftp_user', '');
+ $ftp_pass = htmlspecialchars_decode($this->iohandler->get_input('ftp_pass', ''));
+ $ftp_path = $this->iohandler->get_input('ftp_path', '');
+ $ftp_port = $this->iohandler->get_input('ftp_port', 21);
+ $ftp_time = $this->iohandler->get_input('ftp_timeout', 10);
+
+ $this->installer_config->set('ftp_host', $ftp_host);
+ $this->installer_config->set('ftp_user', $ftp_user);
+ $this->installer_config->set('ftp_pass', $ftp_pass);
+ $this->installer_config->set('ftp_path', $ftp_path);
+ $this->installer_config->set('ftp_port', (int) $ftp_port);
+ $this->installer_config->set('ftp_timeout', (int) $ftp_time);
+ $this->installer_config->set('ftp_method', $method);
+ }
+ else
+ {
+ $this->iohandler->add_user_form_group('FTP_SETTINGS', array(
+ 'ftp_host' => array(
+ 'label' => 'FTP_HOST',
+ 'description' => 'FTP_HOST_EXPLAIN',
+ 'type' => 'text',
+ ),
+ 'ftp_user' => array(
+ 'label' => 'FTP_USERNAME',
+ 'description' => 'FTP_USERNAME_EXPLAIN',
+ 'type' => 'text',
+ ),
+ 'ftp_pass' => array(
+ 'label' => 'FTP_PASSWORD',
+ 'description' => 'FTP_PASSWORD_EXPLAIN',
+ 'type' => 'password',
+ ),
+ 'ftp_path' => array(
+ 'label' => 'FTP_ROOT_PATH',
+ 'description' => 'FTP_ROOT_PATH_EXPLAIN',
+ 'type' => 'text',
+ ),
+ 'ftp_port' => array(
+ 'label' => 'FTP_PORT',
+ 'description' => 'FTP_PORT_EXPLAIN',
+ 'type' => 'text',
+ 'default' => 21,
+ ),
+ 'ftp_timeout' => array(
+ 'label' => 'FTP_TIMEOUT',
+ 'description' => 'FTP_TIMEOUT_EXPLAIN',
+ 'type' => 'text',
+ 'default' => 10,
+ ),
+ 'submit_ftp' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ ));
+
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_update_settings.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_settings.php
new file mode 100644
index 0000000000..6a98721e77
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_update_settings.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\obtain_data\task;
+
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\task_base;
+
+class obtain_update_settings extends task_base
+{
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $installer_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * Constructor
+ *
+ * @param config $installer_config
+ * @param iohandler_interface $iohandler
+ */
+ public function __construct(config $installer_config, iohandler_interface $iohandler)
+ {
+ $this->installer_config = $installer_config;
+ $this->iohandler = $iohandler;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Check if data is sent
+ if ($this->iohandler->get_input('submit_update', false))
+ {
+ $update_files = $this->iohandler->get_input('update_type', 'all') === 'all';
+ $this->installer_config->set('do_update_files', $update_files);
+ }
+ else
+ {
+ $this->iohandler->add_user_form_group('UPDATE_TYPE', array(
+ 'update_type' => array(
+ 'label' => 'UPDATE_TYPE',
+ 'type' => 'radio',
+ 'options' => array(
+ array(
+ 'value' => 'all',
+ 'label' => 'UPDATE_TYPE_ALL',
+ 'selected' => true,
+ ),
+ array(
+ 'value' => 'db_only',
+ 'label' => 'UPDATE_TYPE_DB_ONLY',
+ 'selected' => false,
+ ),
+ ),
+ ),
+ 'submit_update' => array(
+ 'label' => 'SUBMIT',
+ 'type' => 'submit',
+ ),
+ ));
+
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/obtain_data/update_module.php b/phpBB/phpbb/install/module/obtain_data/update_module.php
new file mode 100644
index 0000000000..c2f9019d34
--- /dev/null
+++ b/phpBB/phpbb/install/module/obtain_data/update_module.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\install\module\obtain_data;
+
+class update_module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('update', 0, 'obtain_data');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/abstract_requirements_module.php b/phpBB/phpbb/install/module/requirements/abstract_requirements_module.php
new file mode 100644
index 0000000000..26593e6777
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/abstract_requirements_module.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements;
+
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\module_base;
+
+/**
+ * Base class for requirements installer module
+ */
+abstract class abstract_requirements_module extends module_base
+{
+ public function run()
+ {
+ $tests_passed = true;
+
+ // Recover install progress
+ $task_name = $this->recover_progress();
+ $task_found = false;
+
+ /**
+ * @var string $name ID of the service
+ * @var \phpbb\install\task_interface $task Task object
+ */
+ foreach ($this->task_collection as $name => $task)
+ {
+ // Run until there are available resources
+ if ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0)
+ {
+ throw new resource_limit_reached_exception();
+ }
+
+ // Skip forward until the next task is reached
+ if (!$task_found)
+ {
+ if ($name === $task_name || empty($task_name))
+ {
+ $task_found = true;
+
+ if ($name === $task_name)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // Check if we can run the task
+ if (!$task->is_essential() && !$task->check_requirements())
+ {
+ continue;
+ }
+
+ if ($this->allow_progress_bar)
+ {
+ $this->install_config->increment_current_task_progress();
+ }
+
+ $test_result = $task->run();
+ $tests_passed = ($tests_passed) ? $test_result : false;
+ }
+
+ // Module finished, so clear task progress
+ $this->install_config->set_finished_task('');
+
+ // Check if tests have failed
+ if (!$tests_passed)
+ {
+ // If requirements are not met, exit form installer
+ // Set up UI for retesting
+ $this->iohandler->add_user_form_group('', array(
+ 'install' => array(
+ 'label' => 'RETEST_REQUIREMENTS',
+ 'type' => 'submit',
+ ),
+ ));
+
+ // Send the response and quit
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/install_module.php b/phpBB/phpbb/install/module/requirements/install_module.php
new file mode 100644
index 0000000000..ed0c5fbd94
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/install_module.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements;
+
+class install_module extends abstract_requirements_module
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('install', 0, 'requirements');
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/task/check_filesystem.php b/phpBB/phpbb/install/module/requirements/task/check_filesystem.php
new file mode 100644
index 0000000000..2aec3915e0
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/task/check_filesystem.php
@@ -0,0 +1,275 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements\task;
+
+/**
+ * Checks filesystem requirements
+ */
+class check_filesystem extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * @var array
+ */
+ protected $files_to_check;
+
+ /**
+ * @var bool
+ */
+ protected $tests_passed;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $response;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\filesystem\filesystem_interface $filesystem filesystem handler
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $response response helper
+ * @param string $phpbb_root_path relative path to phpBB's root
+ * @param string $php_ext extension of php files
+ * @param bool $check_config_php Whether or not to check if config.php is writable
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, \phpbb\install\helper\iohandler\iohandler_interface $response, $phpbb_root_path, $php_ext, $check_config_php = true)
+ {
+ parent::__construct(true);
+
+ $this->filesystem = $filesystem;
+ $this->response = $response;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ $this->tests_passed = false;
+
+ // Files/Directories to check
+ // All file/directory names must be relative to phpBB's root path
+ $this->files_to_check = array(
+ array(
+ 'path' => 'cache/',
+ 'failable' => false,
+ 'is_file' => false,
+ ),
+ array(
+ 'path' => 'store/',
+ 'failable' => false,
+ 'is_file' => false,
+ ),
+ array(
+ 'path' => 'files/',
+ 'failable' => false,
+ 'is_file' => false,
+ ),
+ array(
+ 'path' => 'images/avatars/upload/',
+ 'failable' => true,
+ 'is_file' => false,
+ ),
+ );
+
+ if ($check_config_php)
+ {
+ $this->files_to_check[] = array(
+ 'path' => "config.$php_ext",
+ 'failable' => false,
+ 'is_file' => true,
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->tests_passed = true;
+
+ // Check files/directories to be writable
+ foreach ($this->files_to_check as $file)
+ {
+ if ($file['is_file'])
+ {
+ $this->check_file($file['path'], $file['failable']);
+ }
+ else
+ {
+ $this->check_dir($file['path'], $file['failable']);
+ }
+ }
+
+ return $this->tests_passed;
+ }
+
+ /**
+ * Sets $this->tests_passed
+ *
+ * @param bool $is_passed
+ */
+ protected function set_test_passed($is_passed)
+ {
+ // If one test failed, tests_passed should be false
+ $this->tests_passed = (!$this->tests_passed) ? false : $is_passed;
+ }
+
+ /**
+ * Check if a file is readable and writable
+ *
+ * @param string $file Filename
+ * @param bool $failable Whether failing test should interrupt installation process
+ */
+ protected function check_file($file, $failable = false)
+ {
+ $path = $this->phpbb_root_path . $file;
+ $exists = $writable = true;
+
+ // Try to create file if it does not exists
+ if (!file_exists($path))
+ {
+ $fp = @fopen($path, 'w');
+ @fclose($fp);
+ try
+ {
+ $this->filesystem->phpbb_chmod($path,
+ \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE
+ );
+ $exists = true;
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ }
+
+ if (file_exists($path))
+ {
+ if (!$this->filesystem->is_writable($path))
+ {
+ $writable = false;
+ }
+ }
+ else
+ {
+ $exists = $writable = false;
+ }
+
+ $this->set_test_passed(($exists && $writable) || $failable);
+
+ if (!($exists && $writable))
+ {
+ $title = ($exists) ? 'FILE_NOT_WRITABLE' : 'FILE_NOT_EXISTS';
+ $description = array($title . '_EXPLAIN', $file);
+
+ if ($failable)
+ {
+ $this->response->add_warning_message($title, $description);
+ }
+ else
+ {
+ $this->response->add_error_message($title, $description);
+ }
+ }
+ }
+
+ /**
+ * Check if a directory is readable and writable
+ *
+ * @param string $dir Filename
+ * @param bool $failable Whether failing test should abort the installation process
+ */
+ protected function check_dir($dir, $failable = false)
+ {
+ $path = $this->phpbb_root_path . $dir;
+ $exists = $writable = false;
+
+ // Try to create the directory if it does not exist
+ if (!file_exists($path))
+ {
+ try
+ {
+ $this->filesystem->mkdir($path, 0777);
+ $this->filesystem->phpbb_chmod($path,
+ \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE
+ );
+ $exists = true;
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ }
+
+ // Now really check
+ if (file_exists($path) && is_dir($path))
+ {
+ try
+ {
+ $exists = true;
+ $this->filesystem->phpbb_chmod($path,
+ \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE
+ );
+ }
+ catch (\phpbb\filesystem\exception\filesystem_exception $e)
+ {
+ // Do nothing
+ }
+ }
+
+ if ($this->filesystem->is_writable($path))
+ {
+ $writable = true;
+ }
+
+ $this->set_test_passed(($exists && $writable) || $failable);
+
+ if (!($exists && $writable))
+ {
+ $title = ($exists) ? 'DIRECTORY_NOT_WRITABLE' : 'DIRECTORY_NOT_EXISTS';
+ $description = array($title . '_EXPLAIN', $dir);
+
+ if ($failable)
+ {
+ $this->response->add_warning_message($title, $description);
+ }
+ else
+ {
+ $this->response->add_error_message($title, $description);
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/task/check_server_environment.php b/phpBB/phpbb/install/module/requirements/task/check_server_environment.php
new file mode 100644
index 0000000000..62485a2097
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/task/check_server_environment.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements\task;
+
+/**
+ * Installer task that checks if the server meats phpBB requirements
+ */
+class check_server_environment extends \phpbb\install\task_base
+{
+ /**
+ * @var \phpbb\install\helper\database
+ */
+ protected $database_helper;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $response_helper;
+
+ /**
+ * @var bool
+ */
+ protected $tests_passed;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\database $database_helper
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $response
+ */
+ public function __construct(\phpbb\install\helper\database $database_helper,
+ \phpbb\install\helper\iohandler\iohandler_interface $response)
+ {
+ $this->database_helper = $database_helper;
+ $this->response_helper = $response;
+ $this->tests_passed = true;
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ //
+ // Check requirements
+ // The error messages should be set in the check_ functions
+ //
+
+ // Check PHP version
+ $this->check_php_version();
+
+ // Check for getimagesize()
+ $this->check_image_size();
+
+ // Check for PCRE support
+ $this->check_pcre();
+
+ // Check for JSON support
+ $this->check_json();
+
+ // Check for dbms support
+ $this->check_available_dbms();
+
+ return $this->tests_passed;
+ }
+
+ /**
+ * Sets $this->tests_passed
+ *
+ * @param bool $is_passed
+ */
+ protected function set_test_passed($is_passed)
+ {
+ // If one test failed, tests_passed should be false
+ $this->tests_passed = (!$this->tests_passed) ? false : $is_passed;
+ }
+
+ /**
+ * Check if the requirements for PHP version is met
+ */
+ protected function check_php_version()
+ {
+ $php_version = PHP_VERSION;
+
+ if (version_compare($php_version, '5.4') < 0)
+ {
+ $this->response_helper->add_error_message('PHP_VERSION_REQD', 'PHP_VERSION_REQD_EXPLAIN');
+
+ $this->set_test_passed(false);
+ return;
+ }
+
+ $this->set_test_passed(true);
+ }
+
+ /**
+ * Checks if the installed PHP has getimagesize() available
+ */
+ protected function check_image_size()
+ {
+ if (!@function_exists('getimagesize'))
+ {
+ $this->response_helper->add_error_message('PHP_GETIMAGESIZE_SUPPORT', 'PHP_GETIMAGESIZE_SUPPORT_EXPLAIN');
+
+ $this->set_test_passed(false);
+ return;
+ }
+
+ $this->set_test_passed(true);
+ }
+
+ /**
+ * Checks if the installed PHP supports PCRE
+ */
+ protected function check_pcre()
+ {
+ if (@preg_match('//u', ''))
+ {
+ $this->set_test_passed(true);
+ return;
+ }
+
+ $this->response_helper->add_error_message('PCRE_UTF_SUPPORT', 'PCRE_UTF_SUPPORT_EXPLAIN');
+
+ $this->set_test_passed(false);
+ }
+
+ /**
+ * Checks whether PHP's JSON extension is available or not
+ */
+ protected function check_json()
+ {
+ if (@extension_loaded('json'))
+ {
+ $this->set_test_passed(true);
+ return;
+ }
+
+ $this->response_helper->add_error_message('PHP_JSON_SUPPORT', 'PHP_JSON_SUPPORT_EXPLAIN');
+
+ $this->set_test_passed(false);
+ }
+
+ /**
+ * Check if any supported DBMS is available
+ */
+ protected function check_available_dbms()
+ {
+ $available_dbms = $this->database_helper->get_available_dbms(false, true);
+
+ if ($available_dbms['ANY_DB_SUPPORT'])
+ {
+ $this->set_test_passed(true);
+ return;
+ }
+
+ $this->response_helper->add_error_message('PHP_SUPPORTED_DB', 'PHP_SUPPORTED_DB_EXPLAIN');
+
+ $this->set_test_passed(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/task/check_update.php b/phpBB/phpbb/install/module/requirements/task/check_update.php
new file mode 100644
index 0000000000..c986c76810
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/task/check_update.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements\task;
+
+use phpbb\filesystem\filesystem;
+use phpbb\install\helper\container_factory;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\helper\update_helper;
+use phpbb\install\task_base;
+
+/**
+ * Check the availability of updater files and update version
+ */
+class check_update extends task_base
+{
+ /**
+ * @var \phpbb\config\db
+ */
+ protected $config;
+
+ /**
+ * @var filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var \phpbb\version_helper
+ */
+ protected $version_helper;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var bool
+ */
+ protected $tests_passed;
+
+ /**
+ * Constructor
+ *
+ * @param container_factory $container
+ * @param filesystem $filesystem
+ * @param iohandler_interface $iohandler
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(container_factory $container, filesystem $filesystem, iohandler_interface $iohandler, update_helper $update_helper, $phpbb_root_path, $php_ext)
+ {
+ $this->filesystem = $filesystem;
+ $this->iohandler = $iohandler;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->tests_passed = true;
+
+ $this->config = $container->get('config');
+ $this->version_helper = $container->get('version_helper');
+
+ parent::__construct(true);
+ }
+
+ /**
+ * Sets $this->tests_passed
+ *
+ * @param bool $is_passed
+ */
+ protected function set_test_passed($is_passed)
+ {
+ // If one test failed, tests_passed should be false
+ $this->tests_passed = $this->tests_passed && $is_passed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Array of update files
+ $update_files = array(
+ $this->phpbb_root_path . 'install/update',
+ $this->phpbb_root_path . 'install/update/index.' . $this->php_ext,
+ );
+
+ // Check for a valid update directory
+ if (!$this->filesystem->exists($update_files) || !$this->filesystem->is_readable($update_files))
+ {
+ $this->iohandler->add_error_message('UPDATE_FILES_NOT_FOUND');
+ $this->set_test_passed(false);
+
+ // If there are no update files, we can't check the version
+ return false;
+ }
+
+ // Recover version numbers
+ $update_info = array();
+ @include($this->phpbb_root_path . 'install/update/index.' . $this->php_ext);
+ $info = (empty($update_info) || !is_array($update_info)) ? false : $update_info;
+ $update_version = false;
+
+ if ($info !== false)
+ {
+ $update_version = (!empty($info['version']['to'])) ? trim($info['version']['to']) : false;
+ }
+
+ // Get current and latest version
+ try
+ {
+ $latest_version = $this->version_helper->get_latest_on_current_branch(true);
+ }
+ catch (\RuntimeException $e)
+ {
+ $latest_version = $update_version;
+ }
+
+ $current_version = (!empty($this->config['version_update_from'])) ? $this->config['version_update_from'] : $this->config['version'];
+
+ // Check if the update package
+ if (!$this->update_helper->phpbb_version_compare($current_version, $update_version, '<'))
+ {
+ $this->iohandler->add_error_message('NO_UPDATE_FILES_UP_TO_DATE');
+ $this->tests_passed = false;
+ }
+
+ // Check if the update package works with the installed version
+ if (empty($info['version']['from']) || $info['version']['from'] !== $current_version)
+ {
+ $this->iohandler->add_error_message(array('INCOMPATIBLE_UPDATE_FILES', $current_version, $info['version']['from'], $update_version));
+ $this->tests_passed = false;
+ }
+
+ // check if this is the latest update package
+ if ($this->update_helper->phpbb_version_compare($update_version, $latest_version, '<'))
+ {
+ $this->iohandler->add_warning_message(array('OLD_UPDATE_FILES', $info['version']['from'], $update_version, $latest_version));
+ }
+
+ return $this->tests_passed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/requirements/update_module.php b/phpBB/phpbb/install/module/requirements/update_module.php
new file mode 100644
index 0000000000..223d12faf3
--- /dev/null
+++ b/phpBB/phpbb/install/module/requirements/update_module.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\requirements;
+
+class update_module extends abstract_requirements_module
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('update', 0, 'requirements');
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_database/module.php b/phpBB/phpbb/install/module/update_database/module.php
new file mode 100644
index 0000000000..ee38afe17d
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_database/module.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\install\module\update_database;
+
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('update', 0, 'update_database');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_database/task/update.php b/phpBB/phpbb/install/module/update_database/task/update.php
new file mode 100644
index 0000000000..84ec6f73f5
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_database/task/update.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\update_database\task;
+
+use phpbb\db\migration\exception;
+use phpbb\db\output_handler\installer_migrator_output_handler;
+use phpbb\db\output_handler\log_wrapper_migrator_output_handler;
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\task_base;
+
+/**
+ * Database updater task
+ */
+class update extends task_base
+{
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\extension\manager
+ */
+ protected $extension_manager;
+
+ /**
+ * @var \phpbb\filesystem\filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var \phpbb\install\helper\config
+ */
+ protected $installer_config;
+
+ /**
+ * @var \phpbb\install\helper\iohandler\iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
+ /**
+ * @var \phpbb\log\log
+ */
+ protected $log;
+
+ /**
+ * @var \phpbb\db\migrator
+ */
+ protected $migrator;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\install\helper\container_factory $container
+ * @param \phpbb\filesystem\filesystem $filesystem
+ * @param \phpbb\install\helper\config $installer_config
+ * @param \phpbb\install\helper\iohandler\iohandler_interface $iohandler
+ * @param \phpbb\language\language $language
+ * @param string $phpbb_root_path
+ */
+ public function __construct(\phpbb\install\helper\container_factory $container, \phpbb\filesystem\filesystem $filesystem, \phpbb\install\helper\config $installer_config, \phpbb\install\helper\iohandler\iohandler_interface $iohandler, \phpbb\language\language $language, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->installer_config = $installer_config;
+ $this->iohandler = $iohandler;
+ $this->language = $language;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ $this->cache = $container->get('cache.driver');
+ $this->config = $container->get('config');
+ $this->extension_manager = $container->get('ext.manager');
+ $this->log = $container->get('log');
+ $this->migrator = $container->get('migrator');
+ $this->user = $container->get('user');
+
+ parent::__construct(true);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->language->add_lang('migrator');
+
+ if (!isset($this->config['version_update_from']))
+ {
+ $this->config->set('version_update_from', $this->config['version']);
+ }
+
+ $original_version = $this->config['version_update_from'];
+
+ $this->migrator->set_output_handler(
+ new log_wrapper_migrator_output_handler(
+ $this->language,
+ new installer_migrator_output_handler($this->iohandler),
+ $this->phpbb_root_path . 'store/migrations_' . time() . '.log',
+ $this->filesystem
+ )
+ );
+
+ $this->migrator->create_migrations_table();
+
+ $migrations = $this->extension_manager
+ ->get_finder()
+ ->core_path('phpbb/db/migration/data/')
+ ->extension_directory('/migrations')
+ ->get_classes();
+
+ $this->migrator->set_migrations($migrations);
+ $migration_count = count($migrations);
+ $this->iohandler->set_task_count($migration_count, true);
+ $progress_count = $this->installer_config->get('database_update_count', 0);
+
+ while (!$this->migrator->finished())
+ {
+ try
+ {
+ $this->migrator->update();
+ $progress_count++;
+ $this->iohandler->set_progress('STAGE_UPDATE_DATABASE', $progress_count);
+ }
+ catch (exception $e)
+ {
+ $msg = $e->getParameters();
+ array_unshift($msg, $e->getMessage());
+
+ $this->iohandler->add_error_message($msg);
+ throw new user_interaction_required_exception();
+ }
+
+ if ($this->installer_config->get_time_remaining() <= 0 || $this->installer_config->get_memory_remaining() <= 0)
+ {
+ $this->installer_config->set('database_update_count', $progress_count);
+ throw new resource_limit_reached_exception();
+ }
+ }
+
+ if ($original_version !== $this->config['version'])
+ {
+ $this->log->add(
+ 'admin',
+ (isset($this->user->data['user_id'])) ? $this->user->data['user_id'] : ANONYMOUS,
+ $this->user->ip,
+ 'LOG_UPDATE_DATABASE',
+ false,
+ array(
+ $original_version,
+ $this->config['version']
+ )
+ );
+ }
+
+ $this->iohandler->finish_progress('INLINE_UPDATE_SUCCESSFUL');
+
+ $this->iohandler->add_success_message('INLINE_UPDATE_SUCCESSFUL');
+
+ $this->config->delete('version_update_from');
+
+ $this->cache->purge();
+
+ $this->config->increment('assets_version', 1);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/module.php b/phpBB/phpbb/install/module/update_filesystem/module.php
new file mode 100644
index 0000000000..157c78a1ac
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/module.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\install\module\update_filesystem;
+
+class module extends \phpbb\install\module_base
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_navigation_stage_path()
+ {
+ return array('update', 0, 'update_files');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ return 0;
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php
new file mode 100644
index 0000000000..e3e6db6263
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php
@@ -0,0 +1,205 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\update_filesystem\task;
+
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\container_factory;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\helper\update_helper;
+use phpbb\install\task_base;
+
+/**
+ * Merges user made changes into the files
+ */
+class diff_files extends task_base
+{
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * Constructor
+ *
+ * @param container_factory $container
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(container_factory $container, config $config, iohandler_interface $iohandler, update_helper $update_helper, $phpbb_root_path, $php_ext)
+ {
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ $this->cache = $container->get('cache.driver');
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ $files_to_diff = $this->installer_config->get('update_files', array());
+ $files_to_diff = (isset($files_to_diff['update_with_diff'])) ? $files_to_diff['update_with_diff'] : array();
+
+ return $this->installer_config->get('do_update_files', false) && count($files_to_diff) > 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Include diff engine
+ $this->update_helper->include_file('includes/diff/diff.' . $this->php_ext);
+ $this->update_helper->include_file('includes/diff/engine.' . $this->php_ext);
+
+ // Set up basic vars
+ $old_path = $this->update_helper->get_path_to_old_update_files();
+ $new_path = $this->update_helper->get_path_to_new_update_files();
+
+ $files_to_diff = $this->installer_config->get('update_files', array());
+ $files_to_diff = $files_to_diff['update_with_diff'];
+
+ // Set progress bar
+ $this->iohandler->set_task_count(count($files_to_diff), true);
+ $this->iohandler->set_progress('UPDATE_FILE_DIFF', 0);
+ $progress_count = $this->installer_config->get('file_diff_update_count', 0);
+
+ // Recover progress
+ $progress_key = $this->installer_config->get('differ_progress_key', -1);
+ $progress_recovered = ($progress_key === -1);
+ $merge_conflicts = $this->installer_config->get('merge_conflict_list', array());
+
+ foreach ($files_to_diff as $key => $filename)
+ {
+ if ($progress_recovered === false)
+ {
+ if ($progress_key === $key)
+ {
+ $progress_recovered = true;
+ }
+
+ continue;
+ }
+
+ // Read in files' content
+ $file_contents = array();
+
+ // Handle the special case when user created a file with the filename that is now new in the core
+ $file_contents[0] = (file_exists($old_path . $filename)) ? file_get_contents($old_path . $filename) : '';
+
+ $filenames = array(
+ $this->phpbb_root_path . $filename,
+ $new_path . $filename
+ );
+
+ foreach ($filenames as $file_to_diff)
+ {
+ $file_contents[] = file_get_contents($file_to_diff);
+
+ if ($file_contents[sizeof($file_contents) - 1] === false)
+ {
+ $this->iohandler->add_error_message(array('FILE_DIFFER_ERROR_FILE_CANNOT_BE_READ', $files_to_diff));
+ unset($file_contents);
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ $diff = new \diff3($file_contents[0], $file_contents[1], $file_contents[2]);
+ unset($file_contents);
+
+ // Handle conflicts
+ if ($diff->get_num_conflicts() !== 0)
+ {
+ $merge_conflicts[] = $filename;
+ }
+
+ // Save merged output
+ $this->cache->put(
+ '_file_' . md5($filename),
+ base64_encode(implode("\n", $diff->merged_output()))
+ );
+
+ unset($diff);
+
+ $progress_count++;
+ $this->iohandler->set_progress('UPDATE_FILE_DIFF', $progress_count);
+
+ if ($this->installer_config->get_time_remaining() <= 0 || $this->installer_config->get_memory_remaining() <= 0)
+ {
+ // Save differ progress
+ $this->installer_config->set('differ_progress_key', $key);
+ $this->installer_config->set('merge_conflict_list', $merge_conflicts);
+ $this->installer_config->set('file_diff_update_count', $progress_count);
+
+ // Request refresh
+ throw new resource_limit_reached_exception();
+ }
+ }
+
+ $this->iohandler->finish_progress('ALL_FILES_DIFFED');
+ $this->installer_config->set('merge_conflict_list', $merge_conflicts);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php
new file mode 100644
index 0000000000..9271e8fd50
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\update_filesystem\task;
+
+use phpbb\filesystem\filesystem;
+use phpbb\install\exception\jump_to_restart_point_exception;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\task_base;
+
+class download_updated_files extends task_base
+{
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * Constructor
+ *
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param filesystem $filesystem
+ */
+ public function __construct(config $config, iohandler_interface $iohandler, filesystem $filesystem)
+ {
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->filesystem = $filesystem;
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false)
+ && $this->installer_config->get('file_update_method', '') === 'compression';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ if ($this->iohandler->get_input('database_update_submit', false))
+ {
+ // Remove archive
+ $this->filesystem->remove(
+ $this->installer_config->get('update_file_archive', null)
+ );
+
+ $this->installer_config->set('update_file_archive', null);
+ }
+ else if ($this->iohandler->get_input('update_recheck_files_submit', false))
+ {
+ throw new jump_to_restart_point_exception('check_update_files');
+ }
+ else
+ {
+ // Render download box
+ $this->iohandler->add_download_link(
+ 'phpbb_installer_update_file_download',
+ 'DOWNLOAD_UPDATE_METHOD',
+ 'DOWNLOAD_UPDATE_METHOD_EXPLAIN'
+ );
+
+ // Add form to continue update
+ $this->iohandler->add_user_form_group('UPDATE_CONTINUE_UPDATE_PROCESS', array(
+ 'update_recheck_files_submit' => array(
+ 'label' => 'UPDATE_RECHECK_UPDATE_FILES',
+ 'type' => 'submit',
+ ),
+ 'database_update_submit' => array(
+ 'label' => 'UPDATE_CONTINUE_UPDATE_PROCESS',
+ 'type' => 'submit',
+ ),
+ ));
+
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/task/file_check.php b/phpBB/phpbb/install/module/update_filesystem/task/file_check.php
new file mode 100644
index 0000000000..5dbee6c259
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/task/file_check.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\install\module\update_filesystem\task;
+
+use phpbb\filesystem\filesystem;
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\helper\update_helper;
+use phpbb\install\task_base;
+
+/**
+ * Updater task performing file checking
+ */
+class file_check extends task_base
+{
+ /**
+ * @var filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Construct
+ *
+ * @param filesystem $filesystem
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ */
+ public function __construct(filesystem $filesystem, config $config, iohandler_interface $iohandler, update_helper $update_helper, $phpbb_root_path)
+ {
+ $this->filesystem = $filesystem;
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ if (!$this->installer_config->has_restart_point('check_update_files'))
+ {
+ $this->installer_config->create_progress_restart_point('check_update_files');
+ }
+
+ $old_path = $this->update_helper->get_path_to_old_update_files();
+ $new_path = $this->update_helper->get_path_to_new_update_files();
+
+ $update_info = $this->installer_config->get('update_info', array());
+ $file_update_info = $this->installer_config->get('update_files', array());
+
+ if (empty($update_info))
+ {
+ $root_path = $this->phpbb_root_path;
+
+ $update_info = $this->installer_config->get('update_info_unprocessed', array());
+
+ $file_update_info = array();
+ $file_update_info['update_without_diff'] = array_diff($update_info['binary'], $update_info['deleted']);
+
+ // Filter out files that are already deleted
+ $file_update_info['delete'] = array_filter(
+ $update_info['deleted'],
+ function ($filename) use ($root_path)
+ {
+ return file_exists($root_path . $filename);
+ }
+ );
+ }
+
+ $progress_count = $this->installer_config->get('file_check_progress_count', 0);
+ $task_count = count($update_info['files']);
+ $this->iohandler->set_task_count($task_count);
+ $this->iohandler->set_progress('UPDATE_CHECK_FILES', 0);
+
+ foreach ($update_info['files'] as $key => $filename)
+ {
+ $old_file = $old_path . $filename;
+ $new_file = $new_path . $filename;
+ $file = $this->phpbb_root_path . $filename;
+
+ if ($this->installer_config->get_time_remaining() <= 0 || $this->installer_config->get_memory_remaining() <= 0)
+ {
+ // Save progress
+ $this->installer_config->set('update_info', $update_info);
+ $this->installer_config->set('file_check_progress_count', $progress_count);
+ $this->installer_config->set('update_files', $file_update_info);
+
+ // Request refresh
+ throw new resource_limit_reached_exception();
+ }
+
+ $progress_count++;
+ $this->iohandler->set_progress('UPDATE_CHECK_FILES', $progress_count);
+
+ if (!$this->filesystem->exists($file))
+ {
+ $file_update_info['new'][] = $filename;
+ }
+ else
+ {
+ $file_checksum = md5_file($file);
+
+ if ($file_checksum === md5_file($new_file))
+ {
+ // File already up to date
+ continue;
+ }
+ else if ($this->filesystem->exists($old_file) && $file_checksum === md5_file($old_file))
+ {
+ // No need to diff the file
+ $file_update_info['update_without_diff'][] = $filename;
+ }
+ else
+ {
+ $file_update_info['update_with_diff'][] = $filename;
+ }
+ }
+
+ unset($update_info['files'][$key]);
+ }
+
+ $this->installer_config->set('update_files', $file_update_info);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php b/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php
new file mode 100644
index 0000000000..e712b8ad6a
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\update_filesystem\task;
+
+use phpbb\filesystem\filesystem;
+use phpbb\install\exception\user_interaction_required_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\container_factory;
+use phpbb\install\helper\file_updater\factory;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\task_base;
+
+class show_file_status extends task_base
+{
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var filesystem
+ */
+ protected $filesystem;
+
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var \phpbb\install\helper\file_updater\compression_file_updater
+ */
+ protected $file_updater;
+
+ /**
+ * Constructor
+ *
+ * @param container_factory $container
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param filesystem $filesystem
+ * @param factory $file_updater_factory
+ */
+ public function __construct(container_factory $container, config $config, iohandler_interface $iohandler, filesystem $filesystem, factory $file_updater_factory)
+ {
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->filesystem = $filesystem;
+
+ $this->cache = $container->get('cache.driver');
+
+ // Initialize compression file updater
+ $compression_method = $this->installer_config->get('compression_method', '');
+ $this->file_updater = $file_updater_factory->get('compression');
+ $conflict_archive = $this->file_updater->init($compression_method);
+
+ $this->installer_config->set('update_file_conflict_archive', $conflict_archive);
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ if (!$this->iohandler->get_input('submit_continue_file_update', false))
+ {
+ // Handle merge conflicts
+ $merge_conflicts = $this->installer_config->get('merge_conflict_list', array());
+
+ // Create archive for merge conflicts
+ if (!empty($merge_conflicts))
+ {
+ foreach ($merge_conflicts as $filename)
+ {
+ $this->file_updater->create_new_file(
+ $filename,
+ base64_decode($this->cache->get('_file_' . md5($filename))),
+ true
+ );
+ }
+
+ // Render download box
+ $this->iohandler->add_download_link(
+ 'phpbb_installer_update_conflict_download',
+ 'DOWNLOAD_CONFLICTS',
+ 'DOWNLOAD_CONFLICTS_EXPLAIN'
+ );
+ }
+
+ $this->file_updater->close();
+
+ // Render update file statuses
+ $file_update_info = $this->installer_config->get('update_files', array());
+ $file_status = array(
+ 'deleted' => (!isset($file_update_info['delete'])) ? array() : $file_update_info['delete'],
+ 'new' => (!isset($file_update_info['new'])) ? array() : $file_update_info['new'],
+ 'conflict' => $this->installer_config->get('merge_conflict_list', array()),
+ 'modified' => (!isset($file_update_info['update_with_diff'])) ? array() : $file_update_info['update_with_diff'],
+ 'not_modified' => (!isset($file_update_info['update_without_diff'])) ? array() : $file_update_info['update_without_diff'],
+ );
+
+ $this->iohandler->render_update_file_status($file_status);
+
+ // Add form to continue update
+ $this->iohandler->add_user_form_group('UPDATE_CONTINUE_FILE_UPDATE', array(
+ 'submit_continue_file_update' => array(
+ 'label' => 'UPDATE_CONTINUE_FILE_UPDATE',
+ 'type' => 'submit',
+ ),
+ ));
+
+ // Show results to the user
+ $this->iohandler->send_response();
+ throw new user_interaction_required_exception();
+ }
+ else
+ {
+ // Remove archive
+ $this->filesystem->remove(
+ $this->installer_config->get('update_file_conflict_archive', null)
+ );
+
+ $this->installer_config->set('update_file_conflict_archive', null);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module/update_filesystem/task/update_files.php b/phpBB/phpbb/install/module/update_filesystem/task/update_files.php
new file mode 100644
index 0000000000..fbb465cc66
--- /dev/null
+++ b/phpBB/phpbb/install/module/update_filesystem/task/update_files.php
@@ -0,0 +1,294 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install\module\update_filesystem\task;
+
+use phpbb\exception\runtime_exception;
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\container_factory;
+use phpbb\install\helper\file_updater\factory;
+use phpbb\install\helper\file_updater\file_updater_interface;
+use phpbb\install\helper\iohandler\iohandler_interface;
+use phpbb\install\helper\update_helper;
+use phpbb\install\task_base;
+
+/**
+ * File updater task
+ */
+class update_files extends task_base
+{
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var config
+ */
+ protected $installer_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var factory
+ */
+ protected $factory;
+
+ /**
+ * @var file_updater_interface
+ */
+ protected $file_updater;
+
+ /**
+ * @var update_helper
+ */
+ protected $update_helper;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param container_factory $container
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ * @param factory $file_updater_factory
+ * @param update_helper $update_helper
+ * @param string $phpbb_root_path
+ */
+ public function __construct(container_factory $container, config $config, iohandler_interface $iohandler, factory $file_updater_factory, update_helper $update_helper, $phpbb_root_path)
+ {
+ $this->factory = $file_updater_factory;
+ $this->installer_config = $config;
+ $this->iohandler = $iohandler;
+ $this->update_helper = $update_helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+
+ $this->cache = $container->get('cache.driver');
+ $this->file_updater = null;
+
+ parent::__construct(false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check_requirements()
+ {
+ return $this->installer_config->get('do_update_files', false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $new_path = $this->update_helper->get_path_to_new_update_files();
+
+ $file_update_info = $this->installer_config->get('update_files', array());
+
+ $update_type_progress = $this->installer_config->get('file_updater_type_progress', '');
+ $update_elem_progress = $this->installer_config->get('file_updater_elem_progress', '');
+ $type_progress_found = false;
+ $elem_progress_found = false;
+
+ // Progress bar
+ $task_count = 0;
+ foreach ($file_update_info as $sub_array)
+ {
+ $task_count += count($sub_array);
+ }
+
+ // Everything is up to date, so just continue
+ if ($task_count === 0)
+ {
+ return;
+ }
+
+ $progress_count = $this->installer_config->get('file_update_progress_count', 0);
+ $this->iohandler->set_task_count($task_count, true);
+ $this->iohandler->set_progress('UPDATE_UPDATING_FILES', 0);
+
+ $this->file_updater = $this->get_file_updater();
+
+ // File updater fallback logic
+ try
+ {
+ // Update files
+ foreach ($file_update_info as $type => $file_update_vector)
+ {
+ if (!$type_progress_found)
+ {
+ if ($type === $update_type_progress || empty($update_elem_progress))
+ {
+ $type_progress_found = true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ foreach ($file_update_vector as $path)
+ {
+ if (!$elem_progress_found)
+ {
+ if ($path === $update_elem_progress || empty($update_elem_progress))
+ {
+ $elem_progress_found = true;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ switch ($type)
+ {
+ case 'delete':
+ $this->file_updater->delete_file($path);
+ break;
+ case 'new':
+ $this->file_updater->create_new_file($path, $new_path . $path);
+ break;
+ case 'update_without_diff':
+ $this->file_updater->update_file($path, $new_path . $path);
+ break;
+ case 'update_with_diff':
+ $this->file_updater->update_file(
+ $path,
+ base64_decode($this->cache->get('_file_' . md5($path))),
+ true
+ );
+ break;
+ }
+
+ // Save progress
+ $this->installer_config->set('file_updater_type_progress', $type);
+ $this->installer_config->set('file_updater_elem_progress', $path);
+ $progress_count++;
+ $this->iohandler->set_progress('UPDATE_UPDATING_FILES', $progress_count);
+
+ if ($this->installer_config->get_time_remaining() <= 0 || $this->installer_config->get_memory_remaining() <= 0)
+ {
+ // Request refresh
+ throw new resource_limit_reached_exception();
+ }
+ }
+ }
+
+ $this->iohandler->finish_progress('UPDATE_UPDATING_FILES');
+ }
+ catch (runtime_exception $e)
+ {
+ if ($e instanceof resource_limit_reached_exception)
+ {
+ throw new resource_limit_reached_exception();
+ }
+
+ $current_method = $this->installer_config->get('file_update_method', '');
+
+ // File updater failed, try to fallback to download file update mode
+ if ($current_method !== 'compression')
+ {
+ $this->iohandler->add_warning_message(array(
+ 'UPDATE_FILE_UPDATER_HAS_FAILED',
+ $current_method,
+ 'compression'
+ ));
+ $this->installer_config->set('file_update_method', 'compression');
+
+ // We only want a simple refresh here
+ throw new resource_limit_reached_exception();
+ }
+ else
+ {
+ // Nowhere to fallback to :(
+ // Due to the way the installer handles fatal errors, we need to throw a low level exception
+ throw new runtime_exception('UPDATE_FILE_UPDATERS_HAVE_FAILED');
+ }
+ }
+
+ $file_updater_method = $this->installer_config->get('file_update_method', '');
+ if ($file_updater_method === 'compression' || $file_updater_method === 'ftp')
+ {
+ $this->file_updater->close();
+ }
+ }
+
+ /**
+ * Get file updater
+ *
+ * @param null|string $file_updater_method Name of the file updater to use
+ *
+ * @return file_updater_interface File updater
+ */
+ protected function get_file_updater($file_updater_method = null)
+ {
+ $file_updater_method = ($file_updater_method === null) ? $this->installer_config->get('file_update_method', '') : $file_updater_method;
+
+ if ($file_updater_method === 'compression')
+ {
+ $compression_method = $this->installer_config->get('file_update_compression', '');
+
+ /** @var \phpbb\install\helper\file_updater\compression_file_updater $file_updater */
+ $file_updater = $this->factory->get('compression');
+ $archive_path = $file_updater->init($compression_method);
+ $this->installer_config->set('update_file_archive', $archive_path);
+ }
+ else if ($file_updater_method === 'ftp')
+ {
+ /** @var \phpbb\install\helper\file_updater\ftp_file_updater $file_updater */
+ $file_updater = $this->factory->get('ftp');
+ $file_updater->init(
+ $this->installer_config->get('ftp_method', ''),
+ $this->installer_config->get('ftp_host', ''),
+ $this->installer_config->get('ftp_user', ''),
+ $this->installer_config->get('ftp_pass', ''),
+ $this->installer_config->get('ftp_path', ''),
+ $this->installer_config->get('ftp_port', 0),
+ $this->installer_config->get('ftp_timeout', 10)
+ );
+ }
+ else
+ {
+ /** @var file_updater_interface $file_updater */
+ $file_updater = $this->factory->get('direct_file');
+ }
+
+ return $file_updater;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ static public function get_step_count()
+ {
+ return 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_task_lang_name()
+ {
+ return '';
+ }
+}
diff --git a/phpBB/phpbb/install/module_base.php b/phpBB/phpbb/install/module_base.php
new file mode 100644
index 0000000000..fb68c3aca2
--- /dev/null
+++ b/phpBB/phpbb/install/module_base.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install;
+
+use phpbb\di\ordered_service_collection;
+use phpbb\install\exception\resource_limit_reached_exception;
+use phpbb\install\helper\config;
+use phpbb\install\helper\iohandler\iohandler_interface;
+
+/**
+ * Base class for installer module
+ */
+abstract class module_base implements module_interface
+{
+ /**
+ * @var config
+ */
+ protected $install_config;
+
+ /**
+ * @var iohandler_interface
+ */
+ protected $iohandler;
+
+ /**
+ * @var bool
+ */
+ protected $is_essential;
+
+ /**
+ * Array of tasks for installer module
+ *
+ * @var ordered_service_collection
+ */
+ protected $task_collection;
+
+ /**
+ * @var array
+ */
+ protected $task_step_count;
+
+ /**
+ * @var bool
+ */
+ protected $allow_progress_bar;
+
+ /**
+ * Installer module constructor
+ *
+ * @param ordered_service_collection $tasks array of installer tasks for installer module
+ * @param bool $essential flag indicating whether the module is essential or not
+ * @param bool $allow_progress_bar flag indicating whether or not to send progress information from within the module
+ */
+ public function __construct(ordered_service_collection $tasks, $essential = true, $allow_progress_bar = true)
+ {
+ $this->task_collection = $tasks;
+ $this->is_essential = $essential;
+ $this->allow_progress_bar = $allow_progress_bar;
+ }
+
+ /**
+ * Dependency getter
+ *
+ * @param config $config
+ * @param iohandler_interface $iohandler
+ */
+ public function setup(config $config, iohandler_interface $iohandler)
+ {
+ $this->install_config = $config;
+ $this->iohandler = $iohandler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_essential()
+ {
+ return $this->is_essential;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Overwrite this method if your task is non-essential!
+ */
+ public function check_requirements()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ // Recover install progress
+ $task_name = $this->recover_progress();
+ $task_found = false;
+
+ /**
+ * @var string $name ID of the service
+ * @var \phpbb\install\task_interface $task Task object
+ */
+ foreach ($this->task_collection as $name => $task)
+ {
+ // Run until there are available resources
+ if ($this->install_config->get_time_remaining() <= 0 || $this->install_config->get_memory_remaining() <= 0)
+ {
+ throw new resource_limit_reached_exception();
+ }
+
+ // Skip forward until the next task is reached
+ if (!$task_found)
+ {
+ if ($name === $task_name || empty($task_name))
+ {
+ $task_found = true;
+
+ if ($name === $task_name)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // Send progress information
+ if ($this->allow_progress_bar)
+ {
+ $this->iohandler->set_progress(
+ $task->get_task_lang_name(),
+ $this->install_config->get_current_task_progress()
+ );
+ }
+
+ // Check if we can run the task
+ if (!$task->is_essential() && !$task->check_requirements())
+ {
+ $this->iohandler->add_log_message(array(
+ 'SKIP_TASK',
+ $name,
+ ));
+
+ $this->install_config->increment_current_task_progress($this->task_step_count[$name]);
+ continue;
+ }
+
+ if ($this->allow_progress_bar)
+ {
+ // Only increment progress by one, as if a task has more than one steps
+ // then that should be incremented in the task itself
+ $this->install_config->increment_current_task_progress();
+ }
+
+ $task->run();
+
+ // Log install progress
+ $this->install_config->set_finished_task($name);
+
+ // Send progress information
+ if ($this->allow_progress_bar)
+ {
+ $this->iohandler->set_progress(
+ $task->get_task_lang_name(),
+ $this->install_config->get_current_task_progress()
+ );
+ }
+
+ $this->iohandler->send_response();
+ }
+
+ // Module finished, so clear task progress
+ $this->install_config->set_finished_task('');
+ }
+
+ /**
+ * Returns the next task's name
+ *
+ * @return string Index of the array element of the next task
+ */
+ protected function recover_progress()
+ {
+ $progress_array = $this->install_config->get_progress_data();
+ return $progress_array['last_task_name'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_step_count()
+ {
+ $task_step_count = 0;
+ $task_class_names = $this->task_collection->get_service_classes();
+
+ foreach ($task_class_names as $name => $task_class)
+ {
+ $step_count = $task_class::get_step_count();
+ $task_step_count += $step_count;
+ $this->task_step_count[$name] = $step_count;
+ }
+
+ return $task_step_count;
+ }
+}
diff --git a/phpBB/phpbb/install/module_interface.php b/phpBB/phpbb/install/module_interface.php
new file mode 100644
index 0000000000..a2d61e3958
--- /dev/null
+++ b/phpBB/phpbb/install/module_interface.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\install;
+
+/**
+ * Interface for installer modules
+ *
+ * An installer module is a task collection which executes installer tasks.
+ */
+interface module_interface
+{
+ /**
+ * Checks if the execution of the module is essential to install phpBB or it can be skipped
+ *
+ * Note: Please note that all the non-essential modules have to implement check_requirements()
+ * method.
+ *
+ * @return bool true if the module is essential, false otherwise
+ */
+ public function is_essential();
+
+ /**
+ * Checks requirements for the tasks
+ *
+ * Note: Only need to be implemented for non-essential tasks, as essential tasks
+ * requirements should be checked in the requirements install module.
+ *
+ * @return bool true if the task's requirements are met
+ */
+ public function check_requirements();
+
+ /**
+ * Executes the task
+ *
+ * @return null
+ */
+ public function run();
+
+ /**
+ * Returns the number of tasks in the module
+ *
+ * @return int
+ */
+ public function get_step_count();
+
+ /**
+ * Returns an array to the correct navigation stage
+ *
+ * @return array
+ */
+ public function get_navigation_stage_path();
+}
diff --git a/phpBB/phpbb/install/task_base.php b/phpBB/phpbb/install/task_base.php
new file mode 100644
index 0000000000..b5199be4af
--- /dev/null
+++ b/phpBB/phpbb/install/task_base.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\install;
+
+/**
+ * Base class for installer task
+ */
+abstract class task_base implements task_interface
+{
+ /**
+ * @var bool
+ */
+ protected $is_essential;
+
+ /**
+ * Constructor
+ *
+ * @param bool $essential
+ */
+ public function __construct($essential = true)
+ {
+ $this->is_essential = $essential;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_essential()
+ {
+ return $this->is_essential;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Note: Overwrite this method if your task is non-essential!
+ */
+ public function check_requirements()
+ {
+ return true;
+ }
+}
diff --git a/phpBB/phpbb/install/task_interface.php b/phpBB/phpbb/install/task_interface.php
new file mode 100644
index 0000000000..794cb16482
--- /dev/null
+++ b/phpBB/phpbb/install/task_interface.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\install;
+
+/**
+ * Interface for installer tasks
+ */
+interface task_interface
+{
+ /**
+ * Returns the number of steps the task contains
+ *
+ * This is a helper method to provide a better progress bar for the front-end.
+ *
+ * @return int The number of steps that the task contains
+ */
+ static public function get_step_count();
+
+ /**
+ * Checks if the task is essential to install phpBB or it can be skipped
+ *
+ * Note: Please note that all the non-essential modules have to implement check_requirements()
+ * method.
+ *
+ * @return bool true if the task is essential, false otherwise
+ */
+ public function is_essential();
+
+ /**
+ * Checks requirements for the tasks
+ *
+ * Note: Only need to be implemented for non-essential tasks, as essential tasks
+ * requirements should be checked in the requirements install module.
+ *
+ * @return bool true if the task's requirements are met
+ */
+ public function check_requirements();
+
+ /**
+ * Executes the task
+ */
+ public function run();
+
+ /**
+ * Returns the language key of the name of the task
+ *
+ * @return string
+ */
+ public function get_task_lang_name();
+}
diff --git a/phpBB/phpbb/json_response.php b/phpBB/phpbb/json_response.php
index 45c2f6cac4..5219cd0c32 100644
--- a/phpBB/phpbb/json_response.php
+++ b/phpBB/phpbb/json_response.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb;
/**
* JSON class
-* @package phpBB3
*/
class json_response
{
diff --git a/phpBB/phpbb/language/exception/invalid_plural_rule_exception.php b/phpBB/phpbb/language/exception/invalid_plural_rule_exception.php
new file mode 100644
index 0000000000..94e3466208
--- /dev/null
+++ b/phpBB/phpbb/language/exception/invalid_plural_rule_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\language\exception;
+
+/**
+ * Thrown when nonexistent plural rule is specified
+ */
+class invalid_plural_rule_exception extends language_exception
+{
+
+}
diff --git a/phpBB/phpbb/language/exception/language_exception.php b/phpBB/phpbb/language/exception/language_exception.php
new file mode 100644
index 0000000000..b1258414aa
--- /dev/null
+++ b/phpBB/phpbb/language/exception/language_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\language\exception;
+
+/**
+ * Base exception class for language exceptions
+ */
+class language_exception extends \phpbb\exception\runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/language/exception/language_file_not_found.php b/phpBB/phpbb/language/exception/language_file_not_found.php
new file mode 100644
index 0000000000..89364267eb
--- /dev/null
+++ b/phpBB/phpbb/language/exception/language_file_not_found.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\language\exception;
+
+/**
+ * This exception is thrown when the language file is not found
+ */
+class language_file_not_found extends language_exception
+{
+
+}
diff --git a/phpBB/phpbb/language/language.php b/phpBB/phpbb/language/language.php
new file mode 100644
index 0000000000..382d4db89e
--- /dev/null
+++ b/phpBB/phpbb/language/language.php
@@ -0,0 +1,653 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\language;
+
+use phpbb\language\exception\invalid_plural_rule_exception;
+
+/**
+ * Wrapper class for loading translations
+ */
+class language
+{
+ /**
+ * Global fallback language
+ *
+ * ISO code of the language to fallback to when the specified language entries
+ * cannot be found.
+ *
+ * @var string
+ */
+ const FALLBACK_LANGUAGE = 'en';
+
+ /**
+ * @var array List of common language files
+ */
+ protected $common_language_files;
+
+ /**
+ * @var bool
+ */
+ protected $common_language_files_loaded;
+
+ /**
+ * @var string ISO code of the default board language
+ */
+ protected $default_language;
+
+ /**
+ * @var string ISO code of the User's language
+ */
+ protected $user_language;
+
+ /**
+ * @var array Language fallback array (the order is important)
+ */
+ protected $language_fallback;
+
+ /**
+ * @var array Array of language variables
+ */
+ protected $lang;
+
+ /**
+ * @var array Loaded language sets
+ */
+ protected $loaded_language_sets;
+
+ /**
+ * @var \phpbb\language\language_file_loader Language file loader
+ */
+ protected $loader;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\language\language_file_loader $loader Language file loader
+ * @param array|null $common_modules Array of common language modules to load (optional)
+ */
+ public function __construct(language_file_loader $loader, $common_modules = null)
+ {
+ $this->loader = $loader;
+
+ // Set up default information
+ $this->user_language = false;
+ $this->default_language = false;
+ $this->lang = array();
+ $this->loaded_language_sets = array(
+ 'core' => array(),
+ 'ext' => array(),
+ );
+
+ // Common language files
+ if (is_array($common_modules))
+ {
+ $this->common_language_files = $common_modules;
+ }
+ else
+ {
+ $this->common_language_files = array(
+ 'common',
+ );
+ }
+
+ $this->common_language_files_loaded = false;
+
+ $this->language_fallback = array(self::FALLBACK_LANGUAGE);
+ }
+
+ /**
+ * Function to set user's language to display.
+ *
+ * @param string $user_lang_iso ISO code of the User's language
+ * @param bool $reload Whether or not to reload language files
+ */
+ public function set_user_language($user_lang_iso, $reload = false)
+ {
+ $this->user_language = $user_lang_iso;
+
+ $this->set_fallback_array($reload);
+ }
+
+ /**
+ * Function to set the board's default language to display.
+ *
+ * @param string $default_lang_iso ISO code of the board's default language
+ * @param bool $reload Whether or not to reload language files
+ */
+ public function set_default_language($default_lang_iso, $reload = false)
+ {
+ $this->default_language = $default_lang_iso;
+
+ $this->set_fallback_array($reload);
+ }
+
+ /**
+ * Returns language array
+ *
+ * Note: This function is needed for the BC purposes, until \phpbb\user::lang[] is
+ * not removed.
+ *
+ * @return array Array of loaded language strings
+ */
+ public function get_lang_array()
+ {
+ // Load common language files if they not loaded yet
+ if (!$this->common_language_files_loaded)
+ {
+ $this->load_common_language_files();
+ }
+
+ return $this->lang;
+ }
+
+ /**
+ * Add Language Items
+ *
+ * Examples:
+ * <code>
+ * $component = array('posting');
+ * $component = array('posting', 'viewtopic')
+ * $component = 'posting'
+ * </code>
+ *
+ * @param string|array $component The name of the language component to load
+ * @param string|null $extension_name Name of the extension to load component from, or null for core file
+ */
+ public function add_lang($component, $extension_name = null)
+ {
+ // Load common language files if they not loaded yet
+ // This needs to be here to correctly merge language arrays
+ if (!$this->common_language_files_loaded)
+ {
+ $this->load_common_language_files();
+ }
+
+ if (!is_array($component))
+ {
+ if (!is_null($extension_name))
+ {
+ $this->load_extension($extension_name, $component);
+ }
+ else
+ {
+ $this->load_core_file($component);
+ }
+ }
+ else
+ {
+ foreach ($component as $lang_file)
+ {
+ $this->add_lang($lang_file, $extension_name);
+ }
+ }
+ }
+
+ /**
+ * @param $key array|string The language key we want to know more about. Can be string or array.
+ *
+ * @return bool Returns whether the language key is set.
+ */
+ public function is_set($key)
+ {
+ // Load common language files if they not loaded yet
+ if (!$this->common_language_files_loaded)
+ {
+ $this->load_common_language_files();
+ }
+
+ if (is_array($key))
+ {
+ $lang = &$this->lang[array_shift($key)];
+
+ foreach ($key as $_key)
+ {
+ $lang = &$lang[$_key];
+ }
+ }
+ else
+ {
+ $lang = &$this->lang[$key];
+ }
+
+ return isset($lang);
+ }
+
+ /**
+ * Advanced language substitution
+ *
+ * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
+ * Params are the language key and the parameters to be substituted.
+ * This function/functionality is inspired by SHS` and Ashe.
+ *
+ * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp>
+ *
+ * If the first parameter is an array, the elements are used as keys and subkeys to get the language entry:
+ * Example: <samp>$user->lang(array('datetime', 'AGO'), 1)</samp> uses $user->lang['datetime']['AGO'] as language entry.
+ *
+ * @return string Return localized string or the language key if the translation is not available
+ */
+ public function lang()
+ {
+ $args = func_get_args();
+ $key = array_shift($args);
+
+ return $this->lang_array($key, $args);
+ }
+
+ /**
+ * Act like lang() but takes a key and an array of parameters instead of using variadic
+ *
+ * @param string|array $key Language key
+ * @param array $args Parameters
+ *
+ * @return array|string
+ */
+ public function lang_array($key, $args = array())
+ {
+ // Load common language files if they not loaded yet
+ if (!$this->common_language_files_loaded)
+ {
+ $this->load_common_language_files();
+ }
+
+ if (is_array($key))
+ {
+ $lang = &$this->lang[array_shift($key)];
+
+ foreach ($key as $_key)
+ {
+ $lang = &$lang[$_key];
+ }
+ }
+ else
+ {
+ $lang = &$this->lang[$key];
+ }
+
+ // Return if language string does not exist
+ if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
+ {
+ return $key;
+ }
+
+ // If the language entry is a string, we simply mimic sprintf() behaviour
+ if (is_string($lang))
+ {
+ if (count($args) === 0)
+ {
+ return $lang;
+ }
+
+ // Replace key with language entry and simply pass along...
+ return vsprintf($lang, $args);
+ }
+ else if (sizeof($lang) == 0)
+ {
+ // If the language entry is an empty array, we just return the language key
+ return $key;
+ }
+
+ // It is an array... now handle different nullar/singular/plural forms
+ $key_found = false;
+
+ // We now get the first number passed and will select the key based upon this number
+ for ($i = 0, $num_args = sizeof($args); $i < $num_args; $i++)
+ {
+ if (is_int($args[$i]) || is_float($args[$i]))
+ {
+ if ($args[$i] == 0 && isset($lang[0]))
+ {
+ // We allow each translation using plural forms to specify a version for the case of 0 things,
+ // so that "0 users" may be displayed as "No users".
+ $key_found = 0;
+ break;
+ }
+ else
+ {
+ $use_plural_form = $this->get_plural_form($args[$i]);
+ if (isset($lang[$use_plural_form]))
+ {
+ // The key we should use exists, so we use it.
+ $key_found = $use_plural_form;
+ }
+ else
+ {
+ // If the key we need to use does not exist, we fall back to the previous one.
+ $numbers = array_keys($lang);
+
+ foreach ($numbers as $num)
+ {
+ if ($num > $use_plural_form)
+ {
+ break;
+ }
+
+ $key_found = $num;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
+ if ($key_found === false)
+ {
+ $numbers = array_keys($lang);
+ $key_found = end($numbers);
+ }
+
+ // Use the language string we determined and pass it to sprintf()
+ return vsprintf($lang[$key_found], $args);
+ }
+
+ /**
+ * Loads common language files
+ */
+ protected function load_common_language_files()
+ {
+ if (!$this->common_language_files_loaded)
+ {
+ foreach ($this->common_language_files as $lang_file)
+ {
+ $this->load_core_file($lang_file);
+ }
+
+ $this->common_language_files_loaded = true;
+ }
+ }
+
+ /**
+ * Determine which plural form we should use.
+ *
+ * For some languages this is not as simple as for English.
+ *
+ * @param int|float $number The number we want to get the plural case for. Float numbers are floored.
+ * @param int|bool $force_rule False to use the plural rule of the language package
+ * or an integer to force a certain plural rule
+ *
+ * @return int The plural-case we need to use for the number plural-rule combination
+ *
+ * @throws \phpbb\language\exception\invalid_plural_rule_exception When $force_rule has an invalid value
+ */
+ public function get_plural_form($number, $force_rule = false)
+ {
+ $number = (int) $number;
+ $plural_rule = ($force_rule !== false) ? $force_rule : ((isset($this->lang['PLURAL_RULE'])) ? $this->lang['PLURAL_RULE'] : 1);
+
+ if ($plural_rule > 15 || $plural_rule < 0)
+ {
+ throw new invalid_plural_rule_exception('INVALID_PLURAL_RULE', array(
+ 'plural_rule' => $plural_rule,
+ ));
+ }
+
+ /**
+ * The following plural rules are based on a list published by the Mozilla Developer Network
+ * https://developer.mozilla.org/en/Localization_and_Plurals
+ */
+ switch ($plural_rule)
+ {
+ case 0:
+ /**
+ * Families: Asian (Chinese, Japanese, Korean, Vietnamese), Persian, Turkic/Altaic (Turkish), Thai, Lao
+ * 1 - everything: 0, 1, 2, ...
+ */
+ return 1;
+
+ case 1:
+ /**
+ * Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan)
+ * 1 - 1
+ * 2 - everything else: 0, 2, 3, ...
+ */
+ return ($number === 1) ? 1 : 2;
+
+ case 2:
+ /**
+ * Families: Romanic (French, Brazilian Portuguese)
+ * 1 - 0, 1
+ * 2 - everything else: 2, 3, ...
+ */
+ return (($number === 0) || ($number === 1)) ? 1 : 2;
+
+ case 3:
+ /**
+ * Families: Baltic (Latvian)
+ * 1 - 0
+ * 2 - ends in 1, not 11: 1, 21, ... 101, 121, ...
+ * 3 - everything else: 2, 3, ... 10, 11, 12, ... 20, 22, ...
+ */
+ return ($number === 0) ? 1 : ((($number % 10 === 1) && ($number % 100 != 11)) ? 2 : 3);
+
+ case 4:
+ /**
+ * Families: Celtic (Scottish Gaelic)
+ * 1 - is 1 or 11: 1, 11
+ * 2 - is 2 or 12: 2, 12
+ * 3 - others between 3 and 19: 3, 4, ... 10, 13, ... 18, 19
+ * 4 - everything else: 0, 20, 21, ...
+ */
+ return ($number === 1 || $number === 11) ? 1 : (($number === 2 || $number === 12) ? 2 : (($number >= 3 && $number <= 19) ? 3 : 4));
+
+ case 5:
+ /**
+ * Families: Romanic (Romanian)
+ * 1 - 1
+ * 2 - is 0 or ends in 01-19: 0, 2, 3, ... 19, 101, 102, ... 119, 201, ...
+ * 3 - everything else: 20, 21, ...
+ */
+ return ($number === 1) ? 1 : ((($number === 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 2 : 3);
+
+ case 6:
+ /**
+ * Families: Baltic (Lithuanian)
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
+ * 2 - ends in 0 or ends in 10-20: 0, 10, 11, 12, ... 19, 20, 30, 40, ...
+ * 3 - everything else: 2, 3, ... 8, 9, 22, 23, ... 29, 32, 33, ...
+ */
+ return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 < 2) || (($number % 100 >= 10) && ($number % 100 < 20))) ? 2 : 3);
+
+ case 7:
+ /**
+ * Families: Slavic (Croatian, Serbian, Russian, Ukrainian)
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
+ * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ...
+ * 3 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, ...
+ */
+ return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 2 : 3);
+
+ case 8:
+ /**
+ * Families: Slavic (Slovak, Czech)
+ * 1 - 1
+ * 2 - 2, 3, 4
+ * 3 - everything else: 0, 5, 6, 7, ...
+ */
+ return ($number === 1) ? 1 : ((($number >= 2) && ($number <= 4)) ? 2 : 3);
+
+ case 9:
+ /**
+ * Families: Slavic (Polish)
+ * 1 - 1
+ * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ... 104, 122, ...
+ * 3 - everything else: 0, 5, 6, ... 11, 12, 13, 14, 15, ... 20, 21, 25, ...
+ */
+ return ($number === 1) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 2 : 3);
+
+ case 10:
+ /**
+ * Families: Slavic (Slovenian, Sorbian)
+ * 1 - ends in 01: 1, 101, 201, ...
+ * 2 - ends in 02: 2, 102, 202, ...
+ * 3 - ends in 03-04: 3, 4, 103, 104, 203, 204, ...
+ * 4 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, ...
+ */
+ return ($number % 100 === 1) ? 1 : (($number % 100 === 2) ? 2 : ((($number % 100 === 3) || ($number % 100 === 4)) ? 3 : 4));
+
+ case 11:
+ /**
+ * Families: Celtic (Irish Gaeilge)
+ * 1 - 1
+ * 2 - 2
+ * 3 - is 3-6: 3, 4, 5, 6
+ * 4 - is 7-10: 7, 8, 9, 10
+ * 5 - everything else: 0, 11, 12, ...
+ */
+ return ($number === 1) ? 1 : (($number === 2) ? 2 : (($number >= 3 && $number <= 6) ? 3 : (($number >= 7 && $number <= 10) ? 4 : 5)));
+
+ case 12:
+ /**
+ * Families: Semitic (Arabic)
+ * 1 - 1
+ * 2 - 2
+ * 3 - ends in 03-10: 3, 4, ... 10, 103, 104, ... 110, 203, 204, ...
+ * 4 - ends in 11-99: 11, ... 99, 111, 112, ...
+ * 5 - everything else: 100, 101, 102, 200, 201, 202, ...
+ * 6 - 0
+ */
+ return ($number === 1) ? 1 : (($number === 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : (($number != 0) ? 5 : 6))));
+
+ case 13:
+ /**
+ * Families: Semitic (Maltese)
+ * 1 - 1
+ * 2 - is 0 or ends in 01-10: 0, 2, 3, ... 9, 10, 101, 102, ...
+ * 3 - ends in 11-19: 11, 12, ... 18, 19, 111, 112, ...
+ * 4 - everything else: 20, 21, ...
+ */
+ return ($number === 1) ? 1 : ((($number === 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 2 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 3 : 4));
+
+ case 14:
+ /**
+ * Families: Slavic (Macedonian)
+ * 1 - ends in 1: 1, 11, 21, ...
+ * 2 - ends in 2: 2, 12, 22, ...
+ * 3 - everything else: 0, 3, 4, ... 10, 13, 14, ... 20, 23, ...
+ */
+ return ($number % 10 === 1) ? 1 : (($number % 10 === 2) ? 2 : 3);
+
+ case 15:
+ /**
+ * Families: Icelandic
+ * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, 131, ...
+ * 2 - everything else: 0, 2, 3, ... 10, 11, 12, ... 20, 22, ...
+ */
+ return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : 2;
+ }
+ }
+
+ /**
+ * Returns the ISO code of the used language
+ *
+ * @return string The ISO code of the currently used language
+ */
+ public function get_used_language()
+ {
+ return $this->language_fallback[0];
+ }
+
+ /**
+ * Returns language fallback data
+ *
+ * @param bool $reload Whether or not to reload language files
+ *
+ * @return array
+ */
+ protected function set_fallback_array($reload = false)
+ {
+ $fallback_array = array();
+
+ if ($this->user_language)
+ {
+ $fallback_array[] = $this->user_language;
+ }
+
+ if ($this->default_language)
+ {
+ $fallback_array[] = $this->default_language;
+ }
+
+ $fallback_array[] = self::FALLBACK_LANGUAGE;
+
+ $this->language_fallback = $fallback_array;
+
+ if ($reload)
+ {
+ $this->reload_language_files();
+ }
+ }
+
+ /**
+ * Load core language file
+ *
+ * @param string $component Name of the component to load
+ */
+ protected function load_core_file($component)
+ {
+ // Check if the component is already loaded
+ if (isset($this->loaded_language_sets['core'][$component]))
+ {
+ return;
+ }
+
+ $this->loader->load($component, $this->language_fallback, $this->lang);
+ $this->loaded_language_sets['core'][$component] = true;
+ }
+
+ /**
+ * Load extension language file
+ *
+ * @param string $extension_name Name of the extension to load language from
+ * @param string $component Name of the component to load
+ */
+ protected function load_extension($extension_name, $component)
+ {
+ // Check if the component is already loaded
+ if (isset($this->loaded_language_sets['ext'][$extension_name][$component]))
+ {
+ return;
+ }
+
+ $this->loader->load_extension($extension_name, $component, $this->language_fallback, $this->lang);
+ $this->loaded_language_sets['ext'][$extension_name][$component] = true;
+ }
+
+ /**
+ * Reload language files
+ */
+ protected function reload_language_files()
+ {
+ $loaded_files = $this->loaded_language_sets;
+ $this->loaded_language_sets = array(
+ 'core' => array(),
+ 'ext' => array(),
+ );
+
+ // Reload core files
+ foreach ($loaded_files['core'] as $component => $value)
+ {
+ $this->load_core_file($component);
+ }
+
+ // Reload extension files
+ foreach ($loaded_files['ext'] as $ext_name => $ext_info)
+ {
+ foreach ($ext_info as $ext_component => $value)
+ {
+ $this->load_extension($ext_name, $ext_component);
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/language/language_file_helper.php b/phpBB/phpbb/language/language_file_helper.php
new file mode 100644
index 0000000000..18d7b62e21
--- /dev/null
+++ b/phpBB/phpbb/language/language_file_helper.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\language;
+
+use Symfony\Component\Finder\Finder;
+
+/**
+ * Helper class for language file related functions
+ */
+class language_file_helper
+{
+ /**
+ * @var string Path to phpBB's root
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path Path to phpBB's root
+ */
+ public function __construct($phpbb_root_path)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ }
+
+ /**
+ * Returns available languages
+ *
+ * @return array
+ */
+ public function get_available_languages()
+ {
+ // Find available language packages
+ $finder = new Finder();
+ $finder->files()
+ ->name('iso.txt')
+ ->depth('== 1')
+ ->in($this->phpbb_root_path . 'language');
+
+ $available_languages = array();
+ foreach ($finder as $file)
+ {
+ $path = $file->getRelativePath();
+ $info = explode("\n", $file->getContents());
+
+ $available_languages[] = array(
+ // Get the name of the directory containing iso.txt
+ 'iso' => $path,
+
+ // Recover data from file
+ 'name' => trim($info[0]),
+ 'local_name' => trim($info[1]),
+ 'author' => trim($info[2])
+ );
+ }
+
+ return $available_languages;
+ }
+}
diff --git a/phpBB/phpbb/language/language_file_loader.php b/phpBB/phpbb/language/language_file_loader.php
new file mode 100644
index 0000000000..359202fd63
--- /dev/null
+++ b/phpBB/phpbb/language/language_file_loader.php
@@ -0,0 +1,206 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\language;
+
+use \phpbb\language\exception\language_file_not_found;
+
+/**
+ * Language file loader
+ */
+class language_file_loader
+{
+ /**
+ * @var string Path to phpBB's root
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string Extension of PHP files
+ */
+ protected $php_ext;
+
+ /**
+ * @var \phpbb\extension\manager Extension manager
+ */
+ protected $extension_manager;
+
+ /**
+ * Constructor
+ *
+ * @param string $phpbb_root_path Path to phpBB's root
+ * @param string $php_ext Extension of PHP files
+ */
+ public function __construct($phpbb_root_path, $php_ext)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ $this->extension_manager = null;
+ }
+
+ /**
+ * Extension manager setter
+ *
+ * @param \phpbb\extension\manager $extension_manager Extension manager
+ */
+ public function set_extension_manager(\phpbb\extension\manager $extension_manager)
+ {
+ $this->extension_manager = $extension_manager;
+ }
+
+ /**
+ * Loads language array for the given component
+ *
+ * @param string $component Name of the language component
+ * @param string|array $locale ISO code of the language to load, or array of ISO codes if you want to
+ * specify additional language fallback steps
+ * @param array $lang Array reference containing language strings
+ */
+ public function load($component, $locale, &$lang)
+ {
+ $locale = (array) $locale;
+
+ // Determine path to language directory
+ $path = $this->phpbb_root_path . 'language/';
+
+ $this->load_file($path, $component, $locale, $lang);
+ }
+
+ /**
+ * Loads language array for the given extension component
+ *
+ * @param string $extension Name of the extension
+ * @param string $component Name of the language component
+ * @param string|array $locale ISO code of the language to load, or array of ISO codes if you want to
+ * specify additional language fallback steps
+ * @param array $lang Array reference containing language strings
+ */
+ public function load_extension($extension, $component, $locale, &$lang)
+ {
+ // Check if extension manager was loaded
+ if ($this->extension_manager === null)
+ {
+ // If not, let's return
+ return;
+ }
+
+ $locale = (array) $locale;
+
+ // Determine path to language directory
+ $path = $this->extension_manager->get_extension_path($extension, true) . 'language/';
+
+ $this->load_file($path, $component, $locale, $lang);
+ }
+
+ /**
+ * Prepares language file loading
+ *
+ * @param string $path Path to search for file in
+ * @param string $component Name of the language component
+ * @param array $locale Array containing language fallback options
+ * @param array $lang Array reference of language strings
+ */
+ protected function load_file($path, $component, $locale, &$lang)
+ {
+ // This is BC stuff and not the best idea as it makes language fallback
+ // implementation quite hard like below.
+ if (strpos($this->phpbb_root_path . $component, $path) === 0)
+ {
+ // Filter out the path
+ $path_diff = str_replace($path, '', dirname($this->phpbb_root_path . $component));
+ $language_file = basename($component, '.' . $this->php_ext);
+ $component = '';
+
+ // This step is needed to resolve language/en/subdir style $component
+ // $path already points to the language base directory so we need to eliminate
+ // the first directory from the path (that should be the language directory)
+ $path_diff_parts = explode('/', $path_diff);
+
+ if (sizeof($path_diff_parts) > 1)
+ {
+ array_shift($path_diff_parts);
+ $component = implode('/', $path_diff_parts) . '/';
+ }
+
+ $component .= $language_file;
+ }
+
+ // Determine filename
+ $filename = $component . '.' . $this->php_ext;
+
+ // Determine path to file
+ $file_path = $this->get_language_file_path($path, $filename, $locale);
+
+ // Load language array
+ $this->load_language_file($file_path, $lang);
+ }
+
+ /**
+ * This function implements language fallback logic
+ *
+ * @param string $path Path to language directory
+ * @param string $filename Filename to load language strings from
+ *
+ * @return string Relative path to language file
+ *
+ * @throws language_file_not_found When the path to the file cannot be resolved
+ */
+ protected function get_language_file_path($path, $filename, $locales)
+ {
+ $language_file_path = $filename;
+
+ // Language fallback logic
+ foreach ($locales as $locale)
+ {
+ $language_file_path = $path . $locale . '/' . $filename;
+
+ // If we are in install, try to use the updated version, when available
+ if (defined('IN_INSTALL'))
+ {
+ $install_language_path = str_replace('language/', 'install/update/new/language/', $language_file_path);
+ if (file_exists($install_language_path))
+ {
+ return $install_language_path;
+ }
+ }
+
+ if (file_exists($language_file_path))
+ {
+ return $language_file_path;
+ }
+ }
+
+ // The language file is not exist
+ throw new language_file_not_found('Language file ' . $language_file_path . ' couldn\'t be opened.');
+ }
+
+ /**
+ * Loads language file
+ *
+ * @param string $path Path to language file to load
+ * @param array $lang Reference of the array of language strings
+ */
+ protected function load_language_file($path, &$lang)
+ {
+ // Do not suppress error if in DEBUG mode
+ if (defined('DEBUG'))
+ {
+ include $path;
+ }
+ else
+ {
+ @include $path;
+ }
+ }
+}
diff --git a/phpBB/phpbb/lock/db.php b/phpBB/phpbb/lock/db.php
index 461adda045..85ba9a7aa3 100644
--- a/phpBB/phpbb/lock/db.php
+++ b/phpBB/phpbb/lock/db.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\lock;
/**
* Database locking class
-* @package phpBB3
*/
class db
{
@@ -42,7 +45,7 @@ class db
/**
* A database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
private $db;
@@ -51,11 +54,11 @@ class db
*
* You have to call acquire() to actually create the lock.
*
- * @param string $config_name A config variable to be used for locking
- * @param array $config The phpBB configuration
- * @param \phpbb\db\driver\driver $db A database connection
+ * @param string $config_name A config variable to be used for locking
+ * @param \phpbb\config\config $config The phpBB configuration
+ * @param \phpbb\db\driver\driver_interface $db A database connection
*/
- public function __construct($config_name, \phpbb\config\config $config, \phpbb\db\driver\driver $db)
+ public function __construct($config_name, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db)
{
$this->config_name = $config_name;
$this->config = $config;
diff --git a/phpBB/phpbb/lock/flock.php b/phpBB/phpbb/lock/flock.php
index 94a5895440..df88e1490a 100644
--- a/phpBB/phpbb/lock/flock.php
+++ b/phpBB/phpbb/lock/flock.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\lock;
/**
* File locking class
-* @package phpBB3
*/
class flock
{
diff --git a/phpBB/phpbb/log/null.php b/phpBB/phpbb/log/dummy.php
index 77d0fbe2d7..5c2d145e15 100644
--- a/phpBB/phpbb/log/null.php
+++ b/phpBB/phpbb/log/dummy.php
@@ -1,20 +1,22 @@
<?php
/**
*
-* @package phpbb_log
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\log;
/**
-* Null logger
-*
-* @package phpbb_log
+* Dummy logger
*/
-class null implements log_interface
+class dummy implements log_interface
{
/**
* {@inheritdoc}
@@ -49,6 +51,13 @@ class null implements log_interface
/**
* {@inheritdoc}
*/
+ public function delete($mode, $conditions = array())
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function get_logs($mode, $count_logs = true, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $log_time = 0, $sort_by = 'l.log_time DESC', $keywords = '')
{
return array();
diff --git a/phpBB/phpbb/log/log.php b/phpBB/phpbb/log/log.php
index 62edc6a77f..436c21bdad 100644
--- a/phpBB/phpbb/log/log.php
+++ b/phpBB/phpbb/log/log.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\log\log
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\log;
/**
* This class is used to add entries into the log table.
-*
-* @package \phpbb\log\log
*/
class log implements \phpbb\log\log_interface
{
@@ -25,7 +27,7 @@ class log implements \phpbb\log\log_interface
/**
* An array with the disabled log types. Logs of such types will not be
- * added when add_log() is called.
+ * added when add() is called.
* @var array
*/
protected $disabled_types;
@@ -68,7 +70,7 @@ class log implements \phpbb\log\log_interface
/**
* Event dispatcher object
- * @var phpbb_dispatcher
+ * @var \phpbb\event\dispatcher_interface
*/
protected $dispatcher;
@@ -93,15 +95,14 @@ class log implements \phpbb\log\log_interface
/**
* Constructor
*
- * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\db\driver\driver_interface $db Database object
* @param \phpbb\user $user User object
* @param \phpbb\auth\auth $auth Auth object
- * @param phpbb_dispatcher $phpbb_dispatcher Event dispatcher
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher
* @param string $phpbb_root_path Root path
* @param string $relative_admin_path Relative admin root path
* @param string $php_ext PHP Extension
* @param string $log_table Name of the table we use to store our logs
- * @return null
*/
public function __construct($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, $relative_admin_path, $php_ext, $log_table)
{
@@ -157,8 +158,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * This function returns the state of the log system.
- *
* {@inheritDoc}
*/
public function is_enabled($type = '')
@@ -171,12 +170,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * Disable log
- *
- * This function allows disabling the log system or parts of it, for this
- * page call. When add_log is called and the type is disabled,
- * the log will not be added to the database.
- *
* {@inheritDoc}
*/
public function disable($type = '')
@@ -199,10 +192,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * Enable log
- *
- * This function allows re-enabling the log system.
- *
* {@inheritDoc}
*/
public function enable($type = '')
@@ -225,8 +214,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * Adds a log to the database
- *
* {@inheritDoc}
*/
public function add($mode, $user_id, $log_ip, $log_operation, $log_time = false, $additional_data = array())
@@ -236,14 +223,14 @@ class log implements \phpbb\log\log_interface
return false;
}
- if ($log_time == false)
+ if ($log_time === false)
{
$log_time = time();
}
$sql_ary = array(
- 'user_id' => $user_id,
- 'log_ip' => $log_ip,
+ 'user_id' => $user_id ? (int) $user_id : ANONYMOUS,
+ 'log_ip' => empty($log_ip) ? '' : $log_ip,
'log_time' => $log_time,
'log_operation' => $log_operation,
);
@@ -258,14 +245,17 @@ class log implements \phpbb\log\log_interface
break;
case 'mod':
- $forum_id = (int) $additional_data['forum_id'];
+ $forum_id = isset($additional_data['forum_id']) ? (int) $additional_data['forum_id'] : 0;
unset($additional_data['forum_id']);
- $topic_id = (int) $additional_data['topic_id'];
+ $topic_id = isset($additional_data['topic_id']) ? (int) $additional_data['topic_id'] : 0;
unset($additional_data['topic_id']);
+ $post_id = isset($additional_data['post_id']) ? (int) $additional_data['post_id'] : 0;
+ unset($additional_data['post_id']);
$sql_ary += array(
'log_type' => LOG_MOD,
'forum_id' => $forum_id,
'topic_id' => $topic_id,
+ 'post_id' => $post_id,
'log_data' => (!empty($additional_data)) ? serialize($additional_data) : '',
);
break;
@@ -305,10 +295,18 @@ class log implements \phpbb\log\log_interface
* @var array sql_ary Array with log data we insert into the
* database. If sql_ary[log_type] is not set,
* we won't add the entry to the database.
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
- $vars = array('mode', 'user_id', 'log_ip', 'log_operation', 'log_time', 'additional_data', 'sql_ary');
- extract($this->dispatcher->trigger_event('core.add_log', $vars));
+ $vars = array(
+ 'mode',
+ 'user_id',
+ 'log_ip',
+ 'log_operation',
+ 'log_time',
+ 'additional_data',
+ 'sql_ary',
+ );
+ extract($this->dispatcher->trigger_event('core.add_log', compact($vars)));
// We didn't find a log_type, so we don't save it in the database.
if (!isset($sql_ary['log_type']))
@@ -322,8 +320,99 @@ class log implements \phpbb\log\log_interface
}
/**
- * Grab the logs from the database
- *
+ * {@inheritDoc}
+ */
+ public function delete($mode, $conditions = array())
+ {
+ switch ($mode)
+ {
+ case 'admin':
+ $log_type = LOG_ADMIN;
+ break;
+
+ case 'mod':
+ $log_type = LOG_MOD;
+ break;
+
+ case 'user':
+ $log_type = LOG_USERS;
+ break;
+
+ case 'users':
+ $log_type = LOG_USERS;
+ break;
+
+ case 'critical':
+ $log_type = LOG_CRITICAL;
+ break;
+
+ default:
+ $log_type = false;
+ }
+
+ /**
+ * Allows to modify log data before we delete it from the database
+ *
+ * NOTE: if sql_ary does not contain a log_type value, the entry will
+ * not be deleted in the database. So ensure to set it, if needed.
+ *
+ * @event core.delete_log
+ * @var string mode Mode of the entry we log
+ * @var string log_type Type ID of the log (should be different than false)
+ * @var array conditions An array of conditions, 3 different forms are accepted
+ * 1) <key> => <value> transformed into 'AND <key> = <value>' (value should be an integer)
+ * 2) <key> => array(<operator>, <value>) transformed into 'AND <key> <operator> <value>' (values can't be an array)
+ * 3) <key> => array('IN' => array(<values>)) transformed into 'AND <key> IN <values>'
+ * A special field, keywords, can also be defined. In this case only the log entries that have the keywords in log_operation or log_data will be deleted.
+ * @since 3.1.0-b4
+ */
+ $vars = array(
+ 'mode',
+ 'log_type',
+ 'conditions',
+ );
+ extract($this->dispatcher->trigger_event('core.delete_log', compact($vars)));
+
+ if ($log_type === false)
+ {
+ return;
+ }
+
+ $sql_where = 'WHERE log_type = ' . $log_type;
+
+ if (isset($conditions['keywords']))
+ {
+ $sql_where .= $this->generate_sql_keyword($conditions['keywords'], '');
+
+ unset($conditions['keywords']);
+ }
+
+ foreach ($conditions as $field => $field_value)
+ {
+ $sql_where .= ' AND ';
+
+ if (is_array($field_value) && sizeof($field_value) == 2 && !is_array($field_value[1]))
+ {
+ $sql_where .= $field . ' ' . $field_value[0] . ' ' . $field_value[1];
+ }
+ else if (is_array($field_value) && isset($field_value['IN']) && is_array($field_value['IN']))
+ {
+ $sql_where .= $this->db->sql_in_set($field, $field_value['IN']);
+ }
+ else
+ {
+ $sql_where .= $field . ' = ' . $field_value;
+ }
+ }
+
+ $sql = 'DELETE FROM ' . LOG_TABLE . "
+ $sql_where";
+ $this->db->sql_query($sql);
+
+ $this->add('admin', $this->user->data['user_id'], $this->user->ip, 'LOG_CLEAR_' . strtoupper($mode));
+ }
+
+ /**
* {@inheritDoc}
*/
public function get_logs($mode, $count_logs = true, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $log_time = 0, $sort_by = 'l.log_time DESC', $keywords = '')
@@ -403,10 +492,24 @@ class log implements \phpbb\log\log_interface
* is false, no entries will be returned.
* @var string sql_additional Additional conditions for the entries,
* e.g.: 'AND l.forum_id = 1'
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
- $vars = array('mode', 'count_logs', 'limit', 'offset', 'forum_id', 'topic_id', 'user_id', 'log_time', 'sort_by', 'keywords', 'profile_url', 'log_type', 'sql_additional');
- extract($this->dispatcher->trigger_event('core.get_logs_modify_type', $vars));
+ $vars = array(
+ 'mode',
+ 'count_logs',
+ 'limit',
+ 'offset',
+ 'forum_id',
+ 'topic_id',
+ 'user_id',
+ 'log_time',
+ 'sort_by',
+ 'keywords',
+ 'profile_url',
+ 'log_type',
+ 'sql_additional',
+ );
+ extract($this->dispatcher->trigger_event('core.get_logs_modify_type', compact($vars)));
if ($log_type === false)
{
@@ -421,15 +524,77 @@ class log implements \phpbb\log\log_interface
$sql_keywords = $this->generate_sql_keyword($keywords);
}
- if ($count_logs)
- {
- $sql = 'SELECT COUNT(l.log_id) AS total_entries
- FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u
- WHERE l.log_type = ' . (int) $log_type . '
+ $get_logs_sql_ary = array(
+ 'SELECT' => 'l.*, u.username, u.username_clean, u.user_colour',
+ 'FROM' => array(
+ $this->log_table => 'l',
+ USERS_TABLE => 'u',
+ ),
+ 'WHERE' => 'l.log_type = ' . (int) $log_type . "
AND l.user_id = u.user_id
- AND l.log_time >= ' . (int) $log_time . "
$sql_keywords
- $sql_additional";
+ $sql_additional",
+
+ 'ORDER_BY' => $sort_by,
+ );
+
+ if ($log_time)
+ {
+ $get_logs_sql_ary['WHERE'] = 'l.log_time >= ' . (int) $log_time . '
+ AND ' . $get_logs_sql_ary['WHERE'];
+ }
+
+ /**
+ * Modify the query to obtain the logs data
+ *
+ * @event core.get_logs_main_query_before
+ * @var array get_logs_sql_ary The array in the format of the query builder with the query
+ * to get the log count and the log list
+ * @var string mode Mode of the entries we display
+ * @var bool count_logs Do we count all matching entries?
+ * @var int limit Limit the number of entries
+ * @var int offset Offset when fetching the entries
+ * @var mixed forum_id Limit entries to the forum_id,
+ * can also be an array of forum_ids
+ * @var int topic_id Limit entries to the topic_id
+ * @var int user_id Limit entries to the user_id
+ * @var int log_time Limit maximum age of log entries
+ * @var string sort_by SQL order option
+ * @var string keywords Will only return entries that have the
+ * keywords in log_operation or log_data
+ * @var string profile_url URL to the users profile
+ * @var int log_type Limit logs to a certain type. If log_type
+ * is false, no entries will be returned.
+ * @var string sql_additional Additional conditions for the entries,
+ * e.g.: 'AND l.forum_id = 1'
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'get_logs_sql_ary',
+ 'mode',
+ 'count_logs',
+ 'limit',
+ 'offset',
+ 'forum_id',
+ 'topic_id',
+ 'user_id',
+ 'log_time',
+ 'sort_by',
+ 'keywords',
+ 'profile_url',
+ 'log_type',
+ 'sql_additional',
+ );
+ extract($this->dispatcher->trigger_event('core.get_logs_main_query_before', compact($vars)));
+
+ if ($count_logs)
+ {
+ $count_logs_sql_ary = $get_logs_sql_ary;
+
+ $count_logs_sql_ary['SELECT'] = 'COUNT(l.log_id) AS total_entries';
+ unset($count_logs_sql_ary['ORDER_BY']);
+
+ $sql = $this->db->sql_build_query('SELECT', $count_logs_sql_ary);
$result = $this->db->sql_query($sql);
$this->entry_count = (int) $this->db->sql_fetchfield('total_entries');
$this->db->sql_freeresult($result);
@@ -448,14 +613,7 @@ class log implements \phpbb\log\log_interface
}
}
- $sql = 'SELECT l.*, u.username, u.username_clean, u.user_colour
- FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u
- WHERE l.log_type = ' . (int) $log_type . '
- AND u.user_id = l.user_id
- ' . (($log_time) ? 'AND l.log_time >= ' . (int) $log_time : '') . "
- $sql_keywords
- $sql_additional
- ORDER BY $sort_by";
+ $sql = $this->db->sql_build_query('SELECT', $get_logs_sql_ary);
$result = $this->db->sql_query_limit($sql, $limit, $this->last_page_offset);
$i = 0;
@@ -488,9 +646,10 @@ class log implements \phpbb\log\log_interface
'time' => (int) $row['log_time'],
'forum_id' => (int) $row['forum_id'],
'topic_id' => (int) $row['topic_id'],
+ 'post_id' => (int) $row['post_id'],
'viewforum' => ($row['forum_id'] && $this->auth->acl_get('f_read', $row['forum_id'])) ? append_sid("{$this->phpbb_root_path}viewforum.{$this->php_ext}", 'f=' . $row['forum_id']) : false,
- 'action' => (isset($this->user->lang[$row['log_operation']])) ? $this->user->lang[$row['log_operation']] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}',
+ 'action' => (isset($this->user->lang[$row['log_operation']])) ? $row['log_operation'] : '{' . ucfirst(str_replace('_', ' ', $row['log_operation'])) . '}',
);
/**
@@ -499,10 +658,10 @@ class log implements \phpbb\log\log_interface
* @event core.get_logs_modify_entry_data
* @var array row Entry data from the database
* @var array log_entry_data Entry's data which is returned
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
$vars = array('row', 'log_entry_data');
- extract($this->dispatcher->trigger_event('core.get_logs_modify_entry_data', $vars));
+ extract($this->dispatcher->trigger_event('core.get_logs_modify_entry_data', compact($vars)));
$log[$i] = $log_entry_data;
@@ -517,12 +676,26 @@ class log implements \phpbb\log\log_interface
// arguments, if there are we fill out the arguments
// array. It doesn't matter if we add more arguments than
// placeholders.
- if ((substr_count($log[$i]['action'], '%') - sizeof($log_data_ary)) > 0)
+ $num_args = 0;
+ if (!is_array($this->user->lang[$row['log_operation']]))
{
- $log_data_ary = array_merge($log_data_ary, array_fill(0, substr_count($log[$i]['action'], '%') - sizeof($log_data_ary), ''));
+ $num_args = substr_count($this->user->lang[$row['log_operation']], '%');
+ }
+ else
+ {
+ foreach ($this->user->lang[$row['log_operation']] as $case => $plural_string)
+ {
+ $num_args = max($num_args, substr_count($plural_string, '%'));
+ }
}
- $log[$i]['action'] = vsprintf($log[$i]['action'], $log_data_ary);
+ if (($num_args - sizeof($log_data_ary)) > 0)
+ {
+ $log_data_ary = array_merge($log_data_ary, array_fill(0, $num_args - sizeof($log_data_ary), ''));
+ }
+
+ $lang_arguments = array_merge(array($log[$i]['action']), $log_data_ary);
+ $log[$i]['action'] = call_user_func_array(array($this->user, 'lang'), $lang_arguments);
// If within the admin panel we do not censor text out
if ($this->get_is_admin())
@@ -544,6 +717,10 @@ class log implements \phpbb\log\log_interface
$log[$i]['action'] = make_clickable($log[$i]['action']);
*/
}
+ else
+ {
+ $log[$i]['action'] = $this->user->lang($log[$i]['action']);
+ }
$i++;
}
@@ -558,10 +735,10 @@ class log implements \phpbb\log\log_interface
* get the permission data
* @var array reportee_id_list Array of additional user IDs we
* get the username strings for
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
$vars = array('log', 'topic_id_list', 'reportee_id_list');
- extract($this->dispatcher->trigger_event('core.get_logs_get_additional_data', $vars));
+ extract($this->dispatcher->trigger_event('core.get_logs_get_additional_data', compact($vars)));
if (sizeof($topic_id_list))
{
@@ -570,6 +747,7 @@ class log implements \phpbb\log\log_interface
foreach ($log as $key => $row)
{
$log[$key]['viewtopic'] = (isset($topic_auth['f_read'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&amp;t=' . $row['topic_id']) : false;
+ $log[$key]['viewpost'] = (isset($topic_auth['f_read'][$row['topic_id']]) && $row['post_id']) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&amp;t=' . $row['topic_id'] . '&amp;p=' . $row['post_id']) : false;
$log[$key]['viewlogs'] = (isset($topic_auth['m_'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}mcp.{$this->php_ext}", 'i=logs&amp;mode=topic_logs&amp;t=' . $row['topic_id'], true, $this->user->session_id) : false;
}
}
@@ -590,17 +768,63 @@ class log implements \phpbb\log\log_interface
}
}
+ /**
+ * Allow modifying or execute extra final filter on log entries
+ *
+ * @event core.get_logs_after
+ * @var array log Array with all our log entries
+ * @var array topic_id_list Array of topic ids, for which we
+ * get the permission data
+ * @var array reportee_id_list Array of additional user IDs we
+ * get the username strings for
+ * @var string mode Mode of the entries we display
+ * @var bool count_logs Do we count all matching entries?
+ * @var int limit Limit the number of entries
+ * @var int offset Offset when fetching the entries
+ * @var mixed forum_id Limit entries to the forum_id,
+ * can also be an array of forum_ids
+ * @var int topic_id Limit entries to the topic_id
+ * @var int user_id Limit entries to the user_id
+ * @var int log_time Limit maximum age of log entries
+ * @var string sort_by SQL order option
+ * @var string keywords Will only return entries that have the
+ * keywords in log_operation or log_data
+ * @var string profile_url URL to the users profile
+ * @var int log_type The type of logs it was filtered
+ * @since 3.1.3-RC1
+ */
+ $vars = array(
+ 'log',
+ 'topic_id_list',
+ 'reportee_id_list',
+ 'mode',
+ 'count_logs',
+ 'limit',
+ 'offset',
+ 'forum_id',
+ 'topic_id',
+ 'user_id',
+ 'log_time',
+ 'sort_by',
+ 'keywords',
+ 'profile_url',
+ 'log_type',
+ );
+ extract($this->dispatcher->trigger_event('core.get_logs_after', compact($vars)));
+
return $log;
}
/**
* Generates a sql condition for the specified keywords
*
- * @param string $keywords The keywords the user specified to search for
+ * @param string $keywords The keywords the user specified to search for
+ * @param string $table_alias The alias of the logs' table ('l.' by default)
+ * @param string $statement_operator The operator used to prefix the statement ('AND' by default)
*
* @return string Returns the SQL condition searching for the keywords
*/
- protected function generate_sql_keyword($keywords)
+ protected function generate_sql_keyword($keywords, $table_alias = 'l.', $statement_operator = 'AND')
{
// Use no preg_quote for $keywords because this would lead to sole
// backslashes being added. We also use an OR connection here for
@@ -617,7 +841,7 @@ class log implements \phpbb\log\log_interface
for ($i = 0, $num_keywords = sizeof($keywords); $i < $num_keywords; $i++)
{
$keywords_pattern[] = preg_quote($keywords[$i], '#');
- $keywords[$i] = $this->db->sql_like_expression($this->db->any_char . $keywords[$i] . $this->db->any_char);
+ $keywords[$i] = $this->db->sql_like_expression($this->db->get_any_char() . $keywords[$i] . $this->db->get_any_char());
}
$keywords_pattern = '#' . implode('|', $keywords_pattern) . '#ui';
@@ -625,18 +849,32 @@ class log implements \phpbb\log\log_interface
$operations = array();
foreach ($this->user->lang as $key => $value)
{
- if (substr($key, 0, 4) == 'LOG_' && preg_match($keywords_pattern, $value))
+ if (substr($key, 0, 4) == 'LOG_')
{
- $operations[] = $key;
+ if (is_array($value))
+ {
+ foreach ($value as $plural_value)
+ {
+ if (preg_match($keywords_pattern, $plural_value))
+ {
+ $operations[] = $key;
+ break;
+ }
+ }
+ }
+ else if (preg_match($keywords_pattern, $value))
+ {
+ $operations[] = $key;
+ }
}
}
- $sql_keywords = 'AND (';
+ $sql_keywords = ' ' . $statement_operator . ' (';
if (!empty($operations))
{
- $sql_keywords .= $this->db->sql_in_set('l.log_operation', $operations) . ' OR ';
+ $sql_keywords .= $this->db->sql_in_set($table_alias . 'log_operation', $operations) . ' OR ';
}
- $sql_lower = $this->db->sql_lower_text('l.log_data');
+ $sql_lower = $this->db->sql_lower_text($table_alias . 'log_data');
$sql_keywords .= " $sql_lower " . implode(" OR $sql_lower ", $keywords) . ')';
}
@@ -712,8 +950,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * Get total log count
- *
* {@inheritDoc}
*/
public function get_log_count()
@@ -722,8 +958,6 @@ class log implements \phpbb\log\log_interface
}
/**
- * Get offset of the last valid log page
- *
* {@inheritDoc}
*/
public function get_valid_offset()
diff --git a/phpBB/phpbb/log/log_interface.php b/phpBB/phpbb/log/log_interface.php
index 420ba79691..86286e6f88 100644
--- a/phpBB/phpbb/log/log_interface.php
+++ b/phpBB/phpbb/log/log_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\log\log
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\log;
/**
* The interface for the log-system.
-*
-* @package \phpbb\log\log
*/
interface log_interface
{
@@ -30,8 +32,8 @@ interface log_interface
* Disable log
*
* This function allows disabling the log system or parts of it, for this
- * page call. When add_log is called and the type is disabled,
- * the log will not be added to the database.
+ * page call. When add() is called and the type is disabled, the log will
+ * not be added to the database.
*
* @param mixed $type The log type we want to disable. Empty to
* disable all logs. Can also be an array of types.
@@ -55,18 +57,30 @@ interface log_interface
/**
* Adds a log entry to the database
*
- * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved
- * @param int $user_id User ID of the user
- * @param string $log_ip IP address of the user
- * @param string $log_operation Name of the operation
- * @param int $log_time Timestamp when the log entry was added, if empty time() will be used
- * @param array $additional_data More arguments can be added, depending on the log_type
+ * @param string $mode The mode defines which log_type is used and from which log the entry is retrieved
+ * @param int $user_id User ID of the user
+ * @param string $log_ip IP address of the user
+ * @param string $log_operation Name of the operation
+ * @param int|bool $log_time Timestamp when the log entry was added. If false, time() will be used
+ * @param array $additional_data More arguments can be added, depending on the log_type
*
* @return int|bool Returns the log_id, if the entry was added to the database, false otherwise.
*/
public function add($mode, $user_id, $log_ip, $log_operation, $log_time = false, $additional_data = array());
/**
+ * Delete entries in the logs
+ *
+ * @param string $mode The mode defines which log_type is used and from which log the entries are deleted
+ * @param array $conditions An array of conditions, 3 different forms are accepted
+ * 1) <key> => <value> transformed into 'AND <key> = <value>' (value should be an integer)
+ * 2) <key> => array(<operator>, <value>) transformed into 'AND <key> <operator> <value>' (values can't be an array)
+ * 3) <key> => array('IN' => array(<values>)) transformed into 'AND <key> IN <values>'
+ * A special field, keywords, can also be defined. In this case only the log entries that have the keywords in log_operation or log_data will be deleted.
+ */
+ public function delete($mode, $conditions = array());
+
+ /**
* Grab the logs from the database
*
* @param string $mode The mode defines which log_type is used and ifrom which log the entry is retrieved
diff --git a/phpBB/phpbb/message/admin_form.php b/phpBB/phpbb/message/admin_form.php
new file mode 100644
index 0000000000..96b8d3499e
--- /dev/null
+++ b/phpBB/phpbb/message/admin_form.php
@@ -0,0 +1,192 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\message;
+
+/**
+* Class admin_form
+* Displays a message to the user and allows him to send an email
+*/
+class admin_form extends form
+{
+ /** @var \phpbb\config\db_text */
+ protected $config_text;
+
+ /** @var string */
+ protected $subject;
+ /** @var string */
+ protected $sender_name;
+ /** @var string */
+ protected $sender_address;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\auth\auth $auth
+ * @param \phpbb\config\config $config
+ * @param \phpbb\config\db_text $config_text
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\user $user
+ * @param string $phpbb_root_path
+ * @param string $phpEx
+ */
+ public function __construct(\phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\config\db_text $config_text, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, $phpbb_root_path, $phpEx)
+ {
+ parent::__construct($auth, $config, $db, $user, $phpbb_root_path, $phpEx);
+ $this->config_text = $config_text;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function check_allow()
+ {
+ $error = parent::check_allow();
+ if ($error)
+ {
+ return $error;
+ }
+
+ if (!$this->config['contact_admin_form_enable'])
+ {
+ return 'NO_CONTACT_PAGE';
+ }
+
+ return false;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function bind(\phpbb\request\request_interface $request)
+ {
+ parent::bind($request);
+
+ $this->subject = $request->variable('subject', '', true);
+ $this->sender_address = $request->variable('email', '');
+ $this->sender_name = $request->variable('name', '', true);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function submit(\messenger $messenger)
+ {
+ if (!$this->subject)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_SUBJECT_EMAIL'];
+ }
+ if (!$this->body)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_MESSAGE_EMAIL'];
+ }
+
+ if ($this->user->data['is_registered'])
+ {
+ $this->message->set_sender_from_user($this->user);
+ $this->sender_name = $this->user->data['username'];
+ $this->sender_address = $this->user->data['user_email'];
+ }
+ else
+ {
+ if (!$this->sender_name)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_SENDER_NAME'];
+ }
+
+ if (!function_exists('validate_data'))
+ {
+ require($this->phpbb_root_path . 'includes/functions_user.' . $this->phpEx);
+ }
+
+ $validate_array = validate_data(
+ array(
+ 'email' => $this->sender_address,
+ ),
+ array(
+ 'email' => array(
+ array('string', false, 6, 60),
+ array('email'),
+ ),
+ )
+ );
+
+ foreach ($validate_array as $error)
+ {
+ $this->errors[] = $this->user->lang[$error];
+ }
+
+ $this->message->set_sender($this->user->ip, $this->sender_name, $this->sender_address, $this->user->lang_name);
+ $this->message->set_sender_notify_type(NOTIFY_EMAIL);
+ }
+
+ $this->message->set_template('contact_admin');
+ $this->message->set_subject($this->subject);
+ $this->message->set_body($this->body);
+ $this->message->add_recipient(
+ $this->user->lang['ADMINISTRATOR'],
+ $this->config['board_contact'],
+ $this->config['default_lang'],
+ NOTIFY_EMAIL
+ );
+
+ $this->message->set_template_vars(array(
+ 'FROM_EMAIL_ADDRESS' => $this->sender_address,
+ 'FROM_IP_ADDRESS' => $this->user->ip,
+ 'S_IS_REGISTERED' => $this->user->data['is_registered'],
+
+ 'U_FROM_PROFILE' => generate_board_url() . '/memberlist.' . $this->phpEx . '?mode=viewprofile&u=' . $this->user->data['user_id'],
+ ));
+
+ parent::submit($messenger);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function render(\phpbb\template\template $template)
+ {
+ $l_admin_info = $this->config_text->get('contact_admin_info');
+ if ($l_admin_info)
+ {
+ $contact_admin_data = $this->config_text->get_array(array(
+ 'contact_admin_info',
+ 'contact_admin_info_uid',
+ 'contact_admin_info_bitfield',
+ 'contact_admin_info_flags',
+ ));
+
+ $l_admin_info = generate_text_for_display(
+ $contact_admin_data['contact_admin_info'],
+ $contact_admin_data['contact_admin_info_uid'],
+ $contact_admin_data['contact_admin_info_bitfield'],
+ $contact_admin_data['contact_admin_info_flags']
+ );
+ }
+
+ $template->assign_vars(array(
+ 'S_CONTACT_ADMIN' => true,
+ 'S_CONTACT_FORM' => $this->config['contact_admin_form_enable'],
+ 'S_IS_REGISTERED' => $this->user->data['is_registered'],
+ 'S_POST_ACTION' => append_sid($this->phpbb_root_path . 'memberlist.' . $this->phpEx, 'mode=contactadmin'),
+
+ 'CONTACT_INFO' => $l_admin_info,
+ 'MESSAGE' => $this->body,
+ 'SUBJECT' => $this->subject,
+ 'NAME' => $this->sender_name,
+ 'EMAIL' => $this->sender_address,
+ ));
+
+ parent::render($template);
+ }
+}
diff --git a/phpBB/phpbb/message/form.php b/phpBB/phpbb/message/form.php
new file mode 100644
index 0000000000..21d4de0b4d
--- /dev/null
+++ b/phpBB/phpbb/message/form.php
@@ -0,0 +1,175 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\message;
+
+/**
+* Abstract class form
+*/
+abstract class form
+{
+ /** @var \phpbb\auth\auth */
+ protected $auth;
+ /** @var \phpbb\config\config */
+ protected $config;
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+ /** @var \phpbb\message\message */
+ protected $message;
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var string */
+ protected $phpbb_root_path;
+ /** @var string */
+ protected $phpEx;
+
+ /** @var array */
+ protected $errors = array();
+ /** @var bool */
+ protected $cc_sender;
+ /** @var string */
+ protected $body;
+
+ /**
+ * Construct
+ *
+ * @param \phpbb\auth\auth $auth
+ * @param \phpbb\config\config $config
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\user $user
+ * @param string $phpbb_root_path
+ * @param string $phpEx
+ */
+ public function __construct(\phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, $phpbb_root_path, $phpEx)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->phpEx = $phpEx;
+ $this->user = $user;
+ $this->auth = $auth;
+ $this->config = $config;
+ $this->db = $db;
+
+ $this->message = new message($config['server_name']);
+ $this->message->set_sender_from_user($this->user);
+ }
+
+ /**
+ * Returns the title for the email form page
+ *
+ * @return string
+ */
+ public function get_page_title()
+ {
+ return $this->user->lang['SEND_EMAIL'];
+ }
+
+ /**
+ * Returns the file name of the form template
+ *
+ * @return string
+ */
+ public function get_template_file()
+ {
+ return 'memberlist_email.html';
+ }
+
+ /**
+ * Checks whether the user is allowed to use the form
+ *
+ * @return false|string Error string if not allowed, false otherwise
+ */
+ public function check_allow()
+ {
+ if (!$this->config['email_enable'])
+ {
+ return 'EMAIL_DISABLED';
+ }
+
+ if (time() - $this->user->data['user_emailtime'] < $this->config['flood_interval'])
+ {
+ return 'FLOOD_EMAIL_LIMIT';
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the return link after the message has been sent
+ *
+ * @return string
+ */
+ public function get_return_message()
+ {
+ return sprintf($this->user->lang['RETURN_INDEX'], '<a href="' . append_sid($this->phpbb_root_path . 'index.' . $this->phpEx) . '">', '</a>');
+ }
+
+ /**
+ * Bind the values of the request to the form
+ *
+ * @param \phpbb\request\request_interface $request
+ * @return null
+ */
+ public function bind(\phpbb\request\request_interface $request)
+ {
+ $this->cc_sender = $request->is_set_post('cc_sender');
+ $this->body = $request->variable('message', '', true);
+ }
+
+ /**
+ * Submit form, generate the email and send it
+ *
+ * @param \messenger $messenger
+ * @return null
+ */
+ public function submit(\messenger $messenger)
+ {
+ if (!check_form_key('memberlist_email'))
+ {
+ $this->errors[] = 'FORM_INVALID';
+ }
+
+ if (!sizeof($this->errors))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_emailtime = ' . time() . '
+ WHERE user_id = ' . $this->user->data['user_id'];
+ $this->db->sql_query($sql);
+
+ if ($this->cc_sender && $this->user->data['is_registered'])
+ {
+ $this->message->cc_sender();
+ }
+
+ $this->message->send($messenger, phpbb_get_board_contact($this->config, $this->phpEx));
+
+ meta_refresh(3, append_sid($this->phpbb_root_path . 'index.' . $this->phpEx));
+ trigger_error($this->user->lang['EMAIL_SENT'] . '<br /><br />' . $this->get_return_message());
+ }
+ }
+
+ /**
+ * Render the template of the form
+ *
+ * @param \phpbb\template\template $template
+ * @return null
+ */
+ public function render(\phpbb\template\template $template)
+ {
+ add_form_key('memberlist_email');
+
+ $template->assign_vars(array(
+ 'ERROR_MESSAGE' => (sizeof($this->errors)) ? implode('<br />', $this->errors) : '',
+ ));
+ }
+}
diff --git a/phpBB/phpbb/message/message.php b/phpBB/phpbb/message/message.php
new file mode 100644
index 0000000000..5fd24b542e
--- /dev/null
+++ b/phpBB/phpbb/message/message.php
@@ -0,0 +1,282 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\message;
+
+/**
+* Class message
+* Holds all information for an email and sends it in the end
+*/
+class message
+{
+ /** @var string */
+ protected $server_name;
+
+ /** @var string */
+ protected $subject = '';
+ /** @var string */
+ protected $body = '';
+ /** @var string */
+ protected $template = '';
+ /** @var array */
+ protected $template_vars = array();
+
+ /** @var string */
+ protected $sender_ip = '';
+ /** @var string */
+ protected $sender_name = '';
+ /** @var string */
+ protected $sender_address = '';
+ /** @var string */
+ protected $sender_lang = '';
+ /** @var string */
+ protected $sender_id = '';
+ /** @var string */
+ protected $sender_username = '';
+ /** @var string */
+ protected $sender_jabber = '';
+ /** @var int */
+ protected $sender_notify_type = NOTIFY_EMAIL;
+
+ /** @var array */
+ protected $recipients;
+
+ /**
+ * Construct
+ *
+ * @param string $server_name Used for AntiAbuse header
+ */
+ public function __construct($server_name)
+ {
+ $this->server_name = $server_name;
+ }
+
+ /**
+ * Set the subject of the email
+ *
+ * @param string $subject
+ * @return null
+ */
+ public function set_subject($subject)
+ {
+ $this->subject = $subject;
+ }
+
+ /**
+ * Set the body of the email text
+ *
+ * @param string $body
+ * @return null
+ */
+ public function set_body($body)
+ {
+ $this->body = $body;
+ }
+
+ /**
+ * Set the name of the email template to use
+ *
+ * @param string $template
+ * @return null
+ */
+ public function set_template($template)
+ {
+ $this->template = $template;
+ }
+
+ /**
+ * Set the array with the "template" data for the email
+ *
+ * @param array $template_vars
+ * @return null
+ */
+ public function set_template_vars($template_vars)
+ {
+ $this->template_vars = $template_vars;
+ }
+
+ /**
+ * Add a recipient from \phpbb\user
+ *
+ * @param array $user
+ * @return null
+ */
+ public function add_recipient_from_user_row(array $user)
+ {
+ $this->add_recipient(
+ $user['username'],
+ $user['user_email'],
+ $user['user_lang'],
+ $user['user_notify_type'],
+ $user['username'],
+ $user['user_jabber']
+ );
+ }
+
+ /**
+ * Add a recipient
+ *
+ * @param string $recipient_name Displayed sender name
+ * @param string $recipient_address Email address
+ * @param string $recipient_lang
+ * @param int $recipient_notify_type Used notification methods (Jabber, Email, ...)
+ * @param string $recipient_username User Name (used for AntiAbuse header)
+ * @param string $recipient_jabber
+ * @return null
+ */
+ public function add_recipient($recipient_name, $recipient_address, $recipient_lang, $recipient_notify_type = NOTIFY_EMAIL, $recipient_username = '', $recipient_jabber = '')
+ {
+ $this->recipients[] = array(
+ 'name' => $recipient_name,
+ 'address' => $recipient_address,
+ 'lang' => $recipient_lang,
+ 'username' => $recipient_username,
+ 'jabber' => $recipient_jabber,
+ 'notify_type' => $recipient_notify_type,
+ 'to_name' => $recipient_name,
+ );
+ }
+
+ /**
+ * Set the senders data from \phpbb\user object
+ *
+ * @param \phpbb\user $user
+ * @return null
+ */
+ public function set_sender_from_user($user)
+ {
+ $this->set_sender(
+ $user->ip,
+ $user->data['username'],
+ $user->data['user_email'],
+ $user->lang_name,
+ $user->data['user_id'],
+ $user->data['username'],
+ $user->data['user_jabber']
+ );
+
+ $this->set_sender_notify_type($user->data['user_notify_type']);
+ }
+
+ /**
+ * Set the senders data
+ *
+ * @param string $sender_ip
+ * @param string $sender_name Displayed sender name
+ * @param string $sender_address Email address
+ * @param string $sender_lang
+ * @param int $sender_id User ID
+ * @param string $sender_username User Name (used for AntiAbuse header)
+ * @param string $sender_jabber
+ * @return null
+ */
+ public function set_sender($sender_ip, $sender_name, $sender_address, $sender_lang = '', $sender_id = 0, $sender_username = '', $sender_jabber = '')
+ {
+ $this->sender_ip = $sender_ip;
+ $this->sender_name = $sender_name;
+ $this->sender_address = $sender_address;
+ $this->sender_lang = $sender_lang;
+ $this->sender_id = $sender_id;
+ $this->sender_username = $sender_username;
+ $this->sender_jabber = $sender_jabber;
+ }
+
+ /**
+ * Which notification type should be used? Jabber, Email, ...?
+ *
+ * @param int $sender_notify_type
+ * @return null
+ */
+ public function set_sender_notify_type($sender_notify_type)
+ {
+ $this->sender_notify_type = $sender_notify_type;
+ }
+
+ /**
+ * Ok, now the same email if CC specified, but without exposing the user's email address
+ *
+ * @return null
+ */
+ public function cc_sender()
+ {
+ if (!sizeof($this->recipients))
+ {
+ trigger_error('No email recipients specified');
+ }
+ if (!$this->sender_address)
+ {
+ trigger_error('No email sender specified');
+ }
+
+ $this->recipients[] = array(
+ 'lang' => $this->sender_lang,
+ 'address' => $this->sender_address,
+ 'name' => $this->sender_name,
+ 'username' => $this->sender_username,
+ 'jabber' => $this->sender_jabber,
+ 'notify_type' => $this->sender_notify_type,
+ 'to_name' => $this->recipients[0]['to_name'],
+ );
+ }
+
+ /**
+ * Send the email
+ *
+ * @param \messenger $messenger
+ * @param string $contact
+ * @return null
+ */
+ public function send(\messenger $messenger, $contact)
+ {
+ if (!sizeof($this->recipients))
+ {
+ return;
+ }
+
+ foreach ($this->recipients as $recipient)
+ {
+ $messenger->template($this->template, $recipient['lang']);
+ $messenger->replyto($this->sender_address);
+ $messenger->to($recipient['address'], $recipient['name']);
+ $messenger->im($recipient['jabber'], $recipient['username']);
+
+ $messenger->headers('X-AntiAbuse: Board servername - ' . $this->server_name);
+ $messenger->headers('X-AntiAbuse: User IP - ' . $this->sender_ip);
+
+ if ($this->sender_id)
+ {
+ $messenger->headers('X-AntiAbuse: User_id - ' . $this->sender_id);
+ }
+ if ($this->sender_username)
+ {
+ $messenger->headers('X-AntiAbuse: Username - ' . $this->sender_username);
+ }
+
+ $messenger->subject(htmlspecialchars_decode($this->subject));
+
+ $messenger->assign_vars(array(
+ 'BOARD_CONTACT' => $contact,
+ 'TO_USERNAME' => htmlspecialchars_decode($recipient['to_name']),
+ 'FROM_USERNAME' => htmlspecialchars_decode($this->sender_name),
+ 'MESSAGE' => htmlspecialchars_decode($this->body))
+ );
+
+ if (sizeof($this->template_vars))
+ {
+ $messenger->assign_vars($this->template_vars);
+ }
+
+ $messenger->send($recipient['notify_type']);
+ }
+ }
+}
diff --git a/phpBB/phpbb/message/topic_form.php b/phpBB/phpbb/message/topic_form.php
new file mode 100644
index 0000000000..174643bb81
--- /dev/null
+++ b/phpBB/phpbb/message/topic_form.php
@@ -0,0 +1,158 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\message;
+
+/**
+* Class topic_form
+* Form used to send topics as notification emails
+*/
+class topic_form extends form
+{
+ /** @var int */
+ protected $topic_id;
+ /** @var array */
+ protected $topic_row;
+ /** @var string */
+ protected $recipient_address;
+ /** @var string */
+ protected $recipient_name;
+ /** @var string */
+ protected $recipient_lang;
+
+ /**
+ * Get the data of the topic
+ *
+ * @param int $topic_id
+ * @return false|array false if the topic does not exist, array otherwise
+ */
+ protected function get_topic_row($topic_id)
+ {
+ $sql = 'SELECT forum_id, topic_title
+ FROM ' . TOPICS_TABLE . '
+ WHERE topic_id = ' . (int) $topic_id;
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ return $row;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function check_allow()
+ {
+ $error = parent::check_allow();
+ if ($error)
+ {
+ return $error;
+ }
+
+ if (!$this->auth->acl_get('u_sendemail'))
+ {
+ return 'NO_EMAIL';
+ }
+
+ if (!$this->topic_row)
+ {
+ return 'NO_TOPIC';
+ }
+
+ if (!$this->auth->acl_get('f_read', $this->topic_row['forum_id']))
+ {
+ return 'SORRY_AUTH_READ';
+ }
+
+ if (!$this->auth->acl_get('f_email', $this->topic_row['forum_id']))
+ {
+ return 'NO_EMAIL';
+ }
+
+ return false;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function bind(\phpbb\request\request_interface $request)
+ {
+ parent::bind($request);
+
+ $this->topic_id = $request->variable('t', 0);
+ $this->recipient_address = $request->variable('email', '');
+ $this->recipient_name = $request->variable('name', '', true);
+ $this->recipient_lang = $request->variable('lang', $this->config['default_lang']);
+
+ $this->topic_row = $this->get_topic_row($this->topic_id);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function submit(\messenger $messenger)
+ {
+ if (!$this->recipient_address || !preg_match('/^' . get_preg_expression('email') . '$/i', $this->recipient_address))
+ {
+ $this->errors[] = $this->user->lang['EMPTY_ADDRESS_EMAIL'];
+ }
+
+ if (!$this->recipient_name)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_NAME_EMAIL'];
+ }
+
+ $this->message->set_template('email_notify');
+ $this->message->set_template_vars(array(
+ 'TOPIC_NAME' => htmlspecialchars_decode($this->topic_row['topic_title']),
+ 'U_TOPIC' => generate_board_url() . '/viewtopic.' . $this->phpEx . '?f=' . $this->topic_row['forum_id'] . '&t=' . $this->topic_id,
+ ));
+ $this->message->set_body($this->body);
+ $this->message->add_recipient(
+ $this->recipient_name,
+ $this->recipient_address,
+ $this->recipient_lang,
+ NOTIFY_EMAIL
+ );
+ $this->message->set_sender_notify_type(NOTIFY_EMAIL);
+
+ parent::submit($messenger);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function get_return_message()
+ {
+ return sprintf($this->user->lang['RETURN_TOPIC'], '<a href="' . append_sid($this->phpbb_root_path . 'viewtopic.' . $this->phpEx, 'f=' . $this->topic_row['forum_id'] . '&amp;t=' . $this->topic_id) . '">', '</a>');
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function render(\phpbb\template\template $template)
+ {
+ parent::render($template);
+
+ $this->user->add_lang('viewtopic');
+ $template->assign_vars(array(
+ 'EMAIL' => $this->recipient_address,
+ 'NAME' => $this->recipient_name,
+ 'S_LANG_OPTIONS' => language_select($this->recipient_lang),
+ 'MESSAGE' => $this->body,
+
+ 'L_EMAIL_BODY_EXPLAIN' => $this->user->lang['EMAIL_TOPIC_EXPLAIN'],
+ 'S_POST_ACTION' => append_sid($this->phpbb_root_path . 'memberlist.' . $this->phpEx, 'mode=email&amp;t=' . $this->topic_id))
+ );
+ }
+}
diff --git a/phpBB/phpbb/message/user_form.php b/phpBB/phpbb/message/user_form.php
new file mode 100644
index 0000000000..007e575407
--- /dev/null
+++ b/phpBB/phpbb/message/user_form.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\message;
+
+/**
+* Class user_form
+* Allows users to send emails to other users
+*/
+class user_form extends form
+{
+ /** @var int */
+ protected $recipient_id;
+ /** @var array */
+ protected $recipient_row;
+ /** @var string */
+ protected $subject;
+
+ /**
+ * Get the data of the recipient
+ *
+ * @param int $user_id
+ * @return false|array false if the user does not exist, array otherwise
+ */
+ protected function get_user_row($user_id)
+ {
+ $sql = 'SELECT user_id, username, user_colour, user_email, user_allow_viewemail, user_lang, user_jabber, user_notify_type
+ FROM ' . USERS_TABLE . '
+ WHERE user_id = ' . (int) $user_id . '
+ AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ return $row;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function check_allow()
+ {
+ $error = parent::check_allow();
+ if ($error)
+ {
+ return $error;
+ }
+
+ if (!$this->auth->acl_get('u_sendemail'))
+ {
+ return 'NO_EMAIL';
+ }
+
+ if ($this->recipient_id == ANONYMOUS || !$this->config['board_email_form'])
+ {
+ return 'NO_EMAIL';
+ }
+
+ if (!$this->recipient_row)
+ {
+ return 'NO_USER';
+ }
+
+ // Can we send email to this user?
+ if (!$this->recipient_row['user_allow_viewemail'] && !$this->auth->acl_get('a_user'))
+ {
+ return 'NO_EMAIL';
+ }
+
+ return false;
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function bind(\phpbb\request\request_interface $request)
+ {
+ parent::bind($request);
+
+ $this->recipient_id = $request->variable('u', 0);
+ $this->subject = $request->variable('subject', '', true);
+
+ $this->recipient_row = $this->get_user_row($this->recipient_id);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function submit(\messenger $messenger)
+ {
+ if (!$this->subject)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_SUBJECT_EMAIL'];
+ }
+
+ if (!$this->body)
+ {
+ $this->errors[] = $this->user->lang['EMPTY_MESSAGE_EMAIL'];
+ }
+
+ $this->message->set_template('profile_send_email');
+ $this->message->set_subject($this->subject);
+ $this->message->set_body($this->body);
+ $this->message->add_recipient_from_user_row($this->recipient_row);
+
+ parent::submit($messenger);
+ }
+
+ /**
+ * {inheritDoc}
+ */
+ public function render(\phpbb\template\template $template)
+ {
+ parent::render($template);
+
+ $template->assign_vars(array(
+ 'S_SEND_USER' => true,
+ 'S_POST_ACTION' => append_sid($this->phpbb_root_path . 'memberlist.' . $this->phpEx, 'mode=email&amp;u=' . $this->recipient_id),
+
+ 'L_SEND_EMAIL_USER' => $this->user->lang('SEND_EMAIL_USER', $this->recipient_row['username']),
+ 'USERNAME_FULL' => get_username_string('full', $this->recipient_row['user_id'], $this->recipient_row['username'], $this->recipient_row['user_colour']),
+ 'SUBJECT' => $this->subject,
+ 'MESSAGE' => $this->body,
+ ));
+ }
+}
diff --git a/phpBB/phpbb/mimetype/content_guesser.php b/phpBB/phpbb/mimetype/content_guesser.php
index 2d74582a21..f3ad7f5f41 100644
--- a/phpBB/phpbb/mimetype/content_guesser.php
+++ b/phpBB/phpbb/mimetype/content_guesser.php
@@ -1,30 +1,30 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\mimetype;
-/**
-* @package mimetype
-*/
-
class content_guesser extends guesser_base
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function is_supported()
{
- return function_exists('mime_content_type');
+ return function_exists('mime_content_type') && is_callable('mime_content_type');
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function guess($file, $file_name = '')
{
diff --git a/phpBB/phpbb/mimetype/extension_guesser.php b/phpBB/phpbb/mimetype/extension_guesser.php
index f6f4ae0138..9e36c07f91 100644
--- a/phpBB/phpbb/mimetype/extension_guesser.php
+++ b/phpBB/phpbb/mimetype/extension_guesser.php
@@ -1,18 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\mimetype;
-/**
-* @package mimetype
-*/
-
class extension_guesser extends guesser_base
{
/**
@@ -470,7 +470,7 @@ class extension_guesser extends guesser_base
);
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function is_supported()
{
@@ -478,7 +478,7 @@ class extension_guesser extends guesser_base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function guess($file, $file_name = '')
{
diff --git a/phpBB/phpbb/mimetype/guesser.php b/phpBB/phpbb/mimetype/guesser.php
index 3499b3b0f7..8baa77089b 100644
--- a/phpBB/phpbb/mimetype/guesser.php
+++ b/phpBB/phpbb/mimetype/guesser.php
@@ -1,18 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\mimetype;
-/**
-* @package mimetype
-*/
-
class guesser
{
/**
@@ -21,7 +21,7 @@ class guesser
const PRIORITY_DEFAULT = 0;
/**
- * @var mimetype guessers
+ * @var array guessers
*/
protected $guessers;
@@ -99,6 +99,7 @@ class guesser
* Guess mimetype of supplied file
*
* @param string $file Path to file
+ * @param string $file_name The real file name
*
* @return string Guess for mimetype of file
*/
@@ -114,17 +115,42 @@ class guesser
return false;
}
+ $mimetype = 'application/octet-stream';
+
foreach ($this->guessers as $guesser)
{
- $mimetype = $guesser->guess($file, $file_name);
+ $mimetype_guess = $guesser->guess($file, $file_name);
- // Try to guess something that is not the fallback application/octet-stream
- if ($mimetype !== null && $mimetype !== 'application/octet-stream')
- {
- return $mimetype;
- }
+ $mimetype = $this->choose_mime_type($mimetype, $mimetype_guess);
}
// Return any mimetype if we got a result or the fallback value
- return (!empty($mimetype)) ? $mimetype : 'application/octet-stream';
+ return $mimetype;
+ }
+
+ /**
+ * Choose the best mime type based on the current mime type and the guess
+ * If a guesser returns nulls or application/octet-stream, we will keep
+ * the current guess. Guesses with a slash inside them will be favored over
+ * already existing ones. However, any guess that will pass the first check
+ * will always overwrite the default application/octet-stream.
+ *
+ * @param string $mime_type The current mime type
+ * @param string $guess The current mime type guess
+ *
+ * @return string The best mime type based on current mime type and guess
+ */
+ public function choose_mime_type($mime_type, $guess)
+ {
+ if ($guess === null || $guess == 'application/octet-stream')
+ {
+ return $mime_type;
+ }
+
+ if ($mime_type == 'application/octet-stream' || strpos($guess, '/') !== false)
+ {
+ $mime_type = $guess;
+ }
+
+ return $mime_type;
}
}
diff --git a/phpBB/phpbb/mimetype/guesser_base.php b/phpBB/phpbb/mimetype/guesser_base.php
index 082b098028..225dfd57dc 100644
--- a/phpBB/phpbb/mimetype/guesser_base.php
+++ b/phpBB/phpbb/mimetype/guesser_base.php
@@ -1,18 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\mimetype;
-/**
-* @package mimetype
-*/
-
abstract class guesser_base implements guesser_interface
{
/**
@@ -21,7 +21,7 @@ abstract class guesser_base implements guesser_interface
protected $priority;
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_priority()
{
@@ -29,7 +29,7 @@ abstract class guesser_base implements guesser_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function set_priority($priority)
{
diff --git a/phpBB/phpbb/mimetype/guesser_interface.php b/phpBB/phpbb/mimetype/guesser_interface.php
index 103689765e..a4005287e4 100644
--- a/phpBB/phpbb/mimetype/guesser_interface.php
+++ b/phpBB/phpbb/mimetype/guesser_interface.php
@@ -1,18 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\mimetype;
-/**
-* @package mimetype
-*/
-
interface guesser_interface
{
/**
@@ -26,6 +26,7 @@ interface guesser_interface
* Guess mimetype of supplied file
*
* @param string $file Path to file
+ * @param string $file_name The real file name
*
* @return string Guess for mimetype of file
*/
diff --git a/phpBB/phpbb/module/exception/module_exception.php b/phpBB/phpbb/module/exception/module_exception.php
new file mode 100644
index 0000000000..8ad75112bc
--- /dev/null
+++ b/phpBB/phpbb/module/exception/module_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\module\exception;
+
+class module_exception extends \phpbb\exception\runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/module/exception/module_not_found_exception.php b/phpBB/phpbb/module/exception/module_not_found_exception.php
new file mode 100644
index 0000000000..2d485e7b35
--- /dev/null
+++ b/phpBB/phpbb/module/exception/module_not_found_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\module\exception;
+
+class module_not_found_exception extends module_exception
+{
+
+}
diff --git a/phpBB/phpbb/module/module_manager.php b/phpBB/phpbb/module/module_manager.php
new file mode 100644
index 0000000000..a812d06736
--- /dev/null
+++ b/phpBB/phpbb/module/module_manager.php
@@ -0,0 +1,564 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\module;
+
+use phpbb\module\exception\module_exception;
+use phpbb\module\exception\module_not_found_exception;
+
+class module_manager
+{
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\extension\manager
+ */
+ protected $extension_manager;
+
+ /**
+ * @var string
+ */
+ protected $modules_table;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\cache\driver\driver_interface $cache Cache driver
+ * @param \phpbb\db\driver\driver_interface $db Database driver
+ * @param \phpbb\extension\manager $ext_manager Extension manager
+ * @param string $modules_table Module database table's name
+ * @param string $phpbb_root_path Path to phpBB's root
+ * @param string $php_ext Extension of PHP files
+ */
+ public function __construct(\phpbb\cache\driver\driver_interface $cache, \phpbb\db\driver\driver_interface $db, \phpbb\extension\manager $ext_manager, $modules_table, $phpbb_root_path, $php_ext)
+ {
+ $this->cache = $cache;
+ $this->db = $db;
+ $this->extension_manager = $ext_manager;
+ $this->modules_table = $modules_table;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Get row for specified module
+ *
+ * @param int $module_id ID of the module
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ *
+ * @return array Array of data fetched from the database
+ *
+ * @throws \phpbb\module\exception\module_not_found_exception When there is no module with $module_id
+ */
+ public function get_module_row($module_id, $module_class)
+ {
+ $module_id = (int) $module_id;
+
+ $sql = 'SELECT *
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND module_id = $module_id";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ throw new module_not_found_exception('NO_MODULE');
+ }
+
+ return $row;
+ }
+
+ /**
+ * Get available module information from module files
+ *
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ * @param string $module ID of module
+ * @param bool $use_all_available Use all available instead of just all
+ * enabled extensions
+ *
+ * @return array Array with module information gathered from module info files.
+ */
+ public function get_module_infos($module_class, $module = '', $use_all_available = false)
+ {
+ $directory = $this->phpbb_root_path . 'includes/' . $module_class . '/info/';
+ $fileinfo = array();
+
+ $finder = $this->extension_manager->get_finder($use_all_available);
+
+ $modules = $finder
+ ->extension_suffix('_module')
+ ->extension_directory("/$module_class")
+ ->core_path("includes/$module_class/info/")
+ ->core_prefix($module_class . '_')
+ ->get_classes(true);
+
+ foreach ($modules as $cur_module)
+ {
+ // Skip entries we do not need if we know the module we are
+ // looking for
+ if ($module && strpos(str_replace('\\', '_', $cur_module), $module) === false && $module !== $cur_module)
+ {
+ continue;
+ }
+
+ $info_class = preg_replace('/_module$/', '_info', $cur_module);
+
+ // If the class does not exist it might be following the old
+ // format. phpbb_acp_info_acp_foo needs to be turned into
+ // acp_foo_info and the respective file has to be included
+ // manually because it does not support auto loading
+ $old_info_class_file = str_replace("phpbb_{$module_class}_info_", '', $cur_module);
+ $old_info_class = $old_info_class_file . '_info';
+
+ if (class_exists($old_info_class))
+ {
+ $info_class = $old_info_class;
+ }
+ else if (!class_exists($info_class))
+ {
+ $info_class = $old_info_class;
+
+ // need to check class exists again because previous checks triggered autoloading
+ if (!class_exists($info_class) && file_exists($directory . $old_info_class_file . '.' . $this->php_ext))
+ {
+ include($directory . $old_info_class_file . '.' . $this->php_ext);
+ }
+ }
+
+ if (class_exists($info_class))
+ {
+ $info = new $info_class();
+ $module_info = $info->module();
+
+ $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $cur_module;
+
+ $fileinfo[$main_class] = $module_info;
+ }
+ }
+
+ ksort($fileinfo);
+
+ return $fileinfo;
+ }
+
+ /**
+ * Get module branch
+ *
+ * @param int $module_id ID of the module
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ * @param string $type Type of branch (Expected values: all, parents or children)
+ * @param bool $include_module Whether or not to include the specified module with $module_id
+ *
+ * @return array Returns an array containing the modules in the specified branch type.
+ */
+ public function get_module_branch($module_id, $module_class, $type = 'all', $include_module = true)
+ {
+ $module_id = (int) $module_id;
+
+ switch ($type)
+ {
+ case 'parents':
+ $condition = 'm1.left_id BETWEEN m2.left_id AND m2.right_id';
+ break;
+
+ case 'children':
+ $condition = 'm2.left_id BETWEEN m1.left_id AND m1.right_id';
+ break;
+
+ default:
+ $condition = 'm2.left_id BETWEEN m1.left_id AND m1.right_id OR m1.left_id BETWEEN m2.left_id AND m2.right_id';
+ break;
+ }
+
+ $rows = array();
+
+ $sql = 'SELECT m2.*
+ FROM ' . $this->modules_table . ' m1
+ LEFT JOIN ' . $this->modules_table . " m2 ON ($condition)
+ WHERE m1.module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND m2.module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND m1.module_id = $module_id
+ ORDER BY m2.left_id DESC";
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (!$include_module && $row['module_id'] == $module_id)
+ {
+ continue;
+ }
+
+ $rows[] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
+ * Remove modules cache file
+ *
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ */
+ public function remove_cache_file($module_class)
+ {
+ // Sanitise for future path use, it's escaped as appropriate for queries
+ $cache_class = str_replace(array('.', '/', '\\'), '', basename($module_class));
+ $this->cache->destroy('_modules_' . $cache_class);
+ $this->cache->destroy('sql', $this->modules_table);
+ }
+
+ /**
+ * Update/Add module
+ *
+ * @param array &$module_data The module data
+ *
+ * @throws \phpbb\module\exception\module_not_found_exception When parent module or the category is not exist
+ */
+ public function update_module_data(&$module_data)
+ {
+ if (!isset($module_data['module_id']))
+ {
+ // no module_id means we're creating a new category/module
+ if ($module_data['parent_id'])
+ {
+ $sql = 'SELECT left_id, right_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "'
+ AND module_id = " . (int) $module_data['parent_id'];
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ throw new module_not_found_exception('PARENT_NOT_EXIST');
+ }
+
+ // Workaround
+ $row['left_id'] = (int) $row['left_id'];
+ $row['right_id'] = (int) $row['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($module_data['module_class']) . "'
+ AND left_id > {$row['right_id']}";
+ $this->db->sql_query($sql);
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET right_id = right_id + 2
+ WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "'
+ AND {$row['left_id']} BETWEEN left_id AND right_id";
+ $this->db->sql_query($sql);
+
+ $module_data['left_id'] = (int) $row['right_id'];
+ $module_data['right_id'] = (int) $row['right_id'] + 1;
+ }
+ else
+ {
+ $sql = 'SELECT MAX(right_id) AS right_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $module_data['left_id'] = (int) $row['right_id'] + 1;
+ $module_data['right_id'] = (int) $row['right_id'] + 2;
+ }
+
+ $sql = 'INSERT INTO ' . $this->modules_table . ' ' . $this->db->sql_build_array('INSERT', $module_data);
+ $this->db->sql_query($sql);
+
+ $module_data['module_id'] = $this->db->sql_nextid();
+ }
+ else
+ {
+ $row = $this->get_module_row($module_data['module_id'], $module_data['module_class']);
+
+ if ($module_data['module_basename'] && !$row['module_basename'])
+ {
+ // we're turning a category into a module
+ $branch = $this->get_module_branch($module_data['module_id'], $module_data['module_class'], 'children', false);
+
+ if (sizeof($branch))
+ {
+ throw new module_not_found_exception('NO_CATEGORY_TO_MODULE');
+ }
+ }
+
+ if ($row['parent_id'] != $module_data['parent_id'])
+ {
+ $this->move_module($module_data['module_id'], $module_data['parent_id'], $module_data['module_class']);
+ }
+
+ $update_ary = $module_data;
+ unset($update_ary['module_id']);
+
+ $sql = 'UPDATE ' . $this->modules_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $update_ary) . "
+ WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "'
+ AND module_id = " . (int) $module_data['module_id'];
+ $this->db->sql_query($sql);
+ }
+ }
+
+ /**
+ * Move module around the tree
+ *
+ * @param int $from_module_id ID of the current parent module
+ * @param int $to_parent_id ID of the target parent module
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ *
+ * @throws \phpbb\module\exception\module_not_found_exception If the module specified to move modules from does not
+ * have any children.
+ */
+ public function move_module($from_module_id, $to_parent_id, $module_class)
+ {
+ $moved_modules = $this->get_module_branch($from_module_id, $module_class, 'children');
+
+ if (empty($moved_modules))
+ {
+ throw new module_not_found_exception();
+ }
+
+ $from_data = $moved_modules[0];
+ $diff = sizeof($moved_modules) * 2;
+
+ $moved_ids = array();
+ for ($i = 0; $i < sizeof($moved_modules); ++$i)
+ {
+ $moved_ids[] = $moved_modules[$i]['module_id'];
+ }
+
+ // Resync parents
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET right_id = right_id - $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id < " . (int) $from_data['right_id'] . '
+ AND right_id > ' . (int) $from_data['right_id'];
+ $this->db->sql_query($sql);
+
+ // Resync righthand side of tree
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id - $diff, right_id = right_id - $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id > " . (int) $from_data['right_id'];
+ $this->db->sql_query($sql);
+
+ if ($to_parent_id > 0)
+ {
+ $to_data = $this->get_module_row($to_parent_id, $module_class);
+
+ // Resync new parents
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET right_id = right_id + $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND " . (int) $to_data['right_id'] . ' BETWEEN left_id AND right_id
+ AND ' . $this->db->sql_in_set('module_id', $moved_ids, true);
+ $this->db->sql_query($sql);
+
+ // Resync the righthand side of the tree
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id + $diff, right_id = right_id + $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id > " . (int) $to_data['right_id'] . '
+ AND ' . $this->db->sql_in_set('module_id', $moved_ids, true);
+ $this->db->sql_query($sql);
+
+ // Resync moved branch
+ $to_data['right_id'] += $diff;
+ if ($to_data['right_id'] > $from_data['right_id'])
+ {
+ $diff = '+ ' . ($to_data['right_id'] - $from_data['right_id'] - 1);
+ }
+ else
+ {
+ $diff = '- ' . abs($to_data['right_id'] - $from_data['right_id'] - 1);
+ }
+ }
+ else
+ {
+ $sql = 'SELECT MAX(right_id) AS right_id
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND " . $this->db->sql_in_set('module_id', $moved_ids, true);
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ $diff = '+ ' . (int) ($row['right_id'] - $from_data['left_id'] + 1);
+ }
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id $diff, right_id = right_id $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND " . $this->db->sql_in_set('module_id', $moved_ids);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Remove module from tree
+ *
+ * @param int $module_id ID of the module to delete
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ *
+ * @throws \phpbb\module\exception\module_exception When the specified module cannot be removed
+ */
+ public function delete_module($module_id, $module_class)
+ {
+ $module_id = (int) $module_id;
+
+ $row = $this->get_module_row($module_id, $module_class);
+
+ $branch = $this->get_module_branch($module_id, $module_class, 'children', false);
+
+ if (sizeof($branch))
+ {
+ throw new module_exception('CANNOT_REMOVE_MODULE');
+ }
+
+ // If not move
+ $diff = 2;
+ $sql = 'DELETE FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND module_id = $module_id";
+ $this->db->sql_query($sql);
+
+ $row['right_id'] = (int) $row['right_id'];
+ $row['left_id'] = (int) $row['left_id'];
+
+ // Resync tree
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET right_id = right_id - $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id < {$row['right_id']} AND right_id > {$row['right_id']}";
+ $this->db->sql_query($sql);
+
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id - $diff, right_id = right_id - $diff
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id > {$row['right_id']}";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Move module position by $steps up/down
+ *
+ * @param array $module_row Array of module data
+ * @param string $module_class Class of the module (acp, ucp, mcp etc...)
+ * @param string $action Direction of moving (valid values: move_up or move_down)
+ * @param int $steps Number of steps to move module
+ *
+ * @return string Returns the language name of the module
+ *
+ * @throws \phpbb\module\exception\module_not_found_exception When the specified module does not exists
+ */
+ public function move_module_by($module_row, $module_class, $action = 'move_up', $steps = 1)
+ {
+ /**
+ * Fetch all the siblings between the module's current spot
+ * and where we want to move it to. If there are less than $steps
+ * siblings between the current spot and the target then the
+ * module will move as far as possible
+ */
+ $sql = 'SELECT module_id, left_id, right_id, module_langname
+ FROM ' . $this->modules_table . "
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND parent_id = " . (int) $module_row['parent_id'] . '
+ AND ' . (($action == 'move_up') ? 'right_id < ' . (int) $module_row['right_id'] . ' ORDER BY right_id DESC' : 'left_id > ' . (int) $module_row['left_id'] . ' ORDER BY left_id ASC');
+ $result = $this->db->sql_query_limit($sql, $steps);
+
+ $target = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $target = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!sizeof($target))
+ {
+ // The module is already on top or bottom
+ throw new module_not_found_exception();
+ }
+
+ /**
+ * $left_id and $right_id define the scope of the nodes that are affected by the move.
+ * $diff_up and $diff_down are the values to substract or add to each node's left_id
+ * and right_id in order to move them up or down.
+ * $move_up_left and $move_up_right define the scope of the nodes that are moving
+ * up. Other nodes in the scope of ($left_id, $right_id) are considered to move down.
+ */
+ if ($action == 'move_up')
+ {
+ $left_id = (int) $target['left_id'];
+ $right_id = (int) $module_row['right_id'];
+
+ $diff_up = (int) ($module_row['left_id'] - $target['left_id']);
+ $diff_down = (int) ($module_row['right_id'] + 1 - $module_row['left_id']);
+
+ $move_up_left = (int) $module_row['left_id'];
+ $move_up_right = (int) $module_row['right_id'];
+ }
+ else
+ {
+ $left_id = (int) $module_row['left_id'];
+ $right_id = (int) $target['right_id'];
+
+ $diff_up = (int) ($module_row['right_id'] + 1 - $module_row['left_id']);
+ $diff_down = (int) ($target['right_id'] - $module_row['right_id']);
+
+ $move_up_left = (int) ($module_row['right_id'] + 1);
+ $move_up_right = (int) $target['right_id'];
+ }
+
+ // Now do the dirty job
+ $sql = 'UPDATE ' . $this->modules_table . "
+ SET left_id = left_id + CASE
+ WHEN left_id BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up}
+ ELSE {$diff_down}
+ END,
+ right_id = right_id + CASE
+ WHEN right_id BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up}
+ ELSE {$diff_down}
+ END
+ WHERE module_class = '" . $this->db->sql_escape($module_class) . "'
+ AND left_id BETWEEN {$left_id} AND {$right_id}
+ AND right_id BETWEEN {$left_id} AND {$right_id}";
+ $this->db->sql_query($sql);
+
+ $this->remove_cache_file($module_class);
+
+ return $target['module_langname'];
+ }
+}
diff --git a/phpBB/phpbb/notification/exception.php b/phpBB/phpbb/notification/exception.php
index 6bdded3fd8..e416438061 100644
--- a/phpBB/phpbb/notification/exception.php
+++ b/phpBB/phpbb/notification/exception.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-license.php GNU Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,13 +15,8 @@ namespace phpbb\notification;
/**
* Notifications exception
-*
-* @package notifications
*/
-class exception extends \Exception
+
+class exception extends \phpbb\exception\runtime_exception
{
- public function __toString()
- {
- return $this->getMessage();
- }
}
diff --git a/phpBB/phpbb/notification/manager.php b/phpBB/phpbb/notification/manager.php
index 2e8652771b..ea1b800dc5 100644
--- a/phpBB/phpbb/notification/manager.php
+++ b/phpBB/phpbb/notification/manager.php
@@ -1,17 +1,22 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\notification;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* Notifications service class
-* @package notifications
*/
class manager
{
@@ -19,39 +24,36 @@ class manager
protected $notification_types;
/** @var array */
+ protected $subscription_types;
+
+ /** @var method\method_interface[] */
protected $notification_methods;
- /** @var ContainerBuilder */
+ /** @var ContainerInterface */
protected $phpbb_container;
/** @var \phpbb\user_loader */
protected $user_loader;
- /** @var \phpbb\config\config */
- protected $config;
+ /** @var \phpbb\event\dispatcher_interface */
+ protected $phpbb_dispatcher;
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var \phpbb\cache\service */
protected $cache;
+ /** @var \phpbb\language\language */
+ protected $language;
+
/** @var \phpbb\user */
protected $user;
/** @var string */
- protected $phpbb_root_path;
-
- /** @var string */
- protected $php_ext;
-
- /** @var string */
protected $notification_types_table;
/** @var string */
- protected $notifications_table;
-
- /** @var string */
protected $user_notifications_table;
/**
@@ -59,41 +61,39 @@ class manager
*
* @param array $notification_types
* @param array $notification_methods
- * @param ContainerBuilder $phpbb_container
+ * @param ContainerInterface $phpbb_container
* @param \phpbb\user_loader $user_loader
- * @param \phpbb\config\config $config
- * @param \phpbb\db\driver\driver $db
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\cache\service $cache
+ * @param \phpbb\language\language $language
* @param \phpbb\user $user
- * @param string $phpbb_root_path
- * @param string $php_ext
* @param string $notification_types_table
- * @param string $notifications_table
* @param string $user_notifications_table
+ *
* @return \phpbb\notification\manager
*/
- public function __construct($notification_types, $notification_methods, $phpbb_container, \phpbb\user_loader $user_loader, \phpbb\config\config $config, \phpbb\db\driver\driver $db, \phpbb\cache\service $cache, $user, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table)
+ public function __construct($notification_types, $notification_methods, ContainerInterface $phpbb_container, \phpbb\user_loader $user_loader, \phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\language\language $language, \phpbb\user $user, $notification_types_table, $user_notifications_table)
{
$this->notification_types = $notification_types;
$this->notification_methods = $notification_methods;
$this->phpbb_container = $phpbb_container;
$this->user_loader = $user_loader;
- $this->config = $config;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->db = $db;
$this->cache = $cache;
+ $this->language = $language;
$this->user = $user;
- $this->phpbb_root_path = $phpbb_root_path;
- $this->php_ext = $php_ext;
-
$this->notification_types_table = $notification_types_table;
- $this->notifications_table = $notifications_table;
$this->user_notifications_table = $user_notifications_table;
}
/**
- * Load the user's notifications
+ * Load the user's notifications for a given method
*
+ * @param string $method_name
* @param array $options Optional options to control what notifications are loaded
* notification_id Notification id to load (or array of notification ids)
* user_id User id to load notifications for (Default: $user->data['user_id'])
@@ -108,27 +108,21 @@ class manager
* 'notifications' array of notification type objects
* 'unread_count' number of unread notifications the user has if count_unread is true in the options
* 'total_count' number of notifications the user has if count_total is true in the options
+ * @throws \phpbb\notification\exception when the method doesn't refer to a class extending \phpbb\notification\method\method_interface
*/
- public function load_notifications(array $options = array())
+ public function load_notifications($method_name, array $options = array())
{
- // Merge default options
- $options = array_merge(array(
- 'notification_id' => false,
- 'user_id' => $this->user->data['user_id'],
- 'order_by' => 'notification_time',
- 'order_dir' => 'DESC',
- 'limit' => 0,
- 'start' => 0,
- 'all_unread' => false,
- 'count_unread' => false,
- 'count_total' => false,
- ), $options);
+ $method = $this->get_method_class($method_name);
- // If all_unread, count_unread must be true
- $options['count_unread'] = ($options['all_unread']) ? true : $options['count_unread'];
-
- // Anonymous users and bots never receive notifications
- if ($options['user_id'] == $this->user->data['user_id'] && ($this->user->data['user_id'] == ANONYMOUS || $this->user->data['user_type'] == USER_IGNORE))
+ if (! $method instanceof \phpbb\notification\method\method_interface)
+ {
+ throw new \phpbb\notification\exception($this->language->lang('NOTIFICATION_METHOD_INVALID', $method_name));
+ }
+ else if ($method->is_available())
+ {
+ return $method->load_notifications($options);
+ }
+ else
{
return array(
'notifications' => array(),
@@ -136,172 +130,112 @@ class manager
'total_count' => 0,
);
}
+ }
- $notifications = $user_ids = array();
- $load_special = array();
- $total_count = $unread_count = 0;
+ /**
+ * Mark notifications read or unread for all available methods
+ *
+ * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types
+ * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids
+ * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ *
+ * @deprecated since 3.2
+ */
+ public function mark_notifications_read($notification_type_name, $item_id, $user_id, $time = false)
+ {
+ $this->mark_notifications($notification_type_name, $item_id, $user_id, $time);
+ }
- if ($options['count_unread'])
+ /**
+ * Mark notifications read or unread for all available methods
+ *
+ * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types
+ * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids
+ * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
+ */
+ public function mark_notifications($notification_type_name, $item_id, $user_id, $time = false, $mark_read = true)
+ {
+ if (is_array($notification_type_name))
{
- // Get the total number of unread notifications
- $sql = 'SELECT COUNT(n.notification_id) AS unread_count
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.user_id = ' . (int) $options['user_id'] . '
- AND n.notification_read = 0
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- $unread_count = (int) $this->db->sql_fetchfield('unread_count');
- $this->db->sql_freeresult($result);
+ $notification_type_id = $this->get_notification_type_ids($notification_type_name);
}
-
- if ($options['count_total'])
+ else if ($notification_type_name !== false)
{
- // Get the total number of notifications
- $sql = 'SELECT COUNT(n.notification_id) AS total_count
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.user_id = ' . (int) $options['user_id'] . '
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- $total_count = (int) $this->db->sql_fetchfield('total_count');
- $this->db->sql_freeresult($result);
+ $notification_type_id = $this->get_notification_type_id($notification_type_name);
}
-
- if (!$options['count_total'] || $total_count)
+ else
{
- $rowset = array();
-
- // Get the main notifications
- $sql = 'SELECT n.*, nt.notification_type_name
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.user_id = ' . (int) $options['user_id'] .
- (($options['notification_id']) ? ((is_array($options['notification_id'])) ? ' AND ' . $this->db->sql_in_set('n.notification_id', $options['notification_id']) : ' AND n.notification_id = ' . (int) $options['notification_id']) : '') . '
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1
- ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
- $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
-
- while ($row = $this->db->sql_fetchrow($result))
- {
- $rowset[$row['notification_id']] = $row;
- }
- $this->db->sql_freeresult($result);
-
- // Get all unread notifications
- if ($unread_count && $options['all_unread'] && !empty($rowset))
- {
- $sql = 'SELECT n.*, nt.notification_type_name
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.user_id = ' . (int) $options['user_id'] . '
- AND n.notification_read = 0
- AND ' . $this->db->sql_in_set('n.notification_id', array_keys($rowset), true) . '
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1
- ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
- $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
-
- while ($row = $this->db->sql_fetchrow($result))
- {
- $rowset[$row['notification_id']] = $row;
- }
- $this->db->sql_freeresult($result);
- }
-
- foreach ($rowset as $row)
- {
- $notification = $this->get_item_type_class($row['notification_type_name'], $row);
-
- // Array of user_ids to query all at once
- $user_ids = array_merge($user_ids, $notification->users_to_query());
-
- // Some notification types also require querying additional tables themselves
- if (!isset($load_special[$row['notification_type_name']]))
- {
- $load_special[$row['notification_type_name']] = array();
- }
- $load_special[$row['notification_type_name']] = array_merge($load_special[$row['notification_type_name']], $notification->get_load_special());
-
- $notifications[$row['notification_id']] = $notification;
- }
-
- $this->user_loader->load_users($user_ids);
-
- // Allow each type to load its own special items
- foreach ($load_special as $item_type => $data)
- {
- $item_class = $this->get_item_type_class($item_type);
-
- $item_class->load_special($data, $notifications);
- }
+ $notification_type_id = false;
}
- return array(
- 'notifications' => $notifications,
- 'unread_count' => $unread_count,
- 'total_count' => $total_count,
- );
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->mark_notifications($notification_type_id, $item_id, $user_id, $time, $mark_read);
+ }
}
/**
- * Mark notifications read
- *
- * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to mark read for all item types
- * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids
- * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
- * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
- */
- public function mark_notifications_read($notification_type_name, $item_id, $user_id, $time = false)
+ * Mark notifications read or unread from a parent identifier for all available methods
+ *
+ * @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)
+ * @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids
+ * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ *
+ * @deprecated since 3.2
+ */
+ public function mark_notifications_read_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false)
{
- $time = ($time !== false) ? $time : time();
-
- $sql = 'UPDATE ' . $this->notifications_table . "
- SET notification_read = 1
- WHERE notification_time <= " . (int) $time .
- (($notification_type_name !== false) ? ' AND ' .
- (is_array($notification_type_name) ? $this->db->sql_in_set('notification_type_id', $this->get_notification_type_ids($notification_type_name)) : 'notification_type_id = ' . $this->get_notification_type_id($notification_type_name)) : '') .
- (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '') .
- (($item_id !== false) ? ' AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) : '');
- $this->db->sql_query($sql);
+ $this->mark_notifications_by_parent($notification_type_name, $item_parent_id, $user_id, $time);
}
/**
- * Mark notifications read from a parent identifier
+ * Mark notifications read or unread from a parent identifier for all available methods
*
* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)
* @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids
* @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
*/
- public function mark_notifications_read_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false)
+ public function mark_notifications_by_parent($notification_type_name, $item_parent_id, $user_id, $time = false, $mark_read = true)
{
- $time = ($time !== false) ? $time : time();
-
- $sql = 'UPDATE ' . $this->notifications_table . "
- SET notification_read = 1
- WHERE notification_time <= " . (int) $time .
- (($notification_type_name !== false) ? ' AND ' .
- (is_array($notification_type_name) ? $this->db->sql_in_set('notification_type_id', $this->get_notification_type_ids($notification_type_name)) : 'notification_type_id = ' . $this->get_notification_type_id($notification_type_name)) : '') .
- (($item_parent_id !== false) ? ' AND ' . (is_array($item_parent_id) ? $this->db->sql_in_set('item_parent_id', $item_parent_id) : 'item_parent_id = ' . (int) $item_parent_id) : '') .
- (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '');
- $this->db->sql_query($sql);
+ if (is_array($notification_type_name))
+ {
+ $notification_type_id = $this->get_notification_type_ids($notification_type_name);
+ }
+ else
+ {
+ $notification_type_id = $this->get_notification_type_id($notification_type_name);
+ }
+
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time, $mark_read);
+ }
}
/**
- * Mark notifications read
+ * Mark notifications read or unread for a given method
*
+ * @param string $method_name
* @param int|array $notification_id Notification id or array of notification ids.
* @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
*/
- public function mark_notifications_read_by_id($notification_id, $time = false)
+ public function mark_notifications_by_id($method_name, $notification_id, $time = false, $mark_read = true)
{
- $time = ($time !== false) ? $time : time();
+ $method = $this->get_method_class($method_name);
- $sql = 'UPDATE ' . $this->notifications_table . "
- SET notification_read = 1
- WHERE notification_time <= " . (int) $time . '
- AND ' . ((is_array($notification_id)) ? $this->db->sql_in_set('notification_id', $notification_id) : 'notification_id = ' . (int) $notification_id);
- $this->db->sql_query($sql);
+ if ($method instanceof \phpbb\notification\method\method_interface && $method->is_available())
+ {
+ $method->mark_notifications_by_id($notification_id, $time, $mark_read);
+ }
}
/**
@@ -335,11 +269,29 @@ class manager
return $notified_users;
}
- $item_id = $this->get_item_type_class($notification_type_name)->get_item_id($data);
-
// find out which users want to receive this type of notification
$notify_users = $this->get_item_type_class($notification_type_name)->find_users_for_notification($data, $options);
+ /**
+ * Allow filtering the notify_users array for a notification that is about to be sent.
+ * Here, $notify_users is already filtered by f_read and the ignored list included in the options variable
+ *
+ * @event core.notification_manager_add_notifications
+ * @var string notification_type_name The forum id from where the topic belongs
+ * @var array data Data specific for the notification_type_name used will be inserted
+ * @var array notify_users The array of userid that are going to be notified for this notification. Set to array() to cancel.
+ * @var array options The options that were used when this method was called (read only)
+ *
+ * @since 3.1.3-RC1
+ */
+ $vars = array(
+ 'notification_type_name',
+ 'data',
+ 'notify_users',
+ 'options',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.notification_manager_add_notifications', compact($vars)));
+
$this->add_notifications_for_users($notification_type_name, $data, $notify_users);
return $notify_users;
@@ -369,25 +321,23 @@ class manager
$item_id = $this->get_item_type_class($notification_type_name)->get_item_id($data);
$user_ids = array();
- $notification_objects = $notification_methods = array();
+ $notification_methods = array();
// Never send notifications to the anonymous user!
unset($notify_users[ANONYMOUS]);
// Make sure not to send new notifications to users who've already been notified about this item
// This may happen when an item was added, but now new users are able to see the item
- $sql = 'SELECT n.user_id
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.notification_type_id = ' . (int) $notification_type_id . '
- AND n.item_id = ' . (int) $item_id . '
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
+ // We remove each user which was already notified by at least one method.
+ /** @var method\method_interface $method */
+ foreach ($this->get_subscription_methods_instances() as $method)
{
- unset($notify_users[$row['user_id']]);
+ $notified_users = $method->get_notified_users($notification_type_id, array('item_id' => $item_id));
+ foreach ($notified_users as $user => $notifications)
+ {
+ unset($notify_users[$user]);
+ }
}
- $this->db->sql_freeresult($result);
if (!sizeof($notify_users))
{
@@ -399,8 +349,6 @@ class manager
$pre_create_data = $notification->pre_create_insert_array($data, $notify_users);
unset($notification);
- $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->notifications_table);
-
// Go through each user so we can insert a row in the DB and then notify them by their desired means
foreach ($notify_users as $user => $methods)
{
@@ -408,8 +356,8 @@ class manager
$notification->user_id = (int) $user;
- // Insert notification row using buffer.
- $insert_buffer->insert($notification->create_insert_array($data, $pre_create_data));
+ // Generate the insert_array
+ $notification->create_insert_array($data, $pre_create_data);
// Users are needed to send notifications
$user_ids = array_merge($user_ids, $notification->users_to_query());
@@ -417,20 +365,15 @@ class manager
foreach ($methods as $method)
{
// setup the notification methods and add the notification to the queue
- if ($method) // blank means we just insert it as a notification, but do not notify them by any other means
+ if (!isset($notification_methods[$method]))
{
- if (!isset($notification_methods[$method]))
- {
- $notification_methods[$method] = $this->get_method_class($method);
- }
-
- $notification_methods[$method]->add_to_queue($notification);
+ $notification_methods[$method] = $this->get_method_class($method);
}
+
+ $notification_methods[$method]->add_to_queue($notification);
}
}
- $insert_buffer->flush();
-
// We need to load all of the users to send notifications
$this->user_loader->load_users($user_ids);
@@ -442,12 +385,13 @@ class manager
}
/**
- * Update a notification
+ * Update notification
*
* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types)
* @param array $data Data specific for this type that will be updated
+ * @param array $options
*/
- public function update_notifications($notification_type_name, $data)
+ public function update_notifications($notification_type_name, array $data, array $options = array())
{
if (is_array($notification_type_name))
{
@@ -459,27 +403,28 @@ class manager
return;
}
- $notification = $this->get_item_type_class($notification_type_name);
+ $this->update_notification($this->get_item_type_class($notification_type_name), $data, $options);
+ }
- // Allow the notifications class to over-ride the update_notifications functionality
- if (method_exists($notification, 'update_notifications'))
+ /**
+ * Update a notification
+ *
+ * @param \phpbb\notification\type\type_interface $notification The notification
+ * @param array $data Data specific for this type that will be updated
+ * @param array $options
+ */
+ public function update_notification(\phpbb\notification\type\type_interface $notification, array $data, array $options = array())
+ {
+ if (empty($options))
{
- // Return False to over-ride the rest of the update
- if ($notification->update_notifications($data) === false)
- {
- return;
- }
+ $options['item_id'] = $notification->get_item_id($data);
}
- $notification_type_id = $this->get_notification_type_id($notification_type_name);
- $item_id = $notification->get_item_id($data);
- $update_array = $notification->create_update_array($data);
-
- $sql = 'UPDATE ' . $this->notifications_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $update_array) . '
- WHERE notification_type_id = ' . (int) $notification_type_id . '
- AND item_id = ' . (int) $item_id;
- $this->db->sql_query($sql);
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->update_notification($notification, $data, $options);
+ }
}
/**
@@ -488,14 +433,15 @@ class manager
* @param string|array $notification_type_name Type identifier or array of item types (only acceptable if the $item_id is identical for the specified types)
* @param int|array $item_id Identifier within the type (or array of ids)
* @param mixed $parent_id Parent identifier within the type (or array of ids), used in combination with item_id if specified (Default: false; not checked)
+ * @param mixed $user_id User id (Default: false; not checked)
*/
- public function delete_notifications($notification_type_name, $item_id, $parent_id = false)
+ public function delete_notifications($notification_type_name, $item_id, $parent_id = false, $user_id = false)
{
if (is_array($notification_type_name))
{
foreach ($notification_type_name as $type)
{
- $this->delete_notifications($type, $item_id, $parent_id);
+ $this->delete_notifications($type, $item_id, $parent_id, $user_id);
}
return;
@@ -503,11 +449,11 @@ class manager
$notification_type_id = $this->get_notification_type_id($notification_type_name);
- $sql = 'DELETE FROM ' . $this->notifications_table . '
- WHERE notification_type_id = ' . (int) $notification_type_id . '
- AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) .
- (($parent_id !== false) ? ' AND ' . ((is_array($parent_id) ? $this->db->sql_in_set('item_parent_id', $parent_id) : 'item_parent_id = ' . (int) $parent_id)) : '');
- $this->db->sql_query($sql);
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->delete_notifications($notification_type_id, $item_id, $parent_id, $user_id);
+ }
}
/**
@@ -517,33 +463,37 @@ class manager
*/
public function get_subscription_types()
{
- $subscription_types = array();
-
- foreach ($this->notification_types as $type_name => $data)
+ if ($this->subscription_types === null)
{
- $type = $this->get_item_type_class($type_name);
+ $this->subscription_types = array();
- if ($type instanceof \phpbb\notification\type\type_interface && $type->is_available())
+ foreach ($this->notification_types as $type_name => $data)
{
- $options = array_merge(array(
- 'id' => $type->get_type(),
- 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()),
- 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS',
- ), (($type::$notification_option !== false) ? $type::$notification_option : array()));
+ /** @var type\base $type */
+ $type = $this->get_item_type_class($type_name);
+
+ if ($type instanceof \phpbb\notification\type\type_interface && $type->is_available())
+ {
+ $options = array_merge(array(
+ 'id' => $type->get_type(),
+ 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()),
+ 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS',
+ ), (($type::$notification_option !== false) ? $type::$notification_option : array()));
- $subscription_types[$options['group']][$options['id']] = $options;
+ $this->subscription_types[$options['group']][$options['id']] = $options;
+ }
}
- }
- // Move Miscellaneous to the very last section
- if (isset($subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']))
- {
- $miscellaneous = $subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'];
- unset($subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']);
- $subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'] = $miscellaneous;
+ // Move Miscellaneous to the very last section
+ if (isset($this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']))
+ {
+ $miscellaneous = $this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'];
+ unset($this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']);
+ $this->subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS'] = $miscellaneous;
+ }
}
- return $subscription_types;
+ return $this->subscription_types;
}
/**
@@ -555,22 +505,89 @@ class manager
{
$subscription_methods = array();
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method_name => $method)
+ {
+ $subscription_methods[$method_name] = array(
+ 'id' => $method->get_type(),
+ 'lang' => str_replace('.', '_', strtoupper($method->get_type())),
+ );
+ }
+
+ return $subscription_methods;
+ }
+
+ /**
+ * Get all of the subscription methods
+ *
+ * @return array Array of method's instances
+ */
+ private function get_subscription_methods_instances()
+ {
+ $subscription_methods = array();
+
foreach ($this->notification_methods as $method_name => $data)
{
$method = $this->get_method_class($method_name);
- if ($method instanceof \phpbb\notification\method\method_interface && $method->is_available())
+ if ($method instanceof \phpbb\notification\method\method_interface)
+ {
+ $subscription_methods[$method_name] = $method;
+ }
+ }
+
+ return $subscription_methods;
+ }
+
+ /**
+ * Get all of the available subscription methods
+ *
+ * @return array Array of method's instances
+ */
+ private function get_available_subscription_methods()
+ {
+ $subscription_methods = array();
+
+ /** @var method\method_interface $method */
+ foreach ($this->get_subscription_methods_instances() as $method_name => $method)
+ {
+ if ($method->is_available())
{
- $subscription_methods[$method_name] = array(
- 'id' => $method->get_type(),
- 'lang' => 'NOTIFICATION_METHOD_' . strtoupper($method->get_type()),
- );
+ $subscription_methods[$method_name] = $method;
}
}
return $subscription_methods;
}
+
+ /**
+ * Get user's notification data
+ *
+ * @param int $user_id The user_id of the user to get the notifications for
+ *
+ * @return array User's notification
+ */
+ protected function get_user_notifications($user_id)
+ {
+ $sql = 'SELECT method, notify, item_type
+ FROM ' . $this->user_notifications_table . '
+ WHERE user_id = ' . (int) $user_id . '
+ AND item_id = 0';
+
+ $result = $this->db->sql_query($sql);
+ $user_notifications = array();
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $user_notifications[$row['item_type']][] = $row;
+ }
+
+ $this->db->sql_freeresult($result);
+
+ return $user_notifications;
+ }
+
/**
* Get global subscriptions (item_id = 0)
*
@@ -580,47 +597,43 @@ class manager
*/
public function get_global_subscriptions($user_id = false)
{
- $user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id;
+ $user_id = $user_id ?: $this->user->data['user_id'];
$subscriptions = array();
+ $default_methods = $this->get_default_methods();
+
+ $user_notifications = $this->get_user_notifications($user_id);
- foreach ($this->get_subscription_types() as $group_name => $types)
+ foreach ($this->get_subscription_types() as $types)
{
foreach ($types as $id => $type)
{
- $sql = 'SELECT method, notify
- FROM ' . $this->user_notifications_table . '
- WHERE user_id = ' . (int) $user_id . "
- AND item_type = '" . $this->db->sql_escape($id) . "'
- AND item_id = 0";
- $result = $this->db->sql_query($sql);
-
- $row = $this->db->sql_fetchrow($result);
- if (!$row)
+ $type_subscriptions = $default_methods;
+ if (!empty($user_notifications[$id]))
{
- // No rows at all, default to ''
- $subscriptions[$id] = array('');
- }
- else
- {
- do
+ foreach ($user_notifications[$id] as $user_notification)
{
- if (!$row['notify'])
+ $key = array_search($user_notification['method'], $type_subscriptions, true);
+ if (!$user_notification['notify'])
{
+ if ($key !== false)
+ {
+ unset($type_subscriptions[$key]);
+ }
+
continue;
}
-
- if (!isset($subscriptions[$id]))
+ else if ($key === false)
{
- $subscriptions[$id] = array();
+ $type_subscriptions[] = $user_notification['method'];
}
-
- $subscriptions[$id][] = $row['method'];
}
- while ($row = $this->db->sql_fetchrow($result));
}
- $this->db->sql_freeresult($result);
+ if (!empty($type_subscriptions))
+ {
+ $subscriptions[$id] = $type_subscriptions;
+ }
}
}
@@ -632,15 +645,20 @@ class manager
*
* @param string $item_type Type identifier of the subscription
* @param int $item_id The id of the item
- * @param string $method The method of the notification e.g. '', 'email', or 'jabber'
+ * @param string $method The method of the notification e.g. 'board', 'email', or 'jabber'
+ * (if null a subscription will be added for all the defaults methods)
* @param bool|int $user_id The user_id to add the subscription for (bool false for current user)
*/
- public function add_subscription($item_type, $item_id = 0, $method = '', $user_id = false)
+ public function add_subscription($item_type, $item_id = 0, $method = null, $user_id = false)
{
- if ($method !== '')
+ if ($method === null)
{
- // Make sure to subscribe them to the base subscription
- $this->add_subscription($item_type, $item_id, '', $user_id);
+ foreach ($this->get_default_methods() as $method_name)
+ {
+ $this->add_subscription($item_type, $item_id, $method_name, $user_id);
+ }
+
+ return;
}
$user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id;
@@ -684,33 +702,23 @@ class manager
*
* @param string $item_type Type identifier of the subscription
* @param int $item_id The id of the item
- * @param string $method The method of the notification e.g. '', 'email', or 'jabber'
+ * @param string $method The method of the notification e.g. 'board', 'email', or 'jabber'
* @param bool|int $user_id The user_id to add the subscription for (bool false for current user)
*/
- public function delete_subscription($item_type, $item_id = 0, $method = '', $user_id = false)
+ public function delete_subscription($item_type, $item_id = 0, $method = null, $user_id = false)
{
- $user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id;
-
- // If no method, make sure that no other notification methods for this item are selected before deleting
- if ($method === '')
+ if ($method === null)
{
- $sql = 'SELECT COUNT(*) as num_notifications
- FROM ' . $this->user_notifications_table . "
- WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
- AND item_id = " . (int) $item_id . '
- AND user_id = ' .(int) $user_id . "
- AND method <> ''
- AND notify = 1";
- $this->db->sql_query($sql);
- $num_notifications = $this->db->sql_fetchfield('num_notifications');
- $this->db->sql_freeresult();
-
- if ($num_notifications)
+ foreach ($this->get_default_methods() as $method_name)
{
- return;
+ $this->delete_subscription($item_type, $item_id, $method_name, $user_id);
}
+
+ return;
}
+ $user_id = $user_id ?: $this->user->data['user_id'];
+
$sql = 'UPDATE ' . $this->user_notifications_table . "
SET notify = 0
WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
@@ -760,17 +768,27 @@ class manager
*/
public function purge_notifications($notification_type_name)
{
- $notification_type_id = $this->get_notification_type_id($notification_type_name);
-
- $sql = 'DELETE FROM ' . $this->notifications_table . '
- WHERE notification_type_id = ' . (int) $notification_type_id;
- $this->db->sql_query($sql);
+ // If a notification is never used, its type will not be added to the database
+ // nor its id cached. If this method is called by an extension during the
+ // purge step, and that extension never used its notifications,
+ // get_notification_type_id() will throw an exception. However,
+ // because no notification type was added to the database,
+ // there is nothing to delete, so we can silently drop the exception.
+ try
+ {
+ $notification_type_id = $this->get_notification_type_id($notification_type_name);
- $sql = 'DELETE FROM ' . $this->notification_types_table . '
- WHERE notification_type_id = ' . (int) $notification_type_id;
- $this->db->sql_query($sql);
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->purge_notifications($notification_type_id);
+ }
- $this->cache->destroy('notification_type_ids');
+ }
+ catch (\phpbb\notification\exception $e)
+ {
+ // Continue
+ }
}
/**
@@ -794,25 +812,46 @@ class manager
* Delete all notifications older than a certain time
*
* @param int $timestamp Unix timestamp to delete all notifications that were created before
- * @param bool $only_unread True (default) to only prune read notifications
+ * @param bool $only_read True (default) to only prune read notifications
*/
public function prune_notifications($timestamp, $only_read = true)
{
- $sql = 'DELETE FROM ' . $this->notifications_table . '
- WHERE notification_time < ' . (int) $timestamp .
- (($only_read) ? ' AND notification_read = 1' : '');
- $this->db->sql_query($sql);
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $method->prune_notifications($timestamp, $only_read);
+ }
+ }
- $this->config->set('read_notification_last_gc', time(), false);
+ /**
+ * Helper to get the list of methods enabled by default
+ *
+ * @return method\method_interface[]
+ */
+ public function get_default_methods()
+ {
+ $default_methods = array();
+
+ foreach ($this->notification_methods as $method)
+ {
+ if ($method->is_enabled_by_default() && $method->is_available())
+ {
+ $default_methods[] = $method->get_type();
+ }
+ }
+
+ return $default_methods;
}
/**
- * Helper to get the notifications item type class and set it up
- */
+ * Helper to get the notifications item type class and set it up
+ *
+ * @param string $notification_type_name
+ * @param array $data
+ * @return type\type_interface
+ */
public function get_item_type_class($notification_type_name, $data = array())
{
- $notification_type_name = (strpos($notification_type_name, 'notification.type.') === 0) ? $notification_type_name : 'notification.type.' . $notification_type_name;
-
$item = $this->load_object($notification_type_name);
$item->set_initial_data($data);
@@ -821,18 +860,22 @@ class manager
}
/**
- * Helper to get the notifications method class and set it up
- */
+ * Helper to get the notifications method class and set it up
+ *
+ * @param string $method_name
+ * @return method\method_interface
+ */
public function get_method_class($method_name)
{
- $method_name = (strpos($method_name, 'notification.method.') === 0) ? $method_name : 'notification.method.' . $method_name;
-
return $this->load_object($method_name);
}
/**
- * Helper to load objects (notification types/methods)
- */
+ * Helper to load objects (notification types/methods)
+ *
+ * @param string $object_name
+ * @return method\method_interface|type\type_interface
+ */
protected function load_object($object_name)
{
$object = $this->phpbb_container->get($object_name);
@@ -850,6 +893,7 @@ class manager
*
* @param string $notification_type_name The name
* @return int the notification_type_id
+ * @throws \phpbb\notification\exception
*/
public function get_notification_type_id($notification_type_name)
{
@@ -875,7 +919,7 @@ class manager
{
if (!isset($this->notification_types[$notification_type_name]) && !isset($this->notification_types['notification.type.' . $notification_type_name]))
{
- throw new \phpbb\notification\exception($this->user->lang('NOTIFICATION_TYPE_NOT_EXIST', $notification_type_name));
+ throw new \phpbb\notification\exception('NOTIFICATION_TYPE_NOT_EXIST', array($notification_type_name));
}
$sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array(
@@ -909,4 +953,26 @@ class manager
return $notification_type_ids;
}
+
+ /**
+ * Find the users which are already notified
+ *
+ * @param bool|string|array $notification_type_name Type identifier or array of item types (only acceptable if the $data is identical for the specified types). False to retrieve all item types
+ * @param array $options
+ * @return array The list of the notified users
+ */
+ public function get_notified_users($notification_type_name, array $options)
+ {
+ $notification_type_id = $this->get_notification_type_id($notification_type_name);
+
+ $notified_users = array();
+
+ /** @var method\method_interface $method */
+ foreach ($this->get_available_subscription_methods() as $method)
+ {
+ $notified_users = $notified_users + $method->get_notified_users($notification_type_id, $options);
+ }
+
+ return $notified_users;
+ }
}
diff --git a/phpBB/phpbb/notification/method/base.php b/phpBB/phpbb/notification/method/base.php
index 4ce42de830..4a183ca508 100644
--- a/phpBB/phpbb/notification/method/base.php
+++ b/phpBB/phpbb/notification/method/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,43 +15,12 @@ namespace phpbb\notification\method;
/**
* Base notifications method class
-* @package notifications
*/
abstract class base implements \phpbb\notification\method\method_interface
{
/** @var \phpbb\notification\manager */
protected $notification_manager;
- /** @var \phpbb\user_loader */
- protected $user_loader;
-
- /** @var \phpbb\db\driver\driver */
- protected $db;
-
- /** @var \phpbb\cache\driver\driver_interface */
- protected $cache;
-
- /** @var \phpbb\template\template */
- protected $template;
-
- /** @var \phpbb\extension\manager */
- protected $extension_manager;
-
- /** @var \phpbb\user */
- protected $user;
-
- /** @var \phpbb\auth\auth */
- protected $auth;
-
- /** @var \phpbb\config\config */
- protected $config;
-
- /** @var string */
- protected $phpbb_root_path;
-
- /** @var string */
- protected $php_ext;
-
/**
* Queue of messages to be sent
*
@@ -56,38 +29,43 @@ abstract class base implements \phpbb\notification\method\method_interface
protected $queue = array();
/**
- * Notification Method Base Constructor
- *
- * @param \phpbb\user_loader $user_loader
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\cache\driver\driver_interface $cache
- * @param \phpbb\user $user
- * @param \phpbb\auth\auth $auth
- * @param \phpbb\config\config $config
- * @param string $phpbb_root_path
- * @param string $php_ext
- * @return \phpbb\notification\method\base
+ * Set notification manager (required)
+ *
+ * @param \phpbb\notification\manager $notification_manager
+ */
+ public function set_notification_manager(\phpbb\notification\manager $notification_manager)
+ {
+ $this->notification_manager = $notification_manager;
+ }
+
+ /**
+ * Is the method enable by default?
+ *
+ * @return bool
*/
- public function __construct(\phpbb\user_loader $user_loader, \phpbb\db\driver\driver $db, \phpbb\cache\driver\driver_interface $cache, $user, \phpbb\auth\auth $auth, \phpbb\config\config $config, $phpbb_root_path, $php_ext)
+ public function is_enabled_by_default()
{
- $this->user_loader = $user_loader;
- $this->db = $db;
- $this->cache = $cache;
- $this->user = $user;
- $this->auth = $auth;
- $this->config = $config;
- $this->phpbb_root_path = $phpbb_root_path;
- $this->php_ext = $php_ext;
+ return false;
}
/**
- * Set notification manager (required)
- *
- * @param \phpbb\notification\manager $notification_manager
+ * {@inheritdoc}
*/
- public function set_notification_manager(\phpbb\notification\manager $notification_manager)
+ public function get_notified_users($notification_type_id, array $options)
{
- $this->notification_manager = $notification_manager;
+ return array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function load_notifications(array $options = array())
+ {
+ return array(
+ 'notifications' => array(),
+ 'unread_count' => 0,
+ 'total_count' => 0,
+ );
}
/**
@@ -101,6 +79,55 @@ abstract class base implements \phpbb\notification\method\method_interface
}
/**
+ * {@inheritdoc}
+ */
+ public function update_notification($notification, array $data, array $options)
+ {
+ }
+
+ /**
+ * {@inheritdoc
+ */
+ public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mark_notifications_by_id($notification_id, $time = false, $mark_read = true)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function delete_notifications($notification_type_id, $item_id, $parent_id = false, $user_id = false)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prune_notifications($timestamp, $only_read = true)
+ {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function purge_notifications($notification_type_id)
+ {
+ }
+
+ /**
* Empty the queue
*/
protected function empty_queue()
diff --git a/phpBB/phpbb/notification/method/board.php b/phpBB/phpbb/notification/method/board.php
new file mode 100644
index 0000000000..931b252daa
--- /dev/null
+++ b/phpBB/phpbb/notification/method/board.php
@@ -0,0 +1,399 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\notification\method;
+
+/**
+* In Board notification method class
+* This class handles in board notifications. This method is enabled by default.
+*
+* @package notifications
+*/
+class board extends \phpbb\notification\method\base
+{
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var \phpbb\db\driver\driver_interface */
+ protected $db;
+
+ /** @var \phpbb\cache\driver\driver_interface */
+ protected $cache;
+
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var string */
+ protected $notification_types_table;
+
+ /** @var string */
+ protected $notifications_table;
+
+ /**
+ * Notification Method Board Constructor
+ *
+ * @param \phpbb\user_loader $user_loader
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\cache\driver\driver_interface $cache
+ * @param \phpbb\user $user
+ * @param \phpbb\config\config $config
+ * @param string $notification_types_table
+ * @param string $notifications_table
+ */
+ public function __construct(\phpbb\user_loader $user_loader, \phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache, \phpbb\user $user, \phpbb\config\config $config, $notification_types_table, $notifications_table)
+ {
+ $this->user_loader = $user_loader;
+ $this->db = $db;
+ $this->cache = $cache;
+ $this->user = $user;
+ $this->config = $config;
+ $this->notification_types_table = $notification_types_table;
+ $this->notifications_table = $notifications_table;
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add_to_queue(\phpbb\notification\type\type_interface $notification)
+ {
+ $this->queue[] = $notification;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_type()
+ {
+ return 'notification.method.board';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_available()
+ {
+ return $this->config['allow_board_notifications'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_enabled_by_default()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_notified_users($notification_type_id, array $options)
+ {
+ $notified_users = array();
+ $sql = 'SELECT n.*
+ FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
+ WHERE n.notification_type_id = ' . (int) $notification_type_id .
+ (isset($options['item_id']) ? ' AND n.item_id = ' . (int) $options['item_id'] : '') .
+ (isset($options['item_parent_id']) ? ' AND n.item_parent_id = ' . (int) $options['item_parent_id'] : '') .
+ (isset($options['user_id']) ? ' AND n.user_id = ' . (int) $options['user_id'] : '') .
+ (isset($options['read']) ? ' AND n.notification_read = ' . (int) $options['read'] : '') .'
+ AND nt.notification_type_id = n.notification_type_id
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $notified_users[$row['user_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $notified_users;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function load_notifications(array $options = array())
+ {
+ // Merge default options
+ $options = array_merge(array(
+ 'notification_id' => false,
+ 'user_id' => $this->user->data['user_id'],
+ 'order_by' => 'notification_time',
+ 'order_dir' => 'DESC',
+ 'limit' => 0,
+ 'start' => 0,
+ 'all_unread' => false,
+ 'count_unread' => false,
+ 'count_total' => false,
+ ), $options);
+
+ // If all_unread, count_unread must be true
+ $options['count_unread'] = ($options['all_unread']) ? true : $options['count_unread'];
+
+ // Anonymous users and bots never receive notifications
+ if ($options['user_id'] == $this->user->data['user_id'] && ($this->user->data['user_id'] == ANONYMOUS || $this->user->data['user_type'] == USER_IGNORE))
+ {
+ return array(
+ 'notifications' => array(),
+ 'unread_count' => 0,
+ 'total_count' => 0,
+ );
+ }
+
+ $notifications = $user_ids = array();
+ $load_special = array();
+ $total_count = $unread_count = 0;
+
+ if ($options['count_unread'])
+ {
+ // Get the total number of unread notifications
+ $sql = 'SELECT COUNT(n.notification_id) AS unread_count
+ FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
+ WHERE n.user_id = ' . (int) $options['user_id'] . '
+ AND n.notification_read = 0
+ AND nt.notification_type_id = n.notification_type_id
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ $unread_count = (int) $this->db->sql_fetchfield('unread_count');
+ $this->db->sql_freeresult($result);
+ }
+
+ if ($options['count_total'])
+ {
+ // Get the total number of notifications
+ $sql = 'SELECT COUNT(n.notification_id) AS total_count
+ FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
+ WHERE n.user_id = ' . (int) $options['user_id'] . '
+ AND nt.notification_type_id = n.notification_type_id
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ $total_count = (int) $this->db->sql_fetchfield('total_count');
+ $this->db->sql_freeresult($result);
+ }
+
+ if (!$options['count_total'] || $total_count)
+ {
+ $rowset = array();
+
+ // Get the main notifications
+ $sql = 'SELECT n.*, nt.notification_type_name
+ FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
+ WHERE n.user_id = ' . (int) $options['user_id'] .
+ (($options['notification_id']) ? ((is_array($options['notification_id'])) ? ' AND ' . $this->db->sql_in_set('n.notification_id', $options['notification_id']) : ' AND n.notification_id = ' . (int) $options['notification_id']) : '') . '
+ AND nt.notification_type_id = n.notification_type_id
+ AND nt.notification_type_enabled = 1
+ ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
+ $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $rowset[$row['notification_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ // Get all unread notifications
+ if ($unread_count && $options['all_unread'] && !empty($rowset))
+ {
+ $sql = 'SELECT n.*, nt.notification_type_name
+ FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
+ WHERE n.user_id = ' . (int) $options['user_id'] . '
+ AND n.notification_read = 0
+ AND ' . $this->db->sql_in_set('n.notification_id', array_keys($rowset), true) . '
+ AND nt.notification_type_id = n.notification_type_id
+ AND nt.notification_type_enabled = 1
+ ORDER BY n.' . $this->db->sql_escape($options['order_by']) . ' ' . $this->db->sql_escape($options['order_dir']);
+ $result = $this->db->sql_query_limit($sql, $options['limit'], $options['start']);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $rowset[$row['notification_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ foreach ($rowset as $row)
+ {
+ $notification = $this->notification_manager->get_item_type_class($row['notification_type_name'], $row);
+
+ // Array of user_ids to query all at once
+ $user_ids = array_merge($user_ids, $notification->users_to_query());
+
+ // Some notification types also require querying additional tables themselves
+ if (!isset($load_special[$row['notification_type_name']]))
+ {
+ $load_special[$row['notification_type_name']] = array();
+ }
+ $load_special[$row['notification_type_name']] = array_merge($load_special[$row['notification_type_name']], $notification->get_load_special());
+
+ $notifications[$row['notification_id']] = $notification;
+ }
+
+ $this->user_loader->load_users($user_ids);
+
+ // Allow each type to load its own special items
+ foreach ($load_special as $item_type => $data)
+ {
+ $item_class = $this->notification_manager->get_item_type_class($item_type);
+
+ $item_class->load_special($data, $notifications);
+ }
+ }
+
+ return array(
+ 'notifications' => $notifications,
+ 'unread_count' => $unread_count,
+ 'total_count' => $total_count,
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function notify()
+ {
+ $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->notifications_table);
+
+ /** @var \phpbb\notification\type\type_interface $notification */
+ foreach ($this->queue as $notification)
+ {
+ $data = $notification->get_insert_array();
+ $insert_buffer->insert($data);
+ }
+
+ $insert_buffer->flush();
+
+ // We're done, empty the queue
+ $this->empty_queue();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function update_notification($notification, array $data, array $options)
+ {
+ // Allow the notifications class to over-ride the update_notifications functionality
+ if (method_exists($notification, 'update_notifications'))
+ {
+ // Return False to over-ride the rest of the update
+ if ($notification->update_notifications($data) === false)
+ {
+ return;
+ }
+ }
+
+ $notification_type_id = $this->notification_manager->get_notification_type_id($notification->get_type());
+ $update_array = $notification->create_update_array($data);
+
+ $sql = 'UPDATE ' . $this->notifications_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $update_array) . '
+ WHERE notification_type_id = ' . (int) $notification_type_id .
+ (isset($options['item_id']) ? ' AND item_id = ' . (int) $options['item_id'] : '') .
+ (isset($options['item_parent_id']) ? ' AND item_parent_id = ' . (int) $options['item_parent_id'] : '') .
+ (isset($options['user_id']) ? ' AND user_id = ' . (int) $options['user_id'] : '') .
+ (isset($options['read']) ? ' AND notification_read = ' . (int) $options['read'] : '');
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true)
+ {
+ $time = ($time !== false) ? $time : time();
+
+ $sql = 'UPDATE ' . $this->notifications_table . '
+ SET notification_read = ' . ($mark_read ? 1 : 0) . '
+ WHERE notification_time <= ' . (int) $time .
+ (($notification_type_id !== false) ? ' AND ' .
+ (is_array($notification_type_id) ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : 'notification_type_id = ' . $notification_type_id) : '') .
+ (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '') .
+ (($item_id !== false) ? ' AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) : '');
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true)
+ {
+ $time = ($time !== false) ? $time : time();
+
+ $sql = 'UPDATE ' . $this->notifications_table . '
+ SET notification_read = ' . ($mark_read ? 1 : 0) . '
+ WHERE notification_time <= ' . (int) $time .
+ (($notification_type_id !== false) ? ' AND ' .
+ (is_array($notification_type_id) ? $this->db->sql_in_set('notification_type_id', $notification_type_id) : 'notification_type_id = ' . $notification_type_id) : '') .
+ (($item_parent_id !== false) ? ' AND ' . (is_array($item_parent_id) ? $this->db->sql_in_set('item_parent_id', $item_parent_id, false, true) : 'item_parent_id = ' . (int) $item_parent_id) : '') .
+ (($user_id !== false) ? ' AND ' . (is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id) : '');
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function mark_notifications_by_id($notification_id, $time = false, $mark_read = true)
+ {
+ $time = ($time !== false) ? $time : time();
+
+ $sql = 'UPDATE ' . $this->notifications_table . '
+ SET notification_read = ' . ($mark_read ? 1 : 0) . '
+ WHERE notification_time <= ' . (int) $time . '
+ AND ' . ((is_array($notification_id)) ? $this->db->sql_in_set('notification_id', $notification_id) : 'notification_id = ' . (int) $notification_id);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function delete_notifications($notification_type_id, $item_id, $parent_id = false, $user_id = false)
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . '
+ WHERE notification_type_id = ' . (int) $notification_type_id . '
+ AND ' . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id) .
+ (($parent_id !== false) ? ' AND ' . ((is_array($parent_id) ? $this->db->sql_in_set('item_parent_id', $parent_id) : 'item_parent_id = ' . (int) $parent_id)) : '') .
+ (($user_id !== false) ? ' AND ' . ((is_array($user_id) ? $this->db->sql_in_set('user_id', $user_id) : 'user_id = ' . (int) $user_id)) : '');
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prune_notifications($timestamp, $only_read = true)
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . '
+ WHERE notification_time < ' . (int) $timestamp .
+ (($only_read) ? ' AND notification_read = 1' : '');
+ $this->db->sql_query($sql);
+
+ $this->config->set('read_notification_last_gc', time(), false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function purge_notifications($notification_type_id)
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . '
+ WHERE notification_type_id = ' . (int) $notification_type_id;
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . $this->notification_types_table . '
+ WHERE notification_type_id = ' . (int) $notification_type_id;
+ $this->db->sql_query($sql);
+
+ $this->cache->destroy('notification_type_ids');
+ }
+}
diff --git a/phpBB/phpbb/notification/method/email.php b/phpBB/phpbb/notification/method/email.php
index e039fae8de..21a6559012 100644
--- a/phpBB/phpbb/notification/method/email.php
+++ b/phpBB/phpbb/notification/method/email.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,11 +16,33 @@ namespace phpbb\notification\method;
/**
* Email notification method class
* This class handles sending emails for notifications
-*
-* @package notifications
*/
+
class email extends \phpbb\notification\method\messenger_base
{
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /**
+ * Notification Method email Constructor
+ *
+ * @param \phpbb\user_loader $user_loader
+ * @param \phpbb\user $user
+ * @param \phpbb\config\config $config
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\user_loader $user_loader, \phpbb\user $user, \phpbb\config\config $config, $phpbb_root_path, $php_ext)
+ {
+ parent::__construct($user_loader, $phpbb_root_path, $php_ext);
+
+ $this->user = $user;
+ $this->config = $config;
+ }
+
/**
* Get notification method name
*
@@ -24,7 +50,7 @@ class email extends \phpbb\notification\method\messenger_base
*/
public function get_type()
{
- return 'email';
+ return 'notification.method.email';
}
/**
diff --git a/phpBB/phpbb/notification/method/jabber.php b/phpBB/phpbb/notification/method/jabber.php
index bdfaf5a6fc..509c6b432c 100644
--- a/phpBB/phpbb/notification/method/jabber.php
+++ b/phpBB/phpbb/notification/method/jabber.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,11 +16,33 @@ namespace phpbb\notification\method;
/**
* Jabber notification method class
* This class handles sending Jabber messages for notifications
-*
-* @package notifications
*/
+
class jabber extends \phpbb\notification\method\messenger_base
{
+ /** @var \phpbb\user */
+ protected $user;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /**
+ * Notification Method jabber Constructor
+ *
+ * @param \phpbb\user_loader $user_loader
+ * @param \phpbb\user $user
+ * @param \phpbb\config\config $config
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\user_loader $user_loader, \phpbb\user $user, \phpbb\config\config $config, $phpbb_root_path, $php_ext)
+ {
+ parent::__construct($user_loader, $phpbb_root_path, $php_ext);
+
+ $this->user = $user;
+ $this->config = $config;
+ }
+
/**
* Get notification method name
*
@@ -24,7 +50,7 @@ class jabber extends \phpbb\notification\method\messenger_base
*/
public function get_type()
{
- return 'jabber';
+ return 'notification.method.jabber';
}
/**
@@ -58,6 +84,6 @@ class jabber extends \phpbb\notification\method\messenger_base
return;
}
- return $this->notify_using_messenger(NOTIFY_IM, 'short/');
+ $this->notify_using_messenger(NOTIFY_IM, 'short/');
}
}
diff --git a/phpBB/phpbb/notification/method/messenger_base.php b/phpBB/phpbb/notification/method/messenger_base.php
index 7cb38eb59d..8a8e284e13 100644
--- a/phpBB/phpbb/notification/method/messenger_base.php
+++ b/phpBB/phpbb/notification/method/messenger_base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,11 +16,32 @@ namespace phpbb\notification\method;
/**
* Abstract notification method handling email and jabber notifications
* using the phpBB messenger.
-*
-* @package notifications
*/
abstract class messenger_base extends \phpbb\notification\method\base
{
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var string */
+ protected $phpbb_root_path;
+
+ /** @var string */
+ protected $php_ext;
+
+ /**
+ * Notification Method Board Constructor
+ *
+ * @param \phpbb\user_loader $user_loader
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\user_loader $user_loader, $phpbb_root_path, $php_ext)
+ {
+ $this->user_loader = $user_loader;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
/**
* Notify using phpBB messenger
*
@@ -55,9 +80,9 @@ abstract class messenger_base extends \phpbb\notification\method\base
include($this->phpbb_root_path . 'includes/functions_messenger.' . $this->php_ext);
}
$messenger = new \messenger();
- $board_url = generate_board_url();
// Time to go through the queue and send emails
+ /** @var \phpbb\notification\type\type_interface $notification */
foreach ($this->queue as $notification)
{
if ($notification->get_email_template() === false)
@@ -67,7 +92,7 @@ abstract class messenger_base extends \phpbb\notification\method\base
$user = $this->user_loader->get_user($notification->user_id);
- if ($user['user_type'] == USER_IGNORE || in_array($notification->user_id, $banned_users))
+ if ($user['user_type'] == USER_IGNORE || ($user['user_type'] == USER_INACTIVE && $user['user_inactive_reason'] == INACTIVE_MANUAL) || in_array($notification->user_id, $banned_users))
{
continue;
}
diff --git a/phpBB/phpbb/notification/method/method_interface.php b/phpBB/phpbb/notification/method/method_interface.php
index 4830d06b86..c2e4940485 100644
--- a/phpBB/phpbb/notification/method/method_interface.php
+++ b/phpBB/phpbb/notification/method/method_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\notification\method;
/**
* Base notifications method interface
-* @package notifications
*/
interface method_interface
{
@@ -23,12 +26,48 @@ interface method_interface
public function get_type();
/**
+ * Is the method enable by default?
+ *
+ * @return bool
+ */
+ public function is_enabled_by_default();
+
+ /**
* Is this method available for the user?
* This is checked on the notifications options
*/
public function is_available();
/**
+ * Return the list of the users already notified
+ *
+ * @param int $notification_type_id Type of the notification
+ * @param array $options
+ * @return array User
+ */
+ public function get_notified_users($notification_type_id, array $options);
+
+ /**
+ * Load the user's notifications
+ *
+ * @param array $options Optional options to control what notifications are loaded
+ * notification_id Notification id to load (or array of notification ids)
+ * user_id User id to load notifications for (Default: $user->data['user_id'])
+ * order_by Order by (Default: notification_time)
+ * order_dir Order direction (Default: DESC)
+ * limit Number of notifications to load (Default: 5)
+ * start Notifications offset (Default: 0)
+ * all_unread Load all unread notifications? If set to true, count_unread is set to true (Default: false)
+ * count_unread Count all unread notifications? (Default: false)
+ * count_total Count all notifications? (Default: false)
+ * @return array Array of information based on the request with keys:
+ * 'notifications' array of notification type objects
+ * 'unread_count' number of unread notifications the user has if count_unread is true in the options
+ * 'total_count' number of notifications the user has if count_total is true in the options
+ */
+ public function load_notifications(array $options = array());
+
+ /**
* Add a notification to the queue
*
* @param \phpbb\notification\type\type_interface $notification
@@ -39,4 +78,72 @@ interface method_interface
* Parse the queue and notify the users
*/
public function notify();
+
+ /**
+ * Update a notification
+ *
+ * @param \phpbb\notification\type\type_interface $notification Notification to update
+ * @param array $data Data specific for this type that will be updated
+ * @param array $options
+ */
+ public function update_notification($notification, array $data, array $options);
+
+ /**
+ * Mark notifications read or unread
+ *
+ * @param bool|string $notification_type_id Type identifier of item types. False to mark read for all item types
+ * @param bool|int|array $item_id Item id or array of item ids. False to mark read for all item ids
+ * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
+ */
+ public function mark_notifications($notification_type_id, $item_id, $user_id, $time = false, $mark_read = true);
+
+ /**
+ * Mark notifications read or unread from a parent identifier
+ *
+ * @param string $notification_type_id Type identifier of item types
+ * @param bool|int|array $item_parent_id Item parent id or array of item parent ids. False to mark read for all item parent ids
+ * @param bool|int|array $user_id User id or array of user ids. False to mark read for all user ids
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
+ */
+ public function mark_notifications_by_parent($notification_type_id, $item_parent_id, $user_id, $time = false, $mark_read = true);
+
+ /**
+ * Mark notifications read or unread
+ *
+ * @param int $notification_id Notification id of notification ids.
+ * @param bool|int $time Time at which to mark all notifications prior to as read. False to mark all as read. (Default: False)
+ * @param bool $mark_read Define if the notification as to be set to True or False. (Default: True)
+ */
+ public function mark_notifications_by_id($notification_id, $time = false, $mark_read = true);
+
+ /**
+ * Delete a notification
+ *
+ * @param string $notification_type_id Type identifier of item types
+ * @param int|array $item_id Identifier within the type (or array of ids)
+ * @param mixed $parent_id Parent identifier within the type (or array of ids), used in combination with item_id if specified (Default: false; not checked)
+ * @param mixed $user_id User id (Default: false; not checked)
+ */
+ public function delete_notifications($notification_type_id, $item_id, $parent_id = false, $user_id = false);
+
+ /**
+ * Delete all notifications older than a certain time
+ *
+ * @param int $timestamp Unix timestamp to delete all notifications that were created before
+ * @param bool $only_read True (default) to only prune read notifications
+ */
+ public function prune_notifications($timestamp, $only_read = true);
+
+ /**
+ * Purge all notifications of a certain type
+ *
+ * This should be called when an extension which has notification types
+ * is purged so that all those notifications are removed
+ *
+ * @param string $notification_type_id Type identifier of the subscription
+ */
+ public function purge_notifications($notification_type_id);
}
diff --git a/phpBB/phpbb/notification/type/admin_activate_user.php b/phpBB/phpbb/notification/type/admin_activate_user.php
index 5f146e18ff..9f2ae857ef 100644
--- a/phpBB/phpbb/notification/type/admin_activate_user.php
+++ b/phpBB/phpbb/notification/type/admin_activate_user.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Admin activation notifications class
* This class handles notifications for users requiring admin activation
-*
-* @package notifications
*/
+
class admin_activate_user extends \phpbb\notification\type\base
{
/**
@@ -22,7 +25,7 @@ class admin_activate_user extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'admin_activate_user';
+ return 'notification.type.admin_activate_user';
}
/**
@@ -33,11 +36,27 @@ class admin_activate_user extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_ADMIN_ACTIVATE_USER',
'group' => 'NOTIFICATION_GROUP_ADMINISTRATION',
);
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ public function set_config(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ public function set_user_loader(\phpbb\user_loader $user_loader)
+ {
+ $this->user_loader = $user_loader;
+ }
+
/**
* {@inheritdoc}
*/
@@ -49,7 +68,7 @@ class admin_activate_user extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_id($user)
+ static public function get_item_id($user)
{
return (int) $user['user_id'];
}
@@ -57,7 +76,7 @@ class admin_activate_user extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_parent_id($post)
+ static public function get_item_parent_id($post)
{
return 0;
}
@@ -81,7 +100,7 @@ class admin_activate_user extends \phpbb\notification\type\base
WHERE user_type = ' . USER_FOUNDER;
$result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($sql))
+ while ($row = $this->db->sql_fetchrow($result))
{
$users[] = (int) $row['user_id'];
}
@@ -101,7 +120,7 @@ class admin_activate_user extends \phpbb\notification\type\base
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->item_id);
+ return $this->user_loader->get_avatar($this->item_id, false, true);
}
/**
@@ -111,7 +130,7 @@ class admin_activate_user extends \phpbb\notification\type\base
{
$username = $this->user_loader->get_username($this->item_id, 'no_profile');
- return $this->user->lang($this->language_key, $username);
+ return $this->language->lang($this->language_key, $username);
}
/**
@@ -128,7 +147,7 @@ class admin_activate_user extends \phpbb\notification\type\base
public function get_email_template_variables()
{
$board_url = generate_board_url();
- $username = $this->user_loader->get_username($this->item_id, 'no_profile');
+ $username = $this->user_loader->get_username($this->item_id, 'username');
return array(
'USERNAME' => htmlspecialchars_decode($username),
@@ -142,7 +161,7 @@ class admin_activate_user extends \phpbb\notification\type\base
*/
public function get_url()
{
- return append_sid($this->phpbb_root_path . 'memberlist.' . $this->php_ext, "mode=viewprofile&u={$this->item_id}");
+ return $this->user_loader->get_username($this->item_id, 'profile');
}
/**
@@ -161,6 +180,6 @@ class admin_activate_user extends \phpbb\notification\type\base
$this->set_data('user_actkey', $user['user_actkey']);
$this->notification_time = $user['user_regdate'];
- return parent::create_insert_array($user, $pre_create_data);
+ parent::create_insert_array($user, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/approve_post.php b/phpBB/phpbb/notification/type/approve_post.php
index e51ff12b3e..e4b111e4da 100644
--- a/phpBB/phpbb/notification/type/approve_post.php
+++ b/phpBB/phpbb/notification/type/approve_post.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Post approved notifications class
* This class handles notifications for posts when they are approved (to their authors)
-*
-* @package notifications
*/
+
class approve_post extends \phpbb\notification\type\post
{
/**
@@ -24,7 +27,7 @@ class approve_post extends \phpbb\notification\type\post
*/
public function get_type()
{
- return 'approve_post';
+ return 'notification.type.approve_post';
}
/**
@@ -47,7 +50,7 @@ class approve_post extends \phpbb\notification\type\post
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'id' => 'moderation_queue',
'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_POSTING',
@@ -64,7 +67,8 @@ class approve_post extends \phpbb\notification\type\post
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -75,17 +79,10 @@ class approve_post extends \phpbb\notification\type\post
), $options);
$users = array();
- $users[$post['poster_id']] = array('');
+ $users[$post['poster_id']] = $this->notification_manager->get_default_methods();
- $auth_read = $this->auth->acl_get_list(array_keys($users), 'f_read', $post['forum_id']);
-
- if (empty($auth_read))
- {
- return array();
- }
-
- return $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], array_merge($options, array(
- 'item_type' => self::$notification_option['id'],
+ return $this->get_authorised_recipients(array_keys($users), $post['forum_id'], array_merge($options, array(
+ 'item_type' => static::$notification_option['id'],
)));
}
@@ -110,21 +107,24 @@ class approve_post extends \phpbb\notification\type\post
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
$this->set_data('post_subject', $post['post_subject']);
- $data = parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
- $this->notification_time = $data['notification_time'] = time();
+ $this->notification_time = time();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
@@ -138,4 +138,12 @@ class approve_post extends \phpbb\notification\type\post
{
return 'post_approved';
}
+
+ /**
+ * {inheritDoc}
+ */
+ public function get_redirect_url()
+ {
+ return $this->get_url();
+ }
}
diff --git a/phpBB/phpbb/notification/type/approve_topic.php b/phpBB/phpbb/notification/type/approve_topic.php
index 11a240e03d..f8a3fdec6f 100644
--- a/phpBB/phpbb/notification/type/approve_topic.php
+++ b/phpBB/phpbb/notification/type/approve_topic.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Topic approved notifications class
* This class handles notifications for topics when they are approved (for authors)
-*
-* @package notifications
*/
+
class approve_topic extends \phpbb\notification\type\topic
{
/**
@@ -24,7 +27,7 @@ class approve_topic extends \phpbb\notification\type\topic
*/
public function get_type()
{
- return 'approve_topic';
+ return 'notification.type.approve_topic';
}
/**
@@ -47,7 +50,7 @@ class approve_topic extends \phpbb\notification\type\topic
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'id' => 'moderation_queue',
'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_POSTING',
@@ -64,7 +67,8 @@ class approve_topic extends \phpbb\notification\type\topic
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -75,17 +79,10 @@ class approve_topic extends \phpbb\notification\type\topic
), $options);
$users = array();
- $users[$post['poster_id']] = array('');
-
- $auth_read = $this->auth->acl_get_list(array_keys($users), 'f_read', $post['forum_id']);
+ $users[$post['poster_id']] = $this->notification_manager->get_default_methods();
- if (empty($auth_read))
- {
- return array();
- }
-
- return $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], array_merge($options, array(
- 'item_type' => self::$notification_option['id'],
+ return $this->get_authorised_recipients(array_keys($users), $post['forum_id'], array_merge($options, array(
+ 'item_type' => static::$notification_option['id'],
)));
}
@@ -110,19 +107,23 @@ class approve_topic extends \phpbb\notification\type\topic
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
- $data = parent::create_insert_array($post, $pre_create_data);
- $this->notification_time = $data['notification_time'] = time();
+ parent::create_insert_array($post, $pre_create_data);
+
+ $this->notification_time = time();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/base.php b/phpBB/phpbb/notification/type/base.php
index 10c876b286..4aacb1c99e 100644
--- a/phpBB/phpbb/notification/type/base.php
+++ b/phpBB/phpbb/notification/type/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,24 +15,17 @@ namespace phpbb\notification\type;
/**
* Base notifications class
-* @package notifications
*/
abstract class base implements \phpbb\notification\type\type_interface
{
/** @var \phpbb\notification\manager */
protected $notification_manager;
- /** @var \phpbb\user_loader */
- protected $user_loader;
-
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
- /** @var \phpbb\cache\driver\driver_interface */
- protected $cache;
-
- /** @var \phpbb\template\template */
- protected $template;
+ /** @var \phpbb\language\language */
+ protected $language;
/** @var \phpbb\user */
protected $user;
@@ -36,9 +33,6 @@ abstract class base implements \phpbb\notification\type\type_interface
/** @var \phpbb\auth\auth */
protected $auth;
- /** @var \phpbb\config\config */
- protected $config;
-
/** @var string */
protected $phpbb_root_path;
@@ -46,12 +40,6 @@ abstract class base implements \phpbb\notification\type\type_interface
protected $php_ext;
/** @var string */
- protected $notification_types_table;
-
- /** @var string */
- protected $notifications_table;
-
- /** @var string */
protected $user_notifications_table;
/**
@@ -60,7 +48,7 @@ abstract class base implements \phpbb\notification\type\type_interface
* @var bool|array False if the service should use its default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = false;
+ static public $notification_option = false;
/**
* The notification_type_id, set upon creation of the class
@@ -71,7 +59,7 @@ abstract class base implements \phpbb\notification\type\type_interface
protected $notification_type_id;
/**
- * Indentification data
+ * Identification data
* notification_type_id - ID of the item type (auto generated, from notification types table)
* item_id - ID of the item (e.g. post_id, msg_id)
* item_parent_id - Parent item id (ex: for topic => forum_id, for post => topic_id, etc)
@@ -86,35 +74,26 @@ abstract class base implements \phpbb\notification\type\type_interface
private $data = array();
/**
- * Notification Type Base Constructor
- *
- * @param \phpbb\user_loader $user_loader
- * @param \phpbb\db\driver\driver $db
- * @param \phpbb\cache\driver\driver_interface $cache
- * @param \phpbb\user $user
- * @param \phpbb\auth\auth $auth
- * @param \phpbb\config\config $config
- * @param string $phpbb_root_path
- * @param string $php_ext
- * @param string $notification_types_table
- * @param string $notifications_table
- * @param string $user_notifications_table
- * @return \phpbb\notification\type\base
- */
- public function __construct(\phpbb\user_loader $user_loader, \phpbb\db\driver\driver $db, \phpbb\cache\driver\driver_interface $cache, $user, \phpbb\auth\auth $auth, \phpbb\config\config $config, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table)
+ * Notification Type Base Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\language\language $language
+ * @param \phpbb\user $user
+ * @param \phpbb\auth\auth $auth
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ * @param string $user_notifications_table
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\language\language $language, \phpbb\user $user, \phpbb\auth\auth $auth, $phpbb_root_path, $php_ext, $user_notifications_table)
{
- $this->user_loader = $user_loader;
$this->db = $db;
- $this->cache = $cache;
+ $this->language = $language;
$this->user = $user;
$this->auth = $auth;
- $this->config = $config;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
- $this->notification_types_table = $notification_types_table;
- $this->notifications_table = $notifications_table;
$this->user_notifications_table = $user_notifications_table;
}
@@ -158,6 +137,8 @@ abstract class base implements \phpbb\notification\type\type_interface
* Magic method to set data on this notification
*
* @param mixed $name
+ * @param mixed $value
+ *
* @return null
*/
public function __set($name, $value)
@@ -171,7 +152,6 @@ abstract class base implements \phpbb\notification\type\type_interface
*
* Primarily for testing
*
- * @param string $name
* @return mixed
*/
public function __toString()
@@ -203,12 +183,7 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $type_data Data unique to this notification type
- * @param array $pre_create_data Data from pre_create_insert_array()
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($type_data, $pre_create_data = array())
{
@@ -221,9 +196,15 @@ abstract class base implements \phpbb\notification\type\type_interface
'notification_time' => time(),
'notification_read' => false,
- 'notification_data' => array(),
+ 'notification_data' => array(),
), $this->data);
+ }
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
$data = $this->data;
$data['notification_data'] = serialize($data['notification_data']);
@@ -240,7 +221,8 @@ abstract class base implements \phpbb\notification\type\type_interface
*/
public function create_update_array($type_data)
{
- $data = $this->create_insert_array($type_data);
+ $this->create_insert_array($type_data);
+ $data = $this->get_insert_array();
// Unset data unique to each row
unset(
@@ -276,6 +258,14 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
+ * {inheritDoc}
+ */
+ public function get_redirect_url()
+ {
+ return $this->get_url();
+ }
+
+ /**
* Prepare to output the notification to the template
*
* @return array Template variables
@@ -297,16 +287,15 @@ abstract class base implements \phpbb\notification\type\type_interface
return array(
'NOTIFICATION_ID' => $this->notification_id,
-
+ 'STYLING' => $this->get_style_class(),
'AVATAR' => $this->get_avatar(),
-
'FORMATTED_TITLE' => $this->get_title(),
-
+ 'REFERENCE' => $this->get_reference(),
+ 'FORUM' => $this->get_forum(),
+ 'REASON' => $this->get_reason(),
'URL' => $this->get_url(),
'TIME' => $this->user->format_date($this->notification_time),
-
'UNREAD' => !$this->notification_read,
-
'U_MARK_READ' => (!$this->notification_read) ? $u_mark_read : '',
);
}
@@ -319,6 +308,7 @@ abstract class base implements \phpbb\notification\type\type_interface
* URL to unsubscribe to this notification (fall back)
*
* @param string|bool $method Method name to unsubscribe from (email|jabber|etc), False to unsubscribe from all notifications for this item
+ * @return false
*/
public function get_unsubscribe_url($method = false)
{
@@ -326,6 +316,16 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
+ * Get the CSS style class of the notification (fall back)
+ *
+ * @return string
+ */
+ public function get_style_class()
+ {
+ return '';
+ }
+
+ /**
* Get the user's avatar (fall back)
*
* @return string
@@ -336,6 +336,36 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
+ * Get the reference of the notifcation (fall back)
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return '';
+ }
+
+ /**
+ * Get the forum of the notification reference (fall back)
+ *
+ * @return string
+ */
+ public function get_forum()
+ {
+ return '';
+ }
+
+ /**
+ * Get the reason for the notifcation (fall back)
+ *
+ * @return string
+ */
+ public function get_reason()
+ {
+ return '';
+ }
+
+ /**
* Get the special items to load (fall back)
*
* @return array
@@ -346,8 +376,11 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
- * Load the special items (fall back)
- */
+ * Load the special items (fall back)
+ *
+ * @param array $data
+ * @param array $notifications
+ */
public function load_special($data, $notifications)
{
return;
@@ -364,10 +397,12 @@ abstract class base implements \phpbb\notification\type\type_interface
}
/**
- * Pre create insert array function (fall back)
- *
- * @return array
- */
+ * Pre create insert array function (fall back)
+ *
+ * @param array $type_data
+ * @param array $notify_users
+ * @return array
+ */
public function pre_create_insert_array($type_data, $notify_users)
{
return array();
@@ -378,13 +413,13 @@ abstract class base implements \phpbb\notification\type\type_interface
*/
/**
- * Find the users who want to receive notifications (helper)
- *
- * @param array $user_ids User IDs to check if they want to receive notifications
- * (Bool False to check all users besides anonymous and bots (USER_IGNORE))
- *
- * @return array
- */
+ * Find the users who want to receive notifications (helper)
+ *
+ * @param array|bool $user_ids User IDs to check if they want to receive notifications
+ * (Bool False to check all users besides anonymous and bots (USER_IGNORE))
+ * @param array $options
+ * @return array
+ */
protected function check_user_notification_options($user_ids = false, $options = array())
{
$options = array_merge(array(
@@ -446,8 +481,8 @@ abstract class base implements \phpbb\notification\type\type_interface
{
if (!in_array($user_id, $resulting_user_ids) && !isset($options['ignore_users'][$user_id]))
{
- // No rows at all for this user, default to ''
- $rowset[$user_id] = array('');
+ // No rows at all for this user, use the default methods
+ $rowset[$user_id] = $this->notification_manager->get_default_methods();
}
}
@@ -465,21 +500,55 @@ abstract class base implements \phpbb\notification\type\type_interface
{
$this->notification_read = (bool) !$unread;
- $where = array(
- 'notification_type_id = ' . (int) $this->notification_type_id,
- 'item_id = ' . (int) $this->item_id,
- 'user_id = ' . (int) $this->user_id,
- );
- $where = implode(' AND ', $where);
-
if ($return)
{
+ $where = array(
+ 'notification_type_id = ' . (int) $this->notification_type_id,
+ 'item_id = ' . (int) $this->item_id,
+ 'user_id = ' . (int) $this->user_id,
+ );
+
+ $where = implode(' AND ', $where);
return $where;
}
+ else
+ {
+ $this->notification_manager->mark_notifications($this->get_type(), (int) $this->item_id, (int) $this->user_id, false, $this->notification_read);
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a list of users that are authorised to receive notifications
+ *
+ * @param array $users Array of users that have subscribed to a notification
+ * @param int $forum_id Forum ID of the forum
+ * @param array $options Array of notification options
+ * @param bool $sort Whether the users array should be sorted. Default: false
+ * @return array Array of users that are authorised recipients
+ */
+ protected function get_authorised_recipients($users, $forum_id, $options, $sort = false)
+ {
+ if (empty($users))
+ {
+ return array();
+ }
+
+ $users = array_unique($users);
+
+ if ($sort)
+ {
+ sort($users);
+ }
+
+ $auth_read = $this->auth->acl_get_list($users, 'f_read', $forum_id);
+
+ if (empty($auth_read))
+ {
+ return array();
+ }
- $sql = 'UPDATE ' . $this->notifications_table . '
- SET notification_read = ' . (int) $this->notification_read . '
- WHERE ' . $where;
- $this->db->sql_query($sql);
+ return $this->check_user_notification_options($auth_read[$forum_id]['f_read'], $options);
}
}
diff --git a/phpBB/phpbb/notification/type/bookmark.php b/phpBB/phpbb/notification/type/bookmark.php
index 5e6fdd2523..ebbb961c57 100644
--- a/phpBB/phpbb/notification/type/bookmark.php
+++ b/phpBB/phpbb/notification/type/bookmark.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Bookmark updating notifications class
* This class handles notifications for replies to a bookmarked topic
-*
-* @package notifications
*/
+
class bookmark extends \phpbb\notification\type\post
{
/**
@@ -24,7 +27,7 @@ class bookmark extends \phpbb\notification\type\post
*/
public function get_type()
{
- return 'bookmark';
+ return 'notification.type.bookmark';
}
/**
@@ -40,7 +43,7 @@ class bookmark extends \phpbb\notification\type\post
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_BOOKMARK',
'group' => 'NOTIFICATION_GROUP_POSTING',
);
@@ -56,7 +59,8 @@ class bookmark extends \phpbb\notification\type\post
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -75,47 +79,39 @@ class bookmark extends \phpbb\notification\type\post
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
- $users[] = $row['user_id'];
+ $users[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
- if (empty($users))
- {
- return array();
- }
- sort($users);
-
- $auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']);
+ $notify_users = $this->get_authorised_recipients($users, $post['forum_id'], $options, true);
- if (empty($auth_read))
+ if (empty($notify_users))
{
return array();
}
- $notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options);
-
// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications
- $update_notifications = array();
- $sql = 'SELECT n.*
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.notification_type_id = ' . (int) $this->notification_type_id . '
- AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '
- AND n.notification_read = 0
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
+ $notified_users = $this->notification_manager->get_notified_users($this->get_type(), array(
+ 'item_parent_id' => static::get_item_parent_id($post),
+ 'read' => 0,
+ ));
+
+ foreach ($notified_users as $user => $notification_data)
{
- // Do not create a new notification
- unset($notify_users[$row['user_id']]);
-
- $notification = $this->notification_manager->get_item_type_class($this->get_type(), $row);
- $sql = 'UPDATE ' . $this->notifications_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . '
- WHERE notification_id = ' . $row['notification_id'];
- $this->db->sql_query($sql);
+ unset($notify_users[$user]);
+
+ /** @var bookmark $notification */
+ $notification = $this->notification_manager->get_item_type_class($this->get_type(), $notification_data);
+ $update_responders = $notification->add_responders($post);
+ if (!empty($update_responders))
+ {
+ $this->notification_manager->update_notification($notification, $update_responders, array(
+ 'item_parent_id' => self::get_item_parent_id($post),
+ 'read' => 0,
+ 'user_id' => $user,
+ ));
+ }
}
- $this->db->sql_freeresult($result);
return $notify_users;
}
diff --git a/phpBB/phpbb/notification/type/disapprove_post.php b/phpBB/phpbb/notification/type/disapprove_post.php
index 70de2a3e10..2d908eb254 100644
--- a/phpBB/phpbb/notification/type/disapprove_post.php
+++ b/phpBB/phpbb/notification/type/disapprove_post.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Post disapproved notifications class
* This class handles notifications for posts when they are disapproved (for authors)
-*
-* @package notifications
*/
+
class disapprove_post extends \phpbb\notification\type\approve_post
{
/**
@@ -24,7 +27,17 @@ class disapprove_post extends \phpbb\notification\type\approve_post
*/
public function get_type()
{
- return 'disapprove_post';
+ return 'notification.type.disapprove_post';
+ }
+
+ /**
+ * Get the CSS style class of the notification
+ *
+ * @return string
+ */
+ public function get_style_class()
+ {
+ return 'notification-disapproved';
}
/**
@@ -47,7 +60,7 @@ class disapprove_post extends \phpbb\notification\type\approve_post
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'id' => 'moderation_queue',
'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_POSTING',
@@ -60,9 +73,31 @@ class disapprove_post extends \phpbb\notification\type\approve_post
*/
public function get_title()
{
- return $this->user->lang(
- $this->language_key,
- censor_text($this->get_data('topic_title')),
+ return $this->language->lang($this->language_key);
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('topic_title'))
+ );
+ }
+
+ /**
+ * Get the reason for the disapproval notification
+ *
+ * @return string
+ */
+ public function get_reason()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('disapprove_reason')
);
}
@@ -90,21 +125,24 @@ class disapprove_post extends \phpbb\notification\type\approve_post
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
$this->set_data('disapprove_reason', $post['disapprove_reason']);
- $data = parent::create_insert_array($post);
+ parent::create_insert_array($post, $pre_create_data);
+
+ $this->notification_time = time();
+ }
- $this->notification_time = $data['notification_time'] = time();
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/disapprove_topic.php b/phpBB/phpbb/notification/type/disapprove_topic.php
index d39201d928..c2522fb562 100644
--- a/phpBB/phpbb/notification/type/disapprove_topic.php
+++ b/phpBB/phpbb/notification/type/disapprove_topic.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Topic disapproved notifications class
* This class handles notifications for topics when they are disapproved (for authors)
-*
-* @package notifications
*/
+
class disapprove_topic extends \phpbb\notification\type\approve_topic
{
/**
@@ -24,7 +27,17 @@ class disapprove_topic extends \phpbb\notification\type\approve_topic
*/
public function get_type()
{
- return 'disapprove_topic';
+ return 'notification.type.disapprove_topic';
+ }
+
+ /**
+ * Get the CSS style class of the notification
+ *
+ * @return string
+ */
+ public function get_style_class()
+ {
+ return 'notification-disapproved';
}
/**
@@ -47,7 +60,7 @@ class disapprove_topic extends \phpbb\notification\type\approve_topic
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'id' => 'moderation_queue',
'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_POSTING',
@@ -60,9 +73,31 @@ class disapprove_topic extends \phpbb\notification\type\approve_topic
*/
public function get_title()
{
- return $this->user->lang(
- $this->language_key,
- censor_text($this->get_data('topic_title')),
+ return $this->language->lang($this->language_key);
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('topic_title'))
+ );
+ }
+
+ /**
+ * Get the reason for the disapproval notification
+ *
+ * @return string
+ */
+ public function get_reason()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('disapprove_reason')
);
}
@@ -90,21 +125,24 @@ class disapprove_topic extends \phpbb\notification\type\approve_topic
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
$this->set_data('disapprove_reason', $post['disapprove_reason']);
- $data = parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
+
+ $this->notification_time = time();
+ }
- $this->notification_time = $data['notification_time'] = time();
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/group_request.php b/phpBB/phpbb/notification/type/group_request.php
index e0527fe220..28a9e73bf9 100644
--- a/phpBB/phpbb/notification/type/group_request.php
+++ b/phpBB/phpbb/notification/type/group_request.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -16,16 +20,24 @@ class group_request extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'group_request';
+ return 'notification.type.group_request';
}
/**
* {@inheritdoc}
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_GROUP_REQUEST',
);
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ public function set_user_loader(\phpbb\user_loader $user_loader)
+ {
+ $this->user_loader = $user_loader;
+ }
+
/**
* {@inheritdoc}
*/
@@ -46,7 +58,7 @@ class group_request extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_id($group)
+ static public function get_item_id($group)
{
return (int) $group['user_id'];
}
@@ -54,7 +66,7 @@ class group_request extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_parent_id($group)
+ static public function get_item_parent_id($group)
{
// Group id is the parent
return (int) $group['group_id'];
@@ -92,7 +104,7 @@ class group_request extends \phpbb\notification\type\base
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->item_id);
+ return $this->user_loader->get_avatar($this->item_id, false, true);
}
/**
@@ -102,7 +114,7 @@ class group_request extends \phpbb\notification\type\base
{
$username = $this->user_loader->get_username($this->item_id, 'no_profile');
- return $this->user->lang('NOTIFICATION_GROUP_REQUEST', $username, $this->get_data('group_name'));
+ return $this->language->lang('NOTIFICATION_GROUP_REQUEST', $username, $this->get_data('group_name'));
}
/**
@@ -152,6 +164,6 @@ class group_request extends \phpbb\notification\type\base
{
$this->set_data('group_name', $group['group_name']);
- return parent::create_insert_array($group, $pre_create_data);
+ parent::create_insert_array($group, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/group_request_approved.php b/phpBB/phpbb/notification/type/group_request_approved.php
index 448f049412..f55d28bafd 100644
--- a/phpBB/phpbb/notification/type/group_request_approved.php
+++ b/phpBB/phpbb/notification/type/group_request_approved.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -16,7 +20,7 @@ class group_request_approved extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'group_request_approved';
+ return 'notification.type.group_request_approved';
}
/**
@@ -30,7 +34,7 @@ class group_request_approved extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_id($group)
+ static public function get_item_id($group)
{
return (int) $group['group_id'];
}
@@ -38,7 +42,7 @@ class group_request_approved extends \phpbb\notification\type\base
/**
* {@inheritdoc}
*/
- public static function get_item_parent_id($group)
+ static public function get_item_parent_id($group)
{
return 0;
}
@@ -54,7 +58,7 @@ class group_request_approved extends \phpbb\notification\type\base
foreach ($group['user_ids'] as $user_id)
{
- $users[$user_id] = array('');
+ $users[$user_id] = $this->notification_manager->get_default_methods();
}
return $users;
@@ -65,7 +69,7 @@ class group_request_approved extends \phpbb\notification\type\base
*/
public function get_title()
{
- return $this->user->lang('NOTIFICATION_GROUP_REQUEST_APPROVED', $this->get_data('group_name'));
+ return $this->language->lang('NOTIFICATION_GROUP_REQUEST_APPROVED', $this->get_data('group_name'));
}
/**
@@ -83,7 +87,7 @@ class group_request_approved extends \phpbb\notification\type\base
{
$this->set_data('group_name', $group['group_name']);
- return parent::create_insert_array($group, $pre_create_data);
+ parent::create_insert_array($group, $pre_create_data);
}
/**
diff --git a/phpBB/phpbb/notification/type/pm.php b/phpBB/phpbb/notification/type/pm.php
index 584a30efa6..8fb9172911 100644
--- a/phpBB/phpbb/notification/type/pm.php
+++ b/phpBB/phpbb/notification/type/pm.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Private message notifications class
* This class handles notifications for private messages
-*
-* @package notifications
*/
+
class pm extends \phpbb\notification\type\base
{
/**
@@ -24,7 +27,7 @@ class pm extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'pm';
+ return 'notification.type.pm';
}
/**
@@ -33,10 +36,26 @@ class pm extends \phpbb\notification\type\base
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_PM',
);
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ public function set_config(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ public function set_user_loader(\phpbb\user_loader $user_loader)
+ {
+ $this->user_loader = $user_loader;
+ }
+
/**
* Is available
*/
@@ -50,7 +69,7 @@ class pm extends \phpbb\notification\type\base
*
* @param array $pm The data from the private message
*/
- public static function get_item_id($pm)
+ static public function get_item_id($pm)
{
return (int) $pm['msg_id'];
}
@@ -60,7 +79,7 @@ class pm extends \phpbb\notification\type\base
*
* @param array $pm The data from the pm
*/
- public static function get_item_parent_id($pm)
+ static public function get_item_parent_id($pm)
{
// No parent
return 0;
@@ -69,7 +88,8 @@ class pm extends \phpbb\notification\type\base
/**
* Find the users who want to receive notifications
*
- * @param array $pm Data from
+ * @param array $pm Data from submit_pm
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -96,7 +116,7 @@ class pm extends \phpbb\notification\type\base
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('from_user_id'));
+ return $this->user_loader->get_avatar($this->get_data('from_user_id'), false, true);
}
/**
@@ -108,7 +128,20 @@ class pm extends \phpbb\notification\type\base
{
$username = $this->user_loader->get_username($this->get_data('from_user_id'), 'no_profile');
- return $this->user->lang('NOTIFICATION_PM', $username, $this->get_data('message_subject'));
+ return $this->language->lang('NOTIFICATION_PM', $username);
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ $this->get_data('message_subject')
+ );
}
/**
@@ -159,13 +192,7 @@ class pm extends \phpbb\notification\type\base
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($pm, $pre_create_data = array())
{
@@ -173,6 +200,6 @@ class pm extends \phpbb\notification\type\base
$this->set_data('message_subject', $pm['message_subject']);
- return parent::create_insert_array($pm, $pre_create_data);
+ parent::create_insert_array($pm, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/post.php b/phpBB/phpbb/notification/type/post.php
index bc42c4422b..b9afc6d70a 100644
--- a/phpBB/phpbb/notification/type/post.php
+++ b/phpBB/phpbb/notification/type/post.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Post notifications class
* This class handles notifications for replies to a topic
-*
-* @package notifications
*/
+
class post extends \phpbb\notification\type\base
{
/**
@@ -24,7 +27,7 @@ class post extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'post';
+ return 'notification.type.post';
}
/**
@@ -47,11 +50,27 @@ class post extends \phpbb\notification\type\base
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_POST',
'group' => 'NOTIFICATION_GROUP_POSTING',
);
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ public function set_config(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ public function set_user_loader(\phpbb\user_loader $user_loader)
+ {
+ $this->user_loader = $user_loader;
+ }
+
/**
* Is available
*/
@@ -64,8 +83,9 @@ class post extends \phpbb\notification\type\base
* Get the id of the item
*
* @param array $post The data from the post
+ * @return int The post id
*/
- public static function get_item_id($post)
+ static public function get_item_id($post)
{
return (int) $post['post_id'];
}
@@ -74,8 +94,9 @@ class post extends \phpbb\notification\type\base
* Get the id of the parent
*
* @param array $post The data from the post
+ * @return int The topic id
*/
- public static function get_item_parent_id($post)
+ static public function get_item_parent_id($post)
{
return (int) $post['topic_id'];
}
@@ -83,7 +104,8 @@ class post extends \phpbb\notification\type\base
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -103,7 +125,7 @@ class post extends \phpbb\notification\type\base
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
- $users[] = $row['user_id'];
+ $users[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
@@ -115,49 +137,39 @@ class post extends \phpbb\notification\type\base
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
- $users[] = $row['user_id'];
+ $users[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
- if (empty($users))
- {
- return array();
- }
-
- $users = array_unique($users);
- sort($users);
-
- $auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']);
+ $notify_users = $this->get_authorised_recipients($users, $post['forum_id'], $options, true);
- if (empty($auth_read))
+ if (empty($notify_users))
{
return array();
}
- $notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options);
-
// Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications
- $update_notifications = array();
- $sql = 'SELECT n.*
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.notification_type_id = ' . (int) $this->notification_type_id . '
- AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '
- AND n.notification_read = 0
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
+ $notified_users = $this->notification_manager->get_notified_users($this->get_type(), array(
+ 'item_parent_id' => static::get_item_parent_id($post),
+ 'read' => 0,
+ ));
+
+ foreach ($notified_users as $user => $notification_data)
{
- // Do not create a new notification
- unset($notify_users[$row['user_id']]);
-
- $notification = $this->notification_manager->get_item_type_class($this->get_type(), $row);
- $sql = 'UPDATE ' . $this->notifications_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . '
- WHERE notification_id = ' . $row['notification_id'];
- $this->db->sql_query($sql);
+ unset($notify_users[$user]);
+
+ /** @var post $notification */
+ $notification = $this->notification_manager->get_item_type_class($this->get_type(), $notification_data);
+ $update_responders = $notification->add_responders($post);
+ if (!empty($update_responders))
+ {
+ $this->notification_manager->update_notification($notification, $update_responders, array(
+ 'item_parent_id' => self::get_item_parent_id($post),
+ 'read' => 0,
+ 'user_id' => $user,
+ ));
+ }
}
- $this->db->sql_freeresult($result);
return $notify_users;
}
@@ -167,7 +179,7 @@ class post extends \phpbb\notification\type\base
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('poster_id'));
+ return $this->user_loader->get_avatar($this->get_data('poster_id'), false, true);
}
/**
@@ -205,18 +217,33 @@ class post extends \phpbb\notification\type\base
$usernames[] = $this->user_loader->get_username($responder['poster_id'], 'no_profile');
}
}
- $lang_key = $this->language_key;
- if ($trimmed_responders_cnt)
+ if ($trimmed_responders_cnt > 20)
+ {
+ $usernames[] = $this->language->lang('NOTIFICATION_MANY_OTHERS');
+ }
+ else if ($trimmed_responders_cnt)
{
- $lang_key .= '_TRIMMED';
+ $usernames[] = $this->language->lang('NOTIFICATION_X_OTHERS', $trimmed_responders_cnt);
}
- return $this->user->lang(
- $lang_key,
- implode($this->user->lang['COMMA_SEPARATOR'], $usernames),
- censor_text($this->get_data('topic_title')),
- $trimmed_responders_cnt
+ return $this->language->lang(
+ $this->language_key,
+ phpbb_generate_string_list($usernames, $this->user),
+ $responders_cnt
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('topic_title'))
);
}
@@ -271,6 +298,14 @@ class post extends \phpbb\notification\type\base
}
/**
+ * {inheritDoc}
+ */
+ public function get_redirect_url()
+ {
+ return append_sid($this->phpbb_root_path . 'viewtopic.' . $this->php_ext, "t={$this->item_parent_id}&amp;view=unread#unread");
+ }
+
+ /**
* Users needed to query before this notification can be displayed
*
* @return array Array of user_ids
@@ -336,18 +371,13 @@ class post extends \phpbb\notification\type\base
{
$tracking_data[$row['user_id']] = $row['mark_time'];
}
+ $this->db->sql_freeresult($result);
return $tracking_data;
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
@@ -372,32 +402,41 @@ class post extends \phpbb\notification\type\base
$this->notification_read = true;
}
- return parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
}
/**
* Add responders to the notification
*
* @param mixed $post
+ * @return array Array of responder data
*/
public function add_responders($post)
{
// Do not add them as a responder if they were the original poster that created the notification
if ($this->get_data('poster_id') == $post['poster_id'])
{
- return array('notification_data' => serialize($this->get_data(false)));
+ return array();
}
$responders = $this->get_data('responders');
$responders = ($responders === null) ? array() : $responders;
+ // Do not add more than 25 responders,
+ // we trim the username list to "a, b, c and x others" anyway
+ // so there is no use to add all of them anyway.
+ if (sizeof($responders) > 25)
+ {
+ return array();
+ }
+
foreach ($responders as $responder)
{
// Do not add them as a responder multiple times
if ($responder['poster_id'] == $post['poster_id'])
{
- return array('notification_data' => serialize($this->get_data(false)));
+ return array();
}
}
@@ -408,6 +447,15 @@ class post extends \phpbb\notification\type\base
$this->set_data('responders', $responders);
- return array('notification_data' => serialize($this->get_data(false)));
+ $serialized_data = serialize($this->get_data(false));
+
+ // If the data is longer then 4000 characters, it would cause a SQL error.
+ // We don't add the username to the list if this is the case.
+ if (utf8_strlen($serialized_data) >= 4000)
+ {
+ return array();
+ }
+
+ return array('notification_data' => $serialized_data);
}
}
diff --git a/phpBB/phpbb/notification/type/post_in_queue.php b/phpBB/phpbb/notification/type/post_in_queue.php
index db16763583..2d556fc9c8 100644
--- a/phpBB/phpbb/notification/type/post_in_queue.php
+++ b/phpBB/phpbb/notification/type/post_in_queue.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Post in queue notifications class
* This class handles notifications for posts that are put in the moderation queue (for moderators)
-*
-* @package notifications
*/
+
class post_in_queue extends \phpbb\notification\type\post
{
/**
@@ -24,7 +27,7 @@ class post_in_queue extends \phpbb\notification\type\post
*/
public function get_type()
{
- return 'post_in_queue';
+ return 'notification.type.post_in_queue';
}
/**
@@ -40,8 +43,8 @@ class post_in_queue extends \phpbb\notification\type\post
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
- 'id' => 'needs_approval',
+ static public $notification_option = array(
+ 'id' => 'notification.type.needs_approval',
'lang' => 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_MODERATION',
);
@@ -67,6 +70,7 @@ class post_in_queue extends \phpbb\notification\type\post
* Find the users who want to receive notifications
*
* @param array $post Data from the post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -104,7 +108,7 @@ class post_in_queue extends \phpbb\notification\type\post
}
return $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], array_merge($options, array(
- 'item_type' => self::$notification_option['id'],
+ 'item_type' => static::$notification_option['id'],
)));
}
@@ -119,19 +123,30 @@ class post_in_queue extends \phpbb\notification\type\post
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {inheritDoc}
+ */
+ public function get_redirect_url()
+ {
+ return parent::get_url();
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
- $data = parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
+
+ $this->notification_time = time();
+ }
- $this->notification_time = $data['notification_time'] = time();
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/quote.php b/phpBB/phpbb/notification/type/quote.php
index e8527261d8..684463c8c3 100644
--- a/phpBB/phpbb/notification/type/quote.php
+++ b/phpBB/phpbb/notification/type/quote.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,29 +16,26 @@ namespace phpbb\notification\type;
/**
* Post quoting notifications class
* This class handles notifying users when they have been quoted in a post
-*
-* @package notifications
*/
+
class quote extends \phpbb\notification\type\post
{
/**
+ * @var \phpbb\textformatter\utils_interface
+ */
+ protected $utils;
+
+ /**
* Get notification type name
*
* @return string
*/
public function get_type()
{
- return 'quote';
+ return 'notification.type.quote';
}
/**
- * regular expression to match to find usernames
- *
- * @var string
- */
- protected static $regular_expression_match = '#\[quote=&quot;(.+?)&quot;#';
-
- /**
* Language key used to output the text
*
* @var string
@@ -47,7 +48,7 @@ class quote extends \phpbb\notification\type\post
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_QUOTE',
'group' => 'NOTIFICATION_GROUP_POSTING',
);
@@ -63,7 +64,8 @@ class quote extends \phpbb\notification\type\post
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -73,17 +75,16 @@ class quote extends \phpbb\notification\type\post
'ignore_users' => array(),
), $options);
- $usernames = false;
- preg_match_all(self::$regular_expression_match, $post['post_text'], $usernames);
+ $usernames = $this->utils->get_outermost_quote_authors($post['post_text']);
- if (empty($usernames[1]))
+ if (empty($usernames))
{
return array();
}
- $usernames[1] = array_unique($usernames[1]);
+ $usernames = array_unique($usernames);
- $usernames = array_map('utf8_clean_string', $usernames[1]);
+ $usernames = array_map('utf8_clean_string', $usernames);
$users = array();
@@ -94,77 +95,30 @@ class quote extends \phpbb\notification\type\post
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
- $users[] = $row['user_id'];
+ $users[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
- if (empty($users))
- {
- return array();
- }
- sort($users);
-
- $auth_read = $this->auth->acl_get_list($users, 'f_read', $post['forum_id']);
-
- if (empty($auth_read))
- {
- return array();
- }
-
- $notify_users = $this->check_user_notification_options($auth_read[$post['forum_id']]['f_read'], $options);
-
- // Try to find the users who already have been notified about replies and have not read the topic since and just update their notifications
- $update_notifications = array();
- $sql = 'SELECT n.*
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.notification_type_id = ' . (int) $this->notification_type_id . '
- AND n.item_parent_id = ' . (int) self::get_item_parent_id($post) . '
- AND n.notification_read = 0
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
- {
- // Do not create a new notification
- unset($notify_users[$row['user_id']]);
-
- $notification = $this->notification_manager->get_item_type_class($this->get_type(), $row);
- $sql = 'UPDATE ' . $this->notifications_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $notification->add_responders($post)) . '
- WHERE notification_id = ' . $row['notification_id'];
- $this->db->sql_query($sql);
- }
- $this->db->sql_freeresult($result);
-
- return $notify_users;
+ return $this->get_authorised_recipients($users, $post['forum_id'], $options, true);
}
/**
* Update a notification
*
- * @param array $data Data specific for this type that will be updated
+ * @param array $post Data specific for this type that will be updated
+ * @return true
*/
public function update_notifications($post)
{
- $old_notifications = array();
- $sql = 'SELECT n.user_id
- FROM ' . $this->notifications_table . ' n, ' . $this->notification_types_table . ' nt
- WHERE n.notification_type_id = ' . (int) $this->notification_type_id . '
- AND n.item_id = ' . self::get_item_id($post) . '
- AND nt.notification_type_id = n.notification_type_id
- AND nt.notification_type_enabled = 1';
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
- {
- $old_notifications[] = $row['user_id'];
- }
- $this->db->sql_freeresult($result);
+ $old_notifications = $this->notification_manager->get_notified_users($this->get_type(), array(
+ 'item_id' => static::get_item_id($post),
+ ));
// Find the new users to notify
- $notifications = $this->find_users_for_notification($post);
+ $notifications = array_keys($this->find_users_for_notification($post));
// Find the notifications we must delete
- $remove_notifications = array_diff($old_notifications, array_keys($notifications));
+ $remove_notifications = array_diff(array_keys($old_notifications), array_keys($notifications));
// Find the notifications we must add
$add_notifications = array();
@@ -179,11 +133,7 @@ class quote extends \phpbb\notification\type\post
// Remove the necessary notifications
if (!empty($remove_notifications))
{
- $sql = 'DELETE FROM ' . $this->notifications_table . '
- WHERE notification_type_id = ' . (int) $this->notification_type_id . '
- AND item_id = ' . self::get_item_id($post) . '
- AND ' . $this->db->sql_in_set('user_id', $remove_notifications);
- $this->db->sql_query($sql);
+ $this->notification_manager->delete_notifications($this->get_type(), static::get_item_id($post), false, $remove_notifications);
}
// return true to continue with the update code in the notifications service (this will update the rest of the notifications)
@@ -191,6 +141,14 @@ class quote extends \phpbb\notification\type\post
}
/**
+ * {inheritDoc}
+ */
+ public function get_redirect_url()
+ {
+ return $this->get_url();
+ }
+
+ /**
* Get email template
*
* @return string|bool
@@ -213,4 +171,14 @@ class quote extends \phpbb\notification\type\post
'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']),
));
}
+
+ /**
+ * Set the utils service used to retrieve quote authors
+ *
+ * @param \phpbb\textformatter\utils_interface $utils
+ */
+ public function set_utils(\phpbb\textformatter\utils_interface $utils)
+ {
+ $this->utils = $utils;
+ }
}
diff --git a/phpBB/phpbb/notification/type/report_pm.php b/phpBB/phpbb/notification/type/report_pm.php
index 55f6bf946d..6091919769 100644
--- a/phpBB/phpbb/notification/type/report_pm.php
+++ b/phpBB/phpbb/notification/type/report_pm.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Private message reported notifications class
* This class handles notifications for private messages when they are reported
-*
-* @package notifications
*/
+
class report_pm extends \phpbb\notification\type\pm
{
/**
@@ -24,7 +27,17 @@ class report_pm extends \phpbb\notification\type\pm
*/
public function get_type()
{
- return 'report_pm';
+ return 'notification.type.report_pm';
+ }
+
+ /**
+ * Get the CSS style class of the notification
+ *
+ * @return string
+ */
+ public function get_style_class()
+ {
+ return 'notification-reported';
}
/**
@@ -47,8 +60,8 @@ class report_pm extends \phpbb\notification\type\pm
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
- 'id' => 'report',
+ static public $notification_option = array(
+ 'id' => 'notification.type.report',
'lang' => 'NOTIFICATION_TYPE_REPORT',
'group' => 'NOTIFICATION_GROUP_MODERATION',
);
@@ -57,8 +70,9 @@ class report_pm extends \phpbb\notification\type\pm
* Get the id of the parent
*
* @param array $pm The data from the pm
+ * @return int The report id
*/
- public static function get_item_parent_id($pm)
+ static public function get_item_parent_id($pm)
{
return (int) $pm['report_id'];
}
@@ -81,6 +95,7 @@ class report_pm extends \phpbb\notification\type\pm
* (copied from post_in_queue)
*
* @param array $post Data from the post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -106,7 +121,7 @@ class report_pm extends \phpbb\notification\type\pm
}
return $this->check_user_notification_options($auth_approve[$post['forum_id']][$this->permission], array_merge($options, array(
- 'item_type' => self::$notification_option['id'],
+ 'item_type' => static::$notification_option['id'],
)));
}
@@ -127,11 +142,13 @@ class report_pm extends \phpbb\notification\type\pm
*/
public function get_email_template_variables()
{
+ $user_data = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile');
+
return array(
- 'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']),
- 'SUBJECT' => htmlspecialchars_decode(censor_text($this->get_data('message_subject'))),
+ 'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']),
+ 'SUBJECT' => htmlspecialchars_decode(censor_text($this->get_data('message_subject'))),
- 'U_VIEW_REPORT' => generate_board_url() . "mcp.{$this->php_ext}?r={$this->item_parent_id}&amp;i=pm_reports&amp;mode=pm_report_details",
+ 'U_VIEW_REPORT' => generate_board_url() . "mcp.{$this->php_ext}?r={$this->item_parent_id}&amp;i=pm_reports&amp;mode=pm_report_details",
);
}
@@ -152,34 +169,54 @@ class report_pm extends \phpbb\notification\type\pm
*/
public function get_title()
{
- $this->user->add_lang('mcp');
+ $this->language->add_lang('mcp');
$username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile');
+ return $this->language->lang(
+ $this->language_key,
+ $username
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('message_subject'))
+ );
+ }
+
+ /**
+ * Get the reason for the notification
+ *
+ * @return string
+ */
+ public function get_reason()
+ {
if ($this->get_data('report_text'))
{
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('message_subject')),
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('report_text')
);
}
- if (isset($this->user->lang[$this->get_data('reason_title')]))
+ if ($this->language->is_set($this->get_data('reason_title')))
{
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('message_subject')),
- $this->user->lang[$this->get_data('reason_title')]
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
+ $this->language->lang($this->get_data('reason_title'))
);
}
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('message_subject')),
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('reason_description')
);
}
@@ -189,7 +226,7 @@ class report_pm extends \phpbb\notification\type\pm
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('reporter_id'));
+ return $this->user_loader->get_avatar($this->get_data('reporter_id'), false, true);
}
/**
@@ -203,13 +240,7 @@ class report_pm extends \phpbb\notification\type\pm
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
@@ -218,6 +249,6 @@ class report_pm extends \phpbb\notification\type\pm
$this->set_data('reason_description', $post['reason_description']);
$this->set_data('report_text', $post['report_text']);
- return parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/report_pm_closed.php b/phpBB/phpbb/notification/type/report_pm_closed.php
index 56485f5d37..5e98eb5feb 100644
--- a/phpBB/phpbb/notification/type/report_pm_closed.php
+++ b/phpBB/phpbb/notification/type/report_pm_closed.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* PM report closed notifications class
* This class handles notifications for when reports are closed on PMs (for the one who reported the PM)
-*
-* @package notifications
*/
+
class report_pm_closed extends \phpbb\notification\type\pm
{
/**
@@ -24,7 +27,7 @@ class report_pm_closed extends \phpbb\notification\type\pm
*/
public function get_type()
{
- return 'report_pm_closed';
+ return 'notification.type.report_pm_closed';
}
/**
@@ -49,7 +52,8 @@ class report_pm_closed extends \phpbb\notification\type\pm
/**
* Find the users who want to receive notifications
*
- * @param array $pm Data from
+ * @param array $pm Data from submit_pm
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -60,7 +64,7 @@ class report_pm_closed extends \phpbb\notification\type\pm
return array();
}
- return array($pm['reporter'] => array(''));
+ return array($pm['reporter'] => $this->notification_manager->get_default_methods());
}
/**
@@ -102,9 +106,21 @@ class report_pm_closed extends \phpbb\notification\type\pm
{
$username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile');
- return $this->user->lang(
+ return $this->language->lang(
$this->language_key,
- $username,
+ $username
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
censor_text($this->get_data('message_subject'))
);
}
@@ -114,7 +130,7 @@ class report_pm_closed extends \phpbb\notification\type\pm
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('closer_id'));
+ return $this->user_loader->get_avatar($this->get_data('closer_id'), false, true);
}
/**
@@ -128,21 +144,24 @@ class report_pm_closed extends \phpbb\notification\type\pm
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $pm PM Data
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($pm, $pre_create_data = array())
{
$this->set_data('closer_id', $pm['closer_id']);
- $data = parent::create_insert_array($pm, $pre_create_data);
+ parent::create_insert_array($pm, $pre_create_data);
- $this->notification_time = $data['notification_time'] = time();
+ $this->notification_time = time();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/report_post.php b/phpBB/phpbb/notification/type/report_post.php
index 9bf035b91e..84a5241417 100644
--- a/phpBB/phpbb/notification/type/report_post.php
+++ b/phpBB/phpbb/notification/type/report_post.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,8 +16,6 @@ namespace phpbb\notification\type;
/**
* Reported post notifications class
* This class handles notifications for reported posts
-*
-* @package notifications
*/
class report_post extends \phpbb\notification\type\post_in_queue
{
@@ -24,7 +26,17 @@ class report_post extends \phpbb\notification\type\post_in_queue
*/
public function get_type()
{
- return 'report_post';
+ return 'notification.type.report_post';
+ }
+
+ /**
+ * Get the CSS style class of the notification
+ *
+ * @return string
+ */
+ public function get_style_class()
+ {
+ return 'notification-reported';
}
/**
@@ -54,8 +66,8 @@ class report_post extends \phpbb\notification\type\post_in_queue
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id' and 'lang')
*/
- public static $notification_option = array(
- 'id' => 'report',
+ static public $notification_option = array(
+ 'id' => 'notification.type.report',
'lang' => 'NOTIFICATION_TYPE_REPORT',
'group' => 'NOTIFICATION_GROUP_MODERATION',
);
@@ -64,6 +76,7 @@ class report_post extends \phpbb\notification\type\post_in_queue
* Find the users who want to receive notifications
*
* @param array $post Data from the post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -126,34 +139,54 @@ class report_post extends \phpbb\notification\type\post_in_queue
*/
public function get_title()
{
- $this->user->add_lang('mcp');
+ $this->language->add_lang('mcp');
$username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile');
+ return $this->language->lang(
+ $this->language_key,
+ $username
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('post_subject'))
+ );
+ }
+
+ /**
+ * Get the reason for the notification
+ *
+ * @return string
+ */
+ public function get_reason()
+ {
if ($this->get_data('report_text'))
{
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('post_subject')),
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('report_text')
);
}
- if (isset($this->user->lang[$this->get_data('reason_title')]))
+ if ($this->language->is_set($this->get_data('reason_title')))
{
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('post_subject')),
- $this->user->lang[$this->get_data('reason_title')]
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
+ $this->language->lang($this->get_data('reason_title'))
);
}
- return $this->user->lang(
- $this->language_key,
- $username,
- censor_text($this->get_data('post_subject')),
+ return $this->language->lang(
+ 'NOTIFICATION_REASON',
$this->get_data('reason_description')
);
}
@@ -163,7 +196,7 @@ class report_post extends \phpbb\notification\type\post_in_queue
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('reporter_id'));
+ return $this->user_loader->get_avatar($this->get_data('reporter_id'), false, true);
}
/**
@@ -177,13 +210,7 @@ class report_post extends \phpbb\notification\type\post_in_queue
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
@@ -192,6 +219,6 @@ class report_post extends \phpbb\notification\type\post_in_queue
$this->set_data('reason_description', $post['reason_description']);
$this->set_data('report_text', $post['report_text']);
- return parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/report_post_closed.php b/phpBB/phpbb/notification/type/report_post_closed.php
index fff45612b3..165034d57e 100644
--- a/phpBB/phpbb/notification/type/report_post_closed.php
+++ b/phpBB/phpbb/notification/type/report_post_closed.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Post report closed notifications class
* This class handles notifications for when reports are closed on posts (for the one who reported the post)
-*
-* @package notifications
*/
+
class report_post_closed extends \phpbb\notification\type\post
{
/**
@@ -24,7 +27,7 @@ class report_post_closed extends \phpbb\notification\type\post
*/
public function get_type()
{
- return 'report_post_closed';
+ return 'notification.type.report_post_closed';
}
/**
@@ -56,7 +59,8 @@ class report_post_closed extends \phpbb\notification\type\post
/**
* Find the users who want to receive notifications
*
- * @param array $post Data from
+ * @param array $post Data from submit_post
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -67,7 +71,7 @@ class report_post_closed extends \phpbb\notification\type\post
return array();
}
- return array($post['reporter'] => array(''));
+ return array($post['reporter'] => $this->notification_manager->get_default_methods());
}
/**
@@ -109,9 +113,21 @@ class report_post_closed extends \phpbb\notification\type\post
{
$username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile');
- return $this->user->lang(
+ return $this->language->lang(
$this->language_key,
- $username,
+ $username
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
censor_text($this->get_data('post_subject'))
);
}
@@ -121,7 +137,7 @@ class report_post_closed extends \phpbb\notification\type\post
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('closer_id'));
+ return $this->user_loader->get_avatar($this->get_data('closer_id'), false, true);
}
/**
@@ -135,21 +151,24 @@ class report_post_closed extends \phpbb\notification\type\post
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
$this->set_data('closer_id', $post['closer_id']);
- $data = parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
- $this->notification_time = $data['notification_time'] = time();
+ $this->notification_time = time();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/topic.php b/phpBB/phpbb/notification/type/topic.php
index 98f086a50b..671c34fe96 100644
--- a/phpBB/phpbb/notification/type/topic.php
+++ b/phpBB/phpbb/notification/type/topic.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Topic notifications class
* This class handles notifications for new topics
-*
-* @package notifications
*/
+
class topic extends \phpbb\notification\type\base
{
/**
@@ -24,7 +27,7 @@ class topic extends \phpbb\notification\type\base
*/
public function get_type()
{
- return 'topic';
+ return 'notification.type.topic';
}
/**
@@ -47,11 +50,27 @@ class topic extends \phpbb\notification\type\base
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
+ static public $notification_option = array(
'lang' => 'NOTIFICATION_TYPE_TOPIC',
'group' => 'NOTIFICATION_GROUP_POSTING',
);
+ /** @var \phpbb\user_loader */
+ protected $user_loader;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ public function set_config(\phpbb\config\config $config)
+ {
+ $this->config = $config;
+ }
+
+ public function set_user_loader(\phpbb\user_loader $user_loader)
+ {
+ $this->user_loader = $user_loader;
+ }
+
/**
* Is available
*/
@@ -64,8 +83,9 @@ class topic extends \phpbb\notification\type\base
* Get the id of the item
*
* @param array $post The data from the post
+ * @return int The topic id
*/
- public static function get_item_id($post)
+ static public function get_item_id($post)
{
return (int) $post['topic_id'];
}
@@ -74,8 +94,9 @@ class topic extends \phpbb\notification\type\base
* Get the id of the parent
*
* @param array $post The data from the post
+ * @return int The forum id
*/
- public static function get_item_parent_id($post)
+ static public function get_item_parent_id($post)
{
return (int) $post['forum_id'];
}
@@ -84,6 +105,7 @@ class topic extends \phpbb\notification\type\base
* Find the users who want to receive notifications
*
* @param array $topic Data from the topic
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -103,23 +125,11 @@ class topic extends \phpbb\notification\type\base
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
- $users[] = $row['user_id'];
+ $users[] = (int) $row['user_id'];
}
$this->db->sql_freeresult($result);
- if (empty($users))
- {
- return array();
- }
-
- $auth_read = $this->auth->acl_get_list($users, 'f_read', $topic['forum_id']);
-
- if (empty($auth_read))
- {
- return array();
- }
-
- return $this->check_user_notification_options($auth_read[$topic['forum_id']]['f_read'], $options);
+ return $this->get_authorised_recipients($users, $topic['forum_id'], $options);
}
/**
@@ -127,7 +137,7 @@ class topic extends \phpbb\notification\type\base
*/
public function get_avatar()
{
- return $this->user_loader->get_avatar($this->get_data('poster_id'));
+ return $this->user_loader->get_avatar($this->get_data('poster_id'), false, true);
}
/**
@@ -146,10 +156,34 @@ class topic extends \phpbb\notification\type\base
$username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile');
}
- return $this->user->lang(
+ return $this->language->lang(
$this->language_key,
- $username,
- censor_text($this->get_data('topic_title')),
+ $username
+ );
+ }
+
+ /**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_REFERENCE',
+ censor_text($this->get_data('topic_title'))
+ );
+ }
+
+ /**
+ * Get the forum of the notification reference
+ *
+ * @return string
+ */
+ public function get_forum()
+ {
+ return $this->language->lang(
+ 'NOTIFICATION_FORUM',
$this->get_data('forum_name')
);
}
@@ -241,18 +275,13 @@ class topic extends \phpbb\notification\type\base
{
$tracking_data[$row['user_id']] = $row['mark_time'];
}
+ $this->db->sql_freeresult($result);
return $tracking_data;
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $post Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($post, $pre_create_data = array())
{
@@ -273,6 +302,6 @@ class topic extends \phpbb\notification\type\base
$this->notification_read = true;
}
- return parent::create_insert_array($post, $pre_create_data);
+ parent::create_insert_array($post, $pre_create_data);
}
}
diff --git a/phpBB/phpbb/notification/type/topic_in_queue.php b/phpBB/phpbb/notification/type/topic_in_queue.php
index c8c1b5b7e2..2d732b9cd7 100644
--- a/phpBB/phpbb/notification/type/topic_in_queue.php
+++ b/phpBB/phpbb/notification/type/topic_in_queue.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,9 +16,8 @@ namespace phpbb\notification\type;
/**
* Topic in queue notifications class
* This class handles notifications for topics when they are put in the moderation queue (for moderators)
-*
-* @package notifications
*/
+
class topic_in_queue extends \phpbb\notification\type\topic
{
/**
@@ -24,7 +27,7 @@ class topic_in_queue extends \phpbb\notification\type\topic
*/
public function get_type()
{
- return 'topic_in_queue';
+ return 'notification.type.topic_in_queue';
}
/**
@@ -40,8 +43,8 @@ class topic_in_queue extends \phpbb\notification\type\topic
* @var bool|array False if the service should use it's default data
* Array of data (including keys 'id', 'lang', and 'group')
*/
- public static $notification_option = array(
- 'id' => 'needs_approval',
+ static public $notification_option = array(
+ 'id' => 'notification.type.needs_approval',
'lang' => 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE',
'group' => 'NOTIFICATION_GROUP_MODERATION',
);
@@ -67,6 +70,7 @@ class topic_in_queue extends \phpbb\notification\type\topic
* Find the users who want to receive notifications
*
* @param array $topic Data from the topic
+ * @param array $options Options for finding users for notification
*
* @return array
*/
@@ -104,7 +108,7 @@ class topic_in_queue extends \phpbb\notification\type\topic
}
return $this->check_user_notification_options($auth_read[$topic['forum_id']]['f_read'], array_merge($options, array(
- 'item_type' => self::$notification_option['id'],
+ 'item_type' => static::$notification_option['id'],
)));
}
@@ -119,19 +123,22 @@ class topic_in_queue extends \phpbb\notification\type\topic
}
/**
- * Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
- *
- * @param array $topic Data from submit_post
- * @param array $pre_create_data Data from pre_create_insert_array()
- *
- * @return array Array of data ready to be inserted into the database
+ * {@inheritdoc}
*/
public function create_insert_array($topic, $pre_create_data = array())
{
- $data = parent::create_insert_array($topic, $pre_create_data);
+ parent::create_insert_array($topic, $pre_create_data);
+
+ $this->notification_time = time();
+ }
- $this->notification_time = $data['notification_time'] = time();
+ /**
+ * {@inheritdoc}
+ */
+ public function get_insert_array()
+ {
+ $data = parent::get_insert_array();
+ $data['notification_time'] = $this->notification_time;
return $data;
}
diff --git a/phpBB/phpbb/notification/type/type_interface.php b/phpBB/phpbb/notification/type/type_interface.php
index e3e6898172..f9f832bdda 100644
--- a/phpBB/phpbb/notification/type/type_interface.php
+++ b/phpBB/phpbb/notification/type/type_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package notifications
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\notification\type;
/**
* Base notifications interface
-* @package notifications
*/
interface type_interface
{
@@ -34,14 +37,14 @@ interface type_interface
*
* @param array $type_data The type specific data
*/
- public static function get_item_id($type_data);
+ static public function get_item_id($type_data);
/**
* Get the id of the parent
*
* @param array $type_data The type specific data
*/
- public static function get_item_parent_id($type_data);
+ static public function get_item_parent_id($type_data);
/**
* Is this type available to the current user (defines whether or not it will be shown in the UCP Edit notification options)
@@ -85,6 +88,13 @@ interface type_interface
public function load_special($data, $notifications);
/**
+ * Get the CSS style class of the notification
+ *
+ * @return string
+ */
+ public function get_style_class();
+
+ /**
* Get the HTML formatted title of this notification
*
* @return string
@@ -92,6 +102,20 @@ interface type_interface
public function get_title();
/**
+ * Get the HTML formatted reference of the notification
+ *
+ * @return string
+ */
+ public function get_reference();
+
+ /**
+ * Get the forum of the notification reference
+ *
+ * @return string
+ */
+ public function get_forum();
+
+ /**
* Get the url to this item
*
* @return string URL
@@ -99,6 +123,13 @@ interface type_interface
public function get_url();
/**
+ * Get the url to redirect after the item has been marked as read
+ *
+ * @return string URL
+ */
+ public function get_redirect_url();
+
+ /**
* URL to unsubscribe to this notification
*
* @param string|bool $method Method name to unsubscribe from (email|jabber|etc), False to unsubscribe from all notifications for this item
@@ -146,14 +177,18 @@ interface type_interface
/**
* Function for preparing the data for insertion in an SQL query
- * (The service handles insertion)
*
* @param array $type_data The type specific data
* @param array $pre_create_data Data from pre_create_insert_array()
+ */
+ public function create_insert_array($type_data, $pre_create_data);
+
+ /**
+ * Function for getting the data for insertion in an SQL query
*
* @return array Array of data ready to be inserted into the database
*/
- public function create_insert_array($type_data, $pre_create_data);
+ public function get_insert_array();
/**
* Function for preparing the data for update in an SQL query
@@ -171,7 +206,7 @@ interface type_interface
* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False)
* @return string
*/
- public function mark_read($return);
+ public function mark_read($return = false);
/**
* Mark this item unread
@@ -179,5 +214,5 @@ interface type_interface
* @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False)
* @return string
*/
- public function mark_unread($return);
+ public function mark_unread($return = false);
}
diff --git a/phpBB/phpbb/pagination.php b/phpBB/phpbb/pagination.php
index 57e7932341..7a81c25ad2 100644
--- a/phpBB/phpbb/pagination.php
+++ b/phpBB/phpbb/pagination.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpbb
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -17,16 +21,26 @@ class pagination
/** @var \phpbb\user */
protected $user;
+ /** @var \phpbb\controller\helper */
+ protected $helper;
+
+ /** @var \phpbb\event\dispatcher_interface */
+ protected $phpbb_dispatcher;
+
/**
* Constructor
*
- * @param \phpbb\template\template $template
- * @param \phpbb\user $user
+ * @param \phpbb\template\template $template
+ * @param \phpbb\user $user
+ * @param \phpbb\controller\helper $helper
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher
*/
- public function __construct(\phpbb\template\template $template, \phpbb\user $user)
+ public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\controller\helper $helper, \phpbb\event\dispatcher_interface $phpbb_dispatcher)
{
$this->template = $template;
$this->user = $user;
+ $this->helper = $helper;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
}
/**
@@ -40,13 +54,60 @@ class pagination
* If you use page numbers inside your controller route, start name should be the string
* that should be removed for the first page (example: /page/%d)
* @param int $per_page the number of items, posts, etc. to display per page, used to determine the number of pages to produce
- * @return URL for the requested page
+ * @return string URL for the requested page
*/
protected function generate_page_link($base_url, $on_page, $start_name, $per_page)
{
- if (strpos($start_name, '%d') !== false)
+ // A listener can set this variable to the new pagination URL
+ // to override the generate_page_link() function generated value
+ $generate_page_link_override = false;
+
+ /**
+ * Execute code and/or override generate_page_link()
+ *
+ * To override the generate_page_link() function generated value
+ * set $generate_page_link_override to the new URL value
+ *
+ * @event core.pagination_generate_page_link
+ * @var string base_url is url prepended to all links generated within the function
+ * If you use page numbers inside your controller route, base_url should contains a placeholder (%d)
+ * for the page. Also be sure to specify the pagination path information into the start_name argument
+ * @var string on_page is the page for which we want to generate the link
+ * @var string start_name is the name of the parameter containing the first item of the given page (example: start=20)
+ * If you use page numbers inside your controller route, start name should be the string
+ * that should be removed for the first page (example: /page/%d)
+ * @var int per_page the number of items, posts, etc. to display per page, used to determine the number of pages to produce
+ * @var bool|string generate_page_link_override Shall we return custom pagination link (string URL) or not (false)
+ * @since 3.1.0-RC5
+ */
+ $vars = array('base_url', 'on_page', 'start_name', 'per_page', 'generate_page_link_override');
+ extract($this->phpbb_dispatcher->trigger_event('core.pagination_generate_page_link', compact($vars)));
+
+ if ($generate_page_link_override)
+ {
+ return $generate_page_link_override;
+ }
+
+ if (!is_string($base_url))
{
- return ($on_page > 1) ? sprintf($base_url, (int) $on_page) : str_replace($start_name, '', $base_url);
+ if (is_array($base_url['routes']))
+ {
+ $route = ($on_page > 1) ? $base_url['routes'][1] : $base_url['routes'][0];
+ }
+ else
+ {
+ $route = $base_url['routes'];
+ }
+ $params = (isset($base_url['params'])) ? $base_url['params'] : array();
+ $is_amp = (isset($base_url['is_amp'])) ? $base_url['is_amp'] : true;
+ $session_id = (isset($base_url['session_id'])) ? $base_url['session_id'] : false;
+
+ if ($on_page > 1 || !is_array($base_url['routes']))
+ {
+ $params[$start_name] = (int) $on_page;
+ }
+
+ return $this->helper->route($route, $params, $is_amp, $session_id);
}
else
{
@@ -103,8 +164,8 @@ class pagination
// determine this number. Again at most five pages? Then just display them all. More than
// five and we first (min) determine whether we'd end up listing more pages than exist.
// We then (max) ensure we're displaying the minimum number of pages.
- $start_page = ($total_pages > 5) ? min(max(1, $on_page - 3), $total_pages - 4) : 1;
- $end_page = ($total_pages > 5) ? max(min($total_pages, $on_page + 3), 5) : $total_pages;
+ $start_page = ($total_pages > 5) ? min(max(1, $on_page - 2), $total_pages - 4) : 1;
+ $end_page = ($total_pages > 5) ? max(min($total_pages, $on_page + 2), 5) : $total_pages;
}
if ($on_page != 1)
@@ -194,7 +255,8 @@ class pagination
$tpl_prefix = ($tpl_prefix == 'PAGINATION') ? '' : $tpl_prefix . '_';
$template_array = array(
- $tpl_prefix . 'BASE_URL' => $base_url,
+ $tpl_prefix . 'BASE_URL' => is_string($base_url) ? $base_url : '',//@todo: Fix this for routes
+ $tpl_prefix . 'START_NAME' => $start_name,
$tpl_prefix . 'PER_PAGE' => $per_page,
'U_' . $tpl_prefix . 'PREVIOUS_PAGE' => ($on_page != 1) ? $u_previous_page : '',
'U_' . $tpl_prefix . 'NEXT_PAGE' => ($on_page != $total_pages) ? $u_next_page : '',
diff --git a/phpBB/phpbb/passwords/driver/base.php b/phpBB/phpbb/passwords/driver/base.php
index 8256fd721c..fd07a61bf4 100644
--- a/phpBB/phpbb/passwords/driver/base.php
+++ b/phpBB/phpbb/passwords/driver/base.php
@@ -1,23 +1,24 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
abstract class base implements driver_interface
{
- /** @var phpbb\config\config */
+ /** @var \phpbb\config\config */
protected $config;
- /** @var phpbb\passwords\driver\helper */
+ /** @var \phpbb\passwords\driver\helper */
protected $helper;
/** @var driver name */
@@ -36,10 +37,26 @@ abstract class base implements driver_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function is_supported()
{
return true;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_settings_only($hash, $full = false)
+ {
+ return false;
+ }
}
diff --git a/phpBB/phpbb/passwords/driver/bcrypt.php b/phpBB/phpbb/passwords/driver/bcrypt.php
index 1d1b1e267d..eab1c3d569 100644
--- a/phpBB/phpbb/passwords/driver/bcrypt.php
+++ b/phpBB/phpbb/passwords/driver/bcrypt.php
@@ -1,23 +1,24 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
class bcrypt extends base
{
const PREFIX = '$2a$';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_prefix()
{
@@ -25,7 +26,7 @@ class bcrypt extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function hash($password, $salt = '')
{
@@ -57,9 +58,9 @@ class bcrypt extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function check($password, $hash)
+ public function check($password, $hash, $user_row = array())
{
$salt = substr($hash, 0, 29);
if (strlen($salt) != 29)
@@ -67,7 +68,7 @@ class bcrypt extends base
return false;
}
- if ($hash == $this->hash($password, $salt))
+ if ($this->helper->string_compare($hash, $this->hash($password, $salt)))
{
return true;
}
@@ -85,7 +86,7 @@ class bcrypt extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_settings_only($hash, $full = false)
{
diff --git a/phpBB/phpbb/passwords/driver/bcrypt_2y.php b/phpBB/phpbb/passwords/driver/bcrypt_2y.php
index 11c3617e49..c710e0d04a 100644
--- a/phpBB/phpbb/passwords/driver/bcrypt_2y.php
+++ b/phpBB/phpbb/passwords/driver/bcrypt_2y.php
@@ -1,23 +1,24 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
class bcrypt_2y extends bcrypt
{
const PREFIX = '$2y$';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_prefix()
{
@@ -25,7 +26,7 @@ class bcrypt_2y extends bcrypt
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function is_supported()
{
diff --git a/phpBB/phpbb/passwords/driver/bcrypt_wcf2.php b/phpBB/phpbb/passwords/driver/bcrypt_wcf2.php
new file mode 100644
index 0000000000..0eee98d7b7
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/bcrypt_wcf2.php
@@ -0,0 +1,84 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class bcrypt_wcf2 extends base
+{
+ const PREFIX = '$wcf2$';
+
+ /** @var \phpbb\passwords\driver\bcrypt */
+ protected $bcrypt;
+
+ /** @var \phpbb\passwords\driver\helper */
+ protected $helper;
+
+ /**
+ * Constructor of passwords driver object
+ *
+ * @param \phpbb\passwords\driver\bcrypt $bcrypt Salted md5 driver
+ * @param \phpbb\passwords\driver\helper $helper Password driver helper
+ */
+ public function __construct(\phpbb\passwords\driver\bcrypt $bcrypt, helper $helper)
+ {
+ $this->bcrypt = $bcrypt;
+ $this->helper = $helper;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (empty($hash) || strlen($hash) != 60)
+ {
+ return false;
+ }
+ else
+ {
+ $salt = substr($hash, 0, 29);
+
+ if (strlen($salt) != 29)
+ {
+ return false;
+ }
+ // Works for standard WCF 2.x, i.e. WBB4 and similar
+ return $this->helper->string_compare($hash, $this->bcrypt->hash($this->bcrypt->hash($password, $salt), $salt));
+ }
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/convert_password.php b/phpBB/phpbb/passwords/driver/convert_password.php
new file mode 100644
index 0000000000..eb70434df2
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/convert_password.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\passwords\driver;
+
+class convert_password extends base
+{
+ const PREFIX = '$CP$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/driver_interface.php b/phpBB/phpbb/passwords/driver/driver_interface.php
index ebaf0626af..3974484f13 100644
--- a/phpBB/phpbb/passwords/driver/driver_interface.php
+++ b/phpBB/phpbb/passwords/driver/driver_interface.php
@@ -1,17 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
interface driver_interface
{
/**
@@ -22,6 +23,13 @@ interface driver_interface
public function is_supported();
/**
+ * Check if hash type is a legacy hash type
+ *
+ * @return bool True if it's a legacy hash type, false if not
+ */
+ public function is_legacy();
+
+ /**
* Returns the hash prefix
*
* @return string Hash prefix
@@ -43,10 +51,11 @@ interface driver_interface
*
* @param string $password The password to check
* @param string $hash The password hash to check against
+ * @param array $user_row User's row in users table
*
* @return bool True if password is correct, else false
*/
- public function check($password, $hash);
+ public function check($password, $hash, $user_row = array());
/**
* Get only the settings of the specified hash
diff --git a/phpBB/phpbb/passwords/driver/helper.php b/phpBB/phpbb/passwords/driver/helper.php
index 4b8dc9a123..f80c3e3df6 100644
--- a/phpBB/phpbb/passwords/driver/helper.php
+++ b/phpBB/phpbb/passwords/driver/helper.php
@@ -1,21 +1,22 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
class helper
{
/**
- * @var phpbb\config\config
+ * @var \phpbb\config\config
*/
protected $config;
@@ -28,7 +29,7 @@ class helper
/**
* Construct a driver helper object
*
- * @param phpbb\config\config $config phpBB configuration
+ * @param \phpbb\config\config $config phpBB configuration
*/
public function __construct(\phpbb\config\config $config)
{
@@ -141,4 +142,36 @@ class helper
}
return $random;
}
+
+ /**
+ * Compare two strings byte by byte
+ *
+ * @param string $string_a The first string
+ * @param string $string_b The second string
+ *
+ * @return bool True if strings are the same, false if not
+ */
+ public function string_compare($string_a, $string_b)
+ {
+ // Return if input variables are not strings or if length does not match
+ if (!is_string($string_a) || !is_string($string_b) || strlen($string_a) != strlen($string_b))
+ {
+ return false;
+ }
+
+ // Use hash_equals() if it's available
+ if (function_exists('hash_equals'))
+ {
+ return hash_equals($string_a, $string_b);
+ }
+
+ $difference = 0;
+
+ for ($i = 0; $i < strlen($string_a) && $i < strlen($string_b); $i++)
+ {
+ $difference |= ord($string_a[$i]) ^ ord($string_b[$i]);
+ }
+
+ return $difference === 0;
+ }
}
diff --git a/phpBB/phpbb/passwords/driver/md5_mybb.php b/phpBB/phpbb/passwords/driver/md5_mybb.php
new file mode 100644
index 0000000000..f631ceae78
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/md5_mybb.php
@@ -0,0 +1,60 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class md5_mybb extends base
+{
+ const PREFIX = '$md5_mybb$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (empty($hash) || strlen($hash) != 32 || !isset($user_row['user_passwd_salt']))
+ {
+ return false;
+ }
+ else
+ {
+ // Works for myBB 1.1.x, 1.2.x, 1.4.x, 1.6.x
+ return $this->helper->string_compare($hash, md5(md5($user_row['user_passwd_salt']) . md5($password)));
+ }
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/md5_phpbb2.php b/phpBB/phpbb/passwords/driver/md5_phpbb2.php
new file mode 100644
index 0000000000..bd8cc51e5a
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/md5_phpbb2.php
@@ -0,0 +1,123 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class md5_phpbb2 extends base
+{
+ const PREFIX = '$md5_phpbb2$';
+
+ /** @var \phpbb\request\request phpBB request object */
+ protected $request;
+
+ /** @var \phpbb\passwords\driver\salted_md5 */
+ protected $salted_md5;
+
+ /** @var \phpbb\passwords\driver\helper */
+ protected $helper;
+
+ /** @var string phpBB root path */
+ protected $phpbb_root_path;
+
+ /** @var string php file extension */
+ protected $php_ext;
+
+ /**
+ * Constructor of passwords driver object
+ *
+ * @param \phpbb\request\request $request phpBB request object
+ * @param \phpbb\passwords\driver\salted_md5 $salted_md5 Salted md5 driver
+ * @param \phpbb\passwords\driver\helper $helper Driver helper
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $php_ext PHP file extension
+ */
+ public function __construct($request, salted_md5 $salted_md5, helper $helper, $phpbb_root_path, $php_ext)
+ {
+ $this->request = $request;
+ $this->salted_md5 = $salted_md5;
+ $this->helper = $helper;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (strlen($hash) != 32 && strlen($hash) != 34)
+ {
+ return false;
+ }
+
+ // enable super globals to get literal value
+ // this is needed to prevent unicode normalization
+ $super_globals_disabled = $this->request->super_globals_disabled();
+ if ($super_globals_disabled)
+ {
+ $this->request->enable_super_globals();
+ }
+
+ // in phpBB2 passwords were used exactly as they were sent, with addslashes applied
+ $password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : '';
+ $password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format;
+ $password_new_format = $this->request->variable('password', '', true);
+
+ if ($super_globals_disabled)
+ {
+ $this->request->disable_super_globals();
+ }
+
+ if ($password == $password_new_format)
+ {
+ if (!function_exists('utf8_to_cp1252'))
+ {
+ include($this->phpbb_root_path . 'includes/utf/data/recode_basic.' . $this->php_ext);
+ }
+
+ if ($this->helper->string_compare(md5($password_old_format), $hash) || $this->helper->string_compare(md5(\utf8_to_cp1252($password_old_format)), $hash)
+ || $this->salted_md5->check(md5($password_old_format), $hash) === true
+ || $this->salted_md5->check(md5(\utf8_to_cp1252($password_old_format)), $hash) === true)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/md5_vb.php b/phpBB/phpbb/passwords/driver/md5_vb.php
new file mode 100644
index 0000000000..280b7114c7
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/md5_vb.php
@@ -0,0 +1,60 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class md5_vb extends base
+{
+ const PREFIX = '$md5_vb$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (empty($hash) || strlen($hash) != 32 || !isset($user_row['user_passwd_salt']))
+ {
+ return false;
+ }
+ else
+ {
+ // Works for vB 3.8.x, 4.x.x, 5.0.x
+ return $this->helper->string_compare($hash, md5(md5($password) . $user_row['user_passwd_salt']));
+ }
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/phpass.php b/phpBB/phpbb/passwords/driver/phpass.php
index 80c4d7a7f0..bef8355276 100644
--- a/phpBB/phpbb/passwords/driver/phpass.php
+++ b/phpBB/phpbb/passwords/driver/phpass.php
@@ -1,23 +1,24 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords\driver;
-/**
-* @package passwords
-*/
class phpass extends salted_md5
{
const PREFIX = '$P$';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_prefix()
{
diff --git a/phpBB/phpbb/passwords/driver/salted_md5.php b/phpBB/phpbb/passwords/driver/salted_md5.php
index 5c72726422..38d6d9cd2c 100644
--- a/phpBB/phpbb/passwords/driver/salted_md5.php
+++ b/phpBB/phpbb/passwords/driver/salted_md5.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -37,15 +41,12 @@ namespace phpbb\passwords\driver;
*
*/
-/**
-* @package passwords
-*/
class salted_md5 extends base
{
const PREFIX = '$H$';
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_prefix()
{
@@ -53,7 +54,15 @@ class salted_md5 extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function hash($password, $setting = '')
{
@@ -66,7 +75,7 @@ class salted_md5 extends base
// happen if pre-determined settings are
// directly passed to the driver. The manager
// will not do this. Same as the old hashing
- // implementatio in phpBB 3.0
+ // implementation in phpBB 3.0
return md5($password);
}
}
@@ -89,16 +98,16 @@ class salted_md5 extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
- public function check($password, $hash)
+ public function check($password, $hash, $user_row = array())
{
if (strlen($hash) !== 34)
{
return md5($password) === $hash;
}
- return $hash === $this->hash($password, $hash);
+ return $this->helper->string_compare($hash, $this->hash($password, $hash));
}
/**
@@ -151,7 +160,7 @@ class salted_md5 extends base
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_settings_only($hash, $full = false)
{
diff --git a/phpBB/phpbb/passwords/driver/sha1.php b/phpBB/phpbb/passwords/driver/sha1.php
new file mode 100644
index 0000000000..1abead42cd
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/sha1.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\passwords\driver;
+
+class sha1 extends base
+{
+ const PREFIX = '$sha1$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ return (strlen($hash) == 40) ? $this->helper->string_compare($hash, sha1($password)) : false;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/sha1_smf.php b/phpBB/phpbb/passwords/driver/sha1_smf.php
new file mode 100644
index 0000000000..b30d87265e
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/sha1_smf.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\passwords\driver;
+
+class sha1_smf extends base
+{
+ const PREFIX = '$smf$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ return (isset($user_row['login_name'])) ? sha1(strtolower($user_row['login_name']) . $password) : false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ return (strlen($hash) == 40) ? $this->helper->string_compare($hash, $this->hash($password, $user_row)) : false;
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/sha1_wcf1.php b/phpBB/phpbb/passwords/driver/sha1_wcf1.php
new file mode 100644
index 0000000000..68006486c4
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/sha1_wcf1.php
@@ -0,0 +1,60 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class sha1_wcf1 extends base
+{
+ const PREFIX = '$wcf1$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (empty($hash) || strlen($hash) != 40 || !isset($user_row['user_passwd_salt']))
+ {
+ return false;
+ }
+ else
+ {
+ // Works for standard WCF 1.x, i.e. WBB3 and similar
+ return $this->helper->string_compare($hash, sha1($user_row['user_passwd_salt'] . sha1($user_row['user_passwd_salt'] . sha1($password))));
+ }
+ }
+}
diff --git a/phpBB/phpbb/passwords/driver/sha_xf1.php b/phpBB/phpbb/passwords/driver/sha_xf1.php
new file mode 100644
index 0000000000..9d8f01796e
--- /dev/null
+++ b/phpBB/phpbb/passwords/driver/sha_xf1.php
@@ -0,0 +1,68 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\passwords\driver;
+
+class sha_xf1 extends base
+{
+ const PREFIX = '$xf1$';
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_prefix()
+ {
+ return self::PREFIX;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is_legacy()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hash($password, $user_row = '')
+ {
+ // Do not support hashing
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function check($password, $hash, $user_row = array())
+ {
+ if (empty($hash) || (strlen($hash) != 40 && strlen($hash) != 64) || !isset($user_row['user_passwd_salt']))
+ {
+ return false;
+ }
+ else
+ {
+ // Works for xenforo 1.0, 1.1
+ if ($this->helper->string_compare($hash, sha1(sha1($password) . $user_row['user_passwd_salt']))
+ || $this->helper->string_compare($hash, hash('sha256', hash('sha256', $password) . $user_row['user_passwd_salt'])))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/phpBB/phpbb/passwords/helper.php b/phpBB/phpbb/passwords/helper.php
index 95bad5805f..c2a49202cd 100644
--- a/phpBB/phpbb/passwords/helper.php
+++ b/phpBB/phpbb/passwords/helper.php
@@ -1,17 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords;
-/**
-* @package passwords
-*/
class helper
{
/**
@@ -59,7 +60,7 @@ class helper
$data[$type] .= ($data[$type] !== '$') ? '\\' : '';
$data[$type] .= str_replace('$', '', $value);
}
- elseif ($type == 'settings')
+ else if ($type == 'settings')
{
$data[$type] .= ($data[$type] !== '$') ? '$' : '';
$data[$type] .= $value;
diff --git a/phpBB/phpbb/passwords/manager.php b/phpBB/phpbb/passwords/manager.php
index 0ac6b05ec4..b2caba81f2 100644
--- a/phpBB/phpbb/passwords/manager.php
+++ b/phpBB/phpbb/passwords/manager.php
@@ -1,17 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\passwords;
-/**
-* @package passwords
-*/
class manager
{
/**
@@ -38,32 +39,58 @@ class manager
/**
* Passwords helper
- * @var phpbb\passwords\helper
+ * @var \phpbb\passwords\helper
*/
protected $helper;
/**
* phpBB configuration
- * @var phpbb\config\config
+ * @var \phpbb\config\config
*/
protected $config;
/**
+ * @var bool Whether or not initialized() has been called
+ */
+ private $initialized = false;
+
+ /**
+ * @var array Hashing driver service collection
+ */
+ private $hashing_algorithms;
+
+ /**
+ * @var array List of default driver types
+ */
+ private $defaults;
+
+ /**
* Construct a passwords object
*
- * @param phpbb\config\config $config phpBB configuration
- * @param array $hashing_algorithms Hashing driver
- * service collection
- * @param phpbb\passwords\helper $helper Passwords helper object
- * @param string $defaults List of default driver types
+ * @param \phpbb\config\config $config phpBB configuration
+ * @param array $hashing_algorithms Hashing driver service collection
+ * @param \phpbb\passwords\helper $helper Passwords helper object
+ * @param array $defaults List of default driver types
*/
public function __construct(\phpbb\config\config $config, $hashing_algorithms, helper $helper, $defaults)
{
$this->config = $config;
$this->helper = $helper;
+ $this->hashing_algorithms = $hashing_algorithms;
+ $this->defaults = $defaults;
+ }
- $this->fill_type_map($hashing_algorithms);
- $this->register_default_type($defaults);
+ /**
+ * Initialize the internal state
+ */
+ protected function initialize()
+ {
+ if (!$this->initialized)
+ {
+ $this->initialized = true;
+ $this->fill_type_map($this->hashing_algorithms);
+ $this->register_default_type($this->defaults);
+ }
}
/**
@@ -88,7 +115,7 @@ class manager
/**
* Fill algorithm type map
*
- * @param phpbb\di\service_collection $hashing_algorithms
+ * @param \phpbb\di\service_collection $hashing_algorithms
*/
protected function fill_type_map($hashing_algorithms)
{
@@ -140,9 +167,11 @@ class manager
*/
if (!preg_match('#^\$([a-zA-Z0-9\\\]*?)\$#', $hash, $match))
{
- return $this->get_algorithm('$H$');
+ return false;
}
+ $this->initialize();
+
// Be on the lookout for multiple hashing algorithms
// 2 is correct: H\2a > 2, H\P > 2
if (strlen($match[1]) > 2)
@@ -191,6 +220,8 @@ class manager
return false;
}
+ $this->initialize();
+
// Try to retrieve algorithm by service name if type doesn't
// start with dollar sign
if (!is_array($type) && strpos($type, '$') !== 0 && isset($this->algorithms[$type]))
@@ -223,9 +254,10 @@ class manager
*
* @param string $password Password that should be checked
* @param string $hash Stored hash
+ * @param array $user_row User's row in users table
* @return string|bool True if password is correct, false if not
*/
- public function check($password, $hash)
+ public function check($password, $hash, $user_row = array())
{
if (strlen($password) > 4096)
{
@@ -234,11 +266,21 @@ class manager
return false;
}
+ // Empty hashes can't be checked
+ if (empty($hash))
+ {
+ return false;
+ }
+
+ $this->initialize();
+
// First find out what kind of hash we're dealing with
$stored_hash_type = $this->detect_algorithm($hash);
if ($stored_hash_type == false)
{
- return false;
+ // Still check MD5 hashes as that is what the installer
+ // will default to for the admin user
+ return $this->get_algorithm('$H$')->check($password, $hash);
}
// Multiple hash passes needed
@@ -258,6 +300,21 @@ class manager
$this->convert_flag = false;
}
+ // Check all legacy hash types if prefix is $CP$
+ if ($stored_hash_type->get_prefix() === '$CP$')
+ {
+ // Remove $CP$ prefix for proper checking
+ $hash = substr($hash, 4);
+
+ foreach ($this->type_map as $algorithm)
+ {
+ if ($algorithm->is_legacy() && $algorithm->check($password, $hash, $user_row) === true)
+ {
+ return true;
+ }
+ }
+ }
+
return $stored_hash_type->check($password, $hash);
}
@@ -272,6 +329,8 @@ class manager
*/
public function combined_hash_password($password_hash, $type)
{
+ $this->initialize();
+
$data = array(
'prefix' => '$',
'settings' => '$',
diff --git a/phpBB/phpbb/path_helper.php b/phpBB/phpbb/path_helper.php
index a8e12c4063..7b0d6f0fba 100644
--- a/phpBB/phpbb/path_helper.php
+++ b/phpBB/phpbb/path_helper.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,16 +15,18 @@ namespace phpbb;
/**
* A class with various functions that are related to paths, files and the filesystem
-* @package phpBB3
*/
class path_helper
{
/** @var \phpbb\symfony_request */
protected $symfony_request;
- /** @var \phpbb\filesystem */
+ /** @var \phpbb\filesystem\filesystem_interface */
protected $filesystem;
+ /** @var \phpbb\request\request_interface */
+ protected $request;
+
/** @var string */
protected $phpbb_root_path;
@@ -37,14 +43,17 @@ class path_helper
* Constructor
*
* @param \phpbb\symfony_request $symfony_request
- * @param \phpbb\filesystem $filesystem
+ * @param \phpbb\filesystem\filesystem_interface $filesystem
+ * @param \phpbb\request\request_interface $request
* @param string $phpbb_root_path Relative path to phpBB root
- * @param string $php_ext PHP extension (php)
+ * @param string $php_ext PHP file extension
+ * @param mixed $adm_relative_path Relative path admin path to adm/ root
*/
- public function __construct(\phpbb\symfony_request $symfony_request, \phpbb\filesystem $filesystem, $phpbb_root_path, $php_ext, $adm_relative_path = null)
+ public function __construct(\phpbb\symfony_request $symfony_request, \phpbb\filesystem\filesystem_interface $filesystem, \phpbb\request\request_interface $request, $phpbb_root_path, $php_ext, $adm_relative_path = null)
{
$this->symfony_request = $symfony_request;
$this->filesystem = $filesystem;
+ $this->request = $request;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->adm_relative_path = $adm_relative_path;
@@ -95,7 +104,13 @@ class path_helper
{
$path = substr($path, strlen($this->phpbb_root_path));
- return $this->get_web_root_path() . $path;
+ $web_root_path = $this->get_web_root_path();
+ if (substr($web_root_path, -8) === 'app.php/' && substr($path, 0, 7) === 'app.php')
+ {
+ $path = substr($path, 8);
+ }
+
+ return $this->filesystem->clean_path($web_root_path . $path);
}
return $path;
@@ -139,6 +154,7 @@ class path_helper
return $this->web_root_path;
}
+ // We do not need to escape $path_info, $request_uri and $script_name because we can not find their content in the result.
// Path info (e.g. /foo/bar)
$path_info = $this->filesystem->clean_path($this->symfony_request->getPathInfo());
@@ -149,6 +165,16 @@ class path_helper
$script_name = $this->symfony_request->getScriptName();
/*
+ * If the path info is empty but we're using app.php, then we
+ * might be using an empty route like app.php/ which is
+ * supported by symfony's routing
+ */
+ if ($path_info === '/' && preg_match('/app\.' . $this->php_ext . '\/$/', $request_uri))
+ {
+ return $this->web_root_path = $this->filesystem->clean_path('./../' . $this->phpbb_root_path);
+ }
+
+ /*
* If the path info is empty (single /), then we're not using
* a route like app.php/foo/bar
*/
@@ -157,31 +183,120 @@ class path_helper
return $this->web_root_path = $this->phpbb_root_path;
}
+ /*
+ * Check AJAX request:
+ * If the current request is a AJAX we need to fix the paths.
+ * We need to get the root path based on the Referer, so we can use
+ * the generated URLs in the template of the Referer. If we do not
+ * generate the relative path based on the Referer, but based on the
+ * currently requested URL, the generated URLs will not point to the
+ * intended locations:
+ * Referer desired URL desired relative root path
+ * memberlist.php faq.php ./
+ * memberlist.php app.php/foo/bar ./
+ * app.php/foo memberlist.php ../
+ * app.php/foo app.php/fox ../
+ * app.php/foo/bar memberlist.php ../../
+ * ../page.php memberlist.php ./phpBB/
+ * ../sub/page.php memberlist.php ./../phpBB/
+ *
+ * The referer must be specified as a parameter in the query.
+ */
+ if ($this->request->is_ajax() && $this->symfony_request->get('_referer'))
+ {
+ // We need to escape $absolute_board_url because it can be partially concatenated to the result.
+ $absolute_board_url = $this->request->escape($this->symfony_request->getSchemeAndHttpHost() . $this->symfony_request->getBasePath(), true);
+
+ $referer_web_root_path = $this->get_web_root_path_from_ajax_referer(
+ $this->symfony_request->get('_referer'),
+ $absolute_board_url
+ );
+ return $this->web_root_path = $this->phpbb_root_path . $referer_web_root_path;
+ }
+
// How many corrections might we need?
$corrections = substr_count($path_info, '/');
/*
- * If the script name (e.g. phpBB/app.php) exists in the
- * requestUri (e.g. phpBB/app.php/foo/template), then we
- * are have a non-rewritten URL.
+ * If the script name (e.g. phpBB/app.php) does not exists in the
+ * requestUri (e.g. phpBB/app.php/foo/template), then we are rewriting
+ * the URL. So we must reduce the slash count by 1.
*/
- if (strpos($request_uri, $script_name) === 0)
+ if (strpos($request_uri, $script_name) !== 0)
{
- /*
- * Append ../ to the end of the phpbb_root_path as many times
- * as / exists in path_info
- */
- return $this->web_root_path = $this->phpbb_root_path . str_repeat('../', $corrections);
+ $corrections--;
}
- /*
- * If we're here it means we're at a re-written path, so we must
- * correct the relative path for web URLs. We must append ../
- * to the end of the root path as many times as / exists in path_info
- * less one time (because the script, e.g. /app.php, doesn't exist in
- * the URL)
- */
- return $this->web_root_path = $this->phpbb_root_path . str_repeat('../', $corrections - 1);
+ // Prepend ../ to the phpbb_root_path as many times as / exists in path_info
+ $this->web_root_path = $this->filesystem->clean_path(
+ './' . str_repeat('../', $corrections) . $this->phpbb_root_path
+ );
+ return $this->web_root_path;
+ }
+
+ /**
+ * Get the web root path of the referer form an ajax request
+ *
+ * @param string $absolute_referer_url
+ * @param string $absolute_board_url
+ * @return string
+ */
+ public function get_web_root_path_from_ajax_referer($absolute_referer_url, $absolute_board_url)
+ {
+ // If the board URL is in the beginning of the referer, this means
+ // we the referer is in the board URL or a subdirectory of it.
+ // So we just need to count the / (slashes) in the left over part of
+ // the referer and prepend ../ the the current root_path, to get the
+ // web root path of the referer.
+ if (strpos($absolute_referer_url, $absolute_board_url) === 0)
+ {
+ $relative_referer_path = substr($absolute_referer_url, strlen($absolute_board_url));
+ $has_params = strpos($relative_referer_path, '?');
+ if ($has_params !== false)
+ {
+ $relative_referer_path = substr($relative_referer_path, 0, $has_params);
+ }
+ $corrections = substr_count($relative_referer_path, '/');
+ return $this->phpbb_root_path . str_repeat('../', $corrections - 1);
+ }
+
+ // If not, it's a bit more complicated. We go to the parent directory
+ // of the referer until we find the remaining referer in the board URL.
+ // Foreach directory we need to add a ../ to the fixed root_path.
+ // When we finally found it, we need to remove the remaining referer
+ // from the board URL, to get the boards root path.
+ // If the then append these two strings, we get our fixed web root path.
+ $fixed_root_path = '';
+ $referer_dir = $absolute_referer_url;
+ $has_params = strpos($referer_dir, '?');
+ if ($has_params !== false)
+ {
+ $referer_dir = substr($referer_dir, 0, $has_params);
+ }
+
+ // If we do not find a slash at the end of the referer, we come
+ // from a file. So the first dirname() does not need a traversal
+ // path correction.
+ if (substr($referer_dir, -1) !== '/')
+ {
+ $referer_dir = dirname($referer_dir);
+ }
+
+ while (($dir_position = strpos($absolute_board_url, $referer_dir)) !== 0)
+ {
+ $fixed_root_path .= '../';
+ $referer_dir = dirname($referer_dir);
+
+ // Just return phpbb_root_path if we reach the top directory
+ if ($referer_dir === '.')
+ {
+ return $this->phpbb_root_path;
+ }
+ }
+
+ $fixed_root_path .= substr($absolute_board_url, strlen($referer_dir) + 1);
+ // Add trailing slash
+ return $this->phpbb_root_path . $fixed_root_path . '/';
}
/**
@@ -206,4 +321,172 @@ class path_helper
return $scheme . $this->filesystem->clean_path($path);
}
+
+ /**
+ * Glue URL parameters together
+ *
+ * @param array $params URL parameters in the form of array(name => value)
+ * @return string Returns the glued string, e.g. name1=value1&amp;name2&amp;name3=value3
+ */
+ public function glue_url_params($params)
+ {
+ $_params = array();
+
+ foreach ($params as $key => $value)
+ {
+ // some parameters do not have value
+ if ($value !== null)
+ {
+ $_params[] = $key . '=' . $value;
+ }
+ else
+ {
+ $_params[] = $key;
+ }
+ }
+ return implode('&amp;', $_params);
+ }
+
+ /**
+ * Get the base and parameters of a URL
+ *
+ * @param string $url URL to break apart
+ * @param bool $is_amp Is the parameter separator &amp;. Defaults to true.
+ * @return array Returns the base and parameters in the form of array('base' => string, 'params' => array(name => value))
+ */
+ public function get_url_parts($url, $is_amp = true)
+ {
+ $separator = ($is_amp) ? '&amp;' : '&';
+ $params = array();
+
+ if (strpos($url, '?') !== false)
+ {
+ $base = substr($url, 0, strpos($url, '?'));
+ $args = substr($url, strlen($base) + 1);
+ $args = ($args) ? explode($separator, $args) : array();
+
+ foreach ($args as $argument)
+ {
+ if (empty($argument))
+ {
+ continue;
+ }
+
+ // some parameters don't have value
+ if (strpos($argument, '=') !== false)
+ {
+ list($key, $value) = explode('=', $argument, 2);
+ }
+ else
+ {
+ $key = $argument;
+ $value = null;
+ }
+
+ if ($key === '')
+ {
+ continue;
+ }
+
+ $params[$key] = $value;
+ }
+ }
+ else
+ {
+ $base = $url;
+ }
+
+ return array(
+ 'base' => $base,
+ 'params' => $params,
+ );
+ }
+
+ /**
+ * Strip parameters from an already built URL.
+ *
+ * @param string $url URL to strip parameters from
+ * @param array|string $strip Parameters to strip.
+ * @param bool $is_amp Is the parameter separator &amp;. Defaults to true.
+ * @return string Returns the new URL.
+ */
+ public function strip_url_params($url, $strip, $is_amp = true)
+ {
+ $url_parts = $this->get_url_parts($url, $is_amp);
+ $params = $url_parts['params'];
+
+ if (!is_array($strip))
+ {
+ $strip = array($strip);
+ }
+
+ if (!empty($params))
+ {
+ // Strip the parameters off
+ foreach ($strip as $param)
+ {
+ unset($params[$param]);
+ }
+ }
+
+ return $url_parts['base'] . (($params) ? '?' . $this->glue_url_params($params) : '');
+ }
+
+ /**
+ * Append parameters to an already built URL.
+ *
+ * @param string $url URL to append parameters to
+ * @param array $new_params Parameters to add in the form of array(name => value)
+ * @param bool $is_amp Is the parameter separator &amp;. Defaults to true.
+ * @return string Returns the new URL.
+ */
+ public function append_url_params($url, $new_params, $is_amp = true)
+ {
+ $url_parts = $this->get_url_parts($url, $is_amp);
+ $params = array_merge($url_parts['params'], $new_params);
+
+ // Move the sid to the end if it's set
+ if (isset($params['sid']))
+ {
+ $sid = $params['sid'];
+ unset($params['sid']);
+ $params['sid'] = $sid;
+ }
+
+ return $url_parts['base'] . (($params) ? '?' . $this->glue_url_params($params) : '');
+ }
+
+ /**
+ * Get a valid page
+ *
+ * @param string $page The page to verify
+ * @param bool $mod_rewrite Whether mod_rewrite is enabled, default: false
+ *
+ * @return string A valid page based on given page and mod_rewrite
+ */
+ public function get_valid_page($page, $mod_rewrite = false)
+ {
+ // We need to be cautious here.
+ // On some situations, the redirect path is an absolute URL, sometimes a relative path
+ // For a relative path, let's prefix it with $phpbb_root_path to point to the correct location,
+ // else we use the URL directly.
+ $url_parts = parse_url($page);
+
+ // URL
+ if ($url_parts === false || empty($url_parts['scheme']) || empty($url_parts['host']))
+ {
+ // Remove 'app.php/' from the page, when rewrite is enabled.
+ // Treat app.php as a reserved file name and remove on mod rewrite
+ // even if it might not be in the phpBB root.
+ if ($mod_rewrite && ($app_position = strpos($page, 'app.' . $this->php_ext . '/')) !== false)
+ {
+ $page = substr($page, 0, $app_position) . substr($page, $app_position + strlen('app.' . $this->php_ext . '/'));
+ }
+
+ // Remove preceding slashes from page name and prepend root path
+ $page = $this->get_phpbb_root_path() . ltrim($page, '/\\');
+ }
+
+ return $page;
+ }
}
diff --git a/phpBB/phpbb/permissions.php b/phpBB/phpbb/permissions.php
index a3fddb0b9e..c9181e6202 100644
--- a/phpBB/phpbb/permissions.php
+++ b/phpBB/phpbb/permissions.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,7 +17,7 @@ class permissions
{
/**
* Event dispatcher object
- * @var \phpbb\event\dispatcher
+ * @var \phpbb\event\dispatcher_interface
*/
protected $dispatcher;
@@ -26,11 +30,10 @@ class permissions
/**
* Constructor
*
- * @param \phpbb\event\dispatcher $phpbb_dispatcher Event dispatcher
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher
* @param \phpbb\user $user User Object
- * @return null
*/
- public function __construct(\phpbb\event\dispatcher $phpbb_dispatcher, \phpbb\user $user)
+ public function __construct(\phpbb\event\dispatcher_interface $phpbb_dispatcher, \phpbb\user $user)
{
$this->dispatcher = $phpbb_dispatcher;
$this->user = $user;
@@ -57,7 +60,7 @@ class permissions
* 'lang' => 'ACL_U_VIEWPROFILE',
* 'cat' => 'profile',
* ),
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
$vars = array('types', 'categories', 'permissions');
extract($phpbb_dispatcher->trigger_event('core.permissions', compact($vars)));
@@ -157,6 +160,28 @@ class permissions
}
/**
+ * Checks if a category has been defined
+ *
+ * @param string $category Identifier of the category
+ * @return bool True if the category is defined, false otherwise
+ */
+ public function category_defined($category)
+ {
+ return isset($this->categories[$category]);
+ }
+
+ /**
+ * Checks if a permission has been defined
+ *
+ * @param string $permission Identifier of the permission
+ * @return bool True if the permission is defined, false otherwise
+ */
+ public function permission_defined($permission)
+ {
+ return isset($this->permissions[$permission]);
+ }
+
+ /**
* Returns the language string of a permission
*
* @param string $permission Identifier of the permission
@@ -248,6 +273,7 @@ class permissions
'f_post' => array('lang' => 'ACL_F_POST', 'cat' => 'post'),
'f_sticky' => array('lang' => 'ACL_F_STICKY', 'cat' => 'post'),
'f_announce' => array('lang' => 'ACL_F_ANNOUNCE', 'cat' => 'post'),
+ 'f_announce_global' => array('lang' => 'ACL_F_ANNOUNCE_GLOBAL', 'cat' => 'post'),
'f_reply' => array('lang' => 'ACL_F_REPLY', 'cat' => 'post'),
'f_edit' => array('lang' => 'ACL_F_EDIT', 'cat' => 'post'),
'f_delete' => array('lang' => 'ACL_F_DELETE', 'cat' => 'post'),
@@ -274,15 +300,17 @@ class permissions
'm_approve' => array('lang' => 'ACL_M_APPROVE', 'cat' => 'post_actions'),
'm_report' => array('lang' => 'ACL_M_REPORT', 'cat' => 'post_actions'),
'm_chgposter' => array('lang' => 'ACL_M_CHGPOSTER', 'cat' => 'post_actions'),
+ 'm_info' => array('lang' => 'ACL_M_INFO', 'cat' => 'post_actions'),
+ 'm_softdelete' => array('lang' => 'ACL_M_SOFTDELETE', 'cat' => 'post_actions'),
'm_move' => array('lang' => 'ACL_M_MOVE', 'cat' => 'topic_actions'),
'm_lock' => array('lang' => 'ACL_M_LOCK', 'cat' => 'topic_actions'),
'm_split' => array('lang' => 'ACL_M_SPLIT', 'cat' => 'topic_actions'),
'm_merge' => array('lang' => 'ACL_M_MERGE', 'cat' => 'topic_actions'),
- 'm_info' => array('lang' => 'ACL_M_INFO', 'cat' => 'misc'),
- 'm_warn' => array('lang' => 'ACL_M_WARN', 'cat' => 'misc'),
- 'm_ban' => array('lang' => 'ACL_M_BAN', 'cat' => 'misc'),
+ 'm_warn' => array('lang' => 'ACL_M_WARN', 'cat' => 'misc'),
+ 'm_pm_report' => array('lang' => 'ACL_M_PM_REPORT', 'cat' => 'misc'),
+ 'm_ban' => array('lang' => 'ACL_M_BAN', 'cat' => 'misc'),
// Admin Permissions
'a_board' => array('lang' => 'ACL_A_BOARD', 'cat' => 'settings'),
diff --git a/phpBB/phpbb/php/ini.php b/phpBB/phpbb/php/ini.php
index f0f53807fe..73a30659a6 100644
--- a/phpBB/phpbb/php/ini.php
+++ b/phpBB/phpbb/php/ini.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -13,8 +17,6 @@ namespace phpbb\php;
* Wrapper class for ini_get function.
*
* Provides easier handling of the different interpretations of ini values.
-*
-* @package phpBB
*/
class ini
{
diff --git a/phpBB/phpbb/plupload/plupload.php b/phpBB/phpbb/plupload/plupload.php
index 6bff2b8a7e..a47fc87adf 100644
--- a/phpBB/phpbb/plupload/plupload.php
+++ b/phpBB/phpbb/plupload/plupload.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\plupload;
/**
* This class handles all server-side plupload functions
-*
-* @package \phpbb\plupload\plupload
*/
class plupload
{
@@ -37,7 +39,7 @@ class plupload
protected $user;
/**
- * @var \phpbb\php\ini
+ * @var \bantu\IniGetWrapper\IniGetWrapper
*/
protected $php_ini;
@@ -65,12 +67,10 @@ class plupload
* @param \phpbb\config\config $config
* @param \phpbb\request\request_interface $request
* @param \phpbb\user $user
- * @param \phpbb\php\ini $php_ini
+ * @param \bantu\IniGetWrapper\IniGetWrapper $php_ini
* @param \phpbb\mimetype\guesser $mimetype_guesser
- *
- * @return null
*/
- public function __construct($phpbb_root_path, \phpbb\config\config $config, \phpbb\request\request_interface $request, \phpbb\user $user, \phpbb\php\ini $php_ini, \phpbb\mimetype\guesser $mimetype_guesser)
+ public function __construct($phpbb_root_path, \phpbb\config\config $config, \phpbb\request\request_interface $request, \phpbb\user $user, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \phpbb\mimetype\guesser $mimetype_guesser)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->config = $config;
@@ -79,8 +79,7 @@ class plupload
$this->php_ini = $php_ini;
$this->mimetype_guesser = $mimetype_guesser;
- $this->upload_directory = $this->phpbb_root_path . $this->config['upload_path'];
- $this->temporary_directory = $this->upload_directory . '/plupload';
+ $this->set_default_directories();
}
/**
@@ -120,10 +119,13 @@ class plupload
{
rename("{$file_path}.part", $file_path);
+ // Reset upload directories to defaults once completed
+ $this->set_default_directories();
+
// Need to modify some of the $_FILES values to reflect the new file
return array(
'tmp_name' => $file_path,
- 'name' => $this->request->variable('real_filename', ''),
+ 'name' => $this->request->variable('real_filename', '', true),
'size' => filesize($file_path),
'type' => $this->mimetype_guesser->guess($file_path, $file_name),
);
@@ -146,10 +148,11 @@ class plupload
* @param \phpbb\template\template $template
* @param string $s_action The URL to submit the POST data to
* @param int $forum_id The ID of the forum
+ * @param int $max_files Maximum number of files allowed. 0 for unlimited.
*
* @return null
*/
- public function configure(\phpbb\cache\service $cache, \phpbb\template\template $template, $s_action, $forum_id)
+ public function configure(\phpbb\cache\service $cache, \phpbb\template\template $template, $s_action, $forum_id, $max_files)
{
$filters = $this->generate_filter_string($cache, $forum_id);
$chunk_size = $this->get_chunk_size();
@@ -161,6 +164,9 @@ class plupload
'FILTERS' => $filters,
'CHUNK_SIZE' => $chunk_size,
'S_PLUPLOAD_URL' => htmlspecialchars_decode($s_action),
+ 'MAX_ATTACHMENTS' => $max_files,
+ 'ATTACH_ORDER' => ($this->config['display_order']) ? 'asc' : 'desc',
+ 'L_TOO_MANY_ATTACHMENTS' => $this->user->lang('TOO_MANY_ATTACHMENTS', $max_files),
));
$this->user->add_lang('plupload');
@@ -261,8 +267,8 @@ class plupload
{
$resize = sprintf(
'resize: {width: %d, height: %d, quality: 100},',
- (int) $this->config['img_max_height'],
- (int) $this->config['img_max_width']
+ (int) $this->config['img_max_width'],
+ (int) $this->config['img_max_height']
);
}
@@ -278,9 +284,9 @@ class plupload
public function get_chunk_size()
{
$max = min(
- $this->php_ini->get_bytes('upload_max_filesize'),
- $this->php_ini->get_bytes('post_max_size'),
- max(1, $this->php_ini->get_bytes('memory_limit')),
+ $this->php_ini->getBytes('upload_max_filesize'),
+ $this->php_ini->getBytes('post_max_size'),
+ max(1, $this->php_ini->getBytes('memory_limit')),
$this->config['max_filesize']
);
@@ -297,7 +303,7 @@ class plupload
$this->temporary_directory,
$this->config['plupload_salt'],
md5($file_name),
- \filespec::get_extension($file_name)
+ \phpbb\files\filespec::get_extension($file_name)
);
}
@@ -320,7 +326,7 @@ class plupload
$tmp_file = $this->temporary_filepath($upload['tmp_name']);
- if (!move_uploaded_file($upload['tmp_name'], $tmp_file))
+ if (!phpbb_is_writable($this->temporary_directory) || !move_uploaded_file($upload['tmp_name'], $tmp_file))
{
$this->emit_error(103, 'PLUPLOAD_ERR_MOVE_UPLOADED');
}
@@ -368,4 +374,29 @@ class plupload
);
}
}
+
+ /**
+ * Sets the default directories for uploads
+ *
+ * @return null
+ */
+ protected function set_default_directories()
+ {
+ $this->upload_directory = $this->phpbb_root_path . $this->config['upload_path'];
+ $this->temporary_directory = $this->upload_directory . '/plupload';
+ }
+
+ /**
+ * Sets the upload directories to the specified paths
+ *
+ * @param string $upload_directory Upload directory
+ * @param string $temporary_directory Temporary directory
+ *
+ * @return null
+ */
+ public function set_upload_directories($upload_directory, $temporary_directory)
+ {
+ $this->upload_directory = $upload_directory;
+ $this->temporary_directory = $temporary_directory;
+ }
}
diff --git a/phpBB/phpbb/profilefields/lang_helper.php b/phpBB/phpbb/profilefields/lang_helper.php
index 7bae1bdc18..2e353722b2 100644
--- a/phpBB/phpbb/profilefields/lang_helper.php
+++ b/phpBB/phpbb/profilefields/lang_helper.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\profilefields;
/**
* Custom Profile Fields
-* @package phpBB3
*/
class lang_helper
{
@@ -23,7 +26,7 @@ class lang_helper
/**
* Database object
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
@@ -36,7 +39,7 @@ class lang_helper
/**
* Construct
*
- * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\db\driver\driver_interface $db Database object
* @param string $language_table Table where the language strings are stored
*/
public function __construct($db, $language_table)
@@ -46,43 +49,50 @@ class lang_helper
}
/**
- * Get language entries for options and store them here for later use
+ * Loads preview options into language entries for options
+ *
+ * @param int $field_id
+ * @param int $lang_id
+ * @param mixed $preview_options
*/
- public function get_option_lang($field_id, $lang_id, $field_type, $preview_options)
+ public function load_preview_options($field_id, $lang_id, $preview_options)
{
- if ($preview_options !== false)
- {
- $lang_options = (!is_array($preview_options)) ? explode("\n", $preview_options) : $preview_options;
+ $lang_options = (!is_array($preview_options)) ? explode("\n", $preview_options) : $preview_options;
- foreach ($lang_options as $num => $var)
+ foreach ($lang_options as $num => $var)
+ {
+ if (!isset($this->options_lang[$field_id]))
{
- if (!isset($this->options_lang[$field_id]))
- {
- $this->options_lang[$field_id] = array();
- }
- if (!isset($this->options_lang[$field_id][$lang_id]))
- {
- $this->options_lang[$field_id][$lang_id] = array();
- }
- $this->options_lang[$field_id][$lang_id][($num + 1)] = $var;
+ $this->options_lang[$field_id] = array();
}
+ if (!isset($this->options_lang[$field_id][$lang_id]))
+ {
+ $this->options_lang[$field_id][$lang_id] = array();
+ }
+ $this->options_lang[$field_id][$lang_id][($num + 1)] = $var;
}
- else
- {
- $sql = 'SELECT option_id, lang_value
+ }
+
+ /**
+ * Fetches language entries for options from DB
+ *
+ * @param int $lang_id
+ */
+ public function load_option_lang($lang_id)
+ {
+ $sql = 'SELECT field_id, option_id, lang_value
FROM ' . $this->language_table . '
- WHERE field_id = ' . (int) $field_id . '
- AND lang_id = ' . (int) $lang_id . "
- AND field_type = '" . $this->db->sql_escape($field_type) . "'
+ WHERE lang_id = ' . (int) $lang_id . "
ORDER BY option_id";
- $result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
- {
- $this->options_lang[$field_id][$lang_id][($row['option_id'] + 1)] = $row['lang_value'];
- }
- $this->db->sql_freeresult($result);
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->options_lang[$row['field_id']][$lang_id][($row['option_id'] + 1)] = $row['lang_value'];
}
+
+ $this->db->sql_freeresult($result);
}
/**
diff --git a/phpBB/phpbb/profilefields/manager.php b/phpBB/phpbb/profilefields/manager.php
index ac2542a6d4..ea4b24af56 100644
--- a/phpBB/phpbb/profilefields/manager.php
+++ b/phpBB/phpbb/profilefields/manager.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb\profilefields;
/**
* Custom Profile Fields
-* @package phpBB3
*/
class manager
{
@@ -23,11 +26,17 @@ class manager
/**
* Database object
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
+ * Event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
* Request object
* @var \phpbb\request\request
*/
@@ -63,7 +72,8 @@ class manager
* Construct
*
* @param \phpbb\auth\auth $auth Auth object
- * @param \phpbb\db\driver\driver $db Database object
+ * @param \phpbb\db\driver\driver_interface $db Database object
+ * @param \phpbb\event\dispatcher_interface $dispatcher Event dispatcher object
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\di\service_collection $type_collection
@@ -72,10 +82,11 @@ class manager
* @param string $fields_language_table
* @param string $fields_data_table
*/
- public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver $db, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\di\service_collection $type_collection, \phpbb\user $user, $fields_table, $fields_language_table, $fields_data_table)
+ public function __construct(\phpbb\auth\auth $auth, \phpbb\db\driver\driver_interface $db, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\di\service_collection $type_collection, \phpbb\user $user, $fields_table, $fields_language_table, $fields_data_table)
{
$this->auth = $auth;
$this->db = $db;
+ $this->dispatcher = $dispatcher;
$this->request = $request;
$this->template = $template;
$this->type_collection = $type_collection;
@@ -231,14 +242,11 @@ class manager
if (!$this->db->sql_affectedrows())
{
+ $cp_data = $this->build_insert_sql_array($cp_data);
$cp_data['user_id'] = (int) $user_id;
- $this->db->sql_return_on_error(true);
-
$sql = 'INSERT INTO ' . $this->fields_data_table . ' ' . $this->db->sql_build_array('INSERT', $cp_data);
$this->db->sql_query($sql);
-
- $this->db->sql_return_on_error(false);
}
}
@@ -268,116 +276,200 @@ class manager
$profile_field = $this->type_collection[$field_data['field_type']];
$tpl_fields[] = array(
+ 'PROFILE_FIELD_IDENT' => $field_ident,
'PROFILE_FIELD_TYPE' => $field_data['field_type'],
'PROFILE_FIELD_NAME' => $profile_field->get_field_name($field_data['lang_name']),
'PROFILE_FIELD_EXPLAIN' => $this->user->lang($field_data['lang_explain']),
);
}
+ $profile_cache = $this->profile_cache;
+
+ /**
+ * Event to modify template headlines of the generated profile fields
+ *
+ * @event core.generate_profile_fields_template_headlines
+ * @var string restrict_option Restrict the published fields to a certain profile field option
+ * @var array tpl_fields Array with template data fields
+ * @var array profile_cache A copy of the profile cache to make additional checks
+ * @since 3.1.6-RC1
+ */
+ $vars = array(
+ 'restrict_option',
+ 'tpl_fields',
+ 'profile_cache',
+ );
+ extract($this->dispatcher->trigger_event('core.generate_profile_fields_template_headlines', compact($vars)));
+ unset($profile_cache);
+
return $tpl_fields;
}
/**
- * Assign fields to template, used for viewprofile, viewtopic and memberlist (if load setting is enabled)
- * This is directly connected to the user -> mode == grab is to grab the user specific fields, mode == show is for assigning the row to the template
+ * Grab the user specific profile fields data
+ *
+ * @param int|array $user_ids Single user id or an array of ids
+ * @return array Users profile fields data
*/
- public function generate_profile_fields_template($mode, $user_id = 0, $profile_row = false)
+ public function grab_profile_fields_data($user_ids = 0)
{
- if ($mode == 'grab')
+ if (!is_array($user_ids))
{
- if (!is_array($user_id))
- {
- $user_id = array($user_id);
- }
+ $user_ids = array($user_ids);
+ }
- if (!sizeof($this->profile_cache))
- {
- $this->build_cache();
- }
+ if (!sizeof($this->profile_cache))
+ {
+ $this->build_cache();
+ }
- if (!sizeof($user_id))
- {
- return array();
- }
+ if (!sizeof($user_ids))
+ {
+ return array();
+ }
- $sql = 'SELECT *
- FROM ' . $this->fields_data_table . '
- WHERE ' . $this->db->sql_in_set('user_id', array_map('intval', $user_id));
- $result = $this->db->sql_query($sql);
+ $sql = 'SELECT *
+ FROM ' . $this->fields_data_table . '
+ WHERE ' . $this->db->sql_in_set('user_id', array_map('intval', $user_ids));
+ $result = $this->db->sql_query($sql);
- $field_data = array();
- while ($row = $this->db->sql_fetchrow($result))
- {
- $field_data[$row['user_id']] = $row;
- }
- $this->db->sql_freeresult($result);
+ $field_data = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $field_data[$row['user_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
- $user_fields = array();
+ /**
+ * Event to modify profile fields data retrieved from the database
+ *
+ * @event core.grab_profile_fields_data
+ * @var array user_ids Single user id or an array of ids
+ * @var array field_data Array with profile fields data
+ * @since 3.1.0-b3
+ */
+ $vars = array('user_ids', 'field_data');
+ extract($this->dispatcher->trigger_event('core.grab_profile_fields_data', compact($vars)));
- $user_ids = $user_id;
+ $user_fields = array();
- // Go through the fields in correct order
- foreach (array_keys($this->profile_cache) as $used_ident)
+ // Go through the fields in correct order
+ foreach (array_keys($this->profile_cache) as $used_ident)
+ {
+ foreach ($field_data as $user_id => $row)
{
- foreach ($field_data as $user_id => $row)
- {
- $user_fields[$user_id][$used_ident]['value'] = $row['pf_' . $used_ident];
- $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
- }
+ $user_fields[$user_id][$used_ident]['value'] = $row['pf_' . $used_ident];
+ $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
+ }
- foreach ($user_ids as $user_id)
+ foreach ($user_ids as $user_id)
+ {
+ if (!isset($user_fields[$user_id][$used_ident]) && $this->profile_cache[$used_ident]['field_show_novalue'])
{
- if (!isset($user_fields[$user_id][$used_ident]) && $this->profile_cache[$used_ident]['field_show_novalue'])
- {
- $user_fields[$user_id][$used_ident]['value'] = '';
- $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
- }
+ $user_fields[$user_id][$used_ident]['value'] = '';
+ $user_fields[$user_id][$used_ident]['data'] = $this->profile_cache[$used_ident];
}
}
-
- return $user_fields;
}
- else if ($mode == 'show')
+
+ return $user_fields;
+ }
+
+ /**
+ * Assign the user's profile fields data to the template
+ *
+ * @param array $profile_row Array with users profile field data
+ * @param bool $use_contact_fields Should we display contact fields as such?
+ * This requires special treatments (links should not be parsed in the values, and more)
+ * @return array
+ */
+ public function generate_profile_fields_template_data($profile_row, $use_contact_fields = true)
+ {
+ // $profile_row == $user_fields[$row['user_id']];
+ $tpl_fields = array();
+ $tpl_fields['row'] = $tpl_fields['blockrow'] = array();
+
+ /**
+ * Event to modify data of the generated profile fields, before the template assignment loop
+ *
+ * @event core.generate_profile_fields_template_data_before
+ * @var array profile_row Array with users profile field data
+ * @var array tpl_fields Array with template data fields
+ * @var bool use_contact_fields Should we display contact fields as such?
+ * @since 3.1.0-b3
+ */
+ $vars = array('profile_row', 'tpl_fields', 'use_contact_fields');
+ extract($this->dispatcher->trigger_event('core.generate_profile_fields_template_data_before', compact($vars)));
+
+ foreach ($profile_row as $ident => $ident_ary)
{
- // $profile_row == $user_fields[$row['user_id']];
- $tpl_fields = array();
- $tpl_fields['row'] = $tpl_fields['blockrow'] = array();
+ $profile_field = $this->type_collection[$ident_ary['data']['field_type']];
+ $value = $profile_field->get_profile_value($ident_ary['value'], $ident_ary['data']);
+ $value_raw = $profile_field->get_profile_value_raw($ident_ary['value'], $ident_ary['data']);
- foreach ($profile_row as $ident => $ident_ary)
+ if ($value === null)
{
- $profile_field = $this->type_collection[$ident_ary['data']['field_type']];
- $value = $profile_field->get_profile_value($ident_ary['value'], $ident_ary['data']);
+ continue;
+ }
- if ($value === null)
+ $field_desc = $contact_url = '';
+ if ($use_contact_fields && $ident_ary['data']['field_is_contact'])
+ {
+ $value = $profile_field->get_profile_contact_value($ident_ary['value'], $ident_ary['data']);
+ $field_desc = $this->user->lang($ident_ary['data']['field_contact_desc']);
+ if (strpos($field_desc, '%s') !== false)
{
- continue;
+ $field_desc = sprintf($field_desc, $value);
}
+ $contact_url = '';
+ if (strpos($ident_ary['data']['field_contact_url'], '%s') !== false)
+ {
+ $contact_url = sprintf($ident_ary['data']['field_contact_url'], $value);
+ }
+ }
- $tpl_fields['row'] += array(
- 'PROFILE_' . strtoupper($ident) . '_VALUE' => $value,
- 'PROFILE_' . strtoupper($ident) . '_TYPE' => $ident_ary['data']['field_type'],
- 'PROFILE_' . strtoupper($ident) . '_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
- 'PROFILE_' . strtoupper($ident) . '_EXPLAIN'=> $this->user->lang($ident_ary['data']['lang_explain']),
-
- 'S_PROFILE_' . strtoupper($ident) => true,
- );
+ $tpl_fields['row'] += array(
+ 'PROFILE_' . strtoupper($ident) . '_IDENT' => $ident,
+ 'PROFILE_' . strtoupper($ident) . '_VALUE' => $value,
+ 'PROFILE_' . strtoupper($ident) . '_VALUE_RAW' => $value_raw,
+ 'PROFILE_' . strtoupper($ident) . '_CONTACT' => $contact_url,
+ 'PROFILE_' . strtoupper($ident) . '_DESC' => $field_desc,
+ 'PROFILE_' . strtoupper($ident) . '_TYPE' => $ident_ary['data']['field_type'],
+ 'PROFILE_' . strtoupper($ident) . '_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
+ 'PROFILE_' . strtoupper($ident) . '_EXPLAIN' => $this->user->lang($ident_ary['data']['lang_explain']),
+
+ 'S_PROFILE_' . strtoupper($ident) . '_CONTACT' => $ident_ary['data']['field_is_contact'],
+ 'S_PROFILE_' . strtoupper($ident) => true,
+ );
- $tpl_fields['blockrow'][] = array(
- 'PROFILE_FIELD_VALUE' => $value,
- 'PROFILE_FIELD_TYPE' => $ident_ary['data']['field_type'],
- 'PROFILE_FIELD_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
- 'PROFILE_FIELD_EXPLAIN' => $this->user->lang($ident_ary['data']['lang_explain']),
+ $tpl_fields['blockrow'][] = array(
+ 'PROFILE_FIELD_IDENT' => $ident,
+ 'PROFILE_FIELD_VALUE' => $value,
+ 'PROFILE_FIELD_VALUE_RAW' => $value_raw,
+ 'PROFILE_FIELD_CONTACT' => $contact_url,
+ 'PROFILE_FIELD_DESC' => $field_desc,
+ 'PROFILE_FIELD_TYPE' => $ident_ary['data']['field_type'],
+ 'PROFILE_FIELD_NAME' => $this->user->lang($ident_ary['data']['lang_name']),
+ 'PROFILE_FIELD_EXPLAIN' => $this->user->lang($ident_ary['data']['lang_explain']),
+
+ 'S_PROFILE_CONTACT' => $ident_ary['data']['field_is_contact'],
+ 'S_PROFILE_' . strtoupper($ident) => true,
+ );
+ }
- 'S_PROFILE_' . strtoupper($ident) => true,
- );
- }
+ /**
+ * Event to modify template data of the generated profile fields
+ *
+ * @event core.generate_profile_fields_template_data
+ * @var array profile_row Array with users profile field data
+ * @var array tpl_fields Array with template data fields
+ * @var bool use_contact_fields Should we display contact fields as such?
+ * @since 3.1.0-b3
+ */
+ $vars = array('profile_row', 'tpl_fields', 'use_contact_fields');
+ extract($this->dispatcher->trigger_event('core.generate_profile_fields_template_data', compact($vars)));
- return $tpl_fields;
- }
- else
- {
- trigger_error('Wrong mode for custom profile', E_USER_ERROR);
- }
+ return $tpl_fields;
}
/**
diff --git a/phpBB/phpbb/profilefields/type/type_base.php b/phpBB/phpbb/profilefields/type/type_base.php
index 9c363a7b4e..9b4bada26d 100644
--- a/phpBB/phpbb/profilefields/type/type_base.php
+++ b/phpBB/phpbb/profilefields/type/type_base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -35,7 +39,6 @@ abstract class type_base implements type_interface
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -87,6 +90,14 @@ abstract class type_base implements type_interface
/**
* {@inheritDoc}
*/
+ public function get_profile_contact_value($field_value, $field_data)
+ {
+ return $this->get_profile_value($field_value, $field_data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function get_language_options_input($field_data)
{
$field_data['l_lang_name'] = $this->request->variable('l_lang_name', array(0 => ''), true);
@@ -122,7 +133,7 @@ abstract class type_base implements type_interface
{
foreach ($field_data[$key] as $lang_id => $options)
{
- $field_data[$key][$lang_id] = explode("\n", $options);
+ $field_data[$key][$lang_id] = is_array($options) ? $options : explode("\n", $options);
}
return $current_value;
@@ -147,7 +158,19 @@ abstract class type_base implements type_interface
}
else
{
- return $this->request->variable($key, '', true);
+ $default_value = '';
+ $lang_fields = array(
+ 'l_lang_name',
+ 'l_lang_explain',
+ 'l_lang_default_value',
+ 'l_lang_options',
+ );
+
+ if (in_array($key, $lang_fields))
+ {
+ $default_value = array(0 => '');
+ }
+ return $this->request->variable($key, $default_value, true);
}
}
diff --git a/phpBB/phpbb/profilefields/type/type_bool.php b/phpBB/phpbb/profilefields/type/type_bool.php
index fa9c0a8714..f6f3f17a6c 100644
--- a/phpBB/phpbb/profilefields/type/type_bool.php
+++ b/phpBB/phpbb/profilefields/type/type_bool.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -42,7 +46,6 @@ class type_bool extends type_base
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\profilefields\lang_helper $lang_helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -152,7 +155,7 @@ class type_bool extends type_base
if (!$this->lang_helper->is_set($field_id, $lang_id))
{
- $this->lang_helper->get_option_lang($field_id, $lang_id, FIELD_BOOL, false);
+ $this->lang_helper->load_option_lang($lang_id);
}
if (!$field_value && $field_data['field_show_novalue'])
@@ -170,8 +173,26 @@ class type_bool extends type_base
}
else
{
- return $this->lang_helper->is_set($field_id, $lang_id, $field_value + 1);
+ return $this->lang_helper->is_set($field_id, $lang_id, $field_value + 1) ? $this->lang_helper->get($field_id, $lang_id, $field_value + 1) : null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_value_raw($field_value, $field_data)
+ {
+ if ($field_value == $field_data['field_novalue'] && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ if (!$field_value && $field_data['field_show_novalue'])
+ {
+ $field_value = $field_data['field_novalue'];
}
+
+ return $field_value;
}
/**
@@ -200,7 +221,14 @@ class type_bool extends type_base
{
if (!$this->lang_helper->is_set($profile_row['field_id'], $profile_row['lang_id'], 1))
{
- $this->lang_helper->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], $this->get_service_name(), $preview_options);
+ if ($preview_options)
+ {
+ $this->lang_helper->load_preview_options($profile_row['field_id'], $profile_row['lang_id'], $preview_options);
+ }
+ else
+ {
+ $this->lang_helper->load_option_lang($profile_row['lang_id']);
+ }
}
$options = $this->lang_helper->get($profile_row['field_id'], $profile_row['lang_id']);
@@ -324,7 +352,7 @@ class type_bool extends type_base
}
}
- if ($step == 3 && ($field_data[$key] || $action != 'edit') && $key == 'l_lang_options')
+ if ($key == 'l_lang_options' && $this->request->is_set($key))
{
$field_data[$key] = $this->request->variable($key, array(0 => array('')), true);
@@ -339,29 +367,29 @@ class type_bool extends type_base
*/
public function prepare_hidden_fields($step, $key, $action, &$field_data)
{
- if ($key == 'l_lang_options' && $this->request->is_set('l_lang_options'))
+ if ($key == 'field_default_value')
{
- return $this->request->variable($key, array(array('')), true);
- }
- else if ($key == 'field_default_value')
- {
- return $this->request->variable($key, $field_data[$key]);
- }
- else
- {
- if (!$this->request->is_set($key))
- {
- return false;
- }
- else if ($key == 'field_ident' && isset($field_data[$key]))
- {
- return $field_data[$key];
- }
- else
+ $field_length = $this->request->variable('field_length', 0);
+
+ // Do a simple is set check if using checkbox.
+ if ($field_length == 2)
{
- return ($key == 'lang_options') ? $this->request->variable($key, array(''), true) : $this->request->variable($key, '', true);
+ return $this->request->is_set($key);
}
+ return $this->request->variable($key, $field_data[$key], true);
+ }
+
+ $default_lang_options = array(
+ 'l_lang_options' => array(0 => array('')),
+ 'lang_options' => array(0 => ''),
+ );
+
+ if (isset($default_lang_options[$key]) && $this->request->is_set($key))
+ {
+ return $this->request->variable($key, $default_lang_options[$key], true);
}
+
+ return parent::prepare_hidden_fields($step, $key, $action, $field_data);
}
/**
diff --git a/phpBB/phpbb/profilefields/type/type_date.php b/phpBB/phpbb/profilefields/type/type_date.php
index fc012dd97a..414484920b 100644
--- a/phpBB/phpbb/profilefields/type/type_date.php
+++ b/phpBB/phpbb/profilefields/type/type_date.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -35,7 +39,6 @@ class type_date extends type_base
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -66,9 +69,10 @@ class type_date extends type_base
'field_ident' => 'field_default_value',
'field_type' => $this->get_service_name(),
'field_length' => $field_data['field_length'],
+ 'lang_options' => $field_data['lang_options'],
);
- $always_now = request_var('always_now', -1);
+ $always_now = $request->variable('always_now', -1);
if ($always_now == -1)
{
$s_checked = ($field_data['field_default_value'] == 'now') ? true : false;
@@ -204,6 +208,19 @@ class type_date extends type_base
/**
* {@inheritDoc}
*/
+ public function get_profile_value_raw($field_value, $field_data)
+ {
+ if (($field_value === '' || $field_value === null) && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ return $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function generate_field($profile_row, $preview_options = false)
{
$profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
diff --git a/phpBB/phpbb/profilefields/type/type_dropdown.php b/phpBB/phpbb/profilefields/type/type_dropdown.php
index bcf0ba05f9..17ae89e1b2 100644
--- a/phpBB/phpbb/profilefields/type/type_dropdown.php
+++ b/phpBB/phpbb/profilefields/type/type_dropdown.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -42,7 +46,6 @@ class type_dropdown extends type_base
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\profilefields\lang_helper $lang_helper, \phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -132,7 +135,7 @@ class type_dropdown extends type_base
// retrieve option lang data if necessary
if (!$this->lang_helper->is_set($field_data['field_id'], $field_data['lang_id'], 1))
{
- $this->lang_helper->get_option_lang($field_data['field_id'], $field_data['lang_id'], $this->get_service_name(), false);
+ $this->lang_helper->load_option_lang($field_data['lang_id']);
}
if (!$this->lang_helper->is_set($field_data['field_id'], $field_data['lang_id'], $field_value))
@@ -157,7 +160,7 @@ class type_dropdown extends type_base
$lang_id = $field_data['lang_id'];
if (!$this->lang_helper->is_set($field_id, $lang_id))
{
- $this->lang_helper->get_option_lang($field_id, $lang_id, $this->get_service_name(), false);
+ $this->lang_helper->load_option_lang($lang_id);
}
if ($field_value == $field_data['field_novalue'] && !$field_data['field_show_novalue'])
@@ -186,6 +189,24 @@ class type_dropdown extends type_base
/**
* {@inheritDoc}
*/
+ public function get_profile_value_raw($field_value, $field_data)
+ {
+ if ($field_value == $field_data['field_novalue'] && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ if (!$field_value && $field_data['field_show_novalue'])
+ {
+ $field_value = $field_data['field_novalue'];
+ }
+
+ return $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function generate_field($profile_row, $preview_options = false)
{
$profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
@@ -196,7 +217,14 @@ class type_dropdown extends type_base
if (!$this->lang_helper->is_set($profile_row['field_id'], $profile_row['lang_id'], 1))
{
- $this->lang_helper->get_option_lang($profile_row['field_id'], $profile_row['lang_id'], $this->get_service_name(), $preview_options);
+ if ($preview_options)
+ {
+ $this->lang_helper->load_preview_options($profile_row['field_id'], $profile_row['lang_id'], $preview_options);
+ }
+ else
+ {
+ $this->lang_helper->load_option_lang($profile_row['lang_id']);
+ }
}
$profile_row['field_value'] = (int) $value;
diff --git a/phpBB/phpbb/profilefields/type/type_googleplus.php b/phpBB/phpbb/profilefields/type/type_googleplus.php
new file mode 100644
index 0000000000..e6729b1935
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_googleplus.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\profilefields\type;
+
+class type_googleplus extends type_string
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name()
+ {
+ return $this->user->lang('FIELD_GOOGLEPLUS');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_service_name()
+ {
+ return 'profilefields.type.googleplus';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 20,
+ 'field_minlen' => 3,
+ 'field_maxlen' => 255,
+ 'field_validation' => '(?:(?!\.{2,})([^<>=+]))+',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_contact_value($field_value, $field_data)
+ {
+ if (!$field_value && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ if (!is_numeric($field_value))
+ {
+ $field_value = '+' . $field_value;
+ }
+
+ return $field_value;
+ }
+}
diff --git a/phpBB/phpbb/profilefields/type/type_int.php b/phpBB/phpbb/profilefields/type/type_int.php
index 267f522d5d..dd08df94c1 100644
--- a/phpBB/phpbb/profilefields/type/type_int.php
+++ b/phpBB/phpbb/profilefields/type/type_int.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -35,7 +39,6 @@ class type_int extends type_base
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -61,7 +64,7 @@ class type_int extends type_base
0 => array('TITLE' => $this->user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_length" size="5" value="' . $field_data['field_length'] . '" />'),
1 => array('TITLE' => $this->user->lang['MIN_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_minlen" size="5" value="' . $field_data['field_minlen'] . '" />'),
2 => array('TITLE' => $this->user->lang['MAX_FIELD_NUMBER'], 'FIELD' => '<input type="number" min="0" max="99999" name="field_maxlen" size="5" value="' . $field_data['field_maxlen'] . '" />'),
- 3 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => '<input type="post" name="field_default_value" value="' . $field_data['field_default_value'] . '" />'),
+ 3 => array('TITLE' => $this->user->lang['DEFAULT_VALUE'], 'FIELD' => '<input type="number" name="field_default_value" value="' . $field_data['field_default_value'] . '" />'),
);
return $options;
@@ -151,6 +154,18 @@ class type_int extends type_base
/**
* {@inheritDoc}
*/
+ public function get_profile_value_raw($field_value, $field_data)
+ {
+ if (($field_value === '' || $field_value === null) && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+ return (int) $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function generate_field($profile_row, $preview_options = false)
{
$profile_row['field_ident'] = (isset($profile_row['var_name'])) ? $profile_row['var_name'] : 'pf_' . $profile_row['field_ident'];
diff --git a/phpBB/phpbb/profilefields/type/type_interface.php b/phpBB/phpbb/profilefields/type/type_interface.php
index 94f6882524..ec770f9467 100644
--- a/phpBB/phpbb/profilefields/type/type_interface.php
+++ b/phpBB/phpbb/profilefields/type/type_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -90,6 +94,26 @@ interface type_interface
public function get_profile_value($field_value, $field_data);
/**
+ * Get Profile Value ID for display (the raw, unprocessed user data)
+ *
+ * @param mixed $field_value Field value as stored in the database
+ * @param array $field_data Array with requirements of the field
+ * @return mixed Field value ID to display
+ */
+ public function get_profile_value_raw($field_value, $field_data);
+
+ /**
+ * Get Profile Value for display
+ *
+ * When displaying a contact field, we don't want to have links already parsed and more
+ *
+ * @param mixed $field_value Field value as stored in the database
+ * @param array $field_data Array with requirements of the field
+ * @return mixed Field value to display
+ */
+ public function get_profile_contact_value($field_value, $field_data);
+
+ /**
* Generate the input field for display
*
* @param array $profile_row Array with data for this field
@@ -165,8 +189,8 @@ interface type_interface
/**
* Allows manipulating the intended variables if needed
*
- * @param string $key Name of the option
* @param int $step Step on which the option is hidden
+ * @param string $key Name of the option
* @param string $action Currently performed action (create|edit)
* @param array $field_data Array with data for this field
* @return mixed Final value of the option
diff --git a/phpBB/phpbb/profilefields/type/type_string.php b/phpBB/phpbb/profilefields/type/type_string.php
index 9d241c49ef..67befc457d 100644
--- a/phpBB/phpbb/profilefields/type/type_string.php
+++ b/phpBB/phpbb/profilefields/type/type_string.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -35,7 +39,6 @@ class type_string extends type_string_common
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
@@ -109,7 +112,7 @@ class type_string extends type_string_common
$default_value = $profile_row['lang_default_value'];
$profile_row['field_value'] = ($this->request->is_set($field_ident)) ? $this->request->variable($field_ident, $default_value, true) : ((!isset($this->user->profile_fields[$field_ident]) || $preview_options !== false) ? $default_value : $this->user->profile_fields[$field_ident]);
- $this->template->assign_block_vars('string', array_change_key_case($profile_row, CASE_UPPER));
+ $this->template->assign_block_vars($this->get_name_short(), array_change_key_case($profile_row, CASE_UPPER));
}
/**
diff --git a/phpBB/phpbb/profilefields/type/type_string_common.php b/phpBB/phpbb/profilefields/type/type_string_common.php
index f00a7e6a08..f5e1992044 100644
--- a/phpBB/phpbb/profilefields/type/type_string_common.php
+++ b/phpBB/phpbb/profilefields/type/type_string_common.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,15 +15,28 @@ namespace phpbb\profilefields\type;
abstract class type_string_common extends type_base
{
+ protected $validation_options = array(
+ 'CHARS_ANY' => '.*',
+ 'NUMBERS_ONLY' => '[0-9]+',
+ 'ALPHA_ONLY' => '[a-zA-Z0-9]+',
+ 'ALPHA_UNDERSCORE' => '[\w]+',
+ 'ALPHA_DOTS' => '[a-zA-Z0-9.]+',
+ 'ALPHA_SPACERS' => '[\w\x20+\-\[\]]+',
+ 'ALPHA_PUNCTUATION' => '[a-zA-Z][\w\.,\-]+',
+ 'LETTER_NUM_ONLY' => '[\p{Lu}\p{Ll}0-9]+',
+ 'LETTER_NUM_UNDERSCORE' => '[\p{Lu}\p{Ll}0-9_]+',
+ 'LETTER_NUM_DOTS' => '[\p{Lu}\p{Ll}0-9.]+',
+ 'LETTER_NUM_SPACERS' => '[\p{Lu}\p{Ll}0-9\x20_+\-\[\]]+',
+ 'LETTER_NUM_PUNCTUATION' => '[\p{Lu}\p{Ll}][\p{Lu}\p{Ll}0-9.,\-_]+',
+ );
+
/**
* Return possible validation options
*/
- function validate_options($field_data)
+ public function validate_options($field_data)
{
- $validate_ary = array('CHARS_ANY' => '.*', 'NUMBERS_ONLY' => '[0-9]+', 'ALPHA_ONLY' => '[\w]+', 'ALPHA_SPACERS' => '[\w_\+\. \-\[\]]+');
-
$validate_options = '';
- foreach ($validate_ary as $lang => $value)
+ foreach ($this->validation_options as $lang => $value)
{
$selected = ($field_data['field_validation'] == $value) ? ' selected="selected"' : '';
$validate_options .= '<option value="' . $value . '"' . $selected . '>' . $this->user->lang[$lang] . '</option>';
@@ -59,7 +76,7 @@ abstract class type_string_common extends type_base
{
return $this->user->lang('FIELD_TOO_SHORT', (int) $field_data['field_minlen'], $this->get_field_name($field_data['lang_name']));
}
- else if ($field_data['field_maxlen'] && utf8_strlen($field_value) > $field_data['field_maxlen'])
+ else if ($field_data['field_maxlen'] && utf8_strlen(html_entity_decode($field_value)) > $field_data['field_maxlen'])
{
return $this->user->lang('FIELD_TOO_LONG', (int) $field_data['field_maxlen'], $this->get_field_name($field_data['lang_name']));
}
@@ -67,19 +84,14 @@ abstract class type_string_common extends type_base
if (!empty($field_data['field_validation']) && $field_data['field_validation'] != '.*')
{
$field_validate = ($field_type != 'text') ? $field_value : bbcode_nl2br($field_value);
- if (!preg_match('#^' . str_replace('\\\\', '\\', $field_data['field_validation']) . '$#i', $field_validate))
+ if (!preg_match('#^' . str_replace('\\\\', '\\', $field_data['field_validation']) . '$#iu', $field_validate))
{
- switch ($row['field_validation'])
+ $validation = array_search($field_data['field_validation'], $this->validation_options);
+ if ($validation)
{
- case '[0-9]+':
- return $this->user->lang('FIELD_INVALID_CHARS_NUMBERS_ONLY', $this->get_field_name($field_data['lang_name']));
-
- case '[\w]+':
- return $this->user->lang('FIELD_INVALID_CHARS_ALPHA_ONLY', $this->get_field_name($field_data['lang_name']));
-
- case '[\w_\+\. \-\[\]]+':
- return $this->user->lang('FIELD_INVALID_CHARS_SPACERS_ONLY', $this->get_field_name($field_data['lang_name']));
+ return $this->user->lang('FIELD_INVALID_CHARS_' . $validation, $this->get_field_name($field_data['lang_name']));
}
+ return $this->user->lang('FIELD_INVALID_CHARS_INVALID', $this->get_field_name($field_data['lang_name']));
}
}
@@ -91,7 +103,7 @@ abstract class type_string_common extends type_base
*/
public function get_profile_value($field_value, $field_data)
{
- if (!$field_value && !$field_data['field_show_novalue'])
+ if (($field_value === null || $field_value === '') && !$field_data['field_show_novalue'])
{
return null;
}
@@ -105,6 +117,27 @@ abstract class type_string_common extends type_base
/**
* {@inheritDoc}
*/
+ public function get_profile_value_raw($field_value, $field_data)
+ {
+ if (($field_value === null || $field_value === '') && !$field_data['field_show_novalue'])
+ {
+ return null;
+ }
+
+ return $field_value;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_profile_contact_value($field_value, $field_data)
+ {
+ return $this->get_profile_value_raw($field_value, $field_data);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function prepare_options_form(&$exclude_options, &$visibility_options)
{
$exclude_options[1][] = 'lang_default_value';
diff --git a/phpBB/phpbb/profilefields/type/type_text.php b/phpBB/phpbb/profilefields/type/type_text.php
index 660bb20ef8..bacf60a213 100644
--- a/phpBB/phpbb/profilefields/type/type_text.php
+++ b/phpBB/phpbb/profilefields/type/type_text.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB
-* @copyright (c) 2014 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -35,7 +39,6 @@ class type_text extends type_string_common
* @param \phpbb\request\request $request Request object
* @param \phpbb\template\template $template Template object
* @param \phpbb\user $user User object
- * @param string $language_table Table where the language strings are stored
*/
public function __construct(\phpbb\request\request $request, \phpbb\template\template $template, \phpbb\user $user)
{
diff --git a/phpBB/phpbb/profilefields/type/type_url.php b/phpBB/phpbb/profilefields/type/type_url.php
new file mode 100644
index 0000000000..fe0bffd582
--- /dev/null
+++ b/phpBB/phpbb/profilefields/type/type_url.php
@@ -0,0 +1,74 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\profilefields\type;
+
+class type_url extends type_string
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function get_name_short()
+ {
+ return 'url';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_options($default_lang_id, $field_data)
+ {
+ $options = array(
+ 0 => array('TITLE' => $this->user->lang['FIELD_LENGTH'], 'FIELD' => '<input type="number" min="0" name="field_length" size="5" value="' . $field_data['field_length'] . '" />'),
+ 1 => array('TITLE' => $this->user->lang['MIN_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" name="field_minlen" size="5" value="' . $field_data['field_minlen'] . '" />'),
+ 2 => array('TITLE' => $this->user->lang['MAX_FIELD_CHARS'], 'FIELD' => '<input type="number" min="0" name="field_maxlen" size="5" value="' . $field_data['field_maxlen'] . '" />'),
+ );
+
+ return $options;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get_default_option_values()
+ {
+ return array(
+ 'field_length' => 40,
+ 'field_minlen' => 0,
+ 'field_maxlen' => 200,
+ 'field_validation' => '',
+ 'field_novalue' => '',
+ 'field_default_value' => '',
+ );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function validate_profile_field(&$field_value, $field_data)
+ {
+ $field_value = trim($field_value);
+
+ if ($field_value === '' && !$field_data['field_required'])
+ {
+ return false;
+ }
+
+ if (!preg_match('#^' . get_preg_expression('url') . '$#iu', $field_value))
+ {
+ return $this->user->lang('FIELD_INVALID_URL', $this->get_field_name($field_data['lang_name']));
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/recursive_dot_prefix_filter_iterator.php b/phpBB/phpbb/recursive_dot_prefix_filter_iterator.php
new file mode 100644
index 0000000000..2500ba0cf8
--- /dev/null
+++ b/phpBB/phpbb/recursive_dot_prefix_filter_iterator.php
@@ -0,0 +1,30 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb;
+
+/**
+* Class recursive_dot_prefix_filter_iterator
+*
+* This filter ignores directories starting with a dot.
+* When searching for php classes and template files of extensions
+* we don't need to look inside these directories.
+*/
+class recursive_dot_prefix_filter_iterator extends \RecursiveFilterIterator
+{
+ public function accept()
+ {
+ $filename = $this->current()->getFilename();
+ return !$this->current()->isDir() || $filename[0] !== '.';
+ }
+}
diff --git a/phpBB/phpbb/report/controller/report.php b/phpBB/phpbb/report/controller/report.php
new file mode 100644
index 0000000000..f703d1cc60
--- /dev/null
+++ b/phpBB/phpbb/report/controller/report.php
@@ -0,0 +1,319 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\controller;
+
+use phpbb\exception\http_exception;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+class report
+{
+ /**
+ * @var \phpbb\config\db
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * @var \phpbb\controller\helper
+ */
+ protected $helper;
+
+ /**
+ * @var \phpbb\request\request_interface
+ */
+ protected $request;
+
+ /**
+ * @var \phpbb\captcha\factory
+ */
+ protected $captcha_factory;
+
+ /**
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * @var \phpbb\report\report_handler_interface
+ */
+ protected $report_handler;
+
+ /**
+ * @var \phpbb\report\report_reason_list_provider
+ */
+ protected $report_reason_provider;
+
+ public function __construct(\phpbb\config\db $config, \phpbb\user $user, \phpbb\template\template $template, \phpbb\controller\helper $helper, \phpbb\request\request_interface $request, \phpbb\captcha\factory $captcha_factory, \phpbb\report\handler_factory $report_factory, \phpbb\report\report_reason_list_provider $ui_provider, $phpbb_root_path, $php_ext)
+ {
+ $this->config = $config;
+ $this->user = $user;
+ $this->template = $template;
+ $this->helper = $helper;
+ $this->request = $request;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->captcha_factory = $captcha_factory;
+ $this->report_handler = $report_factory;
+
+ // User interface factory
+ $this->report_reason_provider = $ui_provider;
+ }
+
+ /**
+ * Controller for /path_to_entities/{id}/report routes
+ *
+ * Because of how phpBB organizes routes $mode must be set in the route config.
+ *
+ * @param int $id ID of the entity to report
+ * @param string $mode
+ * @return \Symfony\Component\HttpFoundation\Response a Symfony response object
+ * @throws \phpbb\exception\http_exception when $mode or $id is invalid for some reason
+ */
+ public function handle($id, $mode)
+ {
+ // Get report handler
+ $this->report_handler = $this->report_handler->get_instance($mode);
+
+ $this->user->add_lang('mcp');
+
+ $user_notify = ($this->user->data['is_registered']) ? $this->request->variable('notify', 0) : false;
+ $reason_id = $this->request->variable('reason_id', 0);
+ $report_text = $this->request->variable('report_text', '', true);
+
+ $submit = $this->request->variable('submit', '');
+ $cancel = $this->request->variable('cancel', '');
+
+ $error = array();
+ $s_hidden_fields = '';
+
+ $redirect_url = append_sid(
+ $this->phpbb_root_path . ( ($mode === 'pm') ? 'ucp' : 'viewtopic' ) . ".{$this->php_ext}",
+ ($mode == 'pm') ? "i=pm&mode=view&p=$id" : "p=$id"
+ );
+ $redirect_url .= ($mode === 'post') ? "#p$id" : '';
+
+ // Set up CAPTCHA if necessary
+ if ($this->config['enable_post_confirm'] && !$this->user->data['is_registered'])
+ {
+ $captcha = $this->captcha_factory->get_instance($this->config['captcha_plugin']);
+ $captcha->init(CONFIRM_REPORT);
+ }
+
+ //Has the report been cancelled?
+ if (!empty($cancel))
+ {
+ return new RedirectResponse($redirect_url, 302);
+ }
+
+ // Check CAPTCHA, if the form was submited
+ if (!empty($submit) && isset($captcha))
+ {
+ $captcha_template_array = $this->check_captcha($captcha);
+ $error = $captcha_template_array['error'];
+ $s_hidden_fields = $captcha_template_array['hidden_fields'];
+ }
+
+ // Handle request
+ try
+ {
+ if (!empty($submit) && sizeof($error) === 0)
+ {
+ $this->report_handler->add_report(
+ (int) $id,
+ (int) $reason_id,
+ (string) $report_text,
+ (int) $user_notify
+ );
+
+ // Send success message
+ switch ($mode)
+ {
+ case 'pm':
+ $lang_return = $this->user->lang['RETURN_PM'];
+ $lang_success = $this->user->lang['PM_REPORTED_SUCCESS'];
+ break;
+ case 'post':
+ $lang_return = $this->user->lang['RETURN_TOPIC'];
+ $lang_success = $this->user->lang['POST_REPORTED_SUCCESS'];
+ break;
+ }
+
+ $this->helper->assign_meta_refresh_var(3, $redirect_url);
+ $message = $lang_success . '<br /><br />' . sprintf($lang_return, '<a href="' . $redirect_url . '">', '</a>');
+ return $this->helper->message($message);
+ }
+ else
+ {
+ $this->report_handler->validate_report_request($id);
+ }
+ }
+ catch (\phpbb\report\exception\pm_reporting_disabled_exception $exception)
+ {
+ throw new http_exception(404, 'PAGE_NOT_FOUND');
+ }
+ catch (\phpbb\report\exception\already_reported_exception $exception)
+ {
+ switch ($mode)
+ {
+ case 'pm':
+ $message = $this->user->lang['ALREADY_REPORTED_PM'];
+ $message .= '<br /><br />' . sprintf($this->user->lang['RETURN_PM'], '<a href="' . $redirect_url . '">', '</a>');
+ break;
+ case 'post':
+ $message = $this->user->lang['ALREADY_REPORTED'];
+ $message .= '<br /><br />' . sprintf($this->user->lang['RETURN_TOPIC'], '<a href="' . $redirect_url . '">', '</a>');
+ break;
+ }
+
+ return $this->helper->message($message);
+ }
+ catch (\phpbb\report\exception\report_permission_denied_exception $exception)
+ {
+ $message = $exception->getMessage();
+ if (isset($this->user->lang[$message]))
+ {
+ $message = $this->user->lang[$message];
+ }
+
+ throw new http_exception(403, $message);
+ }
+ catch (\phpbb\report\exception\entity_not_found_exception $exception)
+ {
+ $message = $exception->getMessage();
+ if (isset($this->user->lang[$message]))
+ {
+ $message = $this->user->lang[$message];
+ }
+
+ throw new http_exception(404, $message);
+ }
+ catch (\phpbb\report\exception\empty_report_exception $exception)
+ {
+ $error[] = $this->user->lang['EMPTY_REPORT'];
+ }
+ catch (\phpbb\report\exception\invalid_report_exception $exception)
+ {
+ return $this->helper->message($exception->getMessage());
+ }
+
+ // Setting up an rendering template
+ $page_title = ($mode === 'pm') ? $this->user->lang['REPORT_MESSAGE'] : $this->user->lang['REPORT_POST'];
+ $this->assign_template_data(
+ $mode,
+ $id,
+ $reason_id,
+ $report_text,
+ $user_notify,
+ $error,
+ $s_hidden_fields,
+ ( isset($captcha) ? $captcha : false )
+ );
+
+ return $this->helper->render('report_body.html', $page_title);
+ }
+
+ /**
+ * Assigns template variables
+ *
+ * @param int $mode
+ * @param int $id
+ * @param int $reason_id
+ * @param string $report_text
+ * @param mixed $user_notify
+ * @param array $error
+ * @param string $s_hidden_fields
+ * @param mixed $captcha
+ * @return null
+ */
+ protected function assign_template_data($mode, $id, $reason_id, $report_text, $user_notify, $error = array(), $s_hidden_fields = '', $captcha = false)
+ {
+ if ($captcha !== false && $captcha->is_solved() === false)
+ {
+ $this->template->assign_vars(array(
+ 'S_CONFIRM_CODE' => true,
+ 'CAPTCHA_TEMPLATE' => $captcha->get_template(),
+ ));
+ }
+
+ $this->report_reason_provider->display_reasons($reason_id);
+
+ switch ($mode)
+ {
+ case 'pm':
+ $report_route = $this->helper->route('phpbb_report_pm_controller', array('id' => $id));
+ break;
+ case 'post':
+ $report_route = $this->helper->route('phpbb_report_post_controller', array('id' => $id));
+ break;
+ }
+
+ $this->template->assign_vars(array(
+ 'ERROR' => (sizeof($error) > 0) ? implode('<br />', $error) : '',
+ 'S_REPORT_POST' => ($mode === 'pm') ? false : true,
+ 'REPORT_TEXT' => $report_text,
+ 'S_HIDDEN_FIELDS' => (!empty($s_hidden_fields)) ? $s_hidden_fields : null,
+ 'S_REPORT_ACTION' => $report_route,
+
+ 'S_NOTIFY' => $user_notify,
+ 'S_CAN_NOTIFY' => ($this->user->data['is_registered']) ? true : false,
+ 'S_IN_REPORT' => true,
+ ));
+ }
+
+ /**
+ * Check CAPTCHA
+ *
+ * @param object $captcha A phpBB CAPTCHA object
+ * @return array template variables which ensures that CAPTCHA's work correctly
+ */
+ protected function check_captcha($captcha)
+ {
+ $error = array();
+ $captcha_hidden_fields = '';
+
+ $visual_confirmation_response = $captcha->validate();
+ if ($visual_confirmation_response)
+ {
+ $error[] = $visual_confirmation_response;
+ }
+
+ if (sizeof($error) === 0)
+ {
+ $captcha->reset();
+ }
+ else if ($captcha->is_solved() !== false)
+ {
+ $captcha_hidden_fields = build_hidden_fields($captcha->get_hidden_fields());
+ }
+
+ return array(
+ 'error' => $error,
+ 'hidden_fields' => $captcha_hidden_fields,
+ );
+ }
+}
diff --git a/phpBB/phpbb/report/exception/already_reported_exception.php b/phpBB/phpbb/report/exception/already_reported_exception.php
new file mode 100644
index 0000000000..54174044fe
--- /dev/null
+++ b/phpBB/phpbb/report/exception/already_reported_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+class already_reported_exception extends invalid_report_exception
+{
+
+}
diff --git a/phpBB/phpbb/report/exception/empty_report_exception.php b/phpBB/phpbb/report/exception/empty_report_exception.php
new file mode 100644
index 0000000000..8c968dca80
--- /dev/null
+++ b/phpBB/phpbb/report/exception/empty_report_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+class empty_report_exception extends invalid_report_exception
+{
+ public function __construct()
+ {
+ parent::__construct('EMPTY_REPORT');
+ }
+}
diff --git a/phpBB/phpbb/report/exception/entity_not_found_exception.php b/phpBB/phpbb/report/exception/entity_not_found_exception.php
new file mode 100644
index 0000000000..732aa58a13
--- /dev/null
+++ b/phpBB/phpbb/report/exception/entity_not_found_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+class entity_not_found_exception extends invalid_report_exception
+{
+
+}
diff --git a/phpBB/phpbb/report/exception/factory_invalid_argument_exception.php b/phpBB/phpbb/report/exception/factory_invalid_argument_exception.php
new file mode 100644
index 0000000000..19de91eea3
--- /dev/null
+++ b/phpBB/phpbb/report/exception/factory_invalid_argument_exception.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+use \phpbb\exception\runtime_exception;
+
+class factory_invalid_argument_exception extends runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/report/exception/invalid_report_exception.php b/phpBB/phpbb/report/exception/invalid_report_exception.php
new file mode 100644
index 0000000000..03ff0a872d
--- /dev/null
+++ b/phpBB/phpbb/report/exception/invalid_report_exception.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+use \phpbb\exception\runtime_exception;
+
+class invalid_report_exception extends runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/report/exception/pm_reporting_disabled_exception.php b/phpBB/phpbb/report/exception/pm_reporting_disabled_exception.php
new file mode 100644
index 0000000000..2c8ab8cf84
--- /dev/null
+++ b/phpBB/phpbb/report/exception/pm_reporting_disabled_exception.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+class pm_reporting_disabled_exception extends invalid_report_exception
+{
+ public function __construct()
+ {
+
+ }
+}
diff --git a/phpBB/phpbb/report/exception/report_permission_denied_exception.php b/phpBB/phpbb/report/exception/report_permission_denied_exception.php
new file mode 100644
index 0000000000..c7069288b8
--- /dev/null
+++ b/phpBB/phpbb/report/exception/report_permission_denied_exception.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report\exception;
+
+class report_permission_denied_exception extends invalid_report_exception
+{
+
+}
diff --git a/phpBB/phpbb/report/handler_factory.php b/phpBB/phpbb/report/handler_factory.php
new file mode 100644
index 0000000000..ec229aac54
--- /dev/null
+++ b/phpBB/phpbb/report/handler_factory.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\report;
+
+use phpbb\report\exception\factory_invalid_argument_exception;
+
+class handler_factory
+{
+ /**
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * Constructor
+ *
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+ */
+ public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
+ /**
+ * Return a new instance of an appropriate report handler
+ *
+ * @param string $type
+ * @return \phpbb\report\report_handler_interface
+ * @throws \phpbb\report\exception\factory_invalid_argument_exception if $type is not valid
+ */
+ public function get_instance($type)
+ {
+ switch ($type)
+ {
+ case 'pm':
+ return $this->container->get('phpbb.report.handlers.report_handler_pm');
+ break;
+ case 'post':
+ return $this->container->get('phpbb.report.handlers.report_handler_post');
+ break;
+ }
+
+ throw new factory_invalid_argument_exception();
+ }
+}
diff --git a/phpBB/phpbb/report/report_handler.php b/phpBB/phpbb/report/report_handler.php
new file mode 100644
index 0000000000..126a206dbf
--- /dev/null
+++ b/phpBB/phpbb/report/report_handler.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report;
+
+abstract class report_handler implements report_handler_interface
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
+ * @var \phpbb\config\db
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\auth\auth
+ */
+ protected $auth;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * @var \phpbb\notification\manager
+ */
+ protected $notifications;
+
+ /**
+ * @var array
+ */
+ protected $report_data;
+
+ /**
+ * Construtor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\event\dispatcher_interface $dispatcher
+ * @param \phpbb\config\db $config
+ * @param \phpbb\auth\auth $auth
+ * @param \phpbb\user $user
+ * @param \phpbb\notification\manager $notification
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\config\db $config, \phpbb\auth\auth $auth, \phpbb\user $user, \phpbb\notification\manager $notification)
+ {
+ $this->db = $db;
+ $this->dispatcher = $dispatcher;
+ $this->config = $config;
+ $this->auth = $auth;
+ $this->user = $user;
+ $this->notifications = $notification;
+ $this->report_data = array();
+ }
+
+ /**
+ * Creates a report entity in the database
+ *
+ * @param array $report_data
+ * @return int the ID of the created entity
+ */
+ protected function create_report(array $report_data)
+ {
+ $sql_ary = array(
+ 'reason_id' => (int) $report_data['reason_id'],
+ 'post_id' => $report_data['post_id'],
+ 'pm_id' => $report_data['pm_id'],
+ 'user_id' => (int) $this->user->data['user_id'],
+ 'user_notify' => (int) $report_data['user_notify'],
+ 'report_closed' => 0,
+ 'report_time' => (int) time(),
+ 'report_text' => (string) $report_data['report_text'],
+ 'reported_post_text' => $report_data['reported_post_text'],
+ 'reported_post_uid' => $report_data['reported_post_uid'],
+ 'reported_post_bitfield' => $report_data['reported_post_bitfield'],
+ 'reported_post_enable_bbcode' => $report_data['reported_post_enable_bbcode'],
+ 'reported_post_enable_smilies' => $report_data['reported_post_enable_smilies'],
+ 'reported_post_enable_magic_url' => $report_data['reported_post_enable_magic_url'],
+ );
+
+ $sql = 'INSERT INTO ' . REPORTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+
+ return $this->db->sql_nextid();
+ }
+}
diff --git a/phpBB/phpbb/report/report_handler_interface.php b/phpBB/phpbb/report/report_handler_interface.php
new file mode 100644
index 0000000000..8dafc392d0
--- /dev/null
+++ b/phpBB/phpbb/report/report_handler_interface.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\report;
+
+interface report_handler_interface
+{
+ /**
+ * Reports a message
+ *
+ * @param int $id
+ * @param int $reason_id
+ * @param string $report_text
+ * @param int $user_notify
+ * @return null
+ * @throws \phpbb\report\exception\empty_report_exception when the given report is empty
+ * @throws \phpbb\report\exception\already_reported_exception when the entity is already reported
+ * @throws \phpbb\report\exception\entity_not_found_exception when the entity does not exist or the user does not have viewing permissions for it
+ * @throws \phpbb\report\exception\invalid_report_exception when the entity cannot be reported for some other reason
+ */
+ public function add_report($id, $reason_id, $report_text, $user_notify);
+
+ /**
+ * Checks if the message is reportable
+ *
+ * @param int $id
+ * @return null
+ * @throws \phpbb\report\exception\already_reported_exception when the entity is already reported
+ * @throws \phpbb\report\exception\entity_not_found_exception when the entity does not exist or the user does not have viewing permissions for it
+ * @throws \phpbb\report\exception\invalid_report_exception when the entity cannot be reported for some other reason
+ */
+ public function validate_report_request($id);
+}
diff --git a/phpBB/phpbb/report/report_handler_pm.php b/phpBB/phpbb/report/report_handler_pm.php
new file mode 100644
index 0000000000..2f2a697efc
--- /dev/null
+++ b/phpBB/phpbb/report/report_handler_pm.php
@@ -0,0 +1,137 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report;
+
+use phpbb\report\exception\empty_report_exception;
+use phpbb\report\exception\already_reported_exception;
+use phpbb\report\exception\pm_reporting_disabled_exception;
+use phpbb\report\exception\entity_not_found_exception;
+
+class report_handler_pm extends report_handler
+{
+ /**
+ * {@inheritdoc}
+ * @throws \phpbb\report\exception\pm_reporting_disabled_exception when PM reporting is disabled on the board
+ */
+ public function add_report($id, $reason_id, $report_text, $user_notify)
+ {
+ // Cast the input variables
+ $id = (int) $id;
+ $reason_id = (int) $reason_id;
+ $report_text = (string) $report_text;
+ $user_notify = (int) $user_notify;
+
+ $this->validate_report_request($id);
+
+ $sql = 'SELECT *
+ FROM ' . REPORTS_REASONS_TABLE . "
+ WHERE reason_id = $reason_id";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row || (empty($report_text) && strtolower($row['reason_title']) === 'other'))
+ {
+ throw new empty_report_exception();
+ }
+
+ $report_data = array(
+ 'reason_id' => $reason_id,
+ 'post_id' => 0,
+ 'pm_id' => $id,
+ 'user_notify' => $user_notify,
+ 'report_text' => $report_text,
+ 'reported_post_text' => $this->report_data['message_text'],
+ 'reported_post_uid' => $this->report_data['bbcode_bitfield'],
+ 'reported_post_bitfield' => $this->report_data['bbcode_uid'],
+ 'reported_post_enable_bbcode' => $this->report_data['enable_bbcode'],
+ 'reported_post_enable_smilies' => $this->report_data['enable_smilies'],
+ 'reported_post_enable_magic_url' => $this->report_data['enable_magic_url'],
+ );
+
+ $report_id = $this->create_report($report_data);
+
+ $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
+ SET message_reported = 1
+ WHERE msg_id = ' . $id;
+ $this->db->sql_query($sql);
+
+ $sql_ary = array(
+ 'msg_id' => $id,
+ 'user_id' => ANONYMOUS,
+ 'author_id' => (int) $this->report_data['author_id'],
+ 'pm_deleted' => 0,
+ 'pm_new' => 0,
+ 'pm_unread' => 0,
+ 'pm_replied' => 0,
+ 'pm_marked' => 0,
+ 'pm_forwarded' => 0,
+ 'folder_id' => PRIVMSGS_INBOX,
+ );
+
+ $sql = 'INSERT INTO ' . PRIVMSGS_TO_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->db->sql_query($sql);
+
+ $this->notifications->add_notifications('notification.type.report_pm', array_merge($this->report_data, $row, array(
+ 'report_text' => $report_text,
+ 'from_user_id' => $this->report_data['author_id'],
+ 'report_id' => $report_id,
+ )));
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws \phpbb\report\exception\pm_reporting_disabled_exception when PM reporting is disabled on the board
+ */
+ public function validate_report_request($id)
+ {
+ $id = (int) $id;
+
+ // Check if reporting PMs is enabled
+ if (!$this->config['allow_pm_report'])
+ {
+ throw new pm_reporting_disabled_exception();
+ }
+ else if ($id <= 0)
+ {
+ throw new entity_not_found_exception('NO_POST_SELECTED');
+ }
+
+ // Grab all relevant data
+ $sql = 'SELECT p.*, pt.*
+ FROM ' . PRIVMSGS_TABLE . ' p, ' . PRIVMSGS_TO_TABLE . " pt
+ WHERE p.msg_id = $id
+ AND p.msg_id = pt.msg_id
+ AND (p.author_id = " . $this->user->data['user_id'] . "
+ OR pt.user_id = " . $this->user->data['user_id'] . ")";
+ $result = $this->db->sql_query($sql);
+ $report_data = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ // Check if message exists
+ if (!$report_data)
+ {
+ $this->user->add_lang('ucp');
+ throw new entity_not_found_exception('NO_MESSAGE');
+ }
+
+ // Check if message is already reported
+ if ($report_data['message_reported'])
+ {
+ throw new already_reported_exception();
+ }
+
+ $this->report_data = $report_data;
+ }
+}
diff --git a/phpBB/phpbb/report/report_handler_post.php b/phpBB/phpbb/report/report_handler_post.php
new file mode 100644
index 0000000000..5574a16dc0
--- /dev/null
+++ b/phpBB/phpbb/report/report_handler_post.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\report;
+
+use phpbb\report\exception\invalid_report_exception;
+use phpbb\report\exception\empty_report_exception;
+use phpbb\report\exception\already_reported_exception;
+use phpbb\report\exception\entity_not_found_exception;
+use phpbb\report\exception\report_permission_denied_exception;
+
+class report_handler_post extends report_handler
+{
+ /**
+ * @var array
+ */
+ protected $forum_data;
+
+ /**
+ * {@inheritdoc}
+ * @throws \phpbb\report\exception\report_permission_denied_exception when the user does not have permission to report the post
+ */
+ public function add_report($id, $reason_id, $report_text, $user_notify)
+ {
+ // Cast the input variables
+ $id = (int) $id;
+ $reason_id = (int) $reason_id;
+ $report_text = (string) $report_text;
+ $user_notify = (int) $user_notify;
+
+ $this->validate_report_request($id);
+
+ $sql = 'SELECT *
+ FROM ' . REPORTS_REASONS_TABLE . "
+ WHERE reason_id = $reason_id";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row || (empty($report_text) && strtolower($row['reason_title']) === 'other'))
+ {
+ throw new empty_report_exception();
+ }
+
+ $report_data = array(
+ 'reason_id' => $reason_id,
+ 'post_id' => $id,
+ 'pm_id' => 0,
+ 'user_notify' => $user_notify,
+ 'report_text' => $report_text,
+ 'reported_post_text' => $this->report_data['post_text'],
+ 'reported_post_uid' => $this->report_data['bbcode_bitfield'],
+ 'reported_post_bitfield' => $this->report_data['bbcode_uid'],
+ 'reported_post_enable_bbcode' => $this->report_data['enable_bbcode'],
+ 'reported_post_enable_smilies' => $this->report_data['enable_smilies'],
+ 'reported_post_enable_magic_url' => $this->report_data['enable_magic_url'],
+ );
+
+ $this->create_report($report_data);
+
+ $sql = 'UPDATE ' . POSTS_TABLE . '
+ SET post_reported = 1
+ WHERE post_id = ' . $id;
+ $this->db->sql_query($sql);
+
+ if (!$this->report_data['topic_reported'])
+ {
+ $sql = 'UPDATE ' . TOPICS_TABLE . '
+ SET topic_reported = 1
+ WHERE topic_id = ' . $this->report_data['topic_id'] . '
+ OR topic_moved_id = ' . $this->report_data['topic_id'];
+ $this->db->sql_query($sql);
+ }
+
+ $this->notifications->add_notifications('notification.type.report_post', array_merge($this->report_data, $row, $this->forum_data, array(
+ 'report_text' => $report_text,
+ )));
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws \phpbb\report\exception\report_permission_denied_exception when the user does not have permission to report the post
+ */
+ public function validate_report_request($id)
+ {
+ $id = (int) $id;
+
+ // Check if id is valid
+ if ($id <= 0)
+ {
+ throw new entity_not_found_exception('NO_POST_SELECTED');
+ }
+
+ // Grab all relevant data
+ $sql = 'SELECT t.*, p.*
+ FROM ' . POSTS_TABLE . ' p, ' . TOPICS_TABLE . " t
+ WHERE p.post_id = $id
+ AND p.topic_id = t.topic_id";
+ $result = $this->db->sql_query($sql);
+ $report_data = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$report_data)
+ {
+ throw new entity_not_found_exception('POST_NOT_EXIST');
+ }
+
+ $forum_id = (int) $report_data['forum_id'];
+
+ $sql = 'SELECT *
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_id = ' . $forum_id;
+ $result = $this->db->sql_query($sql);
+ $forum_data = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$forum_data)
+ {
+ throw new invalid_report_exception('FORUM_NOT_EXIST');
+ }
+
+ $acl_check_ary = array(
+ 'f_list' => 'POST_NOT_EXIST',
+ 'f_read' => 'USER_CANNOT_READ',
+ 'f_report' => 'USER_CANNOT_REPORT'
+ );
+
+ /**
+ * This event allows you to do extra auth checks and verify if the user
+ * has the required permissions
+ *
+ * @event core.report_post_auth
+ * @var array forum_data All data available from the forums table on this post's forum
+ * @var array report_data All data available from the topics and the posts tables on this post (and its topic)
+ * @var array acl_check_ary An array with the ACL to be tested. The evaluation is made in the same order as the array is sorted
+ * The key is the ACL name and the value is the language key for the error message.
+ * @since 3.1.3-RC1
+ */
+ $vars = array(
+ 'forum_data',
+ 'report_data',
+ 'acl_check_ary',
+ );
+ extract($this->dispatcher->trigger_event('core.report_post_auth', compact($vars)));
+
+ $this->auth->acl($this->user->data);
+
+ foreach ($acl_check_ary as $acl => $error)
+ {
+ if (!$this->auth->acl_get($acl, $forum_id))
+ {
+ throw new report_permission_denied_exception($error);
+ }
+ }
+ unset($acl_check_ary);
+
+ if ($report_data['post_reported'])
+ {
+ throw new already_reported_exception();
+ }
+
+ $this->report_data = $report_data;
+ $this->forum_data = $forum_data;
+ }
+}
diff --git a/phpBB/phpbb/report/report_reason_list_provider.php b/phpBB/phpbb/report/report_reason_list_provider.php
new file mode 100644
index 0000000000..388a61d577
--- /dev/null
+++ b/phpBB/phpbb/report/report_reason_list_provider.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\report;
+
+class report_reason_list_provider
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var \phpbb\template\template
+ */
+ protected $template;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\template\template $template
+ * @param \phpbb\user $user
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\template\template $template, \phpbb\user $user)
+ {
+ $this->db = $db;
+ $this->template = $template;
+ $this->user = $user;
+ }
+
+ /**
+ * Sets template variables to render report reasons select HTML input
+ *
+ * @param int $reason_id
+ * @return null
+ */
+ public function display_reasons($reason_id = 0)
+ {
+ $sql = 'SELECT *
+ FROM ' . REPORTS_REASONS_TABLE . '
+ ORDER BY reason_order ASC';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // If the reason is defined within the language file, we will use the localized version, else just use the database entry...
+ if (isset($this->user->lang['report_reasons']['TITLE'][strtoupper($row['reason_title'])]) && isset($this->user->lang['report_reasons']['DESCRIPTION'][strtoupper($row['reason_title'])]))
+ {
+ $row['reason_description'] = $this->user->lang['report_reasons']['DESCRIPTION'][strtoupper($row['reason_title'])];
+ $row['reason_title'] = $this->user->lang['report_reasons']['TITLE'][strtoupper($row['reason_title'])];
+ }
+
+ $this->template->assign_block_vars('reason', array(
+ 'ID' => $row['reason_id'],
+ 'TITLE' => $row['reason_title'],
+ 'DESCRIPTION' => $row['reason_description'],
+ 'S_SELECTED' => ($row['reason_id'] == $reason_id) ? true : false,
+ ));
+ }
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/phpbb/request/deactivated_super_global.php b/phpBB/phpbb/request/deactivated_super_global.php
index b6940cf51f..ab56240b14 100644
--- a/phpBB/phpbb/request/deactivated_super_global.php
+++ b/phpBB/phpbb/request/deactivated_super_global.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\request\request
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,8 +16,6 @@ namespace phpbb\request;
/**
* Replacement for a superglobal (like $_GET or $_POST) which calls
* trigger_error on all operations but isset, overloads the [] operator with SPL.
-*
-* @package \phpbb\request\request
*/
class deactivated_super_global implements \ArrayAccess, \Countable, \IteratorAggregate
{
@@ -54,7 +56,7 @@ class deactivated_super_global implements \ArrayAccess, \Countable, \IteratorAgg
$file = '';
$line = 0;
- $message = 'Illegal use of $' . $this->name . '. You must use the request class or request_var() to access input data. Found in %s on line %d. This error message was generated by deactivated_super_global.';
+ $message = 'Illegal use of $' . $this->name . '. You must use the request class to access input data. Found in %s on line %d. This error message was generated by deactivated_super_global.';
$backtrace = debug_backtrace();
if (isset($backtrace[1]))
diff --git a/phpBB/phpbb/request/request.php b/phpBB/phpbb/request/request.php
index 3171a6edb7..56ce3999ed 100644
--- a/phpBB/phpbb/request/request.php
+++ b/phpBB/phpbb/request/request.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\request\request
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,8 +18,6 @@ namespace phpbb\request;
*
* It provides a method to disable access to input data through super globals.
* This should force MOD authors to read about data validation.
-*
-* @package \phpbb\request\request
*/
class request implements \phpbb\request\request_interface
{
@@ -273,7 +275,7 @@ class request implements \phpbb\request\request_interface
*/
public function file($form_name)
{
- return $this->variable($form_name, array('name' => 'none'), false, \phpbb\request\request_interface::FILES);
+ return $this->variable($form_name, array('name' => 'none'), true, \phpbb\request\request_interface::FILES);
}
/**
@@ -414,4 +416,27 @@ class request implements \phpbb\request\request_interface
{
return $this->input[$super_global];
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function escape($var, $multibyte)
+ {
+ if (is_array($var))
+ {
+ $result = array();
+ foreach ($var as $key => $value)
+ {
+ $this->type_cast_helper->set_var($key, $key, gettype($key), $multibyte);
+ $result[$key] = $this->escape($value, $multibyte);
+ }
+ $var = $result;
+ }
+ else
+ {
+ $this->type_cast_helper->set_var($var, $var, 'string', $multibyte);
+ }
+
+ return $var;
+ }
}
diff --git a/phpBB/phpbb/request/request_interface.php b/phpBB/phpbb/request/request_interface.php
index 1f9978b276..47b3b3a4ed 100644
--- a/phpBB/phpbb/request/request_interface.php
+++ b/phpBB/phpbb/request/request_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\request\request
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\request;
/**
* An interface through which all application input can be accessed.
-*
-* @package \phpbb\request\request
*/
interface request_interface
{
@@ -140,4 +142,14 @@ interface request_interface
* @return array The original array of the requested super global.
*/
public function get_super_global($super_global = \phpbb\request\request_interface::REQUEST);
+
+ /**
+ * Escape a string variable.
+ *
+ * @param mixed $value The contents to fill with
+ * @param bool $multibyte Indicates whether string values may contain UTF-8 characters.
+ * Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks.
+ * @return string|array
+ */
+ public function escape($value, $multibyte);
}
diff --git a/phpBB/phpbb/request/type_cast_helper.php b/phpBB/phpbb/request/type_cast_helper.php
index e9b55663af..96e66950ca 100644
--- a/phpBB/phpbb/request/type_cast_helper.php
+++ b/phpBB/phpbb/request/type_cast_helper.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\request\request
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\request;
/**
* A helper class that provides convenience methods for type casting.
-*
-* @package \phpbb\request\request
*/
class type_cast_helper implements \phpbb\request\type_cast_helper_interface
{
@@ -170,7 +172,6 @@ class type_cast_helper implements \phpbb\request\type_cast_helper_interface
}
list($default_key, $default_value) = each($default);
- $value_type = gettype($default_value);
$key_type = gettype($default_key);
$_var = $var;
diff --git a/phpBB/phpbb/request/type_cast_helper_interface.php b/phpBB/phpbb/request/type_cast_helper_interface.php
index f12795eef9..2cb28d021f 100644
--- a/phpBB/phpbb/request/type_cast_helper_interface.php
+++ b/phpBB/phpbb/request/type_cast_helper_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package \phpbb\request\request
-* @copyright (c) 2010 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\request;
/**
* An interface for type cast operations.
-*
-* @package \phpbb\request\request
*/
interface type_cast_helper_interface
{
diff --git a/phpBB/phpbb/routing/file_locator.php b/phpBB/phpbb/routing/file_locator.php
new file mode 100644
index 0000000000..64efcc6c76
--- /dev/null
+++ b/phpBB/phpbb/routing/file_locator.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\routing;
+
+use phpbb\filesystem\filesystem_interface;
+use Symfony\Component\Config\FileLocator;
+
+class file_locator extends FileLocator
+{
+ public function __construct(filesystem_interface $filesystem, $paths = [])
+ {
+ $paths = (array) $paths;
+ $absolute_paths = [];
+
+ foreach ($paths as $path)
+ {
+ $absolute_paths[] = $filesystem->realpath($path);
+ }
+
+ parent::__construct($absolute_paths);
+ }
+}
diff --git a/phpBB/phpbb/routing/helper.php b/phpBB/phpbb/routing/helper.php
new file mode 100644
index 0000000000..f56974a354
--- /dev/null
+++ b/phpBB/phpbb/routing/helper.php
@@ -0,0 +1,153 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\routing;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RequestContext;
+
+/**
+* Controller helper class, contains methods that do things for controllers
+*/
+class helper
+{
+ /**
+ * config object
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * phpBB router
+ * @var \phpbb\routing\router
+ */
+ protected $router;
+
+ /**
+ * @var \phpbb\symfony_request
+ */
+ protected $symfony_request;
+
+ /**
+ * @var \phpbb\request\request_interface
+ */
+ protected $request;
+
+ /**
+ * @var \phpbb\filesystem The filesystem object
+ */
+ protected $filesystem;
+
+ /**
+ * phpBB root path
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP file extension
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\routing\router $router phpBB router
+ * @param \phpbb\symfony_request $symfony_request Symfony Request object
+ * @param \phpbb\request\request_interface $request phpBB request object
+ * @param \phpbb\filesystem\filesystem $filesystem The filesystem object
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $php_ext PHP file extension
+ */
+ public function __construct(\phpbb\config\config $config, \phpbb\routing\router $router, \phpbb\symfony_request $symfony_request, \phpbb\request\request_interface $request, \phpbb\filesystem\filesystem $filesystem, $phpbb_root_path, $php_ext)
+ {
+ $this->config = $config;
+ $this->router = $router;
+ $this->symfony_request = $symfony_request;
+ $this->request = $request;
+ $this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ }
+
+ /**
+ * Generate a URL to a route
+ *
+ * @param string $route Name of the route to travel
+ * @param array $params String or array of additional url parameters
+ * @param bool $is_amp Is url using &amp; (true) or & (false)
+ * @param string|bool $session_id Possibility to use a custom session id instead of the global one
+ * @param bool|string $reference_type The type of reference to be generated (one of the constants)
+ * @return string The URL already passed through append_sid()
+ */
+ public function route($route, array $params = array(), $is_amp = true, $session_id = false, $reference_type = UrlGeneratorInterface::ABSOLUTE_PATH)
+ {
+ $anchor = '';
+ if (isset($params['#']))
+ {
+ $anchor = '#' . $params['#'];
+ unset($params['#']);
+ }
+
+ $context = new RequestContext();
+ $context->fromRequest($this->symfony_request);
+
+ $script_name = $this->symfony_request->getScriptName();
+ $page_name = substr($script_name, -1, 1) == '/' ? '' : utf8_basename($script_name);
+
+ $base_url = $context->getBaseUrl();
+
+ // Append page name if base URL does not contain it
+ if (!empty($page_name) && strpos($base_url, '/' . $page_name) === false)
+ {
+ $base_url .= '/' . $page_name;
+ }
+
+ // If enable_mod_rewrite is false we need to replace the current front-end by app.php, otherwise we need to remove it.
+ $base_url = str_replace('/' . $page_name, empty($this->config['enable_mod_rewrite']) ? '/app.' . $this->php_ext : '', $base_url);
+
+ // We need to update the base url to move to the directory of the app.php file if the current script is not app.php
+ if ($page_name !== 'app.php')
+ {
+ if (empty($this->config['enable_mod_rewrite']))
+ {
+ $base_url = str_replace('/app.' . $this->php_ext, '/' . $this->phpbb_root_path . 'app.' . $this->php_ext, $base_url);
+ }
+ else
+ {
+ $base_url .= preg_replace(get_preg_expression('path_remove_dot_trailing_slash'), '$2', $this->phpbb_root_path);
+ }
+ }
+
+ $base_url = $this->request->escape($this->filesystem->clean_path($base_url), true);
+
+ $context->setBaseUrl($base_url);
+
+ $this->router->setContext($context);
+ $route_url = $this->router->generate($route, $params, $reference_type);
+
+ if ($is_amp)
+ {
+ $route_url = str_replace(array('&amp;', '&'), array('&', '&amp;'), $route_url);
+ }
+
+ if ($reference_type === UrlGeneratorInterface::RELATIVE_PATH && empty($this->config['enable_mod_rewrite']))
+ {
+ $route_url = 'app.' . $this->php_ext . '/' . $route_url;
+ }
+
+ return append_sid($route_url . $anchor, false, $is_amp, $session_id, true);
+ }
+}
diff --git a/phpBB/phpbb/routing/loader_resolver.php b/phpBB/phpbb/routing/loader_resolver.php
new file mode 100644
index 0000000000..13fbc6405c
--- /dev/null
+++ b/phpBB/phpbb/routing/loader_resolver.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\routing;
+
+use Symfony\Component\Config\Loader\LoaderResolverInterface;
+
+/**
+ * @see Symfony\Component\Config\Loader\LoaderResolver
+ */
+class loader_resolver implements LoaderResolverInterface
+{
+ /**
+ * @var \Symfony\Component\Config\Loader\LoaderInterface[] An array of LoaderInterface objects
+ */
+ protected $loaders = [];
+
+ public function __construct($loaders = [])
+ {
+ $this->loaders = $loaders;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function resolve($resource, $type = null)
+ {
+ /** @var \Symfony\Component\Config\Loader\LoaderInterface $loader */
+ foreach ($this->loaders as $loader)
+ {
+ if ($loader->supports($resource, $type))
+ {
+ $loader->setResolver($this);
+ return $loader;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/phpBB/phpbb/routing/resources_locator/chained_resources_locator.php b/phpBB/phpbb/routing/resources_locator/chained_resources_locator.php
new file mode 100644
index 0000000000..db9abf2095
--- /dev/null
+++ b/phpBB/phpbb/routing/resources_locator/chained_resources_locator.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\routing\resources_locator;
+
+class chained_resources_locator implements resources_locator_interface
+{
+ /**
+ * @var resources_locator_interface[]
+ */
+ protected $locators;
+
+ /**
+ * Construct method
+ *
+ * @param resources_locator_interface[] $locators Locators
+ */
+ public function __construct($locators)
+ {
+ $this->locators = $locators;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function locate_resources()
+ {
+ $resources = [];
+
+ foreach ($this->locators as $locator)
+ {
+ $resources = array_merge($resources, $locator->locate_resources());
+ }
+
+ return $resources;
+ }
+}
diff --git a/phpBB/phpbb/routing/resources_locator/default_resources_locator.php b/phpBB/phpbb/routing/resources_locator/default_resources_locator.php
new file mode 100644
index 0000000000..90c3877007
--- /dev/null
+++ b/phpBB/phpbb/routing/resources_locator/default_resources_locator.php
@@ -0,0 +1,105 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\routing\resources_locator;
+
+use phpbb\extension\manager;
+
+/**
+ * Locates the yaml routing resources located in the default locations
+ */
+class default_resources_locator implements resources_locator_interface
+{
+ /**
+ * phpBB root path
+ *
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Name of the current environment
+ *
+ * @var string
+ */
+ protected $environment;
+
+ /**
+ * Extension manager
+ *
+ * @var manager
+ */
+ protected $extension_manager;
+
+ /**
+ * Construct method
+ *
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $environment Name of the current environment
+ * @param manager $extension_manager Extension manager
+ */
+ public function __construct($phpbb_root_path, $environment, manager $extension_manager = null)
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->environment = $environment;
+ $this->extension_manager = $extension_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function locate_resources()
+ {
+ $resources = [['config/' . $this->environment . '/routing/environment.yml', 'yaml']];
+
+ $resources = $this->append_ext_resources($resources);
+
+ return $resources;
+ }
+
+ /**
+ * Append extension resources to an array of resouces
+ *
+ * @see resources_locator_interface::locate_resources()
+ *
+ * @param mixed[] $resources List of resources
+ *
+ * @return mixed[] List of resources
+ */
+ protected function append_ext_resources(array $resources)
+ {
+ if ($this->extension_manager !== null)
+ {
+ foreach ($this->extension_manager->all_enabled(false) as $path)
+ {
+ if (file_exists($this->phpbb_root_path . $path . 'config/' . $this->environment . '/routing/environment.yml'))
+ {
+ $resources[] = [$path . 'config/' . $this->environment . '/routing/environment.yml', 'yaml'];
+ }
+ else if (!is_dir($this->phpbb_root_path . $path . 'config/' . $this->environment))
+ {
+ if (file_exists($this->phpbb_root_path . $path . 'config/default/routing/environment.yml'))
+ {
+ $resources[] = [$path . 'config/default/routing/environment.yml', 'yaml'];
+ }
+ else if (!is_dir($this->phpbb_root_path . $path . 'config/default/routing') && file_exists($this->phpbb_root_path . $path . 'config/routing.yml'))
+ {
+ $resources[] = [$path . 'config/routing.yml', 'yaml'];
+ }
+ }
+ }
+ }
+
+ return $resources;
+ }
+}
diff --git a/phpBB/phpbb/routing/resources_locator/installer_resources_locator.php b/phpBB/phpbb/routing/resources_locator/installer_resources_locator.php
new file mode 100644
index 0000000000..42cd0f11af
--- /dev/null
+++ b/phpBB/phpbb/routing/resources_locator/installer_resources_locator.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\routing\resources_locator;
+
+use phpbb\filesystem\filesystem_interface;
+
+/**
+ * Locates the yaml routing resources taking update directories into consideration
+ */
+class installer_resources_locator implements resources_locator_interface
+{
+ /**
+ * phpBB's filesystem handler
+ *
+ * @var filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * phpBB root path
+ *
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * Name of the current environment
+ *
+ * @var string
+ */
+ protected $environment;
+
+ /**
+ * Construct method
+ *
+ * @param filesystem_interface $filesystem phpBB's filesystem handler
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $environment Name of the current environment
+ */
+ public function __construct(filesystem_interface $filesystem, $phpbb_root_path, $environment)
+ {
+ $this->filesystem = $filesystem;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->environment = $environment;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function locate_resources()
+ {
+ if ($this->filesystem->exists($this->phpbb_root_path . 'install/update/new/config'))
+ {
+ $resources = array(
+ array('install/update/new/config/' . $this->environment . '/routing/environment.yml', 'yaml')
+ );
+ }
+ else
+ {
+ $resources = array(
+ array('config/' . $this->environment . '/routing/environment.yml', 'yaml')
+ );
+ }
+
+ return $resources;
+ }
+}
diff --git a/phpBB/phpbb/routing/resources_locator/resources_locator_interface.php b/phpBB/phpbb/routing/resources_locator/resources_locator_interface.php
new file mode 100644
index 0000000000..46335cb288
--- /dev/null
+++ b/phpBB/phpbb/routing/resources_locator/resources_locator_interface.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\routing\resources_locator;
+
+interface resources_locator_interface
+{
+ /**
+ * Locates a list of resources used to load the routes
+ *
+ * Each entry of the list can be either the resource or an array composed of 2 elements:
+ * the resource and its type.
+ *
+ * @return mixed[] List of resources
+ */
+ public function locate_resources();
+}
diff --git a/phpBB/phpbb/routing/router.php b/phpBB/phpbb/routing/router.php
new file mode 100644
index 0000000000..5d237b6433
--- /dev/null
+++ b/phpBB/phpbb/routing/router.php
@@ -0,0 +1,406 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\routing;
+
+use phpbb\routing\resources_locator\resources_locator_interface;
+use Symfony\Component\Config\ConfigCache;
+use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
+use Symfony\Component\DependencyInjection\Exception\RuntimeException;
+use Symfony\Component\Filesystem\Exception\IOException;
+use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
+use Symfony\Component\Routing\Generator\UrlGenerator;
+use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
+use Symfony\Component\Routing\Matcher\UrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\RouterInterface;
+
+/**
+ * Integration of all pieces of the routing system for easier use.
+ */
+class router implements RouterInterface
+{
+ /**
+ * @var ContainerInterface
+ */
+ protected $container;
+
+ /**
+ * @var resources_locator_interface
+ */
+ protected $resources_locator;
+
+ /**
+ * @var LoaderInterface
+ */
+ protected $loader;
+
+ /**
+ * phpBB root path
+ *
+ * @var string
+ */
+ protected $phpbb_root_path;
+
+ /**
+ * PHP file extensions
+ *
+ * @var string
+ */
+ protected $php_ext;
+
+ /**
+ * Name of the current environment
+ *
+ * @var string
+ */
+ protected $environment;
+
+ /**
+ * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|null
+ */
+ protected $matcher;
+
+ /**
+ * @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface|null
+ */
+ protected $generator;
+
+ /**
+ * @var RequestContext
+ */
+ protected $context;
+
+ /**
+ * @var RouteCollection
+ */
+ protected $route_collection;
+
+ /**
+ * Construct method
+ *
+ * @param ContainerInterface $container DI container
+ * @param resources_locator_interface $resources_locator Resources locator
+ * @param LoaderInterface $loader Resources loader
+ * @param string $phpbb_root_path phpBB root path
+ * @param string $php_ext PHP file extension
+ * @param string $environment Name of the current environment
+ */
+ public function __construct(ContainerInterface $container, resources_locator_interface $resources_locator, LoaderInterface $loader, $phpbb_root_path, $php_ext, $environment)
+ {
+ $this->container = $container;
+ $this->resources_locator = $resources_locator;
+ $this->loader = $loader;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->environment = $environment;
+ $this->context = new RequestContext();
+ }
+
+ /**
+ * Get the list of routes
+ *
+ * @return RouteCollection Get the route collection
+ */
+ public function get_routes()
+ {
+ if ($this->route_collection === null /*|| $this->route_collection->count() === 0*/)
+ {
+ $this->route_collection = new RouteCollection;
+ foreach ($this->resources_locator->locate_resources() as $resource)
+ {
+ if (is_array($resource))
+ {
+ $this->route_collection->addCollection($this->loader->load($resource[0], $resource[1]));
+ }
+ else
+ {
+ $this->route_collection->addCollection($this->loader->load($resource));
+ }
+ }
+
+ $this->resolveParameters($this->route_collection);
+ }
+
+ return $this->route_collection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getRouteCollection()
+ {
+ return $this->get_routes();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setContext(RequestContext $context)
+ {
+ $this->context = $context;
+
+ if ($this->matcher !== null)
+ {
+ $this->get_matcher()->setContext($context);
+ }
+ if ($this->generator !== null)
+ {
+ $this->get_generator()->setContext($context);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContext()
+ {
+ return $this->context;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
+ {
+ return $this->get_generator()->generate($name, $parameters, $referenceType);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function match($pathinfo)
+ {
+ return $this->get_matcher()->match($pathinfo);
+ }
+
+ /**
+ * Gets the UrlMatcher instance associated with this Router.
+ *
+ * @return \Symfony\Component\Routing\Matcher\UrlMatcherInterface A UrlMatcherInterface instance
+ */
+ public function get_matcher()
+ {
+ if ($this->matcher !== null)
+ {
+ return $this->matcher;
+ }
+
+ $this->create_dumped_url_matcher();
+
+ return $this->matcher;
+ }
+
+ /**
+ * Creates a new dumped URL Matcher (dump it if necessary)
+ */
+ protected function create_dumped_url_matcher()
+ {
+ try
+ {
+ $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_matcher.{$this->php_ext}", defined('DEBUG'));
+ if (!$cache->isFresh())
+ {
+ $dumper = new PhpMatcherDumper($this->get_routes());
+
+ $options = array(
+ 'class' => 'phpbb_url_matcher',
+ 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
+ );
+
+ $cache->write($dumper->dump($options), $this->get_routes()->getResources());
+ }
+
+ require_once($cache->getPath());
+
+ $this->matcher = new \phpbb_url_matcher($this->context);
+ }
+ catch (IOException $e)
+ {
+ $this->create_new_url_matcher();
+ }
+ }
+
+ /**
+ * Creates a new URL Matcher
+ */
+ protected function create_new_url_matcher()
+ {
+ $this->matcher = new UrlMatcher($this->get_routes(), $this->context);
+ }
+
+ /**
+ * Gets the UrlGenerator instance associated with this Router.
+ *
+ * @return \Symfony\Component\Routing\Generator\UrlGeneratorInterface A UrlGeneratorInterface instance
+ */
+ public function get_generator()
+ {
+ if ($this->generator !== null)
+ {
+ return $this->generator;
+ }
+
+ $this->create_dumped_url_generator();
+
+ return $this->generator;
+ }
+
+ /**
+ * Creates a new dumped URL Generator (dump it if necessary)
+ */
+ protected function create_dumped_url_generator()
+ {
+ try
+ {
+ $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_generator.{$this->php_ext}", defined('DEBUG'));
+ if (!$cache->isFresh())
+ {
+ $dumper = new PhpGeneratorDumper($this->get_routes());
+
+ $options = array(
+ 'class' => 'phpbb_url_generator',
+ 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
+ );
+
+ $cache->write($dumper->dump($options), $this->get_routes()->getResources());
+ }
+
+ require_once($cache->getPath());
+
+ $this->generator = new \phpbb_url_generator($this->context);
+ }
+ catch (IOException $e)
+ {
+ $this->create_new_url_generator();
+ }
+ }
+
+ /**
+ * Creates a new URL Generator
+ */
+ protected function create_new_url_generator()
+ {
+ $this->generator = new UrlGenerator($this->get_routes(), $this->context);
+ }
+
+ /**
+ * Replaces placeholders with service container parameter values in:
+ * - the route defaults,
+ * - the route requirements,
+ * - the route path,
+ * - the route host,
+ * - the route schemes,
+ * - the route methods.
+ *
+ * @param RouteCollection $collection
+ */
+ protected function resolveParameters(RouteCollection $collection)
+ {
+ /** @var \Symfony\Component\Routing\Route $route */
+ foreach ($collection as $route)
+ {
+ foreach ($route->getDefaults() as $name => $value)
+ {
+ $route->setDefault($name, $this->resolve($value));
+ }
+
+ $requirements = $route->getRequirements();
+ unset($requirements['_scheme']);
+ unset($requirements['_method']);
+
+ foreach ($requirements as $name => $value)
+ {
+ $route->setRequirement($name, $this->resolve($value));
+ }
+
+ $route->setPath($this->resolve($route->getPath()));
+ $route->setHost($this->resolve($route->getHost()));
+
+ $schemes = array();
+ foreach ($route->getSchemes() as $scheme)
+ {
+ $schemes = array_merge($schemes, explode('|', $this->resolve($scheme)));
+ }
+
+ $route->setSchemes($schemes);
+ $methods = array();
+ foreach ($route->getMethods() as $method)
+ {
+ $methods = array_merge($methods, explode('|', $this->resolve($method)));
+ }
+
+ $route->setMethods($methods);
+ $route->setCondition($this->resolve($route->getCondition()));
+ }
+ }
+
+ /**
+ * Recursively replaces placeholders with the service container parameters.
+ *
+ * @param mixed $value The source which might contain "%placeholders%"
+ *
+ * @return mixed The source with the placeholders replaced by the container
+ * parameters. Arrays are resolved recursively.
+ *
+ * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter
+ * @throws RuntimeException When a container value is not a string or a numeric value
+ */
+ private function resolve($value)
+ {
+ if (is_array($value))
+ {
+ foreach ($value as $key => $val)
+ {
+ $value[$key] = $this->resolve($val);
+ }
+
+ return $value;
+ }
+
+ if (!is_string($value))
+ {
+ return $value;
+ }
+
+ $container = $this->container;
+ $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value)
+ {
+ // skip %%
+ if (!isset($match[1]))
+ {
+ return '%%';
+ }
+
+ $resolved = $container->getParameter($match[1]);
+ if (is_string($resolved) || is_numeric($resolved))
+ {
+ return (string) $resolved;
+ }
+
+ throw new RuntimeException(sprintf(
+ 'The container parameter "%s", used in the route configuration value "%s", '.
+ 'must be a string or numeric, but it is of type %s.',
+ $match[1],
+ $value,
+ gettype($resolved)
+ )
+ );
+ }, $value);
+
+ return str_replace('%%', '%', $escapedValue);
+ }
+}
diff --git a/phpBB/phpbb/search/base.php b/phpBB/phpbb/search/base.php
index 9ecf3751d0..30781975d8 100644
--- a/phpBB/phpbb/search/base.php
+++ b/phpBB/phpbb/search/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -17,10 +21,8 @@ define('SEARCH_RESULT_IN_CACHE', 1);
define('SEARCH_RESULT_INCOMPLETE', 2);
/**
-* \phpbb\search\base
* optional base class for search plugins providing simple caching based on ACM
* and functions to retrieve ignore_words and synonyms
-* @package search
*/
class base
{
@@ -83,8 +85,12 @@ class base
/**
* Retrieves cached search results
*
- * @param int &$result_count will contain the number of all results for the search (not only for the current page)
- * @param array &$id_ary is filled with the ids belonging to the requested page that are stored in the cache
+ * @param string $search_key an md5 string generated from all the passed search options to identify the results
+ * @param int &$result_count will contain the number of all results for the search (not only for the current page)
+ * @param array &$id_ary is filled with the ids belonging to the requested page that are stored in the cache
+ * @param int &$start indicates the first index of the page
+ * @param int $per_page number of ids each page is supposed to contain
+ * @param string $sort_dir is either a or d representing ASC and DESC
*
* @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE
*/
@@ -158,8 +164,16 @@ class base
/**
* Caches post/topic ids
*
- * @param array &$id_ary contains a list of post or topic ids that shall be cached, the first element
+ * @param string $search_key an md5 string generated from all the passed search options to identify the results
+ * @param string $keywords contains the keywords as entered by the user
+ * @param array $author_ary an array of author ids, if the author should be ignored during the search the array is empty
+ * @param int $result_count contains the number of all results for the search (not only for the current page)
+ * @param array &$id_ary contains a list of post or topic ids that shall be cached, the first element
* must have the absolute index $start in the result set.
+ * @param int $start indicates the first index of the page
+ * @param string $sort_dir is either a or d representing ASC and DESC
+ *
+ * @return null
*/
function save_ids($search_key, $keywords, $author_ary, $result_count, &$id_ary, $start, $sort_dir)
{
@@ -280,7 +294,7 @@ class base
$sql_where = '';
foreach ($words as $word)
{
- $sql_where .= " OR search_keywords " . $db->sql_like_expression($db->any_char . $word . $db->any_char);
+ $sql_where .= " OR search_keywords " . $db->sql_like_expression($db->get_any_char() . $word . $db->get_any_char());
}
$sql = 'SELECT search_key
@@ -301,7 +315,7 @@ class base
$sql_where = '';
foreach ($authors as $author)
{
- $sql_where .= (($sql_where) ? ' OR ' : '') . 'search_authors ' . $db->sql_like_expression($db->any_char . ' ' . (int) $author . ' ' . $db->any_char);
+ $sql_where .= (($sql_where) ? ' OR ' : '') . 'search_authors ' . $db->sql_like_expression($db->get_any_char() . ' ' . (int) $author . ' ' . $db->get_any_char());
}
$sql = 'SELECT search_key
diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php
index 509b73e26e..d1962bc8cc 100644
--- a/phpBB/phpbb/search/fulltext_mysql.php
+++ b/phpBB/phpbb/search/fulltext_mysql.php
@@ -1,18 +1,20 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\search;
/**
-* fulltext_mysql
* Fulltext search for MySQL
-* @package search
*/
class fulltext_mysql extends \phpbb\search\base
{
@@ -36,11 +38,17 @@ class fulltext_mysql extends \phpbb\search\base
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
+ * phpBB event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* User object
* @var \phpbb\user
*/
@@ -71,11 +79,19 @@ class fulltext_mysql extends \phpbb\search\base
* Creates a new \phpbb\search\fulltext_mysql, which is used as a search backend
*
* @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $phpEx PHP file extension
+ * @param \phpbb\auth\auth $auth Auth object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\db\driver\driver_interface Database object
+ * @param \phpbb\user $user User object
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
*/
- public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user)
+ public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
{
$this->config = $config;
$this->db = $db;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->user = $user;
$this->word_length = array('min' => $this->config['fulltext_mysql_min_word_len'], 'max' => $this->config['fulltext_mysql_max_word_len']);
@@ -138,7 +154,7 @@ class fulltext_mysql extends \phpbb\search\base
*/
public function init()
{
- if ($this->db->sql_layer != 'mysql4' && $this->db->sql_layer != 'mysqli')
+ if ($this->db->get_sql_layer() != 'mysql4' && $this->db->get_sql_layer() != 'mysqli')
{
return $this->user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE'];
}
@@ -180,8 +196,8 @@ class fulltext_mysql extends \phpbb\search\base
}
$this->db->sql_freeresult($result);
- set_config('fulltext_mysql_max_word_len', $mysql_info['ft_max_word_len']);
- set_config('fulltext_mysql_min_word_len', $mysql_info['ft_min_word_len']);
+ $this->config->set('fulltext_mysql_max_word_len', $mysql_info['ft_max_word_len']);
+ $this->config->set('fulltext_mysql_min_word_len', $mysql_info['ft_min_word_len']);
return false;
}
@@ -363,7 +379,7 @@ class fulltext_mysql extends \phpbb\search\base
}
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
implode(', ', $this->split_words),
$type,
$fields,
@@ -374,7 +390,39 @@ class fulltext_mysql extends \phpbb\search\base
implode(',', $ex_fid_ary),
$post_visibility,
implode(',', $author_ary)
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_mysql_by_keyword_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var string type Searching type ('posts', 'topics')
+ * @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
+ * @var string terms Searching terms ('all', 'any')
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'type',
+ 'fields',
+ 'terms',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_by_keyword_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
if ($start < 0)
{
@@ -439,6 +487,55 @@ class fulltext_mysql extends \phpbb\search\base
break;
}
+ $search_query = $this->search_query;
+
+ /**
+ * Allow changing the query used to search for posts using fulltext_mysql
+ *
+ * @event core.search_mysql_keywords_main_query_before
+ * @var string search_query The parsed keywords used for this search
+ * @var int result_count The previous result count for the format of the query.
+ * Set to 0 to force a re-count
+ * @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant)
+ * @var array ex_fid_ary Which forums not to search on
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sql_sort_table Extra tables to include in the SQL query.
+ * Used in conjunction with sql_sort_join
+ * @var string sql_sort_join SQL conditions to join all the tables used together.
+ * Used in conjunction with sql_sort_table
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sql_match Which columns to do the search on.
+ * @var string sql_match_where Extra conditions to use to properly filter the matching process
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'search_query',
+ 'result_count',
+ 'join_topic',
+ 'author_ary',
+ 'author_name',
+ 'ex_fid_ary',
+ 'topic_id',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'sort_days',
+ 'sql_match',
+ 'sql_match_where',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_keywords_main_query_before', compact($vars)));
+
$sql_select = (!$result_count) ? 'SQL_CALC_FOUND_ROWS ' : '';
$sql_select = ($type == 'posts') ? $sql_select . 'p.post_id' : 'DISTINCT ' . $sql_select . 't.topic_id';
$sql_from = ($join_topic) ? TOPICS_TABLE . ' t, ' : '';
@@ -545,7 +642,7 @@ class fulltext_mysql extends \phpbb\search\base
}
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
'',
$type,
($firstpost_only) ? 'firstpost' : '',
@@ -558,7 +655,39 @@ class fulltext_mysql extends \phpbb\search\base
$post_visibility,
implode(',', $author_ary),
$author_name,
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_mysql_by_author_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var string type Searching type ('posts', 'topics')
+ * @var boolean firstpost_only Flag indicating if only topic starting posts are considered
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name The username to search on
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'type',
+ 'firstpost_only',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ 'author_name',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_by_author_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
if ($start < 0)
{
@@ -612,6 +741,59 @@ class fulltext_mysql extends \phpbb\search\base
$m_approve_fid_sql = ' AND ' . $post_visibility;
+ /**
+ * Allow changing the query used to search for posts by author in fulltext_mysql
+ *
+ * @event core.search_mysql_author_query_before
+ * @var int result_count The previous result count for the format of the query.
+ * Set to 0 to force a re-count
+ * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen
+ * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table
+ * @var string type Either "posts" or "topics" specifying the type of search being made
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name An extra username to search on
+ * @var string sql_author SQL WHERE condition for the post author ids
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sql_topic_id SQL of topic_id
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var string sort_days Time, in days, that the oldest post showing can have
+ * @var string sql_time The SQL to search on the time specifyed by sort_days
+ * @var bool firstpost_only Wether or not to search only on the first post of the topics
+ * @var string sql_firstpost The SQL with the conditions to join the tables when using firstpost_only
+ * @var array ex_fid_ary Forum ids that must not be searched on
+ * @var array sql_fora SQL query for ex_fid_ary
+ * @var string m_approve_fid_sql WHERE clause condition on post_visibility restrictions
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'result_count',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'type',
+ 'author_ary',
+ 'author_name',
+ 'sql_author',
+ 'topic_id',
+ 'sql_topic_id',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'sort_days',
+ 'sql_time',
+ 'firstpost_only',
+ 'sql_firstpost',
+ 'ex_fid_ary',
+ 'sql_fora',
+ 'm_approve_fid_sql',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_author_query_before', compact($vars)));
+
// If the cache was completely empty count the results
$calc_results = ($result_count) ? '' : 'SQL_CALC_FOUND_ROWS ';
@@ -737,7 +919,7 @@ class fulltext_mysql extends \phpbb\search\base
// destroy too old cached search results
$this->destroy_cache(array());
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
}
/**
@@ -762,7 +944,7 @@ class fulltext_mysql extends \phpbb\search\base
if (!isset($this->stats['post_subject']))
{
- if ($this->db->sql_layer == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
+ if ($this->db->get_sql_layer() == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
{
$alter[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
}
@@ -775,7 +957,7 @@ class fulltext_mysql extends \phpbb\search\base
if (!isset($this->stats['post_content']))
{
- if ($this->db->sql_layer == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
+ if ($this->db->get_sql_layer() == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>='))
{
$alter[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL';
}
@@ -870,7 +1052,7 @@ class fulltext_mysql extends \phpbb\search\base
*/
protected function get_stats()
{
- if (strpos($this->db->sql_layer, 'mysql') === false)
+ if (strpos($this->db->get_sql_layer(), 'mysql') === false)
{
$this->stats = array();
return;
diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php
index 1a89182978..521eebb7ee 100644
--- a/phpBB/phpbb/search/fulltext_native.php
+++ b/phpBB/phpbb/search/fulltext_native.php
@@ -1,21 +1,30 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\search;
/**
-* fulltext_native
* phpBB's own db driven fulltext search, version 2
-* @package search
*/
class fulltext_native extends \phpbb\search\base
{
+ const UTF8_HANGUL_FIRST = "\xEA\xB0\x80";
+ const UTF8_HANGUL_LAST = "\xED\x9E\xA3";
+ const UTF8_CJK_FIRST = "\xE4\xB8\x80";
+ const UTF8_CJK_LAST = "\xE9\xBE\xBB";
+ const UTF8_CJK_B_FIRST = "\xF0\xA0\x80\x80";
+ const UTF8_CJK_B_LAST = "\xF0\xAA\x9B\x96";
+
/**
* Associative array holding index stats
* @var array
@@ -55,7 +64,7 @@ class fulltext_native extends \phpbb\search\base
protected $must_not_contain_ids = array();
/**
- * Post ids of posts containing atleast one word that needs to be excluded
+ * Post ids of posts containing at least one word that needs to be excluded
* @var array
*/
protected $must_exclude_one_ids = array();
@@ -80,27 +89,35 @@ class fulltext_native extends \phpbb\search\base
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
+ * phpBB event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* User object
* @var \phpbb\user
*/
protected $user;
/**
- * Initialises the fulltext_native search backend with min/max word length and makes sure the UTF-8 normalizer is loaded
+ * Initialises the fulltext_native search backend with min/max word length
*
* @param boolean|string &$error is passed by reference and should either be set to false on success or an error message on failure
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
*/
- public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user)
+ public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $phpEx;
$this->config = $config;
$this->db = $db;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->user = $user;
$this->word_length = array('min' => $this->config['fulltext_native_min_chars'], 'max' => $this->config['fulltext_native_max_chars']);
@@ -108,10 +125,6 @@ class fulltext_native extends \phpbb\search\base
/**
* Load the UTF tools
*/
- if (!class_exists('utf_normalizer'))
- {
- include($this->phpbb_root_path . 'includes/utf/utf_normalizer.' . $this->php_ext);
- }
if (!function_exists('utf8_decode_ncr'))
{
include($this->phpbb_root_path . 'includes/utf/utf_tools.' . $this->php_ext);
@@ -298,7 +311,7 @@ class fulltext_native extends \phpbb\search\base
$this->search_query = $keywords;
$exact_words = array();
- preg_match_all('#([^\\s+\\-|*()]+)(?:$|[\\s+\\-|()])#u', $keywords, $exact_words);
+ preg_match_all('#([^\\s+\\-|()]+)(?:$|[\\s+\\-|()])#u', $keywords, $exact_words);
$exact_words = $exact_words[1];
$common_ids = $words = array();
@@ -325,7 +338,12 @@ class fulltext_native extends \phpbb\search\base
}
$this->db->sql_freeresult($result);
}
- unset($exact_words);
+
+ // Handle +, - without preceeding whitespace character
+ $match = array('#(\S)\+#', '#(\S)-#');
+ $replace = array('$1 +', '$1 +');
+
+ $keywords = preg_replace($match, $replace, $keywords);
// now analyse the search query, first split it using the spaces
$query = explode(' ', $keywords);
@@ -334,9 +352,6 @@ class fulltext_native extends \phpbb\search\base
$this->must_not_contain_ids = array();
$this->must_exclude_one_ids = array();
- $mode = '';
- $ignore_no_id = true;
-
foreach ($query as $word)
{
if (empty($word))
@@ -427,7 +442,7 @@ class fulltext_native extends \phpbb\search\base
// throw an error if we shall not ignore unexistant words
else if (!$ignore_no_id && sizeof($non_common_words))
{
- trigger_error(sprintf($user->lang['WORDS_IN_NO_POST'], implode($user->lang['COMMA_SEPARATOR'], $non_common_words)));
+ trigger_error(sprintf($this->user->lang['WORDS_IN_NO_POST'], implode($this->user->lang['COMMA_SEPARATOR'], $non_common_words)));
}
unset($non_common_words);
}
@@ -451,39 +466,21 @@ class fulltext_native extends \phpbb\search\base
$this->{$mode . '_ids'}[] = $words[$word];
}
}
- // throw an error if we shall not ignore unexistant words
- else if (!$ignore_no_id)
+ else
{
if (!isset($common_ids[$word]))
{
$len = utf8_strlen($word);
- if ($len >= $this->word_length['min'] && $len <= $this->word_length['max'])
- {
- trigger_error(sprintf($this->user->lang['WORD_IN_NO_POST'], $word));
- }
- else
+ if ($len < $this->word_length['min'] || $len > $this->word_length['max'])
{
$this->common_words[] = $word;
}
}
}
- else
- {
- $len = utf8_strlen($word);
- if ($len < $this->word_length['min'] || $len > $this->word_length['max'])
- {
- $this->common_words[] = $word;
- }
- }
- }
-
- // we can't search for negatives only
- if (!sizeof($this->must_contain_ids))
- {
- return false;
}
- if (!empty($this->search_query))
+ // Return true if all words are not common words
+ if (sizeof($exact_words) - sizeof($this->common_words) > 0)
{
return true;
}
@@ -518,6 +515,12 @@ class fulltext_native extends \phpbb\search\base
return false;
}
+ // we can't search for negatives only
+ if (empty($this->must_contain_ids))
+ {
+ return false;
+ }
+
$must_contain_ids = $this->must_contain_ids;
$must_not_contain_ids = $this->must_not_contain_ids;
$must_exclude_one_ids = $this->must_exclude_one_ids;
@@ -527,7 +530,7 @@ class fulltext_native extends \phpbb\search\base
sort($must_exclude_one_ids);
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
serialize($must_contain_ids),
serialize($must_not_contain_ids),
serialize($must_exclude_one_ids),
@@ -541,7 +544,45 @@ class fulltext_native extends \phpbb\search\base
$post_visibility,
implode(',', $author_ary),
$author_name,
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_native_by_keyword_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var array must_contain_ids Array with post ids of posts containing words that are to be included
+ * @var array must_not_contain_ids Array with post ids of posts containing words that should not be included
+ * @var array must_exclude_one_ids Array with post ids of posts containing at least one word that needs to be excluded
+ * @var string type Searching type ('posts', 'topics')
+ * @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
+ * @var string terms Searching terms ('all', 'any')
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'must_contain_ids',
+ 'must_not_contain_ids',
+ 'must_exclude_one_ids',
+ 'type',
+ 'fields',
+ 'terms',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_native_by_keyword_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
// try reading the results from cache
$total_results = 0;
@@ -553,7 +594,6 @@ class fulltext_native extends \phpbb\search\base
$id_ary = array();
$sql_where = array();
- $group_by = false;
$m_num = 0;
$w_num = 0;
@@ -717,6 +757,70 @@ class fulltext_native extends \phpbb\search\base
$sql_where[] = $post_visibility;
+ $search_query = $this->search_query;
+ $must_exclude_one_ids = $this->must_exclude_one_ids;
+ $must_not_contain_ids = $this->must_not_contain_ids;
+ $must_contain_ids = $this->must_contain_ids;
+
+ /**
+ * Allow changing the query used for counting for posts using fulltext_native
+ *
+ * @event core.search_native_keywords_count_query_before
+ * @var string search_query The parsed keywords used for this search
+ * @var array must_not_contain_ids Ids that cannot be taken into account for the results
+ * @var array must_exclude_one_ids Ids that cannot be on the results
+ * @var array must_contain_ids Ids that must be on the results
+ * @var int total_results The previous result count for the format of the query
+ * Set to 0 to force a re-count
+ * @var array sql_array The data on how to search in the DB at this point
+ * @var bool left_join_topics Whether or not TOPICS_TABLE should be CROSS JOIN'ED
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant)
+ * @var array ex_fid_ary Which forums not to search on
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sql_sort_table Extra tables to include in the SQL query.
+ * Used in conjunction with sql_sort_join
+ * @var string sql_sort_join SQL conditions to join all the tables used together.
+ * Used in conjunction with sql_sort_table
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sql_where An array of the current WHERE clause conditions
+ * @var string sql_match Which columns to do the search on
+ * @var string sql_match_where Extra conditions to use to properly filter the matching process
+ * @var bool group_by Whether or not the SQL query requires a GROUP BY for the elements in the SELECT clause
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'search_query',
+ 'must_not_contain_ids',
+ 'must_exclude_one_ids',
+ 'must_contain_ids',
+ 'total_results',
+ 'sql_array',
+ 'left_join_topics',
+ 'author_ary',
+ 'author_name',
+ 'ex_fid_ary',
+ 'topic_id',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'sort_days',
+ 'sql_where',
+ 'sql_match',
+ 'sql_match_where',
+ 'group_by',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_native_keywords_count_query_before', compact($vars)));
+
if ($topic_id)
{
$sql_where[] = 'p.topic_id = ' . $topic_id;
@@ -763,7 +867,7 @@ class fulltext_native extends \phpbb\search\base
);
}
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'mysql4':
case 'mysqli':
@@ -775,6 +879,7 @@ class fulltext_native extends \phpbb\search\base
break;
case 'sqlite':
+ case 'sqlite3':
$sql_array_count['SELECT'] = ($type == 'posts') ? 'DISTINCT p.post_id' : 'DISTINCT p.topic_id';
$sql = 'SELECT COUNT(' . (($type == 'posts') ? 'post_id' : 'topic_id') . ') as total_results
FROM (' . $this->db->sql_build_query('SELECT', $sql_array_count) . ')';
@@ -827,6 +932,13 @@ class fulltext_native extends \phpbb\search\base
);
}
+ // if using mysql and the total result count is not calculated yet, get it from the db
+ if (!$total_results && $is_mysql)
+ {
+ // Also count rows for the query as if there was not LIMIT. Add SQL_CALC_FOUND_ROWS to SQL
+ $sql_array['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $sql_array['SELECT'];
+ }
+
$sql_array['WHERE'] = implode(' AND ', $sql_where);
$sql_array['GROUP_BY'] = ($group_by) ? (($type == 'posts') ? 'p.post_id' : 'p.topic_id') . ', ' . $sort_by_sql[$sort_key] : '';
$sql_array['ORDER_BY'] = $sql_sort;
@@ -842,19 +954,9 @@ class fulltext_native extends \phpbb\search\base
}
$this->db->sql_freeresult($result);
- // if we use mysql and the total result count is not cached yet, retrieve it from the db
if (!$total_results && $is_mysql)
{
- // Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it
- $sql_array_copy = $sql_array;
- $sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id ';
-
- $sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy);
- unset($sql_array_copy);
-
- $this->db->sql_query($sql_calc);
- $this->db->sql_freeresult($result);
-
+ // Get the number of results as calculated by MySQL
$sql_count = 'SELECT FOUND_ROWS() as total_results';
$result = $this->db->sql_query($sql_count);
$total_results = (int) $this->db->sql_fetchfield('total_results');
@@ -915,7 +1017,7 @@ class fulltext_native extends \phpbb\search\base
}
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
'',
$type,
($firstpost_only) ? 'firstpost' : '',
@@ -928,7 +1030,39 @@ class fulltext_native extends \phpbb\search\base
$post_visibility,
implode(',', $author_ary),
$author_name,
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_native_by_author_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var string type Searching type ('posts', 'topics')
+ * @var boolean firstpost_only Flag indicating if only topic starting posts are considered
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name The username to search on
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'type',
+ 'firstpost_only',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ 'author_name',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_native_by_author_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
// try reading the results from cache
$total_results = 0;
@@ -979,10 +1113,57 @@ class fulltext_native extends \phpbb\search\base
$select = ($type == 'posts') ? 'p.post_id' : 't.topic_id';
$is_mysql = false;
+ /**
+ * Allow changing the query used to search for posts by author in fulltext_native
+ *
+ * @event core.search_native_author_count_query_before
+ * @var int total_results The previous result count for the format of the query.
+ * Set to 0 to force a re-count
+ * @var string type The type of search being made
+ * @var string select SQL SELECT clause for what to get
+ * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen
+ * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table
+ * @var array sql_author SQL WHERE condition for the post author ids
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var string sort_days Time, in days, that the oldest post showing can have
+ * @var string sql_time The SQL to search on the time specifyed by sort_days
+ * @var bool firstpost_only Wether or not to search only on the first post of the topics
+ * @var string sql_firstpost The SQL used in the WHERE claused to filter by firstpost.
+ * @var array ex_fid_ary Forum ids that must not be searched on
+ * @var array sql_fora SQL query for ex_fid_ary
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'total_results',
+ 'type',
+ 'select',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'sql_author',
+ 'topic_id',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'sort_days',
+ 'sql_time',
+ 'firstpost_only',
+ 'sql_firstpost',
+ 'ex_fid_ary',
+ 'sql_fora',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_native_author_count_query_before', compact($vars)));
+
// If the cache was completely empty count the results
if (!$total_results)
{
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'mysql4':
case 'mysqli':
@@ -1004,7 +1185,7 @@ class fulltext_native extends \phpbb\search\base
}
else
{
- if ($this->db->sql_layer == 'sqlite')
+ if ($this->db->get_sql_layer() == 'sqlite' || $this->db->get_sql_layer() == 'sqlite3')
{
$sql = 'SELECT COUNT(topic_id) as total_results
FROM (SELECT DISTINCT t.topic_id';
@@ -1021,7 +1202,7 @@ class fulltext_native extends \phpbb\search\base
$post_visibility
$sql_fora
AND t.topic_id = p.topic_id
- $sql_time" . (($this->db->sql_layer == 'sqlite') ? ')' : '');
+ $sql_time" . (($this->db->get_sql_layer() == 'sqlite' || $this->db->get_sql_layer() == 'sqlite3') ? ')' : '');
}
$result = $this->db->sql_query($sql);
@@ -1082,7 +1263,7 @@ class fulltext_native extends \phpbb\search\base
// Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it.
$sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql);
- $this->db->sql_query($sql_calc);
+ $result = $this->db->sql_query($sql_calc);
$this->db->sql_freeresult($result);
$sql_count = 'SELECT FOUND_ROWS() as total_results';
@@ -1143,7 +1324,6 @@ class fulltext_native extends \phpbb\search\base
$match[] = '#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?[0-9a-z]{5,})\]#';
$min = $this->word_length['min'];
- $max = $this->word_length['max'];
$isset_min = $min - 1;
@@ -1179,9 +1359,9 @@ class fulltext_native extends \phpbb\search\base
* Note: this could be optimized. If the codepoint is lower than Hangul's range
* we know that it will also be lower than CJK ranges
*/
- if ((strncmp($word, UTF8_HANGUL_FIRST, 3) < 0 || strncmp($word, UTF8_HANGUL_LAST, 3) > 0)
- && (strncmp($word, UTF8_CJK_FIRST, 3) < 0 || strncmp($word, UTF8_CJK_LAST, 3) > 0)
- && (strncmp($word, UTF8_CJK_B_FIRST, 4) < 0 || strncmp($word, UTF8_CJK_B_LAST, 4) > 0))
+ if ((strncmp($word, self::UTF8_HANGUL_FIRST, 3) < 0 || strncmp($word, self::UTF8_HANGUL_LAST, 3) > 0)
+ && (strncmp($word, self::UTF8_CJK_FIRST, 3) < 0 || strncmp($word, self::UTF8_CJK_LAST, 3) > 0)
+ && (strncmp($word, self::UTF8_CJK_B_FIRST, 4) < 0 || strncmp($word, self::UTF8_CJK_B_LAST, 4) > 0))
{
$word = strtok(' ');
continue;
@@ -1426,7 +1606,7 @@ class fulltext_native extends \phpbb\search\base
// carry on ... it's okay ... I know when I'm not wanted boo hoo
if (!$this->config['fulltext_native_load_upd'])
{
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
return;
}
@@ -1461,7 +1641,7 @@ class fulltext_native extends \phpbb\search\base
// by setting search_last_gc to the new time here we make sure that if a user reloads because the
// following query takes too long, he won't run into it again
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
// Delete the matches
$sql = 'DELETE FROM ' . SEARCH_WORDMATCH_TABLE . '
@@ -1477,7 +1657,7 @@ class fulltext_native extends \phpbb\search\base
$this->destroy_cache(array_unique($destroy_cache_words));
}
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
}
/**
@@ -1485,10 +1665,10 @@ class fulltext_native extends \phpbb\search\base
*/
public function delete_index($acp_module, $u_action)
{
- switch ($this->db->sql_layer)
+ switch ($this->db->get_sql_layer())
{
case 'sqlite':
- case 'firebird':
+ case 'sqlite3':
$this->db->sql_query('DELETE FROM ' . SEARCH_WORDLIST_TABLE);
$this->db->sql_query('DELETE FROM ' . SEARCH_WORDMATCH_TABLE);
$this->db->sql_query('DELETE FROM ' . SEARCH_RESULTS_TABLE);
@@ -1548,13 +1728,11 @@ class fulltext_native extends \phpbb\search\base
* @param string $allowed_chars String of special chars to allow
* @param string $encoding Text encoding
* @return string Cleaned up text, only alphanumeric chars are left
- *
- * @todo \normalizer::cleanup being able to be used?
*/
protected function cleanup($text, $allowed_chars = null, $encoding = 'utf-8')
{
static $conv = array(), $conv_loaded = array();
- $words = $allow = array();
+ $allow = array();
// Convert the text to UTF-8
$encoding = strtolower($encoding);
@@ -1576,12 +1754,9 @@ class fulltext_native extends \phpbb\search\base
$text = htmlspecialchars_decode(utf8_decode_ncr($text), ENT_QUOTES);
/**
- * Load the UTF-8 normalizer
- *
- * If we use it more widely, an instance of that class should be held in a
- * a global variable instead
+ * Normalize to NFC
*/
- \utf_normalizer::nfc($text);
+ $text = \Normalizer::normalize($text);
/**
* The first thing we do is:
@@ -1674,9 +1849,9 @@ class fulltext_native extends \phpbb\search\base
$utf_char = substr($text, $pos, $utf_len);
$pos += $utf_len;
- if (($utf_char >= UTF8_HANGUL_FIRST && $utf_char <= UTF8_HANGUL_LAST)
- || ($utf_char >= UTF8_CJK_FIRST && $utf_char <= UTF8_CJK_LAST)
- || ($utf_char >= UTF8_CJK_B_FIRST && $utf_char <= UTF8_CJK_B_LAST))
+ if (($utf_char >= self::UTF8_HANGUL_FIRST && $utf_char <= self::UTF8_HANGUL_LAST)
+ || ($utf_char >= self::UTF8_CJK_FIRST && $utf_char <= self::UTF8_CJK_LAST)
+ || ($utf_char >= self::UTF8_CJK_B_FIRST && $utf_char <= self::UTF8_CJK_B_LAST))
{
/**
* All characters within these ranges are valid
diff --git a/phpBB/phpbb/search/fulltext_postgres.php b/phpBB/phpbb/search/fulltext_postgres.php
index 063bf52a19..42425cbc6b 100644
--- a/phpBB/phpbb/search/fulltext_postgres.php
+++ b/phpBB/phpbb/search/fulltext_postgres.php
@@ -1,18 +1,20 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\search;
/**
-* fulltext_postgres
* Fulltext search for PostgreSQL
-* @package search
*/
class fulltext_postgres extends \phpbb\search\base
{
@@ -29,18 +31,6 @@ class fulltext_postgres extends \phpbb\search\base
protected $split_words = array();
/**
- * True if PostgreSQL version supports tsearch
- * @var boolean
- */
- protected $tsearch_usable = false;
-
- /**
- * Stores the PostgreSQL version
- * @var string
- */
- protected $version;
-
- /**
* Stores the tsearch query
* @var string
*/
@@ -61,11 +51,17 @@ class fulltext_postgres extends \phpbb\search\base
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
+ * phpBB event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* User object
* @var \phpbb\user
*/
@@ -96,25 +92,23 @@ class fulltext_postgres extends \phpbb\search\base
* Creates a new \phpbb\search\fulltext_postgres, which is used as a search backend
*
* @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $phpEx PHP file extension
+ * @param \phpbb\auth\auth $auth Auth object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\db\driver\driver_interface Database object
+ * @param \phpbb\user $user User object
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
*/
- public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user)
+ public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
{
$this->config = $config;
$this->db = $db;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->user = $user;
$this->word_length = array('min' => $this->config['fulltext_postgres_min_word_len'], 'max' => $this->config['fulltext_postgres_max_word_len']);
- if ($this->db->sql_layer == 'postgres')
- {
- $pgsql_version = explode(',', substr($this->db->sql_server_info(), 10));
- $this->version = trim($pgsql_version[0]);
- if (version_compare($this->version, '8.3', '>='))
- {
- $this->tsearch_usable = true;
- }
- }
-
/**
* Load the UTF tools
*/
@@ -183,16 +177,11 @@ class fulltext_postgres extends \phpbb\search\base
*/
public function init()
{
- if ($this->db->sql_layer != 'postgres')
+ if ($this->db->get_sql_layer() != 'postgres')
{
return $this->user->lang['FULLTEXT_POSTGRES_INCOMPATIBLE_DATABASE'];
}
- if (!$this->tsearch_usable)
- {
- return $this->user->lang['FULLTEXT_POSTGRES_TS_NOT_USABLE'];
- }
-
return false;
}
@@ -261,12 +250,12 @@ class fulltext_postgres extends \phpbb\search\base
$this->search_query .= $word . ' ';
$this->tsearch_query .= '&' . substr($word, 1) . ' ';
}
- elseif (strpos($word, '-') === 0)
+ else if (strpos($word, '-') === 0)
{
$this->search_query .= $word . ' ';
$this->tsearch_query .= '&!' . substr($word, 1) . ' ';
}
- elseif (strpos($word, '|') === 0)
+ else if (strpos($word, '|') === 0)
{
$this->search_query .= $word . ' ';
$this->tsearch_query .= '|' . substr($word, 1) . ' ';
@@ -352,7 +341,7 @@ class fulltext_postgres extends \phpbb\search\base
}
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
implode(', ', $this->split_words),
$type,
$fields,
@@ -363,7 +352,39 @@ class fulltext_postgres extends \phpbb\search\base
implode(',', $ex_fid_ary),
$post_visibility,
implode(',', $author_ary)
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_postgres_by_keyword_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var string type Searching type ('posts', 'topics')
+ * @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
+ * @var string terms Searching terms ('all', 'any')
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'type',
+ 'fields',
+ 'terms',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_by_keyword_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
if ($start < 0)
{
@@ -428,10 +449,58 @@ class fulltext_postgres extends \phpbb\search\base
break;
}
+ $tsearch_query = $this->tsearch_query;
+
+ /**
+ * Allow changing the query used to search for posts using fulltext_postgres
+ *
+ * @event core.search_postgres_keywords_main_query_before
+ * @var string tsearch_query The parsed keywords used for this search
+ * @var int result_count The previous result count for the format of the query.
+ * Set to 0 to force a re-count
+ * @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant)
+ * @var array ex_fid_ary Which forums not to search on
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sql_sort_table Extra tables to include in the SQL query.
+ * Used in conjunction with sql_sort_join
+ * @var string sql_sort_join SQL conditions to join all the tables used together.
+ * Used in conjunction with sql_sort_table
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sql_match Which columns to do the search on.
+ * @var string sql_match_where Extra conditions to use to properly filter the matching process
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'tsearch_query',
+ 'result_count',
+ 'join_topic',
+ 'author_ary',
+ 'author_name',
+ 'ex_fid_ary',
+ 'topic_id',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'sort_days',
+ 'sql_match',
+ 'sql_match_where',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_keywords_main_query_before', compact($vars)));
+
$sql_select = ($type == 'posts') ? 'p.post_id' : 'DISTINCT t.topic_id';
$sql_from = ($join_topic) ? TOPICS_TABLE . ' t, ' : '';
$field = ($type == 'posts') ? 'post_id' : 'topic_id';
- $sql_author = (sizeof($author_ary) == 1) ? ' = ' . $author_ary[0] : 'IN (' . implode(', ', $author_ary) . ')';
if (sizeof($author_ary) && $author_name)
{
@@ -456,16 +525,13 @@ class fulltext_postgres extends \phpbb\search\base
$sql_where_options .= ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
$sql_where_options .= $sql_match_where;
- $tmp_sql_match = array();
- foreach (explode(',', $sql_match) as $sql_match_column)
- {
- $tmp_sql_match[] = "to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', " . $sql_match_column . ") @@ to_tsquery ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', '" . $this->db->sql_escape($this->tsearch_query) . "')";
- }
+ $sql_match = str_replace(',', " || ' ' ||", $sql_match);
+ $tmp_sql_match = "to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', " . $sql_match . ") @@ to_tsquery ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', '" . $this->db->sql_escape($this->tsearch_query) . "')";
$this->db->sql_transaction('begin');
$sql_from = "FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p";
- $sql_where = "WHERE (" . implode(' OR ', $tmp_sql_match) . ")
+ $sql_where = "WHERE (" . $tmp_sql_match . ")
$sql_where_options";
$sql = "SELECT $sql_select
$sql_from
@@ -549,7 +615,7 @@ class fulltext_postgres extends \phpbb\search\base
}
// generate a search_key from all the options to identify the results
- $search_key = md5(implode('#', array(
+ $search_key_array = array(
'',
$type,
($firstpost_only) ? 'firstpost' : '',
@@ -562,7 +628,39 @@ class fulltext_postgres extends \phpbb\search\base
$post_visibility,
implode(',', $author_ary),
$author_name,
- )));
+ );
+
+ /**
+ * Allow changing the search_key for cached results
+ *
+ * @event core.search_postgres_by_author_modify_search_key
+ * @var array search_key_array Array with search parameters to generate the search_key
+ * @var string type Searching type ('posts', 'topics')
+ * @var boolean firstpost_only Flag indicating if only topic starting posts are considered
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name The username to search on
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'search_key_array',
+ 'type',
+ 'firstpost_only',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ 'author_name',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_by_author_modify_search_key', compact($vars)));
+
+ $search_key = md5(implode('#', $search_key_array));
if ($start < 0)
{
@@ -616,6 +714,55 @@ class fulltext_postgres extends \phpbb\search\base
$m_approve_fid_sql = ' AND ' . $post_visibility;
+ /**
+ * Allow changing the query used to search for posts by author in fulltext_postgres
+ *
+ * @event core.search_postgres_author_count_query_before
+ * @var int result_count The previous result count for the format of the query.
+ * Set to 0 to force a re-count
+ * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen
+ * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name An extra username to search on
+ * @var string sql_author SQL WHERE condition for the post author ids
+ * @var int topic_id Limit the search to this topic_id only
+ * @var string sql_topic_id SQL of topic_id
+ * @var string sort_by_sql The possible predefined sort types
+ * @var string sort_key The sort type used from the possible sort types
+ * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used
+ * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir
+ * @var string sort_days Time, in days, that the oldest post showing can have
+ * @var string sql_time The SQL to search on the time specifyed by sort_days
+ * @var bool firstpost_only Wether or not to search only on the first post of the topics
+ * @var array ex_fid_ary Forum ids that must not be searched on
+ * @var array sql_fora SQL query for ex_fid_ary
+ * @var string m_approve_fid_sql WHERE clause condition on post_visibility restrictions
+ * @var int start How many posts to skip in the search results (used for pagination)
+ * @since 3.1.5-RC1
+ */
+ $vars = array(
+ 'result_count',
+ 'sql_sort_table',
+ 'sql_sort_join',
+ 'author_ary',
+ 'author_name',
+ 'sql_author',
+ 'topic_id',
+ 'sql_topic_id',
+ 'sort_by_sql',
+ 'sort_key',
+ 'sort_dir',
+ 'sql_sort',
+ 'sort_days',
+ 'sql_time',
+ 'firstpost_only',
+ 'ex_fid_ary',
+ 'sql_fora',
+ 'm_approve_fid_sql',
+ 'start',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_author_count_query_before', compact($vars)));
+
// Build the query for really selecting the post_ids
if ($type == 'posts')
{
@@ -689,7 +836,7 @@ class fulltext_postgres extends \phpbb\search\base
GROUP BY t.topic_id, $sort_by_sql[$sort_key]";
}
- $result = $this->db->sql_query($sql_count);
+ $this->db->sql_query($sql_count);
$result_count = (int) $this->db->sql_fetchfield('result_count');
if (!$result_count)
@@ -767,7 +914,7 @@ class fulltext_postgres extends \phpbb\search\base
// destroy too old cached search results
$this->destroy_cache(array());
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
}
/**
@@ -793,9 +940,9 @@ class fulltext_postgres extends \phpbb\search\base
$this->db->sql_query("CREATE INDEX " . POSTS_TABLE . "_" . $this->config['fulltext_postgres_ts_name'] . "_post_subject ON " . POSTS_TABLE . " USING gin (to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', post_subject))");
}
- if (!isset($this->stats['post_text']))
+ if (!isset($this->stats['post_content']))
{
- $this->db->sql_query("CREATE INDEX " . POSTS_TABLE . "_" . $this->config['fulltext_postgres_ts_name'] . "_post_text ON " . POSTS_TABLE . " USING gin (to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', post_text))");
+ $this->db->sql_query("CREATE INDEX " . POSTS_TABLE . "_" . $this->config['fulltext_postgres_ts_name'] . "_post_content ON " . POSTS_TABLE . " USING gin (to_tsvector ('" . $this->db->sql_escape($this->config['fulltext_postgres_ts_name']) . "', post_text || ' ' || post_subject))");
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
@@ -826,9 +973,9 @@ class fulltext_postgres extends \phpbb\search\base
$this->db->sql_query('DROP INDEX ' . $this->stats['post_subject']['relname']);
}
- if (isset($this->stats['post_text']))
+ if (isset($this->stats['post_content']))
{
- $this->db->sql_query('DROP INDEX ' . $this->stats['post_text']['relname']);
+ $this->db->sql_query('DROP INDEX ' . $this->stats['post_content']['relname']);
}
$this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
@@ -846,7 +993,7 @@ class fulltext_postgres extends \phpbb\search\base
$this->get_stats();
}
- return (isset($this->stats['post_text']) && isset($this->stats['post_subject'])) ? true : false;
+ return (isset($this->stats['post_subject']) && isset($this->stats['post_content'])) ? true : false;
}
/**
@@ -869,7 +1016,7 @@ class fulltext_postgres extends \phpbb\search\base
*/
protected function get_stats()
{
- if ($this->db->sql_layer != 'postgres')
+ if ($this->db->get_sql_layer() != 'postgres')
{
$this->stats = array();
return;
@@ -888,13 +1035,13 @@ class fulltext_postgres extends \phpbb\search\base
// deal with older PostgreSQL versions which didn't use Index_type
if (strpos($row['indexdef'], 'to_tsvector') !== false)
{
- if ($row['relname'] == POSTS_TABLE . '_' . $this->config['fulltext_postgres_ts_name'] . '_post_text' || $row['relname'] == POSTS_TABLE . '_post_text')
+ if ($row['relname'] == POSTS_TABLE . '_' . $this->config['fulltext_postgres_ts_name'] . '_post_subject' || $row['relname'] == POSTS_TABLE . '_post_subject')
{
- $this->stats['post_text'] = $row;
+ $this->stats['post_subject'] = $row;
}
- else if ($row['relname'] == POSTS_TABLE . '_' . $this->config['fulltext_postgres_ts_name'] . '_post_subject' || $row['relname'] == POSTS_TABLE . '_post_subject')
+ else if ($row['relname'] == POSTS_TABLE . '_' . $this->config['fulltext_postgres_ts_name'] . '_post_content' || $row['relname'] == POSTS_TABLE . '_post_content')
{
- $this->stats['post_subject'] = $row;
+ $this->stats['post_content'] = $row;
}
}
}
@@ -913,13 +1060,13 @@ class fulltext_postgres extends \phpbb\search\base
$tpl = '
<dl>
<dt><label>' . $this->user->lang['FULLTEXT_POSTGRES_VERSION_CHECK'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_VERSION_CHECK_EXPLAIN'] . '</span></dt>
- <dd>' . (($this->tsearch_usable) ? $this->user->lang['YES'] : $this->user->lang['NO']) . ' (PostgreSQL ' . $this->version . ')</dd>
+ <dd>' . (($this->db->get_sql_layer() == 'postgres') ? $this->user->lang['YES'] : $this->user->lang['NO']) . '</dd>
</dl>
<dl>
<dt><label>' . $this->user->lang['FULLTEXT_POSTGRES_TS_NAME'] . '</label><br /><span>' . $this->user->lang['FULLTEXT_POSTGRES_TS_NAME_EXPLAIN'] . '</span></dt>
<dd><select name="config[fulltext_postgres_ts_name]">';
- if ($this->db->sql_layer == 'postgres' && $this->tsearch_usable)
+ if ($this->db->get_sql_layer() == 'postgres')
{
$sql = 'SELECT cfgname AS ts_name
FROM pg_ts_config';
diff --git a/phpBB/phpbb/search/fulltext_sphinx.php b/phpBB/phpbb/search/fulltext_sphinx.php
index acbfad9474..504065e8cd 100644
--- a/phpBB/phpbb/search/fulltext_sphinx.php
+++ b/phpBB/phpbb/search/fulltext_sphinx.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,9 +18,7 @@ define('SPHINX_CONNECT_RETRIES', 3);
define('SPHINX_CONNECT_WAIT_TIME', 300);
/**
-* fulltext_sphinx
* Fulltext search based on the sphinx search deamon
-* @package search
*/
class fulltext_sphinx
{
@@ -77,13 +79,13 @@ class fulltext_sphinx
/**
* Database connection
- * @var \phpbb\db\driver\driver
+ * @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
* Database Tools object
- * @var \phpbb\db\tools
+ * @var \phpbb\db\tools\tools_interface
*/
protected $db_tools;
@@ -94,6 +96,12 @@ class fulltext_sphinx
protected $dbtype;
/**
+ * phpBB event dispatcher object
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $phpbb_dispatcher;
+
+ /**
* User object
* @var \phpbb\user
*/
@@ -117,22 +125,31 @@ class fulltext_sphinx
* Creates a new \phpbb\search\fulltext_postgres, which is used as a search backend
*
* @param string|bool $error Any error that occurs is passed on through this reference variable otherwise false
+ * @param string $phpbb_root_path Relative path to phpBB root
+ * @param string $phpEx PHP file extension
+ * @param \phpbb\auth\auth $auth Auth object
+ * @param \phpbb\config\config $config Config object
+ * @param \phpbb\db\driver\driver_interface Database object
+ * @param \phpbb\user $user User object
+ * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object
*/
- public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user)
+ public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $phpEx;
$this->config = $config;
+ $this->phpbb_dispatcher = $phpbb_dispatcher;
$this->user = $user;
$this->db = $db;
$this->auth = $auth;
- // Initialize \phpbb\db\tools object
- $this->db_tools = new \phpbb\db\tools($this->db);
+ // Initialize \phpbb\db\tools\tools object
+ global $phpbb_container; // TODO inject into object
+ $this->db_tools = $phpbb_container->get('dbal.tools');
- if(!$this->config['fulltext_sphinx_id'])
+ if (!$this->config['fulltext_sphinx_id'])
{
- set_config('fulltext_sphinx_id', unique_id());
+ $this->config->set('fulltext_sphinx_id', unique_id());
}
$this->id = $this->config['fulltext_sphinx_id'];
$this->indexes = 'index_phpbb_' . $this->id . '_delta;index_phpbb_' . $this->id . '_main';
@@ -197,13 +214,13 @@ class fulltext_sphinx
*/
public function init()
{
- if ($this->db->sql_layer != 'mysql' && $this->db->sql_layer != 'mysql4' && $this->db->sql_layer != 'mysqli' && $this->db->sql_layer != 'postgres')
+ if ($this->db->get_sql_layer() != 'mysql' && $this->db->get_sql_layer() != 'mysql4' && $this->db->get_sql_layer() != 'mysqli' && $this->db->get_sql_layer() != 'postgres')
{
return $this->user->lang['FULLTEXT_SPHINX_WRONG_DATABASE'];
}
// Move delta to main index each hour
- set_config('search_gc', 3600);
+ $this->config->set('search_gc', 3600);
return false;
}
@@ -216,11 +233,11 @@ class fulltext_sphinx
protected function config_generate()
{
// Check if Database is supported by Sphinx
- if ($this->db->sql_layer =='mysql' || $this->db->sql_layer == 'mysql4' || $this->db->sql_layer == 'mysqli')
+ if ($this->db->get_sql_layer() =='mysql' || $this->db->get_sql_layer() == 'mysql4' || $this->db->get_sql_layer() == 'mysqli')
{
$this->dbtype = 'mysql';
}
- else if ($this->db->sql_layer == 'postgres')
+ else if ($this->db->get_sql_layer() == 'postgres')
{
$this->dbtype = 'pgsql';
}
@@ -248,8 +265,8 @@ class fulltext_sphinx
array('type', $this->dbtype . ' # mysql or pgsql'),
// This config value sql_host needs to be changed incase sphinx and sql are on different servers
array('sql_host', $dbhost . ' # SQL server host sphinx connects to'),
- array('sql_user', $dbuser),
- array('sql_pass', $dbpasswd),
+ array('sql_user', '[dbuser]'),
+ array('sql_pass', '[dbpassword]'),
array('sql_db', $dbname),
array('sql_port', $dbport . ' # optional, default is 3306 for mysql and 5432 for pgsql'),
array('sql_query_pre', 'SET NAMES \'utf8\''),
@@ -282,9 +299,9 @@ class fulltext_sphinx
array('sql_attr_uint', 'post_visibility'),
array('sql_attr_bool', 'topic_first_post'),
array('sql_attr_bool', 'deleted'),
- array('sql_attr_timestamp' , 'post_time'),
- array('sql_attr_timestamp' , 'topic_last_post_time'),
- array('sql_attr_str2ordinal', 'post_subject'),
+ array('sql_attr_timestamp', 'post_time'),
+ array('sql_attr_timestamp', 'topic_last_post_time'),
+ array('sql_attr_string', 'post_subject'),
),
'source source_phpbb_' . $this->id . '_delta : source_phpbb_' . $this->id . '_main' => array(
array('sql_query_pre', ''),
@@ -342,6 +359,23 @@ class fulltext_sphinx
$non_unique = array('sql_query_pre' => true, 'sql_attr_uint' => true, 'sql_attr_timestamp' => true, 'sql_attr_str2ordinal' => true, 'sql_attr_bool' => true);
$delete = array('sql_group_column' => true, 'sql_date_column' => true, 'sql_str2ordinal_column' => true);
+
+ /**
+ * Allow adding/changing the Sphinx configuration data
+ *
+ * @event core.search_sphinx_modify_config_data
+ * @var array config_data Array with the Sphinx configuration data
+ * @var array non_unique Array with the Sphinx non-unique variables to delete
+ * @var array delete Array with the Sphinx variables to delete
+ * @since 3.1.7-RC1
+ */
+ $vars = array(
+ 'config_data',
+ 'non_unique',
+ 'delete',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_sphinx_modify_config_data', compact($vars)));
+
foreach ($config_data as $section_name => $section_data)
{
$section = $config_object->get_section_by_name($section_name);
@@ -370,7 +404,7 @@ class fulltext_sphinx
$variable = $section->get_variable_by_name($key);
if (!$variable)
{
- $variable = $section->create_variable($key, $value);
+ $section->create_variable($key, $value);
}
else
{
@@ -379,7 +413,7 @@ class fulltext_sphinx
}
else
{
- $variable = $section->create_variable($key, $value);
+ $section->create_variable($key, $value);
}
}
}
@@ -403,7 +437,6 @@ class fulltext_sphinx
$match = array('#\sand\s#i', '#\sor\s#i', '#\snot\s#i', '#\+#', '#-#', '#\|#', '#@#');
$replace = array(' & ', ' | ', ' - ', ' +', ' -', ' |', '');
- $replacements = 0;
$keywords = preg_replace($match, $replace, $keywords);
$this->sphinx->SetMatchMode(SPH_MATCH_EXTENDED);
}
@@ -446,6 +479,8 @@ class fulltext_sphinx
*/
public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $post_visibility, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
+ global $user, $phpbb_log;
+
// No keywords? No posts.
if (!strlen($this->search_query) && !sizeof($author_ary))
{
@@ -454,8 +489,6 @@ class fulltext_sphinx
$id_ary = array();
- $join_topic = ($type != 'posts');
-
// Sorting
if ($type == 'topics')
@@ -515,6 +548,41 @@ class fulltext_sphinx
$this->sphinx->SetFilter('topic_id', array($topic_id));
}
+ /**
+ * Allow modifying the Sphinx search options
+ *
+ * @event core.search_sphinx_keywords_modify_options
+ * @var string type Searching type ('posts', 'topics')
+ * @var string fields Searching fields ('titleonly', 'msgonly', 'firstpost', 'all')
+ * @var string terms Searching terms ('all', 'any')
+ * @var int sort_days Time, in days, of the oldest possible post to list
+ * @var string sort_key The sort type used from the possible sort types
+ * @var int topic_id Limit the search to this topic_id only
+ * @var array ex_fid_ary Which forums not to search on
+ * @var string post_visibility Post visibility data
+ * @var array author_ary Array of user_id containing the users to filter the results to
+ * @var string author_name The username to search on
+ * @var object sphinx The Sphinx searchd client object
+ * @since 3.1.7-RC1
+ */
+ $sphinx = $this->sphinx;
+ $vars = array(
+ 'type',
+ 'fields',
+ 'terms',
+ 'sort_days',
+ 'sort_key',
+ 'topic_id',
+ 'ex_fid_ary',
+ 'post_visibility',
+ 'author_ary',
+ 'author_name',
+ 'sphinx',
+ );
+ extract($this->phpbb_dispatcher->trigger_event('core.search_sphinx_keywords_modify_options', compact($vars)));
+ $this->sphinx = $sphinx;
+ unset($sphinx);
+
$search_query_prefix = '';
switch ($fields)
@@ -593,7 +661,7 @@ class fulltext_sphinx
if ($this->sphinx->GetLastError())
{
- add_log('critical', 'LOG_SPHINX_ERROR', $this->sphinx->GetLastError());
+ $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_SPHINX_ERROR', false, array($this->sphinx->GetLastError()));
if ($this->auth->acl_get('a_'))
{
trigger_error($this->user->lang('SPHINX_SEARCH_FAILED', $this->sphinx->GetLastError()));
@@ -691,7 +759,7 @@ class fulltext_sphinx
{
if ($mode == 'edit')
{
- $this->sphinx->UpdateAttributes($this->indexes, array('forum_id', 'poster_id'), array((int)$post_id => array((int)$forum_id, (int)$poster_id)));
+ $this->sphinx->UpdateAttributes($this->indexes, array('forum_id', 'poster_id'), array((int) $post_id => array((int) $forum_id, (int) $poster_id)));
}
else if ($mode != 'post' && $post_id)
{
@@ -707,6 +775,7 @@ class fulltext_sphinx
),
'ON' => 'p1.topic_id = p2.topic_id',
)),
+ 'WHERE' => 'p2.post_id = ' . ((int) $post_id),
);
$sql = $this->db->sql_build_query('SELECT', $sql_array);
@@ -716,7 +785,7 @@ class fulltext_sphinx
$post_time = time();
while ($row = $this->db->sql_fetchrow($result))
{
- $post_updates[(int)$row['post_id']] = array($post_time);
+ $post_updates[(int) $row['post_id']] = array($post_time);
}
$this->db->sql_freeresult($result);
@@ -746,7 +815,7 @@ class fulltext_sphinx
*/
public function tidy($create = false)
{
- set_config('search_last_gc', time(), true);
+ $this->config->set('search_last_gc', time(), false);
}
/**
diff --git a/phpBB/phpbb/search/sphinx/config.php b/phpBB/phpbb/search/sphinx/config.php
index cb8e4524df..675649b460 100644
--- a/phpBB/phpbb/search/sphinx/config.php
+++ b/phpBB/phpbb/search/sphinx/config.php
@@ -1,19 +1,21 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\search\sphinx;
/**
-* \phpbb\search\sphinx\config
* An object representing the sphinx configuration
* Can read it from file and write it back out after modification
-* @package search
*/
class config
{
diff --git a/phpBB/phpbb/search/sphinx/config_comment.php b/phpBB/phpbb/search/sphinx/config_comment.php
index 20b1c19af1..b5cd0a3db5 100644
--- a/phpBB/phpbb/search/sphinx/config_comment.php
+++ b/phpBB/phpbb/search/sphinx/config_comment.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/search/sphinx/config_section.php b/phpBB/phpbb/search/sphinx/config_section.php
index 8f9253ec56..14ab3a752c 100644
--- a/phpBB/phpbb/search/sphinx/config_section.php
+++ b/phpBB/phpbb/search/sphinx/config_section.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/search/sphinx/config_variable.php b/phpBB/phpbb/search/sphinx/config_variable.php
index c0f6d28dcc..85cee20b62 100644
--- a/phpBB/phpbb/search/sphinx/config_variable.php
+++ b/phpBB/phpbb/search/sphinx/config_variable.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package search
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php
index f530d30f1f..3f7146c59b 100644
--- a/phpBB/phpbb/session.php
+++ b/phpBB/phpbb/session.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,6 @@ namespace phpbb;
/**
* Session class
-* @package phpBB3
*/
class session
{
@@ -28,10 +31,11 @@ class session
var $update_session_page = true;
/**
- * Extract current session page
- *
- * @param string $root_path current root path (phpbb_root_path)
- */
+ * Extract current session page
+ *
+ * @param string $root_path current root path (phpbb_root_path)
+ * @return array
+ */
static function extract_current_page($root_path)
{
global $request, $symfony_request, $phpbb_filesystem;
@@ -39,8 +43,8 @@ class session
$page_array = array();
// First of all, get the request uri...
- $script_name = $symfony_request->getScriptName();
- $args = explode('&', $symfony_request->getQueryString());
+ $script_name = $request->escape($symfony_request->getScriptName(), true);
+ $args = $request->escape(explode('&', $symfony_request->getQueryString()), true);
// If we are unable to get the script name we use REQUEST_URI as a failover and note it within the page array for easier support...
if (!$script_name)
@@ -58,8 +62,8 @@ class session
// Since some browser do not encode correctly we need to do this with some "special" characters...
// " -> %22, ' => %27, < -> %3C, > -> %3E
- $find = array('"', "'", '<', '>');
- $replace = array('%22', '%27', '%3C', '%3E');
+ $find = array('"', "'", '<', '>', '&quot;', '&lt;', '&gt;');
+ $replace = array('%22', '%27', '%3C', '%3E', '%22', '%3C', '%3E');
foreach ($args as $key => $argument)
{
@@ -84,12 +88,12 @@ class session
$symfony_request_path = $phpbb_filesystem->clean_path($symfony_request->getPathInfo());
if ($symfony_request_path !== '/')
{
- $page_name .= $symfony_request_path;
+ $page_name .= str_replace('%2F', '/', urlencode($symfony_request_path));
}
// current directory within the phpBB root (for example: adm)
- $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path)));
- $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
+ $root_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath($root_path)));
+ $page_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath('./')));
$intersection = array_intersect_assoc($root_dirs, $page_dirs);
$root_dirs = array_diff_assoc($root_dirs, $intersection);
@@ -126,6 +130,10 @@ class session
$script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/';
$root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/';
+ $forum_id = $request->variable('f', 0);
+ // maximum forum id value is maximum value of mediumint unsigned column
+ $forum_id = ($forum_id > 0 && $forum_id < 16777215) ? $forum_id : 0;
+
$page_array += array(
'page_name' => $page_name,
'page_dir' => $page_dir,
@@ -135,7 +143,7 @@ class session
'root_script_path' => str_replace(' ', '%20', htmlspecialchars($root_script_path)),
'page' => $page,
- 'forum' => request_var('f', 0),
+ 'forum' => $forum_id,
);
return $page_array;
@@ -211,7 +219,7 @@ class session
function session_begin($update_session_page = true)
{
global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path;
- global $request, $phpbb_container;
+ global $request, $phpbb_container, $user, $phpbb_log;
// Give us some basic information
$this->time_now = time();
@@ -249,23 +257,23 @@ class session
if ($request->is_set($config['cookie_name'] . '_sid', \phpbb\request\request_interface::COOKIE) || $request->is_set($config['cookie_name'] . '_u', \phpbb\request\request_interface::COOKIE))
{
- $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
- $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
- $this->session_id = request_var($config['cookie_name'] . '_sid', '', false, true);
+ $this->cookie_data['u'] = $request->variable($config['cookie_name'] . '_u', 0, false, \phpbb\request\request_interface::COOKIE);
+ $this->cookie_data['k'] = $request->variable($config['cookie_name'] . '_k', '', false, \phpbb\request\request_interface::COOKIE);
+ $this->session_id = $request->variable($config['cookie_name'] . '_sid', '', false, \phpbb\request\request_interface::COOKIE);
$SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid=';
$_SID = (defined('NEED_SID')) ? $this->session_id : '';
if (empty($this->session_id))
{
- $this->session_id = $_SID = request_var('sid', '');
+ $this->session_id = $_SID = $request->variable('sid', '');
$SID = '?sid=' . $this->session_id;
$this->cookie_data = array('u' => 0, 'k' => '');
}
}
else
{
- $this->session_id = $_SID = request_var('sid', '');
+ $this->session_id = $_SID = $request->variable('sid', '');
$SID = '?sid=' . $this->session_id;
}
@@ -341,8 +349,8 @@ class session
}
else
{
- set_config('limit_load', '0');
- set_config('limit_search_load', '0');
+ $config->set('limit_load', '0');
+ $config->set('limit_search_load', '0');
}
}
@@ -405,9 +413,9 @@ class session
$session_expired = false;
// Check whether the session is still valid if we have one
- $method = basename(trim($config['auth_method']));
-
- $provider = $phpbb_container->get('auth.provider.' . $method);
+ /* @var $provider_collection \phpbb\auth\provider_collection */
+ $provider_collection = $phpbb_container->get('auth.provider_collection');
+ $provider = $provider_collection->get_provider();
if (!($provider instanceof \phpbb\auth\provider\provider_interface))
{
@@ -439,38 +447,6 @@ class session
if (!$session_expired)
{
- // Only update session DB a minute or so after last update or if page changes
- if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
- {
- $sql_ary = array('session_time' => $this->time_now);
-
- if ($this->update_session_page)
- {
- $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
- $sql_ary['session_forum_id'] = $this->page['forum'];
- }
-
- $db->sql_return_on_error(true);
-
- $this->update_session($sql_ary);
-
- $db->sql_return_on_error(false);
-
- // If the database is not yet updated, there will be an error due to the session_forum_id
- // @todo REMOVE for 3.0.2
- if ($result === false)
- {
- unset($sql_ary['session_forum_id']);
-
- $this->update_session($sql_ary);
- }
-
- if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts'])
- {
- $this->leave_newly_registered();
- }
- }
-
$this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
$this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false;
$this->data['user_lang'] = basename($this->data['user_lang']);
@@ -485,11 +461,18 @@ class session
{
if ($referer_valid)
{
- add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for));
+ $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_IP_BROWSER_FORWARDED_CHECK', false, array(
+ $u_ip,
+ $s_ip,
+ $u_browser,
+ $s_browser,
+ htmlspecialchars($u_forwarded_for),
+ htmlspecialchars($s_forwarded_for)
+ ));
}
else
{
- add_log('critical', 'LOG_REFERER_INVALID', $this->referer);
+ $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_REFERER_INVALID', false, array($this->referer));
}
}
}
@@ -511,7 +494,7 @@ class session
*/
function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
{
- global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx, $phpbb_container;
+ global $SID, $_SID, $db, $config, $cache, $phpbb_container, $phpbb_dispatcher;
$this->data = array();
@@ -574,11 +557,16 @@ class session
}
}
- $method = basename(trim($config['auth_method']));
-
- $provider = $phpbb_container->get('auth.provider.' . $method);
+ /* @var $provider_collection \phpbb\auth\provider_collection */
+ $provider_collection = $phpbb_container->get('auth.provider_collection');
+ $provider = $provider_collection->get_provider();
$this->data = $provider->autologin();
+ if ($user_id !== false && sizeof($this->data) && $this->data['user_id'] != $user_id)
+ {
+ $this->data = array();
+ }
+
if (sizeof($this->data))
{
$this->cookie_data['k'] = '';
@@ -596,11 +584,18 @@ class session
AND k.user_id = u.user_id
AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
$result = $db->sql_query($sql);
- $this->data = $db->sql_fetchrow($result);
+ $user_data = $db->sql_fetchrow($result);
+
+ if ($user_id === false || (isset($user_data['user_id']) && $user_id == $user_data['user_id']))
+ {
+ $this->data = $user_data;
+ $bot = false;
+ }
+
$db->sql_freeresult($result);
- $bot = false;
}
- else if ($user_id !== false && !sizeof($this->data))
+
+ if ($user_id !== false && !sizeof($this->data))
{
$this->cookie_data['k'] = '';
$this->cookie_data['u'] = $user_id;
@@ -715,18 +710,6 @@ class session
// Only update session DB a minute or so after last update or if page changes
if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
{
- $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
-
- $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0);
-
- if ($this->update_session_page)
- {
- $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
- $sql_ary['session_forum_id'] = $this->page['forum'];
- }
-
- $this->update_session($sql_ary);
-
// Update the last visit time
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_lastvisit = ' . (int) $this->data['session_time'] . '
@@ -874,6 +857,19 @@ class session
$_SID = '';
}
+ $session_data = $sql_ary;
+ /**
+ * Event to send new session data to extension
+ * Read-only event
+ *
+ * @event core.session_create_after
+ * @var array session_data Associative array of session keys to be updated
+ * @since 3.1.6-RC1
+ */
+ $vars = array('session_data');
+ extract($phpbb_dispatcher->trigger_event('core.session_create_after', compact($vars)));
+ unset($session_data);
+
return true;
}
@@ -887,17 +883,34 @@ class session
*/
function session_kill($new_session = true)
{
- global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx, $phpbb_container;
+ global $SID, $_SID, $db, $phpbb_container, $phpbb_dispatcher;
$sql = 'DELETE FROM ' . SESSIONS_TABLE . "
WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
AND session_user_id = " . (int) $this->data['user_id'];
$db->sql_query($sql);
- // Allow connecting logout with external auth method logout
- $method = basename(trim($config['auth_method']));
+ $user_id = (int) $this->data['user_id'];
+ $session_id = $this->session_id;
+ /**
+ * Event to send session kill information to extension
+ * Read-only event
+ *
+ * @event core.session_kill_after
+ * @var int user_id user_id of the session user.
+ * @var string session_id current user's session_id
+ * @var bool new_session should we create new session for user
+ * @since 3.1.6-RC1
+ */
+ $vars = array('user_id', 'session_id', 'new_session');
+ extract($phpbb_dispatcher->trigger_event('core.session_kill_after', compact($vars)));
+ unset($user_id);
+ unset($session_id);
- $provider = $phpbb_container->get('auth.provider.' . $method);
+ // Allow connecting logout with external auth method logout
+ /* @var $provider_collection \phpbb\auth\provider_collection */
+ $provider_collection = $phpbb_container->get('auth.provider_collection');
+ $provider = $provider_collection->get_provider();
$provider->logout($this->data, $new_session);
if ($this->data['user_id'] != ANONYMOUS)
@@ -962,7 +975,7 @@ class session
*/
function session_gc()
{
- global $db, $config, $phpbb_root_path, $phpEx;
+ global $db, $config, $phpbb_container, $phpbb_dispatcher;
$batch_size = 10;
@@ -1012,7 +1025,7 @@ class session
{
// Less than 10 users, update gc timer ... else we want gc
// called again to delete other sessions
- set_config('session_last_gc', $this->time_now, true);
+ $config->set('session_last_gc', $this->time_now, false);
if ($config['max_autologin_time'])
{
@@ -1022,11 +1035,8 @@ class session
}
// only called from CRON; should be a safe workaround until the infrastructure gets going
- if (!class_exists('phpbb_captcha_factory', false))
- {
- include($phpbb_root_path . "includes/captcha/captcha_factory." . $phpEx);
- }
- $captcha_factory = new \phpbb_captcha_factory();
+ /* @var $captcha_factory \phpbb\captcha\factory */
+ $captcha_factory = $phpbb_container->get('captcha.factory');
$captcha_factory->garbage_collect($config['captcha_plugin']);
$sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
@@ -1034,6 +1044,14 @@ class session
$db->sql_query($sql);
}
+ /**
+ * Event to trigger extension on session_gc
+ *
+ * @event core.session_gc_after
+ * @since 3.1.6-RC1
+ */
+ $phpbb_dispatcher->dispatch('core.session_gc_after');
+
return;
}
@@ -1045,33 +1063,42 @@ class session
* @param string $name Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.
* @param string $cookiedata The data to hold within the cookie
* @param int $cookietime The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set.
+ * @param bool $httponly Use HttpOnly. Defaults to true. Use false to make cookie accessible by client-side scripts.
*/
- function set_cookie($name, $cookiedata, $cookietime)
+ function set_cookie($name, $cookiedata, $cookietime, $httponly = true)
{
global $config;
+ // If headers are already set, we just return
+ if (headers_sent())
+ {
+ return;
+ }
+
$name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
$expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
- $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
+ $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == '127.0.0.1' || strpos($config['cookie_domain'], '.') === false) ? '' : '; domain=' . $config['cookie_domain'];
- header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
+ header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . ';' . (($httponly) ? ' HttpOnly' : ''), false);
}
/**
* Check for banned user
*
* Checks whether the supplied user is banned by id, ip or email. If no parameters
- * are passed to the method pre-existing session data is used. If $return is false
- * this routine does not return on finding a banned user, it outputs a relevant
- * message and stops execution.
+ * are passed to the method pre-existing session data is used.
*
- * @param string|array $user_ips Can contain a string with one IP or an array of multiple IPs
+ * @param int|false $user_id The user id
+ * @param mixed $user_ips Can contain a string with one IP or an array of multiple IPs
+ * @param string|false $user_email The user email
+ * @param bool $return If $return is false this routine does not return on finding a banned user,
+ * it outputs a relevant message and stops execution.
*/
function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
{
- global $config, $db;
+ global $config, $db, $phpbb_dispatcher;
- if (defined('IN_CHECK_BAN'))
+ if (defined('IN_CHECK_BAN') || defined('SKIP_CHECK_BAN'))
{
return;
}
@@ -1183,9 +1210,23 @@ class session
}
$db->sql_freeresult($result);
+ /**
+ * Event to set custom ban type
+ *
+ * @event core.session_set_custom_ban
+ * @var bool return If $return is false this routine does not return on finding a banned user, it outputs a relevant message and stops execution
+ * @var bool banned Check if user already banned
+ * @var array|false ban_row Ban data
+ * @var string ban_triggered_by Method that caused ban, can be your custom method
+ * @since 3.1.3-RC1
+ */
+ $ban_row = isset($ban_row) ? $ban_row : false;
+ $vars = array('return', 'banned', 'ban_row', 'ban_triggered_by');
+ extract($phpbb_dispatcher->trigger_event('core.session_set_custom_ban', compact($vars)));
+
if ($banned && !$return)
{
- global $template;
+ global $phpbb_root_path, $phpEx;
// If the session is empty we need to create a valid one...
if (empty($this->session_id))
@@ -1206,8 +1247,6 @@ class session
// We show a login box here to allow founders accessing the board if banned by IP
if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)
{
- global $phpEx;
-
$this->setup('ucp');
$this->data['is_registered'] = $this->data['is_bot'] = false;
@@ -1231,7 +1270,8 @@ class session
$till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';
$message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
- $message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . $config['board_contact'] . '">', '</a>');
+ $contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx);
+ $message = sprintf($this->lang[$message], $till_date, '<a href="' . $contact_link . '">', '</a>');
$message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';
$message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>';
@@ -1254,12 +1294,14 @@ class session
/**
* Check if ip is blacklisted
- * This should be called only where absolutly necessary
+ * This should be called only where absolutely necessary
*
* Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
*
* @author satmd (from the php manual)
- * @param string $mode register/post - spamcop for example is ommitted for posting
+ * @param string $mode register/post - spamcop for example is ommitted for posting
+ * @param string|false $ip the IPv4 address to check
+ *
* @return false if ip is not blacklisted, else an array([checked server], [lookup])
*/
function check_dnsbl($mode, $ip = false)
@@ -1361,7 +1403,7 @@ class session
*/
function set_login_key($user_id = false, $key = false, $user_ip = false)
{
- global $config, $db;
+ global $db;
$user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
$user_ip = ($user_ip === false) ? $this->ip : $user_ip;
@@ -1371,7 +1413,7 @@ class session
$sql_ary = array(
'key_id' => (string) md5($key_id),
- 'last_ip' => (string) $this->ip,
+ 'last_ip' => (string) $user_ip,
'last_login' => (int) time()
);
@@ -1408,7 +1450,7 @@ class session
*/
function reset_login_keys($user_id = false)
{
- global $config, $db;
+ global $db;
$user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id;
@@ -1509,12 +1551,59 @@ class session
*/
public function update_session($session_data, $session_id = null)
{
- global $db;
+ global $db, $phpbb_dispatcher;
$session_id = ($session_id) ? $session_id : $this->session_id;
$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $session_data) . "
WHERE session_id = '" . $db->sql_escape($session_id) . "'";
$db->sql_query($sql);
+
+ /**
+ * Event to send update session information to extension
+ * Read-only event
+ *
+ * @event core.update_session_after
+ * @var array session_data Associative array of session keys to be updated
+ * @var string session_id current user's session_id
+ * @since 3.1.6-RC1
+ */
+ $vars = array('session_data', 'session_id');
+ extract($phpbb_dispatcher->trigger_event('core.update_session_after', compact($vars)));
+ }
+
+ public function update_session_infos()
+ {
+ global $config, $db, $request;
+
+ // No need to update if it's a new session. Informations are already inserted by session_create()
+ if (isset($this->data['session_created']) && $this->data['session_created'])
+ {
+ return;
+ }
+
+ // Only update session DB a minute or so after last update or if page changes
+ if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
+ {
+ $sql_ary = array('session_time' => $this->time_now);
+
+ // Do not update the session page for ajax requests, so the view online still works as intended
+ if ($this->update_session_page && !$request->is_ajax())
+ {
+ $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
+ $sql_ary['session_forum_id'] = $this->page['forum'];
+ }
+
+ $db->sql_return_on_error(true);
+
+ $this->update_session($sql_ary);
+
+ $db->sql_return_on_error(false);
+
+ if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts'])
+ {
+ $this->leave_newly_registered();
+ }
+ }
}
}
diff --git a/phpBB/phpbb/symfony_request.php b/phpBB/phpbb/symfony_request.php
index ebe862a565..2931cae3cc 100644
--- a/phpBB/phpbb/symfony_request.php
+++ b/phpBB/phpbb/symfony_request.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,30 +15,25 @@ namespace phpbb;
use Symfony\Component\HttpFoundation\Request;
+/**
+ * WARNING: The Symfony request does not escape the input and should be used very carefully
+ * prefer the phpbb request as possible
+ */
class symfony_request extends Request
{
/**
* Constructor
*
- * @param phpbb\request\request_interface $phpbb_request
+ * @param \phpbb\request\request_interface $phpbb_request
*/
public function __construct(\phpbb\request\request_interface $phpbb_request)
{
- // This function is meant to sanitize the global input arrays
- $sanitizer = function(&$value, $key) {
- $type_cast_helper = new \phpbb\request\type_cast_helper();
- $type_cast_helper->set_var($value, $value, gettype($value), true);
- };
-
$get_parameters = $phpbb_request->get_super_global(\phpbb\request\request_interface::GET);
$post_parameters = $phpbb_request->get_super_global(\phpbb\request\request_interface::POST);
$server_parameters = $phpbb_request->get_super_global(\phpbb\request\request_interface::SERVER);
$files_parameters = $phpbb_request->get_super_global(\phpbb\request\request_interface::FILES);
$cookie_parameters = $phpbb_request->get_super_global(\phpbb\request\request_interface::COOKIE);
- array_walk_recursive($get_parameters, $sanitizer);
- array_walk_recursive($post_parameters, $sanitizer);
-
parent::__construct($get_parameters, $post_parameters, array(), $cookie_parameters, $files_parameters, $server_parameters);
}
}
diff --git a/phpBB/phpbb/template/asset.php b/phpBB/phpbb/template/asset.php
index 24e0d6698d..cb00f16549 100644
--- a/phpBB/phpbb/template/asset.php
+++ b/phpBB/phpbb/template/asset.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -16,14 +20,20 @@ class asset
/** @var \phpbb\path_helper **/
protected $path_helper;
+ /** @var \phpbb\filesystem\filesystem */
+ protected $filesystem;
+
/**
* Constructor
*
* @param string $url URL
+ * @param \phpbb\path_helper $path_helper Path helper object
+ * @param \phpbb\filesystem\filesystem $filesystem
*/
- public function __construct($url, \phpbb\path_helper $path_helper)
+ public function __construct($url, \phpbb\path_helper $path_helper, \phpbb\filesystem\filesystem $filesystem)
{
$this->path_helper = $path_helper;
+ $this->filesystem = $filesystem;
$this->set_url($url);
}
@@ -147,6 +157,24 @@ class asset
*/
public function set_path($path, $urlencode = false)
{
+ // Since 1.7.0 Twig returns the real path of the file. We need it to be relative.
+ $real_root_path = $this->filesystem->realpath($this->path_helper->get_phpbb_root_path()) . DIRECTORY_SEPARATOR;
+
+ // If the asset is under the phpBB root path we need to remove its path and then prepend $phpbb_root_path
+ if ($real_root_path && substr($path . DIRECTORY_SEPARATOR, 0, strlen($real_root_path)) === $real_root_path)
+ {
+ $path = $this->path_helper->get_phpbb_root_path() . str_replace('\\', '/', substr($path, strlen($real_root_path)));
+ }
+ else
+ {
+ // Else we make the path relative to the current working directory
+ $real_root_path = $this->filesystem->realpath('.') . DIRECTORY_SEPARATOR;
+ if ($real_root_path && substr($path . DIRECTORY_SEPARATOR, 0, strlen($real_root_path)) === $real_root_path)
+ {
+ $path = str_replace('\\', '/', substr($path, strlen($real_root_path)));
+ }
+ }
+
if ($urlencode)
{
$paths = explode('/', $path);
@@ -156,6 +184,7 @@ class asset
}
$path = implode('/', $paths);
}
+
$this->components['path'] = $path;
}
diff --git a/phpBB/phpbb/template/base.php b/phpBB/phpbb/template/base.php
index 6044effa1f..9a40702ba8 100644
--- a/phpBB/phpbb/template/base.php
+++ b/phpBB/phpbb/template/base.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -113,6 +117,16 @@ abstract class base implements template
/**
* {@inheritdoc}
*/
+ public function assign_block_vars_array($blockname, array $block_vars_array)
+ {
+ $this->context->assign_block_vars_array($blockname, $block_vars_array);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert')
{
return $this->context->alter_block_array($blockname, $vararray, $key, $mode);
@@ -128,11 +142,11 @@ abstract class base implements template
{
global $phpbb_hook;
- if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this))
+ if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array('template', $method), $handle, $this))
{
- if ($phpbb_hook->hook_return(array(__CLASS__, $method)))
+ if ($phpbb_hook->hook_return(array('template', $method)))
{
- $result = $phpbb_hook->hook_return_result(array(__CLASS__, $method));
+ $result = $phpbb_hook->hook_return_result(array('template', $method));
return array($result);
}
}
diff --git a/phpBB/phpbb/template/context.php b/phpBB/phpbb/template/context.php
index 65c7d094a0..4ee48205c8 100644
--- a/phpBB/phpbb/template/context.php
+++ b/phpBB/phpbb/template/context.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2011 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,8 +15,6 @@ namespace phpbb\template;
/**
* Stores variables assigned to template.
-*
-* @package phpBB3
*/
class context
{
@@ -32,6 +34,11 @@ class context
*/
private $rootref;
+ /**
+ * @var bool
+ */
+ private $num_rows_is_set;
+
public function __construct()
{
$this->clear();
@@ -44,6 +51,7 @@ class context
{
$this->tpldata = array('.' => array(0 => array()));
$this->rootref = &$this->tpldata['.'][0];
+ $this->num_rows_is_set = false;
}
/**
@@ -53,6 +61,7 @@ class context
*
* @param string $varname Variable name
* @param string $varval Value to assign to variable
+ * @return true
*/
public function assign_var($varname, $varval)
{
@@ -68,6 +77,7 @@ class context
*
* @param string $varname Variable name
* @param string $varval Value to append to variable
+ * @return true
*/
public function append_var($varname, $varval)
{
@@ -91,10 +101,59 @@ class context
// returning a reference directly is not
// something php is capable of doing
$ref = &$this->tpldata;
+
+ if (!$this->num_rows_is_set)
+ {
+ /*
+ * We do not set S_NUM_ROWS while adding a row, to reduce the complexity
+ * If we would set it on adding, each subsequent adding would cause
+ * n modifications, resulting in a O(n!) complexity, rather then O(n)
+ */
+ foreach ($ref as $loop_name => &$loop_data)
+ {
+ if ($loop_name === '.')
+ {
+ continue;
+ }
+
+ $this->set_num_rows($loop_data);
+ }
+ $this->num_rows_is_set = true;
+ }
+
return $ref;
}
/**
+ * Set S_NUM_ROWS for each row in this template block
+ *
+ * @param array $loop_data
+ */
+ protected function set_num_rows(&$loop_data)
+ {
+ $s_num_rows = sizeof($loop_data);
+ foreach ($loop_data as &$mod_block)
+ {
+ foreach ($mod_block as $sub_block_name => &$sub_block)
+ {
+ // If the key name is lowercase and the data is an array,
+ // it could be a template loop. So we set the S_NUM_ROWS there
+ // aswell.
+ if ($sub_block_name === strtolower($sub_block_name) && is_array($sub_block))
+ {
+ $this->set_num_rows($sub_block);
+ }
+ }
+
+ // Check whether we are inside a block before setting the variable
+ if (isset($mod_block['S_BLOCK_NAME']))
+ {
+ $mod_block['S_NUM_ROWS'] = $s_num_rows;
+ }
+ }
+ }
+
+ /**
* Returns a reference to template root scope.
*
* This function is public so that template renderer may invoke it.
@@ -115,9 +174,11 @@ class context
*
* @param string $blockname Name of block to assign $vararray to
* @param array $vararray A hash of variable name => value pairs
+ * @return true
*/
public function assign_block_vars($blockname, array $vararray)
{
+ $this->num_rows_is_set = false;
if (strpos($blockname, '.') !== false)
{
// Nested block.
@@ -155,12 +216,6 @@ class context
// We're adding a new iteration to this block with the given
// variable assignments.
$str[$blocks[$blockcount]][] = $vararray;
-
- // Set S_NUM_ROWS
- foreach ($str[$blocks[$blockcount]] as &$mod_block)
- {
- $mod_block['S_NUM_ROWS'] = sizeof($str[$blocks[$blockcount]]);
- }
}
else
{
@@ -186,12 +241,23 @@ class context
// Add a new iteration to this block with the variable assignments we were given.
$this->tpldata[$blockname][] = $vararray;
+ }
- // Set S_NUM_ROWS
- foreach ($this->tpldata[$blockname] as &$mod_block)
- {
- $mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]);
- }
+ return true;
+ }
+
+ /**
+ * Assign key variable pairs from an array to a whole specified block loop
+ *
+ * @param string $blockname Name of block to assign $block_vars_array to
+ * @param array $block_vars_array An array of hashes of variable name => value pairs
+ * @return true
+ */
+ public function assign_block_vars_array($blockname, array $block_vars_array)
+ {
+ foreach ($block_vars_array as $vararray)
+ {
+ $this->assign_block_vars($blockname, $vararray);
}
return true;
@@ -226,6 +292,7 @@ class context
*/
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert')
{
+ $this->num_rows_is_set = false;
if (strpos($blockname, '.') !== false)
{
// Nested block.
@@ -325,12 +392,6 @@ class context
$block[$key] = $vararray;
$block[$key]['S_ROW_COUNT'] = $block[$key]['S_ROW_NUM'] = $key;
- // Set S_NUM_ROWS
- foreach ($this->tpldata[$blockname] as &$mod_block)
- {
- $mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]);
- }
-
return true;
}
@@ -354,9 +415,11 @@ class context
* Reset/empty complete block
*
* @param string $blockname Name of block to destroy
+ * @return true
*/
public function destroy_block_vars($blockname)
{
+ $this->num_rows_is_set = false;
if (strpos($blockname, '.') !== false)
{
// Nested block.
diff --git a/phpBB/phpbb/template/exception/user_object_not_available.php b/phpBB/phpbb/template/exception/user_object_not_available.php
new file mode 100644
index 0000000000..62fd2743c1
--- /dev/null
+++ b/phpBB/phpbb/template/exception/user_object_not_available.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\template\exception;
+
+/**
+ * This exception is thrown when the user object was not set but it is required by the called method
+ */
+class user_object_not_available extends \phpbb\exception\runtime_exception
+{
+
+}
diff --git a/phpBB/phpbb/template/template.php b/phpBB/phpbb/template/template.php
index d95b0a822c..041ecb12e4 100644
--- a/phpBB/phpbb/template/template.php
+++ b/phpBB/phpbb/template/template.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -132,6 +136,14 @@ interface template
public function assign_block_vars($blockname, array $vararray);
/**
+ * Assign key variable pairs from an array to a whole specified block loop
+ * @param string $blockname Name of block to assign $block_vars_array to
+ * @param array $block_vars_array An array of hashes of variable name => value pairs
+ * @return \phpbb\template\template $this
+ */
+ public function assign_block_vars_array($blockname, array $block_vars_array);
+
+ /**
* Change already assigned key variable pair (one-dimensional - single loop entry)
*
* An example of how to use this function:
@@ -163,6 +175,7 @@ interface template
/**
* Get path to template for handle (required for BBCode parser)
*
+ * @param string $handle Handle to retrieve the source file
* @return string
*/
public function get_source_file_for_handle($handle);
diff --git a/phpBB/phpbb/template/twig/definition.php b/phpBB/phpbb/template/twig/definition.php
index 945c46675e..cb3c953692 100644
--- a/phpBB/phpbb/template/twig/definition.php
+++ b/phpBB/phpbb/template/twig/definition.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -21,6 +25,8 @@ class definition
* Get a DEFINE'd variable
*
* @param string $name
+ * @param array $arguments
+ *
* @return mixed Null if not found
*/
public function __call($name, $arguments)
diff --git a/phpBB/phpbb/template/twig/environment.php b/phpBB/phpbb/template/twig/environment.php
index aa55f1e011..6e75403159 100644
--- a/phpBB/phpbb/template/twig/environment.php
+++ b/phpBB/phpbb/template/twig/environment.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,9 +18,15 @@ class environment extends \Twig_Environment
/** @var \phpbb\config\config */
protected $phpbb_config;
+ /** @var \phpbb\filesystem\filesystem */
+ protected $filesystem;
+
/** @var \phpbb\path_helper */
protected $phpbb_path_helper;
+ /** @var \Symfony\Component\DependencyInjection\ContainerInterface */
+ protected $container;
+
/** @var \phpbb\extension\manager */
protected $extension_manager;
@@ -32,27 +42,53 @@ class environment extends \Twig_Environment
/**
* Constructor
*
- * @param \phpbb\config\config $phpbb_config
- * @param \phpbb\path_helper
- * @param \phpbb\extension\manager
- * @param string $phpbb_root_path
- * @param Twig_LoaderInterface $loader
+ * @param \phpbb\config\config $phpbb_config The phpBB configuration
+ * @param \phpbb\filesystem\filesystem $filesystem
+ * @param \phpbb\path_helper $path_helper phpBB path helper
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface $container The dependency injection container
+ * @param string $cache_path The path to the cache directory
+ * @param \phpbb\extension\manager $extension_manager phpBB extension manager
+ * @param \Twig_LoaderInterface $loader Twig loader interface
* @param array $options Array of options to pass to Twig
*/
- public function __construct($phpbb_config, \phpbb\path_helper $path_helper, \phpbb\extension\manager $extension_manager = null, \Twig_LoaderInterface $loader = null, $options = array())
+ public function __construct(\phpbb\config\config $phpbb_config, \phpbb\filesystem\filesystem $filesystem, \phpbb\path_helper $path_helper, \Symfony\Component\DependencyInjection\ContainerInterface $container, $cache_path, \phpbb\extension\manager $extension_manager = null, \Twig_LoaderInterface $loader = null, $options = array())
{
$this->phpbb_config = $phpbb_config;
+ $this->filesystem = $filesystem;
$this->phpbb_path_helper = $path_helper;
$this->extension_manager = $extension_manager;
+ $this->container = $container;
$this->phpbb_root_path = $this->phpbb_path_helper->get_phpbb_root_path();
$this->web_root_path = $this->phpbb_path_helper->get_web_root_path();
+ $options = array_merge(array(
+ 'cache' => (defined('IN_INSTALL')) ? false : $cache_path,
+ 'debug' => false,
+ 'auto_reload' => (bool) $this->phpbb_config['load_tplcompile'],
+ 'autoescape' => false,
+ ), $options);
+
return parent::__construct($loader, $options);
}
/**
+ * {@inheritdoc}
+ */
+ public function getLexer()
+ {
+ if (null === $this->lexer)
+ {
+ $this->lexer = $this->container->get('template.twig.lexer');
+ $this->lexer->set_environment($this);
+ }
+
+ return $this->lexer;
+ }
+
+
+ /**
* Get the list of enabled phpBB extensions
*
* Used in EVENT node
@@ -75,16 +111,26 @@ class environment extends \Twig_Environment
}
/**
- * Get the phpBB root path
- *
- * @return string
- */
+ * Get the phpBB root path
+ *
+ * @return string
+ */
public function get_phpbb_root_path()
{
return $this->phpbb_root_path;
}
/**
+ * Get the filesystem object
+ *
+ * @return \phpbb\filesystem\filesystem
+ */
+ public function get_filesystem()
+ {
+ return $this->filesystem;
+ }
+
+ /**
* Get the web root path
*
* @return string
@@ -118,7 +164,7 @@ class environment extends \Twig_Environment
* Set the namespace look up order to load templates from
*
* @param array $namespace
- * @return Twig_Environment
+ * @return \Twig_Environment
*/
public function setNamespaceLookUpOrder($namespace)
{
@@ -128,12 +174,13 @@ class environment extends \Twig_Environment
}
/**
- * Loads a template by name.
- *
- * @param string $name The template name
- * @param integer $index The index if it is an embedded template
- * @return Twig_TemplateInterface A template instance representing the given template name
- */
+ * Loads a template by name.
+ *
+ * @param string $name The template name
+ * @param integer $index The index if it is an embedded template
+ * @return \Twig_TemplateInterface A template instance representing the given template name
+ * @throws \Twig_Error_Loader
+ */
public function loadTemplate($name, $index = null)
{
if (strpos($name, '@') === false)
@@ -164,11 +211,12 @@ class environment extends \Twig_Environment
}
/**
- * Finds a template by name.
- *
- * @param string $name The template name
- * @return string
- */
+ * Finds a template by name.
+ *
+ * @param string $name The template name
+ * @return string
+ * @throws \Twig_Error_Loader
+ */
public function findTemplate($name)
{
if (strpos($name, '@') === false)
@@ -184,7 +232,7 @@ class environment extends \Twig_Environment
return parent::getLoader()->getCacheKey('@' . $namespace . '/' . $name);
}
- catch (Twig_Error_Loader $e)
+ catch (\Twig_Error_Loader $e)
{
}
}
diff --git a/phpBB/phpbb/template/twig/extension.php b/phpBB/phpbb/template/twig/extension.php
index 6847dbd9f8..92f87a0331 100644
--- a/phpBB/phpbb/template/twig/extension.php
+++ b/phpBB/phpbb/template/twig/extension.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,20 +18,20 @@ class extension extends \Twig_Extension
/** @var \phpbb\template\context */
protected $context;
- /** @var \phpbb\user */
- protected $user;
+ /** @var \phpbb\language\language */
+ protected $language;
/**
* Constructor
*
* @param \phpbb\template\context $context
- * @param \phpbb\user $user
+ * @param \phpbb\language\language $language
* @return \phpbb\template\twig\extension
*/
- public function __construct(\phpbb\template\context $context, $user)
+ public function __construct(\phpbb\template\context $context, $language)
{
$this->context = $context;
- $this->user = $user;
+ $this->language = $language;
}
/**
@@ -67,6 +71,7 @@ class extension extends \Twig_Extension
{
return array(
new \Twig_SimpleFilter('subset', array($this, 'loop_subset'), array('needs_environment' => true)),
+ // @deprecated 3.2.0 Uses twig's JS escape method instead of addslashes
new \Twig_SimpleFilter('addslashes', 'addslashes'),
);
}
@@ -123,7 +128,7 @@ class extension extends \Twig_Extension
/**
* Grabs a subset of a loop
*
- * @param Twig_Environment $env A Twig_Environment instance
+ * @param \Twig_Environment $env A Twig_Environment instance
* @param mixed $item A variable
* @param integer $start Start of the subset
* @param integer $end End of the subset
@@ -158,7 +163,6 @@ class extension extends \Twig_Extension
* (e.g. in the ACP, L_TITLE)
* If not, we return the result of $user->lang()
*
- * @param string $lang name
* @return string
*/
function lang()
@@ -174,9 +178,9 @@ class extension extends \Twig_Extension
return $context_vars['L_' . $key];
}
- // LA_ is transformed into lang(\'$1\')|addslashes, so we should not
+ // LA_ is transformed into lang(\'$1\')|escape('js'), so we should not
// need to check for it
- return call_user_func_array(array($this->user, 'lang'), $args);
+ return call_user_func_array(array($this->language, 'lang'), $args);
}
}
diff --git a/phpBB/phpbb/template/twig/extension/routing.php b/phpBB/phpbb/template/twig/extension/routing.php
new file mode 100644
index 0000000000..829ce738eb
--- /dev/null
+++ b/phpBB/phpbb/template/twig/extension/routing.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\template\twig\extension;
+
+use Symfony\Bridge\Twig\Extension\RoutingExtension;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+
+class routing extends RoutingExtension
+{
+ /** @var \phpbb\controller\helper */
+ protected $helper;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\routing\helper $helper
+ */
+ public function __construct(\phpbb\routing\helper $helper)
+ {
+ $this->helper = $helper;
+ }
+
+ public function getPath($name, $parameters = array(), $relative = false)
+ {
+ return $this->helper->route($name, $parameters, true, false, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH);
+ }
+
+ public function getUrl($name, $parameters = array(), $schemeRelative = false)
+ {
+ return $this->helper->route($name, $parameters, true, false, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL);
+ }
+}
diff --git a/phpBB/phpbb/template/twig/lexer.php b/phpBB/phpbb/template/twig/lexer.php
index f4efc58540..f1542109a4 100644
--- a/phpBB/phpbb/template/twig/lexer.php
+++ b/phpBB/phpbb/template/twig/lexer.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,6 +15,11 @@ namespace phpbb\template\twig;
class lexer extends \Twig_Lexer
{
+ public function set_environment(\Twig_Environment $env)
+ {
+ $this->env = $env;
+ }
+
public function tokenize($code, $filename = null)
{
// Our phpBB tags
@@ -108,9 +117,9 @@ class lexer extends \Twig_Lexer
// Appends any filters after lang()
$code = preg_replace('#{L_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2 }}', $code);
- // Replace all of our escaped language variables, {LA_VARNAME}, with Twig style, {{ lang('NAME')|addslashes }}
- // Appends any filters after lang(), but before addslashes
- $code = preg_replace('#{LA_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2|addslashes }}', $code);
+ // Replace all of our escaped language variables, {LA_VARNAME}, with Twig style, {{ lang('NAME')|escape('js') }}
+ // Appends any filters after lang(), but before escape('js')
+ $code = preg_replace('#{LA_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2|escape(\'js\') }}', $code);
// Replace all of our variables, {VARNAME}, with Twig style, {{ VARNAME }}
// Appends any filters
@@ -191,9 +200,16 @@ class lexer extends \Twig_Lexer
$parent_class = $this;
$callback = function ($matches) use ($parent_class, $parent_nodes)
{
- $name = $matches[1];
- $subset = trim(substr($matches[2], 1, -1)); // Remove parenthesis
- $body = $matches[3];
+ $hard_parents = explode('.', $matches[1]);
+ array_pop($hard_parents); // ends with .
+ if ($hard_parents)
+ {
+ $parent_nodes = array_merge($hard_parents, $parent_nodes);
+ }
+
+ $name = $matches[2];
+ $subset = trim(substr($matches[3], 1, -1)); // Remove parenthesis
+ $body = $matches[4];
// Replace <!-- BEGINELSE -->
$body = str_replace('<!-- BEGINELSE -->', '{% else %}', $body);
@@ -242,7 +258,7 @@ class lexer extends \Twig_Lexer
return "{% for {$name} in {$parent}{$name}{$subset} %}{$body}{% endfor %}";
};
- return preg_replace_callback('#<!-- BEGIN ([!a-zA-Z0-9_]+)(\([0-9,\-]+\))? -->(.+?)<!-- END \1 -->#s', $callback, $code);
+ return preg_replace_callback('#<!-- BEGIN ((?:[a-zA-Z0-9_]+\.)*)([!a-zA-Z0-9_]+)(\([0-9,\-]+\))? -->(.+?)<!-- END \1\2 -->#s', $callback, $code);
}
/**
@@ -274,7 +290,7 @@ class lexer extends \Twig_Lexer
return "<!-- {$matches[1]}IF{$inner}-->";
};
- return preg_replace_callback('#<!-- (ELSE)?IF((.*?) \(*!?[\$|\.]([^\s]+)(.*?))-->#', $callback, $code);
+ return preg_replace_callback('#<!-- (ELSE)?IF((.*?) (?:\(*!?[\$|\.]([^\s]+)(.*?))?)-->#', $callback, $code);
}
/**
diff --git a/phpBB/phpbb/template/twig/loader.php b/phpBB/phpbb/template/twig/loader.php
index e01e9de467..8b12188a77 100644
--- a/phpBB/phpbb/template/twig/loader.php
+++ b/phpBB/phpbb/template/twig/loader.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,17 +15,34 @@ namespace phpbb\template\twig;
/**
* Twig Template loader
-* @package phpBB3
*/
class loader extends \Twig_Loader_Filesystem
{
protected $safe_directories = array();
/**
+ * @var \phpbb\filesystem\filesystem_interface
+ */
+ protected $filesystem;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\filesystem\filesystem_interface $filesystem
+ * @param string|array $paths
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $paths = array())
+ {
+ $this->filesystem = $filesystem;
+
+ parent::__construct($paths);
+ }
+
+ /**
* Set safe directories
*
* @param array $directories Array of directories that are safe (empty to clear)
- * @return Twig_Loader_Filesystem
+ * @return \Twig_Loader_Filesystem
*/
public function setSafeDirectories($directories = array())
{
@@ -42,11 +63,11 @@ class loader extends \Twig_Loader_Filesystem
* Add safe directory
*
* @param string $directory Directory that should be added
- * @return Twig_Loader_Filesystem
+ * @return \Twig_Loader_Filesystem
*/
public function addSafeDirectory($directory)
{
- $directory = phpbb_realpath($directory);
+ $directory = $this->filesystem->realpath($directory);
if ($directory !== false)
{
@@ -94,7 +115,8 @@ class loader extends \Twig_Loader_Filesystem
// If this is in the cache we can skip the entire process below
// as it should have already been validated
- if (isset($this->cache[$name])) {
+ if (isset($this->cache[$name]))
+ {
return $this->cache[$name];
}
@@ -107,7 +129,7 @@ class loader extends \Twig_Loader_Filesystem
// Try validating the name (which may throw an exception)
parent::validateName($name);
}
- catch (Twig_Error_Loader $e)
+ catch (\Twig_Error_Loader $e)
{
if (strpos($e->getRawMessage(), 'Looks like you try to load a template outside configured directories') === 0)
{
@@ -115,7 +137,7 @@ class loader extends \Twig_Loader_Filesystem
// can now check if we're within a "safe" directory
// Find the real path of the directory the file is in
- $directory = phpbb_realpath(dirname($file));
+ $directory = $this->filesystem->realpath(dirname($file));
if ($directory === false)
{
diff --git a/phpBB/phpbb/template/twig/node/definenode.php b/phpBB/phpbb/template/twig/node/definenode.php
index 6a9969f8c6..ddbd151d20 100644
--- a/phpBB/phpbb/template/twig/node/definenode.php
+++ b/phpBB/phpbb/template/twig/node/definenode.php
@@ -1,15 +1,19 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @copyright Portions (c) 2009 Fabien Potencier, Armin Ronacher
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node;
-
class definenode extends \Twig_Node
{
public function __construct($capture, \Twig_NodeInterface $name, \Twig_NodeInterface $value, $lineno, $tag = null)
@@ -18,15 +22,16 @@ class definenode extends \Twig_Node
}
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
- if ($this->getAttribute('capture')) {
+ if ($this->getAttribute('capture'))
+ {
$compiler
->write("ob_start();\n")
->subcompile($this->getNode('value'))
diff --git a/phpBB/phpbb/template/twig/node/event.php b/phpBB/phpbb/template/twig/node/event.php
index 7a1181a866..11fdb75247 100644
--- a/phpBB/phpbb/template/twig/node/event.php
+++ b/phpBB/phpbb/template/twig/node/event.php
@@ -1,24 +1,27 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node;
-
class event extends \Twig_Node
{
/**
- * The subdirectory in which all template listener files must be placed
- * @var string
- */
+ * The subdirectory in which all template listener files must be placed
+ * @var string
+ */
protected $listener_directory = 'event/';
- /** @var Twig_Environment */
+ /** @var \Twig_Environment */
protected $environment;
public function __construct(\Twig_Node_Expression $expr, \phpbb\template\twig\environment $environment, $lineno, $tag = null)
@@ -29,10 +32,10 @@ class event extends \Twig_Node
}
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
@@ -43,7 +46,7 @@ class event extends \Twig_Node
{
$ext_namespace = str_replace('/', '_', $ext_namespace);
- if (defined('DEBUG'))
+ if ($this->environment->isDebug())
{
// If debug mode is enabled, lets check for new/removed EVENT
// templates on page load rather than at compile. This is
@@ -55,7 +58,7 @@ class event extends \Twig_Node
;
}
- if (defined('DEBUG') || $this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html'))
+ if ($this->environment->isDebug() || $this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html'))
{
$compiler
->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
@@ -67,7 +70,7 @@ class event extends \Twig_Node
;
}
- if (defined('DEBUG'))
+ if ($this->environment->isDebug())
{
$compiler
->outdent()
diff --git a/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php b/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php
index f3bbfa6691..2cd15d59da 100644
--- a/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php
+++ b/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php
@@ -1,15 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node\expression\binary;
-
class equalequal extends \Twig_Node_Expression_Binary
{
public function operator(\Twig_Compiler $compiler)
diff --git a/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php b/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php
index c9c2687e08..5f2908fb9b 100644
--- a/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php
+++ b/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php
@@ -1,15 +1,18 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node\expression\binary;
-
class notequalequal extends \Twig_Node_Expression_Binary
{
public function operator(\Twig_Compiler $compiler)
diff --git a/phpBB/phpbb/template/twig/node/includeasset.php b/phpBB/phpbb/template/twig/node/includeasset.php
index f6c9dc9c58..324823b8d7 100644
--- a/phpBB/phpbb/template/twig/node/includeasset.php
+++ b/phpBB/phpbb/template/twig/node/includeasset.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,7 @@ namespace phpbb\template\twig\node;
abstract class includeasset extends \Twig_Node
{
- /** @var Twig_Environment */
+ /** @var \Twig_Environment */
protected $environment;
public function __construct(\Twig_Node_Expression $expr, \phpbb\template\twig\environment $environment, $lineno, $tag = null)
@@ -21,10 +25,10 @@ abstract class includeasset extends \Twig_Node
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
@@ -35,7 +39,7 @@ abstract class includeasset extends \Twig_Node
->write("\$asset_file = ")
->subcompile($this->getNode('expr'))
->raw(";\n")
- ->write("\$asset = new \phpbb\\template\\asset(\$asset_file, \$this->getEnvironment()->get_path_helper());\n")
+ ->write("\$asset = new \phpbb\\template\\asset(\$asset_file, \$this->getEnvironment()->get_path_helper(), \$this->getEnvironment()->get_filesystem());\n")
->write("if (substr(\$asset_file, 0, 2) !== './' && \$asset->is_relative()) {\n")
->indent()
->write("\$asset_path = \$asset->get_path();")
@@ -70,7 +74,7 @@ abstract class includeasset extends \Twig_Node
/**
* Append the output code for the asset
*
- * @param Twig_Compiler A Twig_Compiler instance
+ * @param \Twig_Compiler A Twig_Compiler instance
* @return null
*/
abstract protected function append_asset(\Twig_Compiler $compiler);
diff --git a/phpBB/phpbb/template/twig/node/includecss.php b/phpBB/phpbb/template/twig/node/includecss.php
index deb279fa4a..2dac154036 100644
--- a/phpBB/phpbb/template/twig/node/includecss.php
+++ b/phpBB/phpbb/template/twig/node/includecss.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -27,7 +31,7 @@ class includecss extends \phpbb\template\twig\node\includeasset
$compiler
->raw("<link href=\"' . ")
->raw("\$asset_file . '\"")
- ->raw(' rel="stylesheet" type="text/css" media="screen, projection" />')
+ ->raw(' rel="stylesheet" type="text/css" media="screen" />')
;
}
}
diff --git a/phpBB/phpbb/template/twig/node/includejs.php b/phpBB/phpbb/template/twig/node/includejs.php
index 696b640eac..e77f2afeed 100644
--- a/phpBB/phpbb/template/twig/node/includejs.php
+++ b/phpBB/phpbb/template/twig/node/includejs.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -24,8 +28,6 @@ class includejs extends \phpbb\template\twig\node\includeasset
*/
protected function append_asset(\Twig_Compiler $compiler)
{
- $config = $this->environment->get_phpbb_config();
-
$compiler
->raw("<script type=\"text/javascript\" src=\"' . ")
->raw("\$asset_file")
diff --git a/phpBB/phpbb/template/twig/node/includenode.php b/phpBB/phpbb/template/twig/node/includenode.php
index d9b45d6407..c36ac3c324 100644
--- a/phpBB/phpbb/template/twig/node/includenode.php
+++ b/phpBB/phpbb/template/twig/node/includenode.php
@@ -1,22 +1,25 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node;
-
class includenode extends \Twig_Node_Include
{
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
diff --git a/phpBB/phpbb/template/twig/node/includephp.php b/phpBB/phpbb/template/twig/node/includephp.php
index 3f4621c0a9..76182c2f84 100644
--- a/phpBB/phpbb/template/twig/node/includephp.php
+++ b/phpBB/phpbb/template/twig/node/includephp.php
@@ -1,18 +1,22 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* Sections (c) 2009 Fabien Potencier, Armin Ronacher
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node;
-
class includephp extends \Twig_Node
{
- /** @var Twig_Environment */
+ /** @var \Twig_Environment */
protected $environment;
public function __construct(\Twig_Node_Expression $expr, \phpbb\template\twig\environment $environment, $lineno, $ignoreMissing = false, $tag = null)
@@ -23,10 +27,10 @@ class includephp extends \Twig_Node
}
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
@@ -42,7 +46,8 @@ class includephp extends \Twig_Node
return;
}
- if ($this->getAttribute('ignore_missing')) {
+ if ($this->getAttribute('ignore_missing'))
+ {
$compiler
->write("try {\n")
->indent()
@@ -71,7 +76,8 @@ class includephp extends \Twig_Node
->write("}\n")
;
- if ($this->getAttribute('ignore_missing')) {
+ if ($this->getAttribute('ignore_missing'))
+ {
$compiler
->outdent()
->write("} catch (\Twig_Error_Loader \$e) {\n")
diff --git a/phpBB/phpbb/template/twig/node/php.php b/phpBB/phpbb/template/twig/node/php.php
index 2b18551266..4ee415e446 100644
--- a/phpBB/phpbb/template/twig/node/php.php
+++ b/phpBB/phpbb/template/twig/node/php.php
@@ -1,18 +1,21 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\node;
-
class php extends \Twig_Node
{
- /** @var Twig_Environment */
+ /** @var \Twig_Environment */
protected $environment;
public function __construct(\Twig_Node_Text $text, \phpbb\template\twig\environment $environment, $lineno, $tag = null)
@@ -23,10 +26,10 @@ class php extends \Twig_Node
}
/**
- * Compiles the node to PHP.
- *
- * @param Twig_Compiler A Twig_Compiler instance
- */
+ * Compiles the node to PHP.
+ *
+ * @param \Twig_Compiler A Twig_Compiler instance
+ */
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
diff --git a/phpBB/phpbb/template/twig/tokenparser/defineparser.php b/phpBB/phpbb/template/twig/tokenparser/defineparser.php
index 8484f2e81a..b755836ccd 100644
--- a/phpBB/phpbb/template/twig/tokenparser/defineparser.php
+++ b/phpBB/phpbb/template/twig/tokenparser/defineparser.php
@@ -1,24 +1,30 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @copyright Portions (c) 2009 Fabien Potencier, Armin Ronacher
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class defineparser extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ * @throws \Twig_Error_Syntax
+ * @throws \phpbb\template\twig\node\definenode
+ */
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
@@ -26,7 +32,8 @@ class defineparser extends \Twig_TokenParser
$name = $this->parser->getExpressionParser()->parseExpression();
$capture = false;
- if ($stream->test(\Twig_Token::OPERATOR_TYPE, '=')) {
+ if ($stream->test(\Twig_Token::OPERATOR_TYPE, '='))
+ {
$stream->next();
$value = $this->parser->getExpressionParser()->parseExpression();
@@ -38,7 +45,9 @@ class defineparser extends \Twig_TokenParser
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
- } else {
+ }
+ else
+ {
$capture = true;
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
@@ -56,10 +65,10 @@ class defineparser extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'DEFINE';
diff --git a/phpBB/phpbb/template/twig/tokenparser/event.php b/phpBB/phpbb/template/twig/tokenparser/event.php
index 8864e879f8..f73ef4ae25 100644
--- a/phpBB/phpbb/template/twig/tokenparser/event.php
+++ b/phpBB/phpbb/template/twig/tokenparser/event.php
@@ -1,24 +1,27 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class event extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
@@ -30,10 +33,10 @@ class event extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'EVENT';
diff --git a/phpBB/phpbb/template/twig/tokenparser/includecss.php b/phpBB/phpbb/template/twig/tokenparser/includecss.php
index 7bf4c610b1..1f30811754 100644
--- a/phpBB/phpbb/template/twig/tokenparser/includecss.php
+++ b/phpBB/phpbb/template/twig/tokenparser/includecss.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -12,12 +16,12 @@ namespace phpbb\template\twig\tokenparser;
class includecss extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
@@ -29,10 +33,10 @@ class includecss extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'INCLUDECSS';
diff --git a/phpBB/phpbb/template/twig/tokenparser/includejs.php b/phpBB/phpbb/template/twig/tokenparser/includejs.php
index 0e46915b86..4b67d2c468 100644
--- a/phpBB/phpbb/template/twig/tokenparser/includejs.php
+++ b/phpBB/phpbb/template/twig/tokenparser/includejs.php
@@ -1,24 +1,27 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class includejs extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
@@ -30,10 +33,10 @@ class includejs extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'INCLUDEJS';
diff --git a/phpBB/phpbb/template/twig/tokenparser/includeparser.php b/phpBB/phpbb/template/twig/tokenparser/includeparser.php
index d351f1b4cd..aa7236aaa6 100644
--- a/phpBB/phpbb/template/twig/tokenparser/includeparser.php
+++ b/phpBB/phpbb/template/twig/tokenparser/includeparser.php
@@ -1,24 +1,28 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @copyright Portions (c) 2009 Fabien Potencier, Armin Ronacher
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class includeparser extends \Twig_TokenParser_Include
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
@@ -29,10 +33,10 @@ class includeparser extends \Twig_TokenParser_Include
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'INCLUDE';
diff --git a/phpBB/phpbb/template/twig/tokenparser/includephp.php b/phpBB/phpbb/template/twig/tokenparser/includephp.php
index 1b3d1742e3..3992636f8c 100644
--- a/phpBB/phpbb/template/twig/tokenparser/includephp.php
+++ b/phpBB/phpbb/template/twig/tokenparser/includephp.php
@@ -1,24 +1,28 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @copyright Portions (c) 2009 Fabien Potencier, Armin Ronacher
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class includephp extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
@@ -26,7 +30,8 @@ class includephp extends \Twig_TokenParser
$stream = $this->parser->getStream();
$ignoreMissing = false;
- if ($stream->test(\Twig_Token::NAME_TYPE, 'ignore')) {
+ if ($stream->test(\Twig_Token::NAME_TYPE, 'ignore'))
+ {
$stream->next();
$stream->expect(\Twig_Token::NAME_TYPE, 'missing');
@@ -39,10 +44,10 @@ class includephp extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'INCLUDEPHP';
diff --git a/phpBB/phpbb/template/twig/tokenparser/php.php b/phpBB/phpbb/template/twig/tokenparser/php.php
index b427969e2d..f11ce35896 100644
--- a/phpBB/phpbb/template/twig/tokenparser/php.php
+++ b/phpBB/phpbb/template/twig/tokenparser/php.php
@@ -1,24 +1,27 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig\tokenparser;
-
class php extends \Twig_TokenParser
{
/**
- * Parses a token and returns a node.
- *
- * @param Twig_Token $token A Twig_Token instance
- *
- * @return Twig_NodeInterface A Twig_NodeInterface instance
- */
+ * Parses a token and returns a node.
+ *
+ * @param \Twig_Token $token A Twig_Token instance
+ *
+ * @return \Twig_NodeInterface A Twig_NodeInterface instance
+ */
public function parse(\Twig_Token $token)
{
$stream = $this->parser->getStream();
@@ -38,10 +41,10 @@ class php extends \Twig_TokenParser
}
/**
- * Gets the tag name associated with this token parser.
- *
- * @return string The tag name
- */
+ * Gets the tag name associated with this token parser.
+ *
+ * @return string The tag name
+ */
public function getTag()
{
return 'PHP';
diff --git a/phpBB/phpbb/template/twig/twig.php b/phpBB/phpbb/template/twig/twig.php
index 83630f5992..6b3cf32bc8 100644
--- a/phpBB/phpbb/template/twig/twig.php
+++ b/phpBB/phpbb/template/twig/twig.php
@@ -1,17 +1,22 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
namespace phpbb\template\twig;
+use phpbb\template\exception\user_object_not_available;
+
/**
* Twig Template class.
-* @package phpBB3
*/
class twig extends \phpbb\template\base
{
@@ -64,7 +69,7 @@ class twig extends \phpbb\template\base
/**
* Twig Environment
*
- * @var Twig_Environment
+ * @var \Twig_Environment
*/
protected $twig;
@@ -73,11 +78,14 @@ class twig extends \phpbb\template\base
*
* @param \phpbb\path_helper $path_helper
* @param \phpbb\config\config $config
- * @param \phpbb\user $user
* @param \phpbb\template\context $context template context
+ * @param \phpbb\template\twig\environment $twig_environment
+ * @param string $cache_path
+ * @param \phpbb\user|null $user
+ * @param array|\ArrayAccess $extensions
* @param \phpbb\extension\manager $extension_manager extension manager, if null then template events will not be invoked
*/
- public function __construct(\phpbb\path_helper $path_helper, $config, $user, \phpbb\template\context $context, \phpbb\extension\manager $extension_manager = null)
+ public function __construct(\phpbb\path_helper $path_helper, $config, \phpbb\template\context $context, \phpbb\template\twig\environment $twig_environment, $cache_path, \phpbb\user $user = null, $extensions = array(), \phpbb\extension\manager $extension_manager = null)
{
$this->path_helper = $path_helper;
$this->phpbb_root_path = $path_helper->get_phpbb_root_path();
@@ -86,35 +94,13 @@ class twig extends \phpbb\template\base
$this->user = $user;
$this->context = $context;
$this->extension_manager = $extension_manager;
+ $this->cachepath = $cache_path;
+ $this->twig = $twig_environment;
- $this->cachepath = $this->phpbb_root_path . 'cache/twig/';
-
- // Initiate the loader, __main__ namespace paths will be setup later in set_style_names()
- $loader = new \phpbb\template\twig\loader('');
-
- $this->twig = new \phpbb\template\twig\environment(
- $this->config,
- $this->path_helper,
- $this->extension_manager,
- $loader,
- array(
- 'cache' => (defined('IN_INSTALL')) ? false : $this->cachepath,
- 'debug' => defined('DEBUG'),
- 'auto_reload' => (bool) $this->config['load_tplcompile'],
- 'autoescape' => false,
- )
- );
-
- $this->twig->addExtension(
- new \phpbb\template\twig\extension(
- $this->context,
- $this->user
- )
- );
-
- $lexer = new \phpbb\template\twig\lexer($this->twig);
-
- $this->twig->setLexer($lexer);
+ foreach ($extensions as $extension)
+ {
+ $this->twig->addExtension($extension);
+ }
// Add admin namespace
if ($this->path_helper->get_adm_relative_path() !== null && is_dir($this->phpbb_root_path . $this->path_helper->get_adm_relative_path() . 'style/'))
@@ -142,9 +128,16 @@ class twig extends \phpbb\template\base
* Get the style tree of the style preferred by the current user
*
* @return array Style tree, most specific first
+ *
+ * @throws \phpbb\template\exception\user_object_not_available When user service was not set
*/
public function get_user_style()
{
+ if ($this->user === null)
+ {
+ throw new user_object_not_available();
+ }
+
$style_list = array(
$this->user->style['style_path'],
);
@@ -174,6 +167,10 @@ class twig extends \phpbb\template\base
}
$names = $this->get_user_style();
+ // Add 'all' folder to $names array
+ // It allows extensions to load a template file from 'all' folder,
+ // if a style doesn't include it.
+ $names[] = 'all';
$paths = array();
foreach ($style_directories as $directory)
@@ -182,13 +179,24 @@ class twig extends \phpbb\template\base
{
$path = $this->phpbb_root_path . trim($directory, '/') . "/{$name}/";
$template_path = $path . 'template/';
+ $theme_path = $path . 'theme/';
+ $is_valid_dir = false;
if (is_dir($template_path))
{
+ $is_valid_dir = true;
+ $paths[] = $template_path;
+ }
+ if (is_dir($theme_path))
+ {
+ $is_valid_dir = true;
+ $paths[] = $theme_path;
+ }
+
+ if ($is_valid_dir)
+ {
// Add the base style directory as a safe directory
$this->twig->getLoader()->addSafeDirectory($path);
-
- $paths[] = $template_path;
}
}
}
@@ -211,9 +219,13 @@ class twig extends \phpbb\template\base
*
* Note: Templates are still compiled to phpBB's cache directory.
*
- * @param string|array $names Array of names or string of name of template(s) in inheritance tree order, used by extensions.
- * @param string|array or string $paths Array of style paths, relative to current root directory
- * @return phpbb_template $this
+ * @param string|array $names Array of names (or detailed names) or string of name of template(s) in inheritance tree order, used by extensions.
+ * E.g. array(
+ * 'name' => 'adm',
+ * 'ext_path' => 'adm/style/',
+ * )
+ * @param string|array of string $paths Array of style paths, relative to current root directory
+ * @return \phpbb\template\template $this
*/
public function set_custom_style($names, $paths)
{
@@ -234,17 +246,46 @@ class twig extends \phpbb\template\base
$namespace = str_replace('/', '_', $ext_namespace);
$paths = array();
- foreach ($names as $style_name)
+ foreach ($names as $template_dir)
{
- $ext_style_path = $ext_path . 'styles/' . $style_name . '/';
- $ext_style_template_path = $ext_style_path . 'template/';
+ if (is_array($template_dir))
+ {
+ if (isset($template_dir['ext_path']))
+ {
+ $ext_style_template_path = $ext_path . $template_dir['ext_path'];
+ $ext_style_path = dirname($ext_style_template_path);
+ $ext_style_theme_path = $ext_style_path . 'theme/';
+ }
+ else
+ {
+ $ext_style_path = $ext_path . 'styles/' . $template_dir['name'] . '/';
+ $ext_style_template_path = $ext_style_path . 'template/';
+ $ext_style_theme_path = $ext_style_path . 'theme/';
+ }
+ }
+ else
+ {
+ $ext_style_path = $ext_path . 'styles/' . $template_dir . '/';
+ $ext_style_template_path = $ext_style_path . 'template/';
+ $ext_style_theme_path = $ext_style_path . 'theme/';
+ }
+ $is_valid_dir = false;
if (is_dir($ext_style_template_path))
{
+ $is_valid_dir = true;
+ $paths[] = $ext_style_template_path;
+ }
+ if (is_dir($ext_style_theme_path))
+ {
+ $is_valid_dir = true;
+ $paths[] = $ext_style_theme_path;
+ }
+
+ if ($is_valid_dir)
+ {
// Add the base style directory as a safe directory
$this->twig->getLoader()->addSafeDirectory($ext_style_path);
-
- $paths[] = $ext_style_template_path;
}
}
@@ -312,21 +353,29 @@ class twig extends \phpbb\template\base
$context_vars['.'][0], // To get normal vars
array(
'definition' => new \phpbb\template\twig\definition(),
- 'user' => $this->user,
'loops' => $context_vars, // To get loops
)
);
+ if ($this->user instanceof \phpbb\user)
+ {
+ $vars['user'] = $this->user;
+ }
+
// cleanup
unset($vars['loops']['.']);
+ // Inject in the main context the value added by assign_block_vars() to be able to use directly the Twig loops.
+ foreach ($vars['loops'] as $key => &$value)
+ {
+ $vars[$key] = $value;
+ }
+
return $vars;
}
/**
- * Get path to template for handle (required for BBCode parser)
- *
- * @return string
+ * {@inheritdoc}
*/
public function get_source_file_for_handle($handle)
{
diff --git a/phpBB/phpbb/textformatter/cache_interface.php b/phpBB/phpbb/textformatter/cache_interface.php
new file mode 100644
index 0000000000..f6b5f195c7
--- /dev/null
+++ b/phpBB/phpbb/textformatter/cache_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\textformatter;
+
+/**
+* Currently only used to signal that something that could effect the rendering has changed.
+* BBCodes, smilies, censored words, templates, etc...
+*/
+interface cache_interface
+{
+ /**
+ * Invalidate and/or regenerate this text formatter's cache(s)
+ */
+ public function invalidate();
+
+ /**
+ * Tidy/prune this text formatter's cache(s)
+ */
+ public function tidy();
+}
diff --git a/phpBB/phpbb/textformatter/data_access.php b/phpBB/phpbb/textformatter/data_access.php
new file mode 100644
index 0000000000..2103bf8e60
--- /dev/null
+++ b/phpBB/phpbb/textformatter/data_access.php
@@ -0,0 +1,228 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter;
+
+/**
+* Data access layer that fetchs BBCodes, smilies and censored words from the database.
+* To be extended to include insert/update/delete operations.
+*
+* Also used to get templates.
+*/
+class data_access
+{
+ /**
+ * @var string Name of the BBCodes table
+ */
+ protected $bbcodes_table;
+
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var string Name of the smilies table
+ */
+ protected $smilies_table;
+
+ /**
+ * @var string Name of the styles table
+ */
+ protected $styles_table;
+
+ /**
+ * @var string Path to the styles dir
+ */
+ protected $styles_path;
+
+ /**
+ * @var string Name of the words table
+ */
+ protected $words_table;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param string $bbcodes_table Name of the BBCodes table
+ * @param string $smilies_table Name of the smilies table
+ * @param string $styles_table Name of the styles table
+ * @param string $words_table Name of the words table
+ * @param string $styles_path Path to the styles dir
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, $bbcodes_table, $smilies_table, $styles_table, $words_table, $styles_path)
+ {
+ $this->db = $db;
+
+ $this->bbcodes_table = $bbcodes_table;
+ $this->smilies_table = $smilies_table;
+ $this->styles_table = $styles_table;
+ $this->words_table = $words_table;
+
+ $this->styles_path = $styles_path;
+ }
+
+ /**
+ * Return the list of custom BBCodes
+ *
+ * @return array
+ */
+ public function get_bbcodes()
+ {
+ $sql = 'SELECT bbcode_match, bbcode_tpl FROM ' . $this->bbcodes_table;
+ $result = $this->db->sql_query($sql);
+ $rows = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
+ * Return the list of smilies
+ *
+ * @return array
+ */
+ public function get_smilies()
+ {
+ // NOTE: smilies that are displayed on the posting page are processed first because they're
+ // typically the most used smilies and it ends up producing a slightly more efficient
+ // renderer
+ $sql = 'SELECT code, emotion, smiley_url, smiley_width, smiley_height
+ FROM ' . $this->smilies_table . '
+ ORDER BY display_on_posting DESC';
+ $result = $this->db->sql_query($sql);
+ $rows = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
+ * Return the list of installed styles
+ *
+ * @return array
+ */
+ protected function get_styles()
+ {
+ $sql = 'SELECT style_id, style_path, style_parent_id, bbcode_bitfield FROM ' . $this->styles_table;
+ $result = $this->db->sql_query($sql);
+ $rows = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
+ * Return the bbcode.html template for every installed style
+ *
+ * @return array 2D array. style_id as keys, each element is an array with a "template" element that contains the style's bbcode.html and a "bbcodes" element that contains the name of each BBCode that is to be stylised
+ */
+ public function get_styles_templates()
+ {
+ $templates = array();
+
+ $bbcode_ids = array(
+ 'quote' => 0,
+ 'b' => 1,
+ 'i' => 2,
+ 'url' => 3,
+ 'img' => 4,
+ 'size' => 5,
+ 'color' => 6,
+ 'u' => 7,
+ 'code' => 8,
+ 'list' => 9,
+ '*' => 9,
+ 'email' => 10,
+ 'flash' => 11,
+ 'attachment' => 12,
+ );
+
+ $styles = array();
+ foreach ($this->get_styles() as $row)
+ {
+ $styles[$row['style_id']] = $row;
+ }
+
+ foreach ($styles as $style_id => $style)
+ {
+ $bbcodes = array();
+
+ // Collect the name of the BBCodes whose bit is set in the style's bbcode_bitfield
+ $template_bitfield = new \bitfield($style['bbcode_bitfield']);
+ foreach ($bbcode_ids as $bbcode_name => $bit)
+ {
+ if ($template_bitfield->get($bit))
+ {
+ $bbcodes[] = $bbcode_name;
+ }
+ }
+
+ $filename = $this->resolve_style_filename($styles, $style);
+ if ($filename === false)
+ {
+ // Ignore this style, it will use the default templates
+ continue;
+ }
+
+ $templates[$style_id] = array(
+ 'bbcodes' => $bbcodes,
+ 'template' => file_get_contents($filename),
+ );
+ }
+
+ return $templates;
+ }
+
+ /**
+ * Resolve inheritance for given style and return the path to their bbcode.html file
+ *
+ * @param array $styles Associative array of [style_id => style] containing all styles
+ * @param array $style Style for which we resolve
+ * @return string|bool Path to this style's bbcode.html, or FALSE
+ */
+ protected function resolve_style_filename(array $styles, array $style)
+ {
+ // Look for a bbcode.html in this style's dir
+ $filename = $this->styles_path . $style['style_path'] . '/template/bbcode.html';
+ if (file_exists($filename))
+ {
+ return $filename;
+ }
+
+ // Resolve using this style's parent
+ $parent_id = $style['style_parent_id'];
+ if ($parent_id && !empty($styles[$parent_id]))
+ {
+ return $this->resolve_style_filename($styles, $styles[$parent_id]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the list of censored words
+ *
+ * @return array
+ */
+ public function get_censored_words()
+ {
+ $sql = 'SELECT word, replacement FROM ' . $this->words_table;
+ $result = $this->db->sql_query($sql);
+ $rows = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+}
diff --git a/phpBB/phpbb/textformatter/parser_interface.php b/phpBB/phpbb/textformatter/parser_interface.php
new file mode 100644
index 0000000000..ad611fb5b4
--- /dev/null
+++ b/phpBB/phpbb/textformatter/parser_interface.php
@@ -0,0 +1,112 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter;
+
+interface parser_interface
+{
+ /**
+ * Parse given text
+ *
+ * @param string $text
+ * @return string
+ */
+ public function parse($text);
+
+ /**
+ * Disable a specific BBCode
+ *
+ * @param string $name BBCode name
+ * @return null
+ */
+ public function disable_bbcode($name);
+
+ /**
+ * Disable BBCodes in general
+ */
+ public function disable_bbcodes();
+
+ /**
+ * Disable the censor
+ */
+ public function disable_censor();
+
+ /**
+ * Disable magic URLs
+ */
+ public function disable_magic_url();
+
+ /**
+ * Disable smilies
+ */
+ public function disable_smilies();
+
+ /**
+ * Enable a specific BBCode
+ *
+ * @param string $name BBCode name
+ * @return null
+ */
+ public function enable_bbcode($name);
+
+ /**
+ * Enable BBCodes in general
+ */
+ public function enable_bbcodes();
+
+ /**
+ * Enable the censor
+ */
+ public function enable_censor();
+
+ /**
+ * Enable magic URLs
+ */
+ public function enable_magic_url();
+
+ /**
+ * Enable smilies
+ */
+ public function enable_smilies();
+
+ /**
+ * Get the list of errors that were generated during last parsing
+ *
+ * @return array[] Array of arrays. Each array contains a lang string at index 0 plus any number
+ * of optional parameters
+ */
+ public function get_errors();
+
+ /**
+ * Set a variable to be used by the parser
+ *
+ * - max_font_size
+ * - max_img_height
+ * - max_img_width
+ * - max_smilies
+ * - max_urls
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return null
+ */
+ public function set_var($name, $value);
+
+ /**
+ * Set multiple variables to be used by the parser
+ *
+ * @param array $vars Associative array of [name => value]
+ * @return null
+ */
+ public function set_vars(array $vars);
+}
diff --git a/phpBB/phpbb/textformatter/renderer_interface.php b/phpBB/phpbb/textformatter/renderer_interface.php
new file mode 100644
index 0000000000..609b0bb642
--- /dev/null
+++ b/phpBB/phpbb/textformatter/renderer_interface.php
@@ -0,0 +1,92 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter;
+
+interface renderer_interface
+{
+ /**
+ * Render given text
+ *
+ * @param string $text Text, as parsed by something that implements \phpbb\textformatter\parser
+ * @return string
+ */
+ public function render($text);
+
+ /**
+ * Set the smilies' path
+ *
+ * @return null
+ */
+ public function set_smilies_path($path);
+
+ /**
+ * Return the value of the "viewcensors" option
+ *
+ * @return bool Option's value
+ */
+ public function get_viewcensors();
+
+ /**
+ * Return the value of the "viewflash" option
+ *
+ * @return bool Option's value
+ */
+ public function get_viewflash();
+
+ /**
+ * Return the value of the "viewimg" option
+ *
+ * @return bool Option's value
+ */
+ public function get_viewimg();
+
+ /**
+ * Return the value of the "viewsmilies" option
+ *
+ * @return bool Option's value
+ */
+ public function get_viewsmilies();
+
+ /**
+ * Set the "viewcensors" option
+ *
+ * @param bool $value Option's value
+ * @return null
+ */
+ public function set_viewcensors($value);
+
+ /**
+ * Set the "viewflash" option
+ *
+ * @param bool $value Option's value
+ * @return null
+ */
+ public function set_viewflash($value);
+
+ /**
+ * Set the "viewimg" option
+ *
+ * @param bool $value Option's value
+ * @return null
+ */
+ public function set_viewimg($value);
+
+ /**
+ * Set the "viewsmilies" option
+ *
+ * @param bool $value Option's value
+ * @return null
+ */
+ public function set_viewsmilies($value);
+}
diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php
new file mode 100644
index 0000000000..8c273c342e
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/factory.php
@@ -0,0 +1,617 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter\s9e;
+
+use s9e\TextFormatter\Configurator;
+use s9e\TextFormatter\Configurator\Items\AttributeFilters\RegexpFilter;
+use s9e\TextFormatter\Configurator\Items\UnsafeTemplate;
+
+/**
+* Creates s9e\TextFormatter objects
+*/
+class factory implements \phpbb\textformatter\cache_interface
+{
+ /**
+ * @var \phpbb\textformatter\s9e\link_helper
+ */
+ protected $link_helper;
+
+ /**
+ * @var \phpbb\cache\driver\driver_interface
+ */
+ protected $cache;
+
+ /**
+ * @var string Path to the cache dir
+ */
+ protected $cache_dir;
+
+ /**
+ * @var string Cache key used for the parser
+ */
+ protected $cache_key_parser;
+
+ /**
+ * @var string Cache key used for the renderer
+ */
+ protected $cache_key_renderer;
+
+ /**
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * @var array Custom tokens used in bbcode.html and their corresponding token from the definition
+ */
+ protected $custom_tokens = array(
+ 'email' => array('{DESCRIPTION}' => '{TEXT}'),
+ 'flash' => array('{WIDTH}' => '{NUMBER1}', '{HEIGHT}' => '{NUMBER2}'),
+ 'img' => array('{URL}' => '{IMAGEURL}'),
+ 'list' => array('{LIST_TYPE}' => '{HASHMAP}'),
+ 'quote' => array('{USERNAME}' => '{TEXT1}'),
+ 'size' => array('{SIZE}' => '{FONTSIZE}'),
+ 'url' => array('{DESCRIPTION}' => '{TEXT}'),
+ );
+
+ /**
+ * @var \phpbb\textformatter\data_access
+ */
+ protected $data_access;
+
+ /**
+ * @var array Default BBCode definitions
+ */
+ protected $default_definitions = array(
+ 'attachment' => '[ATTACHMENT index={NUMBER} filename={TEXT;useContent}]',
+ 'b' => '[B]{TEXT}[/B]',
+ 'code' => '[CODE lang={IDENTIFIER;optional}]{TEXT}[/CODE]',
+ 'color' => '[COLOR={COLOR}]{TEXT}[/COLOR]',
+ 'email' => '[EMAIL={EMAIL;useContent} subject={TEXT;optional;postFilter=rawurlencode} body={TEXT;optional;postFilter=rawurlencode}]{TEXT}[/EMAIL]',
+ 'flash' => '[FLASH={NUMBER1},{NUMBER2} width={NUMBER1;postFilter=#flashwidth} height={NUMBER2;postFilter=#flashheight} url={URL;useContent} /]',
+ 'i' => '[I]{TEXT}[/I]',
+ 'img' => '[IMG src={IMAGEURL;useContent}]',
+ 'list' => '[LIST type={HASHMAP=1:decimal,a:lower-alpha,A:upper-alpha,i:lower-roman,I:upper-roman;optional;postFilter=#simpletext}]{TEXT}[/LIST]',
+ 'li' => '[* $tagName=LI]{TEXT}[/*]',
+ 'quote' =>
+ "[QUOTE
+ author={TEXT1;optional}
+ post_id={UINT;optional}
+ post_url={URL;optional;postFilter=#false}
+ profile_url={URL;optional;postFilter=#false}
+ time={UINT;optional}
+ url={URL;optional}
+ user_id={UINT;optional}
+ author={PARSE=/^\\[url=(?'url'.*?)](?'author'.*)\\[\\/url]$/i}
+ author={PARSE=/^\\[url](?'author'(?'url'.*?))\\[\\/url]$/i}
+ author={PARSE=/(?'url'https?:\\/\\/[^[\\]]+)/i}
+ ]{TEXT2}[/QUOTE]",
+ 'size' => '[SIZE={FONTSIZE}]{TEXT}[/SIZE]',
+ 'u' => '[U]{TEXT}[/U]',
+ 'url' => '[URL={URL;useContent} $forceLookahead=true]{TEXT}[/URL]',
+ );
+
+ /**
+ * @var array Default templates, taken from bbcode::bbcode_tpl()
+ */
+ protected $default_templates = array(
+ 'b' => '<span style="font-weight: bold"><xsl:apply-templates/></span>',
+ 'i' => '<span style="font-style: italic"><xsl:apply-templates/></span>',
+ 'u' => '<span style="text-decoration: underline"><xsl:apply-templates/></span>',
+ 'img' => '<img src="{IMAGEURL}" class="postimage" alt="{L_IMAGE}"/>',
+ 'size' => '<span style="font-size: {FONTSIZE}%; line-height: normal"><xsl:apply-templates/></span>',
+ 'color' => '<span style="color: {COLOR}"><xsl:apply-templates/></span>',
+ 'email' => '<a>
+ <xsl:attribute name="href">
+ <xsl:text>mailto:</xsl:text>
+ <xsl:value-of select="@email"/>
+ <xsl:if test="@subject or @body">
+ <xsl:text>?</xsl:text>
+ <xsl:if test="@subject">subject=<xsl:value-of select="@subject"/></xsl:if>
+ <xsl:if test="@body"><xsl:if test="@subject">&amp;</xsl:if>body=<xsl:value-of select="@body"/></xsl:if>
+ </xsl:if>
+ </xsl:attribute>
+ <xsl:apply-templates/>
+ </a>',
+ );
+
+ /**
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\textformatter\data_access $data_access
+ * @param \phpbb\cache\driver\driver_interface $cache
+ * @param \phpbb\event\dispatcher_interface $dispatcher
+ * @param \phpbb\config\config $config
+ * @param \phpbb\textformatter\s9e\link_helper $link_helper
+ * @param string $cache_dir Path to the cache dir
+ * @param string $cache_key_parser Cache key used for the parser
+ * @param string $cache_key_renderer Cache key used for the renderer
+ */
+ public function __construct(\phpbb\textformatter\data_access $data_access, \phpbb\cache\driver\driver_interface $cache, \phpbb\event\dispatcher_interface $dispatcher, \phpbb\config\config $config, \phpbb\textformatter\s9e\link_helper $link_helper, $cache_dir, $cache_key_parser, $cache_key_renderer)
+ {
+ $this->link_helper = $link_helper;
+ $this->cache = $cache;
+ $this->cache_dir = $cache_dir;
+ $this->cache_key_parser = $cache_key_parser;
+ $this->cache_key_renderer = $cache_key_renderer;
+ $this->config = $config;
+ $this->data_access = $data_access;
+ $this->dispatcher = $dispatcher;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function invalidate()
+ {
+ $this->regenerate();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Will remove old renderers from the cache dir but won't touch the current renderer
+ */
+ public function tidy()
+ {
+ // Get the name of current renderer
+ $renderer_data = $this->cache->get($this->cache_key_renderer);
+ $renderer_file = ($renderer_data) ? $renderer_data['class'] . '.php' : null;
+
+ foreach (glob($this->cache_dir . 's9e_*') as $filename)
+ {
+ // Only remove the file if it's not the current renderer
+ if (!$renderer_file || substr($filename, -strlen($renderer_file)) !== $renderer_file)
+ {
+ unlink($filename);
+ }
+ }
+ }
+
+ /**
+ * Generate and return a new configured instance of s9e\TextFormatter\Configurator
+ *
+ * @return Configurator
+ */
+ public function get_configurator()
+ {
+ // Create a new Configurator
+ $configurator = new Configurator;
+
+ /**
+ * Modify the s9e\TextFormatter configurator before the default settings are set
+ *
+ * @event core.text_formatter_s9e_configure_before
+ * @var \s9e\TextFormatter\Configurator configurator Configurator instance
+ * @since 3.2.0-a1
+ */
+ $vars = array('configurator');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_before', compact($vars)));
+
+ // Reset the list of allowed schemes
+ foreach ($configurator->urlConfig->getAllowedSchemes() as $scheme)
+ {
+ $configurator->urlConfig->disallowScheme($scheme);
+ }
+ foreach (explode(',', $this->config['allowed_schemes_links']) as $scheme)
+ {
+ $configurator->urlConfig->allowScheme(trim($scheme));
+ }
+
+ // Convert newlines to br elements by default
+ $configurator->rootRules->enableAutoLineBreaks();
+
+ // Don't automatically ignore text in places where text is not allowed
+ $configurator->rulesGenerator->remove('IgnoreTextIfDisallowed');
+
+ // Don't remove comments and instead convert them to xsl:comment elements
+ $configurator->templateNormalizer->remove('RemoveComments');
+ $configurator->templateNormalizer->add('TransposeComments');
+
+ // Set the rendering engine and configure it to save to the cache dir
+ $configurator->rendering->engine = 'PHP';
+ $configurator->rendering->engine->cacheDir = $this->cache_dir;
+ $configurator->rendering->engine->defaultClassPrefix = 's9e_renderer_';
+ $configurator->rendering->engine->enableQuickRenderer = true;
+
+ // Create custom filters for BBCode tokens that are supported in phpBB but not in
+ // s9e\TextFormatter
+ $filter = new RegexpFilter('#^' . get_preg_expression('relative_url') . '$#Du');
+ $configurator->attributeFilters->add('#local_url', $filter);
+ $configurator->attributeFilters->add('#relative_url', $filter);
+
+ // INTTEXT regexp from acp_bbcodes
+ $filter = new RegexpFilter('!^([\p{L}\p{N}\-+,_. ]+)$!Du');
+ $configurator->attributeFilters->add('#inttext', $filter);
+
+ // Create custom filters for Flash restrictions, which use the same values as the image
+ // restrictions but have their own error message
+ $configurator->attributeFilters
+ ->add('#flashheight', __NAMESPACE__ . '\\parser::filter_flash_height')
+ ->addParameterByName('max_img_height')
+ ->addParameterByName('logger');
+
+ $configurator->attributeFilters
+ ->add('#flashwidth', __NAMESPACE__ . '\\parser::filter_flash_width')
+ ->addParameterByName('max_img_width')
+ ->addParameterByName('logger');
+
+ // Create a custom filter for phpBB's per-mode font size limits
+ $configurator->attributeFilters
+ ->add('#fontsize', __NAMESPACE__ . '\\parser::filter_font_size')
+ ->addParameterByName('max_font_size')
+ ->addParameterByName('logger')
+ ->markAsSafeInCSS();
+
+ // Create a custom filter for image URLs
+ $configurator->attributeFilters
+ ->add('#imageurl', __NAMESPACE__ . '\\parser::filter_img_url')
+ ->addParameterByName('urlConfig')
+ ->addParameterByName('logger')
+ ->addParameterByName('max_img_height')
+ ->addParameterByName('max_img_width')
+ ->markAsSafeAsURL();
+
+ // Add default BBCodes
+ foreach ($this->get_default_bbcodes($configurator) as $bbcode)
+ {
+ $configurator->BBCodes->addCustom($bbcode['usage'], $bbcode['template']);
+ }
+
+ // Modify the template to disable images/flash depending on user's settings
+ foreach (array('FLASH', 'IMG') as $name)
+ {
+ $tag = $configurator->tags[$name];
+ $tag->template = '<xsl:choose><xsl:when test="$S_VIEW' . $name . '">' . $tag->template . '</xsl:when><xsl:otherwise><xsl:apply-templates/></xsl:otherwise></xsl:choose>';
+ }
+
+ // Load custom BBCodes
+ foreach ($this->data_access->get_bbcodes() as $row)
+ {
+ // Insert the board's URL before {LOCAL_URL} tokens
+ $tpl = preg_replace_callback(
+ '#\\{LOCAL_URL\\d*\\}#',
+ function ($m)
+ {
+ return generate_board_url() . '/' . $m[0];
+ },
+ $row['bbcode_tpl']
+ );
+
+ try
+ {
+ $configurator->BBCodes->addCustom($row['bbcode_match'], new UnsafeTemplate($tpl));
+ }
+ catch (\Exception $e)
+ {
+ /**
+ * @todo log an error?
+ */
+ }
+ }
+
+ // Load smilies
+ foreach ($this->data_access->get_smilies() as $row)
+ {
+ $configurator->Emoticons->add(
+ $row['code'],
+ '<img class="smilies" src="{$T_SMILIES_PATH}/' . htmlspecialchars($row['smiley_url']) . '" alt="{.}" title="' . htmlspecialchars($row['emotion']) . '"/>'
+ );
+ }
+
+ if (isset($configurator->Emoticons))
+ {
+ // Force emoticons to be rendered as text if $S_VIEWSMILIES is not set
+ $configurator->Emoticons->notIfCondition = 'not($S_VIEWSMILIES)';
+
+ // Only parse emoticons at the beginning of the text or if they're preceded by any
+ // one of: a new line, a space, a dot, or a right square bracket
+ $configurator->Emoticons->notAfter = '[^\\n .\\]]';
+ }
+
+ // Load the censored words
+ $censor = $this->data_access->get_censored_words();
+ if (!empty($censor))
+ {
+ // Use a namespaced tag to avoid collisions
+ $configurator->plugins->load('Censor', array('tagName' => 'censor:tag'));
+ foreach ($censor as $row)
+ {
+ // NOTE: words are stored as HTML, we need to decode them to plain text
+ $configurator->Censor->add(htmlspecialchars_decode($row['word']), htmlspecialchars_decode($row['replacement']));
+ }
+ }
+
+ // Load the magic links plugins. We do that after BBCodes so that they use the same tags
+ $this->configure_autolink($configurator);
+
+ // Register some vars with a default value. Those should be set at runtime by whatever calls
+ // the parser
+ $configurator->registeredVars['max_font_size'] = 0;
+ $configurator->registeredVars['max_img_height'] = 0;
+ $configurator->registeredVars['max_img_width'] = 0;
+
+ // Load the Emoji plugin and modify its tag's template to obey viewsmilies
+ $configurator->Emoji->setImageSize(18);
+ $tag = $configurator->Emoji->getTag();
+ $tag->template = '<xsl:choose><xsl:when test="$S_VIEWSMILIES">' . str_replace('class="emoji"', 'class="smilies"', $tag->template) . '</xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>';
+
+ /**
+ * Modify the s9e\TextFormatter configurator after the default settings are set
+ *
+ * @event core.text_formatter_s9e_configure_after
+ * @var \s9e\TextFormatter\Configurator configurator Configurator instance
+ * @since 3.2.0-a1
+ */
+ $vars = array('configurator');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_after', compact($vars)));
+
+ return $configurator;
+ }
+
+ /**
+ * Regenerate and cache a new parser and renderer
+ *
+ * @return array Associative array with at least two elements: "parser" and "renderer"
+ */
+ public function regenerate()
+ {
+ $configurator = $this->get_configurator();
+
+ // Get the censor helper and remove the Censor plugin if applicable
+ if (isset($configurator->Censor))
+ {
+ $censor = $configurator->Censor->getHelper();
+ unset($configurator->Censor);
+ unset($configurator->tags['censor:tag']);
+ }
+
+ $objects = $configurator->finalize();
+ $parser = $objects['parser'];
+ $renderer = $objects['renderer'];
+
+ // Cache the parser as-is
+ $this->cache->put($this->cache_key_parser, $parser);
+
+ // We need to cache the name of the renderer's generated class
+ $renderer_data = array('class' => get_class($renderer));
+ if (isset($censor))
+ {
+ $renderer_data['censor'] = $censor;
+ }
+ $this->cache->put($this->cache_key_renderer, $renderer_data);
+
+ return array('parser' => $parser, 'renderer' => $renderer);
+ }
+
+ /**
+ * Configure the Autolink / Autoemail plugins used to linkify text
+ *
+ * @param \s9e\TextFormatter\Configurator $configurator
+ * @return void
+ */
+ protected function configure_autolink(Configurator $configurator)
+ {
+ $configurator->plugins->load('Autoemail');
+ $configurator->plugins->load('Autolink', array('matchWww' => true));
+
+ // Add a tag filter that creates a tag that stores and replace the
+ // content of a link created by the Autolink plugin
+ $configurator->Autolink->getTag()->filterChain
+ ->add(array($this->link_helper, 'generate_link_text_tag'))
+ ->resetParameters()
+ ->addParameterByName('tag')
+ ->addParameterByName('parser');
+
+ // Create a tag that will be used to display the truncated text by
+ // replacing the original content with the content of the @text attribute
+ $tag = $configurator->tags->add('LINK_TEXT');
+ $tag->attributes->add('text');
+ $tag->template = '<xsl:value-of select="@text"/>';
+
+ $tag->filterChain
+ ->add(array($this->link_helper, 'truncate_local_url'))
+ ->resetParameters()
+ ->addParameterByName('tag')
+ ->addParameterByValue(generate_board_url() . '/');
+ $tag->filterChain
+ ->add(array($this->link_helper, 'truncate_text'))
+ ->resetParameters()
+ ->addParameterByName('tag');
+ $tag->filterChain
+ ->add(array($this->link_helper, 'cleanup_tag'))
+ ->resetParameters()
+ ->addParameterByName('tag')
+ ->addParameterByName('parser');
+ }
+
+ /**
+ * Return the default BBCodes configuration
+ *
+ * @return array 2D array. Each element has a 'usage' key, a 'template' key, and an optional 'options' key
+ */
+ protected function get_default_bbcodes($configurator)
+ {
+ // For each BBCode, build an associative array matching style_ids to their template
+ $templates = array();
+ foreach ($this->data_access->get_styles_templates() as $style_id => $data)
+ {
+ foreach ($this->extract_templates($data['template']) as $bbcode_name => $template)
+ {
+ $templates[$bbcode_name][$style_id] = $template;
+ }
+
+ // Add default templates wherever missing, or for BBCodes that were not specified in
+ // this template's bitfield. For instance, prosilver has a custom template for b but its
+ // bitfield does not enable it so the default template is used instead
+ foreach ($this->default_templates as $bbcode_name => $template)
+ {
+ if (!isset($templates[$bbcode_name][$style_id]) || !in_array($bbcode_name, $data['bbcodes'], true))
+ {
+ $templates[$bbcode_name][$style_id] = $template;
+ }
+ }
+ }
+
+ // Replace custom tokens and normalize templates
+ foreach ($templates as $bbcode_name => $style_templates)
+ {
+ foreach ($style_templates as $i => $template)
+ {
+ if (isset($this->custom_tokens[$bbcode_name]))
+ {
+ $template = strtr($template, $this->custom_tokens[$bbcode_name]);
+ }
+
+ $templates[$bbcode_name][$i] = $configurator->templateNormalizer->normalizeTemplate($template);
+ }
+ }
+
+ $bbcodes = array();
+ foreach ($this->default_definitions as $bbcode_name => $usage)
+ {
+ $bbcodes[$bbcode_name] = array(
+ 'usage' => $usage,
+ 'template' => $this->merge_templates($templates[$bbcode_name]),
+ );
+ }
+
+ return $bbcodes;
+ }
+
+ /**
+ * Extract and recompose individual BBCode templates from a style's template file
+ *
+ * @param string $template Style template (bbcode.html)
+ * @return array Associative array matching BBCode names to their template
+ */
+ protected function extract_templates($template)
+ {
+ // Capture the template fragments
+ preg_match_all('#<!-- BEGIN (.*?) -->(.*?)<!-- END .*? -->#s', $template, $matches, PREG_SET_ORDER);
+
+ $fragments = array();
+ foreach ($matches as $match)
+ {
+ // Normalize the whitespace
+ $fragment = preg_replace('#>\\n\\t*<#', '><', trim($match[2]));
+
+ $fragments[$match[1]] = $fragment;
+ }
+
+ // Automatically recompose templates split between *_open and *_close
+ foreach ($fragments as $fragment_name => $fragment)
+ {
+ if (preg_match('#^(\\w+)_close$#', $fragment_name, $match))
+ {
+ $bbcode_name = $match[1];
+
+ if (isset($fragments[$bbcode_name . '_open']))
+ {
+ $templates[$bbcode_name] = $fragments[$bbcode_name . '_open'] . '<xsl:apply-templates/>' . $fragment;
+ }
+ }
+ }
+
+ // Manually recompose and overwrite irregular templates
+ $templates['list'] =
+ '<xsl:choose>
+ <xsl:when test="not(@type)">
+ ' . $fragments['ulist_open_default'] . '<xsl:apply-templates/>' . $fragments['ulist_close'] . '
+ </xsl:when>
+ <xsl:when test="contains(\'upperlowerdecim\',substring(@type,1,5))">
+ ' . $fragments['olist_open'] . '<xsl:apply-templates/>' . $fragments['olist_close'] . '
+ </xsl:when>
+ <xsl:otherwise>
+ ' . $fragments['ulist_open'] . '<xsl:apply-templates/>' . $fragments['ulist_close'] . '
+ </xsl:otherwise>
+ </xsl:choose>';
+
+ $templates['li'] = $fragments['listitem'] . '<xsl:apply-templates/>' . $fragments['listitem_close'];
+
+ // Replace the regular quote template with the extended quote template if available
+ if (isset($fragments['quote_extended']))
+ {
+ $templates['quote'] = $fragments['quote_extended'];
+ }
+
+ // The [attachment] BBCode uses the inline_attachment template to output a comment that
+ // is post-processed by parse_attachments()
+ $templates['attachment'] = $fragments['inline_attachment_open'] . '<xsl:comment> ia<xsl:value-of select="@index"/> </xsl:comment><xsl:value-of select="@filename"/><xsl:comment> ia<xsl:value-of select="@index"/> </xsl:comment>' . $fragments['inline_attachment_close'];
+
+ // Add fragments as templates
+ foreach ($fragments as $fragment_name => $fragment)
+ {
+ if (preg_match('#^\\w+$#', $fragment_name))
+ {
+ $templates[$fragment_name] = $fragment;
+ }
+ }
+
+ // Keep only templates that are named after an existing BBCode
+ $templates = array_intersect_key($templates, $this->default_definitions);
+
+ return $templates;
+ }
+
+ /**
+ * Merge the templates from any number of styles into one BBCode template
+ *
+ * When multiple templates are available for the same BBCode (because of multiple styles) we
+ * merge them into a single template that uses an xsl:choose construct that determines which
+ * style to use at rendering time.
+ *
+ * @param array $style_templates Associative array matching style_ids to their template
+ * @return string
+ */
+ protected function merge_templates(array $style_templates)
+ {
+ // Return the template as-is if there's only one style or all styles share the same template
+ if (count(array_unique($style_templates)) === 1)
+ {
+ return end($style_templates);
+ }
+
+ // Group identical templates together
+ $grouped_templates = array();
+ foreach ($style_templates as $style_id => $style_template)
+ {
+ $grouped_templates[$style_template][] = '$STYLE_ID=' . $style_id;
+ }
+
+ // Sort templates by frequency descending
+ $templates_cnt = array_map('sizeof', $grouped_templates);
+ array_multisort($grouped_templates, $templates_cnt);
+
+ // Remove the most frequent template from the list; It becomes the default
+ reset($grouped_templates);
+ $default_template = key($grouped_templates);
+ unset($grouped_templates[$default_template]);
+
+ // Build an xsl:choose switch
+ $template = '<xsl:choose>';
+ foreach ($grouped_templates as $style_template => $exprs)
+ {
+ $template .= '<xsl:when test="' . implode(' or ', $exprs) . '">' . $style_template . '</xsl:when>';
+ }
+ $template .= '<xsl:otherwise>' . $default_template . '</xsl:otherwise></xsl:choose>';
+
+ return $template;
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/link_helper.php b/phpBB/phpbb/textformatter/s9e/link_helper.php
new file mode 100644
index 0000000000..0f44603dec
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/link_helper.php
@@ -0,0 +1,118 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter\s9e;
+
+class link_helper
+{
+ /**
+ * Clean up and invalidate a LINK_TEXT tag if applicable
+ *
+ * Will invalidate the tag if its replacement text is the same as the original
+ * text and would have no visible effect
+ *
+ * @param \s9e\TextFormatter\Parser\Tag $tag LINK_TEXT tag
+ * @param \s9e\TextFormatter\Parser $parser Parser
+ * @return bool Whether the tag is valid
+ */
+ public function cleanup_tag(\s9e\TextFormatter\Parser\Tag $tag, \s9e\TextFormatter\Parser $parser)
+ {
+ // Invalidate if the content of the tag matches the text attribute
+ $text = substr($parser->getText(), $tag->getPos(), $tag->getLen());
+
+ return ($text !== $tag->getAttribute('text'));
+ }
+
+ /**
+ * Create a LINK_TEXT tag inside of a link
+ *
+ * Meant to only apply to linkified URLs and [url] BBCodes without a parameter
+ *
+ * @param \s9e\TextFormatter\Parser\Tag $tag URL tag (start tag)
+ * @param \s9e\TextFormatter\Parser $parser Parser
+ * @return bool Always true to indicate that the tag is valid
+ */
+ public function generate_link_text_tag(\s9e\TextFormatter\Parser\Tag $tag, \s9e\TextFormatter\Parser $parser)
+ {
+ // Only create a LINK_TEXT tag if the start tag is paired with an end
+ // tag, which is the case with tags from the Autolink plugins and with
+ // the [url] BBCode when its content is used for the URL
+ if (!$tag->getEndTag() || !$this->should_shorten($tag, $parser->getText()))
+ {
+ return true;
+ }
+
+ // Capture the text between the start tag and its end tag
+ $start = $tag->getPos() + $tag->getLen();
+ $end = $tag->getEndTag()->getPos();
+ $length = $end - $start;
+ $text = substr($parser->getText(), $start, $length);
+
+ // Create a tag that consumes the link's text
+ $parser->addSelfClosingTag('LINK_TEXT', $start, $length)->setAttribute('text', $text);
+
+ return true;
+ }
+
+ /**
+ * Test whether we should shorten this tag's text
+ *
+ * Will test whether the tag either does not use any markup or uses a single
+ * [url] BBCode
+ *
+ * @param \s9e\TextFormatter\Parser\Tag $tag URL tag
+ * @param string $text Original text
+ * @return bool
+ */
+ protected function should_shorten(\s9e\TextFormatter\Parser\Tag $tag, $text)
+ {
+ return ($tag->getLen() === 0 || strtolower(substr($text, $tag->getPos(), $tag->getLen())) === '[url]');
+ }
+
+ /**
+ * Remove the board's root URL from a the start of a string
+ *
+ * @param \s9e\TextFormatter\Parser\Tag $tag LINK_TEXT tag
+ * @param string $board_url Forum's root URL (with trailing slash)
+ * @return bool Always true to indicate that the tag is valid
+ */
+ public function truncate_local_url(\s9e\TextFormatter\Parser\Tag $tag, $board_url)
+ {
+ $text = $tag->getAttribute('text');
+ if (stripos($text, $board_url) === 0 && strlen($text) > strlen($board_url))
+ {
+ $tag->setAttribute('text', substr($text, strlen($board_url)));
+ }
+
+ return true;
+ }
+
+ /**
+ * Truncate the replacement text set in a LINK_TEXT tag
+ *
+ * @param \s9e\TextFormatter\Parser\Tag $tag LINK_TEXT tag
+ * @return bool Always true to indicate that the tag is valid
+ */
+ public function truncate_text(\s9e\TextFormatter\Parser\Tag $tag)
+ {
+ $text = $tag->getAttribute('text');
+ if (utf8_strlen($text) > 55)
+ {
+ $text = utf8_substr($text, 0, 39) . ' ... ' . utf8_substr($text, -10);
+ }
+
+ $tag->setAttribute('text', $text);
+
+ return true;
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php
new file mode 100644
index 0000000000..e2653d60f0
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/parser.php
@@ -0,0 +1,397 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter\s9e;
+
+use s9e\TextFormatter\Parser\BuiltInFilters;
+use s9e\TextFormatter\Parser\Logger;
+
+/**
+* s9e\TextFormatter\Parser adapter
+*/
+class parser implements \phpbb\textformatter\parser_interface
+{
+ /**
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
+ * @var \s9e\TextFormatter\Parser
+ */
+ protected $parser;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\cache\driver_interface $cache
+ * @param string $key Cache key
+ * @param factory $factory
+ * @param \phpbb\event\dispatcher_interface $dispatcher
+ */
+ public function __construct(\phpbb\cache\driver\driver_interface $cache, $key, factory $factory, \phpbb\event\dispatcher_interface $dispatcher)
+ {
+ $parser = $cache->get($key);
+ if (!$parser)
+ {
+ $objects = $factory->regenerate();
+ $parser = $objects['parser'];
+ }
+
+ $this->dispatcher = $dispatcher;
+ $this->parser = $parser;
+
+ $parser = $this;
+
+ /**
+ * Configure the parser service
+ *
+ * Can be used to:
+ * - toggle features or BBCodes
+ * - register variables or custom parsers in the s9e\TextFormatter parser
+ * - configure the s9e\TextFormatter parser's runtime settings
+ *
+ * @event core.text_formatter_s9e_parser_setup
+ * @var \phpbb\textformatter\s9e\parser parser This parser service
+ * @since 3.2.0-a1
+ */
+ $vars = array('parser');
+ extract($dispatcher->trigger_event('core.text_formatter_s9e_parser_setup', compact($vars)));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function parse($text)
+ {
+ $parser = $this;
+
+ /**
+ * Modify a text before it is parsed
+ *
+ * @event core.text_formatter_s9e_parse_before
+ * @var \phpbb\textformatter\s9e\parser parser This parser service
+ * @var string text The original text
+ * @since 3.2.0-a1
+ */
+ $vars = array('parser', 'text');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_parse_before', compact($vars)));
+
+ $xml = $this->parser->parse($text);
+
+ /**
+ * Modify a parsed text in its XML form
+ *
+ * @event core.text_formatter_s9e_parse_after
+ * @var \phpbb\textformatter\s9e\parser parser This parser service
+ * @var string xml The parsed text, in XML
+ * @since 3.2.0-a1
+ */
+ $vars = array('parser', 'xml');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_parse_after', compact($vars)));
+
+ return $xml;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable_bbcode($name)
+ {
+ $this->parser->disableTag(strtoupper($name));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable_bbcodes()
+ {
+ $this->parser->disablePlugin('BBCodes');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable_censor()
+ {
+ $this->parser->disablePlugin('Censor');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable_magic_url()
+ {
+ $this->parser->disablePlugin('Autoemail');
+ $this->parser->disablePlugin('Autolink');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disable_smilies()
+ {
+ $this->parser->disablePlugin('Emoticons');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable_bbcode($name)
+ {
+ $this->parser->enableTag(strtoupper($name));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable_bbcodes()
+ {
+ $this->parser->enablePlugin('BBCodes');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable_censor()
+ {
+ $this->parser->enablePlugin('Censor');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable_magic_url()
+ {
+ $this->parser->enablePlugin('Autoemail');
+ $this->parser->enablePlugin('Autolink');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function enable_smilies()
+ {
+ $this->parser->enablePlugin('Emoticons');
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This will convert the log entries found in s9e\TextFormatter's logger into phpBB error
+ * messages
+ */
+ public function get_errors()
+ {
+ $errors = array();
+ foreach ($this->parser->getLogger()->get() as $entry)
+ {
+ list(, $msg, $context) = $entry;
+
+ if ($msg === 'Tag limit exceeded')
+ {
+ if ($context['tagName'] === 'E')
+ {
+ $errors[] = array('TOO_MANY_SMILIES', $context['tagLimit']);
+ }
+ else if ($context['tagName'] === 'URL')
+ {
+ $errors[] = array('TOO_MANY_URLS', $context['tagLimit']);
+ }
+ }
+ else if ($msg === 'MAX_FONT_SIZE_EXCEEDED')
+ {
+ $errors[] = array($msg, $context['max_size']);
+ }
+ else if (preg_match('/^MAX_(?:FLASH|IMG)_(HEIGHT|WIDTH)_EXCEEDED$/D', $msg, $m))
+ {
+ $errors[] = array($msg, $context['max_' . strtolower($m[1])]);
+ }
+ else if ($msg === 'Tag is disabled')
+ {
+ $name = strtolower($context['tag']->getName());
+ $errors[] = array('UNAUTHORISED_BBCODE', '[' . $name . ']');
+ }
+ else if ($msg === 'UNABLE_GET_IMAGE_SIZE')
+ {
+ $errors[] = array($msg);
+ }
+ }
+
+ // Deduplicate error messages. array_unique() only works on strings so we have to serialize
+ if (!empty($errors))
+ {
+ $errors = array_map('unserialize', array_unique(array_map('serialize', $errors)));
+ }
+
+ return $errors;
+ }
+
+ /**
+ * Return the instance of s9e\TextFormatter\Parser used by this object
+ *
+ * @return \s9e\TextFormatter\Parser
+ */
+ public function get_parser()
+ {
+ return $this->parser;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_var($name, $value)
+ {
+ if ($name === 'max_smilies')
+ {
+ $this->parser->setTagLimit('E', $value ?: PHP_INT_MAX);
+ }
+ else if ($name === 'max_urls')
+ {
+ $this->parser->setTagLimit('URL', $value ?: PHP_INT_MAX);
+ }
+ else
+ {
+ $this->parser->registeredVars[$name] = $value;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_vars(array $vars)
+ {
+ foreach ($vars as $name => $value)
+ {
+ $this->set_var($name, $value);
+ }
+ }
+
+ /**
+ * Filter a flash object's height
+ *
+ * @see bbcode_firstpass::bbcode_flash()
+ *
+ * @param string $height
+ * @param integer $max_height
+ * @param Logger $logger
+ * @return mixed Original value if valid, FALSE otherwise
+ */
+ static public function filter_flash_height($height, $max_height, Logger $logger)
+ {
+ if ($max_height && $height > $max_height)
+ {
+ $logger->err('MAX_FLASH_HEIGHT_EXCEEDED', array('max_height' => $max_height));
+
+ return false;
+ }
+
+ return $height;
+ }
+
+ /**
+ * Filter a flash object's width
+ *
+ * @see bbcode_firstpass::bbcode_flash()
+ *
+ * @param string $width
+ * @param integer $max_width
+ * @param Logger $logger
+ * @return mixed Original value if valid, FALSE otherwise
+ */
+ static public function filter_flash_width($width, $max_width, Logger $logger)
+ {
+ if ($max_width && $width > $max_width)
+ {
+ $logger->err('MAX_FLASH_WIDTH_EXCEEDED', array('max_width' => $max_width));
+
+ return false;
+ }
+
+ return $width;
+ }
+
+ /**
+ * Filter the value used in a [size] BBCode
+ *
+ * @see bbcode_firstpass::bbcode_size()
+ *
+ * @param string $size Original size
+ * @param integer $max_size Maximum allowed size
+ * @param Logger $logger
+ * @return mixed Original value if valid, FALSE otherwise
+ */
+ static public function filter_font_size($size, $max_size, Logger $logger)
+ {
+ if ($max_size && $size > $max_size)
+ {
+ $logger->err('MAX_FONT_SIZE_EXCEEDED', array('max_size' => $max_size));
+
+ return false;
+ }
+
+ if ($size < 1)
+ {
+ return false;
+ }
+
+ return $size;
+ }
+
+ /**
+ * Filter an image's URL to enforce restrictions on its dimensions
+ *
+ * @see bbcode_firstpass::bbcode_img()
+ *
+ * @param string $url Original URL
+ * @param array $url_config Config used by the URL filter
+ * @param Logger $logger
+ * @param integer $max_height Maximum height allowed
+ * @param integer $max_width Maximum width allowed
+ * @return string|bool Original value if valid, FALSE otherwise
+ */
+ static public function filter_img_url($url, array $url_config, Logger $logger, $max_height, $max_width)
+ {
+ // Validate the URL
+ $url = BuiltInFilters::filterUrl($url, $url_config, $logger);
+ if ($url === false)
+ {
+ return false;
+ }
+
+ if ($max_height || $max_width)
+ {
+ $imagesize = new \FastImageSize\FastImageSize();
+ $size_info = $imagesize->getImageSize($url);
+ if ($size_info === false)
+ {
+ $logger->err('UNABLE_GET_IMAGE_SIZE');
+ return false;
+ }
+
+ if ($max_height && $max_height < $size_info['height'])
+ {
+ $logger->err('MAX_IMG_HEIGHT_EXCEEDED', array('max_height' => $max_height));
+ return false;
+ }
+
+ if ($max_width && $max_width < $size_info['width'])
+ {
+ $logger->err('MAX_IMG_WIDTH_EXCEEDED', array('max_width' => $max_width));
+ return false;
+ }
+ }
+
+ return $url;
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/quote_helper.php b/phpBB/phpbb/textformatter/s9e/quote_helper.php
new file mode 100644
index 0000000000..24109ac8cc
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/quote_helper.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\textformatter\s9e;
+
+class quote_helper
+{
+ /**
+ * @var string Base URL for a post link, uses {POST_ID} as placeholder
+ */
+ protected $post_url;
+
+ /**
+ * @var string Base URL for a profile link, uses {USER_ID} as placeholder
+ */
+ protected $profile_url;
+
+ /**
+ * @var \phpbb\user
+ */
+ protected $user;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\user $user
+ * @param string $root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\user $user, $root_path, $php_ext)
+ {
+ $this->post_url = append_sid($root_path . 'viewtopic.' . $php_ext, 'p={POST_ID}#p{POST_ID}');
+ $this->profile_url = append_sid($root_path . 'memberlist.' . $php_ext, 'mode=viewprofile&u={USER_ID}');
+ $this->user = $user;
+ }
+
+ /**
+ * Inject dynamic metadata into QUOTE tags in given XML
+ *
+ * @param string $xml Original XML
+ * @return string Modified XML
+ */
+ public function inject_metadata($xml)
+ {
+ $post_url = $this->post_url;
+ $profile_url = $this->profile_url;
+ $user = $this->user;
+
+ return \s9e\TextFormatter\Utils::replaceAttributes(
+ $xml,
+ 'QUOTE',
+ function ($attributes) use ($post_url, $profile_url, $user)
+ {
+ if (isset($attributes['post_id']))
+ {
+ $attributes['post_url'] = str_replace('{POST_ID}', $attributes['post_id'], $post_url);
+ }
+ if (isset($attributes['time']))
+ {
+ $attributes['date'] = $user->format_date($attributes['time']);
+ }
+ if (isset($attributes['user_id']))
+ {
+ $attributes['profile_url'] = str_replace('{USER_ID}', $attributes['user_id'], $profile_url);
+ }
+
+ return $attributes;
+ }
+ );
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/renderer.php b/phpBB/phpbb/textformatter/s9e/renderer.php
new file mode 100644
index 0000000000..9be20b7f53
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/renderer.php
@@ -0,0 +1,315 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter\s9e;
+
+/**
+* s9e\TextFormatter\Renderer adapter
+*/
+class renderer implements \phpbb\textformatter\renderer_interface
+{
+ /**
+ * @var \s9e\TextFormatter\Plugins\Censor\Helper
+ */
+ protected $censor;
+
+ /**
+ * @var \phpbb\event\dispatcher_interface
+ */
+ protected $dispatcher;
+
+ /**
+ * @var quote_helper
+ */
+ protected $quote_helper;
+
+ /**
+ * @var \s9e\TextFormatter\Renderer
+ */
+ protected $renderer;
+
+ /**
+ * @var bool Status of the viewcensors option
+ */
+ protected $viewcensors = false;
+
+ /**
+ * @var bool Status of the viewflash option
+ */
+ protected $viewflash = false;
+
+ /**
+ * @var bool Status of the viewimg option
+ */
+ protected $viewimg = false;
+
+ /**
+ * @var bool Status of the viewsmilies option
+ */
+ protected $viewsmilies = false;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\cache\driver\driver_interface $cache
+ * @param string $cache_dir Path to the cache dir
+ * @param string $key Cache key
+ * @param factory $factory
+ * @param \phpbb\event\dispatcher_interface $dispatcher
+ */
+ public function __construct(\phpbb\cache\driver\driver_interface $cache, $cache_dir, $key, factory $factory, \phpbb\event\dispatcher_interface $dispatcher)
+ {
+ $renderer_data = $cache->get($key);
+ if ($renderer_data)
+ {
+ $class = $renderer_data['class'];
+ if (!class_exists($class, false))
+ {
+ // Try to load the renderer class from its cache file
+ $cache_file = $cache_dir . $class . '.php';
+
+ if (file_exists($cache_file))
+ {
+ include($cache_file);
+ }
+ }
+ if (class_exists($class, false))
+ {
+ $renderer = new $class;
+ }
+ if (isset($renderer_data['censor']))
+ {
+ $censor = $renderer_data['censor'];
+ }
+ }
+ if (!isset($renderer))
+ {
+ $objects = $factory->regenerate();
+ $renderer = $objects['renderer'];
+ }
+
+ if (isset($censor))
+ {
+ $this->censor = $censor;
+ }
+ $this->dispatcher = $dispatcher;
+ $this->renderer = $renderer;
+ $renderer = $this;
+
+ /**
+ * Configure the renderer service
+ *
+ * @event core.text_formatter_s9e_renderer_setup
+ * @var \phpbb\textformatter\s9e\renderer renderer This renderer service
+ * @since 3.2.0-a1
+ */
+ $vars = array('renderer');
+ extract($dispatcher->trigger_event('core.text_formatter_s9e_renderer_setup', compact($vars)));
+ }
+
+ /**
+ * Configure the quote_helper object used to display extended information in quotes
+ *
+ * @param quote_helper $quote_helper
+ */
+ public function configure_quote_helper(quote_helper $quote_helper)
+ {
+ $this->quote_helper = $quote_helper;
+ }
+
+ /**
+ * Automatically set the smilies path based on config
+ *
+ * @param \phpbb\config\config $config
+ * @param \phpbb\path_helper $path_helper
+ * @return null
+ */
+ public function configure_smilies_path(\phpbb\config\config $config, \phpbb\path_helper $path_helper)
+ {
+ /**
+ * @see smiley_text()
+ */
+ $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $path_helper->get_web_root_path();
+
+ $this->set_smilies_path($root_path . $config['smilies_path']);
+ }
+
+ /**
+ * Configure this renderer as per the user's settings
+ *
+ * Should set the locale as well as the viewcensor/viewflash/viewimg/viewsmilies options.
+ *
+ * @param \phpbb\user $user
+ * @param \phpbb\config\config $config
+ * @param \phpbb\auth\auth $auth
+ * @return null
+ */
+ public function configure_user(\phpbb\user $user, \phpbb\config\config $config, \phpbb\auth\auth $auth)
+ {
+ $censor = $user->optionget('viewcensors') || !$config['allow_nocensors'] || !$auth->acl_get('u_chgcensors');
+
+ $this->set_viewcensors($censor);
+ $this->set_viewflash($user->optionget('viewflash'));
+ $this->set_viewimg($user->optionget('viewimg'));
+ $this->set_viewsmilies($user->optionget('viewsmilies'));
+
+ // Set the stylesheet parameters
+ foreach (array_keys($this->renderer->getParameters()) as $param_name)
+ {
+ if (strpos($param_name, 'L_') === 0)
+ {
+ // L_FOO is set to $user->lang('FOO')
+ $this->renderer->setParameter($param_name, $user->lang(substr($param_name, 2)));
+ }
+ }
+
+ // Set this user's style id and other parameters
+ $this->renderer->setParameters(array(
+ 'S_IS_BOT' => $user->data['is_bot'],
+ 'S_REGISTERED_USER' => $user->data['is_registered'],
+ 'S_USER_LOGGED_IN' => ($user->data['user_id'] != ANONYMOUS),
+ 'STYLE_ID' => $user->style['style_id'],
+ ));
+ }
+
+ /**
+ * Return the instance of s9e\TextFormatter\Renderer used by this object
+ *
+ * @return \s9e\TextFormatter\Renderer
+ */
+ public function get_renderer()
+ {
+ return $this->renderer;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_viewcensors()
+ {
+ return $this->viewcensors;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_viewflash()
+ {
+ return $this->viewflash;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_viewimg()
+ {
+ return $this->viewimg;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_viewsmilies()
+ {
+ return $this->viewsmilies;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render($xml)
+ {
+ if (isset($this->quote_helper))
+ {
+ $xml = $this->quote_helper->inject_metadata($xml);
+ }
+
+ $renderer = $this;
+
+ /**
+ * Modify a parsed text before it is rendered
+ *
+ * @event core.text_formatter_s9e_render_before
+ * @var \phpbb\textformatter\s9e\renderer renderer This renderer service
+ * @var string xml The parsed text, in its XML form
+ * @since 3.2.0-a1
+ */
+ $vars = array('renderer', 'xml');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_render_before', compact($vars)));
+
+ if (isset($this->censor) && $this->viewcensors)
+ {
+ // NOTE: censorHtml() is XML-safe
+ $xml = $this->censor->censorHtml($xml, true);
+ }
+
+ $html = $this->renderer->render($xml);
+
+ /**
+ * Modify a rendered text
+ *
+ * @event core.text_formatter_s9e_render_after
+ * @var string html The rendered text's HTML
+ * @var \phpbb\textformatter\s9e\renderer renderer This renderer service
+ * @since 3.2.0-a1
+ */
+ $vars = array('html', 'renderer');
+ extract($this->dispatcher->trigger_event('core.text_formatter_s9e_render_after', compact($vars)));
+
+ return $html;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_smilies_path($path)
+ {
+ $this->renderer->setParameter('T_SMILIES_PATH', $path);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_viewcensors($value)
+ {
+ $this->viewcensors = $value;
+ $this->renderer->setParameter('S_VIEWCENSORS', $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_viewflash($value)
+ {
+ $this->viewflash = $value;
+ $this->renderer->setParameter('S_VIEWFLASH', $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_viewimg($value)
+ {
+ $this->viewimg = $value;
+ $this->renderer->setParameter('S_VIEWIMG', $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set_viewsmilies($value)
+ {
+ $this->viewsmilies = $value;
+ $this->renderer->setParameter('S_VIEWSMILIES', $value);
+ }
+}
diff --git a/phpBB/phpbb/textformatter/s9e/utils.php b/phpBB/phpbb/textformatter/s9e/utils.php
new file mode 100644
index 0000000000..b317fe4a8d
--- /dev/null
+++ b/phpBB/phpbb/textformatter/s9e/utils.php
@@ -0,0 +1,139 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textformatter\s9e;
+
+/**
+* Text manipulation utilities
+*/
+class utils implements \phpbb\textformatter\utils_interface
+{
+ /**
+ * Replace BBCodes and other formatting elements with whitespace
+ *
+ * NOTE: preserves smilies as text
+ *
+ * @param string $xml Parsed text
+ * @return string Plain text
+ */
+ public function clean_formatting($xml)
+ {
+ // Insert a space before <s> and <e> then remove formatting
+ $xml = preg_replace('#<[es]>#', ' $0', $xml);
+
+ return \s9e\TextFormatter\Utils::removeFormatting($xml);
+ }
+
+ /**
+ * Format given string to be used as an attribute value
+ *
+ * Will return the string as-is if it can be used in a BBCode without quotes. Otherwise,
+ * it will use either single- or double- quotes depending on whichever requires less escaping.
+ * Quotes and backslashes are escaped with backslashes where necessary
+ *
+ * @param string $str Original string
+ * @return string Same string if possible, escaped string within quotes otherwise
+ */
+ protected function format_attribute_value($str)
+ {
+ if (!preg_match('/[ "\'\\\\\\]]/', $str))
+ {
+ // Return as-is if it contains none of: space, ' " \ or ]
+ return $str;
+ }
+ $singleQuoted = "'" . addcslashes($str, "\\'") . "'";
+ $doubleQuoted = '"' . addcslashes($str, '\\"') . '"';
+
+ return (strlen($singleQuoted) < strlen($doubleQuoted)) ? $singleQuoted : $doubleQuoted;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generate_quote($text, array $attributes = array())
+ {
+ $text = trim($text);
+ $quote = '[quote';
+ if (isset($attributes['author']))
+ {
+ // Add the author as the BBCode's default attribute
+ $quote .= '=' . $this->format_attribute_value($attributes['author']);
+ unset($attributes['author']);
+ }
+
+ if (isset($attributes['user_id']) && $attributes['user_id'] == ANONYMOUS)
+ {
+ unset($attributes['user_id']);
+ }
+
+ ksort($attributes);
+ foreach ($attributes as $name => $value)
+ {
+ $quote .= ' ' . $name . '=' . $this->format_attribute_value($value);
+ }
+ $quote .= ']';
+ $newline = (strlen($quote . $text . '[/quote]') > 80 || strpos($text, "\n") !== false) ? "\n" : '';
+ $quote .= $newline . $text . $newline . '[/quote]';
+
+ return $quote;
+ }
+
+ /**
+ * Get a list of quote authors, limited to the outermost quotes
+ *
+ * @param string $xml Parsed text
+ * @return string[] List of authors
+ */
+ public function get_outermost_quote_authors($xml)
+ {
+ $authors = array();
+ if (strpos($xml, '<QUOTE ') === false)
+ {
+ return $authors;
+ }
+
+ $dom = new \DOMDocument;
+ $dom->loadXML($xml);
+ $xpath = new \DOMXPath($dom);
+ foreach ($xpath->query('//QUOTE[not(ancestor::QUOTE)]/@author') as $author)
+ {
+ $authors[] = $author->textContent;
+ }
+
+ return $authors;
+ }
+
+ /**
+ * Remove given BBCode and its content, at given nesting depth
+ *
+ * @param string $xml Parsed text
+ * @param string $bbcode_name BBCode's name
+ * @param integer $depth Minimum nesting depth (number of parents of the same name)
+ * @return string Parsed text
+ */
+ public function remove_bbcode($xml, $bbcode_name, $depth = 0)
+ {
+ return \s9e\TextFormatter\Utils::removeTag($xml, strtoupper($bbcode_name), $depth);
+ }
+
+ /**
+ * Return a parsed text to its original form
+ *
+ * @param string $xml Parsed text
+ * @return string Original plain text
+ */
+ public function unparse($xml)
+ {
+ return \s9e\TextFormatter\Unparser::unparse($xml);
+ }
+}
diff --git a/phpBB/phpbb/textformatter/utils_interface.php b/phpBB/phpbb/textformatter/utils_interface.php
new file mode 100644
index 0000000000..4810453cd1
--- /dev/null
+++ b/phpBB/phpbb/textformatter/utils_interface.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\textformatter;
+
+/**
+* Used to manipulate a parsed text
+*/
+interface utils_interface
+{
+ /**
+ * Replace BBCodes and other formatting elements with whitespace
+ *
+ * NOTE: preserves smilies as text
+ *
+ * @param string $text Parsed text
+ * @return string Plain text
+ */
+ public function clean_formatting($text);
+
+ /**
+ * Create a quote block for given text
+ *
+ * Possible attributes:
+ * - author: author's name (usually a username)
+ * - post_id: post_id of the post being quoted
+ * - user_id: user_id of the user being quoted
+ * - time: timestamp of the original message
+ *
+ * @param string $text Quote's text
+ * @param array $attributes Quote's attributes
+ * @return string Quote block to be used in a new post/text
+ */
+ public function generate_quote($text, array $attributes = array());
+
+ /**
+ * Get a list of quote authors, limited to the outermost quotes
+ *
+ * @param string $text Parsed text
+ * @return string[] List of authors
+ */
+ public function get_outermost_quote_authors($text);
+
+ /**
+ * Remove given BBCode and its content, at given nesting depth
+ *
+ * @param string $text Parsed text
+ * @param string $bbcode_name BBCode's name
+ * @param integer $depth Minimum nesting depth (number of parents of the same name)
+ * @return string Parsed text
+ */
+ public function remove_bbcode($text, $bbcode_name, $depth = 0);
+
+ /**
+ * Return a parsed text to its original form
+ *
+ * @param string $text Parsed text
+ * @return string Original plain text
+ */
+ public function unparse($text);
+}
diff --git a/phpBB/phpbb/textreparser/base.php b/phpBB/phpbb/textreparser/base.php
new file mode 100644
index 0000000000..3e5ee248a1
--- /dev/null
+++ b/phpBB/phpbb/textreparser/base.php
@@ -0,0 +1,243 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser;
+
+abstract class base implements reparser_interface
+{
+ /**
+ * @var bool Whether to save changes to the database
+ */
+ protected $save_changes = true;
+
+ /**
+ * {@inheritdoc}
+ */
+ abstract public function get_max_id();
+
+ /**
+ * Return all records in given range
+ *
+ * @param integer $min_id Lower bound
+ * @param integer $max_id Upper bound
+ * @return array Array of records
+ */
+ abstract protected function get_records_by_range($min_id, $max_id);
+
+ /**
+ * {@inheritdoc}
+ */
+ abstract protected function save_record(array $record);
+
+ /**
+ * Add fields to given record, if applicable
+ *
+ * The enable_* fields are not always saved to the database. Sometimes we need to guess their
+ * original value based on the text content or possibly other fields
+ *
+ * @param array $record Original record
+ * @return array Complete record
+ */
+ protected function add_missing_fields(array $record)
+ {
+ if (!isset($record['enable_bbcode'], $record['enable_smilies'], $record['enable_magic_url']))
+ {
+ if (isset($record['options']))
+ {
+ $record += array(
+ 'enable_bbcode' => (bool) ($record['options'] & OPTION_FLAG_BBCODE),
+ 'enable_smilies' => (bool) ($record['options'] & OPTION_FLAG_SMILIES),
+ 'enable_magic_url' => (bool) ($record['options'] & OPTION_FLAG_LINKS),
+ );
+ }
+ else
+ {
+ $record += array(
+ 'enable_bbcode' => $this->guess_bbcodes($record),
+ 'enable_smilies' => $this->guess_smilies($record),
+ 'enable_magic_url' => $this->guess_magic_url($record),
+ );
+ }
+ }
+
+ // Those BBCodes are disabled based on context and user permissions and that value is never
+ // stored in the database. Here we test whether they were used in the original text.
+ $bbcodes = array('flash', 'img', 'quote', 'url');
+ foreach ($bbcodes as $bbcode)
+ {
+ $field_name = 'enable_' . $bbcode . '_bbcode';
+ $record[$field_name] = $this->guess_bbcode($record, $bbcode);
+ }
+
+ // Magic URLs are tied to the URL BBCode, that's why if magic URLs are enabled we make sure
+ // that the URL BBCode is also enabled
+ if ($record['enable_magic_url'])
+ {
+ $record['enable_url_bbcode'] = true;
+ }
+
+ return $record;
+ }
+
+ /**
+ * Disable saving changes to the database
+ */
+ public function disable_save()
+ {
+ $this->save_changes = false;
+ }
+
+ /**
+ * Enable saving changes to the database
+ */
+ public function enable_save()
+ {
+ $this->save_changes = true;
+ }
+
+ /**
+ * Guess whether given BBCode is in use in given record
+ *
+ * @param array $record
+ * @param string $bbcode
+ * @return bool
+ */
+ protected function guess_bbcode(array $record, $bbcode)
+ {
+ if (!empty($record['bbcode_uid']))
+ {
+ // Look for the closing tag, e.g. [/url]
+ $match = '[/' . $bbcode . ':' . $record['bbcode_uid'];
+ if (strpos($record['text'], $match) !== false)
+ {
+ return true;
+ }
+ }
+
+ if (substr($record['text'], 0, 2) === '<r')
+ {
+ // Look for the closing tag inside of a e element, in an element of the same name, e.g.
+ // <e>[/url]</e></URL>
+ $match = '<e>[/' . $bbcode . ']</e></' . strtoupper($bbcode) . '>';
+ if (strpos($record['text'], $match) !== false)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Guess whether any BBCode is in use in given record
+ *
+ * @param array $record
+ * @return bool
+ */
+ protected function guess_bbcodes(array $record)
+ {
+ if (!empty($record['bbcode_uid']))
+ {
+ // Test whether the bbcode_uid is in use
+ $match = ':' . $record['bbcode_uid'];
+ if (strpos($record['text'], $match) !== false)
+ {
+ return true;
+ }
+ }
+
+ if (substr($record['text'], 0, 2) === '<r')
+ {
+ // Look for a closing tag inside of an e element
+ return (bool) preg_match('(<e>\\[/\\w+\\]</e>)', $match);
+ }
+
+ return false;
+ }
+
+ /**
+ * Guess whether magic URLs are in use in given record
+ *
+ * @param array $record
+ * @return bool
+ */
+ protected function guess_magic_url(array $record)
+ {
+ // Look for <!-- m --> or for a URL tag that's not immediately followed by <s>
+ return (strpos($record['text'], '<!-- m -->') !== false || preg_match('(<URL [^>]++>(?!<s>))', $record['text']));
+ }
+
+ /**
+ * Guess whether smilies are in use in given record
+ *
+ * @param array $record
+ * @return bool
+ */
+ protected function guess_smilies(array $record)
+ {
+ return (strpos($record['text'], '<!-- s') !== false || strpos($record['text'], '<E>') !== false);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reparse_range($min_id, $max_id)
+ {
+ foreach ($this->get_records_by_range($min_id, $max_id) as $record)
+ {
+ $this->reparse_record($record);
+ }
+ }
+
+ /**
+ * Reparse given record
+ *
+ * @param array $record Associative array containing the record's data
+ */
+ protected function reparse_record(array $record)
+ {
+ $record = $this->add_missing_fields($record);
+ $flags = ($record['enable_bbcode']) ? OPTION_FLAG_BBCODE : 0;
+ $flags |= ($record['enable_smilies']) ? OPTION_FLAG_SMILIES : 0;
+ $flags |= ($record['enable_magic_url']) ? OPTION_FLAG_LINKS : 0;
+ $unparsed = array_merge(
+ $record,
+ generate_text_for_edit($record['text'], $record['bbcode_uid'], $flags)
+ );
+
+ // generate_text_for_edit() and decode_message() actually return the text as HTML. It has to
+ // be decoded to plain text before it can be reparsed
+ $text = html_entity_decode($unparsed['text'], ENT_QUOTES, 'UTF-8');
+ $bitfield = $flags = null;
+ generate_text_for_storage(
+ $text,
+ $unparsed['bbcode_uid'],
+ $bitfield,
+ $flags,
+ $unparsed['enable_bbcode'],
+ $unparsed['enable_magic_url'],
+ $unparsed['enable_smilies'],
+ $unparsed['enable_img_bbcode'],
+ $unparsed['enable_flash_bbcode'],
+ $unparsed['enable_quote_bbcode'],
+ $unparsed['enable_url_bbcode']
+ );
+
+ // Save the new text if it has changed and it's not a dry run
+ if ($text !== $record['text'] && $this->save_changes)
+ {
+ $record['text'] = $text;
+ $this->save_record($record);
+ }
+ }
+}
diff --git a/phpBB/phpbb/textreparser/manager.php b/phpBB/phpbb/textreparser/manager.php
new file mode 100644
index 0000000000..fddd867923
--- /dev/null
+++ b/phpBB/phpbb/textreparser/manager.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\textreparser;
+
+class manager
+{
+ /**
+ * @var \phpbb\config\config
+ */
+ protected $config;
+
+ /**
+ * @var \phpbb\config\db_text
+ */
+ protected $config_text;
+
+ /**
+ * @var \phpbb\di\service_collection
+ */
+ protected $reparsers;
+
+ /**
+ * @var array
+ */
+ protected $resume_data;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\config\config $config
+ * @param \phpbb\config\db_text $config_text
+ * @param \phpbb\di\service_collection $reparsers
+ */
+ public function __construct(\phpbb\config\config $config, \phpbb\config\db_text $config_text, \phpbb\di\service_collection $reparsers)
+ {
+ $this->config = $config;
+ $this->config_text = $config_text;
+ $this->reparsers = $reparsers;
+ }
+
+ /**
+ * Loads resume data from the database
+ *
+ * @param string $name Name of the reparser to which the resume data belongs
+ *
+ * @return array
+ */
+ public function get_resume_data($name)
+ {
+ if ($this->resume_data === null)
+ {
+ $resume_data = $this->config_text->get('reparser_resume');
+ $this->resume_data = !empty($resume_data) ? unserialize($resume_data) : array();
+ }
+
+ return isset($this->resume_data[$name]) ? $this->resume_data[$name] : array();
+ }
+
+ /**
+ * Updates the resume data in the database
+ *
+ * @param string $name Name of the reparser to which the resume data belongs
+ * @param int $min Lowest record ID
+ * @param int $current Current record ID
+ * @param int $size Number of records to process at a time
+ * @param bool $update_db True if the resume data should be written to the database, false if not. (default: true)
+ */
+ public function update_resume_data($name, $min, $current, $size, $update_db = true)
+ {
+ // Prevent overwriting the old, stored array
+ if ($this->resume_data === null)
+ {
+ $this->get_resume_data('');
+ }
+
+ $this->resume_data[$name] = array(
+ 'range-min' => $min,
+ 'range-max' => $current,
+ 'range-size' => $size,
+ );
+
+ if ($update_db)
+ {
+ $this->config_text->set('reparser_resume', serialize($this->resume_data));
+ }
+ }
+
+ /**
+ * Sets the interval for a text_reparser cron task
+ *
+ * @param string $name Name of the reparser to schedule
+ * @param int $interval Interval in seconds, 0 to disable the cron task
+ */
+ public function schedule($name, $interval)
+ {
+ if (isset($this->reparsers[$name]) && isset($this->config[$name . '_cron_interval']))
+ {
+ $this->config->set($name . '_cron_interval', $interval);
+ }
+ }
+
+ /**
+ * Sets the interval for all text_reparser cron tasks
+ *
+ * @param int $interval Interval in seconds, 0 to disable the cron task
+ */
+ public function schedule_all($interval)
+ {
+ // This way we don't construct every registered reparser
+ $reparser_array = array_keys($this->reparsers->getArrayCopy());
+
+ foreach ($reparser_array as $reparser)
+ {
+ $this->schedule($reparser, $interval);
+ }
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/contact_admin_info.php b/phpBB/phpbb/textreparser/plugins/contact_admin_info.php
new file mode 100644
index 0000000000..8910f2256b
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/contact_admin_info.php
@@ -0,0 +1,69 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class contact_admin_info extends \phpbb\textreparser\base
+{
+ /**
+ * @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_max_id()
+ {
+ return 1;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_records_by_range($min_id, $max_id)
+ {
+ $values = $this->config_text->get_array(array(
+ 'contact_admin_info',
+ 'contact_admin_info_uid',
+ 'contact_admin_info_flags',
+ ));
+
+ return array(array(
+ 'id' => 1,
+ 'text' => $values['contact_admin_info'],
+ 'bbcode_uid' => $values['contact_admin_info_uid'],
+ 'enable_bbcode' => $values['contact_admin_info_flags'] & OPTION_FLAG_BBCODE,
+ 'enable_magic_url' => $values['contact_admin_info_flags'] & OPTION_FLAG_LINKS,
+ 'enable_smilies' => $values['contact_admin_info_flags'] & OPTION_FLAG_SMILIES,
+ ));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function save_record(array $record)
+ {
+ $this->config_text->set('contact_admin_info', $record['text']);
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/forum_description.php b/phpBB/phpbb/textreparser/plugins/forum_description.php
new file mode 100644
index 0000000000..b0f5a42452
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/forum_description.php
@@ -0,0 +1,30 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class forum_description extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'forum_id',
+ 'text' => 'forum_desc',
+ 'bbcode_uid' => 'forum_desc_uid',
+ 'options' => 'forum_desc_options',
+ );
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/forum_rules.php b/phpBB/phpbb/textreparser/plugins/forum_rules.php
new file mode 100644
index 0000000000..d131d00707
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/forum_rules.php
@@ -0,0 +1,30 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class forum_rules extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'forum_id',
+ 'text' => 'forum_rules',
+ 'bbcode_uid' => 'forum_rules_uid',
+ 'options' => 'forum_rules_options',
+ );
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/group_description.php b/phpBB/phpbb/textreparser/plugins/group_description.php
new file mode 100644
index 0000000000..2c45c00474
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/group_description.php
@@ -0,0 +1,30 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class group_description extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'group_id',
+ 'text' => 'group_desc',
+ 'bbcode_uid' => 'group_desc_uid',
+ 'options' => 'group_desc_options',
+ );
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/pm_text.php b/phpBB/phpbb/textreparser/plugins/pm_text.php
new file mode 100644
index 0000000000..867da624ee
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/pm_text.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\textreparser\plugins;
+
+class pm_text extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'msg_id',
+ 'enable_bbcode' => 'enable_bbcode',
+ 'enable_smilies' => 'enable_smilies',
+ 'enable_magic_url' => 'enable_magic_url',
+ 'text' => 'message_text',
+ 'bbcode_uid' => 'bbcode_uid',
+ );
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/poll_option.php b/phpBB/phpbb/textreparser/plugins/poll_option.php
new file mode 100644
index 0000000000..44cacfae62
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/poll_option.php
@@ -0,0 +1,74 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class poll_option extends \phpbb\textreparser\base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db)
+ {
+ $this->db = $db;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_max_id()
+ {
+ $sql = 'SELECT MAX(topic_id) AS max_id FROM ' . POLL_OPTIONS_TABLE;
+ $result = $this->db->sql_query($sql);
+ $max_id = (int) $this->db->sql_fetchfield('max_id');
+ $this->db->sql_freeresult($result);
+
+ return $max_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_records_by_range($min_id, $max_id)
+ {
+ $sql = 'SELECT o.topic_id, o.poll_option_id, o.poll_option_text AS text, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.bbcode_uid
+ FROM ' . POLL_OPTIONS_TABLE . ' o, ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
+ WHERE o.topic_id BETWEEN ' . $min_id . ' AND ' . $max_id .'
+ AND t.topic_id = o.topic_id
+ AND p.post_id = t.topic_first_post_id';
+ $result = $this->db->sql_query($sql);
+ $records = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $records;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function save_record(array $record)
+ {
+ $sql = 'UPDATE ' . POLL_OPTIONS_TABLE . "
+ SET poll_option_text = '" . $this->db->sql_escape($record['text']) . "'
+ WHERE topic_id = " . $record['topic_id'] . '
+ AND poll_option_id = ' . $record['poll_option_id'];
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/poll_title.php b/phpBB/phpbb/textreparser/plugins/poll_title.php
new file mode 100644
index 0000000000..76d30655c9
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/poll_title.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\textreparser\plugins;
+
+class poll_title extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'topic_id',
+ 'text' => 'poll_title',
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_records_by_range_query($min_id, $max_id)
+ {
+ $sql = 'SELECT t.topic_id AS id, t.poll_title AS text, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.bbcode_uid
+ FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
+ WHERE t.topic_id BETWEEN ' . $min_id . ' AND ' . $max_id .'
+ AND t.poll_max_options > 0
+ AND p.post_id = t.topic_first_post_id';
+
+ return $sql;
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/post_text.php b/phpBB/phpbb/textreparser/plugins/post_text.php
new file mode 100644
index 0000000000..1c98e86067
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/post_text.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\textreparser\plugins;
+
+class post_text extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'post_id',
+ 'enable_bbcode' => 'enable_bbcode',
+ 'enable_smilies' => 'enable_smilies',
+ 'enable_magic_url' => 'enable_magic_url',
+ 'text' => 'post_text',
+ 'bbcode_uid' => 'bbcode_uid',
+ );
+ }
+}
diff --git a/phpBB/phpbb/textreparser/plugins/user_signature.php b/phpBB/phpbb/textreparser/plugins/user_signature.php
new file mode 100644
index 0000000000..647d3a7b14
--- /dev/null
+++ b/phpBB/phpbb/textreparser/plugins/user_signature.php
@@ -0,0 +1,65 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser\plugins;
+
+class user_signature extends \phpbb\textreparser\row_based_plugin
+{
+ /**
+ * @var array Bit numbers used for user options
+ * @see \phpbb\user
+ */
+ protected $keyoptions;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function add_missing_fields(array $row)
+ {
+ if (!isset($this->keyoptions))
+ {
+ $this->save_keyoptions();
+ }
+
+ $options = $row['user_options'];
+ $row += array(
+ 'enable_bbcode' => phpbb_optionget($this->keyoptions['sig_bbcode'], $options),
+ 'enable_smilies' => phpbb_optionget($this->keyoptions['sig_smilies'], $options),
+ 'enable_magic_url' => phpbb_optionget($this->keyoptions['sig_links'], $options),
+ );
+
+ return parent::add_missing_fields($row);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_columns()
+ {
+ return array(
+ 'id' => 'user_id',
+ 'text' => 'user_sig',
+ 'bbcode_uid' => 'user_sig_bbcode_uid',
+ 'user_options' => 'user_options',
+ );
+ }
+
+ /**
+ * Save the keyoptions var from \phpbb\user
+ */
+ protected function save_keyoptions()
+ {
+ $class_vars = get_class_vars('phpbb\\user');
+ $this->keyoptions = $class_vars['keyoptions'];
+ }
+}
diff --git a/phpBB/phpbb/textreparser/reparser_interface.php b/phpBB/phpbb/textreparser/reparser_interface.php
new file mode 100644
index 0000000000..9ea1732870
--- /dev/null
+++ b/phpBB/phpbb/textreparser/reparser_interface.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\textreparser;
+
+interface reparser_interface
+{
+ /**
+ * Return the highest ID for all existing records
+ *
+ * @return integer
+ */
+ public function get_max_id();
+
+ /**
+ * Reparse all records in given range
+ *
+ * @param integer $min_id Lower bound
+ * @param integer $max_id Upper bound
+ */
+ public function reparse_range($min_id, $max_id);
+}
diff --git a/phpBB/phpbb/textreparser/row_based_plugin.php b/phpBB/phpbb/textreparser/row_based_plugin.php
new file mode 100644
index 0000000000..2d32104493
--- /dev/null
+++ b/phpBB/phpbb/textreparser/row_based_plugin.php
@@ -0,0 +1,117 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb\textreparser;
+
+abstract class row_based_plugin extends base
+{
+ /**
+ * @var \phpbb\db\driver\driver_interface
+ */
+ protected $db;
+
+ /**
+ * @var string
+ */
+ protected $table;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db Database connection
+ * @param string $table
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db, $table)
+ {
+ $this->db = $db;
+ $this->table = $table;
+ }
+
+ /**
+ * Return the name of the column that correspond to each field
+ *
+ * @return array
+ */
+ abstract public function get_columns();
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_max_id()
+ {
+ $columns = $this->get_columns();
+
+ $sql = 'SELECT MAX(' . $columns['id'] . ') AS max_id FROM ' . $this->table;
+ $result = $this->db->sql_query($sql);
+ $max_id = (int) $this->db->sql_fetchfield('max_id');
+ $this->db->sql_freeresult($result);
+
+ return $max_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function get_records_by_range($min_id, $max_id)
+ {
+ $sql = $this->get_records_by_range_query($min_id, $max_id);
+ $result = $this->db->sql_query($sql);
+ $records = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ return $records;
+ }
+
+ /**
+ * Generate the query that retrieves all records for given range
+ *
+ * @param integer $min_id Lower bound
+ * @param integer $max_id Upper bound
+ * @return string SQL query
+ */
+ protected function get_records_by_range_query($min_id, $max_id)
+ {
+ $columns = $this->get_columns();
+ $fields = array();
+ foreach ($columns as $field_name => $column_name)
+ {
+ if ($column_name === $field_name)
+ {
+ $fields[] = $column_name;
+ }
+ else
+ {
+ $fields[] = $column_name . ' AS ' . $field_name;
+ }
+ }
+
+ $sql = 'SELECT ' . implode(', ', $fields) . '
+ FROM ' . $this->table . '
+ WHERE ' . $columns['id'] . ' BETWEEN ' . $min_id . ' AND ' . $max_id;
+
+ return $sql;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function save_record(array $record)
+ {
+ $columns = $this->get_columns();
+
+ $sql = 'UPDATE ' . $this->table . '
+ SET ' . $columns['text'] . " = '" . $this->db->sql_escape($record['text']) . "'
+ WHERE " . $columns['id'] . ' = ' . $record['id'];
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/phpbb/tree/nestedset.php b/phpBB/phpbb/tree/nestedset.php
index 13184cf41c..7149513fd9 100644
--- a/phpBB/phpbb/tree/nestedset.php
+++ b/phpBB/phpbb/tree/nestedset.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package tree
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -11,7 +15,7 @@ namespace phpbb\tree;
abstract class nestedset implements \phpbb\tree\tree_interface
{
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db;
/** @var \phpbb\lock\db */
@@ -52,7 +56,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
/**
* Construct
*
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param \phpbb\lock\db $lock Lock class used to lock the table when moving forums around
* @param string $table_name Table name
* @param string $message_prefix Prefix for the messages thrown by exceptions
@@ -60,7 +64,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
* @param array $item_basic_data Array with basic item data that is stored in item_parents
* @param array $columns Array with column names to overwrite
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\lock\db $lock, $table_name, $message_prefix = '', $sql_where = '', $item_basic_data = array(), $columns = array())
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\lock\db $lock, $table_name, $message_prefix = '', $sql_where = '', $item_basic_data = array(), $columns = array())
{
$this->db = $db;
$this->lock = $lock;
@@ -99,7 +103,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
*
* @return bool True if the lock was acquired, false if it has been acquired previously
*
- * @throws RuntimeException If the lock could not be acquired
+ * @throws \RuntimeException If the lock could not be acquired
*/
protected function acquire_lock()
{
@@ -117,7 +121,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function insert(array $additional_data)
{
@@ -172,6 +176,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
*
* @param int $item_id The item to be deleted
* @return array Item ids that have been removed
+ * @throws \OutOfBoundsException
*/
protected function remove_item_from_nestedset($item_id)
{
@@ -195,7 +200,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function delete($item_id)
{
@@ -210,7 +215,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function move($item_id, $delta)
{
@@ -328,7 +333,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function move_down($item_id)
{
@@ -336,7 +341,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function move_up($item_id)
{
@@ -344,7 +349,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function move_children($current_parent_id, $new_parent_id)
{
@@ -386,7 +391,6 @@ abstract class nestedset implements \phpbb\tree\tree_interface
throw new \OutOfBoundsException($this->message_prefix . 'INVALID_PARENT');
}
- $diff = sizeof($move_items) * 2;
$sql_exclude_moved_items = $this->db->sql_in_set($this->column_item_id, $move_items, true);
$this->db->sql_transaction('begin');
@@ -450,7 +454,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function change_parent($item_id, $new_parent_id)
{
@@ -485,7 +489,6 @@ abstract class nestedset implements \phpbb\tree\tree_interface
throw new \OutOfBoundsException($this->message_prefix . 'INVALID_PARENT');
}
- $diff = sizeof($move_items) * 2;
$sql_exclude_moved_items = $this->db->sql_in_set($this->column_item_id, $move_items, true);
$this->db->sql_transaction('begin');
@@ -549,7 +552,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_path_and_subtree_data($item_id, $order_asc = true, $include_item = true)
{
@@ -560,7 +563,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_path_data($item_id, $order_asc = true, $include_item = true)
{
@@ -570,7 +573,7 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function get_subtree_data($item_id, $order_asc = true, $include_item = true)
{
@@ -664,6 +667,32 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
/**
+ * Get all items from the tree
+ *
+ * @param bool $order_asc Order the items ascending by their left_id
+ * @return array Array of items (containing all columns from the item table)
+ * ID => Item data
+ */
+ public function get_all_tree_data($order_asc = true)
+ {
+ $rows = array();
+
+ $sql = 'SELECT *
+ FROM ' . $this->table_name . ' ' .
+ $this->get_sql_where('WHERE') . '
+ ORDER BY ' . $this->column_left_id . ' ' . ($order_asc ? 'ASC' : 'DESC');
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $rows[(int) $row[$this->column_item_id]] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ return $rows;
+ }
+
+ /**
* Remove a subset from the nested set
*
* @param array $subset_items Subset of items to remove
@@ -806,7 +835,10 @@ abstract class nestedset implements \phpbb\tree\tree_interface
' . $this->get_sql_where('AND') . '
ORDER BY ' . $this->column_left_id . ', ' . $this->column_item_id . ' ASC';
$result = $this->db->sql_query($sql);
- while ($row = $this->db->sql_fetchrow($result))
+ $rows = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ foreach ($rows as $row)
{
// First we update the left_id for this module
if ($row[$this->column_left_id] != $new_id)
@@ -831,7 +863,6 @@ abstract class nestedset implements \phpbb\tree\tree_interface
}
$new_id++;
}
- $this->db->sql_freeresult($result);
if ($acquired_new_lock)
{
diff --git a/phpBB/phpbb/tree/nestedset_forum.php b/phpBB/phpbb/tree/nestedset_forum.php
index ef6023546b..890fc804c0 100644
--- a/phpBB/phpbb/tree/nestedset_forum.php
+++ b/phpBB/phpbb/tree/nestedset_forum.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package tree
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,11 +18,11 @@ class nestedset_forum extends \phpbb\tree\nestedset
/**
* Construct
*
- * @param \phpbb\db\driver\driver $db Database connection
+ * @param \phpbb\db\driver\driver_interface $db Database connection
* @param \phpbb\lock\db $lock Lock class used to lock the table when moving forums around
* @param string $table_name Table name
*/
- public function __construct(\phpbb\db\driver\driver $db, \phpbb\lock\db $lock, $table_name)
+ public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\lock\db $lock, $table_name)
{
parent::__construct(
$db,
diff --git a/phpBB/phpbb/tree/tree_interface.php b/phpBB/phpbb/tree/tree_interface.php
index 90ec27e024..5df01a89cf 100644
--- a/phpBB/phpbb/tree/tree_interface.php
+++ b/phpBB/phpbb/tree/tree_interface.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package tree
-* @copyright (c) 2013 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,7 +18,7 @@ interface tree_interface
/**
* Inserts an item into the database table and into the tree.
*
- * @param array $item The item to be added
+ * @param array $additional_data The item to be added
* @return array Array with item data as set in the database
*/
public function insert(array $additional_data);
diff --git a/phpBB/phpbb/user.php b/phpBB/phpbb/user.php
index 2a7cc602da..5262e10e87 100644
--- a/phpBB/phpbb/user.php
+++ b/phpBB/phpbb/user.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2005 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -14,13 +18,14 @@ namespace phpbb;
*
* This is the overarching class which contains (through session extend)
* all methods utilised for user functionality during a session.
-*
-* @package phpBB3
*/
class user extends \phpbb\session
{
- var $lang = array();
- var $help = array();
+ /**
+ * @var \phpbb\language\language
+ */
+ protected $language;
+
var $style = array();
var $date_format;
@@ -29,39 +34,74 @@ class user extends \phpbb\session
*/
public $timezone;
+ /**
+ * @var string Class name of datetime object
+ */
+ protected $datetime;
+
var $lang_name = false;
var $lang_id = false;
var $lang_path;
var $img_lang;
var $img_array = array();
+ /** @var bool */
+ protected $is_setup_flag;
+
// Able to add new options (up to id 31)
var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'sig_bbcode' => 15, 'sig_smilies' => 16, 'sig_links' => 17);
/**
* Constructor to set the lang path
+ *
+ * @param \phpbb\language\language $lang phpBB's Language loader
+ * @param string $datetime_class Class name of datetime class
*/
- function __construct()
+ function __construct(\phpbb\language\language $lang, $datetime_class)
{
global $phpbb_root_path;
$this->lang_path = $phpbb_root_path . 'language/';
+ $this->language = $lang;
+ $this->datetime = $datetime_class;
+
+ $this->is_setup_flag = false;
}
/**
- * Function to set custom language path (able to use directory outside of phpBB)
- *
- * @param string $lang_path New language path used.
- * @access public
- */
- function set_custom_lang_path($lang_path)
+ * Returns whether user::setup was called
+ *
+ * @return bool
+ */
+ public function is_setup()
{
- $this->lang_path = $lang_path;
+ return $this->is_setup_flag;
+ }
- if (substr($this->lang_path, -1) != '/')
+ /**
+ * Magic getter for BC compatibility
+ *
+ * Implement array access for user::lang.
+ *
+ * @param string $param_name Name of the BC component the user want to access
+ *
+ * @return array The appropriate array
+ *
+ * @deprecated 3.2.0-dev (To be removed: 4.0.0)
+ */
+ public function __get($param_name)
+ {
+ if ($param_name === 'lang')
{
- $this->lang_path .= '/';
+ return $this->language->get_lang_array();
}
+ else if ($param_name === 'help')
+ {
+ $help_array = $this->language->get_lang_array();
+ return $help_array['__help'];
+ }
+
+ return array();
}
/**
@@ -69,9 +109,11 @@ class user extends \phpbb\session
*/
function setup($lang_set = false, $style_id = false)
{
- global $db, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache;
+ global $db, $request, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache;
global $phpbb_dispatcher;
+ $this->language->set_default_language($config['default_lang']);
+
if ($this->data['user_id'] != ANONYMOUS)
{
$user_lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
@@ -80,7 +122,27 @@ class user extends \phpbb\session
}
else
{
- $user_lang_name = basename($config['default_lang']);
+ $lang_override = $request->variable('language', '');
+ if ($lang_override)
+ {
+ $this->set_cookie('lang', $lang_override, 0, false);
+ }
+ else
+ {
+ $lang_override = $request->variable($config['cookie_name'] . '_lang', '', true, \phpbb\request\request_interface::COOKIE);
+ }
+
+ if ($lang_override)
+ {
+ $use_lang = basename($lang_override);
+ $user_lang_name = (file_exists($this->lang_path . $use_lang . "/common.$phpEx")) ? $use_lang : basename($config['default_lang']);
+ $this->data['user_lang'] = $user_lang_name;
+ }
+ else
+ {
+ $user_lang_name = basename($config['default_lang']);
+ }
+
$user_date_format = $config['default_dateformat'];
$user_timezone = $config['board_timezone'];
@@ -143,15 +205,25 @@ class user extends \phpbb\session
* that are absolutely needed globally using this
* event. Use local events otherwise.
* @var mixed style_id Style we are going to display
- * @since 3.1-A1
+ * @since 3.1.0-a1
*/
- $vars = array('user_data', 'user_lang_name', 'user_date_format', 'user_timezone', 'lang_set', 'lang_set_ext', 'style_id');
+ $vars = array(
+ 'user_data',
+ 'user_lang_name',
+ 'user_date_format',
+ 'user_timezone',
+ 'lang_set',
+ 'lang_set_ext',
+ 'style_id',
+ );
extract($phpbb_dispatcher->trigger_event('core.user_setup', compact($vars)));
$this->data = $user_data;
$this->lang_name = $user_lang_name;
$this->date_format = $user_date_format;
+ $this->language->set_user_language($user_lang_name);
+
try
{
$this->timezone = new \DateTimeZone($user_timezone);
@@ -162,17 +234,6 @@ class user extends \phpbb\session
$this->timezone = new \DateTimeZone('UTC');
}
- // We include common language file here to not load it every time a custom language file is included
- $lang = &$this->lang;
-
- // Do not suppress error if in DEBUG mode
- $include_result = (defined('DEBUG')) ? (include $this->lang_path . $this->lang_name . "/common.$phpEx") : (@include $this->lang_path . $this->lang_name . "/common.$phpEx");
-
- if ($include_result === false)
- {
- die('Language file ' . $this->lang_path . $this->lang_name . "/common.$phpEx" . " couldn't be opened.");
- }
-
$this->add_lang($lang_set);
unset($lang_set);
@@ -182,7 +243,7 @@ class user extends \phpbb\session
}
unset($lang_set_ext);
- $style_request = request_var('style', 0);
+ $style_request = $request->variable('style', 0);
if ($style_request && (!$config['override_user_style'] || $auth->acl_get('a_styles')) && !defined('ADMIN_START'))
{
global $SID, $_EXTRA_URL;
@@ -204,6 +265,19 @@ class user extends \phpbb\session
$this->style = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
+ // Fallback to user's standard style
+ if (!$this->style && $style_id != $this->data['user_style'])
+ {
+ $style_id = $this->data['user_style'];
+
+ $sql = 'SELECT *
+ FROM ' . STYLES_TABLE . " s
+ WHERE s.style_id = $style_id";
+ $result = $db->sql_query($sql, 3600);
+ $this->style = $db->sql_fetchrow($result);
+ $db->sql_freeresult($result);
+ }
+
// User has wrong style
if (!$this->style && $style_id == $this->data['user_style'])
{
@@ -253,6 +327,14 @@ class user extends \phpbb\session
// After calling it we continue script execution...
phpbb_user_session_handler();
+ /**
+ * Execute code at the end of user setup
+ *
+ * @event core.user_setup_after
+ * @since 3.1.6-RC1
+ */
+ $phpbb_dispatcher->dispatch('core.user_setup_after');
+
// If this function got called from the error handler we are finished here.
if (defined('IN_ERROR_HANDLER'))
{
@@ -276,7 +358,7 @@ class user extends \phpbb\session
}
// Is board disabled and user not an admin or moderator?
- if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
+ if ($config['board_disable'] && !defined('IN_LOGIN') && !defined('SKIP_CHECK_DISABLED') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
{
if ($this->data['is_bot'])
{
@@ -345,6 +427,8 @@ class user extends \phpbb\session
}
}
+ $this->is_setup_flag = true;
+
return;
}
@@ -358,103 +442,13 @@ class user extends \phpbb\session
*
* If the first parameter is an array, the elements are used as keys and subkeys to get the language entry:
* Example: <samp>$user->lang(array('datetime', 'AGO'), 1)</samp> uses $user->lang['datetime']['AGO'] as language entry.
+ *
+ * @deprecated 3.2.0-dev (To be removed 4.0.0)
*/
function lang()
{
$args = func_get_args();
- $key = $args[0];
-
- if (is_array($key))
- {
- $lang = &$this->lang[array_shift($key)];
-
- foreach ($key as $_key)
- {
- $lang = &$lang[$_key];
- }
- }
- else
- {
- $lang = &$this->lang[$key];
- }
-
- // Return if language string does not exist
- if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
- {
- return $key;
- }
-
- // If the language entry is a string, we simply mimic sprintf() behaviour
- if (is_string($lang))
- {
- if (sizeof($args) == 1)
- {
- return $lang;
- }
-
- // Replace key with language entry and simply pass along...
- $args[0] = $lang;
- return call_user_func_array('sprintf', $args);
- }
- else if (sizeof($lang) == 0)
- {
- // If the language entry is an empty array, we just return the language key
- return $args[0];
- }
-
- // It is an array... now handle different nullar/singular/plural forms
- $key_found = false;
-
- // We now get the first number passed and will select the key based upon this number
- for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++)
- {
- if (is_int($args[$i]) || is_float($args[$i]))
- {
- if ($args[$i] == 0 && isset($lang[0]))
- {
- // We allow each translation using plural forms to specify a version for the case of 0 things,
- // so that "0 users" may be displayed as "No users".
- $key_found = 0;
- break;
- }
- else
- {
- $use_plural_form = $this->get_plural_form($args[$i]);
- if (isset($lang[$use_plural_form]))
- {
- // The key we should use exists, so we use it.
- $key_found = $use_plural_form;
- }
- else
- {
- // If the key we need to use does not exist, we fall back to the previous one.
- $numbers = array_keys($lang);
-
- foreach ($numbers as $num)
- {
- if ($num > $use_plural_form)
- {
- break;
- }
-
- $key_found = $num;
- }
- }
- break;
- }
- }
- }
-
- // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
- if ($key_found === false)
- {
- $numbers = array_keys($lang);
- $key_found = end($numbers);
- }
-
- // Use the language string we determined and pass it to sprintf()
- $args[0] = $lang[$key_found];
- return call_user_func_array('sprintf', $args);
+ return call_user_func_array(array($this->language, 'lang'), $args);
}
/**
@@ -464,24 +458,22 @@ class user extends \phpbb\session
* @param $number int|float The number we want to get the plural case for. Float numbers are floored.
* @param $force_rule mixed False to use the plural rule of the language package
* or an integer to force a certain plural rule
- * @return int The plural-case we need to use for the number plural-rule combination
+ * @return int|bool The plural-case we need to use for the number plural-rule combination, false if $force_rule
+ * was invalid.
+ *
+ * @deprecated: 3.2.0-dev (To be removed: 3.3.0)
*/
function get_plural_form($number, $force_rule = false)
{
- $number = (int) $number;
-
- // Default to English system
- $plural_rule = ($force_rule !== false) ? $force_rule : ((isset($this->lang['PLURAL_RULE'])) ? $this->lang['PLURAL_RULE'] : 1);
-
- return phpbb_get_plural_form($plural_rule, $number);
+ return $this->language->get_plural_form($number, $force_rule);
}
/**
* Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
*
* @param mixed $lang_set specifies the language entries to include
- * @param bool $use_db internal variable for recursion, do not use
- * @param bool $use_help internal variable for recursion, do not use
+ * @param bool $use_db internal variable for recursion, do not use @deprecated 3.2.0-dev (To be removed: 3.3.0)
+ * @param bool $use_help internal variable for recursion, do not use @deprecated 3.2.0-dev (To be removed: 3.3.0)
* @param string $ext_name The extension to load language from, or empty for core files
*
* Examples:
@@ -492,11 +484,14 @@ class user extends \phpbb\session
* $lang_set = 'posting'
* $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
* </code>
+ *
+ * Note: $use_db and $use_help should be removed. The old function was kept for BC purposes,
+ * so the BC logic is handled here.
+ *
+ * @deprecated: 3.2.0-dev (To be removed: 3.3.0)
*/
function add_lang($lang_set, $use_db = false, $use_help = false, $ext_name = '')
{
- global $phpEx;
-
if (is_array($lang_set))
{
foreach ($lang_set as $key => $lang_file)
@@ -507,6 +502,7 @@ class user extends \phpbb\session
if ($key == 'db')
{
+ // This is never used
$this->add_lang($lang_file, true, $use_help, $ext_name);
}
else if ($key == 'help')
@@ -515,7 +511,7 @@ class user extends \phpbb\session
}
else if (!is_array($lang_file))
{
- $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help, $ext_name);
+ $this->set_lang($lang_file, $use_help, $ext_name);
}
else
{
@@ -526,17 +522,50 @@ class user extends \phpbb\session
}
else if ($lang_set)
{
- $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help, $ext_name);
+ $this->set_lang($lang_set, $use_help, $ext_name);
}
}
/**
+ * BC function for loading language files
+ *
+ * @deprecated 3.2.0-dev (To be removed: 3.3.0)
+ */
+ private function set_lang($lang_set, $use_help, $ext_name)
+ {
+ if (empty($ext_name))
+ {
+ $ext_name = null;
+ }
+
+ if ($use_help && strpos($lang_set, '/') !== false)
+ {
+ $component = dirname($lang_set) . '/help_' . basename($lang_set);
+
+ if ($component[0] === '/')
+ {
+ $component = substr($component, 1);
+ }
+ }
+ else
+ {
+ $component = (($use_help) ? 'help_' : '') . $lang_set;
+ }
+
+ $this->language->add_lang($component, $ext_name);
+ }
+
+ /**
* Add Language Items from an extension - use_db and use_help are assigned where needed (only use them to force inclusion)
*
* @param string $ext_name The extension to load language from, or empty for core files
* @param mixed $lang_set specifies the language entries to include
* @param bool $use_db internal variable for recursion, do not use
* @param bool $use_help internal variable for recursion, do not use
+ *
+ * Note: $use_db and $use_help should be removed. Kept for BC purposes.
+ *
+ * @deprecated: 3.2.0-dev (To be removed: 3.3.0)
*/
function add_lang_ext($ext_name, $lang_set, $use_db = false, $use_help = false)
{
@@ -549,107 +578,6 @@ class user extends \phpbb\session
}
/**
- * Set language entry (called by add_lang)
- * @access private
- */
- function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false, $ext_name = '')
- {
- global $phpbb_root_path, $phpEx;
-
- // Make sure the language name is set (if the user setup did not happen it is not set)
- if (!$this->lang_name)
- {
- global $config;
- $this->lang_name = basename($config['default_lang']);
- }
-
- // $lang == $this->lang
- // $help == $this->help
- // - add appropriate variables here, name them as they are used within the language file...
- if (!$use_db)
- {
- if ($use_help && strpos($lang_file, '/') !== false)
- {
- $filename = dirname($lang_file) . '/help_' . basename($lang_file);
- }
- else
- {
- $filename = (($use_help) ? 'help_' : '') . $lang_file;
- }
-
- if ($ext_name)
- {
- global $phpbb_extension_manager;
- $ext_path = $phpbb_extension_manager->get_extension_path($ext_name, true);
-
- $lang_path = $ext_path . 'language/';
- }
- else
- {
- $lang_path = $this->lang_path;
- }
-
- if (strpos($phpbb_root_path . $filename, $lang_path . $this->lang_name . '/') === 0)
- {
- $language_filename = $phpbb_root_path . $filename;
- }
- else
- {
- $language_filename = $lang_path . $this->lang_name . '/' . $filename . '.' . $phpEx;
- }
-
- // If we are in install, try to use the updated version, when available
- $install_language_filename = str_replace('language/', 'install/update/new/language/', $language_filename);
- if (defined('IN_INSTALL') && file_exists($install_language_filename))
- {
- $language_filename = $install_language_filename;
- }
-
- if (!file_exists($language_filename))
- {
- global $config;
-
- if ($this->lang_name == 'en')
- {
- // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en.
- $language_filename = str_replace($lang_path . 'en', $lang_path . $this->data['user_lang'], $language_filename);
- trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
- }
- else if ($this->lang_name == basename($config['default_lang']))
- {
- // Fall back to the English Language
- $this->lang_name = 'en';
- $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name);
- }
- else if ($this->lang_name == $this->data['user_lang'])
- {
- // Fall back to the board default language
- $this->lang_name = basename($config['default_lang']);
- $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name);
- }
-
- // Reset the lang name
- $this->lang_name = (file_exists($lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
- return;
- }
-
- // Do not suppress error if in DEBUG mode
- $include_result = (defined('DEBUG')) ? (include $language_filename) : (@include $language_filename);
-
- if ($include_result === false)
- {
- trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
- }
- }
- else if ($use_db)
- {
- // Get Database Language Strings
- // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
- // For example: help:faq, posting
- }
- }
-
- /**
* Format user date
*
* @param int $gmepoch unix timestamp
@@ -667,7 +595,7 @@ class user extends \phpbb\session
$utc = new \DateTimeZone('UTC');
}
- $time = new \phpbb\datetime($this, "@$gmepoch", $utc);
+ $time = new $this->datetime($this, "@$gmepoch", $utc);
$time->setTimezone($this->timezone);
return $time->format($format, $forcedate);
@@ -684,7 +612,7 @@ class user extends \phpbb\session
public function create_datetime($time = 'now', \DateTimeZone $timezone = null)
{
$timezone = $timezone ?: $this->timezone;
- return new \phpbb\datetime($this, $time, $timezone);
+ return new $this->datetime($this, $time, $timezone);
}
/**
@@ -754,8 +682,14 @@ class user extends \phpbb\session
*/
function img($img, $alt = '')
{
- $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt;
- return '<span class="imageset ' . $img . '">' . $alt . '</span>';
+ $title = '';
+
+ if ($alt)
+ {
+ $alt = $this->language->lang($alt);
+ $title = ' title="' . $alt . '"';
+ }
+ return '<span class="imageset ' . $img . '"' . $title . '>' . $alt . '</span>';
}
/**
@@ -813,8 +747,6 @@ class user extends \phpbb\session
*/
function leave_newly_registered()
{
- global $db;
-
if (empty($this->data['user_new']))
{
return false;
diff --git a/phpBB/phpbb/user_loader.php b/phpBB/phpbb/user_loader.php
index c1d69802f8..cdd28329db 100644
--- a/phpBB/phpbb/user_loader.php
+++ b/phpBB/phpbb/user_loader.php
@@ -1,9 +1,13 @@
<?php
/**
*
-* @package phpBB3
-* @copyright (c) 2012 phpBB Group
-* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
*
*/
@@ -19,7 +23,7 @@ namespace phpbb;
*/
class user_loader
{
- /** @var \phpbb\db\driver\driver */
+ /** @var \phpbb\db\driver\driver_interface */
protected $db = null;
/** @var string */
@@ -41,12 +45,12 @@ class user_loader
/**
* User loader constructor
*
- * @param \phpbb\db\driver\driver $db A database connection
+ * @param \phpbb\db\driver\driver_interface $db A database connection
* @param string $phpbb_root_path Path to the phpbb includes directory.
* @param string $php_ext php file extension
* @param string $users_table The name of the database table (phpbb_users)
*/
- public function __construct(\phpbb\db\driver\driver $db, $phpbb_root_path, $php_ext, $users_table)
+ public function __construct(\phpbb\db\driver\driver_interface $db, $phpbb_root_path, $php_ext, $users_table)
{
$this->db = $db;
@@ -171,25 +175,28 @@ class user_loader
/**
* Get avatar
*
- * @param int $user_id User ID of the user you want to retreive the avatar for
+ * @param int $user_id User ID of the user you want to retrieve the avatar for
* @param bool $query Should we query the database if this user has not yet been loaded?
* Typically this should be left as false and you should make sure
* you load users ahead of time with load_users()
+ * @param bool @lazy If true, will be lazy loaded (requires JS)
* @return string
*/
- public function get_avatar($user_id, $query = false)
+ public function get_avatar($user_id, $query = false, $lazy = false)
{
if (!($user = $this->get_user($user_id, $query)))
{
return '';
}
- if (!function_exists('get_user_avatar'))
- {
- include($this->phpbb_root_path . 'includes/functions_display.' . $this->php_ext);
- }
+ $row = array(
+ 'avatar' => $user['user_avatar'],
+ 'avatar_type' => $user['user_avatar_type'],
+ 'avatar_width' => $user['user_avatar_width'],
+ 'avatar_height' => $user['user_avatar_height'],
+ );
- return get_user_avatar($user['user_avatar'], $user['user_avatar_type'], $user['user_avatar_width'], $user['user_avatar_height']);
+ return phpbb_get_avatar($row, 'USER_AVATAR', false, $lazy);
}
/**
@@ -208,7 +215,7 @@ class user_loader
return '';
}
- if (!function_exists('get_user_rank'))
+ if (!function_exists('phpbb_get_user_rank'))
{
include($this->phpbb_root_path . 'includes/functions_display.' . $this->php_ext);
}
@@ -219,7 +226,10 @@ class user_loader
'rank_img_src',
);
- get_user_rank($user['user_rank'], (($user['user_id'] == ANONYMOUS) ? false : $user['user_posts']), $rank['rank_title'], $rank['rank_img'], $rank['rank_img_src']);
+ $user_rank_data = phpbb_get_user_rank($user, (($user['user_id'] == ANONYMOUS) ? false : $user['user_posts']));
+ $rank['rank_title'] = $user_rank_data['title'];
+ $rank['rank_img'] = $user_rank_data['img'];
+ $rank['rank_img_src'] = $user_rank_data['img_src'];
return $rank;
}
diff --git a/phpBB/phpbb/version_helper.php b/phpBB/phpbb/version_helper.php
new file mode 100644
index 0000000000..a1e66ba8fe
--- /dev/null
+++ b/phpBB/phpbb/version_helper.php
@@ -0,0 +1,306 @@
+<?php
+/**
+*
+* This file is part of the phpBB Forum Software package.
+*
+* @copyright (c) phpBB Limited <https://www.phpbb.com>
+* @license GNU General Public License, version 2 (GPL-2.0)
+*
+* For full copyright and license information, please see
+* the docs/CREDITS.txt file.
+*
+*/
+
+namespace phpbb;
+
+/**
+ * Class to handle version checking and comparison
+ */
+class version_helper
+{
+ /**
+ * @var string Host
+ */
+ protected $host = 'version.phpbb.com';
+
+ /**
+ * @var string Path to file
+ */
+ protected $path = '/phpbb';
+
+ /**
+ * @var string File name
+ */
+ protected $file = 'versions.json';
+
+ /**
+ * @var bool Use SSL or not
+ */
+ protected $use_ssl = false;
+
+ /**
+ * @var string Current version installed
+ */
+ protected $current_version;
+
+ /**
+ * @var null|string Null to not force stability, 'unstable' or 'stable' to
+ * force the corresponding stability
+ */
+ protected $force_stability;
+
+ /** @var \phpbb\cache\service */
+ protected $cache;
+
+ /** @var \phpbb\config\config */
+ protected $config;
+
+ /** @var \phpbb\file_downloader */
+ protected $file_downloader;
+
+ /** @var \phpbb\user */
+ protected $user;
+
+ /**
+ * Constructor
+ *
+ * @param \phpbb\cache\service $cache
+ * @param \phpbb\config\config $config
+ * @param \phpbb\file_downloader $file_downloader
+ * @param \phpbb\user $user
+ */
+ public function __construct(\phpbb\cache\service $cache, \phpbb\config\config $config, \phpbb\file_downloader $file_downloader, \phpbb\user $user)
+ {
+ $this->cache = $cache;
+ $this->config = $config;
+ $this->file_downloader = $file_downloader;
+ $this->user = $user;
+
+ if (defined('PHPBB_QA'))
+ {
+ $this->force_stability = 'unstable';
+ }
+
+ $this->current_version = $this->config['version'];
+ }
+
+ /**
+ * Set location to the file
+ *
+ * @param string $host Host (e.g. version.phpbb.com)
+ * @param string $path Path to file (e.g. /phpbb)
+ * @param string $file File name (Default: versions.json)
+ * @param bool $use_ssl Use SSL or not (Default: false)
+ * @return version_helper
+ */
+ public function set_file_location($host, $path, $file = 'versions.json', $use_ssl = false)
+ {
+ $this->host = $host;
+ $this->path = $path;
+ $this->file = $file;
+ $this->use_ssl = $use_ssl;
+
+ return $this;
+ }
+
+ /**
+ * Set current version
+ *
+ * @param string $version The current version
+ * @return version_helper
+ */
+ public function set_current_version($version)
+ {
+ $this->current_version = $version;
+
+ return $this;
+ }
+
+ /**
+ * Over-ride the stability to force check to include unstable versions
+ *
+ * @param null|string Null to not force stability, 'unstable' or 'stable' to
+ * force the corresponding stability
+ * @return version_helper
+ */
+ public function force_stability($stability)
+ {
+ $this->force_stability = $stability;
+
+ return $this;
+ }
+
+ /**
+ * Wrapper for version_compare() that allows using uppercase A and B
+ * for alpha and beta releases.
+ *
+ * See http://www.php.net/manual/en/function.version-compare.php
+ *
+ * @param string $version1 First version number
+ * @param string $version2 Second version number
+ * @param string $operator Comparison operator (optional)
+ *
+ * @return mixed Boolean (true, false) if comparison operator is specified.
+ * Integer (-1, 0, 1) otherwise.
+ */
+ public function compare($version1, $version2, $operator = null)
+ {
+ return phpbb_version_compare($version1, $version2, $operator);
+ }
+
+ /**
+ * Check whether or not a version is "stable"
+ *
+ * Stable means only numbers OR a pl release
+ *
+ * @param string $version
+ * @return bool Bool true or false
+ */
+ public function is_stable($version)
+ {
+ $matches = false;
+ preg_match('/^[\d\.]+/', $version, $matches);
+
+ if (empty($matches[0]))
+ {
+ return false;
+ }
+
+ return $this->compare($version, $matches[0], '>=');
+ }
+
+ /**
+ * Gets the latest version for the current branch the user is on
+ *
+ * @param bool $force_update Ignores cached data. Defaults to false.
+ * @param bool $force_cache Force the use of the cache. Override $force_update.
+ * @return string
+ * @throws \RuntimeException
+ */
+ public function get_latest_on_current_branch($force_update = false, $force_cache = false)
+ {
+ $versions = $this->get_versions_matching_stability($force_update, $force_cache);
+
+ $self = $this;
+ $current_version = $this->current_version;
+
+ // Filter out any versions less than to the current version
+ $versions = array_filter($versions, function($data) use ($self, $current_version) {
+ return $self->compare($data['current'], $current_version, '>=');
+ });
+
+ // Get the lowest version from the previous list.
+ return array_reduce($versions, function($value, $data) use ($self) {
+ if ($value === null || $self->compare($data['current'], $value, '<'))
+ {
+ return $data['current'];
+ }
+
+ return $value;
+ });
+ }
+
+ /**
+ * Obtains the latest version information
+ *
+ * @param bool $force_update Ignores cached data. Defaults to false.
+ * @param bool $force_cache Force the use of the cache. Override $force_update.
+ * @return string
+ * @throws \RuntimeException
+ */
+ public function get_suggested_updates($force_update = false, $force_cache = false)
+ {
+ $versions = $this->get_versions_matching_stability($force_update, $force_cache);
+
+ $self = $this;
+ $current_version = $this->current_version;
+
+ // Filter out any versions less than or equal to the current version
+ return array_filter($versions, function($data) use ($self, $current_version) {
+ return $self->compare($data['current'], $current_version, '>');
+ });
+ }
+
+ /**
+ * Obtains the latest version information matching the stability of the current install
+ *
+ * @param bool $force_update Ignores cached data. Defaults to false.
+ * @param bool $force_cache Force the use of the cache. Override $force_update.
+ * @return string Version info
+ * @throws \RuntimeException
+ */
+ public function get_versions_matching_stability($force_update = false, $force_cache = false)
+ {
+ $info = $this->get_versions($force_update, $force_cache);
+
+ if ($this->force_stability !== null)
+ {
+ return ($this->force_stability === 'unstable') ? $info['unstable'] : $info['stable'];
+ }
+
+ return ($this->is_stable($this->current_version)) ? $info['stable'] : $info['unstable'];
+ }
+
+ /**
+ * Obtains the latest version information
+ *
+ * @param bool $force_update Ignores cached data. Defaults to false.
+ * @param bool $force_cache Force the use of the cache. Override $force_update.
+ * @return string Version info, includes stable and unstable data
+ * @throws \RuntimeException
+ */
+ public function get_versions($force_update = false, $force_cache = false)
+ {
+ $cache_file = '_versioncheck_' . $this->host . $this->path . $this->file . $this->use_ssl;
+
+ $info = $this->cache->get($cache_file);
+
+ if ($info === false && $force_cache)
+ {
+ throw new \RuntimeException($this->user->lang('VERSIONCHECK_FAIL'));
+ }
+ else if ($info === false || $force_update)
+ {
+ try {
+ $info = $this->file_downloader->get($this->host, $this->path, $this->file, $this->use_ssl ? 443 : 80);
+ }
+ catch (\phpbb\exception\runtime_exception $exception)
+ {
+ $prepare_parameters = array_merge(array($exception->getMessage()), $exception->get_parameters());
+ throw new \RuntimeException(call_user_func_array(array($this->user, 'lang'), $prepare_parameters));
+ }
+ $error_string = $this->file_downloader->get_error_string();
+
+ if (!empty($error_string))
+ {
+ throw new \RuntimeException($error_string);
+ }
+
+ $info = json_decode($info, true);
+
+ // Sanitize any data we retrieve from a server
+ if (!empty($info))
+ {
+ $json_sanitizer = function (&$value, $key) {
+ $type_cast_helper = new \phpbb\request\type_cast_helper();
+ $type_cast_helper->set_var($value, $value, gettype($value), true);
+ };
+ array_walk_recursive($info, $json_sanitizer);
+ }
+
+ if (empty($info['stable']) && empty($info['unstable']))
+ {
+ $this->user->add_lang('acp/common');
+
+ throw new \RuntimeException($this->user->lang('VERSIONCHECK_FAIL'));
+ }
+
+ $info['stable'] = (empty($info['stable'])) ? array() : $info['stable'];
+ $info['unstable'] = (empty($info['unstable'])) ? $info['stable'] : $info['unstable'];
+
+ $this->cache->put($cache_file, $info, 86400); // 24 hours
+ }
+
+ return $info;
+ }
+}
diff --git a/phpBB/phpbb/viewonline_helper.php b/phpBB/phpbb/viewonline_helper.php
new file mode 100644
index 0000000000..89915f2228
--- /dev/null
+++ b/phpBB/phpbb/viewonline_helper.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;
+
+/**
+* Class to handle viewonline related tasks
+*/
+class viewonline_helper
+{
+ /** @var \phpbb\filesystem\filesystem_interface */
+ protected $filesystem;
+
+ /**
+ * @param \phpbb\filesystem\filesystem_interface $filesystem phpBB's filesystem service
+ */
+ public function __construct(\phpbb\filesystem\filesystem_interface $filesystem)
+ {
+ $this->filesystem = $filesystem;
+ }
+
+ /**
+ * Get user page
+ *
+ * @param string $session_page User's session page
+ * @return array Match array filled by preg_match()
+ */
+ public function get_user_page($session_page)
+ {
+ $session_page = $this->filesystem->clean_path($session_page);
+ if (strpos($session_page, './') === 0)
+ {
+ $session_page = substr($session_page, 2);
+ }
+
+ preg_match('#^((\.\./)*([a-z0-9/_-]+))#i', $session_page, $on_page);
+ if (empty($on_page))
+ {
+ $on_page[1] = '';
+ }
+
+ return $on_page;
+ }
+}