Talk:WikiDB/Repeat tag syntax/Archive

From TestWiki
Jump to: navigation, search
Archived Discussion
This page is now archived. It has been retained for reference purposes but any further discussion should take place at Talk:WikiDB/Repeat tag syntax.

Table Formatting[edit]

Moved from Talk:WikiDBdiff

Hi. I love this use for the Wiki, but I've had one frustration - getting formatted tables. To that end, I have made a modification for my own use, and am passing it on. Feel free to include, ignore, whatever. I just thought it might be useful. I have added two internal tags to the repeat command so that there can be footers and headers added to the output produced from the repeat tag.

The trouble was produced because something like:

<repeat table="foo">

gives totally useless results, and after looking at the MediaWiki Parser.php file, it turns out that unless that is rewritten, it always will - the table parsing is all one-pass, and doesn't preserve state while in an extension.

So, I have turned that into:

<repeat table="foo">

Which now gives the right results. Of course, this requires the following code I added (I know it is rudimentary, but it works...)

Change to WikiDB.php

                $Data = $Table->PerformQuery($Where, $Sort);
		if (trim($Input) == "")
			$Output = $Table->FormatTableData($Data);
			$outputDef = new WikiDB_OutputFormatDef($Input);
			$Output = "";
			$Data = $Table->NormaliseData($Data);
//			print_r($Data);

				$Output .= $outputDef->getHeader() . "\n";
			$RowFormat = $outputDef->getRow();
			foreach ($Data as $Row)
				$Output .= pWikiDB_ExpandVariables($RowFormat, $Row);
				$Output .= $outputDef->getFooter() . "\n";
//			print($Output);

Also, you must include the following file:



class WikiDB_OutputFormatDef 
	var $sHeader;
	var $sFooter;
	var $sRow;
	function WikiDB_OutputFormatDef($contents)
		$this->sHeader = $this->stripTagItem($contents, 'header');
		$this->sFooter = $this->stripTagItem($contents, 'footer');
		$this->sRow		= $contents;
	function hasHeader(){ return strlen($this->sHeader) > 0; }
	function hasFooter(){ return strlen($this->sFooter) > 0; }
	function getHeader(){ return $this->sHeader; }
	function getFooter(){ return $this->sFooter; }
	function getRow()   { return $this->sRow; }
	function stripTagItem(&$text, $tag)
		$tagLen = strlen($tag);
		$start = stripos($text, '<' . $tag . '>');
		if($start == FALSE)
			return '';
		$end = stripos($text, '</' . $tag . '>');
		if($end == FALSE)
			return '';
		$tagTextStart  = $start+$tagLen+2;
		$tagTextLength = $end - $tagTextStart - 1;
		$tagTextEnd    = $end + $tagLen + 3;
		$tagContents = substr($text, $tagTextStart, $tagTextLength);
		//strip out the tag
		$text = substr($text, 0, $start) . substr($text, $tagTextEnd);
		return $tagContents;
}  //END: Class WikiDB_OutputFormatDef



-Jacob 15:50, 8 April 2007 (BST)

Slight Modifications[edit]

I integrated your code into my working wiki, because I too saw the uselessness of the table formatting with the repeat tag. However, I made a couple other changes:

  • the table begin and end before and after the repeat are not needed, correct? (the header and footer tags print them)
  • It seems like the table code was inserting an extra line break in the raw input, which was causing a "br" tag in the output, which was skewing the last column of each table row. To change this, I added the last line here:
if (trim($Input) == "")
  $Output = $Table->FormatTableData($Data);
else {
  $Input = trim($Input); // get rid of extra line breaks
  • However, the trim broke the tag processing in the OutputFormatDef file. On the lines "if $start == FALSE" and "if $end == false", I changed the "==" to "===" to be a more correct equality test, since after the trim those tags for me were in position 0 (== FALSE, but not ===FALSE).
  • Finally, "stripos" only works on PHP5, so I changed the 2 lines that use strpos as follows:
OLD: stripos($text, '<' . $tag . '>'));
NEW: strpos($text, strtolower('<' . $tag . '>'));

