Extension talk:TemplateTable

From mediawiki.org
Latest comment: 11 years ago by Schalice in topic Templates inside of Templates
The following discussion has been transferred from Meta-Wiki.
Any user names refer to users of that site, who are not necessarily users of MediaWiki.org (even if they share the same username).

Another useful feature[edit]

This would be a more useful extension (IMHO) if you could filter the table based on a parameter (or an expression of parameters), and also possibly have an option to only show include template usages on the current page. In the example, then, this could let you build a table of all restaurants in the $$$ price range, or make a page of Boston Restaurants that uses this template, and summarize them all in a table at the top.

Also note that for this to work the code should look for more than one template per page! It currently only finds the first one. Vash 16:15, 24 April 2007 (UTC)Reply

re. Another useful feature[edit]

Yes! I agree. This would be a more useful extension (IMHO) if you could filter the table based on a parameter (or an expression of parameters).
Some examples belows here would be nice.
<ttable template='Restaurant' category=Chinese food and category=located America></ttable>
<ttable template='Restaurant' category=Chinese food or category=America food></ttable>
Other ideas which can be found it written in DynamicPageList2 0.9.
--Roc michael 14:52, 25 April 2007 (UTC)Reply

re. Another useful feature[edit]

Ask and Ye shall receive. We are finding this particular extension VERY useful in our environment, and adding the exclusion filter makes it even better.

