<?php
// tòmò web analytics
// Copyright since 2011 by Yves Goergen
// Website: http://unclassified.de/go/tomo
// Licence: GNU General Public License (GPL) Version 3
// Encoding: UTF-8 (without BOM) (auto-detect: °°°°°)
class UnbDatabase
{
private $displayQueryErrors = false;
private $displayQueryInfo = false;
private static $instance = null;
public $traceSql = false;
private $connectData = null;
private $connection = null;
private $driverName = '';
private $tablePrefix = '';
private $transactionLevel = 0;
private $databaseLocked = false;
private $queryCount = 0;
private $totalTime = 0.0;
private $rowsAffected = 0;
private $constraintError = false;
private $busyError = false;
private $errorMessage = null;
private $connectionId = 0;
private $lastAutoValue = null;
private function __construct()
{
}
public static function GetInstance()
{
if (!isset(self::$instance))
{
self::$instance = new self();
}
return self::$instance;
}
public static function GetNewInstance()
{
return new self();
}
public function SetDisplayQueryErrors($v)
{
$this->displayQueryErrors = $v;
}
public function IsOpen()
{
return isset($this->connection);
}
public function GetDriverName()
{
return $this->driverName;
}
public function GetStatistics()
{
return array(
'queryCount' => $this->queryCount,
'totalTime' => $this->totalTime);
}
public function Open($connectData = null)
{
if (isset($connectData))
{
if (!is_array($connectData))
throw new InvalidArgumentException('Invalid argument type: $connectData');
}
else
{
$entries = UnbConfiguration::Get('database.*');
$connectData = array();
foreach ($entries as $name => $data)
{
$connectData[substr($name, 9)] = $data;
}
}
$this->connectData = $connectData;
if ($connectData['table-prefix'] != null)
$this->tablePrefix = $connectData['table-prefix'];
switch ($this->connectData['driver'])
{
case 'mysql':
$dsn = 'mysql:dbname=' . $this->connectData['dbname'] .
(isset($this->connectData['hostname']) ? ';host=' . $this->connectData['hostname'] : '') .
(isset($this->connectData['port']) ? ';port=' . $this->connectData['port'] : '') .
(isset($this->connectData['unix-socket']) ? ';unix_socket=' . $this->connectData['unix-socket'] : '');
break;
case 'odbc':
$dsn = 'odbc:' . $this->connectData['dsn'];
break;
case 'pgsql':
$dsn = 'pgsql:host=' . $this->connectData['hostname'] .
(isset($this->connectData['port']) ? ';port=' . $this->connectData['port'] : '') .
';dbname=' . $this->connectData['dbname'];
break;
case 'sqlite':
if (!file_exists(__DIR__ . '/' . $this->connectData['filename']))
throw new UnbDatabaseException('[UNB#37] Database file does not exist');
if (!filesize(__DIR__ . '/' . $this->connectData['filename']))
throw new UnbDatabaseException('[UNB#37] Database file is empty (0 bytes long)');
$dsn = 'sqlite:' . __DIR__ . '/' . $this->connectData['filename'];
break;
default:
throw new InvalidArgumentException('Unsupported database driver name. Check your configuration.');
}
$this->driverName = $this->connectData['driver'];
$parameters = array();
if ($connectData['persistent'])
$parameters[PDO::ATTR_PERSISTENT] = true;
$this->connection = new PDO($dsn, $this->connectData['username'], $this->connectData['password'], $parameters);
switch ($this->driverName)
{
case 'mysql':
$minVersion = '5.0.15';
$version = $this->QueryValue('SELECT VERSION()');
if (version_compare($version, $minVersion) < 0)
throw new Exception('Unsupported MySQL server version ' . $version . '; version ' . $minVersion . ' or newer is required.');
$this->connection->exec('SET sql_mode = \'ANSI\'');
$this->connection->exec('SET storage_engine = \'InnoDB\'');
$this->connection->exec('SET CHARACTER SET \'utf8\'');
$this->connection->exec('SET NAMES \'utf8\'');
$this->connectionId = $this->QueryValue('SELECT CONNECTION_ID()');
break;
case 'pgsql':
if (isset($this->connectData['schema']))
$this->connection->exec('SET search_path TO ' . $this->connection->quote($this->connectData['schema']));
break;
case 'sqlite':
$minVersion = '3.6.19';
$version = $this->QueryValue('SELECT sqlite_version()');
if (version_compare($version, $minVersion) < 0)
throw new Exception('Unsupported SQLite library version ' . $version . '; version ' . $minVersion . ' or newer is required.');
if (isset($this->connectData['synchronous']))
$this->connection->exec('PRAGMA synchronous = ' . $this->connectData['synchronous']);
$this->connection->exec('PRAGMA foreign_keys = ON');
$this->connection->sqliteCreateFunction('LOWER', 'mb_strtolower', 1);
$this->connection->sqliteCreateFunction('EXTRACTVALUE', array(__CLASS__, 'SQLite_ExtractValue'), 2);
$this->connection->sqliteCreateFunction('LEVEL', array(__CLASS__, 'SQLite_Level'), 2);
$this->connection->sqliteCreateFunction('BROWSERNAME', array(__CLASS__, 'SQLite_BrowserName'), 1);
$this->connection->sqliteCreateFunction('BROWSERVERSION', array(__CLASS__, 'SQLite_BrowserVersion'), 1);
$this->connection->sqliteCreateFunction('OSNAME', array(__CLASS__, 'SQLite_OsName'), 1);
$this->connection->sqliteCreateFunction('OSVERSION', array(__CLASS__, 'SQLite_OsVersion'), 1);
break;
}
$this->queryCount = 0;
$this->totalTime = 0;
}
public function Close()
{
$this->connection = null;
}
public static function SQLite_ExtractValue($xml, $xpath)
{
$xml = new SimpleXMLElement($xml);
$data = $xml->xpath($xpath);
if (is_array($data) && count($data) > 0)
return $data[0];
return '';
}
public static function SQLite_Level($num, $levelsStr)
{
if (!isset($num)) return null;
$levels = array_map('intval', explode(',', $levelsStr));
$li = count($levels) - 1;
if ($num < $levels[0])
{
return $levels[0];
}
if ($num >= $levels[$li])
{
return $levels[$li];
}
for ($i = 0; $i < $li; $i++)
{
if ($num >= $levels[$i] && $num < $levels[$i + 1])
{
$mid = ($levels[$i] + $levels[$i + 1]) / 2;
if ($num < $mid)
{
return $levels[$i];
}
else
{
return $levels[$i + 1];
}
}
}
return null;
}
public static function SQLite_BrowserName($browser)
{
if (!isset($browser)) return null;
if (preg_match('_^webkit/chrome_', $browser)) return 'Chrome';
else if (preg_match('_^galeon_', $browser)) return 'Galeon';
else if (preg_match('_^gecko/(ff|fennec)_', $browser)) return 'Firefox';
else if (preg_match('_^gecko/nn_', $browser)) return 'Netscape';
else if (preg_match('_^gecko/seamonkey_', $browser)) return 'SeaMonkey';
else if (preg_match('_^gecko_', $browser)) return 'Gecko-based';
else if (preg_match('_^ie/_', $browser)) return 'Internet Explorer';
else if (preg_match('_^opera/_', $browser)) return 'Opera';
else if (preg_match('_^webkit/safari_', $browser)) return 'Safari';
else if (preg_match('_^webkit/omniweb_', $browser)) return 'Omniweb';
else if (preg_match('_^webkit_', $browser)) return 'Webkit-based';
else if (preg_match('_^kindle_', $browser)) return 'Kindle';
else if (preg_match('_^text_', $browser)) return 'Text browser';
return null;
}
public static function SQLite_BrowserVersion($browser)
{
if (!isset($browser)) return null;
if (preg_match('_^webkit/chrome/(?:chromium/|frame/)?(\d+)_', $browser, $m)) return $m[1];
else if (preg_match('_^gecko/ff/(3\.\d+|[4-9]|1\d)_', $browser, $m)) return $m[1];
else if (preg_match('_^ie/ie/(\d+)_', $browser, $m)) return $m[1];
else if (preg_match('_^opera/opera/(\d+(\.\d)?)_', $browser, $m)) return $m[1];
else if (preg_match('_^webkit/safari/(\d+)_', $browser, $m)) return $m[1];
return null;
}
public static function SQLite_OsName($os)
{
if (!isset($os)) return null;
if (preg_match('_^win/phone_', $os)) return 'Windows Phone';
else if (preg_match('_^win/mobile_', $os)) return 'Windows Mobile';
else if (preg_match('_^win/ce_', $os)) return 'Windows CE';
else if (preg_match('_^win_', $os)) return 'Windows';
else if (preg_match('_^unix/linux/android_', $os)) return 'Android';
else if (preg_match('_^unix/linux_', $os)) return 'Linux';
else if (preg_match('_^unix/freebsd_', $os)) return 'FreeBSD';
else if (preg_match('_^mac/i(pad|phone)_', $os)) return 'iOS';
else if (preg_match('_^mac/x_', $os)) return 'OSX';
else if (preg_match('_^blackberry_', $os)) return 'Blackberry';
else if (preg_match('_^symbian_', $os)) return 'Symbian';
else if (preg_match('_^unix/sun_', $os)) return 'Sun OS';
else if (preg_match('_^os2_', $os)) return 'OS/2';
else if (preg_match('_^java/me_', $os)) return 'Java ME';
else if (preg_match('_^webos_', $os)) return 'webOS';
return null;
}
public static function SQLite_OsVersion($os)
{
if (!isset($os)) return null;
if (preg_match('_^win/(phone|mobile)/([0-9.]+)_', $os, $m)) return $m[2];
else if (preg_match('_^win/(95|98|me)_', $os, $m)) return strtoupper($m[1]);
else if (preg_match('_^win/nt/4_', $os, $m)) return 'NT 4';
else if (preg_match('_^win/nt/(2000|xp|2003|7|8|10)_', $os, $m)) return strtoupper($m[1]);
else if (preg_match('_^win/nt/vista_', $os, $m)) return 'Vista';
else if (preg_match('_^unix/linux/android/([0-9.]+)_', $os, $m)) return $m[1];
else if (preg_match('_^mac/i(pad|phone)/([0-9.]+)_', $os, $m)) return $m[2];
else if (preg_match('_^mac/x/([0-9.]+)_', $os, $m)) return $m[1];
return null;
}
private function ShowQueryInfo($ps, $r, $parameters, $time)
{
if (!$r || $this->displayQueryInfo)
{
echo '<div class="sql-info" onclick="alert(&quot;' . htmlspecialchars(str_replace("\n", '\n', addslashes(UnbDebug::GetCallTrace(1)))) . '&quot;)"><b>SQL execute:</b> ';
if ($ps instanceof PDOStatement)
{
$sql = htmlspecialchars($ps->queryString, ENT_NOQUOTES);
}
else if ($ps instanceof PDO)
{
$sql = htmlspecialchars($parameters, ENT_NOQUOTES);
$parameters = null;
}
if ($parameters)
{
foreach ($parameters as $p)
{
if (!isset($p))
$p = 'NULL';
else if (is_bool($p))
$p = $p ? '1' : '0';
else if (!is_int($p))
$p = '\'' . htmlspecialchars(mb_substr($p, 0, 30), ENT_NOQUOTES) . '\'';
$sql = preg_replace('_\?_', '<i>' . $p . '</i>', $sql, 1);
}
}
echo $sql;
if ($time >= 0)
{
echo ' &ndash; ' . ($time >= 0.005 ? '<b>' : '') . round($time * 1000, 1) . 'ms' . ($time >= 0.005 ? '</b>' : '');
}
echo '</div>';
}
if (!$r)
{
$ei = $ps->errorInfo();
echo '<div class="sql-error"><b>SQL error:</b> [SQLSTATE:' . $ei[0] . ', ' . $ei[1] . '] ' . htmlspecialchars($ei[2], ENT_NOQUOTES) . '</div>';
}
}
private function DetermineErrorCondition($ps)
{
$this->constraintError = false;
$this->busyError = false;
$ei = $ps->errorInfo();
if (substr($ei[0], 0, 2) == '23')
$this->constraintError = true;
switch ($this->driverName)
{
case 'sqlite':
if ($ei[1] == 5)
{
$this->busyError = true;
usleep(mt_rand(2000, 20000));
}
else if ($ei[1] == 19)
{
$this->constraintError = true;
}
break;
}
$this->errorMessage = $ei[2];
}
public function IsConstraintError()
{
return $this->constraintError;
}
public function GetErrorMessage()
{
return $this->errorMessage;
}
public function Optimise($extensive = false)
{
$r = true;
switch ($this->driverName)
{
case 'mysql':
if ($extensive)
{
$r &= $this->Query('OPTIMIZE TABLE "#action", "#session", "#text", "#visitor"');
}
else
{
$r &= $this->Query('ANALYZE TABLE "#action", "#session", "#text", "#visitor"');
}
break;
case 'pgsql':
if ($extensive)
{
$r &= $this->Execute('VACUUM FULL');
$r &= $this->Execute('REINDEX DATABASE ?', $this->connectData['database']);
}
else
{
$r &= $this->Execute('VACUUM');
$r &= $this->Execute('ANALYZE');
}
break;
case 'sqlite':
if ($extensive)
{
$r &= $this->Execute('VACUUM');
$r &= $this->Execute('ANALYZE');
}
else
{
$r &= $this->Execute('ANALYZE');
}
break;
}
return $r;
}
public function StartTransaction($lock = false)
{
if (!isset($this->connection))
throw new BadMethodCallException('Database not opened');
if ($lock && $this->transactionLevel > 0 && !$this->databaseLocked)
throw new BadMethodCallException('You cannot lock during a transaction that has no lock.');
$r = true;
if ($this->transactionLevel == 0)
{
if ($this->traceSql) UnbDebug::Trace('  SQL: BEGIN TRANSACTION');
$start = microtime(true);
$retries = 500;
do
{
if ($lock)
{
switch ($this->driverName)
{
case 'sqlite':
ignore_user_abort(true);
$ps = $this->connection->prepare('BEGIN EXCLUSIVE TRANSACTION');
$r &= $ps->execute();
break;
default:
$r &= $this->connection->beginTransaction();
break;
}
$this->databaseLocked = true;
}
else
{
$r &= $this->connection->beginTransaction();
}
if (!$r) $this->DetermineErrorCondition($this->connection);
$retries--;
}
while (!$r && $this->busyError && $retries > 0);
if (!$r && $this->busyError)
throw new UnbDatabaseException('Database is busy too long. Failed starting transaction.');
$time = microtime(true) - $start;
$this->totalTime += $time;
if ($this->displayQueryErrors)
$this->ShowQueryInfo($this->connection, $r, 'START TRANSACTION', $time);
}
else if ($this->displayQueryInfo)
{
echo '<div class="sql-info" onclick="alert(&quot;' . htmlspecialchars(str_replace("\n", '\n', addslashes(UnbDebug::GetCallTrace()))) . '&quot;)"><b>UnbDatabase operation:</b> Called StartTransaction, changed transaction level from ' . $this->transactionLevel . ' to ' . ($this->transactionLevel + 1) . '.</div>';
}
$this->transactionLevel++;
return $r;
}
public function RollBack()
{
if (!isset($this->connection))
throw new BadMethodCallException('Database not opened');
if ($this->transactionLevel == 0)
throw new BadMethodCallException('No transaction is active');
$r = true;
$this->transactionLevel--;
if ($this->transactionLevel == 0)
{
if ($this->traceSql) UnbDebug::Trace('  SQL: ROLLBACK');
$start = microtime(true);
if ($this->databaseLocked)
{
switch ($this->driverName)
{
case 'sqlite':
$ps = $this->connection->prepare('ROLLBACK');
$r &= $ps->execute();
ignore_user_abort(false);
break;
default:
$r &= $this->connection->rollBack();
break;
}
}
else
{
$r &= $this->connection->rollBack();
}
$time = microtime(true) - $start;
$this->totalTime += $time;
if ($this->displayQueryErrors)
$this->ShowQueryInfo($this->connection, $r, 'ROLLBACK', $time);
$this->databaseLocked = false;
}
else if ($this->displayQueryInfo)
{
echo '<div class="sql-info" onclick="alert(&quot;' . htmlspecialchars(str_replace("\n", '\n', addslashes(UnbDebug::GetCallTrace()))) . '&quot;)"><b>UnbDatabase operation:</b> Called Rollback, changed transaction level from ' . ($this->transactionLevel + 1) . ' to ' . $this->transactionLevel . '.</div>';
}
return $r;
}
public function Commit()
{
if (!isset($this->connection))
throw new BadMethodCallException('Database not opened');
if ($this->transactionLevel == 0)
throw new BadMethodCallException('No transaction is active');
$r = true;
$this->transactionLevel--;
if ($this->transactionLevel == 0)
{
if ($this->traceSql) UnbDebug::Trace('  SQL: COMMIT');
$start = microtime(true);
if ($this->databaseLocked)
{
switch ($this->driverName)
{
case 'sqlite':
$ps = $this->connection->prepare('COMMIT');
$r &= $ps->execute();
if ($r) ignore_user_abort(false);
break;
default:
$r &= $this->connection->commit();
break;
}
}
else
{
$r &= $this->connection->commit();
}
$time = microtime(true) - $start;
$this->totalTime += $time;
if (!$r) $this->DetermineErrorCondition($this->connection);
if ($this->displayQueryErrors)
$this->ShowQueryInfo($this->connection, $r, 'COMMIT', $time);
$this->databaseLocked = false;
}
else if ($this->displayQueryInfo)
{
echo '<div class="sql-info" onclick="alert(&quot;' . htmlspecialchars(str_replace("\n", '\n', addslashes(UnbDebug::GetCallTrace()))) . '&quot;)"><b>UnbDatabase operation:</b> Called Commit, changed transaction level from ' . ($this->transactionLevel + 1) . ' to ' . $this->transactionLevel . '.</div>';
}
return $r;
}
private function GetSequenceValue($table)
{
switch ($this->driverName)
{
case 'pgsql':
return $this->QueryValue('nextval(\'' . $this->tablePrefix . $table . '_seq\')');
}
return null;
}
private function ResetLastAutoValue()
{
$this->lastAutoValue = null;
}
private function FetchLastAutoValue()
{
switch ($this->driverName)
{
case 'mysql':
case 'sqlite':
$this->lastAutoValue = intval($this->connection->lastInsertId());
break;
}
}
public function GetLastAutoValue()
{
return $this->lastAutoValue;
}
private function ProcessSql($sql)
{
switch ($this->driverName)
{
case 'sqlite':
if (preg_match('_^\s*SELECT\s+.+(\s+FOR\s+UPDATE)$_is', $sql, $m))
{
if (!$this->databaseLocked && $this->displayQueryErrors)
{
echo '<div style="color: gray; font-size: 0.85em;"><b>SQL execute:</b> ' . htmlspecialchars($sql) . '</div>';
echo '<div style="color: red; font-size: 0.85em;"><b>SQL warning:</b> A SELECT ... FOR UPDATE query was issued but the database was not locked for the transaction.</div>';
}
$sql = substr($sql, 0, -strlen($m[1]));
}
break;
}
return preg_replace('_\s*--\s.*?$_m', '',
preg_replace('_(?<=")#(?=[a-zA-Z])_', $this->tablePrefix, $sql));
}
public function Prepare($sql)
{
if (!is_string($sql))
throw new InvalidArgumentException('Invalid argument type: $sql');
if (!isset($this->connection))
throw new BadMethodCallException('Database not opened');
$sql = $this->ProcessSql($sql);
$ps = $this->connection->prepare($sql);
if (!$ps)
{
$this->DetermineErrorCondition($this->connection);
$this->ShowQueryInfo($this->connection, false, $sql, 0);
throw new UnbDatabaseException('Statement could not be prepared. ' . $this->errorMessage);
}
return $ps;
}
private function InternalExecute($sql, $parameters)
{
if (!isset($this->connection))
throw new BadMethodCallException('Database not opened');
$start = microtime(true);
if (!is_array($parameters) && isset($parameters))
{
$parameters = array($parameters);
}
if (is_string($sql))
{
if (count($parameters))
{
$pos = 0;
while (($pos = strpos($sql, '?', $pos)) !== false)
{
if (!count($parameters))
{
throw new InvalidArgumentException('Too few parameters specified for query.');
}
$tmp = substr($sql, 0, $pos);
$param = array_shift($parameters);
if (!isset($param))
{
$tmp .= 'NULL';
}
else if ($param instanceof UnbDbAutoValue)
{
$hasAutoValue = true;
$tmp .= 'NULL';
}
else
{
$tmp .= $this->Quote($param);
}
$sql = $tmp . substr($sql, $pos + 1);
$pos = strlen($tmp);
}
if (count($parameters))
{
throw new InvalidArgumentException('Too many parameters specified for query (' . count($parameters) . ' too many).');
}
}
$ps = $this->Prepare($sql);
}
else if ($sql instanceof PDOStatement)
{
$ps = $sql;
$sql = $ps->queryString;
}
else
{
throw new InvalidArgumentException('Invalid argument type: $sql');
}
$this->ResetLastAutoValue();
if ($this->traceSql) UnbDebug::Trace('  SQL: ' . str_replace("\n", "\n" . str_repeat(' ', 44), $sql));
$retries = 500;
do
{
for ($i = 0; $i < count($parameters); $i++)
{
$v = $parameters[$i];
if (!isset($v)) $type = PDO::PARAM_NULL;
else if (is_int($v)) $type = PDO::PARAM_INT;
else $type = PDO::PARAM_STR;
$ps->bindParam($i + 1, $v, $type);
}
$r = $ps->execute();
if (!$r) $this->DetermineErrorCondition($ps);
$retries--;
}
while (!$r && $this->busyError && $retries > 0);
if (!$r && $this->busyError)
throw new UnbDatabaseException('Database is busy too long. Failed statement: ' . $sql);
$time = microtime(true) - $start;
$this->totalTime += $time;
$this->queryCount++;
if ($this->displayQueryErrors)
$this->ShowQueryInfo($ps, $r, $parameters, $time);
if (!$r)
throw new UnbDatabaseException('Statement could not be executed. ' . $this->errorMessage);
if ($hasAutoValue)
$this->FetchLastAutoValue();
return $ps;
}
public function Execute($sql, $parameters = null)
{
$ps = $this->InternalExecute($sql, $parameters);
if (isset($ps))
$this->rowsAffected = $ps->rowCount();
else
$this->rowsAffected = -1;
}
public function Insert($table, $values)
{
$sqlCols = '';
$sqlValues = '';
foreach ($values as $key => $value)
{
$sqlCols .= ($sqlCols ? ', ' : '') . '"' . $key . '"';
$sqlValues .= ($sqlValues ? ', ' : '') . '?';
}
$this->Execute(
'INSERT INTO "#' . $table . '" (' . $sqlCols . ') VALUES (' . $sqlValues . ')',
array_values($values));
}
public function Update($table, $values, $condSql, $condParams = null)
{
$sql = '';
foreach ($values as $key => $value)
{
$sql .= ($sql ? ', ' : '') . '"' . $key . '" = ?';
}
$values = array_values($values);
if (is_array($condParams) && count($condParams))
{
$values = array_merge($values, $condParams);
}
else if (isset($condParams))
{
$values = array_merge($values, array($condParams));
}
$this->Execute(
'UPDATE "#' . $table . '" SET ' . $sql . ' WHERE ' . $condSql,
$values);
}
public function Delete($table, $condSql, $condParams = null)
{
$this->Execute(
'DELETE FROM "#' . $table . '" WHERE ' . $condSql,
$condParams);
}
public function Query($sql, $parameters = null, $fetchStyle = PDO::FETCH_ASSOC)
{
$ps = $this->InternalExecute($sql, $parameters);
$records = $ps->fetchAll($fetchStyle);
return $records;
}
public function QueryArray($sql, $parameters = null)
{
$ps = $this->InternalExecute($sql, $parameters);
$records = $ps->fetchAll(PDO::FETCH_NUM);
$array = array();
foreach ($records as $record)
{
$array[] = $record[0];
}
return $array;
}
public function QueryRecord($sql, $parameters = null, $fetchStyle = PDO::FETCH_ASSOC)
{
$ps = $this->InternalExecute($sql, $parameters);
$record = $ps->fetch($fetchStyle);
$ps->closeCursor();
return $record;
}
public function QueryValue($sql, $parameters = null)
{
$ps = $this->InternalExecute($sql, $parameters);
$value = $ps->fetchColumn();
$ps->closeCursor();
if ($value === false) return null;
return $value;
}
public function GetRowsAffected()
{
return $this->rowsAffected;
}
public static function ConvertDateTime($in)
{
if (!isset($in))
{
return null;
}
if (is_int($in))
{
if ($in == 0) return '0000-00-00 00:00:00';
return gmdate('Y-m-d H:i:s', $in);
}
if (is_string($in))
{
if (preg_match('_^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}):(\d{2})(\.\d+)?)?$_', $in, $m))
{
if (intval($m[1]) == 0) return 0;
return gmmktime(intval($m[4]), intval($m[5]), intval($m[6]), intval($m[2]), intval($m[3]), intval($m[1]));
}
throw new InvalidArgumentException('Unrecognised date/time format');
}
throw new InvalidArgumentException('Invalid argument type: $in');
}
public static function EscapeLikeWildcards($s)
{
return str_replace(
array('%',   '_',   '\\',   '*', '?'),
array('\\%', '\\_', '\\\\', '%', '_'),
$s);
}
public static function AsBool($s)
{
$s = strtolower(trim($s));
return $s !== '' &&
$s !== '0' &&
$s !== 'false' &&
$s !== 'no' &&
$s !== 'off';
}
public function Quote($s)
{
if (is_int($s) || is_float($s))
{
return $s;
}
else // string
{
return "'" . str_replace("'", "''", $s) . "'";
}
}
}
class UnbDbAutoValue
{
private $table;
public function __construct($table)
{
$this->table = $table;
}
public function GetTable()
{
return $this->table;
}
} // UnbDbAutoValue
class UnbDatabaseException extends Exception
{
}
class UnbConfiguration
{
private static $settingsFilename = 'config.inc.php';
private static $settings = null;
private function __construct()
{
}
public static function SetFilename($filename)
{
if (isset($filename))
{
if (!is_string($filename))
throw new InvalidArgumentException('Invalid argument type: $filename');
$filename = trim($filename);
if (!strlen($filename))
$filename = null;
}
self::$settingsFilename = $filename;
}
private static function ReadFile()
{
if (isset(self::$settingsFilename))
{
if (self::$settingsFilename{0} == '/')
include self::$settingsFilename;
else
include __DIR__ . '/' . self::$settingsFilename;
if (!is_array($settings))
throw new Exception('[UNB#1] Configuration file is corrupt.');
self::$settings = $settings;
}
}
public static function Initialise($settings)
{
if (!is_array($settings))
throw new InvalidArgumentException('Invalid argument type: $settings');
self::$settings = $settings;
}
public static function Get($name, $default = null)
{
if (!isset(self::$settings))
self::ReadFile();
$entries = array();
if (strpos($name, '*') === false)
{
if (array_key_exists($name, self::$settings))
return self::$settings[$name];
}
else
{
$rsName = preg_replace('_([\\\\^$.\\]|()?+{}\\_])_', '\\\\$1', $name);
$rsName = str_replace('*', '.*', $rsName);
foreach (self::$settings as $myname => $data)
{
if (preg_match('_^' . $rsName . '$_i', $myname))
$entries[$myname] = $data;
}
}
if (strpos($name, '*') === false)
{
if (count($entries) > 0)
return $entries[0];
else
return $default;
}
else
{
return $entries;
}
}
}
class UnbEnvironment
{
private static $didSetup = false;
private static $clientPlatform = null;
private static $clientVersion = null;
private static $clientIsBrowser = null;
private static $clientIsMobile = null;
private static $searchTerms = null;
private static $searchTermsArray = null;
private static $searchEngine = null;
private static $searchImageUrl = null;
private static $searchPageNumber = null;
private static $clientHostName = null;
private function __construct()
{
}
public static function SetupEnvironment()
{
if (self::$didSetup) return;
self::SetupErrorReporting();
self::SetupMultibyte();
self::SetupMagicQuotesRuntime();
self::SetupMagicQuotesGPC();
self::SetupNull();
self::SetupRegisterGlobals();
self::EnableHttpCompression();
self::$didSetup = true;
}
public static function SetupNonWebEnvironment()
{
if (self::$didSetup) return;
self::SetupErrorReporting();
self::SetupMultibyte();
self::SetupMagicQuotesRuntime();
self::SetupMagicQuotesGPC();
self::SetupNull();
self::SetupRegisterGlobals();
self::$didSetup = true;
}
public static function SetupErrorReporting()
{
if (defined("E_DEPRECATED"))
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
else
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT);
ini_set('display_errors', 0);
}
public static function SetupMultibyte()
{
mb_internal_encoding('UTF-8');
}
public static function SetupMagicQuotesRuntime()
{
if (function_exists('set_magic_quotes_runtime'))
{
@set_magic_quotes_runtime(0);
}
}
public static function SetupMagicQuotesGPC()
{
if (get_magic_quotes_gpc())
{
self::DeSlashArray($_GET);
self::DeSlashArray($_POST);
self::DeSlashArray($_REQUEST);
self::DeSlashArray($_COOKIE);
self::DeSlashArray($_FILES);
}
}
private static function DeSlashArray(&$a)
{
foreach ($a as $key => $value)
{
if (strpos($key, '\\') !== false)
{
unset($a[$key]);
continue;
}
if (is_string($value))
{
$a[$key] = stripslashes($value);
}
else if (is_array($value))
{
self::DeSlashArray($a[$key]);
}
}
}
public static function SetupNull()
{
self::DeNulArray($_GET);
self::DeNulArray($_POST);
self::DeNulArray($_REQUEST);
self::DeNulArray($_COOKIE);
self::DeNulArray($_FILES);
}
private static function DeNulArray(&$a)
{
foreach ($a as $key => $value)
{
if (strpos($key, "\x00") !== false)
{
unset($a[$key]);
continue;
}
if (is_string($value))
{
$a[$key] = str_replace(array("\x00", "\r"), array('', ''), $value);
}
else if (is_array($value))
{
self::DeNulArray($a[$key]);
}
}
}
public static function SetupRegisterGlobals()
{
if (ini_get('register_globals'))
{
if (sizeof($_SESSION)) foreach (array_keys($_SESSION) as $key) unset($GLOBALS[$key]);
if (sizeof($_GET)) foreach (array_keys($_GET) as $key) unset($GLOBALS[$key]);
if (sizeof($_POST)) foreach (array_keys($_POST) as $key) unset($GLOBALS[$key]);
if (sizeof($_COOKIE)) foreach (array_keys($_COOKIE) as $key) unset($GLOBALS[$key]);
if (sizeof($_SERVER)) foreach (array_keys($_SERVER) as $key) unset($GLOBALS[$key]);
if (is_array($_GET['GLOBALS'])) foreach (array_keys($_GET['GLOBALS']) as $key) unset($GLOBALS[$key]);
if (is_array($_POST['GLOBALS'])) foreach (array_keys($_POST['GLOBALS']) as $key) unset($GLOBALS[$key]);
}
}
public static function EnableHttpCompression()
{
if (!ini_get('zlib.output_compression'))
{
if (ob_get_level() == 1 && ini_get('output_buffering'))
{
ob_end_clean();
}
ob_start('ob_gzhandler');
}
if (!ob_get_level())
{
ob_start();
}
}
public static function GetReferrer()
{
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
}
public static function IsRefererMyDomain()
{
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
if (preg_match('_^https?://(.*?)/_i', $referer, $m))
{
$referer = $m[1];
$me = $_SERVER['SERVER_NAME'];
if (!strcasecmp(substr($me, 0, 4), 'www.')) $me = substr($me, 4);
if (!strcasecmp(substr($referer, 0, 4), 'www.')) $referer = substr($referer, 4);
return !strcasecmp($me, $referer);
}
return false;
}
public static function GetClientAddress()
{
$addr = $_SERVER['HTTP_X_FORWARDED_FOR'];
if (!isset($addr) ||
!preg_match('_^(([0-9]{1,3}\.){3}[0-9]{1,3}|[0-9a-f]+:[0-9a-f:]*)$_i', $addr) ||
preg_match('_^(0\.|10\.|127\.|169\.254\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|255\.255\.255\.255|f[cd][0-9a-f]{2}:)_i', $addr))
{
$addr = $_SERVER['REMOTE_ADDR'];
}
return $addr;
}
public static function GetClientHostName()
{
if (isset(self::$clientHostName))
return self::$clientHostName;
self::$clientHostName = gethostbyaddr(self::GetClientAddress());
return self::$clientHostName;
}
public static function GetClientLanguages($alpha2only = false)
{
$languageCodes = array();
$priorities = array();
$acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
while (preg_match('/^\s*([a-z]{2})(?:[-_]([a-z]{2}))?(?:;\s*q\s*=\s*(\d+(?:\.\d+)?))?(?:\s*,\s*(.*))?$/i', $acceptLanguage, $m))
{
$acceptLanguage = $m[4];
$languageCodes[] = strtolower($m[1]) . (!$alpha2only && $m[2] ? '-' . strtoupper($m[2]) : '');
$priorities[] = $m[3] ? intval(floatval($m[3]) * 100) : 100;
}
array_multisort($priorities, SORT_DESC, SORT_NUMERIC, $languageCodes);
return $languageCodes;
}
public static function GetClientPlatform()
{
if (isset(self::$clientPlatform))
return self::$clientPlatform;
$raw_ua = $_SERVER['HTTP_USER_AGENT'];
$names = array(
'Slurp' => 'search',
'ZyBorg' => 'search',
'Ask Jeeves' => 'search/askjeeves',
'Googlebot' => 'search/google',
'Google Web Preview' => 'search/google',
'Mediapartners-Google' => 'search/google',
'PhantomJS' => 'bot',
'Opera Mini' => 'opera/mini',
'Opera Mobi' => 'opera/mobile',
'Opera' => 'opera/opera',
'iCab' => 'icab',
'OmniWeb' => 'webkit/omniweb',
'Avant Browser' => 'ie/avant',
'Maxthon' => 'ie/maxthon',
'MSIEMobile' => 'ie/mobile',
'IEMobile' => 'ie/mobile',
'MSIE' => 'ie/ie',
'Edge' => 'edge',
'Trident' => 'ie/ie',
'Galeon' => 'galeon',
'Firebird' => 'gecko/ff',
'Firefox' => 'gecko/ff',
'Minefield' => 'gecko/ff',
'Iceweasel' => 'gecko/ff',
'SeaMonkey' => 'gecko/seamonkey',
'Thunderbird' => 'gecko/ff',
'Fennec' => 'gecko/fennec',
'Google Wireless Transcoder' => 'app',
'chromeframe' => 'webkit/chrome/frame',
'Chromium' => 'webkit/chrome/chromium',
'Chrome' => 'webkit/chrome',
'Mobile Safari' => 'webkit/mobile',
'Safari' => 'webkit/safari',
'AppleWebKit' => 'webkit',
'Konqueror' => 'webkit/konq',
'Chimera' => 'gecko/camino',
'Camino' => 'gecko/camino',
'K-Meleon' => 'gecko/kmeleon',
'UCBrowser' => 'ucbrowser',
'UC Browser' => 'ucbrowser',
'Kindle' => 'kindle',
'Lynx' => 'text/lynx',
'ELinks' => 'text/elinks',
'Links' => 'text/links',
'w3m' => 'text/w3m',
'msnbot' => 'search/ms',
'bingbot' => 'search/ms',
'ConveraMultiMediaCrawler' => 'search',
'Spider' => 'search',
'Scooter' => 'search',
'WebCrawler' => 'search',
'crawler' => 'search',
'WiseCrawler' => 'search',
'LinkWalker' => 'search',
'Nutch' => 'search',
'Powermarks' => 'app',
'findlinks' => 'bot',
'Indy Library' => 'bot',
'Jetbot' => 'bot',
'ia_archiver' => 'bot/archive',
'special_archiver' => 'bot/archive',
'Python-urllib' => 'bot',
'Gigabot' => 'bot',
'Firefly' => 'bot',
'ichiro' => 'bot',
'lwp-trivial' => 'bot',
'LWP::Simple' => 'bot/perl',
'RI Checker' => 'bot',
'Spinn3r' => 'bot',
'Wget' => 'app/download',
'DA 4' => 'app/download',
'NetAnts' => 'app/download',
'Mass Downloader' => 'app/download',
'GetRight' => 'app/download',
'PHP' => 'bot/php',
'APT-HTTP' => 'app/download/apt',
'robot' => 'bot',
'psbot' => 'bot',
'bot' => 'bot',
'Seznam screenshot-generator' => 'bot',
'Gecko' => 'gecko',
'Netscape' => 'gecko/nn',
'Mozilla' => 'gecko/nn',
);
$platform = 'unknown';
foreach ($names as $ua => $name)
{
if (stristr($raw_ua, $ua))
{
$platform = $name;
if ($ua == 'Gecko')
$ua = 'rv:';
else if ($ua == 'Trident')
$ua = 'rv:';
else if ($ua == 'Netscape')
$ua .= '[0-9]?[ /]';
else if (($ua == 'Opera' || $ua == 'Opera Mini' || $ua == 'Opera Mobi') && stristr($raw_ua, 'Version/'))
$ua = 'Version/';
else if ($ua == 'Safari' && stristr($raw_ua, 'Version/'))
$ua = 'Version/';
else if ($ua == 'Avant Browser' || $ua == 'Maxthon')
$ua = 'MSIE';
else if ($ua == 'UCBrowser' || $ua == 'UC Browser')
$ua .= '';
else
$ua .= '[ /]';
if (preg_match('~' . $ua . '([0-9.]+)~', $raw_ua, $m))
{
if (preg_match('~^webkit/chrome~', $platform))
{
$parts = explode('.', $m[1]);
while (count($parts) > 2) array_pop($parts);
$m[1] = join('.', $parts);
}
$platform .= '/' . $m[1];
}
break;
}
}
if (preg_match('_^ie/ie/([0-9.]+)_', $platform, $m))
{
$ieVer = $m[1];
if (preg_match('_Trident/([0-9.]+)_', $raw_ua, $m))
{
$tridentVer = $m[1];
if (substr($tridentVer, 0, 2) == '4.' && substr($ieVer, 0, 2) != '8.')
{
$realIEVer = 8;
}
if (substr($tridentVer, 0, 2) == '5.' && substr($ieVer, 0, 2) != '9.')
{
$realIEVer = 9;
}
if (substr($tridentVer, 0, 2) == '6.' && substr($ieVer, 0, 3) != '10.')
{
$realIEVer = 10;
}
if (substr($tridentVer, 0, 2) == '7.' && substr($ieVer, 0, 3) != '11.')
{
$realIEVer = 11;
}
if ($realIEVer)
{
$platform = 'ie/ie/' . $realIEVer . '/compat/' . $ieVer;
}
}
}
$osmap = array(
'PalmOS' => 'palm',
'PalmSource' => 'palm',
'webOS' => 'webos',
'Windows Mobile 6\.1' => 'win/mobile/6.1',
'Windows Mobile' => 'win/mobile',
'Windows Phone 6\.5' => 'win/phone/6.5',
'Windows Phone OS 7\.0' => 'win/phone/7',
'Windows Phone OS 7\.5' => 'win/phone/7.5',
'Windows Phone 8\.0' => 'win/phone/8.0',
'Windows Phone 8\.1' => 'win/phone/8.1',
'Windows Phone' => 'win/phone',
'Windows CE' => 'win/ce',
'Windows 95' => 'win/95',
'Win95' => 'win/95',
'Windows 98' => 'win/98',
'Win98' => 'win/98',
'Windows ME' => 'win/me',
'Windows NT 4' => 'win/nt/4',
'Windows NT 5\.0' => 'win/nt/2000',
'Windows NT 5\.1' => 'win/nt/xp',
'Windows NT 5\.2' => 'win/nt/2003',
'Windows NT 6\.0' => 'win/nt/vista',
'Windows NT 6\.1' => 'win/nt/7',
'Windows NT 6\.2' => 'win/nt/8',
'Windows NT 6\.3' => 'win/nt/8.1',
'Windows NT 6\.4' => 'win/nt/10',
'Windows NT 10' => 'win/nt/10',
'WinNT' => 'win/nt',
'Windows-NT' => 'win/nt',
'Windows NT' => 'win/nt',
'CYGWIN-NT' => 'win/nt',
'Android 7' => 'unix/linux/android/7',
'Android 6' => 'unix/linux/android/6',
'Android 5\.2' => 'unix/linux/android/5.2',
'Android 5\.1' => 'unix/linux/android/5.1',
'Android 5\.0' => 'unix/linux/android/5.0',
'Android 5' => 'unix/linux/android/5',
'Android 4\.4' => 'unix/linux/android/4.4',
'Android 4\.3' => 'unix/linux/android/4.3',
'Android 4\.2' => 'unix/linux/android/4.2',
'Android 4\.1' => 'unix/linux/android/4.1',
'Android 4\.0' => 'unix/linux/android/4.0',
'Android 4' => 'unix/linux/android/4',
'Android 3' => 'unix/linux/android/3',
'Android 2\.3' => 'unix/linux/android/2.3',
'Android 2\.2' => 'unix/linux/android/2.2',
'Android 2\.1' => 'unix/linux/android/2.1',
'Android 2\.0' => 'unix/linux/android/2.0',
'Android 1\.6' => 'unix/linux/android/1.6',
'Android 1\.5' => 'unix/linux/android/1.5',
'Android' => 'unix/linux/android',
'Linux' => 'unix/linux',
'Embedix' => 'unix/linux/mobile',
'FreeBSD' => 'unix/freebsd',
'Unix' => 'unix',
'iPad;.*(iPhone|CPU) OS 10' => 'mac/ipad/10',
'iPad;.*(iPhone|CPU) OS 9' => 'mac/ipad/9',
'iPad;.*(iPhone|CPU) OS 8_1' => 'mac/ipad/8.1',
'iPad;.*(iPhone|CPU) OS 8_0' => 'mac/ipad/8.0',
'iPad;.*(iPhone|CPU) OS 8' => 'mac/ipad/8',
'iPad;.*(iPhone|CPU) OS 7_1' => 'mac/ipad/7.1',
'iPad;.*(iPhone|CPU) OS 7_0' => 'mac/ipad/7.0',
'iPad;.*(iPhone|CPU) OS 7' => 'mac/ipad/7',
'iPad;.*(iPhone|CPU) OS 6_0' => 'mac/ipad/6.0',
'iPad;.*(iPhone|CPU) OS 6' => 'mac/ipad/6',
'iPad;.*(iPhone|CPU) OS 5_1' => 'mac/ipad/5.1',
'iPad;.*(iPhone|CPU) OS 5_0' => 'mac/ipad/5.0',
'iPad;.*(iPhone|CPU) OS 5' => 'mac/ipad/5',
'iPad;.*(iPhone|CPU) OS 4_3' => 'mac/ipad/4.3',
'iPad;.*(iPhone|CPU) OS 4_2' => 'mac/ipad/4.2',
'iPad;.*(iPhone|CPU) OS 4' => 'mac/ipad/4',
'iPad;.*(iPhone|CPU) OS 3_2' => 'mac/ipad/3.2',
'iPad;.*(iPhone|CPU) OS 3' => 'mac/ipad/3',
'iPad;.*(iPhone|CPU) OS' => 'mac/ipad',
'iPhone OS 10' => 'mac/iphone/10',
'iPhone OS 9' => 'mac/iphone/9',
'iPhone OS 8_1' => 'mac/iphone/8.1',
'iPhone OS 8_0' => 'mac/iphone/8.0',
'iPhone OS 8' => 'mac/iphone/8',
'iPhone OS 7_1' => 'mac/iphone/7.1',
'iPhone OS 7_0' => 'mac/iphone/7.0',
'iPhone OS 7' => 'mac/iphone/7',
'iPhone OS 6_0' => 'mac/iphone/6.0',
'iPhone OS 6' => 'mac/iphone/6',
'iPhone OS 5_1' => 'mac/iphone/5.1',
'iPhone OS 5_0' => 'mac/iphone/5.0',
'iPhone OS 5' => 'mac/iphone/5',
'iPhone OS 4_3' => 'mac/iphone/4.3',
'iPhone OS 4_2' => 'mac/iphone/4.2',
'iPhone OS 4_1' => 'mac/iphone/4.1',
'iPhone OS 4_0' => 'mac/iphone/4.0',
'iPhone OS 4' => 'mac/iphone/4',
'iPhone OS 3_1' => 'mac/iphone/3.1',
'iPhone OS 3_0' => 'mac/iphone/3.0',
'iPhone OS 3' => 'mac/iphone/3',
'iPhone OS 2_2' => 'mac/iphone/2.2',
'iPhone OS 2_1' => 'mac/iphone/2.1',
'iPhone OS 2_0' => 'mac/iphone/2.0',
'iPhone OS 2' => 'mac/iphone/2',
'iPhone OS 1' => 'mac/iphone/1',
'iPhone' => 'mac/iphone',
'Mac OS X 10[._]12' => 'mac/x/10.12',
'Mac OS X 10[._]11' => 'mac/x/10.11',
'Mac OS X 10[._]10' => 'mac/x/10.10',
'Mac OS X 10[._]9' => 'mac/x/10.9',
'Mac OS X 10[._]8' => 'mac/x/10.8',
'Mac OS X 10[._]7' => 'mac/x/10.7',
'Mac OS X 10[._]6' => 'mac/x/10.6',
'Mac OS X 10[._]5' => 'mac/x/10.5',
'Mac OS X 10[._]4' => 'mac/x/10.4',
'Mac OS X 10[._]3' => 'mac/x/10.3',
'Mac OS X 10[._]2' => 'mac/x/10.2',
'Mac OS X 10[._]1' => 'mac/x/10.1',
'Mac OS X' => 'mac/x',
'Mac' => 'mac',
'Darwin' => 'mac',
'BlackBerry' => 'blackberry',
'RIM Tablet OS' => 'blackberry',
'Symbian' => 'symbian',
'SunOS' => 'unix/sun',
'NokiaN8' => 'symbian/3',
'NokiaN70' => 'symbian/s60',
'OS/2' => 'os2',
'Win' => 'win',
'MIDP' => 'java/me/midp',
'J2ME' => 'java/me',
'Java' => 'java',
);
$os = '';
foreach ($osmap as $name => $v)
{
if (preg_match('~' . $name . '~i', $raw_ua))
{
$os = $v;
break;
}
}
if (stristr($raw_ua, 'PPC Mac OS X'))
$os .= '/ppc';
else if (stristr($raw_ua, 'Intel Mac OS X'))
$os .= '/intel';
else if (stristr($raw_ua, 'Mac_PowerPC'))
$os .= '/ppc';
else if (preg_match('_win/_', $os) && stristr($raw_ua, 'Trident/') && stristr($raw_ua, 'ARM;'))
$os .= '/arm';
if ($os != '')
$platform .= '@' . $os;
if (isset(self::$clientHostName))
{
if (preg_match('/crawl.*\.archive\.org$/i', self::$clientHostName))
{
$platform = 'bot/archiver';
}
else if (preg_match('/crawl\.baidu\.com$/i', self::$clientHostName))
{
$platform = 'search/baidu';
}
else if (preg_match('/(^google-proxy-.*\.google|googleusercontent)\.com$/i', self::$clientHostName))
{
$platform = 'search/google';
}
else if (preg_match('/(^|\.)ip-pool\.com$/i', self::$clientHostName))
{
$platform = 'bot';
}
}
self::$clientPlatform = $platform;
return $platform;
}
public static function GetClientVersion()
{
if (isset(self::$clientVersion))
return self::$clientVersion;
self::$clientVersion = 0;
$platform = self::GetClientPlatform();
list($name, $os) = explode('@', $platform);
if (preg_match('~/([0-9.]+)/compat~', $name, $m))
self::$clientVersion = $m[1];
else if (preg_match('~/([0-9.]+)[^/]*$~', $name, $m))
self::$clientVersion = $m[1];
return self::$clientVersion;
}
public static function IsClientPlatform($selector)
{
$platform = self::GetClientPlatform();
list($name, $os) = explode('@', $platform);
list($sel_name, $sel_os) = explode('@', $selector);
$sel_name = preg_replace('[~\\^$.\\[\\]|()?+{}]', '\\$1', $sel_name);
$sel_name = str_replace('*', '[^/]*', $sel_name);
$sel_os = preg_replace('[~\\^$.\\[\\]|()?+{}]', '\\$1', $sel_os);
$sel_os = str_replace('*', '[^/]*', $sel_os);
if ($sel_name != '' &&
!preg_match('~^' . $sel_name . '(/|$)~i', $name)) return false;
if ($sel_os != '' &&
!preg_match('~^' . $sel_os . '(/|$)~i', $os)) return false;
return true;
}
public static function IsBrowser()
{
if (isset(self::$clientIsBrowser))
return self::$clientIsBrowser;
$bots = array(
'search',
'bot',
'app',
);
self::$clientIsBrowser = true;
foreach ($bots as $bot)
{
if (self::IsClientPlatform($bot))
{
self::$clientIsBrowser = false;
}
}
if (self::$clientIsBrowser)
{
$hostName = gethostbyaddr(self::GetClientAddress());
if (preg_match('_^crawl.*\.archive\.org$_i', $hostName) ||
preg_match('_^crawl-.*\.googlebot\.com$_i', $hostName) ||
preg_match('_crawl\.yahoo\.net$_i', $hostName) ||
preg_match('_^msnbot.*\.search\.msn\.com$_i', $hostName) ||
preg_match('_^crawler.*\.ask\.com$_i', $hostName))
{
self::$clientIsBrowser = false;
}
}
return self::$clientIsBrowser;
}
public static function IsMobile()
{
if (isset(self::$clientIsMobile))
return self::$clientIsMobile;
$selectors = array(
'gecko/fennec',
'webkit/mobile',
'opera/mini',
'opera/mobile',
'ie/mobile',
'kindle',
'@win/ce',
'@unix/linux/android',
'@mac/iphone',
'@unix/linux/mobile',
'@java/me',
'@blackberry',
'@symbian',
);
self::$clientIsMobile = false;
foreach ($selectors as $sel)
if (self::IsClientPlatform($sel))
self::$clientIsMobile = true;
return self::$clientIsMobile;
}
public static function BestXHtml()
{
$accept = explode(',', $_SERVER['HTTP_ACCEPT']);
foreach ($accept as $h => $v) { $e = explode(';', $v); $accept[$h] = $e[0]; }
if (in_array('application/xhtml+xml', $accept)) return 'application/xhtml+xml';
else if (in_array('text/xml', $accept)) return 'text/xml';
else if (in_array('text/html', $accept)) return 'text/html';
return 'text/html';
}
public static function IsXml()
{
return strstr(self::BestXHtml(), 'xml') != false;
}
public static function DoNotTrack()
{
$dnt = $_SERVER['HTTP_DNT'];
if (isset($dnt))
return $_SERVER['HTTP_DNT'] != '0' ? true : false;
return null;
}
public static function GetSearchTerms($referrer = null)
{
if (!isset(self::$searchTerms) || isset($referrer))
{
if (!isset($referrer)) $referrer = self::GetReferrer();
self::$searchTerms = null;
self::$searchEngine = null;
self::$searchImageUrl = null;
self::$searchPageNumber = null;
$url = parse_url($referrer);
$q = '';
parse_str($url['query'], $arr);
if (preg_match('_plus\.url\.google\.[a-z]{2,3}(?:\.[a-z]{2,3})?$_i', $url['host']))
{
self::$searchEngine = 'Google Plus';
}
else if (preg_match('_google\.[a-z]{2,3}(?:\.[a-z]{2,3})?$_i', $url['host']))
{
if ($url['path'] == '/' && $url['query'] == '')
{
self::$searchEngine = 'Google';
}
else if ($url['path'] == '/url' || $url['path'] == '/search')
{
switch ($arr['source'])
{
case 'videos':
self::$searchEngine = 'Google Videos';
break;
case 'newssearch':
self::$searchEngine = 'Google News';
break;
default:
self::$searchEngine = 'Google';
break;
}
$q = $arr['q'];
self::$searchPageNumber = intval(intval($arr['cd']) / 10) + 1;
}
else if ($url['path'] == '/m' || $url['path'] == '/m/search')
{
self::$searchEngine = 'Google Mobile';
$q = $arr['q'];
self::$searchPageNumber = intval(intval($arr['cd']) / 10) + 1;
}
else if ($url['path'] == '/cse')
{
self::$searchEngine = 'Google Custom Search';
$q = $arr['q'];
self::$searchPageNumber = intval(intval($arr['cd']) / 10) + 1;
}
else if ($url['path'] == '/imgres')
{
self::$searchEngine = 'Google Images';
$q = $arr['q'];
self::$searchImageUrl = urldecode($arr['imgurl']);
self::$searchPageNumber = intval(intval($arr['start']) / 25) + 1;
if (!strlen($q))
{
$url = parse_url($arr['prev']);
parse_str($url['query'], $arr);
$q = $arr['q'];
}
}
}
else if (preg_match('_search\.yahoo\.(com|co\.jp)$_i', $url['host']))
{
if ($url['path'] == '/' && $url['query'] == '')
{
self::$searchEngine = 'Yahoo';
}
else if (substr($url['path'], 0, 7) == '/search')
{
self::$searchEngine = 'Yahoo';
}
else if (substr($url['path'], 0, 7) == '/mobile/s')
{
self::$searchEngine = 'Yahoo Mobile';
}
else if (substr($url['path'], 0, 7) == '/images' ||
preg_match('_images\.search\.yahoo\._i', $url['host']))
{
self::$searchEngine = 'Yahoo Images';
}
$q = $arr['p'];
self::$searchPageNumber = intval(intval($arr['b']) / 10) + 1;
}
else if (preg_match('_bing\.com$_i', $url['host']))
{
if ($url['path'] == '/' && $url['query'] == '')
{
self::$searchEngine = 'Bing';
}
else if ($url['path'] == '/search' || $url['path'] == '/url')
{
self::$searchEngine = 'Bing';
self::$searchPageNumber = intval(intval($arr['first']) / 10) + 1;
}
else if ($url['path'] == '/images/search')
{
self::$searchEngine = 'Bing Images';
self::$searchPageNumber = intval(intval($arr['first']) / 30) + 1;
}
$q = $arr['q'];
}
else if (preg_match('_suche\.web\.de$_i', $url['host']) && $url['path'] == '/search/web/')
{
self::$searchEngine = 'Web.de';
$q = $arr['su'];
self::$searchPageNumber = $arr['pageIndex'] ? intval($arr['pageIndex']) : 1;
}
else if (preg_match('_suche\.gmx\.net$_i', $url['host']) && $url['path'] == '/web')
{
self::$searchEngine = 'GMX';
$q = $arr['su'];
self::$searchPageNumber = $arr['pageIndex'] ? intval($arr['pageIndex']) : 1;
}
else if (preg_match('_ask\.com$_i', $url['host']) && $url['path'] == '/web')
{
self::$searchEngine = 'Ask.com';
$q = $arr['q'];
self::$searchPageNumber = $arr['page'] ? intval($arr['page']) : 1;
}
else if (preg_match('_yandex\.ru$_i', $url['host']) && $url['path'] == '/yandsearch')
{
self::$searchEngine = 'Yandex.ru';
$q = $arr['text'];
self::$searchPageNumber = intval($arr['p']) + 1;
}
else if (preg_match('_baidu\.com$_i', $url['host']) && $url['path'] == '/s')
{
self::$searchEngine = 'Baidu';
$q = $arr['wd'];
self::$searchPageNumber = intval(intval($arr['pn']) / 10) + 1;
}
else if (preg_match('_scroogle\.org$_i', $url['host']) && $url['path'] == '/cgi-bin/nbbw.cgi')
{
self::$searchEngine = 'Scroogle';
}
else if (preg_match('_facebook\.com$_i', $url['host']))
{
self::$searchEngine = 'Facebook';
}
else if (preg_match('_^t\.co$_i', $url['host']))
{
self::$searchEngine = 'Twitter';
}
self::$searchTermsArray = array();
if (strlen($q))
{
if (get_magic_quotes_gpc())
$q = stripslashes($q);
$q = str_replace("\x00", '', $q);
if (strpos($q, ' ') === false &&
substr($q, 0, 1) != '+' &&
substr($q, -1) != '+' &&
strpos($q, '++') === false &&
!preg_match('_%(2[de]|3[0-9]|[46][1-9a-f]|[57][0-9a]|5f)_i', $q) &&
(strpos($q, '+') !== false || preg_match('_%[0-9a-f]{2}_i', $q)))
{
$q = urldecode($q);
}
self::$searchTerms = $q;
if (function_exists('str_getcsv'))
$st = str_getcsv($q, ' ');
else
$st = explode(' ', $q);
foreach ($st as $a)
{
$a = trim($a);
if ($a != '')
self::$searchTermsArray[] = $a;
}
}
}
return self::$searchTerms;
}
public static function GetSearchTermsArray()
{
return self::$searchTermsArray;
}
public static function GetSearchEngine()
{
return self::$searchEngine;
}
public static function GetSearchImageUrl()
{
return self::$searchImageUrl;
}
public static function GetSearchPageNumber()
{
return self::$searchPageNumber;
}
public static function GetServerPlatform()
{
if (substr(strtoupper(PHP_OS), 0, 3) == 'WIN') return 'win';
if (substr(strtoupper(PHP_OS), 0, 5) == 'LINUX') return 'linux';
return '';
}
public static function IsWebRequest()
{
return $_SERVER["REQUEST_METHOD"] != '';
}
}
class UnbDebug
{
private static $showTrace = false;
private static $traceToFile = false;
private static $maxArgLength = 40;
private static $requestTag;
private function __construct()
{
}
public static function SetTraceToFile($v)
{
self::$traceToFile = $v;
}
public static function GetRequestTag()
{
if (!self::$requestTag)
{
self::$requestTag = mt_rand(10000, 99999);
}
return self::$requestTag;
}
public static function Trace($text)
{
if (!self::$traceToFile) return;
list($usec, $time) = explode(' ', microtime());
$text = date('Y-m-d H:i:s', $time) . '.' . substr($usec, 2, 6) .
' [' . self::GetRequestTag() . '] ' .
$text . "\n";
if (!is_dir(__DIR__ . '/logs'))
{
mkdir(__DIR__ . '/logs');
}
file_put_contents(__DIR__ . '/logs/tomo.' . date('Y-m-d', $time) . '.trace', $text, FILE_APPEND);
}
public static function Dump($var)
{
ob_start();
var_dump($var);
return trim(ob_get_clean());
}
public static function TrimFilename($fileName)
{
$fileName = str_replace('\\', '/', $fileName);
$root = rtrim(str_replace('\\', '/', $_SERVER['DOCUMENT_ROOT']), '/');
if (substr($fileName, 0, strlen($root)) == $root)
return substr($fileName, strlen($root));
return $fileName;
}
private static function FormatArgs($args)
{
$str = '';
$c = 0;
if (is_array($args)) foreach ($args as $a)
{
if ($c++) $str .= ', ';
if (is_string($a) && mb_strlen($a) > self::$maxArgLength)
$a = mb_substr($a, 0, self::$maxArgLength - 1) . '…';
if (is_string($a))
$str .= "'" . str_replace(array('\\', "\n", "\r", "\t"), array('\\\\', '\n', '\r', '\t'), $a) . "'";
else if (is_bool($a))
$str .= $a ? 'true' : 'false';
else if ($a === null)
$str .= 'null';
else if (is_object($a))
$str .= get_class($a);
else
$str .= $a;
}
return $str;
}
public static function GetCallTrace($fileName = null, $line = null)
{
$skip = 2;
$start = 0;
if (is_object($fileName) && $fileName instanceof Exception)
{
$ex = $fileName;
$line = $ex->getLine();
$fileName = $ex->getFile();
}
else if (is_int($fileName))
{
$skip = $fileName;
$start = 1;
}
else if (!isset($fileName))
{
$skip = 0;
$start = 1;
}
$str = '';
if ($ex)
$trace = $ex->getTrace();
else
$trace = array_slice(debug_backtrace(), $skip);
for ($i = $start; $i <= count($trace); $i++)
{
if ($i < count($trace))
$str .= '  in ' . $trace[$i]['class'] . $trace[$i]['type'] . $trace[$i]['function'] .
'(' . self::FormatArgs($trace[$i]['args']) . ')';
else
$str .= '  in main()';
if ($i == 0)
$str .= ' at ' . htmlspecialchars(self::TrimFilename($fileName)) . ':' . $line . "\n";
else
$str .= ' at ' . htmlspecialchars(self::TrimFilename($trace[$i - 1]['file'])) . ':' . $trace[$i - 1]['line'] . "\n";
}
return rtrim($str);
}
public static function ErrorHandler($errno, $errstr, $errfile, $errline, $errcontext)
{
$stop = false;
switch ($errno)
{
case E_WARNING: $type = 'PHP warning'; break;
case E_NOTICE: $type = 'PHP notice'; break;
case E_USER_ERROR: $type = 'User-defined error'; $stop = true; break;
case E_USER_WARNING: $type = 'User-defined warning'; break;
case E_USER_NOTICE: $type = 'User-defined notice'; break;
case E_STRICT: $type = 'PHP strict notice'; break;
case E_RECOVERABLE_ERROR: $type = 'PHP recoverable error'; $stop = true; break;
case E_DEPRECATED: $type = 'PHP deprecated notice'; break;
case E_USER_DEPRECATED: $type = 'User-defined deprecated notice'; break;
default: $type = 'Unknown error'; $stop = true; break;
}
echo '<p class="' . ($stop ? 'errorbox' : 'warningbox') . '" style="white-space: pre-wrap;"><strong>' . $type . '</strong>: ';
if (in_array(ini_get('html_errors'), array(1, '1', 'on', 'yes', 'true')))
echo $errstr;
else
echo htmlspecialchars($errstr);
if (self::$showTrace)
{
echo '<br />';
echo str_replace("\n", '<br />', self::GetCallTrace($errfile, $errline));
}
echo '</p>';
if ($stop) die();
}
public static function ExceptionHandler($ex)
{
echo '<p class="errorbox" style="white-space: pre-wrap;"><strong>Unhandled ' . get_class($ex) . '</strong>: ' . htmlspecialchars($ex->getMessage());
if ($ex->getCode())
echo ' (Code ' . $ex->getCode() . ')';
if (self::$showTrace)
{
echo '<br />';
echo str_replace("\n", '<br />', self::GetCallTrace($ex));
}
echo '</p>';
}
public static function StartErrorHandling($showTrace = false)
{
self::$showTrace = $showTrace;
ini_set('html_errors', 1);
ini_set('docref_root', 'http://php.net/');
set_error_handler(array('UnbDebug', 'ErrorHandler'), error_reporting());
set_exception_handler(array('UnbDebug', 'ExceptionHandler'));
}
}
class GeoIP
{
private static $dbFileName = '';
private static $headerLength = 0xa0;
private static $ipv6Length = 6;
public static function SetDBFileName($fileName)
{
self::$dbFileName = $fileName;
}
public static function LookupIP($ipAddress)
{
if (!strlen($ipAddress)) return null;
$dir = __DIR__;
if (strlen(self::$dbFileName) && is_dir(self::$dbFileName))
{
$dir = self::$dbFileName;
self::$dbFileName = '';
}
if (!strlen(self::$dbFileName))
{
if (file_exists($fn = $dir . '/geolitecity.bin'))
self::$dbFileName = $fn;
else if (file_exists($fn = $dir . '/geolitecountry.bin'))
self::$dbFileName = $fn;
else
return null;
}
if (!file_exists(self::$dbFileName)) return null;
$file = fopen(self::$dbFileName, 'r');
if (!$file) return null;
$header = fgets($file, self::$headerLength);
if (substr($header, 0, 37) == 'Combined GeoLite country database v1,')
{
$cityMode = false;
}
else if (substr($header, 0, 34) == 'Combined GeoLite city database v1,')
{
$cityMode = true;
}
else
{
throw new Exception('Invalid or unsupported file format');
}
$ipBin = self::IpToBin($ipAddress);
if ($cityMode)
{
return self::LookupIPCity($ipBin, $file);
}
else
{
return self::LookupIPCountry($ipBin, $file);
}
}
private static function LookupIPCountry($ipBin, $file)
{
$countriesTablePos = self::$headerLength + 2 * 4;
fseek($file, self::$headerLength);
$ip4TablePos = self::BinToNum(fread($file, 4));
$ip6TablePos = self::BinToNum(fread($file, 4));
$stat = fstat($file);
$countriesTableLength = $ip4TablePos - $countriesTablePos;
$ip4TableLength = $ip6TablePos - $ip4TablePos;
$ip6TableLength = intval($stat['size']) - $ip6TablePos;
if (strlen($ipBin) > 4)
{
$ipBin = substr($ipBin, 0, self::$ipv6Length);
$countryPos = self::BinarySearch($ipBin, $file, $ip6TablePos, $ip6TableLength, 2);
}
else
{
$countryPos = self::BinarySearch($ipBin, $file, $ip4TablePos, $ip4TableLength, 2);
}
if ($countryPos === false) return null;
if ($countryPos == 0xFFFF) return null;
if ($countryPos >= $countriesTableLength) throw new Exception('Invalid country pointer: ' . $countryPos . ' >= ' . $countriesTableLength);
fseek($file, $countriesTablePos + $countryPos);
$countryCode = fread($file, 2);
$countryNameLength = self::BinToNum(fread($file, 1));
if ($countryNameLength > 0)
{
$countryName = trim(fread($file, $countryNameLength));
}
else
{
$countryName = '';
}
return array(
'country' => $countryName,
'cc' => $countryCode
);
}
private static function LookupIPCity($ipBin, $file)
{
$countriesTablePos = self::$headerLength + 4 * 4;
fseek($file, self::$headerLength);
$regionsTablePos = self::BinToNum(fread($file, 4));
$citiesTablePos = self::BinToNum(fread($file, 4));
$ip4TablePos = self::BinToNum(fread($file, 4));
$ip6TablePos = self::BinToNum(fread($file, 4));
$stat = fstat($file);
$countriesTableLength = $regionsTablePos - $countriesTablePos;
$regionsTableLength = $citiesTablePos - $regionsTablePos;
$citiesTableLength = $ip4TablePos - $citiesTablePos;
$ip4TableLength = $ip6TablePos - $ip4TablePos;
$ip6TableLength = intval($stat['size']) - $ip6TablePos;
if (strlen($ipBin) > 4)
{
$ipBin = substr($ipBin, 0, self::$ipv6Length);
$cityPos = self::BinarySearch($ipBin, $file, $ip6TablePos, $ip6TableLength, 3);
}
else
{
$cityPos = self::BinarySearch($ipBin, $file, $ip4TablePos, $ip4TableLength, 3);
}
if ($cityPos === false) return null;
if ($cityPos == 0xFFFFFF) return null;
if ($cityPos >= $citiesTableLength) throw new Exception('Invalid city pointer: ' . $cityPos . ' >= ' . $citiesTableLength);
fseek($file, $citiesTablePos + $cityPos);
$lat = self::BinToNum(fread($file, 3)) / 10000 - 90;
$lng = self::BinToNum(fread($file, 3)) / 10000 - 180;
$regionPos = self::BinToNum(fread($file, 2));
$cityLength = self::BinToNum(fread($file, 1));
if ($cityLength > 0)
{
$city = trim(fread($file, $cityLength));
}
else
{
$city = '';
}
if (($regionPos & 0x8000) == 0)
{
$regionPos *= 2;
if ($regionPos >= $regionsTableLength) throw new Exception('Invalid region pointer: ' . $regionPos . ' >= ' . $regionsTableLength);
fseek($file, $regionsTablePos + $regionPos);
$countryPos = self::BinToNum(fread($file, 2));
$regionLength = self::BinToNum(fread($file, 1));
if ($regionLength > 0)
{
$region = trim(fread($file, $regionLength));
}
else
{
$region = '';
}
}
else
{
$countryPos = $regionPos & 0x7FFF;
$region = '';
}
if ($countryPos >= $countriesTableLength) throw new Exception('Invalid country pointer: ' . $countryPos . ' >= ' . $countriesTableLength);
fseek($file, $countriesTablePos + $countryPos);
$countryCode = fread($file, 2);
$countryNameLength = self::BinToNum(fread($file, 1));
if ($countryNameLength > 0)
{
$countryName = trim(fread($file, $countryNameLength));
}
else
{
$countryName = '';
}
return array(
'lat' => $lat,
'lng' => $lng,
'city' => $city,
'region' => $region,
'country' => $countryName,
'cc' => $countryCode
);
}
private static function BinarySearch($ipBin, $file, $start, $length, $posSize)
{
if ($length <= 0) return false;
$ipSize = strlen($ipBin);
$recordLength = $ipSize + $posSize;
$recordCount = $length / $recordLength;
$startIdx = 0;
$endIdx = $recordCount - 1;
do
{
$idx = round(($startIdx + $endIdx) / 2);
fseek($file, $start + $idx * $recordLength);
$ipStart = fread($file, $ipSize);
$posData = fread($file, $posSize);
if ($idx < $recordCount - 1)
{
$ipEnd = fread($file, $ipSize);
}
else
{
$ipEnd = str_repeat(chr(255), $ipSize);
}
if ($ipStart <= $ipBin && $ipBin < $ipEnd)
{
return self::BinToNum($posData);
}
else if ($ipBin < $ipStart)
{
$endIdx = $idx - 1;
}
else if ($ipEnd < $ipBin)
{
$startIdx = $idx + 1;
}
}
while ($startIdx <= $endIdx);
return false;
}
private static function IpToBin($ip)
{
if (preg_match('_^(\d+)\.(\d+)\.(\d+)\.(\d+)$_', $ip, $m))
{
return chr($m[1]) . chr($m[2]) . chr($m[3]) . chr($m[4]);
}
$ipv6 = self::ExpandIPv6($ip);
if ($ipv6 !== false)
{
$bin = '';
for ($i = 0; $i < 8; $i++)
{
$chunk1 = substr($ipv6, $i * 5 + 0, 2);
$chunk2 = substr($ipv6, $i * 5 + 2, 2);
$dec1 = hexdec($chunk1);
$dec2 = hexdec($chunk2);
$bin .= chr($dec1) . chr($dec2);
}
return $bin;
}
throw new Exception('Invalid IP address format');
}
private static function ExpandIPv6($ip_addr)
{
$ip_addr = strtolower($ip_addr);
if (!preg_match('/^[0-9a-f:]+$/', $ip_addr))
return false;
if (substr_count($ip_addr, '::') > 1)
return false;
if ($ip_addr == '::')
$ip_addr = '0:0:0:0:0:0:0:0';
$ip_addr = preg_replace('_^::_', '*:', $ip_addr);
$ip_addr = preg_replace('_::$_', ':*', $ip_addr);
$ip_addr = str_replace('::', ':*:', $ip_addr);
$cnt = count(explode(':', $ip_addr));
if ($cnt <= 8)
{
if (strpos($ip_addr, ':*') !== false)
$ip_addr = str_replace(':*', str_repeat(':0', 8 - ($cnt - 1)), $ip_addr);
else if (strpos($ip_addr, '*:') !== false)
$ip_addr = str_replace('*:', str_repeat('0:', 8 - ($cnt - 1)), $ip_addr);
}
$expanded = '';
$chunks = explode(':', $ip_addr);
if (count($chunks) != 8)
return false;
foreach ($chunks as $chunk)
{
if (!preg_match('/^[0-9a-f]{1,4}$/', $chunk))
return false;
$expanded .= (strlen($expanded) > 0 ? ':' : '') . str_pad($chunk, 4, '0', STR_PAD_LEFT);
}
return $expanded;
}
private static function NumToBin($num, $length = 0)
{
$bin = '';
do
{
$byte = bcmod($num, 256);
$bin = chr($byte) . $bin;
$num = bcsub($num, $byte);
$num = bcdiv($num, 256);
}
while (bccomp($num, 0) != 0);
while (strlen($bin) < $length)
{
$bin = chr(0) . $bin;
}
return $bin;
}
private static function BinToNum($bin)
{
$num = 0;
for ($i = 0; $i < strlen($bin); $i++)
{
$num *= 256;
$num += ord($bin{$i});
}
return $num;
}
}
class PublicSuffix
{
public static $dataFileName;
private static $publicSuffixData;
public static function GetSuffixDataFileName()
{
$fileName = self::$dataFileName;
if (!isset($fileName))
{
return __DIR__ . '/publicsuffixdata.inc.php';
}
return $fileName;
}
public static function GetPublicSuffix($hostName)
{
if (!isset(self::$publicSuffixData))
{
include_once self::GetSuffixDataFileName();
self::$publicSuffixData = $publicSuffixData;
if (!isset(self::$publicSuffixData))
{
return false;
}
}
$hostName = strtolower($hostName);
$hostName = trim($hostName, '.');
$hostName = explode('.', $hostName);
$tld = $hostName[count($hostName) - 1];
for ($type = 0; $type <= 1; $type++)
{
foreach (self::$publicSuffixData[$type] as $rule)
{
if ($tld !== $rule[count($rule) - 1]) continue;
if (count($rule) > count($hostName)) continue;
for ($i = 0; $i < count($rule); $i++)
{
$hnPart = $hostName[count($hostName) - 1 - $i];
$rPart = $rule[count($rule) - 1 - $i];
if ($rPart !== '*' &&
$rPart !== $hnPart) continue 2;
}
$isException = $type == 0;
if (!$isException && count($rule) == count($hostName)) return null;
return join('.', array_slice($hostName, -count($rule) - 1 + ($isException ? 1 : 0)));
}
}
return null;
}
public static function GenerateDataFile()
{
if (file_exists('effective_tld_names.dat'))
$lines = file('effective_tld_names.dat');
else
$lines = file('http://mxr.mozilla.org/mozilla-central/source/netwerk/dns/effective_tld_names.dat?raw=1');
$exceptionRules = array();
$normalRules = array();
foreach ($lines as $rule)
{
$rule = trim($rule);
if (!strlen($rule)) continue;
if (substr($rule, 0, 2) == '//') continue;
$exception = false;
if ($rule{0} === '!')
{
$exception = true;
$rule = substr($rule, 1);
}
$rule = idn_to_ascii($rule);
$rule = explode('.', $rule);
$levels = count($rule);
if ($exception)
{
$exceptionRules[$levels][] = $rule;
}
else
{
$normalRules[$levels][] = $rule;
}
}
$publicSuffixData = array();
for ($i = 10; $i >= 1; $i--)
{
if (is_array($exceptionRules[$i]))
{
foreach ($exceptionRules[$i] as $rule)
{
$publicSuffixData[0][] = $rule;
}
}
}
for ($i = 10; $i >= 1; $i--)
{
if (is_array($normalRules[$i]))
{
foreach ($normalRules[$i] as $rule)
{
$publicSuffixData[1][] = $rule;
}
}
}
$out = "<?php\n" .
"// This Source Code Form is subject to the terms of the Mozilla Public\n" .
"// License, v. 2.0. If a copy of the MPL was not distributed with this\n" .
"// file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n" .
"// Source: http://publicsuffix.org/\n\n" .
"// Generated by tòmò web analytics, (c) Yves Goergen.\n\n" .
"\$publicSuffixData=" . self::GenerateArrayCode($publicSuffixData) . ";\n" .
"?>";
file_put_contents(self::GetSuffixDataFileName(), $out);
}
private static function GenerateArrayCode(&$arr)
{
$out = 'array(';
$count = 0;
foreach ($arr as $a)
{
if ($count++)
{
$out .= ',';
}
if (is_array($a))
{
$out .= self::GenerateArrayCode($a);
}
else if (is_string($a))
{
$out .= '\'' . addslashes($a) . '\'';
}
else
{
$out .= $a;
}
}
$out .= ')';
return $out;
}
}
class Tomo
{
private $lockId;
private $newVisitorId;
private $response = array();
private $trackDntLevel = 0;
private $maxActivityTimespan = 1800;
private $locationValuesCache;
private $referrerValuesCache;
private $browserValuesCache;
private $startTime;
private $dnsTime;
private $lockWaitTime;
private $writePostLog = false;
private $crossDomainSupport = true;
const key_pageviewId          = 'pv';
const key_visitorId           = 'vi';
const key_newVisitor          = 'nv';
const key_newSession          = 'ns';
const key_pageTitle           = 'pt';
const key_pageLanguage        = 'la';
const key_utcOffset           = 'ut';
const key_screenWidth         = 'sw';
const key_screenHeight        = 'sh';
const key_screenAvailWidth    = 'aw';
const key_screenAvailHeight   = 'ah';
const key_screenColors        = 'sc';
const key_cookies             = 'co';
const key_pageLoadTime        = 'pl';
const key_campaign            = 'ca';
const key_campaignLocalRedir  = 'cl';
const key_pageUrl             = 'pu';
const key_referrer            = 're';
const key_viewportWidth       = 'vw';
const key_viewportHeight      = 'vh';
const key_flash               = 'fl';
const key_java                = 'ja';
const key_quicktime           = 'qt';
const key_silverlight         = 'sl';
const key_windowsmedia        = 'wm';
const key_adblock             = 'ab';
const key_prerender           = 'pr';
const key_origPageviewId      = 'op';
const key_activityTimespan    = 'at';
const key_duplicatePageview   = 'dp';
const key_domStorageVisitorId = 'dv';
const key_outlinkUrl          = 'ou';
const key_downloadUrl         = 'du';
const key_imgZoomUrl          = 'iu';
const key_sectionExpand       = 'se';
const key_siteSearch          = 'ss';
const action_Outlink = 1;
const action_Download = 2;
const action_ImgZoom = 3;
const action_SectionExpand = 4;
const action_SiteSearch = 5;
const varNamePattern = '/^[a-z][a-z0-9]*$/i';
public function SetWritePostLog($v)
{
$this->writePostLog = $v;
}
public static function FixParsePostData()
{
if (empty($_POST))
{
$dataStr = file_get_contents('php://input');
if (substr($dataStr, 0, 6) == '!POST&')
{
parse_str(substr($dataStr, 6), $_POST);
$_POST['_fromText'] = true;
$_REQUEST = array_merge($_GET, $_POST);
}
}
}
public function HandlePostRequest()
{
$this->startTime = microtime(true);
$t0 = microtime(true);
UnbEnvironment::GetClientHostName();
$this->dnsTime = (microtime(true) - $t0) * 1000;
if (!UnbEnvironment::IsBrowser()) return;
if (!$this->crossDomainSupport && $_SERVER['HTTP_ORIGIN']) return;
if (UnbEnvironment::DoNotTrack())
{
$this->trackDntLevel = UnbConfiguration::Get('track.dnt.level', 0);
}
$isPageviewRequest = false;
if ($_GET['imgns'])
$isPageviewRequest = true;
else if (!$_REQUEST[self::key_pageviewId])
$isPageviewRequest = true;
else
$isPageviewRequest = false;
$optoutCookieName = UnbConfiguration::Get('cookie.name.optout', 'tomo_optout');
$optoutToken = $_COOKIE[$optoutCookieName];
$password = UnbConfiguration::Get('optout.password');
$saltLength = 8;
if (isset($optoutToken))
{
$salt = substr($optoutToken, 0, $saltLength);
if ($optoutToken != $salt . md5($salt . $password))
{
$optoutToken = null;
}
}
if ($this->trackDntLevel < 4 && !isset($optoutToken))
{
try
{
$maxDelay = 5;
$interval = 25;
$retries = $maxDelay * (1000 / $interval);
$haveLock = false;
$t0 = microtime(true);
while ($retries > 0)
{
if ($haveLock = $this->AcquireLock()) break;
usleep($interval * 1000);
$retries--;
}
if ($haveLock)
{
$this->lockWaitTime = (microtime(true) - $t0) * 1000;
UnbDebug::Trace('AcquireLock: success, waited ' . round($this->lockWaitTime, 2) . ' ms, lock id: ' . $this->lockId);
$this->TrackRequest();
}
else
{
UnbDebug::Trace('EXIT: Could not acquire lock on IP address in ' . $maxDelay . ' seconds');
exit();
}
}
catch (Exception $ex)
{
$errText = get_class($ex) . ': ' . $ex->getMessage() . ' (code ' . $ex->getCode() . ")\n" . UnbDebug::GetCallTrace($ex);
}
$this->ReleaseLock();
$this->PostLog($errText, $isPageviewRequest);
}
if ($this->trackDntLevel < 2 && $this->newVisitorId)
{
$days = intval(UnbConfiguration::Get('cookie.months', 12)) * 30.44;
if ($this->trackDntLevel >= 1)
{
$days = 7;
}
$expire = time() + intval($days * 86400);
setcookie(
UnbConfiguration::Get('cookie.name.visitor', '_tid'),
$this->EncodeVisitorIdForCookie($this->newVisitorId),
$expire,
UnbConfiguration::Get('cookie.path'));
}
if ($this->response[self::key_pageviewId])
{
$privacy = '';
if ($this->trackDntLevel >= 2)
$privacy = 'No visitor cookie is set so we cannot recognise you next time around.';
if ($this->trackDntLevel == 3)
$privacy .= ' Also, activity timespan and in-page events will not be tracked.';
if ($this->trackDntLevel >= 4)
$privacy .= ' Also, page views will not be tracked and your data is not processed.';
if ($privacy)
$this->response['DNT_Info'] = 'Your privacy is respected: ' . $privacy;
}
if ($this->trackDntLevel >= 2 && $this->response[self::key_visitorId])
{
$this->response[self::key_visitorId] = 0;
}
if ($this->trackDntLevel >= 3 && $this->response[self::key_pageviewId])
{
$this->response[self::key_pageviewId] = 0;
}
header('Pragma: no-cache');
header('Cache-Control: no-cache');
header('Expires: 0');
if ($this->crossDomainSupport)
header('Access-Control-Allow-Origin: *');
if ($_GET['img'] || $_GET['imgns'])
{
header('Content-Type: image/png');
echo base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=');
}
else if (count($this->response))
{
header('Content-Type: application/json');
echo json_encode($this->response);
}
}
private function PostLog($errText, $isPageviewRequest)
{
if (!$this->writePostLog) return;
list($usec, $time) = explode(' ', microtime());
$msg = date('Y-m-d H:i:s', $time) . '.' . substr($usec, 2, 6) . "\n";
$msg .= "RequestTag: " . UnbDebug::GetRequestTag() . "\n";
$msg .= "IP: " . UnbEnvironment::GetClientAddress() . "\n";
$msg .= "Referrer: " . UnbEnvironment::GetReferrer() . "\n";
$msg .= "User agent: " . $_SERVER['HTTP_USER_AGENT'] . "\n";
$msg .= "GET:\n";
foreach ($_GET as $name => $value)
{
$msg .= "  " . $name . " = " . str_replace("\n", "\n" . str_repeat(' ', strlen($name) + 5), $value) . "\n";
}
$msg .= "POST:\n";
foreach ($_POST as $name => $value)
{
$msg .= "  " . $name . " = " . str_replace("\n", "\n" . str_repeat(' ', strlen($name) + 5), $value) . "\n";
}
$msg .= "HTTP:\n";
foreach ($_SERVER as $name => $value)
{
if (substr($name, 0, 5) == 'HTTP_')
{
$name = strtolower(substr($name, 5));
$name = str_replace('_', '-', $name);
$msg .= "  " . $name . " = " . str_replace("\n", "\n" . str_repeat(' ', strlen($name) + 5), $value) . "\n";
}
}
$msg .= "Visitor cookie: " . $this->GetVisitorIdFromCookie() . ' (' . $_COOKIE[UnbConfiguration::Get('cookie.name.visitor', '_tid')] . ")\n";
if ($errText)
{
$msg .= "ERROR: " . $errText . "\n";
}
$ms = round((microtime(true) - $this->startTime) * 1000, 2);
$msg .= "Processing time : " . $ms . " ms" . ($ms > 100 ? ' *** LONG PROCESSING TIME ***' : '') . "\n";
$msg .= "  Lock wait time: " . round($this->lockWaitTime, 2) . " ms\n";
$msg .= "  DNS time      : " . round($this->dnsTime, 2) . " ms\n";
if ($this->newVisitorId)
{
$msg .= "Set cookie: " . $this->newVisitorId . " (" . $this->EncodeVisitorIdForCookie($this->newVisitorId) . ")\n";
}
if (!$_GET['img'] && !$_GET['imgns'] && count($this->response))
{
$msg .= "JSON response:\n";
foreach ($this->response as $key => $value)
{
$msg .= '  ' . $key . ' = ' . $value . "\n";
}
}
$msg .= "\n";
if (!is_dir(__DIR__ . '/logs'))
{
mkdir(__DIR__ . '/logs');
}
file_put_contents(__DIR__ . '/logs/post.' . date('Y-m-d', $time) . '.log', $msg, FILE_APPEND);
}
public function TrackRequest()
{
UnbDebug::Trace('ENTER TrackRequest');
if ($_POST)
{
$rawData = $_POST;
}
else
{
$rawData = $_GET;
}
if ($_GET['imgns'])
{
$rawData['javascript'] = false;
if (!$rawData[self::key_pageUrl])
{
$rawData[self::key_pageUrl] = $_SERVER['HTTP_REFERER'];
}
}
else
{
$rawData['javascript'] = true;
}
if (!isset($rawData[self::key_pageviewId]) && !strlen($rawData[self::key_pageUrl]))
throw new Exception('No pageview ID and page URL set in POST data');
$now = time();
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
if (isset($rawData[self::key_pageviewId]))
{
$pageviewId = intval($rawData[self::key_pageviewId]);
$pageview = $this->FindPageviewById($pageviewId);
if (isset($pageview))
{
UnbDebug::Trace('  Branch: Pageview update request');
$sessionId = intval($pageview['SessionId']);
$session = $this->FindSessionById($sessionId);
$visitorId = $this->GetVisitorIdFromCookie();
if (isset($visitorId))
{
if (!$this->ExistsVisitorId($visitorId))
{
UnbDebug::Trace('    Noticed INVALID visitor cookie ' . $visitorId);
$visitorId = null;
}
}
else
{
UnbDebug::Trace('    Noticed missing visitor cookie');
}
if (!isset($visitorId) &&
intval($rawData[self::key_domStorageVisitorId]))
{
$visitorId = intval($rawData[self::key_domStorageVisitorId]);
if (!$this->ExistsVisitorId($visitorId))
{
UnbDebug::Trace('    Noticed INVALID visitor ID from DOM localStorage ' . $visitorId);
$visitorId = null;
}
else
{
UnbDebug::Trace('    Using visitor ID from DOM localStorage ' . $visitorId);
}
}
if (isset($visitorId))
{
if ($visitorId != $session['VisitorId'])
{
UnbDebug::Trace('    Merging two visitor entries');
$this->MergeVisitors($visitorId, intval($session['VisitorId']));
$session = $this->FindSessionById($sessionId);
}
}
else
{
if (!isset($visitorId) && $this->trackDntLevel < 2)
{
$rawData[self::key_cookies] = 0;
}
$visitorId = intval($session['VisitorId']);
$this->newVisitorId = $visitorId;
}
$currentSessions = $this->GetCurrentSessionsForVisitor($visitorId);
if (count($currentSessions) > 1)
{
UnbDebug::Trace('    Found multiple sessions, merging');
for ($i = 0; $i < count($currentSessions); $i++)
{
if ($currentSessions[$i]['SessionId'] == $sessionId)
continue;
$this->MergeSessions($session, $currentSessions[$i]);
$session = $this->FindSessionById($sessionId);
}
}
$this->UpdateSession($session, $rawData, $now);
if (!$rawData[self::key_duplicatePageview] && $pageview['PageLoadTime'] !== '0')
{
$pageviews = $this->FindDuplicatePageviews($pageview);
if (count($pageviews))
{
UnbDebug::Trace('    Found multiple pageviews, merging');
for ($i = 0; $i < count($pageviews); $i++)
{
$this->MergePageviews($pageview, $pageviews[$i]);
$pageview = $this->FindPageviewById($pageviewId);
}
}
}
UnbDebug::Trace('    Updating pageview');
$this->UpdatePageview($pageview, $rawData, $now);
if ($rawData[self::key_outlinkUrl])
{
$type = self::action_Outlink;
$url = $rawData[self::key_outlinkUrl];
$data = null;
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
if ($rawData[self::key_downloadUrl])
{
$type = self::action_Download;
$url = $rawData[self::key_downloadUrl];
$data = null;
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
if ($rawData[self::key_imgZoomUrl])
{
$type = self::action_ImgZoom;
$url = $rawData[self::key_imgZoomUrl];
$data = null;
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
if ($rawData[self::key_sectionExpand])
{
$type = self::action_SectionExpand;
$url = null;
$data = $rawData[self::key_sectionExpand];
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
if ($rawData[self::key_siteSearch])
{
$type = self::action_SiteSearch;
$url = null;
$data = $rawData[self::key_siteSearch];
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
$confActions = self::GetConfActions();
foreach ($confActions as $name => $type)
{
if (isset($rawData['ac_' . $name . '_u']) || isset($rawData['ac_' . $name . '_d']))
{
$url = $rawData['ac_' . $name . '_u'];
$data = $rawData['ac_' . $name . '_d'];
if (!strlen($data))
$data = null;
$actions = $this->FindDuplicateActions($now, $pageviewId, $type, $url, $data);
if (count($actions) == 0)
{
$this->CreateAction($now, $pageviewId, $type, $url, $data);
}
}
}
if ($rawData[self::key_duplicatePageview] && $rawData['javascript'])
{
UnbDebug::Trace('  Pageview duplicate request');
$newRawData = array();
foreach ($rawData as $key => $value)
{
if (preg_match('/^n_(.+)$/', $key, $m))
{
$newRawData[$m[1]] = $value;
}
}
$pageview = $this->DuplicatePageview($pageview, $newRawData, $now);
$this->response[self::key_pageviewId] = intval($pageview['PageviewId']);
}
}
else
{
UnbDebug::Trace('  Branch: INVALID pageview update request');
throw new Exception('Invalid pageview update request: Pageview ID is invalid');
}
}
else
{
UnbDebug::Trace('  Branch: Page enter request, js=' . ($rawData['javascript'] ? 1 : 0));
$visitorId = $this->GetVisitorIdFromCookie();
if (isset($visitorId))
{
if (!$this->ExistsVisitorId($visitorId))
{
UnbDebug::Trace('    Noticed INVALID visitor cookie ' . $visitorId);
$visitorId = null;
}
else
{
if (!$rawData['javascript'])
{
$rawData[self::key_cookies] = 1;
}
}
}
if (!isset($visitorId) &&
intval($rawData[self::key_domStorageVisitorId]))
{
$visitorId = intval($rawData[self::key_domStorageVisitorId]);
if (!$this->ExistsVisitorId($visitorId))
{
UnbDebug::Trace('    Noticed INVALID visitor ID from DOM localStorage ' . $visitorId);
$visitorId = null;
}
else
{
UnbDebug::Trace('    Using visitor ID from DOM localStorage ' . $visitorId);
}
}
if (!$visitorId)
{
$sessionData = $this->PrepareSessionData($rawData, $now, 0, false);
$session = $this->FindSimilarSession($sessionData);
if ($session)
{
UnbDebug::Trace('    Visitor identified by similar session data');
$visitorId = intval($session['VisitorId']);
$this->newVisitorId = $visitorId;
}
else if (intval($rawData[self::key_origPageviewId]) > 0)
{
$origPVId = intval($rawData[self::key_origPageviewId]);
$pageview = $this->FindPageviewById($origPVId);
if (isset($pageview) &&
$now < UnbDatabase::ConvertDateTime($pageview['PageviewTime']) + 86400)
{
$sessionId = intval($pageview['SessionId']);
$session = $this->FindSessionById($sessionId);
$visitorId = intval($session['VisitorId']);
$this->newVisitorId = $visitorId;
}
}
}
if (!$visitorId)
{
UnbDebug::Trace('    Visitor cannot be identified -> create visitor, session and pageview');
$visitorId = $this->CreateVisitor();
$this->response[self::key_visitorId] = $visitorId;
$this->newVisitorId = $visitorId;
$this->response[self::key_newVisitor] = true;
$session = $this->CreateSession($rawData, $now, $visitorId, true);
$this->response[self::key_newSession] = true;
$pageview = $this->CreatePageview($rawData, $now, intval($session['SessionId']), true);
$this->response[self::key_pageviewId] = intval($pageview['PageviewId']);
}
else
{
$this->response[self::key_visitorId] = $visitorId;
$this->response[self::key_newVisitor] = false;
$currentSessions = $this->GetCurrentSessionsForVisitor($visitorId);
if (!$currentSessions)
{
UnbDebug::Trace('    No active sessions -> Create session and pageview');
$session = $this->CreateSession($rawData, $now, $visitorId, false);
$this->response[self::key_newSession] = true;
$pageview = $this->CreatePageview($rawData, $now, intval($session['SessionId']), true);
$this->response[self::key_pageviewId] = intval($pageview['PageviewId']);
}
else
{
UnbDebug::Trace('    ' . count($currentSessions) . ' active session(s) found');
$session = $currentSessions[0];
$sessionId = intval($session['SessionId']);
if (count($currentSessions) > 1)
{
for ($i = 1; $i < count($currentSessions); $i++)
{
$this->MergeSessions($session, $currentSessions[$i]);
$session = $this->FindSessionById($sessionId);
}
}
$this->UpdateSession($session, $rawData, $now);
$this->response[self::key_newSession] = false;
$pageviewData = $this->PreparePageviewData($rawData, $now, $sessionId, false);
$pageviews = $this->FindDuplicatePageviews($pageviewData);
$pageview = $pageviews[0];
if (count($pageviews))
{
UnbDebug::Trace('    ' . count($pageviews) . ' duplicate pageview(s) found');
}
if (count($pageviews) > 1)
{
$pageviewId = intval($pageview['PageviewId']);
for ($i = 1; $i < count($pageviews); $i++)
{
$this->MergePageviews($pageview, $pageviews[$i]);
$pageview = $this->FindPageviewById($pageviewId);
}
}
if (count($pageviews))
{
UnbDebug::Trace('    Existing pageview ' . $pageview['PageviewId'] . ' updated');
$this->UpdatePageview($pageview, $rawData, $now);
$this->response[self::key_activityTimespan] = intval($pageview['ActivityTimespan']) * 1000;
}
else
{
UnbDebug::Trace('    Pageview created');
$pageview = $this->CreatePageview($rawData, $now, $sessionId, false);
}
$this->response[self::key_pageviewId] = intval($pageview['PageviewId']);
}
}
}
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function GetVisitorIdFromCookie()
{
$vCookieName = UnbConfiguration::Get('cookie.name.visitor', '_tid');
$vCookie = $_COOKIE[$vCookieName];
if (!strlen($vCookie)) return null;
//return intval(hexdec($vCookie));
$data = @unpack('N', base64_decode($vCookie));
$visitorId = $data[1];
if ($visitorId <= 0) return null;
return $visitorId;
}
private function EncodeVisitorIdForCookie($visitorId)
{
if (!is_int($visitorId))
throw new InvalidArgumentException('Invalid argument type: $visitorId');
if ($visitorId <= 0)
throw new InvalidArgumentException('Argument out of range: $visitorId');
//return str_pad(dechex($visitorId), 8, '0', STR_PAD_LEFT);
return rtrim(base64_encode(pack('N', $visitorId)), '=');
}
private function GetLocationSessionValues($rawData)
{
if (isset($this->locationValuesCache)) return $this->locationValuesCache;
$ipAddress = UnbEnvironment::GetClientAddress();
$ipVersion = strpos($ipAddress, '.') !== false ? 4 : 6;
$locData = GeoIP::LookupIP($ipAddress);
$countryCode = $locData['cc'];
$region = $locData['region'];
$city = strlen($locData['city']) ? utf8_encode($locData['city']) : null;
$lat = $locData['lat'];
$lng = $locData['lng'];
$hostName = UnbEnvironment::GetClientHostName();
$provider = null;
$providerTld = null;
if ($hostName != $ipAddress)
{
$parts = explode('.', $hostName);
$provider = array_pop($parts);
$providerTld = $provider;
$provider = PublicSuffix::GetPublicSuffix($hostName);
if ($provider === false)
{
$provider = $providerTld;
$next = array_pop($parts);
if ($next) $provider = $next . '.' . $provider;
if (in_array($next, array('co', 'com', 'gov', 'net', 'org')) ||
$providerTld == 'ch' && strlen($next) == 2 ||
$providerTld == 'uk' && in_array($next, array('ac', 'ltd', 'me', 'nhs', 'plc', 'sch')))
{
$next = array_pop($parts);
if ($next) $provider = $next . '.' . $provider;
}
}
}
if ($provider == 'opera-mini.net')
{
$countryCode = null;
$region = null;
$city = null;
$lat = null;
$lng = null;
}
$ccGood = self::ValidateCountry(
$countryCode,
$region,
$city,
$lat,
$lng,
$rawData[self::key_utcOffset],
$providerTld,
UnbEnvironment::GetClientLanguages(),
$rawData[self::key_referrer]);
if (UnbConfiguration::Get('track.ip.hash', false))
{
$ipAddress = base64_encode(substr(md5($ipAddress, true), 0, 9));
}
$values = array(
'IPAddress' => $ipAddress,
'IPVersion' => $ipVersion,
'CountryCode' => $countryCode,
);
if ($ccGood)
{
if (strlen($region))
{
$values['RegionText'] = Tomo::GetTextId($region);
}
if (strlen($city))
{
$values['CityText'] = Tomo::GetTextId($city);
}
if (isset($lat) && isset($lng))
{
$values['Lat'] = $lat * 10000;
$values['Lng'] = $lng * 10000;
}
}
else
{
$values['RegionText'] = null;
$values['CityText'] = null;
$values['Lat'] = null;
$values['Lng'] = null;
}
if (isset($provider))
{
$values['ProviderText'] = Tomo::GetTextId($provider);
}
$this->locationValuesCache = $values;
return $values;
}
private function GetReferrerSessionValues($rawData)
{
if (isset($this->referrerValuesCache)) return $this->referrerValuesCache;
$values = array();
$oref = $this->GetOriginalRefFromPageUrl($rawData[self::key_pageUrl]);
if (isset($oref))
{
$refUrl = parse_url($rawData[self::key_referrer]);
$values['CampaignText'] = Tomo::GetTextId($refUrl['host']);
UnbDebug::Trace('      Manual old-site redirection (Campaign): ' . $oref);
$rawData[self::key_referrer] = $oref;
}
$rref = $this->GetRedirectRefFromPageUrl($rawData[self::key_pageUrl]);
if (isset($rref))
{
$refUrl = parse_url($rref);
$values['CampaignText'] = Tomo::GetTextId($refUrl['host']);
UnbDebug::Trace('      Automatic old-site redirection (Campaign): ' . $refUrl['host']);
}
if (strlen($rawData[self::key_referrer]))
{
$refUrl = parse_url($rawData[self::key_referrer]);
$refSsl = $refUrl['scheme'] == 'https';
$refHost = $refUrl['host'] . ($refUrl['port'] ? ':' . $refUrl['port'] : '');
$refPath = $refUrl['path'];
if (isset($refUrl['query']))
$refPath .= '?' . $refUrl['query'];
if (isset($refUrl['fragment']))
$refPath .= '#' . $refUrl['fragment'];
$searchTerms = UnbEnvironment::GetSearchTerms($rawData[self::key_referrer]);
if ($searchTerms)
{
$values['SearchKeywordsText'] = Tomo::GetTextId($searchTerms);
}
if (UnbEnvironment::GetSearchEngine())
{
$values['SearchEngineText'] = Tomo::GetTextId(UnbEnvironment::GetSearchEngine());
$refPath = '';
}
if (UnbEnvironment::GetSearchImageUrl())
{
$values['SearchImageUrlText'] = Tomo::GetTextId(UnbEnvironment::GetSearchImageUrl());
}
if (UnbEnvironment::GetSearchPageNumber())
{
$values['SearchResultPage'] = UnbEnvironment::GetSearchPageNumber();
}
$values['ReferrerSsl'] = $refSsl ? 1 : 0;
$values['ReferrerHostText'] = Tomo::GetTextId($refHost);
$values['ReferrerUrlPathText'] = Tomo::GetTextId($refPath);
}
if (strlen($rawData[self::key_campaign]))
{
$values['CampaignText'] = Tomo::GetTextId($rawData[self::key_campaign]);
UnbDebug::Trace('      Campaign: ' . $rawData[self::key_campaign]);
}
else
{
$campaign = $this->GetCampaignFromPageUrl($rawData[self::key_pageUrl]);
if ($campaign)
{
$values['CampaignText'] = Tomo::GetTextId($campaign);
UnbDebug::Trace('      Campaign: ' . $campaign);
}
}
$this->referrerValuesCache = $values;
return $values;
}
private function GetCampaignFromPageUrl($url)
{
$pageUrl = parse_url($url);
parse_str(ltrim($pageUrl['query'], '?'), $vars);
return $vars['ref'];
}
private function GetOriginalRefFromPageUrl($url)
{
$pageUrl = parse_url($url);
parse_str(ltrim($pageUrl['query'], '?'), $vars);
return $vars['oref'];
}
private function GetRedirectRefFromPageUrl($url)
{
$pageUrl = parse_url($url);
parse_str(ltrim($pageUrl['query'], '?'), $vars);
return $vars['rref'];
}
private function GetBrowserSessionValues($rawData)
{
if (isset($this->browserValuesCache)) return $this->browserValuesCache;
$language = '';
$langs = UnbEnvironment::GetClientLanguages();
if (count($langs))
{
switch (UnbConfiguration::Get('track.language.level', 2))
{
case 1:
$language = substr($langs[0], 0, 2);
break;
case 2:
default:
$language = $langs[0];
break;
case 3:
$language = join(',', $langs);
break;
}
}
$platform = UnbEnvironment::GetClientPlatform();
if (strpos($platform, '@') !== false)
{
list($browser, $os) = explode('@', $platform);
}
else
{
$browser = $platform;
$os = null;
}
$values = array(
'BrowserText' => self::GetTextId($browser),
'OperatingSystemText' => self::GetTextId($os),
'Mobile' => UnbEnvironment::IsMobile() ? 1 : 0,
'Language' => $language,
'JavaScript' => $rawData['javascript'] ? 1 : 0,
);
$dnt = UnbEnvironment::DoNotTrack();
if (isset($dnt))
{
$values['DNT'] = $dnt ? 1 : 0;
}
if (strlen($rawData[self::key_utcOffset]))
{
$values['UtcOffset'] = intval($rawData[self::key_utcOffset]);
}
if (strlen($rawData[self::key_cookies]))
{
$values['Cookies'] = $rawData[self::key_cookies] ? 1 : 0;
}
if (strlen($rawData[self::key_flash]))
{
$values['Flash'] = $rawData[self::key_flash] ? 1 : 0;
}
if (strlen($rawData[self::key_java]))
{
$values['Java'] = $rawData[self::key_java] ? 1 : 0;
}
if (strlen($rawData[self::key_quicktime]))
{
$values['Quicktime'] = $rawData[self::key_quicktime] ? 1 : 0;
}
if (strlen($rawData[self::key_silverlight]))
{
$values['Silverlight'] = $rawData[self::key_silverlight] ? 1 : 0;
}
if (strlen($rawData[self::key_windowsmedia]))
{
$values['WindowsMedia'] = $rawData[self::key_windowsmedia] ? 1 : 0;
}
if (strlen($rawData[self::key_adblock]))
{
$values['AdBlock'] = $rawData[self::key_adblock] ? 1 : 0;
}
if ($rawData[self::key_screenWidth] > 0)
{
$values['ScreenWidth'] = intval($rawData[self::key_screenWidth]);
}
if ($rawData[self::key_screenHeight] > 0)
{
$values['ScreenHeight'] = intval($rawData[self::key_screenHeight]);
}
if ($rawData[self::key_screenAvailWidth] > 0)
{
$values['ScreenAvailWidth'] = intval($rawData[self::key_screenAvailWidth]);
}
if ($rawData[self::key_screenAvailHeight] > 0)
{
$values['ScreenAvailHeight'] = intval($rawData[self::key_screenAvailHeight]);
}
if ($rawData[self::key_screenColors] > 0)
{
$values['ScreenColors'] = intval($rawData[self::key_screenColors]);
}
if ($rawData[self::key_viewportWidth] > 0)
{
$values['ViewportWidth'] = intval($rawData[self::key_viewportWidth]);
}
if ($rawData[self::key_viewportHeight] > 0)
{
$values['ViewportHeight'] = intval($rawData[self::key_viewportHeight]);
}
$this->browserValuesCache = $values;
return $values;
}
private function MergeSessionHelper($session1, $session2, $field, $minmax, $preferJs)
{
$v1 = $session1[$field];
$v2 = $session2[$field];
if ($v1 === $v2)
return $v1;
if (isset($v1) && !isset($v2))
return $v1;
if (!isset($v1) && isset($v2))
return $v2;
if ($preferJs)
{
if ($session1['JavaScript'] && !$session2['JavaScript'])
return $v1;
if (!$session1['JavaScript'] && $session2['JavaScript'])
return $v2;
}
if ($minmax == 0)
return $min($v1, $v2);
if ($minmax == 1)
return $max($v1, $v2);
return $v1;
}
private function ParseUrlIntoArray($rawData, &$values)
{
if (!isset($rawData[self::key_pageUrl])) return false;
$pageUrl = parse_url($rawData[self::key_pageUrl]);
$pattern = UnbConfiguration::Get('track.url.remove-path-pattern');
if ($pattern)
$pageUrl['path'] = preg_replace($pattern, '', $pageUrl['path']);
$langPath = UnbConfiguration::Get('track.language.first-path', false);
if ($langPath && preg_match('~^/([a-z]{2}([-_][a-z]{2})?)(/|$)~i', $pageUrl['path'], $m))
{
$values['PageLanguage'] = $m[1];
$pageUrl['path'] = substr($pageUrl['path'], strlen($m[1]) + 1);
}
if (!strlen($pageUrl['path']))
$pageUrl['path'] = '/';
$values['DomainText'] = Tomo::GetTextId($pageUrl['host'] . ($pageUrl['port'] ? ':' . $pageUrl['port'] : ''));
$values['UrlPathText'] = Tomo::GetTextId($pageUrl['path']);
$values['UrlQueryText'] = Tomo::GetTextId($pageUrl['query']);
$values['UrlHashText'] = Tomo::GetTextId($pageUrl['fragment']);
$values['Ssl'] = $pageUrl['scheme'] == 'https' ? 1 : 0;
return true;
}
private function PreparePageviewData($rawData, $now, $sessionId, $entryPage)
{
if (!strlen($rawData[self::key_pageUrl]))
throw new Exception('Page URL not set in POST data');
$values = array(
'SessionId' => $sessionId,
'PageviewTime' => UnbDatabase::ConvertDateTime($now),
'PageviewUpdateTime' => UnbDatabase::ConvertDateTime($now),
'EntryPage' => $entryPage ? 1 : 0,
'JavaScriptData' => $rawData['javascript'] ? 1 : 0,
);
if (strlen($rawData[self::key_pageTitle]))
{
$values['PageTitleText'] = Tomo::GetTextId($rawData[self::key_pageTitle]);
}
$this->ParseUrlIntoArray($rawData, $values);
if (strlen($rawData[self::key_pageLanguage]))
{
$values['PageLanguage'] = $rawData[self::key_pageLanguage];
}
if (isset($rawData[self::key_activityTimespan]))
{
$activityTimespan = intval($rawData[self::key_activityTimespan]);
$activityTimespan = intval(($activityTimespan + 200) / 1000);
$pageviewTime = UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
if ($activityTimespan > $now - $pageviewTime + 2)
{
UnbDebug::Trace('  Reported activity timespan is larger than absolute pageview lifetime. Limit value.');
$activityTimespan = $now - $pageviewTime;
}
if ($activityTimespan > $this->maxActivityTimespan * 3)
{
UnbDebug::Trace('  Reported activity timespan is larger than 3 times the maximum allowed value of ' . $this->maxActivityTimespan . ' s. Clear value.');
$activityTimespan = null;
}
if ($activityTimespan > $this->maxActivityTimespan)
{
UnbDebug::Trace('  Reported activity timespan is larger than maximum allowed value of ' . $this->maxActivityTimespan . ' s. Limit value.');
$activityTimespan = $this->maxActivityTimespan;
}
$values['ActivityTimespan'] = $activityTimespan;
}
if ($rawData[self::key_pageLoadTime] != 0)
{
$values['PageLoadTime'] = intval($rawData[self::key_pageLoadTime]);
}
if (isset($rawData[self::key_prerender]))
{
$values['Prerender'] = $rawData[self::key_prerender] ? 1 : 0;
}
if (strlen($rawData[self::key_campaign]) && $rawData['javascript'])
{
$values['CampaignRedirect'] = !$rawData[self::key_campaignLocalRedir] ? 1 : 0;
}
else
{
$values['CampaignRedirect'] = $this->GetCampaignFromPageUrl($rawData[self::key_pageUrl]) != null ? 1 : 0;
}
if (intval($rawData[self::key_origPageviewId]) > 0)
{
$values['OriginalPageviewId'] = intval($rawData[self::key_origPageviewId]);
}
return $values;
}
private static function ValidateCountry(&$countryCode, &$region, &$city, &$lat, &$lng, $utcOffset, $providerTld, $langs, $referrer)
{
if (!isset($countryCode) ||
isset($lng) && isset($utcOffset))
{
$utcOffset = intval($utcOffset);
$diff = $lng / 15 - $utcOffset / 60;
if (!isset($countryCode) ||
$diff < -2 || $diff > 4)
{
if (!isset($countryCode))
{
UnbDebug::Trace('      Country not detected by GeoIP database. Looking for alternatives...');
}
else
{
UnbDebug::Trace('      Country "' . $countryCode . '" longitude ' . $lng . ' does not match time zone ' . ($utcOffset / 60) . '. Looking for alternatives...');
}
$hostnameCC = null;
if (strlen($providerTld) == 2)
{
$hostnameCC = strtoupper($providerTld);
if ($hostnameCC == 'UK') $hostnameCC = 'GB';
if (strtoupper($providerTld) != $countryCode)
{
UnbDebug::Trace('      Different country code from rdns name: ' . $hostnameCC);
}
else
{
UnbDebug::Trace('      rdns name gives the same country, so keep it.');
}
}
$localeCC = null;
if (count($langs) >= 1 && strlen($langs[0]) == 5)
{
$localeCC = substr($langs[0], 3);
if (substr($langs[0], 3) != $countryCode)
{
UnbDebug::Trace('      Different country code from first language locale: ' . $localeCC);
}
else
{
UnbDebug::Trace('      First language locale gives the same country, so keep it.');
}
}
else if (count($langs) >= 2 &&
strlen($langs[0]) == 2 &&
strlen($langs[1]) == 5 &&
$langs[0] == substr($langs[1], 0, 2))
{
$localeCC = substr($langs[1], 3);
if (substr($langs[1], 3) != $countryCode)
{
UnbDebug::Trace('      Different country code from second language locale: ' . $localeCC);
}
else
{
UnbDebug::Trace('      Second language locale gives the same country, so keep it.');
}
}
$googleCC = null;
if (preg_match('_^https?://www\.google(?:\.(?:com?|net|org))?\.([a-z]{2})/(?:url|search|m|imgres)_', $referrer, $m))
{
$googleCC = strtoupper($m[1]);
if ($googleCC == 'UK') $googleCC = 'GB';
if (strtoupper($m[1]) != $countryCode)
{
UnbDebug::Trace('      Different country code from country-specific Google search: ' . $googleCC);
}
else
{
UnbDebug::Trace('      Country-specific Google search gives the same country, so keep it.');
}
}
$newCC = null;
if (isset($hostnameCC))
{
$newCC = $hostnameCC;
}
else if (isset($localeCC) && isset($googleCC))
{
if ($localeCC == $googleCC)
{
$newCC = $localeCC;
}
else
{
$ccs = array($localeCC, $googleCC);
usort($ccs, array(__CLASS__, 'SpecificCountryCodeComparer'));
$newCC = $ccs[0];
}
}
else if (isset($localeCC))
{
$newCC = $localeCC;
}
else if (isset($googleCC))
{
$newCC = $googleCC;
}
if (isset($newCC) &&
isset($utcOffset))
{
}
if (isset($newCC) && $newCC != $countryCode)
{
$countryCode = $newCC;
$region = null;
$city = null;
$lat = null;
$lng = null;
return false;
}
}
}
return true;
}
private static function GetCountryCodeSpecificity($cc)
{
switch ($cc)
{
case 'AR': return 40500;
case 'AT': return 8400;
case 'AU': return 22300;
case 'BE': return 10800;
case 'BR': return 195000;
case 'CA': return 34300;
case 'CH': return 7900;
case 'CN': return 1339700;
case 'DE': return 81700;
case 'ES': return 47100;
case 'FR': return 65400;
case 'GB': return 61800;
case 'HK': return 7100;
case 'MX': return 112300;
case 'NL': return 16700;
case 'NZ': return 4400;
case 'PT': return 10600;
case 'TW': return 9000;
case 'US': return 311500;
default: 0;
}
}
private static function SpecificCountryCodeComparer($a, $b)
{
$sa = self::GetCountryCodeSpecificity($a);
$sb = self::GetCountryCodeSpecificity($b);
return $sa - $sb;
}
private function ExistsVisitorId($visitorId)
{
if (!is_int($visitorId))
throw new InvalidArgumentException('Invalid argument type: $visitorId');
if ($visitorId < 0)
throw new InvalidArgumentException('Argument out of range: $visitorId');
$db = UnbDatabase::GetInstance();
$rec = $db->QueryRecord('SELECT "VisitorId" FROM "#visitor" WHERE "VisitorId" = ?', $visitorId);
return $rec ? true : false;
}
private function CreateVisitor()
{
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$selectionRetries = 3;
while (true)
{
$ps = $db->Prepare('SELECT COUNT(*) FROM "#visitor" WHERE "VisitorId" = ?');
$retries = 20;
$minId = 1;
$maxId = 2147483647;
do
{
$tmpNewId = mt_rand($minId, $maxId);
if ($retries == 1)
{
$tmpNewId = $minId;
}
$count = $db->QueryValue($ps, $tmpNewId);
if ($count > 0 && $retries <= 3)
{
$tmpNewId = $db->QueryValue(
'SELECT MIN("VisitorId" + 1) AS "FreeId"
FROM "#visitor" v1
WHERE NOT EXISTS (SELECT * FROM "#visitor" v2 WHERE v2."VisitorId" = v1."VisitorId" + 1)
AND "VisitorId" >= ?',
$tmpNewId);
if ($tmpNewId <= $maxId)
{
$count = 0;
}
}
$retries--;
}
while ($count > 0 && $retries > 0);
if ($count > 0)
{
throw new Exception('No free visitor ID available.');
}
try
{
$db->Execute('INSERT INTO "#visitor" ("VisitorId") VALUES (?)', $tmpNewId);
}
catch (UnbDatabaseException $ex)
{
if ($db->IsConstraintError())
{
if (strpos($db->GetErrorMessage(), 'PRIMARY') !== false && $selectionRetries-- > 0)
continue;
}
throw $ex;
}
break;
}
$db->Commit();
return $tmpNewId;
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function MergeVisitors($visitorId1, $visitorId2)
{
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$sValues = array(
'VisitorId' => $visitorId1
);
$db->Update('session', $sValues, '"VisitorId" = ?', $visitorId2);
$visitor1 = $db->QueryRecord('SELECT * FROM "#visitor" WHERE "VisitorId" = ?', $visitorId1);
$visitor2 = $db->QueryRecord('SELECT * FROM "#visitor" WHERE "VisitorId" = ?', $visitorId2);
$confVisitorVars = self::GetConfVisitorVars();
$values = array();
foreach ($confVisitorVars as $name => $type)
{
if (!isset($visitor1['vv_' . $name]) && isset($visitor2['vv_' . $name]))
$values['vv_' . $name] = $visitor2['vv_' . $name];
else if (isset($visitor1['vv_' . $name]) && isset($visitor2['vv_' . $name]))
$values['vv_' . $name] = max($visitor1['vv_' . $name], $visitor2['vv_' . $name]);
}
if (count($values))
{
$db->Update('visitor', $values, '"VisitorId" = ?', $visitorId1);
}
$db->Delete('visitor', '"VisitorId" = ?', $visitorId2);
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function FindSessionById($sessionId)
{
if (!is_int($sessionId))
throw new InvalidArgumentException('Invalid argument type: $sessionId');
if ($sessionId <= 0)
throw new InvalidArgumentException('Argument out of range: $sessionId');
$db = UnbDatabase::GetInstance();
$rec = $db->QueryRecord('SELECT * FROM "#session" WHERE "SessionId" = ?', $sessionId);
if (!$rec) return null;
return $rec;
}
private function ExistsSessionId($sessionId)
{
if (!is_int($sessionId))
throw new InvalidArgumentException('Invalid argument type: $sessionId');
if ($sessionId <= 0)
throw new InvalidArgumentException('Argument out of range: $sessionId');
$db = UnbDatabase::GetInstance();
$rec = $db->QueryRecord('SELECT "SessionId" FROM "#session" WHERE "SessionId" = ?', $sessionId);
return $rec ? true : false;
}
private function GetCurrentSessionsForVisitor($visitorId)
{
if (!is_int($visitorId))
throw new InvalidArgumentException('Invalid argument type: $visitorId');
if ($visitorId <= 0)
throw new InvalidArgumentException('Argument out of range: $visitorId');
$db = UnbDatabase::GetInstance();
$recs = $db->Query(
'SELECT *
FROM "#session"
WHERE "VisitorId" = ?
AND "SessionUpdateTime" > ?
ORDER BY "SessionUpdateTime" DESC',
array(
$visitorId,
UnbDatabase::ConvertDateTime(time() - intval(UnbConfiguration::Get('session.maxidle', '30')) * 60)
));
if (!$recs) return null;
return $recs;
}
private function FindSimilarSession($dbData)
{
$db = UnbDatabase::GetInstance();
if ($this->trackDntLevel >= 2)
{
$recs = $db->Query(
'SELECT *
FROM "#session"
WHERE "DNT" = 1 AND "SessionUpdateTime" > ?
ORDER BY "SessionUpdateTime"',
array(
UnbDatabase::ConvertDateTime(time() - intval(UnbConfiguration::Get('session.maxidle', '30')) * 60),
));
}
else
{
$recs = $db->Query(
'SELECT *
FROM "#session"
WHERE (("Cookies" = 0 OR "Cookies" IS NULL) AND "SessionUpdateTime" > ?)
OR "SessionStartTime" > ?
ORDER BY "SessionUpdateTime"',
array(
UnbDatabase::ConvertDateTime(time() - intval(UnbConfiguration::Get('session.maxidle', '30')) * 60),
UnbDatabase::ConvertDateTime(time() - 10)
));
}
if (!$recs) return null;
$minSimilarity = 10;
$bestSession = null;
$bestSimilarity = 0;
UnbDebug::Trace('    considering ' . count($recs) . ' sessions for similarity');
foreach ($recs as $session)
{
$similarity = $this->ComputeSimilarity($dbData, $session);
if ($similarity > $minSimilarity && $similarity > $bestSimilarity)
{
$bestSimilarity = $similarity;
$bestSession = $session;
}
}
if (isSet($bestSession))
{
UnbDebug::Trace('    bestSession: SessionId=' . $bestSession['SessionId'] . ', SessionStartTime=' . $bestSession['SessionStartTime'] . ', SessionUpdateTime=' . $bestSession['SessionUpdateTime'] . ', Cookies=' . $bestSession['Cookies']);
}
return $bestSession;
}
private function ComputeSimilarity($session1, $session2)
{
$sum = 0;
$sum += ($session1['IPAddress'] == $session2['IPAddress'] ? 4 : -4);
$sum += ($session1['UserAgentString'] == $session2['UserAgentString'] ? 4 : -4);
$sum += ($session1['LanguagesString'] == $session2['LanguagesString'] ? 4 : -4);
if ($session1['JavaScript'] && $session2['JavaScript'])
{
$sum += ($session1['ScreenWidth'] == $session2['ScreenWidth'] &&
$session1['ScreenHeight'] == $session2['ScreenHeight'] &&
$session1['ScreenColors'] == $session2['ScreenColors'] ? 2 : -3);
$sum += ($session1['ScreenAvailWidth'] == $session2['ScreenAvailWidth'] &&
$session1['ScreenAvailHeight'] == $session2['ScreenAvailHeight'] ? 2 : -3);
$sum += ($session1['ViewportWidth'] == $session2['ViewportWidth'] &&
$session1['ViewportHeight'] == $session2['ViewportHeight'] ? 3 : -1);
$sum += ($session1['UtcOffset'] == $session2['UtcOffset'] ? 3 : -3);
}
return $sum;
}
private function PrepareSessionData($rawData, $now, $visitorId, $newVisitor)
{
$values = array(
'SessionStartTime' => UnbDatabase::ConvertDateTime($now),
'SessionUpdateTime' => UnbDatabase::ConvertDateTime($now),
'VisitorId' => $visitorId,
'NewVisitor' => $newVisitor ? 1 : 0,
);
$values = array_merge(
$values,
$this->GetLocationSessionValues($rawData),
$this->GetReferrerSessionValues($rawData),
$this->GetBrowserSessionValues($rawData));
$values['UserAgentString'] = $_SERVER['HTTP_USER_AGENT'];
$values['LanguagesString'] = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
return $values;
}
private function CreateSession($rawData, $now, $visitorId, $newVisitor)
{
$values = $this->PrepareSessionData($rawData, $now, $visitorId, $newVisitor);
$values['SessionId'] = new UnbDbAutoValue('session');
$confSessionVars = self::GetConfSessionVars();
foreach ($confSessionVars as $name => $type)
{
if (isset($rawData['sv_' . $name]))
{
if ($type == 'integer')
{
$values['sv_' . $name] = intval($rawData['sv_' . $name]);
}
else if ($type == 'text')
{
$values['sv_' . $name] = self::GetTextId($rawData['sv_' . $name]);
}
}
}
$confVisitorVars = self::GetConfVisitorVars();
$visitorValues = array();
foreach ($confVisitorVars as $name => $type)
{
if (isset($rawData['vv_' . $name]))
{
if ($type == 'integer')
{
$visitorValues['vv_' . $name] = intval($rawData['vv_' . $name]);
}
else if ($type == 'text')
{
$visitorValues['vv_' . $name] = self::GetTextId($rawData['vv_' . $name]);
}
}
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Insert('session', $values);
$values['SessionId'] = $db->GetLastAutoValue();
if (count($visitorValues))
{
$db->Update('visitor', $visitorValues, '"VisitorId" = ?', $visitorId);
}
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
return $values;
}
public function UpdateSession($session, $rawData, $now)
{
$clearExitPages = isset($session['SessionDuration']);
$db = UnbDatabase::GetInstance();
$values = array(
'SessionUpdateTime' => UnbDatabase::ConvertDateTime($now),
'SessionDuration' => null,
);
$locationValues = $this->GetLocationSessionValues($rawData);
$refValues = $this->GetReferrerSessionValues($rawData);
$browserValues = $this->GetBrowserSessionValues($rawData);
if ($browserValues['JavaScript'])
{
$values['JavaScript'] = 1;
$values['Flash'] = max($session['Flash'], $browserValues['Flash']);
$values['Java'] = max($session['Java'], $browserValues['Java']);
$values['Quicktime'] = max($session['Quicktime'], $browserValues['Quicktime']);
$values['Silverlight'] = max($session['Silverlight'], $browserValues['Silverlight']);
$values['WindowsMedia'] = max($session['WindowsMedia'], $browserValues['WindowsMedia']);
$values['AdBlock'] = max($session['AdBlock'], $browserValues['AdBlock']);
if (!isset($session['UtcOffset']) && isset($browserValues['UtcOffset']))
{
$values['UtcOffset'] = $browserValues['UtcOffset'];
if (isset($locationValues['CountryCode']))
{
$values['CountryCode'] = $locationValues['CountryCode'];
$values['RegionText'] = $locationValues['RegionText'];
$values['CityText'] = $locationValues['CityText'];
$values['Lat'] = $locationValues['Lat'];
$values['Lng'] = $locationValues['Lng'];
}
}
if (!$session['ScreenWidth'] && $browserValues['ScreenWidth'])
{
$values['ScreenWidth'] = $browserValues['ScreenWidth'];
}
if (!$session['ScreenHeight'] && $browserValues['ScreenHeight'])
{
$values['ScreenHeight'] = $browserValues['ScreenHeight'];
}
if (!$session['ScreenAvailWidth'] && $browserValues['ScreenAvailWidth'])
{
$values['ScreenAvailWidth'] = $browserValues['ScreenAvailWidth'];
}
if (!$session['ScreenAvailHeight'] && $browserValues['ScreenAvailHeight'])
{
$values['ScreenAvailHeight'] = $browserValues['ScreenAvailHeight'];
}
if (!$session['ScreenColors'] && $browserValues['ScreenColors'])
{
$values['ScreenColors'] = $browserValues['ScreenColors'];
}
if (!$session['ViewportWidth'] && $browserValues['ViewportWidth'])
{
$values['ViewportWidth'] = $browserValues['ViewportWidth'];
}
if (!$session['ViewportHeight'] && $browserValues['ViewportHeight'])
{
$values['ViewportHeight'] = $browserValues['ViewportHeight'];
}
if (!$session['JavaScript'])
{
$pageviewCount = $db->QueryValue(
'SELECT COUNT(*) FROM "#pageview" WHERE "SessionId" = ?',
intval($session['SessionId']));
if (isset($rawData[self::key_pageviewId]))
{
$isFirst = $pageviewCount == 1;
}
else
{
$isFirst = $pageviewCount == 0;
}
if ($isFirst)
{
if (!isset($session['SearchKeywordsText']) && isset($refValues['SearchKeywordsText']))
{
$values['SearchKeywordsText'] = $refValues['SearchKeywordsText'];
}
if (!isset($session['SearchEngineText']) && isset($refValues['SearchEngineText']))
{
$values['SearchEngineText'] = $refValues['SearchEngineText'];
$values['ReferrerUrlPathText'] = null;
}
if (!isset($session['SearchImageUrlText']) && isset($refValues['SearchImageUrlText']))
{
$values['SearchImageUrlText'] = $refValues['SearchImageUrlText'];
}
if (!isset($session['SearchResultPage']) && isset($refValues['SearchResultPage']))
{
$values['SearchResultPage'] = $refValues['SearchResultPage'];
}
if (!isset($session['ReferrerSsl']) && isset($refValues['ReferrerSsl']))
{
$values['ReferrerSsl'] = $refValues['ReferrerSsl'];
}
if (!isset($session['ReferrerHostText']) && isset($refValues['ReferrerHostText']))
{
$values['ReferrerHostText'] = $refValues['ReferrerHostText'];
}
if (!isset($session['ReferrerUrlPathText']) && isset($refValues['ReferrerUrlPathText']))
{
$values['ReferrerUrlPathText'] = $refValues['ReferrerUrlPathText'];
}
if (!$session['CampaignText'] && $refValues['CampaignText'])
{
$values['CampaignText'] = $refValues['CampaignText'];
UnbDebug::Trace('      Campaign: ' . $rawData[self::key_campaign]);
}
}
}
}
if (strlen($rawData[self::key_cookies]))
{
$values['Cookies'] = $rawData[self::key_cookies] ? 1 : 0;
}
$confSessionVars = self::GetConfSessionVars();
foreach ($confSessionVars as $name => $type)
{
if (isset($rawData['sv_' . $name]))
{
if ($type == 'integer')
{
$values['sv_' . $name] = intval($rawData['sv_' . $name]);
}
else if ($type == 'text')
{
$values['sv_' . $name] = self::GetTextId($rawData['sv_' . $name]);
}
}
}
$confVisitorVars = self::GetConfVisitorVars();
$visitorValues = array();
foreach ($confVisitorVars as $name => $type)
{
if (isset($rawData['vv_' . $name]))
{
if ($type == 'integer')
{
$visitorValues['vv_' . $name] = intval($rawData['vv_' . $name]);
}
else if ($type == 'text')
{
$visitorValues['vv_' . $name] = self::GetTextId($rawData['vv_' . $name]);
}
}
}
$db->StartTransaction();
try
{
$db->Update('session', $values, '"SessionId" = ?', intval($session['SessionId']));
if ($clearExitPages)
{
$db->Execute(
'UPDATE "#pageview" SET "ExitPage" = NULL WHERE "SessionId" = ?',
intval($session['SessionId']));
}
if (count($visitorValues))
{
$db->Update('visitor', $visitorValues, '"VisitorId" = ?', intval($session['VisitorId']));
}
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function MergeSessions($session1, $session2)
{
if ($session1['VisitorId'] != $session2['VisitorId'])
throw new Exception('Only sessions of the same visitor can be merged.');
if ($session1['NewVisitor'] != $session2['NewVisitor'])
throw new Exception('The session value NewVisitor cannot be merged.');
$values['SessionStartTime'] = min($session1['SessionStartTime'], $session2['SessionStartTime']);
$values['SessionDuration'] = null;
$values['SessionUpdateTime'] = max($session1['SessionUpdateTime'], $session2['SessionUpdateTime']);
$values['ReferrerSsl'] = $this->MergeSessionHelper($session1, $session2, 'ReferrerSsl', 1, true);
$values['ReferrerHostText'] = $this->MergeSessionHelper($session1, $session2, 'ReferrerHostText', 1, true);
$values['ReferrerUrlPathText'] = $this->MergeSessionHelper($session1, $session2, 'ReferrerUrlPathText', 1, true);
$values['SearchEngineText'] = $this->MergeSessionHelper($session1, $session2, 'SearchEngineText', 1, true);
$values['SearchKeywordsText'] = $this->MergeSessionHelper($session1, $session2, 'SearchKeywordsText', 1, true);
$values['SearchImageUrlText'] = $this->MergeSessionHelper($session1, $session2, 'SearchImageUrlText', 1, true);
$values['SearchResultPage'] = $this->MergeSessionHelper($session1, $session2, 'SearchResultPage', 1, true);
$values['CampaignText'] = $this->MergeSessionHelper($session1, $session2, 'CampaignText', 1, true);
if (isset($session1['CountryCode']) && !isset($session2['CountryCode'])) $locationSession = &$session1;
else if (!isset($session1['CountryCode']) && isset($session2['CountryCode'])) $locationSession = &$session2;
else if ($session1['JavaScript'] && !$session2['JavaScript']) $locationSession = &$session1;
else if (!$session1['JavaScript'] && $session2['JavaScript']) $locationSession = &$session2;
else if (isset($session1['Lat']) && !isset($session2['Lat'])) $locationSession = &$session1;
else if (!isset($session1['Lat']) && isset($session2['Lat'])) $locationSession = &$session2;
else $locationSession = &$session1;
$values['CountryCode'] = $locationSession['CountryCode'];
$values['RegionText'] = $locationSession['RegionText'];
$values['CityText'] = $locationSession['CityText'];
$values['Lat'] = $locationSession['Lat'];
$values['Lng'] = $locationSession['Lng'];
$values['UtcOffset'] = $this->MergeSessionHelper($session1, $session2, 'UtcOffset', 0, true);
$values['JavaScript'] = max($session1['JavaScript'], $session2['JavaScript']);
$values['Cookie'] = max($session1['Cookie'], $session2['Cookie']);
$values['Flash'] = max($session1['Flash'], $session2['Flash']);
$values['Java'] = max($session1['Java'], $session2['Java']);
$values['Quicktime'] = max($session1['Quicktime'], $session2['Quicktime']);
$values['Silverlight'] = max($session1['Silverlight'], $session2['Silverlight']);
$values['WindowsMedia'] = max($session1['WindowsMedia'], $session2['WindowsMedia']);
$values['AdBlock'] = max($session1['AdBlock'], $session2['AdBlock']);
$values['ScreenWidth'] = $this->MergeSessionHelper($session1, $session2, 'ScreenWidth', 1, true);
$values['ScreenHeight'] = $this->MergeSessionHelper($session1, $session2, 'ScreenHeight', 1, true);
$values['ScreenAvailWidth'] = $this->MergeSessionHelper($session1, $session2, 'ScreenAvailWidth', 1, true);
$values['ScreenAvailHeight'] = $this->MergeSessionHelper($session1, $session2, 'ScreenAvailHeight', 1, true);
$values['ScreenColors'] = $this->MergeSessionHelper($session1, $session2, 'ScreenColors', 1, true);
$values['ViewportWidth'] = $this->MergeSessionHelper($session1, $session2, 'ViewportWidth', 1, true);
$values['ViewportHeight'] = $this->MergeSessionHelper($session1, $session2, 'ViewportHeight', 1, true);
$confSessionVars = self::GetConfSessionVars();
foreach ($confSessionVars as $name => $type)
{
$values['sv_' . $name] = $this->MergeSessionHelper($session1, $session2, 'sv_' . $name, 1, true);
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Update('session', $values, '"SessionId" = ?', intval($session1['SessionId']));
$pvValues = array(
'SessionId' => intval($session1['SessionId'])
);
$db->Update('pageview', $pvValues, '"SessionId" = ?', intval($session2['SessionId']));
$db->Delete('session', '"SessionId" = ?', intval($session2['SessionId']));
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
public function PurgeTempSessionData()
{
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Update(
'session',
array(
'UserAgentString' => null,
'LanguagesString' => null),
'"SessionUpdateTime" < ?
AND ("UserAgentString" IS NOT NULL OR "LanguagesString" IS NOT NULL)',
array(
UnbDatabase::ConvertDateTime(time() - intval(UnbConfiguration::Get('session.maxidle', '30')) * 60)
));
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function FindPageviewById($pageviewId)
{
if (!is_int($pageviewId))
throw new InvalidArgumentException('Invalid argument type: $pageviewId');
if ($pageviewId < 0)
throw new InvalidArgumentException('Argument out of range: $pageviewId');
$db = UnbDatabase::GetInstance();
$rec = $db->QueryRecord('SELECT * FROM "#pageview" WHERE "PageviewId" = ?', $pageviewId);
if (!$rec) return null;
return $rec;
}
private function ExistsPageviewId($pageviewId)
{
if (!is_int($pageviewId))
throw new InvalidArgumentException('Invalid argument type: $pageviewId');
if ($pageviewId < 0)
throw new InvalidArgumentException('Argument out of range: $pageviewId');
$db = UnbDatabase::GetInstance();
$rec = $db->QueryRecord('SELECT "PageviewId" FROM "#pageview" WHERE "PageviewId" = ?', $pageviewId);
return $rec ? true : false;
}
private function FindDuplicatePageviews($dbData)
{
$maxOffset = 60;
$db = UnbDatabase::GetInstance();
$recs = $db->Query(
'SELECT *
FROM "#pageview"
WHERE "SessionId" = ?
AND "PageviewId" <> ?
AND "PageviewTime" >= ?
AND "PageviewTime" <= ?
AND "CampaignRedirect" = ?
ORDER BY "PageviewId" DESC',
array(
intval($dbData['SessionId']),
intval($dbData['PageviewId']),
UnbDatabase::ConvertDateTime(time() - $maxOffset),
UnbDatabase::ConvertDateTime(time() + $maxOffset),
intval($dbData['CampaignRedirect'])
));
if (!$recs) return array();
$dupes = array();
foreach ($recs as $pageview)
{
if ($pageview['Ssl'] == $dbData['Ssl'] &&
$pageview['DomainText'] == $dbData['DomainText'] &&
$pageview['UrlPathText'] == $dbData['UrlPathText'] &&
$pageview['UrlQueryText'] == $dbData['UrlQueryText'])
{
if (!$dbData['JavaScriptData'] &&
$pageview['JavaScriptData'] &&
$pageview['PageLoadTime'] > 0)
{
}
else if ($dbData['JavaScriptData'] &&
$pageview['JavaScriptData'] &&
isset($pageview['PageLoadTime']) &&
$pageview['PageLoadTime'] == 0)
{
}
else if ($dbData['JavaScriptData'] &&
$pageview['JavaScriptData'] &&
$pageview['Prerender'] !== $dbData['Prerender'])
{
}
else
{
$dupes[] = $pageview;
}
}
else
{
break;
}
}
return $dupes;
}
private function CreatePageview($rawData, $now, $sessionId, $entryPage)
{
$values = $this->PreparePageviewData($rawData, $now, $sessionId, $entryPage);
$values['PageviewId'] = new UnbDbAutoValue('pageview');
if (!isset($values['Prerender']))
{
$values['Prerender'] = 0;
}
$confPageviewVars = self::GetConfPageviewVars();
foreach ($confPageviewVars as $name => $type)
{
if (isset($rawData['pv_' . $name]))
{
if ($type == 'integer')
{
$values['pv_' . $name] = intval($rawData['pv_' . $name]);
}
else if ($type == 'text')
{
$values['pv_' . $name] = self::GetTextId($rawData['pv_' . $name]);
}
}
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Insert('pageview', $values);
$values['PageviewId'] = $db->GetLastAutoValue();
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
return $values;
}
public function UpdatePageview($pageview, $rawData, $now)
{
$pageviewId = intval($pageview['PageviewId']);
if ($pageviewId <= 0)
throw new InvalidArgumentException('Argument out of range: $pageview[\'PageviewId\']');
$values = array();
if (!$pageview['JavaScriptData'] && $rawData['javascript'])
{
$values['JavaScriptData'] = 1;
if ($now - UnbDatabase::ConvertDateTime($pageview['PageviewUpdateTime']) >= 5)
{
$values['PageviewUpdateTime'] = UnbDatabase::ConvertDateTime($now);
}
if (intval($rawData[self::key_origPageviewId]) > 0)
{
$values['OriginalPageviewId'] = intval($rawData[self::key_origPageviewId]);
}
}
else
{
$values['PageviewUpdateTime'] = UnbDatabase::ConvertDateTime($now);
}
if (!$pageview['UrlHashText'] && isset($rawData[self::key_pageUrl]))
{
$pageUrl = parse_url($rawData[self::key_pageUrl]);
$values['UrlHashText'] = Tomo::GetTextId($pageUrl['fragment']);
}
if (strlen($rawData[self::key_pageTitle]))
{
$values['PageTitleText'] = Tomo::GetTextId($rawData[self::key_pageTitle]);
}
if (strlen($rawData[self::key_pageLanguage]))
{
$values['PageLanguage'] = $rawData[self::key_pageLanguage];
}
if ($rawData[self::key_pageLoadTime] != 0)
{
$values['PageLoadTime'] = intval($rawData[self::key_pageLoadTime]);
}
if (isset($rawData[self::key_activityTimespan]))
{
$activityTimespan = intval($rawData[self::key_activityTimespan]);
$activityTimespan = intval(($activityTimespan + 200) / 1000);
$pageviewTime = UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
if ($activityTimespan > $now - $pageviewTime + 2)
{
UnbDebug::Trace('  Reported activity timespan is larger than absolute pageview lifetime. Limit value.');
$activityTimespan = $now - $pageviewTime;
}
if ($activityTimespan > $this->maxActivityTimespan * 3)
{
UnbDebug::Trace('  Reported activity timespan is larger than 3 times the maximum allowed value of ' . $this->maxActivityTimespan . ' s. Clear value.');
$activityTimespan = null;
}
if ($activityTimespan > $this->maxActivityTimespan)
{
UnbDebug::Trace('  Reported activity timespan is larger than maximum allowed value of ' . $this->maxActivityTimespan . ' s. Limit value.');
$activityTimespan = $this->maxActivityTimespan;
}
$values['ActivityTimespan'] = $activityTimespan;
}
if (isset($rawData[self::key_prerender]) || $rawData[self::key_activityTimespan] > 0)
{
$values['Prerender'] = $rawData[self::key_prerender] ? 1 : 0;
}
if (strlen($rawData[self::key_campaign]) && $rawData['javascript'])
{
$values['CampaignRedirect'] = !$rawData[self::key_campaignLocalRedir] ? 1 : 0;
}
$confPageviewVars = self::GetConfPageviewVars();
foreach ($confPageviewVars as $name => $type)
{
if (isset($rawData['pv_' . $name]))
{
if ($type == 'integer')
{
$values['pv_' . $name] = intval($rawData['pv_' . $name]);
}
else if ($type == 'text')
{
$values['pv_' . $name] = self::GetTextId($rawData['pv_' . $name]);
}
}
}
if (!count($values)) return;
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Update('pageview', $values, '"PageviewId" = ?', $pageviewId);
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function DuplicatePageview($pageview, $newRawData, $now)
{
$values = array();
foreach ($pageview as $key => $value)
{
if (substr($key, 0, 3) != 'pv_')
$values[$key] = $value;
}
$this->ParseUrlIntoArray($newRawData, $values);
$values['PageviewId'] = new UnbDbAutoValue('pageview');
$values['PageviewTime'] = UnbDatabase::ConvertDateTime($now);
$values['PageviewUpdateTime'] = UnbDatabase::ConvertDateTime($now);
if (strlen($newRawData[self::key_pageTitle]))
{
$values['PageTitleText'] = Tomo::GetTextId($newRawData[self::key_pageTitle]);
}
if (strlen($newRawData[self::key_pageLanguage]))
{
$values['PageLanguage'] = $newRawData[self::key_pageLanguage];
}
$values['ActivityTimespan'] = null;
$values['PageLoadTime'] = 0;
$values['EntryPage'] = 0;
$values['JavaScriptData'] = 1;
$values['CampaignRedirect'] = 0;
$values['Prerender'] = 0;
$values['OriginalPageviewId'] = null;
$confPageviewVars = self::GetConfPageviewVars();
foreach ($confPageviewVars as $name => $type)
{
if (isset($newRawData['pv_' . $name]))
{
if ($type == 'integer')
{
$values['pv_' . $name] = intval($newRawData['pv_' . $name]);
}
else if ($type == 'text')
{
$values['pv_' . $name] = self::GetTextId($newRawData['pv_' . $name]);
}
}
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Insert('pageview', $values);
$values['PageviewId'] = $db->GetLastAutoValue();
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
return $values;
}
private function MergePageviews($pageview1, $pageview2)
{
if ($pageview1['SessionId'] != $pageview2['SessionId'])
throw new Exception('Only pageviews of the same session can be merged.');
$values['PageviewTime'] = min($pageview1['PageviewTime'], $pageview2['PageviewTime']);
$values['PageTitleText'] = max($pageview1['PageTitleText'], $pageview2['PageTitleText']);
$values['ActivityTimespan'] = max($pageview1['ActivityTimespan'], $pageview2['ActivityTimespan']);
$values['JavaScriptData'] = max($pageview1['JavaScriptData'], $pageview2['JavaScriptData']);
$values['PageLoadTime'] = max($pageview1['PageLoadTime'], $pageview2['PageLoadTime']);
$values['EntryPage'] = max($pageview1['EntryPage'], $pageview2['EntryPage']);
$values['CampaignRedirect'] = max($pageview1['CampaignRedirect'], $pageview2['CampaignRedirect']);
$values['Prerender'] = max($pageview1['Prerender'], $pageview2['Prerender']);
$values['OriginalPageviewId'] = min($pageview1['OriginalPageviewId'], $pageview2['OriginalPageviewId']);
$values['PageviewUpdateTime'] = max($pageview1['PageviewUpdateTime'], $pageview2['PageviewUpdateTime']);
$confPageviewVars = self::GetConfPageviewVars();
foreach ($confPageviewVars as $name => $type)
{
$values['pv_' . $name] = max($pageview1['pv_' . $name], $pageview2['pv_' . $name]);
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Update('pageview', $values, '"PageviewId" = ?', intval($pageview1['PageviewId']));
$acValues = array(
'PageviewId' => intval($pageview1['PageviewId'])
);
$db->Update('action', $acValues, '"PageviewId" = ?', intval($pageview2['PageviewId']));
$db->Delete('pageview', '"PageviewId" = ?', intval($pageview2['PageviewId']));
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
public function UpdateAllExitPages()
{
$time = time() - (intval(UnbConfiguration::Get('session.maxidle', '30')) + 1) * 60;
$time = UnbDatabase::ConvertDateTime($time);
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
foreach ($db->Query('
SELECT "SessionId", "SessionStartTime", "SessionUpdateTime"
FROM "#session"
WHERE "SessionDuration" IS NULL
AND "SessionUpdateTime" < ?',
$time)
as $session)
{
$sessionId = intval($session['SessionId']);
$lastPageviewId = intval($db->QueryValue(
'SELECT MAX("PageviewId") FROM "#pageview" WHERE "SessionId" = ?',
$sessionId));
$db->Execute(
'UPDATE "#pageview" SET "ExitPage" = NULL WHERE "SessionId" = ?',
intval($sessionId));
$db->Execute(
'UPDATE "#pageview" SET "ExitPage" = 1 WHERE "PageviewId" = ?',
intval($lastPageviewId));
$sessionDuration = UnbDatabase::ConvertDateTime($session['SessionUpdateTime']) -
UnbDatabase::ConvertDateTime($session['SessionStartTime']);
$db->Execute(
'UPDATE "#session" SET "SessionDuration" = ? WHERE "SessionId" = ?',
array($sessionDuration, $sessionId));
}
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
public function PurgeTempPageviews()
{
$time = time() - (intval(UnbConfiguration::Get('session.maxidle', '30')) + 1) * 60;
$time = UnbDatabase::ConvertDateTime($time);
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Execute(
'DELETE FROM "#pageview"
WHERE "PageviewTime" < ?
AND ("CampaignRedirect" OR "Prerender")',
$time);
$db->Execute(
'DELETE FROM "#session"
WHERE NOT EXISTS (
SELECT "PageviewId"
FROM "#pageview" p
WHERE p."SessionId" = "#session"."SessionId")');
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
private function FindDuplicateActions($now, $pageviewId, $type, $url, $data)
{
$maxOffset = 10;
$db = UnbDatabase::GetInstance();
$recs = $db->Query(
'SELECT a.*, t1."Value" as "ActionUrl", t2."Value" as "ActionData"
FROM "#action" a
LEFT JOIN "#text" t1 ON (t1."TextId" = a."ActionUrlText")
LEFT JOIN "#text" t2 ON (t2."TextId" = a."ActionDataText")
WHERE "PageviewId" = ?
AND "ActionTime" >= ?
AND "ActionTime" <= ?
ORDER BY "ActionId" DESC',
array(
$pageviewId,
UnbDatabase::ConvertDateTime($now - $maxOffset),
UnbDatabase::ConvertDateTime($now + $maxOffset)
));
if (!$recs) return array();
$dupes = array();
foreach ($recs as $action)
{
if ($action['ActionType'] == $type &&
$action['ActionUrl'] == $url &&
$action['ActionData'] == $data)
{
$dupes[] = $action;
}
else
{
break;
}
}
return $dupes;
}
private function CreateAction($now, $pageviewId, $type, $url, $data)
{
$values = array(
'ActionId' => new UnbDbAutoValue('action'),
'PageviewId' => $pageviewId,
'ActionTime' => UnbDatabase::ConvertDateTime($now),
'ActionType' => $type,
);
if (strlen($url))
$values['ActionUrlText'] = Tomo::GetTextId($url);
if (strlen($data))
$values['ActionDataText'] = Tomo::GetTextId($data);
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Insert('action', $values);
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
$values['ActionId'] = $db->GetLastAutoValue();
return $values;
}
public static function GetTextId($text)
{
$text = trim($text);
if (!strlen($text)) return null;
$db = UnbDatabase::GetInstance();
$textId = $db->QueryValue('SELECT "TextId" FROM "#text" WHERE "Value" = ?', $text);
if (!$textId)
{
$db->StartTransaction();
try
{
$db->Execute(
'INSERT INTO "#text" ("TextId", "Value") VALUES (?, ?)',
array(new UnbDbAutoValue('text'), $text));
$textId = $db->GetLastAutoValue();
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
return $textId;
}
public static function FindTextById($textId)
{
if (!is_int($textId))
throw new InvalidArgumentException('Invalid argument type: $textId');
if ($textId <= 0)
throw new InvalidArgumentException('Argument out of range: $textId');
$db = UnbDatabase::GetInstance();
$text = $db->QueryValue('SELECT "Value" FROM "#text" WHERE "TextId" = ?', $textId);
return $text;
}
public static function PurgeText()
{
$sessionVars = '';
foreach (self::GetDbSessionVars() as $name => $type)
{
if ($type == 'text')
{
$sessionVars .= "\n" . 'except select distinct "sv_' . $name . '" as "TextId" from "#session"';
}
}
$pageviewVars = '';
foreach (self::GetDbPageviewVars() as $name => $type)
{
if ($type == 'text')
{
$pageviewVars .= "\n" . 'except select distinct "pv_' . $name . '" as "TextId" from "#pageview"';
}
}
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
foreach ($db->QueryArray(
'select "TextId" from "#text"
except select distinct "ReferrerHostText" as "TextId" from "#session"
except select distinct "ReferrerUrlPathText" as "TextId" from "#session"
except select distinct "SearchEngineText" as "TextId" from "#session"
except select distinct "SearchKeywordsText" as "TextId" from "#session"
except select distinct "SearchImageUrlText" as "TextId" from "#session"
except select distinct "CampaignText" as "TextId" from "#session"
except select distinct "ProviderText" as "TextId" from "#session"
except select distinct "CityText" as "TextId" from "#session"
except select distinct "RegionText" as "TextId" from "#session"
except select distinct "BrowserText" as "TextId" from "#session"
except select distinct "OperatingSystemText" as "TextId" from "#session"' . $sessionVars . '
except select distinct "DomainText" as "TextId" from "#pageview"
except select distinct "UrlPathText" as "TextId" from "#pageview"
except select distinct "UrlQueryText" as "TextId" from "#pageview"
except select distinct "UrlHashText" as "TextId" from "#pageview"
except select distinct "PageTitleText" as "TextId" from "#pageview"' . $pageviewVars . '
except select distinct "ActionUrlText" as "TextId" from "#action"
except select distinct "ActionDataText" as "TextId" from "#action"') as $textId)
{
$textId = intval($textId);
$db->Execute(
'DELETE FROM "#text" WHERE "TextId" = ?',
array($textId));
}
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
$db->Optimise(true);
}
public static function GetSetting($name, $defaultValue = null)
{
$db = UnbDatabase::GetInstance();
$value = $db->QueryValue('SELECT "Value" FROM "#setting" WHERE "Name" = ?', $name);
if (!isset($value))
{
$value = $defaultValue;
}
return $value;
}
public static function SetSetting($name, $value)
{
$db = UnbDatabase::GetInstance();
$db->Update('setting', array('Value' => $value), '"Name" = ?', $name);
if (!$db->GetRowsAffected())
{
try
{
$db->Insert('setting', array('Name' => $name, 'Value' => $value));
}
catch (Exception $ex)
{
}
}
}
public function AcquireLock()
{
$id = UnbEnvironment::GetClientAddress();
$this->PurgeLocks();
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$values = array(
'LockId' => $id,
'LockCreateTime' => UnbDatabase::ConvertDateTime(time())
);
$db->Insert('lock', $values);
$db->Commit();
}
catch (UnbDatabaseException $ex)
{
if ($db->IsConstraintError())
{
{
$db->RollBack();
return false;
}
}
$db->RollBack();
throw $ex;
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
$this->lockId = $id;
return true;
}
public function ReleaseLock()
{
if (!isset($this->lockId)) return;
UnbDebug::Trace('ReleaseLock: deleting id: ' . $this->lockId);
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Delete('lock', 'LockId = ?', $this->lockId);
$db->Commit();
$count = $db->QueryValue('SELECT count(*) from "#lock"');
if ($count) UnbDebug::Trace('ReleaseLock: remaining record count: ' . $count);
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
$this->lockId = null;
}
public function PurgeLocks()
{
$lockTimeout = 15;
$db = UnbDatabase::GetInstance();
$db->StartTransaction();
try
{
$db->Delete('lock', 'LockCreateTime < ?', UnbDatabase::ConvertDateTime(time() - $lockTimeout));
$db->Commit();
}
catch (Exception $ex)
{
$db->RollBack();
throw $ex;
}
}
public static function GetConfVisitorVars()
{
$confVisitorVars = UnbConfiguration::Get('custom-vars.visitor');
if (!is_array($confVisitorVars))
$confVisitorVars = array();
return $confVisitorVars;
}
public static function GetConfSessionVars()
{
$confSessionVars = UnbConfiguration::Get('custom-vars.session');
if (!is_array($confSessionVars))
$confSessionVars = array();
return $confSessionVars;
}
public static function GetConfPageviewVars()
{
$confPageviewVars = UnbConfiguration::Get('custom-vars.pageview');
if (!is_array($confPageviewVars))
$confPageviewVars = array();
return $confPageviewVars;
}
public static function GetDbVisitorVars()
{
return self::GetDbCustomVars('visitor');
}
public static function GetDbSessionVars()
{
return self::GetDbCustomVars('session');
}
public static function GetDbPageviewVars()
{
return self::GetDbCustomVars('pageview');
}
private static function GetDbCustomVars($scope)
{
$db = UnbDatabase::GetInstance();
$columns = $db->Query('PRAGMA table_info("#' . $scope . '")');
$tableSql = $db->QueryValue('SELECT sql FROM sqlite_master WHERE type = \'table\' AND name LIKE \'%' . $scope . '\'');
$dbVars = array();
foreach ($columns as $col)
{
if (preg_match('/^' . substr($scope, 0, 1) . 'v_(.+)$/', $col['name'], $m))
{
if (preg_match('/"' . $col['name'] . '" INTEGER REFERENCES ".*text" \("TextId"\)/i', $tableSql))
$type = 'text';
else if (preg_match('/"' . $col['name'] . '" INTEGER/i', $tableSql))
$type = 'integer';
else
$type = 'unknown';
$dbVars[$m[1]] = $type;
}
}
return $dbVars;
}
public function AddVisitorVar($name)
{
$this->AddCustomVar('visitor', $name);
}
public function AddSessionVar($name)
{
$this->AddCustomVar('session', $name);
}
public function AddPageviewVar($name)
{
$this->AddCustomVar('pageview', $name);
}
public function AddCustomVar($scope, $name)
{
if (preg_match(self::varNamePattern, $name))
{
$customVars = UnbConfiguration::Get('custom-vars.' . $scope);
$type = $customVars[$name];
switch ($type)
{
case 'integer':
$type = 'INTEGER';
break;
case 'text':
$type = 'INTEGER REFERENCES "#text" ("TextId")';
break;
default:
$type = null;
}
if (isset($type))
{
$db = UnbDatabase::GetInstance();
$db->Execute('ALTER TABLE "#' . $scope . '" ADD COLUMN "' . substr($scope, 0, 1) . 'v_' . $name . '" ' . $type);
}
}
}
public static function GetConfActions()
{
$confActions = UnbConfiguration::Get('custom-actions');
if (!is_array($confActions))
$confActions = array();
return $confActions;
}
public function GetAllColumnGroups()
{
$cols = array(
'ActionData' => 'page/action',
'ActionId' => 'advanced/ids',
'ActionTime' => 'page/action',
'ActionType' => 'page/action',
'ActionUrl' => 'page/action',
'ActivityTimespan' => 'page/pageview',
'AdBlock' => 'client/plugins',
'Browser' => 'client/browser',
'Campaign' => 'visit/source',
'CampaignRedirect' => 'advanced/data',
'City' => 'visit/location',
'Cookies' => 'client/config',
'CountryCode' => 'visit/location',
'DNT' => 'client/config',
'Domain' => 'page/address',
'EntryPage' => 'page/pageview',
'ExitPage' => 'page/pageview',
'Flash' => 'client/plugins',
'IPAddress' => 'client/browser',
'IPVersion' => 'client/browser',
'Java' => 'client/plugins',
'JavaScript' => 'client/config',
'JavaScriptData' => 'advanced/data',
'Language' => 'client/config',
'Lat' => 'visit/location',
'Lng' => 'visit/location',
'Mobile' => 'client/browser',
'NewVisitor' => 'visit/session',
'OperatingSystem' => 'client/browser',
'OriginalPageviewId' => 'advanced/ids',
'PageLoadTime' => 'page/pageview',
'PageTitle' => 'page/address',
'PageLanguage' => 'page/address',
'PageviewId' => 'advanced/ids',
'PageviewTime' => 'page/pageview',
'PageviewUpdateTime' => 'advanced/data',
'Prerender' => 'advanced/data',
'Provider' => 'client/browser',
'Quicktime' => 'client/plugins',
'ReferrerHost' => 'visit/source',
'ReferrerUrlPath' => 'visit/source',
'ReferrerSsl' => 'visit/source',
'Region' => 'visit/location',
'ScreenAvailHeight' => 'client/screen',
'ScreenAvailWidth' => 'client/screen',
'ScreenColors' => 'client/screen',
'ScreenHeight' => 'client/screen',
'ScreenWidth' => 'client/screen',
'SearchEngine' => 'visit/search',
'SearchImageUrl' => 'visit/search',
'SearchKeywords' => 'visit/search',
'SearchResultPage' => 'visit/search',
'SessionDuration' => 'visit/session',
'SessionId' => 'advanced/ids',
'SessionStartTime' => 'visit/session',
'SessionUpdateTime' => 'advanced/data',
'Silverlight' => 'client/plugins',
'Ssl' => 'page/address',
'UrlHash' => 'page/address',
'UrlPath' => 'page/address',
'UrlQuery' => 'page/address',
'UtcOffset' => 'visit/location',
'ViewportHeight' => 'client/screen',
'ViewportWidth' => 'client/screen',
'VisitorId' => 'advanced/ids',
'WindowsMedia' => 'client/plugins',
);
foreach (self::GetDbVisitorVars() as $name => $type)
{
$cols['vv_' . $name] = 'vars/visitor';
}
foreach (self::GetDbSessionVars() as $name => $type)
{
$cols['sv_' . $name] = 'vars/session';
}
foreach (self::GetDbPageviewVars() as $name => $type)
{
$cols['pv_' . $name] = 'vars/pageview';
}
return $cols;
}
public function GetColumnType($col)
{
switch ($col)
{
case 'SessionId':
case 'VisitorId':
case 'SearchResultPage':
case 'IPVersion':
case 'Lat':
case 'Lng':
case 'ScreenWidth':
case 'ScreenHeight':
case 'ScreenAvailWidth':
case 'ScreenAvailHeight':
case 'ScreenColors':
case 'ViewportWidth':
case 'ViewportHeight':
case 'PageviewId':
case 'ActionId':
case 'ActionType':
case 'PageLoadTime':
case 'SessionDuration':
case 'ActivityTimespan':
case 'UtcOffset':
case 'DNT':
return 'int';
case 'SessionStartTime':
case 'SessionUpdateTime':
case 'PageviewTime':
case 'PageviewUpdateTime':
return 'datetime';
case 'NewVisitor':
case 'Mobile':
case 'JavaScript':
case 'JavaScriptData':
case 'Cookies':
case 'Flash':
case 'Java':
case 'Quicktime':
case 'Silverlight':
case 'WindowsMedia':
case 'AdBlock':
case 'Ssl':
case 'EntryPage':
case 'ExitPage':
return 'bool';
case 'ReferrerHost':
case 'ReferrerUrlPath':
case 'ReferrerSsl':
case 'SearchEngine':
case 'SearchImageUrl':
case 'SearchKeywords':
case 'Campaign':
case 'IPAddress':
case 'Provider':
case 'CountryCode':
case 'Region':
case 'City':
case 'Browser':
case 'OperatingSystem':
case 'Language':
case 'Domain':
case 'ActionUrl':
case 'UrlHash':
case 'UrlPath':
case 'UrlQuery':
case 'PageTitle':
return 'string';
default:
if (substr($col, 0, 3) == 'vv_')
$dbVars = self::GetDbVisitorVars();
if (substr($col, 0, 3) == 'sv_')
$dbVars = self::GetDbSessionVars();
if (substr($col, 0, 3) == 'pv_')
$dbVars = self::GetDbPageviewVars();
if ($dbVars[substr($col, 3)] == 'integer')
{
return 'int';
}
else
{
return 'string';
}
}
}
public function GetPageviews($columns, $filterData, $reverse = false, $limit = 0)
{
$dbVisitorVars = self::GetDbVisitorVars();
$dbSessionVars = self::GetDbSessionVars();
$dbPageviewVars = self::GetDbPageviewVars();
$cols = '';
$joins = '';
$where = '';
$whereParams = array();
$needVisitorTable = false;
foreach ($columns as $col)
{
switch (strtolower($col))
{
case 'referrerhost':
case 'referrerurlpath':
case 'city':
case 'region':
case 'searchengine':
case 'searchimageurl':
case 'searchkeywords':
case 'campaign':
case 'provider':
case 'browser':
case 'operatingsystem':
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#session"."' . $col . 'Text") ';
break;
case 'domain':
case 'urlpath':
case 'urlquery':
case 'urlhash':
case 'pagetitle':
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#pageview"."' . $col . 'Text") ';
break;
case 'actiondata':
case 'actionurl':
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#action"."' . $col . 'Text") ';
break;
case 'visitorid':
$cols .= ($cols ? ', ' : '') .
'"#session"."VisitorId"';
break;
default:
if (substr($col, 0, 3) == 'vv_' && $dbVisitorVars[substr($col, 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#visitor"."' . $col . '") ';
}
else if (substr($col, 0, 3) == 'sv_' && $dbSessionVars[substr($col, 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#session"."' . $col . '") ';
}
else if (substr($col, 0, 3) == 'pv_' && $dbPageviewVars[substr($col, 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col . '."Value" "' . $col . '"';
$joins .= 'LEFT JOIN "#text" t_' . $col . ' ON (t_' . $col . '."TextId" = "#pageview"."' . $col . '") ';
}
else
{
$cols .= ($cols ? ', ' : '') .
'"' . $col . '"';
}
if (substr($col, 0, 3) == 'vv_')
{
$needVisitorTable = true;
}
break;
}
}
if ($filterData['filtercol'])
{
$filterWhere = '';
foreach ($filterData['filtercol'] as $col)
{
$disabled = $filterData[$col . '_disabled'];
if ($disabled) continue;
$realCol = '"' . $col . '"';
if ($col == 'VisitorId')
{
$realCol = '"#session"."VisitorId"';
}
if (substr($col, 0, 3) == 'vv_' && $dbVisitorVars[substr($col, 3)] == 'text' ||
substr($col, 0, 3) == 'sv_' && $dbSessionVars[substr($col, 3)] == 'text' ||
substr($col, 0, 3) == 'pv_' && $dbPageviewVars[substr($col, 3)] == 'text')
{
$realCol = 't_' . $col . '."Value"';
}
$op = $filterData[$col . '_op'];
$value = $filterData[$col . '_value'];
$value2 = $filterData[$col . '_value2'];
$not = $filterData[$col . '_not'];
$filterWhere .= ($filterWhere ? ' ' . ($col['allfilters'] ? 'AND' : 'OR') . ' ' : '');
if ($not)
{
$filterWhere .= 'NOT ';
}
$filterWhere .= '(';
switch ($op)
{
case 'lt':
$filterWhere .= $realCol . ' < ?';
$whereParams[] = $value;
break;
case 'lte':
$filterWhere .= $realCol . ' <= ?';
$whereParams[] = $value;
break;
case 'gt':
$filterWhere .= $realCol . ' > ?';
$whereParams[] = $value;
break;
case 'gte':
$filterWhere .= $realCol . ' >= ?';
$whereParams[] = $value;
break;
case 'ne':
$filterWhere .= $realCol . ' <> ? OR ' . $realCol . ' IS NULL';
$whereParams[] = $value;
break;
case 'between':
$filterWhere .= $realCol . ' BETWEEN ? AND ?';
$whereParams[] = $value;
$whereParams[] = $value2;
break;
case 'inday':
$filterWhere .= $realCol . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($value, 0, 10));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1D'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inmonth':
$filterWhere .= $realCol . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($value, 0, 7) . '-01');
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1M'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inyear':
$filterWhere .= $realCol . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($value, 0, 4) . '-01-01');
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1Y'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'startswith':
$filterWhere .= $realCol . ' LIKE ?';
$whereParams[] = $value . '%';
break;
case 'endswith':
$filterWhere .= $realCol . ' LIKE ?';
$whereParams[] = '%' . $value;
break;
case 'contains':
$filterWhere .= $realCol . ' LIKE ?';
$whereParams[] = '%' . $value . '%';
break;
case 'isnull':
$filterWhere .= $realCol . ' IS NULL';
break;
case 'eq':
default:
$filterWhere .= $realCol . ' = ?';
$whereParams[] = $value;
break;
}
$filterWhere .= ')';
}
if (strlen($filterWhere))
{
$where .= ($where ? ' AND ' : '') .
'(' . $filterWhere . ')';
}
}
if ($needVisitorTable)
{
$joins = 'JOIN "#visitor" USING ("VisitorId") ' . $joins;
}
$sql = 'SELECT ' . $cols . ' FROM "#session" LEFT JOIN "#pageview" USING ("SessionId") LEFT JOIN "#action" USING ("PageviewId") ' . $joins;
$params = array();
if ($where)
{
$sql .= ' WHERE ' . $where;
foreach ($whereParams as $p)
{
$params[] = $p;
}
}
$revStr = $reverse ? ' DESC' : '';
$orderArray = array('"SessionId"' . $revStr, '"PageviewId"' . $revStr, '"ActionId"' . $revStr);
if (count($orderArray))
{
$sql .= ' ORDER BY ' . join(', ', $orderArray);
}
if (intval($limit) > 0)
{
$sql .= ' LIMIT ' . intval($limit);
}
$db = UnbDatabase::GetInstance();
return $db->Query($sql, $params);
}
public function GetRecords($withPageviews, $withActions, $columns, $limits, $groupLevel = 1, $groupValues = null)
{
if (!count($columns)) return array();
$dbVisitorVars = self::GetDbVisitorVars();
$dbSessionVars = self::GetDbSessionVars();
$dbPageviewVars = self::GetDbPageviewVars();
$cols = '';
$joins = '';
$where = '';
$whereParams = array();
$having = '';
$havingParams = array();
$group = '';
$orderArray = array();
$maxGroup = 0;
$needVisitorTable = false;
foreach ($columns as $col)
{
if (!strlen($col['source'])) continue;
$colRealName = '';
$maxGroup = max($maxGroup, $col['group']);
switch ($col['source'])
{
case 'ReferrerHost':
case 'ReferrerUrlPath':
case 'SearchEngine':
case 'SearchImageUrl':
case 'SearchKeywords':
case 'Campaign':
case 'Provider':
case 'City':
case 'Region':
case 'Browser':
case 'OperatingSystem':
if ($col['group'] <= $groupLevel)
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
}
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#session"."' . $col['source'] . 'Text")';
break;
case 'Domain':
case 'UrlPath':
case 'UrlQuery':
case 'UrlHash':
case 'PageTitle':
if ($col['group'] <= $groupLevel)
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
}
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#pageview"."' . $col['source'] . 'Text")';
break;
case 'ActionUrl':
case 'ActionData':
if ($col['group'] <= $groupLevel)
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
}
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#action"."' . $col['source'] . 'Text")';
break;
default:
if ($col['function'])
{
if ($col['group'] <= $groupLevel)
{
$cols .= ($cols ? ', ' : '') .
$col['source'] . ' "' . $col['alias'] . '"';
}
$colRealName = '"' . $col['alias'] . '"';
}
else
{
if ($col['group'] <= $groupLevel)
{
if (substr($col['source'], 0, 3) == 'vv_' && $dbVisitorVars[substr($col['source'], 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#visitor"."' . $col['source'] . '")';
}
else if (substr($col['source'], 0, 3) == 'sv_' && $dbSessionVars[substr($col['source'], 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#session"."' . $col['source'] . '")';
}
else if (substr($col['source'], 0, 3) == 'pv_' && $dbPageviewVars[substr($col['source'], 3)] == 'text')
{
$cols .= ($cols ? ', ' : '') .
't_' . $col['source'] . '."Value" "' . $col['alias'] . '"';
$colRealName = 't_' . $col['source'] . '."Value"';
$joins .= ' LEFT JOIN "#text" t_' . $col['source'] . ' ON (t_' . $col['source'] . '."TextId" = "#pageview"."' . $col['source'] . '")';
}
else
{
$cols .= ($cols ? ', ' : '') .
'"' . $col['source'] . '" "' . $col['alias'] . '"';
}
if (substr($col['source'], 0, 3) == 'vv_')
{
$needVisitorTable = true;
}
}
$colRealName = '"' . $col['source'] . '"';
}
break;
}
if ($col['set'] === true)
{
$where .= ($where ? ' AND ' : '') .
'(' . $colRealName . ' IS NOT NULL AND ' . $colRealName . ' <> \'\')';
}
else if ($col['set'] === false)
{
$where .= ($where ? ' AND ' : '') .
'(' . $colRealName . ' IS NULL OR ' . $colRealName . ' = \'\')';
}
if (is_array($col['filters']) && count($col['filters']))
{
$filterWhere = '';
foreach ($col['filters'] as $filter)
{
$filterWhere .= ($filterWhere ? ' ' . ($col['allfilters'] ? 'AND' : 'OR') . ' ' : '');
if ($filter['not'])
{
$filterWhere .= 'NOT ';
}
$filterWhere .= '(';
switch ($filter['op'])
{
case '<':
case 'lt':
$filterWhere .= $colRealName . ' < ?';
$whereParams[] = $filter['value'];
break;
case '<=':
case 'lte':
$filterWhere .= $colRealName . ' <= ?';
$whereParams[] = $filter['value'];
break;
case '>':
case 'gt':
$filterWhere .= $colRealName . ' > ?';
$whereParams[] = $filter['value'];
break;
case '>=':
case 'gte':
$filterWhere .= $colRealName . ' >= ?';
$whereParams[] = $filter['value'];
break;
case '<>':
case '!=':
case 'ne':
$filterWhere .= $colRealName . ' <> ? OR ' . $colRealName . ' IS NULL';
$whereParams[] = $filter['value'];
break;
case 'between':
$filterWhere .= $colRealName . ' BETWEEN ? AND ?';
$whereParams[] = $filter['value'];
$whereParams[] = $filter['value2'];
break;
case 'inday':
$filterWhere .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 10));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1D'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inmonth':
$filterWhere .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 7) . '-01');
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1M'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inyear':
$filterWhere .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 4) . '-01-01');
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1Y'));
$dt->sub(new DateInterval('PT1S'));
$whereParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'startswith':
$filterWhere .= $colRealName . ' LIKE ?';
$whereParams[] = $filter['value'] . '%';
break;
case 'endswith':
$filterWhere .= $colRealName . ' LIKE ?';
$whereParams[] = '%' . $filter['value'];
break;
case 'contains':
$filterWhere .= $colRealName . ' LIKE ?';
$whereParams[] = '%' . $filter['value'] . '%';
break;
case '=':
case 'eq':
default:
$filterWhere .= $colRealName . ' = ?';
$whereParams[] = $filter['value'];
break;
}
$filterWhere .= ')';
}
$where .= ($where ? ' AND ' : '') .
'(' . $filterWhere . ')';
}
if (is_array($col['pgfilters']) && count($col['pgfilters']))
{
$filterHaving = '';
foreach ($col['pgfilters'] as $filter)
{
$filterHaving .= ($filterHaving ? ' ' . ($col['allpgfilters'] ? 'AND' : 'OR') . ' ' : '');
if ($filter['not'])
{
$filterHaving .= 'NOT ';
}
$filterHaving .= '(';
switch ($filter['op'])
{
case '<':
case 'lt':
$filterHaving .= $colRealName . ' < ?';
$havingParams[] = $filter['value'];
break;
case '<=':
case 'lte':
$filterHaving .= $colRealName . ' <= ?';
$havingParams[] = $filter['value'];
break;
case '>':
case 'gt':
$filterHaving .= $colRealName . ' > ?';
$havingParams[] = $filter['value'];
break;
case '>=':
case 'gte':
$filterHaving .= $colRealName . ' >= ?';
$havingParams[] = $filter['value'];
break;
case '<>':
case '!=':
case 'ne':
$filterHaving .= $colRealName . ' <> ? OR ' . $colRealName . ' IS NULL';
$havingParams[] = $filter['value'];
break;
case 'between':
$filterHaving .= $colRealName . ' BETWEEN ? AND ?';
$havingParams[] = $filter['value'];
$havingParams[] = $filter['value2'];
break;
case 'inday':
$filterHaving .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 10));
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1D'));
$dt->sub(new DateInterval('PT1S'));
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inmonth':
$filterHaving .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 7) . '-01');
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1M'));
$dt->sub(new DateInterval('PT1S'));
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'inyear':
$filterHaving .= $colRealName . ' BETWEEN ? AND ?';
$dt = new DateTime(substr($filter['value'], 0, 4) . '-01-01');
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
$dt->add(new DateInterval('P1Y'));
$dt->sub(new DateInterval('PT1S'));
$havingParams[] = UnbLocale::TimeToUtc($dt->format('Y-m-d H:i:s'));
break;
case 'startswith':
$filterHaving .= $colRealName . ' LIKE ?';
$havingParams[] = $filter['value'] . '%';
break;
case 'endswith':
$filterHaving .= $colRealName . ' LIKE ?';
$havingParams[] = '%' . $filter['value'];
break;
case 'contains':
$filterHaving .= $colRealName . ' LIKE ?';
$havingParams[] = '%' . $filter['value'] . '%';
break;
case '=':
case 'eq':
default:
$filterHaving .= $colRealName . ' = ?';
$havingParams[] = $filter['value'];
break;
}
$filterHaving .= ')';
}
$having .= ($having ? ' AND ' : '') .
'(' . $filterHaving . ')';
}
if ($col['group'] <= $groupLevel)
{
if ($col['group'])
{
$group .= ($group ? ', ' : '') .
'"' . $col['alias']. '"';
}
if ($col['group'] && $col['group'] < $groupLevel)
{
$groupValue = $groupValues[$col['alias']];
if (isset($groupValue))
{
$where .= ($where ? ' AND ' : '') .
'("' . $col['alias']. '" = ?)';
$whereParams[] = $groupValue;
}
else
{
$where .= ($where ? ' AND ' : '') .
'("' . $col['alias']. '" IS NULL)';
}
}
if ($col['sortindex'])
{
$orderArray[$col['sortindex']] = '"' . $col['alias'] . '"' . ($col['sortdir'] == 'desc' ? ' DESC' : '');
}
}
}
$sql = 'SELECT ' . $cols . ' FROM "#session"';
if ($needVisitorTable)
{
$sql .= ' JOIN "#visitor" USING ("VisitorId")';
}
if ($withPageviews || $withActions)
{
$sql .= ' LEFT JOIN "#pageview" USING ("SessionId")';
}
if ($withActions)
{
$sql .= ' LEFT JOIN "#action" USING ("PageviewId")';
}
$sql .= $joins;
$params = array();
if ($where)
{
$sql .= ' WHERE ' . $where;
foreach ($whereParams as $p)
{
$params[] = $p;
}
}
if ($group)
{
$sql .= ' GROUP BY ' . $group;
}
if ($having)
{
$sql .= ' HAVING ' . $having;
foreach ($havingParams as $p)
{
$params[] = $p;
}
}
if (count($orderArray))
{
ksort($orderArray);
$sql .= ' ORDER BY ' . join(', ', $orderArray);
}
if ($limits[$groupLevel - 1])
{
$sql .= ' LIMIT ' . $limits[$groupLevel - 1];
}
$db = UnbDatabase::GetInstance();
$recs = $db->Query($sql, $params);
if (!$recs)
{
$db->traceSql = true;
$db->Query($sql, $params);
$db->traceSql = false;
}
$outRecs = array();
foreach ($recs as $rec)
{
$rec['_group'] = $groupLevel;
$outRecs[] = $rec;
if ($groupLevel < $maxGroup)
{
$subGroupValues = array();
foreach ($columns as $col)
{
if ($col['group'] && $col['group'] <= $groupLevel)
{
$subGroupValues[$col['alias']] = $rec[$col['alias']];
}
}
foreach ($this->GetRecords($withPageviews, $withActions, $columns, $limits, $groupLevel + 1, $subGroupValues) as $subRec)
{
$outRecs[] = $subRec;
}
}
}
return $outRecs;
}
function GetFirstSessionTime()
{
$db = UnbDatabase::GetInstance();
return $db->QueryValue('SELECT MIN("SessionStartTime") FROM "#session"');
}
}
class ContentType
{
public static $contenttypeImgPath = '';
private static $contentTypes = array(
'swf'=>array('application/adobe-flash','Adobe Flash application'),
'class'=>array('application/java-class','Java bytecode class'),
'msi'=>array('application/ms-installer','Microsoft Installer package'),
'bat'=>array('application/ms-script','Microsoft script application'),
'cmd'=>'bat',
'vbs'=>'bat',
'dll'=>array('application/ms-windows','Microsoft Windows application'),
'exe'=>'dll',
'sh'=>array('application/sh','Shell script application'),
'7z'=>array('archive/7zip','7-Zip archive'),
'apk'=>array('archive/android-package','Android application package'),
'bz'=>array('archive/bzip','BZip archive'),
'bz2'=>array('archive/bzip2','BZip2 archive'),
'cpio'=>array('archive/cpio','CPIO archive'),
'deb'=>array('archive/debian-package','Debian package'),
'udeb'=>'deb',
'gz'=>array('archive/gzip','GZip archive'),
'jar'=>array('archive/jar','Java Archive'),
'lzma'=>array('archive/lzma','LZMA archive'),
'xpi'=>array('archive/mozilla-xpinstall','Mozilla add-on'),
'cab'=>array('archive/ms-cab','Microsoft Cabinet archive'),
'rar'=>array('archive/rar','RAR archive'),
'tar'=>array('archive/tar','TAR archive'),
'zip'=>array('archive/zip','ZIP archive'),
'aac'=>array('audio/aac','AAC audio'),
'ac3'=>array('audio/ac3','AC3 audio'),
'aif'=>array('audio/aiff','AIFF audio'),
'aiff'=>'aif',
'aifc'=>'aif',
'mid'=>array('audio/midi','MIDI audio'),
'midi'=>'mid',
'mpga'=>array('audio/mpeg','MPEG audio'),
'mp2'=>'mpga',
'mp2a'=>'mpga',
'mp3'=>'mpga',
'm2a'=>'mpga',
'm3a'=>'mpga',
'mp4a'=>array('audio/mpeg4','MPEG4 audio'),
'wma'=>array('audio/ms-wma','Microsoft Windows Media audio'),
'oga'=>array('audio/ogg','Ogg audio'),
'ogg'=>'oga',
'spx'=>'oga',
'ram'=>array('audio/real','Real audio'),
'ra'=>'ram',
'wav'=>array('audio/wave','WAVE audio'),
'cnf'=>array('document/config','Text configuration file'),
'conf'=>'cnf',
'config'=>'cnf',
'inf'=>'cnf',
'ini'=>'cnf',
'csv'=>array('document/csv','Comma-separated values'),
'diff'=>array('document/diff','Unified diff file'),
'patch'=>'diff',
'eml'=>array('document/email','E-mail document'),
'mime'=>'eml',
'mdb'=>array('document/ms-access','Microsoft Access database'),
'xls'=>array('document/ms-excel','Microsoft Excel document'),
'xlsx'=>'xls',
'chm'=>array('document/ms-htmlhelp','Microsoft HTML help'),
'ppt'=>array('document/ms-powerpoint','Microsoft PowerPoint presentation'),
'pptx'=>'ppt',
'pps'=>'ppt',
'ppsx'=>'ppt',
'vsd'=>array('document/ms-visio','Microsoft Visio document'),
'doc'=>array('document/ms-word','Microsoft Word document'),
'docx'=>'doc',
'dot'=>array('document/ms-word-template','Microsoft Word document template'),
'dotx'=>'dot',
'wri'=>array('document/ms-write','Microsoft Write document'),
'xps'=>array('document/ms-xps','Microsoft XPS document'),
'odc'=>array('document/oasis-chart','OpenDocument chart'),
'otc'=>array('document/oasis-chart-template','OpenDocument chart template'),
'odb'=>array('document/oasis-database','OpenDocument database'),
'odf'=>array('document/oasis-formula','OpenDocument formula'),
'odft'=>array('document/oasis-formula-template','OpenDocument formula template'),
'odg'=>array('document/oasis-graphics','OpenDocument graphics'),
'odp'=>array('document/oasis-presentation','OpenDocument presentation'),
'otp'=>array('document/oasis-presentation-template','OpenDocument presentation template'),
'ods'=>array('document/oasis-spreadsheet','OpenDocument spreadsheet'),
'ots'=>array('document/oasis-spreadsheet-template','OpenDocument spreadsheet template'),
'odt'=>array('document/oasis-text','OpenDocument text'),
'otm'=>array('document/oasis-text-master','OpenDocument text master'),
'ott'=>array('document/oasis-text-template','OpenDocument text template'),
'pdf'=>array('document/pdf','Adobe PDF document'),
'txt'=>array('document/plaintext','Plain text'),
'text'=>'txt',
'eps'=>array('document/postscript','PostScript document'),
'ps'=>'eps',
'rtf'=>array('document/rtf','Rich Text document'),
'sxc'=>array('document/staroffice-calc','Sun StarOffice Calc document'),
'sxd'=>array('document/staroffice-draw','Sun StarOffice Draw document'),
'sxi'=>array('document/staroffice-impress','Sun StarOffice Impress presentation'),
'sxw'=>array('document/staroffice-writer','Sun StarOffice Writer document'),
'tex'=>array('document/tex','TeX document'),
'vcs'=>array('document/vcalendar','vCalendar file'),
'vcf'=>array('document/vcard','vCard file'),
'wpd'=>array('document/wordperfect','WordPerfect document'),
'crt'=>array('document/x509-cert','X.509 certificate'),
'der'=>'crt',
'xml'=>array('document/xml','XML document'),
'eot'=>array('font/ms-eot','Microsoft web font'),
'otf'=>array('font/opentype','OpenType font'),
'ttf'=>array('font/truetype','TrueType font'),
'pfa'=>array('font/type1','Type1 font'),
'pfb'=>'pfa',
'pfm'=>'pfa',
'kml'=>array('generic/google-earth-kml','Google Earth KML'),
'kmz'=>array('generic/google-earth-kmz','Google Earth KMZ'),
'pgp'=>array('generic/pgp-encrypted','PGP encrypted file'),
'asc'=>array('generic/pgp-signature','PGP signature'),
'sig'=>'asc',
'dng'=>array('image/adobe-dng','Adobe digital negative'),
'psd'=>array('image/adobe-photoshop','Adobe Photoshop image'),
'bmp'=>array('image/bitmap','Bitmap image'),
'gif'=>array('image/gif','GIF image'),
'jpeg'=>array('image/jpeg','JPEG image'),
'jpg'=>'jpeg',
'jpe'=>'jpeg',
'jp2'=>array('image/jpeg2000','JPEG2000 image'),
'ico'=>array('image/ms-icon','Microsoft icon image'),
'wmf'=>array('image/ms-wmf','Microsoft Windows Metafile graphic'),
'png'=>array('image/png','PNG image'),
'svg'=>array('image/svg','SVG graphic'),
'svgz'=>'svg',
'tiff'=>array('image/tiff','TIFF image'),
'tif'=>'tiff',
'xbm'=>array('image/xbitmap','X bitmap image'),
'xpm'=>array('image/xpixmap','X pixmap image'),
's'=>array('source/asm','Assembler source code'),
'asm'=>'s',
'bas'=>array('source/basic','BASIC source code'),
'c'=>array('source/c','C/C++ source code'),
'h'=>array('source/c-header','C/C++ header code'),
'cc'=>array('source/cpp','C/C++ source code'),
'cxx'=>'cc',
'cpp'=>'cc',
'hh'=>array('source/cpp-header','C/C++ header code'),
'hpp'=>'hh',
'cs'=>array('source/csharp','C# source code'),
'css'=>array('source/css','Cascading stylesheet'),
'html'=>array('source/html','HTML source code'),
'htm'=>'html',
'java'=>array('source/java','Java source code'),
'js'=>array('source/javascript','JavaScript source code'),
'vb'=>array('source/visual-basic','Visual Basic source code'),
'p'=>array('source/pascal','Pascal source code'),
'pas'=>'p',
'pl'=>array('source/perl','Perl source code'),
'php'=>array('source/php','PHP source code'),
'py'=>array('source/python','Python source code'),
'rb'=>array('source/ruby','Ruby source code'),
'rbw'=>'rb',
'wsdl'=>array('source/wsdl','WSDL source code'),
'xaml'=>array('source/xaml','XAML source code'),
'xhtml'=>array('source/xhtml','XHTML web page'),
'dtd'=>array('source/xml-dtd','XML document type definition'),
'xsd'=>array('source/xsd','XML schema'),
'xsl'=>array('source/xsl','XML stylesheet'),
'xslt'=>array('source/xslt','XSL transformation file'),
'xul'=>array('source/xul','Mozilla XUL source code'),
'3gp'=>array('video/3gpp','3GPP video'),
'qt'=>array('video/apple-quicktime','Apple QuickTime video'),
'mov'=>'qt',
'h264'=>array('video/h264','H.264 video'),
'mj2'=>array('video/mjp2','MJP2 video'),
'mjp2'=>'mj2',
'mpeg'=>array('video/mpeg','MPEG video'),
'mpg'=>'mpeg',
'mpe'=>'mpeg',
'm1v'=>'mpeg',
'm2v'=>'mpeg',
'mp4'=>array('video/mpeg4','MPEG4 video'),
'mp4v'=>'mp4',
'mpg4'=>'mp4',
'asf'=>array('video/ms-asf','Microsoft ASF video'),
'asx'=>'asf',
'avi'=>array('video/ms-avi','Microsoft AVI video'),
'vc1'=>array('video/ms-vc1','Microsoft VC-1 video'),
'wm'=>array('video/ms-wmv','Microsoft Windows Media video'),
'wmv'=>'wm',
'ogv'=>array('video/ogg','Ogg video'),
);
private static $icons = array(
'application',
'application_java-class',
'application_ms-installer',
'application_ms-script',
'application_ms-windows',
'application_sh',
'archive',
'archive_android-package',
'archive_debian-package',
'archive_mozilla-xpinstall',
'audio',
'audio_midi',
'document',
'document_config',
'document_csv',
'document_diff',
'document_email',
'document_ms-access',
'document_ms-excel',
'document_ms-htmlhelp',
'document_ms-powerpoint',
'document_ms-visio',
'document_ms-word',
'document_ms-write',
'document_ms-xps',
'document_oasis-chart-template',
'document_oasis-chart',
'document_oasis-database',
'document_oasis-formula-template',
'document_oasis-formula',
'document_oasis-graphics',
'document_oasis-presentation-template',
'document_oasis-presentation',
'document_oasis-spreadsheet-template',
'document_oasis-spreadsheet',
'document_oasis-text-master',
'document_oasis-text-template',
'document_oasis-text',
'document_pdf',
'document_plaintext',
'document_postscript',
'document_rtf',
'document_staroffice-calc',
'document_staroffice-draw',
'document_tex',
'document_vcalendar',
'document_vcard',
'document_x509-cert',
'document_xml',
'font_opentype',
'font_truetype',
'font_type1',
'generic_google-earth-kml',
'generic_google-earth-kmz',
'generic_pgp-encrypted',
'generic_pgp-signature',
'image',
'image_adobe-dng',
'image_adobe-photoshop',
'image_ms-wmf',
'image_svg',
'source',
'source_c-header',
'source_c',
'source_cpp-header',
'source_cpp',
'source_csharp',
'source_html',
'source_java',
'source_javascript',
'source_pascal',
'source_perl',
'source_php',
'source_python',
'source_ruby',
'source_xhtml',
'source_xml-dtd',
'source_xsd',
'source_xslt',
'unknown',
'video',
);
public static function GetContentTypeForFileName($filename)
{
if (!is_string($filename))
throw new InvalidArgumentException('Invalid argument type: $filename');
if (preg_match('~[^.].([^.]+)$~', $filename, $m))
{
$ext = strtolower($m[1]);
$type = self::$contentTypes[$ext];
if (is_array($type))
{
return $type[0];
}
else if (is_string($type))
{
$type = self::$contentTypes[$type];
if (is_array($type))
{
return $type[0];
}
}
return null;
}
}
public static function GetContentTypeDescription($contentType)
{
if (isset($contentType))
{
if (!is_string($contentType))
throw new InvalidArgumentException('Invalid argument type: $contentType');
if (!strlen($contentType))
$contentType = null;
}
foreach (self::$contentTypes as $type)
{
if (is_array($type))
{
if ($type[0] == $contentType) return $type[1];
}
}
return null;
}
public static function ContentTypeIcon($contentType)
{
$path = self::$contenttypeImgPath;
$fileName = str_replace('/', '_', $contentType);
if (!file_exists($path . $fileName . '.png'))
$fileName = preg_replace('~/.*$~', '', $contentType);
if (!file_exists($path . $fileName . '.png'))
$fileName = 'unknown';
return $path . $fileName . '.png';
}
public static function ContentTypeIconSprite($contentType)
{
$fileName = str_replace('/', '_', $contentType);
if (!in_array($fileName, self::$icons))
$fileName = preg_replace('~/.*$~', '', $contentType);
if (!in_array($fileName, self::$icons))
$fileName = 'unknown';
return $fileName;
}
}
class UnbTimeFormat
{
const Weekday = 1;
const WeekdayLong = 2;
const Date = 4;
const DateLong = 8;
const TimeSec = 16;
const Time = 32;
const TimeZone = 64;
const DateTime = 36;
const DateTimeSec = 20;
const DateTimeLong = 40;
const DateTimeSecLong = 24;
}
class UnbTimeContext
{
const Point = 1;
const Span = 2;
const Before = 3;
}
class UnbLocale
{
protected $code = null;
protected $nativeName = null;
protected $importance = 0;
protected $dependsOn = null;
protected $dict = array();
protected $quoteStart = '"';
protected $quoteEnd = '"';
protected $parentheseStart = '(';
protected $parentheseEnd = ')';
protected $colon = ':';
private static $knownLocales = array();
private static $prefLocales = array();
private static $timezone = null;
public static function AutoRegister($path = null)
{
if (!isset($path))
$path = __DIR__ . '/locales';
if (!is_string($path))
throw new InvalidArgumentException('Invalid argument type: $path');
foreach (glob($path . '/*.locale.class.php') as $file)
{
include_once($file);
}
}
public static function Register($obj)
{
if (!($obj instanceof UnbLocale))
throw new InvalidArgumentException('Invalid argument type: $obj');
self::$knownLocales[$obj->code] = $obj;
}
public static function Prefer($code, $clientSorted = false)
{
if (!is_string($code))
throw new InvalidArgumentException('Invalid argument type: $code');
if (strlen($code) != 2 && strlen($code) != 5)
throw new InvalidArgumentException('Argument out of range: $code');
if (!is_bool($clientSorted))
throw new InvalidArgumentException('Invalid argument type: $clientSorted');
if (!count(self::$knownLocales))
self::AutoRegister();
if (!count(self::$knownLocales))
throw new Exception('No locales defined');
if (strlen($code) == 2)
{
$codes = array();
$sort = array();
foreach (self::$knownLocales as $kl)
{
if (substr($kl->code, 0, 2) == $code)
{
$codes[] = $kl->code;
$sort[] = $kl->importance;
}
}
if (!$clientSorted)
{
array_multisort($sort, SORT_DESC, $codes);
foreach ($codes as $code2)
{
self::Prefer($code2);
}
}
else
{
foreach (UnbEnvironment::GetClientLanguages() as $clientCode)
{
if (in_array($clientCode, $codes))
self::Prefer($clientCode);
}
}
}
else
{
foreach (self::$prefLocales as $pl)
{
if ($pl->code == $code) return;
}
foreach (self::$knownLocales as $kl)
{
if ($kl->code == $code)
{
self::$prefLocales[] = $kl;
if (isset($kl->dependsOn))
{
self::Prefer($kl->dependsOn);
}
return;
}
}
}
}
public static function AutoPrefer()
{
if (!count(self::$knownLocales))
self::AutoRegister();
if (!count(self::$knownLocales))
throw new Exception('No locales defined');
self::$prefLocales = array();
$pageLanguage = null; #UnbEnvironment::PageLanguage();
if (isset($pageLanguage))
{
self::Prefer($pageLanguage, true);
}
foreach (UnbEnvironment::GetClientLanguages() as $code)
{
self::Prefer($code);
}
if (!count(self::$prefLocales))
{
self::Prefer('en-US');
}
if (!count(self::$prefLocales) && count(self::$knownLocales))
{
self::Prefer(self::$knownLocales[0]);
}
}
public static function GetCode()
{
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->code;
}
return null;
}
public static function GetNativeName()
{
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->nativeName;
}
return null;
}
public static function GetPreferredLocaleCodes()
{
$a = array();
foreach (self::$prefLocales as $pl)
{
$a[] = $pl->code;
}
return $a;
}
public static function SetTimeZone($timezone)
{
if (is_string($timezone))
$timezone = new DateTimeZone($timezone);
else if (!($obj instanceof DateTimeZone))
throw new InvalidArgumentException('Invalid argument type: $timezone');
self::$timezone = $timezone;
}
public static function AddDict($code, $dict)
{
if (!is_string($code))
throw new InvalidArgumentException('Invalid argument type: $code');
if (!is_array($dict))
throw new InvalidArgumentException('Invalid argument type: $dict');
if (!count(self::$knownLocales))
self::AutoRegister();
$locale = self::$knownLocales[$code];
if (!isset($locale))
return;
$locale->dict = array_merge($locale->dict, $dict);
}
public static function Translate($key)
{
if (!is_string($key))
throw new InvalidArgumentException('Invalid argument type: $key');
$args = array_slice(func_get_args(), 1);
if (count($args) == 1 && $args[0] === true)
{
$safe = true;
array_shift($args);
}
if (count($args) % 2)
{
$num = intval(array_shift($args));
}
if (($pos = strrpos($key, ':')) !== false)
{
$prefix = substr($key, 0, $pos);
}
foreach (self::$prefLocales as $pl)
{
if ($pl->dict[$prefix . ':*'] === false) break;
if (isset($num))
{
$s = $pl->dict[$key . '#' . $num];
if (!isset($s))
$s = $pl->dict[$key . '#' . ($num % 100) . '%100'];
if (!isset($s))
$s = $pl->dict[$key . '#' . ($num % 10) . '%10'];
if (!isset($s))
$s = $pl->dict[$key];
}
else
{
$s = $pl->dict[$key];
}
if (isset($s)) break;
if (!isset($s) && $pl->dict[$prefix . ':#nofallback'] === true) break;
}
if (!isset($s))
{
if ($safe)
{
if (($pos = strrpos($key, ':')) !== false)
{
$key = substr($key, $pos + 1);
}
return $key;
}
return null;
}
if (isset($num))
{
$s = str_replace('{#}', $num, $s);
}
for ($pos = 0; $pos + 1 < sizeof($args); $pos += 2)
{
$s = str_replace('{' . $args[$pos] . '}', $args[$pos + 1], $s);
}
return $s;
}
public static function GetAllTexts($search)
{
if (!is_string($search))
throw new InvalidArgumentException('Invalid argument type: $search');
$texts = array();
foreach (self::$prefLocales as $pl)
{
foreach ($pl->dict as $key => $text)
{
if (!isset($texts[$key]) && strpos($key, $search) !== false)
{
$texts[$key] = $text;
}
}
}
return $texts;
}
public static function ContentType($contentType)
{
if (!is_string($contentType))
throw new InvalidArgumentException('Invalid argument type: $contentType');
$str = self::Translate('contenttype:' . $contentType);
if (!isset($str))
{
$str = UnbMessage::GetContentTypeDescription($contentType);
}
if (!isset($str))
{
$str = self::Translate('contenttype:unknown');
}
return $str;
}
public static function Capital($str)
{
if (!isset($str)) return null;
if (!is_string($str))
throw new InvalidArgumentException('Invalid argument type: $str');
return mb_strtoupper(mb_substr($str, 0, 1)) . mb_substr($str, 1);
}
public static function Quote($str)
{
if (!is_string($str))
throw new InvalidArgumentException('Invalid argument type: $str');
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->quoteStart .
$str .
self::$prefLocales[0]->quoteEnd;
}
return '"' . $str . '"';
}
public static function Parentheses($str)
{
if (!is_string($str))
throw new InvalidArgumentException('Invalid argument type: $str');
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->parentheseStart .
$str .
self::$prefLocales[0]->parentheseEnd;
}
return '(' . $str . ')';
}
public static function Colon()
{
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->colon;
}
return ':';
}
public static function Number($number, $decimals = 0, $html = false, $trim = true)
{
if (!is_int($number) && !is_float($number) && !is_numeric($number))
throw new InvalidArgumentException('Invalid argument type: $number');
if (!is_int($decimals))
throw new InvalidArgumentException('Invalid argument type: $decimals');
if (!is_bool($html))
throw new InvalidArgumentException('Invalid argument type: $html');
if (!is_bool($trim))
throw new InvalidArgumentException('Invalid argument type: $trim');
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->FormatNumber($number, $decimals, $html, $trim);
}
return $number;
}
public static function ParseNumber($str, $defaultValue = null, $recoveryValue = null)
{
if (!is_string($str))
throw new InvalidArgumentException('Invalid argument type: $str');
if (isset($defaultValue))
{
if (!is_int($defaultValue) && !is_float($defaultValue))
throw new InvalidArgumentException('Invalid argument type: $defaultValue');
}
if (isset($recoveryValue))
{
if (!is_int($recoveryValue) && !is_float($recoveryValue))
throw new InvalidArgumentException('Invalid argument type: $recoveryValue');
}
if (!strlen($str))
{
if (isset($defaultValue))
return $defaultValue;
throw new InvalidArgumentException('No value specified.');
}
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->DoParseNumber($str, $recoveryValue);
}
if (is_numeric($str))
{
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
throw new InvalidArgumentException('Invalid numeric value format.');
}
public static function OrdinalNumber($number, $html = false)
{
if (!is_int($number))
throw new InvalidArgumentException('Invalid argument type: $number');
if (!is_bool($html))
throw new InvalidArgumentException('Invalid argument type: $html');
if (count(self::$prefLocales))
{
return self::$prefLocales[0]->FormatOrdinalNumber($number, $html);
}
return $number . '.';
}
public static function DataSize($size, $html = false)
{
if (!is_int($size))
throw new InvalidArgumentException('Invalid argument type: $size');
if (!is_bool($html))
throw new InvalidArgumentException('Invalid argument type: $html');
if ($size == 0)
return self::Translate('empty');
if ($size < 0.9 * 1024)
return self::NoBreak(self::Translate('unit:bytes', $size));
if ($size < 50 * 1024)
return self::NoBreak(self::Number($size / 1024, 1, $html) . ' KiB');
if ($size < 0.9 * 1024 * 1024)
return self::NoBreak(self::Number($size / 1024, 0, $html) . ' KiB');
if ($size < 50 * 1024 * 1024)
return self::NoBreak(self::Number($size / 1024 / 1024, 1, $html) . ' MiB');
if ($size < 0.9 * 1024 * 1024 * 1024)
return self::NoBreak(self::Number($size / 1024 / 1024, 0, $html) . ' MiB');
if ($size < 50 * 1024 * 1024 * 1024)
return self::NoBreak(self::Number($size / 1024 / 1024 / 1024, 1, $html) . ' GiB');
return self::NoBreak(self::Number($size / 1024 / 1024 / 1024, 0, $html) . ' GiB');
}
public static function Time($time, $details = UnbTimeFormat::DateTime, $html = false, $utc = false)
{
if (!is_int($time) && !is_string($time))
throw new InvalidArgumentException('Invalid argument type: $time');
if (!is_int($details))
throw new InvalidArgumentException('Invalid argument type: $details');
if ($details <= 0)
throw new InvalidArgumentException('Argument out of range: $details');
if (!is_bool($html))
throw new InvalidArgumentException('Invalid argument type: $html');
if (!count(self::$prefLocales))
throw new BadMethodCallException('No locale selected');
if (!isset($time) ||
$time === '' ||
$time === 0)
{
return self::Translate('time:none');
}
if (is_int($time) || is_numeric($time))
$time = '@' . $time;
$dt = new DateTime($time, new DateTimeZone('UTC'));
if (!$utc)
{
if (isset(self::$timezone))
{
$dt->setTimeZone(self::$timezone);
}
else
{
$d = new DateTime('now');
$tz = $d->getTimeZone();
$dt->setTimeZone($tz);
}
}
return self::$prefLocales[0]->FormatTime($dt, $details, $html);
}
public static function TimeToLocal($time)
{
$dt = new DateTime($time, new DateTimeZone('UTC'));
if (isset(self::$timezone))
{
$dt->setTimeZone(self::$timezone);
}
else
{
$d = new DateTime('now');
$tz = $d->getTimeZone();
$dt->setTimeZone($tz);
}
return $dt->format('Y-m-d H:i:s');
}
public static function TimeToUtc($time)
{
if (isset(self::$timezone))
{
$dt = new DateTime($time, self::$timezone);
}
else
{
$dt = new DateTime($time);
}
$dt->setTimeZone(new DateTimeZone('UTC'));
return $dt->format('Y-m-d H:i:s');
}
public static function RelativeTime($time, $context = UnbTimeContext::Point, $html = false)
{
if (!is_int($time) && !is_string($time))
throw new InvalidArgumentException('Invalid argument type: $time');
if (!is_int($context))
throw new InvalidArgumentException('Invalid argument type: $context');
if ($context <= 0)
throw new InvalidArgumentException('Argument out of range: $context');
if (!is_bool($html))
throw new InvalidArgumentException('Invalid argument type: $html');
if (!count(self::$prefLocales))
throw new BadMethodCallException('No locale selected');
if (!isset($time) ||
$time === '' ||
$time === 0)
{
return self::Translate('time:never');
}
if (is_int($time) || is_numeric($time))
$time = '@' . $time;
$utc = new DateTimeZone('UTC');
$dt = new DateTime($time, $utc);
$now = new DateTime('now', $utc);
if ($dt == $now)
{
return self::Translate('time:now');
}
$diff = $now->diff($dt);
if (($diff->y || $diff->m || $diff->d || $diff->h || $diff->i) && $diff->s <= 3)
{
$diff->s = 0;
}
else if ($diff->s >= 57)
{
$diff->i = $diff->i + 1;
$diff->s = 0;
}
if (($diff->y || $diff->m || $diff->d || $diff->h) && $diff->i <= 3)
{
$diff->i = 0;
}
else if ($diff->i >= 57)
{
$diff->h = $diff->h + 1;
$diff->i = 0;
}
if (($diff->y || $diff->m || $diff->d) && $diff->h <= 1)
{
$diff->h = 0;
}
else if ($diff->h >= 23)
{
$diff->d = $diff->d + 1;
$diff->h = 0;
}
if (($diff->y || $diff->m) && $diff->d <= 2)
{
$diff->d = 0;
}
else if ($diff->d >= 28)
{
$diff->m = $diff->m + 1;
$diff->d = 0;
}
if ($diff->y >= 5)
{
if ($diff->m >= 7)
$diff->y = $diff->y + 1;
$diff->m = 0;
}
if ($diff->y >= 1)
{
$diff->d = 0;
$diff->h = 0;
$diff->i = 0;
$diff->s = 0;
}
if ($diff->m >= 5)
{
if ($diff->d >= 16)
$diff->m = $diff->m + 1;
$diff->d = 0;
}
if ($diff->m >= 1)
{
$diff->h = 0;
$diff->i = 0;
$diff->s = 0;
}
if ($diff->d >= 5)
{
if ($diff->h >= 12)
$diff->d = $diff->d + 1;
$diff->h = 0;
}
if ($diff->d >= 1)
{
$diff->i = 0;
$diff->s = 0;
}
if ($diff->h >= 5)
{
if ($diff->i >= 30)
$diff->h = $diff->h + 1;
$diff->i = 0;
}
if ($diff->h >= 1)
{
$diff->s = 0;
}
if ($diff->i >= 5)
{
if ($diff->s >= 30)
$diff->i = $diff->i + 1;
$diff->s = 0;
}
return self::$prefLocales[0]->FormatRelativeTime($diff, $context, $html);
}
public static function NoBreak($str)
{
if (!is_string($str))
throw new InvalidArgumentException('Invalid argument type: $str');
return preg_replace('~(\d) ~', "\\1\xC2\xA0", $str);
}
protected function FormatNumber($number, $decimals, $html)
{
throw new BadMethodCallException('Method not implemented in locale.');
}
protected function DoParseNumber($str, $recoveryValue)
{
throw new BadMethodCallException('Method not implemented in locale.');
}
protected function FormatOrdinalNumber($number, $html)
{
throw new BadMethodCallException('Method not implemented in locale.');
}
protected function FormatTime($datetime, $details, $html)
{
throw new BadMethodCallException('Method not implemented in locale.');
}
protected function FormatRelativeTime($diff, $context, $html)
{
throw new BadMethodCallException('Method not implemented in locale.');
}
}
class UnbLocale_de_AT extends UnbLocale
{
protected $code = 'de-AT';
protected $nativeName = 'Deutsch (Österreich)';
protected $importance = 7500;
protected $dependsOn = 'de-DE';
protected $quoteStart = '„';
protected $quoteEnd = '“';
protected $colon = ':';
protected $dict = array(
);
protected function FormatNumber($number, $decimals, $html, $trim)
{
$str = number_format($number, $decimals, ',', ' ');
if ($trim && strpos($str, ',') !== false)
{
$str = rtrim($str, '0');
$str = rtrim($str, ',');
}
if ($html)
{
if ($number >= 10000)
{
$str = str_replace(' ', "\xE2\x80\xAF" /* UTF-8: U+202F Narrow No-Break Space */, $str);
}
else
{
$str = str_replace(' ', '', $str);
}
}
return $str;
}
protected function DoParseNumber($str, $recoveryValue)
{
$str = str_replace('.', '', $str);
$str = str_replace(' ', '', $str);
$str = str_replace(',', '.', $str);
if (!is_numeric($str))
{
if (isset($recoveryValue))
return $recoveryValue;
throw new InvalidArgumentException('Invalid numeric value format.');
}
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
protected function FormatOrdinalNumber($number, $html)
{
return $number . '.';
}
protected function FormatTime($datetime, $details, $html)
{
$sp = "\xC2\xA0";
$lsp = "\xE2\x80\x87";
$str = '';
if ($details & UnbTimeFormat::Weekday)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekday:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::Weekday ? ',' : '');
}
else if ($details & UnbTimeFormat::WeekdayLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekdaylong:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::WeekdayLong ? ',' : '');
}
if ($details & UnbTimeFormat::Date)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('d.m.Y');
$haveDate = true;
}
else if ($details & UnbTimeFormat::DateLong)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('j.') . $sp .
UnbLocale::Translate('monthlong:' . $datetime->format('n')) . $sp .
$datetime->format('Y');
$haveDate = true;
}
if ($details & UnbTimeFormat::TimeSec)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H:i:s');
}
else if ($details & UnbTimeFormat::Time)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H:i');
}
if ($details & UnbTimeFormat::TimeZone)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('timezone:' . $datetime->format('T'), true);
}
return $str;
}
protected function FormatRelativeTime($diff, $context, $html)
{
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$grammar = 2;
else
$grammar = 2;
break;
case UnbTimeContext::Span:
if ($diff->invert)
$grammar = 2;
else
$grammar = 1;
break;
case UnbTimeContext::Before:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
}
$parts = array();
if ($diff->y && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':years', $diff->y));
if ($diff->m && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':months', $diff->m));
if ($diff->d && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':days', $diff->d));
if ($diff->h && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':hours', $diff->h));
if ($diff->i && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':minutes', $diff->i));
if ($diff->s && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':seconds', $diff->s));
$str = '';
foreach ($parts as $part)
{
$str .= (strlen($str) ? UnbLocale::Translate('time:combine') : '') .
$part;
}
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$str = self::Translate('time:pattern point past', 'time', $str);
else
$str = self::Translate('time:pattern point future', 'time', $str);
break;
case UnbTimeContext::Span:
if ($diff->invert)
$str = self::Translate('time:pattern span past', 'time', $str);
else
$str = self::Translate('time:pattern span future', 'time', $str);
break;
case UnbTimeContext::Before:
if ($diff->invert)
$str = self::Translate('time:pattern before past', 'time', $str);
else
$str = self::Translate('time:pattern before future', 'time', $str);
break;
}
return $str;
}
}
UnbLocale::Register(new UnbLocale_de_AT());
class UnbLocale_de_CH extends UnbLocale
{
protected $code = 'de-CH';
protected $nativeName = 'Deutsch (Schweiz)';
protected $importance = 5000;
protected $dependsOn = 'de-DE';
protected $quoteStart = '«';
protected $quoteEnd = '»';
protected $colon = ':';
protected $dict = array(
'error:password security.character classes' => 'Mindestens 2 Zeichenklassen aus Zahlen, Kleinbuchstaben, Grossbuchstaben und anderen Zeichen erforderlich.',
'screen size' => 'Bildschirmgrösse',
'viewport size' => 'Fensterinnengrösse',
'country:BY' => 'Belarus (Weissrussland)',
'language:be' => 'weissrussisch',
);
protected function FormatNumber($number, $decimals, $html, $trim)
{
$str = number_format($number, $decimals, ',', ' ');
if ($trim && strpos($str, ',') !== false)
{
$str = rtrim($str, '0');
$str = rtrim($str, ',');
}
if ($html)
{
if ($number >= 10000)
{
$str = str_replace(' ', "\xE2\x80\xAF" /* UTF-8: U+202F Narrow No-Break Space */, $str);
}
else
{
$str = str_replace(' ', '', $str);
}
}
return $str;
}
protected function DoParseNumber($str, $recoveryValue)
{
$str = str_replace('.', '', $str);
$str = str_replace(' ', '', $str);
$str = str_replace(',', '.', $str);
if (!is_numeric($str))
{
if (isset($recoveryValue))
return $recoveryValue;
throw new InvalidArgumentException('Invalid numeric value format.');
}
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
protected function FormatOrdinalNumber($number, $html)
{
return $number . '.';
}
protected function FormatTime($datetime, $details, $html)
{
$sp = "\xC2\xA0";
$lsp = "\xE2\x80\x87";
$str = '';
if ($details & UnbTimeFormat::Weekday)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekday:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::Weekday ? ',' : '');
}
else if ($details & UnbTimeFormat::WeekdayLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekdaylong:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::WeekdayLong ? ',' : '');
}
if ($details & UnbTimeFormat::Date)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('d.m.Y');
$haveDate = true;
}
else if ($details & UnbTimeFormat::DateLong)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('j.') . $sp .
UnbLocale::Translate('monthlong:' . $datetime->format('n')) . $sp .
$datetime->format('Y');
$haveDate = true;
}
if ($details & UnbTimeFormat::TimeSec)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H.i.s');
}
else if ($details & UnbTimeFormat::Time)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H.i');
}
if ($details & UnbTimeFormat::TimeZone)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('timezone:' . $datetime->format('T'), true);
}
return $str;
}
protected function FormatRelativeTime($diff, $context, $html)
{
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$grammar = 2;
else
$grammar = 2;
break;
case UnbTimeContext::Span:
if ($diff->invert)
$grammar = 2;
else
$grammar = 1;
break;
case UnbTimeContext::Before:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
}
$parts = array();
if ($diff->y && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':years', $diff->y));
if ($diff->m && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':months', $diff->m));
if ($diff->d && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':days', $diff->d));
if ($diff->h && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':hours', $diff->h));
if ($diff->i && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':minutes', $diff->i));
if ($diff->s && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':seconds', $diff->s));
$str = '';
foreach ($parts as $part)
{
$str .= (strlen($str) ? UnbLocale::Translate('time:combine') : '') .
$part;
}
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$str = self::Translate('time:pattern point past', 'time', $str);
else
$str = self::Translate('time:pattern point future', 'time', $str);
break;
case UnbTimeContext::Span:
if ($diff->invert)
$str = self::Translate('time:pattern span past', 'time', $str);
else
$str = self::Translate('time:pattern span future', 'time', $str);
break;
case UnbTimeContext::Before:
if ($diff->invert)
$str = self::Translate('time:pattern before past', 'time', $str);
else
$str = self::Translate('time:pattern before future', 'time', $str);
break;
}
return $str;
}
}
UnbLocale::Register(new UnbLocale_de_CH());
class UnbLocale_de_DE extends UnbLocale
{
protected $code = 'de-DE';
protected $nativeName = 'Deutsch (Deutschland)';
protected $importance = 77000;
protected $quoteStart = '„';
protected $quoteEnd = '“';
protected $colon = ':';
protected $dict = array(
'exception:BadMethodCallException' => 'Ungültiger Methodenaufruf',
'exception:Exception' => 'Fehler',
'exception:InvalidArgumentException' => 'Ungültiger Parameter',
'exception:LengthException' => 'Falsche Länge',
'error:password security.too short' => 'Mindestens 6 Zeichen erforderlich.',
'error:password security.equals username' => 'Kennwort darf nicht gleich dem Benutzernamen sein.',
'error:password security.contains username' => 'Kennwort darf den Benutzernamen nicht enthalten.',
'error:password security.non ascii' => 'Nur ASCII-Zeichen zulässig.',
'error:password security.character classes' => 'Mindestens 2 Zeichenklassen aus Zahlen, Kleinbuchstaben, Großbuchstaben und anderen Zeichen erforderlich.',
'error:password security.repeated character' => 'Kein Zeichen darf 3 mal oder öfter wiederholt werden.',
'error:password security.simple sequences' => 'Einfache Folgen wie 123 oder abc sind unzulässig.',
'error:password security.different characters' => 'Mindestens 4 unterschiedliche Zeichen erforderlich.',
'empty' => 'empty',
'unit:bytes' => '{#} Bytes',
'unit:bytes#1' => 'ein Byte',
'format:decimal' => ',',
'active' => 'aktiv',
'add field' => 'Feld hinzufügen',
'admin opt-out cookie' => 'Admin-Opt-out-Cookie',
'admin opt-out cookie.do set' => 'Opt-out-Cookie setzen',
'admin opt-out cookie.do unset' => 'Opt-out-Cookie löschen',
'admin opt-out cookie.set' => 'Das Admin-Opt-out-Cookie ist gesetzt und gültig. Sie können sich frei auf Ihrer Website bewegen, ohne erfasst zu werden.',
'admin opt-out cookie.unset' => 'Das Admin-Opt-out-Cookie ist derzeit nicht gesetzt oder ungültig. Alle Ihre Aktionen auf dieser Website werden normal aufgezeichnet.',
'app info' => 'Anwendungs-Info',
'available screen' => 'davon verfügbar',
'bounce' => 'Absprung',
'campaign' => 'Kampagne',
'client' => 'Client',
'column' => 'Spalte',
'current language' => 'aktuelle Sprache',
'custom pageview variable' => 'Benutzerdefinierte Variable für den Seitenaufruf',
'custom pageview variable created' => 'Seitenaufruf-Variable {name} in der Datenbank erstellt.',
'custom pageview variable create error' => 'Fehler beim Erstellen der Seitenaufruf-Variable {name} in der Datenbank.',
'custom session variable' => 'Benutzerdefinierte Variable für den Besuch',
'custom session variable created' => 'Besuchsvariable {name} in der Datenbank erstellt.',
'custom session variable create error' => 'Fehler beim Erstellen der Besuchsvariable {name} in der Datenbank.',
'custom variables' => 'Benutzerdefinierte Variablen',
'custom visitor variable' => 'Benutzerdefinierte Variable für Besucher',
'custom visitor variable created' => 'Besuchervariable {name} in der Datenbank erstellt.',
'custom visitor variable create error' => 'Fehler beim Erstellen der Besuchervariable {name} in der Datenbank.',
'customvar.scope' => 'Bereich',
'customvar.name' => 'Name',
'customvar.type' => 'Typ',
'customvar.notes' => 'Hinweise',
'customvar.create in database' => 'in der Datenbank erstellen',
'customvar.in database not configured' => 'in der Datenbank, nicht konfiguriert',
'customvar.invalid name' => 'ungültiger Variablenname!',
'customvar.none' => 'Keine Variablen konfiguriert oder in der Datenbank.',
'customvar.ok' => 'OK',
'data files status' => 'Status der Datendateien',
'direct entry' => 'direkter Aufruf',
'direct entry or bookmark' => 'direkter Aufruf oder Lesezeichen',
'disabled' => 'deaktiviert',
'download' => 'Download',
'enter access password' => 'Geben Sie das Zugangskennwort ein',
'entry page' => 'Einstiegsseite',
'exit page' => 'Ausstiegsseite',
'field' => 'Feld',
'file size' => 'Dateigröße',
'filter' => 'Filter',
'first entry' => 'Erster Eintrag',
'geoip.country-level database' => 'Länderdatenbank',
'geoip.city-level database' => 'Städtedatenbank',
'image zoom' => 'Bildvergrößerung',
'info and management' => 'Information und Verwaltung',
'limit visitor log.1' => 'Besucherprotokoll auf',
'limit visitor log.2' => 'Zeilen begrenzen',
'loading canceled' => 'Laden abgebrochen',
'local' => 'lokal',
'location' => 'Ort',
'location/duration' => 'Ort/Dauer',
'login' => 'anmelden',
'login.temporary' => 'vorübergehend',
'login.temporary~' => 'Speichert die Anmeldung nur für diese Browsersitzung statt für längere Zeit.',
'logout' => 'abmelden',
'mobile device' => 'mobiles Gerät',
'navigated back' => 'Zurück navigiert',
'navigated forward' => 'Vorwärts navigiert',
'no filter set' => 'kein Filter gesetzt',
'no rdns' => 'kein RDNS',
'no search terms available' => 'Suchbegriffe nicht verfügbar',
'not' => 'nicht',
'not present' => 'nicht vorhanden',
'operation' => 'Vergleich',
'options' => 'Optionen',
'outlink' => 'ausgehender Verweis',
'overview link' => 'Zurück zur Übersichtsseite',
'page' => 'Seite',
'page load time' => 'Seitenladezeit',
'pageview' => 'Seitenaufruf',
'pageviews' => 'Seitenaufrufe',
'public suffix list' => 'Liste der öffentlichen Suffixe',
'query caption' => 'tòmò-Abfrage',
'query returned no data' => 'Diese Abfrage liefert keine Daten zurück.',
'query view' => 'Abfrageansicht',
'query view intro' => 'Wählen Sie eine Abfrage aus der Liste oben rechts aus, um die Auswertung anzuzeigen.',
'referring website' => 'Verweisende Webseite',
'reloaded page' => 'Seite neu geladen',
'remove column' => 'Spalte entfernen',
'returning visitor' => 'wiederkehrender Besucher',
'reverse visitor log order' => 'Reihenfolge umkehren und neueste Seitenaufrufe zuerst anzeigen',
'revisited page' => 'Seite erneut besucht',
'screen size' => 'Bildschirmgröße',
'search engine' => 'Suchmaschine',
'search image' => 'Gefundenes Bild',
'section expand' => 'Bereich geöffnet',
'select query' => 'Abfrage auswählen',
'session' => 'Besuch',
'session continuation' => 'Fortsetzung eines früheren Besuchs',
'session hit count' => '{#} Aufrufe',
'session hit count#1' => 'ein Aufruf',
'session page count' => 'von {#} Seiten',
'session page count#1' => 'von einer Seite',
'session time over' => 'über',
'sessions' => 'Besuche',
'set' => 'gesetzt',
'site search' => 'Seiteninterne Suche',
'source/action' => 'Quelle/Aktion',
'summary' => 'Zusammenfassung',
'summary.unique visitors' => 'Unterschiedliche Besucher',
'summary.first' => 'Erster',
'summary.last' => 'Letzter',
'texts' => 'Texte',
'time' => 'Zeit',
'time since page load' => 'Zeitangabe seit dem Laden der Seite',
'tomo database' => 'tòmò-Datenbank',
'tomo viewer title' => 'tòmò-Betrachter',
'tomo viewer intro' => 'Wählen Sie eine Ansicht:',
'unknown' => 'unbekannt',
'update' => 'aktualisieren',
'updated on date' => 'aktualisiert am {date}',
'value' => 'Wert',
'version' => 'Version',
'view button' => 'anzeigen',
'viewport size' => 'Fensterinnengröße',
'visitor' => 'Besucher',
'visitors' => 'Besucher',
'visitor log' => 'Besucherprotokoll',
'warning.access password unset' => 'Das Zugangskennwort ist nicht gesetzt. Ein Angreifer könnte sich in dieser Anwendung ohne ein Kennwort anmelden. Sie sollten das Kennwort möglichst schnell in der Konfigurationsdatei festlegen.',
'warning.access password default' => 'Das Zugangskennwort ist noch auf den Vorgabewert eingestellt. Ein Angreifer könnte es leicht erraten und sich in dieser Anwendung anmelden. Sie sollten das Kennwort möglichst schnell in der Konfigurationsdatei ändern.',
'warning.opt-out password unset' => 'Das Opt-out-Kennwort ist nicht gesetzt. Jeder Besucher Ihrer Website könnte sich ein eigenes Opt-out-Cookie erstellen und so die Besuchserfassung umgehen. Sie sollten das Kennwort in der Konfigurationsdatei festlegen.',
'warning.opt-out password default' => 'Das Opt-out-Kennwort ist noch auf den Vorgabewert eingestellt. Jeder Besucher Ihrer Website könnte es leicht erraten, sich ein eigenes Opt-out-Cookie erstellen und so die Besuchserfassung umgehen. Sie sollten das Kennwort in der Konfigurationsdatei ändern.',
'column:ActionData' => 'Aktionsdaten',
'column:ActionId' => 'Aktions-ID',
'column:ActionTime' => 'Aktionszeit',
'column:ActionType' => 'Aktionstyp',
'column:ActionUrl' => 'Aktions-URL',
'column:ActivityTimespan' => 'Aktivitätsdauer',
'column:AdBlock' => 'Werbefilter',
'column:Browser' => 'Browser',
'column:Campaign' => 'Kampagne',
'column:CampaignRedirect' => 'CampaignRedirect',
'column:City' => 'Stadt',
'column:Cookies' => 'Cookies',
'column:CountryCode' => 'Ländercode',
'column:DNT' => 'DNT',
'column:Domain' => 'Domain',
'column:EntryPage' => 'Einstiegsseite',
'column:ExitPage' => 'Ausstiegsseite',
'column:Flash' => 'Flash',
'column:IPAddress' => 'IP-Adresse',
'column:IPVersion' => 'IP-Version',
'column:Java' => 'Java',
'column:JavaScript' => 'JavaScript',
'column:JavaScriptData' => 'JavaScript-Daten',
'column:Language' => 'Sprache',
'column:Lat' => 'Breitengrad',
'column:Lng' => 'Längengrad',
'column:Mobile' => 'Mobiles Gerät',
'column:NewVisitor' => 'Neuer Besucher',
'column:OperatingSystem' => 'Betriebssystem',
'column:OriginalPageviewId' => 'Ursprüngliche Seitenaufruf-ID',
'column:PageLoadTime' => 'Seitenladezeit',
'column:PageTitle' => 'Seitentitel',
'column:PageLanguage' => 'Seitensprache',
'column:PageviewId' => 'Seitenaufruf-ID',
'column:PageviewTime' => 'Aufrufzeit',
'column:PageviewUpdateTime' => 'Seitenaufruf-Aktualisierungszeit',
'column:Prerender' => 'Prerender',
'column:Provider' => 'Provider',
'column:Quicktime' => 'Quicktime',
'column:ReferrerHost' => 'Verweis-Host',
'column:ReferrerUrlPath' => 'Verweis-Pfad',
'column:ReferrerSsl' => 'Verweis-SSL',
'column:Region' => 'Region',
'column:ScreenAvailHeight' => 'Desktophöhe',
'column:ScreenAvailWidth' => 'Desktopbreite',
'column:ScreenColors' => 'Farbtiefe',
'column:ScreenHeight' => 'Bildschirmhöhe',
'column:ScreenWidth' => 'Bildschirmbreite',
'column:SearchEngine' => 'Suchmaschine',
'column:SearchImageUrl' => 'Bildsuche-URL',
'column:SearchKeywords' => 'Suchbegriffe',
'column:SearchResultPage' => 'Suchergebnisseite',
'column:SessionDuration' => 'Besuchsdauer',
'column:SessionId' => 'Besuchs-ID',
'column:SessionStartTime' => 'Besuchsstartzeit',
'column:SessionUpdateTime' => 'Besuchs-Aktualisierungszeit',
'column:Silverlight' => 'Silverlight',
'column:Ssl' => 'SSL',
'column:UrlHash' => 'URL-Hash',
'column:UrlPath' => 'URL-Pfad',
'column:UrlQuery' => 'URL-Query',
'column:UtcOffset' => 'UTC-Verschiebung',
'column:ViewportHeight' => 'Seitenhöhe',
'column:ViewportWidth' => 'Seitenbreite',
'column:VisitorId' => 'Besucher-ID',
'column:WindowsMedia' => 'Windows Media',
'coldesc:ActionData' => 'Zusätzliche Daten zur durchgeführten Aktion',
'coldesc:ActionId' => 'Interne ID der durchgeführten Aktion',
'coldesc:ActionTime' => 'Zeit, wann die Aktion durchgeführt wurde',
'coldesc:ActionType' => 'Interner Code des Typs der durchgeführten Aktion',
'coldesc:ActionUrl' => 'URL der durchgeführten Aktion',
'coldesc:ActivityTimespan' => 'Dauer, in der der Besucher auf der Seite aktiv war, in Sekunden',
'coldesc:AdBlock' => 'Gibt an, ob der Besucher einen Werbefilter installiert hat',
'coldesc:Browser' => 'Name und Version des Browsers',
'coldesc:Campaign' => 'Name der Kampagne',
'coldesc:CampaignRedirect' => 'Gibt an, ob der Seitenaufruf von einer Kampagnen-URL weggeleitet hat',
'coldesc:City' => 'Stadt, in der der Besucher lokalisiert wurde',
'coldesc:Cookies' => 'Gibt an, ob der Browser Cookies unterstützt',
'coldesc:CountryCode' => 'Code des Lands, in dem der Besucher lokalisiert wurde',
'coldesc:DNT' => 'Gibt an, ob der Browser den Do-Not-Track-Header übermittelt hat',
'coldesc:Domain' => 'Domain der aufgerufenen Seite',
'coldesc:EntryPage' => 'Gibt an, ob der Seitenaufruf der erste des Besuchs war',
'coldesc:ExitPage' => 'Gibt an, ob der Seitenaufruf der letzte des Besuchs war',
'coldesc:Flash' => 'Gibt an, ob der Browser das Flash-Plug-in unterstützt',
'coldesc:IPAddress' => 'IP-Adresse des Besuchers, oder deren Hashwert',
'coldesc:IPVersion' => 'IP-Version des Besuchers (4 or 6)',
'coldesc:Java' => 'Gibt an, ob der Browser das Java-Plug-in unterstützt',
'coldesc:JavaScript' => 'Gibt an, ob der Browser JavaScript unterstützt',
'coldesc:JavaScriptData' => 'Gibt an, ob die Browserdaten mit JavaScript ermittelt wurden',
'coldesc:Language' => 'Sortierte Sprachcodes, die vom Browser bevorzugt werden, kommagetrennt',
'coldesc:Lat' => 'Breitengrad der ungefähren Geoposition',
'coldesc:Lng' => 'Längengrad der ungefähren Geoposition',
'coldesc:Mobile' => 'Gibt an, ob der Client ein mobiles Gerät ist',
'coldesc:NewVisitor' => 'Gibt an, ob der Besucher zum ersten Mal gesehen wurde',
'coldesc:OperatingSystem' => 'Betriebssystemname und -version',
'coldesc:OriginalPageviewId' => 'Interne ID des ursprünglichen Seitenaufrufs',
'coldesc:PageLoadTime' => 'Zeit, die zum Laden der Seite benötigt wurde, in Millisekunden',
'coldesc:PageTitle' => 'Titel der aufgerufenen Seite',
'coldesc:PageLanguage' => 'Sprache, in der die Seite angezeigt wurde',
'coldesc:PageviewId' => 'Interne ID des Seitenaufrufs',
'coldesc:PageviewTime' => 'Zeit, wann der Seitenaufruf gestartet wurde',
'coldesc:PageviewUpdateTime' => 'Zeit, wann der Seitenaufruf zuletzt aktualisiert wurde',
'coldesc:Prerender' => 'Gibt an, ob die Seite nur zum Pre-Rendering abgerufen wurde, aber nie auf dem Bildschirm angezeigt wurde',
'coldesc:Provider' => 'Name des Internet-Providers des Besuchers',
'coldesc:Quicktime' => 'Gibt an, ob der Browser das Quicktime-Plug-in unterstützt',
'coldesc:ReferrerHost' => 'Hostname der URL der vorherigen Seite',
'coldesc:ReferrerUrlPath' => 'Pfad der URL der vorherigen Seite',
'coldesc:ReferrerSsl' => 'Gibt an, ob die vorherige Seite SSL-verschlüsselt war',
'coldesc:Region' => 'Region, in der der Besucher lokalisiert wurde',
'coldesc:ScreenAvailHeight' => 'Bildschirmhöhe, die Anwendungen zur Verfügung steht, in Pixeln',
'coldesc:ScreenAvailWidth' => 'Bildschirmbreite, die Anwendungen zur Verfügung steht, in Pixeln',
'coldesc:ScreenColors' => 'Bildschirm-Farbtiefe, in Bits',
'coldesc:ScreenHeight' => 'Gesamte physische Bildschirmhöhe, in Pixeln',
'coldesc:ScreenWidth' => 'Gesamte physische Bildschirmbreite, in Pixeln',
'coldesc:SearchEngine' => 'Suchmaschine, die der Besucher genutzt hat',
'coldesc:SearchImageUrl' => 'URL des Bilds, das von der Bildsuchmaschine gefunden wurde',
'coldesc:SearchKeywords' => 'Suchbegriffe, die der Besucher in der Suchmaschine angegeben hat, falls verfügbar',
'coldesc:SearchResultPage' => 'Seitennummer in den Suchergebnissen, falls verfügbar',
'coldesc:SessionDuration' => 'Dauer des gesamten Besuchs, in Sekunden',
'coldesc:SessionId' => 'Interne ID des Besuchs',
'coldesc:SessionStartTime' => 'Zeit, wann der Besuch gestartet wurde',
'coldesc:SessionUpdateTime' => 'Zeit, wann der Besuch zuletzt aktualisiert wurde',
'coldesc:Silverlight' => 'Gibt an, ob der Browser das Silverlight-Plug-in unterstützt',
'coldesc:Ssl' => 'Gibt an, ob die Seite SSL-verschlüsselt aufgerufen wurde',
'coldesc:UrlHash' => 'Hash der URL der aufgerufenen Seite',
'coldesc:UrlPath' => 'Pfad der URL der aufgerufenen Seite',
'coldesc:UrlQuery' => 'Abfrageparameter der URL der aufgerufenen Seite',
'coldesc:UtcOffset' => 'Abweichung der lokalen Zeit des Besuchers zu UTC, in Minuten',
'coldesc:ViewportHeight' => 'Sichtbare Seitenhöhe im Browserfenster, in Pixeln',
'coldesc:ViewportWidth' => 'Sichtbare Seitenbreite im Browserfenster, in Pixeln',
'coldesc:VisitorId' => 'Interne ID des Besuchers',
'coldesc:WindowsMedia' => 'Gibt an, ob der Browser das Windows-Media-Plug-in unterstützt',
'colgroup:visit' => 'Besuch',
'colgroup:visit/session' => 'Besuch',
'colgroup:visit/source' => 'Quelle',
'colgroup:visit/search' => 'Suche',
'colgroup:visit/location' => 'Ort',
'colgroup:page' => 'Seite',
'colgroup:page/pageview' => 'Seitenaufruf',
'colgroup:page/address' => 'Adresse',
'colgroup:page/action' => 'Aktion',
'colgroup:client' => 'Client',
'colgroup:client/browser' => 'Browser',
'colgroup:client/config' => 'Konfiguration',
'colgroup:client/plugins' => 'Plugins',
'colgroup:client/screen' => 'Bildschirm',
'colgroup:vars' => 'Variablen',
'colgroup:vars/visitor' => 'Besucher',
'colgroup:vars/session' => 'Besuch',
'colgroup:vars/pageview' => 'Seitenaufruf',
'colgroup:advanced' => 'Weitere',
'colgroup:advanced/ids' => 'IDs',
'colgroup:advanced/data' => 'Daten',
'op:between' => 'zwischen',
'op:between-and' => 'und',
'op:startswith' => 'beginnt mit',
'op:endswith' => 'endet mit',
'op:contains' => 'enthält',
'op:inday' => 'am Tag',
'op:inmonth' => 'im Monat',
'op:inyear' => 'im Jahr',
'op:isnull' => 'ist NULL',
'contenttype:unknown' => 'unbekannter Dateityp',
'contenttype:application/adobe.flash' => 'Adobe-Flash-Anwendung',
'contenttype:application/java-class' => 'Java-Bytecode-Klasse',
'contenttype:application/ms.installer' => 'Microsoft-Installationspaket',
'contenttype:application/ms.script' => 'Microsoft-Skriptanwendung',
'contenttype:application/ms.windows' => 'Microsoft-Windows-Anwendung',
'contenttype:application/sh' => 'Shellskript-Anwendung',
'contenttype:archive/7zip' => '7-Zip-Archiv',
'contenttype:archive/android-package' => 'Android-Anwendungspaket',
'contenttype:archive/bzip' => 'BZip-Archiv',
'contenttype:archive/bzip2' => 'BZip2-Archiv',
'contenttype:archive/cpio' => 'CPIO-Archiv',
'contenttype:archive/debian-package' => 'Debian-Paket',
'contenttype:archive/gzip' => 'GZip-Archiv',
'contenttype:archive/jar' => 'Java-Archiv',
'contenttype:archive/lzma' => 'LZMA-Archiv',
'contenttype:archive/mozilla.xpinstall' => 'Mozilla-Add-on',
'contenttype:archive/ms.cab' => 'Microsoft-Cabinet-Archiv',
'contenttype:archive/rar' => 'RAR-Archiv',
'contenttype:archive/tar' => 'TAR-Archiv',
'contenttype:archive/zip' => 'ZIP-Archiv',
'contenttype:audio/aac' => 'AAC audio',
'contenttype:audio/ac3' => 'AC3 audio',
'contenttype:audio/aiff' => 'AIFF audio',
'contenttype:audio/midi' => 'MIDI audio',
'contenttype:audio/mpeg' => 'MPEG audio',
'contenttype:audio/mpeg4' => 'MPEG4 audio',
'contenttype:audio/ms.wma' => 'Microsoft Windows Media audio',
'contenttype:audio/ogg' => 'Ogg audio',
'contenttype:audio/real' => 'Real audio',
'contenttype:audio/wave' => 'WAVE audio',
'contenttype:document/config' => 'Text-Konfigurationsdatei',
'contenttype:document/csv' => 'Komma-getrennte Werte',
'contenttype:document/diff' => 'Unified-Diff-Datei',
'contenttype:document/email' => 'E-Mail-Dokument',
'contenttype:document/ms.access' => 'Microsoft-Access-Datenbank',
'contenttype:document/ms.excel' => 'Microsoft-Excel-Dokument',
'contenttype:document/ms.htmlhelp' => 'Microsoft-HTML-Hilfe',
'contenttype:document/ms.powerpoint' => 'Microsoft-PowerPoint-Präsentation',
'contenttype:document/ms.visio' => 'Microsoft-Visio-Dokument',
'contenttype:document/ms.word' => 'Microsoft-Word-Dokument',
'contenttype:document/ms.word-template' => 'Microsoft-Word-Dokumentvorlage',
'contenttype:document/ms.write' => 'Microsoft-Write-Dokument',
'contenttype:document/ms.xps' => 'Microsoft-XPS-Dokument',
'contenttype:document/oasis.chart' => 'OpenDocument-Diagramm',
'contenttype:document/oasis.chart-template' => 'OpenDocument-Diagrammvorlage',
'contenttype:document/oasis.database' => 'OpenDocument-Datenbank',
'contenttype:document/oasis.formula' => 'OpenDocument formula',
'contenttype:document/oasis.formula-template' => 'OpenDocument formula template',
'contenttype:document/oasis.graphics' => 'OpenDocument-Zeichnung',
'contenttype:document/oasis.presentation' => 'OpenDocument-Präsentation',
'contenttype:document/oasis.presentation-template' => 'OpenDocument-Präsentationsvorlage',
'contenttype:document/oasis.spreadsheet' => 'OpenDocument-Tabelle',
'contenttype:document/oasis.spreadsheet-template' => 'OpenDocument-Tabellenvorlage',
'contenttype:document/oasis.text' => 'OpenDocument-Text',
'contenttype:document/oasis.text-master' => 'OpenDocument-Textmaster',
'contenttype:document/oasis.text-template' => 'OpenDocument-Textvorlage',
'contenttype:document/pdf' => 'Adobe-PDF-Dokument',
'contenttype:document/plaintext' => 'Klartext',
'contenttype:document/postscript' => 'PostScript-Dokument',
'contenttype:document/rtf' => 'Rich-Text-Dokument',
'contenttype:document/sun-staroffice.calc' => 'Sun StarOffice Calc document',
'contenttype:document/sun-staroffice.draw' => 'Sun StarOffice Draw document',
'contenttype:document/sun-staroffice.impress' => 'Sun StarOffice Impress presentation',
'contenttype:document/sun-staroffice.writer' => 'Sun StarOffice Writer document',
'contenttype:document/tex' => 'TeX-Document',
'contenttype:document/vcalendar' => 'vCalendar-Datei',
'contenttype:document/vcard' => 'vCard-Datei',
'contenttype:document/wordperfect' => 'WordPerfect-Dokument',
'contenttype:document/x509-cert' => 'X.509-Zertifikat',
'contenttype:document/xml' => 'XML-Dokument',
'contenttype:font/ms.eot' => 'Microsoft-Webschriftart',
'contenttype:font/opentype' => 'OpenType-Schriftart',
'contenttype:font/truetype' => 'TrueType-Schriftart',
'contenttype:font/type1' => 'Type1-Schriftart',
'contenttype:generic/google.earth-kml' => 'Google Earth KML',
'contenttype:generic/google.earth-kmz' => 'Google Earth KMZ',
'contenttype:generic/pgp-encrypted' => 'PGP-verschlüsselte Datei',
'contenttype:generic/pgp-signature' => 'PGP-Signatur',
'contenttype:image/adobe.dng' => 'Adobe digitales Negativ',
'contenttype:image/adobe.photoshop' => 'Adobe-Photoshop-Bild',
'contenttype:image/bitmap' => 'Bitmap-Bild',
'contenttype:image/gif' => 'GIF-Bild',
'contenttype:image/jpeg' => 'JPEG-Bild',
'contenttype:image/jpeg2000' => 'JPEG2000-Bild',
'contenttype:image/ms.icon' => 'Microsoft-Symbol-Bild',
'contenttype:image/ms.wmf' => 'Microsoft Windows Metafile Zeichnung',
'contenttype:image/png' => 'PNG-Bild',
'contenttype:image/svg' => 'SVG-Zeichnung',
'contenttype:image/tiff' => 'TIFF-Bild',
'contenttype:image/xbitmap' => 'X bitmap image',
'contenttype:image/xpixmap' => 'X pixmap image',
'contenttype:source/asm' => 'Assembler-Quelltext',
'contenttype:source/basic' => 'BASIC-Quelltext',
'contenttype:source/c' => 'C/C++-Quelltext',
'contenttype:source/c-header' => 'C/C++-Quelltextheader',
'contenttype:source/cpp' => 'C/C++-Quelltext',
'contenttype:source/cpp-header' => 'C/C++-Quelltextheader',
'contenttype:source/csharp' => 'C#-Quelltext',
'contenttype:source/css' => 'Cascading stylesheet',
'contenttype:source/html' => 'HTML-Quelltext',
'contenttype:source/java' => 'Java-Quelltext',
'contenttype:source/javascript' => 'JavaScript-Quelltext',
'contenttype:source/ms.visual-basic' => 'Visual-Basic-Quelltext',
'contenttype:source/pascal' => 'Pascal-Quelltext',
'contenttype:source/perl' => 'Perl-Quelltext',
'contenttype:source/php' => 'PHP-Quelltext',
'contenttype:source/python' => 'Python-Quelltext',
'contenttype:source/ruby' => 'Ruby-Quelltext',
'contenttype:source/wsdl' => 'WSDL-Quelltext',
'contenttype:source/xaml' => 'XAML-Quelltext',
'contenttype:source/xhtml' => 'XHTML-Webseite',
'contenttype:source/xml-dtd' => 'XML document type definition',
'contenttype:source/xsd' => 'XML-Schema',
'contenttype:source/xsl' => 'XML-Stylesheet',
'contenttype:source/xslt' => 'XSL transformation file',
'contenttype:source/xul' => 'Mozilla-XUL-Quelltext',
'contenttype:video/3gpp' => '3GPP-Video',
'contenttype:video/apple.quicktime' => 'Apple-QuickTime-Video',
'contenttype:video/h264' => 'H.264-Video',
'contenttype:video/mjp2' => 'MJP2-Video',
'contenttype:video/mpeg' => 'MPEG-Video',
'contenttype:video/mpeg4' => 'MPEG4-Video',
'contenttype:video/ms.asf' => 'Microsoft-ASF-Video',
'contenttype:video/ms.avi' => 'Microsoft-AVI-Video',
'contenttype:video/ms.vc1' => 'Microsoft-VC-1-Video',
'contenttype:video/ms.wmv' => 'Microsoft-Windows-Media-Video',
'contenttype:video/ogg' => 'Ogg-Video',
'time:none' => 'keine Zeit',
'time:never' => 'nie',
'time:now' => 'jetzt',
'time:combine' => ' und ',
'time:pattern point past' => 'vor {time}',
'time:pattern point future' => 'in {time}',
'time:pattern span past' => 'seit {time}',
'time:pattern span future' => 'für {time}',
'time:pattern before past' => '{time} zuvor',
'time:pattern before future' => '{time} später',
'time.1:years' => '{#} Jahre',
'time.1:years#1' => 'ein Jahr',
'time.1:months' => '{#} Monate',
'time.1:months#1' => 'einen Monat',
'time.1:days' => '{#} Tage',
'time.1:days#1' => 'einen Tag',
'time.1:hours' => '{#} Stunden',
'time.1:hours#1' => 'eine Stunde',
'time.1:minutes' => '{#} Minuten',
'time.1:minutes#1' => 'eine Minute',
'time.1:seconds' => '{#} Sekunden',
'time.1:seconds#1' => 'eine Sekunde',
'time.2:years' => '{#} Jahren',
'time.2:years#1' => 'einem Jahr',
'time.2:months' => '{#} Monaten',
'time.2:months#1' => 'einem Monat',
'time.2:days' => '{#} Tagen',
'time.2:days#1' => 'einem Tag',
'time.2:hours' => '{#} Stunden',
'time.2:hours#1' => 'einer Stunde',
'time.2:minutes' => '{#} Minuten',
'time.2:minutes#1' => 'einer Minute',
'time.2:seconds' => '{#} Sekunden',
'time.2:seconds#1' => 'einer Sekunde',
'timezone:CET' => 'MEZ',
'timezone:CEST' => 'MESZ',
'month:1' => 'Jan',
'month:2' => 'Feb',
'month:3' => 'Mrz',
'month:4' => 'Apr',
'month:5' => 'Mai',
'month:6' => 'Jun',
'month:7' => 'Jul',
'month:8' => 'Aug',
'month:9' => 'Sep',
'month:10' => 'Okt',
'month:11' => 'Nov',
'month:12' => 'Dez',
'monthlong:1' => 'Januar',
'monthlong:2' => 'Februar',
'monthlong:3' => 'März',
'monthlong:4' => 'April',
'monthlong:5' => 'Mai',
'monthlong:6' => 'Juni',
'monthlong:7' => 'Juli',
'monthlong:8' => 'August',
'monthlong:9' => 'September',
'monthlong:10' => 'Oktober',
'monthlong:11' => 'November',
'monthlong:12' => 'Dezember',
'weekday:1' => 'Mo',
'weekday:2' => 'Di',
'weekday:3' => 'Mi',
'weekday:4' => 'Do',
'weekday:5' => 'Fr',
'weekday:6' => 'Sa',
'weekday:7' => 'So',
'weekdaylong:1' => 'Montag',
'weekdaylong:2' => 'Dienstag',
'weekdaylong:3' => 'Mittwoch',
'weekdaylong:4' => 'Donnerstag',
'weekdaylong:5' => 'Freitag',
'weekdaylong:6' => 'Samstag',
'weekdaylong:7' => 'Sonntag',
'browser:edge' => 'Microsoft Edge',
'browser:galeon' => 'Galeon',
'browser:gecko/camino' => 'Camino (Gecko)',
'browser:gecko/ff' => 'Firefox',
'browser:gecko/kmeleon' => 'K-Meleon (Gecko)',
'browser:gecko/nn' => 'Netscape',
'browser:gecko/seamonkey' => 'SeaMonkey (Gecko)',
'browser:icab' => 'iCab',
'browser:ie/avant' => 'Avant (IE)',
'browser:ie/ie' => 'Internet Explorer',
'browser:ie/ie/compat' => 'Internet Explorer (Kompatibilitätsmodus)',
'browser:ie/maxthon' => 'Maxthon (IE)',
'browser:ie/mobile' => 'Mobile Internet Explorer',
'browser:kindle' => 'Amazon Kindle',
'browser:opera/mini' => 'Opera Mini',
'browser:opera/mobile' => 'Opera Mobile',
'browser:opera/opera' => 'Opera',
'browser:webkit/chrome' => 'Chrome',
'browser:webkit/chrome/chromium' => 'Chromium',
'browser:webkit/chrome/frame' => 'Chrome Frame für Internet Explorer',
'browser:webkit/konq' => 'Konqueror (Webkit)',
'browser:webkit/mobile' => 'Mobile Safari',
'browser:webkit/omniweb' => 'OmniWeb (Webkit)',
'browser:webkit/safari' => 'Safari',
'os:blackberry' => 'BlackBerry',
'os:java/me' => 'Java Mobile',
'os:java/me/midp' => 'Java MIDP',
'os:mac' => 'MacOS',
'os:mac/ipad' => 'iPad OS',
'os:mac/iphone' => 'iPhone OS',
'os:mac/x' => 'MacOS X',
'os:os2' => 'OS/2',
'os:palm' => 'PalmOS',
'os:symbian' => 'Symbian',
'os:unix' => 'Unix',
'os:unix/freebsd' => 'FreeBSD',
'os:unix/linux' => 'Linux',
'os:unix/linux/android' => 'Android',
'os:unix/sun' => 'Solaris',
'os:webos' => 'webOS',
'os:win' => 'Windows',
'os:win/ce' => 'Windows CE',
'os:win/ce/mobile' => 'Windows Mobile',
'os:win/ce/phone' => 'Windows Phone',
'os:win/me' => 'Windows ME',
'os:win/nt' => 'Windows NT',
'os:win/nt/arm' => 'Windows NT auf ARM',
'os:win/nt/2000' => 'Windows 2000',
'os:win/nt/2003' => 'Windows Server 2003',
'os:win/nt/7' => 'Windows 7',
'os:win/nt/8' => 'Windows 8',
'os:win/nt/8/arm' => 'Windows 8 RT',
'os:win/nt/8.1' => 'Windows 8.1',
'os:win/nt/8.1/arm' => 'Windows 8.1 RT',
'os:win/nt/10' => 'Windows 10',
'os:win/nt/vista' => 'Windows Vista',
'os:win/nt/xp' => 'Windows XP',
'os:win/phone' => 'Windows Phone',
'os:win/phone/7' => 'Windows Phone 7',
'os:win/phone/7.5' => 'Windows Phone 7.5',
'os:win/phone/8.0' => 'Windows Phone 8',
'country:AD' => 'Andorra',
'country:AE' => 'Vereinigte Arabische Emirate',
'country:AF' => 'Afghanistan',
'country:AG' => 'Antigua und Barbuda',
'country:AI' => 'Anguilla',
'country:AL' => 'Albanien',
'country:AM' => 'Armenien',
'country:AO' => 'Angola',
'country:AQ' => 'Antarktis',
'country:AR' => 'Argentinien',
'country:AS' => 'Amerikanisch-Samoa',
'country:AT' => 'Österreich',
'country:AU' => 'Australien',
'country:AW' => 'Aruba',
'country:AX' => 'Åland',
'country:AZ' => 'Aserbaidschan',
'country:BA' => 'Bosnien und Herzegowina',
'country:BB' => 'Barbados',
'country:BD' => 'Bangladesch',
'country:BE' => 'Belgien',
'country:BF' => 'Burkina Faso',
'country:BG' => 'Bulgarien',
'country:BH' => 'Bahrain',
'country:BI' => 'Burundi',
'country:BJ' => 'Benin',
'country:BL' => 'Saint-Barthélemy',
'country:BM' => 'Bermuda',
'country:BN' => 'Brunei',
'country:BO' => 'Bolivien',
'country:BQ' => 'Bonaire, Sint Eustatius und Saba',
'country:BR' => 'Brasilien',
'country:BS' => 'Bahamas',
'country:BT' => 'Bhutan',
'country:BV' => 'Bouvetinsel',
'country:BW' => 'Botswana',
'country:BY' => 'Belarus (Weißrussland)',
'country:BZ' => 'Belize',
'country:CA' => 'Kanada',
'country:CC' => 'Kokosinseln',
'country:CD' => 'Kongo',
'country:CF' => 'Zentralafrikanische Republik',
'country:CG' => 'Republik Kongo',
'country:CH' => 'Schweiz',
'country:CI' => 'Côte d’Ivoire (Elfenbeinküste)',
'country:CK' => 'Cookinseln',
'country:CL' => 'Chile',
'country:CM' => 'Kamerun',
'country:CN' => 'China',
'country:CO' => 'Kolumbien',
'country:CR' => 'Costa Rica',
'country:CU' => 'Kuba',
'country:CV' => 'Kap Verde',
'country:CW' => 'Curaçao',
'country:CX' => 'Weihnachtsinsel',
'country:CY' => 'Zypern',
'country:CZ' => 'Tschechische Republik',
'country:DE' => 'Deutschland',
'country:DJ' => 'Dschibuti',
'country:DK' => 'Dänemark',
'country:DM' => 'Dominica',
'country:DO' => 'Dominikanische Republik',
'country:DZ' => 'Algerien',
'country:EC' => 'Ecuador',
'country:EE' => 'Estland',
'country:EG' => 'Ägypten',
'country:EH' => 'Westsahara',
'country:ER' => 'Eritrea',
'country:ES' => 'Spanien',
'country:ET' => 'Äthiopien',
'country:FI' => 'Finnland',
'country:FJ' => 'Fidschi',
'country:FK' => 'Falklandinseln',
'country:FM' => 'Mikronesien',
'country:FO' => 'Färöer',
'country:FR' => 'Frankreich',
'country:GA' => 'Gabun',
'country:GB' => 'Vereinigtes Königreich',
'country:GD' => 'Grenada',
'country:GE' => 'Georgien',
'country:GF' => 'Französisch-Guayana',
'country:GG' => 'Guernsey',
'country:GH' => 'Ghana',
'country:GI' => 'Gibraltar',
'country:GL' => 'Grönland',
'country:GM' => 'Gambia',
'country:GN' => 'Guinea',
'country:GP' => 'Guadeloupe',
'country:GQ' => 'Äquatorialguinea',
'country:GR' => 'Griechenland',
'country:GS' => 'Südgeorgien und die Südlichen Sandwichinseln',
'country:GT' => 'Guatemala',
'country:GU' => 'Guam',
'country:GW' => 'Guinea-Bissau',
'country:GY' => 'Guyana',
'country:HK' => 'Hongkong',
'country:HM' => 'Heard und McDonaldinseln',
'country:HN' => 'Honduras',
'country:HR' => 'Kroatien',
'country:HT' => 'Haiti',
'country:HU' => 'Ungarn',
'country:ID' => 'Indonesien',
'country:IE' => 'Irland',
'country:IL' => 'Israel',
'country:IM' => 'Insel Man',
'country:IN' => 'Indien',
'country:IO' => 'Britisches Territorium im Indischen Ozean',
'country:IQ' => 'Irak',
'country:IR' => 'Iran',
'country:IS' => 'Island',
'country:IT' => 'Italien',
'country:JE' => 'Jersey',
'country:JM' => 'Jamaika',
'country:JO' => 'Jordanien',
'country:JP' => 'Japan',
'country:KE' => 'Kenia',
'country:KG' => 'Kirgisistan',
'country:KH' => 'Kambodscha',
'country:KI' => 'Kiribati',
'country:KM' => 'Komoren',
'country:KN' => 'St. Kitts und Nevis',
'country:KP' => 'Nordkorea',
'country:KR' => 'Südkorea',
'country:KW' => 'Kuwait',
'country:KY' => 'Kaimaninseln',
'country:KZ' => 'Kasachstan',
'country:LA' => 'Laos',
'country:LB' => 'Libanon',
'country:LC' => 'St. Lucia',
'country:LI' => 'Liechtenstein',
'country:LK' => 'Sri Lanka',
'country:LR' => 'Liberia',
'country:LS' => 'Lesotho',
'country:LT' => 'Litauen',
'country:LU' => 'Luxemburg',
'country:LV' => 'Lettland',
'country:LY' => 'Libyen',
'country:MA' => 'Marokko',
'country:MC' => 'Monaco',
'country:MD' => 'Moldawien',
'country:ME' => 'Montenegro',
'country:MF' => 'Saint-Martin (franz. Teil)',
'country:MG' => 'Madagaskar',
'country:MH' => 'Marshallinseln',
'country:MK' => 'Mazedonien',
'country:ML' => 'Mali',
'country:MM' => 'Myanmar (Burma)',
'country:MN' => 'Mongolei',
'country:MO' => 'Macao',
'country:MP' => 'Nördliche Marianen',
'country:MQ' => 'Martinique',
'country:MR' => 'Mauretanien',
'country:MS' => 'Montserrat',
'country:MT' => 'Malta',
'country:MU' => 'Mauritius',
'country:MV' => 'Malediven',
'country:MW' => 'Malawi',
'country:MX' => 'Mexiko',
'country:MY' => 'Malaysia',
'country:MZ' => 'Mosambik',
'country:NA' => 'Namibia',
'country:NC' => 'Neukaledonien',
'country:NE' => 'Niger',
'country:NF' => 'Norfolkinsel',
'country:NG' => 'Nigeria',
'country:NI' => 'Nicaragua',
'country:NL' => 'Niederlande',
'country:NO' => 'Norwegen',
'country:NP' => 'Nepal',
'country:NR' => 'Nauru',
'country:NU' => 'Niue',
'country:NZ' => 'Neuseeland',
'country:OM' => 'Oman',
'country:PA' => 'Panama',
'country:PE' => 'Peru',
'country:PF' => 'Französisch-Polynesien',
'country:PG' => 'Papua-Neuguinea',
'country:PH' => 'Philippinen',
'country:PK' => 'Pakistan',
'country:PL' => 'Polen',
'country:PM' => 'Saint-Pierre und Miquelon',
'country:PN' => 'Pitcairninseln',
'country:PR' => 'Puerto Rico',
'country:PS' => 'Palästinensische Autonomiegebiete',
'country:PT' => 'Portugal',
'country:PW' => 'Palau',
'country:PY' => 'Paraguay',
'country:QA' => 'Katar',
'country:RE' => 'Réunion',
'country:RO' => 'Rumänien',
'country:RS' => 'Serbien',
'country:RU' => 'Russland',
'country:RW' => 'Ruanda',
'country:SA' => 'Saudi-Arabien',
'country:SB' => 'Salomonen',
'country:SC' => 'Seychellen',
'country:SD' => 'Sudan',
'country:SE' => 'Schweden',
'country:SG' => 'Singapur',
'country:SH' => 'St. Helena',
'country:SI' => 'Slowenien',
'country:SJ' => 'Svalbard und Jan Mayen',
'country:SK' => 'Slowakei',
'country:SL' => 'Sierra Leone',
'country:SM' => 'San Marino',
'country:SN' => 'Senegal',
'country:SO' => 'Somalia',
'country:SR' => 'Suriname',
'country:ST' => 'São Tomé und Príncipe',
'country:SV' => 'El Salvador',
'country:SX' => 'Sint Maarten (niederl. Teil)',
'country:SY' => 'Syrien',
'country:SZ' => 'Swasiland',
'country:TC' => 'Turks- und Caicosinseln',
'country:TD' => 'Tschad',
'country:TF' => 'Französische Süd- und Antarktisgebiete',
'country:TG' => 'Togo',
'country:TH' => 'Thailand',
'country:TJ' => 'Tadschikistan',
'country:TK' => 'Tokelau',
'country:TL' => 'Osttimor (Timor-Leste)',
'country:TM' => 'Turkmenistan',
'country:TN' => 'Tunesien',
'country:TO' => 'Tonga',
'country:TR' => 'Türkei',
'country:TT' => 'Trinidad und Tobago',
'country:TV' => 'Tuvalu',
'country:TW' => 'Taiwan',
'country:TZ' => 'Tansania',
'country:UA' => 'Ukraine',
'country:UG' => 'Uganda',
'country:UM' => 'United States Minor Outlying Islands',
'country:US' => 'Vereinigte Staaten von Amerika',
'country:UY' => 'Uruguay',
'country:UZ' => 'Usbekistan',
'country:VA' => 'Vatikanstadt',
'country:VC' => 'St. Vincent und die Grenadinen',
'country:VE' => 'Venezuela',
'country:VG' => 'Britische Jungferninseln',
'country:VI' => 'Amerikanische Jungferninseln',
'country:VN' => 'Vietnam',
'country:VU' => 'Vanuatu',
'country:WF' => 'Wallis und Futuna',
'country:WS' => 'Samoa',
'country:YE' => 'Jemen',
'country:YT' => 'Mayotte',
'country:ZA' => 'Südafrika',
'country:ZM' => 'Sambia',
'country:ZW' => 'Simbabwe',
'region:AT:Karnten' => 'Kärnten',
'region:AT:Niederosterreich' => 'Niederösterreich',
'region:AT:Oberosterreich' => 'Oberösterreich',
'region:AT:#nofallback' => true,
'region:CH:Ausser-Rhoden' => 'Appenzell Ausserrhoden',
'region:CH:Fribourg' => 'Freiburg',
'region:CH:Geneve' => 'Genf',
'region:CH:Graubunden' => 'Graubünden',
'region:CH:Inner-Rhoden' => 'Appenzell Innerrhoden',
'region:CH:Neuchatel' => 'Neuenburg',
'region:CH:Ticino' => 'Tessin',
'region:CH:Valais' => 'Wallis',
'region:CH:Vaud' => 'Waadt',
'region:CH:Zurich' => 'Zürich',
'region:CH:#nofallback' => true,
'region:DE:Baden-Wurttemberg' => 'Baden-Württemberg',
'region:DE:Thuringen' => 'Thüringen',
'region:DE:#nofallback' => true,
'city:AT:Vienna' => 'Wien',
'city:AT:#nofallback' => true,
'city:DE:Frankfurt Am Main' => 'Frankfurt am Main',
'city:DE:Munich' => 'München',
'city:DE:Nuremberg' => 'Nürnberg',
'city:DE:#nofallback' => true,
'language:ar' => 'arabisch',
'language:be' => 'weißrussisch',
'language:bg' => 'bulgarisch',
'language:bs' => 'bosnisch',
'language:ca' => 'katalanisch',
'language:co' => 'korsisch',
'language:cs' => 'tschechisch',
'language:cy' => 'walisisch',
'language:da' => 'dänisch',
'language:de' => 'deutsch',
'language:el' => 'griechisch',
'language:en' => 'englisch',
'language:eo' => 'esperanto',
'language:es' => 'spanisch',
'language:et' => 'estländisch',
'language:fa' => 'persisch',
'language:fi' => 'finnisch',
'language:fr' => 'französisch',
'language:ga' => 'irisch',
'language:he' => 'hebräisch',
'language:hi' => 'hindi',
'language:hr' => 'kroatisch',
'language:hu' => 'ungarisch',
'language:hy' => 'armenisch',
'language:id' => 'indonesisch',
'language:is' => 'isländisch',
'language:it' => 'italienisch',
'language:ja' => 'japanisch',
'language:ka' => 'georgisch',
'language:kk' => 'kasachisch',
'language:ko' => 'koreanisch',
'language:ku' => 'kurdisch',
'language:la' => 'latein',
'language:lb' => 'luxemburgisch',
'language:lt' => 'litauisch',
'language:lv' => 'lettisch',
'language:mk' => 'makedonisch',
'language:mn' => 'mongolisch',
'language:mt' => 'maltesisch',
'language:my' => 'burmesisch',
'language:nb' => 'norwegisch (bokmål)',
'language:nl' => 'niederländisch',
'language:nn' => 'norwegisch (nynorsk)',
'language:no' => 'norwegisch',
'language:pl' => 'polnisch',
'language:pt' => 'portugiesisch',
'language:ro' => 'rumänisch',
'language:ru' => 'russisch',
'language:sk' => 'slowakisch',
'language:sl' => 'slowenisch',
'language:sq' => 'albanisch',
'language:sr' => 'serbisch',
'language:sv' => 'schwedisch',
'language:th' => 'thai',
'language:tr' => 'türkisch',
'language:uk' => 'ukrainisch',
'language:vi' => 'vietnamesisch',
'language:zh' => 'chinesisch',
);
protected function FormatNumber($number, $decimals, $html, $trim)
{
$str = number_format($number, $decimals, ',', ' ');
if ($trim && strpos($str, ',') !== false)
{
$str = rtrim($str, '0');
$str = rtrim($str, ',');
}
if ($html)
{
if ($number >= 10000)
{
$str = str_replace(' ', "\xE2\x80\xAF" /* UTF-8: U+202F Narrow No-Break Space */, $str);
}
else
{
$str = str_replace(' ', '', $str);
}
}
return $str;
}
protected function DoParseNumber($str, $recoveryValue)
{
$str = str_replace('.', '', $str);
$str = str_replace(' ', '', $str);
$str = str_replace(',', '.', $str);
if (!is_numeric($str))
{
if (isset($recoveryValue))
return $recoveryValue;
throw new InvalidArgumentException('Invalid numeric value format.');
}
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
protected function FormatOrdinalNumber($number, $html)
{
return $number . '.';
}
protected function FormatTime($datetime, $details, $html)
{
$sp = "\xC2\xA0";
$lsp = "\xE2\x80\x87";
$str = '';
if ($details & UnbTimeFormat::Weekday)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekday:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::Weekday ? ',' : '');
}
else if ($details & UnbTimeFormat::WeekdayLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekdaylong:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::WeekdayLong ? ',' : '');
}
if ($details & UnbTimeFormat::Date)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('d.m.Y');
$haveDate = true;
}
else if ($details & UnbTimeFormat::DateLong)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('j.') . $sp .
UnbLocale::Translate('monthlong:' . $datetime->format('n')) . $sp .
$datetime->format('Y');
$haveDate = true;
}
if ($details & UnbTimeFormat::TimeSec)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H:i:s');
}
else if ($details & UnbTimeFormat::Time)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('H:i');
}
if ($details & UnbTimeFormat::TimeZone)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('timezone:' . $datetime->format('T'), true);
}
return $str;
}
protected function FormatRelativeTime($diff, $context, $html)
{
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$grammar = 2;
else
$grammar = 2;
break;
case UnbTimeContext::Span:
if ($diff->invert)
$grammar = 2;
else
$grammar = 1;
break;
case UnbTimeContext::Before:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
}
$parts = array();
if ($diff->y && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':years', $diff->y));
if ($diff->m && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':months', $diff->m));
if ($diff->d && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':days', $diff->d));
if ($diff->h && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':hours', $diff->h));
if ($diff->i && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':minutes', $diff->i));
if ($diff->s && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':seconds', $diff->s));
$str = '';
foreach ($parts as $part)
{
$str .= (strlen($str) ? UnbLocale::Translate('time:combine') : '') .
$part;
}
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$str = self::Translate('time:pattern point past', 'time', $str);
else
$str = self::Translate('time:pattern point future', 'time', $str);
break;
case UnbTimeContext::Span:
if ($diff->invert)
$str = self::Translate('time:pattern span past', 'time', $str);
else
$str = self::Translate('time:pattern span future', 'time', $str);
break;
case UnbTimeContext::Before:
if ($diff->invert)
$str = self::Translate('time:pattern before past', 'time', $str);
else
$str = self::Translate('time:pattern before future', 'time', $str);
break;
}
return $str;
}
}
UnbLocale::Register(new UnbLocale_de_DE());
class UnbLocale_en_GB extends UnbLocale
{
protected $code = 'en-GB';
protected $nativeName = 'English (United Kingdom)';
protected $importance = 60000;
protected $dependsOn = 'en-US';
protected $quoteStart = '“';
protected $quoteEnd = '”';
protected $colon = ':';
protected $dict = array(
'loading canceled' => 'loading cancelled',
'column:ScreenColors' => 'Colour depth',
'coldesc:ScreenColors' => 'Screen colour depth, in bits',
);
protected function FormatNumber($number, $decimals, $html, $trim)
{
$str = number_format($number, $decimals, '.', ',');
if ($trim && strpos($str, '.') !== false)
{
$str = rtrim($str, '0');
$str = rtrim($str, '.');
}
return $str;
}
protected function DoParseNumber($str, $recoveryValue)
{
$str = str_replace(',', '', $str);
$str = str_replace(' ', '', $str);
if (!is_numeric($str))
{
if (isset($recoveryValue))
return $recoveryValue;
throw new InvalidArgumentException('Invalid numeric value format.');
}
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
protected function FormatOrdinalNumber($number, $html)
{
if ($number <= 0) return $num;
if ($number % 10 == 1) return $number . ($html ? '<sup>' : '') . 'st' . ($html ? '</sup>' : '');
if ($number % 10 == 2) return $number . ($html ? '<sup>' : '') . 'nd' . ($html ? '</sup>' : '');
if ($number % 10 == 3) return $number . ($html ? '<sup>' : '') . 'rd' . ($html ? '</sup>' : '');
return $number . ($html ? '<sup>' : '') . 'th' . ($html ? '</sup>' : '');
}
protected function FormatTime($datetime, $details, $html)
{
$sp = "\xC2\xA0";
$lsp = "\xE2\x80\x87";
$str = '';
if ($details & UnbTimeFormat::Weekday)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekday:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::Weekday ? ',' : '');
}
else if ($details & UnbTimeFormat::WeekdayLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekdaylong:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::WeekdayLong ? ',' : '');
}
if ($details & UnbTimeFormat::Date)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('d/m/Y');
$haveDate = true;
}
else if ($details & UnbTimeFormat::DateLong)
{
$str .= (strlen($str) ? $sp : '') .
$this->FormatOrdinalNumber($datetime->format('j'), $html) . $sp .
UnbLocale::Translate('monthlong:' . $datetime->format('n')) . $sp .
$datetime->format('Y');
$haveDate = true;
}
if ($details & UnbTimeFormat::TimeSec)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('g:i:s a');
}
else if ($details & UnbTimeFormat::Time)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('g:i a');
}
if ($details & UnbTimeFormat::TimeZone)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('T');
}
return $str;
}
protected function FormatRelativeTime($diff, $context, $html)
{
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
case UnbTimeContext::Span:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
case UnbTimeContext::Before:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
}
$parts = array();
if ($diff->y && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':years', $diff->y));
if ($diff->m && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':months', $diff->m));
if ($diff->d && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':days', $diff->d));
if ($diff->h && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':hours', $diff->h));
if ($diff->i && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':minutes', $diff->i));
if ($diff->s && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':seconds', $diff->s));
$str = '';
foreach ($parts as $part)
{
$str .= (strlen($str) ? UnbLocale::Translate('time:combine') : '') .
$part;
}
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$str = self::Translate('time:pattern point past', 'time', $str);
else
$str = self::Translate('time:pattern point future', 'time', $str);
break;
case UnbTimeContext::Span:
if ($diff->invert)
$str = self::Translate('time:pattern span past', 'time', $str);
else
$str = self::Translate('time:pattern span future', 'time', $str);
break;
case UnbTimeContext::Before:
if ($diff->invert)
$str = self::Translate('time:pattern before past', 'time', $str);
else
$str = self::Translate('time:pattern before future', 'time', $str);
break;
}
return $str;
}
}
UnbLocale::Register(new UnbLocale_en_GB());
class UnbLocale_en_US extends UnbLocale
{
protected $code = 'en-US';
protected $nativeName = 'English (US)';
protected $importance = 251000;
protected $quoteStart = '“';
protected $quoteEnd = '”';
protected $colon = ':';
protected $dict = array(
'exception:BadMethodCallException' => 'Bad method call',
'exception:Exception' => 'Error',
'exception:InvalidArgumentException' => 'Invalid argument',
'exception:LengthException' => 'Length error',
'error:password security.too short' => 'At least 6 characters required.',
'error:password security.equals username' => 'Password must not be equal to the username.',
'error:password security.contains username' => 'Password must not contain the username.',
'error:password security.non ascii' => 'Only ASCII characters are allowed.',
'error:password security.character classes' => 'At least 2 character classes out of numbers, lowercase, uppercase characters and other symbols required.',
'error:password security.repeated character' => 'No character may be repeated 3 times or more.',
'error:password security.simple sequences' => 'Simple sequences like 123 or abc are not allowed.',
'error:password security.different characters' => 'At least 4 different characters must be used.',
'empty' => 'empty',
'unit:bytes' => '{#} bytes',
'unit:bytes#1' => 'one byte',
'format:decimal' => '.',
'active' => 'active',
'add field' => 'add field',
'admin opt-out cookie' => 'admin opt-out cookie',
'admin opt-out cookie.do set' => 'set opt-out cookie',
'admin opt-out cookie.do unset' => 'delete opt-out cookie',
'admin opt-out cookie.set' => 'The admin opt-out cookie is set and valid. You can move freely on your website without being tracked.',
'admin opt-out cookie.unset' => 'The admin opt-out cookie is not currently set or invalid. All of your own actions on this website will be tracked regularly.',
'app info' => 'App info',
'available screen' => 'available',
'bounce' => 'bounce',
'campaign' => 'campaign',
'client' => 'client',
'column' => 'column',
'current language' => 'current language',
'custom pageview variable' => 'Custom variable for the pageview',
'custom pageview variable created' => 'Pageview variable {name} created in the database.',
'custom pageview variable create error' => 'Error adding pageview variable {name} into the database.',
'custom session variable' => 'Custom variable for the session',
'custom session variable created' => 'Session variable {name} created in the database.',
'custom session variable create error' => 'Error adding session variable {name} into the database.',
'custom variables' => 'Custom variables',
'custom visitor variable' => 'Custom variable for the visitor',
'custom visitor variable created' => 'Visitor variable {name} created in the database.',
'custom visitor variable create error' => 'Error adding visitor variable {name} into the database.',
'customvar.scope' => 'scope',
'customvar.name' => 'name',
'customvar.type' => 'type',
'customvar.notes' => 'notes',
'customvar.create in database' => 'create in the database',
'customvar.in database not configured' => 'in the database but not configured',
'customvar.invalid name' => 'invalid variable name!',
'customvar.none' => 'No variables configured or in the database.',
'customvar.ok' => 'OK',
'data files status' => 'Data files status',
'direct entry' => 'direct entry',
'direct entry or bookmark' => 'direct entry or bookmark',
'disabled' => 'disabled',
'download' => 'download',
'enter access password' => 'Enter the access password',
'entry page' => 'entry page',
'exit page' => 'exit page',
'field' => 'field',
'file size' => 'file size',
'filter' => 'filter',
'first entry' => 'first entry',
'geoip.country-level database' => 'country-level database',
'geoip.city-level database' => 'city-level database',
'image zoom' => 'image zoom',
'info and management' => 'Info and management',
'limit visitor log.1' => 'Limit visitor log to',
'limit visitor log.2' => 'records',
'loading canceled' => 'loading canceled',
'local' => 'local',
'location' => 'location',
'location/duration' => 'location/duration',
'login' => 'log in',
'login.temporary' => 'temporary',
'login.temporary~' => 'Stores the login only for this session instead of a longer time.',
'logout' => 'log out',
'mobile device' => 'mobile device',
'navigated back' => 'navigated back',
'navigated forward' => 'navigated forward',
'no filter set' => 'no filter set',
'no rdns' => 'no RDNS',
'no search terms available' => 'no search terms available',
'not' => 'not',
'not present' => 'not present',
'operation' => 'operation',
'options' => 'options',
'outlink' => 'outgoing link',
'overview link' => 'Back to overview page',
'page' => 'page',
'page load time' => 'page load time',
'pageview' => 'pageview',
'pageviews' => 'pageviews',
'public suffix list' => 'public suffix list',
'query caption' => 'tòmò query',
'query returned no data' => 'This query does not return any data.',
'query view' => 'query view',
'query view intro' => 'Select a query from the list at the top right to view the report.',
'referring website' => 'referring website',
'reloaded page' => 'reloaded page',
'remove column' => 'remove column',
'returning visitor' => 'returning visitor',
'reverse visitor log order' => 'Reverse order to show newer pageviews first',
'revisited page' => 'revisited page',
'screen size' => 'screen size',
'search engine' => 'search engine',
'search image' => 'image found',
'section expand' => 'expanded section',
'select query' => 'select query',
'session' => 'session',
'session continuation' => 'continuation of a previous visit',
'session hit count' => '{#} views',
'session hit count#1' => 'one view',
'session page count' => 'of {#} pages',
'session page count#1' => 'of one page',
'session time over' => 'over',
'sessions' => 'sessions',
'set' => 'set',
'site search' => 'site search',
'source/action' => 'source/action',
'summary' => 'summary',
'summary.unique visitors' => 'unique visitors',
'summary.first' => 'first',
'summary.last' => 'last',
'texts' => 'texts',
'time' => 'time',
'time since page load' => 'time since loading the page',
'tomo database' => 'tòmò database',
'tomo viewer title' => 'tòmò viewer',
'tomo viewer intro' => 'Choose a view:',
'unknown' => 'unknown',
'update' => 'update',
'updated on date' => 'updated on {date}',
'value' => 'value',
'version' => 'version',
'view button' => 'view',
'viewport size' => 'viewport size',
'visitors' => 'visitors',
'visitor log' => 'visitor log',
'visitor log caption' => 'tòmò visitor log',
'warning.access password unset' => 'The access password is unset. An attacker could log into this application with no password. You should set this password as soon as possible in the configuration file.',
'warning.access password default' => 'The access password is still set to the default value. An attacker could easily guess it and log into this application. You should change this password as soon as possible in the configuration file.',
'warning.opt-out password unset' => 'The opt-out password is unset. Any visitor of your website could create their own admin opt-out cookie to escape tracking. You should set this password in the configuration file.',
'warning.opt-out password default' => 'The opt-out password is still set to the default value. Any visitor of your website could easily guess it and create their own admin opt-out cookie to escape tracking. You should change this password in the configuration file.',
'column:ActionData' => 'Action data',
'column:ActionId' => 'Action ID',
'column:ActionTime' => 'Action time',
'column:ActionType' => 'Action type',
'column:ActionUrl' => 'Action URL',
'column:ActivityTimespan' => 'Activity timespan',
'column:AdBlock' => 'Ad blocker',
'column:Browser' => 'Browser',
'column:Campaign' => 'Campaign',
'column:CampaignRedirect' => 'Campaign redirection',
'column:City' => 'City',
'column:Cookies' => 'Cookies',
'column:CountryCode' => 'Country code',
'column:DNT' => 'DNT',
'column:Domain' => 'Domain',
'column:EntryPage' => 'Entry page',
'column:ExitPage' => 'Exit page',
'column:Flash' => 'Flash',
'column:IPAddress' => 'IP address',
'column:IPVersion' => 'IP version',
'column:Java' => 'Java',
'column:JavaScript' => 'JavaScript',
'column:JavaScriptData' => 'JavaScript data',
'column:Language' => 'Language',
'column:Lat' => 'Latitude',
'column:Lng' => 'Longitude',
'column:Mobile' => 'Mobile device',
'column:NewVisitor' => 'New visitor',
'column:OperatingSystem' => 'Operating system',
'column:OriginalPageviewId' => 'Original pageview ID',
'column:PageLoadTime' => 'Page load time',
'column:PageTitle' => 'Page title',
'column:PageLanguage' => 'Page language',
'column:PageviewId' => 'Pageview ID',
'column:PageviewTime' => 'Pageview time',
'column:PageviewUpdateTime' => 'Pageview update time',
'column:Prerender' => 'Prerender',
'column:Provider' => 'Provider',
'column:Quicktime' => 'Quicktime',
'column:ReferrerHost' => 'Referrer host',
'column:ReferrerUrlPath' => 'Referrer URL path',
'column:ReferrerSsl' => 'Referrer SSL',
'column:Region' => 'Region',
'column:ScreenAvailHeight' => 'Desktop height',
'column:ScreenAvailWidth' => 'Desktop width',
'column:ScreenColors' => 'Color depth',
'column:ScreenHeight' => 'Screen height',
'column:ScreenWidth' => 'Screen width',
'column:SearchEngine' => 'Search engine',
'column:SearchImageUrl' => 'Search image URL',
'column:SearchKeywords' => 'Search keywords',
'column:SearchResultPage' => 'Search result page',
'column:SessionDuration' => 'Session duration',
'column:SessionId' => 'Session ID',
'column:SessionStartTime' => 'Session start time',
'column:SessionUpdateTime' => 'Session update time',
'column:Silverlight' => 'Silverlight',
'column:Ssl' => 'SSL',
'column:UrlHash' => 'URL hash',
'column:UrlPath' => 'URL path',
'column:UrlQuery' => 'URL query parameters',
'column:UtcOffset' => 'UTC offset',
'column:ViewportHeight' => 'Page height',
'column:ViewportWidth' => 'Page width',
'column:VisitorId' => 'Visitor ID',
'column:WindowsMedia' => 'Windows Media',
'coldesc:ActionData' => 'Additional data of the action that was performed',
'coldesc:ActionId' => 'Internal ID of the action that was performed',
'coldesc:ActionTime' => 'Time when the action was performed',
'coldesc:ActionType' => 'Internal type code of the action that was performed',
'coldesc:ActionUrl' => 'URL of the action that was performed',
'coldesc:ActivityTimespan' => 'Duration of user activity on the page, in seconds',
'coldesc:AdBlock' => 'Indicates whether the visitor has installed an ad blocker',
'coldesc:Browser' => 'Browser name and version',
'coldesc:Campaign' => 'Campaign name',
'coldesc:CampaignRedirect' => 'Indicates whether this pageview was redirecting away from a campaign URL',
'coldesc:City' => 'City where the visitor has been located',
'coldesc:Cookies' => 'Indicates whether the browser supports cookies',
'coldesc:CountryCode' => 'Two-letter code of the country where the visitor has been located',
'coldesc:DNT' => 'Indicates whether the browser has set the Do-Not-Track header',
'coldesc:Domain' => 'Domain of the viewed page',
'coldesc:EntryPage' => 'Indicates whether the pageview was the first page of the session',
'coldesc:ExitPage' => 'Indicates whether the pageview was the last page of the session',
'coldesc:Flash' => 'Indicates whether the browser supports the Flash plug-in',
'coldesc:IPAddress' => 'IP address of the visitor, or a hash value of it',
'coldesc:IPVersion' => 'IP version of the visitor (4 or 6)',
'coldesc:Java' => 'Indicates whether the browser supports the Java plug-in',
'coldesc:JavaScript' => 'Indicates whether the browser supports JavaScript',
'coldesc:JavaScriptData' => 'Indicates whether the browser data was collected with JavaScript',
'coldesc:Language' => 'Ordered language codes preferred by the browser, comma-separated',
'coldesc:Lat' => 'Latitude of the approximate geolocation',
'coldesc:Lng' => 'Longitude of the approximate geolocation',
'coldesc:Mobile' => 'Indicates whether the client is a handheld mobile device',
'coldesc:NewVisitor' => 'Indicates whether the visitor is new to this site',
'coldesc:OperatingSystem' => 'Operating system name and version',
'coldesc:OriginalPageviewId' => 'Internal ID of the original pageview',
'coldesc:PageLoadTime' => 'Time it took for the page to load, in milliseconds',
'coldesc:PageTitle' => 'Title of the page being viewed',
'coldesc:PageLanguage' => 'Language in which the page was displayed',
'coldesc:PageviewId' => 'Internal ID of the pageview',
'coldesc:PageviewTime' => 'Time when the pageview was started',
'coldesc:PageviewUpdateTime' => 'Time when the pageview was last updated',
'coldesc:Prerender' => 'Indicates whether the page was only retrieved for pre-rendering, but was never shown on screen',
'coldesc:Provider' => 'Name of the visitor’s internet provider',
'coldesc:Quicktime' => 'Indicates whether the browser supports the Quicktime plug-in',
'coldesc:ReferrerHost' => 'URL host of the previous page',
'coldesc:ReferrerUrlPath' => 'URL path of the previous page',
'coldesc:ReferrerSsl' => 'Indicates whether the previous page was SSL-encrypted',
'coldesc:Region' => 'Region where the visitor has been located',
'coldesc:ScreenAvailHeight' => 'Screen height available to applications, in pixels',
'coldesc:ScreenAvailWidth' => 'Screen width available to applications, in pixels',
'coldesc:ScreenColors' => 'Screen color depth, in bits',
'coldesc:ScreenHeight' => 'Total physical screen height, in pixels',
'coldesc:ScreenWidth' => 'Total physical screen width, in pixels',
'coldesc:SearchEngine' => 'Search engine that brought the visitor here',
'coldesc:SearchImageUrl' => 'URL of the image found by the image search engine',
'coldesc:SearchKeywords' => 'Search keywords that the visitor has entered in the search engine, if available',
'coldesc:SearchResultPage' => 'Search result page number, if available',
'coldesc:SessionDuration' => 'Duration of the entire visit, in seconds',
'coldesc:SessionId' => 'Internal ID of the visit',
'coldesc:SessionStartTime' => 'Time when the visit was started',
'coldesc:SessionUpdateTime' => 'Time when the visit was last updated',
'coldesc:Silverlight' => 'Indicates whether the browser supports the Silverlight plug-in',
'coldesc:Ssl' => 'Indicates whether the page was requested SSL-encrypted',
'coldesc:UrlHash' => 'URL hash of the viewed page',
'coldesc:UrlPath' => 'URL path of the viewed page',
'coldesc:UrlQuery' => 'URL query parameters of the viewed page',
'coldesc:UtcOffset' => 'Offset of the visitor’s local time to UTC, in minutes',
'coldesc:ViewportHeight' => 'Visible content height in the browser window, in pixels',
'coldesc:ViewportWidth' => 'Visible content width in the browser window, in pixels',
'coldesc:VisitorId' => 'Internal ID of the visitor',
'coldesc:WindowsMedia' => 'Indicates whether the browser supports the Windows Media plug-in',
'colgroup:visit' => 'Visit',
'colgroup:visit/session' => 'Session',
'colgroup:visit/source' => 'Source',
'colgroup:visit/search' => 'Search',
'colgroup:visit/location' => 'Location',
'colgroup:page' => 'Page',
'colgroup:page/pageview' => 'Pageview',
'colgroup:page/address' => 'Address',
'colgroup:page/action' => 'Action',
'colgroup:client' => 'Client',
'colgroup:client/browser' => 'Browser',
'colgroup:client/config' => 'Configuration',
'colgroup:client/plugins' => 'Plug-ins',
'colgroup:client/screen' => 'Screen',
'colgroup:vars' => 'Variables',
'colgroup:vars/visitor' => 'Visitor',
'colgroup:vars/session' => 'Session',
'colgroup:vars/pageview' => 'Pageview',
'colgroup:advanced' => 'Advanced',
'colgroup:advanced/ids' => 'IDs',
'colgroup:advanced/data' => 'Data',
'op:between' => 'between',
'op:between-and' => 'and',
'op:startswith' => 'starts with',
'op:endswith' => 'ends with',
'op:contains' => 'contains',
'op:inday' => 'in day',
'op:inmonth' => 'in month',
'op:inyear' => 'in year',
'op:isnull' => 'is NULL',
'contenttype:unknown' => 'unknown content type',
'time:none' => 'no time',
'time:never' => 'never',
'time:now' => 'now',
'time:combine' => ' and ',
'time:pattern point past' => '{time} ago',
'time:pattern point future' => 'in {time}',
'time:pattern span past' => 'for {time}',
'time:pattern span future' => 'for {time}',
'time:pattern before past' => '{time} before',
'time:pattern before future' => '{time} later',
'time.1:years' => '{#} years',
'time.1:years#1' => 'one year',
'time.1:months' => '{#} months',
'time.1:months#1' => 'one month',
'time.1:days' => '{#} days',
'time.1:days#1' => 'one day',
'time.1:hours' => '{#} hours',
'time.1:hours#1' => 'one hour',
'time.1:minutes' => '{#} minutes',
'time.1:minutes#1' => 'one minute',
'time.1:seconds' => '{#} seconds',
'time.1:seconds#1' => 'one second',
'month:1' => 'Jan',
'month:2' => 'Feb',
'month:3' => 'Mar',
'month:4' => 'Apr',
'month:5' => 'May',
'month:6' => 'Jun',
'month:7' => 'Jul',
'month:8' => 'Aug',
'month:9' => 'Sep',
'month:10' => 'Oct',
'month:11' => 'Nov',
'month:12' => 'Dec',
'monthlong:1' => 'January',
'monthlong:2' => 'February',
'monthlong:3' => 'March',
'monthlong:4' => 'April',
'monthlong:5' => 'May',
'monthlong:6' => 'June',
'monthlong:7' => 'July',
'monthlong:8' => 'August',
'monthlong:9' => 'September',
'monthlong:10' => 'October',
'monthlong:11' => 'November',
'monthlong:12' => 'December',
'weekday:1' => 'Mon',
'weekday:2' => 'Tue',
'weekday:3' => 'Wed',
'weekday:4' => 'Thu',
'weekday:5' => 'Fri',
'weekday:6' => 'Sat',
'weekday:7' => 'Sun',
'weekdaylong:1' => 'Monday',
'weekdaylong:2' => 'Tuesday',
'weekdaylong:3' => 'Wednesday',
'weekdaylong:4' => 'Thursday',
'weekdaylong:5' => 'Friday',
'weekdaylong:6' => 'Saturday',
'weekdaylong:7' => 'Sunday',
'browser:edge' => 'Microsoft Edge',
'browser:galeon' => 'Galeon',
'browser:gecko/camino' => 'Camino (Gecko)',
'browser:gecko/ff' => 'Firefox',
'browser:gecko/kmeleon' => 'K-Meleon (Gecko)',
'browser:gecko/nn' => 'Netscape',
'browser:gecko/seamonkey' => 'SeaMonkey (Gecko)',
'browser:icab' => 'iCab',
'browser:ie/avant' => 'Avant (IE)',
'browser:ie/ie' => 'Internet Explorer',
'browser:ie/ie/compat' => 'Internet Explorer (compatibility mode)',
'browser:ie/maxthon' => 'Maxthon (IE)',
'browser:ie/mobile' => 'Mobile Internet Explorer',
'browser:kindle' => 'Amazon Kindle',
'browser:opera/mini' => 'Opera Mini',
'browser:opera/mobile' => 'Opera Mobile',
'browser:opera/opera' => 'Opera',
'browser:webkit/chrome' => 'Chrome',
'browser:webkit/chrome/chromium' => 'Chromium',
'browser:webkit/chrome/frame' => 'Chrome Frame for Internet Explorer',
'browser:webkit/konq' => 'Konqueror (Webkit)',
'browser:webkit/mobile' => 'Mobile Safari',
'browser:webkit/omniweb' => 'OmniWeb (Webkit)',
'browser:webkit/safari' => 'Safari',
'os:blackberry' => 'BlackBerry',
'os:java/me' => 'Java Mobile',
'os:java/me/midp' => 'Java MIDP',
'os:mac' => 'MacOS',
'os:mac/ipad' => 'iPad OS',
'os:mac/iphone' => 'iPhone OS',
'os:mac/x' => 'MacOS X',
'os:os2' => 'OS/2',
'os:palm' => 'PalmOS',
'os:symbian' => 'Symbian',
'os:unix' => 'Unix',
'os:unix/freebsd' => 'FreeBSD',
'os:unix/linux' => 'Linux',
'os:unix/linux/android' => 'Android',
'os:unix/sun' => 'Solaris',
'os:webos' => 'webOS',
'os:win' => 'Windows',
'os:win/ce' => 'Windows CE',
'os:win/ce/mobile' => 'Windows Mobile',
'os:win/ce/phone' => 'Windows Phone',
'os:win/me' => 'Windows ME',
'os:win/nt' => 'Windows NT',
'os:win/nt/arm' => 'Windows NT on ARM',
'os:win/nt/2000' => 'Windows 2000',
'os:win/nt/2003' => 'Windows Server 2003',
'os:win/nt/7' => 'Windows 7',
'os:win/nt/8' => 'Windows 8',
'os:win/nt/8/arm' => 'Windows 8 RT',
'os:win/nt/8.1' => 'Windows 8.1',
'os:win/nt/8.1/arm' => 'Windows 8.1 RT',
'os:win/nt/10' => 'Windows 10',
'os:win/nt/vista' => 'Windows Vista',
'os:win/nt/xp' => 'Windows XP',
'os:win/phone' => 'Windows Phone',
'os:win/phone/7' => 'Windows Phone 7',
'os:win/phone/7.5' => 'Windows Phone 7.5',
'os:win/phone/8.0' => 'Windows Phone 8',
'country:*' => false,
'region:DE:Bayern' => 'Bavaria',
'region:DE:Hessen' => 'Hesse',
'region:DE:Niedersachsen' => 'Lower Saxony',
'region:DE:Nordrhein-Westfalen' => 'North Rhine-Westphalia',
'region:DE:Rheinland-Pfalz' => 'Rhineland-Palatinate',
'region:DE:Mecklenburg-Vorpommern' => 'Mecklenburg-West Pomerania',
'region:DE:Sachsen' => 'Saxony',
'region:DE:Sachsen-Anhalt' => 'Saxony-Anhalt',
'region:DE:Thuringen' => 'Thuringia',
'region:DE:#nofallback' => true,
'city:*' => false,
'language:aa' => 'Afar',
'language:ab' => 'Abkhazian',
'language:ae' => 'Avestan',
'language:af' => 'Afrikaans',
'language:ak' => 'Akan',
'language:am' => 'Amharic',
'language:an' => 'Aragonese',
'language:ar' => 'Arabic',
'language:as' => 'Assamese',
'language:av' => 'Avaric',
'language:ay' => 'Aymara',
'language:az' => 'Azerbaijani',
'language:ba' => 'Bashkir',
'language:be' => 'Belarusian',
'language:bg' => 'Bulgarian',
'language:bh' => 'Bihari languages',
'language:bi' => 'Bislama',
'language:bm' => 'Bambara',
'language:bn' => 'Bengali',
'language:bo' => 'Tibetan',
'language:br' => 'Breton',
'language:bs' => 'Bosnian',
'language:ca' => 'Catalan',
'language:ce' => 'Chechen',
'language:ch' => 'Chamorro',
'language:co' => 'Corsican',
'language:cr' => 'Cree',
'language:cs' => 'Czech',
'language:cu' => 'Church Slavic',
'language:cv' => 'Chuvash',
'language:cy' => 'Welsh',
'language:da' => 'Danish',
'language:de' => 'German',
'language:dv' => 'Dhivehi',
'language:dz' => 'Dzongkha',
'language:ee' => 'Ewe',
'language:el' => 'Greek',
'language:en' => 'English',
'language:eo' => 'Esperanto',
'language:es' => 'Spanish',
'language:et' => 'Estonian',
'language:eu' => 'Basque',
'language:fa' => 'Persian',
'language:ff' => 'Fulah',
'language:fi' => 'Finnish',
'language:fj' => 'Fijian',
'language:fo' => 'Faroese',
'language:fr' => 'French',
'language:fy' => 'Western Frisian',
'language:ga' => 'Irish',
'language:gd' => 'Scottish Gaelic',
'language:gl' => 'Galician',
'language:gn' => 'Guarani',
'language:gu' => 'Gujarati',
'language:gv' => 'Manx',
'language:ha' => 'Hausa',
'language:he' => 'Hebrew',
'language:hi' => 'Hindi',
'language:ho' => 'Hiri Motu',
'language:hr' => 'Croatian',
'language:ht' => 'Haitian',
'language:hu' => 'Hungarian',
'language:hy' => 'Armenian',
'language:hz' => 'Herero',
'language:ia' => 'Interlingua',
'language:id' => 'Indonesian',
'language:ie' => 'Interlingue',
'language:ig' => 'Igbo',
'language:ii' => 'Sichuan Yi',
'language:ik' => 'Inupiaq',
'language:io' => 'Ido',
'language:is' => 'Icelandic',
'language:it' => 'Italian',
'language:iu' => 'Inuktitut',
'language:ja' => 'Japanese',
'language:jv' => 'Javanese',
'language:ka' => 'Georgian',
'language:kg' => 'Kongo',
'language:ki' => 'Kikuyu',
'language:kj' => 'Kuanyama',
'language:kk' => 'Kazakh',
'language:kl' => 'Kalaallisut',
'language:km' => 'Central Khmer',
'language:kn' => 'Kannada',
'language:ko' => 'Korean',
'language:kr' => 'Kanuri',
'language:ks' => 'Kashmiri',
'language:ku' => 'Kurdish',
'language:kv' => 'Komi',
'language:kw' => 'Cornish',
'language:ky' => 'Kirghiz',
'language:la' => 'Latin',
'language:lb' => 'Luxembourgish',
'language:lg' => 'Ganda',
'language:li' => 'Limburgan',
'language:ln' => 'Lingala',
'language:lo' => 'Lao',
'language:lt' => 'Lithuanian',
'language:lu' => 'Luba-Katanga',
'language:lv' => 'Latvian',
'language:mg' => 'Malagasy',
'language:mh' => 'Marshallese',
'language:mi' => 'Maori',
'language:mk' => 'Macedonian',
'language:ml' => 'Malayalam',
'language:mn' => 'Mongolian',
'language:mr' => 'Marathi',
'language:ms' => 'Malay',
'language:mt' => 'Maltese',
'language:my' => 'Burmese',
'language:na' => 'Nauru',
'language:nb' => 'Norwegian Bokmål',
'language:nd' => 'North Ndebele',
'language:ne' => 'Nepali',
'language:ng' => 'Ndonga',
'language:nl' => 'Dutch',
'language:nn' => 'Norwegian Nynorsk',
'language:no' => 'Norwegian',
'language:nr' => 'South Ndebele',
'language:nv' => 'Navajo',
'language:ny' => 'Nyanja',
'language:oc' => 'Occitan',
'language:oj' => 'Ojibwa',
'language:om' => 'Oromo',
'language:or' => 'Oriya',
'language:os' => 'Ossetian',
'language:pa' => 'Panjabi',
'language:pi' => 'Pali',
'language:pl' => 'Polish',
'language:ps' => 'Pushto',
'language:pt' => 'Portuguese',
'language:qu' => 'Quechua',
'language:rm' => 'Romansh',
'language:rn' => 'Rundi',
'language:ro' => 'Romanian',
'language:ru' => 'Russian',
'language:rw' => 'Kinyarwanda',
'language:sa' => 'Sanskrit',
'language:sc' => 'Sardinian',
'language:sd' => 'Sindhi',
'language:se' => 'Northern Sami',
'language:sg' => 'Sango',
'language:si' => 'Sinhala',
'language:sk' => 'Slovak',
'language:sl' => 'Slovenian',
'language:sm' => 'Samoan',
'language:sn' => 'Shona',
'language:so' => 'Somali',
'language:sq' => 'Albanian',
'language:sr' => 'Serbian',
'language:ss' => 'Swati',
'language:st' => 'Southern Sotho',
'language:su' => 'Sundanese',
'language:sv' => 'Swedish',
'language:sw' => 'Swahili',
'language:ta' => 'Tamil',
'language:te' => 'Telugu',
'language:tg' => 'Tajik',
'language:th' => 'Thai',
'language:ti' => 'Tigrinya',
'language:tk' => 'Turkmen',
'language:tl' => 'Tagalog',
'language:tn' => 'Tswana',
'language:to' => 'Tonga',
'language:tr' => 'Turkish',
'language:ts' => 'Tsonga',
'language:tt' => 'Tatar',
'language:tw' => 'Twi',
'language:ty' => 'Tahitian',
'language:ug' => 'Uighur',
'language:uk' => 'Ukrainian',
'language:ur' => 'Urdu',
'language:uz' => 'Uzbek',
'language:ve' => 'Venda',
'language:vi' => 'Vietnamese',
'language:vo' => 'Volapük',
'language:wa' => 'Walloon',
'language:wo' => 'Wolof',
'language:xh' => 'Xhosa',
'language:yi' => 'Yiddish',
'language:yo' => 'Yoruba',
'language:za' => 'Zhuang',
'language:zh' => 'Chinese',
'language:zu' => 'Zulu',
);
protected function FormatNumber($number, $decimals, $html, $trim)
{
$str = number_format($number, $decimals, '.', ',');
if ($trim && strpos($str, '.') !== false)
{
$str = rtrim($str, '0');
$str = rtrim($str, '.');
}
return $str;
}
protected function DoParseNumber($str, $recoveryValue)
{
$str = str_replace(',', '', $str);
$str = str_replace(' ', '', $str);
if (!is_numeric($str))
{
if (isset($recoveryValue))
return $recoveryValue;
throw new InvalidArgumentException('Invalid numeric value format.');
}
if (strpos($str, '.') !== false)
return floatval($str);
else
return intval($str);
}
protected function FormatOrdinalNumber($number, $html)
{
if ($number <= 0) return $num;
if ($number % 10 == 1) return $number . ($html ? '<sup>' : '') . 'st' . ($html ? '</sup>' : '');
if ($number % 10 == 2) return $number . ($html ? '<sup>' : '') . 'nd' . ($html ? '</sup>' : '');
if ($number % 10 == 3) return $number . ($html ? '<sup>' : '') . 'rd' . ($html ? '</sup>' : '');
return $number . ($html ? '<sup>' : '') . 'th' . ($html ? '</sup>' : '');
}
protected function FormatTime($datetime, $details, $html)
{
$sp = "\xC2\xA0";
$lsp = "\xE2\x80\x87";
$str = '';
if ($details & UnbTimeFormat::Weekday)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekday:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::Weekday ? ',' : '');
}
else if ($details & UnbTimeFormat::WeekdayLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('weekdaylong:' . $datetime->format('N')) .
($details & ~UnbTimeFormat::WeekdayLong ? ',' : '');
}
if ($details & UnbTimeFormat::Date)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('m/d/Y');
$haveDate = true;
}
else if ($details & UnbTimeFormat::DateLong)
{
$str .= (strlen($str) ? $sp : '') .
UnbLocale::Translate('monthlong:' . $datetime->format('n')) . $sp .
$datetime->format('j') . ',' . $sp .
$datetime->format('Y');
$haveDate = true;
}
if ($details & UnbTimeFormat::TimeSec)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('g:i:s a');
}
else if ($details & UnbTimeFormat::Time)
{
$str .= (strlen($str) ? ($haveDate && $html ? $lsp : $sp) : '') .
$datetime->format('g:i a');
}
if ($details & UnbTimeFormat::TimeZone)
{
$str .= (strlen($str) ? $sp : '') .
$datetime->format('T');
}
return $str;
}
protected function FormatRelativeTime($diff, $context, $html)
{
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
case UnbTimeContext::Span:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
case UnbTimeContext::Before:
if ($diff->invert)
$grammar = 1;
else
$grammar = 1;
break;
}
$parts = array();
if ($diff->y && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':years', $diff->y));
if ($diff->m && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':months', $diff->m));
if ($diff->d && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':days', $diff->d));
if ($diff->h && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':hours', $diff->h));
if ($diff->i && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':minutes', $diff->i));
if ($diff->s && count($parts) < 2)
$parts[] = UnbLocale::NoBreak(UnbLocale::Translate('time.' . $grammar . ':seconds', $diff->s));
$str = '';
foreach ($parts as $part)
{
$str .= (strlen($str) ? UnbLocale::Translate('time:combine') : '') .
$part;
}
switch ($context)
{
case UnbTimeContext::Point:
if ($diff->invert)
$str = self::Translate('time:pattern point past', 'time', $str);
else
$str = self::Translate('time:pattern point future', 'time', $str);
break;
case UnbTimeContext::Span:
if ($diff->invert)
$str = self::Translate('time:pattern span past', 'time', $str);
else
$str = self::Translate('time:pattern span future', 'time', $str);
break;
case UnbTimeContext::Before:
if ($diff->invert)
$str = self::Translate('time:pattern before past', 'time', $str);
else
$str = self::Translate('time:pattern before future', 'time', $str);
break;
}
return $str;
}
}
UnbLocale::Register(new UnbLocale_en_US());
function unbtr()
{
return call_user_func_array(array('UnbLocale', 'Translate'), func_get_args());
}
function unbtrc()
{
return UnbLocale::Capital(call_user_func_array(array('UnbLocale', 'Translate'), func_get_args()));
}
function unbcap($str)
{
return UnbLocale::Capital($str);
}
function unbqt($str)
{
return UnbLocale::Quote($str);
}
function unbpar($str)
{
return UnbLocale::Parentheses($str);
}
function unbcol()
{
return UnbLocale::Colon();
}
function unbtm($timestamp, $details = UnbTimeFormat::DateTime, $utc = false)
{
return UnbLocale::Time($timestamp, $details, true, $utc);
}
function unbnum($number, $decimals = 0, $trim = true)
{
return UnbLocale::Number($number, $decimals, true, $trim);
}
function unbpn($str, $defaultValue = null, $recoveryValue = null)
{
return UnbLocale::ParseNumber($str, $defaultValue, $recoveryValue);
}
function unbds($size)
{
return UnbLocale::DataSize($size, true);
}
$_imgPath = 'img/';
function SetImgPath($imgPath)
{
global $_imgPath;
$_imgPath = $imgPath;
}
function GetIcon($file, $title = '', $cssClass = '')
{
if ($file == 'empty')
return '<span class="empty-icon"></span>';
if (preg_match('~^(.+)/(.+)$~', $file, $m))
{
$type = $m[1];
$name = $m[2];
}
else
{
$type = 'general';
$name = $file;
}
if ($type == 'browser' || $type == 'features' || $type == 'os' || $type == 'search')
$type = 'client';
if ($title)
$title = ' title="' . htmlspecialchars($title) . '"';
if ($cssClass)
$cssClass = ' ' . $cssClass;
return '<span class="' . $type . '-icon ' . $type . '-' . $name . $cssClass . '"' . $title . '></span>';
}
function ShowIcon($file, $title = '')
{
echo GetIcon($file, $title);
}
function str_limit($str, $length)
{
if (mb_strlen($str) <= $length)
{
return $str;
}
return mb_substr($str, 0, $length - 1) . '…';
}
function FormatValue($format, $value)
{
if (strlen($format))
{
$out = '';
while (preg_match('_^(.*?)%(\d*)(?:\.(\d+))?([a-z])(.*)$_', $format, $m))
{
$out .= htmlspecialchars($m[1]);
$size = intval($m[2]);
$dec = intval($m[3]);
switch ($m[4])
{
case 'a':
$out .= str_pad(unbtm($value, UnbTimeFormat::Weekday | UnbTimeFormat::Date, true), $size, ' ' /*nbsp*/, STR_PAD_LEFT);
break;
case 'd':
$out .= str_pad(unbnum(floatval($value), $dec, false, false), $size, '0', STR_PAD_LEFT);
break;
case 'e':
$out .= str_pad(unbnum(intval($value) % 60, $dec, false, false), $size, '0', STR_PAD_LEFT);
break;
case 'h':
$out .= str_pad(unbnum(floor(intval($value) / 3600), $dec, false, false), $size, '0', STR_PAD_LEFT);
break;
case 'i':
$out .= str_pad(unbnum((intval($value) % 3600) / 60, $dec, false, false), $size, '0', STR_PAD_LEFT);
break;
case 'm':
$out .= str_pad(unbnum(floor(intval($value) / 60), $dec, false, false), $size, '0', STR_PAD_LEFT);
break;
case 'p':
$out .= str_pad(unbnum(floatval($value) * 100, $dec, false, false), $size, '0', STR_PAD_LEFT) . '%';
break;
case 's':
$out .= htmlspecialchars(str_pad($value, $size, ' ' /*nbsp*/, STR_PAD_LEFT));
break;
case 'u':
$shortValue = preg_replace('_^(ht|f)tps?://_i', '', $value);
$out .= '<a href="' . htmlspecialchars($value) . '">' . htmlspecialchars(str_pad($shortValue, $size, ' ' /*nbsp*/, STR_PAD_LEFT)) . '</a>';
break;
}
$format = $m[5];
}
$out .= htmlspecialchars($format);
return $out;
}
return $value;
}
function BlendColors($c1, $ratio, $c2)
{
$c1 = NormalizeColor($c1);
$c2 = NormalizeColor($c2);
$ratio = max(0, min($ratio, 1));
$r = hexdec(substr($c1, 1, 2)) * (1 - $ratio) + hexdec(substr($c2, 1, 2)) * $ratio;
$g = hexdec(substr($c1, 3, 2)) * (1 - $ratio) + hexdec(substr($c2, 3, 2)) * $ratio;
$b = hexdec(substr($c1, 5, 2)) * (1 - $ratio) + hexdec(substr($c2, 5, 2)) * $ratio;
return '#' .
str_pad(dechex($r), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($g), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($b), 2, '0', STR_PAD_LEFT);
}
function NormalizeColor($c)
{
if (preg_match('~^#[0-9a-f]{6}$~i', $c)) return $c;
if (preg_match('~^rgb\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)$~i', $c, $m))
return '#' .
str_pad(dechex($m[1]), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($m[2]), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($m[3]), 2, '0', STR_PAD_LEFT);
if (strtolower($c) == 'black') return '#000000';
if (strtolower($c) == 'gray') return '#808080';
if (strtolower($c) == 'maroon') return '#800000';
if (strtolower($c) == 'red') return '#ff0000';
if (strtolower($c) == 'green') return '#008000';
if (strtolower($c) == 'lime') return '#00ff00';
if (strtolower($c) == 'olive') return '#808000';
if (strtolower($c) == 'yellow') return '#ffff00';
if (strtolower($c) == 'navy') return '#000080';
if (strtolower($c) == 'blue') return '#0000ff';
if (strtolower($c) == 'purple') return '#800080';
if (strtolower($c) == 'fuchsia') return '#ff00ff';
if (strtolower($c) == 'teal') return '#008080';
if (strtolower($c) == 'aqua') return '#00ffff';
if (strtolower($c) == 'silver') return '#c0c0c0';
if (strtolower($c) == 'white') return '#ffffff';
return '#000000';
}
class TomoViewer
{
public static $queriesPath = 'queries/';
private static $t0;
public static function Start()
{
UnbLocale::AutoPrefer();
UnbEnvironment::SetupEnvironment();
ini_restore('display_errors');
self::$t0 = microtime(true);
header('Content-type: text/html; charset=UTF-8');
echo '<!doctype html>' . "\n";
echo '<html>';
echo '<head>';
echo '<title>' . unbtr('tomo viewer title') . '</title>';
echo '<link rel="stylesheet" type="text/css" href="css.php">';
echo '<meta name="robots" content="noindex, nofollow">';
echo '<link rel="shortcut icon" href="' .
htmlspecialchars(UnbConfiguration::Get('viewer.shortcut-icon', 'img/favicon.ico')) . '">';
echo '<script type="text/javascript" src="viewer.js"></script>';
echo '</head>';
echo '<body>';
UnbDebug::StartErrorHandling(true);
$accessCookieName = UnbConfiguration::Get('cookie.name.access', 'tomo_access');
$accessToken = $_COOKIE[$accessCookieName];
$password = UnbConfiguration::Get('access.password');
$cookiePath = preg_replace('_[^/]*$_', '', $_SERVER['REQUEST_URI']);
$saltLength = 8;
if ($_POST['logout'])
{
$accessToken = null;
setcookie(
$accessCookieName,
'*',
1,
$cookiePath);
}
if (isset($accessToken))
{
$salt = substr($accessToken, 0, $saltLength);
if ($accessToken != $salt . md5($salt . $password))
{
$accessToken = null;
}
}
if (!isset($accessToken) && isset($_POST['login_password']))
{
if ($_POST['login_password'] == $password)
{
$salt = substr(md5(mt_rand()), 0, $saltLength);
$accessToken = $salt . md5($salt . $password);
$expire = $_POST['login_temp'] ?
0 :
time() + intval(intval(UnbConfiguration::Get('cookie.months', 12)) * 30.44 * 86400);
setcookie(
$accessCookieName,
$accessToken,
$expire,
$cookiePath);
}
}
if (!isset($accessToken))
{
echo '<div id="page-content">';
echo '<div class="login-intro">';
echo '<img src="img/globe_chart.png" alt=""><br>';
echo 'tòmò web analytics';
echo '</div>'; // #login-intro
echo '<div class="login-form">';
echo '<form method="post">';
echo unbtr('enter access password') . unbcol() . '<br>';
echo '<input type="text" name="__username" value="tòmò user" style="display: none;">';
echo '<input type="password" name="login_password" size="20" autofocus><br>';
echo '<input type="submit" class="call-to-action" value="' . unbtrc('login') . '">';
echo '<div class="temp"><label title="' . unbtr('login.temporary~') . '"><input type="checkbox" name="login_temp" value="1">' . unbtr('login.temporary') . '</label></div>';
echo '</form>';
echo '</div>'; // #login-form
}
else
{
echo '<div id="page-header">';
echo '<div style="float: right; margin-left: 8px;">';
echo '<form method="post" style="display: inline;">';
echo '<input type="submit" name="logout" value="' . unbtrc('logout') . '">';
echo '</form>';
echo '</div>';
UnbDatabase::GetInstance()->Open();
$db = UnbDatabase::GetInstance();
$tomo = new Tomo();
$lastPurgeTime = Tomo::GetSetting('lastpurgetime', 0);
if (time() > $lastPurgeTime + 14 * 86400)
{
set_time_limit(600);
$tomo->PurgeTempSessionData();
try
{
$tomo->PurgeText();
}
catch (UnbDatabaseException $ex)
{
echo '<p class="error">Purging texts: ' . $ex->getMessage() . '</p>';
}
Tomo::SetSetting('lastpurgetime', time());
set_time_limit(60);
}
$lastQuickPurgeTime = Tomo::GetSetting('lastquickpurgetime', 0);
if (time() > $lastQuickPurgeTime + 60)
{
$tomo->PurgeTempPageviews();
$tomo->UpdateAllExitPages();
Tomo::SetSetting('lastquickpurgetime', time());
}
$viewType = $_GET['view'];
if ($viewType == 'visitorlog')
{
echo '<a href="?view=index" title="' . unbtrc('overview link') . '" class="roundnavlink">' . GetIcon('roundback') . '</a>';
echo '<h1>' . unbtr('visitor log') . ' <span class="domain">tòmò@' . $_SERVER['SERVER_NAME'] . '</span></h1>';
$columns = $tomo->GetAllColumnGroups();
echo '<script type="text/javascript">' . "\n";
foreach ($columns as $col => $group)
{
echo "trdict['column:" . $col . "'] = '" . addslashes(unbtr('column:' . $col, true)) . "';\n";
echo "trdict['coltype:" . $col . "'] = '" . $tomo->GetColumnType($col) . "';\n";
}
echo "trdict['op:between'] = '" . addslashes(unbtr('op:between')) . "';\n";
echo "trdict['op:isnull'] = '" . addslashes(unbtr('op:isnull')) . "';\n";
echo "trdict['set'] = '" . addslashes(unbtr('set')) . "';\n";
echo "trdict['op:contains'] = '" . addslashes(unbtr('op:contains')) . "';\n";
echo "trdict['op:startswith'] = '" . addslashes(unbtr('op:startswith')) . "';\n";
echo "trdict['op:endswith'] = '" . addslashes(unbtr('op:endswith')) . "';\n";
echo "trdict['op:between-and'] = '" . addslashes(unbtr('op:between-and')) . "';\n";
echo "trdict['remove column'] = '" . addslashes(unbtrc('remove column')) . "';\n";
echo "trdict['not'] = '" . addslashes(unbtrc('not')) . "';\n";
echo "trdict['disabled'] = '" . addslashes(unbtrc('disabled')) . "';\n";
echo "trdict['icon.delete'] = '" . addslashes(GetIcon('delete')) . "';\n";
echo '</script>';
echo '<div class="tab-header white" id="addColumnsTab" style="margin-top: 10px;">';
echo '<a href="javascript:undefined" data-tabpage="filterTab">' . unbtrc('filter') . '<span class="shadow"></span></a>';
echo '<a href="javascript:undefined" data-tabpage="filterOptionsTab">' . unbtrc('options') . '<span class="shadow"></span></a>';
echo '<span class="grey" style="margin: 0 6px 0 18px;">' . unbtrc('add field') . unbcol() . '</span>';
$tabLinks = array();
foreach ($columns as $group)
{
if ($group)
{
list($group, $subgroup) = explode('/', $group);
switch ($group)
{
case 'visit': $sortIndex = 1; break;
case 'page': $sortIndex = 2; break;
case 'client': $sortIndex = 3; break;
case 'vars': $sortIndex = 4; break;
case 'advanced': $sortIndex = 5; break;
default: $sortIndex = $group; break;
}
if (!isset($tabLinks[$sortIndex]))
{
$tabLinks[$sortIndex] = '<a href="javascript:undefined" data-tabpage="' . $group . 'Tab">' . unbtr('colgroup:' . $group, true) . '<span class="shadow"></span></a>';
}
}
}
ksort($tabLinks);
foreach ($tabLinks as $tabLink)
{
echo $tabLink;
}
echo '</div>';
$seenGroups = array();
foreach ($columns as $group)
{
if ($group)
{
list($group, $subgroup) = explode('/', $group);
if (!in_array($group, $seenGroups))
{
$seenGroups[] = $group;
echo '<div class="tab-page white" id="' . $group . 'Tab">';
$subgroups = array();
foreach ($columns as $col => $_colgroup)
{
list($colgroup, $colsubgroup) = explode('/', $_colgroup);
if ($colgroup == $group)
{
switch ($_colgroup)
{
case 'visit/session': $sortIndex = 1; break;
case 'visit/source': $sortIndex = 2; break;
case 'visit/search': $sortIndex = 3; break;
case 'visit/location': $sortIndex = 4; break;
case 'page/pageview': $sortIndex = 1; break;
case 'page/address': $sortIndex = 2; break;
case 'page/action': $sortIndex = 3; break;
case 'client/browser': $sortIndex = 1; break;
case 'client/config': $sortIndex = 2; break;
case 'client/plugins': $sortIndex = 3; break;
case 'client/screen': $sortIndex = 4; break;
case 'vars/visitor': $sortIndex = 1; break;
case 'vars/session': $sortIndex = 2; break;
case 'vars/pageview': $sortIndex = 3; break;
case 'advanced/ids': $sortIndex = 1; break;
case 'advanced/data': $sortIndex = 2; break;
default: $sortIndex = $_colgroup; break;
}
if (!$subgroups[$sortIndex])
{
$subgroups[$sortIndex] .= '<span style="display: inline-block; width: 90px;">' . unbtr('colgroup:' . $_colgroup, true) . unbcol() . '</span>';
}
$subgroups[$sortIndex] .= '<input type="button" class="light column-name" onclick="addFilterColumn(\'' . $col . '\', this.title)" title="' . unbtr('coldesc:' . $col) . '" value="' . unbtr('column:' . $col, true) . '">';
}
}
ksort($subgroups);
foreach ($subgroups as $subgroup)
{
echo '<div style="margin-bottom: 2px;">' . $subgroup . '</div>';
}
echo '</div>';
}
}
}
echo '<form method="get">';
echo '<input type="hidden" name="view" value="visitorlog">';
function ShowFilterRow($col, $type)
{
echo '<tr class="va" id="filter_row_' . $col . '">';
echo '<td>';
echo '<a href="javascript:undefined" onclick="removeFilterColumn(\'' . $col . '\')" title="' . unbtrc('remove column') . '">' . GetIcon('delete') . '</a>';
echo '<span title="' . unbtr('coldesc:' . $col) . '">' . unbtr('column:' . $col, true) . '<input type="hidden" name="filtercol[]" value="' . $col . '"></span>';
echo '</td>';
$not = $_REQUEST[$col . '_not'];
echo '<td><label><input type="checkbox" name="' . $col . '_not" value="1"' . ($not ? ' checked="checked"' : '') . '>' . unbtrc('not') . '</label></td>';
$op = $_REQUEST[$col . '_op'];
echo '<td><select name="' . $col . '_op" id="filterop_' . $col . '" onchange="filterOpChanged(this)" data-column="' . $col . '">';
switch ($type)
{
case 'int':
echo '	<option value="eq"' . ($op == 'eq' ? ' selected="selected"' : '') . '>=</option>';
echo '	<option value="lt"' . ($op == 'lt' ? ' selected="selected"' : '') . '>&lt;</option>';
echo '	<option value="lte"' . ($op == 'lte' ? ' selected="selected"' : '') . '>&#x2264;</option>';
echo '	<option value="gt"' . ($op == 'gt' ? ' selected="selected"' : '') . '>&gt;</option>';
echo '	<option value="gte"' . ($op == 'gte' ? ' selected="selected"' : '') . '>&#x2265;</option>';
echo '	<option value="ne"' . ($op == 'ne' ? ' selected="selected"' : '') . '>&#x2260;</option>';
echo '	<option value="between"' . ($op == 'between' ? ' selected="selected"' : '') . '>' . unbtr('op:between') . '</option>';
echo '	<option value="isnull"' . ($op == 'isnull' ? ' selected="selected"' : '') . '>' . unbtr('op:isnull') . '</option>';
break;
case 'datetime':
echo '	<option value="eq"' . ($op == 'eq' ? ' selected="selected"' : '') . '>=</option>';
echo '	<option value="lt"' . ($op == 'lt' ? ' selected="selected"' : '') . '>&lt;</option>';
echo '	<option value="lte"' . ($op == 'lte' ? ' selected="selected"' : '') . '>&#x2264;</option>';
echo '	<option value="gt"' . ($op == 'gt' ? ' selected="selected"' : '') . '>&gt;</option>';
echo '	<option value="gte"' . ($op == 'gte' ? ' selected="selected"' : '') . '>&#x2265;</option>';
echo '	<option value="ne"' . ($op == 'ne' ? ' selected="selected"' : '') . '>&#x2260;</option>';
echo '	<option value="between"' . ($op == 'between' ? ' selected="selected"' : '') . '>' . unbtr('op:between') . '</option>';
echo '	<option value="inday"' . ($op == 'inday' ? ' selected="selected"' : '') . '>' . unbtr('op:inday') . '</option>';
echo '	<option value="inmonth"' . ($op == 'inmonth' ? ' selected="selected"' : '') . '>' . unbtr('op:inmonth') . '</option>';
echo '	<option value="inyear"' . ($op == 'inyear' ? ' selected="selected"' : '') . '>' . unbtr('op:inyear') . '</option>';
echo '	<option value="isnull"' . ($op == 'isnull' ? ' selected="selected"' : '') . '>' . unbtr('op:isnull') . '</option>';
break;
case 'bool':
echo '	<option value="eq"' . ($op == 'eq' ? ' selected="selected"' : '') . '>' . unbtr('set') . '</option>';
echo '	<option value="isnull"' . ($op == 'isnull' ? ' selected="selected"' : '') . '>' . unbtr('op:isnull') . '</option>';
break;
case 'string':
echo '	<option value="eq"' . ($op == 'eq' ? ' selected="selected"' : '') . '>=</option>';
echo '	<option value="lt"' . ($op == 'lt' ? ' selected="selected"' : '') . '>&lt;</option>';
echo '	<option value="lte"' . ($op == 'lte' ? ' selected="selected"' : '') . '>&#x2264;</option>';
echo '	<option value="gt"' . ($op == 'gt' ? ' selected="selected"' : '') . '>&gt;</option>';
echo '	<option value="gte"' . ($op == 'gte' ? ' selected="selected"' : '') . '>&#x2265;</option>';
echo '	<option value="ne"' . ($op == 'ne' ? ' selected="selected"' : '') . '>&#x2260;</option>';
echo '	<option value="between"' . ($op == 'between' ? ' selected="selected"' : '') . '>' . unbtr('op:between') . '</option>';
echo '	<option value="contains"' . ($op == 'contains' ? ' selected="selected"' : '') . '>' . unbtr('op:contains') . '</option>';
echo '	<option value="startswith"' . ($op == 'startswith' ? ' selected="selected"' : '') . '>' . unbtr('op:startswith') . '</option>';
echo '	<option value="endswith"' . ($op == 'endswith' ? ' selected="selected"' : '') . '>' . unbtr('op:endswith') . '</option>';
echo '	<option value="isnull"' . ($op == 'isnull' ? ' selected="selected"' : '') . '>' . unbtr('op:isnull') . '</option>';
break;
}
echo '</select></td>';
$value = $_REQUEST[$col . '_value'];
$value2 = $_REQUEST[$col . '_value2'];
echo '<td>';
switch ($type)
{
case 'int':
echo '<span id="input_' . $col . '_value">';
echo '<input type="text" name="' . $col . '_value" value="' . htmlspecialchars($value) . '" size="20"> ';
echo '<span id="input_' . $col . '_value2">';
echo unbtr('op:between-and') . ' <input type="text" name="' . $col . '_value2" value="' . htmlspecialchars($value2) . '" size="20">';
echo '</span>';
echo '</span>';
break;
case 'datetime':
echo '<span id="input_' . $col . '_value">';
echo '<input type="text" name="' . $col . '_value" value="' . htmlspecialchars($value) . '" size="20"> ';
echo '<span id="input_' . $col . '_value2">';
echo unbtr('op:between-and') . ' <input type="text" name="' . $col . '_value2" value="' . htmlspecialchars($value2) . '" size="20">';
echo '</span>';
echo '</span>';
break;
case 'bool':
echo '<input type="hidden" name="' . $col . '_value" value="1">';
break;
case 'string':
echo '<span id="input_' . $col . '_value">';
echo '<input type="text" name="' . $col . '_value" value="' . htmlspecialchars($value) . '" size="35"> ';
echo '<span id="input_' . $col . '_value2">';
echo unbtr('op:between-and') . ' <input type="text" name="' . $col . '_value2" value="' . htmlspecialchars($value2) . '" size="15">';
echo '</span>';
echo '</span>';
break;
}
echo '<script type="text/javascript">' . "\n";
echo 'filterOpChanged($("#filterop_' . $col . '"));' . "\n";
echo '</script>';
echo '</td>';
$disabled = $_REQUEST[$col . '_disabled'];
echo '<td><label><input type="checkbox" name="' . $col . '_disabled" value="1"' . ($disabled ? ' checked="checked"' : '') . '>' . unbtrc('disabled') . '</label></td>';
echo '</tr>';
} // function ShowFilterRow
$filterCols = $_REQUEST['filtercol'];
if (!is_array($filterCols)) $filterCols = array();
if (!$filterCols)
{
$hours = UnbConfiguration::Get('viewer.visitorlog.default-hours', 2 * 24);
header('Location: ?' . $_SERVER['QUERY_STRING'] . '&filtercol[]=SessionStartTime&SessionStartTime_op=gt&SessionStartTime_value=' .
urlencode(gmdate('Y-m-d H:i:s', time() - $hours * 3600)));
exit();
}
echo '<div class="tab-page white" id="filterTab">';
if ($filterCols)
{
echo '<div id="filter-table-box">';
echo '<table id="filter-table">';
foreach ($filterCols as $col)
{
$type = $tomo->GetColumnType($col);
ShowFilterRow($col, $type);
}
echo '</table>';
echo '</div>'; // #filter-table-box
}
else
{
echo unbtrc('no filter set');
}
echo '<input type="submit" class="call-to-action" value="' . unbtrc('update') . '">';
echo '</div>'; // #filterTab
echo '<div class="tab-page white" id="filterOptionsTab">';
$limit = intval($_REQUEST['limit']);
if (!$limit)
$limit = 1000;
echo unbtrc('limit visitor log.1') . ' <input type="text" name="limit" value="' . $limit . '"> ' . unbtr('limit visitor log.2') . '<br>';
echo '<label><input type="checkbox" name="reverse" value="1"' . ($_REQUEST['reverse'] ? ' checked' : '') . '>' . unbtrc('reverse visitor log order') . '</label><br>';
echo '<input type="submit" class="call-to-action" value="' . unbtrc('update') . '">';
echo '</div>'; // #filterOptionsTab
echo '</form>';
echo '<script type="text/javascript">' . "\n";
echo 'initializeTabs("addColumnsTab");' . "\n";
echo '</script>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
echo '<table class="with-header" style="width: 100%;">';
echo '<tr>';
echo '<th>' . unbtrc('time') . '</th>';
echo '<th>' . unbtrc('location/duration') . '</th>';
echo '<th>' . unbtrc('client') . '</th>';
echo '<th>' . unbtrc('source/action') . '</th>';
echo '</tr>';
$limit = intval($_REQUEST['limit']);
if (!$limit)
$limit = 1000;
$pageviews = $tomo->GetPageviews(array_keys($columns), $_REQUEST, $_REQUEST['reverse'], $limit + 1);
$totalVisitors = array();
$totalSessionCount = 0;
$totalPageviewCount = 0;
$totalFirstTime = null;
$totalLastTime = null;
foreach ($pageviews as $i => $pageview)
{
if ($i >= $limit) break;
if ($i == 0 || $pageview['SessionId'] != $pageviews[$i - 1]['SessionId'])
{
$totalSessionCount++;
$totalVisitors[$pageview['VisitorId']] = 1;
$seenUrls = array();
$prevPageviewTime = $pageview['SessionStartTime'];
$prevPageviewUpdateTime = 0;
$pages = array();
$sessionHitCount = 0;
$sessionTime = null;
$prevPVId = 0;
for ($j = $i; $j < count($pageviews) && $pageviews[$j]['SessionId'] == $pageview['SessionId']; $j++)
{
$p = $pageviews[$j];
if ($p['PageviewId'] != $prevPVId)
{
if (!$p['CampaignRedirect'] && !$p['Prerender'])
{
$sessionHitCount++;
if (isset($p['ActivityTimespan']))
{
if (!isset($sessionTime))
$sessionTime = 0;
$sessionTime += intval($p['ActivityTimespan']);
}
if (!in_array($p['UrlPath'], $pages))
$pages[] = $p['UrlPath'];
}
$prevPVId = $p['PageviewId'];
}
}
$sessionPageCount = count($pages);
if ($i > 0)
{
echo '<tr class="session-separator-row">';
echo '<td colspan="4">';
echo '</td>';
echo '</tr>';
}
$cssClass = 'new-session ';
if (isset($sessionTime) && $sessionTime < 8) $cssClass .= 'short-session ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
echo '<tr' . $cssClass . '>';
echo '<td class="nbr" title="SessionId: ' . $pageview['SessionId'] . '"><strong>' . unbtm(UnbDatabase::ConvertDateTime($pageview['PageviewTime']), UnbTimeFormat::DateTimeSec) . '</strong><br>';
if (isset($pageview['UtcOffset']))
{
$utcOffset = abs(intval($pageview['UtcOffset']));
if ($utcOffset % 60 == 0)
{
$utcOffset = intval($utcOffset / 60);
}
else if ($utcOffset % 60 == 30)
{
$utcOffset = intval($utcOffset / 60) . '½';
}
else
{
$utcOffset = intval($utcOffset / 60) . ':' . str_pad(intval($utcOffset % 60), 2, '0', STR_PAD_LEFT);
}
echo unbtrc('local') . unbcol() . ' ' .
unbtm(UnbDatabase::ConvertDateTime($pageview['PageviewTime']) + intval($pageview['UtcOffset']) * 60, UnbTimeFormat::Time, true) .
' (UTC' . (intval($pageview['UtcOffset']) >= 0 ? '+' : '&minus;') . $utcOffset . ')<br>';
}
echo '</td>';
echo '<td class="nbr">';
$separator = 'separator';
if (strlen($pageview['CountryCode']))
{
$country = unbtr('country:' . $pageview['CountryCode']);
if (!isset($country))
{
$country = GeoIP::GetCountryName($pageview['CountryCode']);
}
$region = null;
if (strlen($pageview['Region']))
{
$region = unbtr('region:' . $pageview['CountryCode'] . ':' . $pageview['Region']);
if (!isset($region))
{
$region = $pageview['Region'];
}
}
$city = null;
if (strlen($pageview['City']))
{
$city = unbtr('city:' . $pageview['CountryCode'] . ':' . $pageview['City']);
if (!isset($city))
{
$city = $pageview['City'];
}
}
$locStr = '';
$locTitle = '';
if (strlen($region))
{
$locStr .= ', ' . $region;
$locTitle .= ", \n" . $region;
}
if (strlen($city))
{
$locStr .= ', ' . $city;
$locTitle .= ", \n" . $city;
}
$locTitle = $country . ' [' . $pageview['CountryCode'] . ']' . $locTitle;
$locStr = $pageview['CountryCode'] . $locStr;
echo '<span title="' . $locTitle . '">';
ShowIcon('country/' . strtolower($pageview['CountryCode']));
if (isset($pageview['Lat']) && isset($pageview['Lng']))
{
echo '<a href="http://maps.google.com/?q=' . urlencode($city) . '+@' . ($pageview['Lat'] / 10000) . ',' . ($pageview['Lng'] / 10000) . '&amp;z=6&amp;t=t">';
}
echo str_limit($locStr, 22);
if (isset($pageview['Lat']) && isset($pageview['Lng']))
{
echo '</a>';
}
echo '</span>';
}
else
{
ShowIcon('unknown');
$separator = 'icon-separator';
}
if (strlen($pageview['Language']))
{
echo '<span class="' . $separator . '"></span>';
echo '<span title="' . htmlspecialchars($pageview['Language']) . '">' . htmlspecialchars(str_limit($pageview['Language'], 10)) . '</span>';
}
if ($pageview['IPVersion'] == 6 || !$pageview['NewVisitor'])
{
echo '<span class="separator"></span>';
if ($pageview['IPVersion'] == 6)
{
ShowIcon('features/ipv6', 'IPv6');
}
if (!$pageview['NewVisitor'])
{
echo '<a href="?view=visitorlog&amp;filtercol[]=VisitorId&amp;VisitorId_value=' . $pageview['VisitorId'] . '">';
ShowIcon('returning', unbtrc('returning visitor'));
echo '</a>';
}
}
echo '<br>' .
'<span title="' . $pageview['IPAddress'] . '"><a href="?view=ipdiag&amp;ip=' . $pageview['IPAddress'] . '" class="nocolor">' .
($pageview['Provider'] ? $pageview['Provider'] : '<em>' . unbtrc('no rdns') . '</em>') . '</a></span>' .
'</td>';
$browserIcon = '';
if (preg_match('_^galeon_', $pageview['Browser'])) $browserIcon = 'galeon';
else if (preg_match('_^gecko/(ff|fennec)_', $pageview['Browser'])) $browserIcon = 'firefox';
else if (preg_match('_^gecko/seamonkey_', $pageview['Browser'])) $browserIcon = 'seamonkey';
else if (preg_match('_^gecko/nn_', $pageview['Browser'])) $browserIcon = 'netscape';
else if (preg_match('_^gecko_', $pageview['Browser'])) $browserIcon = 'gecko';
else if (preg_match('_^ie/ie/10_', $pageview['Browser'])) $browserIcon = 'ie10';
else if (preg_match('_^ie/ie/11_', $pageview['Browser'])) $browserIcon = 'ie11';
else if (preg_match('_^ie/ie/9_', $pageview['Browser'])) $browserIcon = 'ie9';
else if (preg_match('_^ie/ie/8_', $pageview['Browser'])) $browserIcon = 'ie8';
else if (preg_match('_^ie/ie/7_', $pageview['Browser'])) $browserIcon = 'ie7';
else if (preg_match('_^ie/ie/6_', $pageview['Browser'])) $browserIcon = 'ie6';
else if (preg_match('_^ie/_', $pageview['Browser'])) $browserIcon = 'ie';
else if (preg_match('_^opera/[1-9][^0-9]_', $pageview['Browser'])) $browserIcon = 'opera9';
else if (preg_match('_^opera/_', $pageview['Browser'])) $browserIcon = 'opera';
else if (preg_match('_^webkit/chrome/frame_', $pageview['Browser'])) $browserIcon = 'chromeframe';
else if (preg_match('_^webkit/chrome/chromium_', $pageview['Browser'])) $browserIcon = 'chromium';
else if (preg_match('_^webkit/chrome_', $pageview['Browser'])) $browserIcon = 'chrome';
else if (preg_match('_^webkit/konq_', $pageview['Browser'])) $browserIcon = 'konqueror';
else if (preg_match('_^webkit/omniweb_', $pageview['Browser'])) $browserIcon = 'omniweb';
else if (preg_match('_^webkit/safari_', $pageview['Browser'])) $browserIcon = 'safari';
else if (preg_match('_^webkit_', $pageview['Browser'])) $browserIcon = 'webkit';
else if (preg_match('_^kindle_', $pageview['Browser'])) $browserIcon = 'kindle';
else if (preg_match('_^text_', $pageview['Browser'])) $browserIcon = 'text';
$osIcon = '';
if (preg_match('_^unix/linux/android/4_', $pageview['OperatingSystem'])) $osIcon = 'android4';
else if (preg_match('_^unix/linux/android/3_', $pageview['OperatingSystem'])) $osIcon = 'android3';
else if (preg_match('_^unix/linux/android/2_', $pageview['OperatingSystem'])) $osIcon = 'android2';
else if (preg_match('_^unix/linux/android/1_', $pageview['OperatingSystem'])) $osIcon = 'android1';
else if (preg_match('_^unix/linux/android_', $pageview['OperatingSystem'])) $osIcon = 'android';
else if (preg_match('_^blackberry_', $pageview['OperatingSystem'])) $osIcon = 'blackberry';
else if (preg_match('_^unix/freebsd_', $pageview['OperatingSystem'])) $osIcon = 'freebsd';
else if (preg_match('_^mac/i(pad|phone)/7_', $pageview['OperatingSystem'])) $osIcon = 'ios7';
else if (preg_match('_^mac/i(pad|phone)/6_', $pageview['OperatingSystem'])) $osIcon = 'ios6';
else if (preg_match('_^mac/i(pad|phone)/5_', $pageview['OperatingSystem'])) $osIcon = 'ios5';
else if (preg_match('_^mac/i(pad|phone)/4_', $pageview['OperatingSystem'])) $osIcon = 'ios4';
else if (preg_match('_^mac/i(pad|phone)/3_', $pageview['OperatingSystem'])) $osIcon = 'ios3';
else if (preg_match('_^mac/i(pad|phone)/2_', $pageview['OperatingSystem'])) $osIcon = 'ios2';
else if (preg_match('_^mac/i(pad|phone)/1_', $pageview['OperatingSystem'])) $osIcon = 'ios1';
else if (preg_match('_^mac/i(pad|phone)_', $pageview['OperatingSystem'])) $osIcon = 'ios';
else if (preg_match('_^unix/linux_', $pageview['OperatingSystem'])) $osIcon = 'linux';
else if (preg_match('_^mac/x/_', $pageview['OperatingSystem'])) $osIcon = 'macosx';
else if (preg_match('_^mac/_', $pageview['OperatingSystem'])) $osIcon = 'macos';
else if (preg_match('_^palm_', $pageview['OperatingSystem'])) $osIcon = 'palm';
else if (preg_match('_^unix/sun_', $pageview['OperatingSystem'])) $osIcon = 'solaris';
else if (preg_match('_^win/nt/10_', $pageview['OperatingSystem'])) $osIcon = 'win10';
else if (preg_match('_^win/nt/8_', $pageview['OperatingSystem'])) $osIcon = 'win8';
else if (preg_match('_^win/nt/7_', $pageview['OperatingSystem'])) $osIcon = 'win7';
else if (preg_match('_^win/nt/vista_', $pageview['OperatingSystem'])) $osIcon = 'winvista';
else if (preg_match('_^win/nt/(xp|2003)_', $pageview['OperatingSystem'])) $osIcon = 'winxp';
else if (preg_match('_^win/nt/2000_', $pageview['OperatingSystem'])) $osIcon = 'win2000';
else if (preg_match('_^win/_', $pageview['OperatingSystem'])) $osIcon = 'win';
else if (preg_match('_^java/me/_', $pageview['OperatingSystem'])) $osIcon = 'java';
else if (preg_match('_^webos_', $pageview['OperatingSystem'])) $osIcon = 'webos';
echo '<td class="nbr">';
$browserName = $pageview['Browser'];
$browserVersion = '';
if (preg_match('_^(.*)/([0-9.]+)$_', $browserName, $m))
{
$browserName = $m[1];
$browserVersion = ' ' . $m[2];
}
$browserName = unbtr('browser:' . $browserName, true);
$osName = $pageview['OperatingSystem'];
$osVersion = '';
$osSuffix = '';
if (preg_match('_^(mac/x(?:/.*)?)/(intel|ppc)$_', $osName, $m))
{
if ($m[2] == 'intel') $osSuffix = ' (Intel)';
if ($m[2] == 'ppc') $osSuffix = ' (PowerPC)';
$osName = $m[1];
}
if (preg_match('_^(win/phone(?:/.*)?)/(arm)$_', $osName, $m))
{
if ($m[2] == 'arm') $osSuffix = ' (ARM)';
$osName = $m[1];
}
$osName = unbtr('os:' . $osName, true);
if (preg_match('_^(.*)/([0-9.]+)$_', $osName, $m))
{
$osName = $m[1];
$osVersion = ' ' . $m[2];
}
$osName = unbtr('os:' . $osName, true);
if ($browserIcon)
$browserIcon = 'browser/' . $browserIcon;
else
$browserIcon = 'unknown';
if ($osIcon)
$osIcon = 'os/' . $osIcon;
else
$osIcon = 'unknown';
echo '<div>';
ShowIcon($browserIcon, $browserName . $browserVersion);
ShowIcon($osIcon, $osName . $osVersion . $osSuffix);
$features = '';
if ($pageview['JavaScript'])
$features .= GetIcon('features/javascript', 'JavaScript');
if ($pageview['Cookies'] === '0')
$features .= GetIcon('features/cookies0', 'No cookies');
if ($pageview['Cookies'] === '1')
$features .= GetIcon('features/cookies1', 'Cookies');
if ($pageview['DNT'] === '0')
$features .= GetIcon('features/dnt0', 'DNT=0');
if ($pageview['DNT'] === '1')
$features .= GetIcon('features/dnt1', 'DNT=1');
if ($pageview['AdBlock'])
$features .= GetIcon('features/adblock', 'AdBlock');
if ($features)
echo '<span class="icon-separator"></span>' . $features;
$features = '';
if ($pageview['Flash'])
$features .= GetIcon('features/flash', 'Flash');
if ($pageview['Java'])
$features .= GetIcon('features/java', 'Java');
if ($pageview['Silverlight'])
$features .= GetIcon('features/silverlight', 'Silverlight');
if ($pageview['WindowsMedia'])
$features .= GetIcon('features/windowsmedia', 'Windows Media');
if ($pageview['Quicktime'])
$features .= GetIcon('features/quicktime', 'Quicktime');
if ($features)
echo '<span class="icon-separator"></span>' . $features;
echo '</div>';
echo '<div>';
if ($pageview['Mobile'])
ShowIcon('features/mobile', unbtrc('mobile device'));
if (strlen($pageview['ScreenWidth']))
{
$dx = $pageview['ScreenAvailWidth'] - $pageview['ViewportWidth'];
$dy = $pageview['ScreenAvailHeight'] - $pageview['ViewportHeight'];
$fs = ($dx <= 25 && $dy <= 200) ? '_fullscreen' : '';
echo '<span title="' . unbtrc('viewport size') . '">' . GetIcon('window' . $fs) .
$pageview['ViewportWidth'] . '×' . $pageview['ViewportHeight'] . '</span> &nbsp; ' .
'<span title="' . unbtrc('screen size') . ', ' . unbtr('available screen') . unbcol() . ' ' .
$pageview['ScreenAvailWidth'] . '×' . $pageview['ScreenAvailHeight'] . '">' . GetIcon('screen') .
$pageview['ScreenWidth'] . '×' . $pageview['ScreenHeight'] .
'<small style="position: relative; top: -1px;">@</small>' . $pageview['ScreenColors'] . '</span>';
}
echo '</div>';
echo '<div>';
foreach (Tomo::GetConfVisitorVars() as $name => $type)
{
if (isset($pageview['vv_' . $name]))
{
ShowIcon('visitor_variable', unbtrc('custom visitor variable'));
echo htmlspecialchars($name) . ': ' . htmlspecialchars($pageview['vv_' . $name]) . '<br>';
}
}
foreach (Tomo::GetConfSessionVars() as $name => $type)
{
if (isset($pageview['sv_' . $name]))
{
ShowIcon('session_variable', unbtrc('custom session variable'));
echo htmlspecialchars($name) . ': ' . htmlspecialchars($pageview['sv_' . $name]) . '<br>';
}
}
echo '</div>';
echo '</td>';
echo '<td>';
$referrer = null;
if ($pageview['ReferrerHost'])
{
$referrer = ($pageview['ReferrerSsl'] ? 'https://' : 'http://') .
$pageview['ReferrerHost'] . $pageview['ReferrerUrlPath'];
}
$sourceKnown = false;
if ($pageview['Campaign'])
{
ShowIcon('spread', unbtrc('campaign'));
echo unbtrc('campaign') . unbcol() . ' <strong>' . htmlspecialchars($pageview['Campaign']) . '</strong><br>';
$sourceKnown = true;
}
if ($pageview['SearchEngine'])
{
$seIcon = 'search';
if (preg_match('_^ask_i', $pageview['SearchEngine'])) $seIcon = 'ask';
else if (preg_match('_^bing_i', $pageview['SearchEngine'])) $seIcon = 'bing';
else if (preg_match('_^google plus_i', $pageview['SearchEngine'])) $seIcon = 'googleplus';
else if (preg_match('_^google_i', $pageview['SearchEngine'])) $seIcon = 'google';
else if (preg_match('_^web\.de_i', $pageview['SearchEngine'])) $seIcon = 'web_de';
else if (preg_match('_^gmx_i', $pageview['SearchEngine'])) $seIcon = 'gmx';
else if (preg_match('_^yahoo_i', $pageview['SearchEngine'])) $seIcon = 'yahoo';
else if (preg_match('_^yandex\.ru_i', $pageview['SearchEngine'])) $seIcon = 'yandex';
else if (preg_match('_^baidu_i', $pageview['SearchEngine'])) $seIcon = 'baidu';
else if (preg_match('_^facebook_i', $pageview['SearchEngine'])) $seIcon = 'facebook';
else if (preg_match('_^twitter_i', $pageview['SearchEngine'])) $seIcon = 'twitter';
$seUrl = '';
if (preg_match('_^google images_i', $pageview['SearchEngine'])) $seUrl = 'http://www.google.com/images?q=';
else if (preg_match('_^google videos_i', $pageview['SearchEngine'])) $seUrl = 'http://www.google.com/search?tbm=vid&q=';
else if (preg_match('_^google news_i', $pageview['SearchEngine'])) $seUrl = 'http://www.google.com/search?tbm=nws&q=';
else if (preg_match('_^google_i', $pageview['SearchEngine'])) $seUrl = 'http://www.google.com/search?q=';
else if (preg_match('_^yahoo images_i', $pageview['SearchEngine'])) $seUrl = 'http://images.search.yahoo.com/search/images?p=';
else if (preg_match('_^yahoo_i', $pageview['SearchEngine'])) $seUrl = 'http://search.yahoo.com/search?p=';
else if (preg_match('_^bing images_i', $pageview['SearchEngine'])) $seUrl = 'http://www.bing.com/images/search?q=';
else if (preg_match('_^bing_i', $pageview['SearchEngine'])) $seUrl = 'http://www.bing.com/search?q=';
else if (preg_match('_^ask\.com_i', $pageview['SearchEngine'])) $seUrl = 'http://www.ask.com/web?q=';
else if (preg_match('_^yandex\.ru_i', $pageview['SearchEngine'])) $seUrl = 'http://yandex.ru/yandsearch?text=';
else if (preg_match('_^baidu_i', $pageview['SearchEngine'])) $seUrl = 'http://www.baidu.com/s?wd=';
else if (preg_match('_^web\.de_i', $pageview['SearchEngine'])) $seUrl = 'http://suche.web.de/web?q=';
else if (preg_match('_^gmx_i', $pageview['SearchEngine'])) $seUrl = 'http://suche.gmx.net/web?q=';
if (preg_match('_^(google|yahoo)_i', $pageview['SearchEngine']))
{
$seUrl = preg_replace('_^(https?://)[^/]+/_i', '$1' . $pageview['ReferrerHost'] . '/', $seUrl);
}
ShowIcon('search/' . $seIcon, unbtrc('search engine') . unbcol() . ' ' . $pageview['SearchEngine']);
if ($seUrl)
echo '<a href="' . htmlspecialchars($seUrl . urlencode($pageview['SearchKeywords'])) . '">';
echo htmlspecialchars($pageview['SearchEngine']);
if (strlen($pageview['SearchKeywords']))
{
echo ': <strong>' . htmlspecialchars($pageview['SearchKeywords']) . '</strong>';
if ($seUrl)
echo '</a>';
}
else
{
if ($seUrl)
echo '</a>';
echo ' – <em>' . unbtrc('no search terms available') . '</em>';
}
if ($pageview['SearchResultPage'])
{
echo ' &nbsp; <span title="' . unbtrc('page') . ' ' . $pageview['SearchResultPage'] . '">';
ShowIcon('searchpage');
echo $pageview['SearchResultPage'];
echo '</span>';
}
if (strlen($pageview['SearchImageUrl']))
{
echo '<br>';
ShowIcon('empty');
ShowIcon('imgzoom', unbtrc('search image'));
$urlPath = preg_replace('_^https?://.*?/_', '/', $pageview['SearchImageUrl']);
echo '<a href="' . htmlspecialchars($pageview['SearchImageUrl']) . '">' . htmlspecialchars(str_limit($urlPath, 67)) . '</a>';
}
echo '<br>';
$sourceKnown = true;
}
if ($referrer && !$pageview['SearchEngine'])
{
if ($pageview['ReferrerHost'] == $pageview['Domain'])
{
ShowIcon('continuation', unbtrc('session continuation'));
echo '<a href="' . htmlspecialchars($referrer) . '">' . htmlspecialchars(str_limit($pageview['ReferrerUrlPath'], 70)) . '</a>';
}
else
{
ShowIcon('link', unbtrc('referring website'));
echo '<a href="' . htmlspecialchars($referrer) . '">' . htmlspecialchars(str_limit($referrer, 70)) . '</a>';
}
echo '<br>';
$sourceKnown = true;
}
if (!$sourceKnown)
{
if (!$pageview['NewVisitor'])
{
ShowIcon('direct_bookmark', unbtrc('direct entry or bookmark'));
echo unbtrc('direct entry or bookmark');
}
else
{
ShowIcon('direct', unbtrc('direct entry'));
echo unbtrc('direct entry');
}
echo '<br>';
}
ShowIcon('sum');
echo unbtrc('session hit count', $sessionHitCount) . ' ' . unbtr('session page count', $sessionPageCount);
if (isset($sessionTime))
{
echo ' ' . unbtr('session time over') . ' ' .
preg_replace('_^0(?!:)_', '', gmdate($sessionTime >= 3600 ? 'G:i:s' : 'i:s', $sessionTime));
$sessionDuration = intval($pageview['SessionDuration']);
if ($sessionDuration > 0)
echo ' / ' . preg_replace('_^0(?!:)_', '', gmdate($sessionDuration >= 3600 ? 'G:i:s' : 'i:s', $sessionDuration));
}
echo '</td>';
echo '</tr>';
}
if ($i == 0 || $pageview['PageviewId'] != $pageviews[$i - 1]['PageviewId'])
{
$totalPageviewCount++;
if ($i == 0) $totalFirstTime = UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
$totalLastTime = UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
$prevPageviewId = $pageviewId;
$prevOriginalPageviewId = $originalPageviewId;
$pageviewId = intval($pageview['PageviewId']);
$originalPageviewId = intval($pageview['OriginalPageviewId']);
$url = $pageview['UrlPath'];
if ($pageview['UrlQuery'])
$url .= '?' . $pageview['UrlQuery'];
if (!in_array($url, $seenUrls))
{
$seenUrls[] = $url;
$newUrlInSession = true;
}
else
{
$newUrlInSession = false;
}
$pageviewTime = UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
if ($pageview['ActivityTimespan'] > 0)
{
$pageviewUpdateTime = UnbDatabase::ConvertDateTime($pageview['PageviewUpdateTime']);
}
else
{
$pageviewUpdateTime = 0;
}
$isLater = $prevPageviewUpdateTime && $pageviewTime - $prevPageviewUpdateTime > 10;
$prevPageviewUpdateTime = $pageviewUpdateTime;
$cssClass = '';
if (isset($pageview['ActivityTimespan']) && $pageview['ActivityTimespan'] < 8) $cssClass .= 'short-pageview ';
else if ($pageview['CampaignRedirect'] || $pageview['Prerender']) $cssClass .= 'short-pageview ';
if ($isLater) $cssClass .= 'later-pageview ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
echo '<tr' . $cssClass . '>';
echo '<td class="nbr" style="text-align: right;">';
$diff = UnbDatabase::ConvertDateTime($pageview['PageviewTime']) - UnbDatabase::ConvertDateTime($prevPageviewTime);
$prevPageviewTime = $pageview['PageviewTime'];
{
echo '<span title="' . unbtm(UnbDatabase::ConvertDateTime($pageview['PageviewTime']), UnbTimeFormat::DateTimeSec) .
' – ' . unbtm(UnbDatabase::ConvertDateTime($pageview['PageviewUpdateTime']), UnbTimeFormat::TimeSec) .
', PageviewId: ' . $pageview['PageviewId'] . '">+' .
preg_replace('_^0(?!:)_', '', gmdate($diff >= 3600 ? 'G:i:s' : 'i:s', $diff)) . '</span>';
}
echo '</td>';
echo '<td class="nbr">';
echo '<span title="' . unbtrc('page load time') . unbcol() . ' ';
if (isset($pageview['PageLoadTime']) && $pageview['PageLoadTime'] != -1)
echo abs($pageview['PageLoadTime']) . ' ms';
else
echo unbtr('unknown');
echo '" style="padding-left: 1px;">';
$timespan = '';
if (isset($pageview['ActivityTimespan']))
{
$tsi = intval($pageview['ActivityTimespan']);
$timespan = preg_replace('_^0(?!:)_', '', gmdate($tsi >= 3600 ? 'G:i:s' : 'i:s', $tsi));
if ($tsi > 10 * 60)
{
$timespan = '<span class="long-activity-timespan">' . $timespan . '</span>';
}
}
if ($pageview['PageviewUpdateTime'] != $pageview['PageviewTime'])
{
$diff = UnbDatabase::ConvertDateTime($pageview['PageviewUpdateTime']) - UnbDatabase::ConvertDateTime($pageview['PageviewTime']);
{
$timespan .= ($timespan ? ' / ' : '? / ') .
preg_replace('_^0(?!:)_', '', gmdate($diff >= 3600 ? 'G:i:s' : 'i:s', $diff));
}
}
if ($timespan)
{
echo GetIcon('timespan') . $timespan . '&nbsp; &nbsp; ';
}
if (abs($pageview['PageLoadTime']) > 2000)
{
echo GetIcon('sand') . unbnum(abs($pageview['PageLoadTime'])) . ' ms&nbsp; &nbsp; ';
}
if ($pageview['PageLoadTime'] < 0)
{
echo GetIcon('canceled') . unbtrc('loading canceled');
}
echo '</span>';
echo '</td>';
echo '<td>';
foreach (Tomo::GetConfPageviewVars() as $name => $type)
{
if (isset($pageview['pv_' . $name]))
{
ShowIcon('pageview_variable', unbtrc('custom pageview variable'));
echo htmlspecialchars($name) . ': ' . htmlspecialchars($pageview['pv_' . $name]) . '<br>';
}
}
echo '</td>';
echo '<td>';
$isReload =
$originalPageviewId > 0 && $originalPageviewId == $prevPageviewId ||
$originalPageviewId > 0 && $originalPageviewId == $prevOriginalPageviewId;
$isBackNav =
$originalPageviewId > 0 && $prevOriginalPageviewId == 0 ||
$originalPageviewId > 0 && $originalPageviewId < $prevOriginalPageviewId;
$isForwardNav =
$prevOriginalPageviewId > 0 && $originalPageviewId > $prevOriginalPageviewId;
if ($isReload && $prevPageviewOutlink)
{
$isReload = false;
$isBackNav = true;
}
$prevPageviewOutlink = false;
if ($pageview['EntryPage'] && $pageview['ExitPage'])
ShowIcon('bounce', unbtrc('bounce'));
else if ($pageview['EntryPage'])
ShowIcon('entry', unbtrc('entry page'));
else if ($isReload && !$isLater)
ShowIcon('navreload', unbtrc('reloaded page') . ', OriginalPageviewId: ' . $originalPageviewId);
else if ($isBackNav && !$isReload)
ShowIcon('navback', unbtrc('navigated back') . ', OriginalPageviewId: ' . $originalPageviewId);
else if ($isForwardNav && !$isReload)
ShowIcon('navforward', unbtrc('navigated forward') . ', OriginalPageviewId: ' . $originalPageviewId);
else if (!$newUrlInSession)
ShowIcon('navrevisit', unbtrc('revisited page'));
else
ShowIcon('empty');
if ($pageview['Prerender'])
echo '<s>';
echo '<span title="' . htmlspecialchars($pageview['PageTitle']) . '">';
$url = $pageview['UrlPath'];
if ($pageview['UrlQuery'])
$url .= '?' . $pageview['UrlQuery'];
if ($pageview['UrlHash'])
$url .= '#' . $pageview['UrlHash'];
$linkUrl = ($pageview['Ssl'] ? 'https://' : 'http://') . $pageview['Domain'] . $url;
echo '<a href="' . htmlspecialchars($linkUrl) . '" class="nocolor">';
echo htmlspecialchars(str_limit($url, 65));
echo '</a>';
echo '</span>';
if ($pageview['Prerender'])
echo '</s>';
if ($pageview['Ssl'])
echo '&nbsp; ' . GetIcon('ssl', 'SSL', 'smicon');
if ($pageview['PageLanguage'])
echo '<span title="' . unbtrc('language:' . strtolower($pageview['PageLanguage'])) . '" style="margin-left: 1em;">' .
GetIcon('language') . $pageview['PageLanguage'] .
'</span>';
if ($pageview['JavaScript'] && !$pageview['JavaScriptData'])
echo ' [NoJs]';
if (!$pageview['JavaScript'] && $pageview['JavaScriptData'])
echo ' [Js]';
if ($pageview['CampaignRedirect'])
echo ' [CR]';
echo '</td>';
echo '</tr>';
}
if ($pageview['ActionId'])
{
$cssClass = '';
if ($pageview['ActionType']) $cssClass .= 'action ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
echo '<tr' . $cssClass . '>';
echo '<td></td>';
echo '<td class="nbr">';
$diff = UnbDatabase::ConvertDateTime($pageview['ActionTime']) - UnbDatabase::ConvertDateTime($prevPageviewTime);
{
echo '<span title="' . unbtrc('time since page load') . '">';
ShowIcon('timeinpage');
echo '</span>';
echo '<span title="' . unbtm(UnbDatabase::ConvertDateTime($pageview['ActionTime']), UnbTimeFormat::DateTimeSec) . '">' .
preg_replace('_^0(?!:)_', '', gmdate($diff >= 3600 ? 'G:i:s' : 'i:s', $diff)) . '</span>';
}
echo '</td>';
echo '<td></td>';
echo '<td>';
ShowIcon('empty');
if ($pageview['ActionType'] == Tomo::action_Outlink)
{
$prevPageviewOutlink = true;
ShowIcon('outlink', unbtrc('outlink'));
echo '<a href="' . htmlspecialchars($pageview['ActionUrl']) . '">' . htmlspecialchars(str_limit($pageview['ActionUrl'], 67)) . '</a>';
}
else if ($pageview['ActionType'] == Tomo::action_Download)
{
$contentType = ContentType::GetContentTypeForFileName($pageview['ActionUrl']);
ShowIcon('ctype/' . ContentType::ContentTypeIconSprite($contentType), unbtrc('download') . unbcol() . ' ' . ContentType::GetContentTypeDescription($contentType));
$urlPath = preg_replace('_^https?://.*?/_', '/', $pageview['ActionUrl']);
echo '<span title="' . htmlspecialchars($pageview['ActionUrl']) . '">' . htmlspecialchars(str_limit($urlPath, 67)) . '</span>';
}
else if ($pageview['ActionType'] == Tomo::action_ImgZoom)
{
ShowIcon('imgzoom', unbtrc('image zoom'));
$urlPath = preg_replace('_^https?://.*?/_', '/', $pageview['ActionUrl']);
echo '<a href="' . htmlspecialchars($pageview['ActionUrl']) . '">' . htmlspecialchars(str_limit($urlPath, 67)) . '</a>';
}
else if ($pageview['ActionType'] == Tomo::action_SectionExpand)
{
ShowIcon('expand', unbtrc('section expand'));
echo '<span>' . htmlspecialchars($pageview['ActionData']) . '</span>';
}
else if ($pageview['ActionType'] == Tomo::action_SiteSearch)
{
ShowIcon('search/search', unbtrc('site search'));
echo '<span>' . htmlspecialchars($pageview['ActionData']) . '</span>';
}
else
{
$confActions = Tomo::GetConfActions();
$validAction = false;
foreach ($confActions as $name => $type)
{
if ($pageview['ActionType'] == $type)
{
$validAction = true;
ShowIcon('empty');
echo $name;
if (isset($pageview['ActionUrl']))
{
echo ': <a href="' . htmlspecialchars($pageview['ActionUrl']) . '">' . htmlspecialchars(str_limit($pageview['ActionUrl'], 50)) . '</a>';
}
if (isset($pageview['ActionData']))
{
echo isset($pageview['ActionUrl']) ? ', ' : ': ';
echo '<span title="' . htmlspecialchars($pageview['ActionData']) . '">' . htmlspecialchars(str_limit($pageview['ActionData'], 50)) . '</span>';
}
break;
}
}
if (!$validAction)
{
echo '<em>Invalid action type ' . $pageview['ActionType'] . '</em>';
}
}
echo '</td>';
echo '</tr>';
}
}
echo '</table>';
if (count($pageviews) > $limit)
{
echo '<p><em>The output was limited to ' . unbnum($limit) . ' records. More records are available.</em></p>';
}
echo '<table class="with-header" style="margin-top: 2em;">';
echo '<tr><th colspan="2">' . unbtrc('summary') . '</th></tr>';
echo '<tr><td>' . unbtrc('summary.unique visitors') . unbcol() . '</td><td>' . unbnum(count($totalVisitors)) . '</td></tr>';
echo '<tr><td>' . unbtrc('sessions') . unbcol() . '</td><td>' . unbnum($totalSessionCount) . '</td></tr>';
echo '<tr><td>' . unbtrc('pageviews') . unbcol() . '</td><td>' . unbnum($totalPageviewCount) . '</td></tr>';
echo '<tr><td>' . unbtrc('summary.first') . unbcol() . '</td><td>' .
($totalFirstTime ? unbtm($totalFirstTime, UnbTimeFormat::DateTimeSec) : '–') .
'</td></tr>';
echo '<tr><td>' . unbtrc('summary.last') . unbcol() . '</td><td>' .
($totalLastTime ? unbtm($totalLastTime, UnbTimeFormat::DateTimeSec) : '–') .
'</td></tr>';
echo '</table>';
} // visitor log view
else if ($viewType == 'query')
{
echo '<a href="?view=index" title="' . unbtrc('overview link') . '" class="roundnavlink">' . GetIcon('roundback') . '</a>';
$categories = array();
foreach (glob(self::$queriesPath . '*.xml') as $filename)
{
$value = basename($filename, '.xml');
$xquery = simplexml_load_file(self::$queriesPath . $value . '.xml');
$title = (string) $xquery->title;
$category = (string) $xquery->category;
if (!isset($categories[$category]))
{
$categories[$category] = array();
}
$categories[$category][$title] = $value;
}
echo '<form style="float: right; margin-top: 2px; margin-right: 8px;">';
echo '<input type="hidden" name="view" value="query">';
echo '<select name="name" onchange="this.form.submit()">';
echo '<option value="">' . unbtrc('select query') . '</option>';
ksort($categories);
foreach ($categories as $category => $titles)
{
$lines = str_repeat('—', round((35 - strlen($category)) / 3.5));
echo '<option disabled value="">' . $lines . ' ' . htmlspecialchars($category) . ' ' . $lines . '</option>';
ksort($titles);
foreach ($titles as $title => $name)
{
echo '<option value="' . htmlspecialchars($name) . '"' . ($_GET['name'] == $name ? ' selected' : '') . '>' .
htmlspecialchars($title) . '</option>';
}
}
echo '</select><noscript> <input type="submit" value="' . unbtrc('view button') . '"></noscript></form>';
$filename = $_GET['name'];
if ($filename)
{
$title = '';
$withPageviews = false;
$withActions = false;
$groupMaxLevel = 0;
$columns = array();
$limits = array();
if (preg_match('_^[a-z0-9-]+$_', $filename) && file_exists(self::$queriesPath . $filename . '.xml'))
{
$xquery = simplexml_load_file(self::$queriesPath . $filename . '.xml');
$title = (string) $xquery->title;
$withPageviews = (string) $xquery->withpageviews == 'true';
$withActions = (string) $xquery->withactions == 'true';
if ($xquery->groupmax)
$groupMaxLevel = (int) $xquery->groupmax;
foreach ($xquery->columns->column as $xcol)
{
$col = array();
if ($xcol->source)
$col['source'] = (string) $xcol->source;
if ($xcol->source && $xcol->source->attributes()->function)
$col['function'] = (string) $xcol->source->attributes()->function == 'true';
if ($xcol->alias)
$col['alias'] = (string) $xcol->alias;
else
$col['alias'] = $col['source'];
if ($xcol->barsource)
$col['barsource'] = (string) $xcol->barsource;
if ($xcol->title)
$col['title'] = (string) $xcol->title;
if ($xcol->title && $xcol->title->attributes()->colspan)
$col['titlecolspan'] = (int) $xcol->title->attributes()->colspan;
if ($xcol->empty)
$col['empty'] = (string) $xcol->empty;
if ($xcol->null)
$col['null'] = (string) $xcol->null;
if ($xcol->group)
$col['group'] = (int) $xcol->group;
if ($xcol->sort)
$col['sortindex'] = (int) $xcol->sort;
if ($xcol->sort && $xcol->sort->attributes()->dir)
$col['sortdir'] = (string) $xcol->sort->attributes()->dir == 'desc' ? 'desc' : '';
if ($xcol->resolve)
$col['resolve'] = (string) $xcol->resolve;
if ($xcol->set)
$col['set'] = (string) $xcol->set == 'true';
if ($xcol->total)
$col['total'] = (string) $xcol->total;
if ($xcol->format)
$col['format'] = (string) $xcol->format;
if ($xcol->totalformat)
$col['totalformat'] = (string) $xcol->totalformat;
if ($xcol->align)
$col['align'] = (string) $xcol->align;
if ($xcol->width)
$col['width'] = (int) $xcol->width;
if ($xcol->color)
$col['color'] = (string) $xcol->color;
if ($xcol->percent)
$col['percent'] = (string) $xcol->percent == 'true';
if ($xcol->absolute)
$col['absolute'] = true;
if ($xcol->description)
$col['description'] = (string) $xcol->description;
if ($xcol->fill)
$col['fill'] = (string) $xcol->fill;
if ($xcol->default)
$col['default'] = (string) $xcol->default;
if ($xcol->filters)
{
if ($xcol->filters->attributes()->all)
$col['allfilters'] = (string) $xcol->filters->attributes()->all == 'true';
$filters = array();
foreach ($xcol->filters->filter as $xfilter)
{
$filter = array();
if ($xfilter->attributes()->not)
$filter['not'] = (string) $xfilter->attributes()->not == 'true';
if ($xfilter->attributes()->op)
$filter['op'] = (string) $xfilter->attributes()->op;
if ($xfilter->attributes()->value)
$filter['value'] = (string) $xfilter->attributes()->value;
if ($xfilter->attributes()->value2)
$filter['value2'] = (string) $xfilter->attributes()->value2;
if ($xfilter->attributes()->int && (string) $xfilter->attributes()->int == 'true')
{
$filter['value'] = intval($filter['value']);
$filter['value2'] = intval($filter['value2']);
}
$filters[] = $filter;
}
$col['filters'] = $filters;
}
if ($xcol->pgfilters)
{
if ($xcol->pgfilters->attributes()->all)
$col['allpgfilters'] = (string) $xcol->pgfilters->attributes()->all == 'true';
$pgfilters = array();
foreach ($xcol->pgfilters->filter as $xfilter)
{
$filter = array();
if ($xfilter->attributes()->not)
$filter['not'] = (string) $xfilter->attributes()->not == 'true';
if ($xfilter->attributes()->op)
$filter['op'] = (string) $xfilter->attributes()->op;
if ($xfilter->attributes()->value)
$filter['value'] = (string) $xfilter->attributes()->value;
if ($xfilter->attributes()->value2)
$filter['value2'] = (string) $xfilter->attributes()->value2;
if ($xfilter->attributes()->int && (string) $xfilter->attributes()->int == 'true')
{
$filter['value'] = intval($filter['value']);
$filter['value2'] = intval($filter['value2']);
}
$pgfilters[] = $filter;
}
$col['pgfilters'] = $pgfilters;
}
if (isset($col['source']))
{
$firstTime = UnbDatabase::ConvertDateTime($tomo->GetFirstSessionTime());
$daysCount = (time() - $firstTime) / 86400;
$col['source'] = preg_replace('_\$daysCount_i', $daysCount, $col['source']);
}
$columns[] = $col;
}
if ($xquery->limits) foreach ($xquery->limits->limit as $xlim)
{
if ($xlim->attributes()->group)
$limits[intval($xlim->attributes()->group) - 1] = (int) $xlim;
}
}
echo '<h1>' . htmlspecialchars($title) . ' <span class="domain">tòmò@' . $_SERVER['SERVER_NAME'] . '</span></h1>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
$records = $tomo->GetRecords($withPageviews, $withActions, $columns, $limits);
if (count($records))
{
$prevValues = array();
$utc = new DateTimeZone('UTC');
$afterLastRecord = false;
for ($i = 0; $i <= count($records); $i++)
{
if ($i < count($records))
{
$rec = $records[$i];
}
else
{
$rec = array();
$lastRecord = $records[count($records) - 1];
if ($lastRecord['_group']) $rec['_group'] = $lastRecord['_group'];
$afterLastRecord = true;
}
foreach ($columns as $col)
{
if (strlen($col['alias']))
{
$value = $rec[$col['alias']];
$prevValue = $prevValues[$col['alias']];
if (preg_match('/^month:([0-9-]+)\.\.([0-9-]+)$/', $col['fill'], $m) ||
$col['fill'] == 'month' && isset($prevValue) && !$afterLastRecord)
{
$startValue = $m[1];
$endValue = $m[2];
$dtEnd = null;
if ($m)
{
$dtEnd = new DateTime($endValue, $utc);
$dtNow = new DateTime('now');
$dtNow->setTimeZone($utc);
if ($dtEnd > $dtNow)
{
$dtEnd = $dtNow;
}
}
if (!isset($prevValue))
{
if ($m)
{
$prevValue = $startValue;
$dtPrev = new DateTime($prevValue, $utc);
}
}
else
{
$dtPrev = new DateTime($prevValue, $utc);
$dtPrev->add(new DateInterval('P1M'));
}
$dtValue = null;
if (!$afterLastRecord)
{
$dtValue = new DateTime($value, $utc);
}
while (!$afterLastRecord && $dtPrev < $dtValue && (!isset($dtEnd) || $dtPrev <= $dtEnd) ||
$afterLastRecord && $dtPrev <= $dtEnd)
{
$newRec = array();
foreach ($columns as $newCol)
{
$newRec[$newCol['alias']] = $newCol['default'];
}
$newRec[$col['alias']] = $dtPrev->format('Y-m');
if ($rec['_group']) $newRec['_group'] = $rec['_group'];
array_splice($records, $i, 0, array($newRec));
$i++;
$dtPrev->add(new DateInterval('P1M'));
}
}
if (preg_match('/^day:([0-9-]+)\.\.([0-9-]+)$/', $col['fill'], $m) ||
$col['fill'] == 'day' && isset($prevValue) && !$afterLastRecord)
{
$startValue = $m[1];
$endValue = $m[2];
$dtEnd = null;
if ($m)
{
$dtEnd = new DateTime($endValue, $utc);
$dtNow = new DateTime('now');
$dtNow->setTimeZone($utc);
if ($dtEnd > $dtNow)
{
$dtEnd = $dtNow;
}
}
if (!isset($prevValue))
{
if ($m)
{
$prevValue = $startValue;
$dtPrev = new DateTime($prevValue, $utc);
}
}
else
{
$dtPrev = new DateTime($prevValue, $utc);
$dtPrev->add(new DateInterval('P1D'));
}
$dtValue = null;
if (!$afterLastRecord)
{
$dtValue = new DateTime($value, $utc);
}
while (!$afterLastRecord && $dtPrev < $dtValue && (!isset($dtEnd) || $dtPrev <= $dtEnd) ||
$afterLastRecord && $dtPrev <= $dtEnd)
{
$newRec = array();
foreach ($columns as $newCol)
{
$newRec[$newCol['alias']] = $newCol['default'];
}
$newRec[$col['alias']] = $dtPrev->format('Y-m-d');
if ($rec['_group']) $newRec['_group'] = $rec['_group'];
array_splice($records, $i, 0, array($newRec));
$i++;
$dtPrev->add(new DateInterval('P1D'));
}
}
if (preg_match('/^([0-9]+)\.\.([0-9]+)$/', $col['fill'], $m))
{
$startValue = intval($m[1]);
$endValue = intval($m[2]);
if (!isset($prevValue)) $prevValue = $startValue - 1;
$prevValue++;
if ($prevValue < $startValue) $prevValue = $startValue;
while ($prevValue < $value && $prevValue <= $endValue ||
$afterLastRecord && $prevValue <= $endValue)
{
$newRec = array();
foreach ($columns as $newCol)
{
$newRec[$newCol['alias']] = $newCol['default'];
}
$newRec[$col['alias']] = $prevValue;
if ($rec['_group']) $newRec['_group'] = $rec['_group'];
array_splice($records, $i, 0, array($newRec));
$i++;
$prevValue++;
}
}
if (preg_match('/^.*?(;.*?)+$/', $col['fill'], $m))
{
$values = explode(';', $col['fill']);
if (!isset($prevValue))
{
$vi = 0;
$prevValue = $values[$vi];
}
else
{
$vi = array_search($prevValue, $values);
if ($vi !== false)
{
$vi++;
$prevValue = $values[$vi];
}
else
{
$prevValue = $value;
}
}
while (isset($prevValue) &&
($prevValue != $value ||
$afterLastRecord && $vi < count($values)))
{
$newRec = array();
foreach ($columns as $newCol)
{
$newRec[$newCol['alias']] = $newCol['default'];
}
$newRec[$col['alias']] = $prevValue;
if ($rec['_group']) $newRec['_group'] = $rec['_group'];
array_splice($records, $i, 0, array($newRec));
$i++;
$vi++;
$prevValue = $values[$vi];
}
}
$prevValues[$col['alias']] = $value;
}
}
}
echo '<table class="with-header vborder">';
$maxGroup = 0;
$remainingColspan = 0;
echo '<tr>';
foreach ($columns as $col)
{
$maxGroup = max($maxGroup, $col['group']);
if (isset($col['title']))
{
if ($remainingColspan > 0)
{
$remainingColspan--;
continue;
}
$cssClass = '';
if (!strlen($col['title'])) $cssClass .= 'no-vborder ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
$style = '';
if ($col['align'] == 'center' && $col['titlecolspan'] <= 1) $style .= 'text-align: center; ';
if ($col['align'] == 'right' && $col['titlecolspan'] <= 1) $style .= 'text-align: right; ';
if ($style) $style = ' style="' . trim($style) . '"';
$colspan = '';
if ($col['titlecolspan'] > 1)
{
$colspan = ' colspan="' . $col['titlecolspan'] . '"';
$remainingColspan = $col['titlecolspan'] - 1;
}
$title = '';
if (strlen($col['description']) > 0)
{
$title = ' title="' . htmlspecialchars($col['description']) . '"';
}
echo '<th' . $cssClass . $style . $colspan . $title . '>';
$title = htmlspecialchars($col['title']);
$title = str_replace('-~', '-<br>', $title);
$title = str_replace('~', '<br>', $title);
echo $title;
if ($col['sortindex'])
{
if ($col['sortdir'] == 'desc')
{
echo '<span class="sortsym">&#x' . ($col['sortindex'] > 1 ? '25bd' : '25bc') . ';</span>';
}
else
{
echo '<span class="sortsym">&#x' . ($col['sortindex'] > 1 ? '25b3' : '25b2') . ';</span>';
}
}
echo '</th>';
}
}
echo '</tr>';
$sums = array();
$counts = array();
$max = array();
foreach ($records as $rec)
{
foreach ($columns as $col)
{
if (isset($col['title']) &&
strlen($col['alias']))
{
$value = $rec[$col['alias']];
if (strlen($value) &&
is_numeric($value))
{
$max[$col['alias']] = max($max[$col['alias']], floatval($value));
if ($rec['_group'] <= 1)
{
$sums[$col['alias']] += floatval($value);
}
}
}
}
}
foreach ($records as $recIndex => $rec)
{
$cssClass = '';
if ($maxGroup && $rec['_group'] < $maxGroup) $cssClass .= 'group_' . min($rec['_group'], 4) . 'of' . min($maxGroup, 5) . ' ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
$style = '';
if ($style) $style = ' style="' . trim($style) . '"';
echo '<tr' . $cssClass . $style . '>';
foreach ($columns as $col)
{
if (isset($col['title']))
{
$cssClass = '';
if ($col['group'] && $col['group'] < $rec['_group']) $cssClass .= 'same-group ';
if (!strlen($col['title'])) $cssClass .= 'no-vborder ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
$style = '';
if ($col['align'] == 'center') $style .= 'text-align: center; ';
if ($col['align'] == 'right') $style .= 'text-align: right; ';
if ($style) $style = ' style="' . trim($style) . '"';
echo '<td' . $cssClass . $style . '>';
if (!$col['group'] || $col['group'] == $rec['_group'])
{
if (strlen($col['alias']))
{
$value = $rec[$col['alias']];
if (strlen($value))
{
if ($col['resolve'] == 'country')
{
$country = unbtr('country:' . $value);
if (!isset($country))
{
$country = GeoIP::GetCountryName($value);
}
if (isset($country))
{
$value = GetIcon('country/' . strtolower($value)) . ' ' . $country . ' [' . $value . ']';
}
}
if ($col['resolve'] == 'language')
{
$language = unbtrc('language:' . $value);
if (isset($language))
{
$value = $language . ' [' . $value . ']';
}
}
$value = FormatValue($col['format'], $value);
echo $value;
}
else if (isset($value) && strlen($col['empty']))
{
echo '<em>' . htmlspecialchars($col['empty']) . '</em>';
}
else if (!isset($value) && strlen($col['null']))
{
echo '<em>' . htmlspecialchars($col['null']) . '</em>';
}
else if ($value === '')
{
}
else if ($value === null)
{
echo '<em>(NULL)</em>';
}
}
else if (strlen($col['barsource']))
{
if ($groupMaxLevel > 0 && $rec['_group'] == $groupMaxLevel)
{
$max[$col['barsource']] = 0;
$sums[$col['barsource']] = 0;
for ($i = $recIndex + 1; $i < count($records); $i++)
{
$rec2 = $records[$i];
if ($rec2['_group'] <= $groupMaxLevel) break;
$value = floatval($rec2[$col['barsource']]);
if (strlen($value) &&
is_numeric($value))
{
$max[$col['barsource']] = max($max[$col['barsource']], floatval($value));
$sums[$col['barsource']] += floatval($value);
}
}
}
else if ($groupMaxLevel == 0 || $rec['_group'] > $groupMaxLevel)
{
$value = floatval($rec[$col['barsource']]);
$maxWidth = $col['width'];
$outerBar = false;
if ($col['absolute'])
{
$width = $value * $maxWidth;
$outerBar = true;
}
else
{
$maxValue = $max[$col['barsource']];
if ($maxValue > 0)
{
$width = $value / $maxValue * $maxWidth;
}
}
if ($width >= 0)
{
if ($outerBar)
{
$style = 'width: ' . round($maxWidth, 2) . 'pt;';
if (strlen($col['color']))
{
$col2 = BlendColors('white', 0.13, $col['color']);
$style .= ' background-color: ' . $col2 . ';';
$style .= ' background: linear-gradient(to bottom, ' . BlendColors('black', 0.96, $col2) . ', ' . $col2 . ');';
}
if ($rec['_group'] - $groupMaxLevel == 2) $style .= ' opacity: 0.6;';
else if ($rec['_group'] - $groupMaxLevel == 3) $style .= ' opacity: 0.4;';
else if ($rec['_group'] - $groupMaxLevel > 3) $style .= ' opacity: 0.3;';
echo '<span class="backbar" style="' . $style . '">';
}
$style = 'width: ' . round($width, 2) . 'pt;';
if (strlen($col['color']))
{
$style .= ' background-color: ' . $col['color'] . ';';
$style .= ' background: linear-gradient(to bottom, ' . BlendColors('white', 0.7, $col['color']) . ', ' . $col['color'] . ');';
}
if ($rec['_group'] - $groupMaxLevel == 2) $style .= ' opacity: 0.6;';
else if ($rec['_group'] - $groupMaxLevel == 3) $style .= ' opacity: 0.4;';
else if ($rec['_group'] - $groupMaxLevel > 3) $style .= ' opacity: 0.3;';
if ($value > 0) $style .= ' min-width: 1px;';
if ($sums[$col['barsource']] != 0)
{
$v = FormatValue('%.1p', $value / $sums[$col['barsource']]);
}
else
{
$v = 'Inf';
}
echo '<span class="bar" style="' . $style . '">';
if ($col['percent'] && $width >= 30)
{
echo '<span class="bar-percent">' . $v . '</span>';
}
else
{
echo '&nbsp;';
}
echo '</span>';
if ($col['percent'] && $width < 30)
{
echo '<span class="bar-percent">' . $v . '</span>';
}
if ($outerBar)
{
echo '</span>'; // .backbar
}
}
}
}
}
echo '</td>';
if (strlen($value) &&
$rec['_group'] <= 1 && $col['total'])
{
$counts[$col['alias']]++;
}
}
}
echo '</tr>';
}
if (count($counts))
{
echo '<tr class="summary">';
foreach ($columns as $col)
{
if (isset($col['title']))
{
$cssClass = '';
if (!strlen($col['title'])) $cssClass .= 'no-vborder ';
if ($cssClass) $cssClass = ' class="' . trim($cssClass) . '"';
$style = '';
if ($col['align'] == 'center') $style .= 'text-align: center; ';
if ($col['align'] == 'right') $style .= 'text-align: right; ';
if ($style) $style = ' style="' . trim($style) . '"';
echo '<td' . $cssClass . $style . '>';
$value = null;
$sign = '';
if ($col['total'] == 'sum')
{
$value = $sums[$col['alias']];
$sign = '<small>&#x3a3;</small> ';
}
else if ($col['total'] == 'avg')
{
if ($counts[$col['alias']])
$value = $sums[$col['alias']] / $counts[$col['alias']];
else
$value = 0;
$sign = '<small>&#x2205;</small> ';
}
if (isset($value))
{
$format = $col['totalformat'];
if (!strlen($format))
{
$format = $col['format'];
}
$value = FormatValue($format, $value);
echo $sign . $value;
}
echo '</td>';
}
}
echo '</tr>';
}
echo '</table>';
}
else
{
echo '<p><em>' . unbtr('query returned no data') . '</em></p>';
}
}
else // !$filename
{
echo '<h1>' . unbtr('query view') . ' <span class="domain">tòmò@' . $_SERVER['SERVER_NAME'] . '</span></h1>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
echo '<p>' . unbtr('query view intro') . '</p>';
echo '<div style="-moz-column-count: 3;">';
foreach ($categories as $category => $titles)
{
echo '<h2>' . htmlspecialchars($category) . '</h2>';
ksort($titles);
echo '<ul>';
foreach ($titles as $title => $name)
{
echo '<li><a href="?view=query&amp;name=' . htmlspecialchars($name) . '" class="novis">' .
htmlspecialchars($title) . '</a></li>';
}
echo '</ul>';
}
echo '</div>';
}
} // query view
else if ($viewType == 'ipdiag')
{
echo '<a href="?view=index" title="' . unbtrc('overview link') . '" class="roundnavlink">' . GetIcon('roundback') . '</a>';
echo '<h1>IP address diagnosis for ' . $ip . ' <span class="domain">tòmò@' . $_SERVER['SERVER_NAME'] . '</span></h1>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
$host = false;
$ip = $_GET['ip'];
if (preg_match('_^[0-9a-f.:]+$_i', $ip))
{
$host = gethostbyaddr($ip);
}
if ($host !== false)
{
echo '<h2>Hostname</h2>';
echo '<div>' . gethostbyaddr($ip) . '</div>';
echo '<h2>Traceroute</h2>';
echo '<pre style="line-height: 125%; border: solid 1px #cccccc; padding-left: 2px; max-height: 15em; overflow: auto;">';
system('traceroute -q 1 ' . $ip);
echo '</pre>';
echo '<h2>Whois</h2>';
echo '<pre style="line-height: 125%; border: solid 1px #cccccc; padding-left: 2px; max-height: 25em; overflow: auto;">';
system('whois ' . $ip);
echo '</pre>';
}
else
{
echo '<p class="error">Sorry, this is not a valid IP address. Maybe it has been hashed.</p>';
}
} // IP diagnosis view
else if ($viewType == 'info')
{
echo '<a href="?view=index" title="' . unbtrc('overview link') . '" class="roundnavlink">' . GetIcon('roundback') . '</a>';
echo '<h1>' . unbtrc('info and management') . ' <span class="domain">tòmò@' . $_SERVER['SERVER_NAME'] . '</span></h1>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
if (isset($_POST['create-visitor-var']))
{
$name = $_POST['create-visitor-var'];
try
{
$tomo->AddVisitorVar($name);
echo '<p class="success">' . unbtr('custom visitor variable created', 'name', $name) . '</p>';
}
catch (UnbDatabaseException $ex)
{
echo '<p class="error">' . unbtr('custom visitor variable create error', 'name', $name) . '<br>' .
htmlspecialchars($ex->getMessage()) . '</p>';
}
}
if (isset($_POST['create-session-var']))
{
$name = $_POST['create-session-var'];
try
{
$tomo->AddSessionVar($name);
echo '<p class="success">' . unbtr('custom session variable created', 'name', $name) . '</p>';
}
catch (UnbDatabaseException $ex)
{
echo '<p class="error">' . unbtr('custom session variable create error', 'name', $name) . '<br>' .
htmlspecialchars($ex->getMessage()) . '</p>';
}
}
if (isset($_POST['create-pageview-var']))
{
$name = $_POST['create-pageview-var'];
try
{
$tomo->AddPageviewVar($name);
echo '<p class="success">' . unbtr('custom pageview variable created', 'name', $name) . '</p>';
}
catch (UnbDatabaseException $ex)
{
echo '<p class="error">' . unbtr('custom pageview variable create error', 'name', $name) . '<br>' .
htmlspecialchars($ex->getMessage()) . '</p>';
}
}
echo '<h2>' . unbtr('tomo database') . '</h2>';
echo '<table>';
echo '<tr><td>' . unbtrc('file size') . '</td><td class="right">';
$fileName = __DIR__ . '/' . UnbConfiguration::Get('database.filename');
echo unbds(filesize($fileName));
echo '</td></tr>';
echo '<tr><td>' . unbtrc('visitors') . '</td><td class="right">';
echo unbnum(UnbDatabase::GetInstance()->QueryValue('select count(*) from "#visitor"'));
echo '</td></tr>';
echo '<tr><td>' . unbtrc('sessions') . '</td><td class="right">';
echo unbnum(UnbDatabase::GetInstance()->QueryValue('select count(*) from "#session"'));
echo '</td></tr>';
echo '<tr><td>' . unbtrc('pageviews') . '</td><td class="right">';
echo unbnum(UnbDatabase::GetInstance()->QueryValue('select count(*) from "#pageview"'));
echo '</td></tr>';
echo '<tr><td>' . unbtrc('texts') . '</td><td class="right">';
echo unbnum(UnbDatabase::GetInstance()->QueryValue('select count(*) from "#text"'));
echo '</td></tr>';
echo '<tr><td>' . unbtrc('first entry') . '</td><td class="right">';
$minPageviewTime = UnbDatabase::GetInstance()->QueryValue('select min("PageviewTime") from "#pageview"');
if ($minPageviewTime)
{
echo unbtm(UnbDatabase::ConvertDateTime($minPageviewTime), UnbTimeFormat::Date);
}
else
{
echo unbtr('time:never');
}
echo '</td></tr>';
echo '</table>';
echo '<h2>' . unbtrc('data files status') . '</h2>';
$geoIpPath = UnbConfiguration::Get('geoip.path', '');
if (strlen($geoIpPath))
{
$geoIpDir = $geoIpPath;
if (is_file($geoIpDir))
{
$geoIpDir = dirname($geoIpDir);
}
}
else
{
$geoIpDir = __DIR__;
}
echo '<table>';
echo '<tr><td>GeoIP</td><td>' . unbtrc('geoip.country-level database') . '</td><td>';
$fileName = $geoIpDir . '/geolitecountry.bin';
if (file_exists($fileName))
{
echo unbtr('updated on date', 'date', unbtm(filemtime($fileName), UnbTimeFormat::Date));
}
else
{
echo unbtr('not present');
}
echo '</td></tr>';
echo '<tr><td class="same-group"></td><td>' . unbtrc('geoip.city-level database') . '</td><td>';
$fileName = $geoIpDir . '/geolitecity.bin';
if (file_exists($fileName))
{
echo unbtr('updated on date', 'date', unbtm(filemtime($fileName), UnbTimeFormat::Date));
}
else
{
echo unbtr('not present');
}
echo '</td></tr>';
echo '<tr class="new-group"><td colspan="2">' . unbtrc('public suffix list') . '</td><td>';
$fileName = PublicSuffix::GetSuffixDataFileName();
if (file_exists($fileName))
{
echo unbtr('updated on date', 'date', unbtm(filemtime($fileName), UnbTimeFormat::Date));
}
else
{
echo unbtr('not present');
}
echo '</td></tr>';
echo '</table>';
$dbVisitorVars = Tomo::GetDbVisitorVars();
$confVisitorVars = Tomo::GetConfVisitorVars();
$dbSessionVars = Tomo::GetDbSessionVars();
$confSessionVars = Tomo::GetConfSessionVars();
$dbPageviewVars = Tomo::GetDbPageviewVars();
$confPageviewVars = Tomo::GetConfPageviewVars();
echo '<h2>' . unbtrc('custom variables') . '</h2>';
if (count($confVisitorVars) ||
count($dbVisitorVars) ||
count($confSessionVars) ||
count($dbSessionVars) ||
count($confPageviewVars) ||
count($dbPageviewVars))
{
echo '<table class="with-header">';
echo '<tr>';
echo '<th>' . unbtrc('customvar.scope') . '</th>';
echo '<th>' . unbtrc('customvar.name') . '</th>';
echo '<th>' . unbtrc('customvar.type') . '</th>';
echo '<th>' . unbtrc('customvar.notes') . '</th>';
echo '</tr>';
$count = 0;
foreach ($confVisitorVars as $name => $type)
{
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('visitor') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
if (!preg_match(Tomo::varNamePattern, $name))
{
echo '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
else if (!array_key_exists($name, $dbVisitorVars))
{
echo '<form action="?view=info" id="form-create-v-' . $name . '" method="post" style="display: inline;">';
echo '<input type="hidden" name="create-visitor-var" value="' . $name . '">';
echo '<a href="javascript:undefined" onclick="document.getElementById(\'form-create-v-' . $name . '\').submit()">' . unbtrc('customvar.create in database') . '</a>';
echo '</form>';
}
else
{
echo unbtr('customvar.ok');
}
echo '</td></tr>';
}
foreach ($dbVisitorVars as $name => $type)
{
if (array_key_exists($name, $confVisitorVars)) continue;
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('visitor') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
$msg = '';
if (!array_key_exists($name, $confVisitorVars))
{
$msg .= '<em>' . unbtrc('customvar.in database not configured') . '</em>';
}
if (!preg_match(Tomo::varNamePattern, $name))
{
$msg .= ($msg ? ' – ' : '') . '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
echo $msg;
echo '</td></tr>';
}
$count = 0;
foreach ($confSessionVars as $name => $type)
{
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('session') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
if (!preg_match(Tomo::varNamePattern, $name))
{
echo '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
else if (!array_key_exists($name, $dbSessionVars))
{
echo '<form action="?view=info" id="form-create-s-' . $name . '" method="post" style="display: inline;">';
echo '<input type="hidden" name="create-session-var" value="' . $name . '">';
echo '<a href="javascript:undefined" onclick="document.getElementById(\'form-create-s-' . $name . '\').submit()">' . unbtrc('customvar.create in database') . '</a>';
echo '</form>';
}
else
{
echo unbtr('customvar.ok');
}
echo '</td></tr>';
}
foreach ($dbSessionVars as $name => $type)
{
if (array_key_exists($name, $confSessionVars)) continue;
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('session') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
$msg = '';
if (!array_key_exists($name, $confSessionVars))
{
$msg .= '<em>' . unbtrc('customvar.in database not configured') . '</em>';
}
if (!preg_match(Tomo::varNamePattern, $name))
{
$msg .= ($msg ? ' – ' : '') . '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
echo $msg;
echo '</td></tr>';
}
$count = 0;
foreach ($confPageviewVars as $name => $type)
{
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('pageview') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
if (!preg_match(Tomo::varNamePattern, $name))
{
echo '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
else if (!array_key_exists($name, $dbPageviewVars))
{
echo '<form action="?view=info" id="form-create-p-' . $name . '" method="post" style="display: inline;">';
echo '<input type="hidden" name="create-pageview-var" value="' . $name . '">';
echo '<a href="javascript:undefined" onclick="document.getElementById(\'form-create-p-' . $name . '\').submit()">' . unbtrc('customvar.create in database') . '</a>';
echo '</form>';
}
else
{
echo unbtr('customvar.ok');
}
echo '</td></tr>';
}
foreach ($dbPageviewVars as $name => $type)
{
if (array_key_exists($name, $confPageviewVars)) continue;
$count++;
if ($count == 1)
echo '<tr class="new-group"><td>' . unbtrc('pageview') . '</td>';
else
echo '<tr><td class="same-group"></td>';
echo '</td><td>' . $name . '</td><td>' . $type . '</td><td>';
$msg = '';
if (!array_key_exists($name, $confPageviewVars))
{
$msg .= '<em>' . unbtrc('customvar.in database not configured') . '</em>';
}
if (!preg_match(Tomo::varNamePattern, $name))
{
$msg .= ($msg ? ' – ' : '') . '<strong>' . unbtrc('customvar.invalid name') . '</strong>';
}
echo $msg;
echo '</td></tr>';
}
echo '</table>';
}
else
{
echo '<p>' . unbtrc('customvar.none') . '</p>';
}
} // info and management view
else
{
echo '<h1>' . unbtr('tomo viewer title') . ' <span class="domain">' . $_SERVER['SERVER_NAME'] . '</span></h1>';
echo '</div>'; // #page-header
echo '<div id="page-content">';
if (!strlen(UnbConfiguration::Get('access.password')))
{
echo '<p class="warning">' . unbtr('warning.access password unset') . '</p>';
}
if (UnbConfiguration::Get('access.password') == '1234')
{
echo '<p class="warning">' . unbtr('warning.access password default') . '</p>';
}
if (!strlen(UnbConfiguration::Get('optout.password')))
{
echo '<p class="warning">' . unbtr('warning.opt-out password unset') . '</p>';
}
if (UnbConfiguration::Get('optout.password') == '1234')
{
echo '<p class="warning">' . unbtr('warning.opt-out password default') . '</p>';
}
echo '<p>' . unbtr('tomo viewer intro') . '</p>';
echo '<div class="index-links">';
echo '<a href="?view=visitorlog" class="novis main-nav-link mnl-visitorlog">' . unbtrc('visitor log') . '</a>';
echo '<a href="?view=query" class="novis main-nav-link mnl-query">' . unbtrc('query view') . '</a>';
echo '<a href="?view=info" class="novis main-nav-link mnl-info">' . unbtrc('info and management') . '</a>';
echo '</div>'; // .index-links
echo '<div style="clear: both;"></div>';
echo '<h2>' . unbtrc('admin opt-out cookie') . '</h2>';
$optoutCookieName = UnbConfiguration::Get('cookie.name.optout', 'tomo_optout');
$optoutToken = $_COOKIE[$optoutCookieName];
$password = UnbConfiguration::Get('optout.password');
$cookiePath = UnbConfiguration::Get('cookie.path');
$saltLength = 8;
if ($_POST['delete_optout'])
{
$optoutToken = null;
setcookie(
$optoutCookieName,
'*',
1,
$cookiePath);
}
if (isset($optoutToken))
{
$salt = substr($optoutToken, 0, $saltLength);
if ($optoutToken != $salt . md5($salt . $password))
{
$optoutToken = null;
}
}
if (!isset($optoutToken) && isset($_POST['set_optout']))
{
$salt = substr(md5(mt_rand()), 0, $saltLength);
$optoutToken = $salt . md5($salt . $password);
setcookie(
$optoutCookieName,
$optoutToken,
time() + intval(intval(UnbConfiguration::Get('cookie.months', 12)) * 30.44 * 86400),
$cookiePath);
}
if (!isset($optoutToken))
{
echo '<form method="post">';
echo '<p class="optout-cookie-unset">';
echo unbtr('admin opt-out cookie.unset') . '<br>';
echo '<input type="submit" name="set_optout" value="' . unbtrc('admin opt-out cookie.do set') . '">';
echo '</p>';
echo '</form>';
}
else
{
echo '<form method="post">';
echo '<p class="optout-cookie-set">';
echo unbtr('admin opt-out cookie.set') . '<br>';
echo '<input type="submit" name="delete_optout" value="' . unbtrc('admin opt-out cookie.do unset') . '">';
echo '</p>';
echo '</form>';
}
echo '<h2>' . unbtr('app info') . '</h2>';
echo '<p>';
$tomoVersion = '???';
if (file_exists('version.inc.php'))
{
include 'version.inc.php';
}
echo '<a href="http://tomo.unclassified.de/" class="novis">tòmò web analytics</a><br>';
echo unbtrc('version') . ' ' . $tomoVersion . '<br>';
echo '© Yves Goergen<br>';
echo unbtrc('current language') . unbcol() . ' ' . UnbLocale::GetNativeName() . ' ' . unbpar(UnbLocale::GetCode());
echo '</p>';
} // default view
} // if isset($accessToken)
$dbStat = UnbDatabase::GetInstance()->GetStatistics();
echo '<p class="grey">';
if (isset($accessToken))
{
echo 'Execution time: ' . round((microtime(true) - self::$t0) * 1000, 2) . ' ms – Database time: ' .
round($dbStat['totalTime'] * 1000, 2) . ' ms (' . $dbStat['queryCount'] . ' queries)';
}
echo '</p>';
echo '</div>'; // #page-content
echo '</body>';
echo '</html>';
}
public static function LocalExec()
{
global $argv;
UnbEnvironment::SetupNonWebEnvironment();
ini_restore('display_errors');
if ($argv[1] == 'cleanup')
{
$startTime = time();
echo 'Cleaning up...' . "\n";
UnbDatabase::GetInstance()->Open();
$db = UnbDatabase::GetInstance();
$tomo = new Tomo();
set_time_limit(900);
$t0 = time();
$tomo->PurgeTempSessionData();
if (time() - $t0 > 5)
echo 'PurgeTempSessionData took ' . (time() - $t0) . ' s' . "\n";
$t0 = time();
try
{
$tomo->PurgeText();
}
catch (UnbDatabaseException $ex)
{
echo 'Error purging texts: ' . $ex->getMessage() . "\n";
}
if (time() - $t0 > 20)
echo 'PurgeText took ' . (time() - $t0) . ' s' . "\n";
Tomo::SetSetting('lastpurgetime', time());
set_time_limit(60);
$t0 = time();
$tomo->PurgeTempPageviews();
if (time() - $t0 > 5)
echo 'PurgeTempPageviews took ' . (time() - $t0) . ' s' . "\n";
$t0 = time();
$tomo->UpdateAllExitPages();
if (time() - $t0 > 5)
echo 'UpdateAllExitPages took ' . (time() - $t0) . ' s' . "\n";
Tomo::SetSetting('lastquickpurgetime', time());
$t0 = time();
$db->Optimise(true);
if (time() - $t0 > 10)
echo 'Database Optimise took ' . (time() - $t0) . ' s' . "\n";
echo 'Database cleaned up in ' . (time() - $startTime) . ' s.' . "\n";
}
else
{
echo 'Command not understood.' . "\n";
}
}
}
$geoIpPath = UnbConfiguration::Get('geoip.path', '');
if (strlen($geoIpPath))
GeoIP::SetDBFileName($geoIpPath);
TomoViewer::$queriesPath = UnbConfiguration::Get('queries.path', 'queries/');
if (substr(TomoViewer::$queriesPath, -1) != '/')
TomoViewer::$queriesPath .= '/';
if (UnbConfiguration::Get('debug.log', false))
{
UnbDatabase::GetInstance()->SetDisplayQueryErrors(true);
UnbDebug::SetTraceToFile(true);
}
if ($_GET['post'] == '1' ||
$_GET['imgns'] == '1')
{
Tomo::FixParsePostData();
UnbEnvironment::SetupEnvironment();
UnbDatabase::GetInstance()->Open();
$tomo = new Tomo();
if (UnbConfiguration::Get('debug.log', false))
{
$tomo->SetWritePostLog(true);
}
$tomo->HandlePostRequest();
}
else if (strlen($_GET['view']))
{
TomoViewer::Start();
}
else if (!UnbEnvironment::IsWebRequest())
{
TomoViewer::LocalExec();
}
else
{
header('Content-Type: application/javascript');
if (UnbConfiguration::Get('debug.jsnocache', false))
{
header('Pragma: no-cache');
header('Cache-Control: no-cache');
header('Expires: 0');
}
else
{
header('Expires: ' . date('r', time() + 900));
}
UnbEnvironment::EnableHttpCompression();
readfile('tomo.js');
}
?>
