From 099eda0d9428c9cbf4d825cd1928179f5b740cf1 Mon Sep 17 00:00:00 2001 From: mattab Date: Sun, 20 Oct 2013 22:41:46 +1300 Subject: [PATCH] Tracking Actions refactoring to accomodate Custom Events #472 --- core/Tracker/Action.php | 586 ++++-------------- core/Tracker/ActionClickUrl.php | 62 ++ core/Tracker/ActionPageview.php | 61 ++ core/Tracker/ActionSiteSearch.php | 249 ++++++++ core/Tracker/GoalManager.php | 24 +- core/Tracker/PageUrl.php | 2 +- core/Tracker/TableActionIds.php | 102 +++ core/Tracker/Visit.php | 119 ++-- plugins/Actions/API.php | 7 +- plugins/Actions/Actions.php | 5 +- plugins/Actions/Archiver.php | 15 +- plugins/CustomVariables/API.php | 4 +- plugins/Live/API.php | 4 +- .../Fixtures/TwoVisitsWithCustomEvents.php | 1 - tests/PHPUnit/UI | 2 +- 15 files changed, 666 insertions(+), 577 deletions(-) create mode 100644 core/Tracker/ActionClickUrl.php create mode 100644 core/Tracker/ActionPageview.php create mode 100644 core/Tracker/ActionSiteSearch.php create mode 100644 core/Tracker/TableActionIds.php diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php index 842b185ab2d..bcd540bca0a 100644 --- a/core/Tracker/Action.php +++ b/core/Tracker/Action.php @@ -13,10 +13,8 @@ use Exception; use Piwik\Common; -use Piwik\Config; use Piwik\Piwik; use Piwik\Tracker; -use Piwik\UrlHelper; /** * Handles an action (page view, download or outlink) by the visitor. @@ -27,10 +25,33 @@ */ class Action implements ActionInterface { + const DB_COLUMN_CUSTOM_FLOAT = 'custom_float'; + + static public function make(Request $request) + { + $downloadUrl = $request->getParam('download'); + if (!empty($downloadUrl)) { + return new ActionClickUrl(self::TYPE_DOWNLOAD, $downloadUrl, $request); + } + + $outlinkUrl = $request->getParam('link'); + if (!empty($outlinkUrl)) { + return new ActionClickUrl(self::TYPE_OUTLINK, $outlinkUrl, $request); + } + + $url = $request->getParam('url'); + + $action = new ActionSiteSearch($url, $request); + if ($action->isSearchDetected()) { + return $action; + } + return new ActionPageview($url, $request); + } + /** * @var Request */ - private $request; + protected $request; private $idLinkVisitAction; private $idActionName = false; @@ -40,32 +61,13 @@ class Action implements ActionInterface private $actionType; private $actionUrl; - private $searchCategory = false; - private $searchCount = false; - - private $timeGeneration = false; - - /** - * Encoding of HTML page being viewed. See reencodeParameters for more info. - * @var string - */ - private $pageEncoding = false; - - /* Custom Variable names & slots used for Site Search metadata (category, results count) */ - const CVAR_KEY_SEARCH_CATEGORY = '_pk_scat'; - const CVAR_KEY_SEARCH_COUNT = '_pk_scount'; - const CVAR_INDEX_SEARCH_CATEGORY = '4'; - const CVAR_INDEX_SEARCH_COUNT = '5'; - - const DB_COLUMN_TIME_GENERATION = 'custom_float'; - - - public function __construct(Request $request) + public function __construct($type, Request $request) { + $this->actionType = $type; $this->request = $request; - $this->init(); } + /** * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked * @@ -86,38 +88,16 @@ public function getActionType() return $this->actionType; } - public function getActionNameType() + protected function getActionNameType() { - $actionNameType = null; - - // we can add here action types for names of other actions than page views (like downloads, outlinks) - switch ($this->getActionType()) { - case ActionInterface::TYPE_PAGE_URL: - $actionNameType = ActionInterface::TYPE_PAGE_TITLE; - break; - - case ActionInterface::TYPE_SITE_SEARCH: - $actionNameType = ActionInterface::TYPE_SITE_SEARCH; - break; - } - - return $actionNameType; + return ActionInterface::TYPE_PAGE_TITLE; } public function getIdActionUrl() { $idUrl = $this->idActionUrl; - if (!empty($idUrl)) { - return $idUrl; - } - // Site Search, by default, will not track URL. We do not want URL to appear as "Page URL not defined" - // so we specifically set it to NULL in the table (the archiving query does IS NOT NULL) - if ($this->getActionType() == self::TYPE_SITE_SEARCH) { - return null; - } - - // However, for other cases, we record idaction_url = 0 which will be displayed as "Page URL Not Defined" - return 0; + // note; idaction_url = 0 is displayed as "Page URL Not Defined" + return (int)$idUrl; } public function getIdActionName() @@ -125,172 +105,65 @@ public function getIdActionName() return $this->idActionName; } - protected function setActionName($name) + // custom_float column + public function getActionCustomValue() { - $name = PageUrl::cleanupString($name); - $this->actionName = $name; + return false; } - protected function setActionType($type) - { - $this->actionType = $type; - } - protected function setActionUrl($url) + + /** + * Returns the ID of the newly created record in the log_link_visit_action table + * + * @return int + */ + public function getIdLinkVisitAction() { - $this->actionUrl = $url; + return $this->idLinkVisitAction; } - - protected function init() + public function writeDebugInfo() { - $this->pageEncoding = $this->request->getParam('cs'); - - $info = $this->extractUrlAndActionNameFromRequest(); - - $originalUrl = $info['url']; - $info['url'] = PageUrl::excludeQueryParametersFromUrl($originalUrl, $this->request->getIdSite()); - - if ($originalUrl != $info['url']) { - Common::printDebug(' Before was "' . $originalUrl . '"'); - Common::printDebug(' After is "' . $info['url'] . '"'); + if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) { + $type = self::getTypeAsString($this->getActionType()); + Common::printDebug("Action is a $type, + Action name = " . $this->getActionName() . ", + Action URL = " . $this->getActionUrl()); } - - // Set Final attributes for this Action (Pageview, Search, etc.) - $this->setActionName($info['name']); - $this->setActionType($info['type']); - $this->setActionUrl($info['url']); } - static public function getSqlSelectActionId() + + protected function setActionName($name) { - $sql = "SELECT idaction, type, name - FROM " . Common::prefixTable('log_action') - . " WHERE " - . " ( hash = CRC32(?) AND name = ? AND type = ? ) "; - return $sql; + $name = PageUrl::cleanupString((string)$name); + $this->actionName = $name; } - /** - * This function will find the idaction from the lookup table piwik_log_action, - * given an Action name and type. - * - * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories - * - * If the action name does not exist in the lookup table, it will INSERT it - * @param array $actionNamesAndTypes Array of one or many (name,type) - * @return array Returns the input array, with the idaction appended ie. Array of one or many (name,type,idaction) - */ - static public function loadActionId($actionNamesAndTypes) + protected function setActionUrl($url) { - // First, we try and select the actions that are already recorded - $sql = self::getSqlSelectActionId(); - $bind = array(); - $normalizedUrls = array(); - $i = 0; - foreach ($actionNamesAndTypes as $index => &$actionNameType) { - list($name, $type) = $actionNameType; - if (empty($name)) { - $actionNameType[] = false; - continue; - } - if ($i > 0) { - $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) "; - } - if ($type == Tracker\Action::TYPE_PAGE_URL) { - // normalize urls by stripping protocol and www - $normalizedUrls[$index] = PageUrl::normalizeUrl($name); - $name = $normalizedUrls[$index]['url']; - } - $bind[] = $name; - $bind[] = $name; - $bind[] = $type; - $i++; - } - // Case URL & Title are empty - if (empty($bind)) { - return $actionNamesAndTypes; - } - $actionIds = Tracker::getDatabase()->fetchAll($sql, $bind); - - // For the Actions found in the lookup table, add the idaction in the array, - // If not found in lookup table, queue for INSERT - $actionsToInsert = array(); - foreach ($actionNamesAndTypes as $index => &$actionNameType) { - list($name, $type) = $actionNameType; - if (empty($name)) { - continue; - } - if (isset($normalizedUrls[$index])) { - $name = $normalizedUrls[$index]['url']; - } - $found = false; - foreach ($actionIds as $row) { - if ($name == $row['name'] - && $type == $row['type'] - ) { - $found = true; - $actionNameType[] = $row['idaction']; - continue; - } - } - if (!$found) { - $actionsToInsert[] = $index; - } - } + $urlBefore = $url; + $url = PageUrl::excludeQueryParametersFromUrl($url, $this->request->getIdSite()); - $sql = "INSERT INTO " . Common::prefixTable('log_action') . - "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)"; - // Then, we insert all new actions in the lookup table - foreach ($actionsToInsert as $actionToInsert) { - list($name, $type) = $actionNamesAndTypes[$actionToInsert]; - - $urlPrefix = null; - if (isset($normalizedUrls[$actionToInsert])) { - $name = $normalizedUrls[$actionToInsert]['url']; - $urlPrefix = $normalizedUrls[$actionToInsert]['prefixId']; - } - - Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix)); - $actionId = Tracker::getDatabase()->lastInsertId(); - Common::printDebug("Recorded a new action (" . self::getActionTypeName($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")"); - - $actionNamesAndTypes[$actionToInsert][] = $actionId; + if ($url != $urlBefore) { + Common::printDebug(' Before was "' . $urlBefore . '"'); + Common::printDebug(' After is "' . $url . '"'); } - return $actionNamesAndTypes; + + $url = PageUrl::getUrlIfLookValid($url); + $this->actionUrl = $url; } - static public function getActionTypeName($type) + public static function getTypeAsString($type) { - switch ($type) { - case self::TYPE_PAGE_URL: - return 'Page URL'; - break; - case self::TYPE_OUTLINK: - return 'Outlink URL'; - break; - case self::TYPE_DOWNLOAD: - return 'Download URL'; - break; - case self::TYPE_PAGE_TITLE: - return 'Page Title'; - break; - case self::TYPE_SITE_SEARCH: - return 'Site Search'; - break; - case self::TYPE_ECOMMERCE_ITEM_SKU: - return 'Ecommerce Item SKU'; - break; - case self::TYPE_ECOMMERCE_ITEM_NAME: - return 'Ecommerce Item Name'; - break; - case self::TYPE_ECOMMERCE_ITEM_CATEGORY: - return 'Ecommerce Item Category'; - break; - default: - throw new Exception("Unexpected action type " . $type); - break; + $class = new \ReflectionClass("\\Piwik\\Tracker\\ActionInterface"); + $constants = $class->getConstants(); + + $typeId = array_search($type, $constants); + if($typeId === false) { + throw new Exception("Unexpected action type " . $type); } + return str_replace('TYPE_', '', $typeId); } /** @@ -310,35 +183,24 @@ function loadIdActionNameAndUrl() return; } $actions = array(); - $nameType = $this->getActionNameType(); - $action = array($this->getActionName(), $nameType); - if (!is_null($action[1])) { - $actions[] = $action; - } - $urlType = $this->getActionType(); - $url = $this->getActionUrl(); - // this code is a mess, but basically, getActionType() returns SITE_SEARCH, - // but we do want to record the site search URL as an ACTION_URL - if ($nameType == Tracker\Action::TYPE_SITE_SEARCH) { - $urlType = Tracker\Action::TYPE_PAGE_URL; - - // By default, Site Search does not record the URL for the Search Result page, to slightly improve performance - if (empty(Config::getInstance()->Tracker['action_sitesearch_record_url'])) { - $url = false; - } + $actionNameAndType = $this->getNameAndType(); + if($actionNameAndType) { + $actions[] = $actionNameAndType; } - if (!is_null($urlType) && !empty($url)) { - $actions[] = array($url, $urlType); + + $actionUrlAndType = $this->getUrlAndType(); + if($actionUrlAndType) { + $actions[] = $actionUrlAndType; + } - $loadedActionIds = self::loadActionId($actions); + $loadedActionIds = TableActionIds::loadActionId($actions); foreach ($loadedActionIds as $loadedActionId) { - list($name, $type, $actionId) = $loadedActionId; - if ($type == Tracker\Action::TYPE_PAGE_TITLE - || $type == Tracker\Action::TYPE_SITE_SEARCH - ) { + var_dump($loadedActionId); + list($name, $type, $prefixId, $actionId) = $loadedActionId; + if ($type == $this->getActionNameType()) { $this->idActionName = $actionId; } else { $this->idActionUrl = $actionId; @@ -362,7 +224,8 @@ public function record($idVisit, $visitorIdCookie, $idReferrerActionUrl, $idRefe $idActionName = in_array($this->getActionType(), array(Tracker\Action::TYPE_PAGE_TITLE, Tracker\Action::TYPE_PAGE_URL, - Tracker\Action::TYPE_SITE_SEARCH)) + Tracker\Action::TYPE_SITE_SEARCH + )) ? (int)$this->getIdActionName() : null; @@ -378,12 +241,18 @@ public function record($idVisit, $visitorIdCookie, $idReferrerActionUrl, $idRefe 'time_spent_ref_action' => $timeSpentReferrerAction ); - if (!empty($this->timeGeneration)) { - $insert[self::DB_COLUMN_TIME_GENERATION] = $this->timeGeneration; + $customValue = $this->getActionCustomValue(); + if (!empty($customValue)) { + $insert[self::DB_COLUMN_CUSTOM_FLOAT] = $customValue; } $customVariables = $this->getCustomVariables(); + if (!empty($customVariables)) { + Common::printDebug("Page level Custom Variables: "); + Common::printDebug($customVariables); + } + $insert = array_merge($insert, $customVariables); // Mysqli apparently does not like NULL inserts? @@ -423,267 +292,28 @@ public function record($idVisit, $visitorIdCookie, $idReferrerActionUrl, $idRefe public function getCustomVariables() { $customVariables = $this->request->getCustomVariables($scope = 'page'); - - // Enrich Site Search actions with Custom Variables, overwriting existing values - if (!empty($this->searchCategory)) { - if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY])) { - Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_CATEGORY . " for this page view"); - } - $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY] = self::CVAR_KEY_SEARCH_CATEGORY; - $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_CATEGORY] = Request::truncateCustomVariable($this->searchCategory); - } - if ($this->searchCount !== false) { - if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT])) { - Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_COUNT . " for this page view"); - } - $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT] = self::CVAR_KEY_SEARCH_COUNT; - $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_COUNT] = (int)$this->searchCount; - } - - if (!empty($customVariables)) { - Common::printDebug("Page level Custom Variables: "); - Common::printDebug($customVariables); - } return $customVariables; } - /** - * Returns the ID of the newly created record in the log_link_visit_action table - * - * @return int - */ - public function getIdLinkVisitAction() - { - return $this->idLinkVisitAction; - } - - /** - * Generates the name of the action from the URL or the specified name. - * Sets the name as $this->actionName - * - * @return array - */ - protected function extractUrlAndActionNameFromRequest() - { - $actionName = $this->request->getParam('action_name'); - $url = $this->request->getParam('url'); - - $downloadUrl = $this->request->getParam('download'); - if (!empty($downloadUrl)) { - $actionType = self::TYPE_DOWNLOAD; - $url = $downloadUrl; - } - - if (empty($actionType)) { - $outlinkUrl = $this->request->getParam('link'); - if (!empty($outlinkUrl)) { - $actionType = self::TYPE_OUTLINK; - $url = $outlinkUrl; - } - } - - // defaults to page view - if (empty($actionType)) { - $actionType = self::TYPE_PAGE_URL; - $actionName = $this->cleanupActionName($actionName); - - // Look in tracked URL for the Site Search parameters - $siteSearch = $this->detectSiteSearch($url); - if (!empty($siteSearch)) { - $actionType = self::TYPE_SITE_SEARCH; - list($actionName, $url) = $siteSearch; - } - - // Look for performance analytics parameters - $this->timeGeneration = $this->request->getPageGenerationTime(); - } - - $url = PageUrl::getUrlIfLookValid($url); - $actionName = PageUrl::cleanupString((string)$actionName); - return array( - 'name' => $actionName, - 'type' => $actionType, - 'url' => $url, - ); - } - - protected function detectSiteSearch($originalUrl) - { - $website = Cache::getCacheWebsiteAttributes($this->request->getIdSite()); - if (empty($website['sitesearch'])) { - Common::printDebug("Internal 'Site Search' tracking is not enabled for this site. "); - return false; - } - $actionName = $url = $categoryName = $count = false; - $doTrackUrlForSiteSearch = !empty(Config::getInstance()->Tracker['action_sitesearch_record_url']); - - $originalUrl = PageUrl::cleanupUrl($originalUrl); - - // Detect Site search from Tracking API parameters rather than URL - $searchKwd = $this->request->getParam('search'); - if (!empty($searchKwd)) { - $actionName = $searchKwd; - if ($doTrackUrlForSiteSearch) { - $url = $originalUrl; - } - $isCategoryName = $this->request->getParam('search_cat'); - if (!empty($isCategoryName)) { - $categoryName = $isCategoryName; - } - $isCount = $this->request->getParam('search_count'); - if ($this->isValidSearchCount($isCount)) { - $count = $isCount; - } - } - - if (empty($actionName)) { - $parsedUrl = @parse_url($originalUrl); - - // Detect Site Search from URL query parameters - if (!empty($parsedUrl['query']) || !empty($parsedUrl['fragment'])) { - // array($url, $actionName, $categoryName, $count); - $searchInfo = $this->detectSiteSearchFromUrl($website, $parsedUrl); - if (!empty($searchInfo)) { - list ($url, $actionName, $categoryName, $count) = $searchInfo; - } - } - } - - if (empty($actionName)) { - Common::printDebug("(this is not a Site Search request)"); - return false; - } - - Common::printDebug("Detected Site Search keyword '$actionName'. "); - if (!empty($categoryName)) { - Common::printDebug("- Detected Site Search Category '$categoryName'. "); - } - if ($count !== false) { - Common::printDebug("- Search Results Count was '$count'. "); - } - if ($url != $originalUrl) { - Common::printDebug("NOTE: The Page URL was changed / removed, during the Site Search detection, was '$originalUrl', now is '$url'"); - } - - if (!empty($categoryName) || $count !== false) { - $this->setActionSearchMetadata($categoryName, $count); - } - return array( - $actionName, - $url - ); - } - - protected function isValidSearchCount($count) - { - return is_numeric($count) && $count >= 0; - } - - protected function setActionSearchMetadata($category, $count) + protected function getNameAndType() { - if (!empty($category)) { - $this->searchCategory = trim($category); - } - if ($count !== false) { - $this->searchCount = $count; + $nameType = $this->getActionNameType(); + if (!is_null($nameType)) { + return array($this->getActionName(), $nameType, $prefix = false); } + return false; } - protected function detectSiteSearchFromUrl($website, $parsedUrl) + protected function getUrlAndType() { - $doRemoveSearchParametersFromUrl = false; - $separator = '&'; - $count = $actionName = $categoryName = false; - - $keywordParameters = isset($website['sitesearch_keyword_parameters']) - ? $website['sitesearch_keyword_parameters'] - : array(); - $queryString = (!empty($parsedUrl['query']) ? $parsedUrl['query'] : '') . (!empty($parsedUrl['fragment']) ? $separator . $parsedUrl['fragment'] : ''); - $parametersRaw = UrlHelper::getArrayFromQueryString($queryString); - - // strtolower the parameter names for smooth site search detection - $parameters = array(); - foreach ($parametersRaw as $k => $v) { - $parameters[Common::mb_strtolower($k)] = $v; - } - // decode values if they were sent from a client using another charset - PageUrl::reencodeParameters($parameters, $this->pageEncoding); - - // Detect Site Search keyword - foreach ($keywordParameters as $keywordParameterRaw) { - $keywordParameter = Common::mb_strtolower($keywordParameterRaw); - if (!empty($parameters[$keywordParameter])) { - $actionName = $parameters[$keywordParameter]; - break; - } - } - - if (empty($actionName)) { - return false; - } - - $categoryParameters = isset($website['sitesearch_category_parameters']) - ? $website['sitesearch_category_parameters'] - : array(); - - foreach ($categoryParameters as $categoryParameterRaw) { - $categoryParameter = Common::mb_strtolower($categoryParameterRaw); - if (!empty($parameters[$categoryParameter])) { - $categoryName = $parameters[$categoryParameter]; - break; - } - } - - if (isset($parameters['search_count']) - && $this->isValidSearchCount($parameters['search_count']) - ) { - $count = $parameters['search_count']; - } - // Remove search kwd from URL - if ($doRemoveSearchParametersFromUrl) { - // @see excludeQueryParametersFromUrl() - // Excluded the detected parameters from the URL - $parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw); - $parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude); - $parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude); - } - $url = UrlHelper::getParseUrlReverse($parsedUrl); - if (is_array($actionName)) { - $actionName = reset($actionName); - } - $actionName = trim(urldecode($actionName)); - if (empty($actionName)) { - return false; - } - if (is_array($categoryName)) { - $categoryName = reset($categoryName); + $url = $this->getActionUrl(); + if (!is_null($url)) { + // normalize urls by stripping protocol and www + $url = PageUrl::normalizeUrl($url); + return array($url['url'], Tracker\Action::TYPE_PAGE_URL, $url['prefixId']); } - $categoryName = trim(urldecode($categoryName)); - return array($url, $actionName, $categoryName, $count); + return false; } - - protected function cleanupActionName($actionName) - { - // get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067) - $actionCategoryDelimiter = isset(Config::getInstance()->General['action_category_delimiter']) - ? Config::getInstance()->General['action_category_delimiter'] - : Config::getInstance()->General['action_url_category_delimiter']; - - // create an array of the categories delimited by the delimiter - $split = explode($actionCategoryDelimiter, $actionName); - - // trim every category - $split = array_map('trim', $split); - - // remove empty categories - $split = array_filter($split, 'strlen'); - - // rebuild the name from the array of cleaned categories - $actionName = implode($actionCategoryDelimiter, $split); - return $actionName; - } - - } @@ -696,6 +326,7 @@ protected function cleanupActionName($actionName) */ interface ActionInterface { + //FIXMEA lookup uses and check Events compat const TYPE_PAGE_URL = 1; const TYPE_OUTLINK = 2; const TYPE_DOWNLOAD = 3; @@ -705,7 +336,11 @@ interface ActionInterface const TYPE_ECOMMERCE_ITEM_CATEGORY = 7; const TYPE_SITE_SEARCH = 8; - public function __construct(Request $request); + //FIXMEA lookup uses and check Events compat + const TYPE_EVENT = 11; // Same as TYPE_EVENT_ACTION + const TYPE_EVENT_CATEGORY = 10; + const TYPE_EVENT_ACTION = 11; + const TYPE_EVENT_NAME = 12; public function getActionUrl(); @@ -720,4 +355,5 @@ public function getIdActionUrl(); public function getIdActionName(); public function getIdLinkVisitAction(); -} \ No newline at end of file +} + diff --git a/core/Tracker/ActionClickUrl.php b/core/Tracker/ActionClickUrl.php new file mode 100644 index 00000000000..3a7e68a1bcf --- /dev/null +++ b/core/Tracker/ActionClickUrl.php @@ -0,0 +1,62 @@ +setActionUrl($url); + } + + function getActionNameType() + { + return null; + } + + function writeDebugInfo() + { + parent::writeDebugInfo(); + + if (self::detectActionIsOutlinkOnAliasHost($this, $this->request->getIdSite())) { + Common::printDebug("INFO: The outlink URL host is one of the known host for this website. "); + } + } + + /** + * Detect whether action is an outlink given host aliases + * + * @param ActionInterface $action + * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website + */ + public static function detectActionIsOutlinkOnAliasHost(ActionInterface $action, $idSite) + { + if ($action->getActionType() != ActionInterface::TYPE_OUTLINK) { + return false; + } + $decodedActionUrl = $action->getActionUrl(); + $actionUrlParsed = @parse_url($decodedActionUrl); + if (!isset($actionUrlParsed['host'])) { + return false; + } + return Visit::isHostKnownAliasHost($actionUrlParsed['host'], $idSite); + } +} \ No newline at end of file diff --git a/core/Tracker/ActionPageview.php b/core/Tracker/ActionPageview.php new file mode 100644 index 00000000000..ec1e865bb7a --- /dev/null +++ b/core/Tracker/ActionPageview.php @@ -0,0 +1,61 @@ +setActionUrl($url); + + $actionName = $request->getParam('action_name'); + $actionName = $this->cleanupActionName($actionName); + $this->setActionName($actionName); + + $this->timeGeneration = $this->request->getPageGenerationTime(); + } + + function getActionCustomValue() + { + return $this->request->getPageGenerationTime(); + } + + protected function cleanupActionName($actionName) + { + // get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067) + $actionCategoryDelimiter = isset(Config::getInstance()->General['action_category_delimiter']) + ? Config::getInstance()->General['action_category_delimiter'] + : Config::getInstance()->General['action_url_category_delimiter']; + + // create an array of the categories delimited by the delimiter + $split = explode($actionCategoryDelimiter, $actionName); + + // trim every category + $split = array_map('trim', $split); + + // remove empty categories + $split = array_filter($split, 'strlen'); + + // rebuild the name from the array of cleaned categories + $actionName = implode($actionCategoryDelimiter, $split); + return $actionName; + } + +} \ No newline at end of file diff --git a/core/Tracker/ActionSiteSearch.php b/core/Tracker/ActionSiteSearch.php new file mode 100644 index 00000000000..210d848386f --- /dev/null +++ b/core/Tracker/ActionSiteSearch.php @@ -0,0 +1,249 @@ +originalUrl = $url; + } + + function getActionCustomValue() + { + return $this->request->getPageGenerationTime(); + } + + function isSearchDetected() + { + $siteSearch = $this->detectSiteSearch($this->originalUrl); + + if(empty($siteSearch)) { + return false; + } + + list($actionName,$url, $category, $count) = $siteSearch; + + if (!empty($category)) { + $this->searchCategory = trim($category); + } + if ($count !== false) { + $this->searchCount = $count; + } + $this->setActionName($actionName); + $this->setActionUrl($url); + } + + // FIXMEA replace by getNameAndType + protected function getActionNameType() + { + return ActionInterface::TYPE_SITE_SEARCH; + } + + public function getIdActionUrl() + { + // Site Search, by default, will not track URL. We do not want URL to appear as "Page URL not defined" + // so we specifically set it to NULL in the table (the archiving query does IS NOT NULL) + if (empty(Config::getInstance()->Tracker['action_sitesearch_record_url'])) { + return null; + } + return parent::getIdActionUrl(); + } + + public function getCustomVariables() + { + $customVariables = parent::getCustomVariables(); + + // Enrich Site Search actions with Custom Variables, overwriting existing values + if (!empty($this->searchCategory)) { + if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY])) { + Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_CATEGORY . " for this page view"); + } + $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY] = self::CVAR_KEY_SEARCH_CATEGORY; + $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_CATEGORY] = Request::truncateCustomVariable($this->searchCategory); + } + if ($this->searchCount !== false) { + if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT])) { + Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_COUNT . " for this page view"); + } + $customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT] = self::CVAR_KEY_SEARCH_COUNT; + $customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_COUNT] = (int)$this->searchCount; + } + return $customVariables; + } + + public function isSiteSearchDetected($originalUrl) + { + return true; + } + + protected function detectSiteSearchFromUrl($website, $parsedUrl) + { + $doRemoveSearchParametersFromUrl = false; + $separator = '&'; + $count = $actionName = $categoryName = false; + + $keywordParameters = isset($website['sitesearch_keyword_parameters']) + ? $website['sitesearch_keyword_parameters'] + : array(); + $queryString = (!empty($parsedUrl['query']) ? $parsedUrl['query'] : '') . (!empty($parsedUrl['fragment']) ? $separator . $parsedUrl['fragment'] : ''); + $parametersRaw = UrlHelper::getArrayFromQueryString($queryString); + + // strtolower the parameter names for smooth site search detection + $parameters = array(); + foreach ($parametersRaw as $k => $v) { + $parameters[Common::mb_strtolower($k)] = $v; + } + // decode values if they were sent from a client using another charset + $pageEncoding = $this->request->getParam('cs'); + PageUrl::reencodeParameters($parameters, $pageEncoding); + + // Detect Site Search keyword + foreach ($keywordParameters as $keywordParameterRaw) { + $keywordParameter = Common::mb_strtolower($keywordParameterRaw); + if (!empty($parameters[$keywordParameter])) { + $actionName = $parameters[$keywordParameter]; + break; + } + } + + if (empty($actionName)) { + return false; + } + + $categoryParameters = isset($website['sitesearch_category_parameters']) + ? $website['sitesearch_category_parameters'] + : array(); + + foreach ($categoryParameters as $categoryParameterRaw) { + $categoryParameter = Common::mb_strtolower($categoryParameterRaw); + if (!empty($parameters[$categoryParameter])) { + $categoryName = $parameters[$categoryParameter]; + break; + } + } + + if (isset($parameters['search_count']) + && $this->isValidSearchCount($parameters['search_count']) + ) { + $count = $parameters['search_count']; + } + // Remove search kwd from URL + if ($doRemoveSearchParametersFromUrl) { + // @see excludeQueryParametersFromUrl() + // Excluded the detected parameters from the URL + $parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw); + $parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude); + $parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude); + } + $url = UrlHelper::getParseUrlReverse($parsedUrl); + if (is_array($actionName)) { + $actionName = reset($actionName); + } + $actionName = trim(urldecode($actionName)); + if (empty($actionName)) { + return false; + } + if (is_array($categoryName)) { + $categoryName = reset($categoryName); + } + $categoryName = trim(urldecode($categoryName)); + return array($url, $actionName, $categoryName, $count); + } + + protected function isValidSearchCount($count) + { + return is_numeric($count) && $count >= 0; + } + + protected function detectSiteSearch($originalUrl) + { + $website = Cache::getCacheWebsiteAttributes($this->request->getIdSite()); + if (empty($website['sitesearch'])) { + Common::printDebug("Internal 'Site Search' tracking is not enabled for this site. "); + return false; + } + + $actionName = $url = $categoryName = $count = false; + $doTrackUrlForSiteSearch = !empty(Config::getInstance()->Tracker['action_sitesearch_record_url']); + + $originalUrl = PageUrl::cleanupUrl($originalUrl); + + // Detect Site search from Tracking API parameters rather than URL + $searchKwd = $this->request->getParam('search'); + if (!empty($searchKwd)) { + $actionName = $searchKwd; + if ($doTrackUrlForSiteSearch) { + $url = $originalUrl; + } + $isCategoryName = $this->request->getParam('search_cat'); + if (!empty($isCategoryName)) { + $categoryName = $isCategoryName; + } + $isCount = $this->request->getParam('search_count'); + if ($this->isValidSearchCount($isCount)) { + $count = $isCount; + } + } + + if (empty($actionName)) { + $parsedUrl = @parse_url($originalUrl); + + // Detect Site Search from URL query parameters + if (!empty($parsedUrl['query']) || !empty($parsedUrl['fragment'])) { + // array($url, $actionName, $categoryName, $count); + $searchInfo = $this->detectSiteSearchFromUrl($website, $parsedUrl); + if (!empty($searchInfo)) { + list ($url, $actionName, $categoryName, $count) = $searchInfo; + } + } + } + + if (empty($actionName)) { + Common::printDebug("(this is not a Site Search request)"); + return false; + } + + Common::printDebug("Detected Site Search keyword '$actionName'. "); + if (!empty($categoryName)) { + Common::printDebug("- Detected Site Search Category '$categoryName'. "); + } + if ($count !== false) { + Common::printDebug("- Search Results Count was '$count'. "); + } + if ($url != $originalUrl) { + Common::printDebug("NOTE: The Page URL was changed / removed, during the Site Search detection, was '$originalUrl', now is '$url'"); + } + + return array( + $actionName, + $url, + $categoryName, + $count + ); + } + + +} \ No newline at end of file diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index 00975d5cef0..793bb8107ac 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -630,20 +630,28 @@ protected function getCleanedEcommerceItems($items) $actionsToLookupAllItems = array_merge($actionsToLookupAllItems, $actionsToLookup); } - $actionsLookedUp = Action::loadActionId($actionsToLookupAllItems); + // add prefixId = false expected by loadActionId() + foreach($actionsToLookupAllItems as &$actionToLookup) { + $actionToLookup[] = false; + } + + $actionsLookedUp = TableActionIds::loadActionId($actionsToLookupAllItems); + + // id action is returned as the last element of the array + $keyIdAction = 3; // Replace SKU, name & category by their ID action foreach ($cleanedItems as $index => &$item) { // SKU - $item[0] = $actionsLookedUp[$index * $columnsInEachRow + 0][2]; + $item[0] = $actionsLookedUp[$index * $columnsInEachRow + 0][$keyIdAction]; // Name - $item[1] = $actionsLookedUp[$index * $columnsInEachRow + 1][2]; + $item[1] = $actionsLookedUp[$index * $columnsInEachRow + 1][$keyIdAction]; // Categories - $item[2] = $actionsLookedUp[$index * $columnsInEachRow + 2][2]; - $item[3] = $actionsLookedUp[$index * $columnsInEachRow + 3][2]; - $item[4] = $actionsLookedUp[$index * $columnsInEachRow + 4][2]; - $item[5] = $actionsLookedUp[$index * $columnsInEachRow + 5][2]; - $item[6] = $actionsLookedUp[$index * $columnsInEachRow + 6][2]; + $item[2] = $actionsLookedUp[$index * $columnsInEachRow + 2][$keyIdAction]; + $item[3] = $actionsLookedUp[$index * $columnsInEachRow + 3][$keyIdAction]; + $item[4] = $actionsLookedUp[$index * $columnsInEachRow + 4][$keyIdAction]; + $item[5] = $actionsLookedUp[$index * $columnsInEachRow + 5][$keyIdAction]; + $item[6] = $actionsLookedUp[$index * $columnsInEachRow + 6][$keyIdAction]; } return $cleanedItems; } diff --git a/core/Tracker/PageUrl.php b/core/Tracker/PageUrl.php index 37620cc7d47..46fc0685de2 100644 --- a/core/Tracker/PageUrl.php +++ b/core/Tracker/PageUrl.php @@ -35,7 +35,7 @@ class PageUrl /** * Given the Input URL, will exclude all query parameters set for this site - * Note: Site Search parameters are excluded in detectSiteSearch() + * Note: Site Search parameters are excluded in isSiteSearchDetected() * @static * @param $originalUrl * @param $idSite diff --git a/core/Tracker/TableActionIds.php b/core/Tracker/TableActionIds.php new file mode 100644 index 00000000000..6fd46343fe5 --- /dev/null +++ b/core/Tracker/TableActionIds.php @@ -0,0 +1,102 @@ + 0) { + $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) "; + } + $bind[] = $name; + $bind[] = $name; + $bind[] = $type; + $i++; + } + // Case URL & Title are empty + if (empty($bind)) { + return $actionNamesAndTypes; + } + $actionIds = Tracker::getDatabase()->fetchAll($sql, $bind); + + // For the Actions found in the lookup table, add the idaction in the array, + // If not found in lookup table, queue for INSERT + $actionsToInsert = array(); + foreach ($actionNamesAndTypes as $index => &$actionNameType) { + list($name, $type, $urlPrefix) = $actionNameType; + if (empty($name)) { + continue; + } + + $found = false; + foreach ($actionIds as $row) { + if ($name == $row['name'] + && $type == $row['type'] + ) { + $found = true; + $actionNameType[] = $row['idaction']; + continue; + } + } + if (!$found) { + $actionsToInsert[] = $index; + } + } + + $sql = "INSERT INTO " . Common::prefixTable('log_action') . + "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)"; + // Then, we insert all new actions in the lookup table + foreach ($actionsToInsert as $actionToInsert) { + list($name, $type, $urlPrefix) = $actionNamesAndTypes[$actionToInsert]; + + Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix)); + $actionId = Tracker::getDatabase()->lastInsertId(); + Common::printDebug("Recorded a new action (" . Action::getTypeAsString($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")"); + + $actionNamesAndTypes[$actionToInsert][] = $actionId; + } + return $actionNamesAndTypes; + } +} \ No newline at end of file diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index 29da90dd893..be9ce912d7b 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -11,10 +11,8 @@ namespace Piwik\Tracker; -use Exception; use Piwik\Common; use Piwik\Config; - use Piwik\IP; use Piwik\Piwik; use Piwik\Tracker; @@ -107,7 +105,6 @@ public function handle() $this->goalManager = new GoalManager($this->request); $visitIsConverted = false; - $idActionUrl = $idActionName = $actionType = false; $action = null; $requestIsManualGoalConversion = ($this->goalManager->idGoal > 0); @@ -131,27 +128,14 @@ public function handle() } } // normal page view, potentially triggering a URL matching goal else { - $action = $this->newAction(); + $action = Action::make($this->request); + + $action->writeDebugInfo(); - if ($this->detectActionIsOutlinkOnAliasHost($action)) { - Common::printDebug("INFO: The outlink URL host is one of the known host for this website. "); - } - if (isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) { - $type = Action::getActionTypeName($action->getActionType()); - Common::printDebug("Action is a $type, - Action name = " . $action->getActionName() . ", - Action URL = " . $action->getActionUrl()); - } $someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->request->getIdSite(), $action); $visitIsConverted = $someGoalsConverted; $action->loadIdActionNameAndUrl(); - $idActionUrl = $action->getIdActionUrl(); - if ($idActionUrl !== null) { - $idActionUrl = (int)$idActionUrl; - } - $idActionName = (int)$action->getIdActionName(); - $actionType = $action->getActionType(); } // the visitor and session @@ -175,7 +159,7 @@ public function handle() $idReferrerActionUrl = $this->visitorInfo['visit_exit_idaction_url']; $idReferrerActionName = $this->visitorInfo['visit_exit_idaction_name']; try { - $this->handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted); + $this->handleKnownVisit($action, $visitIsConverted); if (!is_null($action)) { $action->record($this->visitorInfo['idvisit'], $this->visitorInfo['idvisitor'], @@ -211,7 +195,7 @@ public function handle() if (!$this->isVisitorKnown() || !$isLastActionInTheSameVisit ) { - $this->handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted); + $this->handleNewVisit($action, $visitIsConverted); if (!is_null($action)) { $action->record($this->visitorInfo['idvisit'], $this->visitorInfo['idvisitor'], 0, 0, 0); } @@ -246,32 +230,40 @@ public function handle() * * Tracker.knownVisitorInformation is triggered after saving the new visit data * Even data is an array with updated information about the visit - * @param $idActionUrl - * @param $idActionName - * @param $actionType + * @param $action * @param $visitIsConverted * @throws VisitorNotFoundInDb */ - protected function handleKnownVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted) + protected function handleKnownVisit($action, $visitIsConverted) { // gather information that needs to be updated $valuesToUpdate = array(); $incrementActions = false; $sqlActionUpdate = ''; - if (!empty($idActionName)) { - $valuesToUpdate['visit_exit_idaction_name'] = (int)$idActionName; - } - if ($idActionUrl !== false) { - $valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl; - $incrementActions = true; - } - if ($actionType == Action::TYPE_SITE_SEARCH) { - $sqlActionUpdate .= "visit_total_searches = visit_total_searches + 1, "; - $incrementActions = true; - } - if ($incrementActions) { - $sqlActionUpdate .= "visit_total_actions = visit_total_actions + 1, "; + if($action) { + $idActionUrl = $action->getIdActionUrl(); + $idActionName = $action->getIdActionName(); + $actionType = $action->getActionType(); + + if (!empty($idActionName)) { + $valuesToUpdate['visit_exit_idaction_name'] = $idActionName; + } + if (!empty($idActionUrl)) { + $valuesToUpdate['visit_exit_idaction_url'] = $idActionUrl; + $incrementActions = true; + } + if ($actionType == Action::TYPE_SITE_SEARCH) { + $sqlActionUpdate .= "visit_total_searches = visit_total_searches + 1, "; + $incrementActions = true; + } else if ($actionType == Action::TYPE_EVENT) { + $sqlActionUpdate .= "visit_total_event = visit_total_event + 1, "; + $incrementActions = true; + } + + if ($incrementActions) { + $sqlActionUpdate .= "visit_total_actions = visit_total_actions + 1, "; + } } Common::printDebug("Visit is known (IP = " . IP::N2P($this->getVisitorIp()) . ")"); @@ -378,13 +370,19 @@ protected function getTimeSpentReferrerAction() * 1) Insert the new action * * 2) Insert the visit information - * @param $idActionUrl - * @param $idActionName - * @param $actionType + * + * @param $action * @param $visitIsConverted */ - protected function handleNewVisit($idActionUrl, $idActionName, $actionType, $visitIsConverted) + protected function handleNewVisit($action, $visitIsConverted) { + $actionType = $idActionName = $idActionUrl = false; + if($action) { + $idActionUrl = $action->getIdActionUrl(); + $idActionName = $action->getIdActionName(); + $actionType = $action->getActionType(); + } + Common::printDebug("New Visit (IP = " . IP::N2P($this->getVisitorIp()) . ")"); $daysSinceFirstVisit = $this->request->getDaysSinceFirstVisit(); @@ -436,9 +434,11 @@ protected function handleNewVisit($idActionUrl, $idActionName, $actionType, $vis array(Action::TYPE_PAGE_URL, Action::TYPE_DOWNLOAD, Action::TYPE_OUTLINK, - Action::TYPE_SITE_SEARCH)) + Action::TYPE_SITE_SEARCH, + Action::TYPE_EVENT)) ? 1 : 0, // if visit starts with something else (e.g. ecommerce order), don't record as an action 'visit_total_searches' => $actionType == Action::TYPE_SITE_SEARCH ? 1 : 0, + 'visit_total_searches' => $actionType == Action::TYPE_EVENT ? 1 : 0, 'visit_total_time' => self::cleanupVisitTotalTime($defaultTimeOnePageVisit), 'visit_goal_converted' => $visitIsConverted ? 1 : 0, 'visit_goal_buyer' => $this->goalManager->getBuyerType(), @@ -892,38 +892,6 @@ protected function isVisitorKnown() return $this->visitorKnown === true; } - /** - * Returns an object able to handle the current action - * Plugins can return an override Action that for example, does not record the action in the DB - * - * @throws Exception - * @return Action child or fake but with same public interface - */ - protected function newAction() - { - $action = new Action($this->request); - return $action; - } - - /** - * Detect whether action is an outlink given host aliases - * - * @param ActionInterface $action - * @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website - */ - protected function detectActionIsOutlinkOnAliasHost(ActionInterface $action) - { - if ($action->getActionType() != ActionInterface::TYPE_OUTLINK) { - return false; - } - $decodedActionUrl = $action->getActionUrl(); - $actionUrlParsed = @parse_url($decodedActionUrl); - if (!isset($actionUrlParsed['host'])) { - return false; - } - return Tracker\Visit::isHostKnownAliasHost($actionUrlParsed['host'], $this->request->getIdSite()); - } - /** * Returns a 64-bit hash of all the configuration settings * @param $os @@ -979,4 +947,5 @@ static public function isHostKnownAliasHost($urlHost, $idSite) } return false; } + } diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index 1251073d944..398278ba9ef 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -13,14 +13,15 @@ use Exception; use Piwik\Archive; use Piwik\Common; -use Piwik\DataTable; +use Piwik\DataTable; use Piwik\Date; -use Piwik\Metrics; +use Piwik\Metrics; use Piwik\Piwik; use Piwik\Plugins\CustomVariables\API as APICustomVariables; use Piwik\Tracker\Action; +use Piwik\Tracker\ActionSiteSearch; use Piwik\Tracker\PageUrl; /** @@ -336,7 +337,7 @@ public function getSiteSearchCategories($idSite, $period, $date, $segment = fals Actions::checkCustomVariablesPluginEnabled(); $customVariables = APICustomVariables::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false, $_leavePiwikCoreVariables = true); - $customVarNameToLookFor = Action::CVAR_KEY_SEARCH_CATEGORY; + $customVarNameToLookFor = ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY; $dataTable = new DataTable(); // Handle case where date=last30&period=day diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 9a3ef201f1f..cd981cbcfc7 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -17,13 +17,14 @@ use Piwik\Menu\MenuMain; use Piwik\MetricsFormatter; use Piwik\Piwik; +use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\SegmentExpression; use Piwik\Site; use Piwik\Tracker\Action; +use Piwik\Tracker\TableActionIds; use Piwik\ViewDataTable\Request as ViewDataTableRequest; use Piwik\WidgetsList; -use \Piwik\Plugin\ViewDataTable; /** * Actions plugin @@ -176,7 +177,7 @@ public function getIdActionFromSegment($valueToMatch, $sqlField, $matchType, $se if ($matchType == SegmentExpression::MATCH_EQUAL || $matchType == SegmentExpression::MATCH_NOT_EQUAL ) { - $sql = Action::getSqlSelectActionId(); + $sql = TableActionIds::getSqlSelectActionId(); $bind = array($valueToMatch, $valueToMatch, $actionType); $idAction = Db::fetchOne($sql, $bind); // if the action is not found, we hack -100 to ensure it tries to match against an integer diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php index 6c8fcf5107b..a5871231417 100644 --- a/plugins/Actions/Archiver.php +++ b/plugins/Actions/Archiver.php @@ -14,6 +14,7 @@ use Piwik\Metrics; use Piwik\RankingQuery; use Piwik\Tracker\Action; +use Piwik\Tracker\ActionSiteSearch; /** * Class encapsulating logic to process Day/Period Archiving for the Actions reports @@ -133,20 +134,20 @@ protected function archiveDayActions($rankingQueryLimit) count(distinct log_link_visit_action.idvisitor) as `" . Metrics::INDEX_NB_UNIQ_VISITORS . "`, count(*) as `" . Metrics::INDEX_PAGE_NB_HITS . "`, sum( - case when " . Action::DB_COLUMN_TIME_GENERATION . " is null + case when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null then 0 - else " . Action::DB_COLUMN_TIME_GENERATION . " + else " . Action::DB_COLUMN_CUSTOM_FLOAT . " end ) / 1000 as `" . Metrics::INDEX_PAGE_SUM_TIME_GENERATION . "`, sum( - case when " . Action::DB_COLUMN_TIME_GENERATION . " is null + case when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null then 0 else 1 end ) as `" . Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`, - min(" . Action::DB_COLUMN_TIME_GENERATION . ") / 1000 + min(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") / 1000 as `" . Metrics::INDEX_PAGE_MIN_TIME_GENERATION . "`, - max(" . Action::DB_COLUMN_TIME_GENERATION . ") / 1000 + max(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") / 1000 as `" . Metrics::INDEX_PAGE_MAX_TIME_GENERATION . "` "; @@ -189,8 +190,8 @@ protected function archiveDayActions($rankingQueryLimit) // 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" . Action::CVAR_INDEX_SEARCH_COUNT . ") = 0 - AND log_link_visit_action.custom_var_k" . Action::CVAR_INDEX_SEARCH_COUNT . " = '" . Action::CVAR_KEY_SEARCH_COUNT . "') + CASE WHEN (MAX(log_link_visit_action.custom_var_v" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . ") = 0 + AND log_link_visit_action.custom_var_k" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . " = '" . ActionSiteSearch::CVAR_KEY_SEARCH_COUNT . "') THEN 1 ELSE 0 END AS `" . Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`"; diff --git a/plugins/CustomVariables/API.php b/plugins/CustomVariables/API.php index 522a05cf817..b985b3f9774 100644 --- a/plugins/CustomVariables/API.php +++ b/plugins/CustomVariables/API.php @@ -10,12 +10,12 @@ */ namespace Piwik\Plugins\CustomVariables; +use Piwik\Tracker\ActionSiteSearch; use Piwik\Archive; use Piwik\DataTable; use Piwik\Date; use Piwik\Metrics; use Piwik\Piwik; -use Piwik\Tracker\Action; /** * The Custom Variables API lets you access reports for your Custom Variables names and values. @@ -77,7 +77,7 @@ public function getCustomVariables($idSite, $period, $date, $segment = false, $e */ public static function getReservedCustomVariableKeys() { - return array('_pks', '_pkn', '_pkc', '_pkp', Action::CVAR_KEY_SEARCH_COUNT, Action::CVAR_KEY_SEARCH_CATEGORY); + return array('_pks', '_pkn', '_pkc', '_pkp', ActionSiteSearch::CVAR_KEY_SEARCH_COUNT, ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY); } /** diff --git a/plugins/Live/API.php b/plugins/Live/API.php index 882935bb7f9..40835151d86 100644 --- a/plugins/Live/API.php +++ b/plugins/Live/API.php @@ -544,8 +544,8 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite, $flat = private function getCustomVariablePrettyKey($key) { $rename = array( - Action::CVAR_KEY_SEARCH_CATEGORY => Piwik::translate('Actions_ColumnSearchCategory'), - Action::CVAR_KEY_SEARCH_COUNT => Piwik::translate('Actions_ColumnSearchResultsCount'), + Tracker\ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY => Piwik::translate('Actions_ColumnSearchCategory'), + Tracker\ActionSiteSearch::CVAR_KEY_SEARCH_COUNT => Piwik::translate('Actions_ColumnSearchResultsCount'), ); if (isset($rename[$key])) { return $rename[$key]; diff --git a/tests/PHPUnit/Fixtures/TwoVisitsWithCustomEvents.php b/tests/PHPUnit/Fixtures/TwoVisitsWithCustomEvents.php index 8ffd5b4c4a2..de47451d137 100644 --- a/tests/PHPUnit/Fixtures/TwoVisitsWithCustomEvents.php +++ b/tests/PHPUnit/Fixtures/TwoVisitsWithCustomEvents.php @@ -34,7 +34,6 @@ public function trackVisits() { $uselocal = false; $vis = self::getTracker($this->idSite, $this->dateTime, $useDefault = true, $uselocal); - $this->moveTimeForward($vis); $this->trackMusicPlaying($vis); $this->trackMusicRatings($vis); diff --git a/tests/PHPUnit/UI b/tests/PHPUnit/UI index a4c99cd1676..b42f538c500 160000 --- a/tests/PHPUnit/UI +++ b/tests/PHPUnit/UI @@ -1 +1 @@ -Subproject commit a4c99cd1676bb7fea4fb1fb11997c08449bb7cfa +Subproject commit b42f538c5002b787cf99b759fe9ad6f059233f32