Merge "Use MediaWiki\SuppressWarnings around trigger_error('') instead @"
[lhc/web/wiklou.git] / includes / Revision / MutableRevisionRecord.php
1 <?php
2 /**
3 * Mutable RevisionRecord implementation, for building new revision entries programmatically.
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 CommentStoreComment;
26 use Content;
27 use InvalidArgumentException;
28 use MediaWiki\Storage\RevisionSlotsUpdate;
29 use MediaWiki\User\UserIdentity;
30 use MWException;
31 use Title;
32 use Wikimedia\Assert\Assert;
33
34 /**
35 * Mutable RevisionRecord implementation, for building new revision entries programmatically.
36 * Provides setters for all fields.
37 *
38 * @since 1.31
39 * @since 1.32 Renamed from MediaWiki\Storage\MutableRevisionRecord
40 */
41 class MutableRevisionRecord extends RevisionRecord {
42
43 /**
44 * Returns an incomplete MutableRevisionRecord which uses $parent as its
45 * parent revision, and inherits all slots form it. If saved unchanged,
46 * the new revision will act as a null-revision.
47 *
48 * @param RevisionRecord $parent
49 *
50 * @return MutableRevisionRecord
51 */
52 public static function newFromParentRevision( RevisionRecord $parent ) {
53 // TODO: ideally, we wouldn't need a Title here
54 $title = Title::newFromLinkTarget( $parent->getPageAsLinkTarget() );
55 $rev = new MutableRevisionRecord( $title, $parent->getWikiId() );
56
57 foreach ( $parent->getSlotRoles() as $role ) {
58 $slot = $parent->getSlot( $role, self::RAW );
59 $rev->inheritSlot( $slot );
60 }
61
62 $rev->setPageId( $parent->getPageId() );
63 $rev->setParentId( $parent->getId() );
64
65 return $rev;
66 }
67
68 /**
69 * @note Avoid calling this constructor directly. Use the appropriate methods
70 * in RevisionStore instead.
71 *
72 * @param Title $title The title of the page this Revision is associated with.
73 * @param bool|string $wikiId the wiki ID of the site this Revision belongs to,
74 * or false for the local site.
75 *
76 * @throws MWException
77 */
78 function __construct( Title $title, $wikiId = false ) {
79 $slots = new MutableRevisionSlots();
80
81 parent::__construct( $title, $slots, $wikiId );
82
83 $this->mSlots = $slots; // redundant, but nice for static analysis
84 }
85
86 /**
87 * @param int $parentId
88 */
89 public function setParentId( $parentId ) {
90 Assert::parameterType( 'integer', $parentId, '$parentId' );
91
92 $this->mParentId = $parentId;
93 }
94
95 /**
96 * Sets the given slot. If a slot with the same role is already present in the revision,
97 * it is replaced.
98 *
99 * @note This can only be used with a fresh "unattached" SlotRecord. Calling code that has a
100 * SlotRecord from another revision should use inheritSlot(). Calling code that has access to
101 * a Content object can use setContent().
102 *
103 * @note This may cause the slot meta-data for the revision to be lazy-loaded.
104 *
105 * @note Calling this method will cause the revision size and hash to be re-calculated upon
106 * the next call to getSize() and getSha1(), respectively.
107 *
108 * @param SlotRecord $slot
109 */
110 public function setSlot( SlotRecord $slot ) {
111 if ( $slot->hasRevision() && $slot->getRevision() !== $this->getId() ) {
112 throw new InvalidArgumentException(
113 'The given slot must be an unsaved, unattached one. '
114 . 'This slot is already attached to revision ' . $slot->getRevision() . '. '
115 . 'Use inheritSlot() instead to preserve a slot from a previous revision.'
116 );
117 }
118
119 $this->mSlots->setSlot( $slot );
120 $this->resetAggregateValues();
121 }
122
123 /**
124 * "Inherits" the given slot's content.
125 *
126 * If a slot with the same role is already present in the revision, it is replaced.
127 *
128 * @note This may cause the slot meta-data for the revision to be lazy-loaded.
129 *
130 * @param SlotRecord $parentSlot
131 */
132 public function inheritSlot( SlotRecord $parentSlot ) {
133 $this->mSlots->inheritSlot( $parentSlot );
134 $this->resetAggregateValues();
135 }
136
137 /**
138 * Sets the content for the slot with the given role.
139 *
140 * If a slot with the same role is already present in the revision, it is replaced.
141 * Calling code that has access to a SlotRecord can use inheritSlot() instead.
142 *
143 * @note This may cause the slot meta-data for the revision to be lazy-loaded.
144 *
145 * @note Calling this method will cause the revision size and hash to be re-calculated upon
146 * the next call to getSize() and getSha1(), respectively.
147 *
148 * @param string $role
149 * @param Content $content
150 */
151 public function setContent( $role, Content $content ) {
152 $this->mSlots->setContent( $role, $content );
153 $this->resetAggregateValues();
154 }
155
156 /**
157 * Removes the slot with the given role from this revision.
158 * This effectively ends the "stream" with that role on the revision's page.
159 * Future revisions will no longer inherit this slot, unless it is added back explicitly.
160 *
161 * @note This may cause the slot meta-data for the revision to be lazy-loaded.
162 *
163 * @note Calling this method will cause the revision size and hash to be re-calculated upon
164 * the next call to getSize() and getSha1(), respectively.
165 *
166 * @param string $role
167 */
168 public function removeSlot( $role ) {
169 $this->mSlots->removeSlot( $role );
170 $this->resetAggregateValues();
171 }
172
173 /**
174 * Applies the given update to the slots of this revision.
175 *
176 * @param RevisionSlotsUpdate $update
177 */
178 public function applyUpdate( RevisionSlotsUpdate $update ) {
179 $update->apply( $this->mSlots );
180 }
181
182 /**
183 * @param CommentStoreComment $comment
184 */
185 public function setComment( CommentStoreComment $comment ) {
186 $this->mComment = $comment;
187 }
188
189 /**
190 * Set revision hash, for optimization. Prevents getSha1() from re-calculating the hash.
191 *
192 * @note This should only be used if the calling code is sure that the given hash is correct
193 * for the revision's content, and there is no chance of the content being manipulated
194 * later. When in doubt, this method should not be called.
195 *
196 * @param string $sha1 SHA1 hash as a base36 string.
197 */
198 public function setSha1( $sha1 ) {
199 Assert::parameterType( 'string', $sha1, '$sha1' );
200
201 $this->mSha1 = $sha1;
202 }
203
204 /**
205 * Set nominal revision size, for optimization. Prevents getSize() from re-calculating the size.
206 *
207 * @note This should only be used if the calling code is sure that the given size is correct
208 * for the revision's content, and there is no chance of the content being manipulated
209 * later. When in doubt, this method should not be called.
210 *
211 * @param int $size nominal size in bogo-bytes
212 */
213 public function setSize( $size ) {
214 Assert::parameterType( 'integer', $size, '$size' );
215
216 $this->mSize = $size;
217 }
218
219 /**
220 * @param int $visibility
221 */
222 public function setVisibility( $visibility ) {
223 Assert::parameterType( 'integer', $visibility, '$visibility' );
224
225 $this->mDeleted = $visibility;
226 }
227
228 /**
229 * @param string $timestamp A timestamp understood by wfTimestamp
230 */
231 public function setTimestamp( $timestamp ) {
232 Assert::parameterType( 'string', $timestamp, '$timestamp' );
233
234 $this->mTimestamp = wfTimestamp( TS_MW, $timestamp );
235 }
236
237 /**
238 * @param bool $minorEdit
239 */
240 public function setMinorEdit( $minorEdit ) {
241 Assert::parameterType( 'boolean', $minorEdit, '$minorEdit' );
242
243 $this->mMinorEdit = $minorEdit;
244 }
245
246 /**
247 * Set the revision ID.
248 *
249 * MCR migration note: this replaces Revision::setId()
250 *
251 * @warning Use this with care, especially when preparing a revision for insertion
252 * into the database! The revision ID should only be fixed in special cases
253 * like preserving the original ID when restoring a revision.
254 *
255 * @param int $id
256 */
257 public function setId( $id ) {
258 Assert::parameterType( 'integer', $id, '$id' );
259
260 $this->mId = $id;
261 }
262
263 /**
264 * Sets the user identity associated with the revision
265 *
266 * @param UserIdentity $user
267 */
268 public function setUser( UserIdentity $user ) {
269 $this->mUser = $user;
270 }
271
272 /**
273 * @param int $pageId
274 */
275 public function setPageId( $pageId ) {
276 Assert::parameterType( 'integer', $pageId, '$pageId' );
277
278 if ( $this->mTitle->exists() && $pageId !== $this->mTitle->getArticleID() ) {
279 throw new InvalidArgumentException(
280 'The given Title does not belong to page ID ' . $this->mPageId
281 );
282 }
283
284 $this->mPageId = $pageId;
285 }
286
287 /**
288 * Returns the nominal size of this revision.
289 *
290 * MCR migration note: this replaces Revision::getSize
291 *
292 * @return int The nominal size, may be computed on the fly if not yet known.
293 */
294 public function getSize() {
295 // If not known, re-calculate and remember. Will be reset when slots change.
296 if ( $this->mSize === null ) {
297 $this->mSize = $this->mSlots->computeSize();
298 }
299
300 return $this->mSize;
301 }
302
303 /**
304 * Returns the base36 sha1 of this revision.
305 *
306 * MCR migration note: this replaces Revision::getSha1
307 *
308 * @return string The revision hash, may be computed on the fly if not yet known.
309 */
310 public function getSha1() {
311 // If not known, re-calculate and remember. Will be reset when slots change.
312 if ( $this->mSha1 === null ) {
313 $this->mSha1 = $this->mSlots->computeSha1();
314 }
315
316 return $this->mSha1;
317 }
318
319 /**
320 * Returns the slots defined for this revision as a MutableRevisionSlots instance,
321 * which can be modified to defined the slots for this revision.
322 *
323 * @return MutableRevisionSlots
324 */
325 public function getSlots() {
326 // Overwritten just guarantee the more narrow return type.
327 return parent::getSlots();
328 }
329
330 /**
331 * Invalidate cached aggregate values such as hash and size.
332 */
333 private function resetAggregateValues() {
334 $this->mSize = null;
335 $this->mSha1 = null;
336 }
337
338 }
339
340 /**
341 * Retain the old class name for backwards compatibility.
342 * @deprecated since 1.32
343 */
344 class_alias( MutableRevisionRecord::class, 'MediaWiki\Storage\MutableRevisionRecord' );