From adb2d7a006149fa391a436844e7561453393169a Mon Sep 17 00:00:00 2001
From: mattpiwik <matthieu.aubry@gmail.com>
Date: Sun, 31 May 2009 11:31:14 +0000
Subject: [PATCH] - on a rainy sunday night, implented one of the most
 requested feature which was little work and should make a lot of users happy!
 fixes #747 adding links on both sub tables for search engines and keywords
 linking directly to the search engine page for this keyword added definition
 for the main search engines (approx 15) users can contribute more if they
 like; if a definition, eg. "search?q={k}" for Google is not found, then we
 simply link to the search engine homepage.

updated FAQ on how to add a search engine see http://piwik.org/faq/general/#faq_39
added tests to check that the search engine file is defined properly
- renamed queuefilter to queueFilter for consistency


git-svn-id: http://dev.piwik.org/svn/trunk@1158 59fd770c-687e-43c8-a1e3-f5a4ff64c105
---
 core/Archive.php                              |   2 +-
 core/Common.php                               |  18 +---
 core/DataFiles/SearchEngines.php              |  91 +++++++++---------
 core/DataTable.php                            |  19 ++++
 core/DataTable/Filter.php                     |   1 +
 .../ColumnCallbackAddColumnPercentage.php     |   2 +-
 .../Filter/ColumnCallbackAddMetadata.php      |  11 ++-
 .../Filter/ColumnCallbackReplace.php          |  14 ++-
 .../Filter/MetadataCallbackReplace.php        |  35 +++++++
 core/DataTable/Row.php                        |  13 ++-
 plugins/Actions/API.php                       |   2 +-
 plugins/Live/Visitor.php                      |   6 +-
 plugins/Provider/API.php                      |   6 +-
 plugins/Referers/API.php                      |  29 ++++--
 plugins/Referers/functions.php                |  26 +++--
 ...www.rambler.ru.png => nova.rambler.ru.png} | Bin
 .../{www.live.com.png => search.live.com.png} | Bin
 .../searchEngines/search2.seznam.cz.png       | Bin 1149 -> 0 bytes
 ...volny.cz.png => volny.zlatestranky.cz.png} | Bin
 plugins/UserCountry/API.php                   |   8 +-
 plugins/UserSettings/API.php                  |  28 +++---
 plugins/VisitTime/API.php                     |   4 +-
 plugins/VisitorInterest/API.php               |   8 +-
 tests/core/Common.test.php                    |  12 +++
 tests/core/DataTable.test.php                 |  16 +++
 25 files changed, 238 insertions(+), 113 deletions(-)
 create mode 100644 core/DataTable/Filter/MetadataCallbackReplace.php
 rename plugins/Referers/images/searchEngines/{www.rambler.ru.png => nova.rambler.ru.png} (100%)
 rename plugins/Referers/images/searchEngines/{www.live.com.png => search.live.com.png} (100%)
 delete mode 100644 plugins/Referers/images/searchEngines/search2.seznam.cz.png
 rename plugins/Referers/images/searchEngines/{web.volny.cz.png => volny.zlatestranky.cz.png} (100%)

diff --git a/core/Archive.php b/core/Archive.php
index a909a76dfe..f5f658d440 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -21,7 +21,7 @@ require_once 'Archive/Single.php';
  * <pre>
  * 		$archive = Piwik_Archive::build($idSite = 1, $period = 'week', '2008-03-08' );
  * 		$dataTable = $archive->getDataTable('Provider_hostnameExt');
- * 		$dataTable->queuefilter('ReplaceColumnNames');
+ * 		$dataTable->queueFilter('ReplaceColumnNames');
  * 		return $dataTable;
  * </pre>
  * 
diff --git a/core/Common.php b/core/Common.php
index fd18c95717..855e5b287a 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -94,7 +94,7 @@ class Piwik_Common
 			require_once "Zend/Auth.php";
 			require_once "Timer.php";
 			require_once "PluginsManager.php";
-			require_once "Piwik.php";
+			require_once "core/Piwik.php";
 			require_once "Access.php";
 			require_once "Auth.php";
 			require_once "API/Proxy.php";
@@ -497,22 +497,12 @@ class Piwik_Common
 		return md5(uniqid(rand(), true));
 	}
 
-	/**
-	 * Convert dotted IP to a stringified integer representation
-	 *
-	 * @return string ip
-	 */
-	static public function getIp()
-	{
-		return sprintf("%u", ip2long(self::getIpString()));
-	}
-
 	/**
 	 * Returns the best possible IP of the current user, in the format A.B.C.D
 	 *
 	 * @return string ip
 	 */
