From 3b248c5a8bfc5d933ca71fcdd409234f5b47e709 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sat, 22 Aug 2026 14:35:07 -0700 Subject: [PATCH 1/6] First rule should not auto-publish --- config/services.yml | 1 + controller/admin_controller.php | 8 +-- operators/rule.php | 11 +++- operators/rule_interface.php | 1 + operators/ruleset.php | 20 ++++++ operators/ruleset_interface.php | 9 +++ tests/controller/admin_controller_test.php | 2 +- tests/migrations/v310_upgrade_test.php | 66 +++++++++++++++++++ .../operators/rule_operator_add_rule_test.php | 4 ++ tests/operators/rule_operator_base.php | 6 +- tests/operators/ruleset_operator_test.php | 17 +++++ 11 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 tests/migrations/v310_upgrade_test.php diff --git a/config/services.yml b/config/services.yml index 334dd14f..1163376b 100644 --- a/config/services.yml +++ b/config/services.yml @@ -58,6 +58,7 @@ services: arguments: - '@service_container' - '@phpbb.boardrules.nestedset_rules' + - '@phpbb.boardrules.ruleset_operator' - '@phpbb.boardrules.table_lock' phpbb.boardrules.ruleset_operator: diff --git a/controller/admin_controller.php b/controller/admin_controller.php index f62c447a..c91d9638 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -712,13 +712,13 @@ protected function add_edit_rule_data($entity, $data) { $this->rule_operator->add_rule($entity, $data['rule_language'], $data['rule_parent_id']); } - catch (\phpbb\boardrules\exception\out_of_bounds $e) + catch (\InvalidArgumentException|\RuntimeException $e) { - trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } - catch (\RuntimeException $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); } // Show user confirmation of the added rule and provide link back to the previous page diff --git a/operators/rule.php b/operators/rule.php index c7c7fb25..74cc4888 100644 --- a/operators/rule.php +++ b/operators/rule.php @@ -27,6 +27,9 @@ class rule implements rule_interface */ protected $nestedset_rules; + /** @var \phpbb\boardrules\operators\ruleset_interface */ + protected $ruleset_operator; + /** @var \phpbb\lock\db */ protected $lock; @@ -35,13 +38,15 @@ class rule implements rule_interface * * @param ContainerInterface $container Service container interface * @param \phpbb\boardrules\operators\nestedset_rules $nestedset_rules Nestedset object for tree functionality + * @param \phpbb\boardrules\operators\ruleset_interface $ruleset_operator Ruleset operator object * @param \phpbb\lock\db $lock Shared Board Rules tree lock * @access public */ - public function __construct(ContainerInterface $container, \phpbb\boardrules\operators\nestedset_rules $nestedset_rules, \phpbb\lock\db $lock) + public function __construct(ContainerInterface $container, \phpbb\boardrules\operators\nestedset_rules $nestedset_rules, \phpbb\boardrules\operators\ruleset_interface $ruleset_operator, \phpbb\lock\db $lock) { $this->container = $container; $this->nestedset_rules = $nestedset_rules; + $this->ruleset_operator = $ruleset_operator; $this->lock = $lock; } @@ -82,6 +87,7 @@ public function get_rules($language, $parent_id = 0) * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public + * @throws \InvalidArgumentException If the language is not installed * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ @@ -94,6 +100,9 @@ public function add_rule($entity, $language, $parent_id = 0) try { + // An empty ruleset must enter draft before its first rule is visible. + $this->ruleset_operator->draft_if_empty($language); + // Insert the rule data to the database for the given language selection $entity->insert($language); diff --git a/operators/rule_interface.php b/operators/rule_interface.php index c8fd252e..88092bd8 100644 --- a/operators/rule_interface.php +++ b/operators/rule_interface.php @@ -36,6 +36,7 @@ public function get_rules($language, $parent_id = 0); * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public + * @throws \InvalidArgumentException If the language is not installed * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds If the entity was already inserted */ diff --git a/operators/ruleset.php b/operators/ruleset.php index 3ff02fe3..384742ff 100644 --- a/operators/ruleset.php +++ b/operators/ruleset.php @@ -194,6 +194,26 @@ public function is_published($language) return $published === false ? true : (bool) $published; } + /** + * {@inheritdoc} + */ + public function draft_if_empty($language) + { + if (!$this->language_exists($language)) + { + throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); + } + + if ($this->get_rule_count($language) > 0) + { + return false; + } + + $this->save_published_state($language, false); + + return true; + } + /** * {@inheritdoc} */ diff --git a/operators/ruleset_interface.php b/operators/ruleset_interface.php index cde55980..a0278d79 100644 --- a/operators/ruleset_interface.php +++ b/operators/ruleset_interface.php @@ -41,6 +41,15 @@ public function copy($source_language, $target_language); */ public function is_published($language); + /** + * Ensure an empty ruleset starts in draft before its first rule is added. + * + * @param string $language + * @return bool True when draft state was saved + * @throws \InvalidArgumentException If the language is not installed + */ + public function draft_if_empty($language); + /** * Get the custom introduction for a language ruleset. * diff --git a/tests/controller/admin_controller_test.php b/tests/controller/admin_controller_test.php index 55a092ea..66579a2b 100644 --- a/tests/controller/admin_controller_test.php +++ b/tests/controller/admin_controller_test.php @@ -124,13 +124,13 @@ protected function setUp(): void $lock = new \phpbb\lock\db('boardrules.table_lock.boardrules_table', $this->config, $this->db); $nestedset = new \phpbb\boardrules\operators\nestedset_rules($this->db, $lock, 'phpbb_boardrules'); - $this->rule_operator = new \phpbb\boardrules\operators\rule($container, $nestedset, $lock); $this->ruleset_operator = new \phpbb\boardrules\operators\ruleset( $this->db, $lock, 'phpbb_boardrules', 'phpbb_boardrules_rulesets' ); + $this->rule_operator = new \phpbb\boardrules\operators\rule($container, $nestedset, $this->ruleset_operator, $lock); $this->request = $this->createMock(\phpbb\request\request::class); $this->request->method('variable') diff --git a/tests/migrations/v310_upgrade_test.php b/tests/migrations/v310_upgrade_test.php new file mode 100644 index 00000000..efabf37a --- /dev/null +++ b/tests/migrations/v310_upgrade_test.php @@ -0,0 +1,66 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +*/ + +namespace phpbb\boardrules\tests\migrations; + +class v310_upgrade_test extends \phpbb_database_test_case +{ + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\boardrules\operators\ruleset */ + protected $operator; + + protected static function setup_extensions() + { + return array('phpbb/boardrules'); + } + + public function getDataSet() + { + // Models a 3.0.1 board after schema migration: existing rules have no + // publication row because that table did not exist in the old release. + return $this->createXMLDataSet(__DIR__ . '/../operators/fixtures/ruleset.xml'); + } + + protected function setUp(): void + { + parent::setUp(); + $this->db = $this->new_dbal(); + $config = new \phpbb\config\config(array('boardrules.table_lock.boardrules_table' => 0)); + $lock = new \phpbb\lock\db('boardrules.table_lock.boardrules_table', $config, $this->db); + $this->operator = new \phpbb\boardrules\operators\ruleset( + $this->db, + $lock, + 'phpbb_boardrules', + 'phpbb_boardrules_rulesets' + ); + } + + public function test_legacy_rules_remain_published_and_new_ruleset_starts_draft(): void + { + self::assertTrue($this->operator->is_published('en')); + + $result = $this->db->sql_query("SELECT COUNT(*) AS ruleset_count + FROM phpbb_boardrules_rulesets + WHERE language_iso = 'en'"); + self::assertSame(0, (int) $this->db->sql_fetchfield('ruleset_count')); + $this->db->sql_freeresult($result); + + self::assertTrue($this->operator->draft_if_empty('fr')); + self::assertFalse($this->operator->is_published('fr')); + + $result = $this->db->sql_query("SELECT rules_published + FROM phpbb_boardrules_rulesets + WHERE language_iso = 'fr'"); + self::assertSame(0, (int) $this->db->sql_fetchfield('rules_published')); + $this->db->sql_freeresult($result); + } +} diff --git a/tests/operators/rule_operator_add_rule_test.php b/tests/operators/rule_operator_add_rule_test.php index 3eee4c71..59d6b87a 100644 --- a/tests/operators/rule_operator_add_rule_test.php +++ b/tests/operators/rule_operator_add_rule_test.php @@ -41,6 +41,9 @@ public function test_add_rule() $test_id = 6; $language = 'en'; $parent_id = 1; // using 1 allows us to test the nestability + $this->ruleset_operator->expects(self::once()) + ->method('draft_if_empty') + ->with($language); // Setup the operator class $operator = $this->get_rule_operator(); @@ -73,6 +76,7 @@ public function test_add_rule_rejects_when_nestedset_lock_is_held() $competing_lock = new \phpbb\lock\db('nestedset_rules_lock', $this->config, $this->db); self::assertTrue($competing_lock->acquire()); + $this->ruleset_operator->expects(self::never())->method('draft_if_empty'); try { diff --git a/tests/operators/rule_operator_base.php b/tests/operators/rule_operator_base.php index 989dc662..ae25ab51 100644 --- a/tests/operators/rule_operator_base.php +++ b/tests/operators/rule_operator_base.php @@ -37,6 +37,9 @@ protected static function setup_extensions() /** @var \phpbb\boardrules\operators\nestedset_rules */ protected $nestedset_rules; + /** @var \PHPUnit\Framework\MockObject\MockObject|\phpbb\boardrules\operators\ruleset_interface */ + protected $ruleset_operator; + /** @var \phpbb\lock\db */ protected $lock; @@ -68,6 +71,7 @@ protected function setUp(): void $this->lock = new \phpbb\lock\db('nestedset_rules_lock', $this->config, $this->db); $this->nestedset_rules = new \phpbb\boardrules\operators\nestedset_rules($this->db, $this->lock, 'phpbb_boardrules'); + $this->ruleset_operator = $this->createMock(\phpbb\boardrules\operators\ruleset_interface::class); } /** @@ -77,6 +81,6 @@ protected function setUp(): void */ protected function get_rule_operator() { - return new \phpbb\boardrules\operators\rule($this->container, $this->nestedset_rules, $this->lock); + return new \phpbb\boardrules\operators\rule($this->container, $this->nestedset_rules, $this->ruleset_operator, $this->lock); } } diff --git a/tests/operators/ruleset_operator_test.php b/tests/operators/ruleset_operator_test.php index 4c32cd51..4ea81842 100644 --- a/tests/operators/ruleset_operator_test.php +++ b/tests/operators/ruleset_operator_test.php @@ -92,6 +92,23 @@ public function test_copy_clones_complete_tree_as_draft(): void self::assertCount(count($bounds), array_unique($bounds)); } + public function test_empty_ruleset_is_forced_to_draft_before_first_rule(): void + { + self::assertTrue($this->operator->draft_if_empty('fr')); + self::assertFalse($this->operator->is_published('fr')); + + $this->operator->set_published('en', true); + self::assertFalse($this->operator->draft_if_empty('en')); + self::assertTrue($this->operator->is_published('en')); + } + + public function test_unknown_ruleset_cannot_be_prepared_for_first_rule(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); + $this->operator->draft_if_empty('xx'); + } + public function test_copy_appends_to_non_empty_target(): void { $this->operator->copy('en', 'fr'); From 128ef34655efa58891c4c1ebefa8b563189f712a Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sat, 22 Aug 2026 21:44:32 -0700 Subject: [PATCH 2/6] Exception policy now consistent --- controller/admin_controller.php | 52 ++++++++++++------- exception/base.php | 21 ++------ operators/rule.php | 11 ++-- operators/rule_interface.php | 9 ++-- operators/ruleset.php | 14 ++--- operators/ruleset_interface.php | 9 ++-- tests/controller/admin_controller_test.php | 13 ++--- .../operators/rule_operator_add_rule_test.php | 2 +- tests/operators/ruleset_operator_test.php | 14 ++--- tests/system/exception_test.php | 4 +- 10 files changed, 74 insertions(+), 75 deletions(-) diff --git a/controller/admin_controller.php b/controller/admin_controller.php index c91d9638..d97aaf7d 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -364,9 +364,9 @@ public function save_ruleset_intro($language) { $this->ruleset_operator->set_intro_text($language, $intro_text); } - catch (\InvalidArgumentException $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } $this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_BOARDRULES_INTRO_LOG', false, array($language)); @@ -406,9 +406,9 @@ public function copy_ruleset($target_language, $return_to = '') { $copy_result = $this->ruleset_operator->copy($source_language, $target_language); } - catch (\Exception $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($return_url), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($return_url), E_USER_WARNING); } $this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_BOARDRULES_COPY_LOG', false, array($source_language, $target_language, $copy_result['rule_count'])); @@ -466,9 +466,9 @@ public function set_ruleset_published($language, $published, $return_to = '') { $this->ruleset_operator->set_published($language, $published); } - catch (\InvalidArgumentException $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($return_url), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($return_url), E_USER_WARNING); } $log_key = $published ? 'ACP_BOARDRULES_PUBLISH_LOG' : 'ACP_BOARDRULES_DRAFT_LOG'; @@ -684,9 +684,9 @@ protected function add_edit_rule_data($entity, $data) { $entity->save(); } - catch (\phpbb\boardrules\exception\out_of_bounds $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } // Change rule parent @@ -696,9 +696,9 @@ protected function add_edit_rule_data($entity, $data) { $this->rule_operator->change_parent($entity->get_id(), $data['rule_parent_id']); } - catch (\Exception $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } } @@ -712,13 +712,9 @@ protected function add_edit_rule_data($entity, $data) { $this->rule_operator->add_rule($entity, $data['rule_language'], $data['rule_parent_id']); } - catch (\InvalidArgumentException|\RuntimeException $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); - } - catch (\phpbb\boardrules\exception\out_of_bounds $e) - { - trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } // Show user confirmation of the added rule and provide link back to the previous page @@ -785,9 +781,9 @@ public function delete_rule($rule_id) { $this->rule_operator->delete_rule($rule_id); } - catch (\Exception $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } // Show user confirmation of the deleted rule and provide link back to the previous page @@ -833,9 +829,9 @@ public function move_rule($rule_id, $direction, $amount = 1) { $this->rule_operator->move($rule_id, $direction, $amount); } - catch (\Exception $e) + catch (\phpbb\exception\runtime_exception $e) { - trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); } // Send a JSON response if an AJAX request was used @@ -1004,4 +1000,20 @@ protected function build_parent_select_menu($entity, $parent_id = 0) )); } } + + /** + * Translate an expected Board Rules or phpBB runtime exception. + * + * @param \phpbb\exception\runtime_exception $exception + * @return string + */ + protected function get_exception_message(\phpbb\exception\runtime_exception $exception) + { + if ($exception instanceof \phpbb\boardrules\exception\base) + { + return $exception->get_message($this->lang); + } + + return $this->lang->lang_array($exception->getMessage(), $exception->get_parameters()); + } } diff --git a/exception/base.php b/exception/base.php index 40afea89..4cb10f1d 100644 --- a/exception/base.php +++ b/exception/base.php @@ -13,7 +13,7 @@ /** * Base exception */ -class base extends \Exception +class base extends \phpbb\exception\runtime_exception { /** * Null if the message is a string, array if the message was submitted as an array @@ -21,8 +21,6 @@ class base extends \Exception */ protected $message_full; - protected $previous; - /** * Constructor * @@ -35,22 +33,11 @@ class base extends \Exception */ public function __construct($message = null, $code = 0, \Exception $previous = null) { - parent::__construct(); - - $this->message = $message; - - if (is_array($message)) - { - $this->message = (string) $message[0]; - } - - // We're slightly changing the way exceptions work - // Tools, such as xDebug, expect the message to be a string, so to prevent errors - // with those tools, we store our full message in message_full and only a string in message $this->message_full = $message; + $message_parts = is_array($message) ? $message : array($message); + $message_key = (string) array_shift($message_parts); - $this->code = $code; - $this->previous = $previous; + parent::__construct($message_key, $message_parts, $previous, $code); } /** diff --git a/operators/rule.php b/operators/rule.php index 74cc4888..e21f0564 100644 --- a/operators/rule.php +++ b/operators/rule.php @@ -87,15 +87,14 @@ public function get_rules($language, $parent_id = 0) * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public - * @throws \InvalidArgumentException If the language is not installed - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the language is invalid or the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function add_rule($entity, $language, $parent_id = 0) { if (!$this->lock->acquire()) { - throw new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + throw new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); } try @@ -133,7 +132,7 @@ public function add_rule($entity, $language, $parent_id = 0) * @param int $rule_id The rule identifier to delete * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function delete_rule($rule_id) @@ -159,7 +158,7 @@ public function delete_rule($rule_id) * @param int $amount The number of places to move the rule * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function move($rule_id, $direction = 'up', $amount = 1) @@ -185,7 +184,7 @@ public function move($rule_id, $direction = 'up', $amount = 1) * @param int $new_parent_id The new rule parent identifier * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function change_parent($rule_id, $new_parent_id) diff --git a/operators/rule_interface.php b/operators/rule_interface.php index 88092bd8..866f13f7 100644 --- a/operators/rule_interface.php +++ b/operators/rule_interface.php @@ -36,8 +36,7 @@ public function get_rules($language, $parent_id = 0); * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public - * @throws \InvalidArgumentException If the language is not installed - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the language is invalid or the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds If the entity was already inserted */ public function add_rule($entity, $language, $parent_id = 0); @@ -48,7 +47,7 @@ public function add_rule($entity, $language, $parent_id = 0); * @param int $rule_id The rule identifier to delete * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function delete_rule($rule_id); @@ -61,7 +60,7 @@ public function delete_rule($rule_id); * @param int $amount The number of places to move the rule * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function move($rule_id, $direction, $amount = 1); @@ -73,7 +72,7 @@ public function move($rule_id, $direction, $amount = 1); * @param int $new_parent_id The new rule parent identifier * @return void * @access public - * @throws \RuntimeException If the nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function change_parent($rule_id, $new_parent_id); diff --git a/operators/ruleset.php b/operators/ruleset.php index 384742ff..a2b65e67 100644 --- a/operators/ruleset.php +++ b/operators/ruleset.php @@ -95,12 +95,12 @@ public function copy($source_language, $target_language) if ($source_language === $target_language || !$this->language_exists($source_language) || !$this->language_exists($target_language)) { - throw new \InvalidArgumentException('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); } if (!$this->lock->acquire()) { - throw new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + throw new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); } $transaction_started = false; @@ -112,7 +112,7 @@ public function copy($source_language, $target_language) $source_rules = $this->get_rules_data($source_language); if (empty($source_rules)) { - throw new \InvalidArgumentException('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); } $id_map = array(); @@ -201,7 +201,7 @@ public function draft_if_empty($language) { if (!$this->language_exists($language)) { - throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); } if ($this->get_rule_count($language) > 0) @@ -236,7 +236,7 @@ public function set_intro_text($language, $intro_text) { if (!$this->language_exists($language)) { - throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); } $this->save_ruleset_value($language, 'rules_intro_text', utf8_encode_ncr((string) $intro_text)); @@ -249,12 +249,12 @@ public function set_published($language, $published) { if (!$this->language_exists($language)) { - throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); } if ($this->get_rule_count($language) === 0) { - throw new \InvalidArgumentException('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); + throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); } $this->save_published_state($language, (bool) $published); diff --git a/operators/ruleset_interface.php b/operators/ruleset_interface.php index a0278d79..2a9607cc 100644 --- a/operators/ruleset_interface.php +++ b/operators/ruleset_interface.php @@ -25,8 +25,7 @@ public function get_languages(); * @param string $source_language * @param string $target_language * @return array Copy result containing rule and renamed-anchor counts - * @throws \InvalidArgumentException If either language is invalid or source ruleset is empty - * @throws \RuntimeException If nested-set lock cannot be acquired + * @throws \phpbb\exception\runtime_exception If a language is invalid, source ruleset is empty, or nested-set lock cannot be acquired * @throws \Exception If ruleset persistence fails */ public function copy($source_language, $target_language); @@ -46,7 +45,7 @@ public function is_published($language); * * @param string $language * @return bool True when draft state was saved - * @throws \InvalidArgumentException If the language is not installed + * @throws \phpbb\exception\runtime_exception If the language is not installed */ public function draft_if_empty($language); @@ -66,7 +65,7 @@ public function get_intro_text($language); * @param string $language * @param string $intro_text * @return void - * @throws \InvalidArgumentException If the language is not installed + * @throws \phpbb\exception\runtime_exception If the language is not installed */ public function set_intro_text($language, $intro_text); @@ -76,7 +75,7 @@ public function set_intro_text($language, $intro_text); * @param string $language * @param bool $published * @return void - * @throws \InvalidArgumentException If the language is not installed or its ruleset is empty + * @throws \phpbb\exception\runtime_exception If the language is not installed or its ruleset is empty */ public function set_published($language, $published); } diff --git a/tests/controller/admin_controller_test.php b/tests/controller/admin_controller_test.php index 66579a2b..2ebb4e0e 100644 --- a/tests/controller/admin_controller_test.php +++ b/tests/controller/admin_controller_test.php @@ -420,7 +420,7 @@ public function test_copy_ruleset_translates_operator_error(): void $this->controller->copy_ruleset('fr'); } - public function test_copy_ruleset_handles_general_operator_exception(): void + public function test_copy_ruleset_does_not_swallow_unexpected_operator_exception(): void { $ruleset_operator = $this->getMockBuilder(\phpbb\boardrules\operators\ruleset::class) ->disableOriginalConstructor() @@ -437,7 +437,8 @@ public function test_copy_ruleset_handles_general_operator_exception(): void $this->replace_controller_service('ruleset_operator', $ruleset_operator); $this->post['submit'] = true; $this->variables['source_language'] = 'en'; - $this->setExpectedTriggerError(E_USER_WARNING, 'ACP_BOARDRULES_COPY_FAILED'); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('ACP_BOARDRULES_COPY_FAILED'); $this->controller->copy_ruleset('fr'); } @@ -585,7 +586,7 @@ public function test_edit_rule_reports_parent_change_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('change_parent')->willThrowException(new \RuntimeException('PARENT_CHANGE_FAILED')); + $operator->method('change_parent')->willThrowException(new \phpbb\exception\runtime_exception('PARENT_CHANGE_FAILED')); $this->replace_controller_service('container', $this->entity_container($entity)); $this->replace_controller_service('rule_operator', $operator); $this->post['submit'] = true; @@ -618,7 +619,7 @@ public static function add_rule_operator_error_data(): array { return array( 'entity bounds failure' => array(new \phpbb\boardrules\exception\out_of_bounds('rule_id'), 'EXCEPTION_OUT_OF_BOUNDS'), - 'nested-set lock failure' => array(new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), + 'nested-set lock failure' => array(new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), ); } @@ -643,7 +644,7 @@ public function test_delete_rule_reports_operator_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('delete_rule')->willThrowException(new \RuntimeException('DELETE_FAILED')); + $operator->method('delete_rule')->willThrowException(new \phpbb\exception\runtime_exception('DELETE_FAILED')); $this->replace_controller_service('rule_operator', $operator); $this->setExpectedTriggerError(E_USER_WARNING, 'DELETE_FAILED'); @@ -680,7 +681,7 @@ public function test_move_rule_reports_operator_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('move')->willThrowException(new \RuntimeException('MOVE_FAILED')); + $operator->method('move')->willThrowException(new \phpbb\exception\runtime_exception('MOVE_FAILED')); $this->replace_controller_service('rule_operator', $operator); $this->setExpectedTriggerError(E_USER_WARNING, 'MOVE_FAILED'); diff --git a/tests/operators/rule_operator_add_rule_test.php b/tests/operators/rule_operator_add_rule_test.php index 59d6b87a..9d237116 100644 --- a/tests/operators/rule_operator_add_rule_test.php +++ b/tests/operators/rule_operator_add_rule_test.php @@ -83,7 +83,7 @@ public function test_add_rule_rejects_when_nestedset_lock_is_held() $this->get_rule_operator()->add_rule($entity, 'en'); self::fail('Rule insertion should not run while the nested-set lock is held.'); } - catch (\RuntimeException $e) + catch (\phpbb\exception\runtime_exception $e) { self::assertSame('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE', $e->getMessage()); } diff --git a/tests/operators/ruleset_operator_test.php b/tests/operators/ruleset_operator_test.php index 4ea81842..48c039d7 100644 --- a/tests/operators/ruleset_operator_test.php +++ b/tests/operators/ruleset_operator_test.php @@ -104,7 +104,7 @@ public function test_empty_ruleset_is_forced_to_draft_before_first_rule(): void public function test_unknown_ruleset_cannot_be_prepared_for_first_rule(): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->draft_if_empty('xx'); } @@ -143,7 +143,7 @@ public function test_copy_rejects_when_nestedset_lock_is_held(): void $this->operator->copy('en', 'fr'); self::fail('Copy should not run while the nested-set lock is held.'); } - catch (\RuntimeException $e) + catch (\phpbb\exception\runtime_exception $e) { self::assertSame('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE', $e->getMessage()); } @@ -164,7 +164,7 @@ public function test_copy_releases_nestedset_lock(): void public function test_copy_rejects_empty_source(): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); $this->operator->copy('de', 'fr'); } @@ -203,7 +203,7 @@ public function test_ruleset_intro_can_be_created_before_rules(): void public function test_unknown_ruleset_intro_language_is_rejected(): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->set_intro_text('xx', 'Unknown'); @@ -211,7 +211,7 @@ public function test_unknown_ruleset_intro_language_is_rejected(): void public function test_empty_ruleset_cannot_be_published(): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); $this->operator->set_published('fr', true); } @@ -221,7 +221,7 @@ public function test_empty_ruleset_cannot_be_published(): void */ public function test_copy_rejects_invalid_languages($source, $target): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); $this->operator->copy($source, $target); } @@ -238,7 +238,7 @@ public static function invalid_copy_language_data(): array public function test_unknown_ruleset_cannot_change_publication_state(): void { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(\phpbb\exception\runtime_exception::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->set_published('xx', true); } diff --git a/tests/system/exception_test.php b/tests/system/exception_test.php index 25b360d1..2fbdcf12 100644 --- a/tests/system/exception_test.php +++ b/tests/system/exception_test.php @@ -115,6 +115,8 @@ public static function exception_string_data(): array */ public function test_exception_string_representation($message, $expected): void { - self::assertSame($expected, (string) new \phpbb\boardrules\exception\base($message)); + $exception = new \phpbb\boardrules\exception\base($message); + self::assertInstanceOf(\phpbb\exception\runtime_exception::class, $exception); + self::assertSame($expected, (string) $exception); } } From d9838deca21773a47e6a41e1e9d53d2a304a1441 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 23 Aug 2026 07:02:17 -0700 Subject: [PATCH 3/6] Rollback exception redesign --- controller/admin_controller.php | 64 +++++++++---------- exception/base.php | 21 ++++-- operators/rule.php | 11 ++-- operators/rule_interface.php | 9 +-- operators/ruleset.php | 14 ++-- operators/ruleset_interface.php | 9 +-- tests/controller/admin_controller_test.php | 53 +++++++++++++-- .../operators/rule_operator_add_rule_test.php | 2 +- tests/operators/ruleset_operator_test.php | 14 ++-- tests/system/exception_test.php | 4 +- 10 files changed, 127 insertions(+), 74 deletions(-) diff --git a/controller/admin_controller.php b/controller/admin_controller.php index d97aaf7d..f5b40794 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -364,9 +364,9 @@ public function save_ruleset_intro($language) { $this->ruleset_operator->set_intro_text($language, $intro_text); } - catch (\phpbb\exception\runtime_exception $e) + catch (\InvalidArgumentException $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } $this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_BOARDRULES_INTRO_LOG', false, array($language)); @@ -406,9 +406,9 @@ public function copy_ruleset($target_language, $return_to = '') { $copy_result = $this->ruleset_operator->copy($source_language, $target_language); } - catch (\phpbb\exception\runtime_exception $e) + catch (\Exception $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($return_url), E_USER_WARNING); + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($return_url), E_USER_WARNING); } $this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_BOARDRULES_COPY_LOG', false, array($source_language, $target_language, $copy_result['rule_count'])); @@ -466,9 +466,9 @@ public function set_ruleset_published($language, $published, $return_to = '') { $this->ruleset_operator->set_published($language, $published); } - catch (\phpbb\exception\runtime_exception $e) + catch (\InvalidArgumentException $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($return_url), E_USER_WARNING); + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($return_url), E_USER_WARNING); } $log_key = $published ? 'ACP_BOARDRULES_PUBLISH_LOG' : 'ACP_BOARDRULES_DRAFT_LOG'; @@ -684,9 +684,9 @@ protected function add_edit_rule_data($entity, $data) { $entity->save(); } - catch (\phpbb\exception\runtime_exception $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); } // Change rule parent @@ -696,9 +696,13 @@ protected function add_edit_rule_data($entity, $data) { $this->rule_operator->change_parent($entity->get_id(), $data['rule_parent_id']); } - catch (\phpbb\exception\runtime_exception $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + } + catch (\Exception $e) + { + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } } @@ -712,9 +716,13 @@ protected function add_edit_rule_data($entity, $data) { $this->rule_operator->add_rule($entity, $data['rule_language'], $data['rule_parent_id']); } - catch (\phpbb\exception\runtime_exception $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + } + catch (\InvalidArgumentException|\RuntimeException $e) + { + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } // Show user confirmation of the added rule and provide link back to the previous page @@ -781,9 +789,13 @@ public function delete_rule($rule_id) { $this->rule_operator->delete_rule($rule_id); } - catch (\phpbb\exception\runtime_exception $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + } + catch (\Exception $e) + { + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } // Show user confirmation of the deleted rule and provide link back to the previous page @@ -829,9 +841,13 @@ public function move_rule($rule_id, $direction, $amount = 1) { $this->rule_operator->move($rule_id, $direction, $amount); } - catch (\phpbb\exception\runtime_exception $e) + catch (\phpbb\boardrules\exception\out_of_bounds $e) + { + trigger_error($e->get_message($this->lang) . adm_back_link($this->u_action), E_USER_WARNING); + } + catch (\Exception $e) { - trigger_error($this->get_exception_message($e) . adm_back_link($this->u_action), E_USER_WARNING); + trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } // Send a JSON response if an AJAX request was used @@ -1000,20 +1016,4 @@ protected function build_parent_select_menu($entity, $parent_id = 0) )); } } - - /** - * Translate an expected Board Rules or phpBB runtime exception. - * - * @param \phpbb\exception\runtime_exception $exception - * @return string - */ - protected function get_exception_message(\phpbb\exception\runtime_exception $exception) - { - if ($exception instanceof \phpbb\boardrules\exception\base) - { - return $exception->get_message($this->lang); - } - - return $this->lang->lang_array($exception->getMessage(), $exception->get_parameters()); - } } diff --git a/exception/base.php b/exception/base.php index 4cb10f1d..40afea89 100644 --- a/exception/base.php +++ b/exception/base.php @@ -13,7 +13,7 @@ /** * Base exception */ -class base extends \phpbb\exception\runtime_exception +class base extends \Exception { /** * Null if the message is a string, array if the message was submitted as an array @@ -21,6 +21,8 @@ class base extends \phpbb\exception\runtime_exception */ protected $message_full; + protected $previous; + /** * Constructor * @@ -33,11 +35,22 @@ class base extends \phpbb\exception\runtime_exception */ public function __construct($message = null, $code = 0, \Exception $previous = null) { + parent::__construct(); + + $this->message = $message; + + if (is_array($message)) + { + $this->message = (string) $message[0]; + } + + // We're slightly changing the way exceptions work + // Tools, such as xDebug, expect the message to be a string, so to prevent errors + // with those tools, we store our full message in message_full and only a string in message $this->message_full = $message; - $message_parts = is_array($message) ? $message : array($message); - $message_key = (string) array_shift($message_parts); - parent::__construct($message_key, $message_parts, $previous, $code); + $this->code = $code; + $this->previous = $previous; } /** diff --git a/operators/rule.php b/operators/rule.php index e21f0564..74cc4888 100644 --- a/operators/rule.php +++ b/operators/rule.php @@ -87,14 +87,15 @@ public function get_rules($language, $parent_id = 0) * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public - * @throws \phpbb\exception\runtime_exception If the language is invalid or the nested-set lock cannot be acquired + * @throws \InvalidArgumentException If the language is not installed + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function add_rule($entity, $language, $parent_id = 0) { if (!$this->lock->acquire()) { - throw new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + throw new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); } try @@ -132,7 +133,7 @@ public function add_rule($entity, $language, $parent_id = 0) * @param int $rule_id The rule identifier to delete * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function delete_rule($rule_id) @@ -158,7 +159,7 @@ public function delete_rule($rule_id) * @param int $amount The number of places to move the rule * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function move($rule_id, $direction = 'up', $amount = 1) @@ -184,7 +185,7 @@ public function move($rule_id, $direction = 'up', $amount = 1) * @param int $new_parent_id The new rule parent identifier * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function change_parent($rule_id, $new_parent_id) diff --git a/operators/rule_interface.php b/operators/rule_interface.php index 866f13f7..88092bd8 100644 --- a/operators/rule_interface.php +++ b/operators/rule_interface.php @@ -36,7 +36,8 @@ public function get_rules($language, $parent_id = 0); * @param int $parent_id Category to display rules from; default: 0 * @return \phpbb\boardrules\entity\rule_interface Added rule entity * @access public - * @throws \phpbb\exception\runtime_exception If the language is invalid or the nested-set lock cannot be acquired + * @throws \InvalidArgumentException If the language is not installed + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds If the entity was already inserted */ public function add_rule($entity, $language, $parent_id = 0); @@ -47,7 +48,7 @@ public function add_rule($entity, $language, $parent_id = 0); * @param int $rule_id The rule identifier to delete * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function delete_rule($rule_id); @@ -60,7 +61,7 @@ public function delete_rule($rule_id); * @param int $amount The number of places to move the rule * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function move($rule_id, $direction, $amount = 1); @@ -72,7 +73,7 @@ public function move($rule_id, $direction, $amount = 1); * @param int $new_parent_id The new rule parent identifier * @return void * @access public - * @throws \phpbb\exception\runtime_exception If the nested-set lock cannot be acquired + * @throws \RuntimeException If the nested-set lock cannot be acquired * @throws \phpbb\boardrules\exception\out_of_bounds */ public function change_parent($rule_id, $new_parent_id); diff --git a/operators/ruleset.php b/operators/ruleset.php index a2b65e67..384742ff 100644 --- a/operators/ruleset.php +++ b/operators/ruleset.php @@ -95,12 +95,12 @@ public function copy($source_language, $target_language) if ($source_language === $target_language || !$this->language_exists($source_language) || !$this->language_exists($target_language)) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); + throw new \InvalidArgumentException('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); } if (!$this->lock->acquire()) { - throw new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + throw new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); } $transaction_started = false; @@ -112,7 +112,7 @@ public function copy($source_language, $target_language) $source_rules = $this->get_rules_data($source_language); if (empty($source_rules)) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); + throw new \InvalidArgumentException('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); } $id_map = array(); @@ -201,7 +201,7 @@ public function draft_if_empty($language) { if (!$this->language_exists($language)) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); } if ($this->get_rule_count($language) > 0) @@ -236,7 +236,7 @@ public function set_intro_text($language, $intro_text) { if (!$this->language_exists($language)) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); } $this->save_ruleset_value($language, 'rules_intro_text', utf8_encode_ncr((string) $intro_text)); @@ -249,12 +249,12 @@ public function set_published($language, $published) { if (!$this->language_exists($language)) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_INVALID_LANGUAGE'); + throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); } if ($this->get_rule_count($language) === 0) { - throw new \phpbb\exception\runtime_exception('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); + throw new \InvalidArgumentException('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); } $this->save_published_state($language, (bool) $published); diff --git a/operators/ruleset_interface.php b/operators/ruleset_interface.php index 2a9607cc..a0278d79 100644 --- a/operators/ruleset_interface.php +++ b/operators/ruleset_interface.php @@ -25,7 +25,8 @@ public function get_languages(); * @param string $source_language * @param string $target_language * @return array Copy result containing rule and renamed-anchor counts - * @throws \phpbb\exception\runtime_exception If a language is invalid, source ruleset is empty, or nested-set lock cannot be acquired + * @throws \InvalidArgumentException If either language is invalid or source ruleset is empty + * @throws \RuntimeException If nested-set lock cannot be acquired * @throws \Exception If ruleset persistence fails */ public function copy($source_language, $target_language); @@ -45,7 +46,7 @@ public function is_published($language); * * @param string $language * @return bool True when draft state was saved - * @throws \phpbb\exception\runtime_exception If the language is not installed + * @throws \InvalidArgumentException If the language is not installed */ public function draft_if_empty($language); @@ -65,7 +66,7 @@ public function get_intro_text($language); * @param string $language * @param string $intro_text * @return void - * @throws \phpbb\exception\runtime_exception If the language is not installed + * @throws \InvalidArgumentException If the language is not installed */ public function set_intro_text($language, $intro_text); @@ -75,7 +76,7 @@ public function set_intro_text($language, $intro_text); * @param string $language * @param bool $published * @return void - * @throws \phpbb\exception\runtime_exception If the language is not installed or its ruleset is empty + * @throws \InvalidArgumentException If the language is not installed or its ruleset is empty */ public function set_published($language, $published); } diff --git a/tests/controller/admin_controller_test.php b/tests/controller/admin_controller_test.php index 2ebb4e0e..d0a04a78 100644 --- a/tests/controller/admin_controller_test.php +++ b/tests/controller/admin_controller_test.php @@ -420,7 +420,7 @@ public function test_copy_ruleset_translates_operator_error(): void $this->controller->copy_ruleset('fr'); } - public function test_copy_ruleset_does_not_swallow_unexpected_operator_exception(): void + public function test_copy_ruleset_handles_general_operator_exception(): void { $ruleset_operator = $this->getMockBuilder(\phpbb\boardrules\operators\ruleset::class) ->disableOriginalConstructor() @@ -437,8 +437,7 @@ public function test_copy_ruleset_does_not_swallow_unexpected_operator_exception $this->replace_controller_service('ruleset_operator', $ruleset_operator); $this->post['submit'] = true; $this->variables['source_language'] = 'en'; - $this->expectException(\Exception::class); - $this->expectExceptionMessage('ACP_BOARDRULES_COPY_FAILED'); + $this->setExpectedTriggerError(E_USER_WARNING, 'ACP_BOARDRULES_COPY_FAILED'); $this->controller->copy_ruleset('fr'); } @@ -586,7 +585,7 @@ public function test_edit_rule_reports_parent_change_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('change_parent')->willThrowException(new \phpbb\exception\runtime_exception('PARENT_CHANGE_FAILED')); + $operator->method('change_parent')->willThrowException(new \RuntimeException('PARENT_CHANGE_FAILED')); $this->replace_controller_service('container', $this->entity_container($entity)); $this->replace_controller_service('rule_operator', $operator); $this->post['submit'] = true; @@ -596,6 +595,22 @@ public function test_edit_rule_reports_parent_change_failure(): void $this->controller->edit_rule(2); } + public function test_edit_rule_translates_parent_change_bounds_failure(): void + { + $entity = $this->mock_entity(2); + $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) + ->disableOriginalConstructor() + ->getMock(); + $operator->method('change_parent')->willThrowException(new \phpbb\boardrules\exception\out_of_bounds('new_parent_id')); + $this->replace_controller_service('container', $this->entity_container($entity)); + $this->replace_controller_service('rule_operator', $operator); + $this->post['submit'] = true; + $this->variables['rule_parent'] = 3; + $this->setExpectedTriggerError(E_USER_WARNING, 'EXCEPTION_OUT_OF_BOUNDS'); + + $this->controller->edit_rule(2); + } + /** * @dataProvider add_rule_operator_error_data */ @@ -619,7 +634,7 @@ public static function add_rule_operator_error_data(): array { return array( 'entity bounds failure' => array(new \phpbb\boardrules\exception\out_of_bounds('rule_id'), 'EXCEPTION_OUT_OF_BOUNDS'), - 'nested-set lock failure' => array(new \phpbb\exception\runtime_exception('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), + 'nested-set lock failure' => array(new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'), ); } @@ -644,13 +659,25 @@ public function test_delete_rule_reports_operator_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('delete_rule')->willThrowException(new \phpbb\exception\runtime_exception('DELETE_FAILED')); + $operator->method('delete_rule')->willThrowException(new \RuntimeException('DELETE_FAILED')); $this->replace_controller_service('rule_operator', $operator); $this->setExpectedTriggerError(E_USER_WARNING, 'DELETE_FAILED'); $this->controller->delete_rule(3); } + public function test_delete_rule_translates_bounds_failure(): void + { + $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) + ->disableOriginalConstructor() + ->getMock(); + $operator->method('delete_rule')->willThrowException(new \phpbb\boardrules\exception\out_of_bounds('rule_id')); + $this->replace_controller_service('rule_operator', $operator); + $this->setExpectedTriggerError(E_USER_WARNING, 'EXCEPTION_OUT_OF_BOUNDS'); + + $this->controller->delete_rule(3); + } + public function test_move_rule_rejects_invalid_hash(): void { admin_test_state::$valid_link_hash = false; @@ -681,13 +708,25 @@ public function test_move_rule_reports_operator_failure(): void $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) ->disableOriginalConstructor() ->getMock(); - $operator->method('move')->willThrowException(new \phpbb\exception\runtime_exception('MOVE_FAILED')); + $operator->method('move')->willThrowException(new \RuntimeException('MOVE_FAILED')); $this->replace_controller_service('rule_operator', $operator); $this->setExpectedTriggerError(E_USER_WARNING, 'MOVE_FAILED'); $this->controller->move_rule(2, 'down'); } + public function test_move_rule_translates_bounds_failure(): void + { + $operator = $this->getMockBuilder(\phpbb\boardrules\operators\rule::class) + ->disableOriginalConstructor() + ->getMock(); + $operator->method('move')->willThrowException(new \phpbb\boardrules\exception\out_of_bounds('rule_id')); + $this->replace_controller_service('rule_operator', $operator); + $this->setExpectedTriggerError(E_USER_WARNING, 'EXCEPTION_OUT_OF_BOUNDS'); + + $this->controller->move_rule(2, 'down'); + } + public function test_send_notification_requests_confirmation(): void { admin_test_state::$confirm = false; diff --git a/tests/operators/rule_operator_add_rule_test.php b/tests/operators/rule_operator_add_rule_test.php index 9d237116..59d6b87a 100644 --- a/tests/operators/rule_operator_add_rule_test.php +++ b/tests/operators/rule_operator_add_rule_test.php @@ -83,7 +83,7 @@ public function test_add_rule_rejects_when_nestedset_lock_is_held() $this->get_rule_operator()->add_rule($entity, 'en'); self::fail('Rule insertion should not run while the nested-set lock is held.'); } - catch (\phpbb\exception\runtime_exception $e) + catch (\RuntimeException $e) { self::assertSame('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE', $e->getMessage()); } diff --git a/tests/operators/ruleset_operator_test.php b/tests/operators/ruleset_operator_test.php index 48c039d7..4ea81842 100644 --- a/tests/operators/ruleset_operator_test.php +++ b/tests/operators/ruleset_operator_test.php @@ -104,7 +104,7 @@ public function test_empty_ruleset_is_forced_to_draft_before_first_rule(): void public function test_unknown_ruleset_cannot_be_prepared_for_first_rule(): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->draft_if_empty('xx'); } @@ -143,7 +143,7 @@ public function test_copy_rejects_when_nestedset_lock_is_held(): void $this->operator->copy('en', 'fr'); self::fail('Copy should not run while the nested-set lock is held.'); } - catch (\phpbb\exception\runtime_exception $e) + catch (\RuntimeException $e) { self::assertSame('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE', $e->getMessage()); } @@ -164,7 +164,7 @@ public function test_copy_releases_nestedset_lock(): void public function test_copy_rejects_empty_source(): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_COPY_SOURCE_EMPTY'); $this->operator->copy('de', 'fr'); } @@ -203,7 +203,7 @@ public function test_ruleset_intro_can_be_created_before_rules(): void public function test_unknown_ruleset_intro_language_is_rejected(): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->set_intro_text('xx', 'Unknown'); @@ -211,7 +211,7 @@ public function test_unknown_ruleset_intro_language_is_rejected(): void public function test_empty_ruleset_cannot_be_published(): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); $this->operator->set_published('fr', true); } @@ -221,7 +221,7 @@ public function test_empty_ruleset_cannot_be_published(): void */ public function test_copy_rejects_invalid_languages($source, $target): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_COPY_INVALID_LANGUAGE'); $this->operator->copy($source, $target); } @@ -238,7 +238,7 @@ public static function invalid_copy_language_data(): array public function test_unknown_ruleset_cannot_change_publication_state(): void { - $this->expectException(\phpbb\exception\runtime_exception::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('ACP_BOARDRULES_INVALID_LANGUAGE'); $this->operator->set_published('xx', true); } diff --git a/tests/system/exception_test.php b/tests/system/exception_test.php index 2fbdcf12..25b360d1 100644 --- a/tests/system/exception_test.php +++ b/tests/system/exception_test.php @@ -115,8 +115,6 @@ public static function exception_string_data(): array */ public function test_exception_string_representation($message, $expected): void { - $exception = new \phpbb\boardrules\exception\base($message); - self::assertInstanceOf(\phpbb\exception\runtime_exception::class, $exception); - self::assertSame($expected, (string) $exception); + self::assertSame($expected, (string) new \phpbb\boardrules\exception\base($message)); } } From 148af48fa868823c0da699d79158b71b8dfaa894 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 23 Aug 2026 07:34:25 -0700 Subject: [PATCH 4/6] Fix possible race and lock issues --- controller/admin_controller.php | 4 +- operators/ruleset.php | 115 ++++++++++++++++----- operators/ruleset_interface.php | 3 + tests/controller/admin_controller_test.php | 28 +++++ tests/operators/ruleset_operator_test.php | 39 +++++++ 5 files changed, 159 insertions(+), 30 deletions(-) diff --git a/controller/admin_controller.php b/controller/admin_controller.php index f5b40794..de2c92e7 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -364,7 +364,7 @@ public function save_ruleset_intro($language) { $this->ruleset_operator->set_intro_text($language, $intro_text); } - catch (\InvalidArgumentException $e) + catch (\InvalidArgumentException|\RuntimeException $e) { trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($this->u_action), E_USER_WARNING); } @@ -466,7 +466,7 @@ public function set_ruleset_published($language, $published, $return_to = '') { $this->ruleset_operator->set_published($language, $published); } - catch (\InvalidArgumentException $e) + catch (\InvalidArgumentException|\RuntimeException $e) { trigger_error($this->lang->lang($e->getMessage()) . adm_back_link($return_url), E_USER_WARNING); } diff --git a/operators/ruleset.php b/operators/ruleset.php index 384742ff..3e749f08 100644 --- a/operators/ruleset.php +++ b/operators/ruleset.php @@ -204,14 +204,22 @@ public function draft_if_empty($language) throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); } - if ($this->get_rule_count($language) > 0) + $acquired_lock = $this->acquire_write_lock(); + try { - return false; - } + if ($this->get_rule_count($language) > 0) + { + return false; + } - $this->save_published_state($language, false); + $this->save_published_state($language, false); - return true; + return true; + } + finally + { + $this->release_write_lock($acquired_lock); + } } /** @@ -252,12 +260,20 @@ public function set_published($language, $published) throw new \InvalidArgumentException('ACP_BOARDRULES_INVALID_LANGUAGE'); } - if ($this->get_rule_count($language) === 0) + $acquired_lock = $this->acquire_write_lock(); + try { - throw new \InvalidArgumentException('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); - } + if ($this->get_rule_count($language) === 0) + { + throw new \InvalidArgumentException('ACP_BOARDRULES_STATUS_CHANGE_EMPTY'); + } - $this->save_published_state($language, (bool) $published); + $this->save_published_state($language, (bool) $published); + } + finally + { + $this->release_write_lock($acquired_lock); + } } /** @@ -396,30 +412,73 @@ protected function save_published_state($language, $published) */ protected function save_ruleset_value($language, $column, $value) { - $sql = 'SELECT language_iso - FROM ' . $this->rulesets_table . " - WHERE language_iso = '" . $this->db->sql_escape($language) . "'"; - $result = $this->db->sql_query_limit($sql, 1); - $exists = (bool) $this->db->sql_fetchfield('language_iso'); - $this->db->sql_freeresult($result); - - if ($exists) + $acquired_lock = $this->acquire_write_lock(); + try { - $sql = 'UPDATE ' . $this->rulesets_table . ' - SET ' . $this->db->sql_build_array('UPDATE', array($column => $value)) . " + $sql = 'SELECT language_iso + FROM ' . $this->rulesets_table . " WHERE language_iso = '" . $this->db->sql_escape($language) . "'"; + $result = $this->db->sql_query_limit($sql, 1); + $exists = (bool) $this->db->sql_fetchfield('language_iso'); + $this->db->sql_freeresult($result); + + if ($exists) + { + $sql = 'UPDATE ' . $this->rulesets_table . ' + SET ' . $this->db->sql_build_array('UPDATE', array($column => $value)) . " + WHERE language_iso = '" . $this->db->sql_escape($language) . "'"; + } + else + { + $sql_data = array( + 'language_iso' => $language, + 'rules_published' => 1, + 'rules_intro_text' => '', + ); + $sql_data[$column] = $value; + $sql = 'INSERT INTO ' . $this->rulesets_table . ' ' . $this->db->sql_build_array('INSERT', $sql_data); + } + + $this->db->sql_query($sql); + } + finally + { + $this->release_write_lock($acquired_lock); + } + } + + /** + * Acquire the shared write lock unless this request already owns it. + * + * @return bool True when this method acquired the lock + * @throws \RuntimeException If another request owns the lock + */ + protected function acquire_write_lock() + { + if ($this->lock->owns_lock()) + { + return false; } - else + + if (!$this->lock->acquire()) { - $sql_data = array( - 'language_iso' => $language, - 'rules_published' => 1, - 'rules_intro_text' => '', - ); - $sql_data[$column] = $value; - $sql = 'INSERT INTO ' . $this->rulesets_table . ' ' . $this->db->sql_build_array('INSERT', $sql_data); + throw new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); } - $this->db->sql_query($sql); + return true; + } + + /** + * Release the shared write lock only when acquired by the current method. + * + * @param bool $acquired_lock + * @return void + */ + protected function release_write_lock($acquired_lock) + { + if ($acquired_lock) + { + $this->lock->release(); + } } } diff --git a/operators/ruleset_interface.php b/operators/ruleset_interface.php index a0278d79..e06b5f01 100644 --- a/operators/ruleset_interface.php +++ b/operators/ruleset_interface.php @@ -47,6 +47,7 @@ public function is_published($language); * @param string $language * @return bool True when draft state was saved * @throws \InvalidArgumentException If the language is not installed + * @throws \RuntimeException If the shared write lock cannot be acquired */ public function draft_if_empty($language); @@ -67,6 +68,7 @@ public function get_intro_text($language); * @param string $intro_text * @return void * @throws \InvalidArgumentException If the language is not installed + * @throws \RuntimeException If the shared write lock cannot be acquired */ public function set_intro_text($language, $intro_text); @@ -77,6 +79,7 @@ public function set_intro_text($language, $intro_text); * @param bool $published * @return void * @throws \InvalidArgumentException If the language is not installed or its ruleset is empty + * @throws \RuntimeException If the shared write lock cannot be acquired */ public function set_published($language, $published); } diff --git a/tests/controller/admin_controller_test.php b/tests/controller/admin_controller_test.php index d0a04a78..8d0e6ba5 100644 --- a/tests/controller/admin_controller_test.php +++ b/tests/controller/admin_controller_test.php @@ -372,6 +372,20 @@ public function test_save_ruleset_intro_rejects_unknown_language(): void $this->controller->save_ruleset_intro('xx'); } + public function test_save_ruleset_intro_reports_lock_failure(): void + { + $ruleset_operator = $this->getMockBuilder(\phpbb\boardrules\operators\ruleset::class) + ->disableOriginalConstructor() + ->setMethods(array('set_intro_text')) + ->getMock(); + $ruleset_operator->method('set_intro_text') + ->willThrowException(new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE')); + $this->replace_controller_service('ruleset_operator', $ruleset_operator); + $this->setExpectedTriggerError(E_USER_WARNING, 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + + $this->controller->save_ruleset_intro('fr'); + } + public function test_display_rules_rejects_unknown_language(): void { $this->setExpectedTriggerError(E_USER_WARNING, 'ACP_BOARDRULES_INVALID_LANGUAGE|back:adm.php?i=boardrules'); @@ -486,6 +500,20 @@ public function test_empty_ruleset_cannot_be_published(): void $this->controller->set_ruleset_published('fr', true); } + public function test_ruleset_status_reports_lock_failure(): void + { + $ruleset_operator = $this->getMockBuilder(\phpbb\boardrules\operators\ruleset::class) + ->disableOriginalConstructor() + ->setMethods(array('set_published')) + ->getMock(); + $ruleset_operator->method('set_published') + ->willThrowException(new \RuntimeException('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE')); + $this->replace_controller_service('ruleset_operator', $ruleset_operator); + $this->setExpectedTriggerError(E_USER_WARNING, 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE'); + + $this->controller->set_ruleset_published('en', true); + } + public function test_add_rule_initial_form_uses_real_entity(): void { $this->controller->add_rule('fr'); diff --git a/tests/operators/ruleset_operator_test.php b/tests/operators/ruleset_operator_test.php index 4ea81842..5d62da47 100644 --- a/tests/operators/ruleset_operator_test.php +++ b/tests/operators/ruleset_operator_test.php @@ -102,6 +102,45 @@ public function test_empty_ruleset_is_forced_to_draft_before_first_rule(): void self::assertTrue($this->operator->is_published('en')); } + public function test_draft_write_reuses_and_preserves_caller_owned_lock(): void + { + self::assertTrue($this->lock->acquire()); + + try + { + self::assertTrue($this->operator->draft_if_empty('fr')); + self::assertTrue($this->lock->owns_lock()); + } + finally + { + $this->lock->release(); + } + + self::assertFalse($this->operator->is_published('fr')); + } + + public function test_intro_write_rejects_competing_ruleset_creation(): void + { + $competing_lock = new \phpbb\lock\db('boardrules.table_lock.boardrules_table', $this->config, $this->db); + self::assertTrue($competing_lock->acquire()); + + try + { + $this->operator->set_intro_text('fr', 'Bienvenue.'); + self::fail('Ruleset creation should not run while the shared lock is held.'); + } + catch (\RuntimeException $e) + { + self::assertSame('RULES_NESTEDSET_LOCK_FAILED_ACQUIRE', $e->getMessage()); + } + finally + { + $competing_lock->release(); + } + + self::assertSame('', $this->operator->get_intro_text('fr')); + } + public function test_unknown_ruleset_cannot_be_prepared_for_first_rule(): void { $this->expectException(\InvalidArgumentException::class); From b5e6b35ab5e9bd2b99ea4eca212f9b31cb943d6a Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 23 Aug 2026 08:49:16 -0700 Subject: [PATCH 5/6] Improve in ACP descriptions of draft and default rules statuses --- adm/style/boardrules_manage_languages.html | 6 +-- adm/style/boardrules_manage_rules.html | 10 ++-- controller/admin_controller.php | 39 +++++++++++---- language/ar/boardrules_acp.php | 7 ++- language/bg/boardrules_acp.php | 7 ++- language/cs/boardrules_acp.php | 7 ++- language/da/boardrules_acp.php | 7 ++- language/de/boardrules_acp.php | 7 ++- language/de_x_sie/boardrules_acp.php | 7 ++- language/el/boardrules_acp.php | 7 ++- language/en/boardrules_acp.php | 6 ++- language/es/boardrules_acp.php | 7 ++- language/et/boardrules_acp.php | 7 ++- language/fi/boardrules_acp.php | 7 ++- language/fr/boardrules_acp.php | 7 ++- language/he/boardrules_acp.php | 7 ++- language/hr/boardrules_acp.php | 7 ++- language/hu/boardrules_acp.php | 7 ++- language/it/boardrules_acp.php | 7 ++- language/nb/boardrules_acp.php | 7 ++- language/nl/boardrules_acp.php | 7 ++- language/pl/boardrules_acp.php | 7 ++- language/pt/boardrules_acp.php | 7 ++- language/pt_br/boardrules_acp.php | 7 ++- language/pt_preao/boardrules_acp.php | 7 ++- language/ro/boardrules_acp.php | 7 ++- language/ru/boardrules_acp.php | 7 ++- language/sk/boardrules_acp.php | 7 ++- language/sv/boardrules_acp.php | 7 ++- language/tr/boardrules_acp.php | 7 ++- language/uk/boardrules_acp.php | 7 ++- language/zh_cmn_hans/boardrules_acp.php | 7 ++- tests/controller/admin_controller_test.php | 56 +++++++++++++++++++++- tests/controller/main_controller_test.php | 25 ++++++++-- tests/functional/admin_controller_test.php | 30 ++++++++++++ 35 files changed, 288 insertions(+), 80 deletions(-) diff --git a/adm/style/boardrules_manage_languages.html b/adm/style/boardrules_manage_languages.html index 56fd4f83..6e8ce4b6 100644 --- a/adm/style/boardrules_manage_languages.html +++ b/adm/style/boardrules_manage_languages.html @@ -17,15 +17,15 @@

