2023-01-23 11:03:31 +01:00

339 lines
12 KiB
PHP

<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
use Piwik\Cache;
use Piwik\CacheId;
use Piwik\Config;
use Piwik\DataTable\Filter\ColumnDelete;
use Piwik\Date;
use Piwik\Metrics\Formatter;
use Piwik\Plugin;
use Piwik\Piwik;
use Piwik\Tracker\GoalManager;
class Visitor implements VisitorInterface
{
private $details = array();
function __construct($visitorRawData)
{
$this->details = $visitorRawData;
}
function getAllVisitorDetails()
{
$visitor = array();
$instances = self::getAllVisitorDetailsInstances();
foreach ($instances as $instance) {
$instance->setDetails($this->details);
$instance->extendVisitorDetails($visitor);
}
/**
* This event can be used to add any details to a visitor. The visitor's details are for instance used in
* API requests like 'Live.getVisitorProfile' and 'Live.getLastVisitDetails'. This can be useful for instance
* in case your plugin defines any visit dimensions and you want to add the value of your dimension to a user.
* It can be also useful if you want to enrich a visitor with custom fields based on other fields or if you
* want to change or remove any fields from the user.
*
* **Example**
*
* Piwik::addAction('Live.getAllVisitorDetails', function (&visitor, $details) {
* $visitor['userPoints'] = $details['actions'] + $details['events'] + $details['searches'];
* unset($visitor['anyFieldYouWantToRemove']);
* });
*
* @param array &visitor You can add or remove fields to the visitor array and it will reflected in the API output
* @param array $details The details array contains all visit dimensions (columns of log_visit table)
*
* @deprecated will be removed in Piwik 4
*/
Piwik::postEvent('Live.getAllVisitorDetails', array(&$visitor, $this->details));
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 are not meant to be displayed (md5 config hash)
* Or that the user should only access if they are Super User or admin (cookie, IP)
*
* @param array $visitorDetails
* @return array
*/
public static function cleanVisitorDetails($visitorDetails)
{
$toUnset = array('config_id');
if (Piwik::isUserIsAnonymous()) {
$toUnset[] = 'idvisitor';
$toUnset[] = 'user_id';
$toUnset[] = 'location_ip';
}
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);
}
usort($actionDetails, array('static', 'sortByServerTime'));
$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 sortByServerTime($a, $b)
{
$ta = strtotime($a['serverTimePretty']);
$tb = strtotime($b['serverTimePretty']);
if ($ta < $tb) {
return -1;
}
if ($ta == $tb) {
if ($a['idlink_va'] == $b['idlink_va']) {
return strcmp($a['type'], $b['type']);
}
if ($a['idlink_va'] > $b['idlink_va']) {
return 1;
}
return -1;
}
return 1;
}
}