-	static public function getIpString()
+	static public function getIp()
 	{
 		if(isset($_SERVER['HTTP_CLIENT_IP'])
 		&& ($ip = Piwik_Common::getFirstIpFromList($_SERVER['HTTP_CLIENT_IP']))
@@ -808,9 +798,9 @@ class Piwik_Common
 		}
 		
 		if(function_exists('iconv') 
-			&& isset($GLOBALS['Piwik_SearchEngines'][$refererHost][2]))
+			&& isset($GLOBALS['Piwik_SearchEngines'][$refererHost][3]))
 		{
-			$charset = trim($GLOBALS['Piwik_SearchEngines'][$refererHost][2]);
+			$charset = trim($GLOBALS['Piwik_SearchEngines'][$refererHost][3]);
 			if(!empty($charset)) 
 			{
 				$key = @iconv($charset, 'utf-8//IGNORE', $key);
diff --git a/core/DataFiles/SearchEngines.php b/core/DataFiles/SearchEngines.php
index 814096265e..a1aafd460f 100644
--- a/core/DataFiles/SearchEngines.php
+++ b/core/DataFiles/SearchEngines.php
@@ -22,13 +22,14 @@
  * The main search engine URL has to be at the top of the list for the given search Engine.
  * You can add new search engines icons by adding the icon in the plugins/Referers/images/SearchEngines directory 
  * using the format "mainSearchEngineUrl.png". Example: www.google.com.png
+ * To help Piwik link directly the search engine result page for the keyword, specify the third entry in the array 
+ * using the macro {k} that will automatically be replaced by the keyword.
  * 
  *  A simple example is:
- *  "www.google.com"		=> array("Google", "q"),
+ *  "www.google.com"		=> array("Google", "q", "search?q={k}"),
  * 
  *  A more complicated example, with an array of possible variable names, and a custom charset:
- *  "www.baidu.com"			=> array("Baidu", array("wd","word","kw"), "gb2312"),
- * 
+ *  "www.baidu.com"			=> array("Baidu", array("wd","word","kw"), "s?wd={k}", "gb2312"),
  */
 if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 {
@@ -37,8 +38,8 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		//" "		=> array(" ", " " [, " "]),
 		
 		// 1
-		"1.cz" 						=> array("1.cz", "q", "iso-8859-2"),
-		"www.1.cz" 					=> array("1.cz", "q", "iso-8859-2"),
+		"1.cz" 						=> array("1.cz", "q", "index.php?q={k}", "iso-8859-2"),
+		"www.1.cz" 					=> array("1.cz", "q", false, "iso-8859-2"),
 
 		// 123people
 		"www.123people.com"			=> array("123people", "search_term"),
@@ -74,7 +75,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"acont.de"			=> array("Acont", "query"),
 		
 		//Alexa
-		"www.alexa.com"		        => array("Alexa", "q"),
+		"www.alexa.com"		        => array("Alexa", "q", "search?q={k}"),
 		"alexa.com"		        => array("Alexa", "q"),
 		
 		//Alice Adsl
@@ -118,7 +119,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"apollo7.de"			=> array("Apollo7", "query"),
 		
 		// AOL
-		"search.aol.com"		=> array("AOL", array("query", "q")),
+		"search.aol.com"		=> array("AOL", array("query", "q"), "aol/search?query={k}"),
 		"aolsearch.aol.com"		=> array("AOL", array("query", "q")),
 		"www.aolrecherche.aol.fr"	=> array("AOL", array("query", "q")),
 		"www.aolrecherches.aol.fr" 	=> array("AOL", array("query", "q")),
@@ -148,7 +149,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"arianna.libero.it" 		=> array("Arianna", "query"),
 		
 		// Ask
-		"www.ask.com"			=> array("Ask", array("ask","q")),
+		"www.ask.com"			=> array("Ask", array("ask","q"), "web?q={k}"),
 		"web.ask.com"			=> array("Ask", array("ask","q")),
 		"www.ask.co.uk"			=> array("Ask", "q"),
 		"uk.ask.com"			=> array("Ask", "q"),
@@ -160,7 +161,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"ask.jp"			=> array("Ask", "q"),
 		
 		// Atlas
-		"search.atlas.cz" 		=> array("Atlas", "q", "windows-1250"),
+		"search.atlas.cz" 		=> array("Atlas", "q", "?q={k}", "windows-1250"),
 		
 		// Austronaut
 		"www2.austronaut.at"		=> array("Austronaut", "begriff"),
@@ -169,11 +170,11 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"search.babylon.com"		=> array("Babylon (Powered by Google)","q"),
 	
 		// Baidu
-		"www.baidu.com"			=> array("Baidu", array("wd","word","kw"), "gb2312"),
-		"www1.baidu.com"		=> array("Baidu", array("wd","word","kw"), "gb2312"),
-		"zhidao.baidu.com"		=> array("Baidu", array("wd","word","kw"), "gb2312"),
-		"tieba.baidu.com"		=> array("Baidu", array("wd","word","kw"), "gb2312"),
-		"news.baidu.com"		=> array("Baidu", array("wd","word","kw"), "gb2312"),
+		"www.baidu.com"			=> array("Baidu", array("wd","word","kw"), "s?wd={k}", "gb2312"),
+		"www1.baidu.com"		=> array("Baidu", array("wd","word","kw"), false, "gb2312"),
+		"zhidao.baidu.com"		=> array("Baidu", array("wd","word","kw"), false, "gb2312"),
+		"tieba.baidu.com"		=> array("Baidu", array("wd","word","kw"), false, "gb2312"),
+		"news.baidu.com"		=> array("Baidu", array("wd","word","kw"), false, "gb2312"),
 		"web.gougou.com"		=> array("Baidu", "search"),
 		
 		// BBC
@@ -207,9 +208,9 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.cegetel.net" 		=> array("Cegetel (Google)", "q"),
 		
 		// Centrum
-		"fulltext.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
-		"morfeo.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
-		"search.centrum.cz" 		=> array("Centrum", "q", "windows-1250"),
+		"search.centrum.cz" 		=> array("Centrum", "q", "index.php?q={k}", "windows-1250"),
+		"fulltext.centrum.cz" 		=> array("Centrum", "q", false, "windows-1250"),
+		"morfeo.centrum.cz" 		=> array("Centrum", "q", false, "windows-1250"),
 		
 		// Chello
 		"www.chello.fr"		 	=> array("Chello", "q1"),
@@ -218,7 +219,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"recherche.club-internet.fr"    => array("Club Internet", "q"),
 		
 		// Conduit
-		"search.conduit.com"         => array("Conduit.com (Powered by Google)", "q"),
+		"search.conduit.com"         => array("Conduit.com", "q", "Results.aspx?q={k}"),
 	
 		// Comcast
 		"www.comcast.net" 		=> array("Comcast", "query"),
@@ -238,7 +239,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.crossbot.de"		=> array("Crossbot", "q"),
 		
 		// Cuil
-		"www.cuil.com"			=> array("Cuil", "q"),
+		"www.cuil.com"			=> array("Cuil", "q", "search?q={k}"),
 	
 		// DasOertliche
 		"www.dasoertliche.de"	        => array("DasOertliche", "kw"),
@@ -301,7 +302,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.excite.fr"			=> array("Excite", "search"),
 		
 		// Exalead
-		"www.exalead.fr"		=> array("Exalead", "q"),
+		"www.exalead.fr"		=> array("Exalead", "q", "search/results?q={k}"),
 		"www.exalead.com"		=> array("Exalead", "q"),
 		
 		// eo
@@ -371,7 +372,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"ocnsearch.goo.ne.jp"		=> array("goo", "mt"),
 		
 		// Google
-		"www.google.com"		=> array("Google", "q"),
+		"www.google.com"		=> array("Google", "q", "search?q={k}"),
 		"gogole.fr"				=> array("Google", "q"),
 		"www.gogole.fr"			=> array("Google", "q"),
 		"wwwgoogle.fr"			=> array("Google", "q"),
@@ -555,7 +556,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"buscador.terra.es" 	        => array("Google", "query"),
 		"buscador.terra.cl" 	        => array("Google", "query"),
 		"buscador.terra.com.br"		=> array("Google", "query"),
-		"www.icq.com" 			=> array("Google", "q"),
 		"www.adelphia.net" 		=> array("Google", "q"),
 		"so.qq.com" 			=> array("Google", "word"),
 		"misc.skynet.be" 		=> array("Google", "keywords"),
@@ -566,7 +566,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		
 		
 		//Google Blogsearch
-		"blogsearch.google.com"		=> array("Google Blogsearch", "q"),
+		"blogsearch.google.com"		=> array("Google Blogsearch", "q", "blogsearch?q={k}"),
 		"blogsearch.google.de"		=> array("Google Blogsearch", "q"),
 		"blogsearch.google.fr"		=> array("Google Blogsearch", "q"),
 		"blogsearch.google.co.uk"	=> array("Google Blogsearch", "q"),
@@ -589,8 +589,9 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"directory.google.com"		=> array("Google Directory", " "),
 		
 		// Google Images        
-		"images.google.com"        => array("Google Images", "q"),
+		"images.google.com"        => array("Google Images", "q", "images?q={k}"),
 		"images.google.cz"        => array("Google Images", "q"),
+        "images.google.lt"        => array("Google Images", "q"),
         "images.google.hr"        => array("Google Images", "q"),
         "images.google.hu"        => array("Google Images", "q"),
         "images.google.com.kw"        => array("Google Images", "q"),
@@ -647,8 +648,6 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"images.google.ru"		=> array("Google Images", "q"),
 		"images.google.se"		=> array("Google Images", "q"),
 		"images.google.sk"		=> array("Google Images", "q"),
-		"images.google.com"		=> array("Google Images", "q"),
-	
 		
 		// Google News
 		"news.google.com" 		=> array("Google News", "q"),
@@ -708,7 +707,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"blogs.icerocket.com"		  => array("Icerocket", "qt"),
 		
 		// ICQ
-		"www.icq.com"			=> array("ICQ", "q"),
+		"www.icq.com"			=> array("ICQ", "q", "search/results.php?q={k}"),
 		"search.icq.com"		=> array("ICQ", "q"),
 		
 		// Ilse
@@ -767,17 +766,17 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"recherche.toile.qc.ca"		=> array("La Toile Du Québec", "query"),
 		
 		// Live.com
-		"www.live.com"			=> array("Live", "q"),
+		"search.live.com"		=> array("Live", "q", "results.aspx?q={k}"),
 		"beta.search.live.com"	=> array("Live", "q"),
-		"search.live.com"		=> array("Live", "q"),
+		"www.live.com"			=> array("Live", "q"),
 		"search.msn.com"		=> array("Live", "q"),
-		"beta.search.msn.fr"		=> array("Live", "q"),
+		"beta.search.msn.fr"	=> array("Live", "q"),
 		"search.msn.fr"			=> array("Live", "q"),
 		"search.msn.es"			=> array("Live", "q"),
 		"search.msn.se"			=> array("Live", "q"),
-		"search.latam.msn.com"		=> array("Live", "q"),
+		"search.latam.msn.com"	=> array("Live", "q"),
 		"search.msn.nl" 		=> array("Live", "q"),
-		"leguide.fr.msn.com"		=> array("Live", "s"),
+		"leguide.fr.msn.com"	=> array("Live", "s"),
 		"leguide.msn.fr"		=> array("Live", "s"),
 		"search.msn.co.jp"		=> array("Live", "q"),
 		"search.msn.no"			=> array("Live", "q"),
@@ -857,6 +856,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"home.meinestadt.de"	        => array("Meinestadt.de", "words"),
 		
 		// Mister Wong
+		"www.mister-wong.com" => array('Mister Wong', 'keywords', "search/?keywords={k}"),
 		"www.mister-wong.de" => array('Mister Wong', 'keywords'),
 	
 		// Monstercrawler
@@ -890,8 +890,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"ko.search.need2find.com"	=> array("Needtofind", "searchfor"),
 
 		// Neti
-		"www.neti.ee" 		    => array("Neti", "query", "iso-8859-1"),
-		"www.neti.ee/odp/"      => array("Neti World", "query", "iso-8859-1"),
+		"www.neti.ee" 		    => array("Neti", "query", "cgi-bin/otsing?query={k}", "iso-8859-1"),
 		
 		// Netster
 		"www.netster.com"		=> array("Netster", "keywords"),
@@ -953,10 +952,10 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.qualigo.nl"	        => array("Qualigo", "q"),
 		
 		// Rambler
+		"nova.rambler.ru"           => array("Rambler", "query", "search?query={k}"), 
 		"search.rambler.ru" 		=> array("Rambler", "words"),
 		"www.rambler.ru"            => array("Rambler", "words"),
-		"nova.rambler.ru"           => array("Rambler", "query"), 
-		"nova.rambler.ru" => array('Rambler', 'q'),
+		"nova.rambler.ru" 			=> array("Rambler", "q"),
 
 		// Reacteur.com
 		"www.reacteur.com"		=> array("Reacteur", "kw"),
@@ -1010,9 +1009,9 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"sesam.no" 					=> array("Sesam", "q"),
 	
 		// Seznam
+		"search.seznam.cz" 		=> array("Seznam", "q"),
 		"search1.seznam.cz" 		=> array("Seznam", "q"),
 		"search2.seznam.cz" 		=> array("Seznam", "q"),
-		"search.seznam.cz" 		=> array("Seznam", "q"),
 		
 		// Sharelook
 		"www.sharelook.fr"		=> array("Sharelook", "keyword"),
@@ -1053,7 +1052,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"search-dyn.tiscali.it" 	=> array("Tiscali", "key"),
 		"www.tiscali.co.uk"		=> array("Tiscali", "query"),
 		"search-dyn.tiscali.de"		=> array("Tiscali", "key"),
-		"hledani.tiscali.cz" 		=> array("Tiscali", "query", "windows-1250"),
+		"hledani.tiscali.cz" 		=> array("Tiscali", "query", false, "windows-1250"),
 		
 		// T-Online
 		"suche.t-online.de"		=> array("T-Online", "q"),
@@ -1083,7 +1082,8 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"beta.voila.fr"			=> array("Voila", "kw"),
 		
 		// Volny
-		"web.volny.cz" 			=> array("Volny", "search", "windows-1250"),
+		"volny.zlatestranky.cz" => array("Volny", "search", "fulltext/?search={k}", "windows-1250"),
+		"web.volny.cz" 			=> array("Volny", "search", false, "windows-1250"),
 		
 		// Wanadoo
 		"search.ke.wanadoo.fr"		=> array("Wanadoo", "kw"),
@@ -1100,7 +1100,7 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.x-recherche.com" 		=> array("X-Recherche", "mots"),
 		
 		// Yahoo
-		"search.yahoo.com"		=> array("Yahoo!", "p"),
+		"search.yahoo.com"		=> array("Yahoo!", "p", "search?p={k}"),
 		"ink.yahoo.com"			=> array("Yahoo!", "p"),
 		"ink.yahoo.fr"			=> array("Yahoo!", "p"),
 		"fr.ink.yahoo.com"		=> array("Yahoo!", "p"),
@@ -1133,11 +1133,12 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"cf.dir.yahoo.com"		=> array("Yahoo! Directory", ""),
 		"fr.dir.yahoo.com"		=> array("Yahoo! Directory", ""),
 
-		"images.search.yahoo.com" => array('Yahoo! Images', 'p'),
+		// Yahoo! Images
+		"images.search.yahoo.com" => array('Yahoo! Images', 'p', "search/images?p={k}"),
 	
 		// Yandex
-		"www.yandex.ru" 		=> array("Yandex", "text"),
-		"yandex.ru" 			=> array("Yandex", "text"),
+		"yandex.ru" 		=> array("Yandex", "text", "yandsearch?text={k}"),
+		"www.yandex.ru" 			=> array("Yandex", "text"),
 		"search.yaca.yandex.ru" 	=> array("Yandex", "text"),
 		"ya.ru" 			=> array("Yandex", "text"),
 		"www.ya.ru" 			=> array("Yandex", "text"),
@@ -1193,8 +1194,8 @@ if(!isset($GLOBALS['Piwik_SearchEngines'] ))
 		"www.zoeken.nl" 		=> array("Zoeken","query"),
 		
 		// Zoohoo
-		"zoohoo.cz" 			=> array("Zoohoo", "q", "windows-1250"),
-		"www.zoohoo.cz" 		=> array("Zoohoo", "q", "windows-1250"),
+		"zoohoo.cz" 			=> array("Zoohoo", "q", "?q={k}", "windows-1250"),
+		"www.zoohoo.cz" 		=> array("Zoohoo", "q", false, "windows-1250"),
 		
 		// Zoznam
 		"www.zoznam.sk" 		=> array("Zoznam", "s"),
diff --git a/core/DataTable.php b/core/DataTable.php
index b4f100b7b9..c69e4fad69 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -485,6 +485,25 @@ class Piwik_DataTable
 		return $this->rows[$id];
 	}
 
+	/**
+	 * Returns a row that has the subtable ID matching the parameter
+	 * 
+	 * @param int $idSubTable
+	 * @return Piwik_DataTable_Row or false if not found
+	 */
+	public function getRowFromIdSubDataTable($idSubTable)
+	{
+		$idSubTable = (int)$idSubTable;
+		foreach($this->rows as $row)
+		{
+			if($row->getIdSubDataTable() === $idSubTable)
+			{
+				return $row;
+			}
+		}
+		return false;
+	}
+	
 	/**
 	 * Add a row to the table and rebuild the index if necessary
 	 * 
diff --git a/core/DataTable/Filter.php b/core/DataTable/Filter.php
index 64654905ce..b1545a500d 100644
--- a/core/DataTable/Filter.php
+++ b/core/DataTable/Filter.php
@@ -47,6 +47,7 @@ require_once "DataTable/Filter/ColumnCallbackAddMetadata.php";
 require_once "DataTable/Filter/ColumnCallbackReplace.php";
 require_once "DataTable/Filter/ColumnCallbackAddColumnPercentage.php";
 require_once "DataTable/Filter/MetadataCallbackAddMetadata.php";
+require_once "DataTable/Filter/MetadataCallbackReplace.php";
 require_once "DataTable/Filter/AddConstantMetadata.php";
 require_once "DataTable/Filter/Null.php";
 require_once "DataTable/Filter/ExcludeLowPopulation.php";
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php b/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
index 15ffab66c5..4ac7096344 100644
--- a/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumnPercentage.php
@@ -9,7 +9,7 @@
  * 
  * Usage:
  *   $nbVisits = Piwik_VisitsSummary_API::getVisits($idSite, $period, $date);
- *   $dataTable->queuefilter('ColumnCallbackAddColumnPercentage', array('nb_visits', 'nb_visits_percentage', $nbVisits, 1));
+ *   $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('nb_visits', 'nb_visits_percentage', $nbVisits, 1));
  *
  */
 class Piwik_DataTable_Filter_ColumnCallbackAddColumnPercentage extends Piwik_DataTable_Filter
diff --git a/core/DataTable/Filter/ColumnCallbackAddMetadata.php b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
index 36558c357f..0dd44f7003 100644
--- a/core/DataTable/Filter/ColumnCallbackAddMetadata.php
+++ b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
@@ -25,12 +25,14 @@ class Piwik_DataTable_Filter_ColumnCallbackAddMetadata extends Piwik_DataTable_F
 {
 	private $columnToRead;
 	private $functionToApply;
+	private $functionParameters;
 	private $metadataToAdd;
 	
-	public function __construct( $table, $columnToRead, $metadataToAdd, $functionToApply )
+	public function __construct( $table, $columnToRead, $metadataToAdd, $functionToApply, $functionParameters = null )
 	{
 		parent::__construct($table);
 		$this->functionToApply = $functionToApply;
+		$this->functionParameters = $functionParameters;
 		$this->columnToRead = $columnToRead;
 		$this->metadataToAdd = $metadataToAdd;
 		$this->filter();
@@ -41,7 +43,12 @@ class Piwik_DataTable_Filter_ColumnCallbackAddMetadata extends Piwik_DataTable_F
 		foreach($this->table->getRows() as $key => $row)
 		{
 			$oldValue = $row->getColumn($this->columnToRead);
-			$newValue = call_user_func( $this->functionToApply, $oldValue);
+			$parameters = array($oldValue);
+			if(!is_null($this->functionParameters))
+			{
+				$parameters = array_merge($parameters, $this->functionParameters);
+			}
+			$newValue = call_user_func_array( $this->functionToApply, $parameters);
 			$row->addMetadata($this->metadataToAdd, $newValue);
 		}
 	}
diff --git a/core/DataTable/Filter/ColumnCallbackReplace.php b/core/DataTable/Filter/ColumnCallbackReplace.php
index ec296cb50e..0fc435589e 100644
--- a/core/DataTable/Filter/ColumnCallbackReplace.php
+++ b/core/DataTable/Filter/ColumnCallbackReplace.php
@@ -34,14 +34,22 @@ class Piwik_DataTable_Filter_ColumnCallbackReplace extends Piwik_DataTable_Filte
 	{
 		foreach($this->table->getRows() as $key => $row)
 		{
-			$parameters = array($row->getColumn($this->columnToFilter));
+			$parameters = array($this->getElementToReplace($row, $this->columnToFilter));
 			if(!is_null($this->functionParameters))
 			{
 				$parameters = array_merge($parameters, $this->functionParameters);
 			}
 			$newValue = call_user_func_array( $this->functionToApply, $parameters);
-			$row->setColumn($this->columnToFilter, $newValue);
+			$this->setElementToReplace($row, $this->columnToFilter, $newValue);
 		}
 	}
+	
+	protected function setElementToReplace($row, $columnToFilter, $newValue)
+	{
+		$row->setColumn($columnToFilter, $newValue);
+	}
+	protected function getElementToReplace($row, $columnToFilter)
+	{
+		return $row->getColumn($columnToFilter);
+	}
 }
-
diff --git a/core/DataTable/Filter/MetadataCallbackReplace.php b/core/DataTable/Filter/MetadataCallbackReplace.php
new file mode 100644
index 0000000000..cf3d071b1a
--- /dev/null
+++ b/core/DataTable/Filter/MetadataCallbackReplace.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ * 
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
+ * @version $Id$
+ * 
+ * @package Piwik_DataTable
+ */
+
+/**
+ * Replace a metadata value with a new value resulting 
+ * from the function called with the metadata's value
+ * 
+ * @package Piwik_DataTable
+ * @subpackage Piwik_DataTable_Filter 
+ */
+class Piwik_DataTable_Filter_MetadataCallbackReplace extends Piwik_DataTable_Filter_ColumnCallbackReplace
+{
+	public function __construct( $table, $metadataToFilter, $functionToApply, $functionParameters = null )
+	{
+		parent::__construct($table, $metadataToFilter, $functionToApply, $functionParameters);
+	}
+
+	protected function setElementToReplace($row, $metadataToFilter, $newValue)
+	{
+		$row->setMetadata($metadataToFilter, $newValue);
+	}
+		
+	protected function getElementToReplace($row, $metadataToFilter)
+	{
+		return $row->getMetadata($metadataToFilter);
+	}
+}
diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php
index d29b70f5e1..de2a2c6831 100644
--- a/core/DataTable/Row.php
+++ b/core/DataTable/Row.php
@@ -276,7 +276,7 @@ class Piwik_DataTable_Row
 	{
 		$this->c[self::COLUMNS] = $columns;
 	}
-	
+
 	/**
 	 * Set the value $value to the column called $name.
 	 * 
@@ -288,6 +288,17 @@ class Piwik_DataTable_Row
 		$this->c[self::COLUMNS][$name] = $value;
 	}
 	
+	/**
+	 * Set the value $value to the metadata called $name.
+	 * 
+	 * @param string $name of the metadata to set
+	 * @param mixed $value of the metadata to set
+	 */
+	public function setMetadata($name, $value)
+	{
+		$this->c[self::METADATA][$name] = $value;
+	}
+	
 	/**
 	 * Add a new column to the row. If the column already exists, throws an exception
 	 * 
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index f9725828d1..38521703e4 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -47,7 +47,7 @@ class Piwik_Actions_API
 			$dataTable = $archive->getDataTable($name, $idSubtable);
 		}
 		$dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded));
-		$dataTable->queuefilter('ReplaceSummaryRowLabel');
+		$dataTable->queueFilter('ReplaceSummaryRowLabel');
 		return $dataTable;
 	}
 	
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index 638dd91e65..9a2a8c10d2 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -168,10 +168,10 @@ class Piwik_Live_Visitor
 	
 	function getSearchEngineIcon()
 	{
-		$searchEngine = $this->getSearchEngineUrl();
-		if( !is_null($searchEngine) )
+		$searchEngineUrl = $this->getSearchEngineUrl();
+		if( !is_null($searchEngineUrl) )
 		{
-			return Piwik_getSearchEngineLogoFromName($searchEngine);
+			return Piwik_getSearchEngineLogoFromUrl($searchEngineUrl);
 		}
 		return null;
 	}
diff --git a/plugins/Provider/API.php b/plugins/Provider/API.php
index 19b185c619..0e2c3f6dd9 100644
--- a/plugins/Provider/API.php
+++ b/plugins/Provider/API.php
@@ -34,9 +34,9 @@ class Piwik_Provider_API
 		$archive = Piwik_Archive::build($idSite, $period, $date );
 		$dataTable = $archive->getDataTable('Provider_hostnameExt');
 		$dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getHostnameUrl'));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getHostnameName'));
-		$dataTable->queuefilter('ReplaceColumnNames');
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getHostnameUrl'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getHostnameName'));
+		$dataTable->queueFilter('ReplaceColumnNames');
 		return $dataTable;
 	}
 }
diff --git a/plugins/Referers/API.php b/plugins/Referers/API.php
index 0f0affebf4..1ae24a1832 100644
--- a/plugins/Referers/API.php
+++ b/plugins/Referers/API.php
@@ -45,8 +45,8 @@ class Piwik_Referers_API
 			$dataTable = $archive->getDataTable($name, $idSubtable);
 		}
 		$dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
-		$dataTable->queuefilter('ReplaceColumnNames', array($expanded));
-		$dataTable->queuefilter('ReplaceSummaryRowLabel');
+		$dataTable->queueFilter('ReplaceColumnNames', array($expanded));
+		$dataTable->queueFilter('ReplaceSummaryRowLabel');
 		return $dataTable;
 	}
 	
@@ -57,7 +57,7 @@ class Piwik_Referers_API
 		{
 			$dataTable->filter('Pattern', array('label', $typeReferer));
 		}
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel'));
 		return $dataTable;
 	}
 	
@@ -70,22 +70,33 @@ class Piwik_Referers_API
 	function getSearchEnginesFromKeywordId($idSite, $period, $date, $idSubtable)
 	{
 		$dataTable = $this->getDataTable('Referers_searchEngineByKeyword',$idSite, $period, $date, $expanded = false, $idSubtable);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
-		$dataTable->queuefilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromName') );
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
+		$dataTable->queueFilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromUrl') );
+		
+		// get the keyword and create the URL to the search result page
+		$keywords = $this->getKeywords($idSite, $period, $date);
+		$keyword = $keywords->getRowFromIdSubDataTable($idSubtable)->getColumn('label');
+		$dataTable->queueFilter('MetadataCallbackReplace', array( 'url', 'Piwik_getSearchEngineUrlFromUrlAndKeyword', array($keyword)) );
 		return $dataTable;
 	}
 
 	function getSearchEngines($idSite, $period, $date, $expanded = false)
 	{
 		$dataTable = $this->getDataTable('Referers_keywordBySearchEngine',$idSite, $period, $date, $expanded);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
-		$dataTable->queuefilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromName') );
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromName') );
+		$dataTable->queueFilter('MetadataCallbackAddMetadata', array( 'url', 'logo', 'Piwik_getSearchEngineLogoFromUrl') );
 		return $dataTable;
 	}
 
 	function getKeywordsFromSearchEngineId($idSite, $period, $date, $idSubtable)
 	{
 		$dataTable = $this->getDataTable('Referers_keywordBySearchEngine',$idSite, $period, $date, $expanded = false, $idSubtable);
+		
+		// get the search engine and create the URL to the search result page
+		$searchEngines = $this->getSearchEngines($idSite, $period, $date);
+		$searchEngines->applyQueuedFilters();
+		$searchEngineUrl = $searchEngines->getRowFromIdSubDataTable($idSubtable)->getMetadata('url');
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', 'Piwik_getSearchEngineUrlFromKeywordAndUrl', array($searchEngineUrl)));
 		return $dataTable;
 	}
 
@@ -110,8 +121,8 @@ class Piwik_Referers_API
 	function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable)
 	{
 		$dataTable = $this->getDataTable('Referers_urlByWebsite',$idSite, $period, $date, $expanded = false, $idSubtable);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array( 'label', 'url', create_function('$label', 'return $label;')) );
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getPathFromUrl'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'url', create_function('$label', 'return $label;')) );
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getPathFromUrl'));
 		return $dataTable;
 	}
 
diff --git a/plugins/Referers/functions.php b/plugins/Referers/functions.php
index 1683d9bdbc..c0118ae837 100644
--- a/plugins/Referers/functions.php
+++ b/plugins/Referers/functions.php
@@ -23,14 +23,10 @@ function Piwik_getSearchEngineUrlFromName($name)
 	return $url;
 }
 
-
-function Piwik_getSearchEngineLogoFromName($url)
+function Piwik_getSearchEngineLogoFromUrl($url)
 {
-	require_once "DataFiles/SearchEngines.php";
-	$beginningUrl = strpos($url,'//') + 2;
-
 	$pathInPiwik = 'plugins/Referers/images/searchEngines/%s.png';
-	$pathWithCode = sprintf($pathInPiwik, substr($url,$beginningUrl));
+	$pathWithCode = sprintf($pathInPiwik, Piwik_getSearchEngineHostFromUrl($url));
 	$absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode;
 	if(file_exists($absolutePath))
 	{
@@ -39,6 +35,24 @@ function Piwik_getSearchEngineLogoFromName($url)
 	return sprintf($pathInPiwik, 'xx');
 }
 
+function Piwik_getSearchEngineHostFromUrl($url)
+{
+	return substr($url, strpos($url,'//') + 2);
+}
+
+function Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword)
+{
+	require_once "DataFiles/SearchEngines.php";
+	$keyword = urlencode($keyword);
+	$path = @$GLOBALS['Piwik_SearchEngines'][Piwik_getSearchEngineHostFromUrl($url)][2];
+	$path = str_replace("{k}", $keyword, $path);
+	return $url . '/' . $path;
+}
+
+function Piwik_getSearchEngineUrlFromKeywordAndUrl($keyword, $url)
+{
+	return Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword);
+}
 
 function Piwik_getRefererTypeLabel($label)
 {
diff --git a/plugins/Referers/images/searchEngines/www.rambler.ru.png b/plugins/Referers/images/searchEngines/nova.rambler.ru.png
similarity index 100%
rename from plugins/Referers/images/searchEngines/www.rambler.ru.png
rename to plugins/Referers/images/searchEngines/nova.rambler.ru.png
diff --git a/plugins/Referers/images/searchEngines/www.live.com.png b/plugins/Referers/images/searchEngines/search.live.com.png
similarity index 100%
rename from plugins/Referers/images/searchEngines/www.live.com.png
rename to plugins/Referers/images/searchEngines/search.live.com.png
diff --git a/plugins/Referers/images/searchEngines/search2.seznam.cz.png b/plugins/Referers/images/searchEngines/search2.seznam.cz.png
deleted file mode 100644
index 49ea24c48cc7eefe6c7dee9a2cd05e7250b37831..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 1149
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<w6XvX4FM)i?64!{5
z;QX|b^2DN42FH~Aq*MjZ+{E<Mpwz^a%EFVWHVh2R3;{kNuD`Z#|Fv)5dku}RRaHON
ztod3~^XKNxA4`^eYj6J)6!g8X?+q*KJ8|)^m6e}-ec$r&z1P(I93TJD-u|PT+mD3{
zzm}H%ee~#CQ`1{c&i86+KjzPW%fazZQu4>Vd7oor-wFwRj*fmOEBouvq0f<#zfYX_
zK55cBMa6g0(r<ZrKZS+8VPyP$?%b!)&|e1*e66c{uc!Bhf#LuE|3*ee#>U1bCMKq)
zre<bl=H})W78aJ4mR43)*4EZGHa51lwsv-Q_V)G;4i1ivj!sTa&d$y*E-tRFu5NB_
z?(Xg$9v+^ao?c#F-rn9mK0dy_zJ7jw{{H>}0Re%5fk8n*!NI{HAt9lmp<!WR;o;#C
z5fPD*kx@}m(b3T{F)^{Rv2k&6@$vBq2?>dbiAhOG$;rtnDJiL`scC6x>FMbi85x<G
znORv`+1c4SIXStxxp{ec`T6+;1qFqLg+)b0#l^)XB_*Y$rDbJh<>loS6&010l~q+$
z)z#HCH8r)hwRLrM_4V})4GoQrjZIBW&CSg%EiJ9Bt!-^>?d|Oy9UYyWon2jB-QC?i
zJw3g>y?uRs{r&wDCQO((apI&&lO|7|JY~w1sZ*y;n>KCw^yxEZ%$PZI=B!z>X3w5I
zXU?3tbLY;RH*fy@`3n{-Sh#TEqD6}qFJ8Q4$&#f@mo8hjZ29u#D^{#nxpL*IRjXF7
zUcF|`nzd`!u3NWm{rdGAHf-3qapR^<n>KIWyk*Olty{Nl+qP}{_U$`%?AW<;=dNA5
zcJJQ3XV0F!d-v|!w{QRc{Ra*lIC${jp+kobA3l8K$dRK*j~+XA?D+BHCr+F=dGh3`
zQ>RX!K7HoQnX_llo;!E${Q2`2E?l^H@#3XRmo8tveC5iOt5>gHyLRpR_3Jlo+_-u3
z=B-<|Zr{Fr=gytGckkZ2ckllF`wt#Gc=+()qeqV(KYsk=$&;r~pFVr`?D_NOFJ8QO
z`SRtfSFc{be*Nano40S@zI*rX{rmSHK79E2@#CjYpFV&7{N>A+uV24@`}Xbo_wPS`
z{P_9v=dWMCe*gac=g*(NfB*jb_YauTmh9yd2WA+fk|4ieAeS`2z#A~*Jy7>iPZ!6K
zid#trEX;+ajvR*t6dEKPSlX8WW0uW0f<d9df$5CHx<;P)av)9v(;J6%Hu~Hl+zt$k
ztPzb&r`ik{1RNX~*>sti<sxS=I50GxNf2OICD`<ekwamFZqgzj2Sq<O0coJI3$$)p
z{^aEmxxvHB$kf4@CpCLcBje<SiVh5lEIhnBd6w`3#W^AyE;ii00CdI#mMK?MBEVvL
z2Ue_Hv6y!WP+IGN!kY7piHs})9uBUdUM}LxgcJ@iSe(#ec--_0RGu(+y85}Sb4q9e
E063OZkpKVy

diff --git a/plugins/Referers/images/searchEngines/web.volny.cz.png b/plugins/Referers/images/searchEngines/volny.zlatestranky.cz.png
similarity index 100%
rename from plugins/Referers/images/searchEngines/web.volny.cz.png
rename to plugins/Referers/images/searchEngines/volny.zlatestranky.cz.png
diff --git a/plugins/UserCountry/API.php b/plugins/UserCountry/API.php
index 976f649344..ba54bcfab8 100644
--- a/plugins/UserCountry/API.php
+++ b/plugins/UserCountry/API.php
@@ -35,8 +35,8 @@ class Piwik_UserCountry_API
 		$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'code', create_function('$label', 'return $label;')));
 		$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getFlagFromCode'));
 		$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_CountryTranslate'));
-		$dataTable->queuefilter('AddConstantMetadata', array('logoWidth', 18));
-		$dataTable->queuefilter('AddConstantMetadata', array('logoHeight', 12));
+		$dataTable->queueFilter('AddConstantMetadata', array('logoWidth', 18));
+		$dataTable->queueFilter('AddConstantMetadata', array('logoHeight', 12));
 		return $dataTable;
 	}
 	
@@ -44,7 +44,7 @@ class Piwik_UserCountry_API
 	{
 		$dataTable = $this->getDataTable('UserCountry_continent', $idSite, $period, $date);
 		$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_ContinentTranslate'));
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'code', create_function('$label', 'return $label;')));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'code', create_function('$label', 'return $label;')));
 		return $dataTable;
 	}
 	
@@ -54,7 +54,7 @@ class Piwik_UserCountry_API
 		$archive = Piwik_Archive::build($idSite, $period, $date );
 		$dataTable = $archive->getDataTable($name);
 		$dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
-		$dataTable->queuefilter('ReplaceColumnNames');
+		$dataTable->queueFilter('ReplaceColumnNames');
 		return $dataTable;
 	}
 	
diff --git a/plugins/UserSettings/API.php b/plugins/UserSettings/API.php
index 9b51464a70..2fe3717054 100644
--- a/plugins/UserSettings/API.php
+++ b/plugins/UserSettings/API.php
@@ -33,7 +33,7 @@ class Piwik_UserSettings_API
 		$archive = Piwik_Archive::build($idSite, $period, $date );
 		$dataTable = $archive->getDataTable($name);
 		$dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
-		$dataTable->queuefilter('ReplaceColumnNames');
+		$dataTable->queueFilter('ReplaceColumnNames');
 		return $dataTable;
 	}
 	public function getResolution( $idSite, $period, $date )
@@ -45,49 +45,49 @@ class Piwik_UserSettings_API
 	public function getConfiguration( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_configuration', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
 		return $dataTable;
 	}
 
 	public function getOS( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_os', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOSLogo'));
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array( 'label', 'shortLabel', 'Piwik_getOSShortLabel') );
-		$dataTable->queuefilter('ColumnCallbackReplace', array( 'label', 'Piwik_getOSLabel') );
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOSLogo'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array( 'label', 'shortLabel', 'Piwik_getOSShortLabel') );
+		$dataTable->queueFilter('ColumnCallbackReplace', array( 'label', 'Piwik_getOSLabel') );
 		return $dataTable;
 	}
 		
 	public function getBrowser( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
 		return $dataTable;
 	}
 	
 	public function getBrowserType( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'ucfirst'));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'ucfirst'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
 		return $dataTable;
 	}
 	
 	public function getWideScreen( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_wideScreen', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getScreensLogo'));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'ucfirst'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getScreensLogo'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
 		return $dataTable;
 	}
 	
 	public function getPlugin( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('UserSettings_plugin', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getPluginsLogo'));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'ucfirst'));
+		$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getPluginsLogo'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
 		return $dataTable;
 	}	
 }
diff --git a/plugins/VisitTime/API.php b/plugins/VisitTime/API.php
index 0e88059bef..6290a60306 100644
--- a/plugins/VisitTime/API.php
+++ b/plugins/VisitTime/API.php
@@ -33,8 +33,8 @@ class Piwik_VisitTime_API
 		$archive = Piwik_Archive::build($idSite, $period, $date );
 		$dataTable = $archive->getDataTable($name);
 		$dataTable->filter('Sort', array('label', 'asc', true));
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
-		$dataTable->queuefilter('ReplaceColumnNames');
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
+		$dataTable->queueFilter('ReplaceColumnNames');
 		return $dataTable;
 	}
 	
diff --git a/plugins/VisitorInterest/API.php b/plugins/VisitorInterest/API.php
index 9d7be0264e..3a95380a66 100644
--- a/plugins/VisitorInterest/API.php
+++ b/plugins/VisitorInterest/API.php
@@ -32,22 +32,22 @@ class Piwik_VisitorInterest_API
 		$archive = Piwik_Archive::build($idSite, $period, $date );
 		$dataTable = $archive->getDataTable($name);
 		$dataTable->filter('Sort',array(Piwik_Archive::INDEX_NB_VISITS));
-		$dataTable->queuefilter('ReplaceColumnNames');
-		$dataTable->queuefilter('Sort', array('label', 'asc', true));
+		$dataTable->queueFilter('ReplaceColumnNames');
+		$dataTable->queueFilter('Sort', array('label', 'asc', true));
 		return $dataTable;
 	}
 	
 	public function getNumberOfVisitsPerVisitDuration( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('VisitorInterest_timeGap', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getDurationLabel'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getDurationLabel'));
 		return $dataTable;
 	}
 
 	public function getNumberOfVisitsPerPage( $idSite, $period, $date )
 	{
 		$dataTable = $this->getDataTable('VisitorInterest_pageGap', $idSite, $period, $date);
-		$dataTable->queuefilter('ColumnCallbackReplace', array('label', 'Piwik_getPageGapLabel'));
+		$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getPageGapLabel'));
 		return $dataTable;
 	}
 }
diff --git a/tests/core/Common.test.php b/tests/core/Common.test.php
index 574e5fe210..1f7c3d8623 100755
--- a/tests/core/Common.test.php
+++ b/tests/core/Common.test.php
@@ -585,6 +585,18 @@ class Test_Piwik_Common extends UnitTestCase
 		}
 	}
 	
+	public function test_SearchEngines_areDefinedCorrectly()
+	{
+		require_once "DataFiles/SearchEngines.php";
+		foreach($GLOBALS['Piwik_SearchEngines'] as $host => $info)
+		{
+			if(isset($info[2]) && $info[2] !== false)
+			{
+				$this->assertTrue(strrpos($info[2], "{k}") !== false, "$host search URL is not defined correctly, found {$info[2]} but must contain the macro {k}");
+			}
+		}
+	}
+	
 	public function test_extractSearchEngineInformationFromUrl()
 	{
 		$urls = array(
diff --git a/tests/core/DataTable.test.php b/tests/core/DataTable.test.php
index b9f679acd4..878ca2e5ef 100644
--- a/tests/core/DataTable.test.php
+++ b/tests/core/DataTable.test.php
@@ -76,6 +76,22 @@ class Test_Piwik_DataTable extends UnitTestCase
 		$this->assertTrue($table->getLastRow(), $table->getRowFromId($rowsCount-2));
 	}
 	
+	public function test_getRowFromIdSubDataTable()
+	{
+		$table1 = $this->getDataTable1ForTest();
+		$idTable1 = $table1->getId();
+		$table2 = $this->getDataTable2ForTest();
+		$this->assertEqual($table2->getRowFromIdSubDataTable($idTable1), false);
+		
+		$table2->getFirstRow()->addSubtable($table1);
+		$this->assertEqual($table2->getRowFromIdSubDataTable($idTable1), $table2->getFirstRow());
+		
+		$table3 = $this->getDataTable1ForTest();
+		$idTable3 = $table3->getId();
+		$table2->getLastRow()->addSubtable($table3);
+		$this->assertEqual($table2->getRowFromIdSubDataTable($idTable3), $table2->getLastRow());
+	}
+	
 	/**
 	 * we test the count rows and the count rows recursive version
 	 * on a Simple array (1 level only)
-- 
GitLab