From d5529e73e8c0ff4f0afe6428d6acba0b23f3c5de Mon Sep 17 00:00:00 2001
From: BeezyT <timo@ezdesign.de>
Date: Fri, 16 Sep 2011 06:28:10 +0000
Subject: [PATCH] Refs #2662 Actions.get* for date range

git-svn-id: http://dev.piwik.org/svn/trunk@5172 59fd770c-687e-43c8-a1e3-f5a4ff64c105
---
 core/DataTable.php                            |  19 +++
 core/DataTable/Array.php                      |  22 ++++
 plugins/Actions/API.php                       | 112 +++++++++++-------
 ..._PeriodIsLast__Actions.getDownload_day.xml |  12 +-
 ...PeriodIsLast__Actions.getDownload_week.xml |  12 +-
 ...t_PeriodIsLast__Actions.getOutlink_day.xml |  12 +-
 ..._PeriodIsLast__Actions.getOutlink_week.xml |  12 +-
 ...PeriodIsLast__Actions.getPageTitle_day.xml |  12 +-
 ...eriodIsLast__Actions.getPageTitle_week.xml |  12 +-
 ...t_PeriodIsLast__Actions.getPageUrl_day.xml |  12 +-
 ..._PeriodIsLast__Actions.getPageUrl_week.xml |  12 +-
 11 files changed, 184 insertions(+), 65 deletions(-)

diff --git a/core/DataTable.php b/core/DataTable.php
index 023735212a..0c44b924e7 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -450,6 +450,25 @@ class Piwik_DataTable
 		return $this->rows[$this->rowsIndexByLabel[$label]];
 	}
 
+	/**
+	 * Returns a Piwik_DataTable that has only the one column that matches $label.
+	 * If no matches are found, an empty data table is returned.
+	 *
+	 * @param string $label Value of the column 'label' to search for
+	 * @return Piwik_DataTable
+	 */
+	public function getFilteredTableFromLabel($label)
+	{
+		$newTable = new Piwik_DataTable;
+		$row = $this->getRowFromLabel($label);
+		if ($row !== false)
+		{
+			$newTable->addRow($row);
+			$newTable->queuedFilters = $this->queuedFilters;
+		}
+		return $newTable;
+	}
+
 	/**
 	 * Rebuilds the index used to lookup a row by label
 	 */
diff --git a/core/DataTable/Array.php b/core/DataTable/Array.php
index bfc7d36591..a32288fada 100644
--- a/core/DataTable/Array.php
+++ b/core/DataTable/Array.php
@@ -176,4 +176,26 @@ class Piwik_DataTable_Array
 			$table->deleteColumns($columns);
 		}
 	}
+
+	/**
+	 * Returns a Piwik_DataTable_Array whose sub tables are filtered by $label
+	 * @see Piwik_DataTable::getFilteredTableFromLabel
+	 *
+	 * @param string $label Value of the column 'label' to search for
+	 * @return Piwik_DataTable_Array
+	 */
+	public function getFilteredTableFromLabel($label)
+	{
+		$newTableArray = new Piwik_DataTable_Array;
+		$newTableArray->metadata = $this->metadata;
+
+		foreach ($this->array as $subTableLabel => $subTable)
+		{
+			$subTable = $subTable->getFilteredTableFromLabel($label);
+			$newTableArray->addTable($subTable, $subTableLabel);
+		}
+
+		return $newTableArray;
+	}
+
 }
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 9cceb05915..d028b4026d 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -116,69 +116,99 @@ class Piwik_Actions_API
 	 * Will search in the DataTable for a Label matching the searched string
 	 * and return only the matching row, or an empty datatable
 	 */
-	protected function getFilterPageDatatableSearch( $callBackParameters, $search, $actionType, $table = false, $searchTree = false, $searchCurrentLevel = 0 )
+	protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false,
+			$searchTree = false)
 	{
-		if($table === false)
-		{
-			$table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
-		}
-		if($searchTree === false)
+		if ($searchTree === false)
 		{
+			// build the query parts that are searched inside the tree
     		if($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME)
     		{
     			$searchedString = Piwik_Common::unsanitizeInputValue($search);
     		}
     		else
     		{
-    			$searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite = $callBackParameters[1]);
+				$idSite = $callBackParameters[1];
+    			$searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite);
     		}
 			$searchTree = Piwik_Actions::getActionExplodedNames($searchedString, $actionType);
 		}
