Index: trunk/extensions/PrivatePageProtection/PrivatePageProtection.i18n.php |
— | — | @@ -0,0 +1,49 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Internationalisation for PrivatePageProtection extension |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Extensions |
| 8 | + */ |
| 9 | + |
| 10 | +$messages = array(); |
| 11 | + |
| 12 | +/** English |
| 13 | + * @author Daniel Kinzler |
| 14 | + */ |
| 15 | +$messages['en'] = array( |
| 16 | + 'privatepp-desc' => 'Allowes restricting page access based on user group', |
| 17 | + |
| 18 | + 'privatepp-lockout-prevented' => 'Lockout prevented: You have tried to restrict access to this page to {{PLURAL:$2|the group|one of the groups}} $1. ' |
| 19 | + . 'Since you are not a member of {{PLURAL:$2|this group|any of these groups}}, you would not be able to access the page after saving it. ' |
| 20 | + . 'Saving was aborted to avoid this.', |
| 21 | + |
| 22 | +); |
| 23 | + |
| 24 | +/** Message documentation (Message documentation) |
| 25 | + * @author Daniel Kinzler |
| 26 | + */ |
| 27 | +$messages['qqq'] = array( |
| 28 | + 'privatepp-desc' => '{{desc}}', |
| 29 | +); |
| 30 | + |
| 31 | +/** German (Deutsch) |
| 32 | + * @author Daniel Kinzler |
| 33 | + */ |
| 34 | +$messages['de'] = array( |
| 35 | + 'privatepp-desc' => 'Beschränkt den Zugang zu Wikiseite auf der Basis von benutzergruppen.', |
| 36 | + |
| 37 | + 'privatepp-lockout-prevented' => 'Aussperrung verhindert: Du hast versucht, den Zugang zu dieser Seite auf {{PLURAL:$2|die Gruppe|die gruppen}} $1 zu beschränken. ' |
| 38 | + . 'Da du kein Mitglied {{PLURAL:$2|dieser Gruppe|einer dieser Gruppen}} bist, könntest du nach dem Speichern nicht mehr auf die Seite zugreifen. ' |
| 39 | + . 'Um dies zu vermeiden wurde das Speichern abgebrochen.', |
| 40 | +); |
| 41 | + |
| 42 | +/** German (Deutsch) |
| 43 | + * @author Daniel Kinzler |
| 44 | + */ |
| 45 | +$messages['de_formal'] = array( |
| 46 | + 'privatepp-lockout-prevented' => 'Aussperrung verhindert: Sie haben versucht, den Zugang zu dieser Seite auf {{PLURAL:$2|die Gruppe|die gruppen}} $1 zu beschränken. ' |
| 47 | + . 'Da sie kein Mitglied {{PLURAL:$2|dieser Gruppe|einer dieser Gruppen}} sind, könnten sie nach dem Speichern nicht mehr auf die Seite zugreifen. ' |
| 48 | + . 'Um dies zu vermeiden wurde das Speichern abgebrochen.', |
| 49 | +); |
| 50 | + |
Index: trunk/extensions/PrivatePageProtection/PrivatePageProtection.php |
— | — | @@ -0,0 +1,175 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/** |
| 5 | + * PrivatePageProtection extension - implements per page acccess restrictions based on user group. |
| 6 | + * Which groups are authorized for viewing is defined on-page, using a parser function. |
| 7 | + * |
| 8 | + * @file |
| 9 | + * @ingroup Extensions |
| 10 | + * @author Daniel Kinzler, brightbyte.de |
| 11 | + * @copyright © 2007 Daniel Kinzler |
| 12 | + * @license GNU General Public Licence 2.0 or later |
| 13 | + */ |
| 14 | + |
| 15 | +/* |
| 16 | +* WARNING: you can use this extension to deny read access to some pages. Keep in mind that this |
| 17 | +* may be circumvented in several ways. This extension doesn't try to |
| 18 | +* plug such holes. Also note that pages that are not readable will still be shown in listings, |
| 19 | +* such as the search page, categories, etc. |
| 20 | +* |
| 21 | +* Known ways to access "hidden" pages: |
| 22 | +* - transcluding as template. can be avoided using $wgNonincludableNamespaces. |
| 23 | +* Some search messages may reveal the page existance by producing links to it (MediaWiki:searchsubtitle, |
| 24 | +* MediaWiki:noexactmatch, MediaWiki:searchmenu-exists, MediaWiki:searchmenu-new...). |
| 25 | +* |
| 26 | +* NOTE: you cannot GRANT access to things forbidden by $wgGroupPermissions. You can only DENY access |
| 27 | +* granted there. |
| 28 | +*/ |
| 29 | + |
| 30 | +if ( !defined( 'MEDIAWIKI' ) ) { |
| 31 | + echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" ); |
| 32 | + die( 1 ); |
| 33 | +} |
| 34 | + |
| 35 | +$wgExtensionCredits['parserfunction'][] = array( |
| 36 | + 'path' => __FILE__, |
| 37 | + 'name' => 'PrivatePageProtection', |
| 38 | + 'author' => array( 'Daniel Kinzler'), |
| 39 | + 'url' => 'http://mediawiki.org/wiki/Extension:PrivatePageProtection', |
| 40 | + 'descriptionmsg' => 'privatepp-desc', |
| 41 | +); |
| 42 | + |
| 43 | +$wgExtensionMessagesFiles['PrivatePageProtection'] = dirname(__FILE__) . '/PrivatePageProtection.i18n.php'; |
| 44 | + |
| 45 | +$wgHooks['ParserFirstCallInit'][] = 'privateppParserFirstCallInit'; |
| 46 | +$wgHooks['LanguageGetMagic'][] = 'privateppLanguageGetMagic'; |
| 47 | +$wgHooks['getUserPermissionsErrorsExpensive'][] = 'privateppUserPermissionsErrors'; |
| 48 | +$wgHooks['ArticleSave'][] = 'privateppArticleSave'; |
| 49 | + |
| 50 | +// Tell MediaWiki that the parser function exists. |
| 51 | +function privateppParserFirstCallInit( &$parser ) { |
| 52 | + |
| 53 | + // Create a function hook associating the magic word |
| 54 | + $parser->setFunctionHook('allow-groups', 'privateppRenderTag'); |
| 55 | + return true; |
| 56 | +} |
| 57 | + |
| 58 | +// Tell MediaWiki which magic words can invoke the parser function. |
| 59 | +function privateppLanguageGetMagic( &$magicWords, $langCode ) { |
| 60 | + |
| 61 | + // Add the magic words. |
| 62 | + // If the first element of the array is 0, the magic word is case insensitive. |
| 63 | + $magicWords['allow-groups'] = array( 0, 'allow-groups', 'allowed-groups', 'ppp' ); #TODO: i18n?! |
| 64 | + return true; |
| 65 | +} |
| 66 | + |
| 67 | +// Render the output of the parser function. |
| 68 | +function privateppRenderTag( $parser, $param1 = '', $param2 = '' ) { |
| 69 | + $args = func_get_args(); |
| 70 | + |
| 71 | + if ( count( $args ) <= 1 ) { |
| 72 | + return true; |
| 73 | + } |
| 74 | + |
| 75 | + $groups = array(); |
| 76 | + |
| 77 | + for ( $i = 1; $i < count( $args ); $i++ ) { |
| 78 | + $groups[] = strtolower( trim( $args[$i] ) ); #XXX: allow localized group names?! |
| 79 | + } |
| 80 | + |
| 81 | + $groups = implode( "|", $groups ); |
| 82 | + |
| 83 | + $out = $parser->getOutput(); |
| 84 | + |
| 85 | + $ppp = $out->getProperty('ppp_allowed_groups'); |
| 86 | + if ( $ppp ) { |
| 87 | + $groups = $ppp . '|' . $groups; |
| 88 | + } |
| 89 | + |
| 90 | + $out->setProperty('ppp_allowed_groups', $groups); |
| 91 | + |
| 92 | + return array( 'text' => '', 'ishtml' => true, 'inline' => true ); |
| 93 | +} |
| 94 | + |
| 95 | +/** |
| 96 | + * Returns a list of allowed groups for the given page. |
| 97 | + */ |
| 98 | +function privateppGetAllowedGroups( $title ) { |
| 99 | + $result = array(); |
| 100 | + $id = $title->getArticleID(); |
| 101 | + |
| 102 | + if ( $id == 0 ) { |
| 103 | + return array(); |
| 104 | + } |
| 105 | + |
| 106 | + $dbr = wfGetDB( DB_SLAVE ); |
| 107 | + $res = $dbr->select( array( 'page_props' ), |
| 108 | + array( 'pp_value' ), |
| 109 | + array( 'pp_page' => $id, 'pp_propname' => 'ppp_allowed_groups' ), |
| 110 | + __METHOD__ ); |
| 111 | + |
| 112 | + if ( $res !== false ) { |
| 113 | + foreach ( $res as $row ) { |
| 114 | + $result[] = $row->pp_value; |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + #TODO: use object cache?! get from parser cache?! |
| 119 | + return $result; |
| 120 | +} |
| 121 | + |
| 122 | +function privateppGetAccessError( $groups, $user ) { |
| 123 | + global $wgLang; |
| 124 | + |
| 125 | + if ( !$groups ) return null; |
| 126 | + if ( is_string( $groups ) ) $groups = explode('|', $groups); |
| 127 | + |
| 128 | + $ugroups = $user->getEffectiveGroups( true );; |
| 129 | + |
| 130 | + $match = array_intersect( $ugroups, $groups ); |
| 131 | + |
| 132 | + if ( $match ) { |
| 133 | + # group is allowed - keep processing |
| 134 | + return null; |
| 135 | + } else { |
| 136 | + # group is denied - abort |
| 137 | + $groupLinks = array_map( array( 'User', 'makeGroupLinkWiki' ), $groups ); |
| 138 | + |
| 139 | + $err = array( |
| 140 | + 'badaccess-groups', |
| 141 | + $wgLang->commaList( $groupLinks ), |
| 142 | + count( $groups ) |
| 143 | + ); |
| 144 | + |
| 145 | + return $err; |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +function privateppUserPermissionsErrors( $title, $user, $action, &$result ) { |
| 150 | + $groups = privateppGetAllowedGroups( $title ); |
| 151 | + $result = privateppGetAccessError( $groups, $user ); |
| 152 | + |
| 153 | + if ( !$result ) return true; |
| 154 | + else return false; |
| 155 | +} |
| 156 | + |
| 157 | +function privateppArticleSave( &$wikipage, &$user, &$text, &$summary, |
| 158 | + $minor, $watchthis, $sectionanchor, &$flags, &$status ) { |
| 159 | + |
| 160 | + # prevent users from saving a page with access restrictions that |
| 161 | + # would lock them out opf the page. |
| 162 | + |
| 163 | + #XXX: calling prepareTextForEdit() causes the text to be parsed a second time! |
| 164 | + # but there doesn't seem to be a hook that has access to the parseroutput... |
| 165 | + $editInfo = $wikipage->prepareTextForEdit( $text, null, $user ); |
| 166 | + $groups = $editInfo->output->getProperty('ppp_allowed_groups'); |
| 167 | + |
| 168 | + $err = privateppGetAccessError( $groups, $user ); |
| 169 | + if ( !$err ) return true; |
| 170 | + |
| 171 | + $err[0] = 'privatepp-lockout-prevented'; #override message key |
| 172 | + throw new PermissionsError( 'edit', array( $err ) ); |
| 173 | + |
| 174 | + #$status->fatal( $err[0], $err[1], $err[2] ); # message, groups, count |
| 175 | + #return false; |
| 176 | +} |