User:Eclecticdave

From FollowTheScore
Jump to: navigation, search

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 ;-)

The patch applies to DPL version 1.6.9.

What you need to know

  • The patch will not help with the 'include' parameter - it is solely designed to address embedded templates.
  • 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
+?>

Comment from Gero 11:29, 24 April 2008 (CEST)

Thank you for your hard work, Dave! Obviously it will not be an easy endeavour to keep DPL´s current functionality up and running. I really hope that you will find a way through the jungle - because I am currently very busy with other things. Th eonly thing I can promise is that I will maintain DPL in the same way I did for the last 18 months or so -- once a stable version on 1.12 exists. But it sounds like horror to me that a given DPL version in future can EITHER be run with 1.11 or lower OR with 1.12 and later. I cannot maintain two versions. IS there are chance to get this solved?

11:29, 24 April 2008 (CEST)

I'll certainly keep tinkering with it. However I also have some other things vying for my time, which is the main reason why I posted the patch now, rather than wait until it was complete - to give others a chance to play with what I've done so far.

As for keeping it working on 1.11 - that could be tricky ... there's certainly going to be a fair amount of code specific to the parser in use. However, it should be possible to factor out the common bits such as the SQL. That's obviously a non-trivial undertaking, but I'll be happy to take a look when I get the time.

Eclecticdave 23:09, 24 April 2008 (CEST)