update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -11,24 +11,24 @@ namespace Piwik\Plugins\Live;
use Exception;
use Piwik\Common;
use Piwik\Config;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Date;
use Piwik\Db;
use Piwik\MetricsFormatter;
use Piwik\Metrics\Formatter;
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;
use Psr\Log\LoggerInterface;
/**
* @see plugins/Live/Visitor.php
*/
require_once PIWIK_INCLUDE_PATH . '/plugins/Live/Visitor.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.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>,
@ -43,19 +43,27 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Live/Visitor.php';
* 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
* With the parameter <a href='http://piwik.org/docs/analytics-api/segmentation/' rel='noreferrer' 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.
* See also the documentation about <a href='http://piwik.org/docs/real-time/' rel='noreferrer' 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%';
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* This will return simple counters, for a given website ID, for visits over the last N minutes
@ -63,43 +71,60 @@ class API extends \Piwik\Plugin\API
* @param int $idSite Id Site
* @param int $lastMinutes Number of minutes to look back at
* @param bool|string $segment
* @param array $showColumns The columns to show / not to request. Eg 'visits', 'actions', ...
* @param array $hideColumns The columns to hide / not to request. Eg 'visits', 'actions', ...
* @return array( visits => N, actions => M, visitsConverted => P )
*/
public function getCounters($idSite, $lastMinutes, $segment = false)
public function getCounters($idSite, $lastMinutes, $segment = false, $showColumns = array(), $hideColumns = array())
{
Piwik::checkUserHasViewAccess($idSite);
$lastMinutes = (int)$lastMinutes;
$model = new Model();
$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";
$counters = array();
$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;
$hasVisits = true;
if ($this->shouldColumnBePresentInResponse('visits', $showColumns, $hideColumns)) {
$counters['visits'] = $model->getNumVisits($idSite, $lastMinutes, $segment);
$hasVisits = !empty($counters['visits']);
}
if (empty($data[0]['visitsConverted'])) {
$data[0]['visitsConverted'] = 0;
if ($this->shouldColumnBePresentInResponse('actions', $showColumns, $hideColumns)) {
if ($hasVisits) {
$counters['actions'] = $model->getNumActions($idSite, $lastMinutes, $segment);
} else {
$counters['actions'] = 0;
}
}
return $data;
if ($this->shouldColumnBePresentInResponse('visitors', $showColumns, $hideColumns)) {
if ($hasVisits) {
$counters['visitors'] = $model->getNumVisitors($idSite, $lastMinutes, $segment);
} else {
$counters['visitors'] = 0;
}
}
if ($this->shouldColumnBePresentInResponse('visitsConverted', $showColumns, $hideColumns)) {
if ($hasVisits) {
$counters['visitsConverted'] = $model->getNumVisitsConverted($idSite, $lastMinutes, $segment);
} else {
$counters['visitsConverted'] = 0;
}
}
return array($counters);
}
private function shouldColumnBePresentInResponse($column, $showColumns, $hideColumns)
{
$show = (empty($showColumns) || in_array($column, $showColumns));
$hide = in_array($column, $hideColumns);
return $show && !$hide;
}
/**
* The same functionnality can be obtained using segment=visitorId==$visitorId with getLastVisitsDetails
* The same functionality can be obtained using segment=visitorId==$visitorId with getLastVisitsDetails
*
* @deprecated
* @ignore
@ -114,9 +139,7 @@ class API extends \Piwik\Plugin\API
{
Piwik::checkUserHasViewAccess($idSite);
$countVisitorsToFetch = $filter_limit;
$table = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $countVisitorsToFetch, $visitorId);
$table = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $offset = 0, $filter_limit, $minTimestamp = false, $filterSortOrder = false, $visitorId);
$this->addFilterToCleanVisitors($table, $idSite, $flat);
return $table;
@ -130,7 +153,7 @@ class API extends \Piwik\Plugin\API
* @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 $countVisitorsToFetch DEPRECATED (optional) Only return the last X visits. Please use the API paramaeter 'filter_offset' and 'filter_limit' instead.
* @param bool|int $minTimestamp (optional) Minimum timestamp to restrict the query to (useful when paginating or refreshing visits)
* @param bool $flat
* @param bool $doNotFetchActions
@ -138,17 +161,34 @@ class API extends \Piwik\Plugin\API
*/
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');
Piwik::checkUserHasViewAccess($idSite);
$countVisitorsToFetch = $filter_limit + $filter_offset;
if ($countVisitorsToFetch !== false) {
$filterLimit = (int) $countVisitorsToFetch;
$filterOffset = 0;
} else {
$filterLimit = Common::getRequestVar('filter_limit', 10, 'int');
$filterOffset = Common::getRequestVar('filter_offset', 0, 'int');
}
Piwik::checkUserHasViewAccess($idSite);
$dataTable = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId = false, $minTimestamp);
$filterSortOrder = Common::getRequestVar('filter_sort_order', false, 'string');
$dataTable = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $filterOffset, $filterLimit, $minTimestamp, $filterSortOrder, $visitorId = false);
$this->addFilterToCleanVisitors($dataTable, $idSite, $flat, $doNotFetchActions);
$filterSortColumn = Common::getRequestVar('filter_sort_column', false, 'string');
if ($filterSortColumn) {
$this->logger->warning('Sorting the API method "Live.getLastVisitDetails" by column is currently not supported. To avoid this warning remove the URL parameter "filter_sort_column" from your API request.');
}
// Usually one would Sort a DataTable and then apply a Limit. In this case we apply a Limit first in SQL
// for fast offset usage see https://github.com/piwik/piwik/issues/7458. Sorting afterwards would lead to a
// wrong sorting result as it would only sort the limited results. Therefore we do not support a Sort for this
// API
$dataTable->disableFilter('Sort');
$dataTable->disableFilter('Limit'); // limit is already applied here
return $dataTable;
}
@ -158,14 +198,19 @@ class API extends \Piwik\Plugin\API
* @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.
* @param bool|false|int $limitVisits
* @return array
*/
public function getVisitorProfile($idSite, $visitorId = false, $segment = false, $checkForLatLong = false)
public function getVisitorProfile($idSite, $visitorId = false, $segment = false, $limitVisits = false)
{
Piwik::checkUserHasViewAccess($idSite);
if ($limitVisits === false) {
$limitVisits = VisitorProfile::VISITOR_PROFILE_MAX_VISITS_TO_SHOW;
} else {
$limitVisits = (int) $limitVisits;
}
if ($visitorId === false) {
$visitorId = $this->getMostRecentVisitorId($idSite, $segment);
}
@ -173,196 +218,16 @@ class API extends \Piwik\Plugin\API
$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);
$offset = 0,
$limit = self::VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE);
$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);
$profile = new VisitorProfile($idSite);
$result = $profile->makeVisitorProfile($visits, $visitorId, $segment, $limitVisits);
/**
* Triggered in the Live.getVisitorProfile API method. Plugins can use this event
@ -395,130 +260,45 @@ class API extends \Piwik\Plugin\API
{
Piwik::checkUserHasViewAccess($idSite);
// for faster performance search for a visitor within the last 7 days first
$minTimestamp = Date::now()->subDay(7)->getTimestamp();
$dataTable = $this->loadLastVisitorDetailsFromDatabase(
$idSite, $period = false, $date = false, $segment, $numVisitorsToFetch = 1,
$visitorId = false, $minTimestamp = false
$idSite, $period = false, $date = false, $segment, $offset = 0, $limit = 1, $minTimestamp
);
if (0 >= $dataTable->getRowsCount()) {
$minTimestamp = Date::now()->subYear(1)->getTimestamp();
// no visitor found in last 7 days, look further back for up to 1 year. This query will be slower
$dataTable = $this->loadLastVisitorDetailsFromDatabase(
$idSite, $period = false, $date = false, $segment, $offset = 0, $limit = 1, $minTimestamp
);
}
if (0 >= $dataTable->getRowsCount()) {
// no visitor found in last year, look over all logs. This query might be quite slow
$dataTable = $this->loadLastVisitorDetailsFromDatabase(
$idSite, $period = false, $date = false, $segment, $offset = 0, $limit = 1
);
}
if (0 >= $dataTable->getRowsCount()) {
return false;
}
$visitDetails = $dataTable->getFirstRow()->getColumns();
$visitor = new Visitor($visitDetails);
$visitorFactory = new VisitorFactory();
$visitDetails = $dataTable->getFirstRow()->getColumns();
$visitor = $visitorFactory->create($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);
return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $filter_limit, $minTimestamp, $flat = false);
}
/**
@ -541,26 +321,34 @@ class API extends \Piwik\Plugin\API
/** @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();
$visitorFactory = new VisitorFactory();
$website = new Site($idSite);
$timezone = $website->getTimezone();
$currency = $website->getCurrency();
$currencies = APISitesManager::getInstance()->getCurrencySymbols();
// live api is not summable, prevents errors like "Unexpected ECommerce status value"
$table->deleteRow(DataTable::ID_SUMMARY_ROW);
foreach ($table->getRows() as $visitorDetailRow) {
$visitorDetailsArray = Visitor::cleanVisitorDetails($visitorDetailRow->getColumns());
$visitor = new Visitor($visitorDetailsArray);
$visitor = $visitorFactory->create($visitorDetailsArray);
$visitorDetailsArray = $visitor->getAllVisitorDetails();
$visitorDetailsArray['siteCurrency'] = $website->getCurrency();
$visitorDetailsArray['siteCurrency'] = $currency;
$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'));
if ($dateTimeVisit) {
$visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized(Date::TIME_FORMAT);
$visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Date::DATE_FORMAT_LONG);
}
$dateTimeVisitFirstAction = Date::factory($visitorDetailsArray['firstActionTimestamp'], $timezone);
$visitorDetailsArray['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Piwik::translate('CoreHome_ShortDateFormat'));
$visitorDetailsArray['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized('%time%');
$visitorDetailsArray['serverDatePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Date::DATE_FORMAT_LONG);
$visitorDetailsArray['serverTimePrettyFirstAction'] = $dateTimeVisitFirstAction->getLocalized(Date::TIME_FORMAT);
$visitorDetailsArray['actionDetails'] = array();
if (!$doNotFetchActions) {
@ -576,124 +364,33 @@ class API extends \Piwik\Plugin\API
});
}
private function loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment = false, $countVisitorsToFetch = 100, $visitorId = false, $minTimestamp = false)
private function loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment = false, $offset = 0, $limit = 100, $minTimestamp = false, $filterSortOrder = false, $visitorId = 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;
}
$model = new Model();
$data = $model->queryLogVisits($idSite, $period, $date, $segment, $offset, $limit, $visitorId, $minTimestamp, $filterSortOrder);
return $this->makeVisitorTableFromArray($data);
}
/**
* @param $data
* @return DataTable
* @throws Exception
*/
private function makeVisitorTableFromArray($data)
{
$dataTable = new DataTable();
$dataTable->addRowsFromSimpleArray($data);
if (!empty($data[0])) {
$columnsToNotAggregate = array_map(function () {
return 'skip';
}, $data[0]);
$dataTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, $columnsToNotAggregate);
}
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);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -11,12 +11,10 @@ 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;
/**
*/
@ -40,31 +38,6 @@ class Controller extends \Piwik\Plugin\Controller
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');
@ -83,29 +56,25 @@ class Controller extends \Piwik\Plugin\Controller
public function indexVisitorLog()
{
$view = new View('@Live/indexVisitorLog.twig');
$view->filterEcommerce = Common::getRequestVar('filterEcommerce', 0, 'int');
$view->visitorLog = $this->getLastVisitsDetails();
$view->visitorLog = $this->renderReport('getLastVisitsDetails');
return $view->render();
}
public function getLastVisitsDetails()
{
return $this->renderReport(__FUNCTION__);
}
/**
* Widget
*/
public function getVisitorLog()
{
return $this->getLastVisitsDetails();
return $this->renderReport('getLastVisitsDetails');
}
public function getLastVisitsStart()
{
// hack, ensure we load today's visits by default
$_GET['date'] = 'today';
\Piwik\Period\Factory::checkPeriodIsEnabled('day');
$_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");
@ -118,9 +87,9 @@ class Controller extends \Piwik\Plugin\Controller
private function setCounters($view)
{
$segment = Request::getRawSegmentFromRequest();
$last30min = API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment);
$last30min = API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment, array('visits', 'actions'));
$last30min = $last30min[0];
$today = API::getInstance()->getCounters($this->idSite, $lastMinutes = 24 * 60, $segment);
$today = API::getInstance()->getCounters($this->idSite, $lastMinutes = 24 * 60, $segment, array('visits', 'actions'));
$today = $today[0];
$view->visitorsCountHalfHour = $last30min['visits'];
$view->visitorsCountToday = $today['visits'];
@ -139,7 +108,7 @@ class Controller extends \Piwik\Plugin\Controller
$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->visitorData = Request::processRequest('Live.getVisitorProfile');
$view->exportLink = $this->getVisitorProfileExportLink();
if (Common::getRequestVar('showMap', 1) == 1
@ -163,7 +132,7 @@ class Controller extends \Piwik\Plugin\Controller
'date' => false
));
$view->visitData = $visits->getFirstRow()->getColumns();
$view->visitReferralSummary = API::getReferrerSummaryForVisit($visits->getFirstRow());
$view->visitReferralSummary = VisitorProfile::getReferrerSummaryForVisit($visits->getFirstRow());
$view->showLocation = true;
$this->setWidgetizedVisitorProfileUrl($view);
$view->exportLink = $this->getVisitorProfileExportLink();
@ -175,18 +144,20 @@ class Controller extends \Piwik\Plugin\Controller
$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_limit' => VisitorProfile::VISITOR_PROFILE_MAX_VISITS_TO_SHOW,
'filter_offset' => $startCounter,
'period' => false,
'date' => false
));
$idSite = Common::getRequestVar('idSite', null, 'int');
if (empty($nextVisits)) {
return;
}
$view = new View('@Live/getVisitList.twig');
$view->idSite = Common::getRequestVar('idSite', null, 'int');
$view->idSite = $idSite;
$view->startCounter = $startCounter + 1;
$view->visits = $nextVisits;
return $view->render();

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -8,11 +8,7 @@
*/
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';
/**
*
@ -21,17 +17,14 @@ class Live extends \Piwik\Plugin
{
/**
* @see Piwik\Plugin::getListHooksRegistered
* @see Piwik\Plugin::registerEvents
*/
public function getListHooksRegistered()
public function registerEvents()
{
return array(
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'WidgetsList.addWidgets' => 'addWidget',
'Menu.Reporting.addItems' => 'addMenu',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'ViewDataTable.getDefaultType' => 'getDefaultTypeViewDataTable'
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
@ -43,22 +36,12 @@ class Live extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "libs/bower_components/visibilityjs/lib/visibility.core.js";
$jsFiles[] = "plugins/Live/javascripts/live.js";
$jsFiles[] = "plugins/Live/javascripts/SegmentedVisitorLog.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');
$jsFiles[] = "plugins/Live/javascripts/rowaction.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
@ -68,10 +51,11 @@ class Live extends \Piwik\Plugin
$translationKeys[] = "Live_ShowMap";
$translationKeys[] = "Live_HideMap";
$translationKeys[] = "Live_PageRefreshed";
}
public function getDefaultTypeViewDataTable(&$defaultViewTypes)
{
$defaultViewTypes['Live.getLastVisitsDetails'] = VisitorLog::ID;
$translationKeys[] = "Live_RowActionTooltipTitle";
$translationKeys[] = "Live_RowActionTooltipDefault";
$translationKeys[] = "Live_RowActionTooltipWithDimension";
$translationKeys[] = "Live_SegmentedVisitorLogTitle";
$translationKeys[] = "General_Segment";
$translationKeys[] = "General_And";
}
}

View file

