From 086ae52d12011746a75f5588e877347bc0457352 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Fri, 21 Mar 2008 11:49:34 +0100 Subject: Update auf MediaWiki 1.12.0 --- includes/SpecialUserrights.php | 431 +++++++++++++++++++++++++++++------------ 1 file changed, 302 insertions(+), 129 deletions(-) (limited to 'includes/SpecialUserrights.php') diff --git a/includes/SpecialUserrights.php b/includes/SpecialUserrights.php index b97e5168..48fb3628 100644 --- a/includes/SpecialUserrights.php +++ b/includes/SpecialUserrights.php @@ -4,64 +4,111 @@ * Special page to allow managing user group membership * * @addtogroup SpecialPage - * @todo This code is disgusting and needs a total rewrite + * @todo Use checkboxes or something, this list thing is incomprehensible to + * normal human beings. */ -/** */ -require_once( dirname(__FILE__) . '/HTMLForm.php'); - -/** Entry point */ -function wfSpecialUserrights() { - global $wgRequest; - $form = new UserrightsForm($wgRequest); - $form->execute(); -} - /** * A class to manage user levels rights. * @addtogroup SpecialPage */ -class UserrightsForm extends HTMLForm { - var $mPosted, $mRequest, $mSaveprefs; - /** Escaped local url name*/ - var $action; - - /** Constructor*/ - public function __construct( &$request ) { - $this->mPosted = $request->wasPosted(); - $this->mRequest =& $request; - $this->mName = 'userrights'; - - $titleObj = SpecialPage::getTitleFor( 'Userrights' ); - $this->action = $titleObj->escapeLocalURL(); +class UserrightsPage extends SpecialPage { + # The target of the local right-adjuster's interest. Can be gotten from + # either a GET parameter or a subpage-style parameter, so have a member + # variable for it. + protected $mTarget; + protected $isself = false; + + public function __construct() { + parent::__construct( 'Userrights' ); + } + + public function isRestricted() { + return true; + } + + public function userCanExecute( $user ) { + $available = $this->changeableGroups(); + return !empty( $available['add'] ) + or !empty( $available['remove'] ) + or ($this->isself and + (!empty( $available['add-self'] ) + or !empty( $available['remove-self'] ))); } /** * Manage forms to be shown according to posted data. * Depending on the submit button used, call a form or a save function. + * + * @param mixed $par String if any subpage provided, else null */ - function execute() { + function execute( $par ) { + // If the visitor doesn't have permissions to assign or remove + // any groups, it's a bit silly to give them the user search prompt. + global $wgUser, $wgRequest; + + if( $par ) { + $this->mTarget = $par; + } else { + $this->mTarget = $wgRequest->getVal( 'user' ); + } + + if (!$this->mTarget) { + /* + * If the user specified no target, and they can only + * edit their own groups, automatically set them as the + * target. + */ + $available = $this->changeableGroups(); + if (empty($available['add']) && empty($available['remove'])) + $this->mTarget = $wgUser->getName(); + } + + if ($this->mTarget == $wgUser->getName()) + $this->isself = true; + + if( !$this->userCanExecute( $wgUser ) ) { + // fixme... there may be intermediate groups we can mention. + global $wgOut; + $wgOut->showPermissionsErrorPage( array( + $wgUser->isAnon() + ? 'userrights-nologin' + : 'userrights-notallowed' ) ); + return; + } + + if ( wfReadOnly() ) { + global $wgOut; + $wgOut->readOnlyPage(); + return; + } + + $this->outputHeader(); + + $this->setHeaders(); + // show the general form $this->switchForm(); - if( $this->mPosted ) { - // show some more forms - if( $this->mRequest->getCheck( 'ssearchuser' ) ) { - $this->editUserGroupsForm( $this->mRequest->getVal( 'user-editname' ) ); - } + if( $wgRequest->wasPosted() ) { // save settings - if( $this->mRequest->getCheck( 'saveusergroups' ) ) { - global $wgUser; - $username = $this->mRequest->getVal( 'user-editname' ); - $reason = $this->mRequest->getVal( 'user-reason' ); - if( $wgUser->matchEditToken( $this->mRequest->getVal( 'wpEditToken' ), $username ) ) { - $this->saveUserGroups( $username, - $this->mRequest->getArray( 'member' ), - $this->mRequest->getArray( 'available' ), - $reason ); + if( $wgRequest->getCheck( 'saveusergroups' ) ) { + $reason = $wgRequest->getVal( 'user-reason' ); + if( $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), $this->mTarget ) ) { + $this->saveUserGroups( + $this->mTarget, + $wgRequest->getArray( 'removable' ), + $wgRequest->getArray( 'available' ), + $reason + ); } } } + + // show some more forms + if( $this->mTarget ) { + $this->editUserGroupsForm( $this->mTarget ); + } } /** @@ -72,50 +119,155 @@ class UserrightsForm extends HTMLForm { * @param array $removegroup id of groups to be removed. * @param array $addgroup id of groups to be added. * @param string $reason Reason for group change - * + * @return null */ - function saveUserGroups( $username, $removegroup, $addgroup, $reason = '' ) { - global $wgOut; - $u = User::newFromName($username); + function saveUserGroups( $username, $removegroup, $addgroup, $reason = '') { + global $wgUser, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf; - if(is_null($u)) { - $wgOut->addWikiText( wfMsg( 'nosuchusershort', htmlspecialchars( $username ) ) ); + $user = $this->fetchUser( $username ); + if( !$user ) { return; } - - if($u->getID() == 0) { - $wgOut->addWikiText( wfMsg( 'nosuchusershort', htmlspecialchars( $username ) ) ); - return; + + // Validate input set... + $changeable = $this->changeableGroups(); + if ($wgUser->getId() != 0 && $wgUser->getId() == $user->getId()) { + $addable = array_merge($changeable['add'], $wgGroupsAddToSelf); + $removable = array_merge($changeable['remove'], $wgGroupsRemoveFromSelf); + } else { + $addable = $changeable['add']; + $removable = $changeable['remove']; } - $oldGroups = $u->getGroups(); + $removegroup = array_unique( + array_intersect( (array)$removegroup, $removable ) ); + $addgroup = array_unique( + array_intersect( (array)$addgroup, $addable ) ); + + $oldGroups = $user->getGroups(); $newGroups = $oldGroups; // remove then add groups - if(isset($removegroup)) { + if( $removegroup ) { $newGroups = array_diff($newGroups, $removegroup); foreach( $removegroup as $group ) { - if ( $this->canRemove( $group ) ) { - $u->removeGroup( $group ); - } + $user->removeGroup( $group ); } } - if(isset($addgroup)) { + if( $addgroup ) { $newGroups = array_merge($newGroups, $addgroup); foreach( $addgroup as $group ) { - if ( $this->canAdd( $group ) ) { - $u->addGroup( $group ); - } + $user->addGroup( $group ); } } $newGroups = array_unique( $newGroups ); + // Ensure that caches are cleared + $user->invalidateCache(); + wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) ); wfDebug( 'newGroups: ' . print_r( $newGroups, true ) ); + if( $user instanceof User ) { + // hmmm + wfRunHooks( 'UserRights', array( &$user, $addgroup, $removegroup ) ); + } + + if( $newGroups != $oldGroups ) { + $log = new LogPage( 'rights' ); + + global $wgRequest; + $log->addEntry( 'rights', + $user->getUserPage(), + $wgRequest->getText( 'user-reason' ), + array( + $this->makeGroupNameList( $oldGroups ), + $this->makeGroupNameList( $newGroups ) + ) + ); + } + } + + /** + * Edit user groups membership + * @param string $username Name of the user. + */ + function editUserGroupsForm( $username ) { + global $wgOut; - wfRunHooks( 'UserRights', array( &$u, $addgroup, $removegroup ) ); - $log = new LogPage( 'rights' ); - $log->addEntry( 'rights', Title::makeTitle( NS_USER, $u->getName() ), $reason, array( $this->makeGroupNameList( $oldGroups ), - $this->makeGroupNameList( $newGroups ) ) ); + $user = $this->fetchUser( $username ); + if( !$user ) { + return; + } + + $groups = $user->getGroups(); + + $this->showEditUserGroupsForm( $user, $groups ); + + // This isn't really ideal logging behavior, but let's not hide the + // interwiki logs if we're using them as is. + $this->showLogFragment( $user, $wgOut ); + } + + /** + * Normalize the input username, which may be local or remote, and + * return a user (or proxy) object for manipulating it. + * + * Side effects: error output for invalid access + * @return mixed User, UserRightsProxy, or null + */ + function fetchUser( $username ) { + global $wgOut, $wgUser; + + $parts = explode( '@', $username ); + if( count( $parts ) < 2 ) { + $name = trim( $username ); + $database = ''; + } else { + list( $name, $database ) = array_map( 'trim', $parts ); + + if( !$wgUser->isAllowed( 'userrights-interwiki' ) ) { + $wgOut->addWikiMsg( 'userrights-no-interwiki' ); + return null; + } + if( !UserRightsProxy::validDatabase( $database ) ) { + $wgOut->addWikiMsg( 'userrights-nodatabase', $database ); + return null; + } + } + + if( $name == '' ) { + $wgOut->addWikiMsg( 'nouserspecified' ); + return false; + } + + if( $name{0} == '#' ) { + // Numeric ID can be specified... + // We'll do a lookup for the name internally. + $id = intval( substr( $name, 1 ) ); + + if( $database == '' ) { + $name = User::whoIs( $id ); + } else { + $name = UserRightsProxy::whoIs( $database, $id ); + } + + if( !$name ) { + $wgOut->addWikiMsg( 'noname' ); + return null; + } + } + + if( $database == '' ) { + $user = User::newFromName( $name ); + } else { + $user = UserRightsProxy::newFromName( $database, $name ); + } + + if( !$user || $user->isAnon() ) { + $wgOut->addWikiMsg( 'nosuchusershort', $username ); + return null; + } + + return $user; } function makeGroupNameList( $ids ) { @@ -126,36 +278,16 @@ class UserrightsForm extends HTMLForm { * Output a form to allow searching for a user */ function switchForm() { - global $wgOut, $wgRequest; - $username = $wgRequest->getText( 'user-editname' ); - $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->action, 'name' => 'uluser' ) ); + global $wgOut, $wgScript; + $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'name' => 'uluser' ) ); + $form .= Xml::hidden( 'title', 'Special:Userrights' ); $form .= '
' . wfMsgHtml( 'userrights-lookup-user' ) . ''; - $form .= '

' . Xml::inputLabel( wfMsg( 'userrights-user-editname' ), 'user-editname', 'username', 30, $username ) . '

'; - $form .= '

' . Xml::submitButton( wfMsg( 'editusergroup' ), array( 'name' => 'ssearchuser' ) ) . '

'; + $form .= '

' . Xml::inputLabel( wfMsg( 'userrights-user-editname' ), 'user', 'username', 30, $this->mTarget ) . '

'; + $form .= '

' . Xml::submitButton( wfMsg( 'editusergroup' ) ) . '

'; $form .= '
'; $form .= ''; $wgOut->addHTML( $form ); } - - /** - * Edit user groups membership - * @param string $username Name of the user. - */ - function editUserGroupsForm($username) { - global $wgOut; - - $user = User::newFromName($username); - if( is_null( $user ) ) { - $wgOut->addWikiText( wfMsg( 'nouserspecified' ) ); - return; - } elseif( $user->getID() == 0 ) { - $wgOut->addWikiText( wfMsg( 'nosuchusershort', wfEscapeWikiText( $username ) ) ); - return; - } - - $this->showEditUserGroupsForm( $username, $user->getGroups() ); - $this->showLogFragment( $user, $wgOut ); - } /** * Go through used and available groups and return the ones that this @@ -166,9 +298,15 @@ class UserrightsForm extends HTMLForm { * @return Array: Tuple of addable, then removable groups */ protected function splitGroups( $groups ) { + global $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf; list($addable, $removable) = array_values( $this->changeableGroups() ); - $removable = array_intersect($removable, $groups ); // Can't remove groups the user doesn't have - $addable = array_diff( $addable, $groups ); // Can't add groups the user does have + + $removable = array_intersect( + array_merge($this->isself ? $wgGroupsRemoveFromSelf : array(), $removable), + $groups ); // Can't remove groups the user doesn't have + $addable = array_diff( + array_merge($this->isself ? $wgGroupsAddToSelf : array(), $addable), + $groups ); // Can't add groups the user does have return array( $addable, $removable ); } @@ -177,21 +315,31 @@ class UserrightsForm extends HTMLForm { * Show the form to edit group memberships. * * @todo make all CSS-y and semantic - * @param $username String: Name of user you're editing + * @param $user User or UserRightsProxy you're editing * @param $groups Array: Array of groups the user is in */ - protected function showEditUserGroupsForm( $username, $groups ) { + protected function showEditUserGroupsForm( $user, $groups ) { global $wgOut, $wgUser; - + list( $addable, $removable ) = $this->splitGroups( $groups ); + $list = array(); + foreach( $user->getGroups() as $group ) + $list[] = self::buildGroupLink( $group ); + + $grouplist = ''; + if( count( $list ) > 0 ) { + $grouplist = '

' . wfMsgHtml( 'userrights-groupsmember' ) . ' ' . implode( ', ', $list ) . '

'; + } $wgOut->addHTML( - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->action, 'name' => 'editGroup' ) ) . - Xml::hidden( 'user-editname', $username ) . - Xml::hidden( 'wpEditToken', $wgUser->editToken( $username ) ) . + Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->escapeLocalURL(), 'name' => 'editGroup' ) ) . + Xml::hidden( 'user', $user->getName() ) . + Xml::hidden( 'wpEditToken', $wgUser->editToken( $user->getName() ) ) . Xml::openElement( 'fieldset' ) . Xml::element( 'legend', array(), wfMsg( 'userrights-editusergroup' ) ) . - $wgOut->parse( wfMsg( 'editinguser', $username ) ) . + wfMsgExt( 'editinguser', array( 'parse' ), + wfEscapeWikiText( $user->getName() ) ) . + $grouplist . $this->explainRights() . " @@ -214,7 +362,7 @@ class UserrightsForm extends HTMLForm { Xml::label( wfMsg( 'userrights-reason' ), 'wpReason' ) . " @@ -228,6 +376,19 @@ class UserrightsForm extends HTMLForm { Xml::closeElement( 'form' ) . "\n" ); } + + /** + * Format a link to a group description page + * + * @param string $group + * @return string + */ + private static function buildGroupLink( $group ) { + static $cache = array(); + if( !isset( $cache[$group] ) ) + $cache[$group] = User::makeGroupLinkHtml( $group, User::getGroupMember( $group ) ); + return $cache[$group]; + } /** * Prepare a list of groups the user is able to add and remove @@ -236,17 +397,25 @@ class UserrightsForm extends HTMLForm { */ private function explainRights() { global $wgUser, $wgLang; - + $out = array(); - list( $add, $remove ) = array_values( $this->changeableGroups() ); - + list( $add, $remove, $addself, $rmself ) = array_values( $this->changeableGroups() ); + if( count( $add ) > 0 ) - $out[] = wfMsgExt( 'userrights-available-add', 'parseinline', $wgLang->listToText( $add ) ); + $out[] = wfMsgExt( 'userrights-available-add', 'parseinline', + $wgLang->listToText( $add ), count( $add ) ); if( count( $remove ) > 0 ) - $out[] = wfMsgExt( 'userrights-available-remove', 'parseinline', $wgLang->listToText( $remove ) ); - + $out[] = wfMsgExt( 'userrights-available-remove', 'parseinline', + $wgLang->listToText( $remove ), count( $add ) ); + if( count( $addself ) > 0 ) + $out[] = wfMsgExt( 'userrights-available-add-self', 'parseinline', + $wgLang->listToText( $addself ), count( $addself ) ); + if( count( $rmself ) > 0 ) + $out[] = wfMsgExt( 'userrights-available-remove-self', 'parseinline', + $wgLang->listToText( $rmself ), count( $rmself ) ); + return count( $out ) > 0 - ? implode( ' ', $out ) + ? implode( '
', $out ) : wfMsgExt( 'userrights-available-none', 'parseinline' ); } @@ -257,7 +426,7 @@ class UserrightsForm extends HTMLForm { * @return string XHTML thingie where you can select what groups to add/remove * * @param array $groups The groups that can be added/removed - * @param string $name 'member' or 'available' + * @param string $name 'removable' or 'available' * @return string XHTML
" . - Xml::input( 'user-reason', 60, false, array( 'id' => 'wpReason' ) ) . + Xml::input( 'user-reason', 60, false, array( 'id' => 'wpReason', 'maxlength' => 255 ) ) . "