Merge "Add .pipeline/ with dev image variant"
[lhc/web/wiklou.git] / includes / block / CompositeBlock.php
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
23 namespace MediaWiki\Block;
24
25 use IContextSource;
26 use Title;
27
28 /**
29 * Multiple Block class.
30 *
31 * Multiple blocks exist to enforce restrictions from more than one block, if several
32 * blocks apply to a user/IP. Multiple blocks are created temporarily on enforcement.
33 *
34 * @since 1.34
35 */
36 class CompositeBlock extends AbstractBlock {
37 /** @var AbstractBlock[] */
38 private $originalBlocks;
39
40 /**
41 * Create a new block with specified parameters on a user, IP or IP range.
42 *
43 * @param array $options Parameters of the block, with options supported by
44 * `AbstractBlock::__construct`, and also:
45 * - originalBlocks: (Block[]) Blocks that this block is composed from
46 */
47 public function __construct( array $options = [] ) {
48 parent::__construct( $options );
49
50 $defaults = [
51 'originalBlocks' => [],
52 ];
53
54 $options += $defaults;
55
56 $this->originalBlocks = $options[ 'originalBlocks' ];
57
58 $this->setHideName( $this->propHasValue( 'mHideName', true ) );
59 $this->isSitewide( $this->propHasValue( 'isSitewide', true ) );
60 $this->isEmailBlocked( $this->propHasValue( 'mBlockEmail', true ) );
61 $this->isCreateAccountBlocked( $this->propHasValue( 'blockCreateAccount', true ) );
62 $this->isUsertalkEditAllowed( !$this->propHasValue( 'allowUsertalk', false ) );
63 }
64
65 /**
66 * Determine whether any original blocks have a particular property set to a
67 * particular value.
68 *
69 * @param string $prop
70 * @param mixed $value
71 * @return bool At least one block has the property set to the value
72 */
73 private function propHasValue( $prop, $value ) {
74 foreach ( $this->originalBlocks as $block ) {
75 if ( $block->$prop == $value ) {
76 return true;
77 }
78 }
79 return false;
80 }
81
82 /**
83 * Determine whether any original blocks have a particular method returning a
84 * particular value.
85 *
86 * @param string $method
87 * @param mixed $value
88 * @param mixed ...$params
89 * @return bool At least one block has the method returning the value
90 */
91 private function methodReturnsValue( $method, $value, ...$params ) {
92 foreach ( $this->originalBlocks as $block ) {
93 if ( $block->$method( ...$params ) == $value ) {
94 return true;
95 }
96 }
97 return false;
98 }
99
100 /**
101 * Get the original blocks from which this block is composed
102 *
103 * @since 1.34
104 * @return AbstractBlock[]
105 */
106 public function getOriginalBlocks() {
107 return $this->originalBlocks;
108 }
109
110 /**
111 * @inheritDoc
112 */
113 public function getExpiry() {
114 $maxExpiry = null;
115 foreach ( $this->originalBlocks as $block ) {
116 $expiry = $block->getExpiry();
117 if ( $maxExpiry === null || $expiry === '' || $expiry > $maxExpiry ) {
118 $maxExpiry = $expiry;
119 }
120 }
121 return $maxExpiry;
122 }
123
124 /**
125 * Get the IDs for the original blocks, ignoring any that are null
126 *
127 * @return int[]
128 */
129 protected function getIds() {
130 $ids = [];
131 foreach ( $this->originalBlocks as $block ) {
132 $id = $block->getId();
133 if ( $id !== null ) {
134 $ids[] = $id;
135 }
136 }
137 return $ids;
138 }
139
140 /**
141 * @inheritDoc
142 */
143 public function getPermissionsError( IContextSource $context ) {
144 $params = $this->getBlockErrorParams( $context );
145
146 $ids = implode( ', ', array_map( function ( $id ) {
147 return '#' . $id;
148 }, $this->getIds() ) );
149 if ( $ids === '' ) {
150 $idsMsg = $context->msg( 'blockedtext-composite-no-ids' )->plain();
151 } else {
152 $idsMsg = $context->msg( 'blockedtext-composite-ids', [ $ids ] )->plain();
153 }
154
155 // TODO: Clean up error messages params so we don't have to do this (T227174)
156 $params[ 4 ] = $idsMsg;
157
158 $msg = 'blockedtext-composite';
159
160 array_unshift( $params, $msg );
161
162 return $params;
163 }
164
165 /**
166 * @inheritDoc
167 *
168 * Determines whether the CompositeBlock applies to a right by checking
169 * whether the original blocks apply to that right. Each block can report
170 * true (applies), false (does not apply) or null (unsure). Then:
171 * - If any original blocks apply, this block applies
172 * - If no original blocks apply but any are unsure, this block is unsure
173 * - If all blocks do not apply, this block does not apply
174 */
175 public function appliesToRight( $right ) {
176 $isUnsure = false;
177
178 foreach ( $this->originalBlocks as $block ) {
179 $appliesToRight = $block->appliesToRight( $right );
180
181 if ( $appliesToRight ) {
182 return true;
183 } elseif ( $appliesToRight === null ) {
184 $isUnsure = true;
185 }
186 }
187
188 return $isUnsure ? null : false;
189 }
190
191 /**
192 * @inheritDoc
193 */
194 public function appliesToUsertalk( Title $usertalk = null ) {
195 return $this->methodReturnsValue( __FUNCTION__, true, $usertalk );
196 }
197
198 /**
199 * @inheritDoc
200 */
201 public function appliesToTitle( Title $title ) {
202 return $this->methodReturnsValue( __FUNCTION__, true, $title );
203 }
204
205 /**
206 * @inheritDoc
207 */
208 public function appliesToNamespace( $ns ) {
209 return $this->methodReturnsValue( __FUNCTION__, true, $ns );
210 }
211
212 /**
213 * @inheritDoc
214 */
215 public function appliesToPage( $pageId ) {
216 return $this->methodReturnsValue( __FUNCTION__, true, $pageId );
217 }
218
219 /**
220 * @inheritDoc
221 */
222 public function appliesToPasswordReset() {
223 return $this->methodReturnsValue( __FUNCTION__, true );
224 }
225
226 }