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 * @param NameTableStore $roleNamesStore
68 public function __construct( NameTableStore
$roleNamesStore ) {
69 $this->roleNamesStore
= $roleNamesStore;
73 * Defines a slot role.
75 * For use by extensions that wish to define roles beyond the main slot role.
77 * @see defineRoleWithModel()
79 * @param string $role The role name of the slot to define. This should follow the
80 * same convention as message keys:
81 * @param callable $instantiator called with $role as a parameter;
82 * Signature: function ( string $role ): SlotRoleHandler
84 public function defineRole( $role, callable
$instantiator ) {
85 if ( $this->isDefinedRole( $role ) ) {
86 throw new LogicException( "Role $role is already defined" );
89 $this->instantiators
[$role] = $instantiator;
93 * Defines a slot role that allows only the given content model, and has no special
96 * For use by extensions that wish to define roles beyond the main slot role, but have
97 * no need to implement any special behavior for that slot.
101 * @param string $role The role name of the slot to define, see defineRole()
102 * for more information.
103 * @param string $model A content model name, see ContentHandler
104 * @param array $layout See SlotRoleHandler getOutputLayoutHints
106 public function defineRoleWithModel( $role, $model, $layout = [] ) {
109 function ( $role ) use ( $model, $layout ) {
110 return new SlotRoleHandler( $role, $model, $layout );
116 * Gets the SlotRoleHandler that should be used when processing content of the given role.
118 * @param string $role
120 * @throws InvalidArgumentException If $role is not a known slot role.
121 * @return SlotRoleHandler The handler to be used for $role. This may be a
122 * FallbackSlotRoleHandler if the slot is "known" but not "defined".
124 public function getRoleHandler( $role ) {
125 if ( !isset( $this->handlers
[$role] ) ) {
126 if ( !$this->isDefinedRole( $role ) ) {
127 if ( $this->isKnownRole( $role ) ) {
128 // The role has no handler defined, but is represented in the database.
129 // This may happen e.g. when the extension that defined the role was uninstalled.
130 wfWarn( __METHOD__
. ": known but undefined slot role $role" );
131 $this->handlers
[$role] = new FallbackSlotRoleHandler( $role );
133 // The role doesn't have a handler defined, and is not represented in
134 // the database. Something must be quite wrong.
135 throw new InvalidArgumentException( "Unknown role $role" );
138 $handler = call_user_func( $this->instantiators
[$role], $role );
140 Assert
::postcondition(
141 $handler instanceof SlotRoleHandler
,
142 "Instantiator for $role role must return a SlotRoleHandler"
145 $this->handlers
[$role] = $handler;
149 return $this->handlers
[$role];
153 * Returns the list of roles allowed when creating a new revision on the given page.
154 * The choice should not depend on external state, such as the page content.
155 * Note that existing revisions of that page are not guaranteed to comply with this list.
157 * All implementations of this method are required to return at least all "required" roles.
159 * @param LinkTarget $title
163 public function getAllowedRoles( LinkTarget
$title ) {
164 // TODO: allow this to be overwritten per namespace (or page type)
165 // TODO: decide how to control which slots are offered for editing per default (T209927)
166 return $this->getDefinedRoles();
170 * Returns the list of roles required when creating a new revision on the given page.
171 * The should not depend on external state, such as the page content.
172 * Note that existing revisions of that page are not guaranteed to comply with this list.
174 * All required roles are implicitly considered "allowed", so any roles
175 * returned by this method will also be returned by getAllowedRoles().
177 * @param LinkTarget $title
181 public function getRequiredRoles( LinkTarget
$title ) {
182 // TODO: allow this to be overwritten per namespace (or page type)
187 * Returns the list of roles defined by calling defineRole().
189 * This list should be used when enumerating slot roles that can be used for editing.
193 public function getDefinedRoles() {
194 return array_keys( $this->instantiators
);
198 * Returns the list of known roles, including the ones returned by getDefinedRoles(),
199 * and roles that exist according to the NameTableStore provided to the constructor.
201 * This list should be used when enumerating slot roles that can be used in queries or
206 public function getKnownRoles() {
207 return array_unique( array_merge(
208 $this->getDefinedRoles(),
209 $this->roleNamesStore
->getMap()
214 * Whether the given role is defined, that is, it was defined by calling defineRole().
216 * @param string $role
219 public function isDefinedRole( $role ) {
220 return in_array( $role, $this->getDefinedRoles(), true );
224 * Whether the given role is known, that is, it's either defined or exist according to
225 * the NameTableStore provided to the constructor.
227 * @param string $role
230 public function isKnownRole( $role ) {
231 return in_array( $role, $this->getKnownRoles(), true );