-		if(!($table instanceof Piwik_DataTable))
-		{
-			throw new Exception("For this API function, date=lastN or date=previousM is not supported");
-		}
-		$rows = $table->getRows();
-		$labelSearch = $searchTree[$searchCurrentLevel];
-		$isEndSearch = ((count($searchTree)-1) == $searchCurrentLevel);
-		foreach($rows as $key => $row)
+
+		if ($table === false)
 		{
-			$found = false;
-			// Found a match at this level
-			$label = $row->getColumn('label');
-			if($label === $labelSearch)
+			// fetch the data table
+			$table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
+			
+			if ($table instanceof Piwik_DataTable_Array)
 			{
-				// Is this the end of the search tree? then we found the requested row
-				if($isEndSearch)
-				{
-//					var_dump($label); var_dump($labelSearch); exit;
-					$table = new Piwik_DataTable();
-					$table->addRow($row);
-					return $table;
-				}
+				// search an array of tables, e.g. when using date=last30
+				// note that if the root is an array, we filter all children
+				// if an array occurs inside the nested table, we only look for the first match (see below)
+				$newTableArray = new Piwik_DataTable_Array;
+				$newTableArray->metadata = $table->metadata;
 				
-				// If we still need to search deeper, call search 
-				$idSubTable = $row->getIdSubDataTable();
-				// Update the idSubtable in the callback parameter list, to fetch this subtable from the archive
-				$callBackParameters[6] = $idSubTable;
-				$subTable = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
-				$found = $this->getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $subTable, $searchTree, $searchCurrentLevel+1);
-				if($found)
+				foreach ($table->getArray() as $label => $subTable)
 				{
-					return $found;
+					$subTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree);
+					$newTableArray->addTable($subTable, $label);
 				}
+				
+				return $newTableArray;
 			}
-			if(!$found)
+			
+		}
+
+		return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
+	}
+
+	protected function doFilterPageDatatableSearch($callBackParameters, $table, $searchTree)
+	{
+		// filter a data table array
+		if ($table instanceof Piwik_DataTable_Array)
+		{
+			foreach ($table->getArray() as $subTable)
 			{
-				$table->deleteRow($key);
+				$filteredSubTable = $this->doFilterPageDatatableSearch($callBackParameters,	$subTable, $searchTree);
+
+				if ($filteredSubTable->getRowsCount() > 0)
+				{
+					// match found in a sub table, return and stop searching the others
+					return $filteredSubTable;
+				}
 			}
+
+			// nothing found in all sub tables
+			return new Piwik_DataTable;
 		}
-		// Case the DataTable was searched but nothing was found, @see getFilterPageDatatableSearch()
-		if($searchCurrentLevel == 0)
+
+		// filter regular data table
+		if ($table instanceof Piwik_DataTable)
 		{
-			return new Piwik_DataTable;
+			// search for the first part of the tree search
+			$search = array_shift($searchTree);
+			$row = $table->getRowFromLabel($search);
+			if ($row === false)
+			{
+				// not found
+				return new Piwik_DataTable;
+			}
+
+			// end of tree search reached
+			if (count($searchTree) == 0)
+			{
+				$table = new Piwik_DataTable();
+				$table->addRow($row);
+				return $table;
+			}
+
+			// match found on this level and more levels remaining: go deeper
+			$idSubTable = $row->getIdSubDataTable();
+			$callBackParameters[6] = $idSubTable;
+			$table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters);
+			return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
 		}
