Index: trunk/extensions/CentralAuth/central-auth.sql |
— | — | @@ -134,3 +134,33 @@ |
135 | 135 | KEY (ggp_group), |
136 | 136 | KEY (ggp_permission) |
137 | 137 | ) /*$wgDBTableOptions*/; |
| 138 | + |
| 139 | +-- Sets of wikis (for things like restricting global groups) |
| 140 | +-- May be defined in two ways: only specified wikis or all wikis except opt-outed |
| 141 | +CREATE TABLE wikiset ( |
| 142 | + -- ID of wikiset |
| 143 | + ws_id int auto_increment, |
| 144 | + -- Display name of wikiset |
| 145 | + ws_name varchar(255) not null, |
| 146 | + -- Type of set: opt-in or opt-out |
| 147 | + ws_type enum ('optin', 'optout'), |
| 148 | + -- Wikis in that set. Why isn't it a seperate table? |
| 149 | + -- Because we can just use such simple list, we don't need complicated queries on it |
| 150 | + -- Let's suppose that max length of db name is 31 (32 with ","), then we have space for |
| 151 | + -- 2048 wikis. More than we need |
| 152 | + ws_wikis blob not null, |
| 153 | + |
| 154 | + PRIMARY KEY ws_id (ws_id), |
| 155 | + UNIQUE ws_name (ws_name) |
| 156 | +) /*$wgDBTableOptions*/; |
| 157 | + |
| 158 | +-- Allow certain global groups to have their permissions only on certain wikis |
| 159 | +CREATE TABLE global_group_restrictions ( |
| 160 | + -- Group to restrict |
| 161 | + ggr_group varchar(255) not null, |
| 162 | + -- Wikiset to use |
| 163 | + ggr_set int not null, |
| 164 | + |
| 165 | + PRIMARY KEY (ggr_group), |
| 166 | + KEY (ggr_set) |
| 167 | +) /*$wgDBTableOptions*/; |
Index: trunk/extensions/CentralAuth/db_patches/patch-wikisets.sql |
— | — | @@ -0,0 +1,29 @@ |
| 2 | +-- Sets of wikis (for things like restricting global groups) |
| 3 | +-- May be defined in two ways: only specified wikis or all wikis except opt-outed |
| 4 | +CREATE TABLE wikiset ( |
| 5 | + -- ID of wikiset |
| 6 | + ws_id int auto_increment, |
| 7 | + -- Display name of wikiset |
| 8 | + ws_name varchar(255) not null, |
| 9 | + -- Type of set: opt-in or opt-out |
| 10 | + ws_type enum ('optin', 'optout'), |
| 11 | + -- Wikis in that set. Why isn't it a seperate table? |
| 12 | + -- Because we can just use such simple list, we don't need complicated queries on it |
| 13 | + -- Let's suppose that max length of db name is 31 (32 with ","), then we have space for |
| 14 | + -- 2048 wikis. More than we need |
| 15 | + ws_wikis blob not null, |
| 16 | + |
| 17 | + PRIMARY KEY ws_id (ws_id), |
| 18 | + UNIQUE ws_name (ws_name) |
| 19 | +) /*$wgDBTableOptions*/; |
| 20 | + |
| 21 | +-- Allow certain global groups to have their permissions only on certain wikis |
| 22 | +CREATE TABLE global_group_restrictions ( |
| 23 | + -- Group to restrict |
| 24 | + ggr_group varchar(255) not null, |
| 25 | + -- Wikiset to use |
| 26 | + ggr_set int not null, |
| 27 | + |
| 28 | + PRIMARY KEY (ggr_group), |
| 29 | + KEY (ggr_set) |
| 30 | +) /*$wgDBTableOptions*/; |
Index: trunk/extensions/CentralAuth/CentralAuthUser.php |
— | — | @@ -18,7 +18,7 @@ |
19 | 19 | */ |
20 | 20 | /*private*/ var $mName; |
21 | 21 | /*private*/ var $mStateDirty = false; |
22 | | - /*private*/ var $mVersion = 1; |
| 22 | + /*private*/ var $mVersion = 2; |
23 | 23 | /*private*/ var $mDelayInvalidation = 0; |
24 | 24 | |
25 | 25 | static $mCacheVars = array( |
— | — | @@ -153,26 +153,40 @@ |
154 | 154 | return; |
155 | 155 | } |
156 | 156 | // We need the user id from the database, but this should be checked by the getId accessor. |
157 | | - |
| 157 | + |
158 | 158 | wfDebugLog( 'CentralAuth', "Loading groups for global user {$this->mName}" ); |
159 | | - |
| 159 | + |
160 | 160 | $dbr = self::getCentralDB(); // We need the master. |
161 | | - |
| 161 | + |
162 | 162 | $res = $dbr->select( |
163 | 163 | array( 'global_group_permissions', 'global_user_groups' ), |
164 | 164 | array( 'ggp_permission', 'ggp_group' ), |
165 | 165 | array( 'ggp_group=gug_group', 'gug_user' => $this->getId() ), |
166 | | - __METHOD__ ); |
167 | | - |
| 166 | + __METHOD__ |
| 167 | + ); |
| 168 | + |
| 169 | + $resSets = $dbr->select( |
| 170 | + array( 'global_user_groups', 'global_group_restrictions', 'wikiset' ), |
| 171 | + array( 'ggr_group', 'ws_id', 'ws_name', 'ws_type', 'ws_wikis' ), |
| 172 | + array( 'ggr_group=gug_group', 'ggr_set=ws_id', 'gug_user' => $this->getId() ), |
| 173 | + __METHOD__ |
| 174 | + ); |
| 175 | + |
| 176 | + $sets = array(); |
| 177 | + while( $row = $dbr->fetchObject( $resSets ) ) { |
| 178 | + $sets[$row->ggr_group] = WikiSet::newFromRow( $row ); |
| 179 | + } |
| 180 | + |
168 | 181 | // Grab the user's rights/groups. |
169 | 182 | $rights = array(); |
170 | 183 | $groups = array(); |
171 | | - |
172 | | - while ($row = $dbr->fetchObject( $res ) ) { |
173 | | - $rights[] = $row->ggp_permission; |
| 184 | + |
| 185 | + while( $row = $dbr->fetchObject( $res ) ) { |
| 186 | + $set = @$sets[$row->ggp_group]; |
| 187 | + $rights[] = array( 'right' => $row->ggp_permission, 'set' => $set ? $set->getID() : false ); |
174 | 188 | $groups[$row->ggp_group] = 1; |
175 | 189 | } |
176 | | - |
| 190 | + |
177 | 191 | $this->mRights = $rights; |
178 | 192 | $this->mGroups = array_keys($groups); |
179 | 193 | } |
— | — | @@ -1620,7 +1634,18 @@ |
1621 | 1635 | function getGlobalRights() { |
1622 | 1636 | $this->loadGroups(); |
1623 | 1637 | |
1624 | | - return $this->mRights; |
| 1638 | + $rights = array(); |
| 1639 | + $sets = array(); |
| 1640 | + foreach( $this->mRights as $right ) { |
| 1641 | + if( $right['set'] ) { |
| 1642 | + $set = isset( $sets[$right['set']] ) ? $sets[$right['set']] : WikiSet::newFromID( $right['set'] ); |
| 1643 | + if( $set->inSet() ) |
| 1644 | + $rights[] = $right['right']; |
| 1645 | + } else { |
| 1646 | + $rights[] = $right['right']; |
| 1647 | + } |
| 1648 | + } |
| 1649 | + return $rights; |
1625 | 1650 | } |
1626 | 1651 | |
1627 | 1652 | function removeFromGlobalGroups( $groups ) { |
Index: trunk/extensions/CentralAuth/SpecialEditWikiSets.php |
— | — | @@ -0,0 +1,213 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Special page to allow managing global groups |
| 5 | + * Prototype for a similar system in core. |
| 6 | + * |
| 7 | + * @addtogroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 11 | + echo "CentralAuth extension\n"; |
| 12 | + exit( 1 ); |
| 13 | +} |
| 14 | + |
| 15 | + |
| 16 | +class SpecialEditWikiSets extends SpecialPage |
| 17 | +{ |
| 18 | + function __construct() { |
| 19 | + parent::__construct('EditWikiSets', 'globalgrouppermissions'); |
| 20 | + wfLoadExtensionMessages('SpecialCentralAuth'); |
| 21 | + } |
| 22 | + |
| 23 | + function getDescription() { |
| 24 | + return wfMsg( 'centralauth-editset' ); |
| 25 | + } |
| 26 | + |
| 27 | + function userCanExecute($user) { |
| 28 | + $globalUser = CentralAuthUser::getInstance( $user ); |
| 29 | + |
| 30 | + ## Should be a global user |
| 31 | + if (!$globalUser->exists() || !$globalUser->isAttached()) { |
| 32 | + return false; |
| 33 | + } |
| 34 | + |
| 35 | + ## Permission MUST be gained from global rights. |
| 36 | + return $globalUser->hasGlobalPermission( 'globalgrouppermissions' ); |
| 37 | + } |
| 38 | + |
| 39 | + function execute( $subpage ) { |
| 40 | + global $wgRequest, $wgOut, $wgUser; |
| 41 | + |
| 42 | + if( !$this->userCanExecute( $wgUser ) ) { |
| 43 | + $this->displayRestrictionError(); |
| 44 | + return; |
| 45 | + } |
| 46 | + |
| 47 | + $this->setHeaders(); |
| 48 | + |
| 49 | + if( $subpage && !is_numeric( $subpage ) ) { |
| 50 | + $set = WikiSet::newFromName( $subpage ); |
| 51 | + if( $set ) { |
| 52 | + $subpage = $set->getID(); |
| 53 | + } else { |
| 54 | + $wgOut->setPageTitle( wfMsg( 'error' ) ); |
| 55 | + $error = wfMsgExt( 'centralauth-editset-notfound', array( 'escapenoentities' ), $subpage ); |
| 56 | + $this->buildMainView( "<strong class='error'>{$error}</strong>" ); |
| 57 | + return; |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + if( ( $subpage || $subpage === '0' ) && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { |
| 62 | + $this->doSubmit( $subpage ); |
| 63 | + } else if( ( $subpage || $subpage === '0' ) && is_numeric( $subpage ) ) { |
| 64 | + $this->buildSetView( $subpage ); |
| 65 | + } else { |
| 66 | + $this->buildMainView(); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + function buildMainView( $msg = '' ) { |
| 71 | + global $wgOut, $wgScript, $wgUser; |
| 72 | + $sk = $wgUser->getSkin(); |
| 73 | + |
| 74 | + $legend = wfMsg( 'centralauth-editset-legend' ); |
| 75 | + $wgOut->addHtml( "<fieldset><legend>{$legend}</legend>" ); |
| 76 | + if( $msg ) |
| 77 | + $wgOut->addHTML( $msg ); |
| 78 | + $wgOut->addWikiMsg( 'centralauth-editset-intro' ); |
| 79 | + $wgOut->addHTML( '<ul>' ); |
| 80 | + |
| 81 | + $sets = WikiSet::getAllWikiSets(); |
| 82 | + foreach( $sets as $set ) { |
| 83 | + $text = wfMsgExt( 'centralauth-editset-item', array( 'parseinline' ), $set->getName(), $set->getID() ); |
| 84 | + $wgOut->addHTML( "<li>{$text}</li>" ); |
| 85 | + } |
| 86 | + |
| 87 | + $target = SpecialPage::getTitleFor( 'EditWikiSets', '0' ); |
| 88 | + $newlink = $sk->makeLinkObj( $target, wfMsgHtml( 'centralauth-editset-new' ) ); |
| 89 | + $wgOut->addHTML( "<li>{$newlink}</li>" ); |
| 90 | + |
| 91 | + $wgOut->addHtml( '</ul></fieldset>' ); |
| 92 | + } |
| 93 | + |
| 94 | + function buildSetView( $subpage, $error = false, $name = null, $type = null, $wikis = null, $reason = null ) { |
| 95 | + global $wgOut, $wgUser; |
| 96 | + |
| 97 | + $set = $subpage ? WikiSet::newFromID( $subpage ) : null; |
| 98 | + if( !$name ) $name = $set ? $set->getName() : ''; |
| 99 | + if( !$type ) $type = $set ? $set->getType() : WikiSet::OPTIN; |
| 100 | + if( !$wikis ) $wikis = implode( "\n", $set ? $set->getWikisRaw() : array() ); |
| 101 | + else $wikis = implode( "\n", $wikis ); |
| 102 | + $url = SpecialPage::getTitleFor( 'EditWikiSets', $subpage )->getLocalUrl(); |
| 103 | + $legend = wfMsgHtml( 'centralauth-editset-legend-' . ($set ? 'edit' : 'new'), $name ); |
| 104 | + |
| 105 | + $wgOut->addHTML( "<fieldset><legend>{$legend}</legend>" ); |
| 106 | + if( $error ) |
| 107 | + $wgOut->addHTML( "<strong class='error'>{$error}</strong>" ); |
| 108 | + $wgOut->addHTML( "<form action='{$url}' method='post'>" ); |
| 109 | + |
| 110 | + if( $set ) { |
| 111 | + $groups = $set->getRestrictedGroups(); |
| 112 | + if ( $groups ) { |
| 113 | + $usage = "<ul>\n"; |
| 114 | + foreach( $groups as $group ) |
| 115 | + $usage .= "<li>" . wfMsgExt( 'centralauth-editset-grouplink', array('parseinline'), $group ) . "</li>\n"; |
| 116 | + $usage .= "</ul>"; |
| 117 | + } else { |
| 118 | + $usage = wfMsgWikiHtml('centralauth-editset-nouse'); |
| 119 | + } |
| 120 | + } else { |
| 121 | + $usage = ''; |
| 122 | + } |
| 123 | + |
| 124 | + $form = array(); |
| 125 | + $form['centralauth-editset-name'] = Xml::input( 'wpName', false, $name ); |
| 126 | + if( $usage ) |
| 127 | + $form['centralauth-editset-usage'] = $usage; |
| 128 | + $form['centralauth-editset-type'] = $this->buildTypeSelector( 'wpType', $type ); |
| 129 | + $form['centralauth-editset-wikis'] = Xml::textarea( 'wpWikis', $wikis ); |
| 130 | + $form['centralauth-editset-reason'] = Xml::input( 'wpReason', false, $reason ); |
| 131 | + |
| 132 | + $wgOut->addHTML( Xml::buildForm( $form, 'centralauth-editset-submit' ) ); |
| 133 | + |
| 134 | + $edittoken = Xml::hidden( 'wpEditToken', $wgUser->editToken() ); |
| 135 | + $wgOut->addHTML( "<p>{$edittoken}</p></form></fieldset>" ); |
| 136 | + } |
| 137 | + |
| 138 | + function buildTypeSelector( $name, $value ) { |
| 139 | + $select = new XmlSelect( $name, 'set-type', $value ); |
| 140 | + foreach( array( WikiSet::OPTIN, WikiSet::OPTOUT ) as $type ) |
| 141 | + $select->addOption( wfMsg( "centralauth-editset-{$type}" ), $type ); |
| 142 | + return $select->getHTML(); |
| 143 | + } |
| 144 | + |
| 145 | + function doSubmit( $id ) { |
| 146 | + global $wgRequest, $wgContLang; |
| 147 | + |
| 148 | + $name = $wgContLang->ucfirst( $wgRequest->getVal( 'wpName' ) ); |
| 149 | + $type = $wgRequest->getVal( 'wpType' ); |
| 150 | + $wikis = array_unique( preg_split( '/(\s+|\s*\W\s*)/', $wgRequest->getVal( 'wpWikis' ), -1, PREG_SPLIT_NO_EMPTY ) ); |
| 151 | + $reason = $wgRequest->getVal( 'wpReason' ); |
| 152 | + |
| 153 | + if( !Title::newFromText( $name ) ) { |
| 154 | + $this->buildSetView( $id, wfMsgHtml( 'centralauth-editset-badname' ), $name, $type, $wikis, $reason ); |
| 155 | + return; |
| 156 | + } |
| 157 | + if( WikiSet::newFromName( $name ) ) { |
| 158 | + $this->buildSetView( $id, wfMsgHtml( 'centralauth-editset-setexists' ), $name, $type, $wikis, $reason ); |
| 159 | + return; |
| 160 | + } |
| 161 | + if( !in_array( $type, array( WikiSet::OPTIN, WikiSet::OPTOUT ) ) ) { |
| 162 | + $this->buildSetView( $id, wfMsgHtml( 'centralauth-editset-badtype' ), $name, $type, $wikis, $reason ); |
| 163 | + return; |
| 164 | + } |
| 165 | + if( !$wikis ) { |
| 166 | + $this->buildSetView( $id, wfMsgHtml( 'centralauth-editset-nowikis' ), $name, $type, $wikis, $reason ); |
| 167 | + return; |
| 168 | + } |
| 169 | + $badwikis = array(); |
| 170 | + $allwikis = CentralAuthUser::getWikiList(); |
| 171 | + foreach( $wikis as $wiki ) |
| 172 | + if( !in_array( $wiki, $allwikis ) ) |
| 173 | + $badwikis[] = $wiki; |
| 174 | + if( $badwikis ) { |
| 175 | + $this->buildSetView( $id, wfMsgExt( 'centralauth-editset-badwikis', array( 'escapenoentities' ), implode( ', ', $badwikis ) ), |
| 176 | + $name, $type, $wikis, $reason ); |
| 177 | + return; |
| 178 | + } |
| 179 | + |
| 180 | + $set = WikiSet::newFromID( $id ); |
| 181 | + if( $set ) { |
| 182 | + $oldname = $set->getName(); |
| 183 | + $oldtype = $set->getType(); |
| 184 | + $oldwikis = $set->getWikisRaw(); |
| 185 | + } else { |
| 186 | + $set = new WikiSet(); |
| 187 | + $oldname = $oldtype = null; $oldwikis = array(); |
| 188 | + } |
| 189 | + $set->setName( $name ); |
| 190 | + $set->setType( $type ); |
| 191 | + $set->setWikisRaw( $wikis ); |
| 192 | + $set->commit(); |
| 193 | + |
| 194 | + // Now logging |
| 195 | + $log = new LogPage( 'gblrights' ); |
| 196 | + $title = SpecialPage::getTitleFor( 'EditWikiSets', $set->getID() ); |
| 197 | + if( !$oldname ) { |
| 198 | + // New set |
| 199 | + $log->addEntry( 'newset', $title, $reason, array( $name, $type, implode( ', ', $wikis ) ) ); |
| 200 | + } else { |
| 201 | + if( $oldname != $name ) |
| 202 | + $log->addEntry( 'setrename', $title, $reason, array( $name, $oldname ) ); |
| 203 | + if( $oldtype != $type ) |
| 204 | + $log->addEntry( 'setnewtype', $title, $reason, array( $name, $oldtype, $type ) ); |
| 205 | + $added = implode( ', ', array_diff( $wikis, $oldwikis ) ); |
| 206 | + $removed = implode( ', ', array_diff( $oldwikis, $wikis ) ); |
| 207 | + if( $added || $removed ) { |
| 208 | + $log->addEntry( 'setchange', $title, $reason, array( $name, $added, $removed ) ); |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + $this->buildMainView( '<strong class="success">' . wfMsgHtml( 'centralauth-editset-success' ) . '</strong>' ); |
| 213 | + } |
| 214 | +} |
Index: trunk/extensions/CentralAuth/SpecialGlobalGroupPermissions.php |
— | — | @@ -112,7 +112,7 @@ |
113 | 113 | } |
114 | 114 | |
115 | 115 | function buildGroupView( $group ) { |
116 | | - global $wgOut, $wgUser,$wgScript; |
| 116 | + global $wgOut, $wgUser, $wgScript; |
117 | 117 | |
118 | 118 | $wgOut->setSubtitle( wfMsg( 'centralauth-editgroup-subtitle', $group ) ); |
119 | 119 | |
— | — | @@ -127,6 +127,7 @@ |
128 | 128 | $fields['centralauth-editgroup-display'] = wfMsgExt( 'centralauth-editgroup-display-edit', array( 'parseinline' ), $group, User::getGroupName( $group ) ); |
129 | 129 | $fields['centralauth-editgroup-member'] = wfMsgExt( 'centralauth-editgroup-member-edit', array( 'parseinline' ), $group, User::getGroupMember( $group ) ); |
130 | 130 | $fields['centralauth-editgroup-members'] = wfMsgExt( 'centralauth-editgroup-members-link', array( 'parseinline' ), $group, User::getGroupMember( $group ) ); |
| 131 | + $fields['centralauth-editgroup-restrictions'] = $this->buildWikiSetSelector($group); |
131 | 132 | $fields['centralauth-editgroup-perms'] = $this->buildCheckboxes($group); |
132 | 133 | $fields['centralauth-editgroup-reason'] = wfInput( 'wpReason' ); |
133 | 134 | |
— | — | @@ -139,7 +140,21 @@ |
140 | 141 | |
141 | 142 | $this->showLogFragment( $group, $wgOut ); |
142 | 143 | } |
143 | | - |
| 144 | + |
| 145 | + function buildWikiSetSelector( $group ) { |
| 146 | + $sets = WikiSet::getAllWikiSets(); |
| 147 | + $default = WikiSet::getWikiSetForGroup( $group ); |
| 148 | + |
| 149 | + $select = new XmlSelect( 'set', 'wikiset', $default ); |
| 150 | + $select->addOption( wfMsg( 'centralauth-editgroup-noset' ), '0' ); |
| 151 | + foreach( $sets as $set ) { |
| 152 | + $select->addOption( $set->getName(), $set->getID() ); |
| 153 | + } |
| 154 | + |
| 155 | + $editlink = wfMsgExt( "centralauth-editgroup-editsets", array( "parseinline" ) ); |
| 156 | + return $select->getHTML() . " {$editlink}"; |
| 157 | + } |
| 158 | + |
144 | 159 | function buildCheckboxes( $group ) { |
145 | 160 | |
146 | 161 | $rights = User::getAllRights(); |
— | — | @@ -221,7 +236,15 @@ |
222 | 237 | // Log it |
223 | 238 | if (!(count($addRights)==0 && count($removeRights)==0)) |
224 | 239 | $this->addLogEntry( $group, $addRights, $removeRights, $reason ); |
225 | | - |
| 240 | + |
| 241 | + // Change set |
| 242 | + $current = WikiSet::getWikiSetForGroup( $group ); |
| 243 | + $new = $wgRequest->getVal( 'set' ); |
| 244 | + if( $current != $new ) { |
| 245 | + $this->setRestrictions( $group, $new ); |
| 246 | + $this->addLogEntry2( $group, $current, $new, $reason ); |
| 247 | + } |
| 248 | + |
226 | 249 | $this->invalidateRightsCache( $group ); |
227 | 250 | |
228 | 251 | // Display success |
— | — | @@ -262,10 +285,8 @@ |
263 | 286 | global $wgRequest; |
264 | 287 | |
265 | 288 | $log = new LogPage( 'gblrights' ); |
266 | | - |
267 | | - $added = |
268 | 289 | |
269 | | - $log->addEntry( 'groupperms2', |
| 290 | + $log->addEntry( 'groupprms2', |
270 | 291 | SpecialPage::getTitleFor( 'GlobalUsers', $group ), |
271 | 292 | $reason, |
272 | 293 | array( |
— | — | @@ -278,7 +299,40 @@ |
279 | 300 | function makeRightsList( $ids ) { |
280 | 301 | return (bool)count($ids) ? implode( ', ', $ids ) : wfMsgForContent( 'rightsnone' ); |
281 | 302 | } |
282 | | - |
| 303 | + |
| 304 | + function setRestrictions( $group, $set ) { |
| 305 | + $dbw = CentralAuthUser::getCentralDB(); |
| 306 | + if( $set == 0 ) { |
| 307 | + $dbw->delete( 'global_group_restrictions', array( 'ggr_group' => $group ), __METHOD__ ); |
| 308 | + } else { |
| 309 | + $dbw->replace( 'global_group_restrictions', array( 'ggr_group' ), |
| 310 | + array( 'ggr_group' => $group, 'ggr_set' => $set, ), __METHOD__ ); |
| 311 | + } |
| 312 | + return (bool)$dbw->affectedRows(); |
| 313 | + } |
| 314 | + |
| 315 | + function addLogEntry2( $group, $old, $new, $reason ) { |
| 316 | + global $wgRequest; |
| 317 | + |
| 318 | + $log = new LogPage( 'gblrights' ); |
| 319 | + |
| 320 | + $log->addEntry( 'groupprms3', |
| 321 | + SpecialPage::getTitleFor( 'GlobalUsers', $group ), |
| 322 | + $reason, |
| 323 | + array( |
| 324 | + $this->getWikiSetName( $old ), |
| 325 | + $this->getWikiSetName( $new ), |
| 326 | + ) |
| 327 | + ); |
| 328 | + } |
| 329 | + |
| 330 | + function getWikiSetName( $id ) { |
| 331 | + if( $id ) |
| 332 | + return WikiSet::newFromID( $id )->getName(); |
| 333 | + else |
| 334 | + return wfMsgForContent( 'centralauth-editgroup-noset' ); |
| 335 | + } |
| 336 | + |
283 | 337 | function invalidateRightsCache( $group ) { |
284 | 338 | global $wgMemc; |
285 | 339 | |
Index: trunk/extensions/CentralAuth/CentralAuth.php |
— | — | @@ -136,12 +136,14 @@ |
137 | 137 | $wgAutoloadClasses['CentralAuthHooks'] = "$caBase/CentralAuthHooks.php"; |
138 | 138 | $wgAutoloadClasses['WikiMap'] = "$caBase/WikiMap.php"; |
139 | 139 | $wgAutoloadClasses['WikiReference'] = "$caBase/WikiMap.php"; |
| 140 | +$wgAutoloadClasses['WikiSet'] = "$caBase/WikiSet.php"; |
140 | 141 | $wgAutoloadClasses['SpecialAutoLogin'] = "$caBase/SpecialAutoLogin.php"; |
141 | 142 | $wgAutoloadClasses['CentralAuthUserArray'] = "$caBase/CentralAuthUserArray.php"; |
142 | 143 | $wgAutoloadClasses['CentralAuthUserArrayFromResult'] = "$caBase/CentralAuthUserArray.php"; |
143 | 144 | $wgAutoloadClasses['SpecialGlobalGroupMembership'] = "$caBase/SpecialGlobalGroupMembership.php"; |
144 | 145 | $wgAutoloadClasses['CentralAuthGroupMembershipProxy'] = "$caBase/CentralAuthGroupMembershipProxy.php"; |
145 | 146 | $wgAutoloadClasses['SpecialGlobalGroupPermissions'] = "$caBase/SpecialGlobalGroupPermissions.php"; |
| 147 | +$wgAutoloadClasses['SpecialEditWikiSets'] = "$caBase/SpecialEditWikiSets.php"; |
146 | 148 | |
147 | 149 | $wgExtensionMessagesFiles['SpecialCentralAuth'] = "$caBase/CentralAuth.i18n.php"; |
148 | 150 | |
— | — | @@ -182,6 +184,7 @@ |
183 | 185 | $wgSpecialPages['MergeAccount'] = 'SpecialMergeAccount'; |
184 | 186 | $wgSpecialPages['GlobalGroupMembership'] = 'SpecialGlobalGroupMembership'; |
185 | 187 | $wgSpecialPages['GlobalGroupPermissions'] = 'SpecialGlobalGroupPermissions'; |
| 188 | +$wgSpecialPages['EditWikiSets'] = 'SpecialEditWikiSets'; |
186 | 189 | $wgSpecialPages['GlobalUsers'] = 'SpecialGlobalUsers'; |
187 | 190 | $wgSpecialPageGroups['GlobalUsers'] = 'users'; |
188 | 191 | |
— | — | @@ -195,9 +198,26 @@ |
196 | 199 | $wgLogActions['globalauth/unhide'] = 'centralauth-log-entry-unhide'; |
197 | 200 | $wgLogActions['globalauth/lockandhid'] = 'centralauth-log-entry-lockandhide'; |
198 | 201 | |
199 | | -$wgLogTypes[] = 'gblrights'; |
200 | | -$wgLogNames['gblrights'] = 'centralauth-rightslog-name'; |
201 | | -$wgLogHeaders['gblrights'] = 'centralauth-rightslog-header'; |
202 | | -$wgLogActions['gblrights/usergroups'] = 'centralauth-rightslog-entry-usergroups'; |
203 | | -$wgLogActions['gblrights/groupperms'] = 'centralauth-rightslog-entry-groupperms'; |
204 | | -$wgLogActions['gblrights/groupperms2'] = 'centralauth-rightslog-entry-groupperms2'; |
| 202 | +$wgLogTypes[] = 'gblrights'; |
| 203 | +$wgLogNames['gblrights'] = 'centralauth-rightslog-name'; |
| 204 | +$wgLogHeaders['gblrights'] = 'centralauth-rightslog-header'; |
| 205 | +$wgLogActions['gblrights/usergroups'] = 'centralauth-rightslog-entry-usergroups'; |
| 206 | +$wgLogActions['gblrights/groupperms'] = 'centralauth-rightslog-entry-groupperms'; |
| 207 | +$wgLogActions['gblrights/groupprms2'] = 'centralauth-rightslog-entry-groupperms2'; |
| 208 | +$wgLogActions['gblrights/groupprms3'] = 'centralauth-rightslog-entry-groupperms3'; |
| 209 | +foreach( array( 'newset', 'setrename', 'setnewtype', 'setchange' ) as $type ) |
| 210 | + $wgLogActionsHandlers["gblrights/{$type}"] = 'efHandleWikiSetLogEntry'; |
| 211 | + |
| 212 | +function efHandleWikiSetLogEntry( $type, $action, $title, $skin, $params, $filterWikilinks = false ) { |
| 213 | + wfLoadExtensionMessages('SpecialCentralAuth'); |
| 214 | + $link = $skin ? $skin->makeLinkObj( $title, $params[0] ) : $params[0]; |
| 215 | + if( $action == 'newset' ) |
| 216 | + $args = array( WikiSet::formatType( $params[1] ), $params[2] ); |
| 217 | + if( $action == 'setrename' ) |
| 218 | + $args = array( $params[1] ); |
| 219 | + if( $action == 'setnewtype' ) |
| 220 | + $args = array( WikiSet::formatType( $params[1] ), WikiSet::formatType( $params[2] ) ); |
| 221 | + if( $action == 'setchange' ) |
| 222 | + $args = array( $params[1] ? $params[1] : wfMsg( 'rightsnone' ), $params[2] ? $params[2] : wfMsg( 'rightsnone' ) ); |
| 223 | + return wfMsgReal( "centralauth-rightslog-entry-{$action}", array_merge( array( $link ), $args ), true, !$skin ); |
| 224 | +} |
\ No newline at end of file |
Index: trunk/extensions/CentralAuth/WikiSet.php |
— | — | @@ -0,0 +1,183 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class WikiSet { |
| 5 | + const OPTIN = 'optin'; |
| 6 | + const OPTOUT = 'optout'; |
| 7 | + const VERSION = 1; |
| 8 | + |
| 9 | + private $mId; //ID of the group |
| 10 | + private $mName; //Display name of the group |
| 11 | + private $mType; //Opt-in based or opt-out based |
| 12 | + private $mWikis; //List of wikis |
| 13 | + private $mVersion = self::VERSION; //Caching purposes |
| 14 | + |
| 15 | + static $mCacheVars = array( |
| 16 | + 'mId', |
| 17 | + 'mName', |
| 18 | + 'mType', |
| 19 | + 'mWikis', |
| 20 | + 'mVersion', |
| 21 | + ); |
| 22 | + |
| 23 | + public function __construct( $name = '', $type = self::OPTIN, $wikis = array(), $id = 0 ) { |
| 24 | + $this->mId = $id; |
| 25 | + $this->mName = $name; |
| 26 | + $this->mType = $type; |
| 27 | + $this->mWikis = $wikis; |
| 28 | + } |
| 29 | + |
| 30 | + protected static function memcKey( $k ) { return "wikiset:{$k}"; } |
| 31 | + |
| 32 | + public function getId() { return $this->mId; } |
| 33 | + public function exists() { return (bool)$this->getID(); } |
| 34 | + public function getName() { return $this->mName; } |
| 35 | + public function setName( $n, $commit = false ) { return $this->setDbField( 'ws_name', $n, $commit ); } |
| 36 | + public function getWikisRaw() { return $this->mWikis; } |
| 37 | + public function setWikisRaw( $w, $commit = false ) { return $this->setDbField( 'ws_wikis', $w, $commit ); } |
| 38 | + public function getType() { return $this->mType; } |
| 39 | + public function setType( $t, $commit = false ) { |
| 40 | + if( !in_array( $t, array( self::OPTIN, self::OPTOUT ) ) ) |
| 41 | + return false; |
| 42 | + return $this->setDbField( 'ws_type', $t, $commit ); |
| 43 | + } |
| 44 | + protected function setDbField( $field, $value, $commit ) { |
| 45 | + $map = array( 'ws_name' => 'mName', 'ws_type' => 'mType', 'ws_wikis' => 'mWikis' ); |
| 46 | + $mname = $map[$field]; |
| 47 | + $this->$mname = $value; |
| 48 | + if( $commit ) |
| 49 | + $this->commit(); |
| 50 | + } |
| 51 | + |
| 52 | + public static function newFromRow( $row ) { |
| 53 | + if( !$row ) return null; |
| 54 | + return new WikiSet( |
| 55 | + $row->ws_name, |
| 56 | + $row->ws_type, |
| 57 | + explode( ',', $row->ws_wikis ), |
| 58 | + $row->ws_id |
| 59 | + ); |
| 60 | + } |
| 61 | + |
| 62 | + public static function newFromName( $id, $useCache = true ) { |
| 63 | + if( $useCache ) { |
| 64 | + global $wgMemc; |
| 65 | + $data = $wgMemc->get( self::memcKey( "name:" . md5( $id ) ) ); |
| 66 | + if( $data ) { |
| 67 | + if( $data['mVersion'] == self::VERSION ) { |
| 68 | + $ws = new WikiSet( null, null, null ); |
| 69 | + foreach( $data as $name => $val ) |
| 70 | + $ws->$name = $val; |
| 71 | + return $ws; |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + $dbr = CentralAuthUser::getCentralSlaveDB(); |
| 76 | + $row = $dbr->selectRow( |
| 77 | + 'wikiset', '*', array( 'ws_name' => $id ), __METHOD__ |
| 78 | + ); |
| 79 | + if( !$row ) |
| 80 | + return null; |
| 81 | + $ws = self::newFromRow( $row ); |
| 82 | + $ws->saveToCache(); |
| 83 | + return $ws; |
| 84 | + } |
| 85 | + |
| 86 | + public static function newFromID( $id, $useCache = true ) { |
| 87 | + if( $useCache ) { |
| 88 | + global $wgMemc; |
| 89 | + $data = $wgMemc->get( self::memcKey( $id ) ); |
| 90 | + if( $data ) { |
| 91 | + if( $data['mVersion'] == self::VERSION ) { |
| 92 | + $ws = new WikiSet( null, null, null ); |
| 93 | + foreach( $data as $name => $val ) |
| 94 | + $ws->$name = $val; |
| 95 | + return $ws; |
| 96 | + } |
| 97 | + } |
| 98 | + } |
| 99 | + $dbr = CentralAuthUser::getCentralSlaveDB(); |
| 100 | + $row = $dbr->selectRow( |
| 101 | + 'wikiset', '*', array( 'ws_id' => $id ), __METHOD__ |
| 102 | + ); |
| 103 | + if( !$row ) |
| 104 | + return null; |
| 105 | + $ws = self::newFromRow( $row ); |
| 106 | + $ws->saveToCache(); |
| 107 | + return $ws; |
| 108 | + } |
| 109 | + |
| 110 | + public function commit() { |
| 111 | + $dbw = CentralAuthUser::getCentralDB(); |
| 112 | + $dbw->replace( 'wikiset', array( 'ws_id' ), |
| 113 | + array( |
| 114 | + 'ws_id' => $this->mId, |
| 115 | + 'ws_name' => $this->mName, |
| 116 | + 'ws_type' => $this->mType, |
| 117 | + 'ws_wikis' => implode( ',', $this->mWikis ), |
| 118 | + ), __METHOD__ |
| 119 | + ); |
| 120 | + $dbw->commit(); |
| 121 | + if( !$this->mId ) |
| 122 | + $this->mId = $dbw->insertId(); |
| 123 | + $this->purge(); |
| 124 | + return (bool)$dbw->affectedRows(); |
| 125 | + } |
| 126 | + |
| 127 | + public function purge() { |
| 128 | + global $wgMemc; |
| 129 | + $wgMemc->delete( self::memcKey( $this->mId ) ); |
| 130 | + $wgMemc->delete( self::memcKey( "name:" . md5( $this->mName ) ) ); |
| 131 | + } |
| 132 | + |
| 133 | + public function saveToCache() { |
| 134 | + global $wgMemc; |
| 135 | + $data = array(); |
| 136 | + foreach( self::$mCacheVars as $var ) { |
| 137 | + $data[$var] = $this->$var; |
| 138 | + } |
| 139 | + $wgMemc->set( self::memcKey( $this->mId ), $data ); |
| 140 | + } |
| 141 | + |
| 142 | + public function getWikis() { |
| 143 | + if( $this->mType == self::OPTIN ) |
| 144 | + return $this->mWikis; |
| 145 | + else |
| 146 | + return array_diff( CentralAuthUser::getWikiList(), $this->mWikis ); |
| 147 | + } |
| 148 | + |
| 149 | + public function inSet( $wiki = '' ) { |
| 150 | + if( !$wiki ) |
| 151 | + $wiki = wfWikiID(); |
| 152 | + return in_array( $wiki, $this->getWikis() ); |
| 153 | + } |
| 154 | + |
| 155 | + public function getRestrictedGroups() { |
| 156 | + $dbr = CentralAuthUser::getCentralSlaveDB(); |
| 157 | + $r = $dbr->select( |
| 158 | + 'global_group_restrictions', '*', array( 'ggr_set' => $this->mId ), __METHOD__ |
| 159 | + ); |
| 160 | + $result = array(); |
| 161 | + foreach( $r as $row ) |
| 162 | + $result[] = $row->ggr_group; |
| 163 | + return $result; |
| 164 | + } |
| 165 | + |
| 166 | + public static function getAllWikiSets() { |
| 167 | + $dbr = CentralAuthUser::getCentralSlaveDB(); |
| 168 | + $res = $dbr->select( 'wikiset', '*', false, __METHOD__ ); |
| 169 | + $result = array(); |
| 170 | + while( $row = $dbr->fetchObject( $res ) ) |
| 171 | + $result[] = self::newFromRow( $row ); |
| 172 | + return $result; |
| 173 | + } |
| 174 | + |
| 175 | + public static function getWikiSetForGroup( $group ) { |
| 176 | + $dbr = CentralAuthUser::getCentralSlaveDB(); |
| 177 | + $res = $dbr->selectRow( 'global_group_restrictions', '*', array( 'ggr_group' => $group ), __METHOD__ ); |
| 178 | + return $res ? $res->ggr_set : 0; |
| 179 | + } |
| 180 | + |
| 181 | + public static function formatType( $type ) { |
| 182 | + return wfMsgHtml( "centralauth-rightslog-set-{$type}" ); |
| 183 | + } |
| 184 | +} |
Index: trunk/extensions/CentralAuth/CentralAuth.i18n.php |
— | — | @@ -235,12 +235,20 @@ |
236 | 236 | 'centralauth-log-entry-unhide' => 'unhid global account "<nowiki>$1</nowiki>"', |
237 | 237 | 'centralauth-log-entry-lockandhide' => 'locked and hid global account "<nowiki>$1</nowiki>"', |
238 | 238 | |
239 | | - 'centralauth-rightslog-name' => 'Global rights log', |
240 | | - 'centralauth-rightslog-entry-usergroups' => 'changed global group membership for $1 from $2 to $3', |
241 | | - 'centralauth-rightslog-entry-groupperms' => 'changed group permissions for $1 from $2 to $3', |
| 239 | + 'centralauth-rightslog-name' => 'Global rights log', |
| 240 | + 'centralauth-rightslog-entry-usergroups' => 'changed global group membership for $1 from $2 to $3', |
| 241 | + 'centralauth-rightslog-entry-groupperms' => 'changed group permissions for $1 from $2 to $3', |
242 | 242 | 'centralauth-rightslog-entry-groupperms2' => 'changed group permissions for $1. Added $2; Removed $3', |
243 | | - 'centralauth-rightslog-header' => 'This log contains operations on global groups: membership and permissions changes', |
| 243 | + 'centralauth-rightslog-entry-groupperms3' => 'changed group restricted wikis set for $1 from $2 to $3', |
| 244 | + 'centralauth-rightslog-header' => 'This log contains operations on global groups: membership and permissions changes', |
244 | 245 | |
| 246 | + 'centralauth-rightslog-entry-newset' => 'created $2 wiki set $1 with following wikis: $3', |
| 247 | + 'centralauth-rightslog-entry-setrename' => 'renamed wiki set "$2" to "$1"', |
| 248 | + 'centralauth-rightslog-entry-setnewtype' => 'changed type of "$1" from $2 to $3', |
| 249 | + 'centralauth-rightslog-entry-setchange' => 'changed wikis in "$1": added: $2; removed: $3', |
| 250 | + 'centralauth-rightslog-set-optin' => 'opt-in based', |
| 251 | + 'centralauth-rightslog-set-optout' => 'opt-out based', |
| 252 | + |
245 | 253 | // Global group membership |
246 | 254 | 'globalgroupmembership' => 'Membership in global groups', |
247 | 255 | |
— | — | @@ -249,7 +257,7 @@ |
250 | 258 | 'centralauth-globalgroupperms-grouplist' => 'The following global groups have been configured. |
251 | 259 | You may view and edit the permissions assigned to any group. |
252 | 260 | A group may be deleted by removing all rights from it.', |
253 | | - 'centralauth-globalgroupperms-grouplistitem' => '$1 ([[Special:GlobalGroupPermissions/$2|View and edit permissions]])', |
| 261 | + 'centralauth-globalgroupperms-grouplistitem' => '$1 ([[Special:GlobalGroupPermissions/$2|view/edit]])', |
254 | 262 | 'centralauth-existinggroup-legend' => 'Existing groups', |
255 | 263 | 'centralauth-newgroup-legend' => 'Create a new group', |
256 | 264 | 'centralauth-newgroup-intro' => 'You can use this form to assign permissions to a new group. |
— | — | @@ -265,16 +273,45 @@ |
266 | 274 | 'centralauth-editgroup-member-edit' => '$2 ([[MediaWiki:Group-$1-member|edit]])', |
267 | 275 | 'centralauth-editgroup-members' => 'Member list:', |
268 | 276 | 'centralauth-editgroup-members-link' => '[[Special:Globalusers/$1|List of users with $2 rights]]', |
| 277 | + 'centralauth-editgroup-restrictions' => 'Set of wikis where this group is active:', |
| 278 | + 'centralauth-editgroup-noset' => '(none)', |
269 | 279 | 'centralauth-editgroup-submit' => 'Save changes to group permissions', |
270 | 280 | 'centralauth-editgroup-perms' => 'Assigned permissions:', |
271 | 281 | 'centralauth-editgroup-reason' => 'Reason for change:', |
272 | 282 | 'centralauth-editgroup-success' => 'Group permissions changed', |
273 | 283 | 'centralauth-editgroup-success-text' => 'You have successfully changed the group permissions for the $1 group. |
274 | 284 | [[Special:GlobalGroupPermissions|Return to group management]]', |
275 | | - 'centralauth-globalgrouppermissions-knownwiki' => "Select a wiki on which they have an account:", |
| 285 | + 'centralauth-editgroup-editsets' => '([[Special:EditWikiSets|edit]])', |
| 286 | + 'centralauth-globalgrouppermissions-knownwiki' => "Wiki on which they have an account:", |
276 | 287 | 'centralauth-globalgroupmembership-badknownwiki' => "The global user '''$1''' is not active on the wiki you specified ('' $2 ''). |
277 | 288 | You may be attempting to assign rights to the wrong user!", |
278 | 289 | |
| 290 | + // Wiki sets editing |
| 291 | + 'centralauth-editset' => 'Edit wiki sets', |
| 292 | + 'centralauth-editset-legend' => 'Edit or create wiki set', |
| 293 | + 'centralauth-editset-intro' => 'The following wiki sets have already been created. You may view and modify any of them, or create a new set.', |
| 294 | + 'centralauth-editset-item' => '$1 ([[Special:EditWikiSets/$2|view/edit]])', |
| 295 | + 'centralauth-editset-new' => 'Create a new set', |
| 296 | + 'centralauth-editset-notfound' => 'Wiki set "$1" not found.', |
| 297 | + 'centralauth-editset-optin' => 'Opt-in based (includes only specified wikis)', |
| 298 | + 'centralauth-editset-optout' => 'Opt-out based (includes all wikis except specified)', |
| 299 | + 'centralauth-editset-legend-edit' => 'Editing wiki set "$1"', |
| 300 | + 'centralauth-editset-legend-new' => 'Creating new wiki set', |
| 301 | + 'centralauth-editset-name' => 'Name:', |
| 302 | + 'centralauth-editset-type' => 'Type:', |
| 303 | + 'centralauth-editset-wikis' => 'Wikis:', |
| 304 | + 'centralauth-editset-reason' => 'Reason:', |
| 305 | + 'centralauth-editset-submit' => 'Submit', |
| 306 | + 'centralauth-editset-badname' => 'Invalid or empty set name.', |
| 307 | + 'centralauth-editset-badtype' => 'Invalid set type.', |
| 308 | + 'centralauth-editset-setexists' => 'Set with that name already exists', |
| 309 | + 'centralauth-editset-nowikis' => 'No wikis specified.', |
| 310 | + 'centralauth-editset-grouplink' => '[[Special:GlobalGroupPermissions/$1|$1]]', |
| 311 | + 'centralauth-editset-nouse' => '(none)', |
| 312 | + 'centralauth-editset-usage' => 'Used in groups:', |
| 313 | + 'centralauth-editset-badwikis' => 'Following wikis do not exist: $1.', |
| 314 | + 'centralauth-editset-success' => 'Successfully changed wiki set.', |
| 315 | + |
279 | 316 | // User rights |
280 | 317 | 'right-globalgroupmembership' => 'Edit membership to global groups', |
281 | 318 | 'right-centralauth-admin' => 'Administrate global accounts', |