User:Eclecticdave
From FollowTheScore
Revision as of 00:43, 24 April 2008 by Eclecticdave (talk | contribs) (New page: == Experimental Patch for DPL on MW 1.12 == The patch below is intended to be a "proof of concept" implementation of DPL under MW 1.12's new parser. It is far from feature complete and a...)
Experimental Patch for DPL on MW 1.12
The patch below is intended to be a "proof of concept" implementation of DPL under MW 1.12's new parser. It is far from feature complete and almost certainly has some bugs so user beware ;-)
What you need to know
- Template escaping using ²{ }² is no longer possible, but is unnecessary on the new parser. You will need to remove the escapes. Any templates used in format/listseparators, secseparators, multisecseparators and any of the resultsheader/footer parameters will automatically be deferred and evaluated for each article returned.
- Substitution of tokens like %PAGE%, %COUNT% etc. within embedded templates is also no longer possible. I've implemented an alternative in the form of a new 'dplvar' parser function - use the form instead.
- Due to a quirk in the new parser the first parameter to DPL is evaluated too early for template expansion to be intercepted (see bug 12842). Don't use format/listseparators etc in the first parameter!
- The patched DPL will not run on MW1.11 or earlier.
- Don't apply this patch to a running wiki - please! Re-read the bits about 'experimental' and 'proof of concept' if you find yourself thinking about doing this ;-)
diff --git a/DynamicPageList2.php b/DynamicPageList2.php index 9371862..81e34c0 100644 --- a/DynamicPageList2.php +++ b/DynamicPageList2.php @@ -312,6 +312,8 @@ class ExtDynamicPageList2 private static $allowedNamespaces = NULL; // to be initialized at first use of DPL2, array of all namespaces except Media and Special, because we cannot use the DB for these to generate dynamic page lists. // Cannot be customized. Use ExtDynamicPageList2::$options['namespace'] or ExtDynamicPageList2::$options['notnamespace'] for customization. + + /** * Map parameters to possible values. * A 'default' key indicates the default value for the parameter. @@ -768,7 +770,7 @@ class ExtDynamicPageList2 //------------------------------ parser #function #dplmatrix: $wgParser->setFunctionHook( 'dplmatrix', array( __CLASS__, 'dplMatrixParserFunction' ) ); //------------------------------ variant as a parser #function - $wgParser->setFunctionHook( 'dpl', array( __CLASS__, 'dplParserFunction' ) ); + $wgParser->setFunctionHook( 'dpl', array( __CLASS__, 'dplParserFunction' ), SFH_OBJECT_ARGS ); // Internationalization file require_once( 'DynamicPageList2.i18n.php' ); @@ -810,6 +812,7 @@ class ExtDynamicPageList2 $magicWords['dpl'] = array( 0, 'dpl' ); $magicWords['dplchapter'] = array( 0, 'dplchapter' ); $magicWords['dplmatrix'] = array( 0, 'dplmatrix' ); + $magicWords['dplvar'] = array( 0, 'dplvar' ); # unless we return true, other parser functions extensions won't get loaded. return true; } @@ -851,26 +854,25 @@ class ExtDynamicPageList2 return $parsedDPL; } - public static function dplParserFunction(&$parser) + public static function dplParserFunction(&$parser, $frame, $arg_list) { // callback for the parser function {{#dpl: $params = array(); $input=""; - $numargs = func_num_args(); + $numargs = count($arg_list); if ($numargs < 2) { $input = "#dpl: no arguments specified"; return str_replace('§','<','§pre>§nowiki>'.$input.'§/nowiki>§/pre>'); } - // fetch all user-provided arguments (skipping $parser) - $arg_list = func_get_args(); + // fetch all user-provided arguments + $input .= $arg_list[0] . "\n"; for ($i = 1; $i < $numargs; $i++) { - $p1 = $arg_list[$i]; - $input .= str_replace("\n","",$p1) ."\n"; + $input .= trim($frame->expand($arg_list[$i])) . "\n"; } // for debugging you may want to uncomment the following statement - // return str_replace('§','<','§pre>§nowiki>'.$input.'§/nowiki>§/pre>'); + //return str_replace('§','<','§pre>'.$input.'§/pre>'); // $dump1 = self::dumpParsedRefs($parser,"before DPL func"); @@ -878,7 +880,7 @@ class ExtDynamicPageList2 // $dump2 = self::dumpParsedRefs($parser,"after DPL func"); // return $dump1.$text.$dump2; - return self::dynamicPageList($input, $params, $parser, $reset, 'func'); + return self::dynamicPageList($arg_list, $frame, $params, $parser, $reset, 'func'); } public static function dplChapterParserFunction(&$parser, $text='', $heading=' ', $maxLength = -1, $page = '?page?', $link = 'default', $trim=false ) { @@ -960,6 +962,8 @@ class ExtDynamicPageList2 } } + + private static function dumpParsedRefs($parser,$label) { if (!preg_match("/Query Q/",$parser->mTitle->getText())) return ''; $text="\n<pre>$label:\n"; @@ -1037,7 +1041,7 @@ class ExtDynamicPageList2 } // The real callback function for converting the input text to HTML output - private static function dynamicPageList( $input, $params, &$parser, &$bReset, $calledInMode ) { + private static function dynamicPageList( &$arg_list, &$frame, $params, &$parser, &$bReset, $calledInMode ) { error_reporting(E_ALL); @@ -1177,6 +1181,10 @@ class ExtDynamicPageList2 $aMultiSecSeparators = explode(',', self::$options['multisecseparators']['default']); $iDominantSection = self::$options['dominantsection']['default']; + $oSecSeparators = NULL; + $oMultiSecSeparators = NULL; + $oListSeparators = NULL; + $_sOffset = self::$options['offset']['default']; $iOffset = ($_sOffset == '') ? 0: intval($_sOffset); @@ -1258,6 +1266,7 @@ class ExtDynamicPageList2 // ###### PARSE PARAMETERS ###### // we replace double angle brackets by < > ; thus we avoid premature tag expansion in the input + /* $input = str_replace('»','>',$input); $input = str_replace('«','<',$input); @@ -1270,6 +1279,7 @@ class ExtDynamicPageList2 $input = str_replace('}²','}}',$input); $aParams = explode("\n", $input); + */ $bIncludeUncat = false; // to check if pseudo-category of Uncategorized pages is included // version 0.9: @@ -1278,14 +1288,26 @@ class ExtDynamicPageList2 // parse the result recursively. This allows to build complex structures in the output // which are only understood by the parser if seen as a whole - foreach($aParams as $iParam => $sParam) { - $aParam = explode('=', $sParam, 2); + foreach($arg_list as $arg) { + // The first argument is always pre-parsed, as part of finding out it starts with '#dpl:'. + if (is_string($arg)) { + $aParam = explode('=', $arg, 2); if( count( $aParam ) < 2 ) { - if (trim($aParam[0])!='') $output .= $logger->escapeMsg(DPL2_i18n::WARN_UNKNOWNPARAM, $aParam[0]. " [missing '=']", implode(', ', array_keys(self::$options))); + if (trim($aParam[0])!='') + $output .= $logger->escapeMsg(DPL2_i18n::WARN_UNKNOWNPARAM, $aParam[0]. " [missing '=']", + implode(', ', array_keys(self::$options))); continue; } $sType = trim($aParam[0]); $sArg = trim($aParam[1]); + $oArg = NULL; + } + else { + $aParam = $arg->splitArg(); + $sType = trim($aParam{'name'}->node->nodeValue); + $oArg = $aParam{'value'}; + $sArg = trim($frame->expand($oArg)); + } if( $sType=='') { $output .= $logger->escapeMsg(DPL2_i18n::WARN_UNKNOWNPARAM, '[empty string]', implode(', ', array_keys(self::$options))); @@ -1935,6 +1957,7 @@ class ExtDynamicPageList2 $sArg = str_replace( '\n', "\n", $sArg ); $sArg = str_replace( "¶", "\n", $sArg ); // the paragraph delimiter is utf8-escaped $aListSeparators = explode (',', $sArg, 4); + $oListSeparators = $oArg; // mode=userformat will be automatically assumed $sPageListMode='userformat'; $sInlTxt = ''; @@ -1945,6 +1968,7 @@ class ExtDynamicPageList2 $sArg = str_replace( '\n', "\n", $sArg ); $sArg = str_replace( "¶", "\n", $sArg ); // the paragraph delimiter is utf8-escaped $aSecSeparators = explode (',',$sArg); + $oSecSeparators = $oArg; break; case 'multisecseparators': @@ -1952,6 +1976,7 @@ class ExtDynamicPageList2 $sArg = str_replace( '\n', "\n", $sArg ); $sArg = str_replace( "¶", "\n", $sArg ); // the paragraph delimiter is utf8-escaped $aMultiSecSeparators = explode (',',$sArg); + $oMultiSecSeparators = $oArg; break; case 'table': @@ -2015,21 +2040,27 @@ class ExtDynamicPageList2 break; case 'resultsheader': $sResultsHeader = $sArg; + $oResultsHeader = $oArg; break; case 'resultsfooter': $sResultsFooter = $sArg; + $oResultsFooter = $oArg; break; case 'noresultsheader': $sNoResultsHeader = $sArg; + $oNoResultsHeader = $oArg; break; case 'noresultsfooter': $sNoResultsFooter = $sArg; + $oNoResultsFooter = $oArg; break; case 'oneresultheader': $sOneResultHeader = $sArg; + $oOneResultHeader = $oArg; break; case 'oneresultfooter': $sOneResultFooter = $sArg; + $oOneResultFooter = $oArg; break; /** @@ -2244,6 +2275,8 @@ class ExtDynamicPageList2 // if 'table' parameter is set: derive values for listseparators, secseparators and multisecseparators $defaultTemplateSuffix='.default'; if ($sTable!='') { + $oSecSeparators = NULL; + $oMultiSecSeparators = NULL; $defaultTemplateSuffix=''; $sPageListMode='userformat'; $sInlTxt = ''; @@ -2288,7 +2321,9 @@ class ExtDynamicPageList2 $sSqlPage_size = ''; $sSqlPage_touched = ''; $sSqlCalcFoundRows = ''; - if ( isset($iCount) && $sGoal != 'categories' && strpos($sResultsHeader.$sResultsFooter,'%TOTALPAGES%')!==false) $sSqlCalcFoundRows = 'SQL_CALC_FOUND_ROWS'; + // Can't figure out a way of checking if TOTALPAGES is used on new parser/syntax + //if ( isset($iCount) && $sGoal != 'categories' && strpos($sResultsHeader.$sResultsFooter,'%TOTALPAGES%')!==false) $sSqlCalcFoundRows = 'SQL_CALC_FOUND_ROWS'; + if ( isset($iCount) && $sGoal != 'categories' ) $sSqlCalcFoundRows = 'SQL_CALC_FOUND_ROWS'; if ($sDistinctResultSet == 'false') $sSqlDistinct = ''; else $sSqlDistinct = 'DISTINCT'; $sSqlGroupBy = ''; @@ -2829,6 +2864,9 @@ class ExtDynamicPageList2 } if ($dbr->numRows( $res ) <= 0) { + if (isset($oNoResultsHeader)) $sNoResultsHeader = trim($frame->expand($oNoResultsHeader)); + if (isset($oNoResultsFooter)) $sNoResultsFooter = trim($frame->expand($oNoResultsFooter)); + if ($sNoResultsHeader != '') $output .= str_replace( '\n', "\n", str_replace( "¶", "\n", $sNoResultsHeader)); if ($sNoResultsFooter != '') $output .= str_replace( '\n', "\n", str_replace( "¶", "\n", $sNoResultsFooter)); if ($sNoResultsHeader == '' && $sNoResultsFooter == '') $output .= $logger->escapeMsg(DPL2_i18n::WARN_NORESULTS); @@ -3021,10 +3059,12 @@ class ExtDynamicPageList2 // ###### SHOW OUTPUT ###### $listMode = new DPL2ListMode($sPageListMode, $aSecSeparators, $aMultiSecSeparators, $sInlTxt, $sListHtmlAttr, - $sItemHtmlAttr, $aListSeparators, $iOffset, $iDominantSection); + $sItemHtmlAttr, $aListSeparators, $iOffset, $iDominantSection, + $oSecSeparators, $oMultiSecSeparators, $oListSeparators, $frame); $hListMode = new DPL2ListMode($sHListMode, $aSecSeparators, $aMultiSecSeparators, '', $sHListHtmlAttr, - $sHItemHtmlAttr, $aListSeparators, $iOffset, $iDominantSection); + $sHItemHtmlAttr, $aListSeparators, $iOffset, $iDominantSection, + $oSecSeparators, $oMultiSecSeparators, $oListSeparators, $frame); $dpl = new DPL2($aHeadings, $bHeadingCount, $iColumns, $iRows, $iRowSize, $sRowColFormat, $aArticles, $aOrderMethods[0], $hListMode, $listMode, $bEscapeLinks, $bIncPage, $iIncludeMaxLen, @@ -3032,8 +3072,18 @@ class ExtDynamicPageList2 $iTitleMaxLen, $defaultTemplateSuffix, $aTableRow, $bIncludeTrim); if ($rowcount == -1) $rowcount = $dpl->getRowCount(); + $dpl->setTotalRows($rowcount); $dpl2result = $dpl->getText(); $header=''; + + // Re-evaluate templates + if (isset($oOneResultHeader)) $sOneResultHeader = trim($frame->expand($oOneResultHeader)); + if (isset($oOneResultFooter)) $sOneResultFooter = trim($frame->expand($oOneResultFooter)); + if (isset($oNoResultsHeader)) $sNoResultsHeader = trim($frame->expand($oNoResultsHeader)); + if (isset($oNoResultsFooter)) $sNoResultsFooter = trim($frame->expand($oNoResultsFooter)); + if (isset($oResultsHeader)) $sResultsHeader = trim($frame->expand($oResultsHeader)); + if (isset($oResultsFooter)) $sResultsFooter = trim($frame->expand($oResultsFooter)); + if ($sOneResultHeader != '' && $rowcount==1) { $header = str_replace('%PAGES%',1,$sOneResultHeader); } else if ($rowcount==0) { @@ -3253,9 +3303,14 @@ class DPL2ListMode { var $sSectionTags = array(); var $aMultiSecSeparators = array(); var $iDominantSection = -1; + var $oSecSeparators = NULL; + var $oMultiSecSeparators = NULL; + var $oListSeparators = NULL; + var $frame = NULL; function DPL2ListMode($listmode, $secseparators, $multisecseparators, $inlinetext, $listattr = '', $itemattr = '', - $listseparators, $iOffset, $dominantSection) { + $listseparators, $iOffset, $dominantSection, + $oSecSeparators, $oMultiSecSeparators, $oListSeparators, $frame) { // default for inlinetext (if not in mode=userformat) if (($listmode != 'userformat') && ($inlinetext == '')) $inlinetext = ' - '; @@ -3265,6 +3320,10 @@ class DPL2ListMode { $this->sSectionTags = $secseparators; $this->aMultiSecSeparators = $multisecseparators; + $this->oSecSeparators = $oSecSeparators; + $this->oMultiSecSeparators = $oMultiSecSeparators; + $this->oListSeparators = $oListSeparators; + $this->frame = $frame; $this->iDominantSection = $dominantSection - 1; // 0 based index switch ($listmode) { @@ -3325,12 +3384,14 @@ class DPL2ListMode { class DPL2 { + private static $totalRows = 0; + private static $article = NULL; + private static $mEscapeLinks; // whether to escape img/cat or not var $mArticles; var $mHeadingType; // type of heading: category, user, etc. (depends on 'ordermethod' param) var $mHListMode; // html list mode for headings var $mListMode; // html list mode for pages - var $mEscapeLinks; // whether to escape img/cat or not var $mIncPage; // true only if page transclusion is enabled var $mIncMaxLen; // limit for text to include var $mIncSecLabels = array(); // array of labels of sections to transclude @@ -3347,15 +3408,61 @@ class DPL2 { var $nameSpaces; var $mTableRow; // formatting rules for table fields + public static function dplVarParserFunction(&$parser, $name) { + $arg_list = func_get_args(); + + if (strcasecmp($name, 'PAGE') == 0) { + $pagename = self::$article->mTitle->getPrefixedText(); + if (self::$mEscapeLinks && (self::$article->mNamespace==14 || self::$article->mNamespace==6) ) { + // links to categories or images need an additional ":" + $pagename = ':'.$pagename; + } + return $pagename; + } + if (strcasecmp($name, 'TITLE') == 0) { + $title = self::$article->mTitle->getText(); + // TODO + //if (strpos($title,'%TITLE')>=0) { + // if ($this->mReplaceInTitle[0]!='') $title = preg_replace($this->mReplaceInTitle[0],$this->mReplaceInTitle[1],$title); + // if( isset($titleMaxLength) && (strlen($title) > $titleMaxLength)) $title = substr($title, 0, $titleMaxLength) . '...'; + //} + return $title; + } + if (strcasecmp($name, 'PAGES') == 0) { + return self::$totalRows; + } + if (strcasecmp($name, 'TOTALPAGES') == 0) { + return self::$totalRows; + } + if (strcasecmp($name, 'VERSION') == 0) { + return self::VERSION; + } + // Can't just put commas in here because listseparators/format also + // uses them and its all parsed in one big lump in the new parser + // So, we use a special escape \c and replace it later. + if (strcasecmp($name, 'CATLIST') == 0) { + return implode('\c', self::$article->mCategoryLinks); + } + if (strcasecmp($name, 'CATNAMES') == 0) { + return implode('\c', self::$article->mCategoryTexts); + } + + // If we get here, just return the value 180 for now. + return "180"; + } + function DPL2($headings, $bHeadingCount, $iColumns, $iRows, $iRowSize, $sRowColFormat, $articles, $headingtype, $hlistmode, $listmode, $bescapelinks, $includepage, $includemaxlen, $includeseclabels, $includeseclabelsmatch, $includeseclabelsnotmatch, $includematchparsed, &$parser, $logger, $replaceInTitle, $iTitleMaxLen, $defaultTemplateSuffix, $aTableRow, $bIncludeTrim ) { - global $wgContLang; + global $wgContLang, $wgParser; + + $wgParser->setFunctionHook( 'dplvar', array( __CLASS__, 'dplVarParserFunction' ) ); + $this->nameSpaces = $wgContLang->getNamespaces(); $this->mArticles = $articles; $this->mListMode = $listmode; - $this->mEscapeLinks = $bescapelinks; + self::$mEscapeLinks = $bescapelinks; $this->mIncPage = $includepage; if($includepage) { $this->mIncSecLabels = $includeseclabels; @@ -3560,8 +3667,45 @@ class DPL2 { // the following statement caused a problem with multiple columns: $this->filteredCount = 0; for ($i = $iStart; $i < $iStart+$iCount; $i++) { $article = $this->mArticles[$i]; + + self::$article = &$article; + if (isset($mode->oSecSeparators)) + $mode->aSecSeparators = explode(',', $mode->frame->expand($mode->oSecSeparators)); + if (isset($mode->oMultiSecSeparators)) + $mode->aMultiSecSeparators = explode(',', $mode->frame->expand($mode->oMultiSecSeparators)); + if (isset($mode->oListSeparators)) + $mode->aListSeparators = explode(',', $mode->frame->expand($mode->oListSeparators)); + + // Convert newlines, plus \c escapes used in CATLIST and CATNAMES + switch(count($mode->aListSeparators)) { + case 4: + $mode->aListSeparators[3] = str_replace( '\n', "\n", str_replace( "¶", "\n", $mode->aListSeparators[3] )); + $mode->aListSeparators[3] = str_replace( '\c', ", ", $mode->aListSeparators[3] ); + case 3: + $mode->aListSeparators[2] = str_replace( '\n', "\n", str_replace( "¶", "\n", $mode->aListSeparators[2] )); + $mode->aListSeparators[2] = str_replace( '\c', ", ", $mode->aListSeparators[2] ); + case 2: + $mode->aListSeparators[1] = str_replace( '\n', "\n", str_replace( "¶", "\n", $mode->aListSeparators[1] )); + $mode->aListSeparators[1] = str_replace( '\c', ", ", $mode->aListSeparators[1] ); + case 1: + $mode->aListSeparators[0] = str_replace( '\n', "\n", str_replace( "¶", "\n", $mode->aListSeparators[0] )); + $mode->aListSeparators[0] = str_replace( '\c', ", ", $mode->aListSeparators[0] ); + } + + /* Taken from DPLListMode ... */ + switch(count($mode->aListSeparators)) { + case 4: + $mode->sListEnd = $mode->aListSeparators[3]; + case 3: + $mode->sItemEnd = $mode->aListSeparators[2]; + case 2: + $mode->sItemStart = $mode->aListSeparators[1]; + case 1: + $mode->sListStart = $mode->aListSeparators[0]; + } + $pagename = $article->mTitle->getPrefixedText(); - if ($this->mEscapeLinks && ($article->mNamespace==14 || $article->mNamespace==6) ) { + if (self::$mEscapeLinks && ($article->mNamespace==14 || $article->mNamespace==6) ) { // links to categories or images need an additional ":" $pagename = ':'.$pagename; } @@ -3823,6 +3967,10 @@ class DPL2 { return $this->filteredCount; } + function setTotalRows($rowcount) { + self::$totalRows = $rowcount; + } + //cut wiki text around lim function cutAt($lim,$text) { if ($lim<0) return $text; @@ -3958,4 +4106,4 @@ class DPL2Logger { } -?> \ No newline at end of file +?>