File: RevisionSlotsUpdate.php

package info (click to toggle)
mediawiki 1%3A1.43.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 417,464 kB
  • sloc: php: 1,062,949; javascript: 664,290; sql: 9,714; python: 5,458; xml: 3,489; sh: 1,131; makefile: 64
file content (302 lines) | stat: -rw-r--r-- 8,953 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 */

namespace MediaWiki\Storage;

use MediaWiki\Content\Content;
use MediaWiki\Revision\MutableRevisionSlots;
use MediaWiki\Revision\RevisionAccessException;
use MediaWiki\Revision\RevisionSlots;
use MediaWiki\Revision\SlotRecord;

/**
 * Value object representing a modification of revision slots.
 *
 * @since 1.32
 */
class RevisionSlotsUpdate {

	/**
	 * @var SlotRecord[] modified slots, using the slot role as the key.
	 */
	private $modifiedSlots = [];

	/**
	 * @var bool[] removed roles, stored in the keys of the array.
	 */
	private $removedRoles = [];

	/**
	 * Constructs a RevisionSlotsUpdate representing the update that turned $parentSlots
	 * into $newSlots. If $parentSlots is not given, $newSlots is assumed to come from a
	 * page's first revision.
	 *
	 * @param RevisionSlots $newSlots
	 * @param RevisionSlots|null $parentSlots
	 *
	 * @return RevisionSlotsUpdate
	 */
	public static function newFromRevisionSlots(
		RevisionSlots $newSlots,
		?RevisionSlots $parentSlots = null
	) {
		$modified = $newSlots->getSlots();
		$removed = [];

		if ( $parentSlots ) {
			foreach ( $parentSlots->getSlots() as $role => $slot ) {
				if ( !isset( $modified[$role] ) ) {
					$removed[] = $role;
				} elseif ( $slot->hasSameContent( $modified[$role] ) ) {
					// Unset slots that had the same content in the parent revision from $modified.
					unset( $modified[$role] );
				}
			}
		}

		return new RevisionSlotsUpdate( $modified, $removed );
	}

	/**
	 * Constructs a RevisionSlotsUpdate representing the update of $parentSlots
	 * when changing $newContent. If a slot has the same content in $newContent
	 * as in $parentSlots, that slot is considered inherited and thus omitted from
	 * the resulting RevisionSlotsUpdate.
	 *
	 * In contrast to newFromRevisionSlots(), slots in $parentSlots that are not present
	 * in $newContent are not considered removed. They are instead assumed to be inherited.
	 *
	 * @param Content[] $newContent The new content, using slot roles as array keys.
	 * @param RevisionSlots|null $parentSlots
	 *
	 * @return RevisionSlotsUpdate
	 */
	public static function newFromContent( array $newContent, ?RevisionSlots $parentSlots = null ) {
		$modified = [];

		foreach ( $newContent as $role => $content ) {
			$slot = SlotRecord::newUnsaved( $role, $content );

			if ( $parentSlots
				&& $parentSlots->hasSlot( $role )
				&& $slot->hasSameContent( $parentSlots->getSlot( $role ) )
			) {
				// Skip slots that had the same content in the parent revision from $modified.
				continue;
			}

			$modified[$role] = $slot;
		}

		return new RevisionSlotsUpdate( $modified );
	}

	/**
	 * @param SlotRecord[] $modifiedSlots
	 * @param string[] $removedRoles
	 */
	public function __construct( array $modifiedSlots = [], array $removedRoles = [] ) {
		foreach ( $modifiedSlots as $slot ) {
			$this->modifySlot( $slot );
		}

		foreach ( $removedRoles as $role ) {
			$this->removeSlot( $role );
		}
	}

	/**
	 * Returns a list of modified slot roles, that is, roles modified by calling modifySlot(),
	 * and not later removed by calling removeSlot().
	 *
	 * Note that slots in modified roles may still be inherited slots. This is for instance
	 * the case when the RevisionSlotsUpdate objects represents some kind of rollback
	 * operation, in which slots that existed in an earlier revision are restored in
	 * a new revision.
	 *
	 * @return string[]
	 */
	public function getModifiedRoles() {
		return array_keys( $this->modifiedSlots );
	}

	/**
	 * Returns a list of removed slot roles, that is, roles removed by calling removeSlot(),
	 * and not later re-introduced by calling modifySlot().
	 *
	 * @return string[]
	 */
	public function getRemovedRoles() {
		return array_keys( $this->removedRoles );
	}

