details = $visitorRawData; } function getAllVisitorDetails() { $visitor = array(); $instances = self::getAllVisitorDetailsInstances(); foreach ($instances as $instance) { $instance->setDetails($this->details); $instance->extendVisitorDetails($visitor); } return $visitor; } /** * Returns all available visitor details instances * * @return VisitorDetailsAbstract[] * @throws \Exception */ public static function getAllVisitorDetailsInstances() { $cacheId = CacheId::pluginAware('VisitorDetails'); $cache = Cache::getTransientCache(); if (!$cache->contains($cacheId)) { $instances = [ new VisitorDetails() // needs to be first ]; /** * Triggered to add new visitor details that cannot be picked up by the platform automatically. * * **Example** * * public function addVisitorDetails(&$visitorDetails) * { * $visitorDetails[] = new CustomVisitorDetails(); * } * * @param VisitorDetailsAbstract[] $visitorDetails An array of visitorDetails */ Piwik::postEvent('Live.addVisitorDetails', array(&$instances)); foreach (self::getAllVisitorDetailsClasses() as $className) { $instance = new $className(); if ($instance instanceof VisitorDetails) { continue; } $instances[] = $instance; } /** * Triggered to filter / restrict vistor details. * * **Example** * * public function filterVisitorDetails(&$visitorDetails) * { * foreach ($visitorDetails as $index => $visitorDetail) { * if (strpos(get_class($visitorDetail), 'MyPluginName') !== false) {} * unset($visitorDetails[$index]); // remove all visitor details for a specific plugin * } * } * } * * @param VisitorDetailsAbstract[] $visitorDetails An array of visitorDetails */ Piwik::postEvent('Live.filterVisitorDetails', array(&$instances)); $cache->save($cacheId, $instances); } return $cache->fetch($cacheId); } /** * Returns class names of all VisitorDetails classes. * * @return string[] * @api */ protected static function getAllVisitorDetailsClasses() { return Plugin\Manager::getInstance()->findComponents('VisitorDetails', 'Piwik\Plugins\Live\VisitorDetailsAbstract'); } function getVisitorId() { if (isset($this->details['idvisitor'])) { return bin2hex($this->details['idvisitor']); } return false; } /** * Removes fields that the user should only access if they are Super User or admin (cookie, IP, * md5 config "fingerprint" hash) * * @param array $visitorDetails * @return array */ public static function cleanVisitorDetails($visitorDetails) { if (Piwik::isUserIsAnonymous()) { $toUnset = array( 'idvisitor', 'user_id', 'location_ip', 'config_id' ); foreach ($toUnset as $keyName) { if (isset($visitorDetails[$keyName])) { unset($visitorDetails[$keyName]); } } } return $visitorDetails; } /** * The &flat=1 feature is used by API.getSuggestedValuesForSegment * * @param $visitorDetailsArray * @return array */ public static function flattenVisitorDetailsArray($visitorDetailsArray) { // NOTE: if you flatten more fields from the "actionDetails" array // ==> also update API/API.php getSuggestedValuesForSegment(), the $segmentsNeedActionsInfo array // flatten visit custom variables if (!empty($visitorDetailsArray['customVariables']) && is_array($visitorDetailsArray['customVariables'])) { foreach ($visitorDetailsArray['customVariables'] as $thisCustomVar) { $visitorDetailsArray = array_merge($visitorDetailsArray, $thisCustomVar); } unset($visitorDetailsArray['customVariables']); } // flatten page views custom variables $count = 1; foreach ($visitorDetailsArray['actionDetails'] as $action) { if (!empty($action['customVariables'])) { foreach ($action['customVariables'] as $thisCustomVar) { foreach ($thisCustomVar as $cvKey => $cvValue) { $flattenedKeyName = $cvKey . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count; $visitorDetailsArray[$flattenedKeyName] = $cvValue; $count++; } } } } // Flatten Goals $count = 1; foreach ($visitorDetailsArray['actionDetails'] as $action) { if (!empty($action['goalId'])) { $flattenedKeyName = 'visitConvertedGoalId' . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count; $visitorDetailsArray[$flattenedKeyName] = $action['goalId']; $count++; } } // Flatten Page Titles/URLs $count = 1; foreach ($visitorDetailsArray['actionDetails'] as $action) { // API.getSuggestedValuesForSegment $flattenForActionType = array( 'outlink' => 'outlinkUrl', 'download' => 'downloadUrl', 'event' => 'eventUrl', 'action' => 'pageUrl' ); foreach($flattenForActionType as $actionType => $flattenedKeyPrefix) { if (!empty($action['url']) && $action['type'] == $actionType) { $flattenedKeyName = $flattenedKeyPrefix . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count; $visitorDetailsArray[$flattenedKeyName] = $action['url']; } } $flatten = array( 'pageTitle', 'siteSearchKeyword', 'eventCategory', 'eventAction', 'eventName', 'eventValue'); foreach($flatten as $toFlatten) { if (!empty($action[$toFlatten])) { $flattenedKeyName = $toFlatten . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count; $visitorDetailsArray[$flattenedKeyName] = $action[$toFlatten]; } } $count++; } // Entry/exit pages $firstAction = $lastAction = false; foreach ($visitorDetailsArray['actionDetails'] as $action) { if ($action['type'] == 'action') { if (empty($firstAction)) { $firstAction = $action; } $lastAction = $action; } } if (!empty($firstAction['pageTitle'])) { $visitorDetailsArray['entryPageTitle'] = $firstAction['pageTitle']; } if (!empty($firstAction['url'])) { $visitorDetailsArray['entryPageUrl'] = $firstAction['url']; } if (!empty($lastAction['pageTitle'])) { $visitorDetailsArray['exitPageTitle'] = $lastAction['pageTitle']; } if (!empty($lastAction['url'])) { $visitorDetailsArray['exitPageUrl'] = $lastAction['url']; } return $visitorDetailsArray; } /** * @param array $visitorDetailsArray * @param array $actionDetails preset action details * * @return array */ public static function enrichVisitorArrayWithActions($visitorDetailsArray, $actionDetails = array()) { $actionsLimit = (int)Config::getInstance()->General['visitor_log_maximum_actions_per_visit']; $visitorDetailsManipulators = self::getAllVisitorDetailsInstances(); foreach ($visitorDetailsManipulators as $instance) { $instance->provideActionsForVisit($actionDetails, $visitorDetailsArray); } foreach ($visitorDetailsManipulators as $instance) { $instance->filterActions($actionDetails, $visitorDetailsArray); } $actionDetails = self::sortActionDetails($actionDetails); $actionDetails = array_values($actionDetails); // limit actions if ($actionsLimit < count($actionDetails)) { $visitorDetailsArray['truncatedActionsCount'] = count($actionDetails) - $actionsLimit; $actionDetails = array_slice($actionDetails, 0, $actionsLimit); } foreach ($actionDetails as $actionIdx => &$actionDetail) { $actionDetail =& $actionDetails[$actionIdx]; $nextAction = isset($actionDetails[$actionIdx+1]) ? $actionDetails[$actionIdx+1] : null; foreach ($visitorDetailsManipulators as $instance) { $instance->extendActionDetails($actionDetail, $nextAction, $visitorDetailsArray); } } $visitorDetailsArray['actionDetails'] = $actionDetails; return $visitorDetailsArray; } private static function sortActionDetails($actions) { usort($actions, function ($a, $b) use ($actions) { $fields = array('serverTimePretty', 'idlink_va', 'type', 'title', 'url', 'pageIdAction', 'goalId'); foreach ($fields as $field) { $sort = VisitorLog::sortByActionsOnPageColumn($a, $b, $field); if ($sort !== 0) { return $sort; } } $indexA = array_search($a, $actions); $indexB = array_search($b, $actions); return $indexA > $indexB ? 1 : -1; }); return $actions; } }