$posPrevious) { $action = $actionType; } } if (!empty($action)) { return $action; } return new ActionPageview($request); } private static function getPriority(Action $actionType) { $key = array_search($actionType->getActionType(), self::$factoryPriority); if (false === $key) { return -1; } return $key; } public static function shouldHandle(Request $request) { return false; } private static function getAllActions(Request $request) { static $actions; if (is_null($actions)) { $actions = Manager::getInstance()->findMultipleComponents('Actions', '\\Piwik\\Tracker\\Action'); } $instances = array(); foreach ($actions as $action) { /** @var \Piwik\Tracker\Action $action */ if ($action::shouldHandle($request)) { $instances[] = new $action($request); } } return $instances; } public function __construct($type, Request $request) { $this->actionType = $type; $this->request = $request; $this->logger = StaticContainer::get(LoggerInterface::class); } /** * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked * * @return string */ public function getActionUrl() { return $this->actionUrl; } /** * Returns URL of page being tracked, including all original Query parameters */ public function getActionUrlRaw() { return $this->rawActionUrl; } public function getActionName() { return $this->actionName; } public function getActionType() { return $this->actionType; } public function getCustomVariables() { return $this->request->getCustomVariables($scope = 'page'); } // custom_float column public function getCustomFloatValue() { return false; } protected function setActionName($name) { $this->actionName = PageUrl::cleanupString((string)$name); } protected function setActionUrl($url) { $this->rawActionUrl = PageUrl::getUrlIfLookValid($url); $url2 = PageUrl::excludeQueryParametersFromUrl($url, $this->request->getIdSite()); $this->actionUrl = PageUrl::getUrlIfLookValid($url2); if ($url != $this->rawActionUrl) { $this->logger->debug(' Before was "{rawActionUrl}"', [ 'rawActionUrl' => $this->rawActionUrl, ]); $this->logger->debug(' After is "{url2}"', [ 'url2' => $url2, ]); } } protected function setActionUrlWithoutExcludingParameters($url) { $url = PageUrl::getUrlIfLookValid($url); $this->rawActionUrl = $url; $this->actionUrl = $url; } abstract protected function getActionsToLookup(); protected function getUrlAndType() { $url = $this->getActionUrl(); if (!empty($url)) { // normalize urls by stripping protocol and www $url = PageUrl::normalizeUrl($url); return array($url['url'], self::TYPE_PAGE_URL, $url['prefixId']); } return false; } public function setCustomField($field, $value) { $this->customFields[$field] = $value; } public function getCustomField($field) { if (isset($this->customFields[$field])) { return $this->customFields[$field]; } } public function getCustomFields() { return $this->customFields; } public function getIdActionUrl() { $idUrl = isset($this->actionIdsCached['idaction_url']) ? $this->actionIdsCached['idaction_url'] : 0; // note; idaction_url = 0 is displayed as "Page URL Not Defined" return (int)$idUrl; } public function getIdActionUrlForEntryAndExitIds() { return false; } public function getIdActionNameForEntryAndExitIds() { return false; } public function getIdActionName() { if (!isset($this->actionIdsCached['idaction_name'])) { return false; } return $this->actionIdsCached['idaction_name']; } /** * Returns the ID of the newly created record in the log_link_visit_action table * * @return int */ public function getIdLinkVisitAction() { return $this->idLinkVisitAction; } public static function getTypeAsString($type) { $class = new \ReflectionClass("\\Piwik\\Tracker\\Action"); $constants = $class->getConstants(); $typeId = array_search($type, $constants); if (false === $typeId) { return $type; } return str_replace('TYPE_', '', $typeId); } /** * Loads the idaction of the current action name and the current action url. * These idactions are used in the visitor logging table to link the visit information * (entry action, exit action) to the actions. * These idactions are also used in the table that links the visits and their actions. * * The methods takes care of creating a new record(s) in the action table if the existing * action name and action url doesn't exist yet. */ public function loadIdsFromLogActionTable() { if (!empty($this->actionIdsCached)) { return; } /** @var ActionDimension[] $dimensions */ $dimensions = ActionDimension::getAllDimensions(); $actions = $this->getActionsToLookup(); foreach ($dimensions as $dimension) { $value = $dimension->onLookupAction($this->request, $this); if (false !== $value) { if (is_float($value)) { $value = Common::forceDotAsSeparatorForDecimalPoint($value); } $field = $dimension->getColumnName(); if (empty($field)) { $dimensionClass = get_class($dimension); throw new Exception('Dimension ' . $dimensionClass . ' does not define a field name'); } $actionId = $dimension->getActionId(); $actions[$field] = array($value, $actionId); $this->logger->debug("$field = $value"); } } $actions = array_filter($actions); if (empty($actions)) { return; } $loadedActionIds = TableLogAction::loadIdsAction($actions); $this->actionIdsCached = $loadedActionIds; return $this->actionIdsCached; } /** * Records in the DB the association between the visit and this action. * * @param int $idReferrerActionUrl is the ID of the last action done by the current visit. * @param $idReferrerActionName * @param Visitor $visitor */ public function record(Visitor $visitor, $idReferrerActionUrl, $idReferrerActionName) { $this->loadIdsFromLogActionTable(); $visitAction = array( 'idvisit' => $visitor->getVisitorColumn('idvisit'), 'idsite' => $this->request->getIdSite(), 'idvisitor' => $visitor->getVisitorColumn('idvisitor'), 'idaction_url' => $this->getIdActionUrl(), 'idaction_url_ref' => $idReferrerActionUrl, 'idaction_name_ref' => $idReferrerActionName ); /** @var ActionDimension[] $dimensions */ $dimensions = ActionDimension::getAllDimensions(); foreach ($dimensions as $dimension) { $value = $dimension->onNewAction($this->request, $visitor, $this); if ($value !== false) { if (is_float($value)) { $value = Common::forceDotAsSeparatorForDecimalPoint($value); } $visitAction[$dimension->getColumnName()] = $value; } } // idaction_name is NULLable. we only set it when applicable if ($this->isActionHasActionName()) { $visitAction['idaction_name'] = (int)$this->getIdActionName(); } foreach ($this->actionIdsCached as $field => $idAction) { $visitAction[$field] = ($idAction === false) ? 0 : $idAction; } $customValue = $this->getCustomFloatValue(); if ($customValue !== false && $customValue !== null && $customValue !== '') { $visitAction[self::DB_COLUMN_CUSTOM_FLOAT] = Common::forceDotAsSeparatorForDecimalPoint($customValue); } $visitAction = array_merge($visitAction, $this->customFields); $this->idLinkVisitAction = $this->getModel()->createAction($visitAction); $visitAction['idlink_va'] = $this->idLinkVisitAction; $visitActionDebug = $visitAction; $visitActionDebug['idvisitor'] = bin2hex($visitActionDebug['idvisitor']); $this->logger->debug("Inserted new action: {action}", [ 'action' => var_export($visitActionDebug, true), ]); } public function writeDebugInfo() { $type = self::getTypeAsString($this->getActionType()); $name = $this->getActionName(); $url = $this->getActionUrl(); $this->logger->debug('Action is a {type}, Action name = {name}, Action URL = {url}', [ 'type' => $type, 'name' => $name, 'url' => $url, ]); return true; } private function getModel() { return new Model(); } /** * @return bool */ private function isActionHasActionName() { $types = array(self::TYPE_PAGE_TITLE, self::TYPE_PAGE_URL, self::TYPE_SITE_SEARCH); return in_array($this->getActionType(), $types); } }