Index: trunk/extensions/PageInCat/PageInCat.i18n.php |
— | — | @@ -11,14 +11,19 @@ |
12 | 12 | $messages['en'] = array( |
13 | 13 | 'pageincat-desc' => 'Adds a parser function <code><nowiki>{{#incat:...}}</nowiki></code> to determine if the current page is in a specified category', |
14 | 14 | 'pageincat-wrong-warn' => '\'\'\'Warning:\'\'\' The {{PLURAL:$2|category $1 was|categories $1 were}} detected incorrectly by <code><nowiki>{{#incat:...}}</nowiki></code>, and as a result this preview may be incorrect. The saved version of this page should be displayed in the correct manner.', |
| 15 | + 'pageincat-very-wrong-warn' => '\'\'\'Warning:\'\'\' The {{PLURAL:$2|category $1 was|categories $1 were}} detected incorrectly by <code><nowiki>{{#incat:...}}</nowiki></code>, and as a result this preview may be incorrect. This can be caused by including categories inside of <code><nowiki>{{#incat:...}}</nowiki></code> statements, and may result in inconsistant display.', |
15 | 16 | ); |
16 | 17 | |
17 | 18 | /** Message documentation (Message documentation) */ |
18 | 19 | $messages['qqq'] = array( |
19 | | - 'pageincat-wrong-warn' => 'Warning displayed during preview when editing a page if #incat parser function acted incorrectly (Acting incorrectly means acting as if page was not in category, but page actually is) . This can happen during preview, since the categories from the last saved revision are used instead of the categories specified in the page text. Once page is saved, the correct categories should be used. This error can also be caused by conditional category inclusion (<code><nowiki>{{#ifpageincat:Foo||[[category:Foo]]}}</nowiki></code> |
| 20 | + 'pageincat-wrong-warn' => 'Warning displayed during preview when editing a page if #incat parser function acted incorrectly (Acting incorrectly means acting as if page was not in category, but page actually is). This can happen during preview, since the categories from the last saved revision are used instead of the categories specified in the page text. Once page is saved, the correct categories should be used. This error can also be caused by conditional category inclusion (<code><nowiki>{{#ifpageincat:Foo||[[category:Foo]]}}</nowiki></code>. See also {{msg-mw|pageincat-very-wrong-warn}}. |
20 | 21 | |
21 | 22 | *$1 is the list of categories (in a localized comma seperated list with the last two items separated by {{msg-mw|and}}. The individual category names will be italicized). |
22 | 23 | *$2 is how many categories', |
| 24 | + 'pageincat-very-wrong-warn' => 'Warning displayed during preview when editing a page if #incat parser function acted incorrectly (Acting incorrectly means acting as if page was not in category, but page actually is) . This can happen if someone does something like \'\'put this page in category foo only if its not in category foo\'\' or more generally when people include category links inside <code>#incat</code> functions. Compare this to {{msg-mw|pageincat-wrong-warn}}. Generally this error message can happen when support for checking actual categories in the preview is enabled (but the category functions still behave incorrectly), the other error message will be triggered when such support is disabled. |
| 25 | + |
| 26 | +*$1 is the list of categories (in a localized comma seperated list with the last two items separated by {{msg-mw|and}}. The individual category names will be italicized). |
| 27 | +*$2 is how many categories', |
23 | 28 | ); |
24 | 29 | |
25 | 30 | /** German (Deutsch) |
Index: trunk/extensions/PageInCat/PageInCat.php |
— | — | @@ -1,7 +1,7 @@ |
2 | 2 | <?php |
3 | 3 | /** |
4 | 4 | * Extension to add parserfunction {{#incat:foo|yes|no}} |
5 | | - * Note, this might give wrong results on preview, but should work once page is saved. |
| 5 | + * |
6 | 6 | * @author Brian Wolff <bawolff+ext _at_ gmail _dot_ com> |
7 | 7 | * |
8 | 8 | * Copyright © Brian Wolff 2011. |
— | — | @@ -31,7 +31,7 @@ |
32 | 32 | 'path' => __FILE__, |
33 | 33 | 'name' => 'PageInCat', |
34 | 34 | 'descriptionmsg' => 'pageincat-desc', |
35 | | - 'version' => 1.1, |
| 35 | + 'version' => 2, |
36 | 36 | 'url' => 'https://mediawiki.org/wiki/Extension:PageInCat', |
37 | 37 | 'author' => '[https://mediawiki.org/wiki/User:Bawolff Brian Wolff]', |
38 | 38 | ); |
— | — | @@ -46,3 +46,10 @@ |
47 | 47 | $wgHooks['ParserFirstCallInit'][] = 'PageInCat::register'; |
48 | 48 | $wgHooks['ParserClearState'][] = 'PageInCat::onClearState'; |
49 | 49 | $wgHooks['ParserAfterTidy'][] = 'PageInCat::onParserAfterTidy'; |
| 50 | +$wgHooks['EditPageGetPreviewText'][] = 'PageInCat::onEditPageGetPreviewText'; |
| 51 | +$wgHooks['ParserBeforeInternalParse'][] = 'PageInCat::onParserBeforeInternalParse'; |
| 52 | + |
| 53 | +# Double parse previews so that #incat: uses the categories |
| 54 | +# in the edit box, instead of from the previous version of the page. |
| 55 | +# A bit hacky, and will double the time it takes to render a preview. |
| 56 | +$wgPageInCatUseAccuratePreview = true; |
Index: trunk/extensions/PageInCat/PageInCat_body.php |
— | — | @@ -1,6 +1,15 @@ |
2 | 2 | <?php |
3 | 3 | class PageInCat { |
4 | 4 | |
| 5 | + /** |
| 6 | + * Really hacky array for categories of page |
| 7 | + * that we are previewing. See onEditPageGetPreviewText |
| 8 | + * method. Each key is an md5sum of page text, and each key |
| 9 | + * is an array of categories |
| 10 | + */ |
| 11 | + public static $categoriesForPreview = array(); |
| 12 | + |
| 13 | + |
5 | 14 | /** |
6 | 15 | * Register the parser hook. |
7 | 16 | * @param $parser Parser |
— | — | @@ -43,24 +52,32 @@ |
44 | 53 | if ( !$catTitle ) return false; |
45 | 54 | $catDBkey = $catTitle->getDBkey(); |
46 | 55 | |
| 56 | + if ( !isset( $parser->pageInCat_cache ) ) { |
| 57 | + $parser->pageInCat_cache = array(); |
| 58 | + } else { |
| 59 | + if ( isset( $parser->pageInCat_cache[$catDBkey] ) ) { |
| 60 | + # been there done that, return cached value |
| 61 | + return $parser->pageInCat_cache[$catDBkey]; |
| 62 | + } elseif( isset( $parser->pageInCat_onlyCache ) && $parser->pageInCat_onlyCache ) { |
| 63 | + # All categories have been preloaded into cache, so |
| 64 | + # we must have hit a cat not in page. |
| 65 | + # Mark it so can be checked for correctness later. |
| 66 | + $parser->PageInCat_cache[$catDBkey] = false; |
| 67 | + return false; |
| 68 | + } |
| 69 | + } |
| 70 | + |
47 | 71 | $pageId = $page->getArticleId(); |
48 | 72 | if ( !$pageId ) { |
49 | 73 | // page hasn't yet been saved (preview) |
50 | 74 | // add to the cache list so the other hook |
51 | 75 | // will warn about incorrect value. |
| 76 | + // Important to do this after checking cache |
| 77 | + // in case categories were pre-loaded during preview. |
52 | 78 | $parser->pageInCat_cache[$catDBkey] = false; |
53 | 79 | return false; |
54 | 80 | } |
55 | 81 | |
56 | | - if ( !isset( $parser->pageInCat_cache ) ) { |
57 | | - $parser->pageInCat_cache = array(); |
58 | | - } else { |
59 | | - if ( isset( $parser->pageInCat_cache[$catDBkey] ) ) { |
60 | | - # been there done that, return cached value |
61 | | - return $parser->pageInCat_cache[$catDBkey]; |
62 | | - } |
63 | | - } |
64 | | - |
65 | 82 | if ( !$parser->incrementExpensiveFunctionCount() ) { |
66 | 83 | # expensive function limit reached. |
67 | 84 | return false; |
— | — | @@ -112,6 +129,7 @@ |
113 | 130 | */ |
114 | 131 | public static function onClearState( Parser $parser ) { |
115 | 132 | $parser->pageInCat_cache = array(); |
| 133 | + $parser->pageInCat_onlyCache = false; |
116 | 134 | return true; |
117 | 135 | } |
118 | 136 | |
— | — | @@ -163,7 +181,20 @@ |
164 | 182 | if ( count( $catList ) !== 0 ) { |
165 | 183 | # We have at least 1 category that was treated |
166 | 184 | # incorrectly by {{#incat: |
167 | | - $msg = wfMessage( 'pageincat-wrong-warn' ) |
| 185 | + if ( isset( $parser->pageInCat_onlyCache ) && $parser->pageInCat_onlyCache ) { |
| 186 | + # categories already preloaded, so not a preview thing |
| 187 | + # yes, creativity in message name is apparently not my strong suit. |
| 188 | + $msgName = 'pageincat-very-wrong-warn'; |
| 189 | + } else { |
| 190 | + # Categories were not pre-loaded, so used last saved revision |
| 191 | + # which is most likely source of errors. |
| 192 | + # Generally triggered by $wgPageInCatUseAccuratePreview = false; |
| 193 | + # but can also be triggered if the pre-loading mechanism fails |
| 194 | + # (extensions hooking into ParserBeforeStrip for example) |
| 195 | + $msgName = 'pageincat-wrong-warn'; |
| 196 | + } |
| 197 | + |
| 198 | + $msg = wfMessage( $msgName ) |
168 | 199 | ->params( $wgLang->listToText( $catList ) ) |
169 | 200 | ->numParams( count( $catList ) ) |
170 | 201 | ->text(); |
— | — | @@ -174,4 +205,105 @@ |
175 | 206 | return true; |
176 | 207 | } |
177 | 208 | |
| 209 | + /** |
| 210 | + * Hook called just before rendering preview. Used to determine current categories |
| 211 | + * |
| 212 | + * This is hacky... Basically double parse the page, so we can determine categories. |
| 213 | + * Store the retrieved categories in a static member of this class because I can't |
| 214 | + * figure out a better way to get the data where it needs to be. |
| 215 | + * See $categoriesForPreview member variable. |
| 216 | + * |
| 217 | + * Have I mentioned this is ugly, icky and hacky? |
| 218 | + * |
| 219 | + * @todo Find a non-ugly way of doing this (is that possible?) |
| 220 | + * |
| 221 | + * @param $editPage EditPage |
| 222 | + * @param $text String wikitext to be parsed |
| 223 | + * @return boolean true |
| 224 | + */ |
| 225 | + public static function onEditPageGetPreviewText( EditPage $editPage, $text ) { |
| 226 | + global $wgPageInCatUseAccuratePreview; |
| 227 | + if ( !$wgPageInCatUseAccuratePreview ) return true; // disable this hacky mess ;) |
| 228 | + |
| 229 | + global $wgParser; // we are not parsing anything yet, so should be safe. |
| 230 | + $curUser = RequestContext::getMain()->getUser(); // aka $wgUser in disguise |
| 231 | + |
| 232 | + # This is copied from EditPage.php |
| 233 | + # Most of these options don't matter, but thought I'd make it as close to |
| 234 | + # EditPage.php as possible |
| 235 | + $parserOptions = ParserOptions::newFromUser( $curUser ); |
| 236 | + $parserOptions->setEditSection( false ); |
| 237 | + $parserOptions->setTidy( true ); |
| 238 | + # Don't set as preview so other hook isn't triggered (Talk about being hacky!) |
| 239 | + # $parserOptions->setIsPreview( true ); |
| 240 | + # $parserOptions->setIsSectionPreview( !is_null($editPage->section) && $editPage->section !== '' ); |
| 241 | + $parserOptions->enableLimitReport(); |
| 242 | + |
| 243 | + // I suppose I should be using $editPage->getTitle() but that's new in 1.19 |
| 244 | + $toparse = $wgParser->preSaveTransform( $text, $editPage->mTitle, $curUser, $parserOptions ); |
| 245 | + $hash = md5( $toparse, true ); |
| 246 | + $parserOutput = $wgParser->parse( $toparse, $editPage->mTitle, $parserOptions ); |
| 247 | + |
| 248 | + if ( count( self::$categoriesForPreview ) > 10 ) { |
| 249 | + # Really this should never have more than 1 element |
| 250 | + # since we should do a preview directly after this |
| 251 | + # and delete the sole element. But good to be paranoid, |
| 252 | + # especially given how fragile this solution is. |
| 253 | + wfDebug( __METHOD__ . ' self::$categoriesForPreview grew too big.' ); |
| 254 | + self::$categoriesForPreview = array(); |
| 255 | + } |
| 256 | + self::$categoriesForPreview[$hash] = $parserOutput->getCategoryLinks(); |
| 257 | + return true; |
| 258 | + } |
| 259 | + |
| 260 | + /** |
| 261 | + * Insert categories from previous pre-preview parse into parser. |
| 262 | + * |
| 263 | + * See onEditPageGetPreviewText. This is rather fragile/scary. |
| 264 | + * If anyone has a suggestion for how to do this better, please let me know. |
| 265 | + * |
| 266 | + * @param $parser Parser |
| 267 | + * @param $pstText String text to parse, all pst'd. In theory untouched but |
| 268 | + * various hooks could have touched it, which would make this all fail. |
| 269 | + * @param $stripState StripState $parser->mStripState - I really don't need this |
| 270 | + * @return boolean true |
| 271 | + */ |
| 272 | + public static function onParserBeforeInternalParse( Parser $parser, $pstText, $stripState ) { |
| 273 | + global $wgPageInCatUseAccuratePreview; |
| 274 | + if ( !$wgPageInCatUseAccuratePreview ) { |
| 275 | + // Disabled |
| 276 | + return true; |
| 277 | + } |
| 278 | + |
| 279 | + if ( !$parser->getOptions()->getIsPreview() ) { |
| 280 | + // We only do stuff on preview |
| 281 | + return true; |
| 282 | + } |
| 283 | + |
| 284 | + $hash = md5( $pstText, true ); |
| 285 | + |
| 286 | + if ( !isset( self::$categoriesForPreview[$hash] ) ) { |
| 287 | + # This probably shouldn't happen |
| 288 | + wfDebug( __METHOD__ . ' Could not find relavent cat list.' ); |
| 289 | + return true; |
| 290 | + } |
| 291 | + |
| 292 | + if ( !isset( $parser->pageInCat_cache ) ) { |
| 293 | + $parser->pageInCat_cache = array(); |
| 294 | + } elseif ( count( $parser->pageInCat_cache ) !== 0 ) { |
| 295 | + # being paranoid |
| 296 | + wfDebug( __METHOD__ . ' $parser->pageInCat_cache not empty!' ); |
| 297 | + $parser->pageInCat_cache = array(); |
| 298 | + } |
| 299 | + |
| 300 | + foreach( self::$categoriesForPreview[$hash] as $catName ) { |
| 301 | + $parser->pageInCat_cache[$catName] = true; |
| 302 | + } |
| 303 | + // Assume anything not in the cache is false. |
| 304 | + $parser->pageInCat_onlyCache = true; |
| 305 | + unset( self::$categoriesForPreview[$hash] ); |
| 306 | + |
| 307 | + return true; |
| 308 | + } |
| 309 | + |
178 | 310 | } |