-		return false;
+
+		throw new Exception("For this API function, DataTable ".get_class($table)." is not supported");
 	}
 	
 	/**
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_day.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_day.xml
index bb2fba3cf8..1c598ec49c 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_day.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_day.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2009-01-04" />
+	<result defaultKeyName="2009-01-05" />
+	<result defaultKeyName="2009-01-06" />
+	<result defaultKeyName="2009-01-07" />
+	<result defaultKeyName="2009-01-08" />
+	<result defaultKeyName="2009-01-09" />
+	<result defaultKeyName="2009-01-10" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_week.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_week.xml
index bb2fba3cf8..7fd44d3e50 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_week.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getDownload_week.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2008-12-29 to 2009-01-04" />
+	<result defaultKeyName="2009-01-05 to 2009-01-11" />
+	<result defaultKeyName="2009-01-12 to 2009-01-18" />
+	<result defaultKeyName="2009-01-19 to 2009-01-25" />
+	<result defaultKeyName="2009-01-26 to 2009-02-01" />
+	<result defaultKeyName="2009-02-02 to 2009-02-08" />
+	<result defaultKeyName="2009-02-09 to 2009-02-15" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_day.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_day.xml
index bb2fba3cf8..1c598ec49c 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_day.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_day.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2009-01-04" />
+	<result defaultKeyName="2009-01-05" />
+	<result defaultKeyName="2009-01-06" />
+	<result defaultKeyName="2009-01-07" />
+	<result defaultKeyName="2009-01-08" />
+	<result defaultKeyName="2009-01-09" />
+	<result defaultKeyName="2009-01-10" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_week.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_week.xml
index bb2fba3cf8..7fd44d3e50 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_week.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getOutlink_week.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2008-12-29 to 2009-01-04" />
+	<result defaultKeyName="2009-01-05 to 2009-01-11" />
+	<result defaultKeyName="2009-01-12 to 2009-01-18" />
+	<result defaultKeyName="2009-01-19 to 2009-01-25" />
+	<result defaultKeyName="2009-01-26 to 2009-02-01" />
+	<result defaultKeyName="2009-02-02 to 2009-02-08" />
+	<result defaultKeyName="2009-02-09 to 2009-02-15" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_day.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_day.xml
index bb2fba3cf8..1c598ec49c 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_day.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_day.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2009-01-04" />
+	<result defaultKeyName="2009-01-05" />
+	<result defaultKeyName="2009-01-06" />
+	<result defaultKeyName="2009-01-07" />
+	<result defaultKeyName="2009-01-08" />
+	<result defaultKeyName="2009-01-09" />
+	<result defaultKeyName="2009-01-10" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_week.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_week.xml
index bb2fba3cf8..7fd44d3e50 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_week.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageTitle_week.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2008-12-29 to 2009-01-04" />
+	<result defaultKeyName="2009-01-05 to 2009-01-11" />
+	<result defaultKeyName="2009-01-12 to 2009-01-18" />
+	<result defaultKeyName="2009-01-19 to 2009-01-25" />
+	<result defaultKeyName="2009-01-26 to 2009-02-01" />
+	<result defaultKeyName="2009-02-02 to 2009-02-08" />
+	<result defaultKeyName="2009-02-09 to 2009-02-15" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_day.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_day.xml
index bb2fba3cf8..1c598ec49c 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_day.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_day.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2009-01-04" />
+	<result defaultKeyName="2009-01-05" />
+	<result defaultKeyName="2009-01-06" />
+	<result defaultKeyName="2009-01-07" />
+	<result defaultKeyName="2009-01-08" />
+	<result defaultKeyName="2009-01-09" />
+	<result defaultKeyName="2009-01-10" />
+</results>
\ No newline at end of file
diff --git a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_week.xml b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_week.xml
index bb2fba3cf8..7fd44d3e50 100644
--- a/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_week.xml
+++ b/tests/integration/expected/test_noVisit_PeriodIsLast__Actions.getPageUrl_week.xml
@@ -1,4 +1,10 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<result>
-	<error message="For this API function, date=lastN or date=previousM is not supported" />
-</result>
\ No newline at end of file
+<results>
+	<result defaultKeyName="2008-12-29 to 2009-01-04" />
+	<result defaultKeyName="2009-01-05 to 2009-01-11" />
+	<result defaultKeyName="2009-01-12 to 2009-01-18" />
+	<result defaultKeyName="2009-01-19 to 2009-01-25" />
+	<result defaultKeyName="2009-01-26 to 2009-02-01" />
+	<result defaultKeyName="2009-02-02 to 2009-02-08" />
+	<result defaultKeyName="2009-02-09 to 2009-02-15" />
+</results>
\ No newline at end of file
-- 
GitLab