Index: trunk/extensions/WikimediaIncubator/IncubatorTest.php |
— | — | @@ -49,27 +49,65 @@ |
50 | 50 | } |
51 | 51 | |
52 | 52 | /* |
53 | | - * This validates a language code. Currently it is set |
54 | | - * to only allow two or three-letter codes strictly, but |
55 | | - * it can be changed when the policy changes. |
56 | | - * See also $wmincLangCodeLength. |
| 53 | + * This validates a given language code. |
| 54 | + * Only "xx[x]" and "xx[x]-x[xxxxxxxx]" are allowed. |
57 | 55 | */ |
58 | 56 | static function validateLanguageCode( $code ) { |
59 | | - return (bool) preg_match( '/[a-z][a-z][a-z]?/', $code ); |
| 57 | + global $wmincLangCodeLength; |
| 58 | + if( strlen( $code ) > $wmincLangCodeLength ) { return false; } |
| 59 | + if( $code == 'be-x-old' ) { return true; } // one exception... |
| 60 | + return (bool) preg_match( '/^[a-z][a-z][a-z]?(-[a-z]+)?$/', $code ); |
60 | 61 | } |
61 | 62 | |
62 | 63 | /* |
63 | | - * Same as above, but for full prefix in a given title. |
| 64 | + * This validates a full prefix in a given title. |
| 65 | + * It gives an array with the project and language code, containing |
| 66 | + * the key 'error' if it is invalid. |
| 67 | + * Use validatePrefix() if you just want true or false. |
| 68 | + * Use displayPrefixedTitle() to make a prefix page title! |
| 69 | + * |
64 | 70 | * @param $onlyprefix Bool Whether to validate only the prefix, or |
65 | 71 | * also allow other text within the page title (Wx/xxx vs Wx/xxx/Text) |
66 | 72 | */ |
67 | | - static function validatePrefix( $title, $onlyprefix = false ) { |
| 73 | + static function analyzePrefix( $title, $onlyprefix = false ) { |
| 74 | + $data = array(); |
| 75 | + // split title into parts |
| 76 | + $titleparts = explode( '/', $title ); |
| 77 | + if( !is_array( $titleparts ) || !isset( $titleparts[1] ) ) { |
| 78 | + $data['error'] = 'noslash'; |
| 79 | + } else { |
| 80 | + $data['project'] = $titleparts[0][1]; // get the x from Wx/... |
| 81 | + $data['lang'] = $titleparts[1]; |
| 82 | + $data['prefix'] = 'W'.$data['project'].'/'.$data['lang']; |
| 83 | + // check language code |
| 84 | + if( !self::validateLanguageCode( $data['lang'] ) ) { |
| 85 | + $data['error'] = 'invalidlangcode'; |
| 86 | + } |
| 87 | + } |
68 | 88 | global $wmincProjects; |
69 | 89 | $listProjects = implode( '', $wmincProjects ); // something like: pbtqn |
70 | | - return (bool) preg_match( '/^W['.$listProjects.']\/[a-z][a-z][a-z]?' . |
71 | | - ($onlyprefix ? '$' : '' ) . '/', $title ); |
| 90 | + if( !preg_match( '/^W['.$listProjects.']\/[a-z-]+' . |
| 91 | + ($onlyprefix ? '$/' : '(\/.+)?$/' ), $title ) ) { |
| 92 | + $data['error'] = 'invalidprefix'; |
| 93 | + } |
| 94 | + if( !$onlyprefix && ( isset( $data['error'] ) && |
| 95 | + $data['error'] != 'invalidprefix' ) ) { // there is a Page_title |
| 96 | + $prefixn = strlen( $data['prefix'].'/' ); // number of chars in prefix |
| 97 | + // get Page_title from Wx/xx/Page_title |
| 98 | + $data['realtitle'] = substr( $title, $prefixn ); |
| 99 | + } |
| 100 | + return $data; // return an array with information |
72 | 101 | } |
73 | 102 | |
| 103 | + /* |
| 104 | + * This returns simply true or false based on analyzePrefix(). |
| 105 | + */ |
| 106 | + static function validatePrefix( $title, $onlyprefix = false ) { |
| 107 | + $data = self::analyzePrefix( $title, $onlyprefix ); |
| 108 | + if( isset( $data['error'] ) ) { return true; } |
| 109 | + return false; |
| 110 | + } |
| 111 | + |
74 | 112 | /* |
75 | 113 | * Returns true if the given project (or preference |
76 | 114 | * by default) is one of the projects using the |
— | — | @@ -99,7 +137,7 @@ |
100 | 138 | |
101 | 139 | /* |
102 | 140 | * Makes a full prefixed title of a given page title and namespace |
103 | | - * @param $ns Tnt numeric value of namespace |
| 141 | + * @param $ns Int numeric value of namespace |
104 | 142 | */ |
105 | 143 | static function displayPrefixedTitle( $title, $ns = 0 ) { |
106 | 144 | global $wgLang, $wmincTestWikiNamespaces; |
— | — | @@ -139,42 +177,69 @@ |
140 | 178 | return true; |
141 | 179 | } |
142 | 180 | |
143 | | - static function checkPrefixOnEditPage( $editpage ) { |
144 | | - global $wmincProjectSite; |
145 | | - // If user has "project" as test wiki preference, it isn't needed to check |
146 | | - if ( self::displayPrefix() == $wmincProjectSite['short'] ) { |
| 181 | + /* Return an error if the user wants to create an unprefixed page |
| 182 | + */ |
| 183 | + static function checkPrefixCreatePermissions( $title, $user, $action, &$result ) { |
| 184 | + global $wmincProjectSite, $wmincTestWikiNamespaces, $wmincPseudoCategoryNSes; |
| 185 | + $titletext = $title->getText(); |
| 186 | + $ns = $title->getNamespace(); |
| 187 | + $prefixdata = self::analyzePrefix( $titletext ); |
| 188 | + if( $action != 'create' ) { |
| 189 | + // only check on page creation |
147 | 190 | return true; |
148 | | - } |
149 | | - global $wgTitle, $wmincTestWikiNamespaces; |
150 | | - $title = $wgTitle->getText(); |
151 | | - $ns = $wgTitle->getNamespace(); |
152 | | - // If it's in one of the content namespaces or if the page title has a prefix, return |
153 | | - if ( !in_array( $ns, $wmincTestWikiNamespaces ) || self::validatePrefix( $title ) ) { |
| 191 | + } elseif( self::displayPrefix() == $wmincProjectSite['short'] ) { |
| 192 | + // If user has "project" as test wiki preference, it isn't needed to check |
154 | 193 | return true; |
155 | | - } |
156 | | - $warning = '<div id="wminc-warning"><span id="wm-warning-unprefixed">' |
157 | | - . wfMsg( 'wminc-warning-unprefixed' ) |
158 | | - . '</span>'; |
159 | | - // If the user has a test wiki pref, suggest a page title with prefix |
160 | | - if ( self::isContentProject() ) { |
161 | | - global $wgUser; |
162 | | - $suggest = self::displayPrefixedTitle( $title, $ns ); |
163 | | - if ( !$wgTitle->exists() ) { // Creating a page, so suggest to create a prefixed page |
164 | | - $warning .= ' <span id="wminc-warning-suggest">' |
165 | | - . wfMsg( 'wminc-warning-suggest', $suggest ) |
166 | | - . '</span>'; |
167 | | - } elseif ( $wgUser->isAllowed( 'move' ) ) { // Page exists, so suggest to move |
168 | | - $warning .= ' <span id="wminc-warning-suggest-move" class="plainlinks">' |
169 | | - . wfMsg( 'wminc-warning-suggest-move', $suggest, wfUrlencode( $suggest ), wfUrlencode( $wgTitle ) ) |
170 | | - . '</span>'; |
| 194 | + } elseif( !in_array( $ns, $wmincTestWikiNamespaces ) ) { |
| 195 | + // OK if it's not in one of the content namespaces |
| 196 | + return true; |
| 197 | + } elseif( !isset( $prefixdata['error'] ) ) { |
| 198 | + // no error in prefix -> no error to show |
| 199 | + return true; |
| 200 | + } elseif( ($ns == NS_CATEGORY || $ns == NS_CATEGORY_TALK) && |
| 201 | + preg_match('/^('.implode('|',$wmincPseudoCategoryNSes).'):.+$/', $titletext) ) { |
| 202 | + // whitelisting |
| 203 | + return true; |
| 204 | + } elseif( $prefixdata['error'] == 'invalidlangcode' ) { |
| 205 | + $error[] = array( 'wminc-error-wronglangcode', $prefixdata['lang'] ); |
| 206 | + } elseif ( self::isContentProject() ) { |
| 207 | + // If the user has a test wiki pref, suggest a page title with prefix |
| 208 | + $suggesttitle = (isset( $prefixdata['realtitle'] ) ? |
| 209 | + $prefixdata['realtitle'] : $titletext ); |
| 210 | + $suggest = self::displayPrefixedTitle( $suggesttitle, $ns ); |
| 211 | + if ( !$title->exists() ) { |
| 212 | + // Creating a page, so suggest to create a prefixed page |
| 213 | + $error[] = array( 'wminc-error-unprefixed-suggest', $suggest ); |
171 | 214 | } |
| 215 | + } else { |
| 216 | + $error = 'wminc-error-unprefixed'; |
172 | 217 | } |
173 | | - $warning .= '</div>'; |
174 | | - global $wgOut; |
175 | | - $wgOut->addWikiText( $warning ); |
176 | | - return true; |
| 218 | + $result = $error; |
| 219 | + return false; |
177 | 220 | } |
178 | 221 | |
| 222 | + /* Return an error if the user wants to move |
| 223 | + * an existing page to an unprefixed title |
| 224 | + */ |
| 225 | + static function checkPrefixMovePermissions( $oldtitle, $newtitle, $user, &$error ) { |
| 226 | + global $wmincProjectSite, $wmincTestWikiNamespaces; |
| 227 | + $prefixdata = self::analyzePrefix( $newtitle->getText() ); |
| 228 | + $ns = $newtitle->getNamespace(); |
| 229 | + if( !isset( $prefixdata['error'] ) ) { |
| 230 | + // if there is no error with the page title |
| 231 | + return true; |
| 232 | + } elseif( self::displayPrefix() == $wmincProjectSite['short'] ) { |
| 233 | + // If user has "project" as test wiki preference, it isn't needed to check |
| 234 | + return true; |
| 235 | + } elseif( !in_array( $ns, $wmincTestWikiNamespaces ) ) { |
| 236 | + // OK if it's not in one of the content namespaces |
| 237 | + return true; |
| 238 | + } |
| 239 | + // now there should be an error with the new page title |
| 240 | + $error = wfMsgWikiHtml( 'wminc-error-move-unprefixed' ); |
| 241 | + return false; |
| 242 | + } |
| 243 | + |
179 | 244 | /** |
180 | 245 | * Add a link to Special:ViewUserLang from Special:Contributions/USERNAME |
181 | 246 | * if the user has 'viewuserlang' permission |
Index: trunk/extensions/WikimediaIncubator/WikimediaIncubator.i18n.php |
— | — | @@ -22,12 +22,14 @@ |
23 | 23 | 'wminc-prefinfo-code' => 'The ISO 639 language code', |
24 | 24 | 'wminc-prefinfo-project' => 'Select the Wikimedia project (Incubator option is for users who do general work)', |
25 | 25 | 'wminc-prefinfo-error' => 'You selected a project that needs a language code.', |
26 | | - 'wminc-warning-unprefixed' => "'''Warning:''' The page you are editing is unprefixed!", |
27 | | - 'wminc-warning-suggest' => 'You can create a page at [[:$1]].', |
28 | | - 'wminc-warning-suggest-move' => 'You can [{{fullurl:Special:MovePage/$3|wpNewTitle=$2}} move this page to $1].', |
| 26 | + 'wminc-error-move-unprefixed' => "Error: The page you are trying to move to [[{{MediaWiki:Helppage}}|is unprefixed or has a wrong prefix]]!", |
| 27 | + 'wminc-error-wronglangcode' => "'''Error:''' The page you are trying to edit contains a [[{{MediaWiki:Helppage}}|wrong language code]] \"$1\"!", |
| 28 | + 'wminc-error-unprefixed' => "'''Error:''' The page you are trying to edit is [[{{MediaWiki:Helppage}}|unprefixed]]!", |
| 29 | + 'wminc-error-unprefixed-suggest' => "'''Error:''' The page you are trying to edit is [[{{MediaWiki:Helppage}}|unprefixed]]! You can create a page at [[:$1]].", |
29 | 30 | 'right-viewuserlang' => 'View [[Special:ViewUserLang|user language and test wiki]]', |
30 | 31 | 'randombytest' => 'Random page by test wiki', |
31 | 32 | 'randombytest-nopages' => 'There are no pages in your test wiki, in the namespace: $1.', |
| 33 | + 'wminc-recentchanges-all' => 'All recent changes', |
32 | 34 | ); |
33 | 35 | |
34 | 36 | /** Message documentation (Message documentation) |
Index: trunk/extensions/WikimediaIncubator/WikimediaIncubator.php |
— | — | @@ -13,7 +13,7 @@ |
14 | 14 | 'path' => __FILE__, |
15 | 15 | 'name' => 'Wikimedia Incubator', |
16 | 16 | 'author' => 'SPQRobin', |
17 | | - 'version' => '3.0.1', |
| 17 | + 'version' => '3.1.0', |
18 | 18 | 'url' => 'http://www.mediawiki.org/wiki/Extension:WikimediaIncubator', |
19 | 19 | 'descriptionmsg' => 'wminc-desc', |
20 | 20 | ); |
— | — | @@ -25,6 +25,7 @@ |
26 | 26 | /* General (globals and/or configuration) */ |
27 | 27 | $wmincPref = 'incubatortestwiki'; // Name of the preference |
28 | 28 | $dir = dirname( __FILE__ ) . '/'; |
| 29 | +// only one-letter codes can be used for projects |
29 | 30 | $wmincProjects = array( |
30 | 31 | 'Wikipedia' => 'p', |
31 | 32 | 'Wikibooks' => 'b', |
— | — | @@ -41,7 +42,11 @@ |
42 | 43 | NS_TEMPLATE, NS_TEMPLATE_TALK, |
43 | 44 | NS_CATEGORY, NS_CATEGORY_TALK, |
44 | 45 | ); |
45 | | -$wmincLangCodeLength = 3; // can be increased if needed (depends on policy) |
| 46 | +$wmincLangCodeLength = 12; // can be changed if needed (depends on policy) |
| 47 | +// Pseudo category namespaces like "Category:Maintenance:Delete", for easy whitelisting and structure |
| 48 | +$wmincPseudoCategoryNSes = array( |
| 49 | + 'Incubator', 'Help', 'Users', 'Maintenance', 'Files', |
| 50 | +); |
46 | 51 | |
47 | 52 | $wgExtensionMessagesFiles['WikimediaIncubator'] = $dir . 'WikimediaIncubator.i18n.php'; |
48 | 53 | |
— | — | @@ -59,8 +64,9 @@ |
60 | 65 | $wgHooks['LanguageGetMagic'][] = 'IncubatorTest::magicWord'; |
61 | 66 | $wgHooks['ParserGetVariableValueSwitch'][] = 'IncubatorTest::magicWordValue'; |
62 | 67 | |
63 | | -/* Edit page */ |
64 | | -$wgHooks['EditPage::showEditForm:initial'][] = 'IncubatorTest::checkPrefixOnEditPage'; |
| 68 | +/* Create/move page permissions */ |
| 69 | +$wgHooks['getUserPermissionsErrors'][] = 'IncubatorTest::checkPrefixCreatePermissions'; |
| 70 | +$wgHooks['AbortMove'][] = 'IncubatorTest::checkPrefixMovePermissions'; |
65 | 71 | |
66 | 72 | /* Recent Changes */ |
67 | 73 | $wgAutoloadClasses['TestWikiRC'] = $dir . 'TestWikiRC.php'; |
Index: trunk/extensions/WikimediaIncubator/SpecialViewUserLang.php |
— | — | @@ -67,7 +67,7 @@ |
68 | 68 | * @param $target Mixed: user whose language and test wiki we're looking up |
69 | 69 | */ |
70 | 70 | function showInfo( $target ) { |
71 | | - global $wgOut, $wgLang, $wmincPref, $wmincProjectSite; |
| 71 | + global $wgOut, $wmincPref, $wmincProjectSite; |
72 | 72 | $sk = $this->getSkin(); |
73 | 73 | $user = User::newFromName( $target ); |
74 | 74 | $langNames = Language::getLanguageNames(); |
— | — | @@ -75,9 +75,13 @@ |
76 | 76 | // show error if a user with that name does not exist |
77 | 77 | $wgOut->addHTML( Xml::span( wfMsg( 'wminc-userdoesnotexist', $target ), 'error' ) ); |
78 | 78 | } else { |
| 79 | + $prefix = IncubatorTest::displayPrefix( |
| 80 | + $user->getOption( $wmincPref . '-project' ), |
| 81 | + $user->getOption( $wmincPref . '-code' ) |
| 82 | + ); |
79 | 83 | if ( IncubatorTest::isContentProject() ) { |
80 | | - $testwiki = $sk->link( Title::newFromText( IncubatorTest::displayPrefix() ) ); |
81 | | - } elseif ( IncubatorTest::displayPrefix() == $wmincProjectSite['short'] ) { |
| 84 | + $testwiki = $sk->link( Title::newFromText( $prefix ) ); |
| 85 | + } elseif ( $prefix == $wmincProjectSite['short'] ) { |
82 | 86 | $testwiki = htmlspecialchars( $wmincProjectSite['name'] ); |
83 | 87 | } else { |
84 | 88 | $testwiki = wfMsgHtml( 'wminc-testwiki-none' ); |