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