diff options
Diffstat (limited to 'plugins/Live')
-rw-r--r-- | plugins/Live/API.php | 143 | ||||
-rw-r--r-- | plugins/Live/Controller.php | 37 | ||||
-rw-r--r-- | plugins/Live/Live.php | 14 | ||||
-rw-r--r-- | plugins/Live/Visitor.php | 262 | ||||
-rw-r--r-- | plugins/Live/templates/images/pause.gif | bin | 0 -> 669 bytes | |||
-rw-r--r-- | plugins/Live/templates/images/pause_disabled.gif | bin | 0 -> 619 bytes | |||
-rw-r--r-- | plugins/Live/templates/images/play.gif | bin | 0 -> 666 bytes | |||
-rw-r--r-- | plugins/Live/templates/images/play_disabled.gif | bin | 0 -> 407 bytes | |||
-rw-r--r-- | plugins/Live/templates/images/returningVisitor.gif | bin | 0 -> 995 bytes | |||
-rw-r--r-- | plugins/Live/templates/index.tpl | 83 | ||||
-rw-r--r-- | plugins/Live/templates/lastVisits.tpl | 15 | ||||
-rw-r--r-- | plugins/Live/templates/scripts/spy.js | 139 |
12 files changed, 693 insertions, 0 deletions
diff --git a/plugins/Live/API.php b/plugins/Live/API.php new file mode 100644 index 0000000000..3ff93c223a --- /dev/null +++ b/plugins/Live/API.php @@ -0,0 +1,143 @@ +<?php +require_once "Live/Visitor.php"; + +class Piwik_Live_API +{ + static private $instance = null; + + /* + * @return Piwik_Live_API + */ + static public function getInstance() + { + if (self::$instance == null) + { + $c = __CLASS__; + self::$instance = new $c(); + } + return self::$instance; + } + + /* + * @return Piwik_DataTable + */ + public function getLastVisitForVisitor( $visitorId, $idSite = null ) + { + return $this->getLastVisitsForVisitor($visitorId, $idSite, 1); + } + + /* + * @return Piwik_DataTable + */ + public function getLastVisitsForVisitor( $visitorId, $idSite, $limit = 10 ) + { + if(is_null($idSite)) + { + Piwik::checkUserIsSuperUser(); + } + else + { + Piwik::checkUserHasViewAccess($idSite); + } + $visitorDetails = self::loadLastVisitorDetailsFromDatabase($visitorId, $idSite, $limit); + $table = self::getCleanedVisitorsFromDetails($visitorDetails); + return $table; + } + + /* + * @return Piwik_DataTable + */ + public function getLastVisits( $idSite = false, $limit = 10, $minIdVisit = false ) + { + if(is_null($idSite)) + { + Piwik::checkUserIsSuperUser(); + } + else + { + Piwik::checkUserHasViewAccess($idSite); + } + $visitorDetails = self::loadLastVisitorDetailsFromDatabase(null, $idSite, $limit, $minIdVisit); + $table = self::getCleanedVisitorsFromDetails($visitorDetails); + return $table; + } + + /* + * @return Piwik_DataTable + */ + static private function getCleanedVisitorsFromDetails($visitorDetails) + { + $table = new Piwik_DataTable(); + foreach($visitorDetails as $visitorDetail) + { + self::cleanVisitorDetails($visitorDetail); + $visitor = new Piwik_Live_Visitor($visitorDetail); + $visitorDetailsArray = $visitor->getAllVisitorDetails(); + $dateTimeVisit = Piwik_Date::factory($visitorDetailsArray['firstActionTimestamp']); + $visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized('%a %d %b'); + $visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%X'); + $table->addRowFromArray( array(Piwik_DataTable_Row::COLUMNS => $visitorDetailsArray)); + } + return $table; + } + + /* + * @return array + */ + private function loadLastVisitorDetailsFromDatabase($visitorId = null, $idSite = null, $limit = null, $minIdVisit = false ) + { + $where = $whereBind = array(); + + if(!is_null($idSite)) + { + $where[] = " idsite = ? "; + $whereBind[] = $idSite; + } + + if(!is_null($visitorId)) + { + $where[] = " visitor_idcookie = ? "; + $whereBind[] = $visitorId; + } + + if(!$minIdVisit) + { + $where[] = " idvisit > ? "; + $whereBind[] = $minIdVisit; + } + + $sqlWhere = ""; + if(count($where) > 0) + { + $sqlWhere = " WHERE " . join(' AND ', $where); + } + + $sql = "SELECT * + FROM " . Piwik::prefixTable('log_visit') . " + $sqlWhere + ORDER BY idvisit DESC + LIMIT $limit"; + + return Piwik_FetchAll($sql, $whereBind); + } + + /* + * @return void + */ + static private function cleanVisitorDetails( &$visitorDetails ) + { + $toUnset = array('config_md5config'); + if(!Piwik::isUserIsSuperUser()) + { + $toUnset[] = 'visitor_idcookie'; + $toUnset[] = 'location_ip'; + } + foreach($toUnset as $keyName) + { + if(isset($visitorDetails[$keyName])) + { + unset($visitorDetails[$keyName]); + } + } + } +} diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php new file mode 100644 index 0000000000..aeb13829d9 --- /dev/null +++ b/plugins/Live/Controller.php @@ -0,0 +1,37 @@ +<?php +require_once 'Live/API.php'; + +Piwik_AddWidget('Live', 'widget', 'Live Visitors!'); + +class Piwik_Live_Controller extends Piwik_Controller +{ + function widget() + { + echo "Live Visitors!"; + } + + function getLastVisits($fetch = false) + { + $idSite = Piwik_Common::getRequestVar('idSite', null, 'int'); + $limit = 10; + $api = new Piwik_API_Request("method=Live.getLastVisits&idSite=$idSite&limit=$limit&format=php&serialize=0&disable_generic_filters=1"); + + $view = new Piwik_View('Live/templates/lastVisits.tpl'); + $view->visitors = $api->process(); + $rendered = $view->render($fetch); + + if($fetch) + { + return $rendered; + } + echo $rendered; + } + + function index() + { + $view = new Piwik_View('Live/templates/index.tpl'); + $this->setGeneralVariablesView($view); + $view->visitors = $this->getLastVisits($fetch = true); + echo $view->render(); + } +} diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php new file mode 100644 index 0000000000..d6432bf447 --- /dev/null +++ b/plugins/Live/Live.php @@ -0,0 +1,14 @@ +<?php +class Piwik_Live extends Piwik_Plugin +{ + public function getInformation() + { + return array( + 'name' => 'Live Visitors', + 'description' => 'Live Visitors!', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/', + 'version' => '0.1', + ); + } +} diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php new file mode 100644 index 0000000000..0145641a69 --- /dev/null +++ b/plugins/Live/Visitor.php @@ -0,0 +1,262 @@ +<?php +//TODO add api to get actions name/count/first/last/etc +require_once "Referers/functions.php"; +require_once "UserCountry/functions.php"; +require_once "UserSettings/functions.php"; +require_once "Provider/functions.php"; + +class Piwik_Live_Visitor +{ + function __construct($visitorRawData) + { + $this->details = $visitorRawData; + } + + function getAllVisitorDetails() + { + return array( + 'ip' => $this->getIp(), + 'idVisit' => $this->getIdVisit(), + 'countActions' => $this->getNumberOfActions(), + 'isVisitorReturning' => $this->isVisitorReturning(), + 'country' => $this->getCountryName(), + 'countryFlag' => $this->getCountryFlag(), + 'continent' => $this->getContinent(), + 'provider' => $this->getProvider(), + 'providerUrl' => $this->getProviderUrl(), + 'idSite' => $this->getIdSite(), + 'serverDate' => $this->getServerDate(), + 'visitLength' => $this->getVisitLength(), + 'visitLengthPretty' => $this->getVisitLengthPretty(), + 'firstActionTimestamp' => $this->getTimestampFirstAction(), + 'lastActionTimestamp' => $this->getTimestampLastAction(), + + 'refererType' => $this->getRefererType(), + 'refererName' => $this->getRefererTypeName(), + 'keywords' => $this->getKeywords(), + 'refererUrl' => $this->getRefererUrl(), + 'refererName' => $this->getRefererName(), + 'searchEngineUrl' => $this->getSearchEngineUrl(), + 'searchEngineIcon' => $this->getSearchEngineIcon(), + + 'operatingSystem' => $this->getOperatingSystem(), + 'operatingSystemShortName' => $this->getOperatingSystemShortName(), + 'operatingSystemIcon' => $this->getOperatingSystemIcon(), + 'browserFamily' => $this->getBrowserFamily(), + 'browserFamilyDescription' => $this->getBrowserFamilyDescription(), + 'browser' => $this->getBrowser(), + 'browserIcon' => $this->getBrowserIcon(), + 'screen' => $this->getScreenType(), + 'resolution' => $this->getResolution(), + 'screenIcon' => $this->getScreenTypeIcon(), + 'plugins' => $this->getPlugins(), + ); + } + + function getServerDate() + { + return $this->details['visit_server_date']; + } + + function getIp() + { + if(isset($this->details['location_ip'])) + { + return long2ip($this->details['location_ip']); + } + return false; + } + + function getIdVisit() + { + return $this->details['idvisit']; + } + + function getIdSite() + { + return $this->details['idsite']; + } + + function getNumberOfActions() + { + return $this->details['visit_total_actions']; + } + + function getVisitLength() + { + return $this->details['visit_total_time']; + } + + function getVisitLengthPretty() + { + return Piwik::getPrettyTimeFromSeconds($this->details['visit_total_time']); + } + + function isVisitorReturning() + { + return $this->details['visitor_returning']; + } + + function getTimestampFirstAction() + { + return strtotime($this->details['visit_first_action_time']); + } + + function getTimestampLastAction() + { + return strtotime($this->details['visit_last_action_time']); + } + + function getCountryName() + { + return Piwik_CountryTranslate($this->details['location_country']); + } + + function getCountryFlag() + { + return Piwik_getFlagFromCode($this->details['location_country']); + } + + function getContinent() + { + return Piwik_ContinentTranslate($this->details['location_continent']); + } + + function getRefererType() + { + $map = array( + Piwik_Common::REFERER_TYPE_SEARCH_ENGINE => 'searchEngine', + Piwik_Common::REFERER_TYPE_WEBSITE => 'website', + Piwik_Common::REFERER_TYPE_DIRECT_ENTRY => 'directEntry', + Piwik_Common::REFERER_TYPE_CAMPAIGN => 'campaign', + ); + if(isset($map[$this->details['referer_type']])) + { + return $map[$this->details['referer_type']]; + } + return $map[Piwik_Common::REFERER_TYPE_DIRECT_ENTRY]; + } + + function getRefererTypeName() + { + return Piwik_getRefererTypeLabel($this->details['referer_type']); + } + + function getKeywords() + { + return $this->details['referer_keyword']; + } + + function getRefererUrl() + { + return $this->details['referer_url']; + } + + function getRefererName() + { + return $this->details['referer_name']; + } + + function getSearchEngineUrl() + { + if($this->getRefererType() == 'searchEngine' + && !empty($this->details['referer_name'])) + { + return Piwik_getSearchEngineUrlFromName($this->details['referer_name']); + } + return null; + } + + function getSearchEngineIcon() + { + $searchEngine = $this->getSearchEngineUrl(); + if( !is_null($searchEngine) ) + { + return Piwik_getSearchEngineLogoFromName($searchEngine); + } + return null; + } + + function getPlugins() + { + $plugins = array( + 'config_pdf', + 'config_flash', + 'config_java', + 'config_director', + 'config_quicktime', + 'config_realplayer', + 'config_windowsmedia' + ); + $return = array(); + foreach($plugins as $plugin) + { + if($this->details[$plugin] == 1) + { + $pluginShortName = substr($plugin, 7); + $return[] = $pluginShortName; + } + } + return implode(", ", $return); + } + + function getOperatingSystem() + { + return Piwik_getOSLabel($this->details['config_os']); + } + + function getOperatingSystemShortName() + { + return Piwik_getOSShortLabel($this->details['config_os']); + } + + function getOperatingSystemIcon() + { + return Piwik_getOSLogo($this->details['config_os']); + } + + function getBrowserFamilyDescription() + { + return Piwik_getBrowserTypeLabel($this->getBrowserFamily()); + } + + function getBrowserFamily() + { + return Piwik_getBrowserFamily($this->details['config_browser_name']); + } + + function getBrowser() + { + return Piwik_getBrowserLabel($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']); + } + + function getBrowserIcon() + { + return Piwik_getBrowsersLogo($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']); + } + + function getScreenType() + { + return Piwik_getScreenTypeFromResolution($this->details['config_resolution']); + } + + function getResolution() + { + return $this->details['config_resolution']; + } + + function getScreenTypeIcon() + { + return Piwik_getScreensLogo($this->getScreenType()); + } + + function getProvider() + { + return Piwik_getHostnameName($this->details['location_provider']); + } + + function getProviderUrl() + { + return Piwik_getHostnameUrl($this->details['location_provider']); + } +} diff --git a/plugins/Live/templates/images/pause.gif b/plugins/Live/templates/images/pause.gif Binary files differnew file mode 100644 index 0000000000..5954f9f545 --- /dev/null +++ b/plugins/Live/templates/images/pause.gif diff --git a/plugins/Live/templates/images/pause_disabled.gif b/plugins/Live/templates/images/pause_disabled.gif Binary files differnew file mode 100644 index 0000000000..98b79f7402 --- /dev/null +++ b/plugins/Live/templates/images/pause_disabled.gif diff --git a/plugins/Live/templates/images/play.gif b/plugins/Live/templates/images/play.gif Binary files differnew file mode 100644 index 0000000000..87eded433b --- /dev/null +++ b/plugins/Live/templates/images/play.gif diff --git a/plugins/Live/templates/images/play_disabled.gif b/plugins/Live/templates/images/play_disabled.gif Binary files differnew file mode 100644 index 0000000000..ef69513d69 --- /dev/null +++ b/plugins/Live/templates/images/play_disabled.gif diff --git a/plugins/Live/templates/images/returningVisitor.gif b/plugins/Live/templates/images/returningVisitor.gif Binary files differnew file mode 100644 index 0000000000..bc71867e55 --- /dev/null +++ b/plugins/Live/templates/images/returningVisitor.gif diff --git a/plugins/Live/templates/index.tpl b/plugins/Live/templates/index.tpl new file mode 100644 index 0000000000..a30eef0739 --- /dev/null +++ b/plugins/Live/templates/index.tpl @@ -0,0 +1,83 @@ +{assign var=showSitesSelection value=true} +{assign var=showPeriodSelection value=false} +{include file="CoreAdminHome/templates/header.tpl"} + +<h1> Plugin Development version </h1> + +{literal} +<script type="text/javascript" src="plugins/Live/templates/scripts/spy.js"></script> + +<script type="text/javascript" charset="utf-8"> + $(document).ready(function() { + $('#visits').spy({ limit: 10, 'fadeInSpeed': '1400', ajax: 'index.php?module=Live&idSite=1&action=getLastVisits', timeout: 5000, customParameterName: 'minIdVisit', customParameterValueCallback: lastIdVisit, fadeInSpeed: 1400 }); + }); + + function lastIdVisit() + { + return $('#visits > div:lt(2) .idvisit').html(); + } + var pauseImage = "plugins/Live/templates/images/pause.gif"; + var pauseDisabledImage = "plugins/Live/templates/images/pause_disabled.gif"; + var playImage = "plugins/Live/templates/images/play.gif"; + var playDisabledImage = "plugins/Live/templates/images/play_disabled.gif"; + + function onClickPause() + { + $('#pauseImage').attr('src', pauseImage); + $('#playImage').attr('src', playDisabledImage); + return pauseSpy(); + } + function onClickPlay() + { + $('#playImage').attr('src', playImage); + $('#pauseImage').attr('src', pauseDisabledImage); + return playSpy(); + } + +</script> + +<style> +#visits { + text-align:left; +} +#visits .datetime, #visits .country, #visits .referer, #visits .settings, #visits .returning { + float:left; + margin-right:10px; + overflow:hidden; + padding-left:1px; + max-width:700px; +} +#visits .datetime { + width:110px; +} +#visits .country { + width:30px; +} +#visits .referer { + width:200px; +} +#visits .settings { + width:100px; +} +#visits .returning { + width:30px; +} +#visits .visit { + border-bottom:1px solid #C1DAD7; + background-color:#F9FAFA; + padding:10px; + line-height:24px; + height:40px; +} +#visits .alt { + background-color:#FFFFFF; +} +</style> +{/literal} + +{$visitors} + +<div> + <a href="#?" onclick="onClickPause();"><img id="pauseImage" border="0" src="plugins/Live/templates/images/pause_disabled.gif"></a> + <a href="#?" onclick="onClickPlay();"><img id="playImage" border="0" src="plugins/Live/templates/images/play.gif"></a> +</div> diff --git a/plugins/Live/templates/lastVisits.tpl b/plugins/Live/templates/lastVisits.tpl new file mode 100644 index 0000000000..ac515f2d63 --- /dev/null +++ b/plugins/Live/templates/lastVisits.tpl @@ -0,0 +1,15 @@ +<div id="visits"> +{foreach from=$visitors item=visitor} + <div class="visit{if $visitor.idVisit % 2} alt{/if}"> + <div style="display:none" class="idvisit">{$visitor.idVisit}</div> + <div class="datetime">{$visitor.serverDatePretty}<br/>{$visitor.serverTimePretty}</div> + <div class="country"><img src="{$visitor.countryFlag}" title="{$visitor.country}, Provider {$visitor.provider}"></div> + <div class="referer">{if $visitor.refererType != 'directEntry'}from <a href="{$visitor.refererUrl}">{$visitor.refererName}</a> {if !empty($visitor.keywords)}"{$visitor.keywords}"{/if}{/if}</div> + <div class="settings"> + <img src="{$visitor.browserIcon}" title="{$visitor.browser} with plugins {$visitor.plugins} enabled"> + <img src="{$visitor.operatingSystemIcon}" title="{$visitor.operatingSystem}, {$visitor.resolution}"> + </div> + <div class="returning">{if $visitor.isVisitorReturning}<img src="plugins/Live/templates/images/returningVisitor.gif" title="Returning Visitor">{/if}</div> + </div> +{/foreach} +</div> diff --git a/plugins/Live/templates/scripts/spy.js b/plugins/Live/templates/scripts/spy.js new file mode 100644 index 0000000000..ac296ab02f --- /dev/null +++ b/plugins/Live/templates/scripts/spy.js @@ -0,0 +1,139 @@ +/* + jQuery Plugin spy (leftlogic.com/info/articles/jquery_spy2) + (c) 2006 Remy Sharp (leftlogic.com) + $Id$ +*/ +var spyRunning = 1; + +$.fn.spy = function(settings) { + var spy = this; + spy.epoch = new Date(1970, 0, 1); + spy.last = ''; + spy.parsing = 0; + spy.waitTimer = 0; + spy.json = null; + + if (!settings.ajax) { + alert("An AJAX/AJAH URL must be set for the spy to work."); + return; + } + + spy.attachHolder = function() { + // not mad on this, but the only way to parse HTML collections + if (o.method == 'html') + $('body').append('<div style="display: none!important;" id="_spyTmp"></div>'); + } + + // returns true for 'no dupe', and false for 'dupe found' + // latest = is latest ajax return value (raw) + // last = is previous ajax return value (raw) + // note that comparing latest and last if they're JSON objects + // always returns false, so you need to implement it manually. + spy.isDupe = function(latest, last) { + if ((last.constructor == Object) && (o.method == 'html')) + return (latest.html() == last.html()); + else if (last.constructor == String) + return (latest == last); + else + return 0; + } + + spy.parse = function(e, r) { + spy.parsing = 1; // flag to stop pull via ajax + if (o.method == 'html') { + $('div#_spyTmp').html(r); // add contents to hidden div + } else if (o.method == 'json') { + eval('spy.json = ' + r); // convert text to json + } + + if ((o.method == 'json' && spy.json.constructor == Array) || o.method == 'html') { + if (spy.parseItem(e)) { + spy.waitTimer = window.setInterval(function() { + if (spyRunning) { + if (!spy.parseItem(e)) { + spy.parsing = 0; + clearInterval(spy.waitTimer); + } + } + }, o.pushTimeout); + } else { + spy.parsing = 0; + } + } else if (o.method == 'json') { // we just have 1 + eval('spy.json = ' + r) + spy.addItem(e, spy.json); + spy.parsing = 0; + } + } + + // returns true if there's more to parse + spy.parseItem = function(e) { + if (o.method == 'html') { + // note: pre jq-1.0 doesn't return the object + var i = $('div#_spyTmp').find('div:first').remove(); + if (i.size() > 0) { + i.hide(); + spy.addItem(e, i); + } + return ($('div#_spyTmp').find('div').size() != 0); + } else { + if (spy.json.length) { + var i = spy.json.shift(); + spy.addItem(e, i); + } + + return (spy.json.length != 0); + } + } + + spy.addItem = function(e, i) { + if (! o.isDupe.call(this, i, spy.last)) { + spy.last = i; // note i is a pointer - so when it gets modified, so does spy.last + $('#' + e.id + ' > div:gt(' + (o.limit - 1) + ')').remove(); + o.push.call(e, i); + $('#' + e.id + ' > div:first').fadeIn(o.fadeInSpeed); + } + } + + spy.push = function(r) { + $('#' + this.id).prepend(r); + } + + var o = { + limit: (settings.limit || 10), + ajax: settings.ajax, + timeout: (settings.timeout || 3000), + pushTimeout: (settings.pushTimeout || settings.timeout || 3000), + method: (settings.method || 'html').toLowerCase(), + push: (settings.push || spy.push), + fadeInSpeed: (settings.fadeInSpeed || 'slow'), // 1400 = crawl + customParameterName: settings.customParameterName, + customParameterValueCallback: settings.customParameterValueCallback, + isDupe: (settings.isDupe || spy.isDupe), + }; + + spy.attachHolder(); + + return this.each(function() { + var e = this; + var lr = ''; // last ajax return + var parameters = new Object; + spy.ajaxTimer = window.setInterval(function() { + if (spyRunning && (!spy.parsing)) { + var customParameterValue = o.customParameterValueCallback.call(); + parameters[o.customParameterName] = customParameterValue; + $.get(o.ajax, parameters, function(r) { + spy.parse(e, r); + }); + } + }, o.timeout); + }); +}; + +function pauseSpy() { + spyRunning = 0; return false; +} + +function playSpy() { + spyRunning = 1; return false; +} |