diff options
Diffstat (limited to 'plugins/Actions')
-rw-r--r-- | plugins/Actions/API.php | 991 | ||||
-rw-r--r-- | plugins/Actions/Actions.php | 1099 | ||||
-rw-r--r-- | plugins/Actions/Archiving.php | 950 | ||||
-rw-r--r-- | plugins/Actions/ArchivingHelper.php | 939 | ||||
-rw-r--r-- | plugins/Actions/Controller.php | 1007 | ||||
-rw-r--r-- | plugins/Actions/templates/indexSiteSearch.tpl | 20 |
6 files changed, 2455 insertions, 2551 deletions
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index bb0367c742..060912f19f 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -1,10 +1,10 @@ <?php /** * Piwik - Open source web analytics - * + * * @link http://piwik.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * + * * @category Piwik_Plugins * @package Piwik_Actions */ @@ -12,543 +12,518 @@ /** * The Actions API lets you request reports for all your Visitor Actions: Page URLs, Page titles (Piwik Events), * File Downloads and Clicks on external websites. - * + * * For example, "getPageTitles" will return all your page titles along with standard <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' target='_blank'>Actions metrics</a> for each row. - * - * It is also possible to request data for a specific Page Title with "getPageTitle" - * and setting the parameter pageName to the page title you wish to request. - * Similarly, you can request metrics for a given Page URL via "getPageUrl", a Download file via "getDownload" + * + * It is also possible to request data for a specific Page Title with "getPageTitle" + * and setting the parameter pageName to the page title you wish to request. + * Similarly, you can request metrics for a given Page URL via "getPageUrl", a Download file via "getDownload" * and an outlink via "getOutlink". - * + * * Note: pageName, pageUrl, outlinkUrl, downloadUrl parameters must be URL encoded before you call the API. * @package Piwik_Actions */ class Piwik_Actions_API { - static private $instance = null; - - /** - * @return Piwik_Actions_API - */ - static public function getInstance() - { - if (self::$instance == null) - { - self::$instance = new self; - } - return self::$instance; - } - - - /** - * Backward compatibility. Fallsback to getPageTitles() instead. - * @deprecated Deprecated since Piwik 0.5 - * @ignore - * - * @param int $idSite - * @param string $period - * @param $date - * @param bool $segment - * @param bool $expanded - * @param bool|int $idSubtable - * @return Piwik_DataTable|Piwik_DataTable_Array - */ - public function getActions( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - return $this->getPageTitles( $idSite, $period, $date, $segment, $expanded, $idSubtable ); - } - - /** - * Returns the list of metrics (pages, downloads, outlinks) - * - * @param int $idSite - * @param string $period - * @param string $date - * @param bool|string $segment - * @param bool|array $columns - * @return Piwik_DataTable - */ - public function get( $idSite, $period, $date, $segment = false, $columns = false) - { - Piwik::checkUserHasViewAccess( $idSite ); - $archive = Piwik_Archive::build( $idSite, $period, $date, $segment ); - - $metrics = array( - 'Actions_nb_pageviews' => 'nb_pageviews', - 'Actions_nb_uniq_pageviews' => 'nb_uniq_pageviews', - 'Actions_nb_downloads' => 'nb_downloads', - 'Actions_nb_uniq_downloads' => 'nb_uniq_downloads', - 'Actions_nb_outlinks' => 'nb_outlinks', - 'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks', - 'Actions_nb_searches' => 'nb_searches', - 'Actions_nb_keywords' => 'nb_keywords', - ); - - // get requested columns - $columns = Piwik::getArrayFromApiParameter($columns); - if(!empty($columns)) - { - // get the columns that are available and requested - $columns = array_intersect($columns, array_values($metrics)); - $columns = array_values($columns); // make sure indexes are right - $nameReplace = array(); - foreach ($columns as $i => $column) - { - $fullColumn = array_search($column, $metrics); - $columns[$i] = $fullColumn; - $nameReplace[$fullColumn] = $column; - } - } - else - { - // get all columns - $columns = array_keys($metrics); - $nameReplace = &$metrics; - } - - $table = $archive->getDataTableFromNumeric($columns); - - // replace labels (remove Actions_) - $table->filter('ReplaceColumnNames', array($nameReplace)); - - return $table; - } + static private $instance = null; /** - * @param int $idSite - * @param string $period - * @param Piwik_Date $date - * @param bool $segment - * @param bool $expanded - * @param bool $idSubtable + * @return Piwik_Actions_API + */ + static public function getInstance() + { + if (self::$instance == null) { + self::$instance = new self; + } + return self::$instance; + } + + + /** + * Backward compatibility. Fallsback to getPageTitles() instead. + * @deprecated Deprecated since Piwik 0.5 + * @ignore * + * @param int $idSite + * @param string $period + * @param $date + * @param bool $segment + * @param bool $expanded + * @param bool|int $idSubtable * @return Piwik_DataTable|Piwik_DataTable_Array */ - public function getPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable ); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable, $expanded); - return $dataTable; - } + public function getActions($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + return $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); + } /** - * @param int $idSite - * @param string $period - * @param Piwik_Date $date - * @param bool $segment - * @param bool $expanded - * @param bool $idSubtable + * Returns the list of metrics (pages, downloads, outlinks) + * + * @param int $idSite + * @param string $period + * @param string $date + * @param bool|string $segment + * @param bool|array $columns + * @return Piwik_DataTable + */ + public function get($idSite, $period, $date, $segment = false, $columns = false) + { + Piwik::checkUserHasViewAccess($idSite); + $archive = Piwik_Archive::build($idSite, $period, $date, $segment); + + $metrics = array( + 'Actions_nb_pageviews' => 'nb_pageviews', + 'Actions_nb_uniq_pageviews' => 'nb_uniq_pageviews', + 'Actions_nb_downloads' => 'nb_downloads', + 'Actions_nb_uniq_downloads' => 'nb_uniq_downloads', + 'Actions_nb_outlinks' => 'nb_outlinks', + 'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks', + 'Actions_nb_searches' => 'nb_searches', + 'Actions_nb_keywords' => 'nb_keywords', + ); + + // get requested columns + $columns = Piwik::getArrayFromApiParameter($columns); + if (!empty($columns)) { + // get the columns that are available and requested + $columns = array_intersect($columns, array_values($metrics)); + $columns = array_values($columns); // make sure indexes are right + $nameReplace = array(); + foreach ($columns as $i => $column) { + $fullColumn = array_search($column, $metrics); + $columns[$i] = $fullColumn; + $nameReplace[$fullColumn] = $column; + } + } else { + // get all columns + $columns = array_keys($metrics); + $nameReplace = & $metrics; + } + + $table = $archive->getDataTableFromNumeric($columns); + + // replace labels (remove Actions_) + $table->filter('ReplaceColumnNames', array($nameReplace)); + + return $table; + } + + /** + * @param int $idSite + * @param string $period + * @param Piwik_Date $date + * @param bool $segment + * @param bool $expanded + * @param bool $idSubtable + * + * @return Piwik_DataTable|Piwik_DataTable_Array + */ + public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable, $expanded); + return $dataTable; + } + + /** + * @param int $idSite + * @param string $period + * @param Piwik_Date $date + * @param bool $segment + * @param bool $expanded + * @param bool $idSubtable * * @return Piwik_DataTable|Piwik_DataTable_Array */ - public function getPageUrlsFollowingSiteSearch( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->keepPagesFollowingSearch($dataTable); - return $dataTable; - } + public function getPageUrlsFollowingSiteSearch($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->keepPagesFollowingSearch($dataTable); + return $dataTable; + } /** - * @param int $idSite - * @param string $period - * @param Piwik_Date $date - * @param bool $segment - * @param bool $expanded - * @param bool $idSubtable + * @param int $idSite + * @param string $period + * @param Piwik_Date $date + * @param bool $segment + * @param bool $expanded + * @param bool $idSubtable * * @return Piwik_DataTable|Piwik_DataTable_Array */ - public function getPageTitlesFollowingSiteSearch( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->keepPagesFollowingSearch($dataTable); - return $dataTable; - } + public function getPageTitlesFollowingSiteSearch($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->keepPagesFollowingSearch($dataTable); + return $dataTable; + } /** * @param Piwik_DataTable $dataTable */ protected function keepPagesFollowingSearch($dataTable) - { - // Keep only pages which are following site search - $dataTable->filter('ColumnCallbackDeleteRow', array( - 'nb_hits_following_search', - create_function('$value', 'return $value > 0;') - )); - } - - /** - * Returns a DataTable with analytics information for every unique entry page URL, for - * the specified site, period & segment. - */ - public function getEntryPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->filterNonEntryActions($dataTable); - return $dataTable; - } - - /** - * Returns a DataTable with analytics information for every unique exit page URL, for - * the specified site, period & segment. - */ - public function getExitPageUrls( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->filterNonExitActions($dataTable); - return $dataTable; - } - - public function getPageUrl( $pageUrl, $idSite, $period, $date, $segment = false) - { - $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false ); - $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable); - return $dataTable; - } - - public function getPageTitles( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) - { - $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable, $expanded); - return $dataTable; - } - - /** - * Returns a Piwik_DataTable with analytics information for every unique entry page title - * for the given site, time period & segment. - */ - public function getEntryPageTitles( $idSite, $period, $date, $segment = false, $expanded = false, - $idSubtable = false ) - { - $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->filterNonEntryActions($dataTable); - return $dataTable; - } - - /** - * Returns a Piwik_DataTable with analytics information for every unique exit page title - * for the given site, time period & segment. - */ - public function getExitPageTitles( $idSite, $period, $date, $segment = false, $expanded = false, - $idSubtable = false ) - { - $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); - $this->filterNonExitActions($dataTable); - return $dataTable; - } - - public function getPageTitle( $pageName, $idSite, $period, $date, $segment = false) - { - $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false ); - $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Piwik_Tracker_Action::TYPE_ACTION_NAME); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable); - return $dataTable; - } - - public function getDownloads( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable ); - $this->filterActionsDataTable($dataTable, $expanded); - return $dataTable; - } - - public function getDownload( $downloadUrl, $idSite, $period, $date, $segment = false) - { - $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false ); - $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Piwik_Tracker_Action::TYPE_DOWNLOAD); - $this->filterActionsDataTable($dataTable); - return $dataTable; - } - - public function getOutlinks( $idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false ) - { - $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable ); - $this->filterActionsDataTable($dataTable, $expanded); - return $dataTable; - } - - public function getOutlink( $outlinkUrl, $idSite, $period, $date, $segment = false) - { - $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false ); - $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Piwik_Tracker_Action::TYPE_OUTLINK); - $this->filterActionsDataTable($dataTable); - return $dataTable; - } - - public function getSiteSearchKeywords( $idSite, $period, $date, $segment = false ) - { - $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment); - $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable); - $this->addPagesPerSearchColumn($dataTable); - return $dataTable; - } - - //Visitors can search, and then click "next" to view more results. This is the average number of search results pages viewed for this keyword. - public function addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_hits') - { - $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_pages_per_search', $columnToRead, 'nb_visits', $precision = 1)); - } - - protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment) - { - $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false); - return $dataTable; - } - - public function getSiteSearchNoResultKeywords( $idSite, $period, $date, $segment = false ) - { - $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment); - // Delete all rows that have some results - $dataTable->filter('ColumnCallbackDeleteRow', - array( - Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, - create_function ( '$value', 'return $value >= 1;') - )); - $dataTable->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW); - $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT); - $this->filterPageDatatable($dataTable); - $this->filterActionsDataTable($dataTable); - $this->addPagesPerSearchColumn($dataTable); - return $dataTable; - } + { + // Keep only pages which are following site search + $dataTable->filter('ColumnCallbackDeleteRow', array( + 'nb_hits_following_search', + create_function('$value', 'return $value > 0;') + )); + } + + /** + * Returns a DataTable with analytics information for every unique entry page URL, for + * the specified site, period & segment. + */ + public function getEntryPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterNonEntryActions($dataTable); + return $dataTable; + } /** - * @param int $idSite - * @param string $period - * @param Piwik_Date $date - * @param bool $segment + * Returns a DataTable with analytics information for every unique exit page URL, for + * the specified site, period & segment. + */ + public function getExitPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = $this->getPageUrls($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterNonExitActions($dataTable); + return $dataTable; + } + + public function getPageUrl($pageUrl, $idSite, $period, $date, $segment = false) + { + $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false); + $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Piwik_Tracker_Action::TYPE_ACTION_URL); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable); + return $dataTable; + } + + public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable, $expanded); + return $dataTable; + } + + /** + * Returns a Piwik_DataTable with analytics information for every unique entry page title + * for the given site, time period & segment. + */ + public function getEntryPageTitles($idSite, $period, $date, $segment = false, $expanded = false, + $idSubtable = false) + { + $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterNonEntryActions($dataTable); + return $dataTable; + } + + /** + * Returns a Piwik_DataTable with analytics information for every unique exit page title + * for the given site, time period & segment. + */ + public function getExitPageTitles($idSite, $period, $date, $segment = false, $expanded = false, + $idSubtable = false) + { + $dataTable = $this->getPageTitles($idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterNonExitActions($dataTable); + return $dataTable; + } + + public function getPageTitle($pageName, $idSite, $period, $date, $segment = false) + { + $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false); + $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Piwik_Tracker_Action::TYPE_ACTION_NAME); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable); + return $dataTable; + } + + public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterActionsDataTable($dataTable, $expanded); + return $dataTable; + } + + public function getDownload($downloadUrl, $idSite, $period, $date, $segment = false) + { + $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false); + $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Piwik_Tracker_Action::TYPE_DOWNLOAD); + $this->filterActionsDataTable($dataTable); + return $dataTable; + } + + public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false) + { + $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable); + $this->filterActionsDataTable($dataTable, $expanded); + return $dataTable; + } + + public function getOutlink($outlinkUrl, $idSite, $period, $date, $segment = false) + { + $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false); + $dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Piwik_Tracker_Action::TYPE_OUTLINK); + $this->filterActionsDataTable($dataTable); + return $dataTable; + } + + public function getSiteSearchKeywords($idSite, $period, $date, $segment = false) + { + $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment); + $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable); + $this->addPagesPerSearchColumn($dataTable); + return $dataTable; + } + + //Visitors can search, and then click "next" to view more results. This is the average number of search results pages viewed for this keyword. + public function addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_hits') + { + $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_pages_per_search', $columnToRead, 'nb_visits', $precision = 1)); + } + + protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment) + { + $dataTable = Piwik_Archive::getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false); + return $dataTable; + } + + public function getSiteSearchNoResultKeywords($idSite, $period, $date, $segment = false) + { + $dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment); + // Delete all rows that have some results + $dataTable->filter('ColumnCallbackDeleteRow', + array( + Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, + create_function('$value', 'return $value >= 1;') + )); + $dataTable->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW); + $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT); + $this->filterPageDatatable($dataTable); + $this->filterActionsDataTable($dataTable); + $this->addPagesPerSearchColumn($dataTable); + return $dataTable; + } + + /** + * @param int $idSite + * @param string $period + * @param Piwik_Date $date + * @param bool $segment * * @return Piwik_DataTable|Piwik_DataTable_Array */ - public function getSiteSearchCategories( $idSite, $period, $date, $segment = false ) - { - Piwik_Actions::checkCustomVariablesPluginEnabled(); - $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false, $_leavePiwikCoreVariables = true); - - $customVarNameToLookFor = Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY; - - $dataTable = new Piwik_DataTable(); - // Handle case where date=last30&period=day - // TODO: this logic should really be refactored somewhere, this is ugly! - if($customVariables instanceof Piwik_DataTable_Array) - { - $dataTable = $customVariables->getEmptyClone(); - - $customVariableDatatables = $customVariables->getArray(); - $dataTables = $dataTable->getArray(); - foreach($customVariableDatatables as $key => $customVariableTableForDate) - { - // we do not enter the IF, in the case idSite=1,3 AND period=day&date=datefrom,dateto, - if(isset($customVariableTableForDate->metadata['period'])) - { - $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor); - if($row) - { - $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString(); - $idSubtable = $row->getIdSubDataTable(); - $categories = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $dateRewrite, $idSubtable, $segment); - $dataTable->addTable($categories, $key); - } - } - } - } - elseif($customVariables instanceof Piwik_DataTable) - { - $row = $customVariables->getRowFromLabel($customVarNameToLookFor); - if($row) - { - $idSubtable = $row->getIdSubDataTable(); - $dataTable = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment); - } - } - $this->filterActionsDataTable($dataTable); - $this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions'); - return $dataTable; - } - - /** - * Will search in the DataTable for a Label matching the searched string - * and return only the matching row, or an empty datatable - */ - protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false, - $searchTree = false) - { - if ($searchTree === false) - { - // build the query parts that are searched inside the tree - if($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME) - { - $searchedString = Piwik_Common::unsanitizeInputValue($search); - } - else - { - $idSite = $callBackParameters[1]; - try { - $searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite); - } catch(Exception $e) { - $searchedString = $search; - } - } - Piwik_Actions_ArchivingHelper::reloadConfig(); - $searchTree = Piwik_Actions_ArchivingHelper::getActionExplodedNames($searchedString, $actionType); - } - - if ($table === false) - { - // fetch the data table - $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters); - - if ($table instanceof Piwik_DataTable_Array) - { - // search an array of tables, e.g. when using date=last30 - // note that if the root is an array, we filter all children - // if an array occurs inside the nested table, we only look for the first match (see below) - $newTableArray = $table->getEmptyClone(); - - foreach ($table->getArray() as $label => $subTable) - { - $newSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree); - - $newTableArray->addTable($newSubTable, $label); - } - - return $newTableArray; - } - - } - - return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree); - } - - /** - * This looks very similar to LabelFilter.php should it be refactored somehow? FIXME - */ - protected function doFilterPageDatatableSearch($callBackParameters, $table, $searchTree) - { - // filter a data table array - if ($table instanceof Piwik_DataTable_Array) - { - foreach ($table->getArray() as $subTable) - { - $filteredSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree); - - if ($filteredSubTable->getRowsCount() > 0) - { - // match found in a sub table, return and stop searching the others - return $filteredSubTable; - } - } - - // nothing found in all sub tables - return new Piwik_DataTable; - } - - // filter regular data table - if ($table instanceof Piwik_DataTable) - { - // search for the first part of the tree search - $search = array_shift($searchTree); - $row = $table->getRowFromLabel($search); - if ($row === false) - { - // not found - $result = new Piwik_DataTable; - $result->metadata = $table->metadata; - return $result; - } - - // end of tree search reached - if (count($searchTree) == 0) - { - $result = new Piwik_DataTable(); - $result->addRow($row); - $result->metadata = $table->metadata; - return $result; - } - - // match found on this level and more levels remaining: go deeper - $idSubTable = $row->getIdSubDataTable(); - $callBackParameters[6] = $idSubTable; - $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters); - return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree); - } - - throw new Exception("For this API function, DataTable ".get_class($table)." is not supported"); - } - - /** - * Common filters for Page URLs and Page Titles - */ - protected function filterPageDatatable($dataTable) - { - // Average time on page = total time on page / number visits on that page - $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_on_page', 'sum_time_spent', 'nb_visits', 0)); - - // Bounce rate = single page visits on this page / visits started on this page - $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'entry_bounce_count', 'entry_nb_visits', 0)); - - // % Exit = Number of visits that finished on this page / visits on this page - $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('exit_rate', 'exit_nb_visits', 'nb_visits', 0)); - - // Handle performance analytics - $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)) > 0); - if ($hasTimeGeneration) { - // Average generation time = total generation time / number of pageviews - $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3)); - } else { - // No generation time: remove it from the API output and add it to empty_columns metadata, so that - // the columns can also be removed from the view - $dataTable->filter('ColumnDelete', array(array(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION))); - if ($dataTable instanceof Piwik_DataTable) { - $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME); - if (!is_array($emptyColumns)) { - $emptyColumns = array(); - } - $emptyColumns[] = 'sum_time_generation'; - $emptyColumns[] = 'avg_time_generation'; - $dataTable->setMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns); - } - } - } - - /** - * Common filters for all Actions API getters - */ - protected function filterActionsDataTable($dataTable, $expanded = false) - { - // Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes - // (in the transition period between pre 1.2 and post 1.2 datatable structure) - $dataTable->filter('ReplaceColumnNames'); - $dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded)); - - $dataTable->queueFilter('ReplaceSummaryRowLabel'); - } - - /** - * Removes DataTable rows referencing actions that were never the first action of a visit. - * - * @param Piwik_DataTable $dataTable - */ - private function filterNonEntryActions( $dataTable ) - { - $dataTable->filter('ColumnCallbackDeleteRow', array('entry_nb_visits', 'strlen')); - } - - /** - * Removes DataTable rows referencing actions that were never the last action of a visit. - * - * @param Piwik_DataTable $dataTable - */ - private function filterNonExitActions( $dataTable ) - { - $dataTable->filter('ColumnCallbackDeleteRow', array('exit_nb_visits', 'strlen')); - } + public function getSiteSearchCategories($idSite, $period, $date, $segment = false) + { + Piwik_Actions::checkCustomVariablesPluginEnabled(); + $customVariables = Piwik_CustomVariables_API::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false, $_leavePiwikCoreVariables = true); + + $customVarNameToLookFor = Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY; + + $dataTable = new Piwik_DataTable(); + // Handle case where date=last30&period=day + // TODO: this logic should really be refactored somewhere, this is ugly! + if ($customVariables instanceof Piwik_DataTable_Array) { + $dataTable = $customVariables->getEmptyClone(); + + $customVariableDatatables = $customVariables->getArray(); + $dataTables = $dataTable->getArray(); + foreach ($customVariableDatatables as $key => $customVariableTableForDate) { + // we do not enter the IF, in the case idSite=1,3 AND period=day&date=datefrom,dateto, + if (isset($customVariableTableForDate->metadata['period'])) { + $row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor); + if ($row) { + $dateRewrite = $customVariableTableForDate->metadata['period']->getDateStart()->toString(); + $idSubtable = $row->getIdSubDataTable(); + $categories = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $dateRewrite, $idSubtable, $segment); + $dataTable->addTable($categories, $key); + } + } + } + } elseif ($customVariables instanceof Piwik_DataTable) { + $row = $customVariables->getRowFromLabel($customVarNameToLookFor); + if ($row) { + $idSubtable = $row->getIdSubDataTable(); + $dataTable = Piwik_CustomVariables_API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment); + } + } + $this->filterActionsDataTable($dataTable); + $this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions'); + return $dataTable; + } + + /** + * Will search in the DataTable for a Label matching the searched string + * and return only the matching row, or an empty datatable + */ + protected function getFilterPageDatatableSearch($callBackParameters, $search, $actionType, $table = false, + $searchTree = false) + { + if ($searchTree === false) { + // build the query parts that are searched inside the tree + if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_NAME) { + $searchedString = Piwik_Common::unsanitizeInputValue($search); + } else { + $idSite = $callBackParameters[1]; + try { + $searchedString = Piwik_Tracker_Action::excludeQueryParametersFromUrl($search, $idSite); + } catch (Exception $e) { + $searchedString = $search; + } + } + Piwik_Actions_ArchivingHelper::reloadConfig(); + $searchTree = Piwik_Actions_ArchivingHelper::getActionExplodedNames($searchedString, $actionType); + } + + if ($table === false) { + // fetch the data table + $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters); + + if ($table instanceof Piwik_DataTable_Array) { + // search an array of tables, e.g. when using date=last30 + // note that if the root is an array, we filter all children + // if an array occurs inside the nested table, we only look for the first match (see below) + $newTableArray = $table->getEmptyClone(); + + foreach ($table->getArray() as $label => $subTable) { + $newSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree); + + $newTableArray->addTable($newSubTable, $label); + } + + return $newTableArray; + } + + } + + return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree); + } + + /** + * This looks very similar to LabelFilter.php should it be refactored somehow? FIXME + */ + protected function doFilterPageDatatableSearch($callBackParameters, $table, $searchTree) + { + // filter a data table array + if ($table instanceof Piwik_DataTable_Array) { + foreach ($table->getArray() as $subTable) { + $filteredSubTable = $this->doFilterPageDatatableSearch($callBackParameters, $subTable, $searchTree); + + if ($filteredSubTable->getRowsCount() > 0) { + // match found in a sub table, return and stop searching the others + return $filteredSubTable; + } + } + + // nothing found in all sub tables + return new Piwik_DataTable; + } + + // filter regular data table + if ($table instanceof Piwik_DataTable) { + // search for the first part of the tree search + $search = array_shift($searchTree); + $row = $table->getRowFromLabel($search); + if ($row === false) { + // not found + $result = new Piwik_DataTable; + $result->metadata = $table->metadata; + return $result; + } + + // end of tree search reached + if (count($searchTree) == 0) { + $result = new Piwik_DataTable(); + $result->addRow($row); + $result->metadata = $table->metadata; + return $result; + } + + // match found on this level and more levels remaining: go deeper + $idSubTable = $row->getIdSubDataTable(); + $callBackParameters[6] = $idSubTable; + $table = call_user_func_array(array('Piwik_Archive', 'getDataTableFromArchive'), $callBackParameters); + return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree); + } + + throw new Exception("For this API function, DataTable " . get_class($table) . " is not supported"); + } + + /** + * Common filters for Page URLs and Page Titles + */ + protected function filterPageDatatable($dataTable) + { + // Average time on page = total time on page / number visits on that page + $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_on_page', 'sum_time_spent', 'nb_visits', 0)); + + // Bounce rate = single page visits on this page / visits started on this page + $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'entry_bounce_count', 'entry_nb_visits', 0)); + + // % Exit = Number of visits that finished on this page / visits on this page + $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('exit_rate', 'exit_nb_visits', 'nb_visits', 0)); + + // Handle performance analytics + $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)) > 0); + if ($hasTimeGeneration) { + // Average generation time = total generation time / number of pageviews + $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3)); + } else { + // No generation time: remove it from the API output and add it to empty_columns metadata, so that + // the columns can also be removed from the view + $dataTable->filter('ColumnDelete', array(array(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION))); + if ($dataTable instanceof Piwik_DataTable) { + $emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME); + if (!is_array($emptyColumns)) { + $emptyColumns = array(); + } + $emptyColumns[] = 'sum_time_generation'; + $emptyColumns[] = 'avg_time_generation'; + $dataTable->setMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns); + } + } + } + + /** + * Common filters for all Actions API getters + */ + protected function filterActionsDataTable($dataTable, $expanded = false) + { + // Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes + // (in the transition period between pre 1.2 and post 1.2 datatable structure) + $dataTable->filter('ReplaceColumnNames'); + $dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded)); + + $dataTable->queueFilter('ReplaceSummaryRowLabel'); + } + + /** + * Removes DataTable rows referencing actions that were never the first action of a visit. + * + * @param Piwik_DataTable $dataTable + */ + private function filterNonEntryActions($dataTable) + { + $dataTable->filter('ColumnCallbackDeleteRow', array('entry_nb_visits', 'strlen')); + } + + /** + * Removes DataTable rows referencing actions that were never the last action of a visit. + * + * @param Piwik_DataTable $dataTable + */ + private function filterNonExitActions($dataTable) + { + $dataTable->filter('ColumnCallbackDeleteRow', array('exit_nb_visits', 'strlen')); + } } diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 230968e66f..635b0d95c4 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -8,7 +8,7 @@ * @category Piwik_Plugins * @package Piwik_Actions */ - + /** * Actions plugin * @@ -18,592 +18,585 @@ */ class Piwik_Actions extends Piwik_Plugin { - public function getInformation() - { - $info = array( - 'description' => Piwik_Translate('Actions_PluginDescription'), - 'author' => 'Piwik', - 'author_homepage' => 'http://piwik.org/', - 'version' => Piwik_Version::VERSION, - ); - return $info; - } - - - public function getListHooksRegistered() - { - $hooks = array( - 'ArchiveProcessing_Day.compute' => 'archiveDay', - 'ArchiveProcessing_Period.compute' => 'archivePeriod', - 'WidgetsList.add' => 'addWidgets', - 'Menu.add' => 'addMenus', - 'API.getReportMetadata' => 'getReportMetadata', - 'API.getSegmentsMetadata' => 'getSegmentsMetadata', - ); - return $hooks; - } - - /** - * @param Piwik_Event_Notification $notification notification object - */ - public function getSegmentsMetadata($notification) - { - $segments =& $notification->getNotificationObject(); - $sqlFilter = array($this, 'getIdActionFromSegment'); - - // entry and exit pages of visit - $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnEntryPageURL', - 'segment' => 'entryPageUrl', - 'sqlSegment' => 'log_visit.visit_entry_idaction_url', - 'sqlFilter' => $sqlFilter, + public function getInformation() + { + $info = array( + 'description' => Piwik_Translate('Actions_PluginDescription'), + 'author' => 'Piwik', + 'author_homepage' => 'http://piwik.org/', + 'version' => Piwik_Version::VERSION, + ); + return $info; + } + + + public function getListHooksRegistered() + { + $hooks = array( + 'ArchiveProcessing_Day.compute' => 'archiveDay', + 'ArchiveProcessing_Period.compute' => 'archivePeriod', + 'WidgetsList.add' => 'addWidgets', + 'Menu.add' => 'addMenus', + 'API.getReportMetadata' => 'getReportMetadata', + 'API.getSegmentsMetadata' => 'getSegmentsMetadata', + ); + return $hooks; + } + + /** + * @param Piwik_Event_Notification $notification notification object + */ + public function getSegmentsMetadata($notification) + { + $segments =& $notification->getNotificationObject(); + $sqlFilter = array($this, 'getIdActionFromSegment'); + + // entry and exit pages of visit + $segments[] = array( + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnEntryPageURL', + 'segment' => 'entryPageUrl', + 'sqlSegment' => 'log_visit.visit_entry_idaction_url', + 'sqlFilter' => $sqlFilter, ); $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnEntryPageTitle', - 'segment' => 'entryPageTitle', - 'sqlSegment' => 'log_visit.visit_entry_idaction_name', - 'sqlFilter' => $sqlFilter, + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnEntryPageTitle', + 'segment' => 'entryPageTitle', + 'sqlSegment' => 'log_visit.visit_entry_idaction_name', + 'sqlFilter' => $sqlFilter, ); $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnExitPageURL', - 'segment' => 'exitPageUrl', - 'sqlSegment' => 'log_visit.visit_exit_idaction_url', - 'sqlFilter' => $sqlFilter, + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnExitPageURL', + 'segment' => 'exitPageUrl', + 'sqlSegment' => 'log_visit.visit_exit_idaction_url', + 'sqlFilter' => $sqlFilter, ); $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnExitPageTitle', - 'segment' => 'exitPageTitle', - 'sqlSegment' => 'log_visit.visit_exit_idaction_name', - 'sqlFilter' => $sqlFilter, + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnExitPageTitle', + 'segment' => 'exitPageTitle', + 'sqlSegment' => 'log_visit.visit_exit_idaction_name', + 'sqlFilter' => $sqlFilter, ); - + // single pages $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnPageURL', - 'segment' => 'pageUrl', - 'sqlSegment' => 'log_link_visit_action.idaction_url', - 'sqlFilter' => $sqlFilter, - 'acceptedValues' => "All these segments must be URL encoded, for example: ".urlencode('http://example.com/path/page?query'), + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnPageURL', + 'segment' => 'pageUrl', + 'sqlSegment' => 'log_link_visit_action.idaction_url', + 'sqlFilter' => $sqlFilter, + 'acceptedValues' => "All these segments must be URL encoded, for example: " . urlencode('http://example.com/path/page?query'), ); - $segments[] = array( - 'type' => 'dimension', - 'category' => 'Actions_Actions', - 'name' => 'Actions_ColumnPageName', - 'segment' => 'pageTitle', - 'sqlSegment' => 'log_link_visit_action.idaction_name', - 'sqlFilter' => $sqlFilter, - ); - // TODO here could add keyword segment and hack $sqlFilter to make it select the right idaction - } - - /** - * Convert segment expression to an action ID or an SQL expression. - * - * This method is used as a sqlFilter-callback for the segments of this plugin. - * Usually, these callbacks only return a value that should be compared to the - * column in the database. In this case, that doesn't work since multiple IDs - * can match an expression (e.g. "pageUrl=@foo"). - * @param string $string - * @param string $sqlField - * @param string $matchType - * @throws Exception - * @return array|int|string - */ - public function getIdActionFromSegment($string, $sqlField, $matchType='==') - { - // Field is visit_*_idaction_url or visit_*_idaction_name - $actionType = strpos($sqlField, '_name') === false - ? Piwik_Tracker_Action::TYPE_ACTION_URL - : Piwik_Tracker_Action::TYPE_ACTION_NAME; - - if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL) - { - // for urls trim protocol and www because it is not recorded in the db - $string = preg_replace('@^http[s]?://(www\.)?@i', '', $string); - } - + $segments[] = array( + 'type' => 'dimension', + 'category' => 'Actions_Actions', + 'name' => 'Actions_ColumnPageName', + 'segment' => 'pageTitle', + 'sqlSegment' => 'log_link_visit_action.idaction_name', + 'sqlFilter' => $sqlFilter, + ); + // TODO here could add keyword segment and hack $sqlFilter to make it select the right idaction + } + + /** + * Convert segment expression to an action ID or an SQL expression. + * + * This method is used as a sqlFilter-callback for the segments of this plugin. + * Usually, these callbacks only return a value that should be compared to the + * column in the database. In this case, that doesn't work since multiple IDs + * can match an expression (e.g. "pageUrl=@foo"). + * @param string $string + * @param string $sqlField + * @param string $matchType + * @throws Exception + * @return array|int|string + */ + public function getIdActionFromSegment($string, $sqlField, $matchType = '==') + { + // Field is visit_*_idaction_url or visit_*_idaction_name + $actionType = strpos($sqlField, '_name') === false + ? Piwik_Tracker_Action::TYPE_ACTION_URL + : Piwik_Tracker_Action::TYPE_ACTION_NAME; + + if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL) { + // for urls trim protocol and www because it is not recorded in the db + $string = preg_replace('@^http[s]?://(www\.)?@i', '', $string); + } + // exact matches work by returning the id directly - if ($matchType == Piwik_SegmentExpression::MATCH_EQUAL - || $matchType == Piwik_SegmentExpression::MATCH_NOT_EQUAL) - { + if ($matchType == Piwik_SegmentExpression::MATCH_EQUAL + || $matchType == Piwik_SegmentExpression::MATCH_NOT_EQUAL + ) { $sql = Piwik_Tracker_Action::getSqlSelectActionId(); $bind = array($string, $string, $actionType); $idAction = Piwik_FetchOne($sql, $bind); // if the action is not found, we hack -100 to ensure it tries to match against an integer // otherwise binding idaction_name to "false" returns some rows for some reasons (in case &segment=pageTitle==Větrnásssssss) - if(empty($idAction)) - { + if (empty($idAction)) { $idAction = -100; } return $idAction; } - + // now, we handle the cases =@ (contains) and !@ (does not contain) - + // build the expression based on the match type - $sql = 'SELECT idaction FROM '.Piwik_Common::prefixTable('log_action').' WHERE '; - switch ($matchType) - { + $sql = 'SELECT idaction FROM ' . Piwik_Common::prefixTable('log_action') . ' WHERE '; + switch ($matchType) { case '=@': // use concat to make sure, no %s occurs because some plugins use %s in their sql - $sql .= '( name LIKE CONCAT("%", ?, "%") AND type = '.$actionType.' )'; + $sql .= '( name LIKE CONCAT("%", ?, "%") AND type = ' . $actionType . ' )'; break; case '!@': - $sql .= '( name NOT LIKE CONCAT("%", ?, "%") AND type = '.$actionType.' )'; + $sql .= '( name NOT LIKE CONCAT("%", ?, "%") AND type = ' . $actionType . ' )'; break; default: throw new Exception("This match type is not available for action-segments."); break; } - + return array( // mark that the returned value is an sql-expression instead of a literal value - 'SQL' => $sql, - 'bind' => $string + 'SQL' => $sql, + 'bind' => $string ); - } - - /** - * Returns metadata for available reports - * - * @param Piwik_Event_Notification $notification notification object - */ - public function getReportMetadata($notification) - { - $reports = &$notification->getNotificationObject(); - - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_Actions') . ' - ' . Piwik_Translate('General_MainMetrics'), - 'module' => 'Actions', - 'action' => 'get', - 'metrics' => array( - 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'), - 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'), - 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'), - 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'), - 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'), - 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks'), - 'nb_searches' => Piwik_Translate('Actions_ColumnSearches'), - 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'), - ), - 'metricsDocumentation' => array( - 'nb_pageviews' => Piwik_Translate('General_ColumnPageviewsDocumentation'), - 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), - 'nb_downloads' => Piwik_Translate('Actions_ColumnClicksDocumentation'), - 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), - 'nb_outlinks' => Piwik_Translate('Actions_ColumnClicksDocumentation'), - 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), - 'nb_searches' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), + } + + /** + * Returns metadata for available reports + * + * @param Piwik_Event_Notification $notification notification object + */ + public function getReportMetadata($notification) + { + $reports = & $notification->getNotificationObject(); + + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_Actions') . ' - ' . Piwik_Translate('General_MainMetrics'), + 'module' => 'Actions', + 'action' => 'get', + 'metrics' => array( + 'nb_pageviews' => Piwik_Translate('General_ColumnPageviews'), + 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviews'), + 'nb_downloads' => Piwik_Translate('Actions_ColumnDownloads'), + 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueDownloads'), + 'nb_outlinks' => Piwik_Translate('Actions_ColumnOutlinks'), + 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueOutlinks'), + 'nb_searches' => Piwik_Translate('Actions_ColumnSearches'), + 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'), + ), + 'metricsDocumentation' => array( + 'nb_pageviews' => Piwik_Translate('General_ColumnPageviewsDocumentation'), + 'nb_uniq_pageviews' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), + 'nb_downloads' => Piwik_Translate('Actions_ColumnClicksDocumentation'), + 'nb_uniq_downloads' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), + 'nb_outlinks' => Piwik_Translate('Actions_ColumnClicksDocumentation'), + 'nb_uniq_outlinks' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), + 'nb_searches' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), // 'nb_keywords' => Piwik_Translate('Actions_ColumnSiteSearchKeywords'), - ), - 'processedMetrics' => false, - 'order' => 1 - ); - - $metrics = array( - 'nb_hits' => Piwik_Translate('General_ColumnPageviews'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), - 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), - 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPage'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRate'), - 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTime') - ); - - $documentation = array( - 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), - 'bounce_rate' => Piwik_Translate('General_ColumnPageBounceRateDocumentation'), - 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPageDocumentation'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation'), - 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTimeDocumentation'), - ); - - // pages report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_PageUrls'), - 'module' => 'Actions', - 'action' => 'getPageUrls', - 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), - 'metrics' => $metrics, - 'metricsDocumentation' => $documentation, - 'documentation' => Piwik_Translate('Actions_PagesReportDocumentation', '<br />') - .'<br />'.Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getPageUrls', - 'order' => 2 - ); - - // entry pages report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_SubmenuPagesEntry'), - 'module' => 'Actions', - 'action' => 'getEntryPageUrls', - 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), - 'metrics' => array( - 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'), - 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'), - 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), - ), - 'metricsDocumentation' => array( - 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'), - 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'), - 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation') - ), - 'documentation' => Piwik_Translate('Actions_EntryPagesReportDocumentation', '<br />') - .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getEntryPageUrls', - 'order' => 3 - ); - - // exit pages report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_SubmenuPagesExit'), - 'module' => 'Actions', - 'action' => 'getExitPageUrls', - 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), - 'metrics' => array( - 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRate') - ), - 'metricsDocumentation' => array( - 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation') - ), - 'documentation' => Piwik_Translate('Actions_ExitPagesReportDocumentation', '<br />') - .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getExitPageUrls', - 'order' => 4 - ); - - // page titles report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_SubmenuPageTitles'), - 'module' => 'Actions', - 'action' => 'getPageTitles', - 'dimension' => Piwik_Translate('Actions_ColumnPageName'), - 'metrics' => $metrics, - 'metricsDocumentation' => $documentation, - 'documentation' => Piwik_Translate('Actions_PageTitlesReportDocumentation', array('<br />', htmlentities('<title>'))), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getPageTitles', - 'order' => 5, - - ); - - // entry page titles report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_EntryPageTitles'), - 'module' => 'Actions', - 'action' => 'getEntryPageTitles', - 'dimension' => Piwik_Translate('Actions_ColumnPageName'), - 'metrics' => array( - 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'), - 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'), - 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), - ), - 'metricsDocumentation' => array( - 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'), - 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'), - 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation') - ), - 'documentation' => Piwik_Translate('Actions_ExitPageTitlesReportDocumentation', '<br />') - .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getEntryPageTitles', - 'order' => 6 - ); - - // exit page titles report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_ExitPageTitles'), - 'module' => 'Actions', - 'action' => 'getExitPageTitles', - 'dimension' => Piwik_Translate('Actions_ColumnPageName'), - 'metrics' => array( - 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRate') - ), - 'metricsDocumentation' => array( - 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'), - 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), - 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation') - ), - 'documentation' => Piwik_Translate('Actions_EntryPageTitlesReportDocumentation', '<br />') - .' '.Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getExitPageTitles', - 'order' => 7 - ); - - $documentation = array( - 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), - 'nb_hits' => Piwik_Translate('Actions_ColumnClicksDocumentation') - ); - - // outlinks report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_SubmenuOutlinks'), - 'module' => 'Actions', - 'action' => 'getOutlinks', - 'dimension' => Piwik_Translate('Actions_ColumnClickedURL'), - 'metrics' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicks'), - 'nb_hits' => Piwik_Translate('Actions_ColumnClicks') - ), - 'metricsDocumentation' => $documentation, - 'documentation' => Piwik_Translate('Actions_OutlinksReportDocumentation').' ' - .Piwik_Translate('Actions_OutlinkDocumentation').'<br />' - .Piwik_Translate('General_UsePlusMinusIconsDocumentation'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getOutlinks', - 'order' => 8, - ); - - // downloads report - $reports[] = array( - 'category' => Piwik_Translate('Actions_Actions'), - 'name' => Piwik_Translate('Actions_SubmenuDownloads'), - 'module' => 'Actions', - 'action' => 'getDownloads', - 'dimension' => Piwik_Translate('Actions_ColumnDownloadURL'), - 'metrics' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueDownloads'), - 'nb_hits' => Piwik_Translate('Actions_ColumnDownloads') - ), - 'metricsDocumentation' => $documentation, - 'documentation' => Piwik_Translate('Actions_DownloadsReportDocumentation', '<br />'), - 'processedMetrics' => false, - 'actionToLoadSubTables' => 'getDownloads', - 'order' => 9, - ); - - if($this->isSiteSearchEnabled()) - { - // Search Keywords - $reports[] = array( - 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), - 'name' => Piwik_Translate('Actions_WidgetSearchKeywords'), - 'module' => 'Actions', - 'action' => 'getSiteSearchKeywords', - 'dimension' => Piwik_Translate('Actions_ColumnSearchKeyword'), - 'metrics' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), - 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), - ), - 'metricsDocumentation' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), - 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), - ), - 'documentation' => Piwik_Translate('Actions_SiteSearchKeywordsDocumentation') . '<br/><br/>' . Piwik_Translate('Actions_SiteSearchIntro') . '<br/><br/>' - . '<a href="http://piwik.org/docs/site-search/" target="_blank">'. Piwik_Translate('Actions_LearnMoreAboutSiteSearchLink') . '</a>', - 'processedMetrics' => false, - 'order' => 15 - ); - // No Result Search Keywords - $reports[] = array( - 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), - 'name' => Piwik_Translate('Actions_WidgetSearchNoResultKeywords'), - 'module' => 'Actions', - 'action' => 'getSiteSearchNoResultKeywords', - 'dimension' => Piwik_Translate('Actions_ColumnNoResultKeyword'), - 'metrics' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), - ), - 'metricsDocumentation' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), - ), - 'documentation' => Piwik_Translate('Actions_SiteSearchIntro'). '<br /><br />' . Piwik_Translate('Actions_SiteSearchKeywordsNoResultDocumentation'), - 'processedMetrics' => false, - 'order' => 16 - ); - - if(self::isCustomVariablesPluginsEnabled()) { - // Search Categories - $reports[] = array( - 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), - 'name' => Piwik_Translate('Actions_WidgetSearchCategories'), - 'module' => 'Actions', - 'action' => 'getSiteSearchCategories', - 'dimension' => Piwik_Translate('Actions_ColumnSearchCategory'), - 'metrics' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), - 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), - ), - 'metricsDocumentation' => array( - 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), - 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'), - 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), - ), - 'documentation' => Piwik_Translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik_Translate('Actions_SiteSearchCategories2'), - 'processedMetrics' => false, - 'order' => 17 - ); - } - - $documentation = Piwik_Translate('Actions_SiteSearchFollowingPagesDoc') .'<br/>'.Piwik_Translate('General_UsePlusMinusIconsDocumentation'); - // Pages URLs following Search - $reports[] = array( - 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), - 'name' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), - 'module' => 'Actions', - 'action' => 'getPageUrlsFollowingSiteSearch', - 'dimension' => Piwik_Translate('General_ColumnDestinationPage'), - 'metrics' => array( - 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'), - 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'), - ), - 'metricsDocumentation' => array( - 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'), - 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), - ), - 'documentation' => $documentation, - 'processedMetrics' => false, - 'order' => 18 - ); - // Pages Titles following Search - $reports[] = array( - 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), - 'name' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), - 'module' => 'Actions', - 'action' => 'getPageTitlesFollowingSiteSearch', - 'dimension' => Piwik_Translate('General_ColumnDestinationPage'), - 'metrics' => array( - 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'), - 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'), - ), - 'metricsDocumentation' => array( - 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'), - 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), - ), - 'documentation' => $documentation, - 'processedMetrics' => false, - 'order' => 19 - ); - } - } - - function addWidgets() - { - Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getPageUrls'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPageTitles', 'Actions', 'getPageTitles'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuOutlinks', 'Actions', 'getOutlinks'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuDownloads', 'Actions', 'getDownloads'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPagesEntry', 'Actions', 'getEntryPageUrls'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetPagesExit', 'Actions', 'getExitPageUrls'); - Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetEntryPageTitles', 'Actions', 'getEntryPageTitles' ); - Piwik_AddWidget( 'Actions_Actions', 'Actions_WidgetExitPageTitles', 'Actions', 'getExitPageTitles' ); - - if($this->isSiteSearchEnabled()) - { - Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchKeywords', 'Actions', 'getSiteSearchKeywords'); - - if(self::isCustomVariablesPluginsEnabled()) { - Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchCategories', 'Actions', 'getSiteSearchCategories'); - } - Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetSearchNoResultKeywords', 'Actions', 'getSiteSearchNoResultKeywords'); - Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetPageUrlsFollowingSearch', 'Actions', 'getPageUrlsFollowingSiteSearch'); - Piwik_AddWidget( 'Actions_SubmenuSitesearch', 'Actions_WidgetPageTitlesFollowingSearch', 'Actions', 'getPageTitlesFollowingSiteSearch'); - } - } - - function addMenus() - { - Piwik_AddMenu('Actions_Actions', '', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 15); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 1); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesEntry', array('module' => 'Actions', 'action' => 'indexEntryPageUrls'), true, 2); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesExit', array('module' => 'Actions', 'action' => 'indexExitPageUrls'), true, 3); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'indexPageTitles'), true, 4); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 6); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 7); - - if($this->isSiteSearchEnabled()) - { - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuSitesearch', array('module' => 'Actions', 'action' => 'indexSiteSearch'), true, 5); - } - } - - protected function isSiteSearchEnabled() - { - $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int'); - if($idSite == 0 ) { - return false; - } - return Piwik_Site::isSiteSearchEnabledFor($idSite); - } - - - /** - * @param Piwik_Event_Notification $notification notification object - * @return mixed - */ - function archivePeriod( $notification ) - { - $archiveProcessing = $notification->getNotificationObject(); - - if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return; - - $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite); - return $actionsArchiving->archivePeriod($archiveProcessing); - } - - /** - * Compute all the actions along with their hierarchies. - * - * For each action we process the "interest statistics" : - * visits, unique visitors, bounce count, sum visit length. - * - * @param Piwik_Event_Notification $notification notification object - */ - public function archiveDay( $notification ) - { - /* @var $archiveProcessing Piwik_ArchiveProcessing_Day */ - $archiveProcessing = $notification->getNotificationObject(); - - if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return; - - $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite); - return $actionsArchiving->archiveDay($archiveProcessing); - } - - static public function checkCustomVariablesPluginEnabled() - { - if(!self::isCustomVariablesPluginsEnabled()) - { - throw new Exception("To Track Site Search Categories, please ask the Piwik Administrator to enable the 'Custom Variables' plugin in Settings > Plugins."); - } - } - - static protected function isCustomVariablesPluginsEnabled() - { - return Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables'); - } + ), + 'processedMetrics' => false, + 'order' => 1 + ); + + $metrics = array( + 'nb_hits' => Piwik_Translate('General_ColumnPageviews'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), + 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), + 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPage'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRate'), + 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTime') + ); + + $documentation = array( + 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), + 'bounce_rate' => Piwik_Translate('General_ColumnPageBounceRateDocumentation'), + 'avg_time_on_page' => Piwik_Translate('General_ColumnAverageTimeOnPageDocumentation'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation'), + 'avg_time_generation' => Piwik_Translate('General_ColumnAverageGenerationTimeDocumentation'), + ); + + // pages report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_PageUrls'), + 'module' => 'Actions', + 'action' => 'getPageUrls', + 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), + 'metrics' => $metrics, + 'metricsDocumentation' => $documentation, + 'documentation' => Piwik_Translate('Actions_PagesReportDocumentation', '<br />') + . '<br />' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getPageUrls', + 'order' => 2 + ); + + // entry pages report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_SubmenuPagesEntry'), + 'module' => 'Actions', + 'action' => 'getEntryPageUrls', + 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), + 'metrics' => array( + 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'), + 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'), + 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), + ), + 'metricsDocumentation' => array( + 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'), + 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'), + 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation') + ), + 'documentation' => Piwik_Translate('Actions_EntryPagesReportDocumentation', '<br />') + . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getEntryPageUrls', + 'order' => 3 + ); + + // exit pages report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_SubmenuPagesExit'), + 'module' => 'Actions', + 'action' => 'getExitPageUrls', + 'dimension' => Piwik_Translate('Actions_ColumnPageURL'), + 'metrics' => array( + 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRate') + ), + 'metricsDocumentation' => array( + 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation') + ), + 'documentation' => Piwik_Translate('Actions_ExitPagesReportDocumentation', '<br />') + . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getExitPageUrls', + 'order' => 4 + ); + + // page titles report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_SubmenuPageTitles'), + 'module' => 'Actions', + 'action' => 'getPageTitles', + 'dimension' => Piwik_Translate('Actions_ColumnPageName'), + 'metrics' => $metrics, + 'metricsDocumentation' => $documentation, + 'documentation' => Piwik_Translate('Actions_PageTitlesReportDocumentation', array('<br />', htmlentities('<title>'))), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getPageTitles', + 'order' => 5, + + ); + + // entry page titles report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_EntryPageTitles'), + 'module' => 'Actions', + 'action' => 'getEntryPageTitles', + 'dimension' => Piwik_Translate('Actions_ColumnPageName'), + 'metrics' => array( + 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrances'), + 'entry_bounce_count' => Piwik_Translate('General_ColumnBounces'), + 'bounce_rate' => Piwik_Translate('General_ColumnBounceRate'), + ), + 'metricsDocumentation' => array( + 'entry_nb_visits' => Piwik_Translate('General_ColumnEntrancesDocumentation'), + 'entry_bounce_count' => Piwik_Translate('General_ColumnBouncesDocumentation'), + 'bounce_rate' => Piwik_Translate('General_ColumnBounceRateForPageDocumentation') + ), + 'documentation' => Piwik_Translate('Actions_ExitPageTitlesReportDocumentation', '<br />') + . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getEntryPageTitles', + 'order' => 6 + ); + + // exit page titles report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_ExitPageTitles'), + 'module' => 'Actions', + 'action' => 'getExitPageTitles', + 'dimension' => Piwik_Translate('Actions_ColumnPageName'), + 'metrics' => array( + 'exit_nb_visits' => Piwik_Translate('General_ColumnExits'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviews'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRate') + ), + 'metricsDocumentation' => array( + 'exit_nb_visits' => Piwik_Translate('General_ColumnExitsDocumentation'), + 'nb_visits' => Piwik_Translate('General_ColumnUniquePageviewsDocumentation'), + 'exit_rate' => Piwik_Translate('General_ColumnExitRateDocumentation') + ), + 'documentation' => Piwik_Translate('Actions_EntryPageTitlesReportDocumentation', '<br />') + . ' ' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getExitPageTitles', + 'order' => 7 + ); + + $documentation = array( + 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicksDocumentation'), + 'nb_hits' => Piwik_Translate('Actions_ColumnClicksDocumentation') + ); + + // outlinks report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_SubmenuOutlinks'), + 'module' => 'Actions', + 'action' => 'getOutlinks', + 'dimension' => Piwik_Translate('Actions_ColumnClickedURL'), + 'metrics' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueClicks'), + 'nb_hits' => Piwik_Translate('Actions_ColumnClicks') + ), + 'metricsDocumentation' => $documentation, + 'documentation' => Piwik_Translate('Actions_OutlinksReportDocumentation') . ' ' + . Piwik_Translate('Actions_OutlinkDocumentation') . '<br />' + . Piwik_Translate('General_UsePlusMinusIconsDocumentation'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getOutlinks', + 'order' => 8, + ); + + // downloads report + $reports[] = array( + 'category' => Piwik_Translate('Actions_Actions'), + 'name' => Piwik_Translate('Actions_SubmenuDownloads'), + 'module' => 'Actions', + 'action' => 'getDownloads', + 'dimension' => Piwik_Translate('Actions_ColumnDownloadURL'), + 'metrics' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnUniqueDownloads'), + 'nb_hits' => Piwik_Translate('Actions_ColumnDownloads') + ), + 'metricsDocumentation' => $documentation, + 'documentation' => Piwik_Translate('Actions_DownloadsReportDocumentation', '<br />'), + 'processedMetrics' => false, + 'actionToLoadSubTables' => 'getDownloads', + 'order' => 9, + ); + + if ($this->isSiteSearchEnabled()) { + // Search Keywords + $reports[] = array( + 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), + 'name' => Piwik_Translate('Actions_WidgetSearchKeywords'), + 'module' => 'Actions', + 'action' => 'getSiteSearchKeywords', + 'dimension' => Piwik_Translate('Actions_ColumnSearchKeyword'), + 'metrics' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), + 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), + ), + 'metricsDocumentation' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), + 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), + ), + 'documentation' => Piwik_Translate('Actions_SiteSearchKeywordsDocumentation') . '<br/><br/>' . Piwik_Translate('Actions_SiteSearchIntro') . '<br/><br/>' + . '<a href="http://piwik.org/docs/site-search/" target="_blank">' . Piwik_Translate('Actions_LearnMoreAboutSiteSearchLink') . '</a>', + 'processedMetrics' => false, + 'order' => 15 + ); + // No Result Search Keywords + $reports[] = array( + 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), + 'name' => Piwik_Translate('Actions_WidgetSearchNoResultKeywords'), + 'module' => 'Actions', + 'action' => 'getSiteSearchNoResultKeywords', + 'dimension' => Piwik_Translate('Actions_ColumnNoResultKeyword'), + 'metrics' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), + ), + 'metricsDocumentation' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), + ), + 'documentation' => Piwik_Translate('Actions_SiteSearchIntro') . '<br /><br />' . Piwik_Translate('Actions_SiteSearchKeywordsNoResultDocumentation'), + 'processedMetrics' => false, + 'order' => 16 + ); + + if (self::isCustomVariablesPluginsEnabled()) { + // Search Categories + $reports[] = array( + 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), + 'name' => Piwik_Translate('Actions_WidgetSearchCategories'), + 'module' => 'Actions', + 'action' => 'getSiteSearchCategories', + 'dimension' => Piwik_Translate('Actions_ColumnSearchCategory'), + 'metrics' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearches'), + 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearch'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExits'), + ), + 'metricsDocumentation' => array( + 'nb_visits' => Piwik_Translate('Actions_ColumnSearchesDocumentation'), + 'nb_pages_per_search' => Piwik_Translate('Actions_ColumnPagesPerSearchDocumentation'), + 'exit_rate' => Piwik_Translate('Actions_ColumnSearchExitsDocumentation'), + ), + 'documentation' => Piwik_Translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik_Translate('Actions_SiteSearchCategories2'), + 'processedMetrics' => false, + 'order' => 17 + ); + } + + $documentation = Piwik_Translate('Actions_SiteSearchFollowingPagesDoc') . '<br/>' . Piwik_Translate('General_UsePlusMinusIconsDocumentation'); + // Pages URLs following Search + $reports[] = array( + 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), + 'name' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), + 'module' => 'Actions', + 'action' => 'getPageUrlsFollowingSiteSearch', + 'dimension' => Piwik_Translate('General_ColumnDestinationPage'), + 'metrics' => array( + 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'), + 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'), + ), + 'metricsDocumentation' => array( + 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'), + 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), + ), + 'documentation' => $documentation, + 'processedMetrics' => false, + 'order' => 18 + ); + // Pages Titles following Search + $reports[] = array( + 'category' => Piwik_Translate('Actions_SubmenuSitesearch'), + 'name' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), + 'module' => 'Actions', + 'action' => 'getPageTitlesFollowingSiteSearch', + 'dimension' => Piwik_Translate('General_ColumnDestinationPage'), + 'metrics' => array( + 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearch'), + 'nb_hits' => Piwik_Translate('General_ColumnTotalPageviews'), + ), + 'metricsDocumentation' => array( + 'nb_hits_following_search' => Piwik_Translate('General_ColumnViewedAfterSearchDocumentation'), + 'nb_hits' => Piwik_Translate('General_ColumnPageviewsDocumentation'), + ), + 'documentation' => $documentation, + 'processedMetrics' => false, + 'order' => 19 + ); + } + } + + function addWidgets() + { + Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getPageUrls'); + Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPageTitles', 'Actions', 'getPageTitles'); + Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuOutlinks', 'Actions', 'getOutlinks'); + Piwik_AddWidget('Actions_Actions', 'Actions_SubmenuDownloads', 'Actions', 'getDownloads'); + Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPagesEntry', 'Actions', 'getEntryPageUrls'); + Piwik_AddWidget('Actions_Actions', 'Actions_WidgetPagesExit', 'Actions', 'getExitPageUrls'); + Piwik_AddWidget('Actions_Actions', 'Actions_WidgetEntryPageTitles', 'Actions', 'getEntryPageTitles'); + Piwik_AddWidget('Actions_Actions', 'Actions_WidgetExitPageTitles', 'Actions', 'getExitPageTitles'); + + if ($this->isSiteSearchEnabled()) { + Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchKeywords', 'Actions', 'getSiteSearchKeywords'); + + if (self::isCustomVariablesPluginsEnabled()) { + Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchCategories', 'Actions', 'getSiteSearchCategories'); + } + Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetSearchNoResultKeywords', 'Actions', 'getSiteSearchNoResultKeywords'); + Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetPageUrlsFollowingSearch', 'Actions', 'getPageUrlsFollowingSiteSearch'); + Piwik_AddWidget('Actions_SubmenuSitesearch', 'Actions_WidgetPageTitlesFollowingSearch', 'Actions', 'getPageTitlesFollowingSiteSearch'); + } + } + + function addMenus() + { + Piwik_AddMenu('Actions_Actions', '', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 15); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 1); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesEntry', array('module' => 'Actions', 'action' => 'indexEntryPageUrls'), true, 2); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPagesExit', array('module' => 'Actions', 'action' => 'indexExitPageUrls'), true, 3); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'indexPageTitles'), true, 4); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 6); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 7); + + if ($this->isSiteSearchEnabled()) { + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuSitesearch', array('module' => 'Actions', 'action' => 'indexSiteSearch'), true, 5); + } + } + + protected function isSiteSearchEnabled() + { + $idSite = Piwik_Common::getRequestVar('idSite', 0, 'int'); + if ($idSite == 0) { + return false; + } + return Piwik_Site::isSiteSearchEnabledFor($idSite); + } + + + /** + * @param Piwik_Event_Notification $notification notification object + * @return mixed + */ + function archivePeriod($notification) + { + $archiveProcessing = $notification->getNotificationObject(); + + if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return; + + $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite); + return $actionsArchiving->archivePeriod($archiveProcessing); + } + + /** + * Compute all the actions along with their hierarchies. + * + * For each action we process the "interest statistics" : + * visits, unique visitors, bounce count, sum visit length. + * + * @param Piwik_Event_Notification $notification notification object + */ + public function archiveDay($notification) + { + /* @var $archiveProcessing Piwik_ArchiveProcessing_Day */ + $archiveProcessing = $notification->getNotificationObject(); + + if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return; + + $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite); + return $actionsArchiving->archiveDay($archiveProcessing); + } + + static public function checkCustomVariablesPluginEnabled() + { + if (!self::isCustomVariablesPluginsEnabled()) { + throw new Exception("To Track Site Search Categories, please ask the Piwik Administrator to enable the 'Custom Variables' plugin in Settings > Plugins."); + } + } + + static protected function isCustomVariablesPluginsEnabled() + { + return Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables'); + } } diff --git a/plugins/Actions/Archiving.php b/plugins/Actions/Archiving.php index c0d36d1083..db71faf788 100644 --- a/plugins/Actions/Archiving.php +++ b/plugins/Actions/Archiving.php @@ -16,100 +16,100 @@ */ class Piwik_Actions_Archiving { - protected $actionsTablesByType = null; - - public static $actionTypes = array( - Piwik_Tracker_Action::TYPE_ACTION_URL, - Piwik_Tracker_Action::TYPE_OUTLINK, - Piwik_Tracker_Action::TYPE_DOWNLOAD, - Piwik_Tracker_Action::TYPE_ACTION_NAME, - Piwik_Tracker_Action::TYPE_SITE_SEARCH, - ); - - 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, - ); - - protected $isSiteSearchEnabled = false; - - function __construct($idSite) - { - $this->isSiteSearchEnabled = Piwik_Site::isSiteSearchEnabledFor($idSite); - } - - /** - * 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', - 'Actions_sitesearch', - ); - $nameToCount = $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', - 'Actions_nb_searches', - )); - - // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table - $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $nameToCount['Actions_sitesearch']['level0']); - 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->initActionsTables(); - $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, + protected $actionsTablesByType = null; + + public static $actionTypes = array( + Piwik_Tracker_Action::TYPE_ACTION_URL, + Piwik_Tracker_Action::TYPE_OUTLINK, + Piwik_Tracker_Action::TYPE_DOWNLOAD, + Piwik_Tracker_Action::TYPE_ACTION_NAME, + Piwik_Tracker_Action::TYPE_SITE_SEARCH, + ); + + 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, + ); + + protected $isSiteSearchEnabled = false; + + function __construct($idSite) + { + $this->isSiteSearchEnabled = Piwik_Site::isSiteSearchEnabledFor($idSite); + } + + /** + * 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', + 'Actions_sitesearch', + ); + $nameToCount = $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', + 'Actions_nb_searches', + )); + + // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table + $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $nameToCount['Actions_sitesearch']['level0']); + 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->initActionsTables(); + $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, @@ -117,432 +117,422 @@ class Piwik_Actions_Archiving count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`, count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`, sum( - case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ." is null + case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null then 0 - else " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ." + else " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " end ) / 1000 as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION . "`, sum( - case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION ." is null + case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null then 0 else 1 end ) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`"; - $from = array( - "log_link_visit_action", - array( - "table" => "log_action", - "joinOn" => "log_link_visit_action.%s = log_action.idaction" - ) - ); + $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 >= ? + $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'); - if ($this->isSiteSearchEnabled()) - { - $rankingQuery->addColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min'); - $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum'); - } - $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, 'sum'); - $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum'); - $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType)); - } - - // Special Magic to get - // 1) No result Keywords - // 2) For each page view, count number of times the referrer page was a Site Search - if($this->isSiteSearchEnabled()) - { - $selectFlagNoResultKeywords = ", - CASE WHEN (MAX(log_link_visit_action.custom_var_v". Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT.") = 0 AND log_link_visit_action.custom_var_k". Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT." = '". Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT. "') THEN 1 ELSE 0 END AS `" . Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`"; - - //we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request - $from[] = array( - "table" => "log_action", - "tableAlias" => "log_action_name_ref", - "joinOn" => "log_link_visit_action.idaction_name_ref = log_action_name_ref.idaction" - ); - - $selectSiteSearchFollowingPages = ", - SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `". Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS."`"; - - $select .= $selectFlagNoResultKeywords - . $selectSiteSearchFollowingPages; - // Not working yet + $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'); + if ($this->isSiteSearchEnabled()) { + $rankingQuery->addColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min'); + $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum'); + } + $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, 'sum'); + $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum'); + $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType)); + } + + // Special Magic to get + // 1) No result Keywords + // 2) For each page view, count number of times the referrer page was a Site Search + if ($this->isSiteSearchEnabled()) { + $selectFlagNoResultKeywords = ", + CASE WHEN (MAX(log_link_visit_action.custom_var_v" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . ") = 0 AND log_link_visit_action.custom_var_k" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . " = '" . Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT . "') THEN 1 ELSE 0 END AS `" . Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`"; + + //we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request + $from[] = array( + "table" => "log_action", + "tableAlias" => "log_action_name_ref", + "joinOn" => "log_link_visit_action.idaction_name_ref = log_action_name_ref.idaction" + ); + + $selectSiteSearchFollowingPages = ", + SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `" . Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`"; + + $select .= $selectFlagNoResultKeywords + . $selectSiteSearchFollowingPages; + // Not working yet // $selectRefPageIsStartingSiteSearch = ", // SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_ACTION_NAME . " THEN 1 ELSE 0 END) AS `". Piwik_Archive::INDEX_PAGE_STARTING_SITE_SEARCH_NB_HITS."`"; // . $selectRefPageIsStartingSiteSearch // . ", idaction_url_ref, idaction_name_ref" - } - - $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, - "idaction_name", $archiveProcessing, $rankingQuery); - - $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, - "idaction_url", $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 + } + + $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, + "idaction_name", $archiveProcessing, $rankingQuery); + + $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, + "idaction_url", $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 >= ? + $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 + $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 >= ? + $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 + $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 >= ? + $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(); - - /** @var Piwik_DataTable $dataTable */ - $dataTable = $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 = $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 = $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 = $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); - - $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_SITE_SEARCH]; - self::deleteInvalidSummedColumnsFromDataTable($dataTable); - $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable); - $s = $dataTable->getSerialized( Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation ); - $archiveProcessing->insertBlobRecord('Actions_sitesearch', $s); - $archiveProcessing->insertNumericRecord('Actions_nb_searches', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS))); - $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $dataTable->getRowsCount()); - destroy($dataTable); - - destroy($this->actionsTablesByType); - } - - protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable) - { - $columnsToDelete = array( - Piwik_Archive::INDEX_NB_UNIQ_VISITORS, - Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, - Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS, - Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS, - Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH, - Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS, - Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT, - Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS, - ); - $dataTable->deleteColumns($columnsToDelete); - } - - static protected function removeEmptyColumns($dataTable) - { - // Delete all columns that have a value of zero - $dataTable->filter('ColumnDelete', array( - $columnsToRemove = array(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS), - $columnsToKeep = array(), - $deleteIfZeroOnly = true - )); - } - - - /** - * 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); - - $bind = array(); - - // get query with segmentation - $query = $archiveProcessing->getSegment()->getSelectQuery( - $select, $from, $where, $bind, $orderBy, $groupBy); - - // extend bindings - $bind = array_merge(array( $archiveProcessing->getStartDatetimeUTC(), - $archiveProcessing->getEndDatetimeUTC(), - $archiveProcessing->idsite - ), - $query['bind'] - ); - - // replace the rest of the %s - $querySql = str_replace("%s", $sprintfField, $query['sql']); - - // apply ranking query - if ($rankingQuery) - { - $querySql = $rankingQuery->generateQuery($querySql); - } - - // 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) - { - if ($idSubtable !== null) - { - $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable); - self::deleteInvalidSummedColumnsFromDataTable($subtable); - } - - if ($row instanceof Piwik_DataTable_Row_DataTableSummary) - { - $row->recalculate(); - } - - foreach(self::$invalidSummedColumnNameToDeleteFromDayArchive as $name) - { - $row->deleteColumn($name); - } - } - } - - // And this as well - self::removeEmptyColumns($dataTable); - } - - /** - * Initializes the DataTables created by the archiveDay function. - */ - private function initActionsTables() - { - $this->actionsTablesByType = array(); - foreach (self::$actionTypes as $type) - { - $dataTable = new Piwik_DataTable(); - $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero); - - $this->actionsTablesByType[$type] = $dataTable; - } - } - - protected function isSiteSearchEnabled() - { - return $this->isSiteSearchEnabled; - } + $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(); + + /** @var Piwik_DataTable $dataTable */ + $dataTable = $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 = $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 = $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 = $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); + + $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_SITE_SEARCH]; + self::deleteInvalidSummedColumnsFromDataTable($dataTable); + $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable); + $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation); + $archiveProcessing->insertBlobRecord('Actions_sitesearch', $s); + $archiveProcessing->insertNumericRecord('Actions_nb_searches', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS))); + $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $dataTable->getRowsCount()); + destroy($dataTable); + + destroy($this->actionsTablesByType); + } + + protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable) + { + $columnsToDelete = array( + Piwik_Archive::INDEX_NB_UNIQ_VISITORS, + Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, + Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS, + Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS, + Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH, + Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS, + Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT, + Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS, + ); + $dataTable->deleteColumns($columnsToDelete); + } + + static protected function removeEmptyColumns($dataTable) + { + // Delete all columns that have a value of zero + $dataTable->filter('ColumnDelete', array( + $columnsToRemove = array(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS), + $columnsToKeep = array(), + $deleteIfZeroOnly = true + )); + } + + + /** + * 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); + + $bind = array(); + + // get query with segmentation + $query = $archiveProcessing->getSegment()->getSelectQuery( + $select, $from, $where, $bind, $orderBy, $groupBy); + + // extend bindings + $bind = array_merge(array($archiveProcessing->getStartDatetimeUTC(), + $archiveProcessing->getEndDatetimeUTC(), + $archiveProcessing->idsite + ), + $query['bind'] + ); + + // replace the rest of the %s + $querySql = str_replace("%s", $sprintfField, $query['sql']); + + // apply ranking query + if ($rankingQuery) { + $querySql = $rankingQuery->generateQuery($querySql); + } + + // 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 + ) { + if ($idSubtable !== null) { + $subtable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable); + self::deleteInvalidSummedColumnsFromDataTable($subtable); + } + + if ($row instanceof Piwik_DataTable_Row_DataTableSummary) { + $row->recalculate(); + } + + foreach (self::$invalidSummedColumnNameToDeleteFromDayArchive as $name) { + $row->deleteColumn($name); + } + } + } + + // And this as well + self::removeEmptyColumns($dataTable); + } + + /** + * Initializes the DataTables created by the archiveDay function. + */ + private function initActionsTables() + { + $this->actionsTablesByType = array(); + foreach (self::$actionTypes as $type) { + $dataTable = new Piwik_DataTable(); + $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero); + + $this->actionsTablesByType[$type] = $dataTable; + } + } + + protected function isSiteSearchEnabled() + { + return $this->isSiteSearchEnabled; + } } diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php index 30fb58b555..739e8a2fef 100644 --- a/plugins/Actions/ArchivingHelper.php +++ b/plugins/Actions/ArchivingHelper.php @@ -19,495 +19,452 @@ 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']; - } - - if($row['type'] != Piwik_Tracker_Action::TYPE_SITE_SEARCH) - { - unset($row[Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT]); - } - - // This will appear as <url /> in the API, which is actually very important to keep - // eg. When there's at least one row in a report that does not have a URL, not having this <url/> would break HTML/PDF reports. - $url = ''; - if($row['type'] == Piwik_Tracker_Action::TYPE_SITE_SEARCH - || $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME) - { - $url = null; - } - elseif(!empty($row['name']) - && $row['name'] != Piwik_DataTable::LABEL_SUMMARY_ROW) - { - $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$row['name'], $row['url_prefix']); - } - - 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; - } - - $actionRow = self::getActionRow($actionName, $actionType, $urlPrefix, $actionsTablesByType); - - self::setCachedActionRow($idaction, $actionType, $actionRow); - } - else - { - $actionRow = self::getCachedActionRow($row['idaction'], $row['type']); - - // Action processed as "to skip" for some reasons - if($actionRow === false) - { - continue; - } - } - - - if (is_null($actionRow)) - { - continue; - } - - // Here we do ensure that, the Metadata URL set for a given row, is the one from the Pageview with the most hits. - // This is to ensure that when, different URLs are loaded with the same page name. - // For example http://piwik.org and http://id.piwik.org are reported in Piwik > Actions > Pages with /index - // But, we must make sure http://piwik.org is used to link & for transitions - // Note: this code is partly duplicated from Piwik_DataTable_Row->sumRowMetadata() - if( !is_null($url) - && !$actionRow->isSummaryRow()) - { - if(($existingUrl = $actionRow->getMetadata('url')) !== false) - { - if( !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) - && $row[Piwik_Archive::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed) - { - $actionRow->setMetadata('url', $url); - $actionRow->maxVisitsSummed = $row[Piwik_Archive::INDEX_PAGE_NB_HITS]; - } - } - else - { - $actionRow->setMetadata('url', $url); - $actionRow->maxVisitsSummed = !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Archive::INDEX_PAGE_NB_HITS] : 0; - } - } - - if ($row['type'] != Piwik_Tracker_Action::TYPE_ACTION_URL - && $row['type'] != Piwik_Tracker_Action::TYPE_ACTION_NAME) { - // only keep performance metrics when they're used (i.e. for URLs and page titles) - if (array_key_exists(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, $row)) { - unset($row[Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION]); - } - if (array_key_exists(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) { - unset($row[Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]); - } - } - - unset($row['name']); - unset($row['type']); - unset($row['idaction']); - unset($row['url_prefix']); - - 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, and 2 actions get recorded for the same name - // - this could also happen when 2 URLs end up having the same label (eg. 2 subdomains get aggregated to the "/index" page name) - if(($alreadyValue = $actionRow->getColumn($name)) !== false) - { - $actionRow->setColumn($name, $alreadyValue+$value); - } - else - { - $actionRow->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($actionRow->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) - { - $actionRow->addColumn($name, $value); - } - } - $rowsProcessed++; - } - - // just to make sure php copies the last $actionRow in the $parentTable array - $actionRow =& $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)Row - $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 static function getActionRow( $actionName, $actionType, $urlPrefix=null, &$actionsTablesByType ) - { - // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.) - /* @var Piwik_DataTable $currentTable */ - $currentTable =& $actionsTablesByType[$actionType]; - - // check for ranking query cut-off - if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW) - { - $summaryRow = $currentTable->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW); - if ($summaryRow === false) - { - $summaryRow = $currentTable->addSummaryRow(self::createSummaryRow()); - } - return $summaryRow; - } - - // go to the level of the subcategory - $actionExplodedNames = self::getActionExplodedNames($actionName, $actionType, $urlPrefix); - list($row, $level) = $currentTable->walkPath( - $actionExplodedNames, self::getDefaultRowColumns(), self::$maximumRowsInSubDataTable); - - return $row; - } - - /** - * 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) - { - // Site Search does not split Search keywords - if($type == Piwik_Tracker_Action::TYPE_SITE_SEARCH) - { - return array($name); - } - - $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]; - $urlFragment = $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( $isUrl ) - { - $urlFragment = Piwik_Tracker_Action::processUrlFragment($urlFragment); - if(!empty($urlFragment)) { - $name .= '#' . $urlFragment; - } - } - - 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 public 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; - } - - /** - * 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; - } - - /** - * Returns the default columns for a row in an Actions DataTable. - * - * @return array - */ - private static function getDefaultRowColumns() - { - return array(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); - } - - /** - * Creates a summary row for an Actions DataTable. - * - * @return Piwik_DataTable_Row - */ - private static function createSummaryRow() - { - return new Piwik_DataTable_Row(array( - Piwik_DataTable_Row::COLUMNS => - array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW) + self::getDefaultRowColumns() - )); - } + 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']; + } + + if ($row['type'] != Piwik_Tracker_Action::TYPE_SITE_SEARCH) { + unset($row[Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT]); + } + + // This will appear as <url /> in the API, which is actually very important to keep + // eg. When there's at least one row in a report that does not have a URL, not having this <url/> would break HTML/PDF reports. + $url = ''; + if ($row['type'] == Piwik_Tracker_Action::TYPE_SITE_SEARCH + || $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME + ) { + $url = null; + } elseif (!empty($row['name']) + && $row['name'] != Piwik_DataTable::LABEL_SUMMARY_ROW + ) { + $url = Piwik_Tracker_Action::reconstructNormalizedUrl((string)$row['name'], $row['url_prefix']); + } + + 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; + } + + $actionRow = self::getActionRow($actionName, $actionType, $urlPrefix, $actionsTablesByType); + + self::setCachedActionRow($idaction, $actionType, $actionRow); + } else { + $actionRow = self::getCachedActionRow($row['idaction'], $row['type']); + + // Action processed as "to skip" for some reasons + if ($actionRow === false) { + continue; + } + } + + + if (is_null($actionRow)) { + continue; + } + + // Here we do ensure that, the Metadata URL set for a given row, is the one from the Pageview with the most hits. + // This is to ensure that when, different URLs are loaded with the same page name. + // For example http://piwik.org and http://id.piwik.org are reported in Piwik > Actions > Pages with /index + // But, we must make sure http://piwik.org is used to link & for transitions + // Note: this code is partly duplicated from Piwik_DataTable_Row->sumRowMetadata() + if (!is_null($url) + && !$actionRow->isSummaryRow() + ) { + if (($existingUrl = $actionRow->getMetadata('url')) !== false) { + if (!empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) + && $row[Piwik_Archive::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed + ) { + $actionRow->setMetadata('url', $url); + $actionRow->maxVisitsSummed = $row[Piwik_Archive::INDEX_PAGE_NB_HITS]; + } + } else { + $actionRow->setMetadata('url', $url); + $actionRow->maxVisitsSummed = !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Archive::INDEX_PAGE_NB_HITS] : 0; + } + } + + if ($row['type'] != Piwik_Tracker_Action::TYPE_ACTION_URL + && $row['type'] != Piwik_Tracker_Action::TYPE_ACTION_NAME + ) { + // only keep performance metrics when they're used (i.e. for URLs and page titles) + if (array_key_exists(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, $row)) { + unset($row[Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION]); + } + if (array_key_exists(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) { + unset($row[Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]); + } + } + + unset($row['name']); + unset($row['type']); + unset($row['idaction']); + unset($row['url_prefix']); + + 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, and 2 actions get recorded for the same name + // - this could also happen when 2 URLs end up having the same label (eg. 2 subdomains get aggregated to the "/index" page name) + if (($alreadyValue = $actionRow->getColumn($name)) !== false) { + $actionRow->setColumn($name, $alreadyValue + $value); + } else { + $actionRow->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 ($actionRow->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) { + $actionRow->addColumn($name, $value); + } + } + $rowsProcessed++; + } + + // just to make sure php copies the last $actionRow in the $parentTable array + $actionRow =& $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)Row + $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 static function getActionRow($actionName, $actionType, $urlPrefix = null, &$actionsTablesByType) + { + // we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.) + /* @var Piwik_DataTable $currentTable */ + $currentTable =& $actionsTablesByType[$actionType]; + + // check for ranking query cut-off + if ($actionName == Piwik_DataTable::LABEL_SUMMARY_ROW) { + $summaryRow = $currentTable->getRowFromId(Piwik_DataTable::ID_SUMMARY_ROW); + if ($summaryRow === false) { + $summaryRow = $currentTable->addSummaryRow(self::createSummaryRow()); + } + return $summaryRow; + } + + // go to the level of the subcategory + $actionExplodedNames = self::getActionExplodedNames($actionName, $actionType, $urlPrefix); + list($row, $level) = $currentTable->walkPath( + $actionExplodedNames, self::getDefaultRowColumns(), self::$maximumRowsInSubDataTable); + + return $row; + } + + /** + * 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) + { + // Site Search does not split Search keywords + if ($type == Piwik_Tracker_Action::TYPE_SITE_SEARCH) { + return array($name); + } + + $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]; + $urlFragment = $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 ($isUrl) { + $urlFragment = Piwik_Tracker_Action::processUrlFragment($urlFragment); + if (!empty($urlFragment)) { + $name .= '#' . $urlFragment; + } + } + + 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 public 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; + } + + /** + * 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; + } + + /** + * Returns the default columns for a row in an Actions DataTable. + * + * @return array + */ + private static function getDefaultRowColumns() + { + return array(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); + } + + /** + * Creates a summary row for an Actions DataTable. + * + * @return Piwik_DataTable_Row + */ + private static function createSummaryRow() + { + return new Piwik_DataTable_Row(array( + Piwik_DataTable_Row::COLUMNS => + array('label' => Piwik_DataTable::LABEL_SUMMARY_ROW) + self::getDefaultRowColumns() + )); + } } diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php index 00eb49a804..aed8efd683 100644 --- a/plugins/Actions/Controller.php +++ b/plugins/Actions/Controller.php @@ -16,513 +16,502 @@ */ class Piwik_Actions_Controller extends Piwik_Controller { - const ACTIONS_REPORT_ROWS_DISPLAY = 100; - - protected function getPageUrlsView($currentAction, $controllerActionSubtable, $apiAction) - { - $view = Piwik_ViewDataTable::factory(); - $view->init($this->pluginName, $currentAction, $apiAction, $controllerActionSubtable); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageURL')); - return $view; - } - - /** - * PAGES - * @param bool $fetch - * @return string - */ - - public function indexPageUrls($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuPages'), - $this->getPageUrls(true), $fetch); - } - - public function getPageUrls($fetch = false) - { - $view = $this->getPageUrlsView(__FUNCTION__, 'getPageUrls', 'Actions.getPageUrls'); - $this->configureViewPages($view); - $this->configureViewActions($view); - return $this->renderView($view, $fetch); - } - - protected function configureViewPages($view) - { - $view->setColumnsToDisplay( array('label','nb_hits','nb_visits', 'bounce_rate', 'avg_time_on_page', 'exit_rate', 'avg_time_generation') ); - } - - /** - * ENTRY PAGES - * @param bool $fetch - * @return string|void - */ - public function indexEntryPageUrls($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuPagesEntry'), - $this->getEntryPageUrls(true), $fetch); - } - - public function getEntryPageUrls($fetch = false) - { - $view = $this->getPageUrlsView(__FUNCTION__, 'getEntryPageUrls', 'Actions.getEntryPageUrls'); - $this->configureViewEntryPageUrls($view); - $this->configureViewActions($view); - return $this->renderView($view, $fetch); - } - - protected function configureViewEntryPageUrls($view) - { - $view->setSortedColumn('entry_nb_visits'); - $view->setColumnsToDisplay( array('label','entry_nb_visits', 'entry_bounce_count', 'bounce_rate') ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageURL')); - $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces')); - $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances')); - $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesEntry'), array( - 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles') - )); - $view->setReportUrl('Actions', $this->getEntryPageUrlActionForLink()); - } - - /* - * EXIT PAGES - */ - public function indexExitPageUrls($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuPagesExit'), - $this->getExitPageUrls(true), $fetch); - } - - public function getExitPageUrls($fetch = false) - { - $view = $this->getPageUrlsView(__FUNCTION__, 'getExitPageUrls', 'Actions.getExitPageUrls'); - $this->configureViewExitPageUrls($view); - $this->configureViewActions($view); - return $this->renderView($view, $fetch); - } - - protected function configureViewExitPageUrls($view) - { - $view->setSortedColumn('exit_nb_visits'); - $view->setColumnsToDisplay( array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate') ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageURL')); - $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits')); - $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesExit'), array( - 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles') - )); - $view->setReportUrl('Actions', $this->getExitPageUrlActionForLink()); - } - - /* - * SITE SEARCH - */ - public function indexSiteSearch() - { - $view = Piwik_View::factory('indexSiteSearch'); - - $view->keywords = $this->getSiteSearchKeywords( true ); - $view->noResultKeywords = $this->getSiteSearchNoResultKeywords( true ); - $view->pagesUrlsFollowingSiteSearch = $this->getPageUrlsFollowingSiteSearch( true ); - - $categoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables'); - if($categoryTrackingEnabled) - { - $view->categories = $this->getSiteSearchCategories( true ); - } - - echo $view->render(); - } - - public function getSiteSearchKeywords($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchKeywords' ); - $this->configureViewSiteSearchKeywords($view); - return $this->renderView($view, $fetch); - } - - public function getSiteSearchNoResultKeywords($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchNoResultKeywords' ); - $this->configureViewSiteSearchKeywords($view); - $view->setColumnsToDisplay(array('label', 'nb_visits', 'exit_rate')); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnNoResultKeyword')); - return $this->renderView($view, $fetch); - } - - public function configureViewSiteSearchKeywords(Piwik_ViewDataTable $view) - { - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchKeyword')); - $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search', 'exit_rate')); - $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches')); - $view->setColumnTranslation('exit_rate', str_replace("% ", "% ", Piwik_Translate('Actions_ColumnSearchExits'))); - $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch')); - $view->disableShowBarChart(); - $view->disableShowAllColumns(); - } - - public function getSiteSearchCategories($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, __FUNCTION__, 'Actions.getSiteSearchCategories' ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchCategory')); - $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches')); - $view->setColumnsToDisplay( array('label','nb_visits', 'nb_pages_per_search') ); - $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch')); - $view->disableShowAllColumns(); - $view->disableShowBarChart(); - $view->disableRowEvolution(); - return $this->renderView($view, $fetch); - } - - - public function getPageUrlsFollowingSiteSearch($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, __FUNCTION__, 'Actions.getPageUrlsFollowingSiteSearch', 'getPageUrlsFollowingSiteSearch' ); - $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), array( - 'Actions.getPageTitlesFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), - )); - $view = $this->configureViewPagesFollowingSiteSearch($view); - return $this->renderView($view, $fetch); - } - - public function getPageTitlesFollowingSiteSearch($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, __FUNCTION__, 'Actions.getPageTitlesFollowingSiteSearch', 'getPageTitlesFollowingSiteSearch' ); - $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), array( - 'Actions.getPageUrlsFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), - )); - $view = $this->configureViewPagesFollowingSiteSearch($view); - return $this->renderView($view, $fetch); - } - - public function configureViewPagesFollowingSiteSearch($view) - { - $view->setColumnsToDisplay(array('label', 'nb_hits_following_search', 'nb_hits')); - $view->setColumnTranslation('nb_hits_following_search', Piwik_Translate('General_ColumnViewedAfterSearch')); - $view->setColumnTranslation('label', Piwik_Translate('General_ColumnDestinationPage')); - $view->setSortedColumn('nb_hits_following_search'); - $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnTotalPageviews')); - $view->disableExcludeLowPopulation(); - $view = $this->configureViewActions($view, $doSetTranslations = false); - return $view; - } - - /* - * PAGE TITLES - */ - public function indexPageTitles($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuPageTitles'), - $this->getPageTitles(true), $fetch); - } - - public function getPageTitles($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getPageTitles', - 'getPageTitlesSubDataTable' ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName')); - $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPageTitles'), array( - 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles'), - 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles'), - )); - $view->setReportUrl('Actions', $this->getPageTitlesActionForLink()); - $this->configureViewPages($view); - $this->configureViewActions($view); - return $this->renderView($view, $fetch); - } - - public function getPageTitlesSubDataTable($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getPageTitles', - 'getPageTitlesSubDataTable' ); - $this->configureViewPages($view); - $this->configureViewActions($view); - return $this->renderView($view, $fetch); - } - - /** - * Echos or returns a report displaying analytics data for every unique entry - * page title. - * - * @param bool $fetch True to return the view as a string, false to echo it. - * @return string - */ - public function getEntryPageTitles( $fetch = false ) - { - $view = Piwik_ViewDataTable::factory(); - $view->init($this->pluginName, __FUNCTION__, 'Actions.getEntryPageTitles', __FUNCTION__); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageTitle')); - $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces')); - $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances')); - $view->setColumnsToDisplay( array('label','entry_nb_visits', 'entry_bounce_count', 'bounce_rate') ); - - $entryPageUrlAction = $this->getEntryPageUrlActionForLink(); - $view->addRelatedReports(Piwik_Translate('Actions_EntryPageTitles'), array( - 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), - "Actions.$entryPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesEntry'), - )); - - $this->configureViewActions($view); - - return $this->renderView($view, $fetch); - } - - /** - * Echos or returns a report displaying analytics data for every unique exit - * page title. - * - * @param bool $fetch True to return the view as a string, false to echo it. - * @return string - */ - public function getExitPageTitles( $fetch = false ) - { - $view = Piwik_ViewDataTable::factory(); - $view->init($this->pluginName, __FUNCTION__, 'Actions.getExitPageTitles', __FUNCTION__); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageTitle')); - $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits')); - $view->setColumnsToDisplay( array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate') ); - - $exitPageUrlAction = $this->getExitPageUrlActionForLink(); - $view->addRelatedReports(Piwik_Translate('Actions_ExitPageTitles'), array( - 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), - "Actions.$exitPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesExit'), - )); - - $this->configureViewActions($view); - - return $this->renderView($view, $fetch); - } - - /* - * DOWNLOADS - */ - - public function indexDownloads($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuDownloads'), - $this->getDownloads(true), $fetch); - } - - public function getDownloads($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getDownloads', - 'getDownloadsSubDataTable' ); - - $this->configureViewDownloads($view); - return $this->renderView($view, $fetch); - } - - public function getDownloadsSubDataTable($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getDownloads', - 'getDownloadsSubDataTable'); - $this->configureViewDownloads($view); - return $this->renderView($view, $fetch); - } - - - /* - * OUTLINKS - */ - - public function indexOutlinks($fetch = false) - { - return Piwik_View::singleReport( - Piwik_Translate('Actions_SubmenuOutlinks'), - $this->getOutlinks(true), $fetch); - } - - public function getOutlinks($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getOutlinks', - 'getOutlinksSubDataTable' ); - $this->configureViewOutlinks($view); - return $this->renderView($view, $fetch); - } - - public function getOutlinksSubDataTable($fetch = false) - { - $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, - __FUNCTION__, - 'Actions.getOutlinks', - 'getOutlinksSubDataTable'); - $this->configureViewOutlinks($view); - return $this->renderView($view, $fetch); - } - - /* - * Page titles & Page URLs reports - */ - protected function configureViewActions($view, $doSetTranslations = true) - { - if($doSetTranslations) - { - $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnPageviews')); - $view->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnUniquePageviews')); - $view->setColumnTranslation('avg_time_on_page', Piwik_Translate('General_ColumnAverageTimeOnPage')); - $view->setColumnTranslation('bounce_rate', Piwik_Translate('General_ColumnBounceRate')); - $view->setColumnTranslation('exit_rate', Piwik_Translate('General_ColumnExitRate')); - $view->setColumnTranslation('avg_time_generation', Piwik_Translate('General_ColumnAverageGenerationTime')); - $view->queueFilter('ColumnCallbackReplace', array('avg_time_on_page', array('Piwik', 'getPrettyTimeFromSeconds'))); - $view->queueFilter('ColumnCallbackReplace', array('avg_time_generation', - create_function('$averageTimeOnSite', 'return $averageTimeOnSite ? Piwik::getPrettyTimeFromSeconds($averageTimeOnSite, true, true, false) : "-";'))); - } - - if(Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string' ) != '0') - { - // computing minimum value to exclude - $visitsInfo = Piwik_VisitsSummary_Controller::getVisitsSummary(); - $visitsInfo = $visitsInfo->getFirstRow(); - $nbActions = $visitsInfo->getColumn('nb_actions'); - $nbActionsLowPopulationThreshold = floor(0.02 * $nbActions); // 2 percent of the total number of actions - // we remove 1 to make sure some actions/downloads are displayed in the case we have a very few of them - // and each of them has 1 or 2 hits... - $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions')-1, $nbActionsLowPopulationThreshold-1); - - $view->setExcludeLowPopulation( 'nb_hits', $nbActionsLowPopulationThreshold ); - } - - $this->configureGenericViewActions($view); - return $view; - } - - /* - * Downloads report - */ - protected function configureViewDownloads($view) - { - $view->setColumnsToDisplay( array('label','nb_visits','nb_hits') ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnDownloadURL')); - $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueDownloads')); - $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnDownloads')); - $view->disableExcludeLowPopulation(); - $this->configureGenericViewActions($view); - } - - /* - * Outlinks report - */ - protected function configureViewOutlinks($view) - { - $view->setColumnsToDisplay( array('label','nb_visits','nb_hits') ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnClickedURL')); - $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueClicks')); - $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnClicks')); - $view->disableExcludeLowPopulation(); - $this->configureGenericViewActions($view); - } - - /* - * Common to all Actions reports, how to use the custom Actions Datatable html - */ - protected function configureGenericViewActions($view) - { - $view->setTemplate('CoreHome/templates/datatable_actions.tpl'); - if(Piwik_Common::getRequestVar('idSubtable', -1) != -1) - { - $view->setTemplate('CoreHome/templates/datatable_actions_subdatable.tpl'); - } - $currentlySearching = $view->setSearchRecursive(); - if($currentlySearching) - { - $view->setTemplate('CoreHome/templates/datatable_actions_recursive.tpl'); - } - // disable Footer icons - $view->disableShowAllViewsIcons(); - $view->disableShowAllColumns(); - - $view->setLimit( self::ACTIONS_REPORT_ROWS_DISPLAY ); - - // if the flat parameter is not provided, make sure it is set to 0 in the URL, - // so users can see that they can set it to 1 (see #3365) - if (Piwik_Common::getRequestVar('flat', false) === false) - { - $view->setCustomParameter('flat', 0); - } - - $view->main(); - // we need to rewrite the phpArray so it contains all the recursive arrays - if($currentlySearching) - { - $phpArrayRecursive = $this->getArrayFromRecursiveDataTable($view->getDataTable()); - $view->getView()->arrayDataTable = $phpArrayRecursive; - } - } - - protected function getArrayFromRecursiveDataTable( $dataTable, $depth = 0 ) - { - $table = array(); - foreach($dataTable->getRows() as $row) - { - $phpArray = array(); - if(($idSubtable = $row->getIdSubDataTable()) !== null) - { - $subTable = Piwik_DataTable_Manager::getInstance()->getTable( $idSubtable ); - - if($subTable->getRowsCount() > 0) - { - $phpArray = $this->getArrayFromRecursiveDataTable( $subTable, $depth + 1 ); - } - } - - $newRow = array( - 'level' => $depth, - 'columns' => $row->getColumns(), - 'metadata' => $row->getMetadata(), - 'idsubdatatable' => $row->getIdSubDataTable() - ); - $table[] = $newRow; - if(count($phpArray) > 0) - { - $table = array_merge( $table, $phpArray); - } - } - return $table; - } - - /** Returns action to use when linking to the exit page URLs report. */ - private function getExitPageUrlActionForLink() - { - // link to the page not, just the report, but only if not a widget - return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexExitPageUrls' : 'getExitPageUrls'; - } - - - /** Returns action to use when linking to the entry page URLs report. */ - private function getEntryPageUrlActionForLink() - { - // link to the page not, just the report, but only if not a widget - return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexEntryPageUrls' : 'getEntryPageUrls'; - } - - /** Returns action to use when linking to the page titles report. */ - private function getPageTitlesActionForLink() - { - // link to the page not, just the report, but only if not a widget - return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexPageTitles' : 'getPageTitles'; - } + const ACTIONS_REPORT_ROWS_DISPLAY = 100; + + protected function getPageUrlsView($currentAction, $controllerActionSubtable, $apiAction) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, $currentAction, $apiAction, $controllerActionSubtable); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageURL')); + return $view; + } + + /** + * PAGES + * @param bool $fetch + * @return string + */ + + public function indexPageUrls($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuPages'), + $this->getPageUrls(true), $fetch); + } + + public function getPageUrls($fetch = false) + { + $view = $this->getPageUrlsView(__FUNCTION__, 'getPageUrls', 'Actions.getPageUrls'); + $this->configureViewPages($view); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + protected function configureViewPages($view) + { + $view->setColumnsToDisplay(array('label', 'nb_hits', 'nb_visits', 'bounce_rate', 'avg_time_on_page', 'exit_rate', 'avg_time_generation')); + } + + /** + * ENTRY PAGES + * @param bool $fetch + * @return string|void + */ + public function indexEntryPageUrls($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuPagesEntry'), + $this->getEntryPageUrls(true), $fetch); + } + + public function getEntryPageUrls($fetch = false) + { + $view = $this->getPageUrlsView(__FUNCTION__, 'getEntryPageUrls', 'Actions.getEntryPageUrls'); + $this->configureViewEntryPageUrls($view); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + protected function configureViewEntryPageUrls($view) + { + $view->setSortedColumn('entry_nb_visits'); + $view->setColumnsToDisplay(array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate')); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageURL')); + $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces')); + $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances')); + $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesEntry'), array( + 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles') + )); + $view->setReportUrl('Actions', $this->getEntryPageUrlActionForLink()); + } + + /* + * EXIT PAGES + */ + public function indexExitPageUrls($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuPagesExit'), + $this->getExitPageUrls(true), $fetch); + } + + public function getExitPageUrls($fetch = false) + { + $view = $this->getPageUrlsView(__FUNCTION__, 'getExitPageUrls', 'Actions.getExitPageUrls'); + $this->configureViewExitPageUrls($view); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + protected function configureViewExitPageUrls($view) + { + $view->setSortedColumn('exit_nb_visits'); + $view->setColumnsToDisplay(array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate')); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageURL')); + $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits')); + $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPagesExit'), array( + 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles') + )); + $view->setReportUrl('Actions', $this->getExitPageUrlActionForLink()); + } + + /* + * SITE SEARCH + */ + public function indexSiteSearch() + { + $view = Piwik_View::factory('indexSiteSearch'); + + $view->keywords = $this->getSiteSearchKeywords(true); + $view->noResultKeywords = $this->getSiteSearchNoResultKeywords(true); + $view->pagesUrlsFollowingSiteSearch = $this->getPageUrlsFollowingSiteSearch(true); + + $categoryTrackingEnabled = Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables'); + if ($categoryTrackingEnabled) { + $view->categories = $this->getSiteSearchCategories(true); + } + + echo $view->render(); + } + + public function getSiteSearchKeywords($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchKeywords'); + $this->configureViewSiteSearchKeywords($view); + return $this->renderView($view, $fetch); + } + + public function getSiteSearchNoResultKeywords($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchNoResultKeywords'); + $this->configureViewSiteSearchKeywords($view); + $view->setColumnsToDisplay(array('label', 'nb_visits', 'exit_rate')); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnNoResultKeyword')); + return $this->renderView($view, $fetch); + } + + public function configureViewSiteSearchKeywords(Piwik_ViewDataTable $view) + { + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchKeyword')); + $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search', 'exit_rate')); + $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches')); + $view->setColumnTranslation('exit_rate', str_replace("% ", "% ", Piwik_Translate('Actions_ColumnSearchExits'))); + $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch')); + $view->disableShowBarChart(); + $view->disableShowAllColumns(); + } + + public function getSiteSearchCategories($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getSiteSearchCategories'); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnSearchCategory')); + $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnSearches')); + $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_pages_per_search')); + $view->setColumnTranslation('nb_pages_per_search', Piwik_Translate('Actions_ColumnPagesPerSearch')); + $view->disableShowAllColumns(); + $view->disableShowBarChart(); + $view->disableRowEvolution(); + return $this->renderView($view, $fetch); + } + + + public function getPageUrlsFollowingSiteSearch($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getPageUrlsFollowingSiteSearch', 'getPageUrlsFollowingSiteSearch'); + $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), array( + 'Actions.getPageTitlesFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), + )); + $view = $this->configureViewPagesFollowingSiteSearch($view); + return $this->renderView($view, $fetch); + } + + public function getPageTitlesFollowingSiteSearch($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getPageTitlesFollowingSiteSearch', 'getPageTitlesFollowingSiteSearch'); + $view->addRelatedReports(Piwik_Translate('Actions_WidgetPageTitlesFollowingSearch'), array( + 'Actions.getPageUrlsFollowingSiteSearch' => Piwik_Translate('Actions_WidgetPageUrlsFollowingSearch'), + )); + $view = $this->configureViewPagesFollowingSiteSearch($view); + return $this->renderView($view, $fetch); + } + + public function configureViewPagesFollowingSiteSearch($view) + { + $view->setColumnsToDisplay(array('label', 'nb_hits_following_search', 'nb_hits')); + $view->setColumnTranslation('nb_hits_following_search', Piwik_Translate('General_ColumnViewedAfterSearch')); + $view->setColumnTranslation('label', Piwik_Translate('General_ColumnDestinationPage')); + $view->setSortedColumn('nb_hits_following_search'); + $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnTotalPageviews')); + $view->disableExcludeLowPopulation(); + $view = $this->configureViewActions($view, $doSetTranslations = false); + return $view; + } + + /* + * PAGE TITLES + */ + public function indexPageTitles($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuPageTitles'), + $this->getPageTitles(true), $fetch); + } + + public function getPageTitles($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getPageTitles', + 'getPageTitlesSubDataTable'); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName')); + $view->addRelatedReports(Piwik_Translate('Actions_SubmenuPageTitles'), array( + 'Actions.getEntryPageTitles' => Piwik_Translate('Actions_EntryPageTitles'), + 'Actions.getExitPageTitles' => Piwik_Translate('Actions_ExitPageTitles'), + )); + $view->setReportUrl('Actions', $this->getPageTitlesActionForLink()); + $this->configureViewPages($view); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + public function getPageTitlesSubDataTable($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getPageTitles', + 'getPageTitlesSubDataTable'); + $this->configureViewPages($view); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + /** + * Echos or returns a report displaying analytics data for every unique entry + * page title. + * + * @param bool $fetch True to return the view as a string, false to echo it. + * @return string + */ + public function getEntryPageTitles($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getEntryPageTitles', __FUNCTION__); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnEntryPageTitle')); + $view->setColumnTranslation('entry_bounce_count', Piwik_Translate('General_ColumnBounces')); + $view->setColumnTranslation('entry_nb_visits', Piwik_Translate('General_ColumnEntrances')); + $view->setColumnsToDisplay(array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate')); + + $entryPageUrlAction = $this->getEntryPageUrlActionForLink(); + $view->addRelatedReports(Piwik_Translate('Actions_EntryPageTitles'), array( + 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), + "Actions.$entryPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesEntry'), + )); + + $this->configureViewActions($view); + + return $this->renderView($view, $fetch); + } + + /** + * Echos or returns a report displaying analytics data for every unique exit + * page title. + * + * @param bool $fetch True to return the view as a string, false to echo it. + * @return string + */ + public function getExitPageTitles($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, __FUNCTION__, 'Actions.getExitPageTitles', __FUNCTION__); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnExitPageTitle')); + $view->setColumnTranslation('exit_nb_visits', Piwik_Translate('General_ColumnExits')); + $view->setColumnsToDisplay(array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate')); + + $exitPageUrlAction = $this->getExitPageUrlActionForLink(); + $view->addRelatedReports(Piwik_Translate('Actions_ExitPageTitles'), array( + 'Actions.getPageTitles' => Piwik_Translate('Actions_SubmenuPageTitles'), + "Actions.$exitPageUrlAction" => Piwik_Translate('Actions_SubmenuPagesExit'), + )); + + $this->configureViewActions($view); + + return $this->renderView($view, $fetch); + } + + /* + * DOWNLOADS + */ + + public function indexDownloads($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuDownloads'), + $this->getDownloads(true), $fetch); + } + + public function getDownloads($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getDownloads', + 'getDownloadsSubDataTable'); + + $this->configureViewDownloads($view); + return $this->renderView($view, $fetch); + } + + public function getDownloadsSubDataTable($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getDownloads', + 'getDownloadsSubDataTable'); + $this->configureViewDownloads($view); + return $this->renderView($view, $fetch); + } + + + /* + * OUTLINKS + */ + + public function indexOutlinks($fetch = false) + { + return Piwik_View::singleReport( + Piwik_Translate('Actions_SubmenuOutlinks'), + $this->getOutlinks(true), $fetch); + } + + public function getOutlinks($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getOutlinks', + 'getOutlinksSubDataTable'); + $this->configureViewOutlinks($view); + return $this->renderView($view, $fetch); + } + + public function getOutlinksSubDataTable($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init($this->pluginName, + __FUNCTION__, + 'Actions.getOutlinks', + 'getOutlinksSubDataTable'); + $this->configureViewOutlinks($view); + return $this->renderView($view, $fetch); + } + + /* + * Page titles & Page URLs reports + */ + protected function configureViewActions($view, $doSetTranslations = true) + { + if ($doSetTranslations) { + $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnPageviews')); + $view->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnUniquePageviews')); + $view->setColumnTranslation('avg_time_on_page', Piwik_Translate('General_ColumnAverageTimeOnPage')); + $view->setColumnTranslation('bounce_rate', Piwik_Translate('General_ColumnBounceRate')); + $view->setColumnTranslation('exit_rate', Piwik_Translate('General_ColumnExitRate')); + $view->setColumnTranslation('avg_time_generation', Piwik_Translate('General_ColumnAverageGenerationTime')); + $view->queueFilter('ColumnCallbackReplace', array('avg_time_on_page', array('Piwik', 'getPrettyTimeFromSeconds'))); + $view->queueFilter('ColumnCallbackReplace', array('avg_time_generation', + create_function('$averageTimeOnSite', 'return $averageTimeOnSite ? Piwik::getPrettyTimeFromSeconds($averageTimeOnSite, true, true, false) : "-";'))); + } + + if (Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string') != '0') { + // computing minimum value to exclude + $visitsInfo = Piwik_VisitsSummary_Controller::getVisitsSummary(); + $visitsInfo = $visitsInfo->getFirstRow(); + $nbActions = $visitsInfo->getColumn('nb_actions'); + $nbActionsLowPopulationThreshold = floor(0.02 * $nbActions); // 2 percent of the total number of actions + // we remove 1 to make sure some actions/downloads are displayed in the case we have a very few of them + // and each of them has 1 or 2 hits... + $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions') - 1, $nbActionsLowPopulationThreshold - 1); + + $view->setExcludeLowPopulation('nb_hits', $nbActionsLowPopulationThreshold); + } + + $this->configureGenericViewActions($view); + return $view; + } + + /* + * Downloads report + */ + protected function configureViewDownloads($view) + { + $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_hits')); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnDownloadURL')); + $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueDownloads')); + $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnDownloads')); + $view->disableExcludeLowPopulation(); + $this->configureGenericViewActions($view); + } + + /* + * Outlinks report + */ + protected function configureViewOutlinks($view) + { + $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_hits')); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnClickedURL')); + $view->setColumnTranslation('nb_visits', Piwik_Translate('Actions_ColumnUniqueClicks')); + $view->setColumnTranslation('nb_hits', Piwik_Translate('Actions_ColumnClicks')); + $view->disableExcludeLowPopulation(); + $this->configureGenericViewActions($view); + } + + /* + * Common to all Actions reports, how to use the custom Actions Datatable html + */ + protected function configureGenericViewActions($view) + { + $view->setTemplate('CoreHome/templates/datatable_actions.tpl'); + if (Piwik_Common::getRequestVar('idSubtable', -1) != -1) { + $view->setTemplate('CoreHome/templates/datatable_actions_subdatable.tpl'); + } + $currentlySearching = $view->setSearchRecursive(); + if ($currentlySearching) { + $view->setTemplate('CoreHome/templates/datatable_actions_recursive.tpl'); + } + // disable Footer icons + $view->disableShowAllViewsIcons(); + $view->disableShowAllColumns(); + + $view->setLimit(self::ACTIONS_REPORT_ROWS_DISPLAY); + + // if the flat parameter is not provided, make sure it is set to 0 in the URL, + // so users can see that they can set it to 1 (see #3365) + if (Piwik_Common::getRequestVar('flat', false) === false) { + $view->setCustomParameter('flat', 0); + } + + $view->main(); + // we need to rewrite the phpArray so it contains all the recursive arrays + if ($currentlySearching) { + $phpArrayRecursive = $this->getArrayFromRecursiveDataTable($view->getDataTable()); + $view->getView()->arrayDataTable = $phpArrayRecursive; + } + } + + protected function getArrayFromRecursiveDataTable($dataTable, $depth = 0) + { + $table = array(); + foreach ($dataTable->getRows() as $row) { + $phpArray = array(); + if (($idSubtable = $row->getIdSubDataTable()) !== null) { + $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable); + + if ($subTable->getRowsCount() > 0) { + $phpArray = $this->getArrayFromRecursiveDataTable($subTable, $depth + 1); + } + } + + $newRow = array( + 'level' => $depth, + 'columns' => $row->getColumns(), + 'metadata' => $row->getMetadata(), + 'idsubdatatable' => $row->getIdSubDataTable() + ); + $table[] = $newRow; + if (count($phpArray) > 0) { + $table = array_merge($table, $phpArray); + } + } + return $table; + } + + /** Returns action to use when linking to the exit page URLs report. */ + private function getExitPageUrlActionForLink() + { + // link to the page not, just the report, but only if not a widget + return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexExitPageUrls' : 'getExitPageUrls'; + } + + + /** Returns action to use when linking to the entry page URLs report. */ + private function getEntryPageUrlActionForLink() + { + // link to the page not, just the report, but only if not a widget + return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexEntryPageUrls' : 'getEntryPageUrls'; + } + + /** Returns action to use when linking to the page titles report. */ + private function getPageTitlesActionForLink() + { + // link to the page not, just the report, but only if not a widget + return Piwik_Common::getRequestVar('widget', 0) == 0 ? 'indexPageTitles' : 'getPageTitles'; + } } diff --git a/plugins/Actions/templates/indexSiteSearch.tpl b/plugins/Actions/templates/indexSiteSearch.tpl index 3af0d5c0a4..a692fdd697 100644 --- a/plugins/Actions/templates/indexSiteSearch.tpl +++ b/plugins/Actions/templates/indexSiteSearch.tpl @@ -1,19 +1,19 @@ <div id='leftcolumn'> - <h2>{'Actions_WidgetSearchKeywords'|translate}</h2> -{$keywords} + <h2>{'Actions_WidgetSearchKeywords'|translate}</h2> + {$keywords} - <h2>{'Actions_WidgetSearchNoResultKeywords'|translate}</h2> -{$noResultKeywords} + <h2>{'Actions_WidgetSearchNoResultKeywords'|translate}</h2> + {$noResultKeywords} -{if isset($categories)} - <h2>{'Actions_WidgetSearchCategories'|translate}</h2> -{$categories} -{/if} + {if isset($categories)} + <h2>{'Actions_WidgetSearchCategories'|translate}</h2> + {$categories} + {/if} </div> <div id='rightcolumn'> - <h2>{'Actions_WidgetPageUrlsFollowingSearch'|translate}</h2> -{$pagesUrlsFollowingSiteSearch} + <h2>{'Actions_WidgetPageUrlsFollowingSearch'|translate}</h2> + {$pagesUrlsFollowingSiteSearch} </div> |