diff --git a/phpBB/adm/style/acp_ext_enable.html b/phpBB/adm/style/acp_ext_enable.html
index 3f7be2c847..35585207eb 100644
--- a/phpBB/adm/style/acp_ext_enable.html
+++ b/phpBB/adm/style/acp_ext_enable.html
@@ -7,7 +7,13 @@
{L_EXTENSIONS_EXPLAIN}
{L_ENABLE_EXPLAIN}
-
+
+
+
{L_MIGRATION_EXCEPTION_ERROR}
+
{MIGRATOR_ERROR}
+
{L_RETURN}
+
+
diff --git a/phpBB/adm/style/acp_ext_purge.html b/phpBB/adm/style/acp_ext_purge.html
index 00a58721cb..94bef82ca5 100644
--- a/phpBB/adm/style/acp_ext_purge.html
+++ b/phpBB/adm/style/acp_ext_purge.html
@@ -7,7 +7,13 @@
{L_EXTENSIONS_EXPLAIN}
{L_PURGE_EXPLAIN}
-
+
+
+
{L_MIGRATION_EXCEPTION_ERROR}
+
{MIGRATOR_ERROR}
+
{L_RETURN}
+
+
diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js
index 16ed04746d..9909359c2c 100644
--- a/phpBB/assets/javascript/core.js
+++ b/phpBB/assets/javascript/core.js
@@ -492,6 +492,19 @@ phpbb.timezonePreselectSelect = function(forceSelector) {
}
};
+// Toggle notification list
+$('#notification_list_button').click(function(e) {
+ $('#notification_list').toggle();
+ e.preventDefault();
+});
+$('#phpbb').click(function(e) {
+ var target = $(e.target);
+
+ if (!target.is('#notification_list') && !target.is('#notification_list_button') && !target.parents().is('#notification_list')) {
+ $('#notification_list').hide();
+ }
+});
+
phpbb.ajaxCallbacks = {};
/**
diff --git a/phpBB/config/migrator.yml b/phpBB/config/migrator.yml
index 999a2d41a3..42445ef9bf 100644
--- a/phpBB/config/migrator.yml
+++ b/phpBB/config/migrator.yml
@@ -10,6 +10,8 @@ services:
- %core.php_ext%
- %core.table_prefix%
- @migrator.tool_collection
+ calls:
+ - [set_extension_manager, [@ext.manager]]
migrator.tool_collection:
class: phpbb_di_service_collection
diff --git a/phpBB/config/notifications.yml b/phpBB/config/notifications.yml
new file mode 100644
index 0000000000..60aa63a854
--- /dev/null
+++ b/phpBB/config/notifications.yml
@@ -0,0 +1,314 @@
+services:
+ notification.type_collection:
+ class: phpbb_di_service_collection
+ arguments:
+ - @service_container
+ tags:
+ - { name: service_collection, tag: notification.type }
+
+ notification.method_collection:
+ class: phpbb_di_service_collection
+ arguments:
+ - @service_container
+ tags:
+ - { name: service_collection, tag: notification.method }
+
+ notification.type.approve_post:
+ class: phpbb_notification_type_approve_post
+ scope: prototype # scope MUST be prototype for this to work! # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.approve_topic:
+ class: phpbb_notification_type_approve_topic
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.bookmark:
+ class: phpbb_notification_type_bookmark
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.disapprove_post:
+ class: phpbb_notification_type_disapprove_post
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.disapprove_topic:
+ class: phpbb_notification_type_disapprove_topic
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.pm:
+ class: phpbb_notification_type_pm
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.post:
+ class: phpbb_notification_type_post
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.post_in_queue:
+ class: phpbb_notification_type_post_in_queue
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.quote:
+ class: phpbb_notification_type_quote
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.report_pm:
+ class: phpbb_notification_type_report_pm
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.report_pm_closed:
+ class: phpbb_notification_type_report_pm_closed
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.report_post:
+ class: phpbb_notification_type_report_post
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.report_post_closed:
+ class: phpbb_notification_type_report_post
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.topic:
+ class: phpbb_notification_type_topic
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.type.topic_in_queue:
+ class: phpbb_notification_type_topic_in_queue
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+ tags:
+ - { name: notification.type }
+
+ notification.method.email:
+ class: phpbb_notification_method_email
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ tags:
+ - { name: notification.method }
+
+ notification.method.jabber:
+ class: phpbb_notification_method_jabber
+ scope: prototype # scope MUST be prototype for this to work!
+ arguments:
+ - @user_loader
+ - @dbal.conn
+ - @cache.driver
+ - @user
+ - @auth
+ - @config
+ - %core.root_path%
+ - %core.php_ext%
+ tags:
+ - { name: notification.method }
diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml
index 645dabdca0..0e300a6b56 100644
--- a/phpBB/config/services.yml
+++ b/phpBB/config/services.yml
@@ -1,6 +1,7 @@
imports:
- { resource: tables.yml }
- { resource: cron_tasks.yml }
+ - { resource: notifications.yml }
- { resource: migrator.yml }
- { resource: avatars.yml }
@@ -120,12 +121,15 @@ services:
ext.manager:
class: phpbb_extension_manager
arguments:
+ - @service_container
- @dbal.conn
- @config
- %tables.ext%
- %core.root_path%
- .%core.php_ext%
- @cache.driver
+ calls:
+ - [set_migrator, [@migrator]]
ext.finder:
class: phpbb_extension_finder
@@ -171,6 +175,21 @@ services:
tags:
- { name: kernel.event_subscriber }
+ notification_manager:
+ class: phpbb_notification_manager
+ arguments:
+ - @notification.type_collection
+ - @notification.method_collection
+ - @service_container
+ - @user_loader
+ - @dbal.conn
+ - @user
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.notification_types%
+ - %tables.notifications%
+ - %tables.user_notifications%
+
request:
class: phpbb_request
@@ -213,3 +232,11 @@ services:
user:
class: phpbb_user
+
+ user_loader:
+ class: phpbb_user_loader
+ arguments:
+ - @dbal.conn
+ - %core.root_path%
+ - %core.php_ext%
+ - %tables.users%
diff --git a/phpBB/config/tables.yml b/phpBB/config/tables.yml
index dd53417b1c..10db8fbab6 100644
--- a/phpBB/config/tables.yml
+++ b/phpBB/config/tables.yml
@@ -1,5 +1,9 @@
parameters:
tables.config: %core.table_prefix%config
tables.ext: %core.table_prefix%ext
+ tables.notification_types: %core.table_prefix%notification_types
+ tables.notifications: %core.table_prefix%notifications
+ tables.user_notifications: %core.table_prefix%user_notifications
+ tables.users: %core.table_prefix%users
tables.migrations: %core.table_prefix%migrations
tables.modules: %core.table_prefix%modules
diff --git a/phpBB/develop/create_schema_files.php b/phpBB/develop/create_schema_files.php
index df721d4907..40259af246 100644
--- a/phpBB/develop/create_schema_files.php
+++ b/phpBB/develop/create_schema_files.php
@@ -1308,6 +1308,32 @@ function get_schema_struct()
),
);
+ $schema_data['phpbb_notification_types'] = array(
+ 'COLUMNS' => array(
+ 'notification_type' => array('VCHAR:255', ''),
+ 'notification_type_enabled' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => array('notification_type', 'notification_type_enabled'),
+ );
+
+ $schema_data['phpbb_notifications'] = array(
+ 'COLUMNS' => array(
+ 'notification_id' => array('UINT', NULL, 'auto_increment'),
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'item_parent_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notification_read' => array('BOOL', 0),
+ 'notification_time' => array('TIMESTAMP', 1),
+ 'notification_data' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'notification_id',
+ 'KEYS' => array(
+ 'item_ident' => array('INDEX', array('item_type', 'item_id')),
+ 'user' => array('INDEX', array('user_id', 'notification_read')),
+ ),
+ );
+
$schema_data['phpbb_poll_options'] = array(
'COLUMNS' => array(
'poll_option_id' => array('TINT:4', 0),
@@ -1769,6 +1795,16 @@ function get_schema_struct()
),
);
+ $schema_data['phpbb_user_notifications'] = array(
+ 'COLUMNS' => array(
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'method' => array('VCHAR:255', ''),
+ 'notify' => array('BOOL', 1),
+ ),
+ );
+
$schema_data['phpbb_user_group'] = array(
'COLUMNS' => array(
'group_id' => array('UINT', 0),
diff --git a/phpBB/docs/AUTHORS b/phpBB/docs/AUTHORS
index c63bd743a7..be82f06c96 100644
--- a/phpBB/docs/AUTHORS
+++ b/phpBB/docs/AUTHORS
@@ -23,11 +23,11 @@ involved in phpBB.
phpBB Lead Developer: naderman (Nils Adermann)
phpBB Developers: bantu (Andreas Fischer)
+ EXreaction (Nathan Guse)
igorw (Igor Wiedler)
imkingdavid (David King)
nickvergessen (Joas Schilling)
Oleg (Oleg Pudeyev)
- rxu (Ruslan Uzdenov)
Contributions by: leviatan21 (Gabriel Vazquez)
Raimon (Raimon Meuldijk)
@@ -53,6 +53,7 @@ phpBB Developers: A_Jelly_Doughnut (Josh Woody) [01/2010 - 11/2010]
dhn (Dominik Dröscher) [05/2007 - 01/2011]
GrahamJE (Graham Eames) [09/2005 - 11/2006]
kellanved (Henry Sudhof) [04/2007 - 03/2011]
+ rxu (Ruslan Uzdenov) [04/2010 - 12/2012]
TerraFrost (Jim Wigginton) [04/2009 - 01/2011]
ToonArmy (Chris Smith) [06/2008 - 11/2011]
Vic D'Elfant (Vic D'Elfant) [04/2007 - 04/2009]
diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php
index 93e1dd71fe..6543427677 100644
--- a/phpBB/includes/acp/acp_board.php
+++ b/phpBB/includes/acp/acp_board.php
@@ -329,6 +329,7 @@ class acp_board
'load_online_time' => array('lang' => 'ONLINE_LENGTH', 'validate' => 'int:0', 'type' => 'text:4:3', 'explain' => true, 'append' => ' ' . $user->lang['MINUTES']),
'legend2' => 'GENERAL_OPTIONS',
+ 'load_notifications' => array('lang' => 'LOAD_NOTIFICATIONS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
'load_db_track' => array('lang' => 'YES_POST_MARKING', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
'load_db_lastread' => array('lang' => 'YES_READ_MARKING', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
'load_anon_lastread' => array('lang' => 'YES_ANON_READ_MARKING', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
@@ -428,8 +429,8 @@ class acp_board
'board_email_form' => array('lang' => 'BOARD_EMAIL_FORM', 'validate' => 'bool', 'type' => 'radio:enabled_disabled', 'explain' => true),
'email_function_name' => array('lang' => 'EMAIL_FUNCTION_NAME', 'validate' => 'string', 'type' => 'text:20:50', 'explain' => true),
'email_package_size' => array('lang' => 'EMAIL_PACKAGE_SIZE', 'validate' => 'int:0', 'type' => 'text:5:5', 'explain' => true),
- 'board_contact' => array('lang' => 'CONTACT_EMAIL', 'validate' => 'string', 'type' => 'text:25:100', 'explain' => true),
- 'board_email' => array('lang' => 'ADMIN_EMAIL', 'validate' => 'string', 'type' => 'text:25:100', 'explain' => true),
+ 'board_contact' => array('lang' => 'CONTACT_EMAIL', 'validate' => 'email', 'type' => 'text:25:100', 'explain' => true),
+ 'board_email' => array('lang' => 'ADMIN_EMAIL', 'validate' => 'email', 'type' => 'text:25:100', 'explain' => true),
'board_email_sig' => array('lang' => 'EMAIL_SIG', 'validate' => 'string', 'type' => 'textarea:5:30', 'explain' => true),
'board_hide_emails' => array('lang' => 'BOARD_HIDE_EMAILS', 'validate' => 'bool', 'type' => 'radio:yes_no', 'explain' => true),
diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php
index a0bcf62ecc..24211196bd 100644
--- a/phpBB/includes/acp/acp_extensions.php
+++ b/phpBB/includes/acp/acp_extensions.php
@@ -37,7 +37,7 @@ class acp_extensions
$this->template = $template;
$this->user = $user;
- $user->add_lang(array('install', 'acp/extensions'));
+ $user->add_lang(array('install', 'acp/extensions', 'migrator'));
$this->page_title = 'ACP_EXTENSIONS';
@@ -103,11 +103,18 @@ class acp_extensions
trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action));
}
- if ($phpbb_extension_manager->enable_step($ext_name))
+ try
{
- $template->assign_var('S_NEXT_STEP', true);
+ if ($phpbb_extension_manager->enable_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
- meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name));
+ meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name));
+ }
+ }
+ catch (phpbb_db_migration_exception $e)
+ {
+ $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));
}
$this->tpl_name = 'acp_ext_enable';
@@ -156,11 +163,18 @@ class acp_extensions
break;
case 'purge':
- if ($phpbb_extension_manager->purge_step($ext_name))
+ try
{
- $template->assign_var('S_NEXT_STEP', true);
+ if ($phpbb_extension_manager->purge_step($ext_name))
+ {
+ $template->assign_var('S_NEXT_STEP', true);
- meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name));
+ meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name));
+ }
+ }
+ catch (phpbb_db_migration_exception $e)
+ {
+ $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user));
}
$this->tpl_name = 'acp_ext_purge';
diff --git a/phpBB/includes/acp/acp_groups.php b/phpBB/includes/acp/acp_groups.php
index 56063759c9..0e67dd51aa 100644
--- a/phpBB/includes/acp/acp_groups.php
+++ b/phpBB/includes/acp/acp_groups.php
@@ -125,13 +125,34 @@ class acp_groups
{
trigger_error($user->lang['NO_GROUP'] . adm_back_link($this->u_action), E_USER_WARNING);
}
+ else if (empty($mark_ary))
+ {
+ trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING);
+ }
if (confirm_box(true))
{
$group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+ group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row);
+ trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
+ }
+ else
+ {
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
+ }
- if (!sizeof($mark_ary))
+ break;
+ case 'set_default_on_all':
+ if (confirm_box(true))
{
+ $group_name = ($group_row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $group_row['group_name']] : $group_row['group_name'];
+
$start = 0;
do
@@ -162,28 +183,25 @@ class acp_groups
$db->sql_freeresult($result);
}
while ($start);
+
+ trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
}
else
{
- group_user_attributes('default', $group_id, $mark_ary, false, $group_name, $group_row);
+ confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
+ 'mark' => $mark_ary,
+ 'g' => $group_id,
+ 'i' => $id,
+ 'mode' => $mode,
+ 'action' => $action))
+ );
}
-
- trigger_error($user->lang['GROUP_DEFS_UPDATED'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id));
- }
- else
- {
- confirm_box(false, $user->lang['CONFIRM_OPERATION'], build_hidden_fields(array(
- 'mark' => $mark_ary,
- 'g' => $group_id,
- 'i' => $id,
- 'mode' => $mode,
- 'action' => $action))
- );
- }
-
break;
-
case 'deleteusers':
+ if (empty($mark_ary))
+ {
+ trigger_error($user->lang['NO_USERS'] . adm_back_link($this->u_action . '&action=list&g=' . $group_id), E_USER_WARNING);
+ }
case 'delete':
if (!$group_id)
{
@@ -683,7 +701,7 @@ class acp_groups
'U_ACTION' => $this->u_action . "&g=$group_id",
'U_BACK' => $this->u_action,
'U_FIND_USERNAME' => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser&form=list&field=usernames'),
- 'U_DEFAULT_ALL' => "{$this->u_action}&action=default&g=$group_id",
+ 'U_DEFAULT_ALL' => "{$this->u_action}&action=set_default_on_all&g=$group_id",
));
// Grab the members
diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php
index 52a82004e8..fce26bf45f 100644
--- a/phpBB/includes/acp/acp_modules.php
+++ b/phpBB/includes/acp/acp_modules.php
@@ -535,8 +535,14 @@ class acp_modules
/**
* Get available module information from module files
+ *
+ * @param string $module
+ * @param bool|string $module_class
+ * @param bool $use_all_available Use all available instead of just all
+ * enabled extensions
+ * @return array
*/
- function get_module_infos($module = '', $module_class = false)
+ function get_module_infos($module = '', $module_class = false, $use_all_available = false)
{
global $phpbb_root_path, $phpEx;
@@ -556,7 +562,7 @@ class acp_modules
->extension_directory("/$module_class")
->core_path("includes/$module_class/info/")
->core_prefix($module_class . '_')
- ->get_classes();
+ ->get_classes(true, $use_all_available);
foreach ($modules as $module)
{
diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php
index 03d7059165..174b365af0 100644
--- a/phpBB/includes/acp/info/acp_extensions.php
+++ b/phpBB/includes/acp/info/acp_extensions.php
@@ -16,10 +16,10 @@ class acp_extensions_info
{
return array(
'filename' => 'acp_extensions',
- 'title' => 'ACP_EXTENSIONS_MANAGEMENT',
+ 'title' => 'ACP_EXTENSION_MANAGEMENT',
'version' => '1.0.0',
'modes' => array(
- 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSIONS_MANAGEMENT')),
+ 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSION_MANAGEMENT')),
),
);
}
diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php
index 68c96a2759..36576e5344 100644
--- a/phpBB/includes/constants.php
+++ b/phpBB/includes/constants.php
@@ -240,6 +240,8 @@ define('LOGIN_ATTEMPT_TABLE', $table_prefix . 'login_attempts');
define('MIGRATIONS_TABLE', $table_prefix . 'migrations');
define('MODERATOR_CACHE_TABLE', $table_prefix . 'moderator_cache');
define('MODULES_TABLE', $table_prefix . 'modules');
+define('NOTIFICATION_TYPES_TABLE', $table_prefix . 'notification_types');
+define('NOTIFICATIONS_TABLE', $table_prefix . 'notifications');
define('POLL_OPTIONS_TABLE', $table_prefix . 'poll_options');
define('POLL_VOTES_TABLE', $table_prefix . 'poll_votes');
define('POSTS_TABLE', $table_prefix . 'posts');
@@ -273,6 +275,7 @@ define('TOPICS_POSTED_TABLE', $table_prefix . 'topics_posted');
define('TOPICS_TRACK_TABLE', $table_prefix . 'topics_track');
define('TOPICS_WATCH_TABLE', $table_prefix . 'topics_watch');
define('USER_GROUP_TABLE', $table_prefix . 'user_group');
+define('USER_NOTIFICATIONS_TABLE', $table_prefix . 'user_notifications');
define('USERS_TABLE', $table_prefix . 'users');
define('WARNINGS_TABLE', $table_prefix . 'warnings');
define('WORDS_TABLE', $table_prefix . 'words');
diff --git a/phpBB/includes/datetime.php b/phpBB/includes/datetime.php
index b3462ddf67..3c6d4971b9 100644
--- a/phpBB/includes/datetime.php
+++ b/phpBB/includes/datetime.php
@@ -143,7 +143,7 @@ class phpbb_datetime extends DateTime
'is_short' => strpos($format, self::RELATIVE_WRAPPER) !== false,
'format_short' => substr($format, 0, strpos($format, self::RELATIVE_WRAPPER)) . self::RELATIVE_WRAPPER . self::RELATIVE_WRAPPER . substr(strrchr($format, self::RELATIVE_WRAPPER), 1),
'format_long' => str_replace(self::RELATIVE_WRAPPER, '', $format),
- 'lang' => $user->lang['datetime'],
+ 'lang' => array_filter($user->lang['datetime'], 'is_string'),
);
// Short representation of month in format? Some languages use different terms for the long and short format of May
diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php
index e8c26fa502..983cdc18ea 100644
--- a/phpBB/includes/db/db_tools.php
+++ b/phpBB/includes/db/db_tools.php
@@ -303,7 +303,7 @@ class phpbb_db_tools
* @param phpbb_db_driver $db Database connection
* @param bool $return_statements True if only statements should be returned and no SQL being executed
*/
- function phpbb_db_tools(&$db, $return_statements = false)
+ function phpbb_db_tools(phpbb_db_driver $db, $return_statements = false)
{
$this->db = $db;
$this->return_statements = $return_statements;
diff --git a/phpBB/includes/db/migration/data/30x/3_0_1.php b/phpBB/includes/db/migration/data/30x/3_0_1.php
new file mode 100644
index 0000000000..c996a0138a
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_1.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_1_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10.php b/phpBB/includes/db/migration/data/30x/3_0_10.php
new file mode 100644
index 0000000000..122f93d6b4
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php
new file mode 100644
index 0000000000..0ed05812dc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc1.php
@@ -0,0 +1,30 @@
+config['version'], '3.0.10-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('email_max_chunk_size', 50)),
+
+ array('config.update', array('version', '3.0.10-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php
new file mode 100644
index 0000000000..b14b3b00aa
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php
new file mode 100644
index 0000000000..473057d65d
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_10_rc3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.10-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.10-rc3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11.php b/phpBB/includes/db/migration/data/30x/3_0_11.php
new file mode 100644
index 0000000000..e063c699cc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.11', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php
new file mode 100644
index 0000000000..dddfc0e0e7
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc1.php
@@ -0,0 +1,95 @@
+config['version'], '3.0.11-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_10');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'cleanup_deactivated_styles'))),
+ array('custom', array(array(&$this, 'delete_orphan_private_messages'))),
+
+ array('config.update', array('version', '3.0.11-rc1')),
+ );
+ }
+
+ public function cleanup_deactivated_styles()
+ {
+ // Updates users having current style a deactivated one
+ $sql = 'SELECT style_id
+ FROM ' . STYLES_TABLE . '
+ WHERE style_active = 0';
+ $result = $this->sql_query($sql);
+
+ $deactivated_style_ids = array();
+ while ($style_id = $this->db->sql_fetchfield('style_id', false, $result))
+ {
+ $deactivated_style_ids[] = (int) $style_id;
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($deactivated_style_ids))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_style = ' . (int) $this->config['default_style'] .'
+ WHERE ' . $this->db->sql_in_set('user_style', $deactivated_style_ids);
+ $this->sql_query($sql);
+ }
+ }
+
+ public function delete_orphan_private_messages()
+ {
+ // Delete orphan private messages
+ $batch_size = 500;
+
+ $sql_array = array(
+ 'SELECT' => 'p.msg_id',
+ 'FROM' => array(
+ PRIVMSGS_TABLE => 'p',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(PRIVMSGS_TO_TABLE => 't'),
+ 'ON' => 'p.msg_id = t.msg_id',
+ ),
+ ),
+ 'WHERE' => 't.user_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $delete_pms = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $delete_pms[] = (int) $row['msg_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($delete_pms))
+ {
+ $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('msg_id', $delete_pms);
+ $this->sql_query($sql);
+
+ // Return false to have the Migrator call this function again
+ return false;
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php
new file mode 100644
index 0000000000..fac8523e8c
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_11_rc2.php
@@ -0,0 +1,50 @@
+config['version'], '3.0.11-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_novalue',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.11-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php
new file mode 100644
index 0000000000..6a31a51201
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_12_rc1.php
@@ -0,0 +1,123 @@
+config['version'], '3.0.12-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, 'update_bots'))),
+ array('custom', array(array(&$this, 'disable_bots_from_receiving_pms'))),
+
+ array('config.update', array('version', '3.0.12-rc1')),
+ );
+ }
+
+ public function disable_bots_from_receiving_pms()
+ {
+ // Disable receiving pms for bots
+ $sql = 'SELECT user_id
+ FROM ' . BOTS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ $bot_user_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $bot_user_ids[] = (int) $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($bot_user_ids))
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_allow_pm = 0
+ WHERE ' . $this->db->sql_in_set('user_id', $bot_user_ids);
+ $this->sql_query($sql);
+ }
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET module_auth = \'acl_u_sig\'
+ WHERE module_class = \'ucp\'
+ AND module_basename = \'profile\'
+ AND module_mode = \'signature\'';
+ $this->sql_query($sql);
+ }
+
+ public function update_bots()
+ {
+ // Update bots
+ if (!function_exists('user_delete'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $bots_updates = array(
+ // Bot Deletions
+ 'NG-Search [Bot]' => false,
+ 'Nutch/CVS [Bot]' => false,
+ 'OmniExplorer [Bot]' => false,
+ 'Seekport [Bot]' => false,
+ 'Synoo [Bot]' => false,
+ 'WiseNut [Bot]' => false,
+
+ // Bot Updates
+ // Bot name to bot user agent map
+ 'Baidu [Spider]' => 'Baiduspider',
+ 'Exabot [Bot]' => 'Exabot',
+ 'Voyager [Bot]' => 'voyager/',
+ 'W3C [Validator]' => 'W3C_Validator',
+ );
+
+ foreach ($bots_updates as $bot_name => $bot_agent)
+ {
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE user_type = ' . USER_IGNORE . "
+ AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($bot_name)) . "'";
+ $result = $this->db->sql_query($sql);
+ $bot_user_id = (int) $this->db->sql_fetchfield('user_id');
+ $this->db->sql_freeresult($result);
+
+ if ($bot_user_id)
+ {
+ if ($bot_agent === false)
+ {
+ $sql = 'DELETE FROM ' . BOTS_TABLE . "
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+
+ user_delete('remove', $bot_user_id);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . BOTS_TABLE . "
+ SET bot_agent = '" . $this->db->sql_escape($bot_agent) . "'
+ WHERE user_id = $bot_user_id";
+ $this->sql_query($sql);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php
new file mode 100644
index 0000000000..562ccf077c
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_1_rc1.php
@@ -0,0 +1,108 @@
+config['version'], '3.0.1-rc1', '>=');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list' => array('BOOL', 1),
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('UINT', 0),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array('session_forum_id'),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name' => array('group_legend', 'group_name'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'display_subforum_list',
+ ),
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('group_legend'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_legend_name',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'fix_unset_last_view_time'))),
+ array('custom', array(array(&$this, 'reset_smiley_size'))),
+
+ array('config.update', array('version', '3.0.1-rc1')),
+ );
+ }
+
+ public function fix_unset_last_view_time()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "topics
+ SET topic_last_view_time = topic_last_post_time
+ WHERE topic_last_view_time = 0";
+ $this->sql_query($sql);
+ }
+
+ public function reset_smiley_size()
+ {
+ // Update smiley sizes
+ $smileys = array('icon_e_surprised.gif', 'icon_eek.gif', 'icon_cool.gif', 'icon_lol.gif', 'icon_mad.gif', 'icon_razz.gif', 'icon_redface.gif', 'icon_cry.gif', 'icon_evil.gif', 'icon_twisted.gif', 'icon_rolleyes.gif', 'icon_exclaim.gif', 'icon_question.gif', 'icon_idea.gif', 'icon_arrow.gif', 'icon_neutral.gif', 'icon_mrgreen.gif', 'icon_e_ugeek.gif');
+
+ foreach ($smileys as $smiley)
+ {
+ if (file_exists($this->phpbb_root_path . 'images/smilies/' . $smiley))
+ {
+ list($width, $height) = getimagesize($this->phpbb_root_path . 'images/smilies/' . $smiley);
+
+ $sql = 'UPDATE ' . SMILIES_TABLE . '
+ SET smiley_width = ' . $width . ', smiley_height = ' . $height . "
+ WHERE smiley_url = '" . $this->db->sql_escape($smiley) . "'";
+
+ $this->sql_query($sql);
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2.php b/phpBB/includes/db/migration/data/30x/3_0_2.php
new file mode 100644
index 0000000000..eed5acef82
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php
new file mode 100644
index 0000000000..a960e90765
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc1.php
@@ -0,0 +1,32 @@
+config['version'], '3.0.2-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('referer_validation', '1')),
+ array('config.add', array('check_attachment_content', '1')),
+ array('config.add', array('mime_triggers', 'body|head|html|img|plaintext|a href|pre|script|table|title')),
+
+ array('config.update', array('version', '3.0.2-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php
new file mode 100644
index 0000000000..8917dfea77
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_2_rc2.php
@@ -0,0 +1,80 @@
+config['version'], '3.0.2-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'drafts' => array(
+ 'draft_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_subject' => array('STEXT_UNI', '', 'true_sort'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_subject' => array('STEXT_UNI', ''),
+ ),
+ $this->table_prefix . 'topics' => array(
+ 'topic_title' => array('STEXT_UNI', '', 'true_sort'),
+ 'topic_last_post_subject' => array('STEXT_UNI', ''),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid' => array('session_forum_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_forum_id' => array(
+ 'session_forum_id',
+ ),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'sessions' => array(
+ 'session_fid',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.2-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_3.php b/phpBB/includes/db/migration/data/30x/3_0_3.php
new file mode 100644
index 0000000000..8984cf7b76
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_3_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php
new file mode 100644
index 0000000000..4b102e1a2e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_3_rc1.php
@@ -0,0 +1,83 @@
+config['version'], '3.0.3-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_2');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles_template' => array(
+ 'template_inherits_id',
+ 'template_inherit_path',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_max_recipients',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('enable_queue_trigger', '0')),
+ array('config.add', array('queue_trigger_posts', '3')),
+ array('config.add', array('pm_max_recipients', '0')),
+ array('custom', array(array(&$this, 'set_group_default_max_recipients'))),
+ array('config.add', array('dbms_version', $this->db->sql_server_info(true))),
+ array('permission.add', array('u_masspm_group', true, 'u_masspm')),
+ array('custom', array(array(&$this, 'correct_acp_email_permissions'))),
+
+ array('config.update', array('version', '3.0.3-rc1')),
+ );
+ }
+
+ public function correct_acp_email_permissions()
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . 'modules
+ SET module_auth = \'acl_a_email && cfg_email_enable\'
+ WHERE module_class = \'acp\'
+ AND module_basename = \'email\'';
+ $this->sql_query($sql);
+ }
+
+ public function set_group_default_max_recipients()
+ {
+ // Set maximum number of recipients for the registered users, bots, guests group
+ $sql = 'UPDATE ' . GROUPS_TABLE . ' SET group_max_recipients = 5
+ WHERE ' . $this->db->sql_in_set('group_name', array('GUESTS', 'REGISTERED', 'REGISTERED_COPPA', 'BOTS'));
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_4.php b/phpBB/includes/db/migration/data/30x/3_0_4.php
new file mode 100644
index 0000000000..9a0c132e78
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_4.php
@@ -0,0 +1,49 @@
+config['version'], '3.0.4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_4_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'rename_log_delete_topic'))),
+
+ array('config.update', array('version', '3.0.4')),
+ );
+ }
+
+ public function rename_log_delete_topic()
+ {
+ if ($this->db->sql_layer == 'oracle')
+ {
+ // log_operation is CLOB - but we can change this later
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation LIKE 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ else
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "log
+ SET log_operation = 'LOG_DELETE_TOPIC'
+ WHERE log_operation = 'LOG_TOPIC_DELETED'";
+ $this->sql_query($sql);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php
new file mode 100644
index 0000000000..8ad75a557b
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_4_rc1.php
@@ -0,0 +1,123 @@
+config['version'], '3.0.4-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_3');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile' => array('BOOL', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'style_id' => array('UINT', NULL, 'auto_increment'),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_imageset' => array(
+ 'imageset_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'image_id' => array('UINT', NULL, 'auto_increment'),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'theme_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'template_id' => array('UINT', NULL, 'auto_increment'),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'template_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_profile',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_custom_profile_fields'))),
+
+ array('config.update', array('version', '3.0.4-rc1')),
+ );
+ }
+
+ public function update_custom_profile_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new format
+ $sql = 'SELECT field_id, field_required, field_show_on_reg, field_hide
+ FROM ' . PROFILE_FIELDS_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'field_required' => 0,
+ 'field_show_on_reg' => 0,
+ 'field_hide' => 0,
+ 'field_show_profile'=> 0,
+ );
+
+ if ($row['field_required'])
+ {
+ $sql_ary['field_required'] = $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_show_on_reg'])
+ {
+ $sql_ary['field_show_on_reg'] = $sql_ary['field_show_profile'] = 1;
+ }
+ else if ($row['field_hide'])
+ {
+ // Only administrators and moderators can see this CPF, if the view is enabled, they can see it, otherwise just admins in the acp_users module
+ $sql_ary['field_hide'] = 1;
+ }
+ else
+ {
+ // equivelant to "none", which is the "Display in user control panel" option
+ $sql_ary['field_show_profile'] = 1;
+ }
+
+ $this->sql_query('UPDATE ' . $this->table_prefix . 'profile_fields SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE field_id = ' . $row['field_id'], $errored, $error_ary);
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5.php b/phpBB/includes/db/migration/data/30x/3_0_5.php
new file mode 100644
index 0000000000..16d2dee457
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.5', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5_rc1part2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php
new file mode 100644
index 0000000000..ea17cc1e31
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1.php
@@ -0,0 +1,124 @@
+config['version'], '3.0.5-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_4');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'forums' => array(
+ 'forum_style' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ $search_indexing_state = $this->config['search_indexing_state'];
+
+ return array(
+ array('config.add', array('captcha_gd_wave', 0)),
+ array('config.add', array('captcha_gd_3d_noise', 1)),
+ array('config.add', array('captcha_gd_fonts', 1)),
+ array('config.add', array('confirm_refresh', 1)),
+ array('config.add', array('max_num_search_keywords', 10)),
+ array('config.remove', array('search_indexing_state')),
+ array('config.add', array('search_indexing_state', $search_indexing_state, true)),
+ array('custom', array(array(&$this, 'hash_old_passwords'))),
+ array('custom', array(array(&$this, 'update_ichiro_bot'))),
+ );
+ }
+
+ public function hash_old_passwords()
+ {
+ $sql = 'SELECT user_id, user_password
+ FROM ' . $this->table_prefix . 'users
+ WHERE user_pass_convert = 1';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ if (strlen($row['user_password']) == 32)
+ {
+ $sql_ary = array(
+ 'user_password' => phpbb_hash($row['user_password']),
+ );
+
+ $this->sql_query('UPDATE ' . $this->table_prefix . 'users SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' WHERE user_id = ' . $row['user_id']);
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_ichiro_bot()
+ {
+ // Adjust bot entry
+ $sql = 'UPDATE ' . $this->table_prefix . "bots
+ SET bot_agent = 'ichiro/'
+ WHERE bot_agent = 'ichiro/2'";
+ $this->sql_query($sql);
+ }
+
+ public function remove_duplicate_auth_options()
+ {
+ // Before we are able to add a unique key to auth_option, we need to remove duplicate entries
+ $sql = 'SELECT auth_option
+ FROM ' . $this->table_prefix . 'acl_options
+ GROUP BY auth_option
+ HAVING COUNT(*) >= 2';
+ $result = $this->db->sql_query($sql);
+
+ $auth_options = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $auth_options[] = $row['auth_option'];
+ }
+ $this->db->sql_freeresult($result);
+
+ // Remove specific auth options
+ if (!empty($auth_options))
+ {
+ foreach ($auth_options as $option)
+ {
+ // Select auth_option_ids... the largest id will be preserved
+ $sql = 'SELECT auth_option_id
+ FROM ' . ACL_OPTIONS_TABLE . "
+ WHERE auth_option = '" . $db->sql_escape($option) . "'
+ ORDER BY auth_option_id DESC";
+ // sql_query_limit not possible here, due to bug in postgresql layer
+ $result = $this->db->sql_query($sql);
+
+ // Skip first row, this is our original auth option we want to preserve
+ $row = $this->db->sql_fetchrow($result);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ // Ok, remove this auth option...
+ $this->sql_query('DELETE FROM ' . ACL_OPTIONS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_ROLES_DATA_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_GROUPS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ $this->sql_query('DELETE FROM ' . ACL_USERS_TABLE . ' WHERE auth_option_id = ' . $row['auth_option_id']);
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php
new file mode 100644
index 0000000000..8538347b1a
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_5_rc1part2.php
@@ -0,0 +1,42 @@
+config['version'], '3.0.5-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5_rc1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'acl_options' => array('auth_option'),
+ ),
+ 'add_unique_index' => array(
+ $this->table_prefix . 'acl_options' => array(
+ 'auth_option' => array('auth_option'),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.5-rc1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6.php b/phpBB/includes/db/migration/data/30x/3_0_6.php
new file mode 100644
index 0000000000..bb651dc7cd
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php
new file mode 100644
index 0000000000..38c282ebf0
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc1.php
@@ -0,0 +1,324 @@
+config['version'], '3.0.6-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_5');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new' => array('BOOL', 1),
+ 'user_reminded' => array('TINT:4', 0),
+ 'user_reminded_time' => array('TIMESTAMP', 0),
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth' => array('BOOL', 0, 'after' => 'group_founder_manage'),
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id' => array('UINT', 0),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options' => array('UINT:20', 0),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_options' => array('UINT:11', 230271),
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id' => array('post_id'),
+ 'pm_id' => array('pm_id'),
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username' => array('post_username:255'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'confirm' => array(
+ 'attempts',
+ ),
+ $this->table_prefix . 'users' => array(
+ 'user_new',
+ 'user_reminded',
+ 'user_reminded_time',
+ ),
+ $this->table_prefix . 'groups' => array(
+ 'group_skip_auth',
+ ),
+ $this->table_prefix . 'privmsgs' => array(
+ 'message_reported',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'pm_id',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_vt',
+ ),
+ $this->table_prefix . 'forums' => array(
+ 'forum_options',
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'reports' => array(
+ 'post_id',
+ 'pm_id',
+ ),
+ $this->table_prefix . 'posts' => array(
+ 'post_username',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('captcha_plugin', 'phpbb_captcha_nogd')),
+ array('if', array(
+ ($this->config['captcha_gd']),
+ array('config.update', array('captcha_plugin', 'phpbb_captcha_gd')),
+ )),
+
+ array('config.add', array('feed_enable', 0)),
+ array('config.add', array('feed_limit', 10)),
+ array('config.add', array('feed_overall_forums', 1)),
+ array('config.add', array('feed_overall_forums_limit', 15)),
+ array('config.add', array('feed_overall_topics', 0)),
+ array('config.add', array('feed_overall_topics_limit', 15)),
+ array('config.add', array('feed_forum', 1)),
+ array('config.add', array('feed_topic', 1)),
+ array('config.add', array('feed_item_statistics', 1)),
+
+ array('config.add', array('smilies_per_page', 50)),
+ array('config.add', array('allow_pm_report', 1)),
+ array('config.add', array('min_post_chars', 1)),
+ array('config.add', array('allow_quick_reply', 1)),
+ array('config.add', array('new_member_post_limit', 0)),
+ array('config.add', array('new_member_group_default', 0)),
+ array('config.add', array('delete_time', $this->config['edit_time'])),
+
+ array('config.add', array('allow_avatar', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_upload'] || $this->config['allow_avatar_local'] || $this->config['allow_avatar_remote']),
+ array('config.update', array('allow_avatar', 1)),
+ )),
+ array('config.add', array('allow_avatar_remote_upload', 0)),
+ array('if', array(
+ ($this->config['allow_avatar_remote'] && $this->config['allow_avatar_upload']),
+ array('config.update', array('allow_avatar_remote_upload', 1)),
+ )),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_BOARD_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_board',
+ 'modes' => array('feed'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_USERS',
+ array(
+ 'module_basename' => 'acp_users',
+ 'modes' => array('warnings'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_SERVER_CONFIGURATION',
+ array(
+ 'module_basename' => 'acp_send_statistics',
+ 'modes' => array('send_statistics'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_FORUM_BASED_PERMISSIONS',
+ array(
+ 'module_basename' => 'acp_permissions',
+ 'modes' => array('setting_forum_copy'),
+ ),
+ )),
+ array('module.add', array(
+ 'mcp',
+ 'MCP_REPORTS',
+ array(
+ 'module_basename' => 'mcp_pm_reports',
+ 'modes' => array('pm_reports','pm_reports_closed','pm_report_details'),
+ ),
+ )),
+ array('custom', array(array(&$this, 'add_newly_registered_group'))),
+ array('custom', array(array(&$this, 'set_user_options_default'))),
+
+ array('config.update', array('version', '3.0.6-rc1')),
+ );
+ }
+
+ public function set_user_options_default()
+ {
+ // 229376 is the added value to enable all three signature options
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_options = user_options + 229376';
+ $this->sql_query($sql);
+ }
+
+ public function add_newly_registered_group()
+ {
+ // Add newly_registered group... but check if it already exists (we always supported running the updater on any schema)
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'NEWLY_REGISTERED'";
+ $result = $this->db->sql_query($sql);
+ $group_id = (int) $this->db->sql_fetchfield('group_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$group_id)
+ {
+ $sql = 'INSERT INTO ' . GROUPS_TABLE . " (group_name, group_type, group_founder_manage, group_colour, group_legend, group_avatar, group_desc, group_desc_uid, group_max_recipients) VALUES ('NEWLY_REGISTERED', 3, 0, '', 0, '', '', '', 5)";
+ $this->sql_query($sql);
+
+ $group_id = $this->db->sql_nextid();
+ }
+
+ // Insert new user role... at the end of the chain
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_USER_NEW_MEMBER'
+ AND role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $u_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$u_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'u_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_USER_NEW_MEMBER', 'ROLE_DESCRIPTION_USER_NEW_MEMBER', 'u_', $next_order_id)";
+ $this->sql_query($sql);
+ $u_role = $this->db->sql_nextid();
+
+ // Now add the correct data to the roles...
+ // The standard role says that new users are not able to send a PM, Mass PM, are not able to PM groups
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $u_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'u_%' AND auth_option IN ('u_sendpm', 'u_masspm', 'u_masspm_group')";
+ $this->sql_query($sql);
+
+ // Add user role to group
+ $sql = 'INSERT INTO ' . ACL_GROUPS_TABLE . " (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES ($group_id, 0, 0, $u_role, 0)";
+ $this->sql_query($sql);
+ }
+
+ // Insert new forum role
+ $sql = 'SELECT role_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_name = 'ROLE_FORUM_NEW_MEMBER'
+ AND role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $f_role = (int) $this->db->sql_fetchfield('role_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$f_role)
+ {
+ $sql = 'SELECT MAX(role_order) as max_order_id
+ FROM ' . ACL_ROLES_TABLE . "
+ WHERE role_type = 'f_'";
+ $result = $this->db->sql_query($sql);
+ $next_order_id = (int) $this->db->sql_fetchfield('max_order_id');
+ $this->db->sql_freeresult($result);
+
+ $next_order_id++;
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . " (role_name, role_description, role_type, role_order) VALUES ('ROLE_FORUM_NEW_MEMBER', 'ROLE_DESCRIPTION_FORUM_NEW_MEMBER', 'f_', $next_order_id)";
+ $this->sql_query($sql);
+ $f_role = $this->db->sql_nextid();
+
+ $sql = 'INSERT INTO ' . ACL_ROLES_DATA_TABLE . " (role_id, auth_option_id, auth_setting) SELECT $f_role, auth_option_id, 0 FROM " . ACL_OPTIONS_TABLE . " WHERE auth_option LIKE 'f_%' AND auth_option IN ('f_noapprove')";
+ $this->sql_query($sql);
+ }
+
+ // Set every members user_new column to 0 (old users) only if there is no one yet (this makes sure we do not execute this more than once)
+ $sql = 'SELECT 1
+ FROM ' . USERS_TABLE . '
+ WHERE user_new = 0';
+ $result = $this->db->sql_query_limit($sql, 1);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row)
+ {
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_new = 0';
+ $this->sql_query($sql);
+ }
+
+ // To mimick the old "feature" we will assign the forum role to every forum, regardless of the setting (this makes sure there are no "this does not work!!!! YUO!!!" posts...
+ // Check if the role is already assigned...
+ $sql = 'SELECT forum_id
+ FROM ' . ACL_GROUPS_TABLE . '
+ WHERE group_id = ' . $group_id . '
+ AND auth_role_id = ' . $f_role;
+ $result = $this->db->sql_query($sql);
+ $is_options = (int) $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ // Not assigned at all... :/
+ if (!$is_options)
+ {
+ // Get postable forums
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type != ' . FORUM_LINK;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->sql_query('INSERT INTO ' . ACL_GROUPS_TABLE . ' (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (' . $group_id . ', ' . (int) $row['forum_id'] . ', 0, ' . $f_role . ', 0)');
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ // Clear permissions...
+ include_once($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
+ $auth_admin = new auth_admin();
+ $auth_admin->acl_clear_prefetch();
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php
new file mode 100644
index 0000000000..a939dbd489
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php
new file mode 100644
index 0000000000..b3f09d8ab8
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc3.php
@@ -0,0 +1,40 @@
+config['version'], '3.0.6-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_cp_fields'))),
+
+ array('config.update', array('version', '3.0.6-rc3')),
+ );
+ }
+
+ public function update_cp_fields()
+ {
+ // Update the Custom Profile Fields based on previous settings to the new format
+ $sql = 'UPDATE ' . PROFILE_FIELDS_TABLE . '
+ SET field_show_on_vt = 1
+ WHERE field_hide = 0
+ AND (field_required = 1 OR field_show_on_reg = 1 OR field_show_profile = 1)';
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php
new file mode 100644
index 0000000000..fc2923f99b
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_6_rc4.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.6-rc4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.6-rc4')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7.php b/phpBB/includes/db/migration/data/30x/3_0_7.php
new file mode 100644
index 0000000000..9ff2e9e4ab
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.7', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php
new file mode 100644
index 0000000000..c9cc9d19ac
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_pl1.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.7-pl1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.7-pl1')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php
new file mode 100644
index 0000000000..ffebf66f2d
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc1.php
@@ -0,0 +1,76 @@
+config['version'], '3.0.7-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_6');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_keys' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time',
+ ),
+ ),
+ 'add_index' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id' => array('topic_id'),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_index' => array(
+ $this->table_prefix . 'log' => array(
+ 'log_time' => array('log_time'),
+ ),
+ ),
+ 'drop_keys' => array(
+ $this->table_prefix . 'topics_track' => array(
+ 'topic_id',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('feed_overall', 1)),
+ array('config.add', array('feed_http_auth', 0)),
+ array('config.add', array('feed_limit_post', $this->config['feed_limit'])),
+ array('config.add', array('feed_limit_topic', $this->config['feed_overall_topics_limit'])),
+ array('config.add', array('feed_topics_new', $this->config['feed_overall_topics'])),
+ array('config.add', array('feed_topics_active', $this->config['feed_overall_topics'])),
+ array('custom', array(array(&$this, 'delete_text_templates'))),
+
+ array('config.update', array('version', '3.0.7-rc1')),
+ );
+ }
+
+ public function delete_text_templates()
+ {
+ // Delete all text-templates from the template_data
+ $sql = 'DELETE FROM ' . STYLES_TEMPLATE_DATA_TABLE . '
+ WHERE template_filename ' . $this->db->sql_like_expression($this->db->any_char . '.txt');
+ $this->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php
new file mode 100644
index 0000000000..55bc2bc679
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_7_rc2.php
@@ -0,0 +1,73 @@
+config['version'], '3.0.7-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_email_hash'))),
+
+ array('config.update', array('version', '3.0.7-rc2')),
+ );
+ }
+
+ public function update_email_hash($start = 0)
+ {
+ $limit = 1000;
+
+ $sql = 'SELECT user_id, user_email, user_email_hash
+ FROM ' . USERS_TABLE . '
+ WHERE user_type <> ' . USER_IGNORE . "
+ AND user_email <> ''";
+ $result = $this->db->sql_query_limit($sql, $limit, $start);
+
+ $i = 0;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $i++;
+
+ // Snapshot of the phpbb_email_hash() function
+ // We cannot call it directly because the auto updater updates the DB first. :/
+ $user_email_hash = sprintf('%u', crc32(strtolower($row['user_email']))) . strlen($row['user_email']);
+
+ if ($user_email_hash != $row['user_email_hash'])
+ {
+ $sql_ary = array(
+ 'user_email_hash' => $user_email_hash,
+ );
+
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE user_id = ' . (int) $row['user_id'];
+ $this->sql_query($sql);
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if ($i < $limit)
+ {
+ // Completed
+ return;
+ }
+
+ // Return the next start, will be sent to $start when this function is called again
+ return $start + $limit;
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_8.php b/phpBB/includes/db/migration/data/30x/3_0_8.php
new file mode 100644
index 0000000000..8998ef9627
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_8.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.8', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_8_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.8')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php
new file mode 100644
index 0000000000..aeff35333e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_8_rc1.php
@@ -0,0 +1,221 @@
+config['version'], '3.0.8-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_7_pl1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'update_module_auth'))),
+ array('custom', array(array(&$this, 'update_bots'))),
+ array('custom', array(array(&$this, 'delete_orphan_shadow_topics'))),
+ array('module.add', array(
+ 'acp',
+ 'ACP_MESSAGES',
+ array(
+ 'module_basename' => 'acp_board',
+ 'modes' => array('post'),
+ ),
+ )),
+ array('config.add', array('load_unreads_search', 1)),
+ array('config.update_if_equals', array(600, 'queue_interval', 60)),
+ array('config.update_if_equals', array(50, 'email_package_size', 20)),
+
+ array('config.update', array('version', '3.0.8-rc1')),
+ );
+ }
+
+ public function update_file_extension_group_names()
+ {
+ // Update file extension group names to use language strings.
+ $sql = 'SELECT lang_dir
+ FROM ' . LANG_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ $extension_groups_updated = array();
+ while ($lang_dir = $this->db->sql_fetchfield('lang_dir'))
+ {
+ $lang_dir = basename($lang_dir);
+
+ // The language strings we need are either in language/.../acp/attachments.php
+ // in the update package if we're updating to 3.0.8-RC1 or later,
+ // or they are in language/.../install.php when we're updating from 3.0.7-PL1 or earlier.
+ // On an already updated board, they can also already be in language/.../acp/attachments.php
+ // in the board root.
+ $lang_files = array(
+ "{$this->phpbb_root_path}install/update/new/language/$lang_dir/acp/attachments.{$this->php_ext}",
+ "{$this->phpbb_root_path}language/$lang_dir/install.{$this->php_ext}",
+ "{$this->phpbb_root_path}language/$lang_dir/acp/attachments.{$this->php_ext}",
+ );
+
+ foreach ($lang_files as $lang_file)
+ {
+ if (!file_exists($lang_file))
+ {
+ continue;
+ }
+
+ $lang = array();
+ include($lang_file);
+
+ foreach($lang as $lang_key => $lang_val)
+ {
+ if (isset($extension_groups_updated[$lang_key]) || strpos($lang_key, 'EXT_GROUP_') !== 0)
+ {
+ continue;
+ }
+
+ $sql_ary = array(
+ 'group_name' => substr($lang_key, 10), // Strip off 'EXT_GROUP_'
+ );
+
+ $sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
+ WHERE group_name = '" . $this->db->sql_escape($lang_val) . "'";
+ $this->sql_query($sql);
+
+ $extension_groups_updated[$lang_key] = true;
+ }
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_module_auth()
+ {
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET module_auth = \'cfg_allow_avatar && (cfg_allow_avatar_local || cfg_allow_avatar_remote || cfg_allow_avatar_upload || cfg_allow_avatar_remote_upload)\'
+ WHERE module_class = \'ucp\'
+ AND module_basename = \'profile\'
+ AND module_mode = \'avatar\'';
+ $this->sql_query($sql);
+ }
+
+ public function update_bots()
+ {
+ $bot_name = 'Bing [Bot]';
+ $bot_name_clean = utf8_clean_string($bot_name);
+
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . "
+ WHERE username_clean = '" . $this->db->sql_escape($bot_name_clean) . "'";
+ $result = $this->db->sql_query($sql);
+ $bing_already_added = (bool) $this->db->sql_fetchfield('user_id');
+ $this->db->sql_freeresult($result);
+
+ if (!$bing_already_added)
+ {
+ $bot_agent = 'bingbot/';
+ $bot_ip = '';
+ $sql = 'SELECT group_id, group_colour
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = 'BOTS'";
+ $result = $this->db->sql_query($sql);
+ $group_row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$group_row)
+ {
+ // default fallback, should never get here
+ $group_row['group_id'] = 6;
+ $group_row['group_colour'] = '9E8DA7';
+ }
+
+ if (!function_exists('user_add'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+
+ $user_row = array(
+ 'user_type' => USER_IGNORE,
+ 'group_id' => $group_row['group_id'],
+ 'username' => $bot_name,
+ 'user_regdate' => time(),
+ 'user_password' => '',
+ 'user_colour' => $group_row['group_colour'],
+ 'user_email' => '',
+ 'user_lang' => $this->config['default_lang'],
+ 'user_style' => $this->config['default_style'],
+ 'user_timezone' => 0,
+ 'user_dateformat' => $this->config['default_dateformat'],
+ 'user_allow_massemail' => 0,
+ );
+
+ $user_id = user_add($user_row);
+
+ $sql = 'INSERT INTO ' . BOTS_TABLE . ' ' . $this->db->sql_build_array('INSERT', array(
+ 'bot_active' => 1,
+ 'bot_name' => (string) $bot_name,
+ 'user_id' => (int) $user_id,
+ 'bot_agent' => (string) $bot_agent,
+ 'bot_ip' => (string) $bot_ip,
+ ));
+
+ $this->sql_query($sql);
+ }
+ }
+
+ public function delete_orphan_shadow_topics()
+ {
+ // Delete shadow topics pointing to not existing topics
+ $batch_size = 500;
+
+ // Set of affected forums we have to resync
+ $sync_forum_ids = array();
+
+ $sql_array = array(
+ 'SELECT' => 't1.topic_id, t1.forum_id',
+ 'FROM' => array(
+ TOPICS_TABLE => 't1',
+ ),
+ 'LEFT_JOIN' => array(
+ array(
+ 'FROM' => array(TOPICS_TABLE => 't2'),
+ 'ON' => 't1.topic_moved_id = t2.topic_id',
+ ),
+ ),
+ 'WHERE' => 't1.topic_moved_id <> 0
+ AND t2.topic_id IS NULL',
+ );
+ $sql = $this->db->sql_build_query('SELECT', $sql_array);
+ $result = $this->db->sql_query_limit($sql, $batch_size);
+
+ $topic_ids = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $topic_ids[] = (int) $row['topic_id'];
+
+ $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($topic_ids))
+ {
+ $sql = 'DELETE FROM ' . TOPICS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('topic_id', $topic_ids);
+ $this->db->sql_query($sql);
+
+ // Sync the forums we have deleted shadow topics from.
+ sync('forum', 'forum_id', $sync_forum_ids, true, true);
+
+ return false;
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9.php b/phpBB/includes/db/migration/data/30x/3_0_9.php
new file mode 100644
index 0000000000..d5269ea6f0
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc4');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php
new file mode 100644
index 0000000000..1f8622798e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc1.php
@@ -0,0 +1,124 @@
+config['version'], '3.0.9-rc1', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_8');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'login_attempts' => array(
+ 'COLUMNS' => array(
+ // this column was removed from the database updater
+ // after 3.0.9-RC3 was released. It might still exist
+ // in 3.0.9-RCX installations and has to be dropped in
+ // 3.0.12 after the db_tools class is capable of properly
+ // removing a primary key.
+ // 'attempt_id' => array('UINT', NULL, 'auto_increment'),
+ 'attempt_ip' => array('VCHAR:40', ''),
+ 'attempt_browser' => array('VCHAR:150', ''),
+ 'attempt_forwarded_for' => array('VCHAR:255', ''),
+ 'attempt_time' => array('TIMESTAMP', 0),
+ 'user_id' => array('UINT', 0),
+ 'username' => array('VCHAR_UNI:255', 0),
+ 'username_clean' => array('VCHAR_CI', 0),
+ ),
+ //'PRIMARY_KEY' => 'attempt_id',
+ 'KEYS' => array(
+ 'att_ip' => array('INDEX', array('attempt_ip', 'attempt_time')),
+ 'att_for' => array('INDEX', array('attempt_forwarded_for', 'attempt_time')),
+ 'att_time' => array('INDEX', array('attempt_time')),
+ 'user_id' => array('INDEX', 'user_id'),
+ ),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'bbcodes' => array(
+ 'bbcode_id' => array('USINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'login_attempts',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.add', array('ip_login_limit_max', 50)),
+ array('config.add', array('ip_login_limit_time', 21600)),
+ array('config.add', array('ip_login_limit_use_forwarded', 0)),
+ array('custom', array(array(&$this, 'update_file_extension_group_names'))),
+ array('custom', array(array(&$this, 'fix_firebird_qa_captcha'))),
+
+ array('config.update', array('version', '3.0.9-rc1')),
+ );
+ }
+
+ public function update_file_extension_group_names()
+ {
+ // Update file extension group names to use language strings, again.
+ $sql = 'SELECT group_id, group_name
+ FROM ' . EXTENSION_GROUPS_TABLE . '
+ WHERE group_name ' . $this->db->sql_like_expression('EXT_GROUP_' . $this->db->any_char);
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql_ary = array(
+ 'group_name' => substr($row['group_name'], 10), // Strip off 'EXT_GROUP_'
+ );
+
+ $sql = 'UPDATE ' . EXTENSION_GROUPS_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE group_id = ' . $row['group_id'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function fix_firebird_qa_captcha()
+ {
+ // Recover from potentially broken Q&A CAPTCHA table on firebird
+ // Q&A CAPTCHA was uninstallable, so it's safe to remove these
+ // without data loss
+ if ($this->db_tools->sql_layer == 'firebird')
+ {
+ $tables = array(
+ $this->table_prefix . 'captcha_questions',
+ $this->table_prefix . 'captcha_answers',
+ $this->table_prefix . 'qa_confirm',
+ );
+ foreach ($tables as $table)
+ {
+ if ($this->db_tools->sql_table_exists($table))
+ {
+ $this->db_tools->sql_table_drop($table);
+ }
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php
new file mode 100644
index 0000000000..c0e662aa45
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc2.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc2', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc1');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc2')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php
new file mode 100644
index 0000000000..d6d1f14b2e
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc3.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc3', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc2');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc3')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php
new file mode 100644
index 0000000000..e673249343
--- /dev/null
+++ b/phpBB/includes/db/migration/data/30x/3_0_9_rc4.php
@@ -0,0 +1,28 @@
+config['version'], '3.0.9-rc4', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_9_rc3');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('version', '3.0.9-rc4')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/dev.php b/phpBB/includes/db/migration/data/310/dev.php
new file mode 100644
index 0000000000..13b36bbf30
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/dev.php
@@ -0,0 +1,405 @@
+config['version'], '3.1.0-dev', '>=');
+ }
+
+ static public function depends_on()
+ {
+ return array(
+ 'phpbb_db_migration_data_310_extensions',
+ 'phpbb_db_migration_data_310_style_update_p2',
+ 'phpbb_db_migration_data_310_timezone_p2',
+ 'phpbb_db_migration_data_310_reported_posts_display',
+ );
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage' => array('UINT', 0, 'after' => 'group_legend'),
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm' => array('BOOL', 0),
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'style_parent_id' => array('UINT:4', 0),
+ 'style_parent_tree' => array('TEXT', ''),
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text' => array('MTEXT_UNI', ''),
+ 'reported_post_uid' => array('VCHAR:8', ''),
+ 'reported_post_bitfield' => array('VCHAR:255', ''),
+ ),
+ ),
+ 'change_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_legend' => array('UINT', 0),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'groups' => array(
+ 'group_teampage',
+ ),
+ $this->table_prefix . 'profile_fields' => array(
+ 'field_show_on_pm',
+ ),
+ $this->table_prefix . 'styles' => array(
+ 'style_path',
+ 'bbcode_bitfield',
+ 'style_parent_id',
+ 'style_parent_tree',
+ ),
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_text',
+ 'reported_post_uid',
+ 'reported_post_bitfield',
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('config.update', array('search_type', 'phpbb_search_' . $this->config['search_type'])),
+
+ array('config.add', array('fulltext_postgres_ts_name', 'simple')),
+ array('config.add', array('fulltext_postgres_min_word_len', 4)),
+ array('config.add', array('fulltext_postgres_max_word_len', 254)),
+ array('config.add', array('fulltext_sphinx_stopwords', 0)),
+ array('config.add', array('fulltext_sphinx_indexer_mem_limit', 512)),
+
+ array('config.add', array('load_jquery_cdn', 0)),
+ array('config.add', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js')),
+
+ array('config.add', array('use_system_cron', 0)),
+
+ array('config.add', array('legend_sort_groupname', 0)),
+ array('config.add', array('teampage_forums', 1)),
+ array('config.add', array('teampage_memberships', 1)),
+
+ array('config.add', array('load_cpf_pm', 0)),
+
+ array('config.add', array('display_last_subject', 1)),
+
+ array('config.add', array('assets_version', 1)),
+
+ array('config.add', array('site_home_url', '')),
+ array('config.add', array('site_home_text', '')),
+
+ array('permission.add', array('u_chgprofileinfo', true, 'u_sig')),
+
+ array('module.add', array(
+ 'acp',
+ 'ACP_GROUPS',
+ array(
+ 'module_basename' => 'acp_groups',
+ 'modes' => array('position'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_ATTACHMENTS',
+ array(
+ 'module_basename' => 'acp_attachments',
+ 'modes' => array('manage'),
+ ),
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_STYLE_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_styles',
+ 'modes' => array('install', 'cache'),
+ ),
+ )),
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PROFILE',
+ array(
+ 'module_basename' => 'ucp_profile',
+ 'modes' => array('autologin_keys'),
+ ),
+ )),
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_LANGUAGE'
+ )),
+
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_TEMPLATES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_THEMES',
+ )),
+ array('module.remove', array(
+ 'acp',
+ false,
+ 'ACP_IMAGESETS',
+ )),
+
+ array('custom', array(array($this, 'rename_module_basenames'))),
+ array('custom', array(array($this, 'rename_styles_module'))),
+ array('custom', array(array($this, 'add_group_teampage'))),
+ array('custom', array(array($this, 'update_group_legend'))),
+ array('custom', array(array($this, 'localise_global_announcements'))),
+ array('custom', array(array($this, 'update_ucp_pm_basename'))),
+ array('custom', array(array($this, 'update_ucp_profile_auth'))),
+ array('custom', array(array($this, 'move_customise_modules'))),
+
+ array('config.update', array('version', '3.1.0-dev')),
+ );
+ }
+
+ public function move_customise_modules()
+ {
+ // Move language management to new location in the Customise tab
+ // First get language module id
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_basename = 'acp_language'";
+ $result = $this->db->sql_query($sql);
+ $language_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+ // Next get language management module id of the one just created
+ $sql = 'SELECT module_id FROM ' . MODULES_TABLE . "
+ WHERE module_langname = 'ACP_LANGUAGE'";
+ $result = $this->db->sql_query($sql);
+ $language_management_module_id = $this->db->sql_fetchfield('module_id');
+ $this->db->sql_freeresult($result);
+
+ if (!class_exists('acp_modules'))
+ {
+ include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext);
+ }
+ // acp_modules calls adm_back_link, which is undefined at this point
+ if (!function_exists('adm_back_link'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext);
+ }
+ $module_manager = new acp_modules();
+ $module_manager->module_class = 'acp';
+ $module_manager->move_module($language_module_id, $language_management_module_id);
+ }
+
+ public function update_ucp_pm_basename()
+ {
+ $sql = 'SELECT module_id, module_basename
+ FROM ' . MODULES_TABLE . "
+ WHERE module_basename <> 'ucp_pm' AND
+ module_langname='UCP_PM'";
+ $result = $this->db->sql_query_limit($sql, 1);
+
+ if ($row = $this->db->sql_fetchrow($result))
+ {
+ // This update is still not applied. Applying it
+
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_basename = 'ucp_pm'
+ WHERE module_id = " . (int) $row['module_id'];
+
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function update_ucp_profile_auth()
+ {
+ // Update the auth setting for the module
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_auth = 'acl_u_chgprofileinfo'
+ WHERE module_class = 'ucp'
+ AND module_basename = 'ucp_profile'
+ AND module_mode = 'profile_info'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_styles_module()
+ {
+ // Rename styles module to Customise
+ $sql = 'UPDATE ' . MODULES_TABLE . "
+ SET module_langname = 'ACP_CAT_CUSTOMISE'
+ WHERE module_langname = 'ACP_CAT_STYLES'";
+ $this->sql_query($sql);
+ }
+
+ public function rename_module_basenames()
+ {
+ // rename all module basenames to full classname
+ $sql = 'SELECT module_id, module_basename, module_class
+ FROM ' . MODULES_TABLE;
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $module_id = (int) $row['module_id'];
+ unset($row['module_id']);
+
+ if (!empty($row['module_basename']) && !empty($row['module_class']))
+ {
+ // all the class names start with class name or with phpbb_ for auto loading
+ if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 &&
+ strpos($row['module_basename'], 'phpbb_') !== 0)
+ {
+ $row['module_basename'] = $row['module_class'] . '_' . $row['module_basename'];
+
+ $sql_update = $this->db->sql_build_array('UPDATE', $row);
+
+ $sql = 'UPDATE ' . MODULES_TABLE . '
+ SET ' . $sql_update . '
+ WHERE module_id = ' . $module_id;
+ $this->sql_query($sql);
+ }
+ }
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+
+ public function add_group_teampage()
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 1
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'ADMINISTRATORS'";
+ $this->sql_query($sql);
+
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_teampage = 2
+ WHERE group_type = ' . GROUP_SPECIAL . "
+ AND group_name = 'GLOBAL_MODERATORS'";
+ $this->sql_query($sql);
+ }
+
+ public function update_group_legend()
+ {
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . '
+ WHERE group_legend = 1
+ ORDER BY group_name ASC';
+ $result = $this->db->sql_query($sql);
+
+ $next_legend = 1;
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . GROUPS_TABLE . '
+ SET group_legend = ' . $next_legend . '
+ WHERE group_id = ' . (int) $row['group_id'];
+ $this->sql_query($sql);
+
+ $next_legend++;
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ public function localise_global_announcements()
+ {
+ // Localise Global Announcements
+ $sql = 'SELECT topic_id, topic_approved, (topic_replies + 1) AS topic_posts, topic_last_post_id, topic_last_post_subject, topic_last_post_time, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour
+ FROM ' . TOPICS_TABLE . '
+ WHERE forum_id = 0
+ AND topic_type = ' . POST_GLOBAL;
+ $result = $this->db->sql_query($sql);
+
+ $global_announcements = $update_lastpost_data = array();
+ $update_lastpost_data['forum_last_post_time'] = 0;
+ $update_forum_data = array(
+ 'forum_posts' => 0,
+ 'forum_topics' => 0,
+ 'forum_topics_real' => 0,
+ );
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $global_announcements[] = (int) $row['topic_id'];
+
+ $update_forum_data['forum_posts'] += (int) $row['topic_posts'];
+ $update_forum_data['forum_topics_real']++;
+ if ($row['topic_approved'])
+ {
+ $update_forum_data['forum_topics']++;
+ }
+
+ if ($update_lastpost_data['forum_last_post_time'] < $row['topic_last_post_time'])
+ {
+ $update_lastpost_data = array(
+ 'forum_last_post_id' => (int) $row['topic_last_post_id'],
+ 'forum_last_post_subject' => $row['topic_last_post_subject'],
+ 'forum_last_post_time' => (int) $row['topic_last_post_time'],
+ 'forum_last_poster_id' => (int) $row['topic_last_poster_id'],
+ 'forum_last_poster_name' => $row['topic_last_poster_name'],
+ 'forum_last_poster_colour' => $row['topic_last_poster_colour'],
+ );
+ }
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!empty($global_announcements))
+ {
+ // Update the post/topic-count for the forum and the last-post if needed
+ $sql = 'SELECT forum_id
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_type = ' . FORUM_POST;
+ $result = $this->db->sql_query_limit($sql, 1);
+ $ga_forum_id = $this->db->sql_fetchfield('forum_id');
+ $this->db->sql_freeresult($result);
+
+ $sql = 'SELECT forum_last_post_time
+ FROM ' . FORUMS_TABLE . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $result = $this->db->sql_query($sql);
+ $lastpost = (int) $this->db->sql_fetchfield('forum_last_post_time');
+ $this->db->sql_freeresult($result);
+
+ $sql_update = 'forum_posts = forum_posts + ' . $update_forum_data['forum_posts'] . ', ';
+ $sql_update .= 'forum_topics_real = forum_topics_real + ' . $update_forum_data['forum_topics_real'] . ', ';
+ $sql_update .= 'forum_topics = forum_topics + ' . $update_forum_data['forum_topics'];
+ if ($lastpost < $update_lastpost_data['forum_last_post_time'])
+ {
+ $sql_update .= ', ' . $this->db->sql_build_array('UPDATE', $update_lastpost_data);
+ }
+
+ $sql = 'UPDATE ' . FORUMS_TABLE . '
+ SET ' . $sql_update . '
+ WHERE forum_id = ' . $ga_forum_id;
+ $this->sql_query($sql);
+
+ // Update some forum_ids
+ $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
+ foreach ($table_ary as $table)
+ {
+ $sql = "UPDATE $table
+ SET forum_id = $ga_forum_id
+ WHERE " . $this->db->sql_in_set('topic_id', $global_announcements);
+ $this->sql_query($sql);
+ }
+ unset($table_ary);
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/extensions.php b/phpBB/includes/db/migration/data/310/extensions.php
new file mode 100644
index 0000000000..6a9caa1cfc
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/extensions.php
@@ -0,0 +1,69 @@
+db_tools->sql_table_exists($this->table_prefix . 'ext');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'ext' => array(
+ 'COLUMNS' => array(
+ 'ext_name' => array('VCHAR', ''),
+ 'ext_active' => array('BOOL', 0),
+ 'ext_state' => array('TEXT', ''),
+ ),
+ 'KEYS' => array(
+ 'ext_name' => array('UNIQUE', 'ext_name'),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'ext',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ // Module will be renamed later
+ array('module.add', array(
+ 'acp',
+ 'ACP_CAT_STYLES',
+ 'ACP_EXTENSION_MANAGEMENT'
+ )),
+ array('module.add', array(
+ 'acp',
+ 'ACP_EXTENSION_MANAGEMENT',
+ array(
+ 'module_basename' => 'acp_extensions',
+ 'modes' => array('main'),
+ ),
+ )),
+ array('permission.add', array('a_extensions', true, 'a_styles')),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/notifications.php b/phpBB/includes/db/migration/data/310/notifications.php
new file mode 100644
index 0000000000..82bfd4cb2d
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/notifications.php
@@ -0,0 +1,160 @@
+db_tools->sql_table_exists($this->table_prefix . 'notifications');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_310_dev');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_tables' => array(
+ $this->table_prefix . 'notification_types' => array(
+ 'COLUMNS' => array(
+ 'notification_type' => array('VCHAR:255', ''),
+ 'notification_type_enabled' => array('BOOL', 1),
+ ),
+ 'PRIMARY_KEY' => array('notification_type', 'notification_type_enabled'),
+ ),
+ $this->table_prefix . 'notifications' => array(
+ 'COLUMNS' => array(
+ 'notification_id' => array('UINT', NULL, 'auto_increment'),
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'item_parent_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'notification_read' => array('BOOL', 0),
+ 'notification_time' => array('TIMESTAMP', 1),
+ 'notification_data' => array('TEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'notification_id',
+ 'KEYS' => array(
+ 'item_ident' => array('INDEX', array('item_type', 'item_id')),
+ 'user' => array('INDEX', array('user_id', 'notification_read')),
+ ),
+ ),
+ $this->table_prefix . 'user_notifications' => array(
+ 'COLUMNS' => array(
+ 'item_type' => array('VCHAR:255', ''),
+ 'item_id' => array('UINT', 0),
+ 'user_id' => array('UINT', 0),
+ 'method' => array('VCHAR:255', ''),
+ 'notify' => array('BOOL', 1),
+ ),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_tables' => array(
+ $this->table_prefix . 'notification_types',
+ $this->table_prefix . 'notifications',
+ $this->table_prefix . 'user_notifications',
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('module.add', array(
+ 'ucp',
+ 'UCP_MAIN',
+ array(
+ 'module_basename' => 'ucp_notifications',
+ 'modes' => array('notification_list'),
+ ),
+ )),
+ array('module.add', array(
+ 'ucp',
+ 'UCP_PREFS',
+ array(
+ 'module_basename' => 'ucp_notifications',
+ 'modes' => array('notification_options'),
+ ),
+ )),
+ array('config.add', array('load_notifications', 1)),
+ array('custom', array(array($this, 'convert_notifications'))),
+ );
+ }
+
+ public function convert_notifications()
+ {
+ $convert_notifications = array(
+ array(
+ 'check' => ($this->config['allow_topic_notify']),
+ 'item_type' => 'post',
+ ),
+ array(
+ 'check' => ($this->config['allow_forum_notify']),
+ 'item_type' => 'topic',
+ ),
+ array(
+ 'check' => ($this->config['allow_bookmarks']),
+ 'item_type' => 'bookmark',
+ ),
+ array(
+ 'check' => ($this->config['allow_privmsg']),
+ 'item_type' => 'pm',
+ ),
+ );
+
+ foreach ($convert_notifications as $convert_data)
+ {
+ if ($convert_data['check'])
+ {
+ $sql = 'SELECT user_id, user_notify_type
+ FROM ' . USERS_TABLE . '
+ WHERE user_notify = 1';
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array(
+ 'item_type' => $convert_data['item_type'],
+ 'item_id' => 0,
+ 'user_id' => $row['user_id'],
+ 'method' => '',
+ )));
+
+ if ($row['user_notify_type'] == NOTIFY_EMAIL || $row['user_notify_type'] == NOTIFY_BOTH)
+ {
+ $this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array(
+ 'item_type' => $convert_data['item_type'],
+ 'item_id' => 0,
+ 'user_id' => $row['user_id'],
+ 'method' => 'email',
+ )));
+ }
+
+ if ($row['user_notify_type'] == NOTIFY_IM || $row['user_notify_type'] == NOTIFY_BOTH)
+ {
+ $this->sql_query('INSERT INTO ' . $this->table_prefix . 'user_notifications ' . $this->db->sql_build_array('INSERT', array(
+ 'item_type' => $convert_data['item_type'],
+ 'item_id' => 0,
+ 'user_id' => $row['user_id'],
+ 'method' => 'jabber',
+ )));
+ }
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/reported_posts_display.php b/phpBB/includes/db/migration/data/310/reported_posts_display.php
new file mode 100644
index 0000000000..80a0a0e43f
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/reported_posts_display.php
@@ -0,0 +1,47 @@
+db_tools->sql_column_exists($this->table_prefix . 'reports', 'reported_post_enable_bbcode');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode' => array('BOOL', 1),
+ 'reported_post_enable_smilies' => array('BOOL', 1),
+ 'reported_post_enable_magic_url' => array('BOOL', 1),
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'reports' => array(
+ 'reported_post_enable_bbcode',
+ 'reported_post_enable_smilies',
+ 'reported_post_enable_magic_url',
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/style_update_p1.php b/phpBB/includes/db/migration/data/310/style_update_p1.php
new file mode 100644
index 0000000000..e324ce7f24
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/style_update_p1.php
@@ -0,0 +1,157 @@
+db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'styles_update'))),
+ );
+ }
+
+ public function styles_update()
+ {
+ // Get list of valid 3.1 styles
+ $available_styles = array('prosilver');
+
+ $iterator = new DirectoryIterator($this->phpbb_root_path . 'styles');
+ $skip_dirs = array('.', '..', 'prosilver');
+ foreach ($iterator as $fileinfo)
+ {
+ if ($fileinfo->isDir() && !in_array($fileinfo->getFilename(), $skip_dirs) && file_exists($fileinfo->getPathname() . '/style.cfg'))
+ {
+ $style_cfg = parse_cfg_file($fileinfo->getPathname() . '/style.cfg');
+ if (isset($style_cfg['phpbb_version']) && version_compare($style_cfg['phpbb_version'], '3.1.0-dev', '>='))
+ {
+ // 3.1 style
+ $available_styles[] = $fileinfo->getFilename();
+ }
+ }
+ }
+
+ // Get all installed styles
+ if ($this->db_tools->sql_table_exists($this->table_prefix . 'styles_imageset'))
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id, i.imageset_path
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . 'styles_theme c, ' . $this->table_prefix . "styles_imageset i
+ WHERE t.template_id = s.template_id
+ AND c.theme_id = s.theme_id
+ AND i.imageset_id = s.imageset_id";
+ }
+ else
+ {
+ $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_id
+ FROM ' . STYLES_TABLE . ' s, ' . $this->table_prefix . 'styles_template t, ' . $this->table_prefix . "stles_theme c
+ WHERE t.template_id = s.template_id
+ AND c.theme_id = s.theme_id";
+ }
+ $result = $this->db->sql_query($sql);
+
+ $styles = array();
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $styles[] = $row;
+ }
+ $this->db->sql_freeresult($result);
+
+ // Decide which styles to keep, all others will be deleted
+ $valid_styles = array();
+ foreach ($styles as $style_row)
+ {
+ if (
+ // Delete styles with parent style (not supported yet)
+ $style_row['template_inherits_id'] == 0 &&
+ // Check if components match
+ $style_row['template_path'] == $style_row['theme_path'] && (!isset($style_row['imageset_path']) || $style_row['template_path'] == $style_row['imageset_path']) &&
+ // Check if components are valid
+ in_array($style_row['template_path'], $available_styles)
+ )
+ {
+ // Valid style. Keep it
+ $sql_ary = array(
+ 'style_path' => $style_row['template_path'],
+ 'bbcode_bitfield' => $style_row['bbcode_bitfield'],
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+ );
+ $this->sql_query('UPDATE ' . STYLES_TABLE . '
+ SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
+ WHERE style_id = ' . $style_row['style_id']);
+ $valid_styles[] = (int) $style_row['style_id'];
+ }
+ }
+
+ // Remove old entries from styles table
+ if (!sizeof($valid_styles))
+ {
+ // No valid styles: remove everything and add prosilver
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary);
+
+ $sql_ary = array(
+ 'style_name' => 'prosilver',
+ 'style_copyright' => '© phpBB Group',
+ 'style_active' => 1,
+ 'style_path' => 'prosilver',
+ 'bbcode_bitfield' => 'lNg=',
+ 'style_parent_id' => 0,
+ 'style_parent_tree' => '',
+
+ // Will be removed in the next step
+ 'imageset_id' => 0,
+ 'template_id' => 0,
+ 'theme_id' => 0,
+ );
+
+ $sql = 'INSERT INTO ' . STYLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
+ $this->sql_query($sql);
+
+ $sql = 'SELECT style_id
+ FROM ' . $table . "
+ WHERE style_name = 'prosilver'";
+ $result = $this->sql_query($sql);
+ $default_style = $this->db->sql_fetchfield($result);
+ $this->db->sql_freeresult($result);
+
+ set_config('default_style', $default_style);
+
+ $sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0';
+ $this->sql_query($sql);
+ }
+ else
+ {
+ // There are valid styles in styles table. Remove styles that are outdated
+ $this->sql_query('DELETE FROM ' . STYLES_TABLE . '
+ WHERE ' . $this->db->sql_in_set('style_id', $valid_styles, true));
+
+ // Change default style
+ if (!in_array($this->config['default_style'], $valid_styles))
+ {
+ $this->sql_query('UPDATE ' . CONFIG_TABLE . "
+ SET config_value = '" . $valid_styles[0] . "'
+ WHERE config_name = 'default_style'");
+ }
+
+ // Reset styles for users
+ $this->sql_query('UPDATE ' . USERS_TABLE . '
+ SET user_style = 0
+ WHERE ' . $this->db->sql_in_set('user_style', $valid_styles, true));
+ }
+ }
+}
diff --git a/phpBB/includes/db/migration/data/310/style_update_p2.php b/phpBB/includes/db/migration/data/310/style_update_p2.php
new file mode 100644
index 0000000000..7b10518a66
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/style_update_p2.php
@@ -0,0 +1,129 @@
+db_tools->sql_table_exists($this->table_prefix . 'styles_imageset');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_310_style_update_p1');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id',
+ 'template_id',
+ 'theme_id',
+ ),
+ ),
+
+ 'drop_tables' => array(
+ $this->table_prefix . 'styles_imageset',
+ $this->table_prefix . 'styles_imageset_data',
+ $this->table_prefix . 'styles_template',
+ $this->table_prefix . 'styles_template_data',
+ $this->table_prefix . 'styles_theme',
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'styles' => array(
+ 'imageset_id' => array('UINT', 0),
+ 'template_id' => array('UINT', 0),
+ 'theme_id' => array('UINT', 0),
+ ),
+ ),
+
+ 'add_tables' => array(
+ $this->table_prefix . 'styles_imageset' => array(
+ 'COLUMNS' => array(
+ 'imageset_id' => array('UINT', NULL, 'auto_increment'),
+ 'imageset_name' => array('VCHAR_UNI:255', ''),
+ 'imageset_copyright' => array('VCHAR_UNI', ''),
+ 'imageset_path' => array('VCHAR:100', ''),
+ ),
+ 'PRIMARY_KEY' => 'imageset_id',
+ 'KEYS' => array(
+ 'imgset_nm' => array('UNIQUE', 'imageset_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_imageset_data' => array(
+ 'COLUMNS' => array(
+ 'image_id' => array('UINT', NULL, 'auto_increment'),
+ 'image_name' => array('VCHAR:200', ''),
+ 'image_filename' => array('VCHAR:200', ''),
+ 'image_lang' => array('VCHAR:30', ''),
+ 'image_height' => array('USINT', 0),
+ 'image_width' => array('USINT', 0),
+ 'imageset_id' => array('UINT', 0),
+ ),
+ 'PRIMARY_KEY' => 'image_id',
+ 'KEYS' => array(
+ 'i_d' => array('INDEX', 'imageset_id'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', NULL, 'auto_increment'),
+ 'template_name' => array('VCHAR_UNI:255', ''),
+ 'template_copyright' => array('VCHAR_UNI', ''),
+ 'template_path' => array('VCHAR:100', ''),
+ 'bbcode_bitfield' => array('VCHAR:255', 'kNg='),
+ 'template_storedb' => array('BOOL', 0),
+ 'template_inherits_id' => array('UINT:4', 0),
+ 'template_inherit_path' => array('VCHAR', ''),
+ ),
+ 'PRIMARY_KEY' => 'template_id',
+ 'KEYS' => array(
+ 'tmplte_nm' => array('UNIQUE', 'template_name'),
+ ),
+ ),
+ $this->table_prefix . 'styles_template_data' => array(
+ 'COLUMNS' => array(
+ 'template_id' => array('UINT', 0),
+ 'template_filename' => array('VCHAR:100', ''),
+ 'template_included' => array('TEXT', ''),
+ 'template_mtime' => array('TIMESTAMP', 0),
+ 'template_data' => array('MTEXT_UNI', ''),
+ ),
+ 'KEYS' => array(
+ 'tid' => array('INDEX', 'template_id'),
+ 'tfn' => array('INDEX', 'template_filename'),
+ ),
+ ),
+ $this->table_prefix . 'styles_theme' => array(
+ 'COLUMNS' => array(
+ 'theme_id' => array('UINT', NULL, 'auto_increment'),
+ 'theme_name' => array('VCHAR_UNI:255', ''),
+ 'theme_copyright' => array('VCHAR_UNI', ''),
+ 'theme_path' => array('VCHAR:100', ''),
+ 'theme_storedb' => array('BOOL', 0),
+ 'theme_mtime' => array('TIMESTAMP', 0),
+ 'theme_data' => array('MTEXT_UNI', ''),
+ ),
+ 'PRIMARY_KEY' => 'theme_id',
+ 'KEYS' => array(
+ 'theme_name' => array('UNIQUE', 'theme_name'),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/update_helpers.php b/phpBB/includes/db/migration/data/310/timezone.php
similarity index 72%
rename from phpBB/includes/update_helpers.php
rename to phpBB/includes/db/migration/data/310/timezone.php
index 69d678b2f8..6e50cbe45f 100644
--- a/phpBB/includes/update_helpers.php
+++ b/phpBB/includes/db/migration/data/310/timezone.php
@@ -1,16 +1,67 @@
db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_30x_3_0_11');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'change_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_timezone' => array('VCHAR:100', ''),
+ ),
+ ),
+ );
+ }
+
+ public function update_data()
+ {
+ return array(
+ array('custom', array(array($this, 'update_timezones'))),
+ );
+ }
+
+ public function update_timezones()
+ {
+ // Update user timezones
+ $sql = 'SELECT user_dst, user_timezone
+ FROM ' . $this->table_prefix . 'users
+ GROUP BY user_timezone, user_dst';
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $sql = 'UPDATE ' . $this->table_prefix . "users
+ SET user_timezone = '" . $this->db->sql_escape($this->convert_phpbb30_timezone($row['user_timezone'], $row['user_dst'])) . "'
+ WHERE user_timezone = '" . $this->db->sql_escape($row['user_timezone']) . "'
+ AND user_dst = " . (int) $row['user_dst'];
+ $this->sql_query($sql);
+ }
+ $this->db->sql_freeresult($result);
+
+ // Update board default timezone
+ $sql = 'UPDATE ' . $this->table_prefix . "config
+ SET config_value = '" . $this->convert_phpbb30_timezone($this->config['board_timezone'], $this->config['board_dst']) . "'
+ WHERE config_name = 'board_timezone'";
+ $this->sql_query($sql);
+ }
+
/**
* Determine the new timezone for a given phpBB 3.0 timezone and
* "Daylight Saving Time" option
@@ -19,7 +70,7 @@ class phpbb_update_helpers
* @param $dst int Users daylight saving time
* @return string Users new php Timezone which is used since 3.1
*/
- function convert_phpbb30_timezone($timezone, $dst)
+ public function convert_phpbb30_timezone($timezone, $dst)
{
$offset = $timezone + $dst;
diff --git a/phpBB/includes/db/migration/data/310/timezone_p2.php b/phpBB/includes/db/migration/data/310/timezone_p2.php
new file mode 100644
index 0000000000..113b979e4f
--- /dev/null
+++ b/phpBB/includes/db/migration/data/310/timezone_p2.php
@@ -0,0 +1,43 @@
+db_tools->sql_column_exists($this->table_prefix . 'users', 'user_dst');
+ }
+
+ static public function depends_on()
+ {
+ return array('phpbb_db_migration_data_310_timezone');
+ }
+
+ public function update_schema()
+ {
+ return array(
+ 'drop_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst',
+ ),
+ ),
+ );
+ }
+
+ public function revert_schema()
+ {
+ return array(
+ 'add_columns' => array(
+ $this->table_prefix . 'users' => array(
+ 'user_dst' => array('BOOL', 0),
+ ),
+ ),
+ );
+ }
+}
diff --git a/phpBB/includes/db/migration/exception.php b/phpBB/includes/db/migration/exception.php
index ffdcd97780..e84330dd71 100644
--- a/phpBB/includes/db/migration/exception.php
+++ b/phpBB/includes/db/migration/exception.php
@@ -52,4 +52,28 @@ class phpbb_db_migration_exception extends \Exception
{
return $this->message . ': ' . var_export($this->parameters, true);
}
+
+ /**
+ * Get the parameters
+ *
+ * @return array
+ */
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Get localised message (with $user->lang())
+ *
+ * @param phpbb_user $user
+ * @return string
+ */
+ public function getLocalisedMessage(phpbb_user $user)
+ {
+ $parameters = $this->getParameters();
+ array_unshift($parameters, $this->getMessage());
+
+ return call_user_func_array(array($user, 'lang'), $parameters);
+ }
}
diff --git a/phpBB/includes/db/migration/tool/config.php b/phpBB/includes/db/migration/tool/config.php
index d9cc20053e..458a25fb66 100644
--- a/phpBB/includes/db/migration/tool/config.php
+++ b/phpBB/includes/db/migration/tool/config.php
@@ -49,7 +49,7 @@ class phpbb_db_migration_tool_config implements phpbb_db_migration_tool_interfac
{
if (isset($this->config[$config_name]))
{
- throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXISTS', $config_name);
+ throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXIST', $config_name);
}
$this->config->set($config_name, $config_value, !$is_dynamic);
diff --git a/phpBB/includes/db/migration/tool/module.php b/phpBB/includes/db/migration/tool/module.php
index afe1f21ec5..ad94c5aadb 100644
--- a/phpBB/includes/db/migration/tool/module.php
+++ b/phpBB/includes/db/migration/tool/module.php
@@ -183,25 +183,7 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac
$basename = str_replace(array('/', '\\'), '', $basename);
$class = str_replace(array('/', '\\'), '', $class);
- $include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path;
- $info_file = "$class/info/$basename.{$this->php_ext}";
-
- // The manual and automatic ways both failed...
- if (!file_exists($include_path . $info_file))
- {
- throw new phpbb_db_migration_exception('MODULE_INFO_FILE_NOT_EXIST', $class, $info_file);
- }
-
- $classname = "{$basename}_info";
-
- if (!class_exists($classname))
- {
- include($include_path . $info_file);
- }
-
- $info = new $classname;
- $module = $info->module();
- unset($info);
+ $module = $this->get_module_info($class, $basename);
$result = '';
foreach ($module['modes'] as $mode => $module_info)
@@ -242,14 +224,14 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac
if (!$module_id)
{
- throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent);
+ throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent);
}
$parent = $data['parent_id'] = $module_id;
}
else if (!$this->exists($class, false, $parent))
{
- throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent);
+ throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent);
}
if ($this->exists($class, $parent, $data['module_langname']))
@@ -373,30 +355,13 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac
$basename = str_replace(array('/', '\\'), '', $module['module_basename']);
$class = str_replace(array('/', '\\'), '', $class);
- $include_path = ($include_path === false) ? $this->phpbb_root_path . 'includes/' : $include_path;
- $info_file = "$class/info/$basename.{$this->php_ext}";
-
- if (!file_exists($include_path . $info_file))
- {
- throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $info_file);
- }
-
- $classname = "{$basename}_info";
-
- if (!class_exists($classname))
- {
- include($include_path . $info_file);
- }
-
- $info = new $classname;
- $module_info = $info->module();
- unset($info);
+ $module_info = $this->get_module_info($class, $basename);
foreach ($module_info['modes'] as $mode => $info)
{
if (!isset($module['modes']) || in_array($mode, $module['modes']))
{
- $this->remove($class, $parent, $info['title']) . '
';
+ $this->remove($class, $parent, $info['title']);
}
}
}
@@ -477,7 +442,7 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac
$result = $acp_modules->delete_module($module_id);
if (!empty($result))
{
- throw new phpbb_db_migration_exception('CANNOT_REMOVE_MODULE', $module_id);
+ throw new phpbb_db_migration_exception('MODULE_NOT_REMOVABLE', $module_id, $result);
}
}
@@ -510,4 +475,28 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac
return call_user_func_array(array(&$this, $call), $arguments);
}
}
+
+ /**
+ * Wrapper for acp_modules::get_module_infos()
+ *
+ * @param string $class Module Class
+ * @param string $basename Module Basename
+ * @return array Module Information
+ */
+ 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);
+
+ if (empty($module))
+ {
+ throw new phpbb_db_migration_exception('MODULE_INFO_FILE_NOT_EXIST', $class, $basename);
+ }
+
+ return array_pop($module);
+ }
}
diff --git a/phpBB/includes/db/migration/tool/permission.php b/phpBB/includes/db/migration/tool/permission.php
index 001d090f5a..4231fbe1dd 100644
--- a/phpBB/includes/db/migration/tool/permission.php
+++ b/phpBB/includes/db/migration/tool/permission.php
@@ -107,7 +107,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte
{
if ($this->exists($auth_option, $global))
{
- throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXISTS', $auth_option);
+ throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXIST', $auth_option);
}
// We've added permissions, so set to true to notify the user.
@@ -252,7 +252,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte
if ($role_id)
{
- throw new phpbb_db_migration_exception('ROLE_ALREADY_EXISTS', $old_role_name);
+ return;
}
$sql = 'SELECT MAX(role_order) AS max_role_order
@@ -290,7 +290,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte
if (!$role_id)
{
- throw new phpbb_db_migration_exception('ROLE_NOT_EXISTS', $old_role_name);
+ throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $old_role_name);
}
$sql = 'UPDATE ' . ACL_ROLES_TABLE . "
diff --git a/phpBB/includes/db/migrator.php b/phpBB/includes/db/migrator.php
index 4456600b0a..de9c06948c 100644
--- a/phpBB/includes/db/migrator.php
+++ b/phpBB/includes/db/migrator.php
@@ -31,6 +31,9 @@ class phpbb_db_migrator
/** @var phpbb_db_tools */
protected $db_tools;
+ /** @var phpbb_extension_manager */
+ protected $extension_manager;
+
/** @var string */
protected $table_prefix;
@@ -90,6 +93,16 @@ class phpbb_db_migrator
$this->load_migration_state();
}
+ /**
+ * Set Extension Manager (required)
+ *
+ * Not in constructor to prevent circular reference error
+ */
+ public function set_extension_manager(phpbb_extension_manager $extension_manager)
+ {
+ $this->extension_manager = $extension_manager;
+ }
+
/**
* Loads all migrations and their application state from the database.
*
@@ -99,18 +112,26 @@ class phpbb_db_migrator
{
$this->migration_state = array();
+ // prevent errors in case the table does not exist yet
+ $this->db->sql_return_on_error(true);
+
$sql = "SELECT *
FROM " . $this->migrations_table;
$result = $this->db->sql_query($sql);
- while ($migration = $this->db->sql_fetchrow($result))
+ if (!$this->db->sql_error_triggered)
{
- $this->migration_state[$migration['migration_name']] = $migration;
+ while ($migration = $this->db->sql_fetchrow($result))
+ {
+ $this->migration_state[$migration['migration_name']] = $migration;
- $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
+ $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
+ }
}
$this->db->sql_freeresult($result);
+
+ $this->db->sql_return_on_error(false);
}
/**
@@ -172,55 +193,32 @@ class phpbb_db_migrator
* If FALSE, we will not check. You SHOULD check at least once
* to prevent errors (if including multiple directories, check
* with the last call to prevent throwing errors unnecessarily).
- * @param bool $recursive Set to true to also load data files from subdirectories
* @return array Array of migration names
*/
- public function load_migrations($path, $check_fulfillable = true, $recursive = true)
+ public function load_migrations($path, $check_fulfillable = true)
{
if (!is_dir($path))
{
throw new phpbb_db_migration_exception('DIRECTORY INVALID', $path);
}
- $handle = opendir($path);
- while (($file = readdir($handle)) !== false)
+ $migrations = array();
+
+ $finder = $this->extension_manager->get_finder();
+ $files = $finder
+ ->extension_directory("/")
+ ->find_from_paths(array('/' => $path));
+ foreach ($files as $file)
{
- if ($file == '.' || $file == '..')
+ $migrations[$file['path'] . $file['filename']] = '';
+ }
+ $migrations = $finder->get_classes_from_files($migrations);
+
+ foreach ($migrations as $migration)
+ {
+ if (!in_array($migration, $this->migrations))
{
- continue;
- }
-
- // Recursion through subdirectories
- if (is_dir($path . $file) && $recursive)
- {
- $this->load_migrations($path . $file . '/', $check_fulfillable, $recursive);
- }
-
- if (strpos($file, '_') !== 0 && strrpos($file, '.' . $this->php_ext) === (strlen($file) - strlen($this->php_ext) - 1))
- {
- // We try to find what class existed by comparing the classes declared before and after including the file.
- $declared_classes = get_declared_classes();
-
- include ($path . $file);
-
- $added_classes = array_diff(get_declared_classes(), $declared_classes);
-
- if (
- // If two classes have been added and phpbb_db_migration is one of them, we've only added one real migration
- !(sizeof($added_classes) == 2 && in_array('phpbb_db_migration', $added_classes)) &&
- // Otherwise there should only be one class added
- sizeof($added_classes) != 1
- )
- {
- throw new phpbb_db_migration_exception('MIGRATION DATA FILE INVALID', $path . $file);
- }
-
- $name = array_pop($added_classes);
-
- if (!in_array($name, $this->migrations))
- {
- $this->migrations[] = $name;
- }
+ $this->migrations[] = $migration;
}
}
@@ -228,9 +226,10 @@ class phpbb_db_migrator
{
foreach ($this->migrations as $name)
{
- if ($this->unfulfillable($name))
+ $unfulfillable = $this->unfulfillable($name);
+ if ($unfulfillable !== false)
{
- throw new phpbb_db_migration_exception('MIGRATION NOT FULFILLABLE', $name);
+ throw new phpbb_db_migration_exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable);
}
}
}
@@ -307,23 +306,22 @@ class phpbb_db_migrator
'class' => $migration,
);
- if ($migration->effectively_installed())
+ if (!isset($this->migration_state[$name]))
{
- $state = array(
- 'migration_depends_on' => $migration->depends_on(),
- 'migration_schema_done' => true,
- 'migration_data_done' => true,
- 'migration_data_state' => '',
- 'migration_start_time' => 0,
- 'migration_end_time' => 0,
- );
- }
- else
- {
- if (!isset($this->migration_state[$name]))
+ if ($migration->effectively_installed())
+ {
+ $state = array(
+ 'migration_depends_on' => $migration->depends_on(),
+ 'migration_schema_done' => true,
+ 'migration_data_done' => true,
+ 'migration_data_state' => '',
+ 'migration_start_time' => 0,
+ 'migration_end_time' => 0,
+ );
+ }
+ else
{
$state['migration_start_time'] = time();
- $this->insert_migration($name, $state);
}
}
@@ -352,14 +350,7 @@ class phpbb_db_migrator
}
}
- $insert = $state;
- $insert['migration_depends_on'] = serialize($state['migration_depends_on']);
- $sql = 'UPDATE ' . $this->migrations_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $insert) . "
- WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
- $this->db->sql_query($sql);
-
- $this->migration_state[$name] = $state;
+ $this->insert_migration($name, $state);
return true;
}
@@ -425,20 +416,13 @@ class phpbb_db_migrator
}
else
{
- $result = $this->process_data_step($migration->revert_data(), $state['migration_data_state'], false);
+ $result = $this->process_data_step($migration->revert_data(), '', false);
$state['migration_data_state'] = ($result === true) ? '' : $result;
$state['migration_data_done'] = ($result === true) ? false : true;
}
- $insert = $state;
- $insert['migration_depends_on'] = serialize($state['migration_depends_on']);
- $sql = 'UPDATE ' . $this->migrations_table . '
- SET ' . $this->db->sql_build_array('UPDATE', $insert) . "
- WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
- $this->db->sql_query($sql);
-
- $this->migration_state[$name] = $state;
+ $this->insert_migration($name, $state);
}
else
{
@@ -651,7 +635,7 @@ class phpbb_db_migrator
}
/**
- * Insert migration row into the database
+ * Insert/Update migration row into the database
*
* @param string $name Name of the migration
* @param array $state
@@ -660,12 +644,22 @@ class phpbb_db_migrator
protected function insert_migration($name, $state)
{
$migration_row = $state;
- $migration_row['migration_name'] = $name;
$migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
- $sql = 'INSERT INTO ' . $this->migrations_table . '
- ' . $this->db->sql_build_array('INSERT', $migration_row);
- $this->db->sql_query($sql);
+ if (isset($this->migration_state[$name]))
+ {
+ $sql = 'UPDATE ' . $this->migrations_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . "
+ WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+ }
+ else
+ {
+ $migration_row['migration_name'] = $name;
+ $sql = 'INSERT INTO ' . $this->migrations_table . '
+ ' . $this->db->sql_build_array('INSERT', $migration_row);
+ $this->db->sql_query($sql);
+ }
$this->migration_state[$name] = $state;
}
@@ -674,7 +668,7 @@ class phpbb_db_migrator
* Checks if a migration's dependencies can even theoretically be satisfied.
*
* @param string $name The class name of the migration
- * @return bool Whether the migration cannot be fulfilled
+ * @return bool|string False if fulfillable, string of missing migration name if unfulfillable
*/
public function unfulfillable($name)
{
@@ -685,7 +679,7 @@ class phpbb_db_migrator
if (!class_exists($name))
{
- return true;
+ return $name;
}
$migration = $this->get_migration($name);
@@ -693,9 +687,10 @@ class phpbb_db_migrator
foreach ($depends as $depend)
{
- if ($this->unfulfillable($depend))
+ $unfulfillable = $this->unfulfillable($depend);
+ if ($unfulfillable !== false)
{
- return true;
+ return $unfulfillable;
}
}
@@ -715,7 +710,7 @@ class phpbb_db_migrator
{
// skip unfulfillable migrations, but fulfillables mean we
// are not finished yet
- if ($this->unfulfillable($name))
+ if ($this->unfulfillable($name) !== false)
{
continue;
}
diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php
index 9d076eb6c5..d51589d719 100644
--- a/phpBB/includes/extension/base.php
+++ b/phpBB/includes/extension/base.php
@@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))
exit;
}
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* A base class for extensions without custom enable/disable/purge code.
*
@@ -22,6 +24,19 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_extension_base implements phpbb_extension_interface
{
+ /** @var ContainerInterface */
+ protected $container;
+
+ /**
+ * Constructor
+ *
+ * @param ContainerInterface $container Container object
+ */
+ public function __construct(ContainerInterface $container)
+ {
+ $this->container = $container;
+ }
+
/**
* Single enable step that does nothing
*
diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php
index fb19b98429..f71e32bc8d 100644
--- a/phpBB/includes/extension/finder.php
+++ b/phpBB/includes/extension/finder.php
@@ -247,15 +247,28 @@ class phpbb_extension_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)
+ public function get_classes($cache = true, $use_all_available = false)
{
$this->query['extension_suffix'] .= $this->php_ext;
$this->query['core_suffix'] .= $this->php_ext;
- $files = $this->find($cache, false);
+ $files = $this->find($cache, false, $use_all_available);
+ return $this->get_classes_from_files($files);
+ }
+
+ /**
+ * Get class names from a list of files
+ *
+ * @param array $files Array of files (from find())
+ * @return array Array of class names
+ */
+ public function get_classes_from_files($files)
+ {
$classes = array();
foreach ($files as $file => $ext_name)
{
@@ -270,23 +283,27 @@ class phpbb_extension_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, $extension_keys = false)
+ public function get_directories($cache = true, $use_all_available = false, $extension_keys = false)
{
- return $this->find_with_root_path($cache, true, $extension_keys);
+ return $this->find_with_root_path($cache, true, $use_all_available, $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)
+ public function get_files($cache = true, $use_all_available = false)
{
- return $this->find_with_root_path($cache, false);
+ return $this->find_with_root_path($cache, false, $use_all_available);
}
/**
@@ -295,13 +312,15 @@ class phpbb_extension_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
* @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, $extension_keys = false)
+ protected function find_with_root_path($cache = true, $is_dir = false, $use_all_available = false, $extension_keys = false)
{
- $items = $this->find($cache, $is_dir);
+ $items = $this->find($cache, $is_dir, $use_all_available);
$result = array();
foreach ($items as $item => $ext_name)
@@ -325,12 +344,51 @@ class phpbb_extension_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)
+ public function find($cache = true, $is_dir = false, $use_all_available = false)
+ {
+ if ($use_all_available)
+ {
+ $extensions = $this->extension_manager->all_available();
+ }
+ else
+ {
+ $extensions = $this->extension_manager->all_enabled();
+ }
+
+ if ($this->query['core_path'])
+ {
+ $extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];
+ }
+
+ $files = array();
+ $file_list = $this->find_from_paths($extensions, $cache, $is_dir);
+
+ foreach ($file_list as $file)
+ {
+ $files[$file['named_path']] = $file['ext_name'];
+ }
+
+ return $files;
+ }
+
+ /**
+ * Finds all file system entries matching the configured options from
+ * an array of paths
+ *
+ * @param array $extensions Array of extensions (name => full relative path)
+ * @param bool $cache Whether the result should be cached
+ * @param bool $is_dir Directories will be returned when true, only files
+ * otherwise
+ * @return array An array of paths to found items
+ */
+ public function find_from_paths($extensions, $cache = true, $is_dir = false)
{
$this->query['is_dir'] = $is_dir;
- $query = md5(serialize($this->query));
+ $query = md5(serialize($this->query) . serialize($extensions));
if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query]))
{
@@ -339,13 +397,6 @@ class phpbb_extension_finder
$files = array();
- $extensions = $this->extension_manager->all_enabled();
-
- if ($this->query['core_path'])
- {
- $extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];
- }
-
foreach ($extensions as $name => $path)
{
$ext_name = $name;
@@ -419,7 +470,12 @@ class phpbb_extension_finder
(!$prefix || substr($filename, 0, strlen($prefix)) === $prefix) &&
(!$directory || preg_match($directory_pattern, $relative_path)))
{
- $files[str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1))] = $ext_name;
+ $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/includes/extension/manager.php b/phpBB/includes/extension/manager.php
index de6f364320..0d760681b9 100644
--- a/phpBB/includes/extension/manager.php
+++ b/phpBB/includes/extension/manager.php
@@ -15,6 +15,8 @@ if (!defined('IN_PHPBB'))
exit;
}
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
/**
* The extension manager provides means to activate/deactivate extensions.
*
@@ -22,8 +24,12 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_extension_manager
{
+ /** @var ContainerInterface */
+ protected $container;
+
protected $db;
protected $config;
+ protected $migrator;
protected $cache;
protected $php_ext;
protected $extensions;
@@ -34,6 +40,7 @@ class phpbb_extension_manager
/**
* Creates a manager and loads information from database
*
+ * @param ContainerInterface $container A container
* @param phpbb_db_driver $db A database connection
* @param phpbb_config $config phpbb_config
* @param string $extension_table The name of the table holding extensions
@@ -42,8 +49,9 @@ class phpbb_extension_manager
* @param phpbb_cache_driver_interface $cache A cache instance or null
* @param string $cache_name The name of the cache variable, defaults to _ext
*/
- public function __construct(phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
+ public function __construct(ContainerInterface $container, phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
{
+ $this->container = $container;
$this->phpbb_root_path = $phpbb_root_path;
$this->db = $db;
$this->config = $config;
@@ -60,6 +68,14 @@ class phpbb_extension_manager
}
}
+ /**
+ * Set migrator (get around circular reference)
+ */
+ public function set_migrator(phpbb_db_migrator $migrator)
+ {
+ $this->migrator = $migrator;
+ }
+
/**
* Loads all extension information from the database
*
@@ -126,11 +142,11 @@ class phpbb_extension_manager
if (class_exists($extension_class_name))
{
- return new $extension_class_name;
+ return new $extension_class_name($this->container);
}
else
{
- return new phpbb_extension_base;
+ return new phpbb_extension_base($this->container);
}
}
@@ -166,6 +182,12 @@ class phpbb_extension_manager
$old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
+ // Returns false if not completed
+ if (!$this->handle_migrations($name, 'enable'))
+ {
+ return true;
+ }
+
$extension = $this->get_extension($name);
$state = $extension->enable_step($old_state);
@@ -317,6 +339,12 @@ class phpbb_extension_manager
$old_state = unserialize($this->extensions[$name]['ext_state']);
+ // Returns false if not completed
+ if (!$this->handle_migrations($name, 'purge'))
+ {
+ return true;
+ }
+
$extension = $this->get_extension($name);
$state = $extension->purge_step($old_state);
@@ -490,4 +518,58 @@ class phpbb_extension_manager
{
return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder');
}
+
+ /**
+ * Handle installing/reverting migrations
+ *
+ * @param string $extension_name Name of the extension
+ * @param string $mode enable or purge
+ * @return bool True if completed, False if not completed
+ */
+ protected function handle_migrations($extension_name, $mode)
+ {
+ $migrations_path = $this->phpbb_root_path . $this->get_extension_path($extension_name) . 'migrations/';
+ if (!file_exists($migrations_path) || !is_dir($migrations_path))
+ {
+ return true;
+ }
+
+ $migrations = $this->migrator->load_migrations($migrations_path);
+
+ // What is a safe limit of execution time? Half the max execution time should be safe.
+ $safe_time_limit = (ini_get('max_execution_time') / 2);
+ $start_time = time();
+
+ if ($mode == 'enable')
+ {
+ while (!$this->migrator->finished())
+ {
+ $this->migrator->update();
+
+ // Are we approaching the time limit? If so we want to pause the update and continue after refreshing
+ if ((time() - $start_time) >= $safe_time_limit)
+ {
+ return false;
+ }
+ }
+ }
+ else if ($mode == 'purge')
+ {
+ foreach ($migrations as $migration)
+ {
+ while ($this->migrator->migration_state($migration) !== false)
+ {
+ $this->migrator->revert($migration);
+
+ // Are we approaching the time limit? If so we want to pause the update and continue after refreshing
+ if ((time() - $start_time) >= $safe_time_limit)
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
}
diff --git a/phpBB/includes/functions.php b/phpBB/includes/functions.php
index d0ef2759d5..6a1f144967 100644
--- a/phpBB/includes/functions.php
+++ b/phpBB/includes/functions.php
@@ -97,7 +97,18 @@ function request_var($var_name, $default, $multibyte = false, $cookie = false, $
}
/**
-* Set config value. Creates missing config entry.
+* Sets a configuration option's value.
+*
+* Please note that this function does not update the is_dynamic value for
+* an already existing config option.
+*
+* @param string $config_name The configuration option's name
+* @param string $config_value New configuration value
+* @param bool $is_dynamic Whether this variable should be cached (false) or
+* if it changes too frequently (true) to be
+* efficiently cached.
+*
+* @return null
*
* @deprecated
*/
@@ -119,7 +130,15 @@ function set_config($config_name, $config_value, $is_dynamic = false, phpbb_conf
}
/**
-* Set dynamic config value with arithmetic operation.
+* Increments an integer config value directly in the database.
+*
+* @param string $config_name The configuration option's name
+* @param int $increment Amount to increment by
+* @param bool $is_dynamic Whether this variable should be cached (false) or
+* if it changes too frequently (true) to be
+* efficiently cached.
+*
+* @return null
*
* @deprecated
*/
@@ -1328,7 +1347,7 @@ function phpbb_timezone_select($user, $default = '', $truncate = false)
function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
{
global $db, $user, $config;
- global $request;
+ global $request, $phpbb_container;
$post_time = ($post_time === 0 || $post_time > time()) ? time() : (int) $post_time;
@@ -1336,6 +1355,20 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $
{
if ($forum_id === false || !sizeof($forum_id))
{
+ // Mark all forums read (index page)
+
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ // Mark all topic notifications read for this user
+ $phpbb_notifications->mark_notifications_read(array(
+ 'topic',
+ 'quote',
+ 'bookmark',
+ 'post',
+ 'approve_topic',
+ 'approve_post',
+ ), false, $user->data['user_id'], $post_time);
+
if ($config['load_db_lastread'] && $user->data['is_registered'])
{
// Mark all forums read (index page)
@@ -1390,6 +1423,32 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $
$forum_id = array($forum_id);
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->mark_notifications_read_by_parent(array(
+ 'topic',
+ 'approve_topic',
+ ), $forum_id, $user->data['user_id'], $post_time);
+
+ // Mark all post/quote notifications read for this user in this forum
+ $topic_ids = array();
+ $sql = 'SELECT topic_id
+ FROM ' . TOPICS_TABLE . '
+ WHERE ' . $db->sql_in_set('forum_id', $forum_id);
+ $result = $db->sql_query($sql);
+ while ($row = $db->sql_fetchrow($result))
+ {
+ $topic_ids[] = $row['topic_id'];
+ }
+ $db->sql_freeresult($result);
+
+ $phpbb_notifications->mark_notifications_read_by_parent(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ 'approve_post',
+ ), $topic_ids, $user->data['user_id'], $post_time);
+
// Add 0 to forums array to mark global announcements correctly
// $forum_id[] = 0;
@@ -1487,6 +1546,21 @@ function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $
return;
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ // Mark post notifications read for this user in this topic
+ $phpbb_notifications->mark_notifications_read(array(
+ 'topic',
+ 'approve_topic',
+ ), $topic_id, $user->data['user_id'], $post_time);
+
+ $phpbb_notifications->mark_notifications_read_by_parent(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ 'approve_post',
+ ), $topic_id, $user->data['user_id'], $post_time);
+
if ($config['load_db_lastread'] && $user->data['is_registered'])
{
$sql = 'UPDATE ' . TOPICS_TRACK_TABLE . "
@@ -4994,7 +5068,7 @@ function phpbb_build_hidden_fields_for_query_params($request, $exclude = null)
function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum')
{
global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path;
- global $phpbb_dispatcher, $request;
+ global $phpbb_dispatcher, $request, $phpbb_container;
if (defined('HEADER_INC'))
{
@@ -5183,8 +5257,26 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
$timezone_name = $user->lang['timezones'][$timezone_name];
}
+ // Output the notifications
+ $notifications = false;
+ if ($config['load_notifications'] && $user->data['user_id'] != ANONYMOUS && $user->data['user_type'] != USER_IGNORE)
+ {
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $notifications = $phpbb_notifications->load_notifications(array(
+ 'all_unread' => true,
+ 'limit' => 5,
+ ));
+
+ foreach ($notifications['notifications'] as $notification)
+ {
+ $template->assign_block_vars('notifications', $notification->prepare_for_display());
+ }
+ }
+
$hidden_fields_for_jumpbox = phpbb_build_hidden_fields_for_query_params($request, array('f'));
+
// The following assigns all _common_ variables that may be used at any point in a template.
$template->assign_vars(array(
'SITENAME' => $config['sitename'],
@@ -5201,6 +5293,12 @@ function page_header($page_title = '', $display_online_list = true, $item_id = 0
'PRIVATE_MESSAGE_INFO_UNREAD' => $l_privmsgs_text_unread,
'HIDDEN_FIELDS_FOR_JUMPBOX' => $hidden_fields_for_jumpbox,
+ 'UNREAD_NOTIFICATIONS_COUNT' => ($notifications !== false) ? $notifications['unread_count'] : '',
+ 'NOTIFICATIONS_COUNT' => ($notifications !== false) ? $user->lang('NOTIFICATIONS_COUNT', $notifications['unread_count']) : '',
+ 'U_VIEW_ALL_NOTIFICATIONS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications'),
+ 'U_NOTIFICATION_SETTINGS' => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_notifications&mode=notification_options'),
+ 'S_NOTIFICATIONS_DISPLAY' => $config['load_notifications'],
+
'S_USER_NEW_PRIVMSG' => $user->data['user_new_privmsg'],
'S_USER_UNREAD_PRIVMSG' => $user->data['user_unread_privmsg'],
'S_USER_NEW' => $user->data['user_new'],
@@ -5582,7 +5680,7 @@ function phpbb_convert_30_dbms_to_31($dbms)
/*
$reflection = new \ReflectionClass($dbms);
-
+
if ($reflection->isSubclassOf('phpbb_db_driver'))
{
return $dbms;
diff --git a/phpBB/includes/functions_acp.php b/phpBB/includes/functions_acp.php
index 32fd76e74d..d6bd9e35dd 100644
--- a/phpBB/includes/functions_acp.php
+++ b/phpBB/includes/functions_acp.php
@@ -443,6 +443,13 @@ function validate_config_vars($config_vars, &$cfg_array, &$error)
}
break;
+ case 'email':
+ if (!preg_match('/^' . get_preg_expression('email') . '$/i', $cfg_array[$config_name]))
+ {
+ $error[] = $user->lang['EMAIL_INVALID_EMAIL'];
+ }
+ break;
+
// Absolute path
case 'script_path':
if (!$cfg_array[$config_name])
diff --git a/phpBB/includes/functions_admin.php b/phpBB/includes/functions_admin.php
index 5529f2af46..baf107bcda 100644
--- a/phpBB/includes/functions_admin.php
+++ b/phpBB/includes/functions_admin.php
@@ -618,7 +618,7 @@ function move_posts($post_ids, $topic_id, $auto_sync = true)
*/
function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
{
- global $db, $config;
+ global $db, $config, $phpbb_container;
$approved_topics = 0;
$forum_ids = $topic_ids = array();
@@ -715,6 +715,14 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s
set_config_count('num_topics', $approved_topics * (-1), true);
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->delete_notifications(array(
+ 'topic',
+ 'approve_topic',
+ 'topic_in_queue',
+ ), $topic_ids);
+
return $return;
}
@@ -723,7 +731,7 @@ function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_s
*/
function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
{
- global $db, $config, $phpbb_root_path, $phpEx, $auth, $user;
+ global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container;
if ($where_type === 'range')
{
@@ -892,6 +900,16 @@ function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync =
delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->delete_notifications(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ 'approve_post',
+ 'post_in_queue',
+ ), $post_ids);
+
return sizeof($post_ids);
}
diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php
index d0a02567ad..821f0d970d 100644
--- a/phpBB/includes/functions_messenger.php
+++ b/phpBB/includes/functions_messenger.php
@@ -392,6 +392,28 @@ class messenger
}
}
+ /**
+ * Generates a valid message id to be used in emails
+ *
+ * @return string message id
+ */
+ function generate_message_id()
+ {
+ global $config;
+
+ $domain = 'phpbb.generated';
+ if ($config['server_name'])
+ {
+ $domain = $config['server_name'];
+ }
+ else if (!empty($_SERVER['SERVER_NAME']))
+ {
+ $domain = $_SERVER['SERVER_NAME'];
+ }
+
+ return md5(unique_id(time())) . '@' . $domain;
+ }
+
/**
* Return email header
*/
@@ -418,7 +440,7 @@ class messenger
$headers[] = 'Return-Path: <' . $config['board_email'] . '>';
$headers[] = 'Sender: <' . $config['board_email'] . '>';
$headers[] = 'MIME-Version: 1.0';
- $headers[] = 'Message-ID: <' . md5(unique_id(time())) . '@' . $config['server_name'] . '>';
+ $headers[] = 'Message-ID: <' . $this->generate_message_id() . '>';
$headers[] = 'Date: ' . date('r', time());
$headers[] = 'Content-Type: text/plain; charset=UTF-8'; // format=flowed
$headers[] = 'Content-Transfer-Encoding: 8bit'; // 7bit
diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php
index 8aea27a9ef..baef7bcda5 100644
--- a/phpBB/includes/functions_posting.php
+++ b/phpBB/includes/functions_posting.php
@@ -61,7 +61,7 @@ function generate_smilies($mode, $forum_id)
'body' => 'posting_smilies.html')
);
- generate_pagination(append_sid("{$phpbb_root_path}posting.$phpEx", 'mode=smilies&f=' . $forum_id), $smiley_count, $config['smilies_per_page'], $start);
+ generate_pagination(append_sid("{$phpbb_root_path}posting.$phpEx", 'mode=smilies&f=' . $forum_id), $smiley_count, $config['smilies_per_page'], $start);
}
$display_link = false;
@@ -1175,238 +1175,6 @@ function topic_review($topic_id, $forum_id, $mode = 'topic_review', $cur_post_id
return true;
}
-/**
-* User Notification
-*/
-function user_notification($mode, $subject, $topic_title, $forum_name, $forum_id, $topic_id, $post_id, $author_name = '')
-{
- global $db, $user, $config, $phpbb_root_path, $phpEx, $auth;
-
- $topic_notification = ($mode == 'reply' || $mode == 'quote') ? true : false;
- $forum_notification = ($mode == 'post') ? true : false;
-
- if (!$topic_notification && !$forum_notification)
- {
- trigger_error('NO_MODE');
- }
-
- if (($topic_notification && !$config['allow_topic_notify']) || ($forum_notification && !$config['allow_forum_notify']))
- {
- return;
- }
-
- $topic_title = ($topic_notification) ? $topic_title : $subject;
- $topic_title = censor_text($topic_title);
-
- // Exclude guests, current user and banned users from notifications
- if (!function_exists('phpbb_get_banned_user_ids'))
- {
- include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
- }
- $sql_ignore_users = phpbb_get_banned_user_ids();
- $sql_ignore_users[ANONYMOUS] = ANONYMOUS;
- $sql_ignore_users[$user->data['user_id']] = $user->data['user_id'];
-
- $notify_rows = array();
-
- // -- get forum_userids || topic_userids
- $sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber
- FROM ' . (($topic_notification) ? TOPICS_WATCH_TABLE : FORUMS_WATCH_TABLE) . ' w, ' . USERS_TABLE . ' u
- WHERE w.' . (($topic_notification) ? 'topic_id' : 'forum_id') . ' = ' . (($topic_notification) ? $topic_id : $forum_id) . '
- AND ' . $db->sql_in_set('w.user_id', $sql_ignore_users, true) . '
- AND w.notify_status = ' . NOTIFY_YES . '
- AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
- AND u.user_id = w.user_id';
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- $notify_user_id = (int) $row['user_id'];
- $notify_rows[$notify_user_id] = array(
- 'user_id' => $notify_user_id,
- 'username' => $row['username'],
- 'user_email' => $row['user_email'],
- 'user_jabber' => $row['user_jabber'],
- 'user_lang' => $row['user_lang'],
- 'notify_type' => ($topic_notification) ? 'topic' : 'forum',
- 'template' => ($topic_notification) ? 'topic_notify' : 'newtopic_notify',
- 'method' => $row['user_notify_type'],
- 'allowed' => false
- );
-
- // Add users who have been already notified to ignore list
- $sql_ignore_users[$notify_user_id] = $notify_user_id;
- }
- $db->sql_freeresult($result);
-
- // forum notification is sent to those not already receiving topic notifications
- if ($topic_notification)
- {
- $sql = 'SELECT u.user_id, u.username, u.user_email, u.user_lang, u.user_notify_type, u.user_jabber
- FROM ' . FORUMS_WATCH_TABLE . ' fw, ' . USERS_TABLE . " u
- WHERE fw.forum_id = $forum_id
- AND " . $db->sql_in_set('fw.user_id', $sql_ignore_users, true) . '
- AND fw.notify_status = ' . NOTIFY_YES . '
- AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
- AND u.user_id = fw.user_id';
- $result = $db->sql_query($sql);
-
- while ($row = $db->sql_fetchrow($result))
- {
- $notify_user_id = (int) $row['user_id'];
- $notify_rows[$notify_user_id] = array(
- 'user_id' => $notify_user_id,
- 'username' => $row['username'],
- 'user_email' => $row['user_email'],
- 'user_jabber' => $row['user_jabber'],
- 'user_lang' => $row['user_lang'],
- 'notify_type' => 'forum',
- 'template' => 'forum_notify',
- 'method' => $row['user_notify_type'],
- 'allowed' => false
- );
- }
- $db->sql_freeresult($result);
- }
-
- if (!sizeof($notify_rows))
- {
- return;
- }
-
- // Make sure users are allowed to read the forum
- foreach ($auth->acl_get_list(array_keys($notify_rows), 'f_read', $forum_id) as $forum_id => $forum_ary)
- {
- foreach ($forum_ary as $auth_option => $user_ary)
- {
- foreach ($user_ary as $user_id)
- {
- $notify_rows[$user_id]['allowed'] = true;
- }
- }
- }
-
- // Now, we have to do a little step before really sending, we need to distinguish our users a little bit. ;)
- $msg_users = $delete_ids = $update_notification = array();
- foreach ($notify_rows as $user_id => $row)
- {
- if (!$row['allowed'] || !trim($row['user_email']))
- {
- $delete_ids[$row['notify_type']][] = $row['user_id'];
- }
- else
- {
- $msg_users[] = $row;
- $update_notification[$row['notify_type']][] = $row['user_id'];
-
- /*
- * We also update the forums watch table for this user when we are
- * sending out a topic notification to prevent sending out another
- * notification in case this user is also subscribed to the forum
- * this topic was posted in.
- * Since an UPDATE query is used, this has no effect on users only
- * subscribed to the topic (i.e. no row is created) and should not
- * be a performance issue.
- */
- if ($row['notify_type'] === 'topic')
- {
- $update_notification['forum'][] = $row['user_id'];
- }
- }
- }
- unset($notify_rows);
-
- // Now, we are able to really send out notifications
- if (sizeof($msg_users))
- {
- include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
- $messenger = new messenger();
-
- $msg_list_ary = array();
- foreach ($msg_users as $row)
- {
- $pos = (!isset($msg_list_ary[$row['template']])) ? 0 : sizeof($msg_list_ary[$row['template']]);
-
- $msg_list_ary[$row['template']][$pos]['method'] = $row['method'];
- $msg_list_ary[$row['template']][$pos]['email'] = $row['user_email'];
- $msg_list_ary[$row['template']][$pos]['jabber'] = $row['user_jabber'];
- $msg_list_ary[$row['template']][$pos]['name'] = $row['username'];
- $msg_list_ary[$row['template']][$pos]['lang'] = $row['user_lang'];
- $msg_list_ary[$row['template']][$pos]['user_id']= $row['user_id'];
- }
- unset($msg_users);
-
- foreach ($msg_list_ary as $email_template => $email_list)
- {
- foreach ($email_list as $addr)
- {
- $messenger->template($email_template, $addr['lang']);
-
- $messenger->to($addr['email'], $addr['name']);
- $messenger->im($addr['jabber'], $addr['name']);
-
- $messenger->assign_vars(array(
- 'USERNAME' => htmlspecialchars_decode($addr['name']),
- 'TOPIC_TITLE' => htmlspecialchars_decode($topic_title),
- 'FORUM_NAME' => htmlspecialchars_decode($forum_name),
- 'AUTHOR_NAME' => htmlspecialchars_decode($author_name),
-
- 'U_FORUM' => generate_board_url() . "/viewforum.$phpEx?f=$forum_id",
- 'U_TOPIC' => generate_board_url() . "/viewtopic.$phpEx?f=$forum_id&t=$topic_id",
- 'U_NEWEST_POST' => generate_board_url() . "/viewtopic.$phpEx?f=$forum_id&t=$topic_id&p=$post_id&e=$post_id",
- 'U_STOP_WATCHING_TOPIC' => generate_board_url() . "/viewtopic.$phpEx?uid={$addr['user_id']}&f=$forum_id&t=$topic_id&unwatch=topic",
- 'U_STOP_WATCHING_FORUM' => generate_board_url() . "/viewforum.$phpEx?uid={$addr['user_id']}&f=$forum_id&unwatch=forum",
- ));
-
- $messenger->send($addr['method']);
- }
- }
- unset($msg_list_ary);
-
- $messenger->save_queue();
- }
-
- // Handle the DB updates
- $db->sql_transaction('begin');
-
- if (!empty($update_notification['topic']))
- {
- $sql = 'UPDATE ' . TOPICS_WATCH_TABLE . '
- SET notify_status = ' . NOTIFY_NO . "
- WHERE topic_id = $topic_id
- AND " . $db->sql_in_set('user_id', $update_notification['topic']);
- $db->sql_query($sql);
- }
-
- if (!empty($update_notification['forum']))
- {
- $sql = 'UPDATE ' . FORUMS_WATCH_TABLE . '
- SET notify_status = ' . NOTIFY_NO . "
- WHERE forum_id = $forum_id
- AND " . $db->sql_in_set('user_id', $update_notification['forum']);
- $db->sql_query($sql);
- }
-
- // Now delete the user_ids not authorised to receive notifications on this topic/forum
- if (!empty($delete_ids['topic']))
- {
- $sql = 'DELETE FROM ' . TOPICS_WATCH_TABLE . "
- WHERE topic_id = $topic_id
- AND " . $db->sql_in_set('user_id', $delete_ids['topic']);
- $db->sql_query($sql);
- }
-
- if (!empty($delete_ids['forum']))
- {
- $sql = 'DELETE FROM ' . FORUMS_WATCH_TABLE . "
- WHERE forum_id = $forum_id
- AND " . $db->sql_in_set('user_id', $delete_ids['forum']);
- $db->sql_query($sql);
- }
-
- $db->sql_transaction('commit');
-}
-
//
// Post handling functions
//
@@ -1642,7 +1410,7 @@ function delete_post($forum_id, $topic_id, $post_id, &$data)
*/
function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $update_message = true, $update_search_index = true)
{
- global $db, $auth, $user, $config, $phpEx, $template, $phpbb_root_path;
+ global $db, $auth, $user, $config, $phpEx, $template, $phpbb_root_path, $phpbb_container;
// We do not handle erasing posts here
if ($mode == 'delete')
@@ -2454,10 +2222,76 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u
}
// Send Notifications
- if (($mode == 'reply' || $mode == 'quote' || $mode == 'post') && $post_approval)
+ $notification_data = array_merge($data, array(
+ 'topic_title' => (isset($data['topic_title'])) ? $data['topic_title'] : $subject,
+ 'post_username' => $username,
+ 'poster_id' => $poster_id,
+ 'post_text' => $data['message'],
+ 'post_time' => $current_time,
+ 'post_subject' => $subject,
+ ));
+
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ if ($post_approval)
{
- $username = ($username) ? $username : $user->data['username'];
- user_notification($mode, $subject, $data['topic_title'], $data['forum_name'], $data['forum_id'], $data['topic_id'], $data['post_id'], $username);
+ switch ($mode)
+ {
+ case 'post':
+ $phpbb_notifications->add_notifications(array(
+ 'quote',
+ 'topic',
+ ), $notification_data);
+ break;
+
+ case 'reply':
+ case 'quote':
+ $phpbb_notifications->add_notifications(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ ), $notification_data);
+ break;
+
+ case 'edit_topic':
+ case 'edit_first_post':
+ case 'edit':
+ case 'edit_last_post':
+ $phpbb_notifications->update_notifications(array(
+ 'quote',
+ 'bookmark',
+ 'topic',
+ 'post',
+ ), $notification_data);
+ break;
+ }
+ }
+ else
+ {
+ switch ($mode)
+ {
+ case 'post':
+ $phpbb_notifications->add_notifications('topic_in_queue', $notification_data);
+ break;
+
+ case 'reply':
+ case 'quote':
+ $phpbb_notifications->add_notifications('post_in_queue', $notification_data);
+ break;
+
+ case 'edit_topic':
+ case 'edit_first_post':
+ case 'edit':
+ case 'edit_last_post':
+ $phpbb_notifications->delete_notifications('topic', $data['topic_id']);
+
+ $phpbb_notifications->delete_notifications(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ ), $data['post_id']);
+ break;
+ }
}
$params = $add_anchor = '';
diff --git a/phpBB/includes/functions_privmsgs.php b/phpBB/includes/functions_privmsgs.php
index ba939d490e..14278a2529 100644
--- a/phpBB/includes/functions_privmsgs.php
+++ b/phpBB/includes/functions_privmsgs.php
@@ -876,7 +876,11 @@ function update_unread_status($unread, $msg_id, $user_id, $folder_id)
return;
}
- global $db, $user;
+ global $db, $user, $phpbb_container;
+
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->mark_notifications_read('pm', $msg_id, $user_id);
$sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . "
SET pm_unread = 0
@@ -981,7 +985,7 @@ function handle_mark_actions($user_id, $mark_action)
*/
function delete_pm($user_id, $msg_ids, $folder_id)
{
- global $db, $user, $phpbb_root_path, $phpEx;
+ global $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;
$user_id = (int) $user_id;
$folder_id = (int) $folder_id;
@@ -1093,6 +1097,10 @@ function delete_pm($user_id, $msg_ids, $folder_id)
$user->data['user_unread_privmsg'] -= $num_unread;
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->delete_notifications('pm', array_keys($delete_rows));
+
// Now we have to check which messages we can delete completely
$sql = 'SELECT msg_id
FROM ' . PRIVMSGS_TO_TABLE . '
@@ -1157,7 +1165,7 @@ function phpbb_delete_user_pms($user_id)
*/
function phpbb_delete_users_pms($user_ids)
{
- global $db, $user, $phpbb_root_path, $phpEx;
+ global $db, $user, $phpbb_root_path, $phpEx, $phpbb_container;
$user_id_sql = $db->sql_in_set('user_id', $user_ids);
$author_id_sql = $db->sql_in_set('author_id', $user_ids);
@@ -1202,6 +1210,8 @@ function phpbb_delete_users_pms($user_ids)
$db->sql_transaction('begin');
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
if (!empty($undelivered_msg))
{
// A pm is delivered, if for any recipient the message was moved
@@ -1270,6 +1280,8 @@ function phpbb_delete_users_pms($user_ids)
WHERE folder_id = ' . PRIVMSGS_NO_BOX . '
AND ' . $db->sql_in_set('msg_id', $delivered_msg);
$db->sql_query($sql);
+
+ $phpbb_notifications->delete_notifications('pm', $delivered_msg);
}
if (!empty($undelivered_msg))
@@ -1281,6 +1293,8 @@ function phpbb_delete_users_pms($user_ids)
$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg);
$db->sql_query($sql);
+
+ $phpbb_notifications->delete_notifications('pm', $undelivered_msg);
}
}
@@ -1323,6 +1337,8 @@ function phpbb_delete_users_pms($user_ids)
$sql = 'DELETE FROM ' . PRIVMSGS_TABLE . '
WHERE ' . $db->sql_in_set('msg_id', $delete_ids);
$db->sql_query($sql);
+
+ $phpbb_notifications->delete_notifications('pm', $delete_ids);
}
}
@@ -1559,7 +1575,7 @@ function get_folder_status($folder_id, $folder)
*/
function submit_pm($mode, $subject, &$data, $put_in_outbox = true)
{
- global $db, $auth, $config, $phpEx, $template, $user, $phpbb_root_path;
+ global $db, $auth, $config, $phpEx, $template, $user, $phpbb_root_path, $phpbb_container;
// We do not handle erasing pms here
if ($mode == 'delete')
@@ -1859,97 +1875,25 @@ function submit_pm($mode, $subject, &$data, $put_in_outbox = true)
$db->sql_transaction('commit');
// Send Notifications
- if ($mode != 'edit')
+ $pm_data = array_merge($data, array(
+ 'message_subject' => $subject,
+ 'recipients' => $recipients,
+ ));
+
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ if ($mode == 'edit')
{
- pm_notification($mode, $data['from_username'], $recipients, $subject, $data['message'], $data['msg_id']);
+ $phpbb_notifications->update_notifications('pm', $pm_data);
+ }
+ else
+ {
+ $phpbb_notifications->add_notifications('pm', $pm_data);
}
return $data['msg_id'];
}
-/**
-* PM Notification
-*/
-function pm_notification($mode, $author, $recipients, $subject, $message, $msg_id)
-{
- global $db, $user, $config, $phpbb_root_path, $phpEx, $auth;
-
- $subject = censor_text($subject);
-
- // Exclude guests, current user and banned users from notifications
- unset($recipients[ANONYMOUS], $recipients[$user->data['user_id']]);
-
- if (!sizeof($recipients))
- {
- return;
- }
-
- if (!function_exists('phpbb_get_banned_user_ids'))
- {
- include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
- }
- $banned_users = phpbb_get_banned_user_ids(array_keys($recipients));
- $recipients = array_diff(array_keys($recipients), $banned_users);
-
- if (!sizeof($recipients))
- {
- return;
- }
-
- $sql = 'SELECT user_id, username, user_email, user_lang, user_notify_pm, user_notify_type, user_jabber
- FROM ' . USERS_TABLE . '
- WHERE ' . $db->sql_in_set('user_id', $recipients);
- $result = $db->sql_query($sql);
-
- $msg_list_ary = array();
- while ($row = $db->sql_fetchrow($result))
- {
- if ($row['user_notify_pm'] == 1 && trim($row['user_email']))
- {
- $msg_list_ary[] = array(
- 'method' => $row['user_notify_type'],
- 'email' => $row['user_email'],
- 'jabber' => $row['user_jabber'],
- 'name' => $row['username'],
- 'lang' => $row['user_lang']
- );
- }
- }
- $db->sql_freeresult($result);
-
- if (!sizeof($msg_list_ary))
- {
- return;
- }
-
- include_once($phpbb_root_path . 'includes/functions_messenger.' . $phpEx);
- $messenger = new messenger();
-
- foreach ($msg_list_ary as $pos => $addr)
- {
- $messenger->template('privmsg_notify', $addr['lang']);
-
- $messenger->to($addr['email'], $addr['name']);
- $messenger->im($addr['jabber'], $addr['name']);
-
- $messenger->assign_vars(array(
- 'SUBJECT' => htmlspecialchars_decode($subject),
- 'AUTHOR_NAME' => htmlspecialchars_decode($author),
- 'USERNAME' => htmlspecialchars_decode($addr['name']),
-
- 'U_INBOX' => generate_board_url() . "/ucp.$phpEx?i=pm&folder=inbox",
- 'U_VIEW_MESSAGE' => generate_board_url() . "/ucp.$phpEx?i=pm&mode=view&p=$msg_id",
- ));
-
- $messenger->send($addr['method']);
- }
- unset($msg_list_ary);
-
- $messenger->save_queue();
-
- unset($messenger);
-}
-
/**
* Display Message History
*/
diff --git a/phpBB/includes/mcp/mcp_pm_reports.php b/phpBB/includes/mcp/mcp_pm_reports.php
index 86650947c7..99ff397a66 100644
--- a/phpBB/includes/mcp/mcp_pm_reports.php
+++ b/phpBB/includes/mcp/mcp_pm_reports.php
@@ -33,7 +33,7 @@ class mcp_pm_reports
function main($id, $mode)
{
global $auth, $db, $user, $template, $cache;
- global $config, $phpbb_root_path, $phpEx, $action;
+ global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;
include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
include_once($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
@@ -89,6 +89,10 @@ class mcp_pm_reports
trigger_error('NO_REPORT');
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->mark_notifications_read_by_parent('report_pm', $report_id, $user->data['user_id']);
+
$pm_id = $report['pm_id'];
$report_id = $report['report_id'];
diff --git a/phpBB/includes/mcp/mcp_queue.php b/phpBB/includes/mcp/mcp_queue.php
index 0b195aa9d8..24afa1f210 100644
--- a/phpBB/includes/mcp/mcp_queue.php
+++ b/phpBB/includes/mcp/mcp_queue.php
@@ -33,7 +33,7 @@ class mcp_queue
function main($id, $mode)
{
global $auth, $db, $user, $template, $cache;
- global $config, $phpbb_root_path, $phpEx, $action;
+ global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;
include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
@@ -78,12 +78,16 @@ class mcp_queue
$post_id = request_var('p', 0);
$topic_id = request_var('t', 0);
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
if ($topic_id)
{
$topic_info = get_topic_data(array($topic_id), 'm_approve');
if (isset($topic_info[$topic_id]['topic_first_post_id']))
{
$post_id = (int) $topic_info[$topic_id]['topic_first_post_id'];
+
+ $phpbb_notifications->mark_notifications_read('topic_in_queue', $topic_id, $user->data['user_id']);
}
else
{
@@ -91,6 +95,8 @@ class mcp_queue
}
}
+ $phpbb_notifications->mark_notifications_read('post_in_queue', $post_id, $user->data['user_id']);
+
$post_info = get_post_data(array($post_id), 'm_approve', true);
if (!sizeof($post_info))
@@ -451,7 +457,7 @@ function approve_post($post_id_list, $id, $mode)
{
global $db, $template, $user, $config;
global $phpEx, $phpbb_root_path;
- global $request;
+ global $request, $phpbb_container;
if (!check_ids($post_id_list, POSTS_TABLE, 'post_id', array('m_approve')))
{
@@ -597,54 +603,51 @@ function approve_post($post_id_list, $id, $mode)
sync('forum', 'forum_id', array_keys($forum_id_list), true, true);
unset($topic_id_list, $forum_id_list);
- $messenger = new messenger();
-
- // Notify Poster?
- if ($notify_poster)
- {
- foreach ($post_info as $post_id => $post_data)
- {
- if ($post_data['poster_id'] == ANONYMOUS)
- {
- continue;
- }
-
- $email_template = ($post_data['post_id'] == $post_data['topic_first_post_id'] && $post_data['post_id'] == $post_data['topic_last_post_id']) ? 'topic_approved' : 'post_approved';
-
- $messenger->template($email_template, $post_data['user_lang']);
-
- $messenger->to($post_data['user_email'], $post_data['username']);
- $messenger->im($post_data['user_jabber'], $post_data['username']);
-
- $messenger->assign_vars(array(
- 'USERNAME' => htmlspecialchars_decode($post_data['username']),
- 'POST_SUBJECT' => htmlspecialchars_decode(censor_text($post_data['post_subject'])),
- 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($post_data['topic_title'])),
-
- 'U_VIEW_TOPIC' => generate_board_url() . "/viewtopic.$phpEx?f={$post_data['forum_id']}&t={$post_data['topic_id']}&e=0",
- 'U_VIEW_POST' => generate_board_url() . "/viewtopic.$phpEx?f={$post_data['forum_id']}&t={$post_data['topic_id']}&p=$post_id&e=$post_id")
- );
-
- $messenger->send($post_data['user_notify_type']);
- }
- }
-
- $messenger->save_queue();
-
// Send out normal user notifications
$email_sig = str_replace('
', "\n", "-- \n" . $config['board_email_sig']);
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ // Handle notifications
foreach ($post_info as $post_id => $post_data)
{
if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id'])
{
- // Forum Notifications
- user_notification('post', $post_data['topic_title'], $post_data['topic_title'], $post_data['forum_name'], $post_data['forum_id'], $post_data['topic_id'], $post_id);
+ $phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']);
+
+ $phpbb_notifications->add_notifications(array(
+ 'quote',
+ 'topic',
+ ), $post_data);
+
+ $phpbb_notifications->mark_notifications_read('quote', $post_data['post_id'], $user->data['user_id']);
+ $phpbb_notifications->mark_notifications_read('topic', $post_data['topic_id'], $user->data['user_id']);
+
+ if ($notify_poster)
+ {
+ $phpbb_notifications->add_notifications('approve_topic', $post_data);
+ }
}
else
{
- // Topic Notifications
- user_notification('reply', $post_data['post_subject'], $post_data['topic_title'], $post_data['forum_name'], $post_data['forum_id'], $post_data['topic_id'], $post_id);
+ $phpbb_notifications->delete_notifications('post_in_queue', $post_id);
+
+ $phpbb_notifications->add_notifications(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ ), $post_data);
+
+ $phpbb_notifications->mark_notifications_read(array(
+ 'quote',
+ 'bookmark',
+ 'post',
+ ),$post_data['post_id'], $user->data['user_id']);
+
+ if ($notify_poster)
+ {
+ $phpbb_notifications->add_notifications('approve_post', $post_data);
+ }
}
}
@@ -734,7 +737,7 @@ function disapprove_post($post_id_list, $id, $mode)
{
global $db, $template, $user, $config;
global $phpEx, $phpbb_root_path;
- global $request;
+ global $request, $phpbb_container;
if (!check_ids($post_id_list, POSTS_TABLE, 'post_id', array('m_approve')))
{
@@ -867,20 +870,29 @@ function disapprove_post($post_id_list, $id, $mode)
}
}
- $messenger = new messenger();
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ foreach ($post_info as $post_id => $post_data)
+ {
+ if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id'])
+ {
+ $phpbb_notifications->delete_notifications('topic_in_queue', $post_data['topic_id']);
+ }
+ else
+ {
+ $phpbb_notifications->delete_notifications('post_in_queue', $post_id);
+ }
+ }
// Notify Poster?
if ($notify_poster)
{
$lang_reasons = array();
+ // Handle notifications
foreach ($post_info as $post_id => $post_data)
{
- if ($post_data['poster_id'] == ANONYMOUS)
- {
- continue;
- }
-
+ $post_data['disapprove_reason'] = '';
if (isset($disapprove_reason_lang))
{
// Okay we need to get the reason from the posters language
@@ -906,33 +918,30 @@ function disapprove_post($post_id_list, $id, $mode)
}
}
- $email_disapprove_reason = $lang_reasons[$post_data['user_lang']];
- $email_disapprove_reason .= ($reason) ? "\n\n" . $reason : '';
+ $post_data['disapprove_reason'] = $lang_reasons[$post_data['user_lang']];
+ $post_data['disapprove_reason'] .= ($reason) ? "\n\n" . $reason : '';
}
- $email_template = ($post_data['post_id'] == $post_data['topic_first_post_id'] && $post_data['post_id'] == $post_data['topic_last_post_id']) ? 'topic_disapproved' : 'post_disapproved';
-
- $messenger->template($email_template, $post_data['user_lang']);
-
- $messenger->to($post_data['user_email'], $post_data['username']);
- $messenger->im($post_data['user_jabber'], $post_data['username']);
-
- $messenger->assign_vars(array(
- 'USERNAME' => htmlspecialchars_decode($post_data['username']),
- 'REASON' => htmlspecialchars_decode($email_disapprove_reason),
- 'POST_SUBJECT' => htmlspecialchars_decode(censor_text($post_data['post_subject'])),
- 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($post_data['topic_title'])))
- );
-
- $messenger->send($post_data['user_notify_type']);
+ if ($post_id == $post_data['topic_first_post_id'] && $post_id == $post_data['topic_last_post_id'])
+ {
+ if ($notify_poster)
+ {
+ $phpbb_notifications->add_notifications('disapprove_topic', $post_data);
+ }
+ }
+ else
+ {
+ if ($notify_poster)
+ {
+ $phpbb_notifications->add_notifications('disapprove_post', $post_data);
+ }
+ }
}
unset($lang_reasons);
}
unset($post_info, $disapprove_reason, $email_disapprove_reason, $disapprove_reason_lang);
- $messenger->save_queue();
-
if ($num_disapproved_topics)
{
$success_msg = ($num_disapproved_topics == 1) ? 'TOPIC_DISAPPROVED_SUCCESS' : 'TOPICS_DISAPPROVED_SUCCESS';
diff --git a/phpBB/includes/mcp/mcp_reports.php b/phpBB/includes/mcp/mcp_reports.php
index 8da303f6e3..0a600d7057 100644
--- a/phpBB/includes/mcp/mcp_reports.php
+++ b/phpBB/includes/mcp/mcp_reports.php
@@ -33,7 +33,7 @@ class mcp_reports
function main($id, $mode)
{
global $auth, $db, $user, $template, $cache;
- global $config, $phpbb_root_path, $phpEx, $action;
+ global $config, $phpbb_root_path, $phpEx, $action, $phpbb_container;
include_once($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
@@ -87,6 +87,10 @@ class mcp_reports
trigger_error('NO_REPORT');
}
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $phpbb_notifications->mark_notifications_read('report_post', $post_id, $user->data['user_id']);
+
if (!$report_id && $report['report_closed'])
{
trigger_error('REPORT_CLOSED');
@@ -436,7 +440,7 @@ class mcp_reports
function close_report($report_id_list, $mode, $action, $pm = false)
{
global $db, $template, $user, $config, $auth;
- global $phpEx, $phpbb_root_path;
+ global $phpEx, $phpbb_root_path, $phpbb_container;
$pm_where = ($pm) ? ' AND r.post_id = 0 ' : ' AND r.pm_id = 0 ';
$id_column = ($pm) ? 'pm_id' : 'post_id';
@@ -622,11 +626,11 @@ function close_report($report_id_list, $mode, $action, $pm = false)
}
}
- $messenger = new messenger();
-
// Notify reporters
if (sizeof($notify_reporters))
{
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
foreach ($notify_reporters as $report_id => $reporter)
{
if ($reporter['user_id'] == ANONYMOUS)
@@ -636,30 +640,25 @@ function close_report($report_id_list, $mode, $action, $pm = false)
$post_id = $reporter[$id_column];
- $messenger->template((($pm) ? 'pm_report_' : 'report_') . $action . 'd', $reporter['user_lang']);
-
- $messenger->to($reporter['user_email'], $reporter['username']);
- $messenger->im($reporter['user_jabber'], $reporter['username']);
-
if ($pm)
{
- $messenger->assign_vars(array(
- 'USERNAME' => htmlspecialchars_decode($reporter['username']),
- 'CLOSER_NAME' => htmlspecialchars_decode($user->data['username']),
- 'PM_SUBJECT' => htmlspecialchars_decode(censor_text($post_info[$post_id]['message_subject'])),
- ));
+ $phpbb_notifications->add_notifications('report_pm_closed', array_merge($post_info[$post_id], array(
+ 'reporter' => $reporter['user_id'],
+ 'closer_id' => $user->data['user_id'],
+ 'from_user_id' => $post_info[$post_id]['author_id'],
+ )));
+
+ $phpbb_notifications->delete_notifications('report_pm', $post_id);
}
else
{
- $messenger->assign_vars(array(
- 'USERNAME' => htmlspecialchars_decode($reporter['username']),
- 'CLOSER_NAME' => htmlspecialchars_decode($user->data['username']),
- 'POST_SUBJECT' => htmlspecialchars_decode(censor_text($post_info[$post_id]['post_subject'])),
- 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($post_info[$post_id]['topic_title'])))
- );
- }
+ $phpbb_notifications->add_notifications('report_post_closed', array_merge($post_info[$post_id], array(
+ 'reporter' => $reporter['user_id'],
+ 'closer_id' => $user->data['user_id'],
+ )));
- $messenger->send($reporter['user_notify_type']);
+ $phpbb_notifications->delete_notifications('report_post', $post_id);
+ }
}
}
@@ -674,8 +673,6 @@ function close_report($report_id_list, $mode, $action, $pm = false)
unset($notify_reporters, $post_info, $reports);
- $messenger->save_queue();
-
$success_msg = (sizeof($report_id_list) == 1) ? "{$pm_prefix}REPORT_" . strtoupper($action) . 'D_SUCCESS' : "{$pm_prefix}REPORTS_" . strtoupper($action) . 'D_SUCCESS';
}
else
diff --git a/phpBB/includes/notification/manager.php b/phpBB/includes/notification/manager.php
new file mode 100644
index 0000000000..ff83d4bb37
--- /dev/null
+++ b/phpBB/includes/notification/manager.php
@@ -0,0 +1,853 @@
+notification_types = $notification_types;
+ $this->notification_methods = $notification_methods;
+ $this->phpbb_container = $phpbb_container;
+
+ $this->user_loader = $user_loader;
+ $this->db = $db;
+ $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
+ *
+ * @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())
+ {
+ // 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 = n.item_type
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ $unread_count = (int) $this->db->sql_fetchfield('unread_count', $result);
+ $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 = n.item_type
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ $total_count = (int) $this->db->sql_fetchfield('total_count', $result);
+ $this->db->sql_freeresult($result);
+ }
+
+ if (!$options['count_total'] || $total_count)
+ {
+ $rowset = array();
+
+ // Get the main notifications
+ $sql = 'SELECT n.*
+ 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 = n.item_type
+ 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.*
+ 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 = n.item_type
+ 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['item_type'], $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['item_type']]))
+ {
+ $load_special[$row['item_type']] = array();
+ }
+ $load_special[$row['item_type']] = array_merge($load_special[$row['item_type']], $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);
+ }
+ }
+
+ return array(
+ 'notifications' => $notifications,
+ 'unread_count' => $unread_count,
+ 'total_count' => $total_count,
+ );
+ }
+
+ /**
+ * Mark notifications read
+ *
+ * @param bool|string|array $item_type 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($item_type, $item_id, $user_id, $time = false)
+ {
+ $time = ($time !== false) ? $time : time();
+
+ $sql = 'UPDATE ' . $this->notifications_table . "
+ SET notification_read = 1
+ WHERE notification_time <= " . (int) $time .
+ (($item_type !== false) ? ' AND ' . (is_array($item_type) ? $this->db->sql_in_set('item_type', $item_type) : " item_type = '" . $this->db->sql_escape($item_type) . "'") : '') .
+ (($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);
+ }
+
+ /**
+ * Mark notifications read from a parent identifier
+ *
+ * @param string|array $item_type 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)
+ */
+ public function mark_notifications_read_by_parent($item_type, $item_parent_id, $user_id, $time = false)
+ {
+ if (is_array($item_type))
+ {
+ foreach ($item_type as $type)
+ {
+ $this->mark_notifications_read_by_parent($type, $item_parent_id, $user_id, $time);
+ }
+
+ return;
+ }
+
+ $time = ($time !== false) ? $time : time();
+
+ $sql = 'UPDATE ' . $this->notifications_table . "
+ SET notification_read = 1
+ WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND notification_time <= " . (int) $time .
+ (($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);
+ }
+
+ /**
+ * Mark notifications read
+ *
+ * @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)
+ */
+ public function mark_notifications_read_by_id($notification_id, $time = false)
+ {
+ $time = ($time !== false) ? $time : time();
+
+ $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);
+ }
+
+ /**
+ * Add a notification
+ *
+ * @param string|array $item_type Type identifier or array of item types (only acceptable if the $data is identical for the specified types)
+ * Note: If you send an array of types, any user who could receive multiple notifications from this single item will only receive
+ * a single notification. If they MUST receive multiple notifications, call this function multiple times instead of sending an array
+ * @param array $data Data specific for this type that will be inserted
+ * @param array $options Optional options to control what notifications are loaded
+ * ignore_users array of data to specify which users should not receive certain types of notifications
+ * @return array Information about what users were notified and how they were notified
+ */
+ public function add_notifications($item_type, $data, array $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ if (is_array($item_type))
+ {
+ $notified_users = array();
+ $temp_options = $options;
+
+ foreach ($item_type as $type)
+ {
+ $temp_options['ignore_users'] = $options['ignore_users'] + $notified_users;
+ $notified_users += $this->add_notifications($type, $data, $temp_options);
+ }
+
+ return $notified_users;
+ }
+
+ $item_id = $this->get_item_type_class($item_type)->get_item_id($data);
+
+ // find out which users want to receive this type of notification
+ $notify_users = $this->get_item_type_class($item_type)->find_users_for_notification($data, $options);
+
+ $this->add_notifications_for_users($item_type, $data, $notify_users);
+
+ return $notify_users;
+ }
+
+ /**
+ * Add a notification for specific users
+ *
+ * @param string|array $item_type 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 inserted
+ * @param array $notify_users User list to notify
+ */
+ public function add_notifications_for_users($item_type, $data, $notify_users)
+ {
+ if (is_array($item_type))
+ {
+ foreach ($item_type as $type)
+ {
+ $this->add_notifications_for_users($type, $data, $notify_users);
+ }
+
+ return;
+ }
+
+ $sql = 'SELECT notification_type
+ FROM ' . $this->notification_types_table . "
+ WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'";
+ $result = $this->db->sql_query($sql);
+
+ if ($this->db->sql_fetchrow($result) === false)
+ {
+ // Does not exist in the database, must add the item type
+ $sql = 'INSERT INTO ' . $this->notification_types_table . ' ' . $this->db->sql_build_array('INSERT', array(
+ 'notification_type' => $item_type,
+ 'notification_type_enabled' => 1,
+ ));
+ $this->db->sql_query($sql);
+ }
+
+ $this->db->sql_freeresult($result);
+
+ $item_id = $this->get_item_type_class($item_type)->get_item_id($data);
+
+ $user_ids = array();
+ $notification_objects = $notification_methods = array();
+ $new_rows = 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.item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND n.item_id = " . (int) $item_id . '
+ AND nt.notification_type = n.item_type
+ AND nt.notification_type_enabled = 1';
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ unset($notify_users[$row['user_id']]);
+ }
+ $this->db->sql_freeresult($result);
+
+ if (!sizeof($notify_users))
+ {
+ return;
+ }
+
+ // Allow notifications to perform actions before creating the insert array (such as run a query to cache some data needed for all notifications)
+ $notification = $this->get_item_type_class($item_type);
+ $pre_create_data = $notification->pre_create_insert_array($data, $notify_users);
+ unset($notification);
+
+ // 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)
+ {
+ $notification = $this->get_item_type_class($item_type);
+
+ $notification->user_id = (int) $user;
+
+ // Store the creation array in our new rows that will be inserted later
+ $new_rows[] = $notification->create_insert_array($data, $pre_create_data);
+
+ // Users are needed to send notifications
+ $user_ids = array_merge($user_ids, $notification->users_to_query());
+
+ 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]))
+ {
+ $notification_methods[$method] = $this->get_method_class($method);
+ }
+
+ $notification_methods[$method]->add_to_queue($notification);
+ }
+ }
+ }
+
+ // insert into the db
+ $this->db->sql_multi_insert($this->notifications_table, $new_rows);
+
+ // We need to load all of the users to send notifications
+ $this->user_loader->load_users($user_ids);
+
+ // run the queue for each method to send notifications
+ foreach ($notification_methods as $method)
+ {
+ $method->notify();
+ }
+ }
+
+ /**
+ * Update a notification
+ *
+ * @param string|array $item_type 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
+ */
+ public function update_notifications($item_type, $data)
+ {
+ if (is_array($item_type))
+ {
+ foreach ($item_type as $type)
+ {
+ $this->update_notifications($type, $data);
+ }
+
+ return;
+ }
+
+ $notification = $this->get_item_type_class($item_type);
+
+ // 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;
+ }
+ }
+
+ $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 item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND item_id = " . (int) $item_id;
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Delete a notification
+ *
+ * @param string|array $item_type 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 array $data Data specific for this type that will be updated
+ */
+ public function delete_notifications($item_type, $item_id)
+ {
+ if (is_array($item_type))
+ {
+ foreach ($item_type as $type)
+ {
+ $this->delete_notifications($type, $item_id);
+ }
+
+ return;
+ }
+
+ $sql = 'DELETE FROM ' . $this->notifications_table . "
+ WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND " . (is_array($item_id) ? $this->db->sql_in_set('item_id', $item_id) : 'item_id = ' . (int) $item_id);
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Get all of the subscription types
+ *
+ * @return array Array of item types
+ */
+ public function get_subscription_types()
+ {
+ $subscription_types = array();
+
+ foreach ($this->notification_types as $type_name => $data)
+ {
+ $type = $this->get_item_type_class($type_name);
+
+ if ($type instanceof phpbb_notification_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;
+ }
+ }
+
+ // 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;
+ }
+
+ return $subscription_types;
+ }
+
+ /**
+ * Get all of the subscription methods
+ *
+ * @return array Array of methods
+ */
+ public function get_subscription_methods()
+ {
+ $subscription_methods = array();
+
+ foreach ($this->notification_methods as $method_name => $data)
+ {
+ $method = $this->get_method_class($method_name);
+
+ if ($method instanceof phpbb_notification_method_interface && $method->is_available())
+ {
+ $subscription_methods[$method_name] = array(
+ 'id' => $method->get_type(),
+ 'lang' => 'NOTIFICATION_METHOD_' . strtoupper($method->get_type()),
+ );
+ }
+ }
+
+ return $subscription_methods;
+ }
+
+ /**
+ * Get global subscriptions (item_id = 0)
+ *
+ * @param bool|int $user_id The user_id to add the subscription for (bool false for current user)
+ *
+ * @return array Subscriptions
+ */
+ public function get_global_subscriptions($user_id = false)
+ {
+ $user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id;
+
+ $subscriptions = array();
+
+ foreach ($this->get_subscription_types() as $group_name => $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)
+ {
+ // No rows at all, default to ''
+ $subscriptions[$id] = array('');
+ }
+ else
+ {
+ do
+ {
+ if (!$row['notify'])
+ {
+ continue;
+ }
+
+ if (!isset($subscriptions[$id]))
+ {
+ $subscriptions[$id] = array();
+ }
+
+ $subscriptions[$id][] = $row['method'];
+ }
+ while ($row = $this->db->sql_fetchrow($result));
+ }
+
+ $this->db->sql_freeresult($result);
+ }
+ }
+
+ return $subscriptions;
+ }
+
+ /**
+ * Add a subscription
+ *
+ * @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 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)
+ {
+ if ($method !== '')
+ {
+ $this->add_subscription($item_type, $item_type, '', $user_id);
+ }
+
+ $user_id = ($user_id === false) ? $this->user->data['user_id'] : $user_id;
+
+ $sql = 'SELECT notify
+ 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 = '" . $this->db->sql_escape($method) . "'";
+ $this->db->sql_query($sql);
+ $current = $this->db->sql_fetchfield('notify');
+ $this->db->sql_freeresult();
+
+ if ($current === false)
+ {
+ $sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' .
+ $this->db->sql_build_array('INSERT', array(
+ 'item_type' => $item_type,
+ 'item_id' => (int) $item_id,
+ 'user_id' => (int) $user_id,
+ 'method' => $method,
+ 'notify' => 1,
+ ));
+ $this->db->sql_query($sql);
+ }
+ else if (!$current)
+ {
+ $sql = 'UPDATE ' . $this->user_notifications_table . "
+ SET notify = 1
+ WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND item_id = " . (int) $item_id . '
+ AND user_id = ' .(int) $user_id . "
+ AND method = '" . $this->db->sql_escape($method) . "'";
+ $this->db->sql_query($sql);
+ }
+ }
+
+ /**
+ * Delete a subscription
+ *
+ * @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 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)
+ {
+ $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 === '')
+ {
+ $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)
+ {
+ return;
+ }
+ }
+
+ $sql = 'UPDATE ' . $this->user_notifications_table . "
+ SET notify = 0
+ WHERE item_type = '" . $this->db->sql_escape($item_type) . "'
+ AND item_id = " . (int) $item_id . '
+ AND user_id = ' .(int) $user_id . "
+ AND method = '" . $this->db->sql_escape($method) . "'";
+ $this->db->sql_query($sql);
+
+ if (!$this->db->sql_affectedrows())
+ {
+ $sql = 'INSERT INTO ' . $this->user_notifications_table . ' ' .
+ $this->db->sql_build_array('INSERT', array(
+ 'item_type' => $item_type,
+ 'item_id' => (int) $item_id,
+ 'user_id' => (int) $user_id,
+ 'method' => $method,
+ 'notify' => 0,
+ ));
+ $this->db->sql_query($sql);
+ }
+ }
+
+ /**
+ * Disable all notifications of a certain type
+ *
+ * This should be called when an extension which has notification types
+ * is disabled so that all those notifications are hidden and do not
+ * cause errors
+ *
+ * @param string $item_type Type identifier of the subscription
+ */
+ public function disable_notifications($item_type)
+ {
+ $sql = 'UPDATE ' . $this->notification_types_table . "
+ SET notification_type_enabled = 0
+ WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * 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 $item_type Type identifier of the subscription
+ */
+ public function purge_notifications($item_type)
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . "
+ WHERE item_type = '" . $this->db->sql_escape($item_type) . "'";
+ $this->db->sql_query($sql);
+
+ $sql = 'DELETE FROM ' . $this->notification_types_table . "
+ WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Enable all notifications of a certain type
+ *
+ * This should be called when an extension which has notification types
+ * that was disabled is re-enabled so that all those notifications that
+ * were hidden are shown again
+ *
+ * @param string $item_type Type identifier of the subscription
+ */
+ public function enable_notifications($item_type)
+ {
+ $sql = 'UPDATE ' . $this->notification_types_table . "
+ SET notification_type_enabled = 1
+ WHERE notification_type = '" . $this->db->sql_escape($item_type) . "'";
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Delete all notifications older than a certain time
+ *
+ * @param int $timestamp Unix timestamp to delete all notifications that were created before
+ */
+ public function prune_notifications($timestamp)
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . '
+ WHERE notification_time < ' . (int) $timestamp;
+ $this->db->sql_query($sql);
+ }
+
+ /**
+ * Helper to get the notifications item type class and set it up
+ */
+ public function get_item_type_class($item_type, $data = array())
+ {
+ $item_type = (strpos($item_type, 'notification.type.') === 0) ? $item_type : 'notification.type.' . $item_type;
+
+ $item = $this->load_object($item_type);
+
+ $item->set_initial_data($data);
+
+ return $item;
+ }
+
+ /**
+ * Helper to get the notifications method class and set it up
+ */
+ 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)
+ */
+ protected function load_object($object_name)
+ {
+ $object = $this->phpbb_container->get($object_name);
+
+ if (method_exists($object, 'set_notification_manager'))
+ {
+ $object->set_notification_manager($this);
+ }
+
+ return $object;
+ }
+}
diff --git a/phpBB/includes/notification/method/base.php b/phpBB/includes/notification/method/base.php
new file mode 100644
index 0000000000..22418c9be8
--- /dev/null
+++ b/phpBB/includes/notification/method/base.php
@@ -0,0 +1,116 @@
+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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Add a notification to the queue
+ *
+ * @param phpbb_notification_type_interface $notification
+ */
+ public function add_to_queue(phpbb_notification_type_interface $notification)
+ {
+ $this->queue[] = $notification;
+ }
+
+ /**
+ * Empty the queue
+ */
+ protected function empty_queue()
+ {
+ $this->queue = array();
+ }
+}
diff --git a/phpBB/includes/notification/method/email.php b/phpBB/includes/notification/method/email.php
new file mode 100644
index 0000000000..429dfda2ba
--- /dev/null
+++ b/phpBB/includes/notification/method/email.php
@@ -0,0 +1,129 @@
+queue))
+ {
+ return;
+ }
+
+ // Load all users we want to notify (we need their email address)
+ $user_ids = $users = array();
+ foreach ($this->queue as $notification)
+ {
+ $user_ids[] = $notification->user_id;
+ }
+
+ // We do not send emails to banned users
+ if (!function_exists('phpbb_get_banned_user_ids'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_user.' . $this->php_ext);
+ }
+ $banned_users = phpbb_get_banned_user_ids($user_ids);
+
+ // Load all the users we need
+ $this->user_loader->load_users($user_ids);
+
+ // Load the messenger
+ if (!class_exists('messenger'))
+ {
+ 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
+ foreach ($this->queue as $notification)
+ {
+ if ($notification->get_email_template() === false)
+ {
+ continue;
+ }
+
+ $user = $this->user_loader->get_user($notification->user_id);
+
+ if ($user['user_type'] == USER_IGNORE || in_array($notification->user_id, $banned_users))
+ {
+ continue;
+ }
+
+ $messenger->template($this->email_template_base_dir . $notification->get_email_template(), $user['user_lang']);
+
+ $messenger->to($user['user_email'], $user['username']);
+
+ $messenger->assign_vars(array_merge(array(
+ 'USERNAME' => $user['username'],
+
+ 'U_NOTIFICATION_SETTINGS' => generate_board_url() . '/ucp.' . $this->php_ext . '?i=ucp_notifications',
+ ), $notification->get_email_template_variables()));
+
+ $messenger->send($this->notify_method);
+ }
+
+ // Save the queue in the messenger class (has to be called or these emails could be lost?)
+ $messenger->save_queue();
+
+ // We're done, empty the queue
+ $this->empty_queue();
+ }
+}
diff --git a/phpBB/includes/notification/method/interface.php b/phpBB/includes/notification/method/interface.php
new file mode 100644
index 0000000000..ef875942cc
--- /dev/null
+++ b/phpBB/includes/notification/method/interface.php
@@ -0,0 +1,48 @@
+global_available() && $this->user->data['jabber']);
+ }
+
+ /**
+ * Is this method available at all?
+ * This is checked before notifications are sent
+ */
+ public function global_available()
+ {
+ return ($this->config['jab_enable'] && @extension_loaded('xml'));
+ }
+
+ public function notify()
+ {
+ if (!$this->global_available())
+ {
+ return;
+ }
+
+ return parent::notify();
+ }
+}
diff --git a/phpBB/includes/notification/type/approve_post.php b/phpBB/includes/notification/type/approve_post.php
new file mode 100644
index 0000000000..1a30781c35
--- /dev/null
+++ b/phpBB/includes/notification/type/approve_post.php
@@ -0,0 +1,140 @@
+ 'moderation_queue',
+ 'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return !$this->auth->acl_get('m_approve');
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $users = array();
+ $users[$post['poster_id']] = array('');
+
+ $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'],
+ )));
+ }
+
+ /**
+ * Pre create insert array function
+ * This allows you to perform certain actions, like run a query
+ * and load data, before create_insert_array() is run. The data
+ * returned from this function will be sent to create_insert_array().
+ *
+ * @param array $post Post data from submit_post
+ * @param array $notify_users Notify users list
+ * Formated from find_users_for_notification()
+ * @return array Whatever you want to send to create_insert_array().
+ */
+ public function pre_create_insert_array($post, $notify_users)
+ {
+ // In the parent class, this is used to check if the post is already
+ // read by a user and marks the notification read if it was marked read.
+ // Returning an empty array in effect, forces it to be marked as unread
+ // (and also saves a query)
+ return array();
+ }
+
+ /**
+ * 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
+ */
+ 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);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'post_approved';
+ }
+}
diff --git a/phpBB/includes/notification/type/approve_topic.php b/phpBB/includes/notification/type/approve_topic.php
new file mode 100644
index 0000000000..e728e9ac30
--- /dev/null
+++ b/phpBB/includes/notification/type/approve_topic.php
@@ -0,0 +1,138 @@
+ 'moderation_queue',
+ 'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return !$this->auth->acl_get('m_approve');
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $users = array();
+ $users[$post['poster_id']] = array('');
+
+ $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'],
+ )));
+ }
+
+ /**
+ * Pre create insert array function
+ * This allows you to perform certain actions, like run a query
+ * and load data, before create_insert_array() is run. The data
+ * returned from this function will be sent to create_insert_array().
+ *
+ * @param array $post Post data from submit_post
+ * @param array $notify_users Notify users list
+ * Formated from find_users_for_notification()
+ * @return array Whatever you want to send to create_insert_array().
+ */
+ public function pre_create_insert_array($post, $notify_users)
+ {
+ // In the parent class, this is used to check if the post is already
+ // read by a user and marks the notification read if it was marked read.
+ // Returning an empty array in effect, forces it to be marked as unread
+ // (and also saves a query)
+ return array();
+ }
+
+ /**
+ * 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
+ */
+ 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();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'topic_approved';
+ }
+}
diff --git a/phpBB/includes/notification/type/base.php b/phpBB/includes/notification/type/base.php
new file mode 100644
index 0000000000..600ef7c965
--- /dev/null
+++ b/phpBB/includes/notification/type/base.php
@@ -0,0 +1,479 @@
+ forum_id, for post => topic_id, etc)
+ * user_id
+ * notification_read
+ * notification_time
+ * notification_data (special serialized field that each notification type can use to store stuff)
+ *
+ * @var array $data Notification row from the database
+ * This must be private, all interaction should use __get(), __set(), get_data(), set_data()
+ */
+ private $data = array();
+
+ /**
+ * Notification Type Base Constructor
+ *
+ * @param phpbb_user_loader $user_loader
+ * @param phpbb_db_driver $db
+ * @param phpbb_cache_driver_interface $cache
+ * @param phpbb_user $user
+ * @param phpbb_auth $auth
+ * @param phpbb_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 $db, phpbb_cache_driver_interface $cache, $user, phpbb_auth $auth, phpbb_config $config, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table)
+ {
+ $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;
+
+ $this->notification_types_table = $notification_types_table;
+ $this->notifications_table = $notifications_table;
+ $this->user_notifications_table = $user_notifications_table;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Set initial data from the database
+ *
+ * @param array $data Row directly from the database
+ */
+ public function set_initial_data($data = array())
+ {
+ // The row from the database (unless this is a new notification we're going to add)
+ $this->data = $data;
+ $this->data['notification_data'] = (isset($this->data['notification_data'])) ? unserialize($this->data['notification_data']) : array();
+ }
+
+ /**
+ * Magic method to get data from this notification
+ *
+ * @param mixed $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return (!isset($this->data[$name])) ? null : $this->data[$name];
+ }
+
+
+ /**
+ * Magic method to set data on this notification
+ *
+ * @param mixed $name
+ * @return null
+ */
+ public function __set($name, $value)
+ {
+ $this->data[$name] = $value;
+ }
+
+
+ /**
+ * Magic method to get a string of this notification
+ *
+ * Primarily for testing
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __toString()
+ {
+ return (!empty($this->data)) ? var_export($this->data, true) : $this->get_type();
+ }
+
+ /**
+ * Get special data (only important for the classes that extend this)
+ *
+ * @param string $name Name of the variable to get
+ * @return mixed
+ */
+ protected function get_data($name)
+ {
+ return ($name === false) ? $this->data['notification_data'] : ((isset($this->data['notification_data'][$name])) ? $this->data['notification_data'][$name] : null);
+ }
+
+ /**
+ * Set special data (only important for the classes that extend this)
+ *
+ * @param string $name Name of the variable to set
+ * @param mixed $value Value to set to the variable
+ * @return mixed
+ */
+ protected function set_data($name, $value)
+ {
+ $this->data['notification_data'][$name] = $value;
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($type_data, $pre_create_data = array())
+ {
+ // Defaults
+ $this->data = array_merge(array(
+ 'item_id' => static::get_item_id($type_data),
+ 'item_type' => $this->get_type(),
+ 'item_parent_id' => static::get_item_parent_id($type_data),
+
+ 'notification_time' => time(),
+ 'notification_read' => false,
+
+ 'notification_data' => array(),
+ ), $this->data);
+
+ $data = $this->data;
+
+ $data['notification_data'] = serialize($data['notification_data']);
+
+ return $data;
+ }
+
+ /**
+ * Function for preparing the data for update in an SQL query
+ * (The service handles insertion)
+ *
+ * @param array $type_data Data unique to this notification type
+ * @return array Array of data ready to be updated in the database
+ */
+ public function create_update_array($type_data)
+ {
+ $data = $this->create_insert_array($type_data);
+
+ // Unset data unique to each row
+ unset(
+ $data['notification_time'], // Also unsetting time, since it always tries to change the time to current (if you actually need to change the time, over-ride this function)
+ $data['notification_id'],
+ $data['notification_read'],
+ $data['user_id']
+ );
+
+ return $data;
+ }
+
+ /**
+ * Mark this item read
+ *
+ * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False)
+ * @return string|null If $return is False, nothing will be returned, else the sql code to update this item
+ */
+ public function mark_read($return = false)
+ {
+ return $this->mark(false, $return);
+ }
+
+ /**
+ * Mark this item unread
+ *
+ * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False)
+ * @return string|null If $return is False, nothing will be returned, else the sql code to update this item
+ */
+ public function mark_unread($return = false)
+ {
+ return $this->mark(true, $return);
+ }
+
+ /**
+ * Prepare to output the notification to the template
+ *
+ * @return array Template variables
+ */
+ public function prepare_for_display()
+ {
+ if ($this->get_url())
+ {
+ $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id);
+ }
+ else
+ {
+ $redirect = (($this->user->page['page_dir']) ? $this->user->page['page_dir'] . '/' : '') . $this->user->page['page_name'] . (($this->user->page['query_string']) ? '?' . $this->user->page['query_string'] : '');
+
+ $u_mark_read = append_sid($this->phpbb_root_path . 'index.' . $this->php_ext, 'mark_notification=' . $this->notification_id . '&redirect=' . urlencode($redirect));
+ }
+
+ return array(
+ 'NOTIFICATION_ID' => $this->notification_id,
+
+ 'AVATAR' => $this->get_avatar(),
+
+ 'FORMATTED_TITLE' => $this->get_title(),
+
+ '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 : '',
+ );
+ }
+
+ /**
+ * -------------- Fall back functions -------------------
+ */
+
+ /**
+ * 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
+ */
+ public function get_unsubscribe_url($method = false)
+ {
+ return false;
+ }
+
+ /**
+ * Get the user's avatar (fall back)
+ *
+ * @return string
+ */
+ public function get_avatar()
+ {
+ return '';
+ }
+
+ /**
+ * Get the special items to load (fall back)
+ *
+ * @return array
+ */
+ public function get_load_special()
+ {
+ return array();
+ }
+
+ /**
+ * Load the special items (fall back)
+ */
+ public function load_special($data, $notifications)
+ {
+ return;
+ }
+
+ /**
+ * Is available (fall back)
+ *
+ * @return bool
+ */
+ public function is_available()
+ {
+ return true;
+ }
+
+ /**
+ * Pre create insert array function (fall back)
+ *
+ * @return array
+ */
+ public function pre_create_insert_array($type_data, $notify_users)
+ {
+ return array();
+ }
+
+ /**
+ * -------------- Helper functions -------------------
+ */
+
+ /**
+ * 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
+ */
+ protected function check_user_notification_options($user_ids = false, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ 'item_type' => $this->get_type(),
+ 'item_id' => 0, // Global by default
+ ), $options);
+
+ if ($user_ids === false)
+ {
+ $user_ids = array();
+
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE user_id <> ' . ANONYMOUS . '
+ AND user_type <> ' . USER_IGNORE;
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $user_ids[] = $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+ }
+
+ if (empty($user_ids))
+ {
+ return array();
+ }
+
+ $rowset = $resulting_user_ids = array();
+
+ $sql = 'SELECT user_id, method, notify
+ FROM ' . $this->user_notifications_table . '
+ WHERE ' . $this->db->sql_in_set('user_id', $user_ids) . "
+ AND item_type = '" . $this->db->sql_escape($options['item_type']) . "'
+ AND item_id = " . (int) $options['item_id'];
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $resulting_user_ids[] = $row['user_id'];
+
+ if (!$row['notify'] || (isset($options['ignore_users'][$row['user_id']]) && in_array($row['method'], $options['ignore_users'][$row['user_id']])))
+ {
+ continue;
+ }
+
+ if (!isset($rowset[$row['user_id']]))
+ {
+ $rowset[$row['user_id']] = array();
+ }
+
+ $rowset[$row['user_id']][] = $row['method'];
+ }
+
+ $this->db->sql_freeresult($result);
+
+ foreach ($user_ids as $user_id)
+ {
+ 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('');
+ }
+ }
+
+ return $rowset;
+ }
+
+ /**
+ * Mark this item read/unread helper
+ *
+ * @param bool $unread Unread (True/False) (Default: False)
+ * @param bool $return True to return a string containing the SQL code to update this item, False to execute it (Default: False)
+ * @return string|null If $return is False, nothing will be returned, else the sql code to update this item
+ */
+ protected function mark($unread = true, $return = false)
+ {
+ $this->notification_read = (bool) !$unread;
+
+ $where = array(
+ "item_type = '" . $this->db->sql_escape($this->item_type) . "'",
+ 'item_id = ' . (int) $this->item_id,
+ 'user_id = ' . (int) $this->user_id,
+ );
+ $where = implode(' AND ', $where);
+
+ if ($return)
+ {
+ return $where;
+ }
+
+ $sql = 'UPDATE ' . $this->notifications_table . '
+ SET notification_read = ' . (int) $this->notification_read . '
+ WHERE ' . $where;
+ $this->db->sql_query($sql);
+ }
+}
diff --git a/phpBB/includes/notification/type/bookmark.php b/phpBB/includes/notification/type/bookmark.php
new file mode 100644
index 0000000000..4e48a967d0
--- /dev/null
+++ b/phpBB/includes/notification/type/bookmark.php
@@ -0,0 +1,137 @@
+ 'NOTIFICATION_TYPE_BOOKMARK',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return $this->config['allow_bookmarks'];
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $users = array();
+
+ $sql = 'SELECT user_id
+ FROM ' . BOOKMARKS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('topic_id', $post['topic_id']) . '
+ AND user_id <> ' . (int) $post['poster_id'];
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $users[] = $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($users))
+ {
+ return array();
+ }
+
+ $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.item_type = '" . $this->get_type() . "'
+ AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . '
+ AND n.notification_read = 0
+ AND nt.notification_type = n.item_type
+ 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;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'bookmark';
+ }
+}
diff --git a/phpBB/includes/notification/type/disapprove_post.php b/phpBB/includes/notification/type/disapprove_post.php
new file mode 100644
index 0000000000..951c7e0254
--- /dev/null
+++ b/phpBB/includes/notification/type/disapprove_post.php
@@ -0,0 +1,120 @@
+ 'moderation_queue',
+ 'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ return $this->user->lang(
+ $this->language_key,
+ censor_text($this->get_data('topic_title')),
+ $this->get_data('disapprove_reason')
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return '';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ return array_merge(parent::get_email_template_variables(), array(
+ 'REASON' => htmlspecialchars_decode($this->get_data('disapprove_reason')),
+ ));
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($post, $pre_create_data = array())
+ {
+ $this->set_data('disapprove_reason', $post['disapprove_reason']);
+
+ $data = parent::create_insert_array($post);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'post_disapproved';
+ }
+}
diff --git a/phpBB/includes/notification/type/disapprove_topic.php b/phpBB/includes/notification/type/disapprove_topic.php
new file mode 100644
index 0000000000..038e528797
--- /dev/null
+++ b/phpBB/includes/notification/type/disapprove_topic.php
@@ -0,0 +1,120 @@
+ 'moderation_queue',
+ 'lang' => 'NOTIFICATION_TYPE_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ return $this->user->lang(
+ $this->language_key,
+ censor_text($this->get_data('topic_title')),
+ $this->get_data('disapprove_reason')
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return '';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ return array_merge(parent::get_email_template_variables(), array(
+ 'REASON' => htmlspecialchars_decode($this->get_data('disapprove_reason')),
+ ));
+ }
+
+ /**
+ * 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
+ */
+ 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);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'topic_disapproved';
+ }
+}
diff --git a/phpBB/includes/notification/type/interface.php b/phpBB/includes/notification/type/interface.php
new file mode 100644
index 0000000000..a40fdafd09
--- /dev/null
+++ b/phpBB/includes/notification/type/interface.php
@@ -0,0 +1,189 @@
+ array of users and user types that should not receive notifications from this type because they've already been notified
+ * e.g.: array(2 => array(''), 3 => array('', 'email'), ...)
+ *
+ * @return array
+ */
+ public function find_users_for_notification($type_data, $options);
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query();
+
+ /**
+ * Get the special items to load
+ *
+ * @return array Data will be combined sent to load_special() so you can run a single query and get data required for this notification type
+ */
+ public function get_load_special();
+
+ /**
+ * Load the special items
+ *
+ * @param array $data Data from get_load_special()
+ * @param array $notifications Array of notifications (key is notification_id, value is the notification objects)
+ */
+ public function load_special($data, $notifications);
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title();
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_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
+ */
+ public function get_unsubscribe_url($method);
+
+ /**
+ * Get the user's avatar (the user who caused the notification typically)
+ *
+ * @return string
+ */
+ public function get_avatar();
+
+ /**
+ * Prepare to output the notification to the template
+ */
+ public function prepare_for_display();
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template();
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables();
+
+ /**
+ * Pre create insert array function
+ * This allows you to perform certain actions, like run a query
+ * and load data, before create_insert_array() is run. The data
+ * returned from this function will be sent to create_insert_array().
+ *
+ * @param array $type_data The type specific data
+ * @param array $notify_users Notify users list
+ * Formated from find_users_for_notification()
+ * @return array Whatever you want to send to create_insert_array().
+ */
+ public function pre_create_insert_array($type_data, $notify_users);
+
+ /**
+ * 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()
+ *
+ * @return array Array of data ready to be inserted into the database
+ */
+ public function create_insert_array($type_data, $pre_create_data);
+
+ /**
+ * Function for preparing the data for update in an SQL query
+ * (The service handles insertion)
+ *
+ * @param array $type_data Data unique to this notification type
+ *
+ * @return array Array of data ready to be updated in the database
+ */
+ public function create_update_array($type_data);
+
+ /**
+ * Mark this item read
+ *
+ * @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);
+
+ /**
+ * Mark this item unread
+ *
+ * @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);
+}
diff --git a/phpBB/includes/notification/type/pm.php b/phpBB/includes/notification/type/pm.php
new file mode 100644
index 0000000000..b3db7ad5ad
--- /dev/null
+++ b/phpBB/includes/notification/type/pm.php
@@ -0,0 +1,184 @@
+ 'NOTIFICATION_TYPE_PM',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return ($this->config['allow_privmsg'] && $this->auth->acl_get('u_readpm'));
+ }
+
+ /**
+ * Get the id of the
+ *
+ * @param array $pm The data from the private message
+ */
+ public static function get_item_id($pm)
+ {
+ return (int) $pm['msg_id'];
+ }
+
+ /**
+ * Get the id of the parent
+ *
+ * @param array $pm The data from the pm
+ */
+ public static function get_item_parent_id($pm)
+ {
+ // No parent
+ return 0;
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $pm Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($pm, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ if (!sizeof($pm['recipients']))
+ {
+ return array();
+ }
+
+ unset($pm['recipients'][$pm['from_user_id']]);
+
+ $this->user_loader->load_users(array_keys($pm['recipients']));
+
+ return $this->check_user_notification_options(array_keys($pm['recipients']), $options);
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('from_user_id'));
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $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'));
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'privmsg_notify';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ $user_data = $this->user_loader->get_user($this->get_data('from_user_id'));
+
+ return array(
+ 'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']),
+ 'SUBJECT' => htmlspecialchars_decode(censor_text($this->get_data('message_subject'))),
+
+ 'U_VIEW_MESSAGE' => generate_board_url() . '/ucp.' . $this->php_ext . "?i=pm&mode=view&p={$this->item_id}",
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'ucp.' . $this->php_ext, "i=pm&mode=view&p={$this->item_id}");
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('from_user_id'));
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($pm, $pre_create_data = array())
+ {
+ $this->set_data('from_user_id', $pm['from_user_id']);
+
+ $this->set_data('message_subject', $pm['message_subject']);
+
+ return parent::create_insert_array($pm, $pre_create_data);
+ }
+}
diff --git a/phpBB/includes/notification/type/post.php b/phpBB/includes/notification/type/post.php
new file mode 100644
index 0000000000..ddfa720e5e
--- /dev/null
+++ b/phpBB/includes/notification/type/post.php
@@ -0,0 +1,370 @@
+ 'NOTIFICATION_TYPE_POST',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return $this->config['allow_topic_notify'];
+ }
+
+ /**
+ * Get the id of the item
+ *
+ * @param array $post The data from the post
+ */
+ public static function get_item_id($post)
+ {
+ return (int) $post['post_id'];
+ }
+
+ /**
+ * Get the id of the parent
+ *
+ * @param array $post The data from the post
+ */
+ public static function get_item_parent_id($post)
+ {
+ return (int) $post['topic_id'];
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $users = array();
+
+ $sql = 'SELECT user_id
+ FROM ' . TOPICS_WATCH_TABLE . '
+ WHERE topic_id = ' . (int) $post['topic_id'] . '
+ AND notify_status = ' . NOTIFY_YES . '
+ AND user_id <> ' . (int) $post['poster_id'];
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $users[] = $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($users))
+ {
+ return array();
+ }
+
+ $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.item_type = '" . $this->get_type() . "'
+ AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . '
+ AND n.notification_read = 0
+ AND nt.notification_type = n.item_type
+ 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;
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('poster_id'));
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $responders = $this->get_data('responders');
+ $usernames = array();
+
+ if (!is_array($responders))
+ {
+ $responders = array();
+ }
+
+ $responders = array_merge(array(array(
+ 'poster_id' => $this->get_data('poster_id'),
+ 'username' => $this->get_data('post_username'),
+ )), $responders);
+
+ foreach ($responders as $responder)
+ {
+ if ($responder['username'])
+ {
+ $usernames[] = $responder['username'];
+ }
+ else
+ {
+ $usernames[] = $this->user_loader->get_username($responder['poster_id'], 'no_profile');
+ }
+ }
+
+ return $this->user->lang(
+ $this->language_key,
+ implode(', ', $usernames),
+ censor_text($this->get_data('topic_title'))
+ );
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'topic_notify';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ if ($this->get_data('post_username'))
+ {
+ $username = $this->get_data('post_username');
+ }
+ else
+ {
+ $username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile');
+ }
+
+ return array(
+ 'AUTHOR_NAME' => htmlspecialchars_decode($username),
+ 'POST_SUBJECT' => htmlspecialchars_decode(censor_text($this->get_data('post_subject'))),
+ 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($this->get_data('topic_title'))),
+
+ 'U_VIEW_POST' => generate_board_url() . "/viewtopic.{$this->php_ext}?p={$this->item_id}#p{$this->item_id}",
+ 'U_NEWEST_POST' => generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}&view=unread#unread",
+ 'U_TOPIC' => generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}",
+ 'U_VIEW_TOPIC' => generate_board_url() . "/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}",
+ 'U_FORUM' => generate_board_url() . "/viewforum.{$this->php_ext}?f={$this->get_data('forum_id')}",
+ 'U_STOP_WATCHING_TOPIC' => generate_board_url() . "/viewtopic.{$this->php_ext}?uid={$this->user_id}&f={$this->get_data('forum_id')}&t={$this->item_parent_id}&unwatch=topic",
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'viewtopic.' . $this->php_ext, "p={$this->item_id}#p{$this->item_id}");
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ $responders = $this->get_data('responders');
+ $users = array(
+ $this->get_data('poster_id'),
+ );
+
+ if (is_array($responders))
+ {
+ foreach ($responders as $responder)
+ {
+ $users[] = $responder['poster_id'];
+ }
+ }
+
+ return $users;
+ }
+
+ /**
+ * Pre create insert array function
+ * This allows you to perform certain actions, like run a query
+ * and load data, before create_insert_array() is run. The data
+ * returned from this function will be sent to create_insert_array().
+ *
+ * @param array $post Post data from submit_post
+ * @param array $notify_users Notify users list
+ * Formated from find_users_for_notification()
+ * @return array Whatever you want to send to create_insert_array().
+ */
+ public function pre_create_insert_array($post, $notify_users)
+ {
+ if (!sizeof($notify_users))
+ {
+ return array();
+ }
+
+ $tracking_data = array();
+ $sql = 'SELECT user_id, mark_time FROM ' . TOPICS_TRACK_TABLE . '
+ WHERE topic_id = ' . (int) $post['topic_id'] . '
+ AND ' . $this->db->sql_in_set('user_id', array_keys($notify_users));
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $tracking_data[$row['user_id']] = $row['mark_time'];
+ }
+
+ 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
+ */
+ public function create_insert_array($post, $pre_create_data = array())
+ {
+ $this->set_data('poster_id', $post['poster_id']);
+
+ $this->set_data('topic_title', $post['topic_title']);
+
+ $this->set_data('post_subject', $post['post_subject']);
+
+ $this->set_data('post_username', (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : ''));
+
+ $this->set_data('forum_id', $post['forum_id']);
+
+ $this->set_data('forum_name', $post['forum_name']);
+
+ $this->notification_time = $post['post_time'];
+
+ // Topics can be "read" before they are public (while awaiting approval).
+ // Make sure that if the user has read the topic, it's marked as read in the notification
+ if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
+ {
+ $this->notification_read = true;
+ }
+
+ return parent::create_insert_array($post, $pre_create_data);
+ }
+
+ /**
+ * Add responders to the notification
+ *
+ * @param mixed $post
+ */
+ 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)));
+ }
+
+ $responders = $this->get_data('responders');
+
+ $responders = ($responders === null) ? array() : $responders;
+
+ 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)));
+ }
+ }
+
+ $responders[] = array(
+ 'poster_id' => $post['poster_id'],
+ 'username' => (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : ''),
+ );
+
+ $this->set_data('responders', $responders);
+
+ return array('notification_data' => serialize($this->get_data(false)));
+ }
+}
diff --git a/phpBB/includes/notification/type/post_in_queue.php b/phpBB/includes/notification/type/post_in_queue.php
new file mode 100644
index 0000000000..1c29bee3cd
--- /dev/null
+++ b/phpBB/includes/notification/type/post_in_queue.php
@@ -0,0 +1,137 @@
+ 'needs_approval',
+ 'lang' => 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_MODERATION',
+ );
+
+ /**
+ * Permission to check for (in find_users_for_notification)
+ *
+ * @var string Permission name
+ */
+ protected $permission = 'm_approve';
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ $m_approve = $this->auth->acl_getf($this->permission, true);
+
+ return (!empty($m_approve));
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from the post
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ // 0 is for global
+ $auth_approve = $this->auth->acl_get_list(false, $this->permission, array($post['forum_id'], 0));
+
+ if (empty($auth_approve))
+ {
+ return array();
+ }
+
+ $auth_approve[$post['forum_id']] = array_unique(array_merge($auth_approve[$post['forum_id']], $auth_approve[0]));
+
+ return $this->check_user_notification_options($auth_approve[$post['forum_id']][$this->permission], array_merge($options, array(
+ 'item_type' => self::$notification_option['id'],
+ )));
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "i=queue&mode=approve_details&f={$this->get_data('forum_id')}&p={$this->item_id}");
+ }
+
+ /**
+ * 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
+ */
+ 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();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'post_in_queue';
+ }
+}
diff --git a/phpBB/includes/notification/type/quote.php b/phpBB/includes/notification/type/quote.php
new file mode 100644
index 0000000000..5453b267c8
--- /dev/null
+++ b/phpBB/includes/notification/type/quote.php
@@ -0,0 +1,221 @@
+ 'NOTIFICATION_TYPE_QUOTE',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return true;
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $usernames = false;
+ preg_match_all(self::$regular_expression_match, $post['post_text'], $usernames);
+
+ if (empty($usernames[1]))
+ {
+ return array();
+ }
+
+ $usernames[1] = array_unique($usernames[1]);
+
+ $usernames = array_map('utf8_clean_string', $usernames[1]);
+
+ $users = array();
+
+ $sql = 'SELECT user_id
+ FROM ' . USERS_TABLE . '
+ WHERE ' . $this->db->sql_in_set('username_clean', $usernames) . '
+ AND user_id <> ' . (int) $post['poster_id'];
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $users[] = $row['user_id'];
+ }
+ $this->db->sql_freeresult($result);
+
+ if (empty($users))
+ {
+ return array();
+ }
+
+ $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.item_type = '" . $this->get_type() . "'
+ AND n.item_parent_id = " . (int) self::get_item_parent_id($post) . '
+ AND n.notification_read = 0
+ AND nt.notification_type = n.item_type
+ 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;
+ }
+
+ /**
+ * Update a notification
+ *
+ * @param array $data Data specific for this type that will be updated
+ */
+ 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.item_type = '" . $this->get_type() . "'
+ AND n.item_id = " . self::get_item_id($post) . '
+ AND nt.notification_type = n.item_type
+ 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);
+
+ // Find the new users to notify
+ $notifications = $this->find_users_for_notification($post);
+
+ // Find the notifications we must delete
+ $remove_notifications = array_diff($old_notifications, array_keys($notifications));
+
+ // Find the notifications we must add
+ $add_notifications = array();
+ foreach (array_diff(array_keys($notifications), $old_notifications) as $user_id)
+ {
+ $add_notifications[$user_id] = $notifications[$user_id];
+ }
+
+ // Add the necessary notifications
+ $this->notification_manager->add_notifications_for_users($this->get_type(), $post, $add_notifications);
+
+ // Remove the necessary notifications
+ if (!empty($remove_notifications))
+ {
+ $sql = 'DELETE FROM ' . $this->notifications_table . "
+ WHERE item_type = '" . $this->get_type() . "'
+ AND item_id = " . self::get_item_id($post) . '
+ AND ' . $this->db->sql_in_set('user_id', $remove_notifications);
+ $this->db->sql_query($sql);
+ }
+
+ // return true to continue with the update code in the notifications service (this will update the rest of the notifications)
+ return true;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'quote';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ $user_data = $this->user_loader->get_user($this->get_data('poster_id'));
+
+ return array_merge(parent::get_email_template_variables(), array(
+ 'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']),
+ ));
+ }
+}
diff --git a/phpBB/includes/notification/type/report_pm.php b/phpBB/includes/notification/type/report_pm.php
new file mode 100644
index 0000000000..3fa73bab41
--- /dev/null
+++ b/phpBB/includes/notification/type/report_pm.php
@@ -0,0 +1,229 @@
+ 'report',
+ 'lang' => 'NOTIFICATION_TYPE_REPORT',
+ 'group' => 'NOTIFICATION_GROUP_MODERATION',
+ );
+
+ /**
+ * Get the id of the parent
+ *
+ * @param array $pm The data from the pm
+ */
+ public static function get_item_parent_id($pm)
+ {
+ return (int) $pm['report_id'];
+ }
+
+ /**
+ * Is this type available to the current user (defines whether or not it will be shown in the UCP Edit notification options)
+ *
+ * @return bool True/False whether or not this is available to the user
+ */
+ public function is_available()
+ {
+ $m_approve = $this->auth->acl_getf($this->permission, true);
+
+ return (!empty($m_approve));
+ }
+
+
+ /**
+ * Find the users who want to receive notifications
+ * (copied from post_in_queue)
+ *
+ * @param array $post Data from the post
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ // Global
+ $post['forum_id'] = 0;
+
+ $auth_approve = $this->auth->acl_get_list(false, $this->permission, $post['forum_id']);
+
+ if (empty($auth_approve))
+ {
+ return array();
+ }
+
+ if (($key = array_search($this->user->data['user_id'], $auth_approve[$post['forum_id']][$this->permission])))
+ {
+ unset($auth_approve[$post['forum_id']][$this->permission][$key]);
+ }
+
+ return $this->check_user_notification_options($auth_approve[$post['forum_id']][$this->permission], array_merge($options, array(
+ 'item_type' => self::$notification_option['id'],
+ )));
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'report_pm';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ return array(
+ '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}&i=pm_reports&mode=pm_report_details",
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "r={$this->item_parent_id}&i=pm_reports&mode=pm_report_details");
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $this->user->add_lang('mcp');
+
+ $username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile');
+
+ if ($this->get_data('report_text'))
+ {
+ return $this->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('message_subject')),
+ $this->get_data('report_text')
+ );
+ }
+
+ if (isset($this->user->lang[$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->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('message_subject')),
+ $this->get_data('reason_description')
+ );
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('reporter_id'));
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('reporter_id'));
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($post, $pre_create_data = array())
+ {
+ $this->set_data('reporter_id', $this->user->data['user_id']);
+ $this->set_data('reason_title', strtoupper($post['reason_title']));
+ $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);
+ }
+}
diff --git a/phpBB/includes/notification/type/report_pm_closed.php b/phpBB/includes/notification/type/report_pm_closed.php
new file mode 100644
index 0000000000..63dfa92064
--- /dev/null
+++ b/phpBB/includes/notification/type/report_pm_closed.php
@@ -0,0 +1,155 @@
+user->data['user_id'])
+ {
+ return array();
+ }
+
+ return array($pm['reporter'] => array(''));
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return false;
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ return array();
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return '';
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile');
+
+ return $this->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('message_subject'))
+ );
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->get_user_avatar($this->get_data('closer_id'));
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('closer_id'));
+ }
+
+ /**
+ * 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
+ */
+ 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);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+}
diff --git a/phpBB/includes/notification/type/report_post.php b/phpBB/includes/notification/type/report_post.php
new file mode 100644
index 0000000000..de5c54a291
--- /dev/null
+++ b/phpBB/includes/notification/type/report_post.php
@@ -0,0 +1,196 @@
+ 'report',
+ 'lang' => 'NOTIFICATION_TYPE_REPORT',
+ 'group' => 'NOTIFICATION_GROUP_MODERATION',
+ );
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $post Data from the post
+ *
+ * @return array
+ */
+ public function find_users_for_notification($post, $options = array())
+ {
+ $notify_users = parent::find_users_for_notification($post, $options);
+
+ // never notify reporter
+ unset($notify_users[$this->user->data['user_id']]);
+
+ return $notify_users;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'report_post';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ $board_url = generate_board_url();
+
+ return array(
+ 'POST_SUBJECT' => htmlspecialchars_decode(censor_text($this->get_data('post_subject'))),
+ 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($this->get_data('topic_title'))),
+
+ 'U_VIEW_REPORT' => "{$board_url}/mcp.{$this->php_ext}?f={$this->get_data('forum_id')}&p={$this->item_id}&i=reports&mode=report_details#reports",
+ 'U_VIEW_POST' => "{$board_url}/viewtopic.{$this->php_ext}?p={$this->item_id}#p{$this->item_id}",
+ 'U_NEWEST_POST' => "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}&view=unread#unread",
+ 'U_TOPIC' => "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}",
+ 'U_VIEW_TOPIC' => "{$board_url}/viewtopic.{$this->php_ext}?f={$this->get_data('forum_id')}&t={$this->item_parent_id}",
+ 'U_FORUM' => "{$board_url}/viewforum.{$this->php_ext}?f={$this->get_data('forum_id')}",
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "f={$this->get_data('forum_id')}&p={$this->item_id}&i=reports&mode=report_details#reports");
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $this->user->add_lang('mcp');
+
+ $username = $this->user_loader->get_username($this->get_data('reporter_id'), 'no_profile');
+
+ if ($this->get_data('report_text'))
+ {
+ return $this->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('post_subject')),
+ $this->get_data('report_text')
+ );
+ }
+
+ if (isset($this->user->lang[$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->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('post_subject')),
+ $this->get_data('reason_description')
+ );
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('reporter_id'));
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('reporter_id'));
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($post, $pre_create_data = array())
+ {
+ $this->set_data('reporter_id', $this->user->data['user_id']);
+ $this->set_data('reason_title', strtoupper($post['reason_title']));
+ $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);
+ }
+}
diff --git a/phpBB/includes/notification/type/report_post_closed.php b/phpBB/includes/notification/type/report_post_closed.php
new file mode 100644
index 0000000000..3916cd8db7
--- /dev/null
+++ b/phpBB/includes/notification/type/report_post_closed.php
@@ -0,0 +1,155 @@
+user->data['user_id'])
+ {
+ return array();
+ }
+
+ return array($post['reporter'] => array(''));
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return false;
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ return array();
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return '';
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ $username = $this->user_loader->get_username($this->get_data('closer_id'), 'no_profile');
+
+ return $this->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('post_subject'))
+ );
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('closer_id'));
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('closer_id'));
+ }
+
+ /**
+ * 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
+ */
+ 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);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+}
diff --git a/phpBB/includes/notification/type/topic.php b/phpBB/includes/notification/type/topic.php
new file mode 100644
index 0000000000..2549b29409
--- /dev/null
+++ b/phpBB/includes/notification/type/topic.php
@@ -0,0 +1,277 @@
+ 'NOTIFICATION_TYPE_TOPIC',
+ 'group' => 'NOTIFICATION_GROUP_POSTING',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ return $this->config['allow_forum_notify'];
+ }
+
+ /**
+ * Get the id of the item
+ *
+ * @param array $post The data from the post
+ */
+ public static function get_item_id($post)
+ {
+ return (int) $post['topic_id'];
+ }
+
+ /**
+ * Get the id of the parent
+ *
+ * @param array $post The data from the post
+ */
+ public static function get_item_parent_id($post)
+ {
+ return (int) $post['forum_id'];
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $topic Data from the topic
+ *
+ * @return array
+ */
+ public function find_users_for_notification($topic, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ $users = array();
+
+ $sql = 'SELECT user_id
+ FROM ' . FORUMS_WATCH_TABLE . '
+ WHERE forum_id = ' . (int) $topic['forum_id'] . '
+ AND notify_status = ' . NOTIFY_YES . '
+ AND user_id <> ' . (int) $topic['poster_id'];
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $users[] = $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);
+ }
+
+ /**
+ * Get the user's avatar
+ */
+ public function get_avatar()
+ {
+ return $this->user_loader->get_avatar($this->get_data('poster_id'));
+ }
+
+ /**
+ * Get the HTML formatted title of this notification
+ *
+ * @return string
+ */
+ public function get_title()
+ {
+ if ($this->get_data('post_username'))
+ {
+ $username = $this->get_data('post_username');
+ }
+ else
+ {
+ $username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile');
+ }
+
+ return $this->user->lang(
+ $this->language_key,
+ $username,
+ censor_text($this->get_data('topic_title')),
+ $this->get_data('forum_name')
+ );
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'newtopic_notify';
+ }
+
+ /**
+ * Get email template variables
+ *
+ * @return array
+ */
+ public function get_email_template_variables()
+ {
+ $board_url = generate_board_url();
+
+ if ($this->get_data('post_username'))
+ {
+ $username = $this->get_data('post_username');
+ }
+ else
+ {
+ $username = $this->user_loader->get_username($this->get_data('poster_id'), 'no_profile');
+ }
+
+ return array(
+ 'AUTHOR_NAME' => htmlspecialchars_decode($username),
+ 'FORUM_NAME' => htmlspecialchars_decode($this->get_data('forum_name')),
+ 'TOPIC_TITLE' => htmlspecialchars_decode(censor_text($this->get_data('topic_title'))),
+
+ 'U_TOPIC' => "{$board_url}/viewtopic.{$this->php_ext}?f={$this->item_parent_id}&t={$this->item_id}",
+ 'U_VIEW_TOPIC' => "{$board_url}/viewtopic.{$this->php_ext}?f={$this->item_parent_id}&t={$this->item_id}",
+ 'U_FORUM' => "{$board_url}/viewforum.{$this->php_ext}?f={$this->item_parent_id}",
+ 'U_STOP_WATCHING_FORUM' => "{$board_url}/viewforum.{$this->php_ext}?uid={$this->user_id}&f={$this->item_parent_id}&unwatch=forum",
+ );
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'viewtopic.' . $this->php_ext, "f={$this->item_parent_id}&t={$this->item_id}");
+ }
+
+ /**
+ * Users needed to query before this notification can be displayed
+ *
+ * @return array Array of user_ids
+ */
+ public function users_to_query()
+ {
+ return array($this->get_data('poster_id'));
+ }
+
+ /**
+ * Pre create insert array function
+ * This allows you to perform certain actions, like run a query
+ * and load data, before create_insert_array() is run. The data
+ * returned from this function will be sent to create_insert_array().
+ *
+ * @param array $post Post data from submit_post
+ * @param array $notify_users Notify users list
+ * Formated from find_users_for_notification()
+ * @return array Whatever you want to send to create_insert_array().
+ */
+ public function pre_create_insert_array($post, $notify_users)
+ {
+ if (!sizeof($notify_users))
+ {
+ return array();
+ }
+
+ $tracking_data = array();
+ $sql = 'SELECT user_id, mark_time FROM ' . TOPICS_TRACK_TABLE . '
+ WHERE topic_id = ' . (int) $post['topic_id'] . '
+ AND ' . $this->db->sql_in_set('user_id', array_keys($notify_users));
+ $result = $this->db->sql_query($sql);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $tracking_data[$row['user_id']] = $row['mark_time'];
+ }
+
+ 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
+ */
+ public function create_insert_array($post, $pre_create_data = array())
+ {
+ $this->set_data('poster_id', $post['poster_id']);
+
+ $this->set_data('topic_title', $post['topic_title']);
+
+ $this->set_data('post_username', (($post['poster_id'] == ANONYMOUS) ? $post['post_username'] : ''));
+
+ $this->set_data('forum_name', $post['forum_name']);
+
+ $this->notification_time = $post['post_time'];
+
+ // Topics can be "read" before they are public (while awaiting approval).
+ // Make sure that if the user has read the topic, it's marked as read in the notification
+ if (isset($pre_create_data[$this->user_id]) && $pre_create_data[$this->user_id] >= $this->notification_time)
+ {
+ $this->notification_read = true;
+ }
+
+ return parent::create_insert_array($post, $pre_create_data);
+ }
+}
diff --git a/phpBB/includes/notification/type/topic_in_queue.php b/phpBB/includes/notification/type/topic_in_queue.php
new file mode 100644
index 0000000000..dc0b9f9869
--- /dev/null
+++ b/phpBB/includes/notification/type/topic_in_queue.php
@@ -0,0 +1,130 @@
+ 'needs_approval',
+ 'lang' => 'NOTIFICATION_TYPE_IN_MODERATION_QUEUE',
+ 'group' => 'NOTIFICATION_GROUP_MODERATION',
+ );
+
+ /**
+ * Is available
+ */
+ public function is_available()
+ {
+ $m_approve = $this->auth->acl_getf('m_approve', true);
+
+ return (!empty($m_approve));
+ }
+
+ /**
+ * Find the users who want to receive notifications
+ *
+ * @param array $topic Data from the topic
+ *
+ * @return array
+ */
+ public function find_users_for_notification($topic, $options = array())
+ {
+ $options = array_merge(array(
+ 'ignore_users' => array(),
+ ), $options);
+
+ // 0 is for global
+ $auth_approve = $this->auth->acl_get_list(false, 'm_approve', array($topic['forum_id'], 0));
+
+ if (empty($auth_approve))
+ {
+ return array();
+ }
+
+ $auth_approve[$topic['forum_id']] = array_unique(array_merge($auth_approve[$topic['forum_id']], $auth_approve[0]));
+
+ return $this->check_user_notification_options($auth_approve[$topic['forum_id']]['m_approve'], array_merge($options, array(
+ 'item_type' => self::$notification_option['id'],
+ )));
+ }
+
+ /**
+ * Get the url to this item
+ *
+ * @return string URL
+ */
+ public function get_url()
+ {
+ return append_sid($this->phpbb_root_path . 'mcp.' . $this->php_ext, "i=queue&mode=approve_details&f={$this->item_parent_id}&t={$this->item_id}");
+ }
+
+ /**
+ * 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
+ */
+ public function create_insert_array($topic, $pre_create_data = array())
+ {
+ $data = parent::create_insert_array($topic, $pre_create_data);
+
+ $this->notification_time = $data['notification_time'] = time();
+
+ return $data;
+ }
+
+ /**
+ * Get email template
+ *
+ * @return string|bool
+ */
+ public function get_email_template()
+ {
+ return 'topic_in_queue';
+ }
+}
diff --git a/phpBB/includes/search/base.php b/phpBB/includes/search/base.php
index b364dead9a..914cef9167 100644
--- a/phpBB/includes/search/base.php
+++ b/phpBB/includes/search/base.php
@@ -94,7 +94,7 @@ class phpbb_search_base
*
* @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE
*/
- function obtain_ids($search_key, &$result_count, &$id_ary, $start, $per_page, $sort_dir)
+ function obtain_ids($search_key, &$result_count, &$id_ary, &$start, $per_page, $sort_dir)
{
global $cache;
@@ -109,6 +109,19 @@ class phpbb_search_base
$reverse_ids = ($stored_ids[-2] != $sort_dir) ? true : false;
$complete = true;
+ // Change start parameter in case out of bounds
+ if ($result_count)
+ {
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+ else if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+ }
+ }
+
// change the start to the actual end of the current request if the sort direction differs
// from the dirction in the cache and reverse the ids later
if ($reverse_ids)
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 324c214e91..adaf025730 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -353,7 +353,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts
if (!$this->search_query)
@@ -375,6 +375,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
implode(',', $author_ary)
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -488,16 +493,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$id_ary = array_unique($id_ary);
- if (!sizeof($id_ary))
- {
- return false;
- }
-
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
- $sql = 'SELECT FOUND_ROWS() as result_count';
- $result = $this->db->sql_query($sql);
+ $sql_found_rows = 'SELECT FOUND_ROWS() as result_count';
+ $result = $this->db->sql_query($sql_found_rows);
$result_count = (int) $this->db->sql_fetchfield('result_count');
$this->db->sql_freeresult($result);
@@ -507,6 +507,21 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
}
}
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -533,7 +548,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -557,6 +572,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
$author_name,
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -662,8 +682,8 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
// retrieve the total result count if needed
if (!$result_count)
{
- $sql = 'SELECT FOUND_ROWS() as result_count';
- $result = $this->db->sql_query($sql);
+ $sql_found_rows = 'SELECT FOUND_ROWS() as result_count';
+ $result = $this->db->sql_query($sql_found_rows);
$result_count = (int) $this->db->sql_fetchfield('result_count');
$this->db->sql_freeresult($result);
@@ -673,6 +693,20 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base
}
}
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php
index 53df8348ae..c9f33054fc 100644
--- a/phpBB/includes/search/fulltext_native.php
+++ b/phpBB/includes/search/fulltext_native.php
@@ -516,7 +516,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts.
if (empty($this->search_query))
@@ -855,10 +855,6 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
$this->db->sql_freeresult($result);
- if (!sizeof($id_ary))
- {
- return false;
- }
// if we use mysql and the total result count is not cached yet, retrieve it from the db
if (!$total_results && $is_mysql)
@@ -867,14 +863,14 @@ class phpbb_search_fulltext_native extends phpbb_search_base
$sql_array_copy = $sql_array;
$sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id ';
- $sql = $this->db->sql_build_query('SELECT', $sql_array_copy);
+ $sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy);
unset($sql_array_copy);
- $this->db->sql_query($sql);
+ $this->db->sql_query($sql_calc);
$this->db->sql_freeresult($result);
- $sql = 'SELECT FOUND_ROWS() as total_results';
- $result = $this->db->sql_query($sql);
+ $sql_count = 'SELECT FOUND_ROWS() as total_results';
+ $result = $this->db->sql_query($sql_count);
$total_results = (int) $this->db->sql_fetchfield('total_results');
$this->db->sql_freeresult($result);
@@ -884,6 +880,20 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
}
+ if ($start >= $total_results)
+ {
+ $start = floor(($total_results - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[(($type == 'posts') ? 'post_id' : 'topic_id')];
+ }
+ $this->db->sql_freeresult($result);
+
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -910,7 +920,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -1096,13 +1106,13 @@ class phpbb_search_fulltext_native extends phpbb_search_base
if (!$total_results && $is_mysql)
{
// Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it.
- $sql = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql);
+ $sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql);
- $this->db->sql_query($sql);
+ $this->db->sql_query($sql_calc);
$this->db->sql_freeresult($result);
- $sql = 'SELECT FOUND_ROWS() as total_results';
- $result = $this->db->sql_query($sql);
+ $sql_count = 'SELECT FOUND_ROWS() as total_results';
+ $result = $this->db->sql_query($sql_count);
$total_results = (int) $this->db->sql_fetchfield('total_results');
$this->db->sql_freeresult($result);
@@ -1112,6 +1122,19 @@ class phpbb_search_fulltext_native extends phpbb_search_base
}
}
+ if ($start >= $total_results)
+ {
+ $start = floor(($total_results - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $total_results, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php
index 1475cc31d0..eeb628b18f 100644
--- a/phpBB/includes/search/fulltext_postgres.php
+++ b/phpBB/includes/search/fulltext_postgres.php
@@ -343,7 +343,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts
if (!$this->search_query)
@@ -371,6 +371,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
implode(',', $author_ary)
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -495,11 +500,6 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$id_ary = array_unique($id_ary);
- if (!sizeof($id_ary))
- {
- return false;
- }
-
// if the total result count is not cached yet, retrieve it from the db
if (!$result_count)
{
@@ -518,6 +518,21 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$this->db->sql_transaction('commit');
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
$id_ary = array_slice($id_ary, 0, (int) $per_page);
@@ -544,7 +559,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No author? No posts
if (!sizeof($author_ary))
@@ -568,6 +583,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$author_name,
)));
+ if ($start < 0)
+ {
+ $start = 0;
+ }
+
// try reading the results from cache
$result_count = 0;
if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
@@ -710,6 +730,20 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base
$this->db->sql_transaction('commit');
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start);
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $id_ary[] = (int) $row[$field];
+ }
+ $this->db->sql_freeresult($result);
+
+ $id_ary = array_unique($id_ary);
+ }
+
if (sizeof($id_ary))
{
$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php
index 4bacf74f93..48445d0794 100644
--- a/phpBB/includes/search/fulltext_sphinx.php
+++ b/phpBB/includes/search/fulltext_sphinx.php
@@ -454,7 +454,7 @@ class phpbb_search_fulltext_sphinx
* @param int $per_page number of ids each page is supposed to contain
* @return boolean|int total number of results
*/
- public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page)
+ public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page)
{
// No keywords? No posts.
if (!strlen($this->search_query) && !sizeof($author_ary))
@@ -609,6 +609,25 @@ class phpbb_search_fulltext_sphinx
}
}
+ $result_count = $result['total_found'];
+
+ if ($start >= $result_count)
+ {
+ $start = floor(($result_count - 1) / $per_page) * $per_page;
+
+ $this->sphinx->SetLimits((int) $start, (int) $per_page, SPHINX_MAX_MATCHES);
+ $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes);
+
+ // Could be connection to localhost:9312 failed (errno=111,
+ // msg=Connection refused) during rotate, retry if so
+ $retries = SPHINX_CONNECT_RETRIES;
+ while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--)
+ {
+ usleep(SPHINX_CONNECT_WAIT_TIME);
+ $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes);
+ }
+ }
+
$id_ary = array();
if (isset($result['matches']))
{
@@ -629,8 +648,6 @@ class phpbb_search_fulltext_sphinx
return false;
}
- $result_count = $result['total_found'];
-
$id_ary = array_slice($id_ary, 0, (int) $per_page);
return $result_count;
@@ -878,8 +895,8 @@ class phpbb_search_fulltext_sphinx
' . $this->user->lang['MIB'] . '
-
' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
- - ' . (($this->config_generate()) ? '' : $this->config_file_data) . '
+
' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
+ - ' . (($this->config_generate()) ? '' : $this->config_file_data) . '
';
diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php
index ee8a4094c7..6bc71da0c1 100644
--- a/phpBB/includes/session.php
+++ b/phpBB/includes/session.php
@@ -346,7 +346,7 @@ class phpbb_session
$session_id = $request->variable('sid', '');
if (defined('NEED_SID') && (empty($session_id) || $this->session_id !== $session_id))
{
- send_status_line(401, 'Not authorized');
+ send_status_line(401, 'Unauthorized');
redirect(append_sid("{$phpbb_root_path}index.$phpEx"));
}
diff --git a/phpBB/includes/style/extension_path_provider.php b/phpBB/includes/style/extension_path_provider.php
index 4eac300424..6976a45ed0 100644
--- a/phpBB/includes/style/extension_path_provider.php
+++ b/phpBB/includes/style/extension_path_provider.php
@@ -92,7 +92,7 @@ class phpbb_style_extension_path_provider extends phpbb_extension_provider imple
if ($path && !phpbb_is_absolute($path))
{
$result = $finder->directory('/' . $this->ext_dir_prefix . $path)
- ->get_directories(true, true);
+ ->get_directories(true, false, true);
foreach ($result as $ext => $ext_path)
{
$directories[$ext][] = $ext_path;
diff --git a/phpBB/includes/ucp/info/ucp_notifications.php b/phpBB/includes/ucp/info/ucp_notifications.php
new file mode 100644
index 0000000000..98d8b9db61
--- /dev/null
+++ b/phpBB/includes/ucp/info/ucp_notifications.php
@@ -0,0 +1,35 @@
+ 'ucp_notifications',
+ 'title' => 'UCP_NOTIFICATION_OPTIONS',
+ 'version' => '1.0.0',
+ 'modes' => array(
+ 'notification_options' => array('title' => 'UCP_NOTIFICATION_OPTIONS', 'auth' => '', 'cat' => array('UCP_PREFS')),
+ 'notification_list' => array('title' => 'UCP_NOTIFICATION_LIST', 'auth' => '', 'cat' => array('UCP_MAIN')),
+ ),
+ );
+ }
+
+ function install()
+ {
+ }
+
+ function uninstall()
+ {
+ }
+}
diff --git a/phpBB/includes/ucp/ucp_notifications.php b/phpBB/includes/ucp/ucp_notifications.php
new file mode 100644
index 0000000000..338c921e94
--- /dev/null
+++ b/phpBB/includes/ucp/ucp_notifications.php
@@ -0,0 +1,226 @@
+variable('start', 0);
+ $form_time = min($request->variable('form_time', 0), time());
+
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ switch ($mode)
+ {
+ case 'notification_options':
+ $subscriptions = $phpbb_notifications->get_global_subscriptions(false);
+
+ // Add/remove subscriptions
+ if ($request->is_set_post('submit'))
+ {
+ if (!check_form_key('ucp_notification'))
+ {
+ trigger_error('FORM_INVALID');
+ }
+
+ $notification_methods = $phpbb_notifications->get_subscription_methods();
+
+ foreach($phpbb_notifications->get_subscription_types() as $group => $subscription_types)
+ {
+ foreach($subscription_types as $type => $data)
+ {
+ foreach($notification_methods as $method => $method_data)
+ {
+ if ($request->is_set_post($type . '_' . $method_data['id']) && (!isset($subscriptions[$type]) || !in_array($method_data['id'], $subscriptions[$type])))
+ {
+ $phpbb_notifications->add_subscription($type, 0, $method_data['id']);
+ }
+ else if (!$request->is_set_post($type . '_' . $method_data['id']) && isset($subscriptions[$type]) && in_array($method_data['id'], $subscriptions[$type]))
+ {
+ $phpbb_notifications->delete_subscription($type, 0, $method_data['id']);
+ }
+ }
+
+ if ($request->is_set_post($type . '_notification') && !isset($subscriptions[$type]))
+ {
+ $phpbb_notifications->add_subscription($type);
+ }
+ else if (!$request->is_set_post($type . '_notification') && isset($subscriptions[$type]))
+ {
+ $phpbb_notifications->delete_subscription($type);
+ }
+ }
+ }
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['PREFERENCES_UPDATED'] . '
' . sprintf($user->lang['RETURN_UCP'], '', '');
+ trigger_error($message);
+ }
+
+ $this->output_notification_methods('notification_methods', $phpbb_notifications, $template, $user);
+
+ $this->output_notification_types($subscriptions, 'notification_types', $phpbb_notifications, $template, $user);
+
+ $this->tpl_name = 'ucp_notifications';
+ $this->page_title = 'UCP_NOTIFICATION_OPTIONS';
+ break;
+
+ case 'notification_list':
+ default:
+ // Mark all items read
+ if ($request->variable('mark', '') == 'all' && (confirm_box(true) || check_link_hash($request->variable('token', ''), 'mark_all_notifications_read')))
+ {
+ if (confirm_box(true))
+ {
+ $phpbb_notifications->mark_notifications_read(false, false, $user->data['user_id'], $form_time);
+
+ meta_refresh(3, $this->u_action);
+ $message = $user->lang['NOTIFICATIONS_MARK_ALL_READ_SUCCESS'] . '
' . sprintf($user->lang['RETURN_UCP'], '', '');
+ trigger_error($message);
+ }
+ else
+ {
+ confirm_box(false, 'NOTIFICATIONS_MARK_ALL_READ', build_hidden_fields(array(
+ 'mark' => 'all',
+ 'form_time' => $form_time,
+ )));
+ }
+ }
+
+ // Mark specific notifications read
+ if ($request->is_set_post('submit'))
+ {
+ if (!check_form_key('ucp_notification'))
+ {
+ trigger_error('FORM_INVALID');
+ }
+
+ $mark_read = $request->variable('mark', array(0));
+
+ if (!empty($mark_read))
+ {
+ $phpbb_notifications->mark_notifications_read_by_id($mark_read, $form_time);
+ }
+ }
+
+ $notifications = $phpbb_notifications->load_notifications(array(
+ 'start' => $start,
+ 'limit' => $config['topics_per_page'],
+ 'count_total' => true,
+ ));
+
+ foreach ($notifications['notifications'] as $notification)
+ {
+ $template->assign_block_vars('notification_list', $notification->prepare_for_display());
+ }
+
+ $base_url = append_sid("{$phpbb_root_path}ucp.$phpEx", "i=ucp_notifications&mode=notification_list");
+ phpbb_generate_template_pagination($template, $base_url, 'pagination', 'start', $notifications['total_count'], $config['topics_per_page'], $start);
+
+ $template->assign_vars(array(
+ 'PAGE_NUMBER' => phpbb_on_page($template, $user, $base_url, $notifications['total_count'], $config['topics_per_page'], $start),
+ 'TOTAL_COUNT' => $user->lang('NOTIFICATIONS_COUNT', $notifications['total_count']),
+ 'U_MARK_ALL' => $base_url . '&mark=all&token=' . generate_link_hash('mark_all_notifications_read'),
+ ));
+
+ $this->tpl_name = 'ucp_notifications';
+ $this->page_title = 'UCP_NOTIFICATION_LIST';
+ break;
+ }
+
+ $template->assign_vars(array(
+ 'TITLE' => $user->lang($this->page_title),
+ 'TITLE_EXPLAIN' => $user->lang($this->page_title . '_EXPLAIN'),
+
+ 'MODE' => $mode,
+
+ 'FORM_TIME' => time(),
+ ));
+ }
+
+ /**
+ * Output all the notification types to the template
+ *
+ * @param string $block
+ * @param phpbb_notification_manager $phpbb_notifications
+ * @param phpbb_template $template
+ * @param phpbb_user $user
+ */
+ public function output_notification_types($subscriptions, $block = 'notification_types', phpbb_notification_manager $phpbb_notifications, phpbb_template $template, phpbb_user $user)
+ {
+ $notification_methods = $phpbb_notifications->get_subscription_methods();
+
+ foreach($phpbb_notifications->get_subscription_types() as $group => $subscription_types)
+ {
+ $template->assign_block_vars($block, array(
+ 'GROUP_NAME' => $user->lang($group),
+ ));
+
+ foreach($subscription_types as $type => $data)
+ {
+ $template->assign_block_vars($block, array(
+ 'TYPE' => $type,
+
+ 'NAME' => $user->lang($data['lang']),
+ 'EXPLAIN' => (isset($user->lang[$data['lang'] . '_EXPLAIN'])) ? $user->lang($data['lang'] . '_EXPLAIN') : '',
+
+ 'SUBSCRIBED' => (isset($subscriptions[$type])) ? true : false,
+ ));
+
+ foreach($notification_methods as $method => $method_data)
+ {
+ $template->assign_block_vars($block . '.notification_methods', array(
+ 'METHOD' => $method_data['id'],
+
+ 'NAME' => $user->lang($method_data['lang']),
+
+ 'SUBSCRIBED' => (isset($subscriptions[$type]) && in_array($method_data['id'], $subscriptions[$type])) ? true : false,
+ ));
+ }
+ }
+ }
+ }
+
+ /**
+ * Output all the notification methods to the template
+ *
+ * @param string $block
+ * @param phpbb_notification_manager $phpbb_notifications
+ * @param phpbb_template $template
+ * @param phpbb_user $user
+ */
+ public function output_notification_methods($block = 'notification_methods', phpbb_notification_manager $phpbb_notifications, phpbb_template $template, phpbb_user $user)
+ {
+ $notification_methods = $phpbb_notifications->get_subscription_methods();
+
+ foreach($notification_methods as $method => $method_data)
+ {
+ $template->assign_block_vars($block, array(
+ 'METHOD' => $method_data['id'],
+
+ 'NAME' => $user->lang($method_data['lang']),
+ ));
+ }
+ }
+}
diff --git a/phpBB/includes/ucp/ucp_prefs.php b/phpBB/includes/ucp/ucp_prefs.php
index 23892c2c8c..7c3286c1d1 100644
--- a/phpBB/includes/ucp/ucp_prefs.php
+++ b/phpBB/includes/ucp/ucp_prefs.php
@@ -46,8 +46,6 @@ class ucp_prefs
'viewemail' => request_var('viewemail', (bool) $user->data['user_allow_viewemail']),
'massemail' => request_var('massemail', (bool) $user->data['user_allow_massemail']),
'hideonline' => request_var('hideonline', (bool) !$user->data['user_allow_viewonline']),
- 'notifypm' => request_var('notifypm', (bool) $user->data['user_notify_pm']),
- 'popuppm' => request_var('popuppm', (bool) $user->optionget('popuppm')),
'allowpm' => request_var('allowpm', (bool) $user->data['user_allow_pm']),
);
@@ -81,15 +79,12 @@ class ucp_prefs
if (!sizeof($error))
{
- $user->optionset('popuppm', $data['popuppm']);
-
$sql_ary = array(
'user_allow_pm' => $data['allowpm'],
'user_allow_viewemail' => $data['viewemail'],
'user_allow_massemail' => $data['massemail'],
'user_allow_viewonline' => ($auth->acl_get('u_hideonline')) ? !$data['hideonline'] : $user->data['user_allow_viewonline'],
'user_notify_type' => $data['notifymethod'],
- 'user_notify_pm' => $data['notifypm'],
'user_options' => $user->data['user_options'],
'user_dateformat' => $data['dateformat'],
@@ -172,8 +167,6 @@ class ucp_prefs
'S_MASS_EMAIL' => $data['massemail'],
'S_ALLOW_PM' => $data['allowpm'],
'S_HIDE_ONLINE' => $data['hideonline'],
- 'S_NOTIFY_PM' => $data['notifypm'],
- 'S_POPUP_PM' => $data['popuppm'],
'DATE_FORMAT' => $data['dateformat'],
'A_DATE_FORMAT' => addslashes($data['dateformat']),
diff --git a/phpBB/includes/user_loader.php b/phpBB/includes/user_loader.php
new file mode 100644
index 0000000000..77128d6570
--- /dev/null
+++ b/phpBB/includes/user_loader.php
@@ -0,0 +1,231 @@
+db = $db;
+
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+
+ $this->users_table = $users_table;
+ }
+
+ /**
+ * Load user helper
+ *
+ * @param array $user_ids
+ */
+ public function load_users(array $user_ids)
+ {
+ $user_ids[] = ANONYMOUS;
+
+ // Load the users
+ $user_ids = array_unique($user_ids);
+
+ // Do not load users we already have in $this->users
+ $user_ids = array_diff($user_ids, array_keys($this->users));
+
+ if (sizeof($user_ids))
+ {
+ $sql = 'SELECT *
+ FROM ' . $this->users_table . '
+ WHERE ' . $this->db->sql_in_set('user_id', $user_ids);
+ $result = $this->db->sql_query($sql);
+
+ while ($row = $this->db->sql_fetchrow($result))
+ {
+ $this->users[$row['user_id']] = $row;
+ }
+ $this->db->sql_freeresult($result);
+ }
+ }
+
+ /**
+ * Load a user by username
+ *
+ * Stores the full data in the user cache so they do not need to be loaded again
+ * Returns the user id so you may use get_user() from the returned value
+ *
+ * @param string $username Raw username to load (will be cleaned)
+ * @return int User ID for the username
+ */
+ public function load_user_by_username($username)
+ {
+ $sql = 'SELECT *
+ FROM ' . $this->users_table . "
+ WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row)
+ {
+ $this->users[$row['user_id']] = $row;
+
+ return $row['user_id'];
+ }
+
+ return ANONYMOUS;
+ }
+
+ /**
+ * Get a user row from our users cache
+ *
+ * @param int $user_id User ID of the user you want to retreive
+ * @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()
+ * @return array|bool Row from the database of the user or Anonymous if the user wasn't loaded/does not exist
+ * or bool False if the anonymous user was not loaded
+ */
+ public function get_user($user_id, $query = false)
+ {
+ if (isset($this->users[$user_id]))
+ {
+ return $this->users[$user_id];
+ }
+ // Query them if we must (if ANONYMOUS is sent as the user_id and we have not loaded Anonymous yet, we must load Anonymous as a last resort)
+ else if ($query || $user_id == ANONYMOUS)
+ {
+ $this->load_users(array($user_id));
+
+ return $this->get_user($user_id);
+ }
+
+ return $this->get_user(ANONYMOUS);
+ }
+
+ /**
+ * Get username
+ *
+ * @param int $user_id User ID of the user you want to retreive the username for
+ * @param string $mode The mode to load (same as get_username_string). One of the following:
+ * profile (for getting an url to the profile)
+ * username (for obtaining the username)
+ * colour (for obtaining the user colour)
+ * full (for obtaining a html string representing a coloured link to the users profile)
+ * no_profile (the same as full but forcing no profile link)
+ * @param string $guest_username Optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then.
+ * @param string $custom_profile_url Optional parameter to specify a profile url. The user id get appended to this url as &u={user_id}
+ * @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()
+ * @return string
+ */
+ public function get_username($user_id, $mode, $guest_username = false, $custom_profile_url = false, $query = false)
+ {
+ if (!($user = $this->get_user($user_id, $query)))
+ {
+ return '';
+ }
+
+ return get_username_string($mode, $user['user_id'], $user['username'], $user['user_colour'], $guest_username, $custom_profile_url);
+ }
+
+ /**
+ * Get avatar
+ *
+ * @param int $user_id User ID of the user you want to retreive 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()
+ * @return string
+ */
+ public function get_avatar($user_id, $query = 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);
+ }
+
+ return get_user_avatar($user['user_avatar'], $user['user_avatar_type'], $user['user_avatar_width'], $user['user_avatar_height']);
+ }
+
+ /**
+ * Get rank
+ *
+ * @param int $user_id User ID of the user you want to retreive the rank 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()
+ * @return array Array with keys 'rank_title', 'rank_img', and 'rank_img_src'
+ */
+ public function get_rank($user_id, $query = false)
+ {
+ if (!($user = $this->get_user($user_id, $query)))
+ {
+ return '';
+ }
+
+ if (!function_exists('get_user_rank'))
+ {
+ include($this->phpbb_root_path . 'includes/functions_display.' . $this->php_ext);
+ }
+
+ $rank = array(
+ 'rank_title',
+ 'rank_img',
+ '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']);
+
+ return $rank;
+ }
+}
diff --git a/phpBB/index.php b/phpBB/index.php
index 845d0f0c02..74fc1b9bda 100644
--- a/phpBB/index.php
+++ b/phpBB/index.php
@@ -24,6 +24,30 @@ $user->session_begin();
$auth->acl($user->data);
$user->setup('viewforum');
+// Mark notifications read
+if (($mark_notification = $request->variable('mark_notification', 0)))
+{
+ $phpbb_notifications = $phpbb_container->get('notification_manager');
+
+ $notification = $phpbb_notifications->load_notifications(array(
+ 'notification_id' => $mark_notification
+ ));
+
+ if (isset($notification['notifications'][$mark_notification]))
+ {
+ $notification = $notification['notifications'][$mark_notification];
+
+ $notification->mark_read();
+
+ if (($redirect = $request->variable('redirect', '')))
+ {
+ redirect(append_sid($phpbb_root_path . $redirect));
+ }
+
+ redirect($notification->get_url());
+ }
+}
+
display_forums('', $config['load_moderators']);
$order_legend = ($config['legend_sort_groupname']) ? 'group_name' : 'group_legend';
diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php
index 28baf101a8..4938ef0f87 100644
--- a/phpBB/install/database_update.php
+++ b/phpBB/install/database_update.php
@@ -1,39 +1,23 @@
purge();
+
+?>
+
+
+
+
+
+
+
+
+
+
+