diff --git a/core/Archive/Array/IndexedByDate.php b/core/Archive/Array/IndexedByDate.php index a21f2d53e5d3c44f6f02d1c77f0e9358e082f552..320e54a99ddb076912bb194fccd1f215c89a3f12 100644 --- a/core/Archive/Array/IndexedByDate.php +++ b/core/Archive/Array/IndexedByDate.php @@ -42,7 +42,7 @@ class Piwik_Archive_Array_IndexedByDate extends Piwik_Archive_Array return 'date'; } - protected function loadMetadata(Piwik_DataTable_Array $table, $archive) + protected function loadMetadata(Piwik_DataTable_Array $table, Piwik_Archive $archive) { $table->metadata[$archive->getPrettyDate()] = array( 'timestamp' => $archive->getTimestampStartDate(), @@ -98,14 +98,15 @@ class Piwik_Archive_Array_IndexedByDate extends Piwik_Archive_Array continue; } - $sql = "SELECT value, name, UNIX_TIMESTAMP(date1) as timestamp + $sql = "SELECT value, name, date1 as startDate FROM $table WHERE idarchive IN ( $inIds ) AND name IN ( $inNames )"; $values = $db->fetchAll($sql); foreach($values as $value) { - $arrayValues[$value['timestamp']][$value['name']] = (float)$value['value']; + $timestamp = Piwik_Date::factory($value['startDate'])->getTimestamp(); + $arrayValues[$timestamp][$value['name']] = (float)$value['value']; } } diff --git a/core/Archive/Single.php b/core/Archive/Single.php index 464ecf7f31be324844ea0eecf6f34ea35ef47f72..71960e7098497b12e7b9b709b0ff3c32e9a7340e 100644 --- a/core/Archive/Single.php +++ b/core/Archive/Single.php @@ -134,9 +134,12 @@ class Piwik_Archive_Single extends Piwik_Archive { if(!is_null($this->archiveProcessing)) { - return $this->archiveProcessing->getTimestampStartDate(); + $timestamp = $this->archiveProcessing->getTimestampStartDate(); + if(!empty($timestamp)) + { + return $timestamp; + } } - return $this->period->getDateStart()->getTimestamp(); } diff --git a/core/ArchiveProcessing.php b/core/ArchiveProcessing.php index b96ce078fcd656883c8c1d2a1e065100468c6d47..d236fafd83c4f55791a6f0b9bce93c464243c8b7 100644 --- a/core/ArchiveProcessing.php +++ b/core/ArchiveProcessing.php @@ -96,11 +96,11 @@ abstract class Piwik_ArchiveProcessing protected $tableArchiveBlob; /** - * Maximum timestamp above which a given archive is considered out of date + * Minimum timestamp looked at for processed archives * * @var int */ - protected $maxTimestampArchive; + protected $minDatetimeArchiveProcessedUTC = false; /** * Compress blobs @@ -134,14 +134,14 @@ abstract class Piwik_ArchiveProcessing public $site = null; /** - * Starting date @see Piwik_Date::toString() + * Starting datetime in UTC * * @var string */ - public $strDateStart; + public $startDatetimeUTC; /** - * Ending date @see Piwik_Date::toString() + * Ending date in UTC * * @var string */ @@ -183,6 +183,9 @@ abstract class Piwik_ArchiveProcessing */ public $isThereSomeVisits = false; + protected $startTimestampUTC; + protected $endTimestampUTC; + /** * Constructor */ @@ -224,63 +227,85 @@ abstract class Piwik_ArchiveProcessing } /** - * Inits the object + * Sets object attributes that will be used throughout the process */ - protected function loadArchiveProperties() - { + public function init() + { $this->idsite = $this->site->getId(); - $this->periodId = $this->period->getId(); - - $this->dateStart = $this->period->getDateStart(); - $this->dateEnd = $this->period->getDateEnd(); + + $dateStartLocalTimezone = $this->period->getDateStart(); + $dateEndLocalTimezone = $this->period->getDateEnd(); $this->tableArchiveNumeric = new Piwik_TablePartitioning_Monthly('archive_numeric'); $this->tableArchiveNumeric->setIdSite($this->idsite); - $this->tableArchiveNumeric->setTimestamp($this->dateStart->get()); + $this->tableArchiveNumeric->setTimestamp($dateStartLocalTimezone->getTimestamp()); $this->tableArchiveBlob = new Piwik_TablePartitioning_Monthly('archive_blob'); $this->tableArchiveBlob->setIdSite($this->idsite); - $this->tableArchiveBlob->setTimestamp($this->dateStart->get()); + $this->tableArchiveBlob->setTimestamp($dateStartLocalTimezone->getTimestamp()); + + $dateStartUTC = $dateStartLocalTimezone->setTimezone($this->site->getTimezone()); + $dateEndUTC = $dateEndLocalTimezone->setTimezone($this->site->getTimezone()); + $this->startDatetimeUTC = $dateStartUTC->getDateStartUTC(); + $this->endDatetimeUTC = $dateEndUTC->getDateEndUTC(); - $this->strDateStart = $this->dateStart->toString(); - $this->strDateEnd = $this->dateEnd->toString(); + $this->startTimestampUTC = $dateStartUTC->getTimestamp(); + $this->endTimestampUTC = strtotime($this->endDatetimeUTC); + $this->minDatetimeArchiveProcessedUTC = $this->getMinTimeArchivedProcessed(); + $db = Zend_Registry::get('db'); + $this->compressBlob = $db->hasBlobDataType(); + } + + public function getStartDatetimeUTC() + { + return $this->startDatetimeUTC; + } + + public function getEndDatetimeUTC() + { + return $this->endDatetimeUTC; + } + + /** + * Returns the minimum archive processed datetime to look at + * + * @return string Datetime string, or false if must look at any archive available + */ + public function getMinTimeArchivedProcessed() + { // if the current archive is a DAY and if it's today, - // we set this maxTimestampArchive that defines the lifetime value of today's archive - $this->maxTimestampArchive = 0; + // we set this minDatetimeArchiveProcessedUTC that defines the lifetime value of today's archive if( $this->period->getNumberOfSubperiods() == 0 - && $this->period->toString() == date("Y-m-d") + && $this->startTimestampUTC <= time() && $this->endTimestampUTC > time() ) { - $this->maxTimestampArchive = time() - Zend_Registry::get('config')->General->time_before_today_archive_considered_outdated; - + $minDatetimeArchiveProcessedUTC = time() - Zend_Registry::get('config')->General->time_before_today_archive_considered_outdated; $browserArchivingEnabled = Zend_Registry::get('config')->General->enable_browser_archiving_triggering; // see #1150; if new archives are not triggered from the browser, // we still want to try and return the latest archive available for today (rather than return nothing) if(!$browserArchivingEnabled) { - $this->maxTimestampArchive = 0; + return false; } } // either // - if the period we're looking for is finished, we look for a ts_archived that // is greater than the last day of the archive // - if the period we're looking for is not finished, we look for a recent enough archive - // recent enough means maxTimestampArchive = 00:00:01 this morning + // recent enough means minDatetimeArchiveProcessedUTC = 00:00:01 this morning else { - if($this->period->isFinished()) + if($this->endTimestampUTC <= time()) { - $this->maxTimestampArchive = $this->period->getDateEnd()->setTime('00:00:00')->addDay(1)->getTimestamp(); + $minDatetimeArchiveProcessedUTC = $this->endTimestampUTC+1; } else { - $this->maxTimestampArchive = Piwik_Date::today()->getTimestamp(); + $minDatetimeArchiveProcessedUTC = Piwik_Date::today()->setTimezone($this->site->getTimezone())->getTimestamp(); } } - - $db = Zend_Registry::get('db'); - $this->compressBlob = $db->hasBlobDataType(); + return $minDatetimeArchiveProcessedUTC; } /** @@ -294,7 +319,7 @@ abstract class Piwik_ArchiveProcessing */ public function loadArchive() { - $this->loadArchiveProperties(); + $this->init(); $this->idArchive = $this->isArchived(); if($this->idArchive === false @@ -351,8 +376,8 @@ abstract class Piwik_ArchiveProcessing { // delete the first done = ERROR Piwik_Query("/* SHARDING_ID_SITE = ".$this->idsite." */ - DELETE FROM ".$this->tableArchiveNumeric->getTableName()." - WHERE idarchive = ? AND name = 'done'", + DELETE FROM ".$this->tableArchiveNumeric->getTableName()." + WHERE idarchive = ? AND name = 'done'", array($this->idArchive) ); @@ -408,14 +433,8 @@ abstract class Piwik_ArchiveProcessing */ public function getTimestampStartDate() { - // case when archive processing is in the past or the future, the starting date has not been set or processed yet - if(is_null($this->timestampDateStart)) - { - return Piwik_Date::factory($this->strDateStart)->getTimestamp(); - } return $this->timestampDateStart; } - // exposing the number of visits publicly (number used to compute conversions rates) protected $nb_visits = null; @@ -446,7 +465,9 @@ abstract class Piwik_ArchiveProcessing protected function loadNextIdarchive() { $db = Zend_Registry::get('db'); - $id = $db->fetchOne("/* SHARDING_ID_SITE = ".$this->idsite." */ SELECT max(idarchive) FROM ".$this->tableArchiveNumeric->getTableName()); + $id = $db->fetchOne("/* SHARDING_ID_SITE = ".$this->idsite." */ + SELECT max(idarchive) + FROM ".$this->tableArchiveNumeric->getTableName()); if(empty($id)) { $id = 0; @@ -523,8 +544,8 @@ abstract class Piwik_ArchiveProcessing Piwik_Query($query, array( $this->idArchive, $this->idsite, - $this->strDateStart, - $this->strDateEnd, + $this->period->getDateStart(), + $this->period->getDateEnd(), $this->periodId, date("Y-m-d H:i:s"), $record->name, @@ -538,7 +559,7 @@ abstract class Piwik_ArchiveProcessing * Returns false if the archive needs to be computed. * * An archive is available if - * - for today, the archive was computed less than maxTimestampArchive seconds ago + * - for today, the archive was computed less than minDatetimeArchiveProcessedUTC seconds ago * - for any other day, if the archive was computed once this day was finished * - for other periods, if the archive was computed once the period was finished * @@ -547,14 +568,20 @@ abstract class Piwik_ArchiveProcessing protected function isArchived() { $bindSQL = array( $this->idsite, - $this->strDateStart, - $this->strDateEnd, - $this->periodId, - ); - $timeStampWhere = " AND UNIX_TIMESTAMP(ts_archived) >= ? "; - $bindSQL[] = $this->maxTimestampArchive; + $this->period->getDateStart(), + $this->period->getDateEnd(), + $this->periodId, + ); + + $timeStampWhere = ''; + + if($this->minDatetimeArchiveProcessedUTC) + { + $timeStampWhere = " AND ts_archived >= ? "; + $bindSQL[] = Piwik_Date::factory($this->minDatetimeArchiveProcessedUTC)->getDatetime(); + } - $sqlQuery = " SELECT idarchive, value, name, UNIX_TIMESTAMP(date1) as timestamp + $sqlQuery = " SELECT idarchive, value, name, date1 as startDate FROM ".$this->tableArchiveNumeric->getTableName()." WHERE idsite = ? AND date1 = ? @@ -577,7 +604,7 @@ abstract class Piwik_ArchiveProcessing if($result['name'] == 'done') { $idarchive = $result['idarchive']; - $this->timestampDateStart = $result['timestamp']; + $this->timestampDateStart = Piwik_Date::factory($result['startDate'])->getTimestamp(); break; } } diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php index df823b978b584175fbc1dbc6410ce278a6c4fab2..709eb46a291c46bb256e5d05bfae319919a6b092 100644 --- a/core/ArchiveProcessing/Day.php +++ b/core/ArchiveProcessing/Day.php @@ -44,12 +44,12 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$this->logTable." - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? - GROUP BY visit_server_date ORDER BY NULL"; - $row = $this->db->fetchRow($query, array($this->strDateStart,$this->idsite ) ); - if($row === false || $row === null) + $row = $this->db->fetchRow($query, array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite ) ); + if($row === false || $row === null || $row['nb_visits'] == 0) { return; } @@ -87,9 +87,10 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing { $query = "SELECT $select FROM ".$this->logTable." - WHERE visit_server_date = ? - AND idsite = ?"; - $data = $this->db->fetchRow($query, array( $this->strDateStart, $this->idsite )); + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? + AND idsite = ?"; + $data = $this->db->fetchRow($query, array( $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite )); foreach($data as $label => &$count) { @@ -152,11 +153,12 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$this->logTable." - WHERE visit_server_date = ? - AND idsite = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? + AND idsite = ? GROUP BY label ORDER BY NULL"; - $query = $this->db->query($query, array( $this->strDateStart, $this->idsite ) ); + $query = $this->db->query($query, array( $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite ) ); $interest = array(); while($row = $query->fetch()) @@ -327,11 +329,12 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(revenue) as revenue $segments FROM ".$this->logConversionTable." - WHERE visit_server_date = ? - AND idsite = ? + WHERE server_time >= ? + AND server_time <= ? + AND idsite = ? GROUP BY idgoal $segments ORDER BY NULL"; - $query = $this->db->query($query, array( $this->strDateStart, $this->idsite )); + $query = $this->db->query($query, array( $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite )); return $query; } @@ -342,11 +345,12 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(revenue) as revenue, $segment as label FROM ".$this->logConversionTable." - WHERE visit_server_date = ? - AND idsite = ? + WHERE server_time >= ? + AND server_time <= ? + AND idsite = ? GROUP BY idgoal, label ORDER BY NULL"; - $query = $this->db->query($query, array( $this->strDateStart, $this->idsite )); + $query = $this->db->query($query, array( $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite )); return $query; } diff --git a/core/ArchiveProcessing/Period.php b/core/ArchiveProcessing/Period.php index bec3f19aefaee3b8d668a4ec8fcac112d1360b09..c001f71189547f53f4bdb29da5de1ec2ed47f101 100644 --- a/core/ArchiveProcessing/Period.php +++ b/core/ArchiveProcessing/Period.php @@ -288,10 +288,14 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing protected function computeNbUniqVisitors() { - $query = "SELECT count(distinct visitor_idcookie) as nb_uniq_visitors FROM ".$this->logTable." - WHERE visit_server_date >= ? AND visit_server_date <= ? AND idsite = ?"; + $query = " + SELECT count(distinct visitor_idcookie) as nb_uniq_visitors + FROM ".$this->logTable." + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? + AND idsite = ?"; - return Zend_Registry::get('db')->fetchOne($query, array( $this->strDateStart, $this->strDateEnd, $this->idsite )); + return Zend_Registry::get('db')->fetchOne($query, array( $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite )); } /** @@ -311,16 +315,21 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing if(!$timestamp || $timestamp < time() - 86400 ) { + //@TODO fix for time zones + // maybe solution would be to add a flag "temporary" in the tables? and delete those with temporary=1 + // we delete out of date daily archives from table, maximum once per day // those for day N that were processed on day N (means the archives are only partial as the day wasn't finished) $query = "/* SHARDING_ID_SITE = ".$this->idsite." */ DELETE FROM %s WHERE period = ? AND date1 = DATE(ts_archived) - AND DATE(ts_archived) <> CURRENT_DATE() + AND DATE(ts_archived) <> ? "; - Piwik_Query(sprintf($query, $blobTable), Piwik::$idPeriods['day']); - Piwik_Query(sprintf($query, $numericTable), Piwik::$idPeriods['day']); + $today = date('Y-m-d'); + /* @fixme */ + //Piwik_Query(sprintf($query, $blobTable), array(Piwik::$idPeriods['day'], $today)); + //Piwik_Query(sprintf($query, $numericTable), array(Piwik::$idPeriods['day'], $today)); // we delete out of date Period records (week/month/etc) // we delete archives that were archived before the end of the period @@ -332,8 +341,8 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing AND date(ts_archived) < date_sub(CURRENT_DATE(), INTERVAL 1 DAY) "; - Piwik_Query(sprintf($query, $blobTable), Piwik::$idPeriods['day']); - Piwik_Query(sprintf($query, $numericTable), Piwik::$idPeriods['day']); + //Piwik_Query(sprintf($query, $blobTable), Piwik::$idPeriods['day']); + //Piwik_Query(sprintf($query, $numericTable), Piwik::$idPeriods['day']); Piwik_SetOption($key, time()); } diff --git a/core/DataTable/Renderer/Rss.php b/core/DataTable/Renderer/Rss.php index f9e7dabedb18bfb25c1734dc433350cd607c4a54..3918046def3ff39e2817f599bda85ba32d1e05ef 100644 --- a/core/DataTable/Renderer/Rss.php +++ b/core/DataTable/Renderer/Rss.php @@ -47,8 +47,9 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer $site = $table->metadata[$date]['site']; $pudDate = date('r', $timestamp); - $dateUrl = date('Y-m-d', $timestamp); - $thisPiwikUrl = htmlentities($piwikUrl . "&date=$dateUrl"); + + $dateInSiteTimezone = Piwik_Date::factory($timestamp)->setTimezone($site->getTimezone())->toString('Y-m-d'); + $thisPiwikUrl = htmlentities($piwikUrl . "&date=$dateInSiteTimezone"); $siteName = $site->getName(); $title = $siteName . " on ". $date; diff --git a/core/Date.php b/core/Date.php index f6aefce606a09ef96167858054ea159af2a21cf0..4e89a0e9f354be8f00c21aa0cf98ca629434844b 100644 --- a/core/Date.php +++ b/core/Date.php @@ -17,12 +17,27 @@ */ class Piwik_Date { + /** + * Builds a Piwik_Date object + * + * @param int timestamp + */ + protected function __construct( $timestamp, $timezone = 'UTC') + { + if(!is_int( $timestamp )) + { + throw new Exception("Piwik_Date is expecting a unix timestamp"); + } + $this->timezone = $timezone; + $this->timestamp = $timestamp ; + } + + /** * Returns a Piwik_Date objects. - * Accepts strings 'today' 'yesterday' or any YYYY-MM-DD or timestamp * - * @param string $strDate - * @return Piwik_Date + * @param string $strDate 'today' 'yesterday' or any YYYY-MM-DD or timestamp + * @return Piwik_Date */ static public function factory($dateString) { @@ -42,42 +57,103 @@ class Piwik_Date return new Piwik_Date($dateString); } + /* + * The stored timestamp is always UTC based. + * The returned timestamp via getTimestamp() will have the conversion applied + */ protected $timestamp = null; + + /* + * Timezone the current date object is set to. + * Timezone will only affect the returned timestamp via getTimestamp() + */ + protected $timezone = 'UTC'; + const DATE_TIME_FORMAT = 'Y-m-d H:i:s'; + + /** + * Returns the datetime start in UTC + * + * @return string + */ + function getDateStartUTC() + { + $dateStartUTC = date('Y-m-d', $this->timestamp); + $date = Piwik_Date::factory($dateStartUTC)->setTimezone($this->timezone); + return $date->toString(self::DATE_TIME_FORMAT); + } + + /** + * Returns the datetime of the current timestamp + * + * @return string + */ + function getDatetime() + { + return $this->toString(self::DATE_TIME_FORMAT); + } + + /** + * Returns the datetime end in UTC + * + * @return string + */ + function getDateEndUTC() + { + $dateEndUTC = date('Y-m-d 23:59:59', $this->timestamp); + $date = Piwik_Date::factory($dateEndUTC)->setTimezone($this->timezone); + return $date->toString(self::DATE_TIME_FORMAT); + } + /** - * Returns the unix timestamp of the date - * - * @return int + * Returns a new date object, copy of $this, with the timezone set + * This timezone is used to offset the UTC timestamp returned by @see getTimestamp() + * Doesn't modify $this + * + * @param string $timezone 'UTC', 'Europe/London', ... */ - public function getTimestamp() + public function setTimezone($timezone) { - return $this->timestamp; + return new Piwik_Date($this->timestamp, $timezone); } /** - * Builds a Piwik_Date object - * - * @param int timestamp + * Returns the unix timestamp of the date in UTC, + * converted from the date timezone + * + * @return int */ - protected function __construct( $date ) + public function getTimestamp() { - if(!is_int( $date )) + if($this->timezone == 'UTC') { - throw new Exception("Piwik_Date is expecting a unix timestamp"); + return (int)$this->timestamp; } - $this->timestamp = $date ; - } - - /** - * Sets the time part of the date - * Doesn't modify $this - * - * @param string $time HH:MM:SS - * @return Piwik_Date The new date with the time part set - */ - public function setTime($time) - { - return new Piwik_Date( strtotime( $this->get("j F Y") . " $time")); + $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. + // I decided to get the date without the timezone information, and create the timestamp from the truncated string. + // Unit tests pass (@see Date.test.php) but I'm pretty sure this is not the right way to do it + date_default_timezone_set($this->timezone); + $dtzone = new DateTimeZone('UTC'); + $time = date('r', $this->timestamp); + $dtime = new DateTime($time); + $dtime->setTimezone($dtzone); + $dateWithTimezone = $dtime->format('r'); + $dateWithoutTimezone = substr( $dateWithTimezone, 0, -6); + $timestamp = strtotime($dateWithoutTimezone); + date_default_timezone_set('UTC'); + + return (int)$timestamp; } /** @@ -113,7 +189,7 @@ class Piwik_Date { return date($part, $this->getTimestamp()); } - + /** * @see toString() * @@ -124,6 +200,104 @@ 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 + * Example: 09.Jan.2007 13:07:25 -> compareWeek(2); -> 0 + * + * @param Piwik_Date $date + * @return integer 0 = equal, 1 = later, -1 = earlier + */ + public function compareWeek(Piwik_Date $date) + { + $currentWeek = date('W', $this->getTimestamp()); + $toCompareWeek = date('W', $date->getTimestamp()); + if( $currentWeek == $toCompareWeek) + { + return 0; + } + if( $currentWeek < $toCompareWeek) + { + return -1; + } + 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 + * For example: 10.03.2000 -> 15.03.1950 -> 0 + * + * @param Piwik_Date $month Month to compare + * @return integer 0 = equal, 1 = later, -1 = earlier + */ + function compareMonth( Piwik_Date $date ) + { + $currentMonth = date('n', $this->getTimestamp()); + $toCompareMonth = date('n', $date->getTimestamp()); + if( $currentMonth == $toCompareMonth) + { + return 0; + } + if( $currentMonth < $toCompareMonth) + { + return -1; + } + return 1; + } + + /** + * Returns true if current date is today + * + * @return bool + */ + public function isToday() + { + return $this->toString('Y-m-d') === Piwik_Date::factory('today', $this->timezone)->toString('Y-m-d'); + } + + /** + * Returns a date object set to now (same as today, except that the time is also set) + * + * @return Piwik_Date + */ + static public function now() + { + return new Piwik_date(time()); + } + + /** + * Returns a date object set to today midnight + * + * @return Piwik_Date + */ + static public function today() + { + return new Piwik_Date(strtotime(date("Y-m-d 00:00:00"))); + } + + /** + * Returns a date object set to yesterday midnight + * + * @return Piwik_Date + */ + static public function yesterday() + { + return new Piwik_Date(strtotime("yesterday")); + } + + /** + * Sets the time part of the date + * Doesn't modify $this + * + * @param string $time HH:MM:SS + * @return Piwik_Date The new date with the time part set + */ + public function setTime($time) + { + return new Piwik_Date( strtotime( date("Y-m-d", $this->timestamp) . " $time"), $this->timezone); + } + /** * Sets a new day * Returned is the new date object @@ -134,7 +308,7 @@ class Piwik_Date */ public function setDay( $day ) { - $ts = $this->getTimestamp(); + $ts = $this->timestamp; $result = mktime( date('H', $ts), date('i', $ts), @@ -143,7 +317,7 @@ class Piwik_Date 1, date('Y', $ts) ); - return new Piwik_Date( $result ); + return new Piwik_Date( $result, $this->timezone ); } /** @@ -156,7 +330,7 @@ class Piwik_Date */ public function setYear( $year ) { - $ts = $this->getTimestamp(); + $ts = $this->timestamp; $result = mktime( date('H', $ts), date('i', $ts), @@ -165,7 +339,7 @@ class Piwik_Date date('j', $ts), $year ); - return new Piwik_Date( $result ); + return new Piwik_Date( $result, $this->timezone ); } @@ -183,8 +357,8 @@ class Piwik_Date { return clone $this; } - $ts = strtotime("-$n day", $this->getTimestamp()); - return new Piwik_Date( $ts ); + $ts = strtotime("-$n day", $this->timestamp); + return new Piwik_Date( $ts, $this->timezone ); } /** @@ -200,7 +374,7 @@ class Piwik_Date { return clone $this; } - $ts = $this->getTimestamp(); + $ts = $this->timestamp; $result = mktime( date('H', $ts), date('i', $ts), @@ -209,23 +383,9 @@ class Piwik_Date 1, // we set the day to 1 date('Y', $ts) ); - return new Piwik_Date( $result ); + return new Piwik_Date( $result, $this->timezone ); } - /** - * Returns a representation of a date or datepart - * - * @param string OPTIONAL Part of the date to return, if null the timestamp is returned - * @return integer|string date or datepart - */ - public function get($part = null) - { - if(is_null($part)) - { - return $this->getTimestamp(); - } - return date($part, $this->getTimestamp()); - } /** * Returns a localized date string, given a template. @@ -252,7 +412,7 @@ class Piwik_Date $out = str_replace(array_keys($patternToValue), array_values($patternToValue), $template); return $out; } - + /** * Adds days to the existing date object. * Returned is the new date object @@ -263,82 +423,30 @@ class Piwik_Date */ public function addDay( $n ) { - $ts = strtotime("+$n day", $this->getTimestamp()); - return new Piwik_Date( $ts ); + $ts = strtotime("+$n day", $this->timestamp); + return new Piwik_Date( $ts, $this->timezone ); } - - /** - * 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 - * Example: 09.Jan.2007 13:07:25 -> compareWeek(2); -> 0 - * - * @param Piwik_Date $date - * @return integer 0 = equal, 1 = later, -1 = earlier - */ - public function compareWeek(Piwik_Date $date) - { - $currentWeek = date('W', $this->getTimestamp()); - $toCompareWeek = date('W', $date->getTimestamp()); - if( $currentWeek == $toCompareWeek) - { - return 0; - } - if( $currentWeek < $toCompareWeek) - { - return -1; - } - 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 - * For example: 10.03.2000 -> 15.03.1950 -> 0 - * - * @param Piwik_Date $month Month to compare - * @return integer 0 = equal, 1 = later, -1 = earlier + * Adds hours to the existing date object. + * Returned is the new date object + * Doesn't modify $this + * + * @param int Number of hours to add + * @return Piwik_Date new date */ - function compareMonth( Piwik_Date $date ) + public function addHour( $n ) { - $currentMonth = date('n', $this->getTimestamp()); - $toCompareMonth = date('n', $date->getTimestamp()); - if( $currentMonth == $toCompareMonth) + if($n > 0 ) { - return 0; + $n = '+'.$n; } - if( $currentMonth < $toCompareMonth) - { - return -1; - } - return 1; - } - - /** - * Returns true if current date is today - * - * @return bool - */ - public function isToday() - { - return $this->get('Y-m-d') === date('Y-m-d', time()); + $ts = strtotime("$n hour", $this->timestamp); + return new Piwik_Date( $ts, $this->timezone ); } - - /** - * Returns a date object set to today midnight - * - * @return Piwik_Date - */ - static public function today() - { - return new Piwik_Date(strtotime(date("Y-m-d 00:00:00"))); - } - - /** - * Returns a date object set to yesterday midnight - * - * @return Piwik_Date - */ - static public function yesterday() + + public function subHour( $n ) { - return new Piwik_Date(strtotime("yesterday")); + return $this->addHour(-$n); } } diff --git a/core/Db.php b/core/Db.php index f16a8a6136fca83309cf6c5778c78be9984f24be..fcf48392afd9c10089f24f76fffd6bde67df9805 100644 --- a/core/Db.php +++ b/core/Db.php @@ -130,10 +130,4 @@ interface Piwik_Db_iAdapter */ public function isConnectionUTF8(); - /** - * Get server timezone offset in seconds - * - * @return string - */ - public function getCurrentTimezone(); } diff --git a/core/Db/Mysqli.php b/core/Db/Mysqli.php index 668585817b5245840ac4b63ef60497b5966d77ed..a7765e33b8abfd94ab9d15dbb0d01874f2463a42 100644 --- a/core/Db/Mysqli.php +++ b/core/Db/Mysqli.php @@ -138,24 +138,6 @@ class Piwik_Db_Mysqli extends Zend_Db_Adapter_Mysqli implements Piwik_Db_iAdapte return $charset === 'utf8'; } - /** - * Get server timezone offset in seconds - * - * @return string - */ - public function getCurrentTimezone() - { - $tzOffset = ''; - try { - // could return SYSTEM, an offset from UTC (e.g., -05:00), or a named timezone (e.g., 'Europe/Helsinki', 'US/Eastern', or 'MET') - $tz = $this->fetchOne('SELECT @@session.time_zone'); - - $tzOffset = $this->fetchOne("SELECT timestampdiff(second, '2004-01-01 12:00:00', CONVERT_TZ('2004-01-01 12:00:00','+00:00','$tz'))"); - } catch(Exception $e) { } - - return $tzOffset; - } - /** * Get client version * diff --git a/core/Db/Pdo/Mysql.php b/core/Db/Pdo/Mysql.php index 87177b8a1658d36375aba878f95ec78ab0aa74ab..d397a7023d72f6d89b0c51e1f952670e650637f6 100644 --- a/core/Db/Pdo/Mysql.php +++ b/core/Db/Pdo/Mysql.php @@ -138,24 +138,6 @@ class Piwik_Db_Pdo_Mysql extends Zend_Db_Adapter_Pdo_Mysql implements Piwik_Db_i return $charset === 'utf8'; } - /** - * Get server timezone offset in seconds - * - * @return string - */ - public function getCurrentTimezone() - { - $tzOffset = ''; - try { - // could return SYSTEM, an offset from UTC (e.g., -05:00), or a named timezone (e.g., 'Europe/Helsinki', 'US/Eastern', or 'MET') - $tz = $this->fetchOne('SELECT @@session.time_zone'); - - $tzOffset = $this->fetchOne("SELECT timestampdiff(second, '2004-01-01 12:00:00', CONVERT_TZ('2004-01-01 12:00:00','+00:00','$tz'))"); - } catch(Exception $e) { } - - return $tzOffset; - } - /** * Retrieve client version in PHP style * diff --git a/core/Db/Pdo/Pgsql.php b/core/Db/Pdo/Pgsql.php index 484021eb128d61cbaf3cdee57ebbe35ef71497b3..a1414408f18845b162c67ba89de8f00da4d05ca3 100644 --- a/core/Db/Pdo/Pgsql.php +++ b/core/Db/Pdo/Pgsql.php @@ -170,17 +170,6 @@ class Piwik_Db_Pdo_Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements Piwik_Db_i return strtolower($charset) === 'utf8'; } - /** - * Get server timezone offset in seconds - * - * @return string - */ - public function getCurrentTimezone() - { - $tzOffset = $this->fetchOne('SELECT extract(timezone FROM now())'); - return $tzOffset; - } - /** * Retrieve client version in PHP style * diff --git a/core/Period.php b/core/Period.php index a8c880f32ff589710c6dd56e27fc3f3c3665f4ee..050a4e2d5f4b65d06ef33f3f830db2e44c5d5354 100644 --- a/core/Period.php +++ b/core/Period.php @@ -44,7 +44,7 @@ abstract class Piwik_Period * @param $date Piwik_Date object * @return Piwik_Period */ - static public function factory($strPeriod, $date) + static public function factory($strPeriod, Piwik_Date $date) { switch ($strPeriod) { case 'day': @@ -186,25 +186,6 @@ abstract class Piwik_Period $this->subperiods[] = $date; } - /** - * A period is finished if all the subperiods are finished - */ - public function isFinished() - { - if(!$this->subperiodsProcessed) - { - $this->generate(); - } - foreach($this->subperiods as $period) - { - if(!$period->isFinished()) - { - return false; - } - } - return true; - } - public function toString() { if(!$this->subperiodsProcessed) @@ -230,7 +211,7 @@ abstract class Piwik_Period { $this->generate(); } - return $this->date->get($part); + return $this->date->toString($part); } abstract public function getPrettyString(); diff --git a/core/Period/Day.php b/core/Period/Day.php index 32686a0f512695843c965ebf57cc6e46aabf504d..a5eda257edcc02267459b96955d6020f9a9c27ef 100644 --- a/core/Period/Day.php +++ b/core/Period/Day.php @@ -41,15 +41,6 @@ class Piwik_Period_Day extends Piwik_Period return $out; } - public function isFinished() - { - $todayMidnight = Piwik_Date::today(); - if($this->date->isEarlier($todayMidnight)) - { - return true; - } - } - public function getNumberOfSubperiods() { return 0; diff --git a/core/Period/Month.php b/core/Period/Month.php index 40fda847cafb4a54e76c8a61556b685a98b1b079..1918ec3650785b0dcbc080cfa1f8e4e301cc2285 100644 --- a/core/Period/Month.php +++ b/core/Period/Month.php @@ -55,18 +55,4 @@ class Piwik_Period_Month extends Piwik_Period $currentDay = $currentDay->addDay(1); } } - - public function isFinished() - { - if(!$this->subperiodsProcessed) - { - $this->generate(); - } - // a month is finished - // if current month > month AND current year == year - // OR if current year > year - $year = $this->date->get("Y"); - return ( date("m") > $this->date->get("m") && date("Y") == $year) - || date("Y") > $year; - } } diff --git a/core/Piwik.php b/core/Piwik.php index f8bde88ce244086972b4c74e4003b89630bd2c21..1f61ad79492459dbbc068990ec6f481e1b116f51 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -611,7 +611,7 @@ class Piwik alias VARCHAR(45) NOT NULL, email VARCHAR(100) NOT NULL, token_auth CHAR(32) NOT NULL, - date_registered TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + date_registered TIMESTAMP NULL, PRIMARY KEY(login), UNIQUE KEY uniq_keytoken(token_auth) ) DEFAULT CHARSET=utf8 @@ -629,8 +629,9 @@ class Piwik idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(90) NOT NULL, main_url VARCHAR(255) NOT NULL, - ts_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + ts_created TIMESTAMP NULL, excluded_ips TEXT NOT NULL, + timezone VARCHAR( 50 ) NOT NULL, PRIMARY KEY(idsite) ) DEFAULT CHARSET=utf8 ", @@ -721,7 +722,6 @@ class Piwik visitor_returning TINYINT(1) NOT NULL, visit_first_action_time DATETIME NOT NULL, visit_last_action_time DATETIME NOT NULL, - visit_server_date DATE NOT NULL, visit_exit_idaction_url INTEGER(11) NOT NULL, visit_entry_idaction_url INTEGER(11) NOT NULL, visit_total_actions SMALLINT(5) UNSIGNED NOT NULL, @@ -751,31 +751,30 @@ class Piwik location_country CHAR(3) NOT NULL, location_continent CHAR(3) NOT NULL, PRIMARY KEY(idvisit), - INDEX index_idsite_date_config (idsite, visit_server_date, config_md5config(8)) + INDEX index_idsite_datetime_config (idsite, visit_last_action_time, config_md5config(8)) ) DEFAULT CHARSET=utf8 ", 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` ( - `idvisit` int(10) unsigned NOT NULL, - `idsite` int(10) unsigned NOT NULL, - `visitor_idcookie` char(32) NOT NULL, - `server_time` datetime NOT NULL, - `visit_server_date` date NOT NULL, - `idaction_url` int(11) default NULL, - `idlink_va` int(11) default NULL, - `referer_idvisit` int(10) unsigned default NULL, - `referer_visit_server_date` date default NULL, - `referer_type` int(10) unsigned default NULL, - `referer_name` varchar(70) default NULL, - `referer_keyword` varchar(255) default NULL, - `visitor_returning` tinyint(1) NOT NULL, - `location_country` char(3) NOT NULL, - `location_continent` char(3) NOT NULL, - `url` text NOT NULL, - `idgoal` int(10) unsigned NOT NULL, - `revenue` float default NULL, - PRIMARY KEY (`idvisit`,`idgoal`), - INDEX `index_idsite_date` (`idsite`,`visit_server_date`) + idvisit int(10) unsigned NOT NULL, + idsite int(10) unsigned NOT NULL, + visitor_idcookie char(32) NOT NULL, + server_time datetime NOT NULL, + idaction_url int(11) default NULL, + idlink_va int(11) default NULL, + referer_idvisit int(10) unsigned default NULL, + referer_visit_server_date date default NULL, + referer_type int(10) unsigned default NULL, + referer_name varchar(70) default NULL, + referer_keyword varchar(255) default NULL, + visitor_returning tinyint(1) NOT NULL, + location_country char(3) NOT NULL, + location_continent char(3) NOT NULL, + url text NOT NULL, + idgoal int(10) unsigned NOT NULL, + revenue float default NULL, + PRIMARY KEY (idvisit,idgoal), + INDEX index_idsite_datetime ( idsite , server_time ) ) DEFAULT CHARSET=utf8 ", @@ -1530,6 +1529,22 @@ class Piwik return (preg_match('/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9_.-]+\.[a-zA-Z]{2,4}$/', $email) > 0); } + /** + * Returns true if the current php version supports timezone manipulation + * (most likely if php >= 5.2) + * + * @return bool + */ + static public function isTimezoneSupportEnabled() + { + return + function_exists( 'date_create' ) && + function_exists( 'date_default_timezone_set' ) && + function_exists( 'timezone_identifiers_list' ) && + function_exists( 'timezone_open' ) && + function_exists( 'timezone_offset_get' ); + } + /** * Creates an entry in the User table for the "anonymous" user. */ @@ -1539,7 +1554,7 @@ class Piwik // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin $db = Zend_Registry::get('db'); $db->query("INSERT INTO ". Piwik::prefixTable("user") . " - VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', CURRENT_TIMESTAMP );" ); + VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', '".time()."' );" ); } /** diff --git a/core/Site.php b/core/Site.php index 539bf08db2c4fa3765230f22163b96a23a1addb2..c57c86e149ec31172b019aa24db67221a3b3b5f2 100644 --- a/core/Site.php +++ b/core/Site.php @@ -31,7 +31,12 @@ class Piwik_Site function __toString() { - return "site id=".$this->getId().", name=".$this->getName(); + return "site id=".$this->getId().", + name=".$this->getName() .", + url = ". $this->getMainUrl() .", + IPs excluded = ".$this->getExcludedIps().", + timezone = ".$this->getTimezone().", + creation date = ".$this->getCreationDate(); } function getName() @@ -55,6 +60,16 @@ class Piwik_Site return Piwik_Date::factory($date); } + function getTimezone() + { + return self::$infoSites[$this->id]['timezone']; + } + + function getExcludedIps() + { + return self::$infoSites[$this->id]['excluded_ips']; + } + /** * @param string comma separated idSite list * @return array of valid integer @@ -70,4 +85,9 @@ class Piwik_Site } return $validIds; } + + static public function clearCache() + { + self::$infoSites = array(); + } } diff --git a/core/SmartyPlugins/modifier.inlineHelp.php b/core/SmartyPlugins/modifier.inlineHelp.php index bd4774960bbaec7bf1d2468b4b3f69e41e5db375..7da02c0717846f08b1fd67790c13f38b1aff4c22 100644 --- a/core/SmartyPlugins/modifier.inlineHelp.php +++ b/core/SmartyPlugins/modifier.inlineHelp.php @@ -16,7 +16,7 @@ function smarty_modifier_inlineHelp($text) { return - '<div class="ui-widget">'. + '<div style=\'width:200px\' class="ui-widget">'. '<div class="ui-state-highlight ui-corner-all" style="margin-top:20px; padding:0 .7em;">'. '<p style="font-size:8pt;"><span class="ui-icon ui-icon-info" style="float:left;margin-right:.3em;"></span>'. $text. diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index fb5eff16799c68fdc0acb54e5166b64e375f2033..ac4da13f615e560ed6ee5adbbd3118fc37e914ac 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -170,7 +170,6 @@ class Piwik_Tracker_GoalManager 'idsite' => $visitorInformation['idsite'], 'visitor_idcookie' => $visitorInformation['visitor_idcookie'], 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']), - 'visit_server_date' => $visitorInformation['visit_server_date'], 'location_country' => $location_country, 'location_continent'=> $location_continent, 'visitor_returning' => $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_VISITOR_RETURNING ), diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index 55198776afa14cedf8eec653f5cb1a96aed95e71..0540640455b594f9fd139826118af813e3900ea7 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -238,21 +238,22 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface SET $sqlActionIdUpdate $sqlUpdateGoalConverted visit_last_action_time = ?, - visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time) + visit_total_time = ? WHERE idvisit = ? AND visitor_idcookie = ? LIMIT 1", array( $datetimeServer, + $visitTotalTime = $this->getCurrentTimestamp() - $this->visitorInfo['visit_first_action_time'], $this->visitorInfo['idvisit'], $this->visitorInfo['visitor_idcookie'] ) ); + printDebug('Updating visitor with idvisit='.$this->visitorInfo['idvisit'].', setting visit_last_action_time='.$datetimeServer.' and visit_total_time='.$visitTotalTime); if(Piwik_Tracker::getDatabase()->rowCount($result) == 0) { throw new Piwik_Tracker_Visit_VisitorNotFoundInDatabase("The visitor with visitor_idcookie=".$this->visitorInfo['visitor_idcookie']." and idvisit=".$this->visitorInfo['idvisit']." wasn't found in the DB, we fallback to a new visitor"); } $this->visitorInfo['idsite'] = $this->idsite; - $this->visitorInfo['visit_server_date'] = $this->getCurrentDate(); // will be updated in cookie $this->visitorInfo['time_spent_ref_action'] = $serverTime - $this->visitorInfo['visit_last_action_time']; @@ -276,7 +277,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface .':'. Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'int', $this->request) .':'. Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'int', $this->request); $serverTime = $this->getCurrentTimestamp(); - $serverDate = $this->getCurrentDate(); $idcookie = $this->getVisitorIdcookie(); $returningVisitor = $this->isVisitorKnown() ? 1 : 0; @@ -298,7 +298,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface 'visitor_returning' => $returningVisitor, 'visit_first_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTime), 'visit_last_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTime), - 'visit_server_date' => $serverDate, 'visit_entry_idaction_url' => $actionUrlId, 'visit_exit_idaction_url' => $actionUrlId, 'visit_total_actions' => 1, @@ -533,7 +532,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface */ protected function getCookieExpire() { - return time() + Piwik_Tracker_Config::getInstance()->Tracker['cookie_expire']; + return $this->getCurrentTimestamp() + Piwik_Tracker_Config::getInstance()->Tracker['cookie_expire']; } /** @@ -621,12 +620,12 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $visitRow = Piwik_Tracker::getDatabase()->fetch( " SELECT visitor_idcookie, - UNIX_TIMESTAMP(visit_last_action_time) as visit_last_action_time, - UNIX_TIMESTAMP(visit_first_action_time) as visit_first_action_time, + visit_last_action_time, + visit_first_action_time, idvisit, visit_exit_idaction_url FROM ".Piwik_Common::prefixTable('log_visit'). - " WHERE visit_server_date = ? + " WHERE DATE(visit_last_action_time) = ? AND idsite = ? AND config_md5config = ? ORDER BY visit_last_action_time DESC @@ -636,14 +635,14 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface && count($visitRow) > 0) { $this->visitorInfo['visitor_idcookie'] = $visitRow['visitor_idcookie']; - $this->visitorInfo['visit_last_action_time'] = $visitRow['visit_last_action_time']; - $this->visitorInfo['visit_first_action_time'] = $visitRow['visit_first_action_time']; + $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']); + $this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']); $this->visitorInfo['idvisit'] = $visitRow['idvisit']; $this->visitorInfo['visit_exit_idaction_url'] = $visitRow['visit_exit_idaction_url']; $this->visitorKnown = true; - printDebug("The visitor is known because of his userSettings+IP (idcookie = {$visitRow['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).") "); + printDebug("The visitor is known because of his userSettings+IP (idcookie = {$visitRow['visitor_idcookie']}, idvisit = {$this->visitorInfo['idvisit']}, last action = ".date("r", $this->visitorInfo['visit_last_action_time']).", first action = ".date("r", $this->visitorInfo['visit_first_action_time']) .")"); } } } diff --git a/core/Updates/0.6.php b/core/Updates/0.6.php index 3de1a24ec2e1a599fcafefe9ed6143bdcdbc709f..4992fd2353e1d30928d6f03be1478897069caa2b 100644 --- a/core/Updates/0.6.php +++ b/core/Updates/0.6.php @@ -17,8 +17,19 @@ class Piwik_Updates_0_6 extends Piwik_Updates { static function getSql($adapter = 'PDO_MYSQL') { + $defaultTimezone = 'UTC'; return array( - 'ALTER TABLE ' . Piwik::prefixTable('site') . ' ADD `excluded_ips` TEXT NOT NULL AFTER `ts_created` ;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('site') . ' ADD `timezone` VARCHAR( 50 ) NOT NULL AFTER `ts_created` ;' => false, + 'UPDATE ' . Piwik::prefixTable('site') . ' SET `timezone` = "'.$defaultTimezone.'";' => false, + 'ALTER TABLE ' . Piwik::prefixTable('site') . ' ADD `excluded_ips` TEXT NOT NULL AFTER `timezone` ;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_visit') . ' DROP INDEX `index_idsite_date_config` ;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_visit') . ' DROP visit_server_date;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_visit') . ' ADD INDEX `index_idsite_datetime_config` ( `idsite` , `visit_last_action_time` , `config_md5config` ( 8 ) ) ;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_conversion') . ' DROP INDEX index_idsite_date' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_conversion') . ' DROP visit_server_date;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_conversion') . ' ADD INDEX index_idsite_datetime ( `idsite` , `server_time` )' => false, + 'ALTER TABLE ' . Piwik::prefixTable('user') . ' CHANGE date_registered date_registered TIMESTAMP NULL' => false, + 'ALTER TABLE ' . Piwik::prefixTable('site') . ' CHANGE ts_created ts_created TIMESTAMP NULL' => false, ); } diff --git a/index.php b/index.php index 114fca3174c9f13a58678c395285d14dd00921cb..f4f0d8e925add1370145321415fd2610926e50e6 100644 --- a/index.php +++ b/index.php @@ -81,7 +81,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php'; // NOTE: the code above this comment must be PHP4 compatible session_cache_limiter('nocache'); -@date_default_timezone_set(date_default_timezone_get()); +@date_default_timezone_set('UTC'); require_once PIWIK_INCLUDE_PATH .'/core/Loader.php'; if(!defined('PIWIK_ENABLE_SESSION_START') || PIWIK_ENABLE_SESSION_START) diff --git a/lang/en.php b/lang/en.php index ea9c97ab22fa013e055a0c568e021c565285232e..e35f953a8f6c4e6f33b8d4eceb311ee9aa9f8445 100644 --- a/lang/en.php +++ b/lang/en.php @@ -369,8 +369,7 @@ $translations = array( 'Installation_DatabaseServerVersion' => 'Database server version', 'Installation_DatabaseClientVersion' => 'Database client version', 'Installation_DatabaseCreation' => 'Database creation', - 'Installation_DatabaseTimezone' => 'Database timezone', - 'Installation_TimezoneMismatch' => 'PHP date.timezone is not the same as the database server timezone. This might cause issues with reports not showing data for the right hours of the day. More information at %s', + 'Installation_PleaseFixTheFollowingErrors' => 'Please fix the following errors', 'Installation_JsTag' => 'JavaScript tag', 'Installation_JsTagHelp' => '<p>To count all visitors, you must insert the JavaScript code on all of your pages.</p><p>Your pages do not have to be made with PHP, Piwik will work on all kinds of pages (whether it is HTML, ASP, Perl or any other languages).</p><p>Here is the code you have to insert: (copy and paste on all your pages) </p>', 'Installation_JsTagHelpTitle' => 'How to insert the tag in your websites?', @@ -545,12 +544,20 @@ $translations = array( 'SitesManager_ExceptionNoUrl' => 'You must specify at least one URL for the website.', 'SitesManager_ExceptionEmptyName' => 'The website name can\'t be empty.', 'SitesManager_ExceptionInvalidUrl' => 'The url \'%s\' is not a valid URL.', - 'SitesManager_SuperUserCanExcludeIpsOnAllWebsites' => 'The Super User can also %1sexclude IPs from being tracked on all websites%2s', + 'SitesManager_SuperUserCan' => 'The Super User can also %1sexclude IPs from being tracked on all websites%2s, %3sset the default timezone for new websites%4s.', 'SitesManager_ExcludedIps' => 'Excluded IPs', 'SitesManager_ListOfIpsToBeExcludedOnAllWebsites' => 'You can enter below the list of IPs to be excluded from being tracked on all websites.', 'SitesManager_GlobalListExcludedIps' => 'Global list of Excluded IPs', 'SitesManager_HelpExcludedIps' => 'Enter the list of IPs, one per line, that you wish to exclude from being tracked by Piwik. You can use wildcards, eg. %1s or %2s ', 'SitesManager_YourCurrentIpAddressIs' => 'Your current IP address is %s', + 'SitesManager_ChooseCityInSameTimezoneAsYou' => 'Choose a city in the same time zone as you.', + 'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward' => 'Changing your time zone will only affect data going forward, and will not be applied retroactively.', + 'SitesManager_SelectCity' => 'Select a city', + 'SitesManager_AdvancedTimezoneSupportNotFound' => 'Advanced timezones support was not found in your PHP (supported in PHP>=5.2). You can still choose a manual UTC offset.', + 'SitesManager_UTCTimeIs' => 'UTC time is %s.', + 'SitesManager_Timezone' => 'Time zone', + 'SitesManager_DefaultTimezone' => 'Default Time zone', + 'SitesManager_SelectDefaultTimezone' => 'You can select the time zone to select by default for new websites.', 'TranslationsAdmin_PluginDescription' => 'Help translate Piwik into your language.', 'TranslationsAdmin_MenuTranslations' => 'Translations', 'TranslationsAdmin_MenuLanguages' => 'Languages', diff --git a/misc/generateVisits.php b/misc/generateVisits.php index c5db4ddb0b603782d6bd91fc4f7c42a397ee1326..e0dd22c14b8117ce318b6b32d2d26f5502b4589c 100644 --- a/misc/generateVisits.php +++ b/misc/generateVisits.php @@ -62,7 +62,6 @@ if( empty($_GET['choice']) return; } -// TODO - generator should generate pages with slash, then test that period archiving doesn't show the unique page view // TODO - should generate goals with keyword or referer that are not found for this day, to simulate a referer 5 days ago and conversion today $minVisitors = 20; $maxVisitors = 100; diff --git a/piwik.php b/piwik.php index e69aad6c1263e7b4ad529a325f824ba518f20c21..1b5d19b79e163a5ea7afcb3d340bcdb2c5e04689 100644 --- a/piwik.php +++ b/piwik.php @@ -57,9 +57,9 @@ require_once PIWIK_INCLUDE_PATH .'/core/Cookie.php'; session_cache_limiter('nocache'); ob_start(); +@date_default_timezone_set('UTC'); if($GLOBALS['PIWIK_TRACKER_DEBUG'] === true) { - @date_default_timezone_set(date_default_timezone_get()); require_once PIWIK_INCLUDE_PATH .'/core/Loader.php'; require_once PIWIK_INCLUDE_PATH .'/core/ErrorHandler.php'; require_once PIWIK_INCLUDE_PATH .'/core/ExceptionHandler.php'; diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 371c0ce21581535ba63f4f762eff11b82b65efe4..930e869b55933d4f8a3a2b8e1c8abe4c20cce19c 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -125,6 +125,7 @@ class Piwik_Actions extends Piwik_Plugin public function archiveDay( $notification ) { //TODO Actions should use integer based keys like other archive in piwik + /* @var $archiveProcessing Piwik_ArchiveProcessing */ $archiveProcessing = $notification->getNotificationObject(); $this->actionsTablesByType = array( @@ -155,11 +156,12 @@ class Piwik_Actions extends Piwik_Plugin FROM (".$archiveProcessing->logTable." as t1 LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit)) LEFT JOIN ".$archiveProcessing->logActionTable." as t3 ON (t2.idaction_url = t3.idaction) - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY t3.idaction ORDER BY nb_hits DESC"; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); /* @@ -173,11 +175,12 @@ class Piwik_Actions extends Piwik_Plugin FROM (".$archiveProcessing->logTable." as t1 LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit)) LEFT JOIN ".$archiveProcessing->logActionTable." as t3 ON (t2.idaction_name = t3.idaction) - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY t3.idaction ORDER BY nb_hits DESC"; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); /* @@ -192,11 +195,12 @@ class Piwik_Actions extends Piwik_Plugin sum(case visit_total_actions when 1 then 1 else 0 end) as entry_bounce_count FROM ".$archiveProcessing->logTable." JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction_url = idaction) - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY visit_entry_idaction_url "; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); @@ -210,11 +214,12 @@ class Piwik_Actions extends Piwik_Plugin sum(case visit_total_actions when 1 then 1 else 0 end) as exit_bounce_count FROM ".$archiveProcessing->logTable." JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction_url = idaction) - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY visit_exit_idaction_url "; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); /* @@ -226,11 +231,12 @@ class Piwik_Actions extends Piwik_Plugin FROM (".$archiveProcessing->logTable." log_visit JOIN ".$archiveProcessing->logVisitActionTable." log_link_visit_action USING (idvisit)) JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_url_ref) - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY idaction_url_ref "; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); $this->archiveDayRecordInDatabase($archiveProcessing); } diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php index 9799931849ea7d1d9dadb38db9ad2db8b98b95c0..e7c948a04b5d1ed9ca1859db7bed5facf8a815f8 100644 --- a/plugins/Installation/Controller.php +++ b/plugins/Installation/Controller.php @@ -257,13 +257,6 @@ class Piwik_Installation_Controller extends Piwik_Controller $this->session->charsetCorrection = true; } - $dbTimezone = $db->getCurrentTimezone(); - $phpTimezone = date('Z'); - if($dbTimezone !== '' && ($dbTimezone != $phpTimezone)) - { - $view->timezoneWarning = true; - } - $view->showNextStep = true; $this->session->currentStepDone = __FUNCTION__; @@ -801,6 +794,7 @@ class Piwik_Installation_Controller extends Piwik_Controller $infos['integrityErrorMessages'] += array_slice($integrityInfo, 1); } + $infos['timezone'] = Piwik::isTimezoneSupportEnabled(); return $infos; } diff --git a/plugins/Installation/templates/databaseCheck.tpl b/plugins/Installation/templates/databaseCheck.tpl index c9eec3f3d777394f5a3faf2ee90786a4d21886df..3374582f7452b26f8e2a69ddb78760ad054da77d 100644 --- a/plugins/Installation/templates/databaseCheck.tpl +++ b/plugins/Installation/templates/databaseCheck.tpl @@ -27,19 +27,6 @@ <td class="label">{'Installation_DatabaseCreation'|translate}</td> <td>{if isset($databaseCreated)}{$ok}{else}{$error}{/if}</td> </tr> - <tr> - <td class="label">{'Installation_DatabaseTimezone'|translate}</td> - <td>{if isset($timezoneWarning)}{$warning}{else}{$ok}{/if}</td> - </tr> -{if isset($timezoneWarning)} - <tr> - <td colspan="2"> - <small> - <span style="color:#FF7F00">{'Installation_TimezoneMismatch'|translate:"<a href='misc/redirectToUrl.php?url=http://piwik.org/FAQ/troubleshooting/#faq_58' target='_blank'>FAQ</a>"}.</span> - </small> - </td> - </tr> -{/if} </table> <p> diff --git a/plugins/Installation/templates/systemCheck.tpl b/plugins/Installation/templates/systemCheck.tpl index 6e9e1dfe424a744fa4551eeadf560e7055536e6a..11b0ed78dc47d69c5faa47e4531df64edca5e416 100644 --- a/plugins/Installation/templates/systemCheck.tpl +++ b/plugins/Installation/templates/systemCheck.tpl @@ -146,7 +146,7 @@ {else} {$error} <i>{$infos.integrityErrorMessages[0]}</i> {/if} - {if count($infos.integrityErrorMessages) > 1} + {if count($infos.integrityErrorMessages) >= 1} <button id="more-results" class="ui-button ui-state-default ui-corner-all">{'General_Details'|translate}</button> {/if} {/if} @@ -160,6 +160,13 @@ <br /><i>{'Installation_SystemCheckMemoryLimitHelp'|translate}</i>{/if} </td> </tr> + <tr> + <td class="label">{'SitesManager_Timezone'|translate}</td> + <td> + {if $infos.timezone}{$ok}{else}{$warning} + <br /><i>{'SitesManager_AdvancedTimezoneSupportNotFound'|translate}</i>{/if} + </td> + </tr> <tr> <td class="label">{'Installation_SystemCheckOpenURL'|translate}</td> <td> diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php index 388913e4208c3399110cec8a1f18e31462eea4c8..a78f9ca9c461b0c4807ac8e9125d68dfd6a04b1c 100644 --- a/plugins/Live/Visitor.php +++ b/plugins/Live/Visitor.php @@ -83,7 +83,7 @@ class Piwik_Live_Visitor function getServerDate() { - return $this->details['visit_server_date']; + return date('Y-m-d', strtotime($this->details['visit_last_action_time'])); } function getIp() diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php index 390c0b21eb3ae9c751d9ba909632fd59bbd49405..bddff604e8b28abe095c11b855b403f5bbe401d6 100644 --- a/plugins/Referers/Referers.php +++ b/plugins/Referers/Referers.php @@ -150,7 +150,7 @@ class Piwik_Referers extends Piwik_Plugin destroy($this->distinctUrls); } - protected function archiveDayAggregateVisits($archiveProcessing) + protected function archiveDayAggregateVisits(Piwik_ArchiveProcessing $archiveProcessing) { $query = "SELECT referer_type, referer_name, @@ -164,11 +164,12 @@ class Piwik_Referers extends Piwik_Plugin sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$archiveProcessing->logTable." - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? GROUP BY referer_type, referer_name, referer_url, referer_keyword ORDER BY nb_visits DESC"; - $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite )); $this->interestBySearchEngine = $this->interestByKeyword = diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 3274f5673d52dedda6b9055645a113ded6b5d36c..5ffe6a7ae457e4935ee53763501bb09282e6fcbe 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -32,6 +32,7 @@ class Piwik_SitesManager_API } const OPTION_EXCLUDED_IPS_GLOBAL = 'SitesManager_ExcludedIpsGlobal'; + const OPTION_DEFAULT_TIMEZONE = 'SitesManager_DefaultTimezone'; /** * Returns the javascript tag for the given idSite. @@ -221,10 +222,11 @@ class Piwik_SitesManager_API * if several URLs are provided in the array, they will be recorded * as Alias URLs for this website. * @param string Comma separated list of IPs to exclude from the reports (allows wildcards) + * @param string Timezone string, eg. 'Europe/London' * * @return int the website ID created */ - public function addSite( $siteName, $urls, $excludedIps = null ) + public function addSite( $siteName, $urls, $excludedIps = null, $timezone = null ) { Piwik::checkUserIsSuperUser(); @@ -232,6 +234,13 @@ class Piwik_SitesManager_API $urls = $this->cleanParameterUrls($urls); $this->checkUrls($urls); $this->checkAtLeastOneUrl($urls); + $timezone = trim($timezone); + + if(empty($timezone)) + { + $timezone = $this->getDefaultTimezone(); + } + $this->checkValidTimezone($timezone); $db = Zend_Registry::get('db'); @@ -240,9 +249,11 @@ class Piwik_SitesManager_API $bind = array( 'name' => $siteName, 'main_url' => $url, + 'ts_created' => Piwik_Date::now()->getDatetime() ); $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); + $bind['timezone'] = $timezone; $db->insert(Piwik::prefixTable("site"), $bind); $idSite = $db->lastInsertId(); @@ -312,6 +323,22 @@ class Piwik_SitesManager_API } } + private function checkValidTimezone($timezone) + { + $timezones = $this->getTimezonesList(); + foreach($timezones as $continent => $cities) + { + foreach($cities as $timezoneId => $city) + { + if($timezoneId == $timezone) + { + return true; + } + } + } + throw new Exception('The timezone "'.$timezone.'" is not valid. Please enter a valid timezone.'); + } + /** * Checks that the submitted IPs (comma separated list) are valid * Returns the cleaned up IPs @@ -383,6 +410,37 @@ class Piwik_SitesManager_API return Piwik_GetOption(self::OPTION_EXCLUDED_IPS_GLOBAL); } + /** + * Returns the default timezone that will be set when creating a website through the API. + * Via the UI, if the default timezone is not UTC, it will be pre-selected in the drop down + * + * @return string Timezone eg. UTC+7 or Europe/Paris + */ + public function getDefaultTimezone() + { + Piwik::checkUserIsSuperUser(); + $defaultTimezone = Piwik_GetOption(self::OPTION_DEFAULT_TIMEZONE); + if($defaultTimezone) + { + return $defaultTimezone; + } + return 'UTC'; + } + + /** + * Sets the default timezone that will be used when creating websites + * + * @param $timezone string eg. Europe/Paris or UTC+8 + * @return bool + */ + public function setDefaultTimezone($defaultTimezone) + { + Piwik::checkUserIsSuperUser(); + $this->checkValidTimezone($defaultTimezone); + Piwik_SetOption(self::OPTION_DEFAULT_TIMEZONE, $defaultTimezone); + return true; + } + /** * Update an existing website. * If only one URL is specified then only the main url will be updated. @@ -392,12 +450,13 @@ class Piwik_SitesManager_API * @param string website name * @param string|array the website URLs * @param string Comma separated list of IPs to exclude from being tracked (allows wildcards) + * @param string Timezone * * @exception if any of the parameter is not correct * * @return bool true on success */ - public function updateSite( $idSite, $siteName, $urls = null, $excludedIps = null) + public function updateSite( $idSite, $siteName, $urls = null, $excludedIps = null, $timezone = null) { Piwik::checkUserHasAdminAccess($idSite); @@ -415,6 +474,14 @@ class Piwik_SitesManager_API $bind['main_url'] = $url; } + + if(!is_null($timezone)) + { + $timezone = trim($timezone); + $this->checkValidTimezone($timezone); + $bind['timezone'] = $timezone; + } + $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps); $bind['name'] = $siteName; $db = Zend_Registry::get('db'); @@ -432,6 +499,72 @@ class Piwik_SitesManager_API $this->postUpdateWebsite($idSite); } + /** + * Returns the list of timezones supported. + * Used for addSite and updateSite + * + * @TODO NOT COMPATIBLE WITH API RESPONSE AUTO BUILDER + * + * @return array of timezone strings + */ + public function getTimezonesList() + { + if(!Piwik::isTimezoneSupportEnabled()) + { + return array('UTC' => $this->getTimezonesListUTCOffsets()); + } + + $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific'); + $timezones = timezone_identifiers_list(); + + $return = array(); + foreach($timezones as $timezone) + { + $timezoneExploded = explode('/', $timezone); + $continent = $timezoneExploded[0]; + + // only display timezones that are grouped by continent + if(!in_array($continent, $continents)) + { + continue; + } + $city = $timezoneExploded[1]; + if(!empty($timezoneExploded[2])) + { + $city .= ' - '.$timezoneExploded[2]; + } + $city = str_replace('_', ' ', $city); + $return[$continent][$timezone] = $city; + } + + $return['UTC'] = $this->getTimezonesListUTCOffsets(); + return $return; + } + + private function getTimezonesListUTCOffsets() + { + // manually add the UTC offsets + $GmtOffsets = array (-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, + 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14); + + $return = array(); + foreach($GmtOffsets as $offset) + { + if($offset > 0) + { + $offset = '+'.$offset; + } + elseif($offset == 0) + { + $offset = ''; + } + $offset = 'UTC' . $offset; + $offsetName = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset); + $return[$offset] = $offsetName; + } + return $return; + } + /** * Insert the list of alias URLs for the website. * The URLs must not exist already for this website! diff --git a/plugins/SitesManager/Controller.php b/plugins/SitesManager/Controller.php index 2647008093a2916649f8fef76a3154d0905f278a..140de42455173450f15e1f4940fa2b5f29c3616a 100644 --- a/plugins/SitesManager/Controller.php +++ b/plugins/SitesManager/Controller.php @@ -26,10 +26,21 @@ class Piwik_SitesManager_Controller extends Piwik_Controller $site['excluded_ips'] = str_replace(',','<br/>', $site['excluded_ips']); } $view->adminSites = $sites; - $view->currentIpAddress = Piwik_Common::getIpString(); - $this->setGeneralVariablesView($view); + + try { + $timezones = Piwik_SitesManager_API::getInstance()->getTimezonesList(); + } catch(Exception $e) { + $timezones = array(); + } + $view->timezoneSupported = Piwik::isTimezoneSupportEnabled(); + $view->timezones = json_encode($timezones); + $view->utcTime = Piwik_Date::now()->getDatetime(); $excludedIpsGlobal = Piwik_SitesManager_API::getInstance()->getExcludedIpsGlobal(); $view->globalExcludedIps = str_replace(',',"\n", $excludedIpsGlobal); + $view->defaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone(); + $view->currentIpAddress = Piwik_Common::getIpString(); + + $this->setGeneralVariablesView($view); $view->menu = Piwik_GetAdminMenu(); echo $view->render(); } diff --git a/plugins/SitesManager/templates/SitesManager.js b/plugins/SitesManager/templates/SitesManager.js index 1c06c8c4acadf1a12b062043a09673bcf6716caf..f60b0b615a6dc262c5accec2f42aefe6c66413be 100644 --- a/plugins/SitesManager/templates/SitesManager.js +++ b/plugins/SitesManager/templates/SitesManager.js @@ -24,13 +24,15 @@ function getAddSiteAJAX( row ) var urls = getApiFormatUrls(urls); var excludedIps = $(row).find('textarea#siteadd_excludedIps').val(); excludedIps = getApiFormatExcludedIps(excludedIps); - + var timezone = encodeURIComponent($(row).find('option:selected').val()); + var request = ''; request += '&module=API'; request += '&format=json'; request += '&method=SitesManager.addSite'; siteName = encodeURIComponent(siteName); request += '&siteName='+siteName; + request += '&timezone='+timezone; request += '&excludedIps='+excludedIps; $.each(urls, function (key,value){ request+= '&urls[]='+escape(value);} ); request += '&token_auth=' + piwik.token_auth; @@ -59,7 +61,7 @@ function getUpdateSiteAJAX( row ) urls = getApiFormatUrls(urls); var excludedIps = $(row).find('textarea#excludedIps').val(); excludedIps = getApiFormatExcludedIps(excludedIps); - + var timezone = encodeURIComponent($(row).find('option:selected').val()); var request = ''; request += '&module=API'; request += '&format=json'; @@ -67,6 +69,7 @@ function getUpdateSiteAJAX( row ) siteName = encodeURIComponent(siteName); request += '&siteName='+siteName; request += '&idSite='+idSite; + request += '&timezone='+timezone; request += '&excludedIps='+excludedIps; $.each(urls, function (key,value){ if(value.length>1) request+= '&urls[]='+value;} ); request += '&token_auth=' + piwik.token_auth; @@ -76,6 +79,20 @@ function getUpdateSiteAJAX( row ) return ajaxRequest; } +function getDefaultTimezoneAJAX() +{ + var ajaxRequest = piwikHelper.getStandardAjaxConf('ajaxLoadingDefaultTimezone', 'ajaxErrorDefaultTimezone'); + var timezone = encodeURIComponent($('#defaultTimezone option:selected').val()); + var request = ''; + request += '&module=API'; + request += '&format=json'; + request += '&method=SitesManager.setDefaultTimezone'; + request += '&defaultTimezone='+timezone; + request += '&token_auth=' + piwik.token_auth; + ajaxRequest.data = request; + return ajaxRequest; +} + function getSetGlobalExcludedIpsAJAX() { var ajaxRequest = piwikHelper.getStandardAjaxConf('ajaxLoadingExcludedIps', 'ajaxErrorExcludedIps'); @@ -104,6 +121,7 @@ $(document).ready( function() { <td><input id="siteadd_name" value="Name" size="25" /></td>\ <td><textarea cols="30" rows="3" id="siteadd_urls">http://siteUrl.com/\nhttp://siteUrl2.com/</textarea><br />'+aliasUrlsHelp+'</td>\ <td><textarea cols="30" rows="3" id="siteadd_excludedIps"></textarea><br />'+excludedIpHelp+'</td>\ + <td>'+getTimezoneSelector(defaultTimezone)+'<br />' + timezoneHelp + '</td>\ <td><img src="plugins/UsersManager/images/ok.png" class="addsite" href="#" /></td>\ <td><img src="plugins/UsersManager/images/remove.png" class="cancel" /></td>\ </tr>') @@ -159,6 +177,12 @@ $(document).ready( function() { contentAfter += '<br />'+excludedIpHelp; $(n).html(contentAfter); } + if(idName == 'timezone') + { + var contentAfter = getTimezoneSelector(contentBefore); + contentAfter += '<br />' + timezoneHelp; + $(n).html(contentAfter); + } } ); $(this) @@ -173,10 +197,39 @@ $(document).ready( function() { $('#globalExcludedIpsSubmit').click( function() { $.ajax( getSetGlobalExcludedIpsAJAX() ); }); + + $('#defaultTimezone').html( getTimezoneSelector(defaultTimezone)); + + $('#defaultTimezoneSubmit').click( function() { + $.ajax( getDefaultTimezoneAJAX() ); + }); $('td.editableSite').click( function(){ $(this).parent().find('.editSite').click(); } ); }); - + +function getTimezoneSelector(selectedTimezone) +{ + var html = '<select id="timezones">'; + if(!selectedTimezone) + { + html += '<option selected="selected" value="'+selectACity+'">'+selectACity+'</option>'; + } + for(var continent in timezones) { + html += '<optgroup label="' + continent + '">'; + for(var timezoneId in timezones[continent]) { + var selected = ''; + if(timezoneId == selectedTimezone) { + selected = ' selected="selected" '; + } + html += '<option ' + selected + ' value="'+ timezoneId + '">' + timezones[continent][timezoneId] + '</option>'; + } + html += "</optgroup>\n"; + } + html += '</select>'; + return html; +} + + function submitSiteOnEnter(e) { var key=e.keyCode || e.which; diff --git a/plugins/SitesManager/templates/SitesManager.tpl b/plugins/SitesManager/templates/SitesManager.tpl index 3331aacd6333c9441673f58bf8fd06921042ec0d..43607ab2264127f3a252c9b152aa7f3997b2c276 100644 --- a/plugins/SitesManager/templates/SitesManager.tpl +++ b/plugins/SitesManager/templates/SitesManager.tpl @@ -9,7 +9,26 @@ {assign var=excludedIpHelp value=$excludedIpHelpPlain|inlineHelp} var excludedIpHelp = '{$excludedIpHelp|escape:javascript}'; var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:javascript}'; +{capture assign=defaultTimezoneHelpPlain} + {if $timezoneSupported} + {'SitesManager_ChooseCityInSameTimezoneAsYou'|translate} + {else} + {'SitesManager_AdvancedTimezoneSupportNotFound'|translate} + {/if} <br /><br />{'SitesManager_UTCTimeIs'|translate:$utcTime} +{/capture} + +{capture assign=timezoneHelpPlain} + {$defaultTimezoneHelpPlain} + <br /><br />{'SitesManager_ChangingYourTimezoneWillOnlyAffectDataForward'|translate} +{/capture} + +var timezoneHelp = '{$timezoneHelpPlain|inlineHelp|escape:javascript}'; +{assign var=defaultTimezoneHelp value=$defaultTimezoneHelpPlain|inlineHelp}; +var timezones = {$timezones}; +var defaultTimezone = '{$defaultTimezone}'; +var selectACity = '{'SitesManager_SelectCity'|translate}'; </script> + <script type="text/javascript" src="plugins/SitesManager/templates/SitesManager.js"></script> {literal} @@ -29,18 +48,22 @@ var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:ja #editSites { valign: top; } +option, select { + font-size:11px; +} </style> {/literal} <h2>{'SitesManager_WebsitesManagement'|translate}</h2> <p>{'SitesManager_MainDescription'|translate} {if $isSuperUser} -<br/>{'SitesManager_SuperUserCanExcludeIpsOnAllWebsites'|translate:"<a href='#globalIpExclusion'>":"</a>"}. +<br/>{'SitesManager_SuperUserCan'|translate:"<a href='#globalIpExclusion'>":"</a>":"<a href='#defaultTimezone'>":"</a>"} {/if} </p> {ajaxErrorDiv} {ajaxLoadingDiv} + {if $adminSites|@count == 0} {'SitesManager_NoWebsites'|translate} {else} @@ -51,6 +74,7 @@ var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:ja <th>{'SitesManager_Name'|translate}</th> <th>{'SitesManager_Urls'|translate}</th> <th>{'SitesManager_ExcludedIps'|translate}</th> + <th>{'SitesManager_Timezone'|translate}</th> <th> </th> <th> </th> <th> {'SitesManager_JsTrackingTag'|translate} </th> @@ -63,6 +87,7 @@ var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:ja <td id="siteName" class="editableSite">{$site.name}</td> <td id="urls" class="editableSite">{foreach from=$site.alias_urls item=url}{$url}<br />{/foreach}</td> <td id="excludedIps" class="editableSite">{foreach from=$site.excluded_ips item=ip}{$ip}<br />{/foreach}</td> + <td id="timezone" class="editableSite">{$site.timezone}</td> <td><img src='plugins/UsersManager/images/edit.png' class="editSite" id="row{$i}" href='#' title="{'General_Edit'|translate}" /></td> <td><img src='plugins/UsersManager/images/remove.png' class="deleteSite" id="row{$i}" title="{'General_Delete'|translate}" value="{'General_Delete'|translate}" /></td> <td><a href='{url action=displayJavascriptCode idsite=$site.idsite}'>{'SitesManager_ShowTrackingTag'|translate}</a></td> @@ -85,6 +110,19 @@ var aliasUrlsHelp = '{'SitesManager_AliasUrlHelp'|translate|inlineHelp|escape:ja {$excludedIpHelp} <input type="hidden" name="token_auth" value="{$token_auth}" /> <p><input type="submit" class="submit" id='globalExcludedIpsSubmit' value="{'General_Save'|translate}" /></p> + + + <a name='defaultTimezone'></a><h2>{'SitesManager_DefaultTimezone'|translate}</h2> + <p>{'SitesManager_SelectDefaultTimezone'|translate} + {ajaxErrorDiv id=ajaxErrorDefaultTimezone} + {ajaxLoadingDiv id=ajaxLoadingDefaultTimezone} + <div id='defaultTimezone'></div> + <br/> + {$defaultTimezoneHelp} + <input type="hidden" name="token_auth" value="{$token_auth}" /> + <p><input type="submit" class="submit" id='defaultTimezoneSubmit' value="{'General_Save'|translate}" /></p> + + {/if} <br /><br /><br /><br /> diff --git a/plugins/SitesManager/tests/SitesManager.test.php b/plugins/SitesManager/tests/SitesManager.test.php index eb090a5c0c4802386700e4f1400a5bb7754d67de..642a6cf970be2a01db098963cec0afd137043941 100644 --- a/plugins/SitesManager/tests/SitesManager.test.php +++ b/plugins/SitesManager/tests/SitesManager.test.php @@ -19,6 +19,9 @@ class Test_Piwik_SitesManager extends Test_Database $pseudoMockAccess = new FakeAccess; FakeAccess::$superUser = true; Zend_Registry::set('access', $pseudoMockAccess); + + // clear static Site cache + Piwik_Site::clearCache(); } /** @@ -107,12 +110,14 @@ class Test_Piwik_SitesManager extends Test_Database /** * Test with valid IPs */ - public function test_addSite_excludedIps_valid() + public function test_addSite_excludedIpsAndtimezone_valid() { - $ips = '1.2.3.4,1.1.1.*,1.2.*.*,1.*.*.*'; - $idsite = Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/", $ips); + $ips = '1.2.3.4,1.1.1.*,1.2.*.*,1.*.*.*'; + $timezone = 'Europe/Paris'; + $idsite = Piwik_SitesManager_API::getInstance()->addSite("name","http://piwik.net/", $ips, $timezone); $siteInfo = Piwik_SitesManager_API::getInstance()->getSiteFromId($idsite); $this->assertEqual($siteInfo['excluded_ips'], $ips); + $this->assertEqual($siteInfo['timezone'], $timezone); } /** @@ -482,8 +487,8 @@ class Test_Piwik_SitesManager extends Test_Database $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => ""), - 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => ""), + 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => "", 'timezone' => 'UTC'), + 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => "", 'timezone' => 'UTC'), ); FakeAccess::setIdSitesAdmin (array(1,3)); @@ -518,8 +523,8 @@ class Test_Piwik_SitesManager extends Test_Database $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => ""), - 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => ""), + 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => "", 'timezone' => 'UTC'), + 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => "", 'timezone' => 'UTC'), ); FakeAccess::setIdSitesView (array(1,3)); @@ -554,8 +559,8 @@ class Test_Piwik_SitesManager extends Test_Database $idsite = Piwik_SitesManager_API::getInstance()->addSite("site3",array("http://piwik.org")); $resultWanted = array( - 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => ""), - 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => ""), + 0 => array("idsite" => 1, "name" => "site1", "main_url" =>"http://piwik.net", "excluded_ips" => "", 'timezone' => 'UTC'), + 1 => array("idsite" => 3, "name" => "site3", "main_url" =>"http://piwik.org", "excluded_ips" => "", 'timezone' => 'UTC'), ); FakeAccess::setIdSitesView (array(1,3)); @@ -686,5 +691,41 @@ class Test_Piwik_SitesManager extends Test_Database $this->assertEqual($allUrls,$newurls); } + + function test_addSites_invalidTimezone() + { + // trying invalid timezones + try { + $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'), '', 'UTC+15'); + $this->fail('invalid timezone should raise an exception'); + } catch(Exception $e) { + } + try { + $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'), '', 'Paris'); + $this->fail('invalid timezone should raise an exception'); + } catch(Exception $e) { + } + $this->pass(); + } + + function test_setDefaultTimezone() + { + $defaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone(); + $this->assertEqual($defaultTimezone, 'UTC'); + + $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'), ''); + $site = new Piwik_Site($idsite); + $this->assertEqual($site->getTimezone(), 'UTC'); + + $newDefaultTimezone = 'UTC+5.5'; + Piwik_SitesManager_API::getInstance()->setDefaultTimezone($newDefaultTimezone); + $defaultTimezone = Piwik_SitesManager_API::getInstance()->getDefaultTimezone(); + $this->assertEqual($defaultTimezone, $newDefaultTimezone); + + $idsite = Piwik_SitesManager_API::getInstance()->addSite("site1",array('http://example.org'), '', $newDefaultTimezone); + $site = new Piwik_Site($idsite); + $this->assertEqual($site->getTimezone(), $newDefaultTimezone); + $this->assertEqual($site->getCreationDate(), date('Y-m-d')); + } } diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php index a96806ae165715a07c52cf8b8a6ca6509cc4b401..94ed2d98c40d128415513f9df614c4afb92fe0eb 100644 --- a/plugins/UsersManager/API.php +++ b/plugins/UsersManager/API.php @@ -17,6 +17,10 @@ class Piwik_UsersManager_API { static private $instance = null; + + /** + * @return Piwik_UsersManager_API + */ static public function getInstance() { if (self::$instance == null) @@ -295,7 +299,8 @@ class Piwik_UsersManager_API 'alias' => $alias, 'email' => $email, 'token_auth' => $token_auth, - ) + 'date_registered' => Piwik_Date::now()->getDatetime() + ) ); // we reload the access list which doesn't yet take in consideration this new user diff --git a/plugins/UsersManager/tests/UsersManager.test.php b/plugins/UsersManager/tests/UsersManager.test.php index 81dc536511cd8a8f365b1b03865194b62e61b70e..d4b802199c9f5d8447cc063fc5d090a558206655 100644 --- a/plugins/UsersManager/tests/UsersManager.test.php +++ b/plugins/UsersManager/tests/UsersManager.test.php @@ -364,11 +364,12 @@ class Test_Piwik_UsersManager extends Test_Database $email = "mgeag4544i@geq.com"; $alias = "her is my alias )(&|\" '£%*(&%+))"; + $time = time(); Piwik_UsersManager_API::getInstance()->addUser($login, $password, $email, $alias); $user = Piwik_UsersManager_API::getInstance()->getUser($login); // check that the date registered is correct - $this->assertTrue( strtotime($user['date_registered']) >= time() - 1 , + $this->assertTrue( strtotime($user['date_registered']) == $time , "the date_registered ".strtotime($user['date_registered'])." is different from the time() ". time()); $this->assertTrue($user['date_registered'] <= time() ); diff --git a/plugins/VisitFrequency/VisitFrequency.php b/plugins/VisitFrequency/VisitFrequency.php index 58d9e45c9fbba62723b6a0f4d40576718a89ae95..16d3126ceed870d1d437ea1b0866d363690fb143 100644 --- a/plugins/VisitFrequency/VisitFrequency.php +++ b/plugins/VisitFrequency/VisitFrequency.php @@ -67,6 +67,7 @@ class Piwik_VisitFrequency extends Piwik_Plugin function archiveDay($notification) { + /* @var $archiveProcessing Piwik_ArchiveProcessing */ $archiveProcessing = $notification->getNotificationObject(); $query = "SELECT count(distinct visitor_idcookie) as nb_uniq_visitors_returning, @@ -77,10 +78,11 @@ class Piwik_VisitFrequency extends Piwik_Plugin sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count_returning, sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted_returning FROM ".$archiveProcessing->logTable." - WHERE visit_server_date = ? + WHERE visit_last_action_time >= ? + AND visit_last_action_time <= ? AND idsite = ? AND visitor_returning = 1"; - $row = $archiveProcessing->db->fetchRow($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ) ); + $row = $archiveProcessing->db->fetchRow($query, array( $archiveProcessing->getStartDatetimeUTC(), $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite ) ); if($row === false || $row === null) { diff --git a/tests/config_test.php b/tests/config_test.php index 44a72ab0f7636e2413ae2dd2ae55add2851ca223..896faf79807943247e240362f65efa70ad94bc24 100644 --- a/tests/config_test.php +++ b/tests/config_test.php @@ -28,8 +28,7 @@ require_once 'simpletest/mock_objects.php'; SimpleTest::prefer(new HtmlReporter()); error_reporting(E_ALL|E_NOTICE); -//@date_default_timezone_set('Europe/London'); -@date_default_timezone_set('America/Toronto'); +@date_default_timezone_set('UTC'); require_once PIWIK_INCLUDE_PATH .'/libs/Zend/Exception.php'; require_once PIWIK_INCLUDE_PATH .'/libs/Zend/Loader.php'; diff --git a/tests/core/ArchiveProcessing.test.php b/tests/core/ArchiveProcessing.test.php new file mode 100644 index 0000000000000000000000000000000000000000..bb7712d42ee62fa386b8e5f888d2870b18819abc --- /dev/null +++ b/tests/core/ArchiveProcessing.test.php @@ -0,0 +1,110 @@ +<?php +if(!defined("PIWIK_PATH_TEST_TO_ROOT")) { + define('PIWIK_PATH_TEST_TO_ROOT', getcwd().'/../..'); +} +if(!defined('PIWIK_CONFIG_TEST_INCLUDED')) +{ + require_once PIWIK_PATH_TEST_TO_ROOT . "/tests/config_test.php"; +} + +require_once "Database.test.php"; +class Test_Piwik_ArchiveProcessing extends Test_Database +{ + public function setUp() + { + parent::setUp(); + + // setup the access layer + $pseudoMockAccess = new FakeAccess; + FakeAccess::$superUser = true; + Zend_Registry::set('access', $pseudoMockAccess); + } + + + public function tearDown() + { + } + + private function createWebsite($timezone = 'UTC') + { + $idsite = Piwik_SitesManager_API::getInstance()->addSite( + "site1", + array("http://piwik.net"), + $excludedIps = "", + $timezone); + return new Piwik_Site($idsite); + } + + private function createArchiveProcessing($periodLabel, $dateLabel, $siteTimezone) + { + $site = $this->createWebsite($siteTimezone); + $date = Piwik_Date::factory($dateLabel); + $period = Piwik_Period::factory($periodLabel, $date); + + $archiveProcessing = Piwik_ArchiveProcessing::factory($periodLabel); + $archiveProcessing->setSite($site); + $archiveProcessing->setPeriod($period); + $archiveProcessing->init(); + return $archiveProcessing; + } + + // test of validity of an archive, for a month not finished + public function test_init_currentMonth() + { + $archiveProcessing = $this->createArchiveProcessing('month', date('Y-m-d'), 'UTC+10'); + + // min finished timestamp considered when looking at archive timestamp + $dateMinArchived = Piwik_Date::today()->setTimezone('UTC+10')->getTimestamp(); + $this->assertEqual($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived); + } + + // test of validity of an archive, for a month in the past + public function test_init_dayInPast() + { + $archiveProcessing = $this->createArchiveProcessing('day', '2010-01-01', 'UTC'); + + // min finished timestamp considered when looking at archive timestamp + $dateMinArchived = Piwik_Date::factory('2010-01-02')->getTimestamp(); + $this->assertEqual($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived); + + $this->assertEqual($archiveProcessing->getStartDatetimeUTC(), '2010-01-01 00:00:00'); + $this->assertEqual($archiveProcessing->getEndDatetimeUTC(), '2010-01-01 23:59:59'); + } + + // test of validity of an archive, for a non UTC date in the past + public function test_init_dayInPast_NonUTCWebsite() + { + $timezone = 'UTC+5.5'; + $archiveProcessing = $this->createArchiveProcessing('day', '2010-01-01', 'UTC+5.5'); + // min finished timestamp considered when looking at archive timestamp + $dateMinArchived = Piwik_Date::factory('2010-01-01 18:30:00'); + $this->assertEqual($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived->getTimestamp()); + + $this->assertEqual($archiveProcessing->getStartDatetimeUTC(), '2009-12-31 18:30:00'); + $this->assertEqual($archiveProcessing->getEndDatetimeUTC(), '2010-01-01 18:29:59'); + + } + + // test of validity of an archive, for today's archive + public function test_init_today() + { + Zend_Registry::get('config')->General->enable_browser_archiving_triggering = true; + + $archiveProcessing = $this->createArchiveProcessing('day', 'today', 'UTC-1'); + + // we look at anything processed in the last time_before_today_archive_considered_outdated seconds + $dateMinArchived = time() - Zend_Registry::get('config')->General->time_before_today_archive_considered_outdated; + $this->assertEqual($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived); + + // when browsers don't trigger archives, we force ArchiveProcessing + // to fetch any of the most recent archive + Zend_Registry::get('config')->General->enable_browser_archiving_triggering = false; + $dateMinArchived = 0; + $this->assertEqual($archiveProcessing->getMinTimeArchivedProcessed(), $dateMinArchived); + + $this->assertEqual($archiveProcessing->getStartDatetimeUTC(), date('Y-m-d').' 01:00:00'); + $this->assertEqual($archiveProcessing->getEndDatetimeUTC(), date('Y-m-d', time()+86400).' 00:59:59'); + } + +} + diff --git a/tests/core/Date.test.php b/tests/core/Date.test.php index 07983e8868912f8e9f3ac581cd24565513239e51..4edf4d0b3c72c9f3da5fe660a92d3bdd9a90f07c 100644 --- a/tests/core/Date.test.php +++ b/tests/core/Date.test.php @@ -28,13 +28,171 @@ class Test_Piwik_Date extends UnitTestCase function test_today() { $date = Piwik_Date::today(); - $this->assertEqual( strtotime(date("Y-m-d "). " 00:00:00"), $date->get()); + $this->assertEqual( strtotime(date("Y-m-d "). " 00:00:00"), $date->getTimestamp()); + + // test getDatetime() + $this->assertEqual( $date->getDatetime(), $date->getDateStartUTC()); + $date = $date->setTime('12:00:00'); + $this->assertEqual( $date->getDatetime(), date('Y-m-d') . ' 12:00:00'); } + //create today object check that timestamp is correct (midnight) function test_yesterday() { $date = Piwik_Date::yesterday(); - $this->assertEqual( strtotime(date("Y-m-d",time()-86400). " 00:00:00"), $date->get()); + $this->assertEqual( strtotime(date("Y-m-d",strtotime('-1day')). " 00:00:00"), $date->getTimestamp()); + } + + function test_setTimezone_dayInUTC() + { + $date = Piwik_Date::factory('2010-01-01'); + + $dayStart = '2010-01-01 00:00:00'; + $dayEnd = '2010-01-01 23:59:59'; + $this->assertEqual($date->getDateStartUTC(), $dayStart); + $this->assertEqual($date->getDateEndUTC(), $dayEnd); + + $date = $date->setTimezone('UTC'); + $this->assertEqual($date->getDateStartUTC(), $dayStart); + $this->assertEqual($date->getDateEndUTC(), $dayEnd); + + $date = $date->setTimezone('Europe/Paris'); + $utcDayStart = '2009-12-31 23:00:00'; + $utcDayEnd = '2010-01-01 22:59:59'; + $this->assertEqual($date->getDateStartUTC(), $utcDayStart); + $this->assertEqual($date->getDateEndUTC(), $utcDayEnd); + + $date = $date->setTimezone('UTC+1'); + $utcDayStart = '2009-12-31 23:00:00'; + $utcDayEnd = '2010-01-01 22:59:59'; + $this->assertEqual($date->getDateStartUTC(), $utcDayStart); + $this->assertEqual($date->getDateEndUTC(), $utcDayEnd); + + $date = $date->setTimezone('UTC-1'); + $utcDayStart = '2010-01-01 01:00:00'; + $utcDayEnd = '2010-01-02 00:59:59'; + $this->assertEqual($date->getDateStartUTC(), $utcDayStart); + $this->assertEqual($date->getDateEndUTC(), $utcDayEnd); + } + + function test_modifyDate_withTimezone() + { + $date = Piwik_Date::factory('2010-01-01'); + $date = $date->setTimezone('UTC-1'); + + $timestamp = $date->getTimestamp(); + $date = $date->addHour(0)->addHour(0)->addHour(0); + $this->assertEqual($timestamp, $date->getTimestamp()); + + + $date = Piwik_Date::factory('2010-01-01')->setTimezone('Europe/Paris'); + $dateExpected = clone $date; + $date = $date->addHour(2); + $dateExpected = $dateExpected->addHour(1)->addHour(1)->addHour(1)->subHour(1); + $this->assertEqual($date->getTimestamp(), $dateExpected->getTimestamp()); } + + function test_getDateStartUTCEnd_DuringDstTimezone() + { + $date = Piwik_Date::factory('2010-03-28'); + $parisDayStart = '2010-03-28 00:00:00'; + $parisDayEnd = '2010-03-28 23:59:59'; + + $date = $date->setTimezone('Europe/Paris'); + $utcDayStart = '2010-03-27 23:00:00'; + $utcDayEnd = '2010-03-28 21:59:59'; + + $this->assertEqual($date->getDateStartUTC(), $utcDayStart); + $this->assertEqual($date->getDateEndUTC(), $utcDayEnd); + } + /* + function _test_setTimezone_standard() + { + $today = Piwik_Date::today(); + $date = Piwik_Date::today(); + + // UTC == GMT + $this->assertEqual( $date->getTimestamp(), $today->getTimestamp() ); + $date->setTimezone('GMT'); + $this->assertEqual( $date->getTimestamp(), $today->getTimestamp() ); + + // Setting the timezone twice + for($i = 0 ; $i < 2; $i++) + { + $date->setTimezone('Etc/GMT+1'); + var_dump(date('Y-m-d H:i:s', $today->getTimestamp())); + var_dump(date('Y-m-d H:i:s', $date->getTimestamp())); + $this->assertEqual( $date->getTimestamp(), $today->getTimestamp() + 3600); + } + + // Setting GMT minus + $date->setTimezone('Etc/GMT-1'); + $this->assertEqual( $date->getTimestamp(), $today->addHour(1)->getTimestamp()); + } + + + function _test_setTimezone_DuringDst() + { + // create a date before DST change + $date = Piwik_Date::factory('2010-03-28'); + // set timezone to Paris, that we know apply DST change on + $date->setTimezone('Europe/Paris'); + $this->assertEqual($date->getTimestamp(), Piwik_Date::factory('2010-03-28 01:00:00')->getTimestamp()); + + // add 3 hours + $date = $date->addHour(3); + + // test that we added 4 hours as expected, after jumping the DST hour + // see http://www.timeanddate.com/worldclock/converted.html?day=28&month=3&year=2010&hour=4&min=0&sec=0&p1=0&p2=195 + $expectedDate = Piwik_Date::factory('2010-03-28 04:00:00')->setTimezone('Europe/Paris'); + + + $this->assertEqual($date->getTimestamp(), $expectedDate->getTimestamp()); + + // the date is 6AM UTC (Paris is now in UTC+2) + $this->assertEqual($date->getTimestamp(), Piwik_Date::factory('2010-03-28 06:00:00')->getTimestamp()); + + // now testing a non DST day (the day before the change) + $date = Piwik_Date::factory('2010-03-27 00:00:00')->addHour(3)->setTimezone('Europe/Paris'); + $expectedDate = Piwik_Date::factory('2010-03-27 03:00:00')->setTimezone('Europe/Paris'); + $this->assertEqual($date->getTimestamp(), $expectedDate->getTimestamp()); + + // testing the same with the addHour after the timezone conversion + $date = Piwik_Date::factory('2010-03-27 00:00:00') + ->setTimezone('Europe/Paris') + ->addHour(1) + ->setTimezone('Europe/Paris') + ; + $expectedDate = Piwik_Date::factory('2010-03-27 03:00:00')->setTimezone('Europe/Paris'); + echo $date; echo "<br>"; + echo $expectedDate;echo "<br>"; + $this->assertEqual($date->getTimestamp(), $expectedDate->getTimestamp()); + + $date = Piwik_Date::factory('2010-03-27 00:00:00') + ->setTimezone('Europe/Paris') + ->addHour(1)->addHour(1)->addHour(1); + $expectedDate = Piwik_Date::factory('2010-03-27 00:00:00') + ->setTimezone('Europe/Paris') + ->addHour(3); + $this->assertEqual($date->getTimestamp(), $expectedDate->getTimestamp()); + + } + + // convert the same date object to multiple time zones + function _test_setTimezone_multiple() + { + $dateString = '2010-03-01 00:00:00'; + $date = Piwik_Date::factory($dateString); + $date->setTimezone('Europe/Paris'); + $date->setTimezone('Etc/GMT+7'); + + $expectedDate = Piwik_Date::factory($dateString)->setTimezone('Etc/GMT+7'); + $this->assertEqual($date->getTimestamp(), $expectedDate->getTimestamp()); + + $date = $date->setTimezone('UTC')->addDay(1); + $expectedDate = Piwik_Date::factory('2010-03-02 00:00:00'); + $this->assertEqual($date->setTimezone('UTC')->getTimestamp(), $expectedDate->getTimestamp()); + }*/ + } diff --git a/tests/core/Period.test.php b/tests/core/Period.test.php index 6f51fb79003ea3673f218e806e135b886da979ec..24f361ac4206e4f97288d1b07e1ac4ec75e19ff4 100644 --- a/tests/core/Period.test.php +++ b/tests/core/Period.test.php @@ -55,7 +55,6 @@ class Test_Piwik_Period extends UnitTestCase function test_day_isFinished_today() { $period = new Piwik_Period_Day( Piwik_Date::today()); - $this->assertEqual( $period->isFinished(), false); $this->assertEqual( $period->toString(), date("Y-m-d")); $this->assertEqual( $period->getSubperiods(), array()); $this->assertEqual( $period->getNumberOfSubperiods(), 0); @@ -65,7 +64,6 @@ class Test_Piwik_Period extends UnitTestCase { $period = new Piwik_Period_Day( Piwik_Date::yesterday()); - $this->assertEqual( $period->isFinished(), true); $this->assertEqual( $period->toString(), date("Y-m-d", time()-86400)); $this->assertEqual( $period->getSubperiods(), array()); $this->assertEqual( $period->getNumberOfSubperiods(), 0); @@ -75,7 +73,6 @@ class Test_Piwik_Period extends UnitTestCase function test_day_isFinished_tomorrow() { $period = new Piwik_Period_Day( Piwik_Date::factory(date("Y-m-d",time()+86400))); - $this->assertEqual( $period->isFinished(), false); $this->assertEqual( $period->toString(), date("Y-m-d", time()+86400)); $this->assertEqual( $period->getSubperiods(), array()); $this->assertEqual( $period->getNumberOfSubperiods(), 0); @@ -85,7 +82,6 @@ class Test_Piwik_Period extends UnitTestCase function test_day_isFinished_31stfeb() { $period = new Piwik_Period_Day( Piwik_Date::factory("2007-02-31")); - $this->assertEqual( $period->isFinished(), true); $this->assertEqual( $period->toString(), "2007-03-03"); $this->assertEqual( $period->getSubperiods(), array()); $this->assertEqual( $period->getNumberOfSubperiods(), 0); @@ -224,7 +220,6 @@ class Test_Piwik_Period extends UnitTestCase "2006-12-31",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 31); - $this->assertEqual( $month->isFinished(), true); } // testing month feb leap year function test_month_FebLeap() @@ -262,7 +257,6 @@ class Test_Piwik_Period extends UnitTestCase "2024-02-29",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 29); - $this->assertEqual( $month->isFinished(), false); } // testing month feb non-leap year function test_month_FebNonLeap() @@ -299,7 +293,6 @@ class Test_Piwik_Period extends UnitTestCase "2023-02-28",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 28); - $this->assertEqual( $month->isFinished(), false); } // testing jan function test_month_Jan() @@ -339,7 +332,6 @@ class Test_Piwik_Period extends UnitTestCase "2007-01-31",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 31); - $this->assertEqual( $month->isFinished(), true); } // testing month containing a time change (DST) @@ -380,7 +372,6 @@ class Test_Piwik_Period extends UnitTestCase "2007-03-31",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 31); - $this->assertEqual( $month->isFinished(), true); } function test_month_DSTChangeOct() { @@ -419,7 +410,6 @@ class Test_Piwik_Period extends UnitTestCase "2017-10-31",); $this->assertEqual( $month->toString(), $correct); $this->assertEqual( $month->getNumberOfSubperiods(), 31); - $this->assertEqual( $month->isFinished(), false); } /** * Testing Period_Week @@ -446,7 +436,6 @@ class Test_Piwik_Period extends UnitTestCase "2006-01-01",); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), true); } // test week between 2 months Week Mai 29 To Mai 31 2006 function test_week_Between2month() @@ -462,7 +451,6 @@ class Test_Piwik_Period extends UnitTestCase "2006-06-04",); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), true); } // test week between feb and march for leap year function test_week_febLeapyear() @@ -479,11 +467,9 @@ class Test_Piwik_Period extends UnitTestCase $week = new Piwik_Period_Week( Piwik_Date::factory('2023-02-27')); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), false); $week = new Piwik_Period_Week( Piwik_Date::factory('2023-03-01')); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), false); } // test week between feb and march for no leap year function test_week_febnonLeapyear() @@ -500,11 +486,9 @@ class Test_Piwik_Period extends UnitTestCase $week = new Piwik_Period_Week( Piwik_Date::factory('2024-02-27')); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), false); $week = new Piwik_Period_Week( Piwik_Date::factory('2024-03-01')); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), false); } // test week normal middle of the month function test_week_middleofmonth() @@ -521,7 +505,6 @@ class Test_Piwik_Period extends UnitTestCase $week = new Piwik_Period_Week( Piwik_Date::factory('2024-10-09')); $this->assertEqual( $week->toString(), $correct); $this->assertEqual( $week->getNumberOfSubperiods(), 7); - $this->assertEqual( $week->isFinished(), false); } /** @@ -548,9 +531,7 @@ class Test_Piwik_Period extends UnitTestCase $year = new Piwik_Period_Year( Piwik_Date::factory('2024-10-09')); $this->assertEqual( $year->getNumberOfSubperiods(), 12); - $this->assertEqual( $year->isFinished(), false); $this->assertEqual( $year->toString(), $correct); - } // test past @@ -573,7 +554,6 @@ class Test_Piwik_Period extends UnitTestCase $week = new Piwik_Period_Year( Piwik_Date::factory('2000-02-15')); $this->assertEqual( $week->getNumberOfSubperiods(), 12); - $this->assertEqual( $week->isFinished(), true); $this->assertEqual( $week->toString(), $correct); } @@ -591,7 +571,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 1); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } @@ -609,7 +588,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 2); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } // test range 3 @@ -627,7 +605,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 50); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } // test range 4 @@ -645,7 +622,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 3); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } @@ -662,7 +638,6 @@ class Test_Piwik_Period extends UnitTestCase ); $this->assertEqual( $range->getNumberOfSubperiods(), count($correct)); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } @@ -689,7 +664,6 @@ class Test_Piwik_Period extends UnitTestCase ); $this->assertEqual( $range->getNumberOfSubperiods(), count($correct)); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } // test range date1,date2 @@ -730,7 +704,6 @@ class Test_Piwik_Period extends UnitTestCase ); $this->assertEqual( $range->getNumberOfSubperiods(), count($correct)); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } // test range date1,date2 @@ -771,7 +744,6 @@ class Test_Piwik_Period extends UnitTestCase ), ); $this->assertEqual( $range->getNumberOfSubperiods(), count($correct)); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } // test range date1,date2 @@ -851,7 +823,6 @@ class Test_Piwik_Period extends UnitTestCase ); $this->assertEqual( $range->getNumberOfSubperiods(), count($correct)); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } @@ -874,7 +845,6 @@ class Test_Piwik_Period extends UnitTestCase $this->assertEqual( $range->getNumberOfSubperiods(), 50); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } @@ -884,7 +854,6 @@ class Test_Piwik_Period extends UnitTestCase $range = new Piwik_Period_Range( 'week', 'last1' ); $currentWeek = new Piwik_Period_Week(Piwik_Date::today()); $this->assertEqual( $range->getNumberOfSubperiods(), 1); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), array($currentWeek->toString())); } @@ -906,7 +875,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 20); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } @@ -916,7 +884,6 @@ class Test_Piwik_Period extends UnitTestCase $range = new Piwik_Period_Range( 'month', 'last1' ); $month = new Piwik_Period_Month(Piwik_Date::today()); $this->assertEqual( $range->getNumberOfSubperiods(), 1); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), array($month->toString())); } @@ -940,7 +907,6 @@ class Test_Piwik_Period extends UnitTestCase $this->assertEqual( $range->getNumberOfSubperiods(), 10); - $this->assertEqual( $range->isFinished(), true); $this->assertEqual( $range->toString(), $correct); } @@ -962,7 +928,6 @@ class Test_Piwik_Period extends UnitTestCase $correct = array_reverse($correct); $this->assertEqual( $range->getNumberOfSubperiods(), 10); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), $correct); } @@ -973,7 +938,6 @@ class Test_Piwik_Period extends UnitTestCase $range = new Piwik_Period_Range( 'year', 'last1' ); $currentYear = new Piwik_Period_Year(Piwik_Date::today()); $this->assertEqual( $range->getNumberOfSubperiods(), 1); - $this->assertEqual( $range->isFinished(), false); $this->assertEqual( $range->toString(), array($currentYear->toString())); } } diff --git a/themes/default/common.css b/themes/default/common.css index 11772ba780595a5152bffc86bfc6d2edb451cf29..489cea3b53bad830ffe26da41d51f47a869febbe 100644 --- a/themes/default/common.css +++ b/themes/default/common.css @@ -190,7 +190,7 @@ input.submit { } /* extending the jquery UI css for inline help */ -.ajaxError .ui-widget { +.ui-widget { text-align:justify; - width:300px; } + diff --git a/themes/default/genericForm.tpl b/themes/default/genericForm.tpl index f8f00d39cbbc3a18f9eb285628d13b77434ec04a..fccbc45d60867fd7429cc12d2e5d46d3f55b746c 100644 --- a/themes/default/genericForm.tpl +++ b/themes/default/genericForm.tpl @@ -2,7 +2,7 @@ {if $form_data.errors} <div class="warning"> <img src="themes/default/images/warning_medium.png"> - <strong>Please fix the following errors:</strong> + <strong>{'Installation_PleaseFixTheFollowingErrors'|translate}:</strong> <ul> {foreach from=$form_data.errors item=data} <li>{$data}</li>