	/**
	 * Returns a list of all slot roles that modified or removed.
	 *
	 * @return string[]
	 */
	public function getTouchedRoles() {
		return array_merge( $this->getModifiedRoles(), $this->getRemovedRoles() );
	}

	/**
	 * Sets the given slot to be modified.
	 * If a slot with the same role is already present, it is replaced.
	 *
	 * The roles used with modifySlot() will be returned from getModifiedRoles(),
	 * unless overwritten with removeSlot().
	 *
	 * @param SlotRecord $slot
	 */
	public function modifySlot( SlotRecord $slot ) {
		$role = $slot->getRole();

		// XXX: We should perhaps require this to be an unsaved slot!
		unset( $this->removedRoles[$role] );
		$this->modifiedSlots[$role] = $slot;
	}

	/**
	 * Sets the content for the slot with the given role to be modified.
	 * If a slot with the same role is already present, it is replaced.
	 *
	 * @param string $role
	 * @param Content $content
	 */
	public function modifyContent( $role, Content $content ) {
		$slot = SlotRecord::newUnsaved( $role, $content );
		$this->modifySlot( $slot );
	}

	/**
	 * Remove the slot for the given role, discontinue the corresponding stream.
	 *
	 * The roles used with removeSlot() will be returned from getRemovedSlots(),
	 * unless overwritten with modifySlot().
	 *
	 * @param string $role
	 */
	public function removeSlot( $role ) {
		unset( $this->modifiedSlots[$role] );
		$this->removedRoles[$role] = true;
	}

	/**
	 * Returns the SlotRecord associated with the given role, if the slot with that role
	 * was modified (and not again removed).
	 *
	 * @note If the SlotRecord returned by this method returns a non-inherited slot,
	 *       the content of that slot may or may not already have PST applied. Methods
	 *       that take a RevisionSlotsUpdate as a parameter should specify whether they
	 *       expect PST to already have been applied to all slots. Inherited slots
	 *       should never have PST applied again.
	 *
	 * @param string $role The role name of the desired slot
	 *
	 * @throws RevisionAccessException if the slot does not exist or was removed.
	 * @return SlotRecord
	 */
	public function getModifiedSlot( $role ) {
		if ( isset( $this->modifiedSlots[$role] ) ) {
			return $this->modifiedSlots[$role];
		} else {
			throw new RevisionAccessException(
				'No such slot: {role}',
				[ 'role' => $role ]
			);
		}
	}

	/**
	 * Returns whether getModifiedSlot() will return a SlotRecord for the given role.
	 *
	 * Will return true for the role names returned by getModifiedRoles(), false otherwise.
	 *
	 * @param string $role The role name of the desired slot
	 *
	 * @return bool
	 */
	public function isModifiedSlot( $role ) {
		return isset( $this->modifiedSlots[$role] );
	}

	/**
	 * Returns whether the given role is to be removed from the page.
	 *
	 * Will return true for the role names returned by getRemovedRoles(), false otherwise.
	 *
	 * @param string $role The role name of the desired slot
	 *
	 * @return bool
	 */
	public function isRemovedSlot( $role ) {
		return isset( $this->removedRoles[$role] );
	}

	/**
	 * Returns true if $other represents the same update - that is,
	 * if all methods defined by RevisionSlotsUpdate when called on $this or $other
	 * will yield the same result when called with the same parameters.
	 *
	 * SlotRecords for the same role are compared based on their model and content.
	 *
	 * @param RevisionSlotsUpdate $other
	 * @return bool
	 */
	public function hasSameUpdates( RevisionSlotsUpdate $other ) {
		// NOTE: use != not !==, since the order of entries is not significant!

		if ( $this->getModifiedRoles() != $other->getModifiedRoles() ) {
			return false;
		}

		if ( $this->getRemovedRoles() != $other->getRemovedRoles() ) {
			return false;
		}

		foreach ( $this->getModifiedRoles() as $role ) {
			$s = $this->getModifiedSlot( $role );
			$t = $other->getModifiedSlot( $role );

			if ( !$s->hasSameContent( $t ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Applies this update to the given MutableRevisionSlots, setting all modified slots,
	 * and removing all removed roles.
	 *
	 * @param MutableRevisionSlots $slots
	 */
	public function apply( MutableRevisionSlots $slots ) {
		foreach ( $this->getModifiedRoles() as $role ) {
			$slots->setSlot( $this->getModifiedSlot( $role ) );
		}

		foreach ( $this->getRemovedRoles() as $role ) {
			$slots->removeSlot( $role );
		}
	}

}