diff --git a/adm/style/banhammer_body.html b/adm/style/banhammer_body.html index fba3bd2..d41c5a8 100644 --- a/adm/style/banhammer_body.html +++ b/adm/style/banhammer_body.html @@ -65,6 +65,10 @@

{L_ACP_BH_SETTINGS}


{L_ACP_MOVE_GROUP_EXPLAIN}
+
+

{L_ACP_RESTRICT_GROUP_EXPLAIN}
+
+

{L_SFS_API_KEY_EXPLAIN}
diff --git a/config/services.yml b/config/services.yml index 7fb8552..1163fef 100644 --- a/config/services.yml +++ b/config/services.yml @@ -13,6 +13,7 @@ services: - '%core.root_path%' - '%core.php_ext%' - '@service_container' + - '%core.table_prefix%banhammer_restrict' tags: - { name: event.listener } phpbbmodders.banhammer.admin.controller: @@ -36,3 +37,13 @@ services: - '@language' - '@request' - '@user' + phpbbmodders.banhammer.cron.restriction_expiry: + class: phpbbmodders\banhammer\cron\task\restriction_expiry + arguments: + - '@config' + - '@dbal.conn' + - '%core.table_prefix%banhammer_restrict' + - '%core.root_path%' + - '%core.php_ext%' + tags: + - { name: cron.task } diff --git a/controller/admin_controller.php b/controller/admin_controller.php index 1a1ade8..abcc457 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -108,7 +108,8 @@ public function display_options() 'DEL_POSTS' => (!empty($this->config['bh_del_posts'])) ? true : false, 'DEL_PROFILE' => (!empty($this->config['bh_del_profile'])) ? true : false, 'DEL_SIGNATURE' => (!empty($this->config['bh_del_signature'])) ? true : false, - 'MOVE_GROUP' => $this->get_groups($this->request->variable('move_group', $this->config['bh_group_id'])), + 'MOVE_GROUP' => $this->get_groups($this->request->variable('move_group', $this->config['bh_group_id'])), + 'RESTRICT_GROUP' => $this->get_groups($this->request->variable('restrict_group', $this->config['bh_restrict_group_id'])), 'SFS_ALLOW_HTTP' => (!empty($this->config['bh_sfs_allow_http'])) ? true : false, 'SFS_API_KEY' => (!empty($this->config['bh_sfs_api_key'])) ? $this->config['bh_sfs_api_key'] : '', 'SFS_CURL' => (function_exists('curl_init')) ? true : false, @@ -133,6 +134,7 @@ protected function set_options() $this->config->set('bh_del_profile', $this->request->variable('del_profile', 0)); $this->config->set('bh_del_signature', $this->request->variable('del_signature', 0)); $this->config->set('bh_group_id', $this->request->variable('move_group', 0)); + $this->config->set('bh_restrict_group_id', $this->request->variable('restrict_group', 0)); $this->config->set('bh_sfs_api_key', $this->request->variable('sfs_api_key', '', true)); $this->config->set('bh_sfs_allow_http', $this->request->variable('sfs_allow_http', 0)); $this->config->set('bh_ban_time', $this->request->variable('ban_time', 0)); diff --git a/cron/task/restriction_expiry.php b/cron/task/restriction_expiry.php new file mode 100644 index 0000000..3ed28e8 --- /dev/null +++ b/cron/task/restriction_expiry.php @@ -0,0 +1,133 @@ +config = $config; + $this->db = $db; + $this->restrict_table = $restrict_table; + $this->root_path = $root_path; + $this->php_ext = $php_ext; + } + + /** + * Restore every restriction whose time has passed. + * + * @return void + * @access public + */ + public function run() + { + $this->config->set('bh_restrict_last_run', time(), false); + + $sql = 'SELECT restrict_id, user_id, original_group_id + FROM ' . $this->restrict_table . ' + WHERE restrict_until > 0 + AND restrict_until <= ' . time(); + $result = $this->db->sql_query($sql); + + $expired = array(); + + while ($row = $this->db->sql_fetchrow($result)) + { + $expired[] = $row; + } + $this->db->sql_freeresult($result); + + if (empty($expired)) + { + return; + } + + if (!function_exists('group_user_add') || !function_exists('group_user_del')) + { + include($this->root_path . 'includes/functions_user.' . $this->php_ext); + } + + $restrict_group_id = (int) $this->config['bh_restrict_group_id']; + + foreach ($expired as $row) + { + $user_id = (int) $row['user_id']; + $original_group_id = (int) $row['original_group_id']; + + if ($restrict_group_id) + { + group_user_del($restrict_group_id, array($user_id)); + } + + if ($original_group_id) + { + group_user_add($original_group_id, array($user_id), false, false, true); + } + + $this->db->sql_query('DELETE FROM ' . $this->restrict_table . ' WHERE restrict_id = ' . (int) $row['restrict_id']); + } + } + + /** + * {@inheritdoc} + */ + public function is_runnable() + { + return true; + } + + /** + * At most once every five minutes, restoring a group a little late is + * harmless and this should not run on every page load. + * + * {@inheritdoc} + */ + public function should_run() + { + return (int) $this->config['bh_restrict_last_run'] < (time() - 300); + } +} diff --git a/event/banhammer_listener.php b/event/banhammer_listener.php index eb02142..ae8a4c8 100644 --- a/event/banhammer_listener.php +++ b/event/banhammer_listener.php @@ -64,6 +64,9 @@ class banhammer_listener implements EventSubscriberInterface /** @var ContainerInterface */ protected $container; + /** @var string */ + protected $restrict_table; + public function __construct( \phpbb\auth\auth $auth, \phpbb\cache\driver\driver_interface $cache, @@ -75,7 +78,8 @@ public function __construct( \phpbbmodders\banhammer\core\bantime $bantime, $root_path, $phpExt, - ContainerInterface $container + ContainerInterface $container, + $restrict_table ) { $this->auth = $auth; @@ -89,12 +93,16 @@ public function __construct( $this->root_path = $root_path; $this->php_ext = $phpExt; $this->container = $container; + $this->restrict_table = $restrict_table; } static public function getSubscribedEvents() { return(array( - 'core.memberlist_view_profile' => 'do_ban_hammer_stuff', + 'core.memberlist_view_profile' => array( + array('do_ban_hammer_stuff'), + array('do_restrict_stuff'), + ), 'core.session_set_custom_ban' => 'undo_bh_group', 'core.mcp_queue_approve_details_template' => 'add_mcp_queue_banhammer_link', )); @@ -450,6 +458,138 @@ public function do_ban_hammer_stuff($event) redirect($url); } + /** + * Move a user into a restricted group for a set time, instead of banning + * them. They keep the ability to log in, just with whatever reduced + * permissions the admin has given the restricted group. + * + * Answers CDB topic 240381 ("would it be possible to slightly change + * this to avoid the ban... add the user from profile to a different + * group, severely limited, for a given time"). + * + * @param \phpbb\event\data $event The event object + * @return void + * @access public + */ + public function do_restrict_stuff($event) + { + $data = $event['member']; + $user_id = (int) $data['user_id']; + $restrict_group_id = (int) $this->config['bh_restrict_group_id']; + + if (!$this->auth->acl_get('m_ban') || $data['user_type'] == USER_FOUNDER || $user_id == $this->user->data['user_id'] || !$restrict_group_id) + { + // Nothing to see here, move on. No group configured in the ACP + // means this feature is simply off. + return; + } + + // A banned user can't log in regardless of group, so restricting + // them on top of a ban is meaningless - same check do_ban_hammer_stuff + // already makes before showing its own options. + if (!function_exists('phpbb_get_banned_user_ids')) + { + include($this->root_path . 'includes/functions_user.' . $this->php_ext); + } + + if (!empty(phpbb_get_banned_user_ids(array($user_id)))) + { + return; + } + + $this->user->add_lang_ext('phpbbmodders/banhammer', 'banhammer'); + $this->user->add_lang('acp/ban'); + + if ($this->active_restriction($user_id) !== null) + { + $this->template->assign_var('RESTRICT_MESSAGE', $this->user->lang['BH_ALREADY_RESTRICTED']); + + return; + } + + if (!$this->request->is_set('restrict') || ($this->request->is_set('restrict') && $this->request->is_set('confirm_key') && !confirm_box(true))) + { + $params = array( + 'mode' => 'viewprofile', + 'u' => $user_id, + 'restrict' => 1, + ); + + $this->template->assign_vars(array( + 'RESTRICT_TIME' => $this->bantime->display_ban_time(0), + 'S_SHOW_RESTRICT' => true, + 'U_RESTRICT_USER' => append_sid($this->root_path . 'memberlist.' . $this->php_ext, $params), + )); + + return; + } + + if (!confirm_box(true)) + { + $hidden_fields = build_hidden_fields(array( + 'restrict_time' => $this->request->variable('restrict_time', 0), + 'mode' => 'viewprofile', + )); + + $ban_length_options = $this->bantime->ban_length_options(); + $length = isset($ban_length_options[$this->request->variable('restrict_time', 0)]) ? $ban_length_options[$this->request->variable('restrict_time', 0)] : ''; + + $message = sprintf($this->user->lang['BH_SURE_RESTRICT'], $data['username']); + $message .= ($length) ? '

' . $this->user->lang('BH_RESTRICT_FOR', $length) : '

' . $this->user->lang['BH_RESTRICT_PERM']; + + confirm_box(false, $message, $hidden_fields); + } + + if (!function_exists('group_user_add')) + { + include($this->root_path . 'includes/functions_user.' . $this->php_ext); + } + + $restrict_time = $this->request->variable('restrict_time', 0); + // $restrict_time is minutes (ban_length_options()'s keys, same unit + // user_ban() expects), not days. + $restrict_until = ($restrict_time > 0) ? time() + ($restrict_time * 60) : 0; + $original_group_id = (int) $data['group_id']; + + $sql_ary = array( + 'user_id' => $user_id, + 'original_group_id' => $original_group_id, + 'restrict_until' => $restrict_until, + ); + $sql = 'INSERT INTO ' . $this->restrict_table . ' ' . $this->db->sql_build_array('INSERT', $sql_ary); + $this->db->sql_query($sql); + + group_user_add($restrict_group_id, array($user_id), false, false, true); + + $args = array( + 'mode' => 'viewprofile', + 'u' => $user_id, + ); + + $url = append_sid(generate_board_url() . '/memberlist.' . $this->php_ext, $args); + + redirect($url); + } + + /** + * Whether a user currently has an active restriction. + * + * @param int $user_id + * @return array|null The restriction row, or null when there is none. + * @access protected + */ + protected function active_restriction($user_id) + { + $sql = 'SELECT restrict_id + FROM ' . $this->restrict_table . ' + WHERE user_id = ' . (int) $user_id; + $result = $this->db->sql_query_limit($sql, 1); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + return ($row) ?: null; + } + // Once a ban is cleared try and remove the user from the banned group set in the ACP of the extension public function undo_bh_group($event) { diff --git a/language/en/banhammer.php b/language/en/banhammer.php index 3385117..3f4c316 100644 --- a/language/en/banhammer.php +++ b/language/en/banhammer.php @@ -64,6 +64,12 @@ 'BH_THIS_USER' => 'Ban Hammer this user', + 'BH_ALREADY_RESTRICTED' => 'This user already has an active restriction', + 'BH_RESTRICT_FOR' => 'Restrict this user for %s', // %s will be a duration + 'BH_RESTRICT_PERM' => 'Restrict this user permanently', + 'BH_RESTRICT_THIS_USER' => 'Restrict this user', + 'BH_SURE_RESTRICT' => 'Are you sure you want to restrict %s?', // %s will be a username + 'SFS_REPORT' => 'Report this user to Stop Forum Spam', 'SURE_BAN' => 'Are you sure you want to ban %s?', // %s will be a username. diff --git a/language/en/banhammer_acp.php b/language/en/banhammer_acp.php index 54b10df..3ff00cc 100644 --- a/language/en/banhammer_acp.php +++ b/language/en/banhammer_acp.php @@ -48,6 +48,8 @@ 'ACP_MOVE_GROUP' => 'Move to group', 'ACP_MOVE_GROUP_EXPLAIN' => 'Name of the group to which banned users should be moved. This will also be their default group.
If nothing but “No group specified.” is in the drop down then you have not set up any groups.', + 'ACP_RESTRICT_GROUP' => 'Restrict to group', + 'ACP_RESTRICT_GROUP_EXPLAIN' => 'Name of the group moderators can move a user into instead of banning them, for a set amount of time. This will also be their default group while restricted; their original group is restored automatically once the restriction expires.
If nothing but “No group specified.” is in the drop down then you have not set up any groups, and the restrict option will not appear on member profiles.', 'BAN_LENGTH_EXPLAIN' => 'If either of the ban options is set then the user will be banned for the amount of time as set here. This is also able to be set when ban hammering the user.', 'SFS_ALLOW_HTTP' => 'Allow HTTP for Stop Forum Spam reports', 'SFS_ALLOW_HTTP_EXPLAIN' => 'Reports are sent over HTTPS by default. Only enable this if your server cannot make outbound HTTPS requests - your API key and the reported user\'s username, IP address, and email are sent in clear text over HTTP.', diff --git a/migrations/restrict_group.php b/migrations/restrict_group.php new file mode 100644 index 0000000..a65ac1b --- /dev/null +++ b/migrations/restrict_group.php @@ -0,0 +1,62 @@ +db_tools->sql_table_exists($this->table_prefix . 'banhammer_restrict'); + } + + static public function depends_on() + { + return array('\phpbbmodders\banhammer\migrations\v104_data'); + } + + public function update_schema() + { + return array( + 'add_tables' => array( + $this->table_prefix . 'banhammer_restrict' => array( + 'COLUMNS' => array( + 'restrict_id' => array('UINT', null, 'auto_increment'), + 'user_id' => array('UINT', 0), + 'original_group_id' => array('UINT', 0), + 'restrict_until' => array('TIMESTAMP', 0), + ), + 'PRIMARY_KEY' => 'restrict_id', + 'KEYS' => array( + 'user_id' => array('INDEX', 'user_id'), + 'restrict_until' => array('INDEX', 'restrict_until'), + ), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_tables' => array( + $this->table_prefix . 'banhammer_restrict', + ), + ); + } + + public function update_data() + { + return array( + array('config.add', array('bh_restrict_group_id', 0)), + array('config.add', array('bh_restrict_last_run', 0)), + ); + } +} diff --git a/styles/prosilver/template/event/memberlist_view_content_prepend.html b/styles/prosilver/template/event/memberlist_view_content_prepend.html index c0a41e6..8074793 100644 --- a/styles/prosilver/template/event/memberlist_view_content_prepend.html +++ b/styles/prosilver/template/event/memberlist_view_content_prepend.html @@ -101,3 +101,32 @@ + + +
+
+ {RESTRICT_MESSAGE} +
+
+ + +
+

{L_BH_RESTRICT_THIS_USER}

+ +
+ diff --git a/styles/prosilver/theme/banhammer.js b/styles/prosilver/theme/banhammer.js index cdfa8e4..84865eb 100644 --- a/styles/prosilver/theme/banhammer.js +++ b/styles/prosilver/theme/banhammer.js @@ -1,8 +1,10 @@ (function($) { // Avoid conflicts with other libraries 'use strict'; + // Toggle whichever panel belongs to the clicked trigger, not a fixed id - + // there can be more than one of these on a page (Ban Hammer, Restrict). $('.bh-click').click(function() { - $('#bh-options').toggle('slow'); + $(this).next('.inner').toggle('slow'); }); $('.bh_hover').hover(function() {