36 private const RESTRICTED_GROUPS_ID_PREFIX =
'group_restrictions-';
47 parent::__construct(
'Listgrouprights' );
60 $out->addModuleStyles(
'mediawiki.special' );
61 $this->
addHelpLink(
'Help:User_rights_and_groups' );
63 $out->wrapWikiMsg(
"<div class=\"mw-listgrouprights-key\">\n$1\n</div>",
'listgrouprights-key' );
66 Html::openElement(
'table', [
'class' => [
'wikitable',
'mw-listgrouprights-table' ] ] ) .
78 $allGroups = array_merge(
79 $this->userGroupManager->listAllGroups(),
80 $this->userGroupManager->listAllImplicitGroups()
86 $restrictedGroups = $this->restrictedUserGroupConfigReader->getConfig();
88 foreach ( $allGroups as $group ) {
89 $permissions = $this->groupPermissionsLookup->getGrantedPermissions( $group );
90 $groupname = ( $group ==
'*' )
94 $groupnameLocalized = $lang->getGroupName( $groupname );
96 $grouppageLocalizedTitle = UserGroupMembership::getGroupPage( $groupname )
97 ?: Title::makeTitleSafe(
NS_PROJECT, $groupname );
99 if ( $group ==
'*' || !$grouppageLocalizedTitle ) {
101 $grouppage = htmlspecialchars( $groupnameLocalized );
103 $grouppage = $linkRenderer->makeLink(
104 $grouppageLocalizedTitle,
109 $groupWithParentheses = $this->
msg(
'parentheses' )->rawParams( $group )->escaped();
110 $groupname =
"<br /><code>$groupWithParentheses</code>";
112 if ( $group ===
'user' ) {
114 $grouplink =
'<br />' . $linkRenderer->makeKnownLink(
116 $this->
msg(
'listgrouprights-members' )->text()
119 $grouplink =
'<br />' . $linkRenderer->makeKnownLink(
121 $this->
msg(
'listgrouprights-members' )->text(),
123 [
'group' => $group ]
130 $restrictionsLink =
'';
131 if ( array_key_exists( $group, $restrictedGroups ) && $restrictedGroups[$group]->hasAnyConditions() ) {
132 $restrictionsSection = Sanitizer::escapeIdForAttribute( self::RESTRICTED_GROUPS_ID_PREFIX . $group );
133 $restrictionsLink = Html::rawElement(
'p', [],
134 $this->
msg(
'listgrouprights-restricted' )
135 ->params(
'#' . $restrictionsSection )
140 $revoke = $this->groupPermissionsLookup->getRevokedPermissions( $group );
141 $addgroups = $addGroups[$group] ?? [];
142 $removegroups = $removeGroups[$group] ?? [];
143 $addgroupsSelf = $groupsAddToSelf[$group] ?? [];
144 $removegroupsSelf = $groupsRemoveFromSelf[$group] ?? [];
146 $id = $group ==
'*' ? false : Sanitizer::escapeIdForAttribute( $group );
147 $out->addHTML( Html::rawElement(
'tr', [
'id' => $id ],
"
148 <td>$grouppage$groupname$grouplink$restrictionsLink</td>
150 $this->formatPermissions( $permissions, $revoke, $addgroups, $removegroups,
151 $addgroupsSelf, $removegroupsSelf ) .
156 $out->addHTML( Html::closeElement(
'table' ) );
157 $this->outputRestrictedGroupsConfig();
158 $this->outputNamespaceProtectionInfo();
161 private function outputNamespaceProtectionInfo() {
165 if ( count( $namespaceProtection ) == 0 ) {
169 $header = $this->
msg(
'listgrouprights-namespaceprotection-header' )->text();
172 'id' => Sanitizer::escapeIdForAttribute( $header )
174 Html::openElement(
'table', [
'class' =>
'wikitable' ] ) .
178 $this->
msg(
'listgrouprights-namespaceprotection-namespace' )->text()
183 $this->
msg(
'listgrouprights-namespaceprotection-restrictedto' )->text()
187 ksort( $namespaceProtection );
188 $validNamespaces = $this->nsInfo->getValidNamespaces();
189 foreach ( $namespaceProtection as $namespace => $rights ) {
190 if ( !in_array( $namespace, $validNamespaces ) ) {
195 $namespaceText = $this->
msg(
'blanknamespace' )->text();
197 $namespaceText = $this->languageConverter->convertNamespace( $namespace );
201 Html::openElement(
'tr' ) .
205 $linkRenderer->makeLink(
209 [
'namespace' => $namespace ]
212 Html::openElement(
'td' ) . Html::openElement(
'ul' )
215 if ( !is_array( $rights ) ) {
216 $rights = [ $rights ];
219 foreach ( $rights as $right ) {
220 $out->addHTML( Html::rawElement(
'li', [],
221 $this->
msg(
'listgrouprights-right-display' )
222 ->params( User::getRightDescription( $right ) )
225 [
'class' =>
'mw-listgrouprights-right-name' ],
232 Html::closeElement(
'ul' ) .
233 Html::closeElement(
'td' ) .
234 Html::closeElement(
'tr' )
237 $out->addHTML( Html::closeElement(
'table' ) );
240 private function outputRestrictedGroupsConfig() {
242 $restrictedGroups = $this->restrictedUserGroupConfigReader->getConfig();
244 if ( !$restrictedGroups ) {
248 $header = $this->
msg(
'listgrouprights-restrictedgroups-header' )->text();
251 'id' => Sanitizer::escapeIdForAttribute( self::RESTRICTED_GROUPS_SECTION_ID )
253 Html::openElement(
'table', [
'class' =>
'wikitable mw-listgrouprights-table' ] ) .
257 $this->
msg(
'listgrouprights-group' )->text()
262 $this->
msg(
'listgrouprights-restrictedgroups-config' )->text()
265 ksort( $restrictedGroups );
269 $allGroups = array_merge(
270 $this->userGroupManager->listAllGroups(),
271 $this->userGroupManager->listAllImplicitGroups()
273 foreach ( $restrictedGroups as $group => $groupConfig ) {
274 if ( !$groupConfig->hasAnyConditions() || !in_array( $group, $allGroups ) ) {
281 [
'id' => Sanitizer::escapeIdForAttribute( self::RESTRICTED_GROUPS_ID_PREFIX . $group ) ]
286 $linkRenderer->makeKnownLink(
288 $lang->getGroupName( $group )
291 Html::openElement(
'td' )
294 $conditionsParts = [];
295 $memberConditions = $groupConfig->getMemberConditions();
296 if ( $memberConditions ) {
297 $memberHtml = $this->
msg(
'listgrouprights-restrictedgroups-memberconditions' )->parse();
298 $memberHtml .= Html::rawElement(
'ul', [],
299 Html::rawElement(
'li', [], $this->formatCondition( $memberConditions ) )
301 $conditionsParts[] = $memberHtml;
303 $updaterConditions = $groupConfig->getUpdaterConditions();
304 if ( $updaterConditions ) {
305 $updaterHtml = $this->
msg(
'listgrouprights-restrictedgroups-updaterconditions' )->parse();
306 $updaterHtml .= Html::rawElement(
'ul', [],
307 Html::rawElement(
'li', [], $this->formatCondition( $updaterConditions ) )
309 $conditionsParts[] = $updaterHtml;
311 if ( $groupConfig->canBeIgnored() ) {
312 $conditionsParts[] = $this->
msg(
'listgrouprights-restrictedgroups-bypassable' )
313 ->params( User::getRightDescription(
'ignore-restricted-groups' ) )
314 ->rawParams(
Html::element(
'code', [],
'ignore-restricted-groups' ) )
317 if ( $groupConfig->allowsAutomaticDemotion() ) {
318 $conditionsParts[] = $this->
msg(
'listgrouprights-restrictedgroups-autodemotion' )->parse();
320 $out->addHTML( implode(
'', $conditionsParts ) );
323 Html::closeElement(
'td' ) .
324 Html::closeElement(
'tr' )
327 $out->addHTML( Html::closeElement(
'table' ) );
341 private function formatPermissions( $permissions, $revoke, $add, $remove, $addSelf, $removeSelf ) {
343 foreach ( $permissions as $permission ) {
345 if ( !isset( $revoke[$permission] ) || !$revoke[$permission] ) {
346 $r[] = $this->
msg(
'listgrouprights-right-display' )
347 ->params( User::getRightDescription( $permission ) )
350 [
'class' =>
'mw-listgrouprights-right-name' ],
355 foreach ( $revoke as $permission ) {
356 $r[] = $this->
msg(
'listgrouprights-right-revoked' )
357 ->params( User::getRightDescription( $permission ) )
360 [
'class' =>
'mw-listgrouprights-right-name' ],
368 $allGroups = $this->userGroupManager->listAllGroups();
372 'removegroup' => $remove,
373 'addgroup-self' => $addSelf,
374 'removegroup-self' => $removeSelf
377 foreach ( $changeGroups as $messageKey => $changeGroup ) {
379 if ( $changeGroup ===
true ) {
382 $r[] = $this->
msg(
'listgrouprights-' . $messageKey .
'-all' )->escaped();
383 } elseif ( is_array( $changeGroup ) ) {
384 $changeGroup = array_intersect( array_values( array_unique( $changeGroup ) ), $allGroups );
385 if ( count( $changeGroup ) ) {
387 foreach ( $changeGroup as $group ) {
388 $groupLinks[] = UserGroupMembership::getLinkWiki( $group, $this->
getContext() );
392 $r[] = $this->
msg(
'listgrouprights-' . $messageKey,
393 $lang->listToText( $groupLinks ), count( $changeGroup ) )->parse();
401 return '<ul><li>' . implode(
"</li>\n<li>", $r ) .
'</li></ul>';
410 private function formatCondition( $condition ): string {
411 if ( is_array( $condition ) && count( $condition ) > 0 ) {
412 $condName = array_shift( $condition );
413 if ( in_array( $condName, UserRequirementsConditionChecker::VALID_OPS ) ) {
415 foreach ( $condition as $subcond ) {
416 $listItems .= Html::rawElement(
'li', [], $this->formatCondition( $subcond ) );
418 $htmlList = Html::rawElement(
'ul', [], $listItems );
419 $condName = match ( $condName ) {
420 '&' =>
'listgrouprights-restrictedgroups-op-and',
421 '|' =>
'listgrouprights-restrictedgroups-op-or',
422 '^' =>
'listgrouprights-restrictedgroups-op-xor',
425 '!' =>
'listgrouprights-restrictedgroups-op-nand',
427 return $this->
msg( $condName )
428 ->rawParams( $htmlList )
431 return $this->formatAtomicCondition( $condName, $condition );
433 } elseif ( is_array( $condition ) ) {
436 return $this->formatAtomicCondition( $condition, [] );
446 private function formatAtomicCondition( $condName, array $args ): string {
447 $msgKey = match ( $condName ) {
449 APCOND_AGE =>
'listgrouprights-restrictedgroups-cond-age',
452 APCOND_ISIP =>
'listgrouprights-restrictedgroups-cond-isip',
455 APCOND_BLOCKED =>
'listgrouprights-restrictedgroups-cond-blocked',
456 APCOND_ISBOT =>
'listgrouprights-restrictedgroups-cond-isbot',
460 if ( $msgKey ===
null ) {
463 $this->getHookRunner()->onUserRequirementsConditionDisplay( $condName, $args, $context, $messageSpec );
464 if ( $messageSpec !==
null ) {
465 return $this->
msg( $messageSpec )->parse();
467 $msgKey =
'listgrouprights-restrictedgroups-cond-' . $condName;
470 $msg = $this->
msg( $msgKey );
474 $msg->durationParams( $minAge );
477 foreach ( $args as $group ) {
478 $groupNames[] = $this->getLanguage()->getGroupName( $group );
480 $msg->params( count( $args ), $this->getLanguage()->listToText( $groupNames ) );
483 $msg->numParams( $minEdits );
485 $msg->params( ...$args );
488 return $msg->parse();