Skip to content
Extraits de code Groupes Projets
Valider c062be74 rédigé par mattpiwik's avatar mattpiwik
Parcourir les fichiers

Refs #56

 * Calendar for a given website will show dates relative to this websites's timezone
 * API results now display relative to website's timezone
 * MultiSites will convert "today" and "yesterday" to Piwik default timezone
 * MultiSites calendar min and max date are the min and max date based on website's timezones. For example, the max date might be tomorrow in UTC if some websites are set to UTC+12

git-svn-id: http://dev.piwik.org/svn/trunk@2067 59fd770c-687e-43c8-a1e3-f5a4ff64c105
parent f058d9e2
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -243,22 +243,6 @@ class Piwik_Access
);
}
/**
* Returns the min date out of all websites
* for which the current user has at least view access
*
* @return int timestamp
*/
public function getSitesMinDate()
{
if($this->isSuperUser())
{
return Piwik_FetchOne('SELECT MIN(ts_created) FROM '.Piwik::prefixTable('site'));
}
return Piwik_FetchOne(self::getSqlAccessSite("MIN(ts_created)"), $this->login);
}
/**
* Returns an array of ID sites for which the user has an ADMIN access.
*
......
......@@ -25,7 +25,7 @@ class Piwik_Archive_Array_IndexedByDate extends Piwik_Archive_Array
*/
function __construct(Piwik_Site $oSite, $strPeriod, $strDate)
{
$rangePeriod = new Piwik_Period_Range($strPeriod, $strDate);
$rangePeriod = new Piwik_Period_Range($strPeriod, $strDate, $oSite->getTimezone());
foreach($rangePeriod->getSubperiods() as $subPeriod)
{
$startDate = $subPeriod->getDateStart();
......
......@@ -161,11 +161,11 @@ class Piwik_Archive_Single extends Piwik_Archive
// we add one day to make sure we don't miss the day of the website creation
if( $this->period->getDateEnd()->addDay(2)->isEarlier( $this->site->getCreationDate() ) )
{
return;
return;
}
// if the starting date is in the future we know there is no visit
if( $this->period->getDateStart()->subDay(1)->isLater( Piwik_Date::today() ) )
if( $this->period->getDateStart()->subDay(2)->isLater( Piwik_Date::today() ) )
{
return;
}
......
......@@ -610,8 +610,8 @@ abstract class Piwik_ArchiveProcessing
protected function isArchived()
{
$bindSQL = array( $this->idsite,
$this->period->getDateStart(),
$this->period->getDateEnd(),
$this->period->getDateStart()->toString('Y-m-d'),
$this->period->getDateEnd()->toString('Y-m-d'),
$this->periodId,
);
......
......@@ -37,6 +37,8 @@ abstract class Piwik_Controller
* @var Piwik_Date|null
*/
protected $date;
protected $idSite;
protected $site = null;
/**
* Builds the controller object, reads the date from the request, extracts plugin name from
......@@ -45,17 +47,60 @@ abstract class Piwik_Controller
{
$aPluginName = explode('_', get_class($this));
$this->pluginName = $aPluginName[1];
$this->strDate = Piwik_Common::getRequestVar('date', 'yesterday', 'string');
try{
// the date looks like YYYY-MM-DD we can build it
$this->date = Piwik_Date::factory($this->strDate);
$this->strDate = $this->date->toString();
$date = Piwik_Common::getRequestVar('date', 'yesterday', 'string');
try {
$this->idSite = Piwik_Common::getRequestVar('idSite', false, 'int');
$this->site = new Piwik_Site($this->idSite);
$date = $this->getDateParameterInTimezone($date, $this->site->getTimezone());
$this->setDate($date);
} catch(Exception $e){
// the date looks like YYYY-MM-DD,YYYY-MM-DD or other format
$this->date = null;
}
}
/**
* Helper method to convert "today" or "yesterday" to the default timezone specified.
* If the date is absolute, ie. YYYY-MM-DD, it will not be converted to the timezone
* @param $date today, yesterday, YYYY-MM-DD
* @param $defaultTimezone
* @return Piwik_Date
*/
protected function getDateParameterInTimezone($date, $defaultTimezone )
{
$timezone = null;
// if the requested date is not YYYY-MM-DD, we need to ensure
// it is relative to the website's timezone
if(in_array($date, array('today', 'yesterday')))
{
// today is at midnight; we really want to get the time now, so that
// * if the website is UTC+12 and it is 5PM now in UTC, the calendar will allow to select the UTC "tomorrow"
// * if the website is UTC-12 and it is 5AM now in UTC, the calendar will allow to select the UTC "yesterday"
if($date == 'today')
{
$date = 'now';
}
elseif($date == 'yesterday')
{
$date = 'yesterdaySameTime';
}
$timezone = $defaultTimezone;
}
return Piwik_Date::factory($date, $timezone);
}
/**
* Sets the date to be used by all other methods in the controller.
* If the date has to be modified, it should be called just after the controller construct
* @param $date
* @return void
*/
protected function setDate(Piwik_Date $date)
{
$this->date = $date;
$this->strDate = $this->date->toString();
}
/**
* Returns the name of the default method that will be called
* when visiting: index.php?module=PluginName without the action parameter
......@@ -181,7 +226,7 @@ abstract class Piwik_Controller
{
$period = $paramsToSet['period'];
}
$last30Relative = new Piwik_Period_Range($period, $range );
$last30Relative = new Piwik_Period_Range($period, $range, $this->site->getTimezone() );
$last30Relative->setDefaultEndDate(Piwik_Date::factory($endDate));
......@@ -227,6 +272,12 @@ abstract class Piwik_Controller
return $url;
}
/**
* Sets the first date available in the calendar
* @param $minDate
* @param $view
* @return void
*/
protected function setMinDateView(Piwik_Date $minDate, $view)
{
$view->minDateYear = $minDate->toString('Y');
......@@ -234,6 +285,24 @@ abstract class Piwik_Controller
$view->minDateDay = $minDate->toString('d');
}
/**
* Sets "today" in the calendar. Today does not always mean "UTC" today, eg. for websites in UTC+12.
* @param $maxDate
* @param $view
* @return void
*/
protected function setMaxDateView(Piwik_Date $maxDate, $view)
{
$view->maxDateYear = $maxDate->toString('Y');
$view->maxDateMonth = $maxDate->toString('m');
$view->maxDateDay = $maxDate->toString('d');
}
/**
* Sets general variables to the view that are used by various templates and Javascript
* @param $view
* @return void
*/
protected function setGeneralVariablesView($view)
{
$view->date = $this->strDate;
......@@ -242,19 +311,20 @@ abstract class Piwik_Controller
$this->setPeriodVariablesView($view);
$period = Piwik_Period::factory(Piwik_Common::getRequestVar('period'), Piwik_Date::factory($this->strDate));
$view->prettyDate = $period->getLocalizedLongString();
$idSite = Piwik_Common::getRequestVar('idSite');
$view->idSite = $idSite;
$site = new Piwik_Site($idSite);
$view->siteName = $site->getName();
$view->siteMainUrl = $site->getMainUrl();
$view->idSite = $this->idSite;
if(is_null($this->site))
{
throw new Exception("invalid website");
}
$view->siteName = $this->site->getName();
$view->siteMainUrl = $this->site->getMainUrl();
$minDate = $site->getCreationDate();
$datetimeMinDate = $this->site->getCreationDate()->getDatetime();
$minDate = Piwik_Date::factory($datetimeMinDate, $this->site->getTimezone());
$this->setMinDateView($minDate, $view);
$maxDate = Piwik_Date::factory('today');
$view->maxDateYear = $maxDate->toString('Y');
$view->maxDateMonth = $maxDate->toString('m');
$view->maxDateDay = $maxDate->toString('d');
$maxDate = Piwik_Date::factory('now', $this->site->getTimezone());
$this->setMaxDateView($maxDate, $view);
$view->currentAdminMenuName = Piwik_GetCurrentAdminMenuName();
$view->debugTrackVisitsInsidePiwikUI = Zend_Registry::get('config')->Debug->track_visits_inside_piwik_ui;
......@@ -265,6 +335,11 @@ abstract class Piwik_Controller
}
}
/**
* Sets general period variables (available periods, current period, period labels) used by templates
* @param $view
* @return void
*/
public static function setPeriodVariablesView($view)
{
if(isset($view->period))
......@@ -294,6 +369,17 @@ abstract class Piwik_Controller
$view->periodsNames = $periodNames;
}
/**
* Helper method used to redirect the current http request to another module/action
* If specified, will also redirect to a given website, period and /or date
*
* @param $moduleToRedirect eg. "MultiSites"
* @param $actionToRedirect eg. "index"
* @param $websiteId eg. 1
* @param $defaultPeriod eg. "day"
* @param $defaultDate eg. "today"
* @return issues a http header redirect and exits
*/
function redirectToIndex($moduleToRedirect, $actionToRedirect, $websiteId = null, $defaultPeriod = null, $defaultDate = null)
{
if(is_null($websiteId))
......@@ -339,7 +425,7 @@ abstract class Piwik_Controller
/**
* Returns default website for Piwik to load
* Returns default website that Piwik should load
* @return Piwik_Site
*/
protected function getDefaultWebsiteId()
......
......@@ -37,24 +37,57 @@ class Piwik_Date
* Returns a Piwik_Date objects.
*
* @param string $strDate 'today' 'yesterday' or any YYYY-MM-DD or timestamp
* @param string $timezone if specified, the dateString will be relative to this $timezone.
* For example, today in UTC+12 will be a timestamp in the future for UTC.
* This is different from using ->setTimezone()
* @return Piwik_Date
*/
static public function factory($dateString)
static public function factory($dateString, $timezone = null)
{
if($dateString == 'today')
if($dateString == 'now')
{
return self::today();
$date = self::now();
}
if($dateString == 'yesterday')
elseif($dateString == 'today')
{
return self::yesterday();
$date = self::today();
}
if (!is_int($dateString)
elseif($dateString == 'yesterday')
{
$date = self::yesterday();
}
elseif($dateString == 'yesterdaySameTime')
{
$date = self::yesterdaySameTime();
}
elseif (!is_int($dateString)
&& ($dateString = strtotime($dateString)) === false)
{
throw new Exception("Date format must be: YYYY-MM-DD, or 'today' or 'yesterday' or any keyword supported by the strtotime function (see http://php.net/strtotime for more information)");
}
return new Piwik_Date($dateString);
else
{
$date = new Piwik_Date($dateString);
}
if(is_null($timezone))
{
return $date;
}
// manually adjust for UTC timezones
$utcOffset = self::extractUtcOffset($timezone);
if($utcOffset !== false)
{
return $date->addHour($utcOffset);
}
date_default_timezone_set($timezone);
$datetime = $date->getDatetime();
date_default_timezone_set('UTC');
$date = Piwik_Date::factory(strtotime($datetime));
return $date;
}
/*
......@@ -117,6 +150,32 @@ class Piwik_Date
return new Piwik_Date($this->timestamp, $timezone);
}
/**
* Helper function that returns the offset in the timezone string 'UTC+14'
* Returns false if the timezone is not UTC+X or UTC-X
*
* @param $timezone
* @return int or false
*/
static protected function extractUtcOffset($timezone)
{
if($timezone == 'UTC')
{
return 0;
}
$start = substr($timezone, 0, 4);
if($start != 'UTC-'
&& $start != 'UTC+')
{
return false;
}
$offset = (float)substr($timezone, 4);
if($start == 'UTC-') {
$offset = -$offset;
}
return $offset;
}
/**
* Returns the unix timestamp of the date in UTC,
* converted from the date timezone
......@@ -125,19 +184,10 @@ class Piwik_Date
*/
public function getTimestamp()
{
if($this->timezone == 'UTC')
{
return (int)$this->timestamp;
$utcOffset = self::extractUtcOffset($this->timezone);
if($utcOffset !== false) {
return (int)($this->timestamp - $utcOffset * 3600);
}
$start = substr($this->timezone, 0, 4);
if($start == 'UTC-' || $start == 'UTC+')
{
$offset = (float)substr($this->timezone, 4);
if($start == 'UTC-') {
$offset = -$offset;
}
return (int)($this->timestamp - $offset * 3600);
}
// @fixme
// The following code seems clunky - I thought the DateTime php class would allow to return timestamps
// after applying the timezone offset. Instead, the underlying timestamp is not changed.
......@@ -200,7 +250,6 @@ class Piwik_Date
return $this->toString();
}
/**
* Compares the week of the current date against the given $date
* Returns 0 if equal, -1 if current week is earlier or 1 if current week is later
......@@ -223,6 +272,7 @@ class Piwik_Date
}
return 1;
}
/**
* Compares the month of the current date against the given $date month
* Returns 0 if equal, -1 if current month is earlier or 1 if current month is later
......@@ -286,6 +336,16 @@ class Piwik_Date
return new Piwik_Date(strtotime("yesterday"));
}
/**
* Returns a date object set to yesterday same time of day
*
* @return Piwik_Date
*/
static public function yesterdaySameTime()
{
return new Piwik_Date(strtotime("yesterday ".date('H:i:s')));
}
/**
* Sets the time part of the date
* Doesn't modify $this
......@@ -342,8 +402,6 @@ class Piwik_Date
return new Piwik_Date( $result, $this->timezone );
}
/**
* Subtracts days from the existing date object and returns a new Piwik_Date object
* Returned is the new date object
......@@ -385,7 +443,6 @@ class Piwik_Date
);
return new Piwik_Date( $result, $this->timezone );
}
/**
* Returns a localized date string, given a template.
......@@ -445,6 +502,14 @@ class Piwik_Date
return new Piwik_Date( $ts, $this->timezone );
}
/**
* Substract hour to the existing date object.
* Returned is the new date object
* Doesn't modify $this
*
* @param int Number of hours to substract
* @return Piwik_Date new date
*/
public function subHour( $n )
{
return $this->addHour(-$n);
......
......@@ -18,11 +18,12 @@
*/
class Piwik_Period_Range extends Piwik_Period
{
public function __construct( $strPeriod, $strDate )
public function __construct( $strPeriod, $strDate, $timezone = 'UTC' )
{
$this->strPeriod = $strPeriod;
$this->strDate = $strDate;
$this->defaultEndDate = null;
$this->timezone = $timezone;
}
public function getLocalizedShortString()
{
......@@ -125,7 +126,7 @@ class Piwik_Period_Range extends Piwik_Period
}
else
{
$defaultEndDate = Piwik_Date::today();
$defaultEndDate = Piwik_Date::factory('now', $this->timezone);
}
if($lastOrPrevious == 'last')
{
......
......@@ -62,14 +62,18 @@ class Piwik_CoreHome_Controller extends Piwik_Controller
protected function setDateTodayIfWebsiteCreatedToday()
{
$date = Piwik_Common::getRequestVar('date', false);
$date = Piwik_Date::factory($date);
if($date->isToday()) {
if($date == 'today')
{
return;
}
$websiteId = Piwik_Common::getRequestVar('idSite', false);
if ($websiteId) {
$website = new Piwik_Site($websiteId);
if( $website->getCreationDate()->isToday() ) {
$datetimeCreationDate = $this->site->getCreationDate()->getDatetime();
$creationDateLocalTimezone = Piwik_Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d');
$todayLocalTimezone = Piwik_Date::factory('now', $website->getTimezone())->toString('Y-m-d');
if( $creationDateLocalTimezone == $todayLocalTimezone )
{
Piwik::redirectToModule( 'CoreHome', 'index',
array( 'date' => 'today',
'idSite' => $websiteId,
......
......@@ -35,27 +35,30 @@ class Piwik_MultiSites_Controller extends Piwik_Controller
public function getSitesInfo()
{
$view = new Piwik_View("MultiSites/templates/index.tpl");
// overwrites the default Date set in the parent controller
// Instead of the default current website's local date,
// we set "today" or "yesterday" based on the default Piwik timezone
$piwikDefaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone();
$date = Piwik_Common::getRequestVar('date', 'today');
$date = $this->getDateParameterInTimezone($date, $piwikDefaultTimezone);
$this->setDate($date);
$mySites = Piwik_SitesManager_API::getInstance()->getSitesWithAtLeastViewAccess();
$params = $this->getGraphParamsModified();
$this->dateToStr = $params['date'];
$ids = 'all';
$this->period = Piwik_Common::getRequestVar('period', 'day');
$this->date = Piwik_Common::getRequestVar('date', 'today');
$lastDate = date('Y-m-d',strtotime("-1 ".$this->period, strtotime($this->date)));
$lastDate = date('Y-m-d',strtotime("-1 ".$this->period, strtotime($this->strDate)));
$visits = Piwik_VisitsSummary_API::getInstance()->getVisits($ids, $this->period, $this->date);
$visits = Piwik_VisitsSummary_API::getInstance()->getVisits($ids, $this->period, $this->strDate);
$lastVisits = Piwik_VisitsSummary_API::getInstance()->getVisits($ids, $this->period, $lastDate);
$actions = Piwik_VisitsSummary_API::getInstance()->getActions($ids, $this->period, $this->date);
$actions = Piwik_VisitsSummary_API::getInstance()->getActions($ids, $this->period, $this->strDate);
$lastActions = Piwik_VisitsSummary_API::getInstance()->getActions($ids, $this->period, $lastDate);
$uniqueUsers = Piwik_VisitsSummary_API::getInstance()->getUniqueVisitors($ids, $this->period, $this->date);
$uniqueUsers = Piwik_VisitsSummary_API::getInstance()->getUniqueVisitors($ids, $this->period, $this->strDate);
$lastUniqueUsers = Piwik_VisitsSummary_API::getInstance()->getUniqueVisitors($ids, $this->period, $lastDate);
$visitsSummary = $this->getSummary($lastVisits, $visits, $mySites, "visits");
......@@ -80,12 +83,14 @@ class Piwik_MultiSites_Controller extends Piwik_Controller
$site['visitsSummaryValue'] = $visitsSummary[$idSite];
$site['actionsSummaryValue'] = $actionsSummary[$idSite];
$site['uniqueSummaryValue'] = $uniqueSummary[$idSite];
}
$view = new Piwik_View("MultiSites/templates/index.tpl");
$view->mySites = $mySites;
$view->evolutionBy = $this->evolutionBy;
$view->period = $this->period;
$view->date = $this->date;
$view->date = $this->strDate;
$view->page = $this->page;
$view->limit = $this->limit;
$view->orderBy = $this->orderBy;
......@@ -93,25 +98,56 @@ class Piwik_MultiSites_Controller extends Piwik_Controller
$view->dateToStr = $this->dateToStr;
$view->autoRefreshTodayReport = false;
// if the current date is today (or yesterday, in case the website is set to UTC-12), we refresh the page every 5min
if(in_array($this->date, array('today', date('Y-m-d'), 'yesterday', Piwik_Date::factory('yesterday')->toString('Y-m-d'))))
// if the current date is today, or yesterday,
// in case the website is set to UTC-12), or today in UTC+14, we refresh the page every 5min
if(in_array($this->strDate, array( 'today', date('Y-m-d'),
'yesterday', Piwik_Date::factory('yesterday')->toString('Y-m-d'),
Piwik_Date::factory('now', 'UTC+14')->toString('Y-m-d'))))
{
$view->autoRefreshTodayReport = true;
}
$this->setGeneralVariablesView($view);
$this->setMinMaxDateAcrossWebsites($mySites, $view);
$minTimestamp = Zend_Registry::get('access')->getSitesMinDate();
if(!empty($minTimestamp))
{
$minDate = Piwik_Date::factory($minTimestamp);
$this->setMinDateView($minDate, $view);
}
echo $view->render();
}
/**
* The Multisites reports displays the first calendar date as the earliest day available for all websites.
* Also, today is the later "today" available across all timezones.
* @param $mySites
* @param $view
* @return void
*/
private function setMinMaxDateAcrossWebsites($mySites, $view)
{
$minDate = null;
$maxDate = Piwik_Date::now();
foreach($mySites as &$site)
{
// look for 'now' in the website's timezone
$timezone = $site['timezone'];
$date = Piwik_Date::factory('now', $timezone);
if($date->isLater($maxDate))
{
$maxDate = clone $date;
}
// look for the absolute minimum date
$creationDate = $site['ts_created'];
$date = Piwik_Date::factory($creationDate, $timezone);
if(is_null($minDate)
|| $date->isEarlier($minDate))
{
$minDate = clone $date;
}
}
$this->setMinDateView($minDate, $view);
$this->setMaxDateView($maxDate, $view);
}
private function getSummary($lastVisits, $currentVisits, $mySites, $type)
{
$currentVisitsArray = $currentVisits->getArray();
$lastVisitsArray = $lastVisits->getArray();
$summaryArray = array();
......
<img src="plugins/MultiSites/images/arrow_desc.gif" style="display: none" />
<img src="plugins/MultiSites/images/arrow_asc.gif" style="display: none" />
{assign var=showSitesSelection value=false}
{assign var=showPeriodSelection value=true}
{include file="CoreHome/templates/header.tpl"}
......@@ -13,7 +12,6 @@
<div id="multisites" style="margin: auto">
<div id="main">
{include file="MultiSites/templates/row.tpl" assign="row"}
<script type="text/javascript">
var allSites = new Array();
var params = new Array();
......
......@@ -128,8 +128,6 @@ vertical-align:middle;
<a name='globalSettings'></a>
<h2>{'SitesManager_GlobalWebsitesSettings'|translate}</h2>
<br/>
{ajaxErrorDiv id=ajaxErrorGlobalSettings}
{ajaxLoadingDiv id=ajaxLoadingGlobalSettings}
<table style='width:600px' class="adminTable adminTableNoBorder" >
<tr><td colspan="2">
......@@ -175,6 +173,8 @@ vertical-align:middle;
</td></tr>
</table>
<span style='margin-left:20px'><input type="submit" class="submit" id='globalSettingsSubmit' value="{'General_Save'|translate}" /></span>
{ajaxErrorDiv id=ajaxErrorGlobalSettings}
{ajaxLoadingDiv id=ajaxLoadingGlobalSettings}
{/if}
<br /><br /><br /><br />
......
......@@ -43,6 +43,24 @@ class Test_Piwik_Date extends UnitTestCase
$this->assertEqual( strtotime(date("Y-m-d",strtotime('-1day')). " 00:00:00"), $date->getTimestamp());
}
function test_factoryTimezone()
{
// now in UTC converted to UTC+10 means adding 10 hours
$date = Piwik_Date::factory('now', 'UTC+10');
$dateExpected = Piwik_Date::now()->addHour(10);
$this->assertEqual($date->getDatetime(), $dateExpected->getDatetime());
// Congo is in UTC+1 all year long (no DST)
$date = Piwik_Date::factory('now', 'Africa/Brazzaville');
$dateExpected = Piwik_Date::factory('now')->addHour(1);
$this->assertEqual($date->getDatetime(), $dateExpected->getDatetime());
// yesterday same time in Congo is the same as today in Congo - 24 hours
$date = Piwik_Date::factory('yesterdaySameTime', 'Africa/Brazzaville');
$dateExpected = Piwik_Date::factory('now', 'Africa/Brazzaville')->subHour(24);
$this->assertEqual($date->getDatetime(), $dateExpected->getDatetime());
}
function test_setTimezone_dayInUTC()
{
$date = Piwik_Date::factory('2010-01-01');
......
......@@ -561,7 +561,6 @@ class Test_Piwik_Period extends UnitTestCase
// test range 1
function test_range_today()
{
$range = new Piwik_Period_Range( 'day', 'last1' );
$today = Piwik_Date::today();
......@@ -574,6 +573,21 @@ class Test_Piwik_Period extends UnitTestCase
$this->assertEqual( $range->toString(), $correct);
}
function test_range_today_UtcPlus12()
{
// rather ugly test, UTC+23 doesn't exist, but it's a way to test that last1 in UTC+23 will be "our" UTC tomorrow
$range = new Piwik_Period_Range( 'day', 'last1', 'UTC+23' );
$today = Piwik_Date::now()->addHour(23);
$correct=array(
$today->toString(),
);
$correct = array_reverse($correct);
$this->assertEqual( $range->getNumberOfSubperiods(), 1);
$this->assertEqual( $range->toString(), $correct);
}
// test range 2
function test_range_2days()
{
......
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