Thanks for your help and work on this. It's a cool extension.

-- Joe Clark (a guest, joeclark AT joeclarkia DOT net)

Using the <repeat> tag to display filtered/sorted Tables[edit]

Moved from Talk:WikiDBdiff

I have a suggestion to efficiently display tables using the <repeat> tag when a table has a very large number of fields that a user may want to only see a small portion of the fields.

Expand the parameters of the <repeat> tag such that somehow a user can specify which fields to display in the rendered wiki page. Perhaps this need be only one additional parameter that contains a comma separated list of field names to display whilst not displaying all other fields in the specified table.


Suppose a table called "Stock Investments" contains the following fields: "stock_symbol", "name", "ceo_name", "address", "main_phone", "annual_revenue", "website", "current_stock_price", "my_shares_count".

There are 9 fields in this table such that when the current <repeat> tag was used to display a sorted and/or filtered table of this table, the rendered table in the browser may proceed beyond the right edge of the screen, so perhaps the <repeat> tag could have a new parameter, filter_columns, that is used as such:

<repeat table="Stock Investments" sort="stock_symbol" filter_columns="stock_symbol,name,current_stock_price,my_shares_count,annual_revenue"></repeat>

And this usage of the <repeat> tag would yield a table such as the following with ONLY the columns specified in the filter_columns parameter AND in the order specified.

Stock Investments
stock_symbol name current_stock_price my_shares_count annual_revenue
AMD AMD, Inc 14.30 100 5.649B
KEI Keithley Instruments, Inc 20 12.55 0.155B
MSFT Microsoft, Inc 29.47 25 44.282B

This feature would be very useful and I recommend including support for this feature in WikiDB.

Thanks! -- Mdrayman 22:02, 30 June 2007 (BST)

There are a lot of syntax issues with the repeat tag at present, so I need to get those resolved first. It may be that after the fix-up this kind of thing will be sufficiently simple not to warrant a separate syntax, however if this is not the case then it is a good suggestion, and one I will revisit when the other issues are fixed. --HappyDog 13:35, 4 July 2007 (BST)

Adding table properties within repeat tag[edit]

Moved from Talk:WikiDBdiff

Could you please provide a patch which enables extensions to <repeat> tag for adding custom attributes for <table> generated by default? This can be the alternative to missing table formating within <repeat>. Especially, I need "style" and "class" attributes to be added to default table output so I can format this table as required and apply the sortable property.

Hi - The table formatting code is in a bit of a mess at the moment (as is quite a lot of the code...!) and I am in the process of tidying it all up at the moment. I'm quite a long way through this process, but haven't touched on the formatting yet. When I do I'll make it a bit more flexible. All of the major elements that the extension outputs will have a class defined for them, so you can style these as you need. If you need the ability to add your own custom classes as well then I don't see a problem with adding that feature. --HappyDog 12:05, 5 February 2008 (GMT)

A hacked TabbedData solution[edit]

I created a somewhat messy but effective hack that allows me to display data from a repeat tag as a table. It is basically a change the repeat tag parser in ParserTags.php. It basically follows the SimpleTable (formerly TabbedData) extension idea. You can see the old code I commented out in the if(0). Please ignore various custom syntax related stuff I've added. It's easy to get the idea.

This hack proved itself as sufficient for almost all my needs, and I have a table with several thousands of entries which is accessed from almost any page in my wiki using a repeat tag.

However there is a problem - I cannot use the tag within a template, and I cannot use a tmeplate to parse a complete row. I think this can be solved if WikiDB hadn't used braces for field markers like regular templates do. not being an expert on the MW code package. However I am not sure if that's possible, and if indeed that would solve the problem. But I think it's worth giving some though

Here's the code

