Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattpiwik <matthieu.aubry@gmail.com>2012-09-26 12:40:42 +0400
committermattpiwik <matthieu.aubry@gmail.com>2012-09-26 12:40:42 +0400
commitc1bd36b2aa1b80e6af95e4e39dbb1d0ceccd59b2 (patch)
tree3e3733030aa52f08123a97c9f307d60995732af0
parent443934a802cc5dc94b278430b29b2e73dac67a5a (diff)
Refs #3330 Refactoring of the Actions.php and moving code to: Archiving and ArchivingHelper
git-svn-id: http://dev.piwik.org/svn/trunk@7065 59fd770c-687e-43c8-a1e3-f5a4ff64c105
-rw-r--r--core/DataTable.php25
-rw-r--r--plugins/Actions/API.php3
-rw-r--r--plugins/Actions/Actions.php831
-rw-r--r--plugins/Actions/Archiving.php425
-rw-r--r--plugins/Actions/ArchivingHelper.php533
-rw-r--r--plugins/CoreHome/templates/header.tpl2
-rw-r--r--plugins/Transitions/API.php3
-rwxr-xr-xtests/PHPUnit/Integration/BlobReportLimitingTest.php2
-rw-r--r--tests/PHPUnit/Plugins/ActionsTest.php12
9 files changed, 980 insertions, 856 deletions
diff --git a/core/DataTable.php b/core/DataTable.php
index a2da7075ec..b697fad2c9 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -140,13 +140,12 @@ class Piwik_DataTable
{
/** Name for metadata that describes when a report was archived. */
const ARCHIVED_DATE_METADATA_NAME = 'archived_date';
-
+ const MAX_DEPTH_DEFAULT = 15;
+
/**
- * Maximum nesting level
- *
- * @var int
+ * Maximum nesting level.
*/
- static private $maximumDepthLevelAllowed = 15;
+ static private $maximumDepthLevelAllowed = self::MAX_DEPTH_DEFAULT;
/**
* Array of Piwik_DataTable_Row
@@ -1006,7 +1005,7 @@ class Piwik_DataTable
if($depth > self::$maximumDepthLevelAllowed)
{
$depth = 0;
- throw new Exception("Maximum recursion level of ".self::$maximumDepthLevelAllowed. " reached. You have probably set a DataTable_Row with an associated DataTable which belongs already to its parent hierarchy.");
+ throw new Exception("Maximum recursion level of ".self::$maximumDepthLevelAllowed. " reached. Maybe you have set a DataTable_Row with an associated DataTable belonging already to one of its parent tables?");
}
if( !is_null($maximumRowsInDataTable) )
{
@@ -1303,17 +1302,6 @@ class Piwik_DataTable
}
/**
- * Gets the maximum nesting level for datatables.
- *
- * @return int
- */
- static public function getMaximumDepthLevelAllowed()
- {
- return self::$maximumDepthLevelAllowed;
- }
-
-
- /**
* Sets the maximum nesting level to at least a certain value. If the current value is
* greater than the supplied level, the maximum nesting level is not changed.
*
@@ -1322,6 +1310,9 @@ class Piwik_DataTable
static public function setMaximumDepthLevelAllowedAtLeast( $atLeastLevel )
{
self::$maximumDepthLevelAllowed = max($atLeastLevel, self::$maximumDepthLevelAllowed);
+ if(self::$maximumDepthLevelAllowed < 1) {
+ self::$maximumDepthLevelAllowed = 1;
+ }
}
/**
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 35b06710a2..3587c3f70c 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -246,7 +246,8 @@ class Piwik_Actions_API
$searchedString = $search;
}
}
- $searchTree = Piwik_Actions::getActionExplodedNames($searchedString, $actionType);
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $searchTree = Piwik_Actions_ArchivingHelper::getActionExplodedNames($searchedString, $actionType);
}
if ($table === false)
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 9192858ec7..cae4d15401 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -19,17 +19,6 @@
*/
class Piwik_Actions extends Piwik_Plugin
{
- static protected $actionUrlCategoryDelimiter = null;
- static protected $actionTitleCategoryDelimiter = null;
- static protected $defaultActionName = null;
- static protected $defaultActionNameWhenNotDefined = null;
- static protected $defaultActionUrlWhenNotDefined = null;
- protected $maximumRowsInDataTableLevelZero;
- protected $maximumRowsInSubDataTable;
- protected $columnToSortByBeforeTruncation;
-
- const OTHERS_ROW_KEY = '';
-
public function getInformation()
{
$info = array(
@@ -129,7 +118,7 @@ class Piwik_Actions extends Piwik_Plugin
* @throws Exception
* @return array|int|string
*/
- function getIdActionFromSegment($string, $sqlField, $matchType='==')
+ public function getIdActionFromSegment($string, $sqlField, $matchType='==')
{
// Field is visit_*_idaction_url or visit_*_idaction_name
$actionType = strpos($sqlField, '_name') === false
@@ -427,47 +416,6 @@ class Piwik_Actions extends Piwik_Plugin
Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 5);
Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 6);
}
-
- static protected $invalidSummedColumnNameToRenamedNameForPeriodArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
- );
-
- protected static $invalidSummedColumnNameToDeleteFromDayArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
- );
-
- public function __construct()
- {
- $this->reloadConfig();
- }
-
- public function reloadConfig()
- {
- // for BC, we read the old style delimiter first (see #1067)
- $actionDelimiter = @Piwik_Config::getInstance()->General['action_category_delimiter'];
- if(empty($actionDelimiter))
- {
- self::$actionUrlCategoryDelimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
- self::$actionTitleCategoryDelimiter = Piwik_Config::getInstance()->General['action_title_category_delimiter'];
- }
- else
- {
- self::$actionUrlCategoryDelimiter = self::$actionTitleCategoryDelimiter = $actionDelimiter;
- }
-
- self::$defaultActionName = Piwik_Config::getInstance()->General['action_default_name'];
- $this->columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
- $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
- $rankingQueryLimit = Piwik_Config::getInstance()->General['archiving_ranking_query_row_limit'];
-
- // Piwik_DataTable::MAXIMUM_DEPTH_LEVEL_ALLOWED must be greater than the category level limit
- Piwik_DataTable::setMaximumDepthLevelAllowedAtLeast(self::getSubCategoryLevelLimit() + 1);
- }
/**
* @param Piwik_Event_Notification $notification notification object
@@ -478,23 +426,9 @@ class Piwik_Actions extends Piwik_Plugin
$archiveProcessing = $notification->getNotificationObject();
if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'Actions_actions',
- 'Actions_downloads',
- 'Actions_outlink',
- 'Actions_actions_url',
- );
- $archiveProcessing->archiveDataTable($dataTableToSum, self::$invalidSummedColumnNameToRenamedNameForPeriodArchive, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
-
- $archiveProcessing->archiveNumericValuesSum(array(
- 'Actions_nb_pageviews',
- 'Actions_nb_uniq_pageviews',
- 'Actions_nb_downloads',
- 'Actions_nb_uniq_downloads',
- 'Actions_nb_outlinks',
- 'Actions_nb_uniq_outlinks'
- ));
+
+ $actionsArchiving = new Piwik_Actions_Archiving;
+ return $actionsArchiving->archivePeriod($archiveProcessing);
}
/**
@@ -511,762 +445,9 @@ class Piwik_Actions extends Piwik_Plugin
$archiveProcessing = $notification->getNotificationObject();
if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->actionsTablesByType = array(
- Piwik_Tracker_Action::TYPE_ACTION_URL => array(),
- Piwik_Tracker_Action::TYPE_DOWNLOAD => array(),
- Piwik_Tracker_Action::TYPE_OUTLINK => array(),
- Piwik_Tracker_Action::TYPE_ACTION_NAME => array(),
- );
-
- // This row is used in the case where an action is know as an exit_action
- // but this action was not properly recorded when it was hit in the first place
- // so we add this fake row information to make sure there is a nb_hits, etc. column for every action
- $this->defaultRow = new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- Piwik_Archive::INDEX_NB_VISITS => 1,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 1,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 1,
- )));
-
- $rankingQueryLimit = self::getRankingQueryLimit();
- $rankingQuery = false;
-
- /*
- * Page URLs and Page names, general stats
- */
-
- $select = "log_action.name,
- log_action.type,
- log_action.idaction,
- log_action.url_prefix,
- count(distinct log_link_visit_action.idvisit) as `". Piwik_Archive::INDEX_NB_VISITS ."`,
- count(distinct log_link_visit_action.idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`,
- count(*) as `". Piwik_Archive::INDEX_PAGE_NB_HITS ."`";
-
- $from = array(
- "log_link_visit_action",
- array(
- "table" => "log_action",
- "joinOn" => "log_link_visit_action.%s = log_action.idaction"
- )
- );
-
- $where = "log_link_visit_action.server_time >= ?
- AND log_link_visit_action.server_time <= ?
- AND log_link_visit_action.idsite = ?
- AND log_link_visit_action.%s IS NOT NULL";
-
- $groupBy = "log_action.idaction";
- $orderBy = "`". Piwik_Archive::INDEX_PAGE_NB_HITS ."` DESC, name ASC";
-
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn(array('idaction', 'name'));
- $rankingQuery->addColumn(array('url_prefix', Piwik_Archive::INDEX_NB_UNIQ_VISITORS));
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_NB_HITS, Piwik_Archive::INDEX_NB_VISITS), 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
- }
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name", $archiveProcessing, $rankingQuery);
-
- /*
- * Entry actions for Page URLs and Page names
- */
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = 'log_action.type, log_action.name,';
- $from = array(
- "log_visit",
- array(
- "table" => "log_action",
- "joinOn" => "log_visit.%s = log_action.idaction"
- )
- );
- $orderBy = "`".Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS."` DESC, log_action.name ASC";
- }
- else
- {
- $extraSelects = false;
- $from = "log_visit";
- $orderBy = false;
- }
-
- $select = "log_visit.%s as idaction, $extraSelects
- count(distinct log_visit.idvisitor) as `". Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS ."`,
- count(*) as `". Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS ."`,
- sum(log_visit.visit_total_actions) as `". Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS ."`,
- sum(log_visit.visit_total_time) as `". Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH ."`,
- sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `". Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT ."`";
-
- $where = "log_visit.visit_last_action_time >= ?
- AND log_visit.visit_last_action_time <= ?
- AND log_visit.idsite = ?
- AND log_visit.%s > 0";
-
- $groupBy = "log_visit.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_url", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_name", $archiveProcessing, $rankingQuery);
-
- /*
- * Exit actions
- */
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = 'log_action.type, log_action.name,';
- $from = array(
- "log_visit",
- array(
- "table" => "log_action",
- "joinOn" => "log_visit.%s = log_action.idaction"
- )
- );
- $orderBy = "`".Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS."` DESC, log_action.name ASC";
- }
- else
- {
- $extraSelects = false;
- $from = "log_visit";
- $orderBy = false;
- }
-
- $select = "log_visit.%s as idaction, $extraSelects
- count(distinct log_visit.idvisitor) as `". Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS ."`,
- count(*) as `". Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS ."`";
-
- $where = "log_visit.visit_last_action_time >= ?
- AND log_visit.visit_last_action_time <= ?
- AND log_visit.idsite = ?
- AND log_visit.%s > 0";
-
- $groupBy = "log_visit.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_url", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_name", $archiveProcessing, $rankingQuery);
-
- /*
- * Time per action
- */
- if ($rankingQueryLimit > 0)
- {
- $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
- $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
- $rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
-
- $extraSelects = "log_action.type, log_action.name, count(*) as `".Piwik_Archive::INDEX_PAGE_NB_HITS."`,";
- $from = array(
- "log_link_visit_action",
- array(
- "table" => "log_action",
- "joinOn" => "log_link_visit_action.%s = log_action.idaction"
- )
- );
- $orderBy = "`".Piwik_Archive::INDEX_PAGE_NB_HITS."` DESC, log_action.name ASC";
- }
- else
- {
- $extraSelects = false;
- $from = "log_link_visit_action";
- $orderBy = false;
- }
-
- $select = "log_link_visit_action.%s as idaction, $extraSelects
- sum(log_link_visit_action.time_spent_ref_action) as `".Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT."`";
-
- $where = "log_link_visit_action.server_time >= ?
- AND log_link_visit_action.server_time <= ?
- AND log_link_visit_action.idsite = ?
- AND log_link_visit_action.time_spent_ref_action > 0
- AND log_link_visit_action.%s > 0";
-
- $groupBy = "log_link_visit_action.%s, idaction";
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url_ref", $archiveProcessing, $rankingQuery);
-
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name_ref", $archiveProcessing, $rankingQuery);
-
- // Empty static cache
- self::$cacheParsedAction = array();
-
- // Record the final datasets
- $this->archiveDayRecordInDatabase($archiveProcessing);
- }
-
- /**
- * @param $select
- * @param $from
- * @param $where
- * @param $orderBy
- * @param $groupBy
- * @param $sprintfField
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @param Piwik_RankingQuery|false $rankingQuery
- * @return int
- */
- protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- $sprintfField, $archiveProcessing, $rankingQuery = false)
- {
- // idaction field needs to be set in select clause before calling getSelectQuery().
- // if a complex segmentation join is needed, the field needs to be propagated
- // to the outer select. therefore, $segment needs to know about it.
- $select = sprintf($select, $sprintfField);
-
- // get query with segmentation
- $bind = array();
- $query = $archiveProcessing->getSegment()->getSelectQuery(
- $select, $from, $where, $bind, $orderBy, $groupBy);
-
- // replace the rest of the %s
- $querySql = str_replace("%s", $sprintfField, $query['sql']);
-
- // apply ranking query
- if ($rankingQuery)
- {
- $querySql = $rankingQuery->generateQuery($querySql);
- }
-
- // extend bindings
- $bind = array_merge(array($archiveProcessing->getStartDatetimeUTC(),
- $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite), $query['bind']);
-
- // get result
- $resultSet = $archiveProcessing->db->query($querySql, $bind);
- $modified = $this->updateActionsTableWithRowQuery($resultSet, $sprintfField);
- return $modified;
- }
-
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL]);
- $this->deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_actions_url', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD]);
- $this->deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized($this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_downloads', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]);
- $this->deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_outlink', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME]);
- $this->deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation );
- $archiveProcessing->insertBlobRecord('Actions_actions', $s);
- destroy($dataTable);
-
- destroy($this->actionsTablesByType);
- }
-
- protected function deleteInvalidSummedColumnsFromDataTable($dataTable)
- {
- foreach($dataTable->getRows() as $id => $row)
- {
- if(($idSubtable = $row->getIdSubDataTable()) !== null || $id === Piwik_DataTable::ID_SUMMARY_ROW)
- {
- foreach(self::$invalidSummedColumnNameToDeleteFromDayArchive as $name)
- {
- $row->deleteColumn($name);
- }
-
- if ($idSubtable !== null)
- {
- $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
- $this->deleteInvalidSummedColumnsFromDataTable($subtable);
- }
- }
- }
- }
-
- /**
- * Explodes action name into an array of elements.
- *
- * for downloads:
- * we explode link http://piwik.org/some/path/piwik.zip into an array( 'piwik.org', '/some/path/piwik.zip' );
- *
- * for outlinks:
- * we explode link http://dev.piwik.org/some/path into an array( 'dev.piwik.org', '/some/path' );
- *
- * for action urls:
- * we explode link http://piwik.org/some/path into an array( 'some', 'path' );
- *
- * for action names:
- * we explode name 'Piwik / Category 1 / Category 2' into an array('Piwik', 'Category 1', 'Category 2');
- *
- * @param string action name
- * @param int action type
- * @param int url prefix (only used for TYPE_ACTION_URL)
- * @return array of exploded elements from $name
- */
- static public function getActionExplodedNames($name, $type, $urlPrefix=null)
- {
- $matches = array();
- $isUrl = false;
- $name = str_replace("\n", "", $name);
-
- $urlRegexAfterDomain = '([^/]+)[/]?([^#]*)[#]?(.*)';
- if ($urlPrefix === null)
- {
- // match url with protocol (used for outlinks / downloads)
- $urlRegex = '@^http[s]?://'.$urlRegexAfterDomain.'$@i';
- }
- else
- {
- // the name is a url that does not contain protocol and www anymore
- // we know that normalization has been done on db level because $urlPrefix is set
- $urlRegex = '@^'.$urlRegexAfterDomain.'$@i';
- }
-
- preg_match($urlRegex, $name, $matches);
- if( count($matches) )
- {
- $isUrl = true;
- $urlHost = $matches[1];
- $urlPath = $matches[2];
- $urlAnchor = $matches[3];
- }
-
- if($type == Piwik_Tracker_Action::TYPE_DOWNLOAD
- || $type == Piwik_Tracker_Action::TYPE_OUTLINK)
- {
- if( $isUrl )
- {
- return array(trim($urlHost), '/' . trim($urlPath));
- }
- }
-
- if( $isUrl )
- {
- $name = $urlPath;
-
- if( $name === '' || substr($name, -1) == '/' )
- {
- $name .= self::$defaultActionName;
- }
- }
-
- if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME)
- {
- $categoryDelimiter = self::$actionTitleCategoryDelimiter;
- }
- else
- {
- $categoryDelimiter = self::$actionUrlCategoryDelimiter;
- }
-
- if(empty($categoryDelimiter))
- {
- return array( trim($name) );
- }
-
- $split = explode($categoryDelimiter, $name, self::getSubCategoryLevelLimit());
-
- // trim every category and remove empty categories
- $split = array_map('trim', $split);
- $split = array_filter($split, 'strlen');
-
- // forces array key to start at 0
- $split = array_values($split);
-
- if( empty($split) )
- {
- $defaultName = self::getUnknownActionName($type);
- return array( trim($defaultName) );
- }
-
- $lastPageName = end($split);
- // we are careful to prefix the page URL / name with some value
- // so that if a page has the same name as a category
- // we don't merge both entries
- if($type != Piwik_Tracker_Action::TYPE_ACTION_NAME )
- {
- $lastPageName = '/' . $lastPageName;
- }
- else
- {
- $lastPageName = ' ' . $lastPageName;
- }
- $split[count($split)-1] = $lastPageName;
- return array_values( $split );
- }
-
- static protected function getUnknownActionName($type)
- {
- if(empty(self::$defaultActionNameWhenNotDefined))
- {
- self::$defaultActionNameWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageName'));
- self::$defaultActionUrlWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL'));
- }
- if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
- return self::$defaultActionNameWhenNotDefined;
- }
- return self::$defaultActionUrlWhenNotDefined;
- }
-
- const CACHE_PARSED_INDEX_NAME = 0;
- const CACHE_PARSED_INDEX_TYPE = 1;
- static $cacheParsedAction = array();
-
- /**
- * Get cached action row by id & type. If $idAction is set to -1, the 'Others' row
- * for the specific action type will be returned.
- *
- * @param int $idAction
- * @param int $actionType
- * @return Piwik_DataTable_Row|false
- */
- private static function getCachedActionRow( $idAction, $actionType )
- {
- $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
-
- if (!isset(self::$cacheParsedAction[$cacheLabel]))
- {
- // This can happen when
- // - We select an entry page ID that was only seen yesterday, so wasn't selected in the first query
- // - We count time spent on a page, when this page was only seen yesterday
- return false;
- }
-
- return self::$cacheParsedAction[$cacheLabel];
- }
-
- /**
- * Set cached action row for an id & type.
- *
- * @param int $idAction
- * @param int $actionType
- * @param Piwik_DataTable_Row
- */
- private static function setCachedActionRow( $idAction, $actionType, $actionRow )
- {
- $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
- self::$cacheParsedAction[$cacheLabel] = $actionRow;
- }
-
- /**
- * Gets the key for the cache of action rows from an action ID and type.
- *
- * @param int $idAction
- * @param int $actionType
- * @return string|int
- */
- private static function getCachedActionRowKey( $idAction, $actionType )
- {
- return $idAction == Piwik_DataTable::LABEL_SUMMARY_ROW ? $actionType.'_others' : $idAction;
- }
-
- /**
- * @param Zend_Db_Statement|PDOStatement $query
- * @param string|bool $fieldQueried
- * @return int
- */
- protected function updateActionsTableWithRowQuery($query, $fieldQueried = false)
- {
- $rowsProcessed = 0;
- while( $row = $query->fetch() )
- {
- if(empty($row['idaction']))
- {
- $row['type'] = ($fieldQueried == 'idaction_url' ? Piwik_Tracker_Action::TYPE_ACTION_URL : Piwik_Tracker_Action::TYPE_ACTION_NAME);
- // This will be replaced with 'X not defined' later
- $row['name'] = '';
- // Yes, this is kind of a hack, so we don't mix 'page url not defined' with 'page title not defined' etc.
- $row['idaction'] = -$row['type'];
- }
- // Only the first query will contain the name and type of actions, for performance reasons
- if(isset($row['name'])
- && isset($row['type']))
- {
- $actionName = $row['name'];
- $actionType = $row['type'];
- $urlPrefix = $row['url_prefix'];
- $idaction = $row['idaction'];
-
- // in some unknown case, the type field is NULL, as reported in #1082 - we ignore this page view
- if(empty($actionType))
- {
- if ($idaction != Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- self::setCachedActionRow($idaction, $actionType, false);
- }
-
- continue;
- }
-
- $currentTable = $this->parseActionNameCategoriesInDataTable($actionName, $actionType, $urlPrefix);
-
- self::setCachedActionRow($idaction, $actionType, $currentTable);
- }
- else
- {
- $currentTable = self::getCachedActionRow($row['idaction'], $row['type']);
-
- // Action processed as "to skip" for some reasons
- if($currentTable === false)
- {
- continue;
- }
- }
-
- unset($row['name']);
- unset($row['type']);
- unset($row['idaction']);
- unset($row['url_prefix']);
-
- if (is_null($currentTable))
- {
- continue;
- }
-
- foreach($row as $name => $value)
- {
- // in some edge cases, we have twice the same action name with 2 different idaction
- // this happens when 2 visitors visit the same new page at the same time, there is a SELECT and an INSERT for each new page,
- // and in between the two the other visitor comes.
- // here we handle the case where there is already a row for this action name, if this is the case we add the value
- if(($alreadyValue = $currentTable->getColumn($name)) !== false)
- {
- $currentTable->setColumn($name, $alreadyValue+$value);
- }
- else
- {
- $currentTable->addColumn($name, $value);
- }
- }
-
- // if the exit_action was not recorded properly in the log_link_visit_action
- // there would be an error message when getting the nb_hits column
- // we must fake the record and add the columns
- if($currentTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) === false)
- {
- // to test this code: delete the entries in log_link_action_visit for
- // a given exit_idaction_url
- foreach($this->defaultRow->getColumns() as $name => $value)
- {
- $currentTable->addColumn($name, $value);
- }
- }
- $rowsProcessed++;
- }
-
- // just to make sure php copies the last $currentTable in the $parentTable array
- $currentTable =& $this->actionsTablesByType;
- return $rowsProcessed;
- }
-
- /**
- * Given a page name and type, builds a recursive datatable where
- * each level of the tree is a category, based on the page name split by a delimiter (slash / by default)
- *
- * @param string $actionName
- * @param int $actionType
- * @param int $urlPrefix
- * @return Piwik_DataTable
- */
- protected function parseActionNameCategoriesInDataTable($actionName, $actionType, $urlPrefix=null)
- {
- // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.)
- $currentTable =& $this->actionsTablesByType[$actionType];
-
- // check for ranking query cut-off
- if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW)
- {
- return $this->createOthersRow($currentTable, $actionType);
- }
-
- // go to the level of the subcategory
- $actionExplodedNames = $this->getActionExplodedNames($actionName, $actionType, $urlPrefix);
- $end = count($actionExplodedNames)-1;
- $maxRows = $this->maximumRowsInDataTableLevelZero;
- for($level = 0 ; $level < $end; $level++)
- {
- $actionCategory = $actionExplodedNames[$level];
-
- $othersRow = $this->getOthersRowIfTableFull($currentTable, $actionCategory, $actionType, $maxRows);
- if ($othersRow)
- {
- return $othersRow;
- }
-
- $currentTable =& $currentTable[$actionCategory];
- $maxRows = $this->maximumRowsInSubDataTable;
- }
- $actionShortName = $actionExplodedNames[$end];
-
- $othersRow = $this->getOthersRowIfTableFull($currentTable, $actionShortName, $actionType, $maxRows);
- if ($othersRow)
- {
- return $othersRow;
- }
-
- // currentTable is now the array element corresponding the the action
- // at this point we may be for example at the 4th level of depth in the hierarchy
- $currentTable =& $currentTable[$actionShortName];
-
- // add the row to the matching sub category subtable
- if(!($currentTable instanceof Piwik_DataTable_Row))
- {
- $currentTable = $this->createActionsTableRow(
- (string)$actionShortName, $actionType, $actionName, $urlPrefix);
- }
- return $currentTable;
- }
-
- /**
- * Checks if the given table is full (has the maximum number of rows allowed in config)
- * and if so, returns an 'Others' summary row. Returns false if the table is not full.
- *
- * @param array $currentTable Array of Piwik_DataTable_Rows.
- * @param string $actionCategory The current table key.
- * @param int $actionType The action type.
- * @param int $maxRows The maximum number of rows allowed in $currentTable.
- * @return Piwik_DataTable_Row|false
- */
- private function getOthersRowIfTableFull( &$currentTable, $actionCategory, $actionType, $maxRows )
- {
- if (!isset($currentTable[$actionCategory]))
- {
- if (count($currentTable) == $maxRows - 1)
- {
- return $this->createOthersRow($currentTable, $actionType);
- }
- else if (count($currentTable) >= $maxRows)
- {
- return $currentTable[self::OTHERS_ROW_KEY]; // return existing 'others' row
- }
- }
-
- return false;
- }
-
- /**
- * Create an 'Others' action row. The row created by this function is used
- * as the summary row in a truncated DataTable.
- *
- * @param array $currentTable The array of rows to add the 'Others' row to.
- * @param int $actionType The type of actions the row will hold stats for.
- * @return Piwik_DataTable_Row
- */
- private function createOthersRow( &$currentTable, $actionType )
- {
- // create other row and return it
- $othersRow = $this->createActionsTableRow(Piwik_DataTable::LABEL_SUMMARY_ROW, $actionType);
- $othersRow->setMetadata('issummaryrow', true);
-
- $currentTable[self::OTHERS_ROW_KEY] = $othersRow;
- return $othersRow;
- }
-
- /**
- * Creates a new empty row for DataTables created by this plugin.
- *
- * @param string $label The row label.
- * @param int $actionType The action type of the action the row will describe.
- * @param string $actionName
- * @param string $urlPrefix
- * @return Piwik_DataTable_Row
- */
- private function createActionsTableRow( $label, $actionType, $actionName = null, $urlPrefix = null )
- {
- $defaultColumnsNewRow = array(
- 'label' => $label,
- Piwik_Archive::INDEX_NB_VISITS => 0,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 0,
- Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 0,
- );
- if( $actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME )
- {
- return new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => $defaultColumnsNewRow,
- ));
- }
- else
- {
- $metadata = array();
- if (!is_null($actionName))
- {
- $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$actionName, $urlPrefix);
- $metadata['url'] = $url;
- }
-
- return new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => $defaultColumnsNewRow,
- Piwik_DataTable_Row::METADATA => $metadata,
- ));
- }
- }
-
- /**
- * Returns the configured sub-category level limit.
- *
- * @return int
- */
- public static function getSubCategoryLevelLimit()
- {
- return Piwik_Config::getInstance()->General['action_category_level_limit'];
- }
-
- /**
- * Returns the limit to use with RankingQuery for this plugin.
- *
- * @return int
- */
- public static function getRankingQueryLimit()
- {
- $configGeneral = Piwik_Config::getInstance()->General;
- $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
- return $configLimit == 0 ? 0 : max(
- $configLimit,
- $configGeneral['datatable_archiving_maximum_rows_actions'],
- $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
- );
+ $actionsArchiving = new Piwik_Actions_Archiving;
+ return $actionsArchiving->archiveDay($archiveProcessing);
}
}
diff --git a/plugins/Actions/Archiving.php b/plugins/Actions/Archiving.php
new file mode 100644
index 0000000000..728685b15f
--- /dev/null
+++ b/plugins/Actions/Archiving.php
@@ -0,0 +1,425 @@
+<?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: Actions.php 6986 2012-09-15 03:42:26Z capedfuzz $
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_Actions
+ */
+
+/**
+ * Class encapsulating logic to process Day/Period Archiving for the Actions reports
+ *
+ * @package Piwik_Actions
+ */
+class Piwik_Actions_Archiving
+{
+ protected $actionsTablesByType = array(
+ Piwik_Tracker_Action::TYPE_ACTION_URL => array(),
+ Piwik_Tracker_Action::TYPE_DOWNLOAD => array(),
+ Piwik_Tracker_Action::TYPE_OUTLINK => array(),
+ Piwik_Tracker_Action::TYPE_ACTION_NAME => array(),
+ );
+
+
+ static protected $invalidSummedColumnNameToRenamedNameFromPeriodArchive = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
+ );
+
+ static protected $invalidSummedColumnNameToDeleteFromDayArchive = array(
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
+ Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ );
+
+ /**
+ * Archives Actions reports for a Period
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return bool
+ */
+ public function archivePeriod(Piwik_ArchiveProcessing $archiveProcessing)
+ {
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $dataTableToSum = array(
+ 'Actions_actions',
+ 'Actions_downloads',
+ 'Actions_outlink',
+ 'Actions_actions_url',
+ );
+ $archiveProcessing->archiveDataTable($dataTableToSum,
+ self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
+ Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+
+ $archiveProcessing->archiveNumericValuesSum(array(
+ 'Actions_nb_pageviews',
+ 'Actions_nb_uniq_pageviews',
+ 'Actions_nb_downloads',
+ 'Actions_nb_uniq_downloads',
+ 'Actions_nb_outlinks',
+ 'Actions_nb_uniq_outlinks'
+ ));
+
+ return true;
+ }
+
+ /**
+ * Archives Actions reports for a Day
+ *
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @return bool
+ */
+ public function archiveDay(Piwik_ArchiveProcessing $archiveProcessing)
+ {
+ $rankingQueryLimit = self::getRankingQueryLimit();
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+
+ $this->archiveDayActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayEntryActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayExitActions($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayActionsTime($archiveProcessing, $rankingQueryLimit);
+
+ // Record the final datasets
+ $this->archiveDayRecordInDatabase($archiveProcessing);
+
+ return true;
+ }
+
+ /*
+ * Page URLs and Page names, general stats
+ */
+ protected function archiveDayActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $select = "log_action.name,
+ log_action.type,
+ log_action.idaction,
+ log_action.url_prefix,
+ count(distinct log_link_visit_action.idvisit) as `" . Piwik_Archive::INDEX_NB_VISITS . "`,
+ count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`";
+
+ $from = array(
+ "log_link_visit_action",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_link_visit_action.%s = log_action.idaction"
+ )
+ );
+
+ $where = "log_link_visit_action.server_time >= ?
+ AND log_link_visit_action.server_time <= ?
+ AND log_link_visit_action.idsite = ?
+ AND log_link_visit_action.%s IS NOT NULL";
+
+ $groupBy = "log_action.idaction";
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
+
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn(array('idaction', 'name'));
+ $rankingQuery->addColumn(array('url_prefix', Piwik_Archive::INDEX_NB_UNIQ_VISITORS));
+ $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_NB_HITS, Piwik_Archive::INDEX_NB_VISITS), 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+ }
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_url", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_name", $archiveProcessing, $rankingQuery);
+ }
+
+
+ /**
+ * Entry actions for Page URLs and Page names
+ */
+ protected function archiveDayEntryActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
+ Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
+ Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = 'log_action.type, log_action.name,';
+ $from = array(
+ "log_visit",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_visit.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_visit";
+ $orderBy = false;
+ }
+
+ $select = "log_visit.%s as idaction, $extraSelects
+ count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS . "`,
+ sum(log_visit.visit_total_actions) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
+ sum(log_visit.visit_total_time) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
+ sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
+
+ $where = "log_visit.visit_last_action_time >= ?
+ AND log_visit.visit_last_action_time <= ?
+ AND log_visit.idsite = ?
+ AND log_visit.%s > 0";
+
+ $groupBy = "log_visit.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_entry_idaction_url", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_entry_idaction_name", $archiveProcessing, $rankingQuery);
+ }
+
+
+ /**
+ * Time per action
+ */
+ protected function archiveDayActionsTime($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0)
+ {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = "log_action.type, log_action.name, count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,";
+ $from = array(
+ "log_link_visit_action",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_link_visit_action.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_link_visit_action";
+ $orderBy = false;
+ }
+
+ $select = "log_link_visit_action.%s as idaction, $extraSelects
+ sum(log_link_visit_action.time_spent_ref_action) as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT . "`";
+
+ $where = "log_link_visit_action.server_time >= ?
+ AND log_link_visit_action.server_time <= ?
+ AND log_link_visit_action.idsite = ?
+ AND log_link_visit_action.time_spent_ref_action > 0
+ AND log_link_visit_action.%s > 0";
+
+ $groupBy = "log_link_visit_action.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_url_ref", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "idaction_name_ref", $archiveProcessing, $rankingQuery);
+ }
+
+ /**
+ * Exit actions
+ */
+ public function archiveDayExitActions($archiveProcessing, $rankingQueryLimit)
+ {
+ $rankingQuery = false;
+ if ($rankingQueryLimit > 0) {
+ $rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
+ $rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
+ $rankingQuery->addLabelColumn('idaction');
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
+
+ $extraSelects = 'log_action.type, log_action.name,';
+ $from = array(
+ "log_visit",
+ array(
+ "table" => "log_action",
+ "joinOn" => "log_visit.%s = log_action.idaction"
+ )
+ );
+ $orderBy = "`" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
+ } else {
+ $extraSelects = false;
+ $from = "log_visit";
+ $orderBy = false;
+ }
+
+ $select = "log_visit.%s as idaction, $extraSelects
+ count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "`";
+
+ $where = "log_visit.visit_last_action_time >= ?
+ AND log_visit.visit_last_action_time <= ?
+ AND log_visit.idsite = ?
+ AND log_visit.%s > 0";
+
+ $groupBy = "log_visit.%s, idaction";
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_exit_idaction_url", $archiveProcessing, $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ "visit_exit_idaction_name", $archiveProcessing, $rankingQuery);
+ return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
+ }
+
+
+ /**
+ * Records in the DB the archived reports for Page views, Downloads, Outlinks, and Page titles
+ *
+ * @param $archiveProcessing
+ */
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ Piwik_Actions_ArchivingHelper::clearActionsCache();
+
+ $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL]);
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
+ $archiveProcessing->insertBlobRecord('Actions_actions_url', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD]);
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
+ $archiveProcessing->insertBlobRecord('Actions_downloads', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]);
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
+ $archiveProcessing->insertBlobRecord('Actions_outlink', $s);
+ $archiveProcessing->insertNumericRecord('Actions_nb_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
+ $archiveProcessing->insertNumericRecord('Actions_nb_uniq_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
+ destroy($dataTable);
+
+ $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME]);
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation );
+ $archiveProcessing->insertBlobRecord('Actions_actions', $s);
+ destroy($dataTable);
+
+ destroy($this->actionsTablesByType);
+ }
+
+ /**
+ * Returns the limit to use with RankingQuery for this plugin.
+ *
+ * @return int
+ */
+ private static function getRankingQueryLimit()
+ {
+ $configGeneral = Piwik_Config::getInstance()->General;
+ $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
+ return $configLimit == 0 ? 0 : max(
+ $configLimit,
+ $configGeneral['datatable_archiving_maximum_rows_actions'],
+ $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
+ );
+ }
+
+
+ /**
+ * @param $select
+ * @param $from
+ * @param $where
+ * @param $orderBy
+ * @param $groupBy
+ * @param $sprintfField
+ * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @param Piwik_RankingQuery|false $rankingQuery
+ * @return int
+ */
+ protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
+ $sprintfField, $archiveProcessing, $rankingQuery = false)
+ {
+ // idaction field needs to be set in select clause before calling getSelectQuery().
+ // if a complex segmentation join is needed, the field needs to be propagated
+ // to the outer select. therefore, $segment needs to know about it.
+ $select = sprintf($select, $sprintfField);
+
+ // get query with segmentation
+ $bind = array();
+ $query = $archiveProcessing->getSegment()->getSelectQuery(
+ $select, $from, $where, $bind, $orderBy, $groupBy);
+
+ // replace the rest of the %s
+ $querySql = str_replace("%s", $sprintfField, $query['sql']);
+
+ // apply ranking query
+ if ($rankingQuery)
+ {
+ $querySql = $rankingQuery->generateQuery($querySql);
+ }
+
+ // extend bindings
+ $bind = array_merge(array( $archiveProcessing->getStartDatetimeUTC(),
+ $archiveProcessing->getEndDatetimeUTC(),
+ $archiveProcessing->idsite
+ ),
+ $query['bind']
+ );
+
+ // get result
+ $resultSet = $archiveProcessing->db->query($querySql, $bind);
+ $modified = Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
+ return $modified;
+ }
+
+
+ /**
+ * For rows which have subtables (eg. directories with sub pages),
+ * deletes columns which don't make sense when all values of sub pages are summed.
+ *
+ * @param $dataTable Piwik_DataTable
+ */
+ static public function deleteInvalidSummedColumnsFromDataTable($dataTable)
+ {
+ foreach($dataTable->getRows() as $id => $row)
+ {
+ if(($idSubtable = $row->getIdSubDataTable()) !== null
+ || $id === Piwik_DataTable::ID_SUMMARY_ROW)
+ {
+ foreach(self::$invalidSummedColumnNameToDeleteFromDayArchive as $name)
+ {
+ $row->deleteColumn($name);
+ }
+
+ if ($idSubtable !== null)
+ {
+ $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable);
+ self::deleteInvalidSummedColumnsFromDataTable($subtable);
+ }
+ }
+ }
+ }
+
+}
diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php
new file mode 100644
index 0000000000..8aa2baeb58
--- /dev/null
+++ b/plugins/Actions/ArchivingHelper.php
@@ -0,0 +1,533 @@
+<?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: Actions.php 6986 2012-09-15 03:42:26Z capedfuzz $
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_Actions
+ */
+
+/**
+ * This static class provides:
+ * - logic to parse/cleanup Action names,
+ * - logic to efficiently process aggregate the array data during Archiving
+ *
+ * @package Piwik_Actions
+ */
+
+class Piwik_Actions_ArchivingHelper
+{
+ const OTHERS_ROW_KEY = '';
+
+ /**
+ * @param Zend_Db_Statement|PDOStatement $query
+ * @param string|bool $fieldQueried
+ * @param array $actionsTablesByType
+ * @return int
+ */
+ static public function updateActionsTableWithRowQuery($query, $fieldQueried, & $actionsTablesByType)
+ {
+ $rowsProcessed = 0;
+ while( $row = $query->fetch() )
+ {
+ if(empty($row['idaction']))
+ {
+ $row['type'] = ($fieldQueried == 'idaction_url' ? Piwik_Tracker_Action::TYPE_ACTION_URL : Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ // This will be replaced with 'X not defined' later
+ $row['name'] = '';
+ // Yes, this is kind of a hack, so we don't mix 'page url not defined' with 'page title not defined' etc.
+ $row['idaction'] = -$row['type'];
+ }
+ // Only the first query will contain the name and type of actions, for performance reasons
+ if(isset($row['name'])
+ && isset($row['type']))
+ {
+ $actionName = $row['name'];
+ $actionType = $row['type'];
+ $urlPrefix = $row['url_prefix'];
+ $idaction = $row['idaction'];
+
+ // in some unknown case, the type field is NULL, as reported in #1082 - we ignore this page view
+ if(empty($actionType))
+ {
+ if ($idaction != Piwik_DataTable::LABEL_SUMMARY_ROW)
+ {
+ self::setCachedActionRow($idaction, $actionType, false);
+ }
+
+ continue;
+ }
+
+ $currentTable = self::parseActionNameCategoriesInDataTable($actionName, $actionType, $urlPrefix, $actionsTablesByType);
+
+ self::setCachedActionRow($idaction, $actionType, $currentTable);
+ }
+ else
+ {
+ $currentTable = self::getCachedActionRow($row['idaction'], $row['type']);
+
+ // Action processed as "to skip" for some reasons
+ if($currentTable === false)
+ {
+ continue;
+ }
+ }
+
+ unset($row['name']);
+ unset($row['type']);
+ unset($row['idaction']);
+ unset($row['url_prefix']);
+
+ if (is_null($currentTable))
+ {
+ continue;
+ }
+
+ foreach($row as $name => $value)
+ {
+ // in some edge cases, we have twice the same action name with 2 different idaction
+ // this happens when 2 visitors visit the same new page at the same time, there is a SELECT and an INSERT for each new page,
+ // and in between the two the other visitor comes.
+ // here we handle the case where there is already a row for this action name, if this is the case we add the value
+ if(($alreadyValue = $currentTable->getColumn($name)) !== false)
+ {
+ $currentTable->setColumn($name, $alreadyValue+$value);
+ }
+ else
+ {
+ $currentTable->addColumn($name, $value);
+ }
+ }
+
+ // if the exit_action was not recorded properly in the log_link_visit_action
+ // there would be an error message when getting the nb_hits column
+ // we must fake the record and add the columns
+ if($currentTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) === false)
+ {
+ // to test this code: delete the entries in log_link_action_visit for
+ // a given exit_idaction_url
+ foreach(self::getDefaultRow()->getColumns() as $name => $value)
+ {
+ $currentTable->addColumn($name, $value);
+ }
+ }
+ $rowsProcessed++;
+ }
+
+ // just to make sure php copies the last $currentTable in the $parentTable array
+ $currentTable =& $actionsTablesByType;
+ return $rowsProcessed;
+ }
+
+ static public $maximumRowsInDataTableLevelZero;
+ static public $maximumRowsInSubDataTable;
+ static public $columnToSortByBeforeTruncation;
+
+ static protected $actionUrlCategoryDelimiter = null;
+ static protected $actionTitleCategoryDelimiter = null;
+ static protected $defaultActionName = null;
+ static protected $defaultActionNameWhenNotDefined = null;
+ static protected $defaultActionUrlWhenNotDefined = null;
+
+ static public function reloadConfig()
+ {
+ // for BC, we read the old style delimiter first (see #1067)
+ $actionDelimiter = @Piwik_Config::getInstance()->General['action_category_delimiter'];
+ if(empty($actionDelimiter))
+ {
+ self::$actionUrlCategoryDelimiter = Piwik_Config::getInstance()->General['action_url_category_delimiter'];
+ self::$actionTitleCategoryDelimiter = Piwik_Config::getInstance()->General['action_title_category_delimiter'];
+ }
+ else
+ {
+ self::$actionUrlCategoryDelimiter = self::$actionTitleCategoryDelimiter = $actionDelimiter;
+ }
+
+ self::$defaultActionName = Piwik_Config::getInstance()->General['action_default_name'];
+ self::$columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+ self::$maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
+ self::$maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
+
+ Piwik_DataTable::setMaximumDepthLevelAllowedAtLeast(self::getSubCategoryLevelLimit() + 1);
+ }
+
+
+ /**
+ * The default row is used when archiving, if data is inconsistent in the DB,
+ * there could be pages that have exit/entry hits, but don't yet
+ * have a record in the table (or the record was truncated).
+ *
+ * @return Piwik_DataTable_Row
+ */
+ static private function getDefaultRow()
+ {
+ static $row = false;
+ if($row === false) {
+ // This row is used in the case where an action is know as an exit_action
+ // but this action was not properly recorded when it was hit in the first place
+ // so we add this fake row information to make sure there is a nb_hits, etc. column for every action
+ $row = new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ Piwik_Archive::INDEX_NB_VISITS => 1,
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 1,
+ Piwik_Archive::INDEX_PAGE_NB_HITS => 1,
+ )));
+ }
+ return $row;
+ }
+
+ /**
+ * Given a page name and type, builds a recursive datatable where
+ * each level of the tree is a category, based on the page name split by a delimiter (slash / by default)
+ *
+ * @param string $actionName
+ * @param int $actionType
+ * @param int $urlPrefix
+ * @param array $actionsTablesByType
+ * @return Piwik_DataTable
+ */
+ protected function parseActionNameCategoriesInDataTable($actionName, $actionType, $urlPrefix=null, &$actionsTablesByType)
+ {
+ // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.)
+ $currentTable =& $actionsTablesByType[$actionType];
+
+ // check for ranking query cut-off
+ if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW)
+ {
+ return self::createOthersRow($currentTable, $actionType);
+ }
+
+ // go to the level of the subcategory
+ $actionExplodedNames = self::getActionExplodedNames($actionName, $actionType, $urlPrefix);
+ $end = count($actionExplodedNames)-1;
+ $maxRows = self::$maximumRowsInDataTableLevelZero;
+ for($level = 0 ; $level < $end; $level++)
+ {
+ $actionCategory = $actionExplodedNames[$level];
+
+ $othersRow = self::getOthersRowIfTableFull($currentTable, $actionCategory, $actionType, $maxRows);
+ if ($othersRow)
+ {
+ return $othersRow;
+ }
+
+ $currentTable =& $currentTable[$actionCategory];
+ $maxRows = self::$maximumRowsInSubDataTable;
+ }
+ $actionShortName = $actionExplodedNames[$end];
+
+ $othersRow = self::getOthersRowIfTableFull($currentTable, $actionShortName, $actionType, $maxRows);
+ if ($othersRow)
+ {
+ return $othersRow;
+ }
+
+ // currentTable is now the array element corresponding the the action
+ // at this point we may be for example at the 4th level of depth in the hierarchy
+ $currentTable =& $currentTable[$actionShortName];
+
+ // add the row to the matching sub category subtable
+ if(!($currentTable instanceof Piwik_DataTable_Row))
+ {
+ $currentTable = self::createActionsTableRow(
+ (string)$actionShortName, $actionType, $actionName, $urlPrefix);
+ }
+ return $currentTable;
+ }
+
+ /**
+ * Explodes action name into an array of elements.
+ *
+ * NOTE: before calling this function make sure Piwik_Actions_ArchivingHelper::reloadConfig(); is called
+ *
+ * for downloads:
+ * we explode link http://piwik.org/some/path/piwik.zip into an array( 'piwik.org', '/some/path/piwik.zip' );
+ *
+ * for outlinks:
+ * we explode link http://dev.piwik.org/some/path into an array( 'dev.piwik.org', '/some/path' );
+ *
+ * for action urls:
+ * we explode link http://piwik.org/some/path into an array( 'some', 'path' );
+ *
+ * for action names:
+ * we explode name 'Piwik / Category 1 / Category 2' into an array('Piwik', 'Category 1', 'Category 2');
+ *
+ * @param string action name
+ * @param int action type
+ * @param int url prefix (only used for TYPE_ACTION_URL)
+ * @return array of exploded elements from $name
+ */
+ static public function getActionExplodedNames($name, $type, $urlPrefix=null)
+ {
+ $matches = array();
+ $isUrl = false;
+ $name = str_replace("\n", "", $name);
+
+ $urlRegexAfterDomain = '([^/]+)[/]?([^#]*)[#]?(.*)';
+ if ($urlPrefix === null)
+ {
+ // match url with protocol (used for outlinks / downloads)
+ $urlRegex = '@^http[s]?://'.$urlRegexAfterDomain.'$@i';
+ }
+ else
+ {
+ // the name is a url that does not contain protocol and www anymore
+ // we know that normalization has been done on db level because $urlPrefix is set
+ $urlRegex = '@^'.$urlRegexAfterDomain.'$@i';
+ }
+
+ preg_match($urlRegex, $name, $matches);
+ if( count($matches) )
+ {
+ $isUrl = true;
+ $urlHost = $matches[1];
+ $urlPath = $matches[2];
+ $urlAnchor = $matches[3];
+ }
+
+ if($type == Piwik_Tracker_Action::TYPE_DOWNLOAD
+ || $type == Piwik_Tracker_Action::TYPE_OUTLINK)
+ {
+ if( $isUrl )
+ {
+ return array(trim($urlHost), '/' . trim($urlPath));
+ }
+ }
+
+ if( $isUrl )
+ {
+ $name = $urlPath;
+
+ if( $name === '' || substr($name, -1) == '/' )
+ {
+ $name .= self::$defaultActionName;
+ }
+ }
+
+ if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME)
+ {
+ $categoryDelimiter = self::$actionTitleCategoryDelimiter;
+ }
+ else
+ {
+ $categoryDelimiter = self::$actionUrlCategoryDelimiter;
+ }
+
+ if(empty($categoryDelimiter))
+ {
+ return array( trim($name) );
+ }
+
+ $split = explode($categoryDelimiter, $name, self::getSubCategoryLevelLimit());
+
+ // trim every category and remove empty categories
+ $split = array_map('trim', $split);
+ $split = array_filter($split, 'strlen');
+
+ // forces array key to start at 0
+ $split = array_values($split);
+
+ if( empty($split) )
+ {
+ $defaultName = self::getUnknownActionName($type);
+ return array( trim($defaultName) );
+ }
+
+ $lastPageName = end($split);
+ // we are careful to prefix the page URL / name with some value
+ // so that if a page has the same name as a category
+ // we don't merge both entries
+ if($type != Piwik_Tracker_Action::TYPE_ACTION_NAME )
+ {
+ $lastPageName = '/' . $lastPageName;
+ }
+ else
+ {
+ $lastPageName = ' ' . $lastPageName;
+ }
+ $split[count($split)-1] = $lastPageName;
+ return array_values( $split );
+ }
+
+ /**
+ * Gets the key for the cache of action rows from an action ID and type.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @return string|int
+ */
+ private static function getCachedActionRowKey( $idAction, $actionType )
+ {
+ return $idAction == Piwik_DataTable::LABEL_SUMMARY_ROW
+ ? $actionType.'_others'
+ : $idAction;
+ }
+
+ /**
+ * Returns the configured sub-category level limit.
+ *
+ * @return int
+ */
+ public static function getSubCategoryLevelLimit()
+ {
+ return Piwik_Config::getInstance()->General['action_category_level_limit'];
+ }
+
+ /**
+ * Returns default label for the action type
+ *
+ * @param $type
+ * @return string
+ */
+ static protected function getUnknownActionName($type)
+ {
+ if(empty(self::$defaultActionNameWhenNotDefined))
+ {
+ self::$defaultActionNameWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageName'));
+ self::$defaultActionUrlWhenNotDefined = Piwik_Translate('General_NotDefined', Piwik_Translate('Actions_ColumnPageURL'));
+ }
+ if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME)
+ {
+ return self::$defaultActionNameWhenNotDefined;
+ }
+ return self::$defaultActionUrlWhenNotDefined;
+ }
+
+
+
+ /**
+ * Checks if the given table is full (has the maximum number of rows allowed in config)
+ * and if so, returns an 'Others' summary row. Returns false if the table is not full.
+ *
+ * @param array $currentTable Array of Piwik_DataTable_Rows.
+ * @param string $actionCategory The current table key.
+ * @param int $actionType The action type.
+ * @param int $maxRows The maximum number of rows allowed in $currentTable.
+ * @return Piwik_DataTable_Row|false
+ */
+ static private function getOthersRowIfTableFull( &$currentTable, $actionCategory, $actionType, $maxRows )
+ {
+ if (!isset($currentTable[$actionCategory]))
+ {
+ if (count($currentTable) == $maxRows - 1)
+ {
+ return self::createOthersRow($currentTable, $actionType);
+ }
+ else if (count($currentTable) >= $maxRows)
+ {
+ return $currentTable[self::OTHERS_ROW_KEY]; // return existing 'others' row
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Create an 'Others' action row. The row created by this function is used
+ * as the summary row in a truncated DataTable.
+ *
+ * @param array $currentTable The array of rows to add the 'Others' row to.
+ * @param int $actionType The type of actions the row will hold stats for.
+ * @return Piwik_DataTable_Row
+ */
+ static private function createOthersRow( &$currentTable, $actionType )
+ {
+ // create other row and return it
+ $othersRow = self::createActionsTableRow(Piwik_DataTable::LABEL_SUMMARY_ROW, $actionType);
+ $othersRow->setMetadata('issummaryrow', true);
+
+ $currentTable[self::OTHERS_ROW_KEY] = $othersRow;
+ return $othersRow;
+ }
+
+ /**
+ * Creates a new empty datatable row for storing Actions .
+ *
+ * @param string $label The row label.
+ * @param int $actionType The action type of the action the row will describe.
+ * @param string $actionName
+ * @param string $urlPrefix
+ * @return Piwik_DataTable_Row
+ */
+ static private function createActionsTableRow( $label, $actionType, $actionName = null, $urlPrefix = null )
+ {
+ $defaultColumnsNewRow = array(
+ 'label' => $label,
+ Piwik_Archive::INDEX_NB_VISITS => 0,
+ Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
+ Piwik_Archive::INDEX_PAGE_NB_HITS => 0,
+ Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 0,
+ );
+ if( $actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME )
+ {
+ return new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => $defaultColumnsNewRow,
+ ));
+ }
+ else
+ {
+ $metadata = array();
+ if (!is_null($actionName))
+ {
+ $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$actionName, $urlPrefix);
+ $metadata['url'] = $url;
+ }
+
+ return new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => $defaultColumnsNewRow,
+ Piwik_DataTable_Row::METADATA => $metadata,
+ ));
+ }
+ }
+
+
+ /**
+ * Static cache to store Rows during processing
+ */
+ static protected $cacheParsedAction = array();
+
+ public static function clearActionsCache()
+ {
+ self::$cacheParsedAction = array();
+ }
+
+ /**
+ * Get cached action row by id & type. If $idAction is set to -1, the 'Others' row
+ * for the specific action type will be returned.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @return Piwik_DataTable_Row|false
+ */
+ private static function getCachedActionRow( $idAction, $actionType )
+ {
+ $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
+
+ if (!isset(self::$cacheParsedAction[$cacheLabel]))
+ {
+ // This can happen when
+ // - We select an entry page ID that was only seen yesterday, so wasn't selected in the first query
+ // - We count time spent on a page, when this page was only seen yesterday
+ return false;
+ }
+
+ return self::$cacheParsedAction[$cacheLabel];
+ }
+
+ /**
+ * Set cached action row for an id & type.
+ *
+ * @param int $idAction
+ * @param int $actionType
+ * @param Piwik_DataTable_Row
+ */
+ private static function setCachedActionRow( $idAction, $actionType, $actionRow )
+ {
+ $cacheLabel = self::getCachedActionRowKey($idAction, $actionType);
+ self::$cacheParsedAction[$cacheLabel] = $actionRow;
+ }
+
+}
diff --git a/plugins/CoreHome/templates/header.tpl b/plugins/CoreHome/templates/header.tpl
index fd4fe450a5..405bcc6e1e 100644
--- a/plugins/CoreHome/templates/header.tpl
+++ b/plugins/CoreHome/templates/header.tpl
@@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
-<title>{if !$isCustomLogo}Piwik &rsaquo; {/if} {'CoreHome_WebAnalyticsReports'|translate} - {$siteName}</title>
+<title>{$siteName} - {if !$isCustomLogo}Piwik &rsaquo; {/if} {'CoreHome_WebAnalyticsReports'|translate}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Piwik - Open Source Web Analytics" />
<meta name="description" content="Web Analytics report for '{$siteName}' - Piwik" />
diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php
index 507f259d5c..8db9d3bccb 100644
--- a/plugins/Transitions/API.php
+++ b/plugins/Transitions/API.php
@@ -79,7 +79,8 @@ class Piwik_Transitions_API
*/
private function addMainPageMetricsToReport(&$report, $pageUrl, $idSite, $period, $date, $segment)
{
- $label = Piwik_Actions::getActionExplodedNames($pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $label = Piwik_Actions_ArchivingHelper::getActionExplodedNames($pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL);
if (count($label) == 1)
{
$label = $label[0];
diff --git a/tests/PHPUnit/Integration/BlobReportLimitingTest.php b/tests/PHPUnit/Integration/BlobReportLimitingTest.php
index 3ba5916613..ca12499737 100755
--- a/tests/PHPUnit/Integration/BlobReportLimitingTest.php
+++ b/tests/PHPUnit/Integration/BlobReportLimitingTest.php
@@ -73,7 +73,7 @@ class Test_Piwik_Integration_BlobReportLimitingTest extends IntegrationTestCase
$generalConfig['datatable_archiving_maximum_rows_subtable_actions'] = 4;
$generalConfig['datatable_archiving_maximum_rows_standard'] = 4;
Piwik_Config::getInstance()->General['archiving_ranking_query_row_limit'] = 3;
- Piwik_PluginsManager::getInstance()->getLoadedPlugin('Actions')->reloadConfig();
+ Piwik_Actions_ArchivingHelper::reloadConfig();
foreach ($this->getApiForTesting() as $pair)
{
diff --git a/tests/PHPUnit/Plugins/ActionsTest.php b/tests/PHPUnit/Plugins/ActionsTest.php
index 7c7ee058c0..fc3778192f 100644
--- a/tests/PHPUnit/Plugins/ActionsTest.php
+++ b/tests/PHPUnit/Plugins/ActionsTest.php
@@ -113,17 +113,9 @@ class ActionsTests extends PHPUnit_Framework_TestCase
*/
public function testGetActionExplodedNames($params, $expected)
{
- $action = new Test_Piwik_Actions_getActionExplodedNames();
-
- $processed = $action->public_getActionExplodedNames($params['name'], $params['type'], (isset($params['urlPrefix'])?$params['urlPrefix']:null));
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $processed = Piwik_Actions_ArchivingHelper::getActionExplodedNames($params['name'], $params['type'], (isset($params['urlPrefix'])?$params['urlPrefix']:null));
$this->assertEquals($expected, $processed);
}
}
-class Test_Piwik_Actions_getActionExplodedNames extends Piwik_Actions
-{
- public function public_getActionExplodedNames($name, $type, $urlPrefix)
- {
- return self::getActionExplodedNames($name, $type, $urlPrefix);
- }
-}