* @return array Tuple of added, then removed groups
*/
function doSaveUserGroups( $user, $add, $remove, $reason = '', $tags = [],
- $groupExpiries = [] ) {
-
+ $groupExpiries = []
+ ) {
// Validate input set...
$isself = $user->getName() == $this->getUser()->getName();
$groups = $user->getGroups();
* @param array $newUGMs Associative array of (group name => UserGroupMembership)
*/
protected function addLogEntry( $user, $oldGroups, $newGroups, $reason, $tags,
- $oldUGMs, $newUGMs ) {
-
+ $oldUGMs, $newUGMs
+ ) {
// make sure $oldUGMs and $newUGMs are in the same order, and serialise
// each UGM object to a simplified array
$oldUGMs = array_map( function ( $group ) use ( $oldUGMs ) {
->rawParams( $userToolLinks )->parse()
);
if ( $canChangeAny ) {
+ $conf = $this->getConfig();
+ $oldCommentSchema = $conf->get( 'CommentTableSchemaMigrationStage' ) === MIGRATION_OLD;
$this->getOutput()->addHTML(
$this->msg( 'userrights-groups-help', $user->getName() )->parse() .
$grouplist .
Xml::label( $this->msg( 'userrights-reason' )->text(), 'wpReason' ) .
"</td>
<td class='mw-input'>" .
- Xml::input( 'user-reason', 60, $this->getRequest()->getVal( 'user-reason', false ),
- [ 'id' => 'wpReason', 'maxlength' => 255 ] ) .
+ Xml::input( 'user-reason', 60, $this->getRequest()->getVal( 'user-reason', false ), [
+ 'id' => 'wpReason',
+ // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+ // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+ // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+ 'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+ ] ) .
"</td>
</tr>
<tr>
/**
* Adds a table with checkboxes where you can select what groups to add/remove
*
- * @param array $usergroups Associative array of (group name as string =>
+ * @param UserGroupMembership[] $usergroups Associative array of (group name as string =>
* UserGroupMembership object) for groups the user belongs to
* @param User $user
* @return Array with 2 elements: the XHTML table element with checkxboes, and
}
$ret .= "\t<td style='vertical-align:top;'>\n";
foreach ( $column as $group => $checkbox ) {
- $attr = $checkbox['disabled'] ? [ 'disabled' => 'disabled' ] : [];
+ $attr = [ 'class' => 'mw-userrights-groupcheckbox' ];
+ if ( $checkbox['disabled'] ) {
+ $attr['disabled'] = 'disabled';
+ }
$member = UserGroupMembership::getGroupMemberName( $group, $user->getName() );
if ( $checkbox['irreversible'] ) {
}
$checkboxHtml = Xml::checkLabel( $text, "wpGroup-" . $group,
"wpGroup-" . $group, $checkbox['set'], $attr );
- $ret .= "\t\t" . ( ( $checkbox['disabled'] && $checkbox['disabled-expiry'] )
- ? Xml::tags( 'div', [ 'class' => 'mw-userrights-disabled' ], $checkboxHtml )
- : Xml::tags( 'div', [], $checkboxHtml )
- ) . "\n";
if ( $this->canProcessExpiries() ) {
$uiUser = $this->getUser();
} else {
$expiryHtml = $this->msg( 'userrights-expiry-none' )->text();
}
+ // T171345: Add a hidden form element so that other groups can still be manipulated,
+ // otherwise saving errors out with an invalid expiry time for this group.
+ $expiryHtml .= Html::Hidden( "wpExpiry-$group",
+ $currentExpiry ? 'existing' : 'infinite' );
$expiryHtml .= "<br />\n";
} else {
$expiryHtml = Xml::element( 'span', null,
$expiryHtml .= $expiryFormOptions->getHTML() . '<br />';
// Add custom expiry field
- $attribs = [ 'id' => "mw-input-wpExpiry-$group-other" ];
+ $attribs = [
+ 'id' => "mw-input-wpExpiry-$group-other",
+ 'class' => 'mw-userrights-expiryfield',
+ ];
if ( $checkbox['disabled-expiry'] ) {
$attribs['disabled'] = 'disabled';
}
'id' => "mw-userrights-nested-wpGroup-$group",
'class' => 'mw-userrights-nested',
];
- $ret .= "\t\t\t" . Xml::tags( 'div', $divAttribs, $expiryHtml ) . "\n";
+ $checkboxHtml .= "\t\t\t" . Xml::tags( 'div', $divAttribs, $expiryHtml ) . "\n";
}
+ $ret .= "\t\t" . ( ( $checkbox['disabled'] && $checkbox['disabled-expiry'] )
+ ? Xml::tags( 'div', [ 'class' => 'mw-userrights-disabled' ], $checkboxHtml )
+ : Xml::tags( 'div', [], $checkboxHtml )
+ ) . "\n";
}
$ret .= "\t</td>\n";
}