Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.84% |
85 / 86 |
|
96.67% |
29 / 30 |
CRAP | |
0.00% |
0 / 1 |
RevisionRecord | |
98.84% |
85 / 86 |
|
96.67% |
29 / 30 |
54 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
hasSameContent | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
getMainContentRaw | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getContent | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getMainContentModel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getContentOrThrow | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getSlot | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
hasSlot | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSlotRoles | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSlots | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOriginalSlots | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInheritedSlots | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPrimarySlots | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getParentId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getSize | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getSha1 | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getPageId | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getWikiId | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageAsLinkTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPage | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUser | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getComment | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
isMinor | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isDeleted | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getVisibility | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTimestamp | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
audienceCan | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
userCan | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
userCanBitfield | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
7 | |||
isReadyForInsertion | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
4 | |||
isCurrent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Page revision base class. |
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\Revision; |
24 | |
25 | use InvalidArgumentException; |
26 | use MediaWiki\CommentStore\CommentStoreComment; |
27 | use MediaWiki\Content\Content; |
28 | use MediaWiki\DAO\WikiAwareEntity; |
29 | use MediaWiki\DAO\WikiAwareEntityTrait; |
30 | use MediaWiki\Linker\LinkTarget; |
31 | use MediaWiki\Page\LegacyArticleIdAccess; |
32 | use MediaWiki\Page\PageIdentity; |
33 | use MediaWiki\Permissions\Authority; |
34 | use MediaWiki\Title\Title; |
35 | use MediaWiki\User\UserIdentity; |
36 | use Wikimedia\NonSerializable\NonSerializableTrait; |
37 | |
38 | /** |
39 | * Page revision base class. |
40 | * |
41 | * RevisionRecords are considered value objects, but they may use callbacks for lazy loading. |
42 | * Note that while the base class has no setters, subclasses may offer a mutable interface. |
43 | * |
44 | * @since 1.31 |
45 | * @since 1.32 Renamed from MediaWiki\Storage\RevisionRecord |
46 | */ |
47 | abstract class RevisionRecord implements WikiAwareEntity { |
48 | use LegacyArticleIdAccess; |
49 | use NonSerializableTrait; |
50 | use WikiAwareEntityTrait; |
51 | |
52 | // RevisionRecord deletion constants |
53 | public const DELETED_TEXT = 1; |
54 | public const DELETED_COMMENT = 2; |
55 | public const DELETED_USER = 4; |
56 | public const DELETED_RESTRICTED = 8; |
57 | public const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED; // convenience |
58 | public const SUPPRESSED_ALL = self::DELETED_TEXT | self::DELETED_COMMENT | self::DELETED_USER | |
59 | self::DELETED_RESTRICTED; // convenience |
60 | |
61 | // Audience options for accessors |
62 | public const FOR_PUBLIC = 1; |
63 | public const FOR_THIS_USER = 2; |
64 | public const RAW = 3; |
65 | |
66 | /** @var string|false Wiki ID; false means the current wiki */ |
67 | protected $wikiId = false; |
68 | /** @var int|null */ |
69 | protected $mId; |
70 | /** @var int */ |
71 | protected $mPageId; |
72 | /** @var UserIdentity|null */ |
73 | protected $mUser; |
74 | /** @var bool */ |
75 | protected $mMinorEdit = false; |
76 | /** @var string|null */ |
77 | protected $mTimestamp; |
78 | /** @var int using the DELETED_XXX and SUPPRESSED_XXX flags */ |
79 | protected $mDeleted = 0; |
80 | /** @var int|null */ |
81 | protected $mSize; |
82 | /** @var string|null */ |
83 | protected $mSha1; |
84 | /** @var int|null */ |
85 | protected $mParentId; |
86 | /** @var CommentStoreComment|null */ |
87 | protected $mComment; |
88 | |
89 | /** @var PageIdentity */ |
90 | protected $mPage; |
91 | |
92 | /** @var RevisionSlots */ |
93 | protected $mSlots; |
94 | |
95 | /** |
96 | * @note Avoid calling this constructor directly. Use the appropriate methods |
97 | * in RevisionStore instead. |
98 | * |
99 | * @param PageIdentity $page The page this RevisionRecord is associated with. |
100 | * @param RevisionSlots $slots The slots of this revision. |
101 | * @param false|string $wikiId Relevant wiki id or self::LOCAL for the current one. |
102 | */ |
103 | public function __construct( PageIdentity $page, RevisionSlots $slots, $wikiId = self::LOCAL ) { |
104 | $this->assertWikiIdParam( $wikiId ); |
105 | |
106 | $this->mPage = $page; |
107 | $this->mSlots = $slots; |
108 | $this->wikiId = $wikiId; |
109 | $this->mPageId = $this->getArticleId( $page ); |
110 | } |
111 | |
112 | /** |
113 | * @param RevisionRecord $rec |
114 | * |
115 | * @return bool True if this RevisionRecord is known to have same content as $rec. |
116 | * False if the content is different (or not known to be the same). |
117 | */ |
118 | public function hasSameContent( RevisionRecord $rec ): bool { |
119 | if ( $rec === $this ) { |
120 | return true; |
121 | } |
122 | |
123 | if ( $this->getId() !== null && $this->getId() === $rec->getId() ) { |
124 | return true; |
125 | } |
126 | |
127 | // check size before hash, since size is quicker to compute |
128 | if ( $this->getSize() !== $rec->getSize() ) { |
129 | return false; |
130 | } |
131 | |
132 | // instead of checking the hash, we could also check the content addresses of all slots. |
133 | |
134 | if ( $this->getSha1() === $rec->getSha1() ) { |
135 | return true; |
136 | } |
137 | |
138 | return false; |
139 | } |
140 | |
141 | /** |
142 | * Returns the Content of the main slot of this revision. |
143 | * |
144 | * @see getContent() |
145 | * |
146 | * @return Content|null The content of the main slot, or null on error |
147 | * @throws RevisionAccessException |
148 | */ |
149 | public function getMainContentRaw(): ?Content { |
150 | return $this->getContent( SlotRecord::MAIN, self::RAW ); |
151 | } |
152 | |
153 | /** |
154 | * Returns the Content of the given slot of this revision. |
155 | * Call getSlotNames() to get a list of available slots. |
156 | * |
157 | * Note that for mutable Content objects, each call to this method will return a |
158 | * fresh clone. |
159 | * |
160 | * Use getContentOrThrow() for more specific error information. |
161 | * |
162 | * @param string $role The role name of the desired slot |
163 | * @param int $audience |
164 | * @param Authority|null $performer user on whose behalf to check |
165 | * |
166 | * @return Content|null The content of the given slot, or null on error |
167 | * @throws RevisionAccessException |
168 | */ |
169 | public function getContent( $role, $audience = self::FOR_PUBLIC, ?Authority $performer = null ): ?Content { |
170 | try { |
171 | $content = $this->getSlot( $role, $audience, $performer )->getContent(); |
172 | } catch ( BadRevisionException | SuppressedDataException $e ) { |
173 | return null; |
174 | } |
175 | return $content->copy(); |
176 | } |
177 | |
178 | /** |
179 | * Returns the content model of the main slot of this revision. |
180 | * |
181 | * @return string The content model |
182 | * @throws RevisionAccessException |
183 | */ |
184 | public function getMainContentModel(): string { |
185 | return $this->getSlot( SlotRecord::MAIN, self::RAW )->getModel(); |
186 | } |
187 | |
188 | /** |
189 | * Get the Content of the given slot of this revision. |
190 | * |
191 | * @param string $role The role name of the desired slot |
192 | * @param int $audience |
193 | * @param Authority|null $performer user on whose behalf to check |
194 | * |
195 | * @return Content |
196 | * @throws SuppressedDataException if the content is not viewable by the given audience |
197 | * @throws BadRevisionException if the content is missing or corrupted |
198 | * @throws RevisionAccessException |
199 | */ |
200 | public function getContentOrThrow( $role, $audience = self::FOR_PUBLIC, ?Authority $performer = null ): Content { |
201 | if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $performer ) ) { |
202 | throw new SuppressedDataException( |
203 | 'Access to the content has been suppressed for this audience' ); |
204 | } |
205 | |
206 | $content = $this->getSlot( $role, $audience, $performer )->getContent(); |
207 | return $content->copy(); |
208 | } |
209 | |
210 | /** |
211 | * Returns meta-data for the given slot. |
212 | * |
213 | * @param string $role The role name of the desired slot |
214 | * @param int $audience |
215 | * @param Authority|null $performer user on whose behalf to check |
216 | * |
217 | * @throws RevisionAccessException if the slot does not exist or slot data |
218 | * could not be lazy-loaded. |
219 | * @return SlotRecord The slot meta-data. If access to the slot's content is forbidden, |
220 | * calling getContent() on the SlotRecord will throw an exception. |
221 | */ |
222 | public function getSlot( $role, $audience = self::FOR_PUBLIC, ?Authority $performer = null ): SlotRecord { |
223 | $slot = $this->mSlots->getSlot( $role ); |
224 | |
225 | if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $performer ) ) { |
226 | return SlotRecord::newWithSuppressedContent( $slot ); |
227 | } |
228 | |
229 | return $slot; |
230 | } |
231 | |
232 | /** |
233 | * Returns whether the given slot is defined in this revision. |
234 | * |
235 | * @param string $role The role name of the desired slot |
236 | * |
237 | * @return bool |
238 | */ |
239 | public function hasSlot( $role ): bool { |
240 | return $this->mSlots->hasSlot( $role ); |
241 | } |
242 | |
243 | /** |
244 | * Returns the slot names (roles) of all slots present in this revision. |
245 | * getContent() will succeed only for the names returned by this method. |
246 | * |
247 | * @return string[] |
248 | */ |
249 | public function getSlotRoles(): array { |
250 | return $this->mSlots->getSlotRoles(); |
251 | } |
252 | |
253 | /** |
254 | * Returns the slots defined for this revision. |
255 | * |
256 | * @note This provides access to slot content with no audience checks applied. |
257 | * Calling getContent() on the RevisionSlots object returned here, or on any |
258 | * SlotRecord it returns from getSlot(), will not fail due to access restrictions. |
259 | * If audience checks are desired, use getSlot( $role, $audience, $performer ) |
260 | * or getContent( $role, $audience, $performer ) instead. |
261 | * |
262 | * @return RevisionSlots |
263 | */ |
264 | public function getSlots(): RevisionSlots { |
265 | return $this->mSlots; |
266 | } |
267 | |
268 | /** |
269 | * Returns the slots that originate in this revision. |
270 | * |
271 | * Note that this does not include any slots inherited from some earlier revision, |
272 | * even if they are different from the slots in the immediate parent revision. |
273 | * This is the case for rollbacks: slots of a rollback revision are inherited from |
274 | * the rollback target, and are different from the slots in the parent revision, |
275 | * which was rolled back. |
276 | * |
277 | * To find all slots modified by this revision against its immediate parent |
278 | * revision, use RevisionSlotsUpdate::newFromRevisionSlots(). |
279 | * |
280 | * @return RevisionSlots |
281 | */ |
282 | public function getOriginalSlots(): RevisionSlots { |
283 | return new RevisionSlots( $this->mSlots->getOriginalSlots() ); |
284 | } |
285 | |
286 | /** |
287 | * Returns slots inherited from some previous revision. |
288 | * |
289 | * "Inherited" slots are all slots that do not originate in this revision. |
290 | * Note that these slots may still differ from the one in the parent revision. |
291 | * This is the case for rollbacks: slots of a rollback revision are inherited from |
292 | * the rollback target, and are different from the slots in the parent revision, |
293 | * which was rolled back. |
294 | * |
295 | * @return RevisionSlots |
296 | */ |
297 | public function getInheritedSlots(): RevisionSlots { |
298 | return new RevisionSlots( $this->mSlots->getInheritedSlots() ); |
299 | } |
300 | |
301 | /** |
302 | * Returns primary slots (those that are not derived). |
303 | * |
304 | * @return RevisionSlots |
305 | * @since 1.36 |
306 | */ |
307 | public function getPrimarySlots(): RevisionSlots { |
308 | return new RevisionSlots( $this->mSlots->getPrimarySlots() ); |
309 | } |
310 | |
311 | /** |
312 | * Get revision ID. Depending on the concrete subclass, this may return null if |
313 | * the revision ID is not known (e.g. because the revision does not yet exist |
314 | * in the database). |
315 | * |
316 | * MCR migration note: this replaced Revision::getId |
317 | * |
318 | * @param string|false $wikiId The wiki ID expected by the caller. |
319 | * @return int|null |
320 | */ |
321 | public function getId( $wikiId = self::LOCAL ) { |
322 | $this->deprecateInvalidCrossWiki( $wikiId, '1.36' ); |
323 | return $this->mId; |
324 | } |
325 | |
326 | /** |
327 | * Get parent revision ID (the original previous page revision). |
328 | * If there is no parent revision, this returns 0. |
329 | * If the parent revision is undefined or unknown, this returns null. |
330 | * |
331 | * @note As of MW 1.31, the database schema allows the parent ID to be |
332 | * NULL to indicate that it is unknown. |
333 | * |
334 | * MCR migration note: this replaced Revision::getParentId |
335 | * |
336 | * @param string|false $wikiId The wiki ID expected by the caller. |
337 | * @return int|null |
338 | */ |
339 | public function getParentId( $wikiId = self::LOCAL ) { |
340 | $this->deprecateInvalidCrossWiki( $wikiId, '1.36' ); |
341 | return $this->mParentId; |
342 | } |
343 | |
344 | /** |
345 | * Returns the nominal size of this revision, in bogo-bytes. |
346 | * May be calculated on the fly if not known, which may in the worst |
347 | * case may involve loading all content. |
348 | * |
349 | * MCR migration note: this replaced Revision::getSize |
350 | * |
351 | * @throws RevisionAccessException if the size was unknown and could not be calculated. |
352 | * @return int |
353 | */ |
354 | abstract public function getSize(); |
355 | |
356 | /** |
357 | * Returns the base36 sha1 of this revision. This hash is derived from the |
358 | * hashes of all slots associated with the revision. |
359 | * May be calculated on the fly if not known, which may in the worst |
360 | * case may involve loading all content. |
361 | * |
362 | * MCR migration note: this replaced Revision::getSha1 |
363 | * |
364 | * @throws RevisionAccessException if the hash was unknown and could not be calculated. |
365 | * @return string |
366 | */ |
367 | abstract public function getSha1(); |
368 | |
369 | /** |
370 | * Get the page ID. If the page does not yet exist, the page ID is 0. |
371 | * |
372 | * MCR migration note: this replaced Revision::getPage |
373 | * |
374 | * @param string|false $wikiId The wiki ID expected by the caller. |
375 | * @return int |
376 | */ |
377 | public function getPageId( $wikiId = self::LOCAL ) { |
378 | $this->deprecateInvalidCrossWiki( $wikiId, '1.36' ); |
379 | return $this->mPageId; |
380 | } |
381 | |
382 | /** |
383 | * Get the ID of the wiki this revision belongs to. |
384 | * |
385 | * @return string|false The wiki's logical name, of false to indicate the local wiki. |
386 | */ |
387 | public function getWikiId() { |
388 | return $this->wikiId; |
389 | } |
390 | |
391 | /** |
392 | * Returns the title of the page this revision is associated with as a LinkTarget object. |
393 | * |
394 | * @throws InvalidArgumentException if this revision does not belong to a local wiki |
395 | * @return LinkTarget |
396 | */ |
397 | public function getPageAsLinkTarget() { |
398 | // TODO: Should be TitleValue::newFromPage( $this->mPage ), |
399 | // but Title is used too much still, so let's keep propagating it |
400 | return Title::newFromPageIdentity( $this->mPage ); |
401 | } |
402 | |
403 | /** |
404 | * Returns the page this revision belongs to. |
405 | * |
406 | * MCR migration note: this replaced Revision::getTitle |
407 | * |
408 | * @since 1.36 |
409 | * |
410 | * @return PageIdentity |
411 | */ |
412 | public function getPage(): PageIdentity { |
413 | return $this->mPage; |
414 | } |
415 | |
416 | /** |
417 | * Fetch revision's author's user identity, if it's available to the specified audience. |
418 | * If the specified audience does not have access to it, null will be |
419 | * returned. Depending on the concrete subclass, null may also be returned if the user is |
420 | * not yet specified. |
421 | * |
422 | * MCR migration note: this replaced Revision::getUser |
423 | * |
424 | * @param int $audience One of: |
425 | * RevisionRecord::FOR_PUBLIC to be displayed to all users |
426 | * RevisionRecord::FOR_THIS_USER to be displayed to the given user |
427 | * RevisionRecord::RAW get the ID regardless of permissions |
428 | * @param Authority|null $performer user on whose behalf to check |
429 | * @return UserIdentity|null |
430 | */ |
431 | public function getUser( $audience = self::FOR_PUBLIC, ?Authority $performer = null ) { |
432 | if ( !$this->audienceCan( self::DELETED_USER, $audience, $performer ) ) { |
433 | return null; |
434 | } else { |
435 | return $this->mUser; |
436 | } |
437 | } |
438 | |
439 | /** |
440 | * Fetch revision comment, if it's available to the specified audience. |
441 | * If the specified audience does not have access to the comment, |
442 | * this will return null. Depending on the concrete subclass, null may also be returned |
443 | * if the comment is not yet specified. |
444 | * |
445 | * MCR migration note: this replaced Revision::getComment |
446 | * |
447 | * @param int $audience One of: |
448 | * RevisionRecord::FOR_PUBLIC to be displayed to all users |
449 | * RevisionRecord::FOR_THIS_USER to be displayed to the given user |
450 | * RevisionRecord::RAW get the text regardless of permissions |
451 | * @param Authority|null $performer user on whose behalf to check |
452 | * |
453 | * @return CommentStoreComment|null |
454 | */ |
455 | public function getComment( $audience = self::FOR_PUBLIC, ?Authority $performer = null ) { |
456 | if ( !$this->audienceCan( self::DELETED_COMMENT, $audience, $performer ) ) { |
457 | return null; |
458 | } else { |
459 | return $this->mComment; |
460 | } |
461 | } |
462 | |
463 | /** |
464 | * MCR migration note: this replaced Revision::isMinor |
465 | * |
466 | * @return bool |
467 | */ |
468 | public function isMinor() { |
469 | return (bool)$this->mMinorEdit; |
470 | } |
471 | |
472 | /** |
473 | * MCR migration note: this replaced Revision::isDeleted |
474 | * |
475 | * @param int $field One of DELETED_* bitfield constants |
476 | * |
477 | * @return bool |
478 | */ |
479 | public function isDeleted( $field ) { |
480 | return ( $this->getVisibility() & $field ) == $field; |
481 | } |
482 | |
483 | /** |
484 | * Get the deletion bitfield of the revision |
485 | * |
486 | * MCR migration note: this replaced Revision::getVisibility |
487 | * |
488 | * @return int |
489 | */ |
490 | public function getVisibility() { |
491 | return (int)$this->mDeleted; |
492 | } |
493 | |
494 | /** |
495 | * MCR migration note: this replaced Revision::getTimestamp. |
496 | * |
497 | * May return null if the timestamp was not specified. |
498 | * |
499 | * @return string|null |
500 | */ |
501 | public function getTimestamp() { |
502 | return $this->mTimestamp; |
503 | } |
504 | |
505 | /** |
506 | * Check that the given audience has access to the given field. |
507 | * |
508 | * MCR migration note: this corresponded to Revision::userCan |
509 | * |
510 | * @param int $field One of self::DELETED_TEXT, |
511 | * self::DELETED_COMMENT, |
512 | * self::DELETED_USER |
513 | * @param int $audience One of: |
514 | * RevisionRecord::FOR_PUBLIC to be displayed to all users |
515 | * RevisionRecord::FOR_THIS_USER to be displayed to the given user |
516 | * RevisionRecord::RAW get the text regardless of permissions |
517 | * @param Authority|null $performer user on whose behalf to check |
518 | * |
519 | * @return bool |
520 | */ |
521 | public function audienceCan( $field, $audience, ?Authority $performer = null ) { |
522 | if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) { |
523 | return false; |
524 | } elseif ( $audience == self::FOR_THIS_USER ) { |
525 | if ( !$performer ) { |
526 | throw new InvalidArgumentException( |
527 | 'An Authority object must be given when checking FOR_THIS_USER audience.' |
528 | ); |
529 | } |
530 | |
531 | if ( !$this->userCan( $field, $performer ) ) { |
532 | return false; |
533 | } |
534 | } |
535 | |
536 | return true; |
537 | } |
538 | |
539 | /** |
540 | * Determine if the give authority is allowed to view a particular |
541 | * field of this revision, if it's marked as deleted. |
542 | * |
543 | * MCR migration note: this corresponded to Revision::userCan |
544 | * |
545 | * @param int $field One of self::DELETED_TEXT, |
546 | * self::DELETED_COMMENT, |
547 | * self::DELETED_USER |
548 | * @param Authority $performer user on whose behalf to check |
549 | * @return bool |
550 | */ |
551 | public function userCan( $field, Authority $performer ) { |
552 | return self::userCanBitfield( $this->getVisibility(), $field, $performer, $this->mPage ); |
553 | } |
554 | |
555 | /** |
556 | * Determine if the current user is allowed to view a particular |
557 | * field of this revision, if it's marked as deleted. This is used |
558 | * by various classes to avoid duplication. |
559 | * |
560 | * MCR migration note: this replaced Revision::userCanBitfield |
561 | * |
562 | * @param int $bitfield Current field |
563 | * @param int $field One of self::DELETED_TEXT = File::DELETED_FILE, |
564 | * self::DELETED_COMMENT = File::DELETED_COMMENT, |
565 | * self::DELETED_USER = File::DELETED_USER |
566 | * @param Authority $performer user on whose behalf to check |
567 | * @param PageIdentity|null $page A PageIdentity object to check for per-page restrictions on, |
568 | * instead of just plain user rights |
569 | * @return bool |
570 | */ |
571 | public static function userCanBitfield( $bitfield, $field, Authority $performer, ?PageIdentity $page = null ) { |
572 | if ( $bitfield & $field ) { // aspect is deleted |
573 | if ( $bitfield & self::DELETED_RESTRICTED ) { |
574 | $permissions = [ 'suppressrevision', 'viewsuppressed' ]; |
575 | } elseif ( $field & self::DELETED_TEXT ) { |
576 | $permissions = [ 'deletedtext' ]; |
577 | } else { |
578 | $permissions = [ 'deletedhistory' ]; |
579 | } |
580 | |
581 | $permissionlist = implode( ', ', $permissions ); |
582 | if ( $page === null ) { |
583 | wfDebug( "Checking for $permissionlist due to $field match on $bitfield" ); |
584 | return $performer->isAllowedAny( ...$permissions ); |
585 | } else { |
586 | wfDebug( "Checking for $permissionlist on $page due to $field match on $bitfield" ); |
587 | foreach ( $permissions as $perm ) { |
588 | if ( $performer->authorizeRead( $perm, $page ) ) { |
589 | return true; |
590 | } |
591 | } |
592 | return false; |
593 | } |
594 | } else { |
595 | return true; |
596 | } |
597 | } |
598 | |
599 | /** |
600 | * Returns whether this RevisionRecord is ready for insertion, that is, whether it contains all |
601 | * information needed to save it to the database. This should trivially be true for |
602 | * RevisionRecords loaded from the database. |
603 | * |
604 | * Note that this may return true even if getId() or getPage() return null or 0, since these |
605 | * are generally assigned while the revision is saved to the database, and may not be available |
606 | * before. |
607 | * |
608 | * @return bool |
609 | */ |
610 | public function isReadyForInsertion() { |
611 | // NOTE: don't check getSize() and getSha1(), since that may cause the full content to |
612 | // be loaded in order to calculate the values. Just assume these methods will not return |
613 | // null if mSlots is not empty. |
614 | |
615 | // NOTE: getId() and getPageId() may return null before a revision is saved, so don't |
616 | // check them. |
617 | |
618 | return $this->getTimestamp() !== null |
619 | && $this->getComment( self::RAW ) !== null |
620 | && $this->getUser( self::RAW ) !== null |
621 | && $this->mSlots->getSlotRoles() !== []; |
622 | } |
623 | |
624 | /** |
625 | * Checks whether the revision record is a stored current revision. |
626 | * @since 1.35 |
627 | * @return bool |
628 | */ |
629 | public function isCurrent() { |
630 | return false; |
631 | } |
632 | } |