function wfWikiDB_RepeatTag($Input, $Args, $Parser = NULL) {

		global $wgOut;

		global $wgpWikiDBParsingForSave;

	// If the page is being parsed for a save operation, then do nothing.

		if ($wgpWikiDBParsingForSave)

			return "";

	// Get $Parser if it was not supplied.

	    if (!$Parser)

			$Parser =& $GLOBALS['wgParser'];

	// Repeat tags change all the time, so disable caching.


	// Get table argument (required)

		if (isset($Args['table']))

			$TableName = $Args['table'];


			$TableName = "";

	// If no table argument, then there is no way of retrieving the data, so we

	// print a warning and exit.

		if ($TableName == "")

			return WikiDB_Parse("''No table specified in <repeat> tag!''",


	// Get sort argument (optional - default to unsorted)

		if (isset($Args['sort']))

			$Sort = $Args['sort'];


			$Sort = "";

	// Get criteria argument (optional - default to unfiltered)

		if (isset($Args['criteria']))

			$Where = $Args['criteria'];


			$Where = "";

		if (isset($Args['header']))

			$Header = $Args['header'];


			$Header = ""; 


		if (isset($Args['bgcolor']))

			$Bgcolor = $Args['bgcolor'];


			$Bgcolor = "";	


		if (isset($Args['width']))

			$Width = $Args['width'];


			$Width = "90%";	

	// Perform the query and get the resulting data set.

		$Query = new WikiDB_Query($TableName, $Where, $Sort);

		$Data = $Query->Run();

	// If returned data is not an array, then an error occurred, so we output the

	// error.

		if (!is_array($Data))

			return $Data;

	// TODO: Should not be needed here.  Rework later code so it doesn't need a

	// 		 table object, as the query may be run on several tables (JOIN).

		$Table = new WikiDB_Table($TableName);

	// If tag contents are empty, display in the standard format.

	// TODO: FormatTableData should be a static function!

		if (trim($Input) == "")

			$Output = $Table->FormatTableData($Data);

	// If tag contents are non-empty then use it as a template for each row

	// of the returned data, expanding variables where necessary.

		else {


			$Output = "";

			$Data = $Table->NormaliseData($Data);

//			print_r($Data);

			foreach ($Data as $Row)

				$Output .= pWikiDB_ExpandVariables($Input, $Row);

//			print($Output);



				$Output = '<table style=\"background:#FF0000;\" border="1" cellpadding="2" cellspacing="0" width=' . $Width .' align=center>' 

						 . "\n";

				$Data = $Table->NormaliseData($Data);

	//			print_r($Data);

				if ($Bgcolor ==""){

					$temp  = '<tr BGCOLOR=\'#FF0000\'>' ;



					$temp  = '<tr BGCOLOR=\'#'. $Bgcolor .'\'>' ;


				$temp .= "<th>" . preg_replace('/\t/', '</th><th align=center >', $Header) . "</th>\n";

				$temp .= '</tr>';

				$Output .= $temp;

				foreach ($Data as $Row){

					$temp = pWikiDB_ExpandVariables($Input, $Row);			

					if($temp != Null && !strstr($temp, "year")){

						$temp = str_replace('p>', 'td>', $temp);			

						$wikitab = "<td>" . preg_replace('/\t/', '</td><td>', $temp) . "</td>\n";

						$Output .= '<tr>' . $wikitab . '</tr>';					

						//$Output .= $temp;



	//			print($Output);

				//$Output .= "</td></tr><tr><td>$Input</td></tr></table>\n";

				$Output .= "</table>\n";



	// Return the resulting output, parsed as wiki text.

		return WikiDB_Parse($Output, $Parser);


Osishkin 17:38, 1 September 2010 (BST)

Leave it as it is[edit]

Hi HappyDog. First of all I'd like to congratulate you. I've been searching for a long time an easy, wiki-like way to represent relational data. Templates are completely useless when it comes to represent a variable number of rows on a table -- if just templates could be nested, so you could do {{Table {{ Row Data1 | Data2 }} {{ Row Data3 | Data4 }} }}, but they can't. Shame on the parser. This is by far the most elegant solution I've seen. I was thinking of installing some of the form-interfaces available out there, but seems to me like taking a sledgehammer to crack a nut.

Concerning the <repeat> tag: leave it as it is. It is powerful enough: if you want a raw table, just enter a <repeat /> tag. If you need something more elaborate, you can always insert text between tags, and put a header and footer. Creating a header-footer syntax would only complicate something which is quite simple and useful. My two cents: go for the select part to choose columns, and refine the criteria property. What makes this extension so powerful is the fact that data is stored consistently and can be retrieved throughout the wiki. And relational data will always be tabular. A css class should suffice for most applications.

I'd like to see, however, a way to display a set of rows defined in one page (with the same SourceArticle meta tag). I've tried to do a "SourceArticle=PageName" query but it won't do the trick. However, the most elegant solution, in my opinion, would be to allow multiple row definition with a template, which would a) define the data b) show it on a table in the same place it was defined. I'm willing to dive into the code and give you a hand, if you agree - I think I'm adapting that for my wiki anyways. Just mail or write me in MW if you feel comfortable with the idea of using my patches so we talk before I code.

By the way, I'm trying to represent this information: a role-playing game player sheet.


-- Kanor

Hi Kanor, Thanks for your detailed and positive comments! The problem I am trying to solve with the header/footer specification is that it is currently not possible to insert the WikiDB output inside a custom MediaWiki table. This is a limitation of MW, which we either live with, or find a suitable syntax that allows us to work around.
For example, currently you are unable to do the following (horizontal rules added for clarity):
{| class="datatable"
! colspan="3" | Client Data
! colspan="2" | Invoicing
! First Name !! Surname !! Company !! Last Invoice Data !! Paid?
<repeat table="Clients">
| {{{FirstName}}} || {{{Surname}}} || {{{Company}}} || {{{LastInvoice}}} || {{{InvoicePaid}}}
This should give output like the following:

Client Data Invoicing
First Name Surname Company Last Invoice Data Paid?
John Smith JS Inc. 23-10-2010 Y
Bill Jones Bill Corp. 14-01-2011 N

However, MW borks on it, and will give something like this:

| {{{FirstName}}} || {{{Surname}}} || {{{Company}}} || {{{LastInvoice}}} || {{{InvoicePaid}}} |-

| {{{FirstName}}} || {{{Surname}}} || {{{Company}}} || {{{LastInvoice}}} || {{{InvoicePaid}}}



Client DataInvoicing
First NameSurnameCompanyLast Invoice DataPaid?

Very broken! The proposed syntax will allow you to acheive this kind of flexible table output by passing the header/footer sections into the extension directly, as there is no other way to do it. The question is, what syntax to use?
In terms of your other enhancement suggestions, if I'm understanding you correctly, then these can already be done. The source article can be displayed using {{{_SourceArticle}}}, as in the following example:

The data for Microsoft is defined in Hoss1.

The data for شرکت من is defined in Hoss1.

The data for Microsoft is defined in WikiDB/Tutorial.

The data for Apple is defined in WikiDB/Tutorial.

(yes - I am aware this feature is currently undocumented). You should also be able to use this field in the criteria, but I don't have any test data I can demonstrate that with (I don't think).
Similarly, multiple data rows can be defined in a single <data> tag:

<data table="Hats" fields="Name,Hat,HatSize" template="default">
Name Hat HatSize
John Trilby 51
Harry Homberg 66
Larry Beret 48

Or, with a non-default template (template:Hats):

  • John is very proud of his size 51 Trilby.
  • Harry is very proud of his size 66 Homberg.
  • Larry is very proud of his size 48 Beret.

Good luck with your project - I hope you find the above info helpful. Please let me know if you have any more ideas or suggestions!
--00:24, 2 February 2011 (GMT)
Hi HappyDog,
Got it. I didn't really get the point of a new syntax. Now I do. Shame on me. Shame on the parser :D
One question: can't you parse what you find inside the repeat tag, expand it (i.e.: create a the table still with wiki format) and then return the whole thing to the parser saying it is wiki text? I knew the parser was weird, but this would be an actual surprise.
Otherwise, you could always create another tag, similar to repeat, which would extract DB rows inside html (or wiki) table rows, i.e.:
! Name !! Hat !! Size
<rows table="Hats">
{{{Name}}} | {{{Hat}}} | {{{Size}}}
Or create a new tag inside the repeat tag:
! Name !! Hat !! Size
<repeat table="Hats">
Or create an attribute in the repeat tag to choose between full-table mode and row-only mode. I like the first solution most, though. My two cents ^^
--Kanor 13:18, 2 February 2011 (GMT)
I'll look into that idea. The problem is that I use the MW parser to parse the contents of the repeat tag, and so if there is no opening table tag (which there wouldn't be) it doesn't recognise that it's a table row and so treats it as normal text. If we have the header/footer block then we can wrap the rows in the supplied opening/closing content and MW has something proper to parse. Introducing a row syntax, as you propose above, means parsing the tag contents manually, which I think is going to end up causing more problems than it solves. That said, I wonder if it's possible to use HTML table markup in the repeat tag (rather than MW table markup)? I will experiment with that and see what it throws up. --HappyDog 15:58, 2 February 2011 (GMT)
No good. You can see the outcome in the Sandbox. --HappyDog 16:04, 2 February 2011 (GMT)
I insist: if the parser was recursive, developers would have much less headaches ^^ --Kanor 22:55, 2 February 2011 (GMT)
Oh, one more thing. It is possible to define table values without specifying the fileds? This would be particularly useful when a table is already defined, so you could just do:
<data table="Hats">
to create new data -- and also ensure data follows a certain pattern. Cheers,
--Kanor 13:23, 2 February 2011 (GMT)
Not possible, nor desirable. It would give too tight a coupling between the data and the table definition (something we are trying to avoid). It would mean that if someone changed the definition (including just re-ordering the fields) all your data would be invalidated and require re-entry! --HappyDog 15:58, 2 February 2011 (GMT)
Well, that would force new inputs to follow the definition, which could be useful to pick up structured data. That way users wouldn't have to mess up with definitions. Anyways. It's just a matter of style :D --Kanor 22:55, 2 February 2011 (GMT)

By the way, have you considered creating a parser function? I know for sure that results parser functions are parsed after processing. Something like:

| Conditions
| Other parameters
| Repeat block {{{field1}}} {{{field2}}}

would in fact pass the result to the parser. That happens in the Loops extension. For instance, the code:

{{ #vardefine: i | 0 }}{{
  | {{ #ifexpr: {{ #var: i }} < 5 | true }}
* {{ #var: i }}{{ #vardefine: i | {{ #expr: {{ #var: i }} + 1 }} }}

creates a list of numbers (check the * usage).

--Kanor 22:55, 2 February 2011 (GMT)

Hmmm... not considered that. Will look into that as a possibility. Cheers! --HappyDog 00:51, 5 February 2011 (GMT)

offer both?[edit]

I don't know the best solution, and did not think much about all the arguments brought forward, but why not test both. Offer <repeat> with the old functionality and <repeat2> with the new header/footer options, etc. --Kebap 12:58, 23 February 2011 (GMT)

header, footer & something in between?[edit]

Hi, when/if you add the possibility to include special areas for header & footer content, I would also like an area for content, that should be inserted in between repeated entries, but not at the beginning nor at the end. Like this:




I was trying to get some result like this with the current setup, by adding Y directly before/after X, but I would only get the same result with an extra Y at the very beginning/end. Thanks! --Kebap 14:59, 23 February 2011 (GMT)

I think a better way of handling that would be to pass some extra arguments into the repeat body. We already have {{{_Row}}}, but adding a {{{_TotalRows}}} variable would allow you to do something like:
{{#ifeq: {{{_Row}}}|{{{_TotalRows}}}||Y}}
That would be a better solution to your specific problem, I think. I will look into adding that functionality. --HappyDog 22:07, 23 February 2011 (GMT)
Now, that is a splendid solution! Thanks a lot. And, while we are at it, can't we solve the header/footer topic like this as well? Try:
{{#ifeq: {{{_Row}}}|{{{_FirstRow}}}|HEADER|}}
{{#ifeq: {{{_Row}}}|{{{_TotalRows}}}||BETWEEN}}
{{#ifeq: {{{_Row}}}|{{{_TotalRows}}}|FOOTER|}}
Maybe _FirstRow isn't even needed and can be replaced with 1? _TotalRows is not yet implemented, so I have to manually set this value. Then it looks like this:

  • Here be header data
  • Here be data from row # 1
  • Here be data inserted between each row
  • Here be data from row # 2
  • Here be footer data

  • Here be data from row # 3
  • Here be data inserted between each row
  • Here be data from row # 4
  • Here be data inserted between each row
It works! That's a beginning. There are still some weird things regarding the formatting, but I will test it more and see where I can take it from here. Thanks again! --Kebap 11:51, 25 February 2011 (GMT)
You might be onto something... I will investigate. Unfortunately, using this kind of method for creating table content is tricky, due to conflicting syntax (the parser function uses | to separate arguments, but this is also part of the table mark-up). It can be worked around using a template (like this example on Wikipedia) but it means that there are some extra steps required in order to make this possible, plus the resulting syntax is pretty unwieldy. However, perhaps that is not too much of a problem, if it actualy solves the main issue of header/footer sections. I will try some experiments, to see if I can get something working. --HappyDog 12:03, 25 February 2011 (GMT)
I now created Template:! here, it is used to include char | via {{!}} where writing out | is not possible. With this, I can create individual tables from wikiDB data, for example from the "Companies" table from the WikiDB/Tutorial page:
name founded revenue
Microsoft 1492 $8
شرکت من 2013 $99

|Microsoft |1492 |$8 |-

|Apple |1984 |$7.42 |-

However, I still can't transform that output into input for other extensions, as I aluded to here. I am able to generate a seemingly correct command structure filled with wikiDB data, but this seems to not get recognised by the wiki parser and is just presented as plain text. Any ideas? --Kebap 14:32, 25 February 2011 (GMT)

And of ORs[edit]

At least for me the main thing missing in the syntax, is the ability to JOIN and OR. I've been able to use a small hack to create ANDs of ORs using the '|' character for ORing a field or value in the criteria string. I only have it currently on my development machine, but it seems to work fine there. I'm no PHP expert, but if anyone wants to use it - or better still extend it - you are more than welcome. All changes in WikiDB_Query.php, first in pGenerateFieldLists()

			// for criteria, use array of array
			if (!isset($Field['dir']))
				$newCriteriaFields = array(); // add another array

			foreach ($fieldoptions as $k => $foption) {
			// NOTE - use a clone!!! if we do have an OR, we need to use same 
			//        attributes several times, only chnaging the Alias (using $foption)
				$fieldCopy = $Field; 
				$FieldName = $foption;

			// Create the table alias that will be used for this field.  Each field
			// requires a separate join to the WikiDB fields table, and this is the name
			// that will be used.
				$Alias = $this->pTables[$TableName]['alias'] . "_" . $foption;
				$Alias = $this->pDB->tableName($Alias);

			// Store the $FieldName => $Alias mapping into the list of fields used by
			// this table, so that they are created as part of the JOIN.
				$this->pTables[$TableName]['fields'][$foption] = $Alias;

			// Also add the alias to the original field definition, so it can be used
			// in the WHERE/ORDER BY clauses.
			// We know that if the item has a 'dir' element, then this is a sort field,
			// otherwise it is a criteria field.
				$fieldCopy['tablealias'] = $Alias;

				if (isset($Field['dir']))
					$this->pSortFields[] = $fieldCopy;
					$newCriteriaFields[] = $fieldCopy;

			// finally, if this is a criteria array, push it to pCriteria
			if (!isset($Field['dir'])){
				$this->pCriteriaFields[] = $newCriteriaFields;

then in pGetQuerySQL()

		foreach ($WhereFields as $i => $Criterias) {
			if ($i > 0)
				$SQL .= " AND (";
				foreach ($Criterias as $j => $Criteria) {
				if ($j > 0)
					$SQL .= " OR ";

				// finally, OR the values
				$fieldvalues = explode("|",$Criteria['value']);
				foreach ($fieldvalues as $k => $fvalue) {
					if ($k > 0)
						$SQL .= " OR ";
					$SQL .= $Criteria['tablealias'] . ".field_value"
						  . " " . $Criteria['op'] . " "
						  . $DB->addQuotes($fvalue);

Osishkin 09:37, 15 January 2012 (UTC)