Exclude filter for <ttable>

  • Pass exclude filters to the ttable with simple arguments (<ttable template='MyTemplate' headers='Name|Address' excludes="header1=value1|header2=value2">
  • Can accept multiple excludes

Below is a diff to enable excludes in the templatetable extension.

40a41,45
> * You can also filter the table rows based on an exclude list
> * passed as an argument to the <ttable>:
> *  <ttable template='MyTemplate' headers='Name|Address'
> *  excludes="header1=value1|header2=value2">
> *
93a99,100
>   $excludesArray = array();
>   $excludeFlag = False;
98a106,112
>   if(isset($argv["excludes"])) {
>       $excludesArray = explode("|",$argv["excludes"]);
>       foreach($excludesArray as $excludeValues) {
>           list($excludeKey,$excludeVal) = explode("=",$excludeValues);
>           $excludesArray[strtolower($excludeKey)] = $excludeVal;
>       }
>   }
150a165,169
>                       // Check to see if this header is excluded
>                       if(isset($excludesArray[$key]) &&
>                       !strcmp(strtolower($excludesArray[$key]), strtolower($val))) {
>                           $excludeFlag=True;
>                           break;
>                       }
154c173
<                   if(sizeof($item) > 0)
---
>                   if(sizeof($item) > 0 && !$excludeFlag) {
155a175,176
>                   }
>                   $excludeFlag = False;

TerrySoucy 18:20, 22 Jan 2010 (UTC)

Two bugs[edit]

This is a great extension, but there are two pretty simple but significant bugs i've encountered.

  1. MediaWiki installations with a db table prefix will not work with this extension, as all three queries did not take this into account.
  2. The article links in the resulting table do not have their namespaces in them.

Both are pretty easily fixed.

Additional:

  1. It does not work if the template does not use named parameters

Suggested: (sorry lots of debug lines... I don't have any PHP background. took apart this ext slowly to try to understand it.)

<?php
/*******************************************************************
* MediaWiki Extension: Template Table
* by CS "Kainaw" Wagner <http://shaunwagner.com>
********************************************************************
********************************************************************
* DESCRIPTION
* This adds a ttable tag to MediaWiki to show template items from
* all pages in a single table.  This is very useful for creating an
* automatically generated comparison between common articles.
********************************************************************
* USAGE
* Simplest usage is <ttable template='TemplateName'></ttable>.
*
* !!!IMPORTANT NOTE!!!
* For technical reasons, TemplateName is case-sensitive.  That means
* that if you have Template:MyTemplate, you must use:
*  <ttable template='MyTemplate'></ttable>
* However, it will find data in pages that use {{mytemplate ...}}
* You just have to make sure the value in the ttable tag matches
* the case of the actual template page.
*
* ADVANCED USAGE
* You can choose which headers to display with the headers option:
*  <ttable template='MyTemplate' headers='Name|Address'></ttable>
* A | (vertical bar/pipe) separates each header.  If you do not
* supply this option, all headers used will be shown.
*
* You may customize the table with the standard table tags:
*  border, cellspacing, cellpadding, class, align, and style.
*
* Any text between <ttable> and </ttable> will appear ABOVE the
* table that is produced.
********************************************************************
* SPECIAL NOTE
* A side effect of how this script works is that you can have fields
* that display in this table and not in the actual template.  If you
* have made a template, you know that you can ignore variables that
* are sent to it.  If you do that and you do not ignore them in this
* table, they will show up here and not in the template.
********************************************************************
* INSTALLATION
* Put this in the extensions directory and include it in your
* LocalSettings.php file before the last ?> like:
*  include("extensions/templatetable.php");
********************************************************************
* CUSTOMIZATION
* The only thing I think anyone would customize is the text of the
* "article" link.  That is easy to change.  Just search the script
* for "article" and you will see it.  It looks like:
*  $output.= "article";
* Change it to whatever you like (it can even be an image).

Added:
*prefixes
*check if mediawiki
*argument: styleclass (for table formatting)

Changed:
*to stop supporting named parameters and support only unnamed parameters
(commented out, can be restored easily)

********************************************************************/
//if not MediaWiki - abort (security feature)
if( !defined( 'MEDIAWIKI' ) ) {
	die();
}

// Register the tag.
$wgExtensionFunctions[] = "wfTemplateTable";
$wgExtensionCredits['parserhook'][] = array(
	'name' => 'TemplateTable',
	'author' => 'CS Wagner (modified)',
	'url' => 'http://meta.wikimedia.org/wiki/TemplateTable_extension',
	'description' => 'Auto-generates a table derived from the template parameters of every instance of a template',
);

function wfTemplateTable()
{
        global $wgParser;
        $wgParser->setHook("ttable", "renderTemplateTable");
}

/**
* This function renders the TemplateTable.
* $input = text between <ttable> and </ttable>
* $argv = key/val array of options in the <ttable> tag.
*/
function renderTemplateTable($input, $argv)
{
        global $wgScript;
        // Print a nice error if template is not given.
        if(!isset($argv["template"])) return "TemplateTable Error: No template given.  Please use the format <tt><ttable template='Name Of Template'>...</ttable></tt>";

        // Header type.  If "headers" is given, use that.
        // Otherwise, use dynamic headers (every variable used in the template)
        $dynhead = true;
        $headers = array();
        if(isset($argv["headers"]))
        {
                $headers = explode("|", $argv["headers"]);
                $dynhead = false;
        }

        // Preset output to the input (stuff between <ttable> and </ttable>
        
		$output = $input;
		
//debug		$output .= "Hello..." . mysql_escape_string($argv["template"]); 
        
		
		// Now the hard part: Hunt down the template data...
        /***************************************************************
        * Instead of commenting all through this, I will just describe
        * it here:
        * The goal is to have an array ($data) that looks like:
        * $data[page name] = array(key1=>val1, key2=>val2...);
        *
        * Step 1:
        * Get a list of all Page IDs that use the template.
        * For every page found...
        *   Step 2:
        *   Get the last Revision ID for each Page ID.
        *   Step 3:
        *   Get the text for the last Revision.
        *   Step 4:
        *   Extract the template text from the Revision.
        *   Step 5:
        *   If there is template text, parse it and create an item for
        *   the $data array.
        ****************************************************************/
     	global $wgDBprefix;
		$prefix = $wgDBprefix; // add this so because my wiki uses prefixes in front of table names
   
  
		$data = array();
        $query = "select * from " . $prefix . "templatelinks left join " . $prefix . "page on " . $prefix . "templatelinks.tl_from=" . $prefix . "page.page_id where tl_title='".$argv["template"]."' order by page_title";
		//This unfortunately selects records irrespective of case: ABC, Abc, abc, etc
		
        $result = mysql_query($query);
        while($row = mysql_fetch_object($result))
        {

//debug:   $output .= "\n\n=". $row->page_title; //Page title is the title of the page
                $q2 = "select rev_text_id from " . $prefix . "revision where rev_page=".$row->page_id." order by rev_timestamp desc limit 1";
                if(($res2 = mysql_query($q2)) && ($row2 = mysql_fetch_object($res2)))
                {
                        $q3 = "select * from " . $prefix . "text where old_id=".$row2->rev_text_id;
                        if(($res3 = mysql_query($q3)) && ($row3 = mysql_fetch_object($res3)))
                        {

//$row3->old_text is the whole text of the file
//debug(show whole file text):           $output .= "\n"."\n"."===".$row2->rev_text_id."==="."\n"."\n". $row3->old_text ;

                                $row3->old_text = str_replace("\n", " ", $row3->old_text); //Unsure of function... works well without
//Works out starting postion of {{Template ... to its end ...}} so that can extract/copy this information
                                
                                $start = stripos($row3->old_text, "{{".$argv["template"]);
    
	//If you specifically only use your template at the beginning of your file:                         
	//Only accepts this file if the header is at the beginning of the file, else ignore    
    //                        if($start == 0)
	//						{ 
                                if($start === false) continue;

                                $end = strpos($row3->old_text, "}}", $start);
                                if($end === false) continue;
//Debug (position)//   $output .= "\n"."\n"."===xx2xx".$start. " to ".$end."==="."\n"."\n";

//The extracted wanted text in raw form
                                $tmp = substr($row3->old_text, $start+strlen("{{".$argv["template"]), $end-strlen("{{".$argv["template"]));
//Debug (raw extracted text)//   $output .= "\n\n==".$tmp ;

                                $item = array();
                                $kvs = explode("|", $tmp); //separate out each of the parameters separated by "|"

//changed so that it will work with template unnamed parameters
   								$key = 0 ; //counter
                                foreach($kvs as $kv)
                                {

                                        $kv = trim($kv);
                                        if($kv == "") continue;
//                                        $eq = strpos($kv, "=");
//   $output .= "\n"."\n"."===xxEq5xx: ".$eq;
//                                        if($eq === false) continue;
//                                        $key = strtolower(trim(substr($kv, 0, $eq)));
										$key = $key + 1;
//                                        $val = trim(substr($kv, $eq+1));
										$val = $kv;
                                        $item[$key] = $val;
//debug (all items)//   $output .= "\n\n*item(". $key. ")=" .$item[$key];
                                        if($dynhead && !in_array($key, $headers)) array_push($headers, $key); //probably can delete this line too
                                }
                                if(sizeof($item) > 0)
                                        $data[$row->page_title] = $item;
              //enable this for: if (beginning of file)://    }  
                        }
                }
        }


        // Skip this if there's no data to display.
        // Otherwise, create our pretty little table!
        if(sizeof($data) < 1)
        {
                // Print a nice (no data found) message:
                $output.= "Template ".$argv["template"]." has no data.";
        }
        else
        {
                $output.= "<table class=\"".$argv["styleclass"]. "\"";
        /*Not needed for unnamed parameters
		        foreach($argv as $key=>$val)
                {
                        if($key == "template" || $key == "headers") continue;
                        $output.= " $key=\"$val\"";
               }
                $output.= ">\n";
                $output.= "<tr><th> </th>\n";
                
                
                foreach($headers as $header)
                {
                        $output.= "<th>$header</th>\n";
                }
                $output.= "</tr>\n";
        */    
                
                foreach($data as $index=>$item) // FOREACH ( array AS key/index => Value of current item (in this case item[array] )
                {
                        $output.= "<tr><td align='center'><a href='".$wgScript."?title=".urlencode($index)."'>";
                        $output.= $index;//"article";  // Changing this line will change the text that appears in the row heading
                        $output.= "</a></td>\n";
                        foreach($headers as $header)
                        {
                                $header = strtolower($header);
                                if(isset($item[$header]))
                                        $output.= "<td>".$item[$header]."</td>\n";
                                else
                                        $output.= "<td> </td>\n";
                        }
                        $output.= "</tr>\n";
                }
                $output.= "</table>\n";

//Display the output data
        }
        return $output;
}
?>

Cache Problem[edit]

While the suggestions and bugs above are nice, there is a crippling problem with this extension - it suffers from the MediaWiki cache. When a template is changed, the ttable needs to change also. How can that be done? The templates do not know if they are part of a ttable, so there's no reasonable way to get them to flush the cache. Because the ttable is cached, it isn't processed - which means that the ttable can't force itself to flush the cache. It would be nice if there was some magical keyword that would tell MediaWiki to never cache a certain page, then that could be added to ttable pages. Kainaw 19:16, 25 July 2007 (UTC)Reply

Scripting vulnerability[edit]

The input text between the opening and closing tags is passed through without further processing as it seems and arbitrary HTML (including Javascript) can be inserted in the resulting page. As a workaround one can replace the line

$output = $input

with

$output = ""

Problably htmlspecialchars can also be used to solve this problem. --Schevv 09:15, 10 November 2010 (UTC)Reply

About Cache Problem[edit]

There is extention Extension:Labeled Section Transclusion that allows you to include section of one page into another page. You just add "{{#lsth:SourcePageName|SourceSectionName}}" into your target page, and there is no "Cache Problem" then you have edited source page, but did not edit target page.

It may be somebody can try to see the salvation way which is used there?

Templates inside of Templates[edit]

This code recusively searches for nested {{ }} pairs:

Schalice (talk) 17:18, 12 January 2013 (UTC)Reply


preg_match_all('/\{\{(?:[^{}]|(?R))*}}/', $row3->old_text, $info);

$kvs = explode( "|", substr($info[0][0], 0, strlen($info[0][0])-2));


\{\{      # match two opening brackets
(?:       # start non-capturing group 1
  [^{}]   #   match any character except '{' and '}'
  |       #   OR
  (?R)    #   recursively call the entire pattern: \{\{(?:[^{}]|(?R))*}}
)         # end non-capturing group 1
*         # repeat non-capturing group 1 zero or more times
}}        # match two closing brackets