CompositeBlock.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. /**
  3. * Class for blocks composed from multiple blocks.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. namespace MediaWiki\Block;
  23. use IContextSource;
  24. use Title;
  25. /**
  26. * Multiple Block class.
  27. *
  28. * Multiple blocks exist to enforce restrictions from more than one block, if several
  29. * blocks apply to a user/IP. Multiple blocks are created temporarily on enforcement.
  30. *
  31. * @since 1.34
  32. */
  33. class CompositeBlock extends AbstractBlock {
  34. /** @var AbstractBlock[] */
  35. private $originalBlocks;
  36. /**
  37. * Create a new block with specified parameters on a user, IP or IP range.
  38. *
  39. * @param array $options Parameters of the block, with options supported by
  40. * `AbstractBlock::__construct`, and also:
  41. * - originalBlocks: (Block[]) Blocks that this block is composed from
  42. */
  43. public function __construct( array $options = [] ) {
  44. parent::__construct( $options );
  45. $defaults = [
  46. 'originalBlocks' => [],
  47. ];
  48. $options += $defaults;
  49. $this->originalBlocks = $options[ 'originalBlocks' ];
  50. $this->setHideName( $this->propHasValue( 'mHideName', true ) );
  51. $this->isSitewide( $this->propHasValue( 'isSitewide', true ) );
  52. $this->isEmailBlocked( $this->propHasValue( 'mBlockEmail', true ) );
  53. $this->isCreateAccountBlocked( $this->propHasValue( 'blockCreateAccount', true ) );
  54. $this->isUsertalkEditAllowed( !$this->propHasValue( 'allowUsertalk', false ) );
  55. }
  56. /**
  57. * Determine whether any original blocks have a particular property set to a
  58. * particular value.
  59. *
  60. * @param string $prop
  61. * @param mixed $value
  62. * @return bool At least one block has the property set to the value
  63. */
  64. private function propHasValue( $prop, $value ) {
  65. foreach ( $this->originalBlocks as $block ) {
  66. if ( $block->$prop == $value ) {
  67. return true;
  68. }
  69. }
  70. return false;
  71. }
  72. /**
  73. * Determine whether any original blocks have a particular method returning a
  74. * particular value.
  75. *
  76. * @param string $method
  77. * @param mixed $value
  78. * @param mixed ...$params
  79. * @return bool At least one block has the method returning the value
  80. */
  81. private function methodReturnsValue( $method, $value, ...$params ) {
  82. foreach ( $this->originalBlocks as $block ) {
  83. if ( $block->$method( ...$params ) == $value ) {
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. /**
  90. * Get the original blocks from which this block is composed
  91. *
  92. * @since 1.34
  93. * @return AbstractBlock[]
  94. */
  95. public function getOriginalBlocks() {
  96. return $this->originalBlocks;
  97. }
  98. /**
  99. * @inheritDoc
  100. */
  101. public function getExpiry() {
  102. $maxExpiry = null;
  103. foreach ( $this->originalBlocks as $block ) {
  104. $expiry = $block->getExpiry();
  105. if ( $maxExpiry === null || $expiry === '' || $expiry > $maxExpiry ) {
  106. $maxExpiry = $expiry;
  107. }
  108. }
  109. return $maxExpiry;
  110. }
  111. /**
  112. * Get the IDs for the original blocks, ignoring any that are null
  113. *
  114. * @return int[]
  115. */
  116. protected function getIds() {
  117. $ids = [];
  118. foreach ( $this->originalBlocks as $block ) {
  119. $id = $block->getId();
  120. if ( $id !== null ) {
  121. $ids[] = $id;
  122. }
  123. }
  124. return $ids;
  125. }
  126. /**
  127. * @inheritDoc
  128. */
  129. public function getPermissionsError( IContextSource $context ) {
  130. $params = $this->getBlockErrorParams( $context );
  131. $ids = implode( ', ', array_map( function ( $id ) {
  132. return '#' . $id;
  133. }, $this->getIds() ) );
  134. if ( $ids === '' ) {
  135. $idsMsg = $context->msg( 'blockedtext-composite-no-ids' )->plain();
  136. } else {
  137. $idsMsg = $context->msg( 'blockedtext-composite-ids', [ $ids ] )->plain();
  138. }
  139. // TODO: Clean up error messages params so we don't have to do this (T227174)
  140. $params[ 4 ] = $idsMsg;
  141. $msg = 'blockedtext-composite';
  142. array_unshift( $params, $msg );
  143. return $params;
  144. }
  145. /**
  146. * @inheritDoc
  147. *
  148. * Determines whether the CompositeBlock applies to a right by checking
  149. * whether the original blocks apply to that right. Each block can report
  150. * true (applies), false (does not apply) or null (unsure). Then:
  151. * - If any original blocks apply, this block applies
  152. * - If no original blocks apply but any are unsure, this block is unsure
  153. * - If all blocks do not apply, this block does not apply
  154. */
  155. public function appliesToRight( $right ) {
  156. $isUnsure = false;
  157. foreach ( $this->originalBlocks as $block ) {
  158. $appliesToRight = $block->appliesToRight( $right );
  159. if ( $appliesToRight ) {
  160. return true;
  161. } elseif ( $appliesToRight === null ) {
  162. $isUnsure = true;
  163. }
  164. }
  165. return $isUnsure ? null : false;
  166. }
  167. /**
  168. * @inheritDoc
  169. */
  170. public function appliesToUsertalk( Title $usertalk = null ) {
  171. return $this->methodReturnsValue( __FUNCTION__, true, $usertalk );
  172. }
  173. /**
  174. * @inheritDoc
  175. */
  176. public function appliesToTitle( Title $title ) {
  177. return $this->methodReturnsValue( __FUNCTION__, true, $title );
  178. }
  179. /**
  180. * @inheritDoc
  181. */
  182. public function appliesToNamespace( $ns ) {
  183. return $this->methodReturnsValue( __FUNCTION__, true, $ns );
  184. }
  185. /**
  186. * @inheritDoc
  187. */
  188. public function appliesToPage( $pageId ) {
  189. return $this->methodReturnsValue( __FUNCTION__, true, $pageId );
  190. }
  191. /**
  192. * @inheritDoc
  193. */
  194. public function appliesToPasswordReset() {
  195. return $this->methodReturnsValue( __FUNCTION__, true );
  196. }
  197. }