3 * This file is part of MediaWiki.
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.
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.
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
23 namespace MediaWiki\Revision
;
25 use InvalidArgumentException
;
27 use MediaWiki\Linker\LinkTarget
;
28 use MediaWiki\Storage\NameTableStore
;
29 use Wikimedia\Assert\Assert
;
32 * A registry service for SlotRoleHandlers, used to define which slot roles are available on
35 * Extensions may use the SlotRoleRegistry to register the slots they define.
37 * In the context of the SlotRoleRegistry, it is useful to distinguish between "defined" and "known"
38 * slot roles: A slot role is "defined" if defineRole() or defineRoleWithModel() was called for
39 * that role. A slot role is "known" if the NameTableStore provided to the constructor as the
40 * $roleNamesStore parameter has an ID associated with that role, which essentially means that
41 * the role at some point has been used on the wiki. Roles that are not "defined" but are
42 * "known" typically belong to extensions that used to be installed on the wiki, but no longer are.
43 * Such slots should be considered ok for display and administrative operations, but only "defined"
44 * slots should be supported for editing.
48 class SlotRoleRegistry
{
53 private $roleNamesStore;
58 private $instantiators = [];
61 * @var SlotRoleHandler[]
66 * SlotRoleRegistry constructor.
68 * @param NameTableStore $roleNamesStore
70 public function __construct( NameTableStore
$roleNamesStore ) {
71 $this->roleNamesStore
= $roleNamesStore;
75 * Defines a slot role.
77 * For use by extensions that wish to define roles beyond the main slot role.
79 * @see defineRoleWithModel()
81 * @param string $role The role name of the slot to define. This should follow the
82 * same convention as message keys:
83 * @param callable $instantiator called with $role as a parameter;
84 * Signature: function ( string $role ): SlotRoleHandler
86 public function defineRole( $role, callable
$instantiator ) {
87 if ( $this->isDefinedRole( $role ) ) {
88 throw new LogicException( "Role $role is already defined" );
91 $this->instantiators
[$role] = $instantiator;
95 * Defines a slot role that allows only the given content model, and has no special
98 * For use by extensions that wish to define roles beyond the main slot role, but have
99 * no need to implement any special behavior for that slot.
103 * @param string $role The role name of the slot to define, see defineRole()
104 * for more information.
105 * @param string $model A content model name, see ContentHandler
106 * @param array $layout See SlotRoleHandler getOutputLayoutHints
108 public function defineRoleWithModel( $role, $model, $layout = [] ) {
111 function ( $role ) use ( $model, $layout ) {
112 return new SlotRoleHandler( $role, $model, $layout );
118 * Gets the SlotRoleHandler that should be used when processing content of the given role.
120 * @param string $role
122 * @throws InvalidArgumentException If $role is not a known slot role.
123 * @return SlotRoleHandler The handler to be used for $role. This may be a
124 * FallbackSlotRoleHandler if the slot is "known" but not "defined".
126 public function getRoleHandler( $role ) {
127 if ( !isset( $this->handlers
[$role] ) ) {
128 if ( !$this->isDefinedRole( $role ) ) {
129 if ( $this->isKnownRole( $role ) ) {
130 // The role has no handler defined, but is represented in the database.
131 // This may happen e.g. when the extension that defined the role was uninstalled.
132 wfWarn( __METHOD__
. ": known but undefined slot role $role" );
133 $this->handlers
[$role] = new FallbackSlotRoleHandler( $role );
135 // The role doesn't have a handler defined, and is not represented in
136 // the database. Something must be quite wrong.
137 throw new InvalidArgumentException( "Unknown role $role" );
140 $handler = call_user_func( $this->instantiators
[$role], $role );
142 Assert
::postcondition(
143 $handler instanceof SlotRoleHandler
,
144 "Instantiator for $role role must return a SlotRoleHandler"
147 $this->handlers
[$role] = $handler;
151 return $this->handlers
[$role];
155 * Returns the list of roles allowed when creating a new revision on the given page.
156 * The choice should not depend on external state, such as the page content.
157 * Note that existing revisions of that page are not guaranteed to comply with this list.
159 * All implementations of this method are required to return at least all "required" roles.
161 * @param LinkTarget $title
165 public function getAllowedRoles( LinkTarget
$title ) {
166 // TODO: allow this to be overwritten per namespace (or page type)
167 // TODO: decide how to control which slots are offered for editing per default (T209927)
168 return $this->getDefinedRoles();
172 * Returns the list of roles required when creating a new revision on the given page.
173 * The should not depend on external state, such as the page content.
174 * Note that existing revisions of that page are not guaranteed to comply with this list.
176 * All required roles are implicitly considered "allowed", so any roles
177 * returned by this method will also be returned by getAllowedRoles().
179 * @param LinkTarget $title
183 public function getRequiredRoles( LinkTarget
$title ) {
184 // TODO: allow this to be overwritten per namespace (or page type)
189 * Returns the list of roles defined by calling defineRole().
191 * This list should be used when enumerating slot roles that can be used for editing.
195 public function getDefinedRoles() {
196 return array_keys( $this->instantiators
);
200 * Returns the list of known roles, including the ones returned by getDefinedRoles(),
201 * and roles that exist according to the NameTableStore provided to the constructor.
203 * This list should be used when enumerating slot roles that can be used in queries or
208 public function getKnownRoles() {
209 return array_unique( array_merge(
210 $this->getDefinedRoles(),
211 $this->roleNamesStore
->getMap()
216 * Whether the given role is defined, that is, it was defined by calling defineRole().
218 * @param string $role
221 public function isDefinedRole( $role ) {
222 return in_array( $role, $this->getDefinedRoles(), true );
226 * Whether the given role is known, that is, it's either defined or exist according to
227 * the NameTableStore provided to the constructor.
229 * @param string $role
232 public function isKnownRole( $role ) {
233 return in_array( $role, $this->getKnownRoles(), true );