{{ lang('ACP_BOARDRULES_LANGUAGES') }}

{{ language.LANG_LOCAL_NAME }}
{{ language.LANG_ENGLISH_NAME }}{% if language.S_DEFAULT %} · {{ lang('ACP_BOARDRULES_DEFAULT_LANGUAGE') }}{% endif %} {{ language.RULE_COUNT }} - {% if language.S_EMPTY %}{{ lang(language.S_DEFAULT ? 'ACP_BOARDRULES_STATUS_NO_RULES' : 'ACP_BOARDRULES_STATUS_EMPTY') }} - {% elseif language.S_DRAFT %}{{ lang('ACP_BOARDRULES_STATUS_DRAFT') }} + {% if language.S_EMPTY %}{{ lang(language.S_DEFAULT ? 'ACP_BOARDRULES_STATUS_NO_RULES' : (language.S_FALLBACK_AVAILABLE ? 'ACP_BOARDRULES_STATUS_EMPTY' : 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE')) }} + {% elseif language.S_DRAFT %}{{ lang('ACP_BOARDRULES_STATUS_DRAFT') }}{% if not language.S_DEFAULT %}
{{ lang(language.S_FALLBACK_AVAILABLE ? 'ACP_BOARDRULES_STATUS_EMPTY' : 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE') }}{% endif %} {% else %}{{ lang('ACP_BOARDRULES_STATUS_PUBLISHED') }}{% endif %} {{ lang('ACP_BOARDRULES_MANAGE_ACTION') }} {% if language.S_CAN_COPY %} · {{ lang('ACP_BOARDRULES_COPY_ACTION') }}{% endif %} {% if language.S_DRAFT %} · {{ lang('ACP_BOARDRULES_PUBLISH') }}{% endif %} - {% if language.S_CAN_DRAFT %} · {{ lang('ACP_BOARDRULES_SET_DRAFT') }}{% endif %} + {% if language.S_PUBLISHED %} · {{ lang('ACP_BOARDRULES_SET_DRAFT') }}{% endif %} {% endfor %} diff --git a/adm/style/boardrules_manage_rules.html b/adm/style/boardrules_manage_rules.html index 9baea62c..ed94b580 100644 --- a/adm/style/boardrules_manage_rules.html +++ b/adm/style/boardrules_manage_rules.html @@ -43,23 +43,23 @@

{{ lang('ACP_BOARDRULES_MANAGE') }}

{% endif %} -{% if S_CAN_COPY_RULESET or S_RULESET_DRAFT or S_CAN_DRAFT_RULESET %} +{% if S_CAN_COPY_RULESET or S_RULESET_DRAFT or S_RULESET_PUBLISHED %}

{% if S_CAN_COPY_RULESET %} {{ lang('ACP_BOARDRULES_COPY_ACTION') }}{% endif %} {% if S_RULESET_DRAFT %} {{ lang('ACP_BOARDRULES_PUBLISH') }}{% endif %} - {% if S_CAN_DRAFT_RULESET %} {{ lang('ACP_BOARDRULES_SET_DRAFT') }}{% endif %} + {% if S_RULESET_PUBLISHED %} {{ lang('ACP_BOARDRULES_SET_DRAFT') }}{% endif %}

{% endif %} {% if S_RULESET_DRAFT %}

{{ lang('ACP_BOARDRULES_STATUS_DRAFT') }}

-

{{ lang(S_DEFAULT_LANGUAGE ? 'ACP_BOARDRULES_DRAFT_DEFAULT_NOTICE' : 'ACP_BOARDRULES_DRAFT_NOTICE') }}

+

{{ lang(S_DEFAULT_LANGUAGE ? 'ACP_BOARDRULES_DRAFT_DEFAULT_NOTICE' : (S_FALLBACK_AVAILABLE ? 'ACP_BOARDRULES_DRAFT_NOTICE' : 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE')) }}

{% elseif S_RULESET_EMPTY %}
-

{{ lang(S_DEFAULT_LANGUAGE ? 'ACP_BOARDRULES_STATUS_NO_RULES' : 'ACP_BOARDRULES_STATUS_EMPTY') }}

-

{{ lang(S_DEFAULT_LANGUAGE ? 'ACP_BOARDRULES_EMPTY_DEFAULT_NOTICE' : 'ACP_BOARDRULES_EMPTY_NOTICE') }}

+

{{ lang(S_DEFAULT_LANGUAGE or not S_FALLBACK_AVAILABLE ? 'ACP_BOARDRULES_STATUS_NO_RULES' : 'ACP_BOARDRULES_STATUS_EMPTY') }}

+

{{ lang(S_DEFAULT_LANGUAGE ? 'ACP_BOARDRULES_EMPTY_DEFAULT_NOTICE' : (S_FALLBACK_AVAILABLE ? 'ACP_BOARDRULES_EMPTY_NOTICE' : 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE')) }}

{% endif %} diff --git a/controller/admin_controller.php b/controller/admin_controller.php index de2c92e7..cb4ace75 100644 --- a/controller/admin_controller.php +++ b/controller/admin_controller.php @@ -217,8 +217,8 @@ public function display_language_dashboard() 'S_EMPTY' => $is_empty, 'S_PUBLISHED' => !$is_empty && $language['published'], 'S_DRAFT' => !$is_empty && !$language['published'], + 'S_FALLBACK_AVAILABLE' => !$is_default && $this->default_ruleset_is_available($languages), 'S_CAN_COPY' => $this->has_copy_source($languages, $language['lang_iso']), - 'S_CAN_DRAFT' => !$is_empty && $language['published'] && !$is_default, 'U_MANAGE' => "{$this->u_action}&language={$language['lang_iso']}", 'U_COPY' => "{$this->u_action}&action=copy&language={$language['lang_iso']}&return_to=dashboard", 'U_PUBLISH' => "{$this->u_action}&action=publish&language={$language['lang_iso']}&return_to=dashboard", @@ -249,6 +249,7 @@ public function display_rules($language, $parent_id = 0) $languages = $this->assign_language_options($language); $current_language = $this->find_language($languages, $language); + $fallback_available = $this->default_ruleset_is_available($languages); if ($current_language === null) { trigger_error($this->lang->lang('ACP_BOARDRULES_INVALID_LANGUAGE') . adm_back_link($this->u_action), E_USER_WARNING); @@ -321,7 +322,7 @@ public function display_rules($language, $parent_id = 0) 'S_DEFAULT_LANGUAGE' => $language === $this->config['default_lang'], 'S_RULESET_PUBLISHED' => $current_language['rule_count'] > 0 && $current_language['published'], 'S_RULESET_DRAFT' => $current_language['rule_count'] > 0 && !$current_language['published'], - 'S_CAN_DRAFT_RULESET' => $current_language['rule_count'] > 0 && $current_language['published'] && $language !== $this->config['default_lang'], + 'S_FALLBACK_AVAILABLE' => $language !== $this->config['default_lang'] && $fallback_available, 'S_CAN_COPY_RULESET' => $this->has_copy_source($languages, $language), 'S_RULESET_ROOT' => $parent_id === 0, )); @@ -455,11 +456,6 @@ public function set_ruleset_published($language, $published, $return_to = '') { $return_url = $this->get_ruleset_return_url($language, $return_to); - if (!$published && $language === $this->config['default_lang']) - { - trigger_error($this->lang->lang('ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT') . adm_back_link($return_url), E_USER_WARNING); - } - if (confirm_box(true)) { try @@ -477,7 +473,21 @@ public function set_ruleset_published($language, $published, $return_to = '') trigger_error($this->lang->lang($message) . adm_back_link($return_url)); } - $confirm_key = $published ? 'ACP_BOARDRULES_PUBLISH_CONFIRM' : 'ACP_BOARDRULES_DRAFT_CONFIRM'; + if ($published) + { + $confirm_key = 'ACP_BOARDRULES_PUBLISH_CONFIRM'; + } + else if ($language === $this->config['default_lang']) + { + $confirm_key = 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM'; + } + else + { + $confirm_key = $this->default_ruleset_is_available($this->ruleset_operator->get_languages()) + ? 'ACP_BOARDRULES_DRAFT_CONFIRM' + : 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM'; + } + confirm_box(false, $this->lang->lang($confirm_key), build_hidden_fields(array( 'mode' => 'manage', 'action' => $published ? 'publish' : 'draft', @@ -954,6 +964,19 @@ protected function find_language(array $languages, $language_iso) return null; } + /** + * Test whether published default-language rules are available as a fallback. + * + * @param array $languages + * @return bool + */ + protected function default_ruleset_is_available(array $languages) + { + $default = $this->find_language($languages, $this->config['default_lang']); + + return $default !== null && $default['rule_count'] > 0 && $default['published']; + } + /** * @param array $languages * @param string $target_language diff --git a/language/ar/boardrules_acp.php b/language/ar/boardrules_acp.php index 978abcf1..2cbe18d0 100644 --- a/language/ar/boardrules_acp.php +++ b/language/ar/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'أدر جميع اللغات المثبتة من مكان واحد. انسخ مجموعة قوانين كاملة إلى لغة أخرى، وترجمها كمسودة، ثم انشرها عندما تصبح جاهزة.', 'ACP_BOARDRULES_RULES' => 'القوانين', 'ACP_BOARDRULES_STATUS' => 'الحالة', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'استخدام اللغة الافتراضية', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'استخدام قواعد اللغة الافتراضية', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'لا توجد قوانين', 'ACP_BOARDRULES_STATUS_DRAFT' => 'مسودة', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'منشور', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => 'أُعيدت تسمية %d من المراسي المتعارضة باستخدام لواحق رقمية.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'اللغة المحددة غير مثبتة.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'لا يمكن نشر مجموعة قوانين فارغة أو تغييرها إلى مسودة.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'لا يمكن تغيير لغة المنتدى الافتراضية إلى مسودة.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'هل تريد نشر مجموعة القوانين الكاملة لهذه اللغة؟ سيراها مستخدمو هذه اللغة فوراً.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'هل تريد تغيير مجموعة القوانين الكاملة لهذه اللغة إلى مسودة؟ سيرى مستخدمو هذه اللغة قوانين اللغة الافتراضية للمنتدى بدلاً منها.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'نُشرت مجموعة قوانين اللغة.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'تم تعديل القانون بنجاح.', 'ACP_RULE_TITLE_EMPTY' => 'يجب إضافة عنوان لهذه القانون.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'لا تتوفر أية قواعد لغة افتراضية منشورة.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'هل تريد تغيير مجموعة قواعد اللغة الافتراضية إلى مسودة؟ لن يكون لدى المستخدمين الذين ليس لديهم مجموعة قواعد منشورة أخرى أي قواعد متاحة.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'هل تريد تغيير مجموعة قواعد اللغة الكاملة هذه إلى مسودة؟ لا تتوفر أي قواعد لغة افتراضية منشورة كبديل.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'فشلت قوانين المنتدى في اغلاق الجدول. ربما هناك عملية أخرى تمنع ذلك حالياً. يتم إيقاف عمليات الإغلاق بعد فترة مُحددة : ساعة واحدة.', diff --git a/language/bg/boardrules_acp.php b/language/bg/boardrules_acp.php index 3911d774..8d4d8175 100644 --- a/language/bg/boardrules_acp.php +++ b/language/bg/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Управлявайте всички инсталирани езици от едно място. Копирайте пълен набор от правила на друг език, преведете го като чернова и го публикувайте, когато е готов.', 'ACP_BOARDRULES_RULES' => 'Правила', 'ACP_BOARDRULES_STATUS' => 'Състояние', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Използва се езикът по подразбиране', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Използват се правилата на езика по подразбиране', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Няма правила', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Чернова', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Публикувано', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d конфликтни котви бяха преименувани с цифрови суфикси.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Избраният език не е инсталиран.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Празен набор от правила не може да бъде публикуван или зададен като чернова.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Езикът по подразбиране на форума не може да бъде зададен като чернова.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Да се публикува ли този пълен набор от правила? Потребителите на този език ще го видят веднага.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Да се зададе ли този пълен набор от правила като чернова? Потребителите на този език вместо него ще виждат правилата на езика по подразбиране на форума.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Наборът от правила на езика е публикуван.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Правилото успешно редактирано.', 'ACP_RULE_TITLE_EMPTY' => 'Трябва да въведете заглавие за това Правило.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Няма публикувани правила за език по подразбиране.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Промяна на набора от правила за език по подразбиране на чернова? Потребителите без друг публикуван набор от правила няма да имат налични правила.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Да промените този пълен набор от езикови правила на чернова? Няма публикувани правила за език по подразбиране, налични като резервен вариант.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/cs/boardrules_acp.php b/language/cs/boardrules_acp.php index 1af91fcc..1f361305 100644 --- a/language/cs/boardrules_acp.php +++ b/language/cs/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Spravujte všechny nainstalované jazyky na jednom místě. Zkopírujte kompletní sadu pravidel do jiného jazyka, přeložte ji jako koncept a po dokončení ji zveřejněte.', 'ACP_BOARDRULES_RULES' => 'Pravidla', 'ACP_BOARDRULES_STATUS' => 'Stav', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Používá se výchozí jazyk', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Používají se pravidla výchozího jazyka', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Žádná pravidla', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Koncept', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Zveřejněno', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d konfliktních odkazů bylo přejmenováno pomocí číselných přípon.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Vybraný jazyk není nainstalován.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Prázdnou sadu pravidel nelze zveřejnit ani nastavit jako koncept.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Výchozí jazyk fóra nelze nastavit jako koncept.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Zveřejnit tuto kompletní jazykovou sadu pravidel? Uživatelé tohoto jazyka ji uvidí okamžitě.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Nastavit tuto kompletní jazykovou sadu pravidel jako koncept? Uživatelé tohoto jazyka místo ní uvidí pravidla ve výchozím jazyce fóra.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Jazyková sada pravidel byla zveřejněna.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Pravidlo úspěšně upraveno.', 'ACP_RULE_TITLE_EMPTY' => 'Musíš zadat název pravidla.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Nejsou k dispozici žádná publikovaná pravidla výchozího jazyka.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Změnit sadu pravidel výchozího jazyka na koncept? Uživatelé bez další publikované sady pravidel nebudou mít žádná pravidla k dispozici.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Změnit tuto úplnou sadu jazykových pravidel na koncept? Žádná publikovaná pravidla výchozího jazyka nejsou k dispozici jako záložní.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules se nepodařilo uzamknout tabulku. Možná je právě uzamčena jiným procesem. Zámky jsou násilně odstraňovány po uplynutí jedné hodiny.', diff --git a/language/da/boardrules_acp.php b/language/da/boardrules_acp.php index c501b7bc..c2feea6b 100644 --- a/language/da/boardrules_acp.php +++ b/language/da/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Administrer alle installerede sprog ét sted. Kopiér et komplet regelsæt til et andet sprog, oversæt det som en kladde, og udgiv det, når det er klar.', 'ACP_BOARDRULES_RULES' => 'Regler', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Bruger standardsprog', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Bruger regler på standardsproget', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Ingen regler', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Kladde', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Udgivet', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ankre med konflikter blev omdøbt med numeriske suffikser.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Det valgte sprog er ikke installeret.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Et tomt regelsæt kan ikke udgives eller angives som kladde.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Boardets standardsprog kan ikke angives som kladde.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Vil du udgive hele dette regelsæt? Brugere af dette sprog vil se det med det samme.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Vil du ændre hele dette regelsæt til en kladde? Brugere af dette sprog vil i stedet se reglerne på boardets standardsprog.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Sprogets regelsæt blev udgivet.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Regel redigeret.', 'ACP_RULE_TITLE_EMPTY' => 'Du skal indtaste en titel til reglen.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Ingen offentliggjorte standardsprogsregler er tilgængelige.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Vil du ændre standardsprogets regelsæt til kladde? Brugere uden et andet offentliggjort regelsæt har ingen tilgængelige regler.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Vil du ændre dette komplette sprogregelsæt til kladde? Ingen offentliggjorte standardsprogsregler er tilgængelige som en reserve.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Boardregler kunne ikke indhente tabellåsen. En anden proces bruger måske låsen. Låse tvinges til frigivelse efter en timeout på 1 time.', diff --git a/language/de/boardrules_acp.php b/language/de/boardrules_acp.php index ca839a08..81f8e63c 100644 --- a/language/de/boardrules_acp.php +++ b/language/de/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Verwalten Sie alle installierten Sprachen an einem zentralen Ort. Kopieren Sie einen vollständigen Regelsatz in eine andere Sprache, übersetzen Sie ihn als Entwurf und veröffentlichen Sie ihn anschließend.', 'ACP_BOARDRULES_RULES' => 'Regeln', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Standardsprache wird verwendet', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Regeln in der Standardsprache werden verwendet', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Keine Regeln', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Entwurf', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Veröffentlicht', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d Anker mit Konflikten wurden mit numerischen Suffixen umbenannt.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Die ausgewählte Sprache ist nicht installiert.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Ein leerer Regelsatz kann weder veröffentlicht noch als Entwurf festgelegt werden.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Die Standardsprache des Forums kann nicht als Entwurf festgelegt werden.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Diesen vollständigen Sprachregelsatz veröffentlichen? Benutzer dieser Sprache sehen ihn sofort.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Diesen vollständigen Sprachregelsatz als Entwurf festlegen? Benutzer dieser Sprache sehen stattdessen die Regeln in der Standardsprache des Forums.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Der Sprachregelsatz wurde veröffentlicht.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regel bearbeitet.', 'ACP_RULE_TITLE_EMPTY' => 'Für die Regel muss ein Regel-Name vergeben werden.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Es sind keine veröffentlichten Regeln in der Standardsprache verfügbar.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Den Regelsatz der Standardsprache in einen Entwurf ändern? Für Benutzer ohne einen anderen veröffentlichten Regelsatz sind keine Regeln verfügbar.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Diesen kompletten Sprachregelsatz in einen Entwurf ändern? Als Rückgriff stehen keine veröffentlichten Regeln in der Standardsprache zur Verfügung.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Die Board-Regeln konnten nicht in die Tabelle geschrieben werden, da ein anderer Prozess dies vermutlich verhindert und angehalten hat. Die Sperren werden nach einer Zeitüberschreitung von 1 Stunde aufgehoben.', diff --git a/language/de_x_sie/boardrules_acp.php b/language/de_x_sie/boardrules_acp.php index 6ca36525..8b3bdf40 100644 --- a/language/de_x_sie/boardrules_acp.php +++ b/language/de_x_sie/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Verwalten Sie alle installierten Sprachen an einem zentralen Ort. Kopieren Sie einen vollständigen Regelsatz in eine andere Sprache, übersetzen Sie ihn als Entwurf und veröffentlichen Sie ihn anschließend.', 'ACP_BOARDRULES_RULES' => 'Regeln', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Standardsprache wird verwendet', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Regeln in der Standardsprache werden verwendet', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Keine Regeln', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Entwurf', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Veröffentlicht', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d Anker mit Konflikten wurden mit numerischen Suffixen umbenannt.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Die ausgewählte Sprache ist nicht installiert.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Ein leerer Regelsatz kann weder veröffentlicht noch als Entwurf festgelegt werden.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Die Standardsprache des Forums kann nicht als Entwurf festgelegt werden.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Diesen vollständigen Sprachregelsatz veröffentlichen? Benutzer dieser Sprache sehen ihn sofort.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Diesen vollständigen Sprachregelsatz als Entwurf festlegen? Benutzer dieser Sprache sehen stattdessen die Regeln in der Standardsprache des Forums.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Der Sprachregelsatz wurde veröffentlicht.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regel bearbeitet.', 'ACP_RULE_TITLE_EMPTY' => 'Für die Regel muss ein Regel-Name vergeben werden.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Es sind keine veröffentlichten Regeln in der Standardsprache verfügbar.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Den Regelsatz der Standardsprache in einen Entwurf ändern? Für Benutzer ohne einen anderen veröffentlichten Regelsatz sind keine Regeln verfügbar.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Diesen kompletten Sprachregelsatz in einen Entwurf ändern? Als Rückgriff stehen keine veröffentlichten Regeln in der Standardsprache zur Verfügung.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Die Board-Regeln konnten nicht in die Tabelle geschrieben werden, da ein anderer Prozess dies vermutlich verhindert und angehalten hat. Die Sperren werden nach einer Zeitüberschreitung von 1 Stunde aufgehoben.', diff --git a/language/el/boardrules_acp.php b/language/el/boardrules_acp.php index 80fc70ce..2d6633f6 100644 --- a/language/el/boardrules_acp.php +++ b/language/el/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Διαχειριστείτε όλες τις εγκατεστημένες γλώσσες από ένα σημείο. Αντιγράψτε ένα πλήρες σύνολο κανόνων σε άλλη γλώσσα, μεταφράστε το ως πρόχειρο και δημοσιεύστε το όταν είναι έτοιμο.', 'ACP_BOARDRULES_RULES' => 'Κανόνες', 'ACP_BOARDRULES_STATUS' => 'Κατάσταση', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Χρήση προεπιλεγμένης γλώσσας', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Χρήση κανόνων προεπιλεγμένης γλώσσας', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Χωρίς κανόνες', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Πρόχειρο', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Δημοσιευμένο', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d άγκυρες με διένεξη μετονομάστηκαν με αριθμητικά επιθήματα.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Η επιλεγμένη γλώσσα δεν είναι εγκατεστημένη.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Ένα κενό σύνολο κανόνων δεν μπορεί να δημοσιευτεί ή να μετατραπεί σε πρόχειρο.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Η προεπιλεγμένη γλώσσα της κοινότητας δεν μπορεί να μετατραπεί σε πρόχειρο.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Να δημοσιευτεί το πλήρες σύνολο κανόνων αυτής της γλώσσας; Οι χρήστες αυτής της γλώσσας θα το δουν αμέσως.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Να μετατραπεί το πλήρες σύνολο κανόνων αυτής της γλώσσας σε πρόχειρο; Οι χρήστες αυτής της γλώσσας θα βλέπουν αντί γι’ αυτό τους κανόνες στην προεπιλεγμένη γλώσσα της κοινότητας.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Το σύνολο κανόνων της γλώσσας δημοσιεύτηκε.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Ο όρος επεξεργάστηκε με επιτυχία.', 'ACP_RULE_TITLE_EMPTY' => 'Πρέπει να εισάγετε ένα τίτλο για αυτό τον όρο.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Δεν υπάρχουν διαθέσιμοι δημοσιευμένοι κανόνες προεπιλεγμένης γλώσσας.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Αλλαγή του συνόλου κανόνων προεπιλεγμένης γλώσσας σε πρόχειρο; Οι χρήστες χωρίς άλλο δημοσιευμένο σύνολο κανόνων δεν θα έχουν διαθέσιμους κανόνες.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Να αλλάξει αυτό το πλήρες σύνολο κανόνων γλώσσας σε πρόχειρο; Δεν υπάρχουν δημοσιευμένοι κανόνες προεπιλεγμένης γλώσσας διαθέσιμοι ως εναλλακτικοί.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/en/boardrules_acp.php b/language/en/boardrules_acp.php index c7508240..09c233aa 100644 --- a/language/en/boardrules_acp.php +++ b/language/en/boardrules_acp.php @@ -75,8 +75,9 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Manage each installed language from one place. Copy a complete ruleset into another language, translate it as a draft, then publish it when ready.', 'ACP_BOARDRULES_RULES' => 'Rules', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Using fallback', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Using default-language rules', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'No rules', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'No published default-language rules are available.', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Draft', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Published', 'ACP_BOARDRULES_MANAGE_ACTION' => 'Manage', @@ -104,9 +105,10 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d conflicting anchors were renamed with numeric suffixes.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Selected language is not installed.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'An empty ruleset cannot be published or changed to draft.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Board default language cannot be changed to draft.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publish this complete language ruleset? Users of this language will see it immediately.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Change this complete language ruleset to draft? Users of this language will see rules from the board’s default language instead.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Change the default-language ruleset to draft? Users without another published ruleset will have no rules available.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Change this complete language ruleset to draft? No published default-language rules are available as a fallback.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Language ruleset published.', 'ACP_BOARDRULES_DRAFT_SUCCESS' => 'Language ruleset changed to draft.', 'ACP_BOARDRULES_CREATE_RULE' => 'Create rule', diff --git a/language/es/boardrules_acp.php b/language/es/boardrules_acp.php index 6710fe12..8a641a33 100644 --- a/language/es/boardrules_acp.php +++ b/language/es/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Gestione todos los idiomas instalados desde un solo lugar. Copie un conjunto completo de normas a otro idioma, tradúzcalo como borrador y publíquelo cuando esté listo.', 'ACP_BOARDRULES_RULES' => 'Normas', 'ACP_BOARDRULES_STATUS' => 'Estado', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Usando idioma alternativo', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Usando las reglas del idioma predeterminado', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Sin normas', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Borrador', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicado', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => 'Se cambiaron los nombres de %d anclajes en conflicto con sufijos numéricos.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'El idioma seleccionado no está instalado.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Un conjunto de normas vacío no se puede publicar ni establecer como borrador.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'El idioma predeterminado del sitio no se puede establecer como borrador.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => '¿Publicar este conjunto completo de normas del idioma? Los usuarios de este idioma lo verán inmediatamente.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => '¿Establecer este conjunto completo de normas del idioma como borrador? Los usuarios de este idioma verán en su lugar las normas del idioma predeterminado del sitio.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Conjunto de normas del idioma publicado.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Norma editada correctamente.', 'ACP_RULE_TITLE_EMPTY' => 'Debe especificar un título para esta norma.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'No hay reglas publicadas disponibles en el idioma predeterminado.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => '¿Cambiar el conjunto de reglas del idioma predeterminado a borrador? Los usuarios sin otro conjunto de reglas publicado no tendrán reglas disponibles.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => '¿Cambiar este conjunto completo de reglas de idioma a borrador? No hay reglas publicadas disponibles en el idioma predeterminado como alternativa.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Las Normas del Foro han fallado al adquirir el bloqueo de la tabla. Otro proceso puede ser que tenga el bloqueo. Los bloqueos se liberan por la fuerza, después de un tiempo de espera de 1 hora.', diff --git a/language/et/boardrules_acp.php b/language/et/boardrules_acp.php index beccd09b..d5cc7368 100644 --- a/language/et/boardrules_acp.php +++ b/language/et/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Halda kõiki paigaldatud keeli ühes kohas. Kopeeri kogu reeglistik teise keelde, tõlgi see mustandina ja avalda, kui see on valmis.', 'ACP_BOARDRULES_RULES' => 'Reeglid', 'ACP_BOARDRULES_STATUS' => 'Olek', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Kasutatakse vaikekeelt', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Kasutatakse vaikekeele reegleid', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Reeglid puuduvad', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Mustand', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Avaldatud', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d konfliktset ankrut nimetati ümber numbriliste järelliidetega.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Valitud keel pole paigaldatud.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Tühja reeglistikku ei saa avaldada ega mustandiks muuta.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Foorumi vaikekeelt ei saa mustandiks muuta.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Kas avaldada selle keele täielik reeglistik? Selle keele kasutajad näevad seda kohe.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Kas muuta selle keele täielik reeglistik mustandiks? Selle keele kasutajad näevad selle asemel foorumi vaikekeele reegleid.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Keele reeglistik avaldati.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Reegel on edukalt muudetud.', 'ACP_RULE_TITLE_EMPTY' => 'Sa pead sisestama pealkirja antud reeglile.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Avaldatud vaikekeele reeglid pole saadaval.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Kas muuta vaikekeele reeglistik mustandiks? Kasutajatel, kellel pole teist avaldatud reeglistikku, pole reegleid saadaval.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Kas muuta see täielik keelereeglite komplekt mustandiks? Avaldatud vaikekeele reeglid pole varuvariandina saadaval.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Laiendusel "Foorumi reeglid" ei õnnestunud omandada tabeli lukustamist. Järgmise toimingu ajal võib õnnestuda lukustamine. Lukustamine on vabastatakse jõuliselt peale ühe tunnist ooteaega.', diff --git a/language/fi/boardrules_acp.php b/language/fi/boardrules_acp.php index 7b211bff..2b48b20f 100644 --- a/language/fi/boardrules_acp.php +++ b/language/fi/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Hallitse kaikkia asennettuja kieliä yhdessä paikassa. Kopioi koko sääntökokonaisuus toiselle kielelle, käännä se luonnoksena ja julkaise se, kun se on valmis.', 'ACP_BOARDRULES_RULES' => 'Säännöt', 'ACP_BOARDRULES_STATUS' => 'Tila', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Käytetään oletuskieltä', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Käytetään oletuskielen sääntöjä', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Ei sääntöjä', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Luonnos', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Julkaistu', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ristiriitaista ankkuria nimettiin uudelleen numeerisilla jälkiliitteillä.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Valittua kieltä ei ole asennettu.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Tyhjää sääntökokonaisuutta ei voi julkaista tai muuttaa luonnokseksi.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Foorumin oletuskieltä ei voi muuttaa luonnokseksi.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Julkaistaanko tämän kielen koko sääntökokonaisuus? Tämän kielen käyttäjät näkevät sen heti.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Muutetaanko tämän kielen koko sääntökokonaisuus luonnokseksi? Tämän kielen käyttäjät näkevät sen sijaan foorumin oletuskielen säännöt.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Kielen sääntökokonaisuus julkaistu.', @@ -133,6 +132,10 @@ 'ACP_RULE_EDITED' => 'Sääntöä muokattu onnistuneesti.', 'ACP_RULE_TITLE_EMPTY' => 'Sinun on annettava tälle säännölle otsikko.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Julkaistuja oletuskielisiä sääntöjä ei ole saatavilla.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Muutetaanko oletuskielen sääntöjoukko luonnokseksi? Käyttäjillä, joilla ei ole toista julkaistua sääntöjoukkoa, ei ole sääntöjä saatavilla.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Muutetaanko tämä täydellinen kielisääntöjoukko luonnokseksi? Julkaistuja oletuskielisiä sääntöjä ei ole saatavilla varavaihtoehtona.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Säännöt eivät onnistuneet saamaan yhteyttä taulukkoon. Toinen prosessi voi käyttää taulukkoa tällä hetkellä. Taulukon yhteys vapautetaan väkisin 1 tunnin aikakatkaisun jälkeen.', diff --git a/language/fr/boardrules_acp.php b/language/fr/boardrules_acp.php index c2a81573..26d1ca14 100644 --- a/language/fr/boardrules_acp.php +++ b/language/fr/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Gérez toutes les langues installées depuis un seul endroit. Copiez un ensemble complet de règles vers une autre langue, traduisez-le comme brouillon, puis publiez-le lorsqu’il est prêt.', 'ACP_BOARDRULES_RULES' => 'Règles', 'ACP_BOARDRULES_STATUS' => 'Statut', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Langue par défaut utilisée', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Règles de la langue par défaut utilisées', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Aucune règle', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Brouillon', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publié', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ancres en conflit ont été renommées avec des suffixes numériques.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'La langue sélectionnée n’est pas installée.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Un ensemble de règles vide ne peut être ni publié ni défini comme brouillon.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'La langue par défaut du forum ne peut pas être définie comme brouillon.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publier cet ensemble complet de règles linguistiques ? Les utilisateurs de cette langue le verront immédiatement.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Définir cet ensemble complet de règles linguistiques comme brouillon ? Les utilisateurs de cette langue verront à la place les règles dans la langue par défaut du forum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'L’ensemble de règles linguistiques a été publié.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Règle modifiée avec succès.', 'ACP_RULE_TITLE_EMPTY' => 'Vous devez saisir un intitulé pour cette règle.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Aucune règle publiée dans la langue par défaut n’est disponible.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Définir l’ensemble de règles de la langue par défaut comme brouillon ? Les utilisateurs sans autre ensemble de règles publié n’auront aucune règle disponible.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Définir cet ensemble complet de règles linguistiques comme brouillon ? Aucune règle publiée dans la langue par défaut n’est disponible comme solution de secours.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'L’extension « Board rules » a échoué lors du verrouillage de la table. Un autre processus doit maintenir ce verrouillage. Les verrouillages sont forcés à s’arrêter après un délais d’une heure.', diff --git a/language/he/boardrules_acp.php b/language/he/boardrules_acp.php index 2a089512..2898edbe 100644 --- a/language/he/boardrules_acp.php +++ b/language/he/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'נהל את כל השפות המותקנות ממקום אחד. העתק מערכת חוקים מלאה לשפה אחרת, תרגם אותה כטיוטה ולאחר מכן פרסם אותה כשהיא מוכנה.', 'ACP_BOARDRULES_RULES' => 'חוקים', 'ACP_BOARDRULES_STATUS' => 'מצב', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'נעשה שימוש בשפת ברירת המחדל', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'נעשה שימוש בכללים בשפת ברירת המחדל', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'אין חוקים', 'ACP_BOARDRULES_STATUS_DRAFT' => 'טיוטה', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'פורסם', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d עוגנים מתנגשים קיבלו שמות חדשים עם סיומות מספריות.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'השפה שנבחרה אינה מותקנת.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'אי אפשר לפרסם מערכת חוקים ריקה או להפוך אותה לטיוטה.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'אי אפשר להפוך את שפת ברירת המחדל של הפורום לטיוטה.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'לפרסם את מערכת החוקים המלאה בשפה זו? משתמשי השפה יראו אותה מיד.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'להפוך את מערכת החוקים המלאה בשפה זו לטיוטה? משתמשי השפה יראו במקומה את החוקים בשפת ברירת המחדל של הפורום.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'מערכת החוקים של השפה פורסמה.', @@ -133,6 +132,10 @@ 'ACP_RULE_EDITED' => 'החוק נערך בהצלחה.', 'ACP_RULE_TITLE_EMPTY' => 'אתה חייב לתת כותרת לחוק.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'אין כללים שפורסמו בשפת ברירת המחדל.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'לשנות את מערכת הכללים של שפת ברירת המחדל לטיוטה? למשתמשים ללא מערכת כללים אחרת שפורסמה לא יהיו כללים זמינים.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'לשנות את מערכת כללי השפה המלאה הזו לטיוטה? אין כללים שפורסמו בשפת ברירת המחדל כחלופה.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'התוספת חוקי הפורום לא הצליחו לנעול את הטבלה המבוקשת. כנראה שתהליך אחר נועל את הטבלה. לאחר שעה הנעילה משתחררת.', diff --git a/language/hr/boardrules_acp.php b/language/hr/boardrules_acp.php index 90e4c982..5dec80ae 100644 --- a/language/hr/boardrules_acp.php +++ b/language/hr/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Upravljajte svim instaliranim jezicima na jednom mjestu. Kopirajte cijeli skup pravila na drugi jezik, prevedite ga kao skicu, a zatim ga objavite kada bude spreman.', 'ACP_BOARDRULES_RULES' => 'Pravila', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Koristi se zadani jezik', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Koriste se pravila zadanog jezika', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Nema pravila', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Skica', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Objavljeno', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d podudarnih sidara preimenovano je pomoću brojčanih sufiksa.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Odabrani jezik nije instaliran.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Prazan skup pravila ne može se objaviti niti postaviti kao skica.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Zadani jezik foruma ne može se postaviti kao skica.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Objaviti cijeli skup pravila za ovaj jezik? Korisnici ovog jezika odmah će ga vidjeti.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Postaviti cijeli skup pravila za ovaj jezik kao skicu? Korisnici ovog jezika umjesto njega vidjet će pravila na zadanom jeziku foruma.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Skup pravila jezika je objavljen.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Pravilo je uređeno.', 'ACP_RULE_TITLE_EMPTY' => 'Moraš upisati naslov pravila.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Nisu dostupna objavljena pravila zadanog jezika.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Promijeniti skup pravila zadanog jezika u nacrt? Korisnici bez drugog objavljenog skupa pravila neće imati dostupna pravila.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Promijeniti kompletan skup jezičnih pravila u nacrt? Nisu dostupna objavljena pravila zadanog jezika kao zamjena.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Forumska pravila nisu uspjela postići zaključavanje tablice. Moguće je da isto ometa neki drugi proces. Zaključavanja se prisilno izvršavaju po isteku vremena od jednog sata.', diff --git a/language/hu/boardrules_acp.php b/language/hu/boardrules_acp.php index 3d988f9f..cf29939f 100644 --- a/language/hu/boardrules_acp.php +++ b/language/hu/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Kezelje az összes telepített nyelvet egy helyen. Másolja át a teljes szabálykészletet egy másik nyelvre, fordítsa le piszkozatként, majd tegye közzé, amikor elkészült.', 'ACP_BOARDRULES_RULES' => 'Szabályok', 'ACP_BOARDRULES_STATUS' => 'Állapot', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Az alapértelmezett nyelv használata', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Az alapértelmezett nyelv szabályainak használata', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Nincsenek szabályok', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Piszkozat', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Közzétéve', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ütköző horgony számozott utótaggal lett átnevezve.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'A kiválasztott nyelv nincs telepítve.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Üres szabálykészlet nem tehető közzé és nem állítható piszkozatként.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'A fórum alapértelmezett nyelve nem állítható piszkozatként.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Közzéteszi a teljes szabálykészletet ezen a nyelven? Az ezt a nyelvet használók azonnal látni fogják.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Piszkozattá teszi a teljes szabálykészletet ezen a nyelven? Az ezt a nyelvet használók helyette a fórum alapértelmezett nyelvén létrehozott szabályokat fogják látni.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'A nyelvi szabálykészlet közzétéve.', @@ -133,6 +132,10 @@ 'ACP_RULE_EDITED' => 'Szabályzat sikeresen szerkesztve.', 'ACP_RULE_TITLE_EMPTY' => 'Adja meg a szabályzat nevét.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Nem állnak rendelkezésre közzétett alapértelmezett nyelvi szabályok.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Módosítja az alapértelmezett nyelvi szabálykészletet vázlatra? Azoknak a felhasználóknak, akik nem rendelkeznek másik közzétett szabálykészlettel, nem állnak rendelkezésre szabályok.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Vázlatra módosítja ezt a teljes nyelvi szabálykészletet? Nincsenek közzétett alapértelmezett nyelvi szabályok tartalékként.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Nem sikerült megnyitni a Fórum Szabályzatot mivel zárolva van és egy másik folyamat használja. Egy óra mulva újra megpróbálhatja.', diff --git a/language/it/boardrules_acp.php b/language/it/boardrules_acp.php index 2fcd0100..16d70181 100644 --- a/language/it/boardrules_acp.php +++ b/language/it/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Gestisci tutte le lingue installate da un unico punto. Copia un set completo di regole in un’altra lingua, traducilo come bozza e pubblicalo quando è pronto.', 'ACP_BOARDRULES_RULES' => 'Regole', 'ACP_BOARDRULES_STATUS' => 'Stato', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Uso della lingua predefinita', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Uso delle regole della lingua predefinita', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Nessuna regola', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Bozza', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Pubblicato', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ancoraggi in conflitto sono stati rinominati con suffissi numerici.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'La lingua selezionata non è installata.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Un set di regole vuoto non può essere pubblicato né impostato come bozza.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'La lingua predefinita della board non può essere impostata come bozza.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Pubblicare questo set completo di regole della lingua? Gli utenti di questa lingua lo vedranno immediatamente.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Impostare questo set completo di regole della lingua come bozza? Gli utenti di questa lingua vedranno invece le regole nella lingua predefinita della board.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Set di regole della lingua pubblicato.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regola modificata con successo.', 'ACP_RULE_TITLE_EMPTY' => 'Devi inserire un titolo per questa regola.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Non sono disponibili regole pubblicate per la lingua predefinita.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Cambiare il set di regole della lingua predefinita in bozza? Gli utenti senza un altro set di regole pubblicato non avranno regole disponibili.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Cambiare questo set completo di regole linguistiche in bozza? Nessuna regola pubblicata per la lingua predefinita è disponibile come alternativa.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/nb/boardrules_acp.php b/language/nb/boardrules_acp.php index cac06744..1ca9b0d7 100644 --- a/language/nb/boardrules_acp.php +++ b/language/nb/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Administrer alle installerte språk fra ett sted. Kopier et komplett regelsett til et annet språk, oversett det som et utkast, og publiser det når det er klart.', 'ACP_BOARDRULES_RULES' => 'Regler', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Bruker standardspråk', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Bruker regler på standardspråket', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Ingen regler', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Utkast', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publisert', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ankere med konflikter fikk nye navn med numeriske suffikser.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Det valgte språket er ikke installert.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Et tomt regelsett kan ikke publiseres eller angis som utkast.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Forumets standardspråk kan ikke angis som utkast.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publisere hele dette regelsettet? Brukere av dette språket vil se det umiddelbart.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Endre hele dette regelsettet til utkast? Brukere av dette språket vil i stedet se reglene på forumets standardspråk.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Språkets regelsett ble publisert.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regelen har blitt oppdatert.', 'ACP_RULE_TITLE_EMPTY' => 'Du må angi en tittel for denne regelen.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Ingen publiserte regler på standardspråket er tilgjengelige.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Endre regelsettet for standardspråket til utkast? Brukere uten et annet publisert regelsett har ingen regler tilgjengelig.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Endre dette komplette språkregelsettet til utkast? Ingen publiserte regler på standardspråket er tilgjengelige som reserve.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/nl/boardrules_acp.php b/language/nl/boardrules_acp.php index 5b5d03ad..247509f2 100644 --- a/language/nl/boardrules_acp.php +++ b/language/nl/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Beheer alle geïnstalleerde talen vanuit één plek. Kopieer een volledige regelset naar een andere taal, vertaal deze als concept en publiceer deze wanneer hij klaar is.', 'ACP_BOARDRULES_RULES' => 'Regels', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Standaardtaal wordt gebruikt', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Regels in de standaardtaal worden gebruikt', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Geen regels', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Concept', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Gepubliceerd', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d conflicterende ankers zijn hernoemd met numerieke achtervoegsels.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'De geselecteerde taal is niet geïnstalleerd.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Een lege regelset kan niet worden gepubliceerd of als concept worden ingesteld.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'De standaardtaal van het forum kan niet als concept worden ingesteld.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Deze volledige regelset publiceren? Gebruikers van deze taal zien deze onmiddellijk.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Deze volledige regelset als concept instellen? Gebruikers van deze taal zien in plaats daarvan de regels in de standaardtaal van het forum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Regelset van taal gepubliceerd.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regel succesvol gewijzigd.', 'ACP_RULE_TITLE_EMPTY' => 'Je moet een titel invullen voor deze regel.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Er zijn geen gepubliceerde regels in de standaardtaal beschikbaar.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'De regelset van de standaardtaal wijzigen in concept? Voor gebruikers zonder een andere gepubliceerde regelset zijn geen regels beschikbaar.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Deze volledige taalregelset wijzigen in concept? Er zijn geen gepubliceerde regels in de standaardtaal beschikbaar als reserve.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Module “forumregels“ kon geen table lock verkrijgen. Een ander proces houdt mogelijk de lock tegen. Locks worden geforceerd vrijgegeven na een timeout van 1 uur.', diff --git a/language/pl/boardrules_acp.php b/language/pl/boardrules_acp.php index 2030f39e..c1de8b5b 100644 --- a/language/pl/boardrules_acp.php +++ b/language/pl/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Zarządzaj wszystkimi zainstalowanymi językami w jednym miejscu. Skopiuj kompletny regulamin do innego języka, przetłumacz go jako szkic, a następnie opublikuj, gdy będzie gotowy.', 'ACP_BOARDRULES_RULES' => 'Przepisy', 'ACP_BOARDRULES_STATUS' => 'Stan', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Używany jest język domyślny', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Używane są zasady w języku domyślnym', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Brak przepisów', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Szkic', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Opublikowano', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => 'Zmieniono nazwy %d zakotwiczeń powodujących konflikt, dodając przyrostki liczbowe.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Wybrany język nie jest zainstalowany.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Pustego regulaminu nie można opublikować ani ustawić jako szkic.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Domyślnego języka forum nie można ustawić jako szkic.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Opublikować ten kompletny regulamin języka? Użytkownicy tego języka zobaczą go natychmiast.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Ustawić ten kompletny regulamin języka jako szkic? Użytkownicy tego języka zobaczą zamiast niego regulamin w domyślnym języku forum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Regulamin języka został opublikowany.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Przepis został zaktualizowany.', 'ACP_RULE_TITLE_EMPTY' => 'Należy wprowadzić tytuł dla tego przepisu.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Brak opublikowanych zasad w języku domyślnym.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Zmienić zestaw zasad języka domyślnego na wersję roboczą? Użytkownicy bez innego opublikowanego zestawu zasad nie będą mieli dostępnych żadnych zasad.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Zmienić ten kompletny zestaw zasad językowych na wersję roboczą? Brak opublikowanych zasad w języku domyślnym jako rozwiązania awaryjnego.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/pt/boardrules_acp.php b/language/pt/boardrules_acp.php index 86b306fc..3ae20f42 100644 --- a/language/pt/boardrules_acp.php +++ b/language/pt/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Faça a gestão de todos os idiomas instalados num único local. Copie um conjunto completo de regras para outro idioma, traduza-o como rascunho e publique-o quando estiver pronto.', 'ACP_BOARDRULES_RULES' => 'Regras', 'ACP_BOARDRULES_STATUS' => 'Estado', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'A usar o idioma predefinido', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'A usar as regras do idioma predefinido', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Sem regras', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Rascunho', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicado', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d âncoras em conflito foram renomeadas com sufixos numéricos.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'O idioma selecionado não está instalado.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Um conjunto de regras vazio não pode ser publicado nem definido como rascunho.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'O idioma predefinido do fórum não pode ser definido como rascunho.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publicar este conjunto completo de regras do idioma? Os utilizadores deste idioma irão vê-lo imediatamente.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Os utilizadores deste idioma verão as regras no idioma predefinido do fórum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Conjunto de regras do idioma publicado.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regra editada com sucesso.', 'ACP_RULE_TITLE_EMPTY' => 'Tem que dar um titulo à regra.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Não estão disponíveis regras publicadas no idioma predefinido.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Definir o conjunto de regras do idioma predefinido como rascunho? Os utilizadores sem outro conjunto de regras publicado não terão regras disponíveis.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Não estão disponíveis regras publicadas no idioma predefinido como alternativa.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/pt_br/boardrules_acp.php b/language/pt_br/boardrules_acp.php index f0718934..42b4cf5e 100644 --- a/language/pt_br/boardrules_acp.php +++ b/language/pt_br/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Gerencie todos os idiomas instalados em um só lugar. Copie um conjunto completo de regras para outro idioma, traduza-o como rascunho e publique-o quando estiver pronto.', 'ACP_BOARDRULES_RULES' => 'Regras', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Usando o idioma padrão', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Usando as regras do idioma padrão', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Sem regras', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Rascunho', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicado', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d âncoras em conflito foram renomeadas com sufixos numéricos.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'O idioma selecionado não está instalado.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Um conjunto de regras vazio não pode ser publicado nem definido como rascunho.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'O idioma padrão do fórum não pode ser definido como rascunho.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publicar este conjunto completo de regras do idioma? Os usuários deste idioma o verão imediatamente.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Os usuários deste idioma verão as regras no idioma padrão do fórum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Conjunto de regras do idioma publicado.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regra editada com sucesso.', 'ACP_RULE_TITLE_EMPTY' => 'Dê um titulo à regra.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Não há regras publicadas disponíveis no idioma padrão.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Definir o conjunto de regras do idioma padrão como rascunho? Os usuários sem outro conjunto de regras publicado não terão regras disponíveis.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Não há regras publicadas disponíveis no idioma padrão como alternativa.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Falha ao tentar bloquear a tabela. Outro processo deve estar bloqueando-a. Bloqueios são forçadamente liberados após uma hora.', diff --git a/language/pt_preao/boardrules_acp.php b/language/pt_preao/boardrules_acp.php index c793072e..a8d6c529 100644 --- a/language/pt_preao/boardrules_acp.php +++ b/language/pt_preao/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Faça a gestão de todos os idiomas instalados num único local. Copie um conjunto completo de regras para outro idioma, traduza-o como rascunho e publique-o quando estiver pronto.', 'ACP_BOARDRULES_RULES' => 'Regras', 'ACP_BOARDRULES_STATUS' => 'Estado', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'A usar o idioma predefinido', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'A usar as regras do idioma predefinido', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Sem regras', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Rascunho', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicado', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d âncoras em conflito foram renomeadas com sufixos numéricos.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'O idioma seleccionado não está instalado.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Um conjunto de regras vazio não pode ser publicado nem definido como rascunho.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'O idioma predefinido do fórum não pode ser definido como rascunho.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publicar este conjunto completo de regras do idioma? Os utilizadores deste idioma irão vê-lo imediatamente.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Os utilizadores deste idioma verão as regras no idioma predefinido do fórum.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Conjunto de regras do idioma publicado.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regra editada com sucesso.', 'ACP_RULE_TITLE_EMPTY' => 'Tem que dar um titulo à regra.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Não estão disponíveis regras publicadas no idioma predefinido.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Definir o conjunto de regras do idioma predefinido como rascunho? Os utilizadores sem outro conjunto de regras publicado não terão regras disponíveis.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Definir este conjunto completo de regras do idioma como rascunho? Não estão disponíveis regras publicadas no idioma predefinido como alternativa.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/ro/boardrules_acp.php b/language/ro/boardrules_acp.php index a3dad194..690b37db 100644 --- a/language/ro/boardrules_acp.php +++ b/language/ro/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Gestionați toate limbile instalate dintr-un singur loc. Copiați un set complet de reguli într-o altă limbă, traduceți-l ca ciornă, apoi publicați-l când este gata.', 'ACP_BOARDRULES_RULES' => 'Reguli', 'ACP_BOARDRULES_STATUS' => 'Stare', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Se utilizează limba implicită', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Se utilizează regulile în limba implicită', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Nicio regulă', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Ciornă', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicat', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ancore aflate în conflict au fost redenumite cu sufixe numerice.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Limba selectată nu este instalată.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Un set de reguli gol nu poate fi publicat sau schimbat în ciornă.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Limba implicită a forumului nu poate fi schimbată în ciornă.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publicați setul complet de reguli al acestei limbi? Utilizatorii acestei limbi îl vor vedea imediat.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Schimbați setul complet de reguli al acestei limbi în ciornă? Utilizatorii acestei limbi vor vedea în schimb regulile în limba implicită a forumului.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Setul de reguli al limbii a fost publicat.', @@ -133,6 +132,10 @@ 'ACP_RULE_EDITED' => 'Regula a fost editata cu succes.', 'ACP_RULE_TITLE_EMPTY' => 'Trbuie sa introduci un titlu.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Nu sunt disponibile reguli publicate pentru limba implicită.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Schimbați setul de reguli pentru limba implicită în ciornă? Utilizatorii fără un alt set de reguli publicat nu vor avea reguli disponibile.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Schimbați acest set complet de reguli de limbă în ciornă? Nu sunt disponibile reguli publicate pentru limba implicită ca alternativă.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/ru/boardrules_acp.php b/language/ru/boardrules_acp.php index 10ca548c..2c0ed410 100644 --- a/language/ru/boardrules_acp.php +++ b/language/ru/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Управляйте всеми установленными языками в одном месте. Скопируйте полный набор правил на другой язык, переведите его как черновик и опубликуйте, когда он будет готов.', 'ACP_BOARDRULES_RULES' => 'Правила', 'ACP_BOARDRULES_STATUS' => 'Статус', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Используется язык по умолчанию', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Используются правила на языке по умолчанию', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Нет правил', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Черновик', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Опубликовано', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d конфликтующих якорей переименовано с добавлением числовых суффиксов.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Выбранный язык не установлен.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Пустой набор правил нельзя опубликовать или сделать черновиком.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Язык форума по умолчанию нельзя сделать черновиком.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Опубликовать этот полный набор правил языка? Пользователи этого языка увидят его немедленно.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Сделать этот полный набор правил языка черновиком? Пользователи этого языка вместо него увидят правила на языке форума по умолчанию.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Набор правил языка опубликован.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Правило успешно отредактировано.', 'ACP_RULE_TITLE_EMPTY' => 'Вы должны ввести заголовок для правила', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Опубликованные правила на языке по умолчанию недоступны.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Изменить набор правил языка по умолчанию на черновик? Пользователям, у которых нет другого опубликованного набора правил, правила будут недоступны.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Изменить этот полный набор языковых правил на черновик? Опубликованные правила языка по умолчанию недоступны в качестве резервного варианта.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Расширению не удалось заблокировать таблицу. Возможно, блокировка используется другим процессом. Разблокировка происходит принудительно через 1 час.', diff --git a/language/sk/boardrules_acp.php b/language/sk/boardrules_acp.php index 50ee44e6..b5576418 100644 --- a/language/sk/boardrules_acp.php +++ b/language/sk/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Spravujte všetky nainštalované jazyky na jednom mieste. Skopírujte kompletnú sadu pravidiel do iného jazyka, preložte ju ako koncept a po dokončení ju zverejnite.', 'ACP_BOARDRULES_RULES' => 'Pravidlá', 'ACP_BOARDRULES_STATUS' => 'Stav', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Používa sa predvolený jazyk', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Používajú sa pravidlá v predvolenom jazyku', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Žiadne pravidlá', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Koncept', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Zverejnené', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d konfliktných odkazov bolo premenovaných pomocou číselných prípon.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Vybraný jazyk nie je nainštalovaný.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Prázdnu sadu pravidiel nemožno zverejniť ani nastaviť ako koncept.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Predvolený jazyk fóra nemožno nastaviť ako koncept.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Zverejniť túto kompletnú jazykovú sadu pravidiel? Používatelia tohto jazyka ju uvidia okamžite.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Nastaviť túto kompletnú jazykovú sadu pravidiel ako koncept? Používatelia tohto jazyka namiesto nej uvidia pravidlá v predvolenom jazyku fóra.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Jazyková sada pravidiel bola zverejnená.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Pravidlo bolo úspešne upravené.', 'ACP_RULE_TITLE_EMPTY' => 'Musíš zadať názov pravidla.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Nie sú k dispozícii žiadne zverejnené pravidlá v predvolenom jazyku.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Zmeniť sadu pravidiel predvoleného jazyka na koncept? Používatelia bez inej zverejnenej sady pravidiel nebudú mať k dispozícii žiadne pravidlá.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Zmeniť túto úplnú sadu jazykových pravidiel na koncept? Nie sú k dispozícii žiadne zverejnené pravidlá predvoleného jazyka ako náhrada.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Board rules failed to acquire the table lock. Another process may be holding the lock. Locks are forcibly released after a timeout of 1 hour.', diff --git a/language/sv/boardrules_acp.php b/language/sv/boardrules_acp.php index c393e07b..df8ecfcd 100644 --- a/language/sv/boardrules_acp.php +++ b/language/sv/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Hantera alla installerade språk på ett ställe. Kopiera en fullständig regeluppsättning till ett annat språk, översätt den som ett utkast och publicera den när den är klar.', 'ACP_BOARDRULES_RULES' => 'Regler', 'ACP_BOARDRULES_STATUS' => 'Status', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Använder standardspråk', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Använder regler på standardspråket', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Inga regler', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Utkast', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Publicerad', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d ankare med konflikter döptes om med numeriska suffix.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Det valda språket är inte installerat.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'En tom regeluppsättning kan inte publiceras eller anges som utkast.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Forumets standardspråk kan inte anges som utkast.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Publicera hela denna regeluppsättning? Användare av detta språk ser den omedelbart.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Ändra hela denna regeluppsättning till utkast? Användare av detta språk ser i stället reglerna på forumets standardspråk.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Språkets regeluppsättning publicerades.', @@ -135,6 +134,10 @@ 'ACP_RULE_EDITED' => 'Regeln har uppdaterats.', 'ACP_RULE_TITLE_EMPTY' => 'Du måste ange en rubrik för denna regel.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Det finns inga publicerade regler på standardspråket.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Ändra regeluppsättningen för standardspråket till utkast? Användare utan en annan publicerad regeluppsättning har inga tillgängliga regler.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Ändra denna fullständiga språkregeluppsättning till utkast? Det finns inga publicerade regler på standardspråket som reserv.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Forumreglerna kunde ej låsa tabellen. En annan process verkar låsa tabellen. Lås öppnas efter en timme.', diff --git a/language/tr/boardrules_acp.php b/language/tr/boardrules_acp.php index 07cfd96f..1f546be0 100644 --- a/language/tr/boardrules_acp.php +++ b/language/tr/boardrules_acp.php @@ -76,7 +76,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Yüklü tüm dilleri tek bir yerden yönetin. Tam bir kural kümesini başka bir dile kopyalayın, taslak olarak çevirin ve hazır olduğunda yayınlayın.', 'ACP_BOARDRULES_RULES' => 'Kurallar', 'ACP_BOARDRULES_STATUS' => 'Durum', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Varsayılan dil kullanılıyor', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Varsayılan dil kuralları kullanılıyor', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Kural yok', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Taslak', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Yayınlandı', @@ -105,7 +105,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d çakışan bağlantı çapası sayısal son eklerle yeniden adlandırıldı.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Seçilen dil yüklü değil.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Boş bir kural kümesi yayınlanamaz veya taslak olarak değiştirilemez.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Forumun varsayılan dili taslak olarak değiştirilemez.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Bu dilin tüm kural kümesi yayınlansın mı? Bu dili kullananlar kural kümesini hemen görecek.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Bu dilin tüm kural kümesi taslak olarak değiştirilsin mi? Bu dili kullananlar bunun yerine forumun varsayılan dilindeki kuralları görecek.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Dil kural kümesi yayınlandı.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Kural başarıyla düzenlendi.', 'ACP_RULE_TITLE_EMPTY' => 'Bu kural için bir başlık girmelisiniz.', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Yayınlanmış varsayılan dil kuralları mevcut değil.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Varsayılan dil kural seti taslak olarak değiştirilsin mi? Yayınlanmış başka bir kural seti olmayan kullanıcılar için hiçbir kural mevcut olmayacaktır.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Bu dil kural setinin tamamı taslak olarak değiştirilsin mi? Geri dönüş olarak yayınlanmış hiçbir varsayılan dil kuralı mevcut değildir.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Mesaj panosu kuralları tablo kilidini alamadı. Başka bir işlem kilidi tutuyor olabilir. Kilitler, 1 saatlik zamanaşımı sonrasında serbest bırakılmaya zorlanır.', diff --git a/language/uk/boardrules_acp.php b/language/uk/boardrules_acp.php index e167c3dd..a2f65528 100644 --- a/language/uk/boardrules_acp.php +++ b/language/uk/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => 'Керуйте всіма встановленими мовами в одному місці. Скопіюйте повний набір правил іншою мовою, перекладіть його як чернетку та опублікуйте, коли він буде готовий.', 'ACP_BOARDRULES_RULES' => 'Правила', 'ACP_BOARDRULES_STATUS' => 'Статус', - 'ACP_BOARDRULES_STATUS_EMPTY' => 'Використовується типова мова', + 'ACP_BOARDRULES_STATUS_EMPTY' => 'Використовуються правила типовою мовою', 'ACP_BOARDRULES_STATUS_NO_RULES' => 'Немає правил', 'ACP_BOARDRULES_STATUS_DRAFT' => 'Чернетка', 'ACP_BOARDRULES_STATUS_PUBLISHED' => 'Опубліковано', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d конфліктних якорів перейменовано з додаванням числових суфіксів.', 'ACP_BOARDRULES_INVALID_LANGUAGE' => 'Вибрану мову не встановлено.', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => 'Порожній набір правил не можна опублікувати або зробити чернеткою.', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => 'Типову мову форуму не можна зробити чернеткою.', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => 'Опублікувати цей повний набір правил мови? Користувачі цієї мови побачать його негайно.', 'ACP_BOARDRULES_DRAFT_CONFIRM' => 'Зробити цей повний набір правил мови чернеткою? Користувачі цієї мови натомість побачать правила типовою мовою форуму.', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => 'Набір правил мови опубліковано.', @@ -134,6 +133,10 @@ 'ACP_RULE_EDITED' => 'Правило успішно відредаговано.', 'ACP_RULE_TITLE_EMPTY' => 'Ви повинні ввести заголовок для правила', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => 'Опубліковані правила типовою мовою недоступні.', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => 'Змінити набір правил типової мови на чернетку? Користувачі без іншого опублікованого набору правил не матимуть доступних правил.', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => 'Змінити цей повний набір мовних правил на чернетку? Опубліковані правила типовою мовою недоступні як запасний варіант.', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => 'Розширенню не вдалося заблокувати таблицю. Можливо, блокування використовується іншим процесом. Розблокування відбувається примусово через 1 годину.', diff --git a/language/zh_cmn_hans/boardrules_acp.php b/language/zh_cmn_hans/boardrules_acp.php index be3e0024..1bfa350b 100644 --- a/language/zh_cmn_hans/boardrules_acp.php +++ b/language/zh_cmn_hans/boardrules_acp.php @@ -75,7 +75,7 @@ 'ACP_BOARDRULES_LANGUAGES_EXPLAIN' => '在一个位置管理所有已安装的语言。将整套章程复制到另一种语言,以草稿形式翻译,准备好后再发布。', 'ACP_BOARDRULES_RULES' => '章程', 'ACP_BOARDRULES_STATUS' => '状态', - 'ACP_BOARDRULES_STATUS_EMPTY' => '正在使用默认语言', + 'ACP_BOARDRULES_STATUS_EMPTY' => '正在使用默认语言规则', 'ACP_BOARDRULES_STATUS_NO_RULES' => '无章程', 'ACP_BOARDRULES_STATUS_DRAFT' => '草稿', 'ACP_BOARDRULES_STATUS_PUBLISHED' => '已发布', @@ -104,7 +104,6 @@ 'ACP_BOARDRULES_COPY_ANCHORS_RENAMED' => '%d 个冲突的锚点已使用数字后缀重命名。', 'ACP_BOARDRULES_INVALID_LANGUAGE' => '所选语言尚未安装。', 'ACP_BOARDRULES_STATUS_CHANGE_EMPTY' => '空章程集不能发布或改为草稿。', - 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT' => '论坛默认语言不能改为草稿。', 'ACP_BOARDRULES_PUBLISH_CONFIRM' => '发布此语言的完整章程集吗?使用此语言的用户将立即看到它。', 'ACP_BOARDRULES_DRAFT_CONFIRM' => '将此语言的完整章程集改为草稿吗?使用此语言的用户将改为看到论坛默认语言的章程。', 'ACP_BOARDRULES_PUBLISH_SUCCESS' => '语言章程集已发布。', @@ -133,6 +132,10 @@ 'ACP_RULE_EDITED' => '章程编辑成功。', 'ACP_RULE_TITLE_EMPTY' => '您必须输入一个标题。', + 'ACP_BOARDRULES_FALLBACK_UNAVAILABLE' => '没有可用的默认语言已发布规则。', + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM' => '将默认语言规则集改为草稿吗?没有其他已发布规则集的用户将没有可用规则。', + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM' => '将这个完整的语言规则集改为草稿吗?没有已发布的默认语言规则可用作后备。', + // Nested set exception messages (only appears in PHP error logging) // Translators: Feel free to not translate these language strings 'RULES_NESTEDSET_LOCK_FAILED_ACQUIRE' => '论坛章程无法锁定,可能已被其它进程锁定了。锁会在超时一个小时后强制释放。 ', diff --git a/tests/controller/admin_controller_test.php b/tests/controller/admin_controller_test.php index 8d0e6ba5..9be6656f 100644 --- a/tests/controller/admin_controller_test.php +++ b/tests/controller/admin_controller_test.php @@ -291,10 +291,21 @@ public function test_display_language_dashboard_covers_empty_published_and_draft self::assertTrue($this->blocks['languages'][0]['S_PUBLISHED']); self::assertFalse($this->blocks['languages'][0]['S_DRAFT']); self::assertTrue($this->blocks['languages'][1]['S_EMPTY']); + self::assertTrue($this->blocks['languages'][1]['S_FALLBACK_AVAILABLE']); self::assertTrue($this->blocks['languages'][1]['S_CAN_COPY']); self::assertTrue($this->assigned_vars['S_LANGUAGE_DASHBOARD']); } + public function test_language_dashboard_reports_unavailable_fallback_when_default_is_draft(): void + { + $this->ruleset_operator->set_published('en', false); + $this->controller->display_language_dashboard(); + + self::assertTrue($this->blocks['languages'][0]['S_DRAFT']); + self::assertFalse($this->blocks['languages'][1]['S_FALLBACK_AVAILABLE']); + self::assertFalse($this->blocks['languages'][2]['S_FALLBACK_AVAILABLE']); + } + public function test_display_rules_uses_real_tree_and_skips_nested_children(): void { $this->controller->display_rules('en'); @@ -317,6 +328,23 @@ public function test_display_rules_uses_real_tree_and_skips_nested_children(): v ), admin_test_state::$form_key_suffixes); } + public function test_display_rules_reports_available_default_fallback(): void + { + $this->controller->display_rules('fr'); + + self::assertTrue($this->assigned_vars['S_RULESET_EMPTY']); + self::assertTrue($this->assigned_vars['S_FALLBACK_AVAILABLE']); + } + + public function test_display_rules_reports_unavailable_fallback_when_default_is_draft(): void + { + $this->ruleset_operator->set_published('en', false); + $this->controller->display_rules('fr'); + + self::assertTrue($this->assigned_vars['S_RULESET_EMPTY']); + self::assertFalse($this->assigned_vars['S_FALLBACK_AVAILABLE']); + } + public function test_display_rules_escapes_encoded_sitename_once(): void { $this->config->set('sitename', 'Extension "Development"'); @@ -481,6 +509,29 @@ public function test_ruleset_status_confirmation_preserves_dashboard_return(): v self::assertSame(array('adm.php?i=boardrules'), admin_test_state::$redirects); } + public function test_default_draft_confirmation_warns_about_fallback_impact(): void + { + admin_test_state::$confirm = false; + $this->controller->set_ruleset_published('en', false); + + self::assertSame( + 'ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM', + admin_test_state::$confirmations[0]['title'] + ); + } + + public function test_draft_confirmation_reports_unavailable_fallback(): void + { + $this->ruleset_operator->set_published('en', false); + admin_test_state::$confirm = false; + $this->controller->set_ruleset_published('fr', false); + + self::assertSame( + 'ACP_BOARDRULES_DRAFT_NO_FALLBACK_CONFIRM', + admin_test_state::$confirmations[0]['title'] + ); + } + public function test_ruleset_can_be_published(): void { $this->log->expects(self::once())->method('add'); @@ -488,9 +539,10 @@ public function test_ruleset_can_be_published(): void $this->controller->set_ruleset_published('en', true); } - public function test_default_ruleset_cannot_be_drafted(): void + public function test_default_ruleset_can_be_returned_to_draft(): void { - $this->setExpectedTriggerError(E_USER_WARNING, 'ACP_BOARDRULES_DEFAULT_CANNOT_DRAFT'); + $this->log->expects(self::once())->method('add'); + $this->setExpectedTriggerError(E_USER_NOTICE, 'ACP_BOARDRULES_DRAFT_SUCCESS'); $this->controller->set_ruleset_published('en', false); } diff --git a/tests/controller/main_controller_test.php b/tests/controller/main_controller_test.php index ad01a9d9..9423d3b2 100644 --- a/tests/controller/main_controller_test.php +++ b/tests/controller/main_controller_test.php @@ -33,6 +33,7 @@ public function display_data() 'get_message_for_display' => 'content', ], true, + true, ), 'A category' => array( 200, @@ -45,6 +46,7 @@ public function display_data() 'get_message_for_display' => 'content', ], true, + true, ), 'A draft language falls back to published default rules' => array( 200, @@ -57,6 +59,20 @@ public function display_data() 'get_message_for_display' => 'fallback content', ], false, + true, + ), + 'A draft language has no fallback when default rules are draft' => array( + 200, + '@phpbb_boardrules/boardrules_controller.html', + [ + 'get_left_id' => 1, + 'get_right_id' => 2, + 'get_anchor' => '', + 'get_title' => 'hidden fallback title', + 'get_message_for_display' => 'hidden fallback content', + ], + false, + false, ), ); } @@ -66,7 +82,7 @@ public function display_data() * * @dataProvider display_data */ - public function test_display($status_code, $page_content, $rule_data, $language_published) + public function test_display($status_code, $page_content, $rule_data, $language_published, $default_published) { global $config, $user, $phpbb_root_path, $phpEx; @@ -91,7 +107,8 @@ public function test_display($status_code, $page_content, $rule_data, $language_ $rule_operator = $this->getMockBuilder('\phpbb\boardrules\operators\rule') ->disableOriginalConstructor() ->getMock(); - $rule_operator->expects($language_published ? self::exactly(2) : self::once()) + $rule_calls = $language_published ? self::exactly(2) : ($default_published ? self::once() : self::never()); + $rule_operator->expects($rule_calls) ->method('get_rules') ->willReturnCallback(function ($language) use ($entity) { return $language === 'fr' ? array() : array($entity); @@ -101,8 +118,8 @@ public function test_display($status_code, $page_content, $rule_data, $language_ ->disableOriginalConstructor() ->getMock(); $ruleset_operator->method('is_published') - ->willReturnCallback(function ($language) use ($language_published) { - return $language === 'fr' ? $language_published : true; + ->willReturnCallback(function ($language) use ($language_published, $default_published) { + return $language === 'fr' ? $language_published : $default_published; }); $ruleset_operator->method('get_intro_text')->willReturn('Custom & details'); diff --git a/tests/functional/admin_controller_test.php b/tests/functional/admin_controller_test.php index 6ca3050a..251a2fb5 100644 --- a/tests/functional/admin_controller_test.php +++ b/tests/functional/admin_controller_test.php @@ -38,6 +38,36 @@ public function test_acp_module() return $crawler; } + /** + * Test drafting default rules removes fallback availability without exposing drafts. + */ + public function test_default_ruleset_draft_reports_unavailable_fallback() + { + $this->login(); + $this->admin_login(); + $this->get_db(); + + $draft_url = "adm/index.php?i=\\phpbb\\boardrules\\acp\\boardrules_module&mode=manage&action=draft&language=en&sid={$this->sid}"; + $crawler = self::request('GET', $draft_url); + $this->assertContainsLang('ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM', $crawler->text()); + + try + { + $form = $crawler->selectButton('confirm')->form(); + $crawler = self::submit($form); + $this->assertContainsLang('ACP_BOARDRULES_DRAFT_SUCCESS', $crawler->text()); + + $crawler = self::request('GET', "adm/index.php?i=\\phpbb\\boardrules\\acp\\boardrules_module&mode=manage&sid={$this->sid}"); + $this->assertContainsLang('ACP_BOARDRULES_FALLBACK_UNAVAILABLE', $crawler->filter('#main')->text()); + } + finally + { + $this->db->sql_query("UPDATE phpbb_boardrules_rulesets + SET rules_published = 1 + WHERE language_iso = 'en'"); + } + } + /** * Test the per-language rules page introduction editor and public output. */ From 98fd516e259a3316752d4ea347401c68beef5aba Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 23 Aug 2026 09:03:48 -0700 Subject: [PATCH 6/6] Fix tests --- tests/functional/admin_controller_test.php | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/functional/admin_controller_test.php b/tests/functional/admin_controller_test.php index 251a2fb5..3dd43700 100644 --- a/tests/functional/admin_controller_test.php +++ b/tests/functional/admin_controller_test.php @@ -47,12 +47,24 @@ public function test_default_ruleset_draft_reports_unavailable_fallback() $this->admin_login(); $this->get_db(); - $draft_url = "adm/index.php?i=\\phpbb\\boardrules\\acp\\boardrules_module&mode=manage&action=draft&language=en&sid={$this->sid}"; - $crawler = self::request('GET', $draft_url); - $this->assertContainsLang('ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM', $crawler->text()); + $test_language = 'zz_boardrules_test'; + $this->db->sql_query("DELETE FROM phpbb_lang + WHERE lang_iso = '" . $this->db->sql_escape($test_language) . "'"); + $sql = 'INSERT INTO phpbb_lang ' . $this->db->sql_build_array('INSERT', array( + 'lang_iso' => $test_language, + 'lang_dir' => 'en', + 'lang_english_name' => 'Board Rules Test', + 'lang_local_name' => 'Board Rules Test', + 'lang_author' => 'phpBB Limited', + )); + $this->db->sql_query($sql); try { + $draft_url = "adm/index.php?i=\\phpbb\\boardrules\\acp\\boardrules_module&mode=manage&action=draft&language=en&sid={$this->sid}"; + $crawler = self::request('GET', $draft_url); + $this->assertContainsLang('ACP_BOARDRULES_DRAFT_DEFAULT_CONFIRM', $crawler->text()); + $form = $crawler->selectButton('confirm')->form(); $crawler = self::submit($form); $this->assertContainsLang('ACP_BOARDRULES_DRAFT_SUCCESS', $crawler->text()); @@ -65,6 +77,8 @@ public function test_default_ruleset_draft_reports_unavailable_fallback() $this->db->sql_query("UPDATE phpbb_boardrules_rulesets SET rules_published = 1 WHERE language_iso = 'en'"); + $this->db->sql_query("DELETE FROM phpbb_lang + WHERE lang_iso = '" . $this->db->sql_escape($test_language) . "'"); } }