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