Skip to content
Extraits de code Groupes Projets
Valider a3bc6979 rédigé par Benaka Moorthi's avatar Benaka Moorthi
Parcourir les fichiers

Refs #3089, add initial visitor profile popup that uses dynamic data from Live...

Refs #3089, add initial visitor profile popup that uses dynamic data from Live plugin API. No client-side behavior yet & placeholder images still present.
parent cfb17ed2
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
Affichage de
avec 216 ajouts et 15 suppressions
......@@ -19,6 +19,8 @@ use Exception;
*/
class Date
{
const NUM_SECONDS_IN_DAY = 86400;
/**
* The stored timestamp is always UTC based.
* The returned timestamp via getTimestamp() will have the conversion applied
......@@ -641,4 +643,15 @@ class Date
{
return $this->addPeriod(-$n, $period);
}
/**
* Returns the number of days represented by a number of seconds.
*
* @param int $secs
* @return float
*/
public static function secondsToDays($secs)
{
return $secs / self::NUM_SECONDS_IN_DAY;
}
}
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
......@@ -54,7 +54,7 @@
.jspTrack
{
background: url("plugins/SegmentEditor/images/slide.png") transparent no-repeat 7px;
background: url("../images/slide.png") transparent no-repeat 7px;
position: relative;
background-size: 20% 100%;
/*height: 447px!important;*/
......@@ -62,7 +62,7 @@
.jspDrag
{
background: url("plugins/SegmentEditor/images/scroller.png") transparent no-repeat;
background: url("../images/scroller.png") transparent no-repeat;
background-size: 100% 100%;
width: 17px;
position: relative;
......@@ -86,10 +86,10 @@
cursor: pointer;
}
.jspArrowDown{
background: url("plugins/SegmentEditor/images/down_arrow.png") transparent no-repeat;
background: url("../images/down_arrow.png") transparent no-repeat;
}
.jspArrowUp{
background: url("plugins/SegmentEditor/images/up_arrow.png") transparent no-repeat;
background: url("../images/up_arrow.png") transparent no-repeat;
}
.jspVerticalBar .jspArrow
......
......@@ -41,6 +41,8 @@ class Piwik_CoreHome extends Plugin
public function getCssFiles(&$cssFiles)
{
$cssFiles[] = "libs/jquery/themes/base/jquery-ui.css";
$cssFiles[] = "libs/jquery/stylesheets/jquery.jscrollpane.css";
$cssFiles[] = "libs/jquery/stylesheets/scroll.less";
$cssFiles[] = "plugins/Zeitgeist/stylesheets/base.less";
$cssFiles[] = "plugins/CoreHome/stylesheets/coreHome.less";
$cssFiles[] = "plugins/CoreHome/stylesheets/menu.less";
......@@ -62,6 +64,9 @@ class Piwik_CoreHome extends Plugin
$jsFiles[] = "libs/jquery/jquery.truncate.js";
$jsFiles[] = "libs/jquery/jquery.scrollTo.js";
$jsFiles[] = "libs/jquery/jquery.history.js";
$jsFiles[] = "libs/jquery/jquery.jscrollpane.js";
$jsFiles[] = "libs/jquery/jquery.mousewheel.js";
$jsFiles[] = "libs/jquery/mwheelIntent.js";
$jsFiles[] = "libs/javascript/sprintf.js";
$jsFiles[] = "plugins/Zeitgeist/javascripts/piwikHelper.js";
$jsFiles[] = "plugins/Zeitgeist/javascripts/ajaxHelper.js";
......
......@@ -17,10 +17,11 @@ var Piwik_Popover = (function () {
}
};
var openPopover = function (title) {
var openPopover = function (title, dialogClass) {
createContainer();
container.dialog({
var options =
{
title: title,
modal: true,
width: '950px',
......@@ -28,6 +29,10 @@ var Piwik_Popover = (function () {
resizable: false,
autoOpen: true,
open: function (event, ui) {
if (dialogClass) {
$(this).parent().addClass(dialogClass).attr('style', '');
}
$('.ui-widget-overlay').on('click.popover', function () {
container.dialog('close');
});
......@@ -45,7 +50,9 @@ var Piwik_Popover = (function () {
closeCallback = false;
}
}
});
};
container.dialog(options);
// override the undocumented _title function to ensure that the title attribute is not escaped (according to jQueryUI bug #6016)
container.data( "uiDialog" )._title = function(title) {
......@@ -70,7 +77,7 @@ var Piwik_Popover = (function () {
* @param {string} [popoverSubject] subject of the popover (e.g. url, optional)
* @param {int} [height] height of the popover in px (optional)
*/
showLoading: function (popoverName, popoverSubject, height) {
showLoading: function (popoverName, popoverSubject, height, dialogClass) {
var loading = $(document.createElement('div')).addClass('Piwik_Popover_Loading');
var loadingMessage = popoverSubject ? translations.General_LoadingPopoverFor_js :
......@@ -94,7 +101,7 @@ var Piwik_Popover = (function () {
}
if (!isOpen) {
openPopover();
openPopover(null, dialogClass);
}
this.setContent(loading);
......@@ -203,9 +210,18 @@ var Piwik_Popover = (function () {
* @param {string} url
* @param {string} loadingName
*/
createPopupAndLoadUrl: function (url, loadingName) {
createPopupAndLoadUrl: function (url, loadingName, dialogClass) {
// make sure the minimum top position of the popover is 106px
var ensureMinimumTop = function () {
var popoverContainer = $('#Piwik_Popover').parent();
if (popoverContainer.position().top < 106) {
popoverContainer.css('top', '106px');
}
};
// open the popover
var box = Piwik_Popover.showLoading(loadingName);
var box = Piwik_Popover.showLoading(loadingName, null, null, dialogClass);
ensureMinimumTop();
var callback = function (html) {
function setPopoverTitleIfOneFoundInContainer() {
......@@ -218,6 +234,7 @@ var Piwik_Popover = (function () {
Piwik_Popover.setContent(html);
setPopoverTitleIfOneFoundInContainer();
ensureMinimumTop();
};
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(piwikHelper.getArrayFromQueryString(url), 'get');
......@@ -226,6 +243,4 @@ var Piwik_Popover = (function () {
ajaxRequest.send(false);
}
};
})();
})();
\ No newline at end of file
......@@ -155,6 +155,158 @@ class Piwik_Live_API
return $dataTable;
}
const VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE = 100;
const VISITOR_PROFILE_MAX_VISITS_TO_SHOW = 10;
const VISITOR_PROFILE_DATE_FORMAT = '%day% %shortMonth% %longYear%';
/**
* TODO
* TODO: add abandoned cart info.
* TODO: check for most recent vs. first visit
* TODO: make sure ecommerce is enabled for site, check for goals plugin, etc.
*/
public function getVisitorProfile($idSite, $period, $date, $idVisitor, $segment = false)
{
if ($segment !== false) {
$segment .= '&';
}
$segment .= 'visitorId==' . $idVisitor; // TODO what happens when visitorId is in the segment?
$visits = $this->getLastVisitsDetails($idSite, $period, $date, $segment, $filter_limit = self::VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE);
if ($visits->getRowsCount() == 0) {
return array();
}
$result = array();
// use the most recent visit for IP/browser/OS/etc. info
// TODO: could just do all of this in twig/JS... really need to do it here?
$mostRecentVisit = $visits->getFirstRow();
$result['latestVisitIp'] = $mostRecentVisit->getColumn('visitIp');
$result['visitorId'] = $mostRecentVisit->getColumn('visitorId');
$result['browserCode'] = $mostRecentVisit->getColumn('browserCode');
$result['browserName'] = Piwik_UserSettings_getBrowserFromBrowserVersion($mostRecentVisit->getColumn('browserName'));
$result['browserLogo'] = $mostRecentVisit->getColumn('browserIcon');
$result['operatingSystemCode'] = $mostRecentVisit->getColumn('operatingSystemCode');
$result['operatingSystemShortName'] = $mostRecentVisit->getColumn('operatingSystemShortName');
$result['operatingSystemLogo'] = $mostRecentVisit->getColumn('operatingSystemIcon');
$result['resolution'] = $mostRecentVisit->getColumn('resolution');
$result['customVariables'] = $mostRecentVisit->getColumn('customVariables');
// aggregate all requested visits info for total_* info
$result['totalVisits'] = 0;
$result['totalVisitDuration'] = 0;
$result['totalActionCount'] = 0;
$result['totalGoalConversions'] = 0;
$result['totalEcommerceConversions'] = 0;
$result['totalEcommerceRevenue'] = 0;
$result['totalEcommerceItems'] = 0;
$result['totalAbandonedCarts'] = 0;
$result['totalAbandonedCartsRevenue'] = 0;
$result['totalAbandonedCartsItems'] = 0;
foreach ($visits->getRows() as $visit) {
++$result['totalVisits'];
$result['totalVisitDuration'] += $visit->getColumn('visitDuration');
$result['totalActionCount'] += $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'];
if (!isset($result['totalConversionsByGoal'][$idGoal])) {
$result['totalConversionsByGoal'][$idGoal] = 0;
}
++$result['totalConversionsByGoal'][$idGoal];
if (!empty($action['revenue'])) {
if (!isset($result['totalRevenueByGoal'][$idGoal])) {
$result['totalRevenueByGoal'][$idGoal] = 0;
}
$result['totalRevenueByGoal'][$idGoal] += $action['revenue'];
}
} else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { // handle ecommerce order
++$result['totalEcommerceConversions'];
$result['totalEcommerceRevenue'] += $action['revenue'];
$result['totalEcommerceItems'] += $action['items'];
} else if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) { // handler abandoned cart
++$result['totalAbandonedCarts'];
$result['totalAbandonedCartsRevenue'] += $action['revenue'];
$result['totalAbandonedCartsItems'] += $action['items'];
}
}
}
$result['totalVisitDurationPretty'] = Piwik::getPrettyTimeFromSeconds($result['totalVisitDuration']);
// use requested visits for first/last visit info
$result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($visits->getRows()));
$result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($visits->getRows()));
// 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);
$dateTimePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT);
$visit->setColumn('serverDatePrettyFirstAction', $dateTimePretty);
}
return $result;
}
/**
* Returns a summary for an important visit. Used to describe the first & last visits of a visitor.
*
* @param Piwik\DataTable\Row $visit
*/
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()),
'referralSummary' => $this->getReferrerSummaryForVisit($visit),
);
}
/**
* Returns a summary for a visit's referral.
*
* @param Piwik\DataTable\Row $visit
*/
private function getReferrerSummaryForVisit($visit)
{
$referrerType = $visit->getColumn('referrerType');
if ($referrerType === false
|| $referrerType == 'direct'
) {
$result = Piwik_Translate('Referers_DirectEntry');
} else if ($referrerType == 'search') {
$result = $visit->getColumn('referrerName');
$keyword = $visit->getColumn('referrerKeyword');
if ($keyword !== false) {
$result .= ' (' . $keyword . ')';
}
} else if ($referrerType == 'campaign') {
$result = Piwik_Translate('Referers_ColumnCampaign') . ' (' . $visit->getColumn('referrerName') . ')';
} else {
$result = $visit->getColumn('referrerName');
}
return $result;
}
/**
* @deprecated
*/
......
......@@ -99,6 +99,7 @@ class Piwik_Live_Controller extends Controller
*/
public function getVisitorLog($fetch = false)
{
$test = array(); $str = (string)$test;
return $this->getLastVisitsDetails($fetch);
}
......@@ -130,4 +131,18 @@ class Piwik_Live_Controller extends Controller
$view->pisToday = $today['actions'];
return $view;
}
/**
* TODO
*/
public function getVisitorProfilePopup()
{
$idSite = Common::getRequestVar('idSite', null, 'int');
$view = new View('@Live/getVisitorProfilePopup.twig');
$view->idSite = $idSite;
$view->goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
$view->visitorData = Request::processRequest('Live.getVisitorProfile');
echo $view->render();
}
}
\ No newline at end of file
......@@ -35,6 +35,7 @@ class Piwik_Live extends Plugin
public function getCssFiles(&$cssFiles)
{
$cssFiles[] = "plugins/Live/stylesheets/live.less";
$cssFiles[] = "plugins/Live/stylesheets/visitor_profile.less";
}
public function getJsFiles(&$jsFiles)
......@@ -89,4 +90,4 @@ class Piwik_Live extends Plugin
)
);
}
}
}
\ No newline at end of file
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
Le fichier a été supprimé par une entrée .gitattributes, ou son encodage n'est pas pris en charge.
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter