diff --git a/phpBB/includes/acp/acp_board.php b/phpBB/includes/acp/acp_board.php index beccef809d..df7c8bf657 100644 --- a/phpBB/includes/acp/acp_board.php +++ b/phpBB/includes/acp/acp_board.php @@ -79,6 +79,7 @@ class acp_board 'board_index_text' => array('lang' => 'BOARD_INDEX_TEXT', 'validate' => 'string', 'type' => 'text:40:255', 'explain' => true), 'board_disable' => array('lang' => 'DISABLE_BOARD', 'validate' => 'bool', 'type' => 'custom', 'method' => 'board_disable', 'explain' => true), 'board_disable_msg' => false, + 'board_disable_access' => array('lang' => 'DISABLE_BOARD_ACCESS', 'validate' => 'int', 'type' => 'select', 'method' => 'board_disable_access', 'explain' => true), 'default_lang' => array('lang' => 'DEFAULT_LANGUAGE', 'validate' => 'lang', 'type' => 'select', 'method' => 'language_select', 'params' => array('{CONFIG_VALUE}'), 'explain' => false), 'default_dateformat' => array('lang' => 'DEFAULT_DATE_FORMAT', 'validate' => 'string', 'type' => 'custom', 'method' => 'dateformat_select', 'explain' => true), 'board_timezone' => array('lang' => 'SYSTEM_TIMEZONE', 'validate' => 'timezone', 'type' => 'custom', 'method' => 'timezone_select', 'explain' => true), @@ -1047,6 +1048,34 @@ class acp_board return h_radio('config[board_disable]', $radio_ary, $value) . '
'; } + /** + * Board disable access for which group: admins: 0; plus global moderators: 1 and plus all moderators: 2 + * + * @param int $value Value from config + * + * @return array Options array for select + */ + public function board_disable_access(int $value) : array + { + return [ + [ + 'value' => 0, + 'selected' => $value == 0, + 'label' => $this->language->lang('DISABLE_BOARD_ACCESS_ADMIN'), + ], + [ + 'value' => 1, + 'selected' => $value == 1, + 'label' => $this->language->lang('DISABLE_BOARD_ACCESS_ADMIN_GLOB_MODS'), + ], + [ + 'value' => 2, + 'selected' => $value == 2, + 'label' => $this->language->lang('DISABLE_BOARD_ACCESS_ADMIN_ALL_MODS'), + ], + ]; + } + /** * Global quick reply enable/disable setting and button to enable in all forums */ diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 4f9e8b1d23..844ba402b3 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -57,6 +57,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_contact', 'c INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_contact_name', ''); INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_disable', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_disable_msg', ''); +INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_disable_access', '2'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_email', 'address@yourdomain.tld'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_email_form', '0'); INSERT INTO phpbb_config (config_name, config_value) VALUES ('board_email_sig', '{L_CONFIG_BOARD_EMAIL_SIG}'); diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php index b6da051249..ec2d0236ec 100644 --- a/phpBB/language/en/acp/board.php +++ b/phpBB/language/en/acp/board.php @@ -50,6 +50,11 @@ $lang = array_merge($lang, array( 'DEFAULT_STYLE_EXPLAIN' => 'The default style for new users.', 'DISABLE_BOARD' => 'Disable board', 'DISABLE_BOARD_EXPLAIN' => 'This will make the board unavailable to users who are neither administrators nor moderators. You can also enter a short (255 character) message to display if you wish.', + 'DISABLE_BOARD_ACCESS' => 'Limit access to disabled board', + 'DISABLE_BOARD_ACCESS_EXPLAIN' => 'This setting limits who can access a disabled board.', + 'DISABLE_BOARD_ACCESS_ADMIN' => 'Only administrators', + 'DISABLE_BOARD_ACCESS_ADMIN_GLOB_MODS' => 'Only administrators and global moderators', + 'DISABLE_BOARD_ACCESS_ADMIN_ALL_MODS' => 'Only administrators and all moderators', 'DISPLAY_LAST_SUBJECT' => 'Display subject of last added post on forum list', 'DISPLAY_LAST_SUBJECT_EXPLAIN' => 'The subject of the last added post will be displayed in the forum list with a hyperlink to the post. Subjects from password protected forums and forums in which user doesn’t have read access are not shown.', 'DISPLAY_UNAPPROVED_POSTS' => 'Display unapproved posts to the author', diff --git a/phpBB/phpbb/db/migration/data/v400/add_disable_board_access_config.php b/phpBB/phpbb/db/migration/data/v400/add_disable_board_access_config.php new file mode 100644 index 0000000000..e534cbdf67 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v400/add_disable_board_access_config.php @@ -0,0 +1,31 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v400; + +class add_disable_board_access_config extends \phpbb\db\migration\migration +{ + public static function depends_on() + { + return [ + '\phpbb\db\migration\data\v400\dev', + ]; + } + + public function update_data() + { + return [ + ['config.add', ['board_disable_access', '2']], + ]; + } +} diff --git a/phpBB/phpbb/user.php b/phpBB/phpbb/user.php index 2a3e3995c0..20a54fe9e4 100644 --- a/phpBB/phpbb/user.php +++ b/phpBB/phpbb/user.php @@ -372,7 +372,27 @@ class user extends \phpbb\session } // Is board disabled and user not an admin or moderator? - if ($config['board_disable'] && !defined('IN_INSTALL') && !defined('IN_LOGIN') && !defined('SKIP_CHECK_DISABLED') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) + // Check acp setting who has access: only admins "case: 0", plus global moderators "case: 1" and plus moderators "case: 2" + $board_disable_access = (int) $config['board_disable_access']; + + switch ($board_disable_access) + { + case 0: + $access_disabled_board = $auth->acl_gets('a_'); + break; + + case 1: + $access_disabled_board = $auth->acl_gets('a_', 'm_'); + break; + + case 2: + default: + $access_disabled_board = $auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'); + break; + + } + + if ($config['board_disable'] && !defined('IN_INSTALL') && !defined('IN_LOGIN') && !defined('SKIP_CHECK_DISABLED') && !$access_disabled_board) { if ($this->data['is_bot']) { diff --git a/tests/functional/browse_disabled_test.php b/tests/functional/browse_disabled_test.php new file mode 100644 index 0000000000..528f95fc25 --- /dev/null +++ b/tests/functional/browse_disabled_test.php @@ -0,0 +1,164 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +/** +* @group functional +*/ +class phpbb_functional_browse_disabled_test extends phpbb_functional_test_case +{ + public function setUp(): void + { + parent::setUp(); + + $this->login(); + $this->admin_login(); + + // Disable board + $crawler = self::request('GET', 'adm/index.php?i=acp_board&mode=settings&sid=' . $this->sid); + $form = $crawler->selectButton('Submit')->form(); + $form->setValues(['config[board_disable]' => 1]); + + $crawler = self::submit($form); + $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('div[class="successbox"] > p')->text()); + + $this->logout(); + } + + public function tearDown(): void + { + $this->login(); + $this->admin_login(); + + // Disable board + $crawler = self::request('GET', 'adm/index.php?i=acp_board&mode=settings&sid=' . $this->sid); + $form = $crawler->selectButton('Submit')->form(); + $form->setValues(['config[board_disable]' => 0]); + + $crawler = self::submit($form); + $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('div[class="successbox"] > p')->text()); + + $this->logout(); + + parent::tearDown(); + } + + public function test_disabled_index_admin() + { + $this->login(); + $this->admin_login(); + + // Board should be fully visible for all variations for admins + for ($i = 0; $i <= 2; $i++) + { + $crawler = self::request('GET', 'adm/index.php?i=acp_board&mode=settings&sid=' . $this->sid); + $form = $crawler->selectButton('Submit')->form(); + $form->setValues(['config[board_disable_access]' => $i]); + + $crawler = self::submit($form); + $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('div[class="successbox"] > p')->text()); + + $crawler = self::request('GET', 'index.php'); + $this->assertGreaterThan(0, $crawler->filter('.topiclist')->count()); + $this->assertContainsLang('BOARD_DISABLED', $crawler->filter('div[class="rules"]')->text()); + } + + $this->logout(); + } + + public function test_disabled_index_global_moderator() + { + $this->create_user('moderator-disabled-index'); + $this->add_user_group('GLOBAL_MODERATORS', ['moderator-disabled-index']); + + // Board should be fully visible for options 1 & 2 + for ($i = 0; $i <= 2; $i++) + { + $this->login(); + $this->admin_login(); + + $crawler = self::request('GET', 'adm/index.php?i=acp_board&mode=settings&sid=' . $this->sid); + $form = $crawler->selectButton('Submit')->form(); + $form->setValues(['config[board_disable_access]' => $i]); + + $crawler = self::submit($form); + $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('div[class="successbox"] > p')->text()); + + $this->logout(); + + $this->login('moderator-disabled-index'); + + $crawler = self::request('GET', 'index.php'); + + if ($i == 0) + { + $this->assertEquals(0, $crawler->filter('.topiclist')->count(), 'Board should not be visible for option ' . $i); + $this->assertContainsLang('BOARD_DISABLE', $crawler->filter('div#message')->text()); + } + else + { + $this->assertGreaterThan(0, $crawler->filter('.topiclist')->count(), 'Board should be visible for option ' . $i); + $this->assertContainsLang('BOARD_DISABLED', $crawler->filter('div[class="rules"]')->text()); + } + $this->logout(); + } + } + + public function test_disabled_index_local_moderator() + { + $user_id = $this->create_user('moduser-disabled-index'); + + // Set m_delete to yes for user --> user has moderator permission + $this->add_lang('acp/permissions'); + $this->login(); + $this->admin_login(); + $crawler = self::request('GET', "adm/index.php?i=acp_permissions&icat=16&mode=setting_user_local&user_id[0]=$user_id&forum_id[0]=2&type=m_&sid={$this->sid}"); + var_export($crawler->text()); + $form = $crawler->selectButton($this->lang('APPLY_PERMISSIONS'))->form(); + $data = array("setting[$user_id][2][m_edit]" => ACL_YES); + $form->setValues($data); + self::submit($form); + $this->logout(); + + // Board should be fully visible for option 2 only + for ($i = 0; $i <= 2; $i++) + { + $this->login(); + $this->admin_login(); + + $crawler = self::request('GET', 'adm/index.php?i=acp_board&mode=settings&sid=' . $this->sid); + $form = $crawler->selectButton('Submit')->form(); + $form->setValues(['config[board_disable_access]' => $i]); + + $crawler = self::submit($form); + $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('div[class="successbox"] > p')->text()); + + $this->logout(); + + $this->login('moduser-disabled-index'); + + $crawler = self::request('GET', 'index.php'); + + if ($i < 2) + { + $this->assertEquals(0, $crawler->filter('.topiclist')->count(), 'Board should not be visible for option ' . $i); + $this->assertContainsLang('BOARD_DISABLE', $crawler->filter('div#message')->text()); + } + else + { + $this->assertGreaterThan(0, $crawler->filter('.topiclist')->count(), 'Board should be visible for option ' . $i); + $this->assertContainsLang('BOARD_DISABLED', $crawler->filter('div[class="rules"]')->text()); + } + $this->logout(); + } + } +} diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index 18fe2bbf38..c0dd68ab14 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -851,8 +851,9 @@ class phpbb_functional_test_case extends phpbb_test_case { $this->add_lang('ucp'); - $crawler = self::request('GET', 'ucp.php'); - $this->assertStringContainsString($this->lang('LOGIN_EXPLAIN_UCP'), $crawler->filter('html')->text()); + $crawler = self::request('GET', 'ucp.php?mode=login'); + $button = $crawler->selectButton($this->lang('LOGIN')); + $this->assertGreaterThan(0, $button->count(), 'No login button found'); $form = $crawler->selectButton($this->lang('LOGIN'))->form(); if ($autologin)