hide map for Character groups Quest Stations when there are no stations
699
www/analytics/plugins/Live/API.php
Normal file
|
|
@ -0,0 +1,699 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Live;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Common;
|
||||
use Piwik\Config;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\MetricsFormatter;
|
||||
use Piwik\Period;
|
||||
use Piwik\Period\Range;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\Referrers\API as APIReferrers;
|
||||
use Piwik\Plugins\SitesManager\API as APISitesManager;
|
||||
use Piwik\Segment;
|
||||
use Piwik\Site;
|
||||
use Piwik\Tracker;
|
||||
|
||||
/**
|
||||
* @see plugins/Live/Visitor.php
|
||||
*/
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/Live/Visitor.php';
|
||||
|
||||
/**
|
||||
* The Live! API lets you access complete visit level information about your visitors. Combined with the power of <a href='http://piwik.org/docs/analytics-api/segmentation/' target='_blank'>Segmentation</a>,
|
||||
* you will be able to request visits filtered by any criteria.
|
||||
*
|
||||
* The method "getLastVisitsDetails" will return extensive data for each visit, which includes: server time, visitId, visitorId,
|
||||
* visitorType (new or returning), number of pages, list of all pages (and events, file downloaded and outlinks clicked),
|
||||
* custom variables names and values set to this visit, number of goal conversions (and list of all Goal conversions for this visit,
|
||||
* with time of conversion, revenue, URL, etc.), but also other attributes such as: days since last visit, days since first visit,
|
||||
* country, continent, visitor IP,
|
||||
* provider, referrer used (referrer name, keyword if it was a search engine, full URL), campaign name and keyword, operating system,
|
||||
* browser, type of screen, resolution, supported browser plugins (flash, java, silverlight, pdf, etc.), various dates & times format to make
|
||||
* it easier for API users... and more!
|
||||
*
|
||||
* With the parameter <a href='http://piwik.org/docs/analytics-api/segmentation/' target='_blank'>'&segment='</a> you can filter the
|
||||
* returned visits by any criteria (visitor IP, visitor ID, country, keyword used, time of day, etc.).
|
||||
*
|
||||
* The method "getCounters" is used to return a simple counter: visits, number of actions, number of converted visits, in the last N minutes.
|
||||
*
|
||||
* See also the documentation about <a href='http://piwik.org/docs/real-time/' target='_blank'>Real time widget and visitor level reports</a> in Piwik.
|
||||
* @method static \Piwik\Plugins\Live\API getInstance()
|
||||
*/
|
||||
class API extends \Piwik\Plugin\API
|
||||
{
|
||||
const VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE = 100;
|
||||
const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10;
|
||||
const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%';
|
||||
|
||||
/**
|
||||
* This will return simple counters, for a given website ID, for visits over the last N minutes
|
||||
*
|
||||
* @param int $idSite Id Site
|
||||
* @param int $lastMinutes Number of minutes to look back at
|
||||
* @param bool|string $segment
|
||||
* @return array( visits => N, actions => M, visitsConverted => P )
|
||||
*/
|
||||
public function getCounters($idSite, $lastMinutes, $segment = false)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
$lastMinutes = (int)$lastMinutes;
|
||||
|
||||
$select = "count(*) as visits,
|
||||
SUM(log_visit.visit_total_actions) as actions,
|
||||
SUM(log_visit.visit_goal_converted) as visitsConverted,
|
||||
COUNT(DISTINCT log_visit.idvisitor) as visitors";
|
||||
|
||||
$from = "log_visit";
|
||||
|
||||
list($whereIdSites, $idSites) = $this->getIdSitesWhereClause($idSite);
|
||||
|
||||
$where = $whereIdSites . "AND log_visit.visit_last_action_time >= ?";
|
||||
$bind = $idSites;
|
||||
$bind[] = Date::factory(time() - $lastMinutes * 60)->toString('Y-m-d H:i:s');
|
||||
|
||||
$segment = new Segment($segment, $idSite);
|
||||
$query = $segment->getSelectQuery($select, $from, $where, $bind);
|
||||
|
||||
$data = Db::fetchAll($query['sql'], $query['bind']);
|
||||
|
||||
// These could be unset for some reasons, ensure they are set to 0
|
||||
if (empty($data[0]['actions'])) {
|
||||
$data[0]['actions'] = 0;
|
||||
}
|
||||
if (empty($data[0]['visitsConverted'])) {
|
||||
$data[0]['visitsConverted'] = 0;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same functionnality can be obtained using segment=visitorId==$visitorId with getLastVisitsDetails
|
||||
*
|
||||
* @deprecated
|
||||
* @ignore
|
||||
* @param int $visitorId
|
||||
* @param int $idSite
|
||||
* @param int $filter_limit
|
||||
* @param bool $flat Whether to flatten the visitor details array
|
||||
*
|
||||
* @return DataTable
|
||||
*/
|
||||
public function getLastVisitsForVisitor($visitorId, $idSite, $filter_limit = 10, $flat = false)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
$countVisitorsToFetch = $filter_limit;
|
||||
|
||||
$table = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $countVisitorsToFetch, $visitorId);
|
||||
$this->addFilterToCleanVisitors($table, $idSite, $flat);
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last visits tracked in the specified website
|
||||
* You can define any number of filters: none, one, many or all parameters can be defined
|
||||
*
|
||||
* @param int $idSite Site ID
|
||||
* @param bool|string $period Period to restrict to when looking at the logs
|
||||
* @param bool|string $date Date to restrict to
|
||||
* @param bool|int $segment (optional) Number of visits rows to return
|
||||
* @param bool|int $countVisitorsToFetch (optional) Only return the last X visits. By default the last GET['filter_offset']+GET['filter_limit'] are returned.
|
||||
* @param bool|int $minTimestamp (optional) Minimum timestamp to restrict the query to (useful when paginating or refreshing visits)
|
||||
* @param bool $flat
|
||||
* @param bool $doNotFetchActions
|
||||
* @return DataTable
|
||||
*/
|
||||
public function getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $countVisitorsToFetch = false, $minTimestamp = false, $flat = false, $doNotFetchActions = false)
|
||||
{
|
||||
if (false === $countVisitorsToFetch) {
|
||||
$filter_limit = Common::getRequestVar('filter_limit', 10, 'int');
|
||||
$filter_offset = Common::getRequestVar('filter_offset', 0, 'int');
|
||||
|
||||
$countVisitorsToFetch = $filter_limit + $filter_offset;
|
||||
}
|
||||
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
$dataTable = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId = false, $minTimestamp);
|
||||
$this->addFilterToCleanVisitors($dataTable, $idSite, $flat, $doNotFetchActions);
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array describing a visitor using her last visits (uses a maximum of 100).
|
||||
*
|
||||
* @param int $idSite Site ID
|
||||
* @param bool|false|string $visitorId The ID of the visitor whose profile to retrieve.
|
||||
* @param bool|false|string $segment
|
||||
* @param bool $checkForLatLong If true, hasLatLong will appear in the output and be true if
|
||||
* one of the first 100 visits has a latitude/longitude.
|
||||
* @return array
|
||||
*/
|
||||
public function getVisitorProfile($idSite, $visitorId = false, $segment = false, $checkForLatLong = false)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
if ($visitorId === false) {
|
||||
$visitorId = $this->getMostRecentVisitorId($idSite, $segment);
|
||||
}
|
||||
|
||||
$newSegment = ($segment === false ? '' : $segment . ';') . 'visitorId==' . $visitorId;
|
||||
|
||||
$visits = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $newSegment,
|
||||
$numVisitorsToFetch = self::VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE,
|
||||
$overrideVisitorId = false,
|
||||
$minTimestamp = false);
|
||||
$this->addFilterToCleanVisitors($visits, $idSite, $flat = false, $doNotFetchActions = false, $filterNow = true);
|
||||
|
||||
if ($visits->getRowsCount() == 0) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$isEcommerceEnabled = Site::isEcommerceEnabledFor($idSite);
|
||||
|
||||
$result = array();
|
||||
$result['totalVisits'] = 0;
|
||||
$result['totalVisitDuration'] = 0;
|
||||
$result['totalActions'] = 0;
|
||||
$result['totalSearches'] = 0;
|
||||
$result['totalPageViews'] = 0;
|
||||
$result['totalGoalConversions'] = 0;
|
||||
$result['totalConversionsByGoal'] = array();
|
||||
|
||||
if ($isEcommerceEnabled) {
|
||||
$result['totalEcommerceConversions'] = 0;
|
||||
$result['totalEcommerceRevenue'] = 0;
|
||||
$result['totalEcommerceItems'] = 0;
|
||||
$result['totalAbandonedCarts'] = 0;
|
||||
$result['totalAbandonedCartsRevenue'] = 0;
|
||||
$result['totalAbandonedCartsItems'] = 0;
|
||||
}
|
||||
|
||||
$countries = array();
|
||||
$continents = array();
|
||||
$cities = array();
|
||||
$siteSearchKeywords = array();
|
||||
|
||||
$pageGenerationTimeTotal = 0;
|
||||
|
||||
// aggregate all requested visits info for total_* info
|
||||
foreach ($visits->getRows() as $visit) {
|
||||
++$result['totalVisits'];
|
||||
|
||||
$result['totalVisitDuration'] += $visit->getColumn('visitDuration');
|
||||
$result['totalActions'] += $visit->getColumn('actions');
|
||||
$result['totalGoalConversions'] += $visit->getColumn('goalConversions');
|
||||
|
||||
// individual goal conversions are stored in action details
|
||||
foreach ($visit->getColumn('actionDetails') as $action) {
|
||||
if ($action['type'] == 'goal') {
|
||||
// handle goal conversion
|
||||
$idGoal = $action['goalId'];
|
||||
$idGoalKey = 'idgoal=' . $idGoal;
|
||||
|
||||
if (!isset($result['totalConversionsByGoal'][$idGoalKey])) {
|
||||
$result['totalConversionsByGoal'][$idGoalKey] = 0;
|
||||
}
|
||||
++$result['totalConversionsByGoal'][$idGoalKey];
|
||||
|
||||
if (!empty($action['revenue'])) {
|
||||
if (!isset($result['totalRevenueByGoal'][$idGoalKey])) {
|
||||
$result['totalRevenueByGoal'][$idGoalKey] = 0;
|
||||
}
|
||||
$result['totalRevenueByGoal'][$idGoalKey] += $action['revenue'];
|
||||
}
|
||||
} else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER // handle ecommerce order
|
||||
&& $isEcommerceEnabled
|
||||
) {
|
||||
++$result['totalEcommerceConversions'];
|
||||
$result['totalEcommerceRevenue'] += $action['revenue'];
|
||||
$result['totalEcommerceItems'] += $action['items'];
|
||||
} else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART // handler abandoned cart
|
||||
&& $isEcommerceEnabled
|
||||
) {
|
||||
++$result['totalAbandonedCarts'];
|
||||
$result['totalAbandonedCartsRevenue'] += $action['revenue'];
|
||||
$result['totalAbandonedCartsItems'] += $action['items'];
|
||||
}
|
||||
|
||||
if (isset($action['siteSearchKeyword'])) {
|
||||
$keyword = $action['siteSearchKeyword'];
|
||||
|
||||
if (!isset($siteSearchKeywords[$keyword])) {
|
||||
$siteSearchKeywords[$keyword] = 0;
|
||||
++$result['totalSearches'];
|
||||
}
|
||||
++$siteSearchKeywords[$keyword];
|
||||
}
|
||||
|
||||
if (isset($action['generationTime'])) {
|
||||
$pageGenerationTimeTotal += $action['generationTime'];
|
||||
++$result['totalPageViews'];
|
||||
}
|
||||
}
|
||||
|
||||
$countryCode = $visit->getColumn('countryCode');
|
||||
if (!isset($countries[$countryCode])) {
|
||||
$countries[$countryCode] = 0;
|
||||
}
|
||||
++$countries[$countryCode];
|
||||
|
||||
$continentCode = $visit->getColumn('continentCode');
|
||||
if (!isset($continents[$continentCode])) {
|
||||
$continents[$continentCode] = 0;
|
||||
}
|
||||
++$continents[$continentCode];
|
||||
|
||||
if (!array_key_exists($countryCode, $cities)) {
|
||||
$cities[$countryCode] = array();
|
||||
}
|
||||
$city = $visit->getColumn('city');
|
||||
if(!empty($city)) {
|
||||
$cities[$countryCode][] = $city;
|
||||
}
|
||||
}
|
||||
|
||||
// sort countries/continents/search keywords by visit/action
|
||||
asort($countries);
|
||||
asort($continents);
|
||||
arsort($siteSearchKeywords);
|
||||
|
||||
// transform country/continents/search keywords into something that will look good in XML
|
||||
$result['countries'] = $result['continents'] = $result['searches'] = array();
|
||||
|
||||
foreach ($countries as $countryCode => $nbVisits) {
|
||||
|
||||
$countryInfo = array('country' => $countryCode,
|
||||
'nb_visits' => $nbVisits,
|
||||
'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode),
|
||||
'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode));
|
||||
if(!empty($cities[$countryCode])) {
|
||||
$countryInfo['cities'] = array_unique($cities[$countryCode]);
|
||||
}
|
||||
$result['countries'][] = $countryInfo;
|
||||
}
|
||||
foreach ($continents as $continentCode => $nbVisits) {
|
||||
$result['continents'][] = array('continent' => $continentCode,
|
||||
'nb_visits' => $nbVisits,
|
||||
'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode));
|
||||
}
|
||||
foreach ($siteSearchKeywords as $keyword => $searchCount) {
|
||||
$result['searches'][] = array('keyword' => $keyword,
|
||||
'searches' => $searchCount);
|
||||
}
|
||||
|
||||
if ($result['totalPageViews']) {
|
||||
$result['averagePageGenerationTime'] =
|
||||
round($pageGenerationTimeTotal / $result['totalPageViews'], $precision = 2);
|
||||
}
|
||||
|
||||
$result['totalVisitDurationPretty'] = MetricsFormatter::getPrettyTimeFromSeconds($result['totalVisitDuration']);
|
||||
|
||||
// use requested visits for first/last visit info
|
||||
$rows = $visits->getRows();
|
||||
$result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows));
|
||||
$result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows));
|
||||
|
||||
// check if requested visits have lat/long
|
||||
if ($checkForLatLong) {
|
||||
$result['hasLatLong'] = false;
|
||||
foreach ($rows as $visit) {
|
||||
if ($visit->getColumn('latitude') !== false) { // realtime map only checks for latitude
|
||||
$result['hasLatLong'] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save count of visits we queries
|
||||
$result['visitsAggregated'] = count($rows);
|
||||
|
||||
// use N most recent visits for last_visits
|
||||
$visits->deleteRowsOffset(self::VISITOR_PROFILE_MAX_VISITS_TO_SHOW);
|
||||
$result['lastVisits'] = $visits;
|
||||
|
||||
// use the right date format for the pretty server date
|
||||
$timezone = Site::getTimezoneFor($idSite);
|
||||
foreach ($result['lastVisits']->getRows() as $visit) {
|
||||
$dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone);
|
||||
|
||||
$datePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT);
|
||||
$visit->setColumn('serverDatePrettyFirstAction', $datePretty);
|
||||
|
||||
$dateTimePretty = $datePretty . ' ' . $visit->getColumn('serverTimePrettyFirstAction');
|
||||
$visit->setColumn('serverDateTimePrettyFirstAction', $dateTimePretty);
|
||||
}
|
||||
|
||||
// get visitor IDs that are adjacent to this one in log_visit
|
||||
// TODO: make sure order of visitor ids is not changed if a returning visitor visits while the user is
|
||||
// looking at the popup.
|
||||
$latestVisitTime = reset($rows)->getColumn('lastActionDateTime');
|
||||
$result['nextVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = true);
|
||||
$result['previousVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = false);
|
||||
|
||||
/**
|
||||
* Triggered in the Live.getVisitorProfile API method. Plugins can use this event
|
||||
* to discover and add extra data to visitor profiles.
|
||||
*
|
||||
* For example, if an email address is found in a custom variable, a plugin could load the
|
||||
* gravatar for the email and add it to the visitor profile, causing it to display in the
|
||||
* visitor profile popup.
|
||||
*
|
||||
* The following visitor profile elements can be set to augment the visitor profile popup:
|
||||
*
|
||||
* - **visitorAvatar**: A URL to an image to display in the top left corner of the popup.
|
||||
* - **visitorDescription**: Text to be used as the tooltip of the avatar image.
|
||||
*
|
||||
* @param array &$visitorProfile The unaugmented visitor profile info.
|
||||
*/
|
||||
Piwik::postEvent('Live.getExtraVisitorDetails', array(&$result));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visitor ID of the most recent visit.
|
||||
*
|
||||
* @param int $idSite
|
||||
* @param bool|string $segment
|
||||
* @return string
|
||||
*/
|
||||
public function getMostRecentVisitorId($idSite, $segment = false)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
$dataTable = $this->loadLastVisitorDetailsFromDatabase(
|
||||
$idSite, $period = false, $date = false, $segment, $numVisitorsToFetch = 1,
|
||||
$visitorId = false, $minTimestamp = false
|
||||
);
|
||||
|
||||
if (0 >= $dataTable->getRowsCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$visitDetails = $dataTable->getFirstRow()->getColumns();
|
||||
$visitor = new Visitor($visitDetails);
|
||||
|
||||
return $visitor->getVisitorId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of a visitor that is adjacent to another visitor (by time of last action)
|
||||
* in the log_visit table.
|
||||
*
|
||||
* @param int $idSite The ID of the site whose visits should be looked at.
|
||||
* @param string $visitorId The ID of the visitor to get an adjacent visitor for.
|
||||
* @param string $visitLastActionTime The last action time of the latest visit for $visitorId.
|
||||
* @param string $segment
|
||||
* @param bool $getNext Whether to retrieve the next visitor or the previous visitor. The next
|
||||
* visitor will be the visitor that appears chronologically later in the
|
||||
* log_visit table. The previous visitor will be the visitor that appears
|
||||
* earlier.
|
||||
* @return string The hex visitor ID.
|
||||
*/
|
||||
private function getAdjacentVisitorId($idSite, $visitorId, $visitLastActionTime, $segment, $getNext)
|
||||
{
|
||||
if ($getNext) {
|
||||
$visitLastActionTimeCondition = "sub.visit_last_action_time <= ?";
|
||||
$orderByDir = "DESC";
|
||||
} else {
|
||||
$visitLastActionTimeCondition = "sub.visit_last_action_time >= ?";
|
||||
$orderByDir = "ASC";
|
||||
}
|
||||
|
||||
$visitLastActionDate = Date::factory($visitLastActionTime);
|
||||
$dateOneDayAgo = $visitLastActionDate->subDay(1);
|
||||
$dateOneDayInFuture = $visitLastActionDate->addDay(1);
|
||||
|
||||
$select = "log_visit.idvisitor, MAX(log_visit.visit_last_action_time) as visit_last_action_time";
|
||||
$from = "log_visit";
|
||||
$where = "log_visit.idsite = ? AND log_visit.idvisitor <> ? AND visit_last_action_time >= ? and visit_last_action_time <= ?";
|
||||
$whereBind = array($idSite, @Common::hex2bin($visitorId), $dateOneDayAgo->toString('Y-m-d H:i:s'), $dateOneDayInFuture->toString('Y-m-d H:i:s'));
|
||||
$orderBy = "MAX(log_visit.visit_last_action_time) $orderByDir";
|
||||
$groupBy = "log_visit.idvisitor";
|
||||
|
||||
$segment = new Segment($segment, $idSite);
|
||||
$queryInfo = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy);
|
||||
|
||||
$sql = "SELECT sub.idvisitor, sub.visit_last_action_time
|
||||
FROM ({$queryInfo['sql']}) as sub
|
||||
WHERE $visitLastActionTimeCondition
|
||||
LIMIT 1";
|
||||
$bind = array_merge($queryInfo['bind'], array($visitLastActionTime));
|
||||
|
||||
$visitorId = Db::fetchOne($sql, $bind);
|
||||
if (!empty($visitorId)) {
|
||||
$visitorId = bin2hex($visitorId);
|
||||
}
|
||||
return $visitorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary for an important visit. Used to describe the first & last visits of a visitor.
|
||||
*
|
||||
* @param Row $visit
|
||||
* @return array
|
||||
*/
|
||||
private function getVisitorProfileVisitSummary($visit)
|
||||
{
|
||||
$today = Date::today();
|
||||
|
||||
$serverDate = $visit->getColumn('serverDate');
|
||||
return array(
|
||||
'date' => $serverDate,
|
||||
'prettyDate' => Date::factory($serverDate)->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT),
|
||||
'daysAgo' => (int)Date::secondsToDays($today->getTimestamp() - Date::factory($serverDate)->getTimestamp()),
|
||||
'referrerType' => $visit->getColumn('referrerType'),
|
||||
'referralSummary' => self::getReferrerSummaryForVisit($visit),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary for a visit's referral.
|
||||
*
|
||||
* @param Row $visit
|
||||
* @return bool|mixed|string
|
||||
* @ignore
|
||||
*/
|
||||
public static function getReferrerSummaryForVisit($visit)
|
||||
{
|
||||
$referrerType = $visit->getColumn('referrerType');
|
||||
if ($referrerType === false
|
||||
|| $referrerType == 'direct'
|
||||
) {
|
||||
$result = Piwik::translate('Referrers_DirectEntry');
|
||||
} else if ($referrerType == 'search') {
|
||||
$result = $visit->getColumn('referrerName');
|
||||
|
||||
$keyword = $visit->getColumn('referrerKeyword');
|
||||
if ($keyword !== false
|
||||
&& $keyword != APIReferrers::getKeywordNotDefinedString()
|
||||
) {
|
||||
$result .= ' (' . $keyword . ')';
|
||||
}
|
||||
} else if ($referrerType == 'campaign') {
|
||||
$result = Piwik::translate('Referrers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')';
|
||||
} else {
|
||||
$result = $visit->getColumn('referrerName');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function getLastVisits($idSite, $filter_limit = 10, $minTimestamp = false)
|
||||
{
|
||||
return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $countVisitorsToFetch = $filter_limit, $minTimestamp, $flat = false);
|
||||
}
|
||||
|
||||
/**
|
||||
* For an array of visits, query the list of pages for this visit
|
||||
* as well as make the data human readable
|
||||
* @param DataTable $dataTable
|
||||
* @param int $idSite
|
||||
* @param bool $flat whether to flatten the array (eg. 'customVariables' names/values will appear in the root array rather than in 'customVariables' key
|
||||
* @param bool $doNotFetchActions If set to true, we only fetch visit info and not actions (much faster)
|
||||
* @param bool $filterNow If true, the visitors will be cleaned immediately
|
||||
*/
|
||||
private function addFilterToCleanVisitors(DataTable $dataTable, $idSite, $flat = false, $doNotFetchActions = false, $filterNow = false)
|
||||
{
|
||||
$filter = 'queueFilter';
|
||||
if ($filterNow) {
|
||||
$filter = 'filter';
|
||||
}
|
||||
|
||||
$dataTable->$filter(function ($table) use ($idSite, $flat, $doNotFetchActions) {
|
||||
/** @var DataTable $table */
|
||||
$actionsLimit = (int)Config::getInstance()->General['visitor_log_maximum_actions_per_visit'];
|
||||
|
||||
$website = new Site($idSite);
|
||||
$timezone = $website->getTimezone();
|
||||
$currencies = APISitesManager::getInstance()->getCurrencySymbols();
|
||||
|
||||
foreach ($table->getRows() as $visitorDetailRow) {
|
||||
$visitorDetailsArray = Visitor::cleanVisitorDetails($visitorDetailRow->getColumns());
|
||||
|
||||
$visitor = new Visitor($visitorDetailsArray);
|
||||
$visitorDetailsArray = $visitor->getAllVisitorDetails();
|
||||
|
||||
$visitorDetailsArray['siteCurrency'] = $website->getCurrency();
|
||||
$visitorDetailsArray['siteCurrencySymbol'] = @$currencies[$visitorDetailsArray['siteCurrency']];
|
||||
$visitorDetailsArray['serverTimestamp'] = $visitorDetailsArray['lastActionTimestamp'];
|
||||
$dateTimeVisit = Date::factory($visitorDetailsArray['lastActionTimestamp'], $timezone);
|
||||
$visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%time%');
|
||||
$visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Piwik::translate('CoreHome_ShortDateFormat'));
|
||||
|
||||
$dateTimeVisitFirstAction = Date::factory($visitorDetailsArray['firstActionTimestamp'], $timezone);
|
||||
$visitorDetailsArray['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Piwik::translate('CoreHome_ShortDateFormat'));
|
||||
$visitorDetailsArray['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized('%time%');
|
||||
|
||||
$visitorDetailsArray['actionDetails'] = array();
|
||||
if (!$doNotFetchActions) {
|
||||
$visitorDetailsArray = Visitor::enrichVisitorArrayWithActions($visitorDetailsArray, $actionsLimit, $timezone);
|
||||
}
|
||||
|
||||
if ($flat) {
|
||||
$visitorDetailsArray = Visitor::flattenVisitorDetailsArray($visitorDetailsArray);
|
||||
}
|
||||
|
||||
$visitorDetailRow->setColumns($visitorDetailsArray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment = false, $countVisitorsToFetch = 100, $visitorId = false, $minTimestamp = false)
|
||||
{
|
||||
$where = $whereBind = array();
|
||||
|
||||
list($whereClause, $idSites) = $this->getIdSitesWhereClause($idSite);
|
||||
|
||||
$where[] = $whereClause;
|
||||
$whereBind = $idSites;
|
||||
|
||||
$orderBy = "idsite, visit_last_action_time DESC";
|
||||
$orderByParent = "sub.visit_last_action_time DESC";
|
||||
if (!empty($visitorId)) {
|
||||
$where[] = "log_visit.idvisitor = ? ";
|
||||
$whereBind[] = @Common::hex2bin($visitorId);
|
||||
}
|
||||
|
||||
if (!empty($minTimestamp)) {
|
||||
$where[] = "log_visit.visit_last_action_time > ? ";
|
||||
$whereBind[] = date("Y-m-d H:i:s", $minTimestamp);
|
||||
}
|
||||
|
||||
// If no other filter, only look at the last 24 hours of stats
|
||||
if (empty($visitorId)
|
||||
&& empty($countVisitorsToFetch)
|
||||
&& empty($period)
|
||||
&& empty($date)
|
||||
) {
|
||||
$period = 'day';
|
||||
$date = 'yesterdaySameTime';
|
||||
}
|
||||
|
||||
// SQL Filter with provided period
|
||||
if (!empty($period) && !empty($date)) {
|
||||
$currentSite = new Site($idSite);
|
||||
$currentTimezone = $currentSite->getTimezone();
|
||||
|
||||
$dateString = $date;
|
||||
if ($period == 'range') {
|
||||
$processedPeriod = new Range('range', $date);
|
||||
if ($parsedDate = Range::parseDateRange($date)) {
|
||||
$dateString = $parsedDate[2];
|
||||
}
|
||||
} else {
|
||||
$processedDate = Date::factory($date);
|
||||
if ($date == 'today'
|
||||
|| $date == 'now'
|
||||
|| $processedDate->toString() == Date::factory('now', $currentTimezone)->toString()
|
||||
) {
|
||||
$processedDate = $processedDate->subDay(1);
|
||||
}
|
||||
$processedPeriod = Period::factory($period, $processedDate);
|
||||
}
|
||||
$dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone);
|
||||
$where[] = "log_visit.visit_last_action_time >= ?";
|
||||
$whereBind[] = $dateStart->toString('Y-m-d H:i:s');
|
||||
|
||||
if (!in_array($date, array('now', 'today', 'yesterdaySameTime'))
|
||||
&& strpos($date, 'last') === false
|
||||
&& strpos($date, 'previous') === false
|
||||
&& Date::factory($dateString)->toString('Y-m-d') != Date::factory('now', $currentTimezone)->toString()
|
||||
) {
|
||||
$dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone);
|
||||
$where[] = " log_visit.visit_last_action_time <= ?";
|
||||
$dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s');
|
||||
$whereBind[] = $dateEndString;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($where) > 0) {
|
||||
$where = join("
|
||||
AND ", $where);
|
||||
} else {
|
||||
$where = false;
|
||||
}
|
||||
|
||||
$segment = new Segment($segment, $idSite);
|
||||
|
||||
// Subquery to use the indexes for ORDER BY
|
||||
$select = "log_visit.*";
|
||||
$from = "log_visit";
|
||||
$subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy);
|
||||
|
||||
$sqlLimit = $countVisitorsToFetch >= 1 ? " LIMIT 0, " . (int)$countVisitorsToFetch : "";
|
||||
|
||||
// Group by idvisit so that a visitor converting 2 goals only appears once
|
||||
$sql = "
|
||||
SELECT sub.*
|
||||
FROM (
|
||||
" . $subQuery['sql'] . "
|
||||
$sqlLimit
|
||||
) AS sub
|
||||
GROUP BY sub.idvisit
|
||||
ORDER BY $orderByParent
|
||||
";
|
||||
try {
|
||||
$data = Db::fetchAll($sql, $subQuery['bind']);
|
||||
} catch (Exception $e) {
|
||||
echo $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
|
||||
$dataTable = new DataTable();
|
||||
$dataTable->addRowsFromSimpleArray($data);
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $idSite
|
||||
* @return array
|
||||
*/
|
||||
private function getIdSitesWhereClause($idSite)
|
||||
{
|
||||
$idSites = array($idSite);
|
||||
Piwik::postEvent('Live.API.getIdSitesString', array(&$idSites));
|
||||
|
||||
$idSitesBind = Common::getSqlStringFieldsArray($idSites);
|
||||
$whereClause = "log_visit.idsite in ($idSitesBind) ";
|
||||
return array($whereClause, $idSites);
|
||||
}
|
||||
}
|
||||
253
www/analytics/plugins/Live/Controller.php
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Live;
|
||||
|
||||
use Piwik\API\Request;
|
||||
use Piwik\Common;
|
||||
use Piwik\Config;
|
||||
use Piwik\MetricsFormatter;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\Goals\API as APIGoals;
|
||||
use Piwik\Url;
|
||||
use Piwik\View;
|
||||
use Piwik\ViewDataTable\Factory;
|
||||
|
||||
/**
|
||||
*/
|
||||
class Controller extends \Piwik\Plugin\Controller
|
||||
{
|
||||
const SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY = 'live_widget_visitor_count_last_minutes';
|
||||
|
||||
function index()
|
||||
{
|
||||
return $this->widget();
|
||||
}
|
||||
|
||||
public function widget()
|
||||
{
|
||||
$view = new View('@Live/index');
|
||||
$view->idSite = $this->idSite;
|
||||
$view = $this->setCounters($view);
|
||||
$view->liveRefreshAfterMs = (int)Config::getInstance()->General['live_widget_refresh_after_seconds'] * 1000;
|
||||
$view->visitors = $this->getLastVisitsStart($fetchPlease = true);
|
||||
$view->liveTokenAuth = Piwik::getCurrentUserTokenAuth();
|
||||
return $this->render($view);
|
||||
}
|
||||
|
||||
public function getSimpleLastVisitCount()
|
||||
{
|
||||
$lastMinutes = Config::getInstance()->General[self::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
|
||||
|
||||
$lastNData = Request::processRequest('Live.getCounters', array('lastMinutes' => $lastMinutes));
|
||||
|
||||
$view = new View('@Live/getSimpleLastVisitCount');
|
||||
$view->lastMinutes = $lastMinutes;
|
||||
$view->visitors = MetricsFormatter::getPrettyNumber($lastNData[0]['visitors']);
|
||||
$view->visits = MetricsFormatter::getPrettyNumber($lastNData[0]['visits']);
|
||||
$view->actions = MetricsFormatter::getPrettyNumber($lastNData[0]['actions']);
|
||||
$view->refreshAfterXSecs = Config::getInstance()->General['live_widget_refresh_after_seconds'];
|
||||
$view->translations = array(
|
||||
'one_visitor' => Piwik::translate('Live_NbVisitor'),
|
||||
'visitors' => Piwik::translate('Live_NbVisitors'),
|
||||
'one_visit' => Piwik::translate('General_OneVisit'),
|
||||
'visits' => Piwik::translate('General_NVisits'),
|
||||
'one_action' => Piwik::translate('General_OneAction'),
|
||||
'actions' => Piwik::translate('VisitsSummary_NbActionsDescription'),
|
||||
'one_minute' => Piwik::translate('General_OneMinute'),
|
||||
'minutes' => Piwik::translate('General_NMinutes')
|
||||
);
|
||||
return $this->render($view);
|
||||
}
|
||||
|
||||
public function ajaxTotalVisitors()
|
||||
{
|
||||
$view = new View('@Live/ajaxTotalVisitors');
|
||||
$view = $this->setCounters($view);
|
||||
$view->idSite = $this->idSite;
|
||||
return $this->render($view);
|
||||
}
|
||||
|
||||
private function render(View $view)
|
||||
{
|
||||
$rendered = $view->render();
|
||||
|
||||
return $rendered;
|
||||
}
|
||||
|
||||
public function indexVisitorLog()
|
||||
{
|
||||
$view = new View('@Live/indexVisitorLog.twig');
|
||||
$view->filterEcommerce = Common::getRequestVar('filterEcommerce', 0, 'int');
|
||||
$view->visitorLog = $this->getLastVisitsDetails();
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function getLastVisitsDetails()
|
||||
{
|
||||
return $this->renderReport(__FUNCTION__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget
|
||||
*/
|
||||
public function getVisitorLog()
|
||||
{
|
||||
return $this->getLastVisitsDetails();
|
||||
}
|
||||
|
||||
public function getLastVisitsStart()
|
||||
{
|
||||
// hack, ensure we load today's visits by default
|
||||
$_GET['date'] = 'today';
|
||||
$_GET['period'] = 'day';
|
||||
$view = new View('@Live/getLastVisitsStart');
|
||||
$view->idSite = $this->idSite;
|
||||
$api = new Request("method=Live.getLastVisitsDetails&idSite={$this->idSite}&filter_limit=10&format=php&serialize=0&disable_generic_filters=1");
|
||||
$visitors = $api->process();
|
||||
$view->visitors = $visitors;
|
||||
|
||||
return $this->render($view);
|
||||
}
|
||||
|
||||
private function setCounters($view)
|
||||
{
|
||||
$segment = Request::getRawSegmentFromRequest();
|
||||
$last30min = API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment);
|
||||
$last30min = $last30min[0];
|
||||
$today = API::getInstance()->getCounters($this->idSite, $lastMinutes = 24 * 60, $segment);
|
||||
$today = $today[0];
|
||||
$view->visitorsCountHalfHour = $last30min['visits'];
|
||||
$view->visitorsCountToday = $today['visits'];
|
||||
$view->pisHalfhour = $last30min['actions'];
|
||||
$view->pisToday = $today['actions'];
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo's HTML for visitor profile popup.
|
||||
*/
|
||||
public function getVisitorProfilePopup()
|
||||
{
|
||||
$idSite = Common::getRequestVar('idSite', null, 'int');
|
||||
|
||||
$view = new View('@Live/getVisitorProfilePopup.twig');
|
||||
$view->idSite = $idSite;
|
||||
$view->goals = APIGoals::getInstance()->getGoals($idSite);
|
||||
$view->visitorData = Request::processRequest('Live.getVisitorProfile', array('checkForLatLong' => true));
|
||||
$view->exportLink = $this->getVisitorProfileExportLink();
|
||||
|
||||
if (Common::getRequestVar('showMap', 1) == 1
|
||||
&& !empty($view->visitorData['hasLatLong'])
|
||||
&& \Piwik\Plugin\Manager::getInstance()->isPluginLoaded('UserCountryMap')
|
||||
) {
|
||||
$view->userCountryMapUrl = $this->getUserCountryMapUrlForVisitorProfile();
|
||||
}
|
||||
|
||||
$this->setWidgetizedVisitorProfileUrl($view);
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function getSingleVisitSummary()
|
||||
{
|
||||
$view = new View('@Live/getSingleVisitSummary.twig');
|
||||
$visits = Request::processRequest('Live.getLastVisitsDetails', array(
|
||||
'segment' => 'visitId==' . Common::getRequestVar('visitId'),
|
||||
'period' => false,
|
||||
'date' => false
|
||||
));
|
||||
$view->visitData = $visits->getFirstRow()->getColumns();
|
||||
$view->visitReferralSummary = API::getReferrerSummaryForVisit($visits->getFirstRow());
|
||||
$view->showLocation = true;
|
||||
$this->setWidgetizedVisitorProfileUrl($view);
|
||||
$view->exportLink = $this->getVisitorProfileExportLink();
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function getVisitList()
|
||||
{
|
||||
$startCounter = Common::getRequestVar('filter_offset', 0, 'int');
|
||||
$nextVisits = Request::processRequest('Live.getLastVisitsDetails', array(
|
||||
'segment' => self::getSegmentWithVisitorId(),
|
||||
'filter_limit' => API::VISITOR_PROFILE_MAX_VISITS_TO_SHOW,
|
||||
'filter_offset' => $startCounter,
|
||||
'period' => false,
|
||||
'date' => false
|
||||
));
|
||||
|
||||
if (empty($nextVisits)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$view = new View('@Live/getVisitList.twig');
|
||||
$view->idSite = Common::getRequestVar('idSite', null, 'int');
|
||||
$view->startCounter = $startCounter + 1;
|
||||
$view->visits = $nextVisits;
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
private function getVisitorProfileExportLink()
|
||||
{
|
||||
return Url::getCurrentQueryStringWithParametersModified(array(
|
||||
'module' => 'API',
|
||||
'action' => 'index',
|
||||
'method' => 'Live.getVisitorProfile',
|
||||
'format' => 'XML',
|
||||
'expanded' => 1
|
||||
));
|
||||
}
|
||||
|
||||
private function setWidgetizedVisitorProfileUrl($view)
|
||||
{
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginLoaded('Widgetize')) {
|
||||
$view->widgetizedLink = Url::getCurrentQueryStringWithParametersModified(array(
|
||||
'module' => 'Widgetize',
|
||||
'action' => 'iframe',
|
||||
'moduleToWidgetize' => 'Live',
|
||||
'actionToWidgetize' => 'getVisitorProfilePopup'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private function getUserCountryMapUrlForVisitorProfile()
|
||||
{
|
||||
$params = array(
|
||||
'module' => 'UserCountryMap',
|
||||
'action' => 'realtimeMap',
|
||||
'segment' => self::getSegmentWithVisitorId(),
|
||||
'visitorId' => false,
|
||||
'changeVisitAlpha' => 0,
|
||||
'removeOldVisits' => 0,
|
||||
'realtimeWindow' => 'false',
|
||||
'showFooterMessage' => 0,
|
||||
'showDateTime' => 0,
|
||||
'doNotRefreshVisits' => 1
|
||||
);
|
||||
return Url::getCurrentQueryStringWithParametersModified($params);
|
||||
}
|
||||
|
||||
private static function getSegmentWithVisitorId()
|
||||
{
|
||||
static $cached = null;
|
||||
if ($cached === null) {
|
||||
$segment = Request::getRawSegmentFromRequest();
|
||||
if (!empty($segment)) {
|
||||
$segment = urldecode($segment) . ';';
|
||||
}
|
||||
|
||||
$idVisitor = Common::getRequestVar('visitorId', false);
|
||||
if ($idVisitor === false) {
|
||||
$idVisitor = Request::processRequest('Live.getMostRecentVisitorId');
|
||||
}
|
||||
|
||||
$cached = urlencode($segment . 'visitorId==' . $idVisitor);
|
||||
}
|
||||
return $cached;
|
||||
}
|
||||
}
|
||||
77
www/analytics/plugins/Live/Live.php
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Live;
|
||||
|
||||
use Piwik\Menu\MenuMain;
|
||||
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
|
||||
use Piwik\WidgetsList;
|
||||
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/Live/VisitorLog.php';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Live extends \Piwik\Plugin
|
||||
{
|
||||
|
||||
/**
|
||||
* @see Piwik\Plugin::getListHooksRegistered
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
{
|
||||
return array(
|
||||
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
|
||||
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
|
||||
'WidgetsList.addWidgets' => 'addWidget',
|
||||
'Menu.Reporting.addItems' => 'addMenu',
|
||||
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
|
||||
'ViewDataTable.getDefaultType' => 'getDefaultTypeViewDataTable'
|
||||
);
|
||||
}
|
||||
|
||||
public function getStylesheetFiles(&$stylesheets)
|
||||
{
|
||||
$stylesheets[] = "plugins/Live/stylesheets/live.less";
|
||||
$stylesheets[] = "plugins/Live/stylesheets/visitor_profile.less";
|
||||
}
|
||||
|
||||
public function getJsFiles(&$jsFiles)
|
||||
{
|
||||
$jsFiles[] = "plugins/Live/javascripts/live.js";
|
||||
$jsFiles[] = "plugins/Live/javascripts/visitorProfile.js";
|
||||
$jsFiles[] = "plugins/Live/javascripts/visitorLog.js";
|
||||
}
|
||||
|
||||
public function addMenu()
|
||||
{
|
||||
MenuMain::getInstance()->add('General_Visitors', 'Live_VisitorLog', array('module' => 'Live', 'action' => 'indexVisitorLog'), true, $order = 5);
|
||||
}
|
||||
|
||||
public function addWidget()
|
||||
{
|
||||
WidgetsList::add('Live!', 'Live_VisitorsInRealTime', 'Live', 'widget');
|
||||
WidgetsList::add('Live!', 'Live_VisitorLog', 'Live', 'getVisitorLog', array('small' => 1));
|
||||
WidgetsList::add('Live!', 'Live_RealTimeVisitorCount', 'Live', 'getSimpleLastVisitCount');
|
||||
WidgetsList::add('Live!', 'Live_VisitorProfile', 'Live', 'getVisitorProfilePopup');
|
||||
}
|
||||
|
||||
public function getClientSideTranslationKeys(&$translationKeys)
|
||||
{
|
||||
$translationKeys[] = "Live_VisitorProfile";
|
||||
$translationKeys[] = "Live_NoMoreVisits";
|
||||
$translationKeys[] = "Live_ShowMap";
|
||||
$translationKeys[] = "Live_HideMap";
|
||||
$translationKeys[] = "Live_PageRefreshed";
|
||||
}
|
||||
|
||||
public function getDefaultTypeViewDataTable(&$defaultViewTypes)
|
||||
{
|
||||
$defaultViewTypes['Live.getLastVisitsDetails'] = VisitorLog::ID;
|
||||
}
|
||||
}
|
||||
992
www/analytics/plugins/Live/Visitor.php
Normal file
|
|
@ -0,0 +1,992 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Live;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\DataAccess\LogAggregator;
|
||||
use Piwik\DataTable\Filter\ColumnDelete;
|
||||
use Piwik\Date;
|
||||
use Piwik\Db;
|
||||
use Piwik\IP;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\API\API as APIMetadata;
|
||||
use Piwik\Plugins\CustomVariables\CustomVariables;
|
||||
use Piwik\Plugins\Referrers\API as APIReferrers;
|
||||
use Piwik\Plugins\UserCountry\LocationProvider\GeoIp;
|
||||
use Piwik\Tracker\Action;
|
||||
use Piwik\Tracker\GoalManager;
|
||||
use Piwik\Tracker;
|
||||
use Piwik\Tracker\Visit;
|
||||
use Piwik\UrlHelper;
|
||||
|
||||
/**
|
||||
* @see plugins/Referrers/functions.php
|
||||
* @see plugins/UserCountry/functions.php
|
||||
* @see plugins/UserSettings/functions.php
|
||||
* @see plugins/Provider/functions.php
|
||||
*/
|
||||
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/Referrers/functions.php';
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php';
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
|
||||
require_once PIWIK_INCLUDE_PATH . '/plugins/Provider/functions.php';
|
||||
|
||||
/**
|
||||
*/
|
||||
class Visitor
|
||||
{
|
||||
const DELIMITER_PLUGIN_NAME = ", ";
|
||||
|
||||
const EVENT_VALUE_PRECISION = 3;
|
||||
|
||||
function __construct($visitorRawData)
|
||||
{
|
||||
$this->details = $visitorRawData;
|
||||
}
|
||||
|
||||
function getAllVisitorDetails()
|
||||
{
|
||||
return array(
|
||||
'idSite' => $this->getIdSite(),
|
||||
'idVisit' => $this->getIdVisit(),
|
||||
'visitIp' => $this->getIp(),
|
||||
'visitorId' => $this->getVisitorId(),
|
||||
'visitorType' => $this->getVisitorReturning(),
|
||||
'visitorTypeIcon' => $this->getVisitorReturningIcon(),
|
||||
'visitConverted' => $this->isVisitorGoalConverted(),
|
||||
'visitConvertedIcon' => $this->getVisitorGoalConvertedIcon(),
|
||||
'visitEcommerceStatus' => $this->getVisitEcommerceStatus(),
|
||||
'visitEcommerceStatusIcon' => $this->getVisitEcommerceStatusIcon(),
|
||||
|
||||
'searches' => $this->getNumberOfSearches(),
|
||||
'events' => $this->getNumberOfEvents(),
|
||||
'actions' => $this->getNumberOfActions(),
|
||||
// => false are placeholders to be filled in API later
|
||||
'actionDetails' => false,
|
||||
'customVariables' => $this->getCustomVariables(),
|
||||
'goalConversions' => false,
|
||||
'siteCurrency' => false,
|
||||
'siteCurrencySymbol' => false,
|
||||
|
||||
// all time entries
|
||||
'serverDate' => $this->getServerDate(),
|
||||
'visitLocalTime' => $this->getVisitLocalTime(),
|
||||
'visitLocalHour' => $this->getVisitLocalHour(),
|
||||
'visitServerHour' => $this->getVisitServerHour(),
|
||||
'firstActionTimestamp' => $this->getTimestampFirstAction(),
|
||||
'lastActionTimestamp' => $this->getTimestampLastAction(),
|
||||
'lastActionDateTime' => $this->getDateTimeLastAction(),
|
||||
|
||||
// standard attributes
|
||||
'visitDuration' => $this->getVisitLength(),
|
||||
'visitDurationPretty' => $this->getVisitLengthPretty(),
|
||||
'visitCount' => $this->getVisitCount(),
|
||||
'daysSinceLastVisit' => $this->getDaysSinceLastVisit(),
|
||||
'daysSinceFirstVisit' => $this->getDaysSinceFirstVisit(),
|
||||
'daysSinceLastEcommerceOrder' => $this->getDaysSinceLastEcommerceOrder(),
|
||||
'continent' => $this->getContinent(),
|
||||
'continentCode' => $this->getContinentCode(),
|
||||
'country' => $this->getCountryName(),
|
||||
'countryCode' => $this->getCountryCode(),
|
||||
'countryFlag' => $this->getCountryFlag(),
|
||||
'region' => $this->getRegionName(),
|
||||
'regionCode' => $this->getRegionCode(),
|
||||
'city' => $this->getCityName(),
|
||||
'location' => $this->getPrettyLocation(),
|
||||
'latitude' => $this->getLatitude(),
|
||||
'longitude' => $this->getLongitude(),
|
||||
'provider' => $this->getProvider(),
|
||||
'providerName' => $this->getProviderName(),
|
||||
'providerUrl' => $this->getProviderUrl(),
|
||||
|
||||
'referrerType' => $this->getReferrerType(),
|
||||
'referrerTypeName' => $this->getReferrerTypeName(),
|
||||
'referrerName' => $this->getReferrerName(),
|
||||
'referrerKeyword' => $this->getKeyword(),
|
||||
'referrerKeywordPosition' => $this->getKeywordPosition(),
|
||||
'referrerUrl' => $this->getReferrerUrl(),
|
||||
'referrerSearchEngineUrl' => $this->getSearchEngineUrl(),
|
||||
'referrerSearchEngineIcon' => $this->getSearchEngineIcon(),
|
||||
'operatingSystem' => $this->getOperatingSystem(),
|
||||
'operatingSystemCode' => $this->getOperatingSystemCode(),
|
||||
'operatingSystemShortName' => $this->getOperatingSystemShortName(),
|
||||
'operatingSystemIcon' => $this->getOperatingSystemIcon(),
|
||||
'browserFamily' => $this->getBrowserFamily(),
|
||||
'browserFamilyDescription' => $this->getBrowserFamilyDescription(),
|
||||
'browserName' => $this->getBrowser(),
|
||||
'browserIcon' => $this->getBrowserIcon(),
|
||||
'browserCode' => $this->getBrowserCode(),
|
||||
'browserVersion' => $this->getBrowserVersion(),
|
||||
'screenType' => $this->getScreenType(),
|
||||
'deviceType' => $this->getDeviceType(),
|
||||
'resolution' => $this->getResolution(),
|
||||
'screenTypeIcon' => $this->getScreenTypeIcon(),
|
||||
'plugins' => $this->getPlugins(),
|
||||
'pluginsIcons' => $this->getPluginIcons(),
|
||||
);
|
||||
}
|
||||
|
||||
function getVisitorId()
|
||||
{
|
||||
if (isset($this->details['idvisitor'])) {
|
||||
return bin2hex($this->details['idvisitor']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getVisitLocalTime()
|
||||
{
|
||||
return $this->details['visitor_localtime'];
|
||||
}
|
||||
|
||||
function getVisitServerHour()
|
||||
{
|
||||
return date('G', strtotime($this->details['visit_last_action_time']));
|
||||
}
|
||||
|
||||
function getVisitLocalHour()
|
||||
{
|
||||
return date('G', strtotime('2012-12-21 ' . $this->details['visitor_localtime']));
|
||||
}
|
||||
|
||||
function getVisitCount()
|
||||
{
|
||||
return $this->details['visitor_count_visits'];
|
||||
}
|
||||
|
||||
function getDaysSinceLastVisit()
|
||||
{
|
||||
return $this->details['visitor_days_since_last'];
|
||||
}
|
||||
|
||||
function getDaysSinceLastEcommerceOrder()
|
||||
{
|
||||
return $this->details['visitor_days_since_order'];
|
||||
}
|
||||
|
||||
function getDaysSinceFirstVisit()
|
||||
{
|
||||
return $this->details['visitor_days_since_first'];
|
||||
}
|
||||
|
||||
function getServerDate()
|
||||
{
|
||||
return date('Y-m-d', strtotime($this->details['visit_last_action_time']));
|
||||
}
|
||||
|
||||
function getIp()
|
||||
{
|
||||
if (isset($this->details['location_ip'])) {
|
||||
return IP::N2P($this->details['location_ip']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getIdVisit()
|
||||
{
|
||||
return $this->details['idvisit'];
|
||||
}
|
||||
|
||||
function getIdSite()
|
||||
{
|
||||
return $this->details['idsite'];
|
||||
}
|
||||
|
||||
function getNumberOfActions()
|
||||
{
|
||||
return $this->details['visit_total_actions'];
|
||||
}
|
||||
|
||||
function getNumberOfEvents()
|
||||
{
|
||||
return $this->details['visit_total_events'];
|
||||
}
|
||||
|
||||
function getNumberOfSearches()
|
||||
{
|
||||
return $this->details['visit_total_searches'];
|
||||
}
|
||||
|
||||
function getVisitLength()
|
||||
{
|
||||
return $this->details['visit_total_time'];
|
||||
}
|
||||
|
||||
function getVisitLengthPretty()
|
||||
{
|
||||
return \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($this->details['visit_total_time']);
|
||||
}
|
||||
|
||||
function getVisitorReturning()
|
||||
{
|
||||
$type = $this->details['visitor_returning'];
|
||||
return $type == 2
|
||||
? 'returningCustomer'
|
||||
: ($type == 1
|
||||
? 'returning'
|
||||
: 'new');
|
||||
}
|
||||
|
||||
function getVisitorReturningIcon()
|
||||
{
|
||||
$type = $this->getVisitorReturning();
|
||||
if ($type == 'returning'
|
||||
|| $type == 'returningCustomer'
|
||||
) {
|
||||
return "plugins/Live/images/returningVisitor.gif";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getTimestampFirstAction()
|
||||
{
|
||||
return strtotime($this->details['visit_first_action_time']);
|
||||
}
|
||||
|
||||
function getTimestampLastAction()
|
||||
{
|
||||
return strtotime($this->details['visit_last_action_time']);
|
||||
}
|
||||
|
||||
function getCountryCode()
|
||||
{
|
||||
return $this->details['location_country'];
|
||||
}
|
||||
|
||||
function getCountryName()
|
||||
{
|
||||
return \Piwik\Plugins\UserCountry\countryTranslate($this->getCountryCode());
|
||||
}
|
||||
|
||||
function getCountryFlag()
|
||||
{
|
||||
return \Piwik\Plugins\UserCountry\getFlagFromCode($this->getCountryCode());
|
||||
}
|
||||
|
||||
function getContinent()
|
||||
{
|
||||
return \Piwik\Plugins\UserCountry\continentTranslate($this->getContinentCode());
|
||||
}
|
||||
|
||||
function getContinentCode()
|
||||
{
|
||||
return Common::getContinent($this->details['location_country']);
|
||||
}
|
||||
|
||||
function getCityName()
|
||||
{
|
||||
if (!empty($this->details['location_city'])) {
|
||||
return $this->details['location_city'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRegionName()
|
||||
{
|
||||
$region = $this->getRegionCode();
|
||||
if ($region != '' && $region != Visit::UNKNOWN_CODE) {
|
||||
return GeoIp::getRegionNameFromCodes(
|
||||
$this->details['location_country'], $region);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRegionCode()
|
||||
{
|
||||
return $this->details['location_region'];
|
||||
}
|
||||
|
||||
function getPrettyLocation()
|
||||
{
|
||||
$parts = array();
|
||||
|
||||
$city = $this->getCityName();
|
||||
if (!empty($city)) {
|
||||
$parts[] = $city;
|
||||
}
|
||||
$region = $this->getRegionName();
|
||||
if (!empty($region)) {
|
||||
$parts[] = $region;
|
||||
}
|
||||
|
||||
// add country & return concatenated result
|
||||
$parts[] = $this->getCountryName();
|
||||
return implode(', ', $parts);
|
||||
}
|
||||
|
||||
function getLatitude()
|
||||
{
|
||||
if (!empty($this->details['location_latitude'])) {
|
||||
return $this->details['location_latitude'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getLongitude()
|
||||
{
|
||||
if (!empty($this->details['location_longitude'])) {
|
||||
return $this->details['location_longitude'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getCustomVariables()
|
||||
{
|
||||
$customVariables = array();
|
||||
|
||||
$maxCustomVariables = CustomVariables::getMaxCustomVariables();
|
||||
|
||||
for ($i = 1; $i <= $maxCustomVariables; $i++) {
|
||||
if (!empty($this->details['custom_var_k' . $i])) {
|
||||
$customVariables[$i] = array(
|
||||
'customVariableName' . $i => $this->details['custom_var_k' . $i],
|
||||
'customVariableValue' . $i => $this->details['custom_var_v' . $i],
|
||||
);
|
||||
}
|
||||
}
|
||||
return $customVariables;
|
||||
}
|
||||
|
||||
function getReferrerType()
|
||||
{
|
||||
return \Piwik\Plugins\Referrers\getReferrerTypeFromShortName($this->details['referer_type']);
|
||||
}
|
||||
|
||||
function getReferrerTypeName()
|
||||
{
|
||||
return \Piwik\Plugins\Referrers\getReferrerTypeLabel($this->details['referer_type']);
|
||||
}
|
||||
|
||||
function getKeyword()
|
||||
{
|
||||
$keyword = $this->details['referer_keyword'];
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('Referrers')
|
||||
&& $this->getReferrerType() == 'search'
|
||||
) {
|
||||
$keyword = \Piwik\Plugins\Referrers\API::getCleanKeyword($keyword);
|
||||
}
|
||||
return urldecode($keyword);
|
||||
}
|
||||
|
||||
function getReferrerUrl()
|
||||
{
|
||||
if ($this->getReferrerType() == 'search') {
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('Referrers')
|
||||
&& $this->details['referer_keyword'] == APIReferrers::LABEL_KEYWORD_NOT_DEFINED
|
||||
) {
|
||||
return 'http://piwik.org/faq/general/#faq_144';
|
||||
} // Case URL is google.XX/url.... then we rewrite to the search result page url
|
||||
elseif ($this->getReferrerName() == 'Google'
|
||||
&& strpos($this->details['referer_url'], '/url')
|
||||
) {
|
||||
$refUrl = @parse_url($this->details['referer_url']);
|
||||
if (isset($refUrl['host'])) {
|
||||
$url = \Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword('http://google.com', $this->getKeyword());
|
||||
$url = str_replace('google.com', $refUrl['host'], $url);
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (\Piwik\UrlHelper::isLookLikeUrl($this->details['referer_url'])) {
|
||||
return $this->details['referer_url'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getKeywordPosition()
|
||||
{
|
||||
if ($this->getReferrerType() == 'search'
|
||||
&& strpos($this->getReferrerName(), 'Google') !== false
|
||||
) {
|
||||
$url = @parse_url($this->details['referer_url']);
|
||||
if (empty($url['query'])) {
|
||||
return null;
|
||||
}
|
||||
$position = UrlHelper::getParameterFromQueryString($url['query'], 'cd');
|
||||
if (!empty($position)) {
|
||||
return $position;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getReferrerName()
|
||||
{
|
||||
return urldecode($this->details['referer_name']);
|
||||
}
|
||||
|
||||
function getSearchEngineUrl()
|
||||
{
|
||||
if ($this->getReferrerType() == 'search'
|
||||
&& !empty($this->details['referer_name'])
|
||||
) {
|
||||
return \Piwik\Plugins\Referrers\getSearchEngineUrlFromName($this->details['referer_name']);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSearchEngineIcon()
|
||||
{
|
||||
$searchEngineUrl = $this->getSearchEngineUrl();
|
||||
if (!is_null($searchEngineUrl)) {
|
||||
return \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl($searchEngineUrl);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPlugins()
|
||||
{
|
||||
$plugins = array(
|
||||
'config_pdf',
|
||||
'config_flash',
|
||||
'config_java',
|
||||
'config_director',
|
||||
'config_quicktime',
|
||||
'config_realplayer',
|
||||
'config_windowsmedia',
|
||||
'config_gears',
|
||||
'config_silverlight',
|
||||
);
|
||||
$pluginShortNames = array();
|
||||
foreach ($plugins as $plugin) {
|
||||
if ($this->details[$plugin] == 1) {
|
||||
$pluginShortName = substr($plugin, 7);
|
||||
$pluginShortNames[] = $pluginShortName;
|
||||
}
|
||||
}
|
||||
return implode(self::DELIMITER_PLUGIN_NAME, $pluginShortNames);
|
||||
}
|
||||
|
||||
function getPluginIcons()
|
||||
{
|
||||
$pluginNames = $this->getPlugins();
|
||||
if (!empty($pluginNames)) {
|
||||
$pluginNames = explode(self::DELIMITER_PLUGIN_NAME, $pluginNames);
|
||||
$pluginIcons = array();
|
||||
|
||||
foreach ($pluginNames as $plugin) {
|
||||
$pluginIcons[] = array("pluginIcon" => \Piwik\Plugins\UserSettings\getPluginsLogo($plugin), "pluginName" => $plugin);
|
||||
}
|
||||
return $pluginIcons;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getOperatingSystemCode()
|
||||
{
|
||||
return $this->details['config_os'];
|
||||
}
|
||||
|
||||
function getOperatingSystem()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getOSLabel($this->details['config_os']);
|
||||
}
|
||||
|
||||
function getOperatingSystemShortName()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getOSShortLabel($this->details['config_os']);
|
||||
}
|
||||
|
||||
function getOperatingSystemIcon()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getOSLogo($this->details['config_os']);
|
||||
}
|
||||
|
||||
function getBrowserFamilyDescription()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getBrowserTypeLabel($this->getBrowserFamily());
|
||||
}
|
||||
|
||||
function getBrowserFamily()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getBrowserFamily($this->details['config_browser_name']);
|
||||
}
|
||||
|
||||
function getBrowserCode()
|
||||
{
|
||||
return $this->details['config_browser_name'];
|
||||
}
|
||||
|
||||
function getBrowserVersion()
|
||||
{
|
||||
return $this->details['config_browser_version'];
|
||||
}
|
||||
|
||||
function getBrowser()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getBrowserLabel($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
|
||||
}
|
||||
|
||||
function getBrowserIcon()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getBrowsersLogo($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
|
||||
}
|
||||
|
||||
function getScreenType()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getScreenTypeFromResolution($this->details['config_resolution']);
|
||||
}
|
||||
|
||||
function getDeviceType()
|
||||
{
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('DevicesDetection')) {
|
||||
return \Piwik\Plugins\DevicesDetection\getDeviceTypeLabel($this->details['config_device_type']);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getResolution()
|
||||
{
|
||||
return $this->details['config_resolution'];
|
||||
}
|
||||
|
||||
function getScreenTypeIcon()
|
||||
{
|
||||
return \Piwik\Plugins\UserSettings\getScreensLogo($this->getScreenType());
|
||||
}
|
||||
|
||||
function getProvider()
|
||||
{
|
||||
if (isset($this->details['location_provider'])) {
|
||||
return $this->details['location_provider'];
|
||||
} else {
|
||||
return Piwik::translate('General_Unknown');
|
||||
}
|
||||
}
|
||||
|
||||
function getProviderName()
|
||||
{
|
||||
return \Piwik\Plugins\Provider\getPrettyProviderName($this->getProvider());
|
||||
}
|
||||
|
||||
function getProviderUrl()
|
||||
{
|
||||
return \Piwik\Plugins\Provider\getHostnameUrl(@$this->details['location_provider']);
|
||||
}
|
||||
|
||||
function getDateTimeLastAction()
|
||||
{
|
||||
return date('Y-m-d H:i:s', strtotime($this->details['visit_last_action_time']));
|
||||
}
|
||||
|
||||
function getVisitEcommerceStatusIcon()
|
||||
{
|
||||
$status = $this->getVisitEcommerceStatus();
|
||||
|
||||
if (in_array($status, array('ordered', 'orderedThenAbandonedCart'))) {
|
||||
return "plugins/Zeitgeist/images/ecommerceOrder.gif";
|
||||
} elseif ($status == 'abandonedCart') {
|
||||
return "plugins/Zeitgeist/images/ecommerceAbandonedCart.gif";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getVisitEcommerceStatus()
|
||||
{
|
||||
return APIMetadata::getVisitEcommerceStatusFromId($this->details['visit_goal_buyer']);
|
||||
}
|
||||
|
||||
function getVisitorGoalConvertedIcon()
|
||||
{
|
||||
return $this->isVisitorGoalConverted()
|
||||
? "plugins/Zeitgeist/images/goal.png"
|
||||
: null;
|
||||
}
|
||||
|
||||
function isVisitorGoalConverted()
|
||||
{
|
||||
return $this->details['visit_goal_converted'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes fields that are not meant to be displayed (md5 config hash)
|
||||
* Or that the user should only access if he is 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[] = '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 (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) {
|
||||
if (!empty($action['url'])) {
|
||||
$flattenedKeyName = 'pageUrl' . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
|
||||
$visitorDetailsArray[$flattenedKeyName] = $action['url'];
|
||||
}
|
||||
|
||||
// API.getSuggestedValuesForSegment
|
||||
$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 $visitorDetailsArray
|
||||
* @param $actionsLimit
|
||||
* @param $timezone
|
||||
* @return array
|
||||
*/
|
||||
public static function enrichVisitorArrayWithActions($visitorDetailsArray, $actionsLimit, $timezone)
|
||||
{
|
||||
$idVisit = $visitorDetailsArray['idVisit'];
|
||||
|
||||
$maxCustomVariables = CustomVariables::getMaxCustomVariables();
|
||||
|
||||
$sqlCustomVariables = '';
|
||||
for ($i = 1; $i <= $maxCustomVariables; $i++) {
|
||||
$sqlCustomVariables .= ', custom_var_k' . $i . ', custom_var_v' . $i;
|
||||
}
|
||||
// The second join is a LEFT join to allow returning records that don't have a matching page title
|
||||
// eg. Downloads, Outlinks. For these, idaction_name is set to 0
|
||||
$sql = "
|
||||
SELECT
|
||||
COALESCE(log_action_event_category.type, log_action.type, log_action_title.type) AS type,
|
||||
log_action.name AS url,
|
||||
log_action.url_prefix,
|
||||
log_action_title.name AS pageTitle,
|
||||
log_action.idaction AS pageIdAction,
|
||||
log_link_visit_action.server_time as serverTimePretty,
|
||||
log_link_visit_action.time_spent_ref_action as timeSpentRef,
|
||||
log_link_visit_action.idlink_va AS pageId,
|
||||
log_link_visit_action.custom_float
|
||||
". $sqlCustomVariables . ",
|
||||
log_action_event_category.name AS eventCategory,
|
||||
log_action_event_action.name as eventAction
|
||||
FROM " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action
|
||||
ON log_link_visit_action.idaction_url = log_action.idaction
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_title
|
||||
ON log_link_visit_action.idaction_name = log_action_title.idaction
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_event_category
|
||||
ON log_link_visit_action.idaction_event_category = log_action_event_category.idaction
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_event_action
|
||||
ON log_link_visit_action.idaction_event_action = log_action_event_action.idaction
|
||||
WHERE log_link_visit_action.idvisit = ?
|
||||
ORDER BY server_time ASC
|
||||
LIMIT 0, $actionsLimit
|
||||
";
|
||||
$actionDetails = Db::fetchAll($sql, array($idVisit));
|
||||
|
||||
foreach ($actionDetails as $actionIdx => &$actionDetail) {
|
||||
$actionDetail =& $actionDetails[$actionIdx];
|
||||
$customVariablesPage = array();
|
||||
|
||||
$maxCustomVariables = CustomVariables::getMaxCustomVariables();
|
||||
|
||||
for ($i = 1; $i <= $maxCustomVariables; $i++) {
|
||||
if (!empty($actionDetail['custom_var_k' . $i])) {
|
||||
$cvarKey = $actionDetail['custom_var_k' . $i];
|
||||
$cvarKey = static::getCustomVariablePrettyKey($cvarKey);
|
||||
$customVariablesPage[$i] = array(
|
||||
'customVariablePageName' . $i => $cvarKey,
|
||||
'customVariablePageValue' . $i => $actionDetail['custom_var_v' . $i],
|
||||
);
|
||||
}
|
||||
unset($actionDetail['custom_var_k' . $i]);
|
||||
unset($actionDetail['custom_var_v' . $i]);
|
||||
}
|
||||
if (!empty($customVariablesPage)) {
|
||||
$actionDetail['customVariables'] = $customVariablesPage;
|
||||
}
|
||||
|
||||
|
||||
if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
|
||||
// Handle Event
|
||||
if(strlen($actionDetail['pageTitle']) > 0) {
|
||||
$actionDetail['eventName'] = $actionDetail['pageTitle'];
|
||||
}
|
||||
|
||||
unset($actionDetail['pageTitle']);
|
||||
|
||||
} else if ($actionDetail['type'] == Action::TYPE_SITE_SEARCH) {
|
||||
// Handle Site Search
|
||||
$actionDetail['siteSearchKeyword'] = $actionDetail['pageTitle'];
|
||||
unset($actionDetail['pageTitle']);
|
||||
}
|
||||
|
||||
// Event value / Generation time
|
||||
if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
|
||||
if(strlen($actionDetail['custom_float']) > 0) {
|
||||
$actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION);
|
||||
}
|
||||
} elseif ($actionDetail['custom_float'] > 0) {
|
||||
$actionDetail['generationTime'] = \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($actionDetail['custom_float'] / 1000);
|
||||
}
|
||||
unset($actionDetail['custom_float']);
|
||||
|
||||
if($actionDetail['type'] != Action::TYPE_EVENT_CATEGORY) {
|
||||
unset($actionDetail['eventCategory']);
|
||||
unset($actionDetail['eventAction']);
|
||||
}
|
||||
|
||||
// Reconstruct url from prefix
|
||||
$actionDetail['url'] = Tracker\PageUrl::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
|
||||
unset($actionDetail['url_prefix']);
|
||||
|
||||
// Set the time spent for this action (which is the timeSpentRef of the next action)
|
||||
if (isset($actionDetails[$actionIdx + 1])) {
|
||||
$actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
|
||||
$actionDetail['timeSpentPretty'] = \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($actionDetail['timeSpent']);
|
||||
}
|
||||
unset($actionDetails[$actionIdx]['timeSpentRef']); // not needed after timeSpent is added
|
||||
|
||||
}
|
||||
|
||||
// If the visitor converted a goal, we shall select all Goals
|
||||
$sql = "
|
||||
SELECT
|
||||
'goal' as type,
|
||||
goal.name as goalName,
|
||||
goal.idgoal as goalId,
|
||||
goal.revenue as revenue,
|
||||
log_conversion.idlink_va as goalPageId,
|
||||
log_conversion.server_time as serverTimePretty,
|
||||
log_conversion.url as url
|
||||
FROM " . Common::prefixTable('log_conversion') . " AS log_conversion
|
||||
LEFT JOIN " . Common::prefixTable('goal') . " AS goal
|
||||
ON (goal.idsite = log_conversion.idsite
|
||||
AND
|
||||
goal.idgoal = log_conversion.idgoal)
|
||||
AND goal.deleted = 0
|
||||
WHERE log_conversion.idvisit = ?
|
||||
AND log_conversion.idgoal > 0
|
||||
ORDER BY server_time ASC
|
||||
LIMIT 0, $actionsLimit
|
||||
";
|
||||
$goalDetails = Db::fetchAll($sql, array($idVisit));
|
||||
|
||||
$sql = "SELECT
|
||||
case idgoal when " . GoalManager::IDGOAL_CART . " then '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART . "' else '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER . "' end as type,
|
||||
idorder as orderId,
|
||||
" . LogAggregator::getSqlRevenue('revenue') . " as revenue,
|
||||
" . LogAggregator::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal,
|
||||
" . LogAggregator::getSqlRevenue('revenue_tax') . " as revenueTax,
|
||||
" . LogAggregator::getSqlRevenue('revenue_shipping') . " as revenueShipping,
|
||||
" . LogAggregator::getSqlRevenue('revenue_discount') . " as revenueDiscount,
|
||||
items as items,
|
||||
|
||||
log_conversion.server_time as serverTimePretty
|
||||
FROM " . Common::prefixTable('log_conversion') . " AS log_conversion
|
||||
WHERE idvisit = ?
|
||||
AND idgoal <= " . GoalManager::IDGOAL_ORDER . "
|
||||
ORDER BY server_time ASC
|
||||
LIMIT 0, $actionsLimit";
|
||||
$ecommerceDetails = Db::fetchAll($sql, array($idVisit));
|
||||
|
||||
foreach ($ecommerceDetails as &$ecommerceDetail) {
|
||||
if ($ecommerceDetail['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
|
||||
unset($ecommerceDetail['orderId']);
|
||||
unset($ecommerceDetail['revenueSubTotal']);
|
||||
unset($ecommerceDetail['revenueTax']);
|
||||
unset($ecommerceDetail['revenueShipping']);
|
||||
unset($ecommerceDetail['revenueDiscount']);
|
||||
}
|
||||
|
||||
// 25.00 => 25
|
||||
foreach ($ecommerceDetail as $column => $value) {
|
||||
if (strpos($column, 'revenue') !== false) {
|
||||
if ($value == round($value)) {
|
||||
$ecommerceDetail[$column] = round($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enrich ecommerce carts/orders with the list of products
|
||||
usort($ecommerceDetails, array('static', 'sortByServerTime'));
|
||||
foreach ($ecommerceDetails as $key => &$ecommerceConversion) {
|
||||
$sql = "SELECT
|
||||
log_action_sku.name as itemSKU,
|
||||
log_action_name.name as itemName,
|
||||
log_action_category.name as itemCategory,
|
||||
" . LogAggregator::getSqlRevenue('price') . " as price,
|
||||
quantity as quantity
|
||||
FROM " . Common::prefixTable('log_conversion_item') . "
|
||||
INNER JOIN " . Common::prefixTable('log_action') . " AS log_action_sku
|
||||
ON idaction_sku = log_action_sku.idaction
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_name
|
||||
ON idaction_name = log_action_name.idaction
|
||||
LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_category
|
||||
ON idaction_category = log_action_category.idaction
|
||||
WHERE idvisit = ?
|
||||
AND idorder = ?
|
||||
AND deleted = 0
|
||||
LIMIT 0, $actionsLimit
|
||||
";
|
||||
$bind = array($idVisit, isset($ecommerceConversion['orderId'])
|
||||
? $ecommerceConversion['orderId']
|
||||
: GoalManager::ITEM_IDORDER_ABANDONED_CART
|
||||
);
|
||||
|
||||
$itemsDetails = Db::fetchAll($sql, $bind);
|
||||
foreach ($itemsDetails as &$detail) {
|
||||
if ($detail['price'] == round($detail['price'])) {
|
||||
$detail['price'] = round($detail['price']);
|
||||
}
|
||||
}
|
||||
$ecommerceConversion['itemDetails'] = $itemsDetails;
|
||||
}
|
||||
|
||||
$actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
|
||||
|
||||
usort($actions, array('static', 'sortByServerTime'));
|
||||
|
||||
$visitorDetailsArray['actionDetails'] = $actions;
|
||||
foreach ($visitorDetailsArray['actionDetails'] as &$details) {
|
||||
switch ($details['type']) {
|
||||
case 'goal':
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/goal.png';
|
||||
break;
|
||||
case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER:
|
||||
case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART:
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/' . $details['type'] . '.gif';
|
||||
break;
|
||||
case Action::TYPE_DOWNLOAD:
|
||||
$details['type'] = 'download';
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/download.png';
|
||||
break;
|
||||
case Action::TYPE_OUTLINK:
|
||||
$details['type'] = 'outlink';
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/link.gif';
|
||||
break;
|
||||
case Action::TYPE_SITE_SEARCH:
|
||||
$details['type'] = 'search';
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/search_ico.png';
|
||||
break;
|
||||
case Action::TYPE_EVENT_CATEGORY:
|
||||
$details['type'] = 'event';
|
||||
$details['icon'] = 'plugins/Zeitgeist/images/event.png';
|
||||
break;
|
||||
default:
|
||||
$details['type'] = 'action';
|
||||
$details['icon'] = null;
|
||||
break;
|
||||
}
|
||||
// Convert datetimes to the site timezone
|
||||
$dateTimeVisit = Date::factory($details['serverTimePretty'], $timezone);
|
||||
$details['serverTimePretty'] = $dateTimeVisit->getLocalized(Piwik::translate('CoreHome_ShortDateFormat') . ' %time%');
|
||||
}
|
||||
$visitorDetailsArray['goalConversions'] = count($goalDetails);
|
||||
return $visitorDetailsArray;
|
||||
}
|
||||
|
||||
private static function getCustomVariablePrettyKey($key)
|
||||
{
|
||||
$rename = array(
|
||||
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];
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
private static function sortByServerTime($a, $b)
|
||||
{
|
||||
$ta = strtotime($a['serverTimePretty']);
|
||||
$tb = strtotime($b['serverTimePretty']);
|
||||
return $ta < $tb
|
||||
? -1
|
||||
: ($ta == $tb
|
||||
? 0
|
||||
: 1);
|
||||
}
|
||||
}
|
||||
107
www/analytics/plugins/Live/VisitorLog.php
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Live;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugin\Visualization;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
* A special DataTable visualization for the Live.getLastVisitsDetails API method.
|
||||
*/
|
||||
class VisitorLog extends Visualization
|
||||
{
|
||||
const ID = 'Piwik\Plugins\Live\VisitorLog';
|
||||
const TEMPLATE_FILE = "@Live/_dataTableViz_visitorLog.twig";
|
||||
|
||||
public function beforeLoadDataTable()
|
||||
{
|
||||
$this->requestConfig->addPropertiesThatShouldBeAvailableClientSide(array(
|
||||
'filter_limit',
|
||||
'filter_offset',
|
||||
'filter_sort_column',
|
||||
'filter_sort_order',
|
||||
));
|
||||
|
||||
$this->requestConfig->filter_sort_column = 'idVisit';
|
||||
$this->requestConfig->filter_sort_order = 'asc';
|
||||
$this->requestConfig->filter_limit = 20;
|
||||
$this->requestConfig->disable_generic_filters = true;
|
||||
|
||||
$offset = Common::getRequestVar('filter_offset', 0);
|
||||
$limit = Common::getRequestVar('filter_limit', $this->requestConfig->filter_limit);
|
||||
|
||||
$this->config->filters[] = array('Limit', array($offset, $limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure visualization.
|
||||
*/
|
||||
public function beforeRender()
|
||||
{
|
||||
$this->config->datatable_js_type = 'VisitorLog';
|
||||
$this->config->enable_sort = false;
|
||||
$this->config->show_search = false;
|
||||
$this->config->show_exclude_low_population = false;
|
||||
$this->config->show_offset_information = false;
|
||||
$this->config->show_all_views_icons = false;
|
||||
$this->config->show_table_all_columns = false;
|
||||
$this->config->show_export_as_rss_feed = false;
|
||||
|
||||
$this->config->documentation = Piwik::translate('Live_VisitorLogDocumentation', array('<br />', '<br />'));
|
||||
|
||||
$filterEcommerce = Common::getRequestVar('filterEcommerce', 0, 'int');
|
||||
$this->config->custom_parameters = array(
|
||||
// set a very high row count so that the next link in the footer of the data table is always shown
|
||||
'totalRows' => 10000000,
|
||||
|
||||
'filterEcommerce' => $filterEcommerce,
|
||||
'pageUrlNotDefined' => Piwik::translate('General_NotDefined', Piwik::translate('Actions_ColumnPageURL')),
|
||||
|
||||
'smallWidth' => 1 == Common::getRequestVar('small', 0, 'int'),
|
||||
);
|
||||
|
||||
$this->config->footer_icons = array(
|
||||
array(
|
||||
'class' => 'tableAllColumnsSwitch',
|
||||
'buttons' => array(
|
||||
array(
|
||||
'id' => static::ID,
|
||||
'title' => Piwik::translate('Live_LinkVisitorLog'),
|
||||
'icon' => 'plugins/Zeitgeist/images/table.png'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// determine if each row has ecommerce activity or not
|
||||
if ($filterEcommerce) {
|
||||
$this->dataTable->filter(
|
||||
'ColumnCallbackAddMetadata',
|
||||
array(
|
||||
'actionDetails',
|
||||
'hasEcommerce',
|
||||
function ($actionDetails) use ($filterEcommerce) {
|
||||
foreach ($actionDetails as $action) {
|
||||
$isEcommerceOrder = $action['type'] == 'ecommerceOrder'
|
||||
&& $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ORDERS;
|
||||
$isAbandonedCart = $action['type'] == 'ecommerceAbandonedCart'
|
||||
&& $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ABANDONED_CARTS;
|
||||
if($isAbandonedCart || $isEcommerceOrder) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
www/analytics/plugins/Live/images/avatar_frame.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
www/analytics/plugins/Live/images/file0.png
Normal file
|
After Width: | Height: | Size: 593 B |
BIN
www/analytics/plugins/Live/images/file1.png
Normal file
|
After Width: | Height: | Size: 637 B |
BIN
www/analytics/plugins/Live/images/file2.png
Normal file
|
After Width: | Height: | Size: 642 B |
BIN
www/analytics/plugins/Live/images/file3.png
Normal file
|
After Width: | Height: | Size: 674 B |
BIN
www/analytics/plugins/Live/images/file4.png
Normal file
|
After Width: | Height: | Size: 611 B |
BIN
www/analytics/plugins/Live/images/file5.png
Normal file
|
After Width: | Height: | Size: 668 B |
BIN
www/analytics/plugins/Live/images/file6.png
Normal file
|
After Width: | Height: | Size: 659 B |
BIN
www/analytics/plugins/Live/images/file7.png
Normal file
|
After Width: | Height: | Size: 681 B |
BIN
www/analytics/plugins/Live/images/file8.png
Normal file
|
After Width: | Height: | Size: 642 B |
BIN
www/analytics/plugins/Live/images/file9.png
Normal file
|
After Width: | Height: | Size: 569 B |
BIN
www/analytics/plugins/Live/images/paperclip.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
www/analytics/plugins/Live/images/pause.gif
Normal file
|
After Width: | Height: | Size: 669 B |
BIN
www/analytics/plugins/Live/images/pause_disabled.gif
Normal file
|
After Width: | Height: | Size: 619 B |
BIN
www/analytics/plugins/Live/images/play.gif
Normal file
|
After Width: | Height: | Size: 666 B |
BIN
www/analytics/plugins/Live/images/play_disabled.gif
Normal file
|
After Width: | Height: | Size: 407 B |
BIN
www/analytics/plugins/Live/images/returningVisitor.gif
Normal file
|
After Width: | Height: | Size: 995 B |
BIN
www/analytics/plugins/Live/images/unknown_avatar.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
www/analytics/plugins/Live/images/visitorProfileLaunch.png
Normal file
|
After Width: | Height: | Size: 661 B |
BIN
www/analytics/plugins/Live/images/visitor_profile_background.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
www/analytics/plugins/Live/images/visitor_profile_close.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
www/analytics/plugins/Live/images/visitor_profile_gradient.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
288
www/analytics/plugins/Live/javascripts/live.js
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* jQueryUI widget for Live visitors widget
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
$.widget('piwik.liveWidget', {
|
||||
|
||||
/**
|
||||
* Default settings for widgetPreview
|
||||
*/
|
||||
options:{
|
||||
// Maximum numbers of rows to display in widget
|
||||
maxRows: 10,
|
||||
// minimal time in microseconds to wait between updates
|
||||
interval: 3000,
|
||||
// maximum time to wait between requests
|
||||
maxInterval: 300000,
|
||||
// url params to use for data request
|
||||
dataUrlParams: null,
|
||||
// callback triggered on a successful update (content of widget changed)
|
||||
onUpdate: null,
|
||||
// speed for fade animation
|
||||
fadeInSpeed: 'slow'
|
||||
},
|
||||
|
||||
/**
|
||||
* current updateInterval used
|
||||
*/
|
||||
currentInterval: null,
|
||||
|
||||
/**
|
||||
* identifies if content has updated (eg new visits/views)
|
||||
*/
|
||||
updated: false,
|
||||
|
||||
/**
|
||||
* window timeout interval
|
||||
*/
|
||||
updateInterval: null,
|
||||
|
||||
/**
|
||||
* identifies if the liveWidget ist started or not
|
||||
*/
|
||||
isStarted: true,
|
||||
|
||||
/**
|
||||
* Update the widget
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
_update: function () {
|
||||
|
||||
this.updated = false;
|
||||
|
||||
var that = this;
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams(this.options.dataUrlParams, 'GET');
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.setCallback(function (r) {
|
||||
that._parseResponse(r);
|
||||
|
||||
// add default interval to last interval if not updated or reset to default if so
|
||||
if (!that.updated) {
|
||||
that.currentInterval += that.options.interval;
|
||||
} else {
|
||||
that.currentInterval = that.options.interval;
|
||||
if (that.options.onUpdate) that.options.onUpdate();
|
||||
}
|
||||
|
||||
// check new interval doesn't reach the defined maximum
|
||||
if (that.options.maxInterval < that.currentInterval) {
|
||||
that.currentInterval = that.options.maxInterval;
|
||||
}
|
||||
|
||||
if (that.isStarted) {
|
||||
window.clearTimeout(that.updateInterval);
|
||||
if ($(that.element).closest('body').length) {
|
||||
that.updateInterval = window.setTimeout(function() { that._update() }, that.currentInterval);
|
||||
}
|
||||
}
|
||||
});
|
||||
ajaxRequest.send(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the given response and updates the widget if newer content is available
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
_parseResponse: function (data) {
|
||||
if (!data || !data.length) {
|
||||
this.updated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var items = $('li', $(data));
|
||||
for (var i = items.length; i--;) {
|
||||
this._parseItem(items[i]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the given item and updates or adds an entry to the list
|
||||
*
|
||||
* @param item to parse
|
||||
* @return void
|
||||
*/
|
||||
_parseItem: function (item) {
|
||||
var visitId = $(item).attr('id');
|
||||
if ($('#' + visitId, this.element).length) {
|
||||
if ($('#' + visitId, this.element).html() != $(item).html()) {
|
||||
this.updated = true;
|
||||
}
|
||||
$('#' + visitId, this.element).remove();
|
||||
$(this.element).prepend(item);
|
||||
} else {
|
||||
this.updated = true;
|
||||
$(item).hide();
|
||||
$(this.element).prepend(item);
|
||||
$(item).fadeIn(this.options.fadeInSpeed);
|
||||
}
|
||||
// remove rows if there are more than the maximum
|
||||
$('li:gt(' + (this.options.maxRows - 1) + ')', this.element).remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
_create: function () {
|
||||
|
||||
if (!this.options.dataUrlParams) {
|
||||
console && console.error('liveWidget error: dataUrlParams needs to be defined in settings.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentInterval = this.options.interval;
|
||||
|
||||
var self = this;
|
||||
|
||||
this.updateInterval = window.setTimeout(function() { self._update(); }, this.currentInterval);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops requests if widget is destroyed
|
||||
*/
|
||||
_destroy: function () {
|
||||
|
||||
this.stop();
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers an update for the widget
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
update: function () {
|
||||
this._update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the automatic update cycle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
start: function () {
|
||||
this.isStarted = true;
|
||||
this.currentInterval = 0;
|
||||
this._update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops the automatic update cycle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
stop: function () {
|
||||
this.isStarted = false;
|
||||
window.clearTimeout(this.updateInterval);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the interval for refresh
|
||||
*
|
||||
* @param {int} interval new interval for refresh
|
||||
* @return void
|
||||
*/
|
||||
setInterval: function (interval) {
|
||||
this.currentInterval = interval;
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
|
||||
$(function() {
|
||||
var refreshWidget = function (element, refreshAfterXSecs) {
|
||||
// if the widget has been removed from the DOM, abort
|
||||
if ($(element).parent().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lastMinutes = $(element).attr('data-last-minutes') || 3,
|
||||
translations = JSON.parse($(element).attr('data-translations'));
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams({
|
||||
module: 'API',
|
||||
method: 'Live.getCounters',
|
||||
format: 'json',
|
||||
lastMinutes: lastMinutes
|
||||
}, 'get');
|
||||
ajaxRequest.setFormat('json');
|
||||
ajaxRequest.setCallback(function (data) {
|
||||
data = data[0];
|
||||
|
||||
// set text and tooltip of visitors count metric
|
||||
var visitors = data['visitors'];
|
||||
if (visitors == 1) {
|
||||
var visitorsCountMessage = translations['one_visitor'];
|
||||
}
|
||||
else {
|
||||
var visitorsCountMessage = translations['visitors'].replace('%s', visitors);
|
||||
}
|
||||
$('.simple-realtime-visitor-counter', element)
|
||||
.attr('title', visitorsCountMessage)
|
||||
.find('div').text(visitors);
|
||||
|
||||
// set text of individual metrics spans
|
||||
var metrics = $('.simple-realtime-metric', element);
|
||||
|
||||
var visitsText = data['visits'] == 1
|
||||
? translations['one_visit'] : translations['visits'].replace('%s', data['visits']);
|
||||
$(metrics[0]).text(visitsText);
|
||||
|
||||
var actionsText = data['actions'] == 1
|
||||
? translations['one_action'] : translations['actions'].replace('%s', data['actions']);
|
||||
$(metrics[1]).text(actionsText);
|
||||
|
||||
var lastMinutesText = lastMinutes == 1
|
||||
? translations['one_minute'] : translations['minutes'].replace('%s', lastMinutes);
|
||||
$(metrics[2]).text(lastMinutesText);
|
||||
|
||||
// schedule another request
|
||||
setTimeout(function () { refreshWidget(element, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
|
||||
});
|
||||
ajaxRequest.send(true);
|
||||
};
|
||||
|
||||
var exports = require("piwik/Live");
|
||||
exports.initSimpleRealtimeVisitorWidget = function () {
|
||||
$('.simple-realtime-visitor-widget').each(function() {
|
||||
var $this = $(this),
|
||||
refreshAfterXSecs = $this.attr('data-refreshAfterXSecs');
|
||||
if ($this.attr('data-inited')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.attr('data-inited', 1);
|
||||
|
||||
setTimeout(function() { refreshWidget($this, refreshAfterXSecs ); }, refreshAfterXSecs * 1000);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
var pauseImage = "plugins/Live/images/pause.gif";
|
||||
var pauseDisabledImage = "plugins/Live/images/pause_disabled.gif";
|
||||
var playImage = "plugins/Live/images/play.gif";
|
||||
var playDisabledImage = "plugins/Live/images/play_disabled.gif";
|
||||
function onClickPause() {
|
||||
$('#pauseImage').attr('src', pauseImage);
|
||||
$('#playImage').attr('src', playDisabledImage);
|
||||
return $('#visitsLive').liveWidget('stop');
|
||||
}
|
||||
function onClickPlay() {
|
||||
$('#playImage').attr('src', playImage);
|
||||
$('#pauseImage').attr('src', pauseDisabledImage);
|
||||
return $('#visitsLive').liveWidget('start');
|
||||
}
|
||||
89
www/analytics/plugins/Live/javascripts/visitorLog.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* Visitor profile popup control.
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($, require) {
|
||||
|
||||
var exports = require('piwik/UI'),
|
||||
DataTable = exports.DataTable,
|
||||
dataTablePrototype = DataTable.prototype;
|
||||
|
||||
/**
|
||||
* DataTable UI class for jqPlot graph datatable visualizations.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
exports.VisitorLog = function (element) {
|
||||
DataTable.call(this, element);
|
||||
};
|
||||
|
||||
$.extend(exports.VisitorLog.prototype, dataTablePrototype, {
|
||||
|
||||
/**
|
||||
* Initializes this class.
|
||||
*/
|
||||
init: function () {
|
||||
dataTablePrototype.init.call(this);
|
||||
|
||||
// Replace duplicated page views by a NX count instead of using too much vertical space
|
||||
$("ol.visitorLog").each(function () {
|
||||
var prevelement;
|
||||
var prevhtml;
|
||||
var counter = 0;
|
||||
$(this).find("li").each(function () {
|
||||
counter++;
|
||||
$(this).val(counter);
|
||||
var current = $(this).html();
|
||||
if (current == prevhtml) {
|
||||
var repeat = prevelement.find(".repeat");
|
||||
if (repeat.length) {
|
||||
repeat.html((parseInt(repeat.html()) + 1) + "x");
|
||||
} else {
|
||||
prevelement.append($("<em>2x</em>").attr({'class': 'repeat', 'title': _pk_translate('Live_PageRefreshed')}));
|
||||
}
|
||||
$(this).hide();
|
||||
} else {
|
||||
prevhtml = current;
|
||||
prevelement = $(this);
|
||||
}
|
||||
|
||||
var $this = $(this);
|
||||
var tooltipIsOpened = false;
|
||||
|
||||
$('a', $this).on('focus', function () {
|
||||
// see http://dev.piwik.org/trac/ticket/4099
|
||||
if (tooltipIsOpened) {
|
||||
$this.tooltip('close');
|
||||
}
|
||||
});
|
||||
|
||||
$this.tooltip({
|
||||
track: true,
|
||||
show: false,
|
||||
hide: false,
|
||||
content: function() {
|
||||
var title = $(this).attr('title');
|
||||
return $('<a>').text( title ).html().replace(/\n/g, '<br />');
|
||||
},
|
||||
tooltipClass: 'small',
|
||||
open: function() { tooltipIsOpened = true; },
|
||||
close: function() { tooltipIsOpened = false; }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// launch visitor profile on visitor profile link click
|
||||
this.$element.on('click', '.visitor-log-visitor-profile-link', function (e) {
|
||||
e.preventDefault();
|
||||
broadcast.propagateNewPopoverParameter('visitorProfile', $(this).attr('data-visitor-id'));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery, require);
|
||||
287
www/analytics/plugins/Live/javascripts/visitorProfile.js
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* Visitor profile popup control.
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($, require) {
|
||||
|
||||
var piwik = require('piwik'),
|
||||
exports = require('piwik/UI'),
|
||||
UIControl = exports.UIControl;
|
||||
|
||||
/**
|
||||
* Sets up and handles events for the visitor profile popup.
|
||||
*
|
||||
* @param {Element} element The HTML element returned by the Live.getVisitorLog controller
|
||||
* action. Should have the CSS class 'visitor-profile'.
|
||||
* @constructor
|
||||
*/
|
||||
var VisitorProfileControl = function (element) {
|
||||
UIControl.call(this, element);
|
||||
this._setupControl();
|
||||
this._bindEventCallbacks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes all elements w/ the .visitor-profile CSS class as visitor profile popups,
|
||||
* if the element has not already been initialized.
|
||||
*/
|
||||
VisitorProfileControl.initElements = function () {
|
||||
UIControl.initElements(this, '.visitor-profile');
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the visitor profile popover for a visitor ID. This should not be called directly.
|
||||
* Instead broadcast.propagateNewPopoverParameter('visitorProfile', visitorId) should be
|
||||
* called. This would make sure the popover would be opened if the URL is copied and pasted
|
||||
* in a new tab/window.
|
||||
*
|
||||
* @param {String} visitorId The string visitor ID.
|
||||
*/
|
||||
VisitorProfileControl.showPopover = function (visitorId) {
|
||||
var url = 'module=Live&action=getVisitorProfilePopup&visitorId=' + encodeURIComponent(visitorId);
|
||||
|
||||
// if there is already a map shown on the screen, do not show the map in the popup. kartograph seems
|
||||
// to only support showing one map at a time.
|
||||
if ($('.RealTimeMap').length > 0) {
|
||||
url += '&showMap=0';
|
||||
}
|
||||
|
||||
Piwik_Popover.createPopupAndLoadUrl(url, _pk_translate('Live_VisitorProfile'), 'visitor-profile-popup');
|
||||
};
|
||||
|
||||
$.extend(VisitorProfileControl.prototype, UIControl.prototype, {
|
||||
|
||||
_setupControl: function () {
|
||||
// focus the popup so it will accept key events
|
||||
this.$element.focus();
|
||||
|
||||
// highlight the first visit
|
||||
$('.visitor-profile-visits>li:first-child', this.$element).addClass('visitor-profile-current-visit');
|
||||
},
|
||||
|
||||
_bindEventCallbacks: function () {
|
||||
var self = this,
|
||||
$element = this.$element;
|
||||
|
||||
$element.on('click', '.visitor-profile-close', function (e) {
|
||||
e.preventDefault();
|
||||
Piwik_Popover.close();
|
||||
return false;
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-more-info>a', function (e) {
|
||||
e.preventDefault();
|
||||
self._loadMoreVisits();
|
||||
return false;
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-see-more-cvars>a', function (e) {
|
||||
e.preventDefault();
|
||||
$('.visitor-profile-extra-cvars', $element).slideToggle();
|
||||
return false;
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-visit-title-row', function () {
|
||||
self._loadIndividualVisitDetails($('h2', this));
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-prev-visitor', function (e) {
|
||||
e.preventDefault();
|
||||
self._loadPreviousVisitor();
|
||||
return false;
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-next-visitor', function (e) {
|
||||
e.preventDefault();
|
||||
self._loadNextVisitor();
|
||||
return false;
|
||||
});
|
||||
|
||||
$element.on('keydown', function (e) {
|
||||
if (e.which == 37) { // on <- key press, load previous visitor
|
||||
self._loadPreviousVisitor();
|
||||
} else if (e.which == 39) { // on -> key press, load next visitor
|
||||
self._loadNextVisitor();
|
||||
}
|
||||
});
|
||||
|
||||
$element.on('click', '.visitor-profile-show-map', function (e) {
|
||||
e.preventDefault();
|
||||
self.toggleMap();
|
||||
return false;
|
||||
});
|
||||
|
||||
// append token_auth dynamically to export link
|
||||
$element.on('mousedown', '.visitor-profile-export', function (e) {
|
||||
var url = $(this).attr('href');
|
||||
if (url.indexOf('&token_auth=') == -1) {
|
||||
$(this).attr('href', url + '&token_auth=' + piwik.token_auth);
|
||||
}
|
||||
});
|
||||
|
||||
// on hover, show export link (chrome won't let me do this via css :( )
|
||||
$element.on('mouseenter mouseleave', '.visitor-profile-id', function (e) {
|
||||
var $exportLink = $(this).find('.visitor-profile-export');
|
||||
if ($exportLink.css('visibility') == 'hidden') {
|
||||
$exportLink.css('visibility', 'visible');
|
||||
} else {
|
||||
$exportLink.css('visibility', 'hidden');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
toggleMap: function () {
|
||||
var $element = this.$element,
|
||||
$map = $('.visitor-profile-map', $element);
|
||||
if (!$map.children().length) { // if the map hasn't been loaded, load it
|
||||
this._loadMap($map);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($map.is(':hidden')) { // show the map if it is hidden
|
||||
if ($map.height() < 1) {
|
||||
$map.resize();
|
||||
}
|
||||
|
||||
$map.slideDown('slow');
|
||||
var newLabel = 'Live_HideMap';
|
||||
|
||||
piwikHelper.lazyScrollTo($('.visitor-profile-location', $element)[0], 400);
|
||||
} else { // hide the map if it is shown
|
||||
$map.slideUp('slow');
|
||||
var newLabel = 'Live_ShowMap';
|
||||
}
|
||||
|
||||
newLabel = _pk_translate(newLabel).replace(' ', '\xA0');
|
||||
$('.visitor-profile-show-map', $element).text('(' + newLabel + ')');
|
||||
},
|
||||
|
||||
_loadMap: function ($map) {
|
||||
var self = this;
|
||||
|
||||
var ajax = new ajaxHelper();
|
||||
ajax.setUrl($map.attr('data-href'));
|
||||
ajax.setCallback(function (response) {
|
||||
$map.html(response);
|
||||
self.toggleMap();
|
||||
});
|
||||
ajax.setFormat('html');
|
||||
ajax.setLoadingElement($('.visitor-profile-location > p > .loadingPiwik', self.$element));
|
||||
ajax.send();
|
||||
},
|
||||
|
||||
_loadMoreVisits: function () {
|
||||
var self = this,
|
||||
$element = this.$element;
|
||||
|
||||
var loading = $('.visitor-profile-more-info > .loadingPiwik', $element);
|
||||
loading.show();
|
||||
|
||||
var ajax = new ajaxHelper();
|
||||
ajax.addParams({
|
||||
module: 'Live',
|
||||
action: 'getVisitList',
|
||||
period: '',
|
||||
date: '',
|
||||
visitorId: $element.attr('data-visitor-id'),
|
||||
filter_offset: $('.visitor-profile-visits>li', $element).length
|
||||
}, 'GET');
|
||||
ajax.setCallback(function (response) {
|
||||
if (response == "") { // no more visits left
|
||||
self._showNoMoreVisitsSpan();
|
||||
} else {
|
||||
response = $(response);
|
||||
loading.hide();
|
||||
|
||||
$('.visitor-profile-visits', $element).append(response);
|
||||
if (response.filter('li').length < 10) {
|
||||
self._showNoMoreVisitsSpan();
|
||||
}
|
||||
|
||||
piwikHelper.lazyScrollTo($(response)[0], 400, true);
|
||||
}
|
||||
});
|
||||
ajax.setFormat('html');
|
||||
ajax.send();
|
||||
},
|
||||
|
||||
_showNoMoreVisitsSpan: function () {
|
||||
var noMoreSpan = $('<span/>').text(_pk_translate('Live_NoMoreVisits')).addClass('visitor-profile-no-visits');
|
||||
$('.visitor-profile-more-info', this.$element).html(noMoreSpan);
|
||||
},
|
||||
|
||||
_loadIndividualVisitDetails: function ($visitElement) {
|
||||
var self = this,
|
||||
$element = this.$element,
|
||||
visitId = $visitElement.attr('data-idvisit');
|
||||
|
||||
$('.visitor-profile-avatar .loadingPiwik', $element).css('display', 'inline-block');
|
||||
piwikHelper.lazyScrollTo($('.visitor-profile-avatar', $element)[0], 400);
|
||||
|
||||
var ajax = new ajaxHelper();
|
||||
ajax.addParams({
|
||||
module: 'Live',
|
||||
action: 'getSingleVisitSummary',
|
||||
visitId: visitId,
|
||||
idSite: piwik.idSite
|
||||
}, 'GET');
|
||||
ajax.setCallback(function (response) {
|
||||
$('.visitor-profile-avatar .loadingPiwik', $element).hide();
|
||||
|
||||
$('.visitor-profile-current-visit', $element).removeClass('visitor-profile-current-visit');
|
||||
$visitElement.closest('li').addClass('visitor-profile-current-visit');
|
||||
|
||||
var $latestVisitSection = $('.visitor-profile-latest-visit', $element);
|
||||
$latestVisitSection
|
||||
.html(response)
|
||||
.parent()
|
||||
.effect('highlight', {color: '#FFFFCB'}, 1200);
|
||||
});
|
||||
ajax.setFormat('html');
|
||||
ajax.send();
|
||||
},
|
||||
|
||||
_loadPreviousVisitor: function () {
|
||||
this._gotoAdjacentVisitor(this.$element.attr('data-prev-visitor'));
|
||||
},
|
||||
|
||||
_loadNextVisitor: function () {
|
||||
this._gotoAdjacentVisitor(this.$element.attr('data-next-visitor'));
|
||||
},
|
||||
|
||||
_gotoAdjacentVisitor: function (idVisitor) {
|
||||
if (!idVisitor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._inPopover()) {
|
||||
broadcast.propagateNewPopoverParameter('visitorProfile', idVisitor);
|
||||
} else if (this._inWidget()) {
|
||||
this.$element.closest('[widgetid]').dashboardWidget('reload', false, true, {visitorId: idVisitor});
|
||||
}
|
||||
},
|
||||
|
||||
_getFirstVisitId: function () {
|
||||
return $('.visitor-profile-visits>li:first-child>h2', this.$element).attr('data-idvisit');
|
||||
},
|
||||
|
||||
_inPopover: function () {
|
||||
return !! this.$element.closest('#Piwik_Popover').length;
|
||||
},
|
||||
|
||||
_inWidget: function () {
|
||||
return !! this.$element.closest('.widget').length;
|
||||
}
|
||||
});
|
||||
|
||||
exports.VisitorProfileControl = VisitorProfileControl;
|
||||
|
||||
// add the popup handler that creates a visitor profile
|
||||
broadcast.addPopoverHandler('visitorProfile', VisitorProfileControl.showPopover);
|
||||
|
||||
})(jQuery, require);
|
||||
220
www/analytics/plugins/Live/stylesheets/live.less
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
#visitsLive {
|
||||
text-align: left;
|
||||
font-size: 90%;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#visitsLive .datetime, #visitsLive .country, #visitsLive .referrer, #visitsLive .settings, #visitsLive .returning {
|
||||
border-bottom: 1px solid #d3d1c5;
|
||||
border-right: 1px solid #d3d1c5;
|
||||
padding: 5px 5px 5px 12px;
|
||||
}
|
||||
|
||||
#visitsLive .datetime {
|
||||
background: #E4E2D7;
|
||||
border-top: 1px solid #d3d1c5;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#visitsLive .country {
|
||||
background: #FFF url(plugins/CoreHome/images/bullet1.gif) no-repeat scroll 0 0;
|
||||
}
|
||||
|
||||
#visitsLive .referrer {
|
||||
background: #F9FAFA none repeat scroll 0 0;
|
||||
}
|
||||
|
||||
#visitsLive .referrer:hover {
|
||||
background: #FFFFF7;
|
||||
}
|
||||
|
||||
#visitsLive .pagesTitle {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#visitsLive .settings {
|
||||
background: #FFF none repeat scroll 0 0;
|
||||
}
|
||||
|
||||
#visitsLive .settings a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#visitsLive .returning {
|
||||
background: #F9FAFA none repeat scroll 0 0;
|
||||
}
|
||||
|
||||
.visitsLiveFooter img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.visitsLiveFooter {
|
||||
line-height: 2.5em;
|
||||
}
|
||||
|
||||
.dataTableVizVisitorLog table img {
|
||||
margin: 0 3px 0 0;
|
||||
}
|
||||
|
||||
.visitsLiveFooter a.rightLink {
|
||||
float: right;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
#visitsLive .datetime a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table.dataTable td.highlightField {
|
||||
background-color: #FFFFCB !important;
|
||||
}
|
||||
|
||||
ol.visitorLog {
|
||||
list-style: decimal inside none;
|
||||
}
|
||||
|
||||
.truncated-text-line {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display:inline-block;
|
||||
max-width:90%;
|
||||
overflow: -moz-hidden-unscrollable;
|
||||
}
|
||||
|
||||
ol.visitorLog li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#visitsLive img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.visitorRank img {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.iconPadding {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.visitorRank {
|
||||
margin-left: 15px;
|
||||
border: 1px solid #D8D8D8;
|
||||
color: #474747;
|
||||
border-radius: 3px;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
|
||||
#visitsLive .visitorRank {
|
||||
padding: 2px;
|
||||
border: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.hash {
|
||||
color: #BBB;
|
||||
font-size: 9pt;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.repeat {
|
||||
font-weight: bold;
|
||||
border: 1px solid #444;
|
||||
border-radius: 3px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.dataTableVizVisitorLog hr {
|
||||
background: none repeat scroll 0 0 transparent;
|
||||
border: 0 none #000;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #eee;
|
||||
margin: 0 2em 0.5em;
|
||||
padding: 0 0 0.5em;
|
||||
}
|
||||
|
||||
.simple-realtime-visitor-widget {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.simple-realtime-visitor-counter {
|
||||
background-color: #F1F0EB;
|
||||
border-radius: 10px;
|
||||
display: inline-block;
|
||||
margin: 2em 0 1em 0;
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
.simple-realtime-visitor-counter > div {
|
||||
font-size: 4.0em;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.simple-realtime-metric {
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.simple-realtime-elaboration {
|
||||
margin: 1em 2em 1em 2em;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ol.visitorLog p {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.dataTableVizVisitorLog table.dataTable .column {
|
||||
white-space: normal;
|
||||
padding: 12px 5px;
|
||||
}
|
||||
.dataTableVizVisitorLog table.dataTable .label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dataTableVizVisitorLog .dataTableWrapper {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.visitor-log-page-list {
|
||||
position:relative;
|
||||
}
|
||||
.dataTableVizVisitorLog tr:hover .visitor-log-visitor-profile-link {
|
||||
display:inline;
|
||||
}
|
||||
a.visitor-log-visitor-profile-link {
|
||||
position:absolute;
|
||||
display:none;
|
||||
right:8px;
|
||||
top:0px;
|
||||
font-style:italic;
|
||||
font-size:13px;
|
||||
|
||||
img {
|
||||
margin-top:-2px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitorLog,.visitor-profile-actions {
|
||||
> li > div {
|
||||
display:inline-block;
|
||||
width:85%;
|
||||
vertical-align:top;
|
||||
}
|
||||
}
|
||||
|
||||
.action-list-action-icon {
|
||||
float:left;
|
||||
margin-top:6px;
|
||||
}
|
||||
|
||||
.action-list-url {
|
||||
display:block;
|
||||
}
|
||||
547
www/analytics/plugins/Live/stylesheets/visitor_profile.less
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
.visitor-profile {
|
||||
position:relative;
|
||||
width:1150px;
|
||||
border:1px solid #a19e96;
|
||||
border-radius:5px;
|
||||
background:url(../images/visitor_profile_background.jpg) repeat;
|
||||
box-shadow:5px 5px 5px rgba(0,0,0,0.22);
|
||||
text-align:left;
|
||||
|
||||
h1 {
|
||||
font-size:18px;
|
||||
color:#7e7363;
|
||||
text-shadow:0 1px 0 rgba(255,255,255,1);
|
||||
margin:9px 0 0 0;
|
||||
padding:0;
|
||||
|
||||
a {
|
||||
font-size:12px;
|
||||
margin-left:3px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size:14px;
|
||||
color:#5e5e5c;
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display:inline-block;
|
||||
font-size:14px;
|
||||
margin:0 0 0 5px;
|
||||
padding:0;
|
||||
font-weight:bold;
|
||||
color:black;
|
||||
}
|
||||
|
||||
span.truncated-text-line {
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-summary,.visitor-profile-important-visits,.visitor-profile-avatar,.visitor-profile-visits-container {
|
||||
span, strong {
|
||||
display:inline;
|
||||
font-size:14px;
|
||||
color:#5e5e5c;
|
||||
line-height:19px;
|
||||
padding-left:4px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-widget-link {
|
||||
color:#5e5e5c;
|
||||
}
|
||||
|
||||
.visitor-profile-widget-link:hover {
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
.visitor-profile-export {
|
||||
float:right;
|
||||
margin-top:3px;
|
||||
}
|
||||
|
||||
.visitor-profile-close {
|
||||
position:absolute;
|
||||
right:-17px;
|
||||
top:-16px;
|
||||
height:35px;
|
||||
width:35px;
|
||||
background:url(../images/visitor_profile_close.png) no-repeat;
|
||||
}
|
||||
|
||||
.visitor-profile a {
|
||||
text-decoration:none;
|
||||
color:#255792;
|
||||
}
|
||||
|
||||
.visitor-profile > div {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.visitor-profile-info {
|
||||
height:auto;
|
||||
border-top:2px solid #f6f6f6;
|
||||
border-bottom:1px solid #d1cec8;
|
||||
border-radius:5px 5px 0 0;
|
||||
box-shadow:inset 0 25px 15px -10px #e0e0e0, inset 0 -25px 15px -10px #e0e0e0;
|
||||
|
||||
> div { // row
|
||||
border-bottom:1px solid #d1cec8;
|
||||
|
||||
> div { // columns
|
||||
vertical-align:top;
|
||||
height:auto;
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-overview { // first column
|
||||
width:574.5px;
|
||||
border-left:none;
|
||||
margin:0 -3px 0 0;
|
||||
border-right:1px solid #d1cec8;
|
||||
}
|
||||
.visitor-profile-visits-info { // last column
|
||||
width:573.5px;
|
||||
border-bottom:none;
|
||||
margin:0 0 0 -3px;
|
||||
border-left:1px solid #d1cec8;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-summary,.visitor-profile-important-visits,.visitor-profile-avatar,.visitor-profile-location {
|
||||
border-bottom:1px solid #d1cec8;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar > div {
|
||||
position:relative;
|
||||
float:left;
|
||||
min-height:145px;
|
||||
margin:12px 15px 0 0;
|
||||
padding-bottom:4px;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar > div:first-child {
|
||||
width:166px;
|
||||
margin-right:0;
|
||||
padding-left:16px;
|
||||
|
||||
> .visitor-profile-image-frame {
|
||||
width:149px;
|
||||
height:154px;
|
||||
background:url(../images/avatar_frame.png) no-repeat;
|
||||
|
||||
> img { // avatar image
|
||||
width:122px;
|
||||
height:120px;
|
||||
margin:11px 0 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
> img { // paperclip image
|
||||
position:absolute;
|
||||
top:-20px;
|
||||
left:3px;
|
||||
z-index:2;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-avatar > div:last-child {
|
||||
margin-right:0;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar h1 {
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.visitor-profile-extra-cvars {
|
||||
border-top: 1px solid #d1cec8;
|
||||
}
|
||||
|
||||
.visitor-profile-more-info {
|
||||
height:18px;
|
||||
border-radius:0 0 5px 5px;
|
||||
text-align:center;
|
||||
padding:0 0 13px;
|
||||
|
||||
> a {
|
||||
font-size:14px;
|
||||
text-decoration:none;
|
||||
color:#255792;
|
||||
text-shadow:0 1px 0 rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
> .loadingPiwik {
|
||||
padding:0 0 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-latest-visit {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.visitor-profile-latest-visit-column {
|
||||
padding-top:6px;
|
||||
display:inline-block;
|
||||
vertical-align:top;
|
||||
}
|
||||
|
||||
.visitor-profile-browser {
|
||||
margin-left: 5px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.visitor-profile-os {
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.visitor-profile-latest-visit-column:last-child {
|
||||
margin-left:9px;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar ul {
|
||||
width:178px;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar ul li {
|
||||
display:inline-block;
|
||||
min-height:24px;
|
||||
border-bottom:1px solid #d1cec8;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.visitor-profile-id {
|
||||
height:24px;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar ul li:last-child {
|
||||
border-bottom:none;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar ul li:first-child {
|
||||
border-bottom:1px solid #d1cec8; // make sure there is a border if only one item is shown in the list
|
||||
}
|
||||
|
||||
.visitor-profile-map {
|
||||
padding:0 21px 13px 21px;
|
||||
|
||||
.dataTableFeatures,.no_data {
|
||||
display:none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-map > div {
|
||||
border-radius:2px;
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
.visitor-profile-show-map {
|
||||
font-size:13px;
|
||||
font-style:italic;
|
||||
}
|
||||
|
||||
.visitor-profile-summary,.visitor-profile-important-visits {
|
||||
overflow:hidden;
|
||||
padding:5px 0 0 22px;
|
||||
}
|
||||
|
||||
.visitor-profile-summary {
|
||||
padding-bottom:18px;
|
||||
}
|
||||
|
||||
.visitor-profile-important-visits {
|
||||
padding-bottom:16px;
|
||||
|
||||
> div > div > p:first-child > strong {
|
||||
padding-left:0;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-summary > div {
|
||||
margin-top:6px;
|
||||
margin-right:1em;
|
||||
}
|
||||
|
||||
.visitor-profile-summary strong {
|
||||
padding-left:0;
|
||||
}
|
||||
|
||||
.visitor-profile-important-visits {
|
||||
> div {
|
||||
float:left;
|
||||
width:265px;
|
||||
height:100%;
|
||||
|
||||
> div {
|
||||
margin-top:13px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left:0;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-location {
|
||||
padding:10px 0 4px 19px;
|
||||
|
||||
p {
|
||||
margin:13px 0;
|
||||
font-size:14px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-location>p>.loadingPiwik {
|
||||
padding:0 0 0 4px;
|
||||
}
|
||||
|
||||
.visitor-profile-pages-visited {
|
||||
height:42px;
|
||||
overflow-y:auto;
|
||||
position:relative;
|
||||
margin-right:10px;
|
||||
border-bottom:none!important;
|
||||
padding: 8px 18px 10px 13px;
|
||||
|
||||
h1 {
|
||||
margin-left:6px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-visits-container {
|
||||
overflow-y:auto;
|
||||
position:relative;
|
||||
margin-right:10px;
|
||||
border-bottom:none!important;
|
||||
padding:0 18px 0 13px;
|
||||
|
||||
.action-list-action-icon {
|
||||
margin-right:4px;
|
||||
}
|
||||
|
||||
ol {
|
||||
> li {
|
||||
display:block;
|
||||
font-size:12px;
|
||||
font-weight:700;
|
||||
line-height:25px;
|
||||
padding:0 0 10px 13px;
|
||||
|
||||
span {
|
||||
font-size:13px;
|
||||
font-weight:700;
|
||||
line-height:25px;
|
||||
padding-left:0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ol.visitor-profile-visits > li {
|
||||
margin:0 0 10px 13px;
|
||||
padding:0;
|
||||
|
||||
> div {
|
||||
margin:0 6px 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
ol.visitor-profile-actions {
|
||||
counter-reset:item;
|
||||
list-style-type:none;
|
||||
|
||||
> li:before {
|
||||
content:counter(item) " ";
|
||||
counter-increment:item;
|
||||
}
|
||||
}
|
||||
|
||||
ol li ol {
|
||||
border-top:1px solid #d1cec8;
|
||||
}
|
||||
|
||||
ol > li > ol > li {
|
||||
margin-left:-12px;
|
||||
}
|
||||
|
||||
ol li ol li {
|
||||
display:block;
|
||||
color:#5e5e5c;
|
||||
font-size:13px;
|
||||
line-height:22px;
|
||||
padding-top:1px;
|
||||
padding-bottom:1px;
|
||||
}
|
||||
|
||||
ol li ol li {
|
||||
padding-bottom:4px;
|
||||
}
|
||||
|
||||
ol > li ol li span {
|
||||
padding-left:4px;
|
||||
}
|
||||
|
||||
ol > li ol li {
|
||||
.action-list-url {
|
||||
margin-left:4px;
|
||||
line-height:14px;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
> div > .action-list-url {
|
||||
line-height:23px;
|
||||
}
|
||||
}
|
||||
|
||||
ol > li ol li img {
|
||||
margin-left:7px;
|
||||
}
|
||||
|
||||
// overrides for _actionsDetails styles
|
||||
strong {
|
||||
font-size:13px;
|
||||
line-height:25px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-current-visit {
|
||||
background-color:#FAFACF;
|
||||
}
|
||||
|
||||
.visitor-profile-date {
|
||||
float:right;
|
||||
font-size:13px;
|
||||
line-height:26px;
|
||||
}
|
||||
|
||||
.visitor-profile-fog {
|
||||
height:25px;
|
||||
width:546px;
|
||||
position:absolute;
|
||||
bottom:51px;
|
||||
right:28px;
|
||||
background:url(../images/visitor_profile_gradient.png) repeat-x;
|
||||
}
|
||||
|
||||
// popup css
|
||||
.visitor-profile-popup {
|
||||
width: 1151px;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
|
||||
> .ui-dialog-titlebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> #Piwik_Popover {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
span.visitor-profile-goal-name {
|
||||
font-style:italic;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.visitor-profile-see-more-cvars {
|
||||
text-align:center;
|
||||
|
||||
> a {
|
||||
font-size:11px;
|
||||
display:inline-block;
|
||||
color:#5e5e5c;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-visit-title-row {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.visitor-profile-visit-title-row:hover {
|
||||
background-color:#FAFACF;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar .loadingPiwik {
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.visitor-profile-visits-info {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.visitor-profile-navigation {
|
||||
height:auto;
|
||||
min-height:inherit;
|
||||
font-size:12px;
|
||||
float:none;
|
||||
display:block;
|
||||
padding:0 0 0 22px;
|
||||
}
|
||||
|
||||
.visitor-profile-header {
|
||||
position:relative;
|
||||
|
||||
> .reportDocumentationIcon {
|
||||
display:none;
|
||||
margin:0;
|
||||
width:14px;
|
||||
background-size:14px;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-prev-visitor {
|
||||
display:none;
|
||||
position:absolute;
|
||||
right:100%;
|
||||
bottom:0;
|
||||
margin-right:2px;
|
||||
}
|
||||
|
||||
a.visitor-profile-next-visitor,a.visitor-profile-prev-visitor {
|
||||
display:none;
|
||||
color:#7e7363;
|
||||
}
|
||||
|
||||
.visitor-profile-avatar:hover {
|
||||
.visitor-profile-next-visitor,.visitor-profile-prev-visitor,.reportDocumentationIcon {
|
||||
display:inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.visitor-profile-no-visits {
|
||||
color:#999;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
.visitor-profile-latest-visit-loc {
|
||||
display:inline-block;
|
||||
position:absolute;
|
||||
right:4px;
|
||||
top:-24px;
|
||||
}
|
||||
|
||||
// overrides for the widgetized visitor profile
|
||||
.widget .visitor-profile {
|
||||
min-width: 100% !important;
|
||||
|
||||
p {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.visitor-profile-close {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.visitor-profile-info {
|
||||
> div { // row
|
||||
> div { // columns
|
||||
min-width:50% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
121
www/analytics/plugins/Live/templates/_actionsList.twig
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
{% for action in actionDetails %}
|
||||
{% set customVariablesTooltip %}
|
||||
{% if action.customVariables is defined %}
|
||||
{{ 'CustomVariables_CustomVariables'|translate }}
|
||||
{% for id,customVariable in action.customVariables %}
|
||||
{% set name = 'customVariablePageName' ~ id %}
|
||||
{% set value = 'customVariablePageValue' ~ id %}
|
||||
- {{ customVariable[name]|raw }} {% if customVariable[value]|length > 0 %} = {{ customVariable[value]|raw }}{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
{% if not clientSideParameters.filterEcommerce or action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %}
|
||||
<li class="{% if action.goalName is defined %}goal{% else %}action{% endif %}"
|
||||
title="{{ action.serverTimePretty }}{% if action.url is defined and action.url|trim|length %}
|
||||
|
||||
{{ action.url }}{% endif %}{% if customVariablesTooltip|trim|length %}
|
||||
|
||||
{{ customVariablesTooltip|trim }}{% endif -%}
|
||||
{%- if action.generationTime is defined %}
|
||||
|
||||
{{ 'General_ColumnGenerationTime'|translate }}: {{ action.generationTime|raw }}{% endif %}
|
||||
{%- if action.timeSpentPretty is defined %}
|
||||
|
||||
{{ 'General_TimeOnPage'|translate }}: {{ action.timeSpentPretty|raw }}{% endif -%}">
|
||||
<div>
|
||||
{% if action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %}
|
||||
{# Ecommerce Abandoned Cart / Ecommerce Order #}
|
||||
<img src="{{ action.icon }}"/>
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
<strong>{{ 'Goals_EcommerceOrder'|translate }}</strong>
|
||||
<span style='color:#666;'>({{ action.orderId }})</span>
|
||||
{% else %}
|
||||
<strong>{{'Goals_AbandonedCart'|translate}}</strong>
|
||||
|
||||
{# TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer #}
|
||||
{% endif %}
|
||||
<p>
|
||||
<span {% if not isWidget %}style='margin-left:20px;'{% endif %}>
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
{# spacing is important for tooltip to look nice #}
|
||||
{% set ecommerceOrderTooltip %}{{ 'General_ColumnRevenue'|translate }}: {{ action.revenue|money(clientSideParameters.idSite)|raw }}
|
||||
{% if action.revenueSubTotal is not empty %} - {{ 'General_Subtotal'|translate }}: {{ action.revenueSubTotal|money(clientSideParameters.idSite)|raw }}{% endif %}
|
||||
|
||||
{% if action.revenueTax is not empty %} - {{ 'General_Tax'|translate }}: {{ action.revenueTax|money(clientSideParameters.idSite)|raw }}{% endif %}
|
||||
|
||||
{% if action.revenueShipping is not empty %} - {{ 'General_Shipping'|translate }}: {{ action.revenueShipping|money(clientSideParameters.idSite)|raw }}{% endif %}
|
||||
|
||||
{% if action.revenueDiscount is not empty %} - {{ 'General_Discount'|translate }}: {{ action.revenueDiscount|money(clientSideParameters.idSite)|raw }}{% endif %}
|
||||
{% endset %}
|
||||
<abbr title="{{ ecommerceOrderTooltip }}">{{ 'General_ColumnRevenue'|translate }}:
|
||||
{% else %}
|
||||
{% set revenueLeft %}{{ 'General_ColumnRevenue'|translate }}{% endset %}
|
||||
{{ 'Goals_LeftInCart'|translate(revenueLeft) }}:
|
||||
{% endif %}
|
||||
<strong>{{ action.revenue|money(clientSideParameters.idSite)|raw }}</strong>
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
</abbr>
|
||||
{% endif %}, {{ 'General_Quantity'|translate }}: {{ action.items }}
|
||||
|
||||
{# Ecommerce items in Cart/Order #}
|
||||
{% if action.itemDetails is not empty %}
|
||||
<ul style='list-style:square;margin-left:{% if isWidget %}15{% else %}50{% endif %}px;'>
|
||||
{% for product in action.itemDetails %}
|
||||
<li>
|
||||
{{ product.itemSKU }}{% if product.itemName is not empty %}: {{ product.itemName }}{% endif %}
|
||||
{% if product.itemCategory is not empty %} ({{ product.itemCategory }}){% endif %}
|
||||
,
|
||||
{{ 'General_Quantity'|translate }}: {{ product.quantity }},
|
||||
{{ 'General_Price'|translate }}: {{ product.price|money(clientSideParameters.idSite)|raw }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
{% elseif action.goalName is not defined%}
|
||||
{# Page view / Download / Outlink / Event #}
|
||||
{% if action.pageTitle|default(false) is not empty %}
|
||||
<span class="truncated-text-line">{{ action.pageTitle }}</span>
|
||||
{% endif %}
|
||||
{% if action.siteSearchKeyword is defined %}
|
||||
{% if action.type == 'search' %}
|
||||
<img src='{{ action.icon }}' title='{{ 'Actions_SubmenuSitesearch'|translate }}' class="action-list-action-icon">
|
||||
{% endif %}
|
||||
<span class="truncated-text-line">{{ action.siteSearchKeyword }}</span>
|
||||
{% endif %}
|
||||
{% if action.eventCategory|default(false) is not empty %}
|
||||
<img src='{{ action.icon }}' title='{{ 'Events_Event'|translate }}' class="action-list-action-icon">
|
||||
<span class="truncated-text-line">{{ action.eventCategory }} - {{ action.eventAction }} {% if action.eventName is defined %}- {{ action.eventName }}{% endif %} {% if action.eventValue is defined %}- {{ action.eventValue }}{% endif %}</span>
|
||||
{% endif %}
|
||||
{% if action.url is not empty %}
|
||||
{% if action.type == 'action' and action.pageTitle|default(false) is not empty %}<p>{% endif %}
|
||||
{% if action.type == 'download' or action.type == 'outlink' %}
|
||||
<img src='{{ action.icon }}' class="action-list-action-icon">
|
||||
{% endif %}
|
||||
<a href="{{ action.url }}" target="_blank" class="{% if action.eventCategory|default(false) is empty %}action-list-url{# don't put URL on new line for events #}{% endif %} truncated-text-line"
|
||||
{% if overrideLinkStyle is not defined or overrideLinkStyle %}style="{% if action.type=='action' and action.pageTitle|default(false) is not empty %}margin-left: 9px;{% endif %}text-decoration:underline;"{% endif %}>
|
||||
{% if action.eventCategory|default(false) is not empty %}
|
||||
(url)
|
||||
{% else %}
|
||||
{{ action.url }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{% if action.type == 'action' and action.pageTitle|default(false) is not empty %}</p>{% endif %}
|
||||
{% elseif action.type != 'search' and action.type != 'event' %}
|
||||
<p>
|
||||
<span style="margin-left: 9px;">{{ clientSideParameters.pageUrlNotDefined }}</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{# Goal conversion #}
|
||||
<img src="{{ action.icon }}" />
|
||||
<strong>{{ action.goalName }}</strong>
|
||||
{% if action.revenue > 0 %}, {{ 'General_ColumnRevenue'|translate }}:
|
||||
<strong>{{ action.revenue|money(clientSideParameters.idSite)|raw }}</strong>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
{% set displayVisitorsInOwnColumn = (isWidget ? false : true) %}
|
||||
{% set displayReferrersInOwnColumn = (clientSideParameters.smallWidth ? false : true) %}
|
||||
<table class="dataTable" cellspacing="0" width="100%" style="width:100%;table-layout:fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="display:none;"></th>
|
||||
<th id="label" class="sortable label" style="cursor: auto;width:190px;" width="190px">
|
||||
<div id="thDIV">{{ 'General_Date'|translate }}</div>
|
||||
</th>
|
||||
{% if displayVisitorsInOwnColumn %}
|
||||
<th id="label" class="sortable label" style="cursor: auto;width:225px;" width="225px">
|
||||
<div id="thDIV">{{ 'General_Visitors'|translate }}</div>
|
||||
</th>
|
||||
{% endif %}
|
||||
{% if displayReferrersInOwnColumn %}
|
||||
<th id="label" class="sortable label" style="cursor: auto;width:230px;" width="230px">
|
||||
<div id="thDIV">{{ 'Live_Referrer_URL'|translate }}</div>
|
||||
</th>
|
||||
{% endif %}
|
||||
<th id="label" class="sortable label" style="cursor: auto;">
|
||||
<div id="thDIV">{{ 'General_ColumnNbActions'|translate }}</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set cycleIndex=0 %}
|
||||
{% for visitor in dataTable.getRows() %}
|
||||
{% set breakBeforeVisitorRank = (visitor.getColumn('visitEcommerceStatusIcon') and visitor.getColumn('visitorTypeIcon')) ? true : false %}
|
||||
{% set visitorColumnContent %}
|
||||
<img src="{{ visitor.getColumn('countryFlag') }}" title="{{ visitor.getColumn('location') }}, Provider {{ visitor.getColumn('providerName') }}"/>
|
||||
|
||||
{% if visitor.getColumn('plugins') %}
|
||||
<img src="{{ visitor.getColumn('browserIcon') }}" title="{{ 'UserSettings_BrowserWithPluginsEnabled'|translate(visitor.getColumn('browserName'),visitor.getColumn('plugins')) }}"/>
|
||||
{% else %}
|
||||
<img src="{{ visitor.getColumn('browserIcon') }}" title="{{ 'UserSettings_BrowserWithNoPluginsEnabled'|translate(visitor.getColumn('browserName')) }}"/>
|
||||
{% endif %}
|
||||
|
||||
<img src="{{ visitor.getColumn('operatingSystemIcon') }}"
|
||||
title="{{ visitor.getColumn('operatingSystem') }}, {{ visitor.getColumn('resolution') }} ({{ visitor.getColumn('screenType') }})"/>
|
||||
{% if visitor.getColumn('visitorTypeIcon') %}
|
||||
-
|
||||
<img src="{{ visitor.getColumn('visitorTypeIcon') }}"
|
||||
title="{{ 'General_ReturningVisitor'|translate }} - {{ 'General_NVisits'|translate(visitor.getColumn('visitCount')) }}"/>
|
||||
{% endif %}
|
||||
{% if not displayVisitorsInOwnColumn or breakBeforeVisitorRank %}<br/><br />{% endif %}
|
||||
{% if visitor.getColumn('visitConverted') %}
|
||||
<span title="{{ 'General_VisitConvertedNGoals'|translate(visitor.getColumn('goalConversions')) }}" class='visitorRank'
|
||||
{% if not displayVisitorsInOwnColumn or breakBeforeVisitorRank %}style="margin-left:0;"{% endif %}>
|
||||
<img src="{{ visitor.getColumn('visitConvertedIcon') }}"/>
|
||||
<span class='hash'>#</span>
|
||||
{{ visitor.getColumn('goalConversions') }}
|
||||
{% if visitor.getColumn('visitEcommerceStatusIcon') %}
|
||||
-
|
||||
<img src="{{ visitor.getColumn('visitEcommerceStatusIcon') }}" title="{{ visitor.getColumn('visitEcommerceStatus') }}"/>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if not displayVisitorsInOwnColumn %}<br/><br />{% endif %}
|
||||
{% if displayVisitorsInOwnColumn %}
|
||||
{% if visitor.getColumn('pluginsIcons')|length > 0 %}
|
||||
<hr/>
|
||||
{{ 'General_Plugins'|translate }}:
|
||||
{% for pluginIcon in visitor.getColumn('pluginsIcons') %}
|
||||
<img src="{{ pluginIcon.pluginIcon }}" title="{{ pluginIcon.pluginName|capitalize(true) }}" alt="{{ pluginIcon.pluginName|capitalize(true) }}"/>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{% set referrerColumnContent %}
|
||||
<div class="referrer">
|
||||
{% if visitor.getColumn('referrerType') == 'website' %}
|
||||
{{ 'Referrers_ColumnWebsite'|translate }}:
|
||||
<a href="{{ visitor.getColumn('referrerUrl') }}" target="_blank" title="{{ visitor.getColumn('referrerUrl') }}"
|
||||
style="text-decoration:underline;">
|
||||
{{ visitor.getColumn('referrerName') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if visitor.getColumn('referrerType') == 'campaign' %}
|
||||
{{ 'Referrers_ColumnCampaign'|translate }}
|
||||
<br/>
|
||||
{{ visitor.getColumn('referrerName') }}
|
||||
{% if visitor.getColumn('referrerKeyword') is not empty %} - {{ visitor.getColumn('referrerKeyword') }}{% endif %}
|
||||
{% endif %}
|
||||
{% if visitor.getColumn('referrerType') == 'search' %}
|
||||
{%- set keywordNotDefined = 'General_NotDefined'|translate('General_ColumnKeyword'|translate) -%}
|
||||
{%- set showKeyword = visitor.getColumn('referrerKeyword') is not empty and visitor.getColumn('referrerKeyword') != keywordNotDefined -%}
|
||||
{% if visitor.getColumn('searchEngineIcon') %}
|
||||
<img src="{{ visitor.getColumn('searchEngineIcon') }}" alt="{{ visitor.getColumn('referrerName') }}"/>
|
||||
{% endif %}
|
||||
<span {% if not showKeyword %}title="{{ keywordNotDefined }}"{% endif %}>{{ visitor.getColumn('referrerName') }}</span>
|
||||
{% if showKeyword %}{{ 'Referrers_Keywords'|translate }}:
|
||||
<br/>
|
||||
<a href="{{ visitor.getColumn('referrerUrl') }}" target="_blank" style="text-decoration:underline;">
|
||||
"{{ visitor.getColumn('referrerKeyword') }}"</a>
|
||||
{% endif %}
|
||||
{% set keyword %}{{ visitor.getColumn('referrerKeyword') }}{% endset %}
|
||||
{% set searchName %}{{ visitor.getColumn('referrerName') }}{% endset %}
|
||||
{% set position %}#{{ visitor.getColumn('referrerKeywordPosition') }}{% endset %}
|
||||
{% if visitor.getColumn('referrerKeywordPosition') %}
|
||||
<span title='{{ 'Live_KeywordRankedOnSearchResultForThisVisitor'|translate(keyword,position,searchName) }}' class='visitorRank'>
|
||||
<span class='hash'>#</span>
|
||||
{{ visitor.getColumn('referrerKeywordPosition') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if visitor.getColumn('referrerType') == 'direct' %}{{ 'Referrers_DirectEntry'|translate }}{% endif %}
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
|
||||
{% set visitorRow %}
|
||||
<tr class="label{{ cycle(['odd','even'], cycleIndex) }}">
|
||||
{% set cycleIndex=cycleIndex+1 %}
|
||||
<td style="display:none;"></td>
|
||||
<td class="label">
|
||||
<strong title="{% if visitor.getColumn('visitorType')=='new' %}{{ 'General_NewVisitor'|translate }}{% else %}{{ 'Live_VisitorsLastVisit'|translate(visitor.getColumn('daysSinceLastVisit')) }}{% endif %}">
|
||||
{{ visitor.getColumn('serverDatePrettyFirstAction') }}
|
||||
{% if isWidget %}<br/>{% else %}-{% endif %} {{ visitor.getColumn('serverTimePrettyFirstAction') }}</strong>
|
||||
{% if visitor.getColumn('visitIp') is not empty %}
|
||||
<br/>
|
||||
<span title="{% if visitor.getColumn('visitorId') is not empty %}{{ 'General_VisitorID'|translate }}: {{ visitor.getColumn('visitorId') }}{% endif -%}
|
||||
{%- if visitor.getColumn('latitude') or visitor.getColumn('longitude') %}
|
||||
|
||||
{{ visitor.getColumn('location') }}
|
||||
|
||||
GPS (lat/long): {{ visitor.getColumn('latitude') }},{{ visitor.getColumn('longitude') }}{% endif %}">
|
||||
IP: {{ visitor.getColumn('visitIp') }}</span>{% endif %}
|
||||
|
||||
{% if visitor.getColumn('provider') and visitor.getColumn('providerName')!='IP' %}
|
||||
<br/>
|
||||
{{ 'Provider_ColumnProvider'|translate }}:
|
||||
<a href="{{ visitor.getColumn('providerUrl') }}" target="_blank" title="{{ visitor.getColumn('providerUrl') }}" style="text-decoration:underline;">
|
||||
{{ visitor.getColumn('providerName') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if visitor.getColumn('customVariables') %}
|
||||
<br/>
|
||||
{% for id,customVariable in visitor.getColumn('customVariables') %}
|
||||
{% set name='customVariableName' ~ id %}
|
||||
{% set value='customVariableValue' ~ id %}
|
||||
<br/>
|
||||
<acronym title="{{ 'CustomVariables_CustomVariables'|translate }} (index {{ id }})">
|
||||
{{ customVariable[name]|truncate(30) }}
|
||||
</acronym>
|
||||
{% if customVariable[value]|length > 0 %}: {{ customVariable[value]|truncate(50) }}{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if not displayVisitorsInOwnColumn %}
|
||||
<br/>
|
||||
{{ visitorColumnContent }}
|
||||
{% endif %}
|
||||
{% if not displayReferrersInOwnColumn %}
|
||||
<br/>
|
||||
{{ referrerColumnContent }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% if displayVisitorsInOwnColumn %}
|
||||
<td class="label">
|
||||
{{ visitorColumnContent }}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
{% if displayReferrersInOwnColumn %}
|
||||
<td class="column">
|
||||
{{ referrerColumnContent }}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
<td class="column {% if visitor.getColumn('visitConverted') and not isWidget %}highlightField{% endif %}">
|
||||
<div class="visitor-log-page-list">
|
||||
{% if visitor.getColumn('visitorId') is not empty %}
|
||||
<a class="visitor-log-visitor-profile-link" title="{{ 'Live_ViewVisitorProfile'|translate }}" data-visitor-id="{{ visitor.getColumn("visitorId") }}">
|
||||
<img src="plugins/Live/images/visitorProfileLaunch.png"/> <span>{{ 'Live_ViewVisitorProfile'|translate }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<strong>
|
||||
{{ visitor.getColumn('actionDetails')|length }}
|
||||
{% if visitor.getColumn('actionDetails')|length <= 1 %}
|
||||
{{ 'General_Action'|translate }}
|
||||
{% else %}
|
||||
{{ 'General_Actions'|translate }}
|
||||
{% endif %}
|
||||
{% if visitor.getColumn('visitDuration') > 0 %}- {{ visitor.getColumn('visitDurationPretty')|raw }}{% endif %}
|
||||
</strong>
|
||||
<br/>
|
||||
<ol class='visitorLog'>
|
||||
{% include "@Live/_actionsList.twig" with {'actionDetails': visitor.getColumn('actionDetails')} %}
|
||||
</ol>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endset %}
|
||||
|
||||
{% if not clientSideParameters.filterEcommerce or visitor.getMetadata('hasEcommerce') %}
|
||||
{{ visitorRow }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
29
www/analytics/plugins/Live/templates/_totalVisitors.twig
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<div id="visitsTotal">
|
||||
<table class="dataTable" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="label" class="sortable label" style="cursor: auto;">
|
||||
<div id="thDIV">{{ 'General_Date'|translate }}</div>
|
||||
</th>
|
||||
<th id="label" class="sortable label" style="cursor: auto;">
|
||||
<div id="thDIV">{{ 'General_ColumnNbVisits'|translate }}</div>
|
||||
</th>
|
||||
<th id="label" class="sortable label" style="cursor: auto;">
|
||||
<div id="thDIV">{{ 'General_ColumnPageviews'|translate }}</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="">
|
||||
<td class="column columnodd">{{ 'Live_LastHours'|translate(24) }}</td>
|
||||
<td class="column columnodd">{{ visitorsCountToday }}</td>
|
||||
<td class="column columnodd">{{ pisToday }}</td>
|
||||
</tr>
|
||||
<tr class="">
|
||||
<td class="column columnodd">{{ 'Live_LastMinutes'|translate(30) }}</td>
|
||||
<td class="column columnodd">{{ visitorsCountHalfHour }}</td>
|
||||
<td class="column columnodd">{{ pisHalfhour }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -0,0 +1 @@
|
|||
{% include "@Live/_totalVisitors.twig" %}
|
||||
144
www/analytics/plugins/Live/templates/getLastVisitsStart.twig
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
{# some users view thousands of pages which can crash the browser viewing Live! #}
|
||||
{% set maxPagesDisplayedByVisitor=100 %}
|
||||
|
||||
<ul id='visitsLive'>
|
||||
{% for visitor in visitors %}
|
||||
<li id="{{ visitor.idVisit }}" class="visit">
|
||||
<div style="display:none;" class="idvisit">{{ visitor.idVisit }}</div>
|
||||
<div title="{{ visitor.actionDetails|length }} {{ 'General_Actions'|translate }}" class="datetime">
|
||||
<span style="display:none;" class="serverTimestamp">{{ visitor.serverTimestamp|raw }}</span>
|
||||
{{ visitor.serverDatePretty }} - {{ visitor.serverTimePretty }} {% if visitor.visitDuration > 0 %}<em>({{ visitor.visitDurationPretty|raw }})</em>{% endif %}
|
||||
<img src="{{ visitor.countryFlag }}" title="{{ visitor.location }}, {{ 'Provider_ColumnProvider'|translate }} {{ visitor.providerName }}"/>
|
||||
<img src="{{ visitor.browserIcon }}" title="{{ visitor.browserName }}, {{ 'General_Plugins'|translate }}: {{ visitor.plugins }}"/>
|
||||
<img src="{{ visitor.operatingSystemIcon }}" title="{{ visitor.operatingSystem }}, {{ visitor.resolution }}"/>
|
||||
|
||||
{% if visitor.visitConverted %}
|
||||
<span title="{{ 'General_VisitConvertedNGoals'|translate(visitor.goalConversions) }}" class='visitorRank'>
|
||||
<img src="{{ visitor.visitConvertedIcon }}" />
|
||||
<span class='hash'>#</span>
|
||||
{{ visitor.goalConversions }}
|
||||
{% if visitor.visitEcommerceStatusIcon %}
|
||||
-
|
||||
<img src="{{ visitor.visitEcommerceStatusIcon }}" title="{{ visitor.visitEcommerceStatus }}"/>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if visitor.visitorTypeIcon %}
|
||||
- <img src="{{ visitor.visitorTypeIcon }}" title="{{ 'General_ReturningVisitor'|translate }}"/>
|
||||
{% endif %}
|
||||
{% if visitor.visitorId|default(false) is not empty %}
|
||||
<a class="visits-live-launch-visitor-profile rightLink" title="{{ 'Live_ViewVisitorProfile'|translate }}" data-visitor-id="{{ visitor.visitorId }}">
|
||||
<img src="plugins/Live/images/visitorProfileLaunch.png"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if visitor.visitIp %}- <span title="{% if visitor.visitorId is not empty %}{{ 'General_VisitorID'|translate }}: {{ visitor.visitorId }}{% endif %}">
|
||||
IP: {{ visitor.visitIp }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!--<div class="settings"></div>-->
|
||||
<div class="referrer">
|
||||
{% if visitor.referrerType != 'direct' %}
|
||||
{{ 'General_FromReferrer'|translate }}
|
||||
{% if visitor.referrerUrl is not empty %}
|
||||
<a href="{{ visitor.referrerUrl }}" target="_blank">
|
||||
{% endif %}
|
||||
{% if visitor.searchEngineIcon is defined %}
|
||||
<img src="{{ visitor.searchEngineIcon }}" />
|
||||
{% endif %}
|
||||
{{ visitor.referrerName }}
|
||||
{% if visitor.referrerUrl is not empty %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if visitor.referrerKeyword is not empty %} - "{{ visitor.referrerKeyword }}"{% endif %}
|
||||
{% set keyword %}{{ visitor.referrerKeyword }}{% endset %}
|
||||
{% set searchName %}{{ visitor.referrerName }}{% endset %}
|
||||
{% set position %}#{{ visitor.referrerKeywordPosition}}{% endset %}
|
||||
{% if visitor.referrerKeywordPosition is not empty %}
|
||||
<span title='{{ 'Live_KeywordRankedOnSearchResultForThisVisitor'|translate(keyword,position,searchName) }}' class='visitorRank'>
|
||||
<span class='hash'>#</span> {{ visitor.referrerKeywordPosition }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ 'Referrers_DirectEntry'|translate }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="{{ visitor.idVisit }}_actions" class="settings">
|
||||
<span class="pagesTitle" title="{{ visitor.actionDetails|length }} {{ 'General_Actions'|translate }}">{{ 'General_Pages'|translate }}:</span>
|
||||
{% set col = 0 %}
|
||||
{% for action in visitor.actionDetails %}
|
||||
{% if loop.index <= maxPagesDisplayedByVisitor %}
|
||||
{% if action.type == 'ecommerceOrder' or action.type == 'ecommerceAbandonedCart' %}
|
||||
{% set title %}
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
{{ 'Goals_EcommerceOrder'|translate }}
|
||||
{% else %}
|
||||
{{ 'Goals_AbandonedCart'|translate }}
|
||||
{% endif %}
|
||||
-
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
{{ 'General_ColumnRevenue'|translate }}:
|
||||
{% else %}
|
||||
{% set revenueLeft %}
|
||||
{{ 'General_ColumnRevenue'|translate }}
|
||||
{% endset %}
|
||||
{{ 'Goals_LeftInCart'|translate(revenueLeft) }}:
|
||||
{% endif %}
|
||||
{{ action.revenue|money(idSite)|raw }} - {{ action.serverTimePretty }}
|
||||
{% if action.itemDetails is not empty %}
|
||||
{% for product in action.itemDetails %}
|
||||
# {{ product.itemSKU }}{% if product.itemName is not empty %}: {{ product.itemName }}{% endif %}{% if product.itemCategory is not empty %} ({{ product.itemCategory }}){% endif %}, {{ 'General_Quantity'|translate }}: {{ product.quantity }}, {{ 'General_Price'|translate }}: {{ product.price|money(idSite)|raw }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
<span title="{{ title }}">
|
||||
<img class='iconPadding' src="{{ action.icon }}"/>
|
||||
{% if action.type == 'ecommerceOrder' %}
|
||||
{{ 'General_ColumnRevenue'|translate }}: {{ action.revenue|money(idSite)|raw }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
{% set col = col + 1 %}
|
||||
{% if col >= 9 %}
|
||||
{% set col = 0 %}
|
||||
{% endif %}
|
||||
<a href="{{ action.url }}" target="_blank">
|
||||
{% if action.type == 'action' %}
|
||||
{# white spacing matters as Chrome tooltip display whitespaces #}
|
||||
{% set title %}
|
||||
{% if action.pageTitle is not empty %}{{ action.pageTitle }}{% endif %}
|
||||
|
||||
{{ action.serverTimePretty }}
|
||||
{% if action.timeSpentPretty is defined %}{{ 'General_TimeOnPage'|translate }}: {{ action.timeSpentPretty|raw }}{% endif %}
|
||||
{%- endset %}
|
||||
<img src="plugins/Live/images/file{{ col }}.png" title="{{- title -}}"/>
|
||||
{% elseif action.type == 'outlink' or action.type == 'download' %}
|
||||
<img class='iconPadding' src="{{ action.icon }}"
|
||||
title="{{ action.url }} - {{ action.serverTimePretty }}"/>
|
||||
{% elseif action.type == 'search' %}
|
||||
<img class='iconPadding' src="{{ action.icon }}"
|
||||
title="{{ 'Actions_SubmenuSitesearch'|translate }}: {{ action.siteSearchKeyword }} - {{ action.serverTimePretty }}"/>
|
||||
{% elseif action.eventCategory|default(false) is not empty %}
|
||||
<img class="iconPadding" src='{{ action.icon }}'
|
||||
title="{{ 'Events_Event'|translate }} {{ action.eventCategory }} - {{ action.eventAction }} {% if action.eventName is defined %}- {{ action.eventName }}{% endif %} {% if action.eventValue is defined %}- {{ action.eventValue }}{% endif %}"/>
|
||||
{% else %}
|
||||
<img class='iconPadding' src="{{ action.icon }}"
|
||||
title="{{ action.goalName }} - {% if action.revenue > 0 %}{{ 'General_ColumnRevenue'|translate }}: {{ action.revenue|money(idSite)|raw }} - {% endif %} {{ action.serverTimePretty }}"/>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if visitor.actionDetails|length > maxPagesDisplayedByVisitor %}
|
||||
<em>({{ 'Live_MorePagesNotDisplayed'|translate }})</em>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<script type="text/javascript">
|
||||
$('#visitsLive').on('click', '.visits-live-launch-visitor-profile', function (e) {
|
||||
e.preventDefault();
|
||||
broadcast.propagateNewPopoverParameter('visitorProfile', $(this).attr('data-visitor-id'));
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<div class='simple-realtime-visitor-widget' data-refreshAfterXSecs="{{ refreshAfterXSecs }}" data-last-minutes="{{ lastMinutes }}" data-translations="{{ translations|json_encode }}">
|
||||
<div class='simple-realtime-visitor-counter' title="{% if visitors == 1 %}{{ 'Live_NbVisitor'|translate }}{% else %}{{ 'Live_NbVisitors'|translate(visitors) }}{% endif %}">
|
||||
<div>{{ visitors }}</div>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div class='simple-realtime-elaboration'>
|
||||
{% set visitsMessage %}
|
||||
<span class="simple-realtime-metric" data-metric="visits">{% if visits == 1 %}{{ 'General_OneVisit'|translate }}{% else %}{{ 'General_NVisits'|translate(visits) }}{% endif %}</span>
|
||||
{% endset %}
|
||||
{% set actionsMessage %}
|
||||
<span class="simple-realtime-metric" data-metric="actions">{% if actions == 1 %}{{ 'General_OneAction'|translate }}{% else %}{{ 'VisitsSummary_NbActionsDescription'|translate(actions) }}{% endif %}</span>
|
||||
{% endset %}
|
||||
{% set minutesMessage %}
|
||||
<span class="simple-realtime-metric" data-metric="minutes">{% if lastMinutes == 1 %}{{ 'General_OneMinute'|translate }}{% else %}{{ 'General_NMinutes'|translate(lastMinutes) }}{% endif %}</span>
|
||||
{% endset %}
|
||||
|
||||
{{ 'Live_SimpleRealTimeWidget_Message'|translate(visitsMessage,actionsMessage,minutesMessage) | raw }}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">$(document).ready(function () {require('piwik/Live').initSimpleRealtimeVisitorWidget();});</script>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
{% macro customVar(id, customVariable) %}
|
||||
{% set name='customVariableName' ~ id %}
|
||||
{% set value='customVariableValue' ~ id %}
|
||||
<li><span>{{ customVariable[name]|truncate(30) }}</span>{% if customVariable[value]|length > 0 %}<strong>{{ customVariable[value]|truncate(50) }}</strong>{% endif %}</li>
|
||||
{% endmacro %}
|
||||
{% import _self as macros %}
|
||||
{% if showLocation|default(false) %}
|
||||
<div class="visitor-profile-latest-visit-loc" title="{{ visitData.location }}">
|
||||
<img src="{{ visitData.countryFlag }}"/> <span>{% if visitData.city is not empty %}{{ visitData.city }}{% else %}{{ visitData.country }}{% endif %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="visitor-profile-latest-visit-column">
|
||||
<ul>
|
||||
<li>
|
||||
<span>{{ 'General_IP'|translate }}</span><strong {% if visitData.providerName is not empty %}title="{{ 'Provider_ColumnProvider'|translate }}: {{ visitData.providerName }}"{% endif %}>{{ visitData.visitIp }}</strong>
|
||||
</li>
|
||||
<li class="visitor-profile-id">
|
||||
<span>{{ 'General_Id'|translate|upper }}</span>
|
||||
{% if widgetizedLink is defined %}<a class="visitor-profile-widget-link" href="{{ widgetizedLink }}" target="_blank" title="{{ 'Widgetize_OpenInNewWindow'|translate }} - {{ 'Live_VisitorProfile'|translate }} {{ 'General_Id'|translate|upper }} {{ visitData.visitorId }}">{% endif %}
|
||||
<strong>{{ visitData.visitorId }}</strong>
|
||||
{% if widgetizedLink is defined %}</a>{% endif %}
|
||||
<a class="visitor-profile-export" href="{{ exportLink }}" target="_blank" title="{{ 'General_ExportThisReport'|translate }}" style="visibility:hidden">
|
||||
<img src="plugins/Zeitgeist/images/export.png"/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="visitor-profile-browser" title="{% if visitData.plugins is defined %}{{ 'UserSettings_BrowserWithPluginsEnabled'|translate(visitData.browserName, visitData.plugins) }}{% else %}{{ 'UserSettings_BrowserWithNoPluginsEnabled'|translate(visitData.browserName) }}{% endif %}">
|
||||
<img src="{{ visitData.browserIcon }}"/><span>{{ visitData.browserName|split(' ')[0] }}</span>
|
||||
</div>
|
||||
<div class="visitor-profile-os">
|
||||
<img src="{{ visitData.operatingSystemIcon }}"/><span>{{ visitData.operatingSystemShortName }}</span>
|
||||
</div>
|
||||
</li>
|
||||
<li><span>{{ 'UserSettings_ColumnResolution'|translate }}</span><strong>{{ visitData.resolution }}</strong></li>
|
||||
{% if visitReferralSummary is defined %}
|
||||
{%- set keywordNotDefined = 'General_NotDefined'|translate('General_ColumnKeyword'|translate) -%}
|
||||
<li>
|
||||
<span>{{ 'General_DateRangeFrom'|translate }}</span>
|
||||
<strong {% if visitData.referrerType == 'search' and '(' not in visitReferralSummary %}title="{{ keywordNotDefined }}"{% endif %}>{{ visitReferralSummary }}</strong>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="visitor-profile-latest-visit-column">
|
||||
<ul>
|
||||
{% for id,customVariable in visitData.customVariables %}
|
||||
{% if loop.index0 < 4 %}
|
||||
{{ macros.customVar(id, customVariable) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if visitData.customVariables|length > 4 %}
|
||||
<ul class="visitor-profile-extra-cvars" style="display:none;">
|
||||
{% for id,customVariable in visitData.customVariables %}
|
||||
{% if loop.index0 >= 4 %}
|
||||
{{ macros.customVar(id, customVariable) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p class="visitor-profile-see-more-cvars"><a href="#">▼</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
17
www/analytics/plugins/Live/templates/getVisitList.twig
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{% for visitInfo in visits.getRows() %}
|
||||
<li>
|
||||
<div>
|
||||
<div class="visitor-profile-visit-title-row"><h2 class="visitor-profile-visit-title" data-idvisit="{{ visitInfo.getColumn('idVisit') }}" title="{{ 'Live_ClickToViewMoreAboutVisit'|translate }}">{{ 'General_Visit'|translate }} #{{ startCounter }}</h2>{% if visitInfo.getColumn('visitDuration') != 0 %}<span> - ({{ visitInfo.getColumn('visitDurationPretty')|raw }})</span>{% endif %}<span class="visitor-profile-date" title="{{ visitInfo.getColumn('serverDateTimePrettyFirstAction') }}">{{ visitInfo.getColumn('serverDatePrettyFirstAction') }}</span></div>
|
||||
<ol class="visitor-profile-actions">
|
||||
{% include "@Live/_actionsList.twig" with {'actionDetails': visitInfo.getColumn('actionDetails'),
|
||||
'clientSideParameters': {
|
||||
'filterEcommerce': false,
|
||||
'idSite': idSite,
|
||||
'pageUrlNotDefined': 'General_NotDefined'|translate('Actions_ColumnPageURL'|translate)
|
||||
},
|
||||
'overrideLinkStyle': false} %}
|
||||
</ol>
|
||||
</div>
|
||||
</li>
|
||||
{% set startCounter = startCounter + 1 %}
|
||||
{% endfor %}
|
||||
149
www/analytics/plugins/Live/templates/getVisitorProfilePopup.twig
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<div class="visitor-profile"
|
||||
data-visitor-id="{{ visitorData.lastVisits.getFirstRow().getColumn('visitorId') }}"
|
||||
data-next-visitor="{{ visitorData.nextVisitorId }}"
|
||||
data-prev-visitor="{{ visitorData.previousVisitorId }}"
|
||||
tabindex="0">
|
||||
<a href class="visitor-profile-close"></a>
|
||||
<div class="visitor-profile-info">
|
||||
<div>
|
||||
<div class="visitor-profile-overview">
|
||||
<div class="visitor-profile-avatar">
|
||||
<div>
|
||||
<div class="visitor-profile-image-frame">
|
||||
<img src="{{ visitorData.visitorAvatar|default("plugins/Live/images/unknown_avatar.jpg") }}"
|
||||
alt="{{ visitorData.visitorDescription|default('') }}"/>
|
||||
</div>
|
||||
<img src="plugins/Live/images/paperclip.png" alt=""/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="visitor-profile-header">
|
||||
{% if visitorData.previousVisitorId is not empty %}<a class="visitor-profile-prev-visitor" href="#" title="{{ 'Live_PreviousVisitor'|translate }}">←</a>{% endif %}
|
||||
<h1>{{ 'Live_VisitorProfile'|translate }} <img class="loadingPiwik" style="display:none;" src="plugins/Zeitgeist/images/loading-blue.gif"/></h1>
|
||||
<a href="http://piwik.org/docs/user-profile/" class="reportDocumentationIcon" target="_blank" title="{{ 'General_ViewDocumentationFor'|translate("Live_VisitorProfile"|translate|ucwords) }}"></a>
|
||||
{% if visitorData.nextVisitorId is not empty %}<a class="visitor-profile-next-visitor" href="#" title="{{ 'Live_NextVisitor'|translate }}">→</a>{% endif %}
|
||||
</div>
|
||||
<div class="visitor-profile-latest-visit">
|
||||
{% include "@Live/getSingleVisitSummary.twig" with {'visitData': visitorData.lastVisits.getFirstRow().getColumns(), 'showLocation': false} %}
|
||||
</div>
|
||||
</div>
|
||||
<p style="clear:both; border:none!important;"></p>
|
||||
</div>
|
||||
<div class="visitor-profile-summary">
|
||||
<h1>{{ 'General_Summary'|translate }}</h1>
|
||||
<div>
|
||||
<p>{{ 'Live_VisitSummary'|translate('<strong>' ~ visitorData.totalVisitDurationPretty ~ '</strong>', '', '', '<strong>', visitorData.totalActions, visitorData.totalVisits, '</strong>')|raw }}</p>
|
||||
<p>{% if visitorData.totalGoalConversions %}<strong>{% endif %}{{ 'Live_ConvertedNGoals'|translate(visitorData.totalGoalConversions) }}{% if visitorData.totalGoalConversions %}</strong>{% endif %}
|
||||
{%- if visitorData.totalGoalConversions %} (
|
||||
{%- for idGoal, totalConversions in visitorData.totalConversionsByGoal -%}
|
||||
{%- set idGoal = idGoal[7:] -%}
|
||||
{%- if not loop.first %}, {% endif -%}{{- totalConversions }} <span class="visitor-profile-goal-name">{{ goals[idGoal]['name'] -}}</span>
|
||||
{%- endfor -%}
|
||||
){% endif %}.</p>
|
||||
{% if visitorData.totalEcommerceConversions|default(0) > 0 or visitorData.totalAbandonedCarts|default(0) > 0%}
|
||||
<p>
|
||||
{{ 'Goals_Ecommerce'|translate }}:
|
||||
{%- if visitorData.totalEcommerceConversions|default(0) > 0 %} {{ 'Live_EcommerceSummaryConversions'|translate('<strong>', visitorData.totalEcommerceConversions, visitorData.totalEcommerceRevenue|money(idSite), '</strong>', visitorData.totalEcommerceItems)|raw }}
|
||||
{%- endif -%}
|
||||
{%- if visitorData.totalAbandonedCarts|default(0) > 0 %} {{ 'Live_AbandonedCartSummary'|translate('<strong>', visitorData.totalAbandonedCarts, '</strong>', visitorData.totalAbandonedCartsItems, '<strong>', visitorData.totalAbandonedCartsRevenue|money(idSite), '</strong>')|raw }}{%- endif -%}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if visitorData.totalSearches|default(0) %}
|
||||
<p>
|
||||
{{ 'Actions_WidgetSearchKeywords'|translate }}:
|
||||
{%- for entry in visitorData.searches %} <strong title="{% if entry.searches == 1 %}{{ 'Actions_OneSearch'|translate }}{% else %}{{ 'UserCountryMap_Searches'|translate(entry.searches) }}{% endif %}">{{ entry.keyword }}</strong>{% if not loop.last %},{% endif %}{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if visitorData.averagePageGenerationTime is defined %}
|
||||
<p title="{{ 'Live_CalculatedOverNPageViews'|translate(visitorData.totalPageViews) }}">
|
||||
{{ 'Live_AveragePageGenerationTime'|translate('<strong>' ~ visitorData.averagePageGenerationTime ~ 's</strong>')|raw }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="visitor-profile-important-visits">
|
||||
{%- set keywordNotDefined = 'General_NotDefined'|translate('General_ColumnKeyword'|translate) -%}
|
||||
<div>
|
||||
<h1>{% if visitorData.visitsAggregated == 100 %}{{ 'General_Visit'|translate }}# 100{% else %}{{ 'Live_FirstVisit'|translate }}{% endif %}</h1>
|
||||
<div>
|
||||
<p><strong>{{ visitorData.firstVisit.prettyDate }}</strong><span> - {{ 'UserCountryMap_DaysAgo'|translate(visitorData.firstVisit.daysAgo) }}</span></p>
|
||||
<p><span>{{ 'General_FromReferrer'|translate }}:</span>
|
||||
<strong {% if visitorData.firstVisit.referrerType == 'search' and '(' not in visitorData.firstVisit.referralSummary %}title="{{ keywordNotDefined }}"{% endif %}>{{ visitorData.firstVisit.referralSummary }}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
{% if visitorData.lastVisits.getRowsCount() != 1 %}
|
||||
<div>
|
||||
<h1>{{ 'Live_LastVisit'|translate }}</h1>
|
||||
<div>
|
||||
<p><strong>{{ visitorData.lastVisit.prettyDate }}</strong><span> - {{ 'UserCountryMap_DaysAgo'|translate(visitorData.lastVisit.daysAgo) }}</span></p>
|
||||
<p><span>{{ 'General_FromReferrer'|translate }}:</span>
|
||||
<strong {% if visitorData.lastVisit.referrerType == 'search' and '(' not in visitorData.lastVisit.referralSummary %}title="{{ keywordNotDefined }}"{% endif %}>{{ visitorData.lastVisit.referralSummary }}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="visitor-profile-location">
|
||||
<h1>{{ 'UserCountry_Location'|translate }}</h1>
|
||||
<p>
|
||||
{%- for entry in visitorData.countries -%}
|
||||
|
||||
{% set entryCity -%}
|
||||
{% if entry.cities is defined and 1 == entry.cities|length and entry.cities|join -%}
|
||||
{{ entry.cities|join }}
|
||||
{%- elseif entry.cities is defined and 1 < entry.cities|length -%}
|
||||
<span title="{{ entry.cities|join(', ') }}">{{ 'UserCountry_FromDifferentCities'|translate }}</span>
|
||||
{%- endif %}
|
||||
{%- endset %}
|
||||
|
||||
{% set entryVisits -%}
|
||||
<strong>
|
||||
{% if entry.nb_visits == 1 -%}
|
||||
{{ 'General_OneVisit'|translate }}
|
||||
{%- else -%}
|
||||
{{ 'General_NVisits'|translate(entry.nb_visits) }}
|
||||
{%- endif -%}
|
||||
</strong>
|
||||
{%- endset %}
|
||||
|
||||
{% set entryCountry -%}
|
||||
{%- if entryCity -%}
|
||||
{{ 'UserCountry_CityAndCountry'|translate(entryCity, entry.prettyName)|raw }}
|
||||
{%- else -%}
|
||||
{{ entry.prettyName }}
|
||||
{%- endif -%}
|
||||
|
||||
<img src="{{ entry.flag }}" title="{{ entry.prettyName }}"/>
|
||||
{%- endset %}
|
||||
|
||||
{{- 'General_XFromY'|translate(entryVisits, entryCountry)|raw -}}{% if not loop.last %}, {% endif %}
|
||||
{%- endfor %}
|
||||
<a class="visitor-profile-show-map" href="#" {% if userCountryMapUrl|default('') is empty %}style="display:none"{% endif %}>({{ 'Live_ShowMap'|translate|replace({' ': ' '})|raw }})</a> <img class="loadingPiwik" style="display:none;" src="plugins/Zeitgeist/images/loading-blue.gif"/>
|
||||
</p>
|
||||
<div class="visitor-profile-map" style="display:none" data-href="{{ userCountryMapUrl|default('') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visitor-profile-visits-info">
|
||||
<div class="visitor-profile-pages-visited">
|
||||
<h1>{{ 'Live_VisitedPages'|translate }}</h1>
|
||||
</div>
|
||||
<div class="visitor-profile-visits-container">
|
||||
<ol class="visitor-profile-visits">
|
||||
{% include "@Live/getVisitList.twig" with {'visits': visitorData.lastVisits, 'startCounter': 1} %}
|
||||
</ol>
|
||||
</div>
|
||||
<div class="visitor-profile-more-info">
|
||||
{% if visitorData.lastVisits.getRowsCount() >= constant("Piwik\\Plugins\\Live\\API::VISITOR_PROFILE_MAX_VISITS_TO_SHOW") %}
|
||||
<a href="#">{{ 'Live_LoadMoreVisits'|translate }}</a> <img class="loadingPiwik" style="display:none;" src="plugins/Zeitgeist/images/loading-blue.gif"/>
|
||||
{% else %}
|
||||
<span class="visitor-profile-no-visits">{{ 'Live_NoMoreVisits'|translate }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(function() { require('piwik/UI').VisitorProfileControl.initElements(); });
|
||||
</script>
|
||||
49
www/analytics/plugins/Live/templates/index.twig
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<script type="text/javascript" charset="utf-8">
|
||||
$(document).ready(function () {
|
||||
var segment = broadcast.getValueFromUrl('segment');
|
||||
|
||||
$('#visitsLive').liveWidget({
|
||||
interval: {{ liveRefreshAfterMs }},
|
||||
onUpdate: function () {
|
||||
//updates the numbers of total visits in startbox
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.addParams({
|
||||
module: 'Live',
|
||||
action: 'ajaxTotalVisitors',
|
||||
segment: segment
|
||||
}, 'GET');
|
||||
ajaxRequest.setCallback(function (r) {
|
||||
$("#visitsTotal").html(r);
|
||||
});
|
||||
ajaxRequest.send(false);
|
||||
},
|
||||
maxRows: 10,
|
||||
fadeInSpeed: 600,
|
||||
dataUrlParams: {
|
||||
module: 'Live',
|
||||
action: 'getLastVisitsStart',
|
||||
segment: segment
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% include "@Live/_totalVisitors.twig" %}
|
||||
|
||||
{{ visitors|raw }}
|
||||
|
||||
{% spaceless %}
|
||||
<div class="visitsLiveFooter">
|
||||
<a title="Pause Live!" href="javascript:void(0);" onclick="onClickPause();">
|
||||
<img id="pauseImage" border="0" src="plugins/Live/images/pause_disabled.gif" />
|
||||
</a>
|
||||
<a title="Start Live!" href="javascript:void(0);" onclick="onClickPlay();">
|
||||
<img id="playImage" border="0" src="plugins/Live/images/play.gif" />
|
||||
</a>
|
||||
{% if not disableLink %}
|
||||
|
||||
<a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<h2 piwik-enriched-headline>{% if filterEcommerce %}{{ 'Goals_EcommerceLog'|translate }}{% else %}{{ 'Live_VisitorLog'|translate }}{% endif %}</h2>
|
||||
|
||||
{{ visitorLog|raw }}
|
||||