@ -0,0 +1,506 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
use Exception;
use Piwik\Common;
use Piwik\DataAccess\LogAggregator;
use Piwik\Date;
use Piwik\Db;
use Piwik\Period;
use Piwik\Period\Range;
use Piwik\Piwik;
use Piwik\Plugins\CustomVariables\CustomVariables;
use Piwik\Segment;
use Piwik\Site;
use Piwik\Tracker\GoalManager;
class Model
{
/**
* @param $idVisit
* @param $actionsLimit
* @return array
* @throws \Exception
*/
public function queryActionsForVisit($idVisit, $actionsLimit)
{
$maxCustomVariables = CustomVariables::getNumUsableCustomVariables();
$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.idlink_va,
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));
return $actionDetails;
}
/**
* @param $idVisit
* @param $limit
* @return array
* @throws \Exception
*/
public function queryGoalConversionsForVisit($idVisit, $limit)
{
$sql = "
SELECT
'goal' as type,
goal.name as goalName,
goal.idgoal as goalId,
log_conversion.revenue as revenue,
log_conversion.idlink_va,
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, $limit
";
$goalDetails = Db::fetchAll($sql, array($idVisit));
return $goalDetails;
}
/**
* @param $idVisit
* @param $limit
* @return array
* @throws \Exception
*/
public function queryEcommerceConversionsForVisit($idVisit, $limit)
{
$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,
log_conversion.idlink_va
FROM " . Common::prefixTable('log_conversion') . " AS log_conversion
WHERE idvisit = ?
AND idgoal <= " . GoalManager::IDGOAL_ORDER . "
ORDER BY server_time ASC
LIMIT 0, $limit";
$ecommerceDetails = Db::fetchAll($sql, array($idVisit));
return $ecommerceDetails;
}
/**
* @param $idVisit
* @param $idOrder
* @param $actionsLimit
* @return array
* @throws \Exception
*/
public function queryEcommerceItemsForOrder($idVisit, $idOrder, $actionsLimit)
{
$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, $idOrder);
$itemsDetails = Db::fetchAll($sql, $bind);
return $itemsDetails;
}
/**
* @param $idSite
* @param $period
* @param $date
* @param $segment
* @param $limit
* @param $visitorId
* @param $minTimestamp
* @param $filterSortOrder
* @return array
* @throws Exception
*/
public function queryLogVisits($idSite, $period, $date, $segment, $offset, $limit, $visitorId, $minTimestamp, $filterSortOrder)
{
list($sql, $bind) = $this->makeLogVisitsQueryString($idSite, $period, $date, $segment, $offset, $limit, $visitorId, $minTimestamp, $filterSortOrder);
return Db::fetchAll($sql, $bind);
}
/**
* @param $idSite
* @param $lastMinutes
* @param $segment
* @return int
* @throws Exception
*/
public function getNumActions($idSite, $lastMinutes, $segment)
{
return $this->getLastMinutesCounterForQuery(
$idSite,
$lastMinutes,
$segment,
'COUNT(*)',
'log_link_visit_action',
'log_link_visit_action.server_time >= ?'
);
}
/**
* @param $idSite
* @param $lastMinutes
* @param $segment
* @return int
* @throws Exception
*/
public function getNumVisitsConverted($idSite, $lastMinutes, $segment)
{
return $this->getLastMinutesCounterForQuery(
$idSite,
$lastMinutes,
$segment,
'COUNT(*)',
'log_conversion',
'log_conversion.server_time >= ?'
);
}
/**
* @param $idSite
* @param $lastMinutes
* @param $segment
* @return int
* @throws Exception
*/
public function getNumVisits($idSite, $lastMinutes, $segment)
{
return $this->getLastMinutesCounterForQuery(
$idSite,
$lastMinutes,
$segment,
'COUNT(log_visit.visit_last_action_time)',
'log_visit',
'log_visit.visit_last_action_time >= ?'
);
}
/**
* @param $idSite
* @param $lastMinutes
* @param $segment
* @return int
* @throws Exception
*/
public function getNumVisitors($idSite, $lastMinutes, $segment)
{
return $this->getLastMinutesCounterForQuery(
$idSite,
$lastMinutes,
$segment,
'COUNT(DISTINCT log_visit.idvisitor)',
'log_visit',
'log_visit.visit_last_action_time >= ?'
);
}
private function getLastMinutesCounterForQuery($idSite, $lastMinutes, $segment, $select, $from, $where)
{
$lastMinutes = (int)$lastMinutes;
if (empty($lastMinutes)) {
return 0;
}
list($whereIdSites, $idSites) = $this->getIdSitesWhereClause($idSite, $from);
$bind = $idSites;
$bind[] = Date::factory(time() - $lastMinutes * 60)->toString('Y-m-d H:i:s');
$where = $whereIdSites . "AND " . $where;
$segment = new Segment($segment, $idSite);
$query = $segment->getSelectQuery($select, $from, $where, $bind);
$numVisitors = Db::fetchOne($query['sql'], $query['bind']);
return $numVisitors;
}
/**
* @param $idSite
* @param string $table
* @return array
*/
private function getIdSitesWhereClause($idSite, $table = 'log_visit')
{
$idSites = array($idSite);
Piwik::postEvent('Live.API.getIdSitesString', array(&$idSites));
$idSitesBind = Common::getSqlStringFieldsArray($idSites);
$whereClause = $table . ".idsite in ($idSitesBind) ";
return array($whereClause, $idSites);
}
/**
* 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.
* @throws Exception
*/
public function queryAdjacentVisitorId($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;
}
/**
* @param $idSite
* @param $period
* @param $date
* @param $segment
* @param int $offset
* @param int $limit
* @param $visitorId
* @param $minTimestamp
* @param $filterSortOrder
* @return array
* @throws Exception
*/
public function makeLogVisitsQueryString($idSite, $period, $date, $segment, $offset, $limit, $visitorId, $minTimestamp, $filterSortOrder)
{
// If no other filter, only look at the last 24 hours of stats
if (empty($visitorId)
&& empty($limit)
&& empty($offset)
&& empty($period)
&& empty($date)
) {
$period = 'day';
$date = 'yesterdaySameTime';
}
list($whereClause, $bindIdSites) = $this->getIdSitesWhereClause($idSite);
list($whereBind, $where) = $this->getWhereClauseAndBind($whereClause, $bindIdSites, $idSite, $period, $date, $visitorId, $minTimestamp);
if (strtolower($filterSortOrder) !== 'asc') {
$filterSortOrder = 'DESC';
}
$segment = new Segment($segment, $idSite);
// Subquery to use the indexes for ORDER BY
$select = "log_visit.*";
$from = "log_visit";
$groupBy = false;
$limit = $limit >= 1 ? (int)$limit : 0;
$offset = $offset >= 1 ? (int)$offset : 0;
$orderBy = '';
if (count($bindIdSites) <= 1) {
$orderBy = 'idsite, ';
}
$orderBy .= "visit_last_action_time " . $filterSortOrder;
$orderByParent = "sub.visit_last_action_time " . $filterSortOrder;
$subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy, $limit, $offset);
$bind = $subQuery['bind'];
// Group by idvisit so that a visitor converting 2 goals only appears once
$sql = "
SELECT sub.* FROM (
" . $subQuery['sql'] . "
) AS sub
GROUP BY sub.idvisit
ORDER BY $orderByParent
";
return array($sql, $bind);
}
/**
* @param $idSite
* @return Site
*/
protected function makeSite($idSite)
{
return new Site($idSite);
}
/**
* @param string $whereClause
* @param array $bindIdSites
* @param $idSite
* @param $period
* @param $date
* @param $visitorId
* @param $minTimestamp
* @return array
* @throws Exception
*/
private function getWhereClauseAndBind($whereClause, $bindIdSites, $idSite, $period, $date, $visitorId, $minTimestamp)
{
$where = array();
$where[] = $whereClause;
$whereBind = $bindIdSites;
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);
}
// SQL Filter with provided period
if (!empty($period) && !empty($date)) {
$currentSite = $this->makeSite($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::build($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;
}
return array($whereBind, $where);
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Reports;
abstract class Base extends \Piwik\Plugin\Report
{
protected function init()
{
$this->category = 'Live!';
}
public function configureReportMetadata(&$availableReports, $infos)
{
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Reports;
class GetLastVisits extends Base
{
// this class only exists to disable the default sort column
protected $defaultSortColumn = '';
public function buildReportMetadata()
{
// do not add this report as metadata
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Reports;
use Piwik\Menu\MenuReporting;
use Piwik\Plugin\Report;
use Piwik\Plugins\Live\Visualizations\VisitorLog;
use Piwik\WidgetsList;
class GetLastVisitsDetails extends Base
{
protected $defaultSortColumn = '';
protected function init()
{
parent::init();
$this->widgetTitle = 'Live_VisitorLog';
$this->order = 2;
}
public function getDefaultTypeViewDataTable()
{
return VisitorLog::ID;
}
public function alwaysUseDefaultViewDataTable()
{
return true;
}
public function configureReportingMenu(MenuReporting $menu)
{
if ($this->isEnabled()) {
$url = array('module' => $this->module, 'action' => 'indexVisitorLog');
$menu->addVisitorsItem($this->widgetTitle, $url, $order = 5);
}
}
public function configureWidget(WidgetsList $widget)
{
$widget->add($this->category, $this->widgetTitle, $this->module, 'getVisitorLog', array('small' => 1));
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Reports;
use Piwik\Config;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\Live\Controller;
use Piwik\API\Request;
use Piwik\View;
class GetSimpleLastVisitCount extends Base
{
protected function init()
{
parent::init();
$this->widgetTitle = 'Live_RealTimeVisitorCount';
$this->order = 3;
}
public function render()
{
$lastMinutes = Config::getInstance()->General[Controller::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
$params = array('lastMinutes' => $lastMinutes, 'showColumns' => array('visits', 'visitors', 'actions'));
$lastNData = Request::processRequest('Live.getCounters', $params);
$formatter = new Formatter();
$view = new View('@Live/getSimpleLastVisitCount');
$view->lastMinutes = $lastMinutes;
$view->visitors = $formatter->getPrettyNumber($lastNData[0]['visitors']);
$view->visits = $formatter->getPrettyNumber($lastNData[0]['visits']);
$view->actions = $formatter->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('Intl_OneMinute'),
'minutes' => Piwik::translate('Intl_NMinutes')
);
return $view->render();
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -9,42 +9,25 @@
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\Metrics\Formatter;
use Piwik\Network\IPUtils;
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\Plugins\Actions\Actions\ActionSiteSearch;
use Piwik\Tracker;
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
class Visitor implements VisitorInterface
{
const DELIMITER_PLUGIN_NAME = ", ";
const EVENT_VALUE_PRECISION = 3;
private $details = array();
function __construct($visitorRawData)
{
$this->details = $visitorRawData;
@ -52,84 +35,45 @@ class Visitor
function getAllVisitorDetails()
{
return array(
$visitor = 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(),
);
/**
* This event can be used to add any details to a visitor. The visitor's details are for instance used in
* API requests like 'Live.getVisitorProfile' and 'Live.getLastVisitDetails'. This can be useful for instance
* in case your plugin defines any visit dimensions and you want to add the value of your dimension to a user.
* It can be also useful if you want to enrich a visitor with custom fields based on other fields or if you
* want to change or remove any fields from the user.
*
* **Example**
*
* Piwik::addAction('Live.getAllVisitorDetails', function (&visitor, $details) {
* $visitor['userPoints'] = $details['actions'] + $details['events'] + $details['searches'];
* unset($visitor['anyFieldYouWantToRemove']);
* });
*
* @param array &visitor You can add or remove fields to the visitor array and it will reflected in the API output
* @param array $details The details array contains all visit dimensions (columns of log_visit table)
*/
Piwik::postEvent('Live.getAllVisitorDetails', array(&$visitor, $this->details));
return $visitor;
}
function getVisitorId()
@ -140,41 +84,11 @@ class Visitor
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']));
@ -183,9 +97,9 @@ class Visitor
function getIp()
{
if (isset($this->details['location_ip'])) {
return IP::N2P($this->details['location_ip']);
return IPUtils::binaryToStringIP($this->details['location_ip']);
}
return false;
return null;
}
function getIdVisit()
@ -198,412 +112,16 @@ class Visitor
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)
@ -616,6 +134,7 @@ class Visitor
$toUnset = array('config_id');
if (Piwik::isUserIsAnonymous()) {
$toUnset[] = 'idvisitor';
$toUnset[] = 'user_id';
$toUnset[] = 'location_ip';
}
foreach ($toUnset as $keyName) {
@ -673,12 +192,21 @@ class Visitor
// 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
$flattenForActionType = array(
'outlink' => 'outlinkUrl',
'download' => 'downloadUrl',
'action' => 'pageUrl'
);
foreach($flattenForActionType as $actionType => $flattenedKeyPrefix) {
if (!empty($action['url'])
&& $action['type'] == $actionType) {
$flattenedKeyName = $flattenedKeyPrefix . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
$visitorDetailsArray[$flattenedKeyName] = $action['url'];
}
}
$flatten = array( 'pageTitle', 'siteSearchKeyword', 'eventCategory', 'eventAction', 'eventName', 'eventValue');
foreach($flatten as $toFlatten) {
if (!empty($action[$toFlatten])) {
@ -713,7 +241,6 @@ class Visitor
$visitorDetailsArray['exitPageUrl'] = $lastAction['url'];
}
return $visitorDetailsArray;
}
@ -727,49 +254,16 @@ class Visitor
{
$idVisit = $visitorDetailsArray['idVisit'];
$maxCustomVariables = CustomVariables::getMaxCustomVariables();
$model = new Model();
$actionDetails = $model->queryActionsForVisit($idVisit, $actionsLimit);
$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));
$formatter = new Formatter();
$maxCustomVariables = CustomVariables::getNumUsableCustomVariables();
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];
@ -786,10 +280,14 @@ class Visitor
$actionDetail['customVariables'] = $customVariablesPage;
}
if ($actionDetail['type'] == Action::TYPE_CONTENT) {
if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
unset($actionDetails[$actionIdx]);
continue;
} elseif ($actionDetail['type'] == Action::TYPE_EVENT) {
// Handle Event
if(strlen($actionDetail['pageTitle']) > 0) {
if (strlen($actionDetail['pageTitle']) > 0) {
$actionDetail['eventName'] = $actionDetail['pageTitle'];
}
@ -802,74 +300,32 @@ class Visitor
}
// Event value / Generation time
if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
if(strlen($actionDetail['custom_float']) > 0) {
if ($actionDetail['type'] == Action::TYPE_EVENT) {
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);
$actionDetail['generationTime'] = $formatter->getPrettyTimeFromSeconds($actionDetail['custom_float'] / 1000, true);
}
unset($actionDetail['custom_float']);
if($actionDetail['type'] != Action::TYPE_EVENT_CATEGORY) {
if ($actionDetail['type'] != Action::TYPE_EVENT) {
unset($actionDetail['eventCategory']);
unset($actionDetail['eventAction']);
}
// Reconstruct url from prefix
$actionDetail['url'] = Tracker\PageUrl::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
$url = Tracker\PageUrl::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
$url = Common::unsanitizeInputValue($url);
$actionDetail['url'] = $url;
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));
$goalDetails = $model->queryGoalConversionsForVisit($idVisit, $actionsLimit);
$ecommerceDetails = $model->queryEcommerceConversionsForVisit($idVisit, $actionsLimit);
foreach ($ecommerceDetails as &$ecommerceDetail) {
if ($ecommerceDetail['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
unset($ecommerceDetail['orderId']);
@ -891,31 +347,10 @@ class Visitor
// 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
);
foreach ($ecommerceDetails as &$ecommerceConversion) {
$idOrder = isset($ecommerceConversion['orderId']) ? $ecommerceConversion['orderId'] : GoalManager::ITEM_IDORDER_ABANDONED_CART;
$itemsDetails = Db::fetchAll($sql, $bind);
$itemsDetails = $model->queryEcommerceItemsForOrder($idVisit, $idOrder, $actionsLimit);
foreach ($itemsDetails as &$detail) {
if ($detail['price'] == round($detail['price'])) {
$detail['price'] = round($detail['price']);
@ -924,54 +359,98 @@ class Visitor
$ecommerceConversion['itemDetails'] = $itemsDetails;
}
$actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
$actionDetails = array_values($actionDetails);
// Enrich with time spent per action
foreach($actionDetails as $actionIdx => &$actionDetail) {
// Set the time spent for this action (which is the timeSpentRef of the next action)
$nextActionFound = isset($actionDetails[$actionIdx + 1]);
if ($nextActionFound) {
$actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
} else {
// Last action of a visit.
// By default, Piwik does not know how long the user stayed on the page
// If enableHeartBeatTimer() is used in piwik.js then we can find the accurate time on page for the last pageview
$visitTotalTime = $visitorDetailsArray['visitDuration'];
$timeOfLastAction = Date::factory($actionDetail['serverTimePretty'])->getTimestamp();
$timeSpentOnAllActionsApartFromLastOne = ($timeOfLastAction - $visitorDetailsArray['firstActionTimestamp']);
$timeSpentOnPage = $visitTotalTime - $timeSpentOnAllActionsApartFromLastOne;
// Safe net, we assume the time is correct when it's more than 10 seconds
if ($timeSpentOnPage > 10) {
$actionDetail['timeSpent'] = $timeSpentOnPage;
}
}
if (isset($actionDetail['timeSpent'])) {
$actionDetail['timeSpentPretty'] = $formatter->getPrettyTimeFromSeconds($actionDetail['timeSpent'], true);
}
unset($actionDetails[$actionIdx]['timeSpentRef']); // not needed after timeSpent is added
}
$actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
usort($actions, array('static', 'sortByServerTime'));
foreach ($actions as &$action) {
unset($action['idlink_va']);
}
$visitorDetailsArray['goalConversions'] = count($goalDetails);
$visitorDetailsArray['actionDetails'] = $actions;
foreach ($visitorDetailsArray['actionDetails'] as &$details) {
switch ($details['type']) {
case 'goal':
$details['icon'] = 'plugins/Zeitgeist/images/goal.png';
$details['icon'] = 'plugins/Morpheus/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';
$details['icon'] = 'plugins/Morpheus/images/' . $details['type'] . '.gif';
break;
case Action::TYPE_DOWNLOAD:
$details['type'] = 'download';
$details['icon'] = 'plugins/Zeitgeist/images/download.png';
$details['icon'] = 'plugins/Morpheus/images/download.png';
break;
case Action::TYPE_OUTLINK:
$details['type'] = 'outlink';
$details['icon'] = 'plugins/Zeitgeist/images/link.gif';
$details['icon'] = 'plugins/Morpheus/images/link.gif';
break;
case Action::TYPE_SITE_SEARCH:
$details['type'] = 'search';
$details['icon'] = 'plugins/Zeitgeist/images/search_ico.png';
$details['icon'] = 'plugins/Morpheus/images/search_ico.png';
break;
case Action::TYPE_EVENT_CATEGORY:
case Action::TYPE_EVENT:
$details['type'] = 'event';
$details['icon'] = 'plugins/Zeitgeist/images/event.png';
$details['icon'] = 'plugins/Morpheus/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%');
$details['serverTimePretty'] = $dateTimeVisit->getLocalized(Date::DATETIME_FORMAT_SHORT);
$details['timestamp'] = $dateTimeVisit->getTimestamp();
}
$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'),
ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY => Piwik::translate('Actions_ColumnSearchCategory'),
ActionSiteSearch::CVAR_KEY_SEARCH_COUNT => Piwik::translate('Actions_ColumnSearchResultsCount'),
);
if (isset($rename[$key])) {
return $rename[$key];
@ -983,10 +462,19 @@ class Visitor
{
$ta = strtotime($a['serverTimePretty']);
$tb = strtotime($b['serverTimePretty']);
return $ta < $tb
? -1
: ($ta == $tb
? 0
: 1);
if ($ta < $tb) {
return -1;
}
if ($ta == $tb) {
if ($a['idlink_va'] > $b['idlink_va']) {
return 1;
}
return -1;
}
return 1;
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
use Exception;
use Piwik\Piwik;
class VisitorFactory
{
/**
* Returns Visitor object.
* This method can be overwritten to use a different Visitor object
*
* @param array $visitorRawData
* @throws \Exception
* @return \Piwik\Plugins\Live\VisitorInterface
* @ignore
*/
public function create(array $visitorRawData = array())
{
$visitor = null;
/**
* Triggered while visit is filtering in live plugin. Subscribers to this
* event can force the use of a custom visitor object that extends from
* {@link Piwik\Plugins\Live\VisitorInterface}.
*
* @param \Piwik\Plugins\Live\VisitorInterface &$visitor Initialized to null, but can be set to
* a new visitor object. If it isn't modified
* Piwik uses the default class.
* @param array $visitorRawData Raw data using in Visitor object constructor.
*/
Piwik::postEvent('Live.makeNewVisitorObject', array(&$visitor, $visitorRawData));
if (is_null($visitor)) {
$visitor = new Visitor($visitorRawData);
} elseif (!($visitor instanceof VisitorInterface)) {
throw new Exception("The Visitor object set in the plugin must implement VisitorInterface");
}
return $visitor;
}
}

View file

@ -0,0 +1,22 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
interface VisitorInterface
{
/**
* @return array
*/
public function getAllVisitorDetails();
/**
* @return string|bool
*/
public function getVisitorId();
}

View file

@ -1,107 +0,0 @@
<?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;
}
)
);
}
}
}

View file

@ -0,0 +1,398 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
use Exception;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Site;
use Piwik\Plugins\Referrers\API as APIReferrers;
class VisitorProfile
{
const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10;
protected $profile = array();
private $siteSearchKeywords = array();
private $continents = array();
private $countries = array();
private $cities = array();
private $pageGenerationTimeTotal = 0;
public function __construct($idSite)
{
$this->idSite = $idSite;
$this->isEcommerceEnabled = Site::isEcommerceEnabledFor($this->idSite);
}
/**
* @param $visits
* @param $visitorId
* @param $segment
* @param $numLastVisits
* @return array
* @throws Exception
*/
public function makeVisitorProfile(DataTable $visits, $visitorId, $segment, $numLastVisits)
{
$this->initVisitorProfile();
/** @var DataTable\Row $visit */
foreach ($visits->getRows() as $visit) {
++$this->profile['totalVisits'];
$this->profile['totalVisitDuration'] += $visit->getColumn('visitDuration');
$this->profile['totalActions'] += $visit->getColumn('actions');
$this->profile['totalGoalConversions'] += $visit->getColumn('goalConversions');
// individual goal conversions are stored in action details
foreach ($visit->getColumn('actionDetails') as $action) {
$this->handleIfGoalAction($action);
$this->handleIfEventAction($action);
$this->handleIfDownloadAction($action);
$this->handleIfOutlinkAction($action);
$this->handleIfEcommerceAction($action);
$this->handleIfSiteSearchAction($action);
$this->handleIfPageViewAction($action);
$this->handleIfPageGenerationTime($action);
}
$this->handleGeoLocation($visit);
}
$this->handleGeoLocationCountries();
$this->handleGeoLocationContinents();
$this->handleSiteSearches();
$this->handleAveragePageGenerationTime();
$formatter = new Formatter();
$this->profile['totalVisitDurationPretty'] = $formatter->getPrettyTimeFromSeconds($this->profile['totalVisitDuration'], true);
$this->handleVisitsSummary($visits);
$this->handleAdjacentVisitorIds($visits, $visitorId, $segment);
// use N most recent visits for last_visits
$visits->deleteRowsOffset($numLastVisits);
$this->profile['lastVisits'] = $visits;
$this->profile['userId'] = $visit->getColumn('userId');
return $this->profile;
}
/**
* Returns a summary for an important visit. Used to describe the first & last visits of a visitor.
*
* @param DataTable\Row $visit
* @return array
*/
private function getVisitorProfileVisitSummary($visit)
{
$today = Date::today();
$serverDate = $visit->getColumn('firstActionTimestamp');
return array(
'date' => $serverDate,
'prettyDate' => Date::factory($serverDate)->getLocalized(Date::DATE_FORMAT_LONG),
'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 DataTable\Row $visit
* @return bool|mixed|string
*/
public static function getReferrerSummaryForVisit($visit)
{
$referrerType = $visit->getColumn('referrerType');
if ($referrerType === false
|| $referrerType == 'direct'
) {
return Piwik::translate('Referrers_DirectEntry');
}
if ($referrerType == 'search') {
$referrerName = $visit->getColumn('referrerName');
$keyword = $visit->getColumn('referrerKeyword');
if ($keyword !== false
&& $keyword != APIReferrers::getKeywordNotDefinedString()
) {
$referrerName .= ' (' . $keyword . ')';
}
return $referrerName;
}
if ($referrerType == 'campaign') {
$summary = Piwik::translate('Referrers_ColumnCampaign') . ': ' . $visit->getColumn('referrerName');
$keyword = $visit->getColumn('referrerKeyword');
if (!empty($keyword)) {
$summary .= ' - ' . $keyword;
}
return $summary;
}
return $visit->getColumn('referrerName');
}
private function isEcommerceEnabled()
{
return $this->isEcommerceEnabled;
}
/**
* @param $action
*/
private function handleIfEventAction($action)
{
if ($action['type'] != 'event') {
return;
}
$this->profile['totalEvents']++;
}
/**
* @param $action
*/
private function handleIfDownloadAction($action)
{
if ($action['type'] != 'download') {
return;
}
$this->profile['totalDownloads']++;
}
/**
* @param $action
*/
private function handleIfOutlinkAction($action)
{
if ($action['type'] != 'outlink') {
return;
}
$this->profile['totalOutlinks']++;
}
/**
* @param $action
*/
private function handleIfPageViewAction($action)
{
if ($action['type'] != 'action') {
return;
}
$this->profile['totalPageViews']++;
}
/**
* @param $action
*/
private function handleIfEcommerceAction($action)
{
if (!$this->isEcommerceEnabled()) {
return;
}
if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
++$this->profile['totalEcommerceConversions'];
$this->profile['totalEcommerceRevenue'] += $action['revenue'];
$this->profile['totalEcommerceItems'] += $action['items'];
} else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
++$this->profile['totalAbandonedCarts'];
$this->profile['totalAbandonedCartsRevenue'] += $action['revenue'];
$this->profile['totalAbandonedCartsItems'] += $action['items'];
}
}
private function handleIfGoalAction($action)
{
if ($action['type'] != 'goal') {
return;
}
$idGoal = $action['goalId'];
$idGoalKey = 'idgoal=' . $idGoal;
if (!isset($this->profile['totalConversionsByGoal'][$idGoalKey])) {
$this->profile['totalConversionsByGoal'][$idGoalKey] = 0;
}
++$this->profile['totalConversionsByGoal'][$idGoalKey];
if (!empty($action['revenue'])) {
if (!isset($this->profile['totalRevenueByGoal'][$idGoalKey])) {
$this->profile['totalRevenueByGoal'][$idGoalKey] = 0;
}
$this->profile['totalRevenueByGoal'][$idGoalKey] += $action['revenue'];
}
}
private function handleIfSiteSearchAction($action)
{
if (!isset($action['siteSearchKeyword'])) {
return;
}
$keyword = $action['siteSearchKeyword'];
if (!isset($this->siteSearchKeywords[$keyword])) {
$this->siteSearchKeywords[$keyword] = 0;
++$this->profile['totalSearches'];
}
++$this->siteSearchKeywords[$keyword];
}
private function handleGeoLocation(DataTable\Row $visit)
{
// realtime map only checks for latitude
$hasLatitude = $visit->getColumn('latitude') !== false;
if ($hasLatitude) {
$this->profile['hasLatLong'] = true;
}
$countryCode = $visit->getColumn('countryCode');
if (!isset($this->countries[$countryCode])) {
$this->countries[$countryCode] = 0;
}
++$this->countries[$countryCode];
$continentCode = $visit->getColumn('continentCode');
if (!isset($this->continents[$continentCode])) {
$this->continents[$continentCode] = 0;
}
++$this->continents[$continentCode];
if ($countryCode && !array_key_exists($countryCode, $this->cities)) {
$this->cities[$countryCode] = array();
}
$city = $visit->getColumn('city');
if (!empty($city)) {
$this->cities[$countryCode][] = $city;
}
}
private function handleSiteSearches()
{
// sort by visit/action
arsort($this->siteSearchKeywords);
foreach ($this->siteSearchKeywords as $keyword => $searchCount) {
$this->profile['searches'][] = array('keyword' => $keyword,
'searches' => $searchCount);
}
}
private function handleGeoLocationContinents()
{
// sort by visit/action
asort($this->continents);
foreach ($this->continents as $continentCode => $nbVisits) {
$this->profile['continents'][] = array('continent' => $continentCode,
'nb_visits' => $nbVisits,
'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode));
}
}
private function handleGeoLocationCountries()
{
// sort by visit/action
asort($this->countries);
// transform country/continents/search keywords into something that will look good in XML
$this->profile['countries'] = $this->profile['continents'] = $this->profile['searches'] = array();
foreach ($this->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($this->cities[$countryCode])) {
$countryInfo['cities'] = array_unique($this->cities[$countryCode]);
}
$this->profile['countries'][] = $countryInfo;
}
}
private function initVisitorProfile()
{
$this->profile['totalVisits'] = 0;
$this->profile['totalVisitDuration'] = 0;
$this->profile['totalActions'] = 0;
$this->profile['totalEvents'] = 0;
$this->profile['totalOutlinks'] = 0;
$this->profile['totalDownloads'] = 0;
$this->profile['totalSearches'] = 0;
$this->profile['totalPageViews'] = 0;
$this->profile['totalPageViewsWithTiming'] = 0;
$this->profile['totalGoalConversions'] = 0;
$this->profile['totalConversionsByGoal'] = array();
$this->profile['hasLatLong'] = false;
if ($this->isEcommerceEnabled()) {
$this->profile['totalEcommerceConversions'] = 0;
$this->profile['totalEcommerceRevenue'] = 0;
$this->profile['totalEcommerceItems'] = 0;
$this->profile['totalAbandonedCarts'] = 0;
$this->profile['totalAbandonedCartsRevenue'] = 0;
$this->profile['totalAbandonedCartsItems'] = 0;
}
}
private function handleAveragePageGenerationTime()
{
if ($this->profile['totalPageViewsWithTiming']) {
$this->profile['averagePageGenerationTime'] =
round($this->pageGenerationTimeTotal / $this->profile['totalPageViewsWithTiming'], $precision = 2);
}
}
private function handleIfPageGenerationTime($action)
{
if (isset($action['generationTime'])) {
$this->pageGenerationTimeTotal += $action['generationTime'];
++$this->profile['totalPageViewsWithTiming'];
}
}
/**
* @param DataTable $visits
* @param $visitorId
* @param $segment
*/
private function handleAdjacentVisitorIds(DataTable $visits, $visitorId, $segment)
{
// 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.
$rows = $visits->getRows();
$latestVisitTime = reset($rows)->getColumn('lastActionDateTime');
$model = new Model();
$this->profile['nextVisitorId'] = $model->queryAdjacentVisitorId($this->idSite, $visitorId, $latestVisitTime, $segment, $getNext = true);
$this->profile['previousVisitorId'] = $model->queryAdjacentVisitorId($this->idSite, $visitorId, $latestVisitTime, $segment, $getNext = false);
}
/**
* @param DataTable $visits
*/
private function handleVisitsSummary(DataTable $visits)
{
$rows = $visits->getRows();
$this->profile['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows));
$this->profile['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows));
$this->profile['visitsAggregated'] = count($rows);
}
}

View file

@ -0,0 +1,101 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Visualizations;
use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugin\Visualization;
use Piwik\View;
/**
* A special DataTable visualization for the Live.getLastVisitsDetails API method.
*
* @property VisitorLog\Config $config
*/
class VisitorLog extends Visualization
{
const ID = 'VisitorLog';
const TEMPLATE_FILE = "@Live/_dataTableViz_visitorLog.twig";
const FOOTER_ICON_TITLE = '';
const FOOTER_ICON = '';
public static function getDefaultConfig()
{
return new VisitorLog\Config();
}
public function beforeLoadDataTable()
{
$this->requestConfig->addPropertiesThatShouldBeAvailableClientSide(array(
'filter_limit',
'filter_offset',
'filter_sort_column',
'filter_sort_order',
));
if (!is_numeric($this->requestConfig->filter_limit)) {
$this->requestConfig->filter_limit = 20;
}
$this->requestConfig->disable_generic_filters = true;
$this->requestConfig->filter_sort_column = false;
}
public function afterGenericFiltersAreAppliedToLoadedDataTable()
{
$this->requestConfig->filter_sort_column = false;
}
/**
* Configure visualization.
*/
public function beforeRender()
{
$this->config->disable_row_actions = true;
$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 />'));
if (!is_array($this->config->custom_parameters)) {
$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
$this->config->custom_parameters['totalRows'] = 10000000;
$this->config->custom_parameters['smallWidth'] = (1 == Common::getRequestVar('small', 0, 'int'));
$this->config->custom_parameters['hideProfileLink'] = (1 == Common::getRequestVar('hideProfileLink', 0, 'int'));
$this->config->custom_parameters['pageUrlNotDefined'] = Piwik::translate('General_NotDefined', Piwik::translate('Actions_ColumnPageURL'));
$this->config->footer_icons = array(
array(
'class' => 'tableAllColumnsSwitch',
'buttons' => array(
array(
'id' => static::ID,
'title' => Piwik::translate('Live_LinkVisitorLog'),
'icon' => 'plugins/Morpheus/images/table.png'
)
)
)
);
}
public static function canDisplayViewDataTable(ViewDataTable $view)
{
return ($view->requestConfig->getApiModuleToRequest() === 'Live');
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live\Visualizations\VisitorLog;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* Controls whether any DataTable Row Action icons are shown. If true, no icons are shown.
*
* Default value: false
*/
public $disable_row_actions = false;
public function __construct()
{
parent::__construct();
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'disable_row_actions',
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'disable_row_actions',
));
}
}

View file

@ -0,0 +1,27 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Live;
use Piwik\Piwik;
class Widgets extends \Piwik\Plugin\Widgets
{
protected $category = 'Live!';
public function init()
{
$this->addWidget('Live_VisitorsInRealTime', 'widget');
// the visitor profile uses a segment that is not accessible to the anonymous user, so don't bother showing this widget
if (!Piwik::isUserIsAnonymous()) {
$this->addWidget('Live_VisitorProfile', 'getVisitorProfilePopup');
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,142 @@
var SegmentedVisitorLog = function() {
function getDataTableFromApiMethod(apiMethod)
{
var div = $(require('piwik/UI').DataTable.getDataTableByReport(apiMethod));
if (div.size() > 0 && div.data('uiControlObject')) {
return div.data('uiControlObject');
}
}
function getLabelFromTr ($tr, apiMethod) {
var label;
if (apiMethod && 0 === apiMethod.indexOf('Actions.')) {
// for now only use this for Actions... I know a hack :( Otherwise in Search Engines
// it would show "http://www.searchenginename.org" instead of "SearchEngineName"
label = $tr.attr('data-url-label');
}
if (!label) {
label = $tr.find('.label .value').text();
}
if (label) {
label = $.trim(label);
}
return label;
}
function getDimensionFromApiMethod(apiMethod)
{
if (!apiMethod) {
return;
}
var dataTable = getDataTableFromApiMethod(apiMethod);
var metadata = getMetadataFromDataTable(dataTable);
if (metadata && metadata.dimension) {
return metadata.dimension;
}
}
function getMetadataFromDataTable(dataTable)
{
if (dataTable) {
return dataTable.getReportMetadata();
}
}
function findTitleOfRowHavingRawSegmentValue(apiMethod, rawSegmentValue)
{
var $tr = $('[data-report="' + apiMethod + '"] tr[data-segment-filter="' + rawSegmentValue + '"]').first();
return getLabelFromTr($tr, apiMethod);
}
function setPopoverTitle(apiMethod, segment, index) {
var dataTable = getDataTableFromApiMethod(apiMethod);
if (!dataTable) {
if (index < 15) {
// this is needed when the popover is opened before the dataTable is there which can often
// happen when opening the popover directly via URL (broadcast.popoverHandler)
setTimeout(function () {
setPopoverTitle(apiMethod, segment, index + 1);
}, 150);
}
return;
}
var segmentName = getDimensionFromApiMethod(apiMethod);
var segmentValue = findTitleOfRowHavingRawSegmentValue(apiMethod, segment);
if (!segmentName || (segment && segment.indexOf(';') > 0)) {
segmentName = _pk_translate('General_Segment');
var segmentParts = segment.split(';');
segmentValue = segmentParts.join(' ' + _pk_translate('General_And') + ' ');
}
segmentName = piwikHelper.escape(segmentName);
segmentName = piwikHelper.htmlEntities(segmentName);
segmentValue = piwikHelper.escape(segmentValue);
segmentValue = piwikHelper.htmlEntities(segmentValue);
segmentName = segmentName.replace(/(&amp;)(#[0-9]{2,5};)/g, '&$2');
segmentValue = segmentValue.replace(/(&amp;)(#[0-9]{2,5};)/g, '&$2');
var title = _pk_translate('Live_SegmentedVisitorLogTitle', [segmentName, segmentValue]);
Piwik_Popover.setTitle(title);
}
function show(apiMethod, segment, extraParams) {
// open the popover
var box = Piwik_Popover.showLoading('Segmented Visitor Log');
box.addClass('segmentedVisitorLogPopover');
var callback = function (html) {
Piwik_Popover.setContent(html);
// remove title returned from the server
var title = box.find('h2[piwik-enriched-headline]');
var defaultTitle = title.text();
if (title.size() > 0) {
title.remove();
}
Piwik_Popover.setTitle(defaultTitle);
setPopoverTitle(apiMethod, segment, 0);
};
// prepare loading the popover contents
var requestParams = {
module: 'Live',
action: 'indexVisitorLog',
segment: segment,
disableLink: 1,
small: 1,
hideProfileLink: 1
};
$.extend(requestParams, extraParams);
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(requestParams, 'get');
ajaxRequest.setCallback(callback);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
}
return {
show: show
}
}();

View file

@ -1,5 +1,5 @@
/*!
* Piwik - Web Analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -82,7 +82,7 @@
if (that.isStarted) {
window.clearTimeout(that.updateInterval);
if ($(that.element).closest('body').length) {
if (that.element.length && $.contains(document, that.element[0])) {
that.updateInterval = window.setTimeout(function() { that._update() }, that.currentInterval);
}
}
@ -188,6 +188,14 @@
window.clearTimeout(this.updateInterval);
},
/**
* Return true in case widget is started.
* @returns {boolean}
*/
started: function() {
return this.isStarted;
},
/**
* Set the interval for refresh
*
@ -200,11 +208,20 @@
});
})(jQuery);
$(function() {
var refreshWidget = function (element, refreshAfterXSecs) {
// if the widget has been removed from the DOM, abort
if ($(element).parent().length == 0) {
if (!element.length || !$.contains(document, element[0])) {
return;
}
function scheduleAnotherRequest()
{
setTimeout(function () { refreshWidget(element, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
}
if (Visibility.hidden()) {
scheduleAnotherRequest();
return;
}
@ -249,8 +266,7 @@ $(function() {
? translations['one_minute'] : translations['minutes'].replace('%s', lastMinutes);
$(metrics[2]).text(lastMinutesText);
// schedule another request
setTimeout(function () { refreshWidget(element, refreshAfterXSecs); }, refreshAfterXSecs * 1000);
scheduleAnotherRequest();
});
ajaxRequest.send(true);
};
@ -271,18 +287,48 @@ $(function() {
};
});
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);
$('#pauseImage').hide();
$('#playImage').show();
return $('#visitsLive').liveWidget('stop');
}
function onClickPlay() {
$('#playImage').attr('src', playImage);
$('#pauseImage').attr('src', pauseDisabledImage);
$('#playImage').hide();
$('#pauseImage').show();
return $('#visitsLive').liveWidget('start');
}
(function () {
if (!Visibility.isSupported()) {
return;
}
var isStoppedByBlur = false;
function isStarted()
{
return $('#visitsLive').liveWidget('started');
}
function onTabBlur() {
if (isStarted()) {
isStoppedByBlur = true;
onClickPause();
}
}
function onTabFocus() {
if (isStoppedByBlur && !isStarted()) {
isStoppedByBlur = false;
onClickPlay();
}
}
Visibility.change(function (event, state) {
if (Visibility.hidden()) {
onTabBlur();
} else {
onTabFocus();
}
});
})();

View file

@ -0,0 +1,166 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* This file registers the Overlay row action on the pages report.
*/
(function () {
var actionName = 'SegmentVisitorLog';
function getRawSegmentValueFromRow(tr)
{
return $(tr).attr('data-segment-filter');
}
function getDataTableFromApiMethod(apiMethod)
{
var div = $(require('piwik/UI').DataTable.getDataTableByReport(apiMethod));
if (div.size() > 0 && div.data('uiControlObject')) {
return div.data('uiControlObject');
}
}
function getMetadataFromDataTable(dataTable)
{
if (dataTable) {
return dataTable.getReportMetadata();
}
}
function getDimensionFromApiMethod(apiMethod)
{
if (!apiMethod) {
return;
}
var dataTable = getDataTableFromApiMethod(apiMethod);
var metadata = getMetadataFromDataTable(dataTable);
if (metadata && metadata.dimension) {
return metadata.dimension;
}
}
function DataTable_RowActions_SegmentVisitorLog(dataTable) {
this.dataTable = dataTable;
this.actionName = actionName;
// has to be overridden in subclasses
this.trEventName = 'piwikTriggerSegmentVisitorLogAction';
}
DataTable_RowActions_SegmentVisitorLog.prototype = new DataTable_RowAction();
DataTable_RowActions_SegmentVisitorLog.prototype.openPopover = function (apiMethod, segment, extraParams) {
var urlParam = apiMethod + ':' + encodeURIComponent(segment) + ':' + encodeURIComponent(JSON.stringify(extraParams));
broadcast.propagateNewPopoverParameter('RowAction', actionName + ':' + urlParam);
};
DataTable_RowActions_SegmentVisitorLog.prototype.trigger = function (tr, e, subTableLabel) {
var segment = getRawSegmentValueFromRow(tr);
this.performAction(segment, tr, e);
};
DataTable_RowActions_SegmentVisitorLog.prototype.performAction = function (segment, tr, e) {
var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action;
this.openPopover(apiMethod, segment, {});
};
DataTable_RowActions_SegmentVisitorLog.prototype.doOpenPopover = function (urlParam) {
var urlParamParts = urlParam.split(':');
var apiMethod = urlParamParts.shift();
var segment = decodeURIComponent(urlParamParts.shift());
var extraParamsString = urlParamParts.shift(),
extraParams = {}; // 0/1 or "0"/"1"
try {
extraParams = JSON.parse(decodeURIComponent(extraParamsString));
} catch (e) {
// assume the parameter is an int/string describing whether to use multi row evolution
}
SegmentedVisitorLog.show(apiMethod, segment, extraParams);
};
DataTable_RowActions_Registry.register({
name: actionName,
dataTableIcon: 'plugins/Live/images/visitorlog.png',
dataTableIconHover: 'plugins/Live/images/visitorlog-hover.png',
order: 30,
dataTableIconTooltip: [
_pk_translate('Live_RowActionTooltipTitle'),
_pk_translate('Live_RowActionTooltipDefault')
],
isAvailableOnReport: function (dataTableParams, undefined) {
return true;
},
isAvailableOnRow: function (dataTableParams, tr) {
var value = getRawSegmentValueFromRow(tr)
if ('undefined' === (typeof value)) {
return false;
}
var reportTitle = null;
var apiMethod = $(tr).parents('div.dataTable').last().attr('data-report');
var dimension = getDimensionFromApiMethod(apiMethod);
if (dimension) {
reportTitle = _pk_translate('Live_RowActionTooltipWithDimension', [dimension])
} else {
reportTitle = _pk_translate('Live_RowActionTooltipDefault');
}
this.dataTableIconTooltip[1] = reportTitle;
return true;
},
createInstance: function (dataTable, param) {
if (dataTable !== null && typeof dataTable.segmentVisitorLogInstance != 'undefined') {
return dataTable.segmentVisitorLogInstance;
}
if (dataTable === null && param) {
// when segmented visitor log is triggered from the url (not a click on the data table)
// we look for the data table instance in the dom
var report = param.split(':')[0];
var tempTable = getDataTableFromApiMethod(report);
if (tempTable) {
dataTable = tempTable;
if (typeof dataTable.segmentVisitorLogInstance != 'undefined') {
return dataTable.segmentVisitorLogInstance;
}
}
}
var instance = new DataTable_RowActions_SegmentVisitorLog(dataTable);
if (dataTable !== null) {
dataTable.segmentVisitorLogInstance = instance;
}
return instance;
}
});
})();

View file

@ -1,5 +1,5 @@
/**
* Piwik - Web Analytics
* Piwik - free/libre analytics platform
*
* Visitor profile popup control.
*
@ -15,7 +15,7 @@
/**
* DataTable UI class for jqPlot graph datatable visualizations.
*
*
* @constructor
*/
exports.VisitorLog = function (element) {
@ -24,12 +24,54 @@
$.extend(exports.VisitorLog.prototype, dataTablePrototype, {
handleColumnHighlighting: function () {
},
setFixWidthToMakeEllipsisWork: function () {
},
/**
* Initializes this class.
*/
init: function () {
dataTablePrototype.init.call(this);
$('.visitorLogIconWithDetails>img').each(function () {
$(this).tooltip({
items: 'img',
track: true,
show: false,
hide: false,
content: function () {
return $('<ul>').html($('ul', $(this).closest('.visitorLogIconWithDetails')).html());
},
tooltipClass: 'small',
open: function () {
tooltipIsOpened = true;
},
close: function () {
tooltipIsOpened = false;
}
});
});
$('.visitorLogTooltip').each(function () {
$(this).tooltip({
track: true,
show: false,
hide: false,
tooltipClass: 'small',
open: function () {
tooltipIsOpened = true;
},
close: function () {
tooltipIsOpened = false;
}
});
});
// Replace duplicated page views by a NX count instead of using too much vertical space
$("ol.visitorLog").each(function () {
var prevelement;
@ -44,7 +86,7 @@
if (repeat.length) {
repeat.html((parseInt(repeat.html()) + 1) + "x");
} else {
prevelement.append($("<em>2x</em>").attr({'class': 'repeat', 'title': _pk_translate('Live_PageRefreshed')}));
prevelement.find('>div').prepend($("<em>2x</em>").attr({'class': 'repeat', 'title': _pk_translate('Live_PageRefreshed')}));
}
$(this).hide();
} else {
@ -56,7 +98,7 @@
var tooltipIsOpened = false;
$('a', $this).on('focus', function () {
// see http://dev.piwik.org/trac/ticket/4099
// see https://github.com/piwik/piwik/issues/4099
if (tooltipIsOpened) {
$this.tooltip('close');
}
@ -86,4 +128,4 @@
}
});
})(jQuery, require);
})(jQuery, require);

View file

@ -1,5 +1,5 @@
/**
* Piwik - Web Analytics
* Piwik - free/libre analytics platform
*
* Visitor profile popup control.
*
@ -15,7 +15,7 @@
/**
* 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
@ -39,7 +39,7 @@
* 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) {
@ -50,7 +50,7 @@
if ($('.RealTimeMap').length > 0) {
url += '&showMap=0';
}
Piwik_Popover.createPopupAndLoadUrl(url, _pk_translate('Live_VisitorProfile'), 'visitor-profile-popup');
};
@ -133,6 +133,28 @@
$exportLink.css('visibility', 'hidden');
}
});
var tooltipIsOpened = false;
$('a', $element).on('focus', function () {
// see https://github.com/piwik/piwik/issues/4099
if (tooltipIsOpened) {
$element.tooltip('close');
}
});
$element.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; }
});
},
toggleMap: function () {
@ -251,7 +273,7 @@
},
_loadNextVisitor: function () {
this._gotoAdjacentVisitor(this.$element.attr('data-next-visitor'));
this._gotoAdjacentVisitor(this.$element.attr('data-next-visitor'));
},
_gotoAdjacentVisitor: function (idVisitor) {

View file

@ -0,0 +1,12 @@
{
"Live": {
"GoalType": "النوع",
"KeywordRankedOnSearchResultForThisVisitor": "الكلمة الدلالية %1$s كان ترتيبها %2$s في %3$s في صفحة نتائج البحث لهذا الزائر.",
"LastHours": "آخر %s ساعة",
"LastMinutes": "آخر %s دقيقة",
"LinkVisitorLog": "مشاهدة سجل تفصيلي للزائر",
"Referrer_URL": "جاء من",
"VisitorLog": "سجل الزائر",
"VisitorsInRealTime": "الزوار في الوقت الحقيقي"
}
}

View file

@ -0,0 +1,13 @@
{
"Live": {
"GoalType": "Тып",
"KeywordRankedOnSearchResultForThisVisitor": "Ключавое слова %1$s заняла %2$s на %3$s старонцы вынікаў пошуку для гэтага наведвальніка",
"LastHours": "Апошніх %s гадзін",
"LastMinutes": "Апошніх %s хвілін",
"LinkVisitorLog": "Паглядзець падрабязны запіс наведвальнікаў",
"Referrer_URL": "URL спасыльніка",
"VisitorLog": "Запіс наведвальнікаў",
"VisitorLogDocumentation": "Гэтая табліца паказвае апошнія наведванні ў межах выбранага дыяпазону дат. %s Калі дыяпазон дат ўключае ў сябе сёння, то вы можаце ўбачыць вашых наведвальнікаў у рэжыме рэальнага часу! %s Дадзеныя, якія адлюстроўваюцца тут заўсёды \"жывыя\", незалежна ад таго, і як часта вы карыстаецеся cron архіваваннем.",
"VisitorsInRealTime": "Наведвальнікаў у рэжыме рэальнага часу"
}
}

View file

@ -0,0 +1,34 @@
{
"Live": {
"AveragePageGenerationTime": "Всяка страница отнема средно по %1$s, за да бъде заредена от този посетител.",
"ClickToViewMoreAboutVisit": "Щракнете, за да видите повече информация за това посещение",
"ConvertedNGoals": "Конвертирани %s цели",
"FirstVisit": "Първо посещение",
"GoalType": "Тип",
"HideMap": "скриване на картата",
"LastHours": "Последните %s часа",
"LastMinutes": "Последните %s минути",
"LastVisit": "Последно посещение",
"LinkVisitorLog": "Виж детайли на лога с посетителите",
"LoadMoreVisits": "Зареждане на повече посещения",
"MorePagesNotDisplayed": "повечето страници от този посетител не се показват",
"NbVisitor": "1 посетител",
"NbVisitors": "%s посетители",
"NextVisitor": "Следващ посетител",
"NoMoreVisits": "Няма повече посещения за този посетител.",
"PageRefreshed": "Броят пъти, които тази страница е гледана \/ обновена в ред.",
"PreviousVisitor": "Предишен посетител",
"RealTimeVisitorCount": "Броене на посетителите в реално време",
"Referrer_URL": "URL Референции",
"ShowMap": "покажи картата",
"SimpleRealTimeWidget_Message": "%s и %s в последното %s",
"ViewVisitorProfile": "Преглед профила на посетителя",
"VisitedPages": "Посетени страници",
"VisitorLog": "Статистика за посетителя",
"VisitorLogDocumentation": "Тази таблица показва последните посещения, включени в избраният обхват от време. Можете да видите кога се е случило последното посещение на посетител, като посочите върху датата на посещението. %s Ако обхвата на датата включва днешния ден, можете да видите вашите посетители в реално време! %s Информацията тук винаги е в реално време, независимо дали и колко често използвате инструментите за архивиране.",
"VisitorProfile": "Профил на посетителя",
"VisitorsInRealTime": "Посетители в реално време",
"VisitorsLastVisit": "Последното посещение от този посетител беше от преди %s дни.",
"VisitsFrom": "%1$s%2$s посещения%3$s от"
}
}

View file

@ -0,0 +1,16 @@
{
"Live": {
"GoalType": "Tipus",
"KeywordRankedOnSearchResultForThisVisitor": "La paraula clau %1$s està a la posició %2$s del ranking de %3$s resultats de cerca per aquest visitant.",
"LastHours": "Últimes %s hores",
"LastMinutes": "Últims %s minuts",
"LinkVisitorLog": "Veure registre detallat del visitant",
"MorePagesNotDisplayed": "No es mostre més pàgines per aquest visitant",
"PageRefreshed": "Nombre de vegades que s'ha vist\/refrescat una pàgina",
"Referrer_URL": "URL del referent",
"VisitorLog": "Registre de visitants",
"VisitorLogDocumentation": "Aquesta taula mostra les últimes visites pel període de temps seleccionat. Podeu veure quan s'ha produït l'últim accès d'un visitant pasant per damunt de la data de visita. %s Si el període de temps inclou avui, podeu veure els visitants en temps real! %s La informació que es mostra és sempre en directa, sense dependre de cada quan executeu el treball programat d'arxivat.",
"VisitorsInRealTime": "Visitants en temps real",
"VisitorsLastVisit": "L'ultima visita d'aquest visitant va ser fa %s dies."
}
}

View file

@ -0,0 +1,47 @@
{
"Live": {
"AbandonedCartSummary": "%1$s opuštěných košíků%2$s a %3$s opuštěných položek%4$s celkem za %5$s%6$s.",
"AveragePageGenerationTime": "Pro tohoto návštěvníka se každá stránka načetla v průměru za %1$s.",
"CalculatedOverNPageViews": "Vypočítáno na základě %1$s posledních zobrazení stránek tohoto návštěvníka.",
"ClickToViewMoreAboutVisit": "Klikněte pro zobrazení více informací o této návštěvě",
"ConvertedNGoals": "Proměněno %s cílů",
"EcommerceSummaryConversions": "%1$s objednávek%2$s celkem za %3$s%4$s, zakoupeno %5$s položek%6$s.",
"FirstVisit": "První návštěva",
"GoalType": "Typ",
"HideMap": "skrýt mapu",
"KeywordRankedOnSearchResultForThisVisitor": "Klíčové slovo %1$s ohodnoceno %2$s na %3$s stránce výsledků vyhledávání pro tohoto návštěvníka",
"LastHours": "Posledních %s hodin",
"LastMinutes": "Posledních %s minut",
"LastVisit": "Poslední návštěva",
"LinkVisitorLog": "Zobrazit detailní pohled na návštěvníky",
"LoadMoreVisits": "Načíst více návštěv",
"MorePagesNotDisplayed": "další stránky tohoto návštěvníka nejsou zobrazeny",
"NbVisitor": "1 návštěvník",
"NbVisitors": "%s návštěvníků",
"NextVisitor": "Další návštěvník",
"NoMoreVisits": "Pro tohoto návštěvníka už nejsou k dispozici další návštěvy.",
"PageRefreshed": "Počet po sobě jdoucích zobrazení\/obnovení stránky",
"PluginDescription": "Poskytuje živý záznam návštěvníků a ve widgetu na nástěnce vám umožňuje ho sledovat živě v reálném čase. Zásuvný modul také umožňuje zobrazit profil pro každého návštěvníka.",
"PreviousVisitor": "Předchozí návštěvník",
"RealTimeVisitorCount": "Počet návštěvníků v reálném čase",
"Referrer_URL": "Odkazující URL",
"ShowMap": "Zobrazit mapu",
"SimpleRealTimeWidget_Message": "%s a %s v posledních %s.",
"ViewVisitorProfile": "Zobrazit profil návštěvníka",
"VisitedPages": "Navštívené stránky",
"VisitorLog": "Pohled na návštěvníky",
"VisitorLogDocumentation": "Tato tabulka zobrazuje poslední návštěvy v daném období. Najetím na datum návštěvy zobrazíte čas, kdy tento návštěvník navštívil stránky naposledy. %s Pokud období zahrnuje dnešek, můžete vidět návštěvníky v reálném čase! %s Zde zobrazovaná data jsou vždy živá bez ohledu na to, jak a kdy probíhá archivační cron úloha.",
"VisitorProfile": "Profil návštěvníka",
"VisitorsInRealTime": "Návštěvníci v reálném čase",
"VisitorsLastVisit": "K poslední návštěvě tohoto návštěvníka došlo před %s dny.",
"VisitsFrom": "%1$s%2$s návštěv%3$s z",
"VisitSummary": "Strávil celkem %1$s%2$s na stránkách%3$s, a zobrazil %4$s stránek%5$s při %6$s návštěvách%7$s.",
"VisitSummaryWithActionDetails": "Strávil celkem %1$s%2$s na stránkách%3$s, a provedl %4$s akcí%5$s (%6$s) při %7$s návštěvách%8$s.",
"RowActionTooltipDefault": "Zobrazit záznam návštěvníků rozdělený podle tohoto řádku",
"RowActionTooltipWithDimension": "Zobrazit záznam návštěvníků rozdělený podle %s",
"RowActionTooltipTitle": "Otevřít rozdělený záznam návštěvníků",
"SegmentedVisitorLogTitle": "Záznam návštěvníků, zobrazující návštěvy, kde %s je \"%s\"",
"OnClickPause": "%s běží. Klikněte pro pozastavení.",
"OnClickStart": "%s je zastaven. Klikněte pro spuštění."
}
}

View file

@ -0,0 +1,40 @@
{
"Live": {
"AveragePageGenerationTime": "Det tog gennemsnitligt %1$s at indlæse hver side for denne besøgende.",
"CalculatedOverNPageViews": "Beregnet på baggrund af denne besøgendes seneste %1$s sidevisninger.",
"ClickToViewMoreAboutVisit": "Klik for at se mere information om dette besøg",
"ConvertedNGoals": "Omregnet %s mål",
"FirstVisit": "Første besøg",
"GoalType": "Type",
"HideMap": "skjul kort",
"KeywordRankedOnSearchResultForThisVisitor": "Nøgleordet %1$s blev rangeret %2$s på %3$s side med søgeresultater for denne besøgende",
"LastHours": "Sidste %s timer",
"LastMinutes": "Sidste %s minutter",
"LastVisit": "Seneste besøg",
"LinkVisitorLog": "Vis detaljeret besøgslog",
"LoadMoreVisits": "Indlæs flere besøg",
"MorePagesNotDisplayed": "yderligere sider vises ikke for denne besøgende",
"NbVisitor": "1 besøgende",
"NbVisitors": "%s besøgende",
"NextVisitor": "Næste besøgende",
"NoMoreVisits": "Der er ikke flere besøg fra denne besøgende.",
"PageRefreshed": "Antal gange siden er blevet vist \/ opdateret i træk.",
"PreviousVisitor": "Forrige besøgende",
"RealTimeVisitorCount": "Tidstro besøgsantal",
"Referrer_URL": "Henvisning netadresse",
"ShowMap": "vis kort",
"SimpleRealTimeWidget_Message": "%s og %s i den sidste %s.",
"ViewVisitorProfile": "Vis besøgendes profil",
"VisitedPages": "Besøgte sider",
"VisitorLog": "Besøgslog",
"VisitorLogDocumentation": "Tabelen viser de nyeste besøg inden for det valgte datointerval.%s Hvis datointervallet omfatter i dag, kan du se dine besøgende i virkelig tid! %s Data, der vises her er altid live, uanset hvor ofte arkiveringsjob kører.",
"VisitorProfile": "Besøgendes profil",
"VisitorsInRealTime": "Besøgende her og nu",
"VisitorsLastVisit": "Besøgendes seneste besøg var for %s dage siden.",
"VisitsFrom": "%1$s%2$s besøg %3$s fra",
"RowActionTooltipDefault": "Vis besøgslog opdelt efter denne række",
"RowActionTooltipWithDimension": "Vis besøgslog opdelt efter %s",
"RowActionTooltipTitle": "Åben segmenteret besøgslog",
"SegmentedVisitorLogTitle": "Besøgslog viser besøg, hvor %s er \\\"%s\\\""
}
}

View file

@ -0,0 +1,44 @@
{
"Live": {
"AbandonedCartSummary": "%1$s verlassene Warenkörbe%2$s und %3$s verlassene Produkte%4$s im Gesamtwert von %5$s%6$s.",
"AveragePageGenerationTime": "Jede Seite benötigte im Durchschnitt %1$s um bei diesem Besucher zu laden.",
"CalculatedOverNPageViews": "Berechnet mithilfe der letzten %1$s Seitenansichten dieses Besuchers.",
"ClickToViewMoreAboutVisit": "Klicken Sie um nähere Informationen zu diesem Besuch zu sehen",
"ConvertedNGoals": "%s Ziele konvertiert",
"FirstVisit": "Erster Besuch",
"GoalType": "Typ",
"HideMap": "Karte ausblenden",
"KeywordRankedOnSearchResultForThisVisitor": "Der Begriff %1$s war für diesen Besucher auf Platz %2$s der Suchergebnisseite von %3$s",
"LastHours": "Letzte %s Stunden",
"LastMinutes": "Letzte %s Minuten",
"LastVisit": "Letzter Besuch",
"LinkVisitorLog": "detailiertes Besucher-Log anzeigen",
"LoadMoreVisits": "Mehr Besuche laden",
"MorePagesNotDisplayed": "weitere Seiten von diesem Besucher werden nicht angezeigt",
"NbVisitor": "1 Besucher",
"NbVisitors": "%s Besucher",
"NextVisitor": "Nächster Besucher",
"NoMoreVisits": "Für diesen Besucher sind keine weiteren Besuche vorhanden.",
"PageRefreshed": "Die Anzahl wie oft diese Seite hintereinander angeschaut\/aktualisiert worden ist.",
"PluginDescription": "Unterstützt den Live Besucher-Log und lässt Sie Ihre Besucher live im Echtzeit-Dashboard-Widget anzeigen. Das Plugin lässt Sie auch ein Besucherprofil für einen vorgegebenen Benutzer anzeigen.",
"PreviousVisitor": "Vorheriger Besucher",
"RealTimeVisitorCount": "Echtzeit-Besucherzähler",
"Referrer_URL": "Herkunftsseite",
"ShowMap": "Karte einblenden",
"SimpleRealTimeWidget_Message": "%s und %s in den letzten %s.",
"ViewVisitorProfile": "Besucherprofil ansehen",
"VisitedPages": "Besuchte Seiten",
"VisitorLog": "Besucher-Log",
"VisitorLogDocumentation": "Diese Tabelle zeigt die letzten Besuche innerhalb der ausgewählten Periode. Sie können sehen, wann der Besucher die Seite zuletzt besucht hat, wenn Sie die Maus über das Datum eines Besuches bewegen. %s Wenn die gewählte Periode den heutigen Tag enthält, können Sie die Besuche in Echtzeit sehen! %s Die angezeigten Daten sind immer live, egal ob und in welchem Intervall Sie den Archivierungs-Cronjob verwenden.",
"VisitorProfile": "Besucherprofil",
"VisitorsInRealTime": "Besucher in Echtzeit",
"VisitorsLastVisit": "Der letzte Besuch dieses Besuchers war vor %s Tagen.",
"VisitsFrom": "%1$s%2$s Besuche%3$s aus",
"RowActionTooltipDefault": "Zeige Besucher-Log aufgeteilt durch diese Zeile",
"RowActionTooltipWithDimension": "Zeige Besucher-Log segmentiert durch %s",
"RowActionTooltipTitle": "Log segmentierter Besucher öffnen",
"SegmentedVisitorLogTitle": "Besucher-Log zeigt Besuche bei denen gilt: %s ist \\\"%s\\\"",
"OnClickPause": "%s ist gestartet. Klicken Sie um zu pausieren.",
"OnClickStart": "%s ist gestoppt. Klicken Sie um zu starten."
}
}

View file

@ -0,0 +1,47 @@
{
"Live": {
"AbandonedCartSummary": "%1$s εγκαταλελειμμένα καλάθια αγορών%2$s και %3$s εγκαταλελειμμένα αντικείμενα%4$s με συνολική αξία %5$s%6$s.",
"AveragePageGenerationTime": "Κάθε σελίδα χρειάστηκε κατά μέσο όρο %1$s για να φορτωθεί στον επισκέπτη.",
"CalculatedOverNPageViews": "Έγινε υπολογισμός βάση των τελευταίων %1$s αναγνώσεων σελίδων του επισκέπτη.",
"ClickToViewMoreAboutVisit": "Κάντε κλικ για να δείτε περισσότερες λεπτομέρειες γύρω από την επίσκεψη",
"ConvertedNGoals": "Μετατράπηκαν %s Στόχοι",
"EcommerceSummaryConversions": "%1$s αγορές%2$s για ένα σύνολο %3$s%4$s, με αγορασμένα %5$s αντικείμενα%6$s.",
"FirstVisit": "Πρώτη επίσκεψη",
"GoalType": "Τύπος",
"HideMap": "απόκρυψη χάρτη",
"KeywordRankedOnSearchResultForThisVisitor": "Η λέξη-κλειδί %1$s βαθμολογήθηκε ως %2$s στη σελίδα αποτελεσμάτων αναζήτησης %3$s για αυτόν τον επισκέπτη",
"LastHours": "Τις τελευταίες %s ώρες",
"LastMinutes": "Τα τελευταία %s λεπτά",
"LastVisit": "Τελευταία επίσκεψη",
"LinkVisitorLog": "Προβολή λεπτομερούς καταγραφής επισκεπτών",
"LoadMoreVisits": "Να φορτωθούν περισσότερες επισκέψεις",
"MorePagesNotDisplayed": "περισσότερες σελίδες από αυτόν τον επισκέπτη δεν προβάλλονται",
"NbVisitor": "1 επισκέπτης",
"NbVisitors": "%s επισκέπτες",
"NextVisitor": "Επόμενος επισκέπτης",
"NoMoreVisits": "Δεν υπάρχουν άλλες επισκέψεις για αυτόν τον επισκέπτη.",
"PageRefreshed": "Φορές που η σελίδα προβλήθηκε \/ ανανεώθηκε στη σειρά.",
"PluginDescription": "Παρέχει ζωντανό Ημερολόγιο Επισκεπτών και επιτρέπει να παρακολουθείτε τους επισκέπτες στο γραφικό συστατικό πραγματικού χρόνου του πίνακα. Το πρόσθετο επίσης επιτρέπει να δείτε οποιοδήποτε προφίλ Επισκέπτη για οποιοδήποτε χρήστη.",
"PreviousVisitor": "Προηγούμενος επισκέπτης",
"RealTimeVisitorCount": "Αριθμός επισκεπτών σε πραγματικό χρόνο",
"Referrer_URL": "Διεύθυνση URL παραπομπού",
"ShowMap": "εμφάνιση χάρτη",
"SimpleRealTimeWidget_Message": "%s και %s στα τελευταία %s.",
"ViewVisitorProfile": "Εμφάνιση του προφίλ επισκέπτη",
"VisitedPages": "Σελίδες που έχουν επισκεφθεί",
"VisitorLog": "Καταγραφή Επισκεπτών",
"VisitorLogDocumentation": "Αυτός ο πίνακας δείχνει τις τελευταίες επισκέψεις εντός της επιλεγμένης χρονικής περιόδου. Μπορείτε να δείτε πότε έγινε η τελευταία επίσκεψη του επισκέπτη μετακινόντας το ποντίκι στην ημερομηνία της επίσκεψης.%s Αν η χρονική περίοδος περιλαμβάνει τη σημερινή μέρα, μπορείτε να δείτε τους επισκέπτες σας σε πραγματικό χρόνο! %s Τα δεδομένα που εμφανίζονται εδώ είναι σε απευθείας μετάδοση, ανεξάρτητα αν και πόσο συχνά χρησιμοποιείτε την εργασία αρχειοθέτησης.",
"VisitorProfile": "Προφίλ επισκέπτη",
"VisitorsInRealTime": "Επισκέπτες σε πραγματικό χρόνο",
"VisitorsLastVisit": "Η τελευταία επίσκεψη αυτού του επισκέπτη ήταν πριν από %s ημέρες.",
"VisitsFrom": "%1$s%2$s επισκέψεις%3$s από",
"VisitSummary": "Σπαταλήθηκε συνολικός χρόνος %1$s%2$s στον ιστοτόπο %3$s και αναγνώστηκαν %4$s σελίδες%5$s σε %6$s επισκέψεις%7$s.",
"VisitSummaryWithActionDetails": "Σπαταλήθηκε συνολικός χρόνος %1$s%2$s στον ιστοτόπο %3$s και έγιναν %4$s ενέργειες%5$s (%6$s) σε %7$s επισκέψεις%8$s.",
"RowActionTooltipDefault": "Εμφάνιση του Ημερολογίου Επισκεπτών κατατμημένο σε αυτή τη γραμμή",
"RowActionTooltipWithDimension": "Εμφάνιση του Ημερολογίου Επισκεπτών κατατμημένο με αυτό το %s",
"RowActionTooltipTitle": "Άνοιγμα κατετμημένου Ημερολογίου Επισκεπτών",
"SegmentedVisitorLogTitle": "Ημερολόγιο επισκεπτών που δείχνει επισκέψεις όπου το %s είναι \"%s\"",
"OnClickPause": "Το %s έχει ξεκινήσει. Πατήστε για παύση.",
"OnClickStart": "Το %s είναι σταματημένο. Πατήστε για εκκίνηση."
}
}

View file

@ -0,0 +1,47 @@
{
"Live": {
"AbandonedCartSummary": "%1$s abandoned carts%2$s and %3$s abandoned items%4$s worth a total of %5$s%6$s.",
"AveragePageGenerationTime": "Each page took on average %1$s to load for this visitor.",
"CalculatedOverNPageViews": "Calculated using this visitor's last %1$s page views.",
"ClickToViewMoreAboutVisit": "Click to view more information about this visit",
"ConvertedNGoals": "Converted %s Goals",
"EcommerceSummaryConversions": "%1$s orders%2$s for a total of %3$s%4$s, purchased %5$s items%6$s.",
"FirstVisit": "First visit",
"GoalType": "Type",
"HideMap": "hide map",
"KeywordRankedOnSearchResultForThisVisitor": "The keyword %1$s was ranked %2$s on the %3$s search result page for this visitor",
"LastHours": "Last %s hours",
"LastMinutes": "Last %s minutes",
"LastVisit": "Last visit",
"LinkVisitorLog": "View detailed visitor log",
"LoadMoreVisits": "Load more visits",
"MorePagesNotDisplayed": "more pages by this visitor are not displayed",
"NbVisitor": "1 visitor",
"NbVisitors": "%s visitors",
"NextVisitor": "Next visitor",
"NoMoreVisits": "There are no more visits for this visitor.",
"PageRefreshed": "Number of times this page was viewed \/ refreshed in a row.",
"PluginDescription": "Provides the live Visitor Log and lets you watch your visitors live in the real-time dashboard widget. The plugin also lets you see a Visitor profile for any given user.",
"PreviousVisitor": "Previous visitor",
"RealTimeVisitorCount": "Real Time Visitor Count",
"Referrer_URL": "Referrer URL",
"ShowMap": "show map",
"SimpleRealTimeWidget_Message": "%1$s and %2$s in the last %3$s",
"ViewVisitorProfile": "View visitor profile",
"VisitedPages": "Visited pages",
"VisitorLog": "Visitor Log",
"VisitorLogDocumentation": "This table shows the latest visits within the selected date range. You can see when a visitor's last visit occurred by hovering over the date of a visit. %1$s If the date range includes today, you can see your visitors real time! %2$s The data displayed here is always live, regardless of whether and how often you are using the archiving cron job.",
"VisitorProfile": "Visitor profile",
"VisitorsInRealTime": "Visitors in Real-time",
"VisitorsLastVisit": "This visitor's last visit was %s days ago.",
"VisitsFrom": "%1$s%2$s visits%3$s from",
"VisitSummary": "Spent a total of %1$s%2$s on the website%3$s, and viewed %4$s pages%5$s in %6$s visits%7$s.",
"VisitSummaryWithActionDetails": "Spent a total of %1$s%2$s on the website%3$s, and performed %4$s actions%5$s (%6$s) in %7$s visits%8$s.",
"RowActionTooltipDefault": "Show Visitor Log segmented by this row",
"RowActionTooltipWithDimension": "Show Visitor Log segmented by this %s",
"RowActionTooltipTitle": "Open segmented Visitor Log",
"SegmentedVisitorLogTitle": "Visitor Log showing visits where %1$s is \"%2$s\"",
"OnClickPause": "%s is started. Click to pause.",
"OnClickStart": "%s is stopped. Click to start."
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "Cada página tomó en promedio %1$s para cargar este visitante.",
"CalculatedOverNPageViews": "Calculado usando las últimas %1$s páginas vistas de este visitante.",
"ClickToViewMoreAboutVisit": "Clic para ver más información acerca de esta visita.",
"ConvertedNGoals": "%s Objetivos convertidos",
"FirstVisit": "Primer visita",
"GoalType": "Tipo",
"HideMap": "ocultar mapa",
"KeywordRankedOnSearchResultForThisVisitor": "La palabra clave %1$s fue posicionada %2$s en la página de resultados de búsqueda %3$s para este visitante",
"LastHours": "Últimas %s horas",
"LastMinutes": "Últimos %s minutos",
"LastVisit": "Última visita",
"LinkVisitorLog": "Ver registro detallado del visitante",
"LoadMoreVisits": "Cargar más visitas",
"MorePagesNotDisplayed": "más páginas de este visitante no son mostradas",
"NbVisitor": "1 visitante",
"NbVisitors": "%s visitantes",
"NextVisitor": "Siguiente visitante",
"NoMoreVisits": "No hay más visitas de este visitante.",
"PageRefreshed": "Número de veces que esta página ha sido vista\/actualizada en una fila",
"PluginDescription": "Suministra el Registro de Visitante en vivo y le permite observar a sus visitantes en tiempo real en el reproductor del tablero correspondiente. El complemento también le permite observar un perfil de visitante para cualquier usuario dado.",
"PreviousVisitor": "Visitante anterior",
"RealTimeVisitorCount": "Contador de visitantes en tiempo real",
"Referrer_URL": "URL de referencia",
"ShowMap": "mostrar mapa",
"SimpleRealTimeWidget_Message": "%s y %s en los últimos %s",
"ViewVisitorProfile": "Ver perfil de visitante",
"VisitedPages": "Páginas vsitadas",
"VisitorLog": "Registro de visitantes",
"VisitorLogDocumentation": "Esta tabla muestra las últimas visitas en el rango de fecha seleccionado. Puede ver cuando fue la última visita de un visitante posicionándose sobre la fecha de la visita. %s Si el rango de fecha incluye el día actual, ¡puede ver a sus visitantes en tiempo real! %s La información mostrada aquí es siempre en tiempo real, independientemente de si y con qué frecuencia está utilizando un cron archivando.",
"VisitorProfile": "Perfil de visitante",
"VisitorsInRealTime": "Visitantes en tiempo real",
"VisitorsLastVisit": "La última visita de este visitante fue hace %s días.",
"VisitsFrom": "%1$s%2$s visitas%3$s desde",
"RowActionTooltipDefault": "Mostrar registro de visitante segmentado por esta fila",
"RowActionTooltipWithDimension": "Mostrar registro de visitante segmentado por este %s",
"RowActionTooltipTitle": "Abrir registro de visitante segmentado",
"SegmentedVisitorLogTitle": "Mostrando visitas del Registro de Visitante donde %s es \"%s\"",
"OnClickPause": "%s está iniciado. Clic para pausar.",
"OnClickStart": "%s está detenido. Clic para iniciar."
}
}

View file

@ -0,0 +1,34 @@
{
"Live": {
"AveragePageGenerationTime": "Iga lehe laadimine võttis antud külastajal keskeltläbi %1$s.",
"CalculatedOverNPageViews": "Arvutatud kasutades külastaja viimast %1$s lehe kuvamist.",
"ClickToViewMoreAboutVisit": "Vali, et kuvada külastuse kohta rohkem infot",
"ConvertedNGoals": "Muutus tulutoovaks %s korral",
"FirstVisit": "Esimene külastus",
"GoalType": "Tüüp",
"HideMap": "peida kaart",
"LastHours": "Viimased %s tundi",
"LastMinutes": "Viimased %s minutit",
"LastVisit": "Viimane külastus",
"LinkVisitorLog": "Vaata detailsemat külastuste logi",
"LoadMoreVisits": "Ava rohkem külastusi",
"MorePagesNotDisplayed": "rohkem lehti külastaja kohta ei kuvata",
"NbVisitor": "1 külastaja",
"NbVisitors": "%s külastajat",
"NextVisitor": "Järgmine külastaja",
"NoMoreVisits": "Antud külastajalt rohkem külastusi ei leitud.",
"PageRefreshed": "Arv, mitu korda antud lehte vaadati \/ värskendati järjest",
"PreviousVisitor": "Eelmine külastaja",
"RealTimeVisitorCount": "Reaalajas külastuste arv",
"Referrer_URL": "Viitaja URL",
"ShowMap": "kuva kaart",
"SimpleRealTimeWidget_Message": "%s ja %s perioodis viimased %s",
"ViewVisitorProfile": "Kuva külastaja profiil",
"VisitedPages": "Külastatud lehed",
"VisitorLog": "Külastuste logi",
"VisitorProfile": "Külastaja profiil",
"VisitorsInRealTime": "Reaalajas külastused",
"VisitorsLastVisit": "Antud külastaja viimane külastus lehel oli %s päeva tagasi.",
"VisitsFrom": "%1$s%2$s külastust %3$s pärineb"
}
}

View file

@ -0,0 +1,6 @@
{
"Live": {
"Referrer_URL": "URL erreferentea",
"VisitorLog": "Bisitariaren loga"
}
}

View file

@ -0,0 +1,35 @@
{
"Live": {
"AveragePageGenerationTime": "هر صفحه به طور متوسط %1$s زمان می برد تا برای بیننده بارگزاری شود.",
"CalculatedOverNPageViews": "محاسبه شده بر اسا آخرید بازدیدکنندگان %1$s بازدید.",
"ClickToViewMoreAboutVisit": "برای مشاهده اطلاعات بیشتر درباره این بازدید کلیک کنید",
"ConvertedNGoals": "تبدیل %s اهداف",
"FirstVisit": "بازدید اول",
"GoalType": "نوع",
"HideMap": "پنهان کردن نقشه",
"LastHours": "آخرین %s ساعات",
"LastMinutes": "آخرین %s دقایق",
"LastVisit": "اخرین بازدید",
"LinkVisitorLog": "مشاهده جزئیات لاگ بازدید کننده",
"LoadMoreVisits": "نمایش بازدیدهای بیشتر",
"MorePagesNotDisplayed": "صفحات بیشتر به این بازدید کننده ها نشان داده نمی شود",
"NbVisitor": "یک ویزیتور",
"NbVisitors": "%s ویزیتور",
"NextVisitor": "دیدن کننده بعدی",
"NoMoreVisits": "این بازدید کننده، بازدید دیگری نداشته است.",
"PageRefreshed": "تعداد بار این صفحه \/ مشاهده شده در یک ردیف تجدید شد.",
"PreviousVisitor": "بازدید کننده قبلی",
"RealTimeVisitorCount": "زمان واقعی تعداد بازدید کنندگان",
"Referrer_URL": "URL بازگشتی",
"ShowMap": "نمایش نقشه",
"SimpleRealTimeWidget_Message": "%s و %s در اخرین %s",
"ViewVisitorProfile": "مشاهده نمایه دیدن کننده",
"VisitedPages": "صفحات بازدید شده",
"VisitorLog": "لاگ بازدیدکننده",
"VisitorProfile": "مشخصات بازدید کننده",
"VisitorsInRealTime": "بازدیدهای زنده",
"VisitorsLastVisit": "بازدید گذشته ی این بازدیدکننده %s روز پیش بوده است.",
"VisitsFrom": "%1$s%2$s بازدیدکننده%3$s از",
"OnClickStart": "%s متوقف شد, اینجا کلیک کنید"
}
}

View file

@ -0,0 +1,36 @@
{
"Live": {
"AveragePageGenerationTime": "Jokainen sivun lataaminen kesti keskimäärin %1$s tälle käyttäjälle.",
"CalculatedOverNPageViews": "Laskettu käyttämällä tämän kävijän %1$s viimeisintä nähtyä sivua.",
"ClickToViewMoreAboutVisit": "Klikkaa saadaksesi lisätietoa tästä käynnistä",
"ConvertedNGoals": "Konvertoidut %s tavoitteet",
"FirstVisit": "Ensimmäinen käynti",
"GoalType": "Tyyppi",
"HideMap": "piilota kartta",
"KeywordRankedOnSearchResultForThisVisitor": "Hakusana %1$s oli %2$s. sivulla %3$s hakukoneen tuloksissa tälle kävijälle.",
"LastHours": "Edelliset %s tuntia",
"LastMinutes": "Edelliset %s minuuttia",
"LastVisit": "Edellinen käynti",
"LinkVisitorLog": "Katso yksityiskohtainen vierailijan loki",
"LoadMoreVisits": "Lataa lisää käyntejä",
"MorePagesNotDisplayed": "muita sivuja tältä kävijältä ei näytetä",
"NbVisitor": "1 kävijä",
"NbVisitors": "%s kävijää",
"NextVisitor": "Seuraava kävijä",
"NoMoreVisits": "Ei enempää käyntejä tälle kävijälle.",
"PageRefreshed": "Montako kertaa tätä sivua katsottiin \/ päivitettiin peräkkäin.",
"PreviousVisitor": "Edellinen kävijä",
"RealTimeVisitorCount": "Reaaliaikainen kävijälaskuri",
"Referrer_URL": "Saapumisosoite",
"ShowMap": "näytä kartta",
"SimpleRealTimeWidget_Message": "%s ja %s viimeisessä %s",
"ViewVisitorProfile": "Näytä kävijäprofiili",
"VisitedPages": "Vieraillut sivut",
"VisitorLog": "Kävijän tiedot",
"VisitorLogDocumentation": "Tämä taulukko näyttää viimeisimmät käynnit valitulta aikaväliltä. Voit nähdä, koska viimeisin käynti oli pitämällä hiirtä päiväyksen päällä. %s Jos aikaväli sisältää tämän päivän, näet kävijät reaaliaikaisena! %s Näytetyt tiedot ovat aina reaaliaikaisia, riippumatta arkistoinnista.",
"VisitorProfile": "Kävijäprofiili",
"VisitorsInRealTime": "Reaaliaikaiset kävijätiedot",
"VisitorsLastVisit": "Tämän kävijän edellinen käynti oli %s päivää sitten.",
"VisitsFrom": "%1$s%2$s käyntiä%3$s lähteestä"
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "Chaque page a pris une moyenne de %1$s à charger pour ce visiteur.",
"CalculatedOverNPageViews": "Calculé sur la base des %1$s dernières pages vues par ce visiteur.",
"ClickToViewMoreAboutVisit": "Cliquez pour afficher plus d'informations à propos de cette visite",
"ConvertedNGoals": "Conversion de %s objectifs",
"FirstVisit": "Première visite",
"GoalType": "Type",
"HideMap": "Cacher la carte",
"KeywordRankedOnSearchResultForThisVisitor": "Le mot-clé %1$s a été noté %2$s dans la page de résultats de recherche %3$s pour ce visiteur",
"LastHours": "Dernières %s heures",
"LastMinutes": "Dernières %s minutes",
"LastVisit": "Dernière visite",
"LinkVisitorLog": "Voir les logs visiteurs détaillés",
"LoadMoreVisits": "Charger plus de visites",
"MorePagesNotDisplayed": "plus de pages de ce visiteur ne sont pas affichées",
"NbVisitor": "1 visiteur",
"NbVisitors": "%s visiteurs",
"NextVisitor": "Visiteur suivant",
"NoMoreVisits": "Il n'y a pas d'autres visites pour ce visiteur.",
"PageRefreshed": "Nombre de fois où cette page a été vue \/ rafraîchie d'affilée.",
"PluginDescription": "Fournit le log en temps réel des visiteurs et vous permet de visualiser vos visiteurs en temps réel au sein d'un gadget du tableau de bord. Ce composant vous permet aussi de voir le profil d'un visiteur pour n'importe quel utilisateur.",
"PreviousVisitor": "Visiteur précédent",
"RealTimeVisitorCount": "Décompte des visiteurs en temps réel",
"Referrer_URL": "URL du référent",
"ShowMap": "Montrer la carte",
"SimpleRealTimeWidget_Message": "%s et %s dans le(s)\/la dernier(s)\/ière(s) %s.",
"ViewVisitorProfile": "Afficher le profil du visiteur",
"VisitedPages": "Pages visitées",
"VisitorLog": "Log visiteur",
"VisitorLogDocumentation": "Ce tableau affiche les dernières visites parmi la période sélectionnée. %s Si la période inclut aujourd'hui, vous pouvez voir vos visiteurs en temps réel! %s Les données affichées ici le sont toujours en temps réel, indépendamment de quand et à quelle fréquence vous utilisez la tâche automatique (cron) d'archivage.",
"VisitorProfile": "Profil visiteur",
"VisitorsInRealTime": "Visiteurs en temps réel",
"VisitorsLastVisit": "La dernière visite du visiteur était il y a %s jours.",
"VisitsFrom": "%1$s%2$s visites%3$s de",
"RowActionTooltipDefault": "Afficher le logs des visiteurs segmenté par cette rangée",
"RowActionTooltipWithDimension": "Afficher le log des visiteurs segmenté en fonction de %s",
"RowActionTooltipTitle": "Ouvrir le log des visiteur segmenté",
"SegmentedVisitorLogTitle": "Log des visiteurs affichant les visites ou %s est \"%s\"",
"OnClickPause": "%s est démarré. Cliquer pour mettre en pause.",
"OnClickStart": "%s est arrêté. Cliquer pour démarrer."
}
}

View file

@ -0,0 +1,33 @@
{
"Live": {
"AveragePageGenerationTime": "प्रत्येक पृष्ठ इस आगंतुक के लिए लोड करने के लिए औसत %1$s पर ले लिया।",
"CalculatedOverNPageViews": "इस आगंतुक के अंतिम %1$s पृष्ठ विचार उपयोग कर की गणना।",
"ClickToViewMoreAboutVisit": "इस यात्रा के बारे में अधिक जानकारी देखने के लिए क्लिक करें",
"ConvertedNGoals": "लक्ष्य %s बदल दिया गया है",
"FirstVisit": "पहला दौरा",
"GoalType": "प्रकार",
"HideMap": "नक्शा छिपाना",
"KeywordRankedOnSearchResultForThisVisitor": "खोजशब्द %1$sके इस आगंतुक %2$sके लिए %3$sखोज परिणाम पृष्ठ पर स्थान दिया गया था",
"LastHours": "अंतिम %s घंटे",
"LastMinutes": "अंतिम %s मिनट",
"LastVisit": "अंतिम दौरा",
"LinkVisitorLog": "विस्तृत आगंतुक लॉग देखें",
"LoadMoreVisits": "अधिक यात्राओं लोड",
"MorePagesNotDisplayed": "इस आगंतुक से अधिक पृष्ठों को प्रदर्शित नहीं कर रहे हैं",
"NbVisitor": "1 आगंतुक",
"NbVisitors": "%s आगंतुकों",
"NextVisitor": "अगले आगंतुक",
"PageRefreshed": "यह पृष्ठ देखा गया था \/ एक पंक्ति में रिफ्रेश की संख्या।",
"RealTimeVisitorCount": "रीयल टाइम आगंतुक गणना",
"Referrer_URL": "संदर्भ URL",
"ShowMap": "नक्शा दिखाना",
"SimpleRealTimeWidget_Message": "अंतिम %s में %s और %s है.",
"VisitedPages": "पृष्ठों का दौरा",
"VisitorLog": "आगंतुक प्रवेश",
"VisitorLogDocumentation": "इस तालिका में चयनित तिथि सीमा के भीतर नवीनतम यात्राओं से पता चलता है. %sआप देख सकते हैं जब एक आगंतुक की अंतिम यात्रा की तारीख के आस-पास उत्पन्न हुई।तिथि सीमा आज भी शामिल है, तो आप अपने दर्शकों के वास्तविक समय देख सकते हैं! %s यहाँ प्रदर्शित डेटा की परवाह किए बिना कि क्या और कितनी बार आप पुरालेखण क्रॉन जॉब का उपयोग कर रहे हैं.",
"VisitorProfile": "आगंतुक रूपरेखा",
"VisitorsInRealTime": "वास्तविक समय से आगंतुक",
"VisitorsLastVisit": "इस आगंतुक की आखिरी यात्रा %s दिन पहले की बात है.",
"VisitsFrom": "%1$s%2$s से दौरा %3$s"
}
}

View file

@ -0,0 +1,15 @@
{
"Live": {
"GoalType": "Vrsta",
"HideMap": "sakrij mapu",
"LoadMoreVisits": "Učitaj više posjeta",
"NbVisitor": "1 visitor",
"NextVisitor": "Slijedeći posjetitelj",
"PreviousVisitor": "Prijašnji posjetitelj",
"Referrer_URL": "Referrer URL",
"ShowMap": "pokaži mapu",
"VisitedPages": "Posjećene stranice",
"VisitorLog": "Zapis posjeta",
"VisitorsInRealTime": "Posjete u stvarnom vremenu"
}
}

View file

@ -0,0 +1,13 @@
{
"Live": {
"GoalType": "Típus",
"KeywordRankedOnSearchResultForThisVisitor": "A %1$s kulcsszó %2$s. helyezésen jelent meg a(z) %3$s. találati lista oldalon a látogató számára.",
"LastHours": "Legutóbbi %s óra",
"LastMinutes": "Legutóbbi %s perc",
"LinkVisitorLog": "Részletes látogatói adatok megtekintése",
"Referrer_URL": "Hivatkozó URL",
"VisitorLog": "Látogatói adatok",
"VisitorLogDocumentation": "Ez a táblázat a legutolsó látogatásokat mutatja a kiválasztott időintervallumon belül.%s Ha az időintervallum tartalmazz a mai napot is, akkor élőben láthatod látogatóid! %s Az itt kijelzett adatok mindig élőek maradnak, függetlenül, hogy hogyan és milyen gyakran használod az archiválásra szolgáló cron job-ot.",
"VisitorsInRealTime": "Látogatók valós időben"
}
}

View file

@ -0,0 +1,20 @@
{
"Live": {
"GoalType": "Jenis",
"KeywordRankedOnSearchResultForThisVisitor": "Kata kunci %1$s menuduki peringkat %2$s dalam halaman pencarian %3$s untuk pengunjung ini",
"LastHours": "Jam %s terakhir",
"LastMinutes": "Menit %s terakhir",
"LinkVisitorLog": "Tampilkan catatan pengunjung lengkap",
"MorePagesNotDisplayed": "halaman lain untuk pengguna tersebut tak dapat ditampilkan",
"NbVisitor": "1 pengunjung",
"NbVisitors": "%s pengunjung",
"PageRefreshed": "Jumlah kali halaman ini ditampilkan \/ disegarkan berurutan.",
"RealTimeVisitorCount": "Jumlah Pengunjung Waktu Nyata",
"Referrer_URL": "URL Pengarah",
"SimpleRealTimeWidget_Message": "%s dan %s dalam %s terakhir.",
"VisitorLog": "Catatan Pengunjung",
"VisitorLogDocumentation": "Tabel berikut menampilkan kunjungan terakhir selama rentang tanggal yang dipilih.%s Bila hari ini termasuk dalam rentang tanggal, Anda dapat melihat pengunjung Anda dalam waktu nyata! %s Data yang ditampilkan di sini selalu ditampilkan secara langsung, tidak terpengaruh bagaimana Anda mengatur pengarsipan di tugas Cron.",
"VisitorsInRealTime": "Pengunjung dalam Waktu Nyata",
"VisitorsLastVisit": "Pengunjung ini telah berkunjung %s hari yang lalu."
}
}

View file

@ -0,0 +1,11 @@
{
"Live": {
"GoalType": "Tegund",
"LastHours": "Síðustu %s klukkustundir",
"LastMinutes": "Síðustu %s mínútur",
"LinkVisitorLog": "Skoða nákvæman heimsóknaannál",
"Referrer_URL": "URL Sendanda",
"VisitorLog": "Heimsóknaannáll",
"VisitorsInRealTime": "Gestir í rauntíma"
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "Ciascuna pagina ha richiesto una media di %1$s per essere caricata per questo visitatore.",
"CalculatedOverNPageViews": "Calcolate usando le ultime %1$s pagine visualizzate da questo visitatore.",
"ClickToViewMoreAboutVisit": "Clicca per vedere ulteriori informazioni su questa visita",
"ConvertedNGoals": "%s Obiettivi Convertiti",
"FirstVisit": "Prima visita",
"GoalType": "Tipo",
"HideMap": "nascondi mappa",
"KeywordRankedOnSearchResultForThisVisitor": "La parola chiave %1$s è stata classificata %2$s su %3$s nei risultati di ricerca per questo visitatore",
"LastHours": "Ultime %s ore",
"LastMinutes": "Ultimi %s minuti",
"LastVisit": "Ultima visita",
"LinkVisitorLog": "Visualizza un log dettagliato dei visitatori.",
"LoadMoreVisits": "Carica più visite",
"MorePagesNotDisplayed": "Altre pagine di questo visitatore non vengono visualizzate",
"NbVisitor": "1 visitatore",
"NbVisitors": "%s visitatori",
"NextVisitor": "Visitatore successivo",
"NoMoreVisits": "Non ci sono più visite per questo visitatore.",
"PageRefreshed": "Numero di volte che la pagina è stata visitata \/ aggiornata in una riga.",
"PluginDescription": "Fornisce un log dei visitatori \\\"live\\\" e ti permette di vedere i tuoi visitatori in tempo reale in un widget della dashboard. Il plugin ti permette anche di vedere un profilo visitatore per ciascuno degli utenti.",
"PreviousVisitor": "Visitatore precedente",
"RealTimeVisitorCount": "Conteggio Visitatori in Tempo Reale",
"Referrer_URL": "URL del referrer",
"ShowMap": "mostra mappa",
"SimpleRealTimeWidget_Message": "%s e %s negli ultimi %s",
"ViewVisitorProfile": "Guarda profilo visitatore",
"VisitedPages": "Pagine visitate",
"VisitorLog": "Log Visitatori",
"VisitorLogDocumentation": "Questa tabella mostra le ultime visite nell'intervallo di date selezionato. Puoi vedere quando c'è stata l'ultima visita di un visitatore posizionando il mouse sopra la data di una visita. %sSe l'intervallo di date comprende oggi, è possibile vedere in tempo reale i tuoi visitatori!%s I dati visualizzati qui sono sempre aggiornati, indipendentemente da quanto spesso usi l'archiviazione automatica.",
"VisitorProfile": "Profilo visitatore",
"VisitorsInRealTime": "Visite in tempo reale",
"VisitorsLastVisit": "L'ultima visita di questo visitatore è stata %s giorni fa.",
"VisitsFrom": "%1$s%2$s visite%3$s da",
"RowActionTooltipDefault": "Mostra Log Visitatori segmentato per questa riga",
"RowActionTooltipWithDimension": "Mostra Log Visitatori segmentato per questo %s",
"RowActionTooltipTitle": "Apri il log segmentato dei visitatori",
"SegmentedVisitorLogTitle": "Log Visitatori che mostra le visite, dove %s è \"%s\"",
"OnClickPause": "%s è avviato. Clicca per mettere in pausa.",
"OnClickStart": "%s è stato arrestato. Clicca per avviare."
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "このビジターのロードに、各ページ平均 %1$s かかりました。",
"CalculatedOverNPageViews": "このビジターの最終 %1$s ページビューを利用して計算",
"ClickToViewMoreAboutVisit": "この訪問に関する詳細情報を見るには、クリックしてください。",
"ConvertedNGoals": "変換された %s 目標",
"FirstVisit": "最初の訪問",
"GoalType": "タイプ",
"HideMap": "隠れたマップ",
"KeywordRankedOnSearchResultForThisVisitor": "キーワード %1$s はこのビジターの %3$s 検索結果ページで %2$s にランクされました。",
"LastHours": "最近の %s 時間",
"LastMinutes": "最近の %s 分",
"LastVisit": "最後の訪問",
"LinkVisitorLog": "詳細なビジターログを表示",
"LoadMoreVisits": "さらに訪問をロード",
"MorePagesNotDisplayed": "このビジターの表示はこれ以上ありません",
"NbVisitor": "1人のビジター",
"NbVisitors": "%s のビジター",
"NextVisitor": "次のビジター",
"NoMoreVisits": "この訪問者のさらなる訪問はありません。",
"PageRefreshed": "このページが見られた\/更新された回数",
"PluginDescription": "行動中のビジターログを提供し、リアルタイムダッシュボードウェジェット内に行動中のビジターを表示します。このプラグインでは、任意のユーザーのビジタープロフィールを見ることもできます。",
"PreviousVisitor": "前のビジター",
"RealTimeVisitorCount": "リアルタイムのビジター数",
"Referrer_URL": "参照元 URL",
"ShowMap": "地図を表示",
"SimpleRealTimeWidget_Message": "最後の %s の %s と %s",
"ViewVisitorProfile": "ビジターのプロフィールをみる",
"VisitedPages": "訪問されたページ",
"VisitorLog": "ビジターログ",
"VisitorLogDocumentation": "この表は選択した日付期間内で最新のビジットについて表示しています。ビジットの日付の上にマウスを移動して、そのビジターの最後のビジットが何日前か見ることが出来ます。%s 期間内に本日が含まれていれば、ビジターをリアルタイムで見られます! %s ここに表示されるのは、アーカイブのためのcronジョブ設定の頻度に関わらず、常にライブのデータです。",
"VisitorProfile": "ビジターのプロフィール",
"VisitorsInRealTime": "リアルタイムのビジター",
"VisitorsLastVisit": "このビジターの最新のビジットは %s 日前です。",
"VisitsFrom": "から %1$s%2$s が %3$s を訪問",
"RowActionTooltipDefault": "この行で分割されたビジターログを表示",
"RowActionTooltipWithDimension": "この %s で分割されたビジターログを表示",
"RowActionTooltipTitle": "セグメントビジターログを表示",
"SegmentedVisitorLogTitle": "%s が \" %s \" のビジットを示すビジターログ",
"OnClickPause": "%s が開始されました。クリックして一時停止します。",
"OnClickStart": "%s は停止しています。クリックして開始します。"
}
}

View file

@ -0,0 +1,11 @@
{
"Live": {
"GoalType": "ტიპი",
"LastHours": "ბოლო %s საათი",
"LastMinutes": "ბოლო %s წუთი",
"LinkVisitorLog": "იხილეთ ვიზიტორების ჟურნალი დეტალურად",
"Referrer_URL": "რეფერერის URL",
"VisitorLog": "ვიზიტორების ჟურნალი",
"VisitorsInRealTime": "ვიზიტორები რეალურ დროში"
}
}

View file

@ -0,0 +1,35 @@
{
"Live": {
"CalculatedOverNPageViews": "해당 방문자의 %1$s 번 페이지 방문을 통해 계산되었습니다.",
"ClickToViewMoreAboutVisit": "이 방문에 대해 더 자세한 정보를 보기 위해서 클릭하세요.",
"ConvertedNGoals": "변환된 %s 목표",
"FirstVisit": "첫 방문",
"GoalType": "유형",
"HideMap": "지도 숨기기",
"KeywordRankedOnSearchResultForThisVisitor": "키워드 %1$s 이 방문자의 %3$s 검색 결과 페이지에서 %2$s에 랭크되었습니다.",
"LastHours": "최근 %s시간",
"LastMinutes": "최근 %s분",
"LastVisit": "최근 방문",
"LinkVisitorLog": "방문자 기록 자세히 보기",
"LoadMoreVisits": "더 많은 방문들 불러오기",
"MorePagesNotDisplayed": "이 방문객의 표시는 더이상 없습니다",
"NbVisitor": "1명 방문자",
"NbVisitors": "%s 방문자들",
"NextVisitor": "다음 방문자",
"NoMoreVisits": "이 방문자가 방문한 페이지는 더 이상 없습니다.",
"PageRefreshed": "이 페이지를 볼 수 있음 \/ 업데이트된 횟수",
"PreviousVisitor": "이전 방문자",
"RealTimeVisitorCount": "실시간 방문자 합계",
"Referrer_URL": "참조 URL",
"ShowMap": "지도 보이기",
"ViewVisitorProfile": "방문자 프로필 보기",
"VisitedPages": "방문한 페이지들",
"VisitorLog": "방문자 기록",
"VisitorLogDocumentation": "이 표는 선택한 날짜 기간 내에 최신 방문에 표시하고 있습니다. 방문 날짜 위로 마우스를 이동하고, 그 방문자의 마지막 방문이 며칠 전인지 볼 수 있습니다. %s 기간내에 오늘이 포함되어 있으면, 방문객을 실시간으로 볼 수 있습니다! %s 여기에 표시되는 것은 보관을위한 cron 작업 설정 빈도에 상관없는 라이브 데이터입니다.",
"VisitorProfile": "방문자 프로필",
"VisitorsInRealTime": "실시간 방문자",
"VisitorsLastVisit": "이 방문객의 최신 방문은 %s일 입니다.",
"OnClickPause": "%s이\/가 시작되었습니다. 멈추기 위해서 클릭해주세요.",
"OnClickStart": "%s이\/가 멈추었습니다. 시작하기 위해서 클릭해주세요."
}
}

View file

@ -0,0 +1,24 @@
{
"Live": {
"FirstVisit": "Pirmasis apsilankymas",
"GoalType": "Tipas",
"HideMap": "paslėpti žemėlapį",
"KeywordRankedOnSearchResultForThisVisitor": "Šiam lankytojui raktažodis %1$s buvo parodytas %2$s %3$s paieškos rezultatuose",
"LastHours": "Paskutinės %s valandos",
"LastMinutes": "Paskutinės %s minutės",
"LastVisit": "Paskutinis apsilankymas",
"LinkVisitorLog": "Peržiūrėti išsamų apsilankymų žurnalą",
"LoadMoreVisits": "Įkelti daugiau apsilankymų",
"NbVisitor": "1 lankytojas",
"NbVisitors": "%s lankytojų",
"NextVisitor": "Kitas lankytojas",
"PreviousVisitor": "Ankstesnis lankytojas",
"Referrer_URL": "Persiunčiančiojo URL",
"ShowMap": "rodyti žemėlapį",
"ViewVisitorProfile": "Peržiūrėti lankytojo profilį",
"VisitedPages": "Aplankyti puslapiai",
"VisitorLog": "Apsilankymų žurnalas",
"VisitorsInRealTime": "Lankytojai realiuoju laiku",
"VisitorsLastVisit": "Šio lankytojo paskutinis apsilankymas buvo prieš %s dienų."
}
}

View file

@ -0,0 +1,14 @@
{
"Live": {
"GoalType": "Tips",
"KeywordRankedOnSearchResultForThisVisitor": "Atslēgvārds %1$s ir %2$s vietā %3$s šī lietotāja meklēšanas rezultātu lapā.",
"LastHours": "Pēdējās %s stundas",
"LastMinutes": "Pēdējās %s minūtes",
"LinkVisitorLog": "Apskatīt detalizētu apmeklētāju žurnālu",
"Referrer_URL": "Atsauces URL",
"VisitorLog": "Apmeklētāju žurnāls",
"VisitorLogDocumentation": "Šī tabula atspoguļo pēdējos apmeklējumus izvēlētajā datumu intervālā. Jūs varat apskatīt pēdējo apmeklētāja apmeklējuma laiku kursoru uzbīdot uz apmeklējuma datuma. %s Ja datumu intervāls iekļauj šodienu, Jūs varat skatīt apmeklētājus reāllaikā! %s Dati, kas apskatāmi šeit, ir vienmēr aktuālie, neatkarīgi no tā vai lietojat arhivēšanas darbu, vai nē.",
"VisitorsInRealTime": "Apmeklētāji reāllaikā",
"VisitorsLastVisit": "Šī apmeklētāja pēdējais apmklējums bija %s dienas atpakaļ."
}
}

View file

@ -0,0 +1,29 @@
{
"Live": {
"ClickToViewMoreAboutVisit": "Klikk for å se mer informasjon om dette besøket",
"ConvertedNGoals": "Konverterte %s mål",
"FirstVisit": "Første besøk",
"GoalType": "Type",
"HideMap": "skjul kart",
"LastHours": "Siste %s timer",
"LastMinutes": "Siste %s minutter",
"LastVisit": "Siste besøk",
"LinkVisitorLog": "Vis detaljert logg over besøkere",
"NbVisitor": "1 besøker",
"NbVisitors": "%s besøkere",
"NextVisitor": "Neste besøker",
"PreviousVisitor": "Forrige besøker",
"RealTimeVisitorCount": "Besøkstall i sanntid",
"Referrer_URL": "Henvisnings-URL",
"ShowMap": "vis kart",
"SimpleRealTimeWidget_Message": "%s og %s i de siste %s",
"ViewVisitorProfile": "Vis besøksprofil",
"VisitedPages": "Besøkte sider",
"VisitorLog": "Logg over besøkere",
"VisitorProfile": "Besøksprofil",
"VisitorsInRealTime": "Besøkende i sanntid",
"VisitsFrom": "%1$s%2$s besøk%3$s fra",
"OnClickPause": "%s er startet. Klikk for å sette på pause.",
"OnClickStart": "%s er stoppet. Klikk for å starte."
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "Elke pagina duurde gemiddeld %1$s om te laden voor deze bezoeker.",
"CalculatedOverNPageViews": "Berekend met de laatste %1$s paginaweergaves van deze bezoeker.",
"ClickToViewMoreAboutVisit": "Klik om meer informatie over dit bezoek te zien",
"ConvertedNGoals": "Behaalde %s Doelen",
"FirstVisit": "Eerste bezoek",
"GoalType": "Type",
"HideMap": "Verberg kaart",
"KeywordRankedOnSearchResultForThisVisitor": "Het trefwoord %1$s scoorde voor deze bezoeker op positie %2$s op de %3$s zoekresultaat pagina.",
"LastHours": "Laatste %s uren",
"LastMinutes": "Laatste %s minuten",
"LastVisit": "Laatste bezoek",
"LinkVisitorLog": "Bekijk gedetailleerd bezoekerslogboek.",
"LoadMoreVisits": "Meer bezoeken laden",
"MorePagesNotDisplayed": "meer pagina's worden niet getoond bij deze bezoeker",
"NbVisitor": "1 bezoeker",
"NbVisitors": "%s bezoekers",
"NextVisitor": "Volgende bezoeker",
"NoMoreVisits": "Er zijn niet meer bezoeken van deze bezoeker.",
"PageRefreshed": "Aantal keren dat deze pagina werd bekeken \/ ververst in een rij.",
"PluginDescription": "Bevat de bezoekerslog en toont je bezoekers real-time in de widget op het dashboard. De plugin toont je ook het bezoekersprofiel van elke bezoeker.",
"PreviousVisitor": "Vorige bezoeker",
"RealTimeVisitorCount": "Real Time bezoekers aantal",
"Referrer_URL": "Referrer URL",
"ShowMap": "Toon kaart",
"SimpleRealTimeWidget_Message": "%s en %s in de laatste %s",
"ViewVisitorProfile": "Bekijk bezoeker profiel",
"VisitedPages": "Bezochte pagina's",
"VisitorLog": "Bezoekerslogboek",
"VisitorLogDocumentation": "Deze tabel toont de laatste bezoeken binnen het geselecteerde datumbereik. Je kunt zien wanneer een bezoeker zijn laatste bezoek bracht door je muis boven de datum te houden. %s Indien het datumbereik ook vandaag bevat, kun je je bezoekers zien in real time! %s De data die hier wordt getoond is steeds live, ongeacht of en wanneer je de archiving cron job gebruikt.",
"VisitorProfile": "Bezoekers profiel",
"VisitorsInRealTime": "Bezoekers in real-time",
"VisitorsLastVisit": "Het laatste bezoek van deze bezoekers was %s dagen geleden.",
"VisitsFrom": "%1$s%2$s bezoeken%3$s van",
"RowActionTooltipDefault": "Toon bezoekers log verdeeld volgens deze rij",
"RowActionTooltipWithDimension": "Toon bezoekers log verdeeld volgens %s",
"RowActionTooltipTitle": "Open gesegmenteerd Bezoekerslogboek",
"SegmentedVisitorLogTitle": "Bezoekers log toont bezoeken waar %s is \"%s\"",
"OnClickPause": "%s is gestart. Klik om te pauzeren.",
"OnClickStart": "%s is gestopt. Klik om te starten."
}
}

View file

@ -0,0 +1,14 @@
{
"Live": {
"GoalType": "Type",
"LastHours": "Seinaste %s timar",
"LastMinutes": "Seinaste %s minutt",
"LinkVisitorLog": "Vis detaljert vitjarlogg",
"MorePagesNotDisplayed": "fleire sider av denne vitjaren er ikkje vist",
"PageRefreshed": "Tal på gonger denne sida var vist \/ oppdatert.",
"VisitorLog": "Vitjarlogg",
"VisitorLogDocumentation": "Denne tabellen visar dei seinaste vitjingane innan tidsperioden. Du kan sjå den førre vitjinga til ein vitjar ved å halda peikaren over datoen til ei vitjing. %s Viss tidsperioden inkluderer idag, kan du sjå vitjarane dine i sanntid! %s Data her er alltid i sanntid, same om og kor ofte Piwik arkiverer dei.",
"VisitorsInRealTime": "Vitjarar i sanntid",
"VisitorsLastVisit": "Den førre vitjinga til vitjaren var %s dagar sidan."
}
}

View file

@ -0,0 +1,28 @@
{
"Live": {
"ClickToViewMoreAboutVisit": "Kliknij po więcej informacji o tej wizycie",
"FirstVisit": "Pierwsza wizyta",
"GoalType": "Rodzaj",
"HideMap": "ukryj mapę",
"KeywordRankedOnSearchResultForThisVisitor": "Słowo kluczowe %1$s zdobyło pozycję %2$s w %3$s wynikach wyszukania strony dla tego odwiedzającego",
"LastHours": "w czasie ostatnich %s godzin",
"LastMinutes": "w czasie ostatnich %s minut",
"LastVisit": "Ostatnia wizyta",
"LinkVisitorLog": "Zobacz szczegółowy log twoich odwiedzających",
"LoadMoreVisits": "Załaduj więcej odwiedzin",
"NbVisitor": "1 gość",
"NbVisitors": "%s gości",
"NextVisitor": "Następny gość",
"PageRefreshed": "Ile razy ta strona została oglądana\/ odświeżana z rzędu.",
"PreviousVisitor": "Poprzedni odwiedzający",
"Referrer_URL": "Przysyłający adres URL",
"ShowMap": "Pokaż mapę",
"SimpleRealTimeWidget_Message": "%s %s na %s ostatni",
"ViewVisitorProfile": "Zobacz profil odwiedzającego",
"VisitedPages": "Odwiedzone strony",
"VisitorLog": "Log odwiedzających",
"VisitorProfile": "Profil odwiedzającego",
"VisitorsInRealTime": "Odwiedzający w czasie rzeczywistym",
"VisitorsLastVisit": "Ostatnia wizyta tego gościa była %s dni temu."
}
}

View file

@ -0,0 +1,47 @@
{
"Live": {
"AbandonedCartSummary": "%1$s carrinhos abandonados%2$s e %3$s itens abandonados%4$s valendo um total de %5$s%6$s.",
"AveragePageGenerationTime": "Cada página levou em média %1$s para carregar para este visitante.",
"CalculatedOverNPageViews": "Calculado usando as últimas %1$s exibições de página deste visitante.",
"ClickToViewMoreAboutVisit": "Clique para ver mais informações sobre esta visita",
"ConvertedNGoals": "%s Metas convertidas",
"EcommerceSummaryConversions": "%1$s pedidos%2$s de um total de %3$s%4$s, comprados %5$s itens%6$s.",
"FirstVisit": "Primeiro visitante",
"GoalType": "Tipo",
"HideMap": "esconder mapa",
"KeywordRankedOnSearchResultForThisVisitor": "A palavra-chave %1$s foi classificada %2$s em %3$s páginas de resultados de pesquisa para este visitante",
"LastHours": "Últimas %s horas",
"LastMinutes": "Últimos %s minuto(s)",
"LastVisit": "Última visita",
"LinkVisitorLog": "Ver log detalhado de visitantes",
"LoadMoreVisits": "Carregar mais visitas",
"MorePagesNotDisplayed": "mais páginas por este visitante não são exibidos",
"NbVisitor": "1 visitante",
"NbVisitors": "%s visitantes",
"NextVisitor": "Próximo visitante",
"NoMoreVisits": "Não existem mais visitas para este visitante.",
"PageRefreshed": "Número de vezes que esta página foi vista \/ recarregada em uma linha.",
"PluginDescription": "Fornece o Registro de Visitantes em tempo real e permite que você assista seus visitantes ao vivo no widget do painel. O plugin também permite que você veja o perfil de Visitante de qualquer usuário.",
"PreviousVisitor": "Visitante anterior",
"RealTimeVisitorCount": "Contagem de visitantes em tempo real",
"Referrer_URL": "URL do Referenciador",
"ShowMap": "mostrar mapa",
"SimpleRealTimeWidget_Message": "%s e %s na última %s.",
"ViewVisitorProfile": "Ver o perfil do visitante",
"VisitedPages": "Páginas visitadas",
"VisitorLog": "Log de visitantes",
"VisitorLogDocumentation": "Esta tabela mostra as últimas visitas dentro do período selecionado. Você pode ver quando a última visita de um visitante ocorreu passando o mouse sobre a data da visita. %s se o intervalo de data incluir hoje, você pode ver os seus visitantes em tempo real! %s Os dados exibidos aqui são sempre ao vivo, independentemente de estar usando o arquivamento agendado por cron.",
"VisitorProfile": "Páginas visitadas",
"VisitorsInRealTime": "Visitantes em tempo real",
"VisitorsLastVisit": "A última visita deste visitante foi a %s dias atrás.",
"VisitsFrom": "%1$s%2$s visitas%3$s de",
"VisitSummary": "Passou um total de %1$s%2$s no website%3$s, e viu %4$s páginas%5$s em %6$s visitas%7$s.",
"VisitSummaryWithActionDetails": "Passou um total de %1$s%2$s no website%3$s, e realizou %4$s ações%5$s (%6$s) em %7$s visitas%8$s.",
"RowActionTooltipDefault": "Mostrar Log de Visitantes segmentado por esta linha",
"RowActionTooltipWithDimension": "Mostrar Log de Visitantes segmentado por este %s",
"RowActionTooltipTitle": "Abrir Log de Visitantes segmentado",
"SegmentedVisitorLogTitle": "Log de Visitantes mostrando visitas onde %s é \"%s\"",
"OnClickPause": "%s iniciou. Clique para pausar.",
"OnClickStart": "%s parou. Clique para iniciar."
}
}

View file

@ -0,0 +1,14 @@
{
"Live": {
"GoalType": "Tipo",
"KeywordRankedOnSearchResultForThisVisitor": "A palavra-chave %1$s foi classificada %2$s na página de resultados de pesquisa %3$s por este visitante",
"LastHours": "Últimas %s horas",
"LastMinutes": "Últimos %s minutos",
"LinkVisitorLog": "Ver registo de visitantes detalhado",
"Referrer_URL": "Endereço do Referente",
"VisitorLog": "Registo de Visitantes",
"VisitorLogDocumentation": "Esta tabela mostra as últimas visitas dentro do período selecionado. Você pode ver quando a última visita de um visitante ocorreu passando o cursor sobre os dados de uma visita. %s Se o intervalo de datas incluir hoje, você pode ver os seus visitantes em tempo real! %s Os dados apresentados aqui são sempre em tempo real, independentemente de quantas vezes você está usando o cron job de arquivamento.",
"VisitorsInRealTime": "Visitantes em Tempo Real",
"VisitorsLastVisit": "A última visita do utilizador ocorreu à %s dias atrás."
}
}

View file

@ -0,0 +1,36 @@
{
"Live": {
"AveragePageGenerationTime": "Fiecare pagină a avut, în medie %1$s pentru a încărca pentru acest vizitator.",
"CalculatedOverNPageViews": "Calculat folosind aceast vizitator trecut %1$s pagini văzute.",
"ClickToViewMoreAboutVisit": "Click pentru a vedea mai multe informații despre această vizită",
"ConvertedNGoals": "Convertit %s Goluri",
"FirstVisit": "Prima vizita",
"GoalType": "Tip",
"HideMap": "ascunde hartă",
"KeywordRankedOnSearchResultForThisVisitor": "Cuvinte cheie %1$s au fost clasate pe locul %2$s pe %3$s căutare pagina a rezultate pentru acest vizitator",
"LastHours": "Ultimele %s ore",
"LastMinutes": "Ultimele %s minute",
"LastVisit": "Ultima vizita",
"LinkVisitorLog": "Vezi registrul detalizat al vizitatorilor",
"LoadMoreVisits": "Încărcați mai multe vizite",
"MorePagesNotDisplayed": "mai multe pagini de acest vizitator nu sunt afișate",
"NbVisitor": "1 vizitator",
"NbVisitors": "%s vizitatori",
"NextVisitor": "Urmatorul vizitator",
"NoMoreVisits": "Nu există mai multe vizite pentru acest vizitator.",
"PageRefreshed": "De cate ori aceasta pagina a fost vizualizata \/ reincarcata într-un rând.",
"PreviousVisitor": "Vizitatorul anterior",
"RealTimeVisitorCount": "Numărul vizitatori în timp real",
"Referrer_URL": "Referal URL",
"ShowMap": "Arata harta",
"SimpleRealTimeWidget_Message": "%s și %s în ultimele %s",
"ViewVisitorProfile": "Vezi profilul vizitatorului",
"VisitedPages": "Pagini vizitate",
"VisitorLog": "Registrul vizitatorilor",
"VisitorLogDocumentation": "Acest tabel arată cele mai recente vizite în intervalul de date selectat. Puteți vedea când a fost ultima vizita a unui vizitator a avut loc. %s În cazul în care intervalul de date include astăzi, puteți vedea vizitatorii dvs. în timp real! %s Datele afișate aici sunt mereu actualizate, indiferent cand si cât de des folosiți arhivarea cron.",
"VisitorProfile": "Profilul vizitator",
"VisitorsInRealTime": "Vizitatori în timp real",
"VisitorsLastVisit": "Ultima vizită a acestui vizitator a avut loc cu %s zile în urmă.",
"VisitsFrom": "%1$s%2$s vizite%3$s de la"
}
}

View file

@ -0,0 +1,40 @@
{
"Live": {
"AveragePageGenerationTime": "В среднем требовалось %1$s у этого посетителя для полной загрузки страницы.",
"CalculatedOverNPageViews": "Рассчитано на основе этого посетителя с %1$s просмотром страниц(ы).",
"ClickToViewMoreAboutVisit": "Посмотреть более подробную информацию об этом визите",
"ConvertedNGoals": "Целей достигнуто: %s",
"FirstVisit": "Первое посещение",
"GoalType": "Тип",
"HideMap": "скрыть карту",
"KeywordRankedOnSearchResultForThisVisitor": "Ключевое слово %1$s занимает %2$s место на %3$s странице поиска для этого посетителя",
"LastHours": "Последние %s часов",
"LastMinutes": "Последние %s минут",
"LastVisit": "Последнее посещение",
"LinkVisitorLog": "Посмотреть детальный журнал посетителей",
"LoadMoreVisits": "Загрузить больше посетителей",
"MorePagesNotDisplayed": "Больше страниц по этому посетителю не отображается.",
"NbVisitor": "1 посетитель",
"NbVisitors": "%s посетителей",
"NextVisitor": "Следующий посетитель",
"NoMoreVisits": "Больше нет посещений у этого посетителя.",
"PageRefreshed": "Сколько раз эта страница была просмотрена \/ обновлена несколько ряд подряд.",
"PluginDescription": "Показывает актуальный журнал посетителей и позволяет смотреть ваших посетителей в режиме реального времени на приборной панели виджетов. Плагин также позволяет просматривать профиль посетитель данного пользователя.",
"PreviousVisitor": "Предыдущий посетитель",
"RealTimeVisitorCount": "Счётчик посетителей в реальном времени",
"Referrer_URL": "URL источника",
"ShowMap": "показать карту",
"SimpleRealTimeWidget_Message": "%s и %s за последние %s",
"ViewVisitorProfile": "Посмотреть профиль посетителя",
"VisitedPages": "Посещённые страницы",
"VisitorLog": "Журнал посетителей",
"VisitorLogDocumentation": "Эта таблица показывает последние посещения за выбранный период. Вы можете видеть последнее посещение пользователя при наведении мыши на дату посещения. %s Если период включает сегодняшний день, вы можете наблюдать за посетителями в реальном времени! %s Данные здесь всегда отображаются в режиме реального времени вне зависимости от того, как часто вы используете архивацию по крону.",
"VisitorProfile": "Профиль посетителя",
"VisitorsInRealTime": "Посетители в реальном времени",
"VisitorsLastVisit": "Последнее посещение этого пользователя было %s дней назад.",
"VisitsFrom": "%1$s%2$s визитов%3$s от",
"RowActionTooltipDefault": "Показать журнал посетителей, сегментированный по этой строке",
"RowActionTooltipTitle": "Открыть сегментированный журнал посетителей",
"SegmentedVisitorLogTitle": "Просмотр посещений где %s равно \\\"%s\\\""
}
}

View file

@ -0,0 +1,22 @@
{
"Live": {
"ClickToViewMoreAboutVisit": "Klinite pre viac informáciío tejto návšteve.",
"FirstVisit": "Prvá návšteva",
"GoalType": "Typ",
"HideMap": "skryť mapu",
"LastHours": "Posledných %s hodín",
"LastMinutes": "Posledných %s minút",
"LastVisit": "Posledná návšteva",
"LinkVisitorLog": "Zobraziť podrobný záznam návštevníkov",
"LoadMoreVisits": "Načítať viac návštev",
"NbVisitor": "1 návštevník",
"NbVisitors": "%s návštevníkov",
"NextVisitor": "Ďalší návštevník",
"PreviousVisitor": "Predchádzajúci návštevník",
"RealTimeVisitorCount": "Sledovanie návštev v reálnom čase",
"Referrer_URL": "Referenčné URL",
"SimpleRealTimeWidget_Message": "%s a %s v posledných %s",
"VisitorLog": "Záznam návštevníka",
"VisitorsInRealTime": "Návštevníci v reálnom čase"
}
}

View file

@ -0,0 +1,13 @@
{
"Live": {
"GoalType": "Tip",
"LastHours": "Zadnjih %s ur",
"LastMinutes": "Zadnjih %s minut",
"LinkVisitorLog": "Ogled podrobnega dnevnika obiskovalcev",
"NbVisitor": "1 obiskovalec",
"NbVisitors": "%s obiskovalcev",
"VisitorLog": "Dnevnik obiskovalcev",
"VisitorsInRealTime": "Obiskovalci v živo",
"VisitorsLastVisit": "Zadnji obisk tega obiskovalce je bil pred %s dnevi."
}
}

View file

@ -0,0 +1,15 @@
{
"Live": {
"GoalType": "Lloj",
"KeywordRankedOnSearchResultForThisVisitor": "Për këtë vizitor, fjalëkyçi %1$s u radhit i %2$s te faqja e përfundimeve të kërkimit për %3$s",
"LastHours": "%s orët e fundit",
"LastMinutes": "%s minutat e fundit",
"LinkVisitorLog": "Shihni regjistër të hollësishëm të përdoruesit",
"PageRefreshed": "Sa herë rresht është parë \/ rifreskuar kjo faqew.",
"Referrer_URL": "URL Referuesi",
"VisitorLog": "Regjistër Vizitori",
"VisitorLogDocumentation": "Kjo tabelë tregon vizitat e fundit brenda intervalit kohor të përzgjedhur. Se kur u bë vizita e fundit nga një vizitor mund ta shihni duke kaluar kursorin përsipër datës së një vizite. %s Nëse intervali kohor e përfshin ditën e sotme, mund ti shihni vizitorët aty për aty! %s Të dhënat e shfaqura këtu janë përherë më të rejat, pavarësisht nëse e përdorni a jo dhe se sa shpesh funksionin cron për arkivim.",
"VisitorsInRealTime": "Vizitorë në Kohë Reale",
"VisitorsLastVisit": "Vizita e fundit e këtij vizitori ndodhi %s ditë më parë."
}
}

View file

@ -0,0 +1,43 @@
{
"Live": {
"AveragePageGenerationTime": "Ovom korisniku je za svaku stranicu u proseku potrebno %1$s kako bi se učitala.",
"CalculatedOverNPageViews": "Izračunato na osnovu poslednjih %1$s prikaza stranica ovog korisnika",
"ClickToViewMoreAboutVisit": "Kliknite kako biste videli više informacija o ovoj poseti.",
"ConvertedNGoals": "Ispunjeno ciljeva: %s",
"FirstVisit": "Prva poseta",
"GoalType": "Tip",
"HideMap": "sakrij mapu",
"KeywordRankedOnSearchResultForThisVisitor": "Ključna reč %1$s je rangirana na mestu %2$s kao %3$s rezultat pretrage ovog posetioca.",
"LastHours": "Poslednjih %s sati",
"LastMinutes": "Poslednjih %s minuta",
"LastVisit": "Poslednja poseta",
"LinkVisitorLog": "Prikaži detaljan zapis poseta",
"LoadMoreVisits": "Učitaj još poseta",
"MorePagesNotDisplayed": "više stranica ovog posetioca nisu prikazane",
"NbVisitor": "1 posetilac",
"NbVisitors": "%s posetilaca",
"NextVisitor": "Sledeći posetilac",
"NoMoreVisits": "Nema više poseta ovog posetioca.",
"PageRefreshed": "Broj prikaza ove stranice zaredom.",
"PluginDescription": "Omogućuje zapis poseta u realnom vremenu i pruža vam mogućnost da pratite vaše posetioce preko vidžeta. Dodatak takođe prikazuje profil posetioca za bilo kog korisnika.",
"PreviousVisitor": "Prethodni posetilac",
"RealTimeVisitorCount": "Brojač posetilaca u realnom vremenu",
"Referrer_URL": "Referenca",
"ShowMap": "prikaži mapu",
"SimpleRealTimeWidget_Message": "%s i %s u poslednjih %s.",
"ViewVisitorProfile": "Profil novog posetioca",
"VisitedPages": "Posećene stranice",
"VisitorLog": "Zapis poseta",
"VisitorLogDocumentation": "Ova tabela prikazuje poslednje posete unutar izabranog vremenskog opsega. Možete videti poslednju posetu pomeranjem miša preko datuma posete. %s Ako vremenski opseg obuhvata i današnji dan, možete videti vaše posetioce u realnom vremenu! %s Podaci ovde prikazani su uvek u realnom vremenu bez obzira na to koliko često koristite cron.",
"VisitorProfile": "Profil posetioca",
"VisitorsInRealTime": "Posetioci u realnom vremenu",
"VisitorsLastVisit": "Poslednja poseta je bila pre %s dana.",
"VisitsFrom": "%1$s%2$s poseta%3$s sa",
"RowActionTooltipDefault": "Prikaži zapis poseta podeljen ovim redom",
"RowActionTooltipWithDimension": "Prikaži zapis poseta podeljen %s",
"RowActionTooltipTitle": "Otvori podeljeni zapis psoeta",
"SegmentedVisitorLogTitle": "Zapis poseta prikazuje posete kod kojih %s je \"%s\"",
"OnClickPause": "%s je pokrenut. Kliknite za pauzu.",
"OnClickStart": "%s je zaustavljen. Kliknite za start."
}
}

View file

@ -0,0 +1,37 @@
{
"Live": {
"AbandonedCartSummary": "%1$s övergivna varukorgar%2$s och %3$s övergivna varor%4$s värda totalt %5$s%6$s.",
"AveragePageGenerationTime": "Varje sida tog i genomsnitt %1$s att ladda för besökaren.",
"CalculatedOverNPageViews": "Beräknat på denna besöakrens senaste %1$s sidvisningar.",
"ClickToViewMoreAboutVisit": "Klicka för mer information om det här besöket",
"ConvertedNGoals": "Konverteringar %s Mål",
"FirstVisit": "Första besöket",
"GoalType": "Sort",
"HideMap": "Dölj mapp",
"KeywordRankedOnSearchResultForThisVisitor": "Nyckelordet %1$s rankades %2$s på %3$s i sökresultatet för besökaren",
"LastHours": "Senaste %s timmarna",
"LastMinutes": "Senaste %s minuterna",
"LastVisit": "Senaste besöket",
"LinkVisitorLog": "Visa detaljerad besökslogg",
"LoadMoreVisits": "Ladda fler besökare",
"MorePagesNotDisplayed": "fler sidor av den här besökaren visas inte",
"NbVisitor": "1 besökare",
"NbVisitors": "%s besökare",
"NextVisitor": "Nästa besökare",
"NoMoreVisits": "Det finns inga fler besök för den här besökaren.",
"PageRefreshed": "Antal gånger denna sida har besökts \/ uppdateras i rad.",
"PreviousVisitor": "Förgående besökare",
"RealTimeVisitorCount": "Besöksräknare i realtid",
"Referrer_URL": "Hänvisningsadress",
"ShowMap": "Visa mapp",
"SimpleRealTimeWidget_Message": "%s och %s dom senaste %s.",
"ViewVisitorProfile": "Ny besöksprofil",
"VisitedPages": "Besökta sidor",
"VisitorLog": "Besökslogg",
"VisitorLogDocumentation": "Denna tabell visar de senaste besök inom det valda datumintervallet. %s Om datumet inkluderar idag kan du se dina besökare i realtid! %s data som visas här är alltid live, oavsett om och hur ofta du använder cron-jobb för arkivering.",
"VisitorProfile": "Besöksprofil",
"VisitorsInRealTime": "Besökare i realtid",
"VisitorsLastVisit": "Denna besökares senaste besök var %s dagar sedan.",
"VisitsFrom": "%1$s%2$s besökta %3$s från"
}
}

View file

@ -0,0 +1,10 @@
{
"Live": {
"GoalType": "வகை",
"LastHours": "கடைசி %s மணிநேரம்",
"LastMinutes": "கடைசி %s நிமிடங்கள்",
"NbVisitor": "1 வருகையாளர்",
"NbVisitors": "%s வருகையாளர்கள்",
"VisitorLog": "வருகையாளர் பதிவு"
}
}

View file

@ -0,0 +1,8 @@
{
"Live": {
"GoalType": "రకం",
"LastHours": "గత %s గంటలు",
"LastMinutes": "గత %s నిమిషాలు",
"VisitorLog": "సందర్శకుల చిట్టా"
}
}

View file

@ -0,0 +1,13 @@
{
"Live": {
"GoalType": "รูปแบบ",
"KeywordRankedOnSearchResultForThisVisitor": "คำหลักที่ %1$s อยู่ในอันดับที่ %2$s บน %3$s หน้าผลการค้นหาสำหรับผู้เข้าชมวันนี้",
"LastHours": "%s ชั่วโมงที่ผ่านมา",
"LastMinutes": "%s นาทีที่ผ่านมา",
"LinkVisitorLog": "ดูไฟล์ประวัติผู้เข้าชม",
"Referrer_URL": "ที่มา URL",
"VisitorLog": "ประวัติผู้เข้าชม",
"VisitorLogDocumentation": "ตารางนี้แสดงการเข้าชมล่าสุดภายในช่วงวันที่ที่เลือก %s หากช่วงวันที่ที่รวมถึงวันนี้คุณสามารถเห็นผู้เข้าชมของคุณเวลาจริง! %s ข้อมูลที่แสดงที่นี่อยู่เสมอโดยไม่คำนึงว่าและความถี่ที่คุณกำลังใช้ cron job",
"VisitorsInRealTime": "ผู้เข้าชมในเวลาจริง"
}
}

View file

@ -0,0 +1,35 @@
{
"Live": {
"AveragePageGenerationTime": "Bawat pahina ay umaabot ng %1$s upang mag-load para sa bisita.",
"CalculatedOverNPageViews": "Kinakalkula gamit ang huling %1$s page view ng bisita na ito.",
"ClickToViewMoreAboutVisit": "Pindutin dito upang makita ang marami pang impormasyon tungkol sa pagbisita.",
"ConvertedNGoals": "Mga na convert %s na goal",
"FirstVisit": "Unang pagbisita",
"GoalType": "Uri",
"HideMap": "itago ang mapa",
"KeywordRankedOnSearchResultForThisVisitor": "Ang keyword na %1$s ay may ranggong %2$s sa %3$s pahina ng resulta.",
"LastHours": "Huling oras %s",
"LastMinutes": "Huling %s minuto",
"LastVisit": "Huling pagbisita",
"LinkVisitorLog": "Tingnan ang detalyadong log ng bisita",
"LoadMoreVisits": "Mag-load ng higit pang mga pagbisita",
"MorePagesNotDisplayed": "marami pang mga pahina ng bisitang ito ang hindi ipinapakita",
"NbVisitors": "mga bisita %s",
"NextVisitor": "Susunod na bisita",
"NoMoreVisits": "Wala nang bumisita para sa bisitang ito.",
"PageRefreshed": "Dami ng beses na binuksan ang pahinang ito \/ ni-refresh sa isang hilera.",
"PreviousVisitor": "nakaraang bisita",
"RealTimeVisitorCount": "Real Time na bilang ng mga bisita",
"Referrer_URL": "Referrer URL",
"ShowMap": "ipakita ang mapa",
"SimpleRealTimeWidget_Message": "%s at %s sa nakaraang %s",
"ViewVisitorProfile": "Tingnan ang profile ng bisita",
"VisitedPages": "Binisitang mga pahina",
"VisitorLog": "Log ng bisita",
"VisitorLogDocumentation": "Ang table na ito ay nagpapakita ng mga pinakabagong mga pagbisita sa loob ng napiling hanay ng petsa. Maaari mong makita kung kelan ang huling pag bisita ng iyong bisita sa pamamagitan ng pag hover sa petsa ng pag bisita. %s Kung ang sakop ng petsa ay kaasama ngayon makikita mo ang lahat ng iyong mga bisita real time! %s Ang datus ng pinapakita dito ay laging live hindi alintana at kung gaano kadalas mo ginagamit ang archiving cron-job.",
"VisitorProfile": "profile ng Bisita",
"VisitorsInRealTime": "Real time na pagbisita",
"VisitorsLastVisit": "Ang huling bisita ng bisitang ito ay %s araw na ang nakakalipas.",
"VisitsFrom": "%1$s%2$s pagbisita sa %3$s mula sa"
}
}

View file

@ -0,0 +1,24 @@
{
"Live": {
"ConvertedNGoals": "Dönüştürülen %s Hedef",
"FirstVisit": "İlk ziyaret",
"GoalType": "Tür",
"HideMap": "haritayı gizle",
"LastHours": "Son %s saat",
"LastMinutes": "Son %s dakika",
"LastVisit": "Son ziyaret",
"LinkVisitorLog": "Ayrıntılı ziyaretçi kayitlari",
"LoadMoreVisits": "Daha fazla ziyaretçi yükle",
"NbVisitor": "1 ziyaretçi",
"NbVisitors": "%s ziyaretçi",
"NextVisitor": "Sonraki ziyaretçi",
"NoMoreVisits": "Bu ziyaretçi için daha fazla ziyaret bilgisi yok.",
"PreviousVisitor": "Önceki ziyaretçi",
"RealTimeVisitorCount": "Gerçek Zamanlı Ziyaretçi Sayacı",
"Referrer_URL": "Yönlendirme Siteleri",
"ViewVisitorProfile": "Ziyaretçi profiline bak",
"VisitedPages": "Ziyaret edilmiş sayfalar",
"VisitorLog": "Ziyaretçi kayıtları",
"VisitorsInRealTime": "Gerçek Zamanlı Ziyaretçiler"
}
}

View file

@ -0,0 +1,11 @@
{
"Live": {
"GoalType": "Тип",
"LastHours": "Останні %s годин(и)",
"LastMinutes": "Останні %s хвилин(и)",
"LinkVisitorLog": "Переглянути детальний протокол відвідувача",
"Referrer_URL": "URL-адреса джерело переходів",
"VisitorLog": "Протокол відвідувача",
"VisitorsInRealTime": "Відвідувачі в реальному часі"
}
}

View file

@ -0,0 +1,36 @@
{
"Live": {
"AveragePageGenerationTime": "Mỗi trang mất trung bình %1$s để nạp cho khách truy cập này.",
"CalculatedOverNPageViews": "Việc tính toán sử dụng lượt xem trang %1$s cuối cùng của khách truy cập này.",
"ClickToViewMoreAboutVisit": "Click để xem thêm thông tin về lượt truy cập này",
"ConvertedNGoals": "%s Mục tiêu đã được chuyển đổi",
"FirstVisit": "Lượt truy cập đầu tiên",
"GoalType": "Kiểu",
"HideMap": "Ẩn bản đồ",
"KeywordRankedOnSearchResultForThisVisitor": "Từ khóa %1$s đã được xếp hạng %2$s trên %3$s trang kết quả tìm kiếm cho khách truy cập này",
"LastHours": "%s giờ trước",
"LastMinutes": "%s phút trước",
"LastVisit": "Lượt truy cập cuối cùng",
"LinkVisitorLog": "Xem sổ ghi chi tiết khách truy cập",
"LoadMoreVisits": "Tải thêm các lượt truy cập",
"MorePagesNotDisplayed": "Nhiều trang được truy cập bởi khách này không được hiển thị",
"NbVisitor": "1 khách truy cập",
"NbVisitors": "%s khách truy cập",
"NextVisitor": "Khách truy cập tiếp theo",
"NoMoreVisits": "Không có nhiều truy cập bởi khách này",
"PageRefreshed": "Số lần trang này được xem\/làm mới liên tiếp.",
"PreviousVisitor": "Khách truy cập trước",
"RealTimeVisitorCount": "Số lượt khách truy cập thời gian thực",
"Referrer_URL": "URL tham chiếu",
"ShowMap": "Hiện thị bản đồ",
"SimpleRealTimeWidget_Message": "%s và %s trong %s cuối cùng",
"ViewVisitorProfile": "Xem hồ sơ khách truy cập",
"VisitedPages": "Các trang đã truy cập",
"VisitorLog": "Khách truy cập đăng nhập",
"VisitorLogDocumentation": "Bảng này cho thấy lần truy cập mới nhất trong phạm vi ngày đã chọn. Bạn có thể thấy khi một lượt truy cập cuối của khách truy cập đã xảy ra bằng cách lướt qua ngày của một truy cập.%s Nếu phạm vi ngày bao gồm ngày hôm nay, bạn có thể thấy khách truy cập thời gian thực của bạn! %s Dữ liệu hiển thị ở đây là luôn luôn sống, bất kể lúc nào và cách bạn thường đang sử dụng công việc lưu trữ theo định kỳ.",
"VisitorProfile": "Hồ sơ cá nhân của khách",
"VisitorsInRealTime": "Các khách truy cập trong thời gian thực",
"VisitorsLastVisit": "Lần truy cập trước của khách truy cập này là %s ngày trước.",
"VisitsFrom": "%1$s%2$s lượt truy cập %3$s từ"
}
}

View file

@ -0,0 +1,36 @@
{
"Live": {
"AveragePageGenerationTime": "这个访客的每个页面加载平均花 %1$s。",
"CalculatedOverNPageViews": "采用这个访客最后 %1$s 个访问页面统计。",
"ClickToViewMoreAboutVisit": "点击查看关于这次访问的详细信息。",
"ConvertedNGoals": "转化了 %s 个目标",
"FirstVisit": "首次访问",
"GoalType": "类型",
"HideMap": "隐藏地图",
"KeywordRankedOnSearchResultForThisVisitor": "这个访客的关键字 %1$s 排名 %2$s 于 %3$s 搜索结果页面。",
"LastHours": "最后%s小时",
"LastMinutes": "最后%s分钟",
"LastVisit": "最后访问",
"LinkVisitorLog": "查看详细的访客日志",
"LoadMoreVisits": "查看更多访问",
"MorePagesNotDisplayed": "该访客访问的更多页面没有显示。",
"NbVisitor": "1 个访客",
"NbVisitors": "%s 个访客",
"NextVisitor": "下一个访问者",
"NoMoreVisits": "这个访客没有其它的访问了。",
"PageRefreshed": "这个页面被查看 \/ 刷新的次数。",
"PreviousVisitor": "前个访客",
"RealTimeVisitorCount": "实时访客计数",
"Referrer_URL": "来源网址",
"ShowMap": "显示地图",
"SimpleRealTimeWidget_Message": "%s 和 %s 在最近 %s。",
"ViewVisitorProfile": "查看访客资料",
"VisitedPages": "访问的页面",
"VisitorLog": "访客日志",
"VisitorLogDocumentation": "本表显示所选时间段内最新的访客资料,鼠标移到日期上可以查看上次访问时间,%s 如果时间段包含今天,您可以实时查看访客! %s 这里显示的数据总是实时的,无论是否使用定时处理任务。",
"VisitorProfile": "访客资料",
"VisitorsInRealTime": "实时访客",
"VisitorsLastVisit": "此访客上次访问是%s天前。",
"VisitsFrom": "%1$s%2$s 次访问%3$s 来自"
}
}

View file

@ -0,0 +1,11 @@
{
"Live": {
"LastHours": "最近 %s 小時內",
"LastMinutes": "最近 %s 分鐘內",
"LinkVisitorLog": "查看詳細的訪客記錄",
"Referrer_URL": "推薦連結",
"VisitorLog": "訪客記錄",
"VisitorsInRealTime": "目前正在參觀網站的訪客",
"VisitorsLastVisit": "此訪客於 %s 天前曾來訪"
}
}

View file

@ -1,32 +1,38 @@
#visitsLive {
text-align: left;
font-size: 90%;
color: #444;
color: @theme-color-text-light;
border-top: 1px solid @color-silver-l90;
.dataTable {
margin-bottom: -1px;
}
}
#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 .settings {
border-bottom: 1px solid @color-silver-l90;
}
#visitsLive .datetime, #visitsLive .country, #visitsLive .settings, #visitsLive .returning {
padding: 10px 5px 10px 12px;
}
#visitsLive .datetime {
background: #E4E2D7;
border-top: 1px solid #d3d1c5;
margin: 0;
line-height: 20px;
text-align: left;
}
#visitsLive .country {
background: #FFF url(plugins/CoreHome/images/bullet1.gif) no-repeat scroll 0 0;
background: @theme-color-background-base 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;
display: block;
padding-top: 4px;
padding-bottom: 1px;
}
#visitsLive .pagesTitle {
@ -35,7 +41,7 @@
}
#visitsLive .settings {
background: #FFF none repeat scroll 0 0;
background: @theme-color-widget-background none repeat scroll 0 0;
}
#visitsLive .settings a {
@ -58,6 +64,17 @@
margin: 0 3px 0 0;
}
.ui-dialog.ui-widget {
.dataTableVizVisitorLog {
.dataTableFeatures {
border-bottom: 0px;
}
.expandDataTableFooterDrawer {
display: none;
}
}
}
.visitsLiveFooter a.rightLink {
float: right;
padding-right: 20px;
@ -67,10 +84,6 @@
text-decoration: none;
}
table.dataTable td.highlightField {
background-color: #FFFFCB !important;
}
ol.visitorLog {
list-style: decimal inside none;
}
@ -82,6 +95,11 @@ ol.visitorLog {
display:inline-block;
max-width:90%;
overflow: -moz-hidden-unscrollable;
&.event {
margin-top: 1px;
margin-right: 15px;
}
}
ol.visitorLog li {
@ -102,7 +120,6 @@ ol.visitorLog li {
}
.visitorRank {
margin-left: 15px;
border: 1px solid #D8D8D8;
color: #474747;
border-radius: 3px;
@ -123,9 +140,12 @@ ol.visitorLog li {
.repeat {
font-weight: bold;
border: 1px solid #444;
border: 1px solid @theme-color-text-light;
border-radius: 3px;
padding: 2px;
display: block;
margin: 5px;
float: left;
}
.dataTableVizVisitorLog hr {
@ -151,7 +171,7 @@ ol.visitorLog li {
.simple-realtime-visitor-counter > div {
font-size: 4.0em;
color: #444;
color: @theme-color-text-light;
}
.simple-realtime-metric {
@ -162,7 +182,7 @@ ol.visitorLog li {
.simple-realtime-elaboration {
margin: 1em 2em 1em 2em;
color: #666;
color: @theme-color-text-lighter;
display: inline-block;
}
@ -171,50 +191,135 @@ ol.visitorLog p {
padding:0;
}
.dataTableVizVisitorLog table.dataTable .column {
white-space: normal;
padding: 12px 5px;
}
.dataTableVizVisitorLog table.dataTable .label {
white-space: nowrap;
}
.dataTableVizVisitorLog {
.dataTableVizVisitorLog .dataTableWrapper {
width:100%;
.card {
padding: 15px 0px;
font-size: 13px;
text-align: left;
a {
text-decoration: none !important;
color: @theme-color-link;
width: inherit;
}
}
.visitorLog > li > div {
width: 95%;
}
.dataTableWrapper {
width:100%;
}
.card {
&:hover .visitor-log-visitor-profile-link {
display:inline;
}
}
}
.visitor-log-page-list {
position:relative;
}
.dataTableVizVisitorLog tr:hover .visitor-log-visitor-profile-link {
display:inline;
}
a.visitor-log-visitor-profile-link {
z-index: 2;
position:absolute;
display:none;
right:8px;
top:0px;
right: 15px;
top: 15px;
font-style:italic;
font-size:13px;
background-color: inherit !important;
img {
margin-top:-2px;
margin-top: -2px;
margin-bottom: -3px;
}
}
.visitorLog,.visitor-profile-actions {
> li > div {
display:inline-block;
width:85%;
width:90%;
vertical-align:top;
}
}
.action-list-action-icon {
float:left;
margin-top:6px;
margin-right:4px;
&.event {
margin-top: 0;
}
&.outlink {
margin-top: 4px;
margin-right: 4px;
margin-left: 4px;
}
&.search {
margin-right: 4px;
}
}
.action-list-url {
display:block;
}
.visitorLogIcons {
position: relative;
display: block;
}
.visitorLogIconWithDetails .details {
display: none;
}
.visitorLogIcons>span>span>img {
margin: auto 0 auto 0px;
}
.visitorLogIcons .visitorLogIconWithDetails>img {
margin: auto 5px auto 0;
}
.visitorLogIcons>span.visitorRank>img {
margin: auto 0;
}
.visitorLogIcons {
.visitorDetails, .visitorType {
display: block;
margin-top: 4px;
}
}
.own-visitor-column {
.visitorLogIcons {
.visitorDetails {
margin-top: 0px;
}
.visitorType {
margin-top: 8px;
}
}
}
.visitorReferrer {
clear:both;
padding-top: 1em;
}
.segmentedlog {
margin: 8px;
display: block;
background: transparent url('plugins/Live/images/visitorlog.png') no-repeat 0 0;
padding-left: 25px;
}
.segmentedlog:hover {
background-image: url('plugins/Live/images/visitorlog-hover.png');
}

View file

@ -33,10 +33,14 @@
padding:0;
font-weight:bold;
color:black;
border: none;
}
span.truncated-text-line {
display:inline-block;
&.event {
max-width: 89%;
}
}
}
@ -64,6 +68,7 @@
}
.visitor-profile-close {
z-index: 10;
position:absolute;
right:-17px;
top:-16px;
@ -99,7 +104,7 @@
}
.visitor-profile-overview { // first column
width:574.5px;
width:574px;
border-left:none;
margin:0 -3px 0 0;
border-right:1px solid #d1cec8;
@ -135,7 +140,7 @@
background:url(../images/avatar_frame.png) no-repeat;
> img { // avatar image
width:122px;
max-width:122px;
height:120px;
margin:11px 0 0 12px;
}
@ -299,7 +304,6 @@
}
.visitor-profile-pages-visited {
height:42px;
overflow-y:auto;
position:relative;
margin-right:10px;
@ -313,6 +317,7 @@
.visitor-profile-visits-container {
overflow-y:auto;
overflow-x: hidden;
position:relative;
margin-right:10px;
border-bottom:none!important;
@ -386,7 +391,7 @@
ol > li ol li {
.action-list-url {
margin-left:4px;
line-height:14px;
line-height:15px;
font-size:13px;
}
@ -486,11 +491,11 @@ div.visitor-profile-navigation {
.visitor-profile-header {
position:relative;
> .reportDocumentationIcon {
display:none;
margin:0;
width:14px;
background-size:14px;
.visitorProfileHelp {
display: none;
&:hover {
text-decoration: none;
}
}
}
@ -508,7 +513,7 @@ a.visitor-profile-next-visitor,a.visitor-profile-prev-visitor {
}
.visitor-profile-avatar:hover {
.visitor-profile-next-visitor,.visitor-profile-prev-visitor,.reportDocumentationIcon {
.visitor-profile-next-visitor,.visitor-profile-prev-visitor,.visitorProfileHelp {
display:inline-block;
}
}
@ -528,7 +533,7 @@ a.visitor-profile-next-visitor,a.visitor-profile-prev-visitor {
// overrides for the widgetized visitor profile
.widget .visitor-profile {
min-width: 100% !important;
p {
padding-bottom: 0;
}
@ -544,4 +549,4 @@ a.visitor-profile-next-visitor,a.visitor-profile-prev-visitor {
}
}
}
}
}

View file

@ -1,42 +1,45 @@
{% set previousAction = false %}
{% for action in actionDetails %}
{% set customVariablesTooltip %}
{% if action.customVariables is defined %}
{{ 'CustomVariables_CustomVariables'|translate }}
{{ '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 %}
{# line break above is important #}
{% 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 %}
<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 %}
{%- if action.generationTime is defined %}
{{ 'General_ColumnGenerationTime'|translate }}: {{ action.generationTime|raw }}{% endif %}
{%- if action.timeSpentPretty is defined %}
{%- 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>
<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' %}
{# 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 %}
@ -47,75 +50,78 @@
{% 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>
<abbr title="{{ ecommerceOrderTooltip }}">{{ 'General_ColumnRevenue'|translate }}:
{% else %}
{% set revenueLeft %}{{ 'General_ColumnRevenue'|translate }}{% endset %}
{{ 'Goals_LeftInCart'|translate(revenueLeft) }}:
{% 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>
{% 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|rawSafeDecoded }}</span>
{% endif %}
</div>
</li>
{% endif %}
{% if action.siteSearchKeyword is defined %}
{% if action.type == 'search' %}
<img src='{{ action.icon }}' title='{{ 'Actions_SubmenuSitesearch'|translate }}' class="action-list-action-icon search">
{% 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 event">
<span class="truncated-text-line event">{{ action.eventCategory|rawSafeDecoded }} - {{ action.eventAction|rawSafeDecoded }} {% if action.eventName is defined %}- {{ action.eventName|rawSafeDecoded }}{% 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 {{ action.type }}">
{% endif %}
{% if action.eventCategory|default(false) is not empty
and previousAction.url|default(false) == action.url %}
{# For events, do not show (url) if the Event URL is the same as the URL last displayed #}
{% else %}
<a href="{{ action.url }}" rel="noreferrer" 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="text-decoration:underline;"{% endif %}>
{{ action.url }}
</a>
{% endif %}
{% if action.type == 'action' and action.pageTitle|default(false) is not empty %}</p>{% endif %}
{% elseif action.type != 'search' and action.type != 'event' %}
<p>
<span>{{ 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>
{% set previousAction = action %}
{% endfor %}

View file

@ -1,85 +1,85 @@
{% 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 visitHasEcommerceActivity = visitor.getColumn('visitEcommerceStatusIcon') %}
{% set breakBeforeVisitorRank = (visitHasEcommerceActivity and visitor.getColumn('visitorTypeIcon')) ? true : false %}
{% set visitorColumnContent %}
<img src="{{ visitor.getColumn('countryFlag') }}" title="{{ visitor.getColumn('location') }}, Provider {{ visitor.getColumn('providerName') }}"/>
&nbsp;
{% 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 %}
&nbsp;
<img src="{{ visitor.getColumn('operatingSystemIcon') }}"
title="{{ visitor.getColumn('operatingSystem') }}, {{ visitor.getColumn('resolution') }} ({{ visitor.getColumn('screenType') }})"/>
{% if visitor.getColumn('visitorTypeIcon') %}
&nbsp;-
<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') %}
&nbsp;-
<img src="{{ visitor.getColumn('visitEcommerceStatusIcon') }}" title="{{ visitor.getColumn('visitEcommerceStatus') }}"/>
{% endif %}
<span class="visitorLogIcons">
<span class="visitorDetails">
{% if visitor.getColumn('browserIcon') %}
<span class="visitorLogIconWithDetails">
<img src="{{ visitor.getColumn('browserIcon') }}"/>
<ul class="details">
<li>{{ 'DevicesDetection_ColumnBrowser'|translate }}: {{ visitor.getColumn('browser') }}</li>
<li>{{ 'DevicesDetection_BrowserEngine'|translate }}: {{ visitor.getColumn('browserFamily') }}</li>
{% if visitor.getColumn('pluginsIcons')|length > 0 %}
<li>
{{ 'General_Plugins'|translate }}:
{% for pluginIcon in visitor.getColumn('pluginsIcons') %}
<img src="{{ pluginIcon.pluginIcon }}" alt="{{ pluginIcon.pluginName|capitalize(true) }}"/>
{% endfor %}
</li>
{% endif %}
</ul>
</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 %}
{% if visitor.getColumn('operatingSystemIcon') %}
<span class="visitorLogIconWithDetails">
<img src="{{ visitor.getColumn('operatingSystemIcon') }}"/>
<ul class="details">
<li>{{ 'DevicesDetection_ColumnOperatingSystem'|translate }}: {{ visitor.getColumn('operatingSystem') }}</li>
</ul>
</span>
{% endif %}
{% if visitor.getColumn('deviceTypeIcon') %}
<span class="visitorLogIconWithDetails">
<img src="{{ visitor.getColumn('deviceTypeIcon') }}"/>
<ul class="details">
<li>{{ 'DevicesDetection_DeviceType'|translate }}: {{ visitor.getColumn('deviceType') }}</li>
{% if visitor.getColumn('deviceBrand') %}<li>{{ 'DevicesDetection_DeviceBrand'|translate }}: {{ visitor.getColumn('deviceBrand') }}</li>{% endif %}
{% if visitor.getColumn('deviceModel') %}<li>{{ 'DevicesDetection_DeviceModel'|translate }}: {{ visitor.getColumn('deviceModel') }}</li>{% endif %}
{% if visitor.getColumn('resolution') %}<li>{{ 'Resolution_ColumnResolution'|translate }}: {{ visitor.getColumn('resolution') }}</li>{% endif %}
</ul>
</span>
{% endif %}
</span>
<span class="visitorType">
{# Goals, and/or Ecommerce activity #}
{% if visitor.getColumn('visitConverted') %}
<span title="{{ 'General_VisitConvertedNGoals'|translate(visitor.getColumn('goalConversions')) }}" class='visitorRank visitorLogTooltip'
{% if not displayVisitorsInOwnColumn or breakBeforeVisitorRank %}style="margin-left:0;"{% endif %}>
<img src="{{ visitor.getColumn('visitConvertedIcon') }}"/>
<span class='hash'>#</span>
{{ visitor.getColumn('goalConversions') }}
{% if visitHasEcommerceActivity %}
&nbsp;
<img src="{{ visitor.getColumn('visitEcommerceStatusIcon') }}" class='visitorLogTooltip' title="{{ visitor.getColumn('visitEcommerceStatus') }}"/>
{% endif %}
</span>
{# Ecommerce activity only (no goal) #}
{% elseif visitHasEcommerceActivity %}
<img class="visitorLogTooltip" src="{{ visitor.getColumn('visitEcommerceStatusIcon') }}" title="{{ visitor.getColumn('visitEcommerceStatus') }}"/>
{% endif %}
</span>
</span>
{% endset %}
{% set referrerColumnContent %}
<div class="referrer">
{% set referrerContent %}
<div class="visitorReferrer">
{% if visitor.getColumn('referrerType') == 'website' %}
{{ 'Referrers_ColumnWebsite'|translate }}:
<a href="{{ visitor.getColumn('referrerUrl') }}" target="_blank" title="{{ visitor.getColumn('referrerUrl') }}"
<a href="{{ visitor.getColumn('referrerUrl') }}" rel="noreferrer" target="_blank" class="visitorLogTooltip" 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') }}
{{ 'Referrers_ColumnCampaign'|translate }}: {{ visitor.getColumn('referrerName') }}
{% if visitor.getColumn('referrerKeyword') is not empty %} - {{ visitor.getColumn('referrerKeyword') }}{% endif %}
{% endif %}
{% if visitor.getColumn('referrerType') == 'search' %}
@ -88,17 +88,16 @@
{% 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>
<span {% if not showKeyword %}title="{{ keywordNotDefined }}" class="visitorLogTooltip"{% endif %}>{{ visitor.getColumn('referrerName') }}</span>
{% if showKeyword %}{{ 'Referrers_Keywords'|translate }}:
<br/>
<a href="{{ visitor.getColumn('referrerUrl') }}" target="_blank" style="text-decoration:underline;">
<a href="{{ visitor.getColumn('referrerUrl') }}" rel="noreferrer" 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 title='{{ 'Live_KeywordRankedOnSearchResultForThisVisitor'|translate(keyword,position,searchName) }}' class='visitorRank visitorLogTooltip'>
<span class='hash'>#</span>
{{ visitor.getColumn('referrerKeywordPosition') }}
</span>
@ -108,31 +107,62 @@
</div>
{% endset %}
{% set visitorRow %}
<tr class="label{{ cycle(['odd','even'], cycleIndex) }}">
<div class="card row">
{% if visitor.getColumn('visitorId') is not empty and not clientSideParameters.hideProfileLink %}
<a class="visitor-log-visitor-profile-link visitorLogTooltip" title="{{ 'Live_ViewVisitorProfile'|translate }}" data-visitor-id="{{ visitor.getColumn("visitorId") }}">
<img src="plugins/Live/images/visitorProfileLaunch.png"/> <span>{{ 'Live_ViewVisitorProfile'|translate }}
{%- if visitor.getColumn('userId') is not empty %}: {{ visitor.getColumn('userId')|raw }}{% endif %}</span>
</a>
{% endif %}
{% 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 %}">
<div class="col-md-{% if displayVisitorsInOwnColumn %}3{% else %}4{% endif %}">
<strong class="visitorLogTooltip" 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') %}
<span class="visitorLogTooltip" title="{% if visitor.getColumn('userId') is not empty %}{{ 'General_UserId'|translate }}: {{ visitor.getColumn('userId')|raw }}{% endif %}
{% 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 %}
IP: {{ visitor.getColumn('visitIp') }}
{% if visitor.getColumn('userId') is not empty %}<br/><br/>{{ visitor.getColumn('userId')|raw }}{% endif %}
{% if visitor.getColumn('provider') and visitor.getColumn('providerName')!='IP' %}
</span>{% endif %}
{% if visitor.getColumn('provider') %}
<br/>
{{ 'Provider_ColumnProvider'|translate }}:
<a href="{{ visitor.getColumn('providerUrl') }}" target="_blank" title="{{ visitor.getColumn('providerUrl') }}" style="text-decoration:underline;">
{{ visitor.getColumn('providerName') }}
</a>
<a href="{{ visitor.getColumn('providerUrl') }}" rel="noreferrer" target="_blank" class="visitorLogTooltip" title="{{ visitor.getColumn('providerName') }} {{ visitor.getColumn('providerUrl') }}" style="text-decoration:underline;">
{{ visitor.getColumn('providerName') }}</a>
{% endif %}
{% if visitor.getColumn('visitorTypeIcon') or visitor.getColumn('countryFlag') %}
<br/>
{% endif %}
{% if visitor.getColumn('visitorTypeIcon') %}
<span class="visitorLogIconWithDetails">
<img src="{{ visitor.getColumn('visitorTypeIcon') }}"/>
<ul class="details">
<li>{{ 'General_ReturningVisitor'|translate }} - {{ 'General_NVisits'|translate(visitor.getColumn('visitCount')) }}</li>
</ul>
</span>
{% endif %}
{% if visitor.getColumn('countryFlag') %}
<span class="visitorLogIconWithDetails">
<img src="{{ visitor.getColumn('countryFlag') }}"/>
<ul class="details">
<li>{{ 'UserCountry_Country'|translate }}: {{ visitor.getColumn('country') }}</li>
{% if visitor.getColumn('region') %}<li>{{ 'UserCountry_Region'|translate }}: {{ visitor.getColumn('region') }}</li>{% endif %}
{% if visitor.getColumn('city') %}<li>{{ 'UserCountry_City'|translate }}: {{ visitor.getColumn('city') }}</li>{% endif %}
</ul>
</span>
{% endif %}
{% if visitor.getColumn('customVariables') %}
<br/>
@ -140,7 +170,7 @@ GPS (lat/long): {{ visitor.getColumn('latitude') }},{{ visitor.getColumn('longit
{% set name='customVariableName' ~ id %}
{% set value='customVariableValue' ~ id %}
<br/>
<acronym title="{{ 'CustomVariables_CustomVariables'|translate }} (index {{ id }})">
<acronym class="visitorLogTooltip" title="{{ 'CustomVariables_CustomVariables'|translate }} (index {{ id }})">
{{ customVariable[name]|truncate(30) }}
</acronym>
{% if customVariable[value]|length > 0 %}: {{ customVariable[value]|truncate(50) }}{% endif %}
@ -150,31 +180,18 @@ GPS (lat/long): {{ visitor.getColumn('latitude') }},{{ visitor.getColumn('longit
<br/>
{{ visitorColumnContent }}
{% endif %}
{% if not displayReferrersInOwnColumn %}
<br/>
{{ referrerColumnContent }}
{% endif %}
</td>
{{ referrerContent }}
</div>
{% if displayVisitorsInOwnColumn %}
<td class="label">
<div class="col-md-2 own-visitor-column">
{{ visitorColumnContent }}
</td>
</div>
{% endif %}
{% if displayReferrersInOwnColumn %}
<td class="column">
{{ referrerColumnContent }}
</td>
{% endif %}
<td class="column {% if visitor.getColumn('visitConverted') and not isWidget %}highlightField{% endif %}">
<div class="col-md-{% if displayVisitorsInOwnColumn %}7{% else %}8{% endif %} column {% if visitor.getColumn('visitConverted') and not isWidget %}highlightField{% endif %}">
{{ postEvent('Live.visitorLogViewBeforeActionsInfo', visitor) }}
<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 %}
@ -189,14 +206,10 @@ GPS (lat/long): {{ visitor.getColumn('latitude') }},{{ visitor.getColumn('longit
{% include "@Live/_actionsList.twig" with {'actionDetails': visitor.getColumn('actionDetails')} %}
</ol>
</div>
</td>
</tr>
{{ postEvent('Live.visitorLogViewAfterActionsInfo', visitor) }}
</div>
</div>
{% endset %}
{% if not clientSideParameters.filterEcommerce or visitor.getMetadata('hasEcommerce') %}
{{ visitorRow }}
{% endif %}
{{ visitorRow }}
{% endfor %}
</tbody>
</table>

View file

@ -2,27 +2,27 @@
<table class="dataTable" cellspacing="0">
<thead>
<tr>
<th id="label" class="sortable label" style="cursor: auto;">
<th id="label" class="sortable label first" 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>
<div id="thDIV">{{ 'General_Actions'|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>
<td class="label column">{{ 'Live_LastHours'|translate(24) }}</td>
<td class="column">{{ visitorsCountToday }}</td>
<td class="column">{{ 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>
<td class="label column">{{ 'Live_LastMinutes'|translate(30) }}</td>
<td class="column">{{ visitorsCountHalfHour }}</td>
<td class="column">{{ pisHalfhour }}</td>
</tr>
</tbody>
</table>

View file

@ -7,10 +7,21 @@
<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 %}
&nbsp;<img src="{{ visitor.countryFlag }}" title="{{ visitor.location }}, {{ 'Provider_ColumnProvider'|translate }} {{ visitor.providerName }}"/>
&nbsp;<img src="{{ visitor.browserIcon }}" title="{{ visitor.browserName }}, {{ 'General_Plugins'|translate }}: {{ visitor.plugins }}"/>
&nbsp;<img src="{{ visitor.operatingSystemIcon }}" title="{{ visitor.operatingSystem }}, {{ visitor.resolution }}"/>
{{ postEvent('Live.visitorLogWidgetViewBeforeVisitInfo', visitor) }}
{% set year = visitor.serverTimestamp|date('Y') %}
{{ visitor.serverDatePretty|replace({(year): ' '}) }} - {{ visitor.serverTimePretty }} {% if visitor.visitDuration > 0 %}<em>({{ visitor.visitDurationPretty|raw }})</em>{% endif %}
{% if visitor.visitorId|default(false) is not empty %}
&nbsp; <a class="visits-live-launch-visitor-profile rightLink" title="{{ 'Live_ViewVisitorProfile'|translate }} {% if visitor.userId is not empty %}{{ visitor.userId|raw }}{% endif %}" data-visitor-id="{{ visitor.visitorId }}">
{% if visitor.userId is not empty %}<br/>{% endif %}
<img src="plugins/Live/images/visitorProfileLaunch.png"/>
{{ visitor.userId|default('')|raw }}
</a>
{% endif %}
<br />
{% if visitor.countryFlag is defined %}&nbsp;<img src="{{ visitor.countryFlag }}" title="{{ visitor.location }}, {{ 'Provider_ColumnProvider'|translate }} {% if visitor.providerName is defined %}{{ visitor.providerName }}{% endif %}"/>{% endif %}
{% if visitor.browserIcon is defined %}&nbsp;<img src="{{ visitor.browserIcon }}" title="{{ visitor.browser }}{% if visitor.plugins is defined %}, {{ 'General_Plugins'|translate }}: {{ visitor.plugins }}{% endif %}"/>{% endif %}
{% if visitor.operatingSystemIcon is defined %}&nbsp;<img src="{{ visitor.operatingSystemIcon }}" title="{{ visitor.operatingSystem }}{% if visitor.resolution is defined %}, {{ visitor.resolution }}{% endif %}"/>{% endif %}
&nbsp;
{% if visitor.visitConverted %}
<span title="{{ 'General_VisitConvertedNGoals'|translate(visitor.goalConversions) }}" class='visitorRank'>
@ -18,29 +29,24 @@
<span class='hash'>#</span>
{{ visitor.goalConversions }}
{% if visitor.visitEcommerceStatusIcon %}
&nbsp;-
<img src="{{ visitor.visitEcommerceStatusIcon }}" title="{{ visitor.visitEcommerceStatus }}"/>
{% endif %}
</span>
{% endif %}
{% if visitor.visitorTypeIcon %}
&nbsp;- <img src="{{ visitor.visitorTypeIcon }}" title="{{ 'General_ReturningVisitor'|translate }}"/>
<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 %}">
{% 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' %}
<span class="referrer">
{% if visitor.referrerType is defined and visitor.referrerType != 'direct' %}
{{ 'General_FromReferrer'|translate }}
{% if visitor.referrerUrl is not empty %}
<a href="{{ visitor.referrerUrl }}" target="_blank">
<a href="{{ visitor.referrerUrl }}" rel="noreferrer" target="_blank">
{% endif %}
{% if visitor.searchEngineIcon is defined %}
<img src="{{ visitor.searchEngineIcon }}" />
@ -58,39 +64,41 @@
<span class='hash'>#</span> {{ visitor.referrerKeywordPosition }}
</span>
{% endif %}
{% else %}
{% elseif visitor.referrerType is defined %}
{{ 'Referrers_DirectEntry'|translate }}
{% endif %}
</div>
</span></div>
<div id="{{ visitor.idVisit }}_actions" class="settings">
<span class="pagesTitle" title="{{ visitor.actionDetails|length }} {{ 'General_Actions'|translate }}">{{ 'General_Pages'|translate }}:</span>&nbsp;
<span class="pagesTitle" title="{{ visitor.actionDetails|length }} {{ 'General_Actions'|translate }}">{{ 'General_Actions'|translate }}:</span>&nbsp;
{% 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 }}
{%- 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 %}
{{- 'Goals_AbandonedCart'|translate -}}
{% endif %}
{{- "\n - " -}}
{%- if action.type == 'ecommerceOrder' -%}
{{- 'General_ColumnRevenue'|translate -}}:
{%- else -%}
{%- set revenueLeft -%}
{{- 'General_ColumnRevenue'|translate -}}
{%- endset -%}
{{- 'Goals_LeftInCart'|translate(revenueLeft) -}}:
{%- endif %} {{ action.revenue|money(idSite)|raw -}}
{{- "\n - " -}}{{- action.serverTimePretty -}}
{{- "\n" -}}
{% if action.itemDetails is not empty -%}
{% for product in action.itemDetails -%}
{{- "\n# " -}}{{ 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 }}">
<span title="{{- title -}}">
<img class='iconPadding' src="{{ action.icon }}"/>
{% if action.type == 'ecommerceOrder' %}
{{ 'General_ColumnRevenue'|translate }}: {{ action.revenue|money(idSite)|raw }}
@ -105,7 +113,7 @@
{% if action.type == 'action' %}
{# white spacing matters as Chrome tooltip display whitespaces #}
{% set title %}
{% if action.pageTitle is not empty %}{{ action.pageTitle }}{% endif %}
{% if action.pageTitle is not empty %}{{ action.pageTitle|rawSafeDecoded }}{% endif %}
{{ action.serverTimePretty }}
{% if action.timeSpentPretty is defined %}{{ 'General_TimeOnPage'|translate }}: {{ action.timeSpentPretty|raw }}{% endif %}

View file

@ -12,7 +12,7 @@
<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>
<span class="simple-realtime-metric" data-metric="minutes">{% if lastMinutes == 1 %}{{ 'Intl_OneMinute'|translate }}{% else %}{{ 'Intl_NMinutes'|translate(lastMinutes) }}{% endif %}</span>
{% endset %}
{{ 'Live_SimpleRealTimeWidget_Message'|translate(visitsMessage,actionsMessage,minutesMessage) | raw }}

View file

@ -12,7 +12,7 @@
<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>
<span>{{ 'General_IP'|translate }}</span><strong {% if visitData.providerName is defined and 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>
@ -20,18 +20,22 @@
<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"/>
<img src="plugins/Morpheus/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>
{% if visitData.browserName is defined %}
<div class="visitor-profile-browser" title="{% if visitData.plugins is not defined %}{{ visitData.browser }}{% elseif visitData.plugins %}{{ 'DevicePlugins_BrowserWithPluginsEnabled'|translate(visitData.browser, visitData.plugins) }}{% else %}{{ 'DevicePlugins_BrowserWithNoPluginsEnabled'|translate(visitData.browser) }}{% endif %}">
{% if visitData.browserIcon is defined %}<img src="{{ visitData.browserIcon }}"/>{% endif %}<span>{{ visitData.browserName|split(' ')[0] }}</span>
</div>
{% endif %}
<div class="visitor-profile-os">
<img src="{{ visitData.operatingSystemIcon }}"/><span>{{ visitData.operatingSystemShortName }}</span>
{% if visitData.operatingSystemIcon is defined %}<img src="{{ visitData.operatingSystemIcon }}"/>{% endif %}{% if visitData.operatingSystem is defined %}<span>{{ visitData.operatingSystem }}</span>{% endif %}
</div>
</li>
<li><span>{{ 'UserSettings_ColumnResolution'|translate }}</span><strong>{{ visitData.resolution }}</strong></li>
{% if visitData.resolution is defined %}<li><span>{{ 'Resolution_ColumnResolution'|translate }}</span><strong>{{ visitData.resolution }}</strong></li>{% endif %}
{% if visitData.userId is not empty %}<li><span>{{ 'General_UserId'|translate }}</span><strong>{{ visitData.userId|raw }}</strong></li>{% endif %}
{% if visitReferralSummary is defined %}
{%- set keywordNotDefined = 'General_NotDefined'|translate('General_ColumnKeyword'|translate) -%}
<li>
@ -42,6 +46,7 @@
</ul>
</div>
<div class="visitor-profile-latest-visit-column">
{% if visitData.customVariables is defined %}
<ul>
{% for id,customVariable in visitData.customVariables %}
{% if loop.index0 < 4 %}
@ -49,7 +54,8 @@
{% endif %}
{% endfor %}
</ul>
{% if visitData.customVariables|length > 4 %}
{% endif %}
{% if visitData.customVariables is defined and visitData.customVariables|length > 4 %}
<ul class="visitor-profile-extra-cvars" style="display:none;">
{% for id,customVariable in visitData.customVariables %}
{% if loop.index0 >= 4 %}

View file

@ -1,11 +1,21 @@
{% 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>&nbsp;- ({{ visitInfo.getColumn('visitDurationPretty')|raw }})</span>{% endif %}<span class="visitor-profile-date" title="{{ visitInfo.getColumn('serverDateTimePrettyFirstAction') }}">{{ visitInfo.getColumn('serverDatePrettyFirstAction') }}</span></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 }}
{% if visitInfo.getColumn('visitDuration') != 0 %}
<span>&nbsp;- ({{ visitInfo.getColumn('visitDurationPretty')|raw }})</span>
{% endif %}
<span class="visitor-profile-date" title="{{ visitInfo.getColumn('serverDatePrettyFirstAction') }} {{ visitInfo.getColumn('serverTimePrettyFirstAction') }}">
{{ visitInfo.getColumn('serverDatePrettyFirstAction') }} {{ visitInfo.getColumn('serverTimePrettyFirstAction') }}
</span>
</h2>
</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)
},
@ -14,4 +24,4 @@
</div>
</li>
{% set startCounter = startCounter + 1 %}
{% endfor %}
{% endfor %}

View file

@ -1,3 +1,6 @@
{% if not visitorData %}
<div class="pk-emptyDataTable">{{ 'CoreHome_ThereIsNoDataForThisReport'|translate }}</div>
{% else %}
<div class="visitor-profile"
data-visitor-id="{{ visitorData.lastVisits.getFirstRow().getColumn('visitorId') }}"
data-next-visitor="{{ visitorData.nextVisitorId }}"
@ -11,15 +14,21 @@
<div>
<div class="visitor-profile-image-frame">
<img src="{{ visitorData.visitorAvatar|default("plugins/Live/images/unknown_avatar.jpg") }}"
alt="{{ visitorData.visitorDescription|default('') }}"/>
alt="{{ visitorData.visitorDescription|default('') }}"
title="{{ 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 }}">&larr;</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>
<h1>{{ 'Live_VisitorProfile'|translate }}
{%- if visitorData.userId is not empty %}: <span title="{{'General_UserId'|translate}}: {{ visitorData.userId|raw }}">{{ visitorData.userId|raw }}</span>{% endif -%}
<img class="loadingPiwik" style="display:none;" src="plugins/Morpheus/images/loading-blue.gif"/>
</h1>
<a href="http://piwik.org/docs/user-profile/" class="visitorProfileHelp" rel="noreferrer" target="_blank" title="{{ 'General_ViewDocumentationFor'|translate("Live_VisitorProfile"|translate|ucwords) }}">
<span class="icon-help"></span>
</a>
{% if visitorData.nextVisitorId is not empty %}<a class="visitor-profile-next-visitor" href="#" title="{{ 'Live_NextVisitor'|translate }}">&rarr;</a>{% endif %}
</div>
<div class="visitor-profile-latest-visit">
@ -31,20 +40,30 @@
<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>
{% if visitorData.totalPageViews != visitorData.totalActions %}
{% set actionDetails = [] %}
{% if visitorData.totalPageViews > 0 %}{% set actionDetails = actionDetails|merge([visitorData.totalPageViews ~ ' ' ~ 'General_ColumnPageviews'|translate]) %}{% endif %}
{% if visitorData.totalEvents > 0 %}{% set actionDetails = actionDetails|merge([visitorData.totalEvents ~ ' ' ~ 'Events_Events'|translate]) %}{% endif %}
{% if visitorData.totalDownloads > 0 %}{% set actionDetails = actionDetails|merge([visitorData.totalDownloads ~ ' ' ~ 'General_Downloads'|translate]) %}{% endif %}
{% if visitorData.totalOutlinks > 0 %}{% set actionDetails = actionDetails|merge([visitorData.totalOutlinks ~ ' ' ~ 'General_Outlinks'|translate]) %}{% endif %}
{% if visitorData.totalSearches > 0 %}{% set actionDetails = actionDetails|merge([visitorData.totalSearches ~ ' ' ~ 'Actions_ColumnSearches'|translate]) %}{% endif %}
<p>{{ 'Live_VisitSummaryWithActionDetails'|translate('<strong>' ~ visitorData.totalVisitDurationPretty ~ '</strong>', '', '', '<strong>' ~ visitorData.totalActions, '</strong>', actionDetails|join(', ') , '<strong>' ~ visitorData.totalVisits, '</strong>')|raw }}</p>
{% else %}
<p>{{ 'Live_VisitSummary'|translate('<strong>' ~ visitorData.totalVisitDurationPretty ~ '</strong>', '', '', '<strong>' ~ visitorData.totalActions, '</strong>', '<strong>' ~ visitorData.totalVisits, '</strong>')|raw }}</p>
{% endif %}
<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>
{%- if not loop.first %}, {% endif -%}{{- totalConversions }} {{ goals[idGoal]['name'] -}}
{%- 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 }}
{%- if visitorData.totalEcommerceConversions|default(0) > 0 %} {{ 'Live_EcommerceSummaryConversions'|translate('<strong>' ~ visitorData.totalEcommerceConversions, '</strong>', '<strong>' ~ visitorData.totalEcommerceRevenue|money(idSite), '</strong>', '<strong>' ~ visitorData.totalEcommerceItems, '</strong>')|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 -%}
{%- if visitorData.totalAbandonedCarts|default(0) > 0 %} {{ 'Live_AbandonedCartSummary'|translate('<strong>' ~ visitorData.totalAbandonedCarts, '</strong>', '<strong>' ~ visitorData.totalAbandonedCartsItems, '</strong>', '<strong>' ~ visitorData.totalAbandonedCartsRevenue|money(idSite), '</strong>')|raw }}{%- endif -%}
</p>
{% endif %}
{% if visitorData.totalSearches|default(0) %}
@ -54,7 +73,7 @@
</p>
{% endif %}
{% if visitorData.averagePageGenerationTime is defined %}
<p title="{{ 'Live_CalculatedOverNPageViews'|translate(visitorData.totalPageViews) }}">
<p title="{{ 'Live_CalculatedOverNPageViews'|translate(visitorData.totalPageViewsWithTiming) }}">
{{ 'Live_AveragePageGenerationTime'|translate('<strong>' ~ visitorData.averagePageGenerationTime ~ 's</strong>')|raw }}
</p>
{% endif %}
@ -65,18 +84,18 @@
<div>
<h1>{% if visitorData.visitsAggregated == 100 %}{{ 'General_Visit'|translate }}# 100{% else %}{{ 'Live_FirstVisit'|translate }}{% endif %}</h1>
<div>
<p><strong>{{ visitorData.firstVisit.prettyDate }}</strong><span>&nbsp;- {{ '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>
<p><strong>{{ visitorData.firstVisit.prettyDate }}</strong>&nbsp;- {{ 'UserCountryMap_DaysAgo'|translate(visitorData.firstVisit.daysAgo) }}</p>
<p>
{{ 'General_FromReferrer'|translate }}<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>&nbsp;- {{ '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>
<p><strong>{{ visitorData.lastVisit.prettyDate }}</strong>&nbsp;- {{ 'UserCountryMap_DaysAgo'|translate(visitorData.lastVisit.daysAgo) }}</p>
<p>
{{ 'General_FromReferrer'|translate }}<strong {% if visitorData.lastVisit.referrerType == 'search' and '(' not in visitorData.lastVisit.referralSummary %}title="{{ keywordNotDefined }}"{% endif %}>{{ visitorData.lastVisit.referralSummary }}</strong></p>
</div>
</div>
{% endif %}
@ -117,7 +136,7 @@
{{- '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({' ': '&nbsp;'})|raw }})</a> <img class="loadingPiwik" style="display:none;" src="plugins/Zeitgeist/images/loading-blue.gif"/>
<a class="visitor-profile-show-map" href="#" {% if userCountryMapUrl|default('') is empty %}style="display:none"{% endif %}>({{ 'Live_ShowMap'|translate|replace({' ': '&nbsp;'})|raw }})</a> <img class="loadingPiwik" style="display:none;" src="plugins/Morpheus/images/loading-blue.gif"/>
</p>
<div class="visitor-profile-map" style="display:none" data-href="{{ userCountryMapUrl|default('') }}">
</div>
@ -134,8 +153,8 @@
</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"/>
{% if visitorData.lastVisits.getRowsCount() >= constant("Piwik\\Plugins\\Live\\VisitorProfile::VISITOR_PROFILE_MAX_VISITS_TO_SHOW") %}
<a href="#">{{ 'Live_LoadMoreVisits'|translate }}</a> <img class="loadingPiwik" style="display:none;" src="plugins/Morpheus/images/loading-blue.gif"/>
{% else %}
<span class="visitor-profile-no-visits">{{ 'Live_NoMoreVisits'|translate }}</span>
{% endif %}
@ -146,4 +165,5 @@
</div>
<script type="text/javascript">
$(function() { require('piwik/UI').VisitorProfileControl.initElements(); });
</script>
</script>
{% endif %}

View file

@ -35,15 +35,15 @@
{% 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 title="{{ 'Live_OnClickPause'|translate('Live_VisitorsInRealTime'|translate) }}" href="javascript:void(0);" onclick="onClickPause();">
<img id="pauseImage" border="0" src="plugins/Live/images/pause.gif" />
</a>
<a title="Start Live!" href="javascript:void(0);" onclick="onClickPlay();">
<img id="playImage" border="0" src="plugins/Live/images/play.gif" />
<a title="{{ 'Live_OnClickStart'|translate('Live_VisitorsInRealTime'|translate) }}" href="javascript:void(0);" onclick="onClickPlay();">
<img id="playImage" style="display: none;" border="0" src="plugins/Live/images/play.gif" />
</a>
{% if not disableLink %}
&nbsp;
<a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a>
<a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=indexVisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a>
{% endif %}
</div>
{% endspaceless %}

View file

@ -1,3 +1,3 @@
<h2 piwik-enriched-headline>{% if filterEcommerce %}{{ 'Goals_EcommerceLog'|translate }}{% else %}{{ 'Live_VisitorLog'|translate }}{% endif %}</h2>
<h2 piwik-enriched-headline>{{ 'Live_VisitorLog'|translate }}</h2>
{{ visitorLog|raw }}