diff options
author | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-17 20:26:15 +0300 |
---|---|---|
committer | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-17 20:26:15 +0300 |
commit | 3966931ccc7f8b2a828ae704e3be1b9affcd1d81 (patch) | |
tree | e1b592d7358d3a01a1dc39cd8c3134d5ee551b2f /plugins/Actions | |
parent | 244fe1904bef08501f3c272d0214e4384019556f (diff) |
- adding Goal Tracking related goodness in core, and plugins
- goal table icon below datatable that have goal segmentation
- rss export icon below datatable
- cleaning code, refactoring, renaming goodness
- adding switch enable_detect_unique_visitor_using_settings that enables/disable heuristic based on config hash
- refactoring how plugins handle archiving for more clarity
Diffstat (limited to 'plugins/Actions')
-rw-r--r-- | plugins/Actions/Actions.php | 591 | ||||
-rw-r--r-- | plugins/Actions/Controller.php | 2 |
2 files changed, 297 insertions, 296 deletions
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 0972d00e46..4545762f64 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -1,21 +1,21 @@ -<?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_Actions
- */
-
-/**
- *
- * @package Piwik_Actions
- */
-class Piwik_Actions extends Piwik_Plugin
-{
- static protected $actionCategoryDelimiter = null;
+<?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_Actions + */ + +/** + * + * @package Piwik_Actions + */ +class Piwik_Actions extends Piwik_Plugin +{ + static protected $actionCategoryDelimiter = null; static protected $limitLevelSubCategory = 10; public function getInformation() @@ -41,309 +41,310 @@ class Piwik_Actions extends Piwik_Plugin ); return $hooks; } -
- public function __construct()
+ + public function __construct() + { + $this->setCategoryDelimiter( Zend_Registry::get('config')->General->action_category_delimiter); + } + + public function setCategoryDelimiter($delimiter) { - $this->setCategoryDelimiter( Zend_Registry::get('config')->General->action_category_delimiter);
- }
-
- public function setCategoryDelimiter($delimiter)
- {
- self::$actionCategoryDelimiter = $delimiter;
- }
-
+ self::$actionCategoryDelimiter = $delimiter; + } + function addWidgets() { Piwik_AddWidget( 'Actions', 'getActions', Piwik_Translate('Actions_SubmenuPages')); Piwik_AddWidget( 'Actions', 'getDownloads', Piwik_Translate('Actions_SubmenuDownloads')); Piwik_AddWidget( 'Actions', 'getOutlinks', Piwik_Translate('Actions_SubmenuOutlinks')); } -
- function addMenus()
- {
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getActions'));
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'getOutlinks'));
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads'));
- }
-
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- $dataTableToSum = array(
- 'Actions_actions',
- 'Actions_downloads',
- 'Actions_outlink',
- );
+ + function addMenus() + { + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getActions')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'getOutlinks')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads')); + } + + function archivePeriod( $notification ) + { + $archiveProcessing = $notification->getNotificationObject(); + + $dataTableToSum = array( + 'Actions_actions', + 'Actions_downloads', + 'Actions_outlink', + ); $maximumRowsInDataTableLevelZero = 200; - $maximumRowsInSubDataTable = 50;
- $archiveProcessing->archiveDataTable($dataTableToSum, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable);
- }
-
- /**
- * Compute all the actions along with their hierarchies.
- *
- * For each action we process the "interest statistics" :
- * visits, unique visitors, bouce count, sum visit length.
- *
- *
- */
- public function archiveDay( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- require_once "Tracker/Action.php";
-
- $this->actionsTablesByType = array(
- Piwik_Tracker_Action::TYPE_ACTION => array(),
- Piwik_Tracker_Action::TYPE_DOWNLOAD => array(),
- Piwik_Tracker_Action::TYPE_OUTLINK => 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(
- 'nb_visits' => 1,
- 'nb_uniq_visitors' => 1,
- 'nb_hits' => 1,
- )));
-
- /*
- * Actions global information
- */
- $query = "SELECT name,
- type,
- count(distinct t1.idvisit) as nb_visits,
- count(distinct visitor_idcookie) as nb_uniq_visitors,
- count(*) as nb_hits
- FROM (".$archiveProcessing->logTable." as t1
- LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit))
- LEFT JOIN ".$archiveProcessing->logActionTable." as t3 USING (idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ $maximumRowsInSubDataTable = 50; + $archiveProcessing->archiveDataTable($dataTableToSum, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); + } + + /** + * Compute all the actions along with their hierarchies. + * + * For each action we process the "interest statistics" : + * visits, unique visitors, bouce count, sum visit length. + * + * + */ + public function archiveDay( $notification ) + { + $archiveProcessing = $notification->getNotificationObject(); + + require_once "Tracker/Action.php"; + + $this->actionsTablesByType = array( + Piwik_Tracker_Action::TYPE_ACTION => array(), + Piwik_Tracker_Action::TYPE_DOWNLOAD => array(), + Piwik_Tracker_Action::TYPE_OUTLINK => 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( + 'nb_visits' => 1, + 'nb_uniq_visitors' => 1, + 'nb_hits' => 1, + ))); + + /* + * Actions global information + */ + $query = "SELECT name, + type, + count(distinct t1.idvisit) as nb_visits, + count(distinct visitor_idcookie) as nb_uniq_visitors, + count(*) as nb_hits + FROM (".$archiveProcessing->logTable." as t1 + LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit)) + LEFT JOIN ".$archiveProcessing->logActionTable." as t3 USING (idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY t3.idaction - ORDER BY nb_hits DESC";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
-
- /*
- * Entry actions
- */
- $query = "SELECT name,
- type,
- count(distinct visitor_idcookie) as entry_nb_unique_visitor,
- count(*) as entry_nb_visits,
- sum(visit_total_actions) as entry_nb_actions,
- sum(visit_total_time) as entry_sum_visit_length,
- sum(case visit_total_actions when 1 then 1 else 0 end) as entry_bounce_count
- FROM ".$archiveProcessing->logTable."
- JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction = idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ ORDER BY nb_hits DESC"; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + + /* + * Entry actions + */ + $query = "SELECT name, + type, + count(distinct visitor_idcookie) as entry_nb_unique_visitor, + count(*) as entry_nb_visits, + sum(visit_total_actions) as entry_nb_actions, + sum(visit_total_time) as entry_sum_visit_length, + sum(case visit_total_actions when 1 then 1 else 0 end) as entry_bounce_count + FROM ".$archiveProcessing->logTable." + JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction = idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY visit_entry_idaction - ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
-
- /*
- * Exit actions
- */
- $query = "SELECT name,
- type,
- count(distinct visitor_idcookie) as exit_nb_unique_visitor,
- count(*) as exit_nb_visits,
- sum(case visit_total_actions when 1 then 1 else 0 end) as exit_bounce_count
-
- FROM ".$archiveProcessing->logTable."
- JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction = idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + + /* + * Exit actions + */ + $query = "SELECT name, + type, + count(distinct visitor_idcookie) as exit_nb_unique_visitor, + count(*) as exit_nb_visits, + sum(case visit_total_actions when 1 then 1 else 0 end) as exit_bounce_count + + FROM ".$archiveProcessing->logTable." + JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction = idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY visit_exit_idaction - ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
- /*
- * Time per action
- */
- $query = "SELECT name,
- type,
- sum(time_spent_ref_action) as sum_time_spent
- FROM (".$archiveProcessing->logTable." log_visit
- JOIN ".$archiveProcessing->logVisitActionTable." log_link_visit_action USING (idvisit))
- JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_ref)
- WHERE visit_server_date = ?
- AND idsite = ?
- GROUP BY idaction_ref
- ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
+ "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + /* + * Time per action + */ + $query = "SELECT name, + type, + sum(time_spent_ref_action) as sum_time_spent + FROM (".$archiveProcessing->logTable." log_visit + JOIN ".$archiveProcessing->logVisitActionTable." log_link_visit_action USING (idvisit)) + JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_ref) + WHERE visit_server_date = ? + AND idsite = ? + GROUP BY idaction_ref + "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + $this->archiveDayRecordInDatabase(); + } + + protected function archiveDayRecordInDatabase() + { $maximumRowsInDataTableLevelZero = 200; $maximumRowsInSubDataTable = 50; $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION]); $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_actions', $s);
+ $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_actions', $s); $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD]); $s = $dataTable->getSerialized($maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_downloads', $s);
-
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]);
- $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable );
- $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_outlink', $s);
+ $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_downloads', $s); + + $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]); + $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_outlink', $s); - unset($this->actionsTablesByType);
- }
-
- static public function splitUrl($url)
- {
- $matches = $split_arr = array();
- $n = preg_match("#://[^/]+(/)#",$url, $matches, PREG_OFFSET_CAPTURE);
- if( $n )
- {
- $host = substr($url, 0, $matches[1][1]);
- $split_arr = array($host, $url);
- }
- else
- {
- $split_arr = array($url);
- }
- return $split_arr;
- }
-
- static public function getActionCategoryFromName($name)
- {
- $isUrl = false;
- // case the name is an URL we dont clean the name the same way
- if(Piwik_Common::isLookLikeUrl($name)
- || preg_match('#^mailto:#',$name))
- {
- $split = self::splitUrl($name);
- $isUrl = true;
- }
- else
+ unset($this->actionsTablesByType); + } + + static public function splitUrl($url) + { + $matches = $split_arr = array(); + $n = preg_match("#://[^/]+(/)#",$url, $matches, PREG_OFFSET_CAPTURE); + if( $n ) + { + $host = substr($url, 0, $matches[1][1]); + $split_arr = array($host, $url); + } + else + { + $split_arr = array($url); + } + return $split_arr; + } + + static public function getActionCategoryFromName($name) + { + $isUrl = false; + // case the name is an URL we dont clean the name the same way + if(Piwik_Common::isLookLikeUrl($name) + || preg_match('#^mailto:#',$name)) + { + $split = self::splitUrl($name); + $isUrl = true; + } + else { if(empty(self::$actionCategoryDelimiter)) { $split = array($name); } else - {
+ { $split = explode(self::$actionCategoryDelimiter, $name, self::$limitLevelSubCategory); - }
- }
- return array( $isUrl, $split);
- }
-
-
- protected function updateActionsTableWithRowQuery($query)
- {
- $rowsProcessed = 0;
-
- while( $row = $query->fetch() )
- {
- // split the actions by category
- $returned = $this->getActionCategoryFromName($row['name']);
- $aActions = $returned[1];
- $isUrl = $returned[0];
-
- // we work on the root table of the given TYPE (either ACTION or DOWNLOAD or OUTLINK etc.)
- $currentTable =& $this->actionsTablesByType[$row['type']];
-
- // go to the level of the subcategory
- $end = count($aActions)-1;
- for($level = 0 ; $level < $end; $level++)
- {
- $actionCategory = $aActions[$level];
- $currentTable =& $currentTable[$actionCategory];
- }
- $actionName = $aActions[$end];
-
- // create a new element in the array for the page
- // we are careful to prefix the pageName with some value so that if a page has the same name
- // as a category we don't overwrite or do other silly things
-
- // if the page name is not a URL we add a / before
- if( !$isUrl )
- {
- // we know that the concatenation of a space and the name of the action
- // will always be unique as all the action names have been trimmed before reaching this point
- $actionName = '/' . $actionName;
- }
- else
- {
- $actionName = ' ' . $actionName;
- }
-
- // 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[$actionName];
-
- // add the row to the matching sub category subtable
- if(!($currentTable instanceof Piwik_DataTable_Row))
- {
- $currentTable = new Piwik_DataTable_Row(
- array( Piwik_DataTable_Row::COLUMNS =>
- array( 'label' => (string)$actionName,
- 'full_url' => (string)$row['name'],
- )
- )
- );
- }
-
- foreach($row as $name => $value)
- {
- // we don't add this information as itnot pertinent
- // name is already set as the label // and it has been cleaned from the categories and extracted from the initial string
- // type is used to partition the different actions type in different table. Adding the info to the row would be a duplicate.
- if($name != 'name' && $name != 'type')
- {
- // in some very rare case, we actually 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('nb_hits') === false)
- {
- // to test this code: delete the entries in log_link_action_visit for
- // a given exit_idaction
- foreach($this->defaultRow->getColumns() as $name => $value)
- {
- $currentTable->addColumn($name, $value);
- }
- }
- // simple count
- $rowsProcessed++;
- }
-
- // just to make sure php copies the last $currentTable in the $parentTable array
- $currentTable =& $this->actionsTablesByType;
-
- return $rowsProcessed;
- }
-}
-
+ } + } + return array( $isUrl, $split); + } + + + protected function updateActionsTableWithRowQuery($query) + { + $rowsProcessed = 0; + + while( $row = $query->fetch() ) + { + // split the actions by category + $returned = $this->getActionCategoryFromName($row['name']); + $aActions = $returned[1]; + $isUrl = $returned[0]; + + // we work on the root table of the given TYPE (either ACTION or DOWNLOAD or OUTLINK etc.) + $currentTable =& $this->actionsTablesByType[$row['type']]; + + // go to the level of the subcategory + $end = count($aActions)-1; + for($level = 0 ; $level < $end; $level++) + { + $actionCategory = $aActions[$level]; + $currentTable =& $currentTable[$actionCategory]; + } + $actionName = $aActions[$end]; + + // create a new element in the array for the page + // we are careful to prefix the pageName with some value so that if a page has the same name + // as a category we don't overwrite or do other silly things + + // if the page name is not a URL we add a / before + if( !$isUrl ) + { + // we know that the concatenation of a space and the name of the action + // will always be unique as all the action names have been trimmed before reaching this point + $actionName = '/' . $actionName; + } + else + { + $actionName = ' ' . $actionName; + } + + // 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[$actionName]; + + // add the row to the matching sub category subtable + if(!($currentTable instanceof Piwik_DataTable_Row)) + { + $currentTable = new Piwik_DataTable_Row( + array( Piwik_DataTable_Row::COLUMNS => + array( 'label' => (string)$actionName, + 'full_url' => (string)$row['name'], + ) + ) + ); + } + + foreach($row as $name => $value) + { + // we don't add this information as itnot pertinent + // name is already set as the label // and it has been cleaned from the categories and extracted from the initial string + // type is used to partition the different actions type in different table. Adding the info to the row would be a duplicate. + if($name != 'name' && $name != 'type') + { + // in some very rare case, we actually 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('nb_hits') === false) + { + // to test this code: delete the entries in log_link_action_visit for + // a given exit_idaction + foreach($this->defaultRow->getColumns() as $name => $value) + { + $currentTable->addColumn($name, $value); + } + } + // simple count + $rowsProcessed++; + } + + // just to make sure php copies the last $currentTable in the $parentTable array + $currentTable =& $this->actionsTablesByType; + + return $rowsProcessed; + } +} + diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php index 4957756f3c..85d3cf9466 100644 --- a/plugins/Actions/Controller.php +++ b/plugins/Actions/Controller.php @@ -149,7 +149,7 @@ class Piwik_Actions_Controller extends Piwik_Controller // and each of them has 1 or 2 hits... $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions')-1, $nbActionsLowPopulationThreshold-1); - $view->setExcludeLowPopulation( $nbActionsLowPopulationThreshold, 'nb_hits' ); + $view->setExcludeLowPopulation( 'nb_hits', $nbActionsLowPopulationThreshold ); $view->main(); |