Update base libraries, fix E_NOTICEs

master
vitalif 2013-02-19 10:51:29 +00:00
parent 2c595b01aa
commit ea2cd9e5e0
11 changed files with 2866 additions and 1927 deletions

641
lib/DatabaseMysql.php Normal file
View File

@ -0,0 +1,641 @@
<?php
/**
* Very stable interface for MySQL - object-oriented at last :)
* Select builder is inspired by MediaWiki's one.
* (c) Vitaliy Filippov, 2012
*/
if (!defined('MS_HASH'))
{
define('MS_HASH', 0);
define('MS_LIST', 1);
define('MS_ROW', 2);
define('MS_ONE', 2);
define('MS_COL', 4);
define('MS_VALUE', 6);
define('MS_RESULT', 8);
}
class DatabaseException extends Exception {}
if (!interface_exists('Database'))
{
interface Database {}
}
class DatabaseMysql implements Database
{
var $host, $port, $socket, $username, $password, $dbname;
var $tableNames = array();
var $queryLogFile;
var $reconnect = true;
var $queryCount = 0;
var $link;
var $transactions;
/**
* Creates a MySQL connection object.
*
* @param array $options Possible options:
* host Host name or IP address to connect to [localhost]
* socket Path to UNIX socket to connect to [/var/run/mysqld/mysqld.sock]
* port TCP port to connect to [3306]
* dbname DB name to use
* username Username
* password Password
* reconnect Whether to reconnect on idle timeout [true]
* queryLogFile Path to query log file
*/
function __construct($options)
{
$defOpts = array(
'host' => 'localhost',
'port' => 3306,
'socket' => '/var/run/mysqld/mysqld.sock',
'dbname' => '',
'username' => '',
'password' => '',
'reconnect' => true,
'queryLogFile' => '',
);
$options += $defOpts;
if ($options['socket'])
{
$options['host'] = 'localhost';
}
foreach ($defOpts as $k => $v)
{
$this->$k = $options[$k];
}
}
function connect()
{
if ($this->socket !== NULL)
{
$this->link = new mysqli($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket);
}
elseif ($this->port !== NULL)
{
$this->link = new mysqli($this->host, $this->username, $this->password, $this->dbname, $this->port);
}
else
{
$this->link = new mysqli($this->host, $this->username, $this->password, $this->dbname);
}
$errno = $this->link->connect_errno;
$error = $this->link->connect_error;
if ($errno)
{
$this->link = NULL;
throw new DatabaseException($error, $errno);
}
else
{
$this->link->set_charset('utf8');
}
}
function getDBName()
{
return $this->dbname;
}
function quoteId($name)
{
return "`".str_replace("`", "``", $name)."`";
}
function quote($value)
{
if ($value === NULL)
{
return "NULL";
}
if (!$this->link)
{
$this->connect();
}
return "'" . $this->link->real_escape_string($value) . "'";
}
function query($sql, $fetchMode = MYSQLI_STORE_RESULT)
{
$this->queryCount++;
if ($this->queryLogFile)
{
file_put_contents($this->queryLogFile, date("[Y-m-d H:i:s] ").$sql."\n", FILE_APPEND);
}
if (!$this->link)
{
$this->connect();
}
$r = $this->link->query($sql, $fetchMode);
if (!$r)
{
if ($this->link->errno == 2006 && $this->reconnect)
{
// "MySQL server has gone away"
$this->link = false;
}
throw new DatabaseException($this->link->error, $this->link->errno);
}
return $r;
}
/**
* Starts a transaction, supports nested calls and savepoints.
* @param boolean $savepoint Creates savepoints only this parameter is true.
*/
function begin($savepoint = false)
{
$this->transactions[] = $savepoint;
$n = count($this->transactions);
if ($n == 1)
{
return $this->query("BEGIN");
}
elseif ($savepoint)
{
return $this->query("SAVEPOINT sp$n");
}
return true;
}
/**
* Commits transaction or releases last savepoint.
* If there is no last savepoint, just returns true.
*/
function commit()
{
$savepoint = array_pop($this->transactions);
if (!$this->transactions)
{
return $this->query("COMMIT");
}
elseif ($savepoint)
{
return $this->query("RELEASE SAVEPOINT sp".count($this->transactions));
}
return true;
}
/**
* Rollbacks transaction or last savepoint.
* If there is no savepoint, returns false.
*/
function rollback()
{
$savepoint = array_pop($this->transactions);
if (!$this->transactions)
{
return $this->query("ROLLBACK");
}
elseif ($savepoint)
{
return $this->query("ROLLBACK TO SAVEPOINT sp".count($this->transactions));
}
return false;
}
function errno()
{
return $this->link->errno;
}
function error()
{
return $this->link->error;
}
/**
* Builds WHERE-part of an SQL query.
* $where can also be a string - then it's passed as-is.
*
* @param array $where Query conditions:
* array(
* 'conditional expression',
* 'field_name' => 'value',
* 'field_name' => array('one', 'of', 'values'),
* 'field1,field2' => array(array(1, 2), array(3, 4)),
* )
*/
function where_builder($where)
{
if (!is_array($where))
return $where;
$wh = array();
foreach ($where as $k => $v)
{
if (ctype_digit("$k"))
$wh[] = $v;
elseif (is_array($v))
{
if (!$v)
{
throw new DatabaseException("Error: empty array for '$k IN (...)', don't know what to do");
}
else
{
if (is_array($v[0]))
foreach ($v as &$l)
$l = "(" . implode(",", array_map(array($this, 'quote'), $l)) . ")";
else
$v = array_map(array($this, 'quote'), $v);
$wh[] = "$k IN (" . implode(",", $v) . ")";
}
}
elseif ($v !== NULL)
$wh[] = "$k=".$this->quote($v);
else
$wh[] = "$k IS NULL";
}
if (!$wh)
$where = '1';
else
$where = '(' . join(') AND (', $wh) . ')';
return $where;
}
/**
* Builds SQL query text.
*
* @param mixed $tables see $this->tablesBuilder()
* @param mixed $fields Field definitions - either a string or an array.
* Strings are passed to resulting query text as-is.
* Arrays have the following format:
* array('field1', 'alias2' => 'expression2', ...)
* @param mixed $where see $this->whereBuilder()
* @param array $options query options - array of:
* 'CALC_FOUND_ROWS'
* 'NO_CACHE' or 'CACHE'
* 'FOR UPDATE' or 'LOCK IN SHARE MODE'
* 'GROUP BY' => array($groupby_field1 => 'ASC', $groupby_field2 => 'DESC')
* 'ORDER BY' => array($orderby_field1 => 'ASC', $orderby_field2 => 'DESC')
* 'LIMIT' => array($limit, $offset) or array($limit) or just $limit
* 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit
*/
function select_builder($tables, $fields, $where, $options = NULL)
{
if (!$options)
$options = array();
else
{
foreach ($options as $k => $v)
if (ctype_digit("$k"))
$options[$v] = true;
}
if (is_array($fields))
{
foreach ($fields as $k => $v)
if (!ctype_digit("$k"))
$fields[$k] = "$v AS ".$this->quoteId($k);
$fields = join(',', $fields);
}
$where = $this->where_builder($where);
$tables = $this->tables_builder($tables);
$sql = 'SELECT ';
if (isset($options['CALC_FOUND_ROWS']) || isset($options['SQL_CALC_FOUND_ROWS']))
$sql .= 'SQL_CALC_FOUND_ROWS ';
if (isset($options['NO_CACHE']) || isset($options['SQL_NO_CACHE']))
$sql .= 'SQL_NO_CACHE ';
elseif (isset($options['CACHE']) || isset($options['SQL_CACHE']))
$sql .= 'SQL_CACHE ';
$sql .= "$fields FROM $tables WHERE $where";
if (isset($options['GROUP BY']))
{
$g = $options['GROUP BY'];
if (is_array($g))
{
$g1 = array();
foreach ($g as $k => $v)
{
if (ctype_digit("$k"))
$g1[] = $v;
else
$g1[] = "$k $v";
}
$g = join(',', $g1);
}
$sql .= " GROUP BY $g";
}
if (isset($options['ORDER BY']))
{
$g = $options['ORDER BY'];
if (is_array($g))
{
$g1 = array();
foreach ($g as $k => $v)
{
if (ctype_digit("$k"))
$g1[] = $v;
else
$g1[] = "$k $v";
}
$g = join(',', $g1);
}
$sql .= " ORDER BY $g";
}
$sql .= $this->limit_option($options);
if (isset($options['FOR UPDATE']))
$sql .= ' FOR UPDATE';
elseif (isset($options['LOCK IN SHARE MODE']))
$sql .= ' LOCK IN SHARE MODE';
return $sql;
}
/**
* Handles a single LIMIT or LIMIT and OFFSET options.
*/
function limit_option($options)
{
if (isset($options['LIMIT']))
{
$g = $options['LIMIT'];
if (is_array($g))
$g = join(',', $g);
elseif ($g && isset($options['OFFSET']))
$g = "$options[OFFSET], $g";
if ($g)
return " LIMIT $g";
}
return '';
}
/**
* Builds FROM-part of an SQL query.
*
* $tables = array(
* 'table',
* 'alias' => 'table',
* 'alias' => array('INNER', 'table_name', $where_for_on_clause),
* 'alias' => array('INNER', $nested_tables),
* )
* or just a string "`table1` INNER JOIN `table2` ON ..."
* names taken into `backticks` will be transformed using $this->tableNames
*/
function tables_builder($tables)
{
if (is_array($tables))
{
$t = '';
foreach ($tables as $k => $v)
{
if (!ctype_digit("$k"))
{
if (is_array($v))
{
$join = strtolower(substr($v[0], 0, 4));
if ($join == 'righ')
$join = 'RIGHT';
elseif ($join == 'left')
$join = 'LEFT';
else /* if (!$join || $join == 'inne' || $join == 'join') */
$join = 'INNER';
if (is_array($v[1])) // nested join (left join (A join B on ...) on ...)
$table = '('.$this->tables_builder($v[1]).')';
else
$table = (isset($this->tableNames[$v[1]]) ? $this->quoteId($this->tableNames[$v[1]]) : $v[1]) . ' ' . $k;
if ($t)
$t .= " $join JOIN $table ON ".$this->where_builder($v[2]);
else
$t = $table;
continue;
}
else
$v = (isset($this->tableNames[$v]) ? $this->quoteId($this->tableNames[$v]) : $v) . ' ' . $k;
}
else
$v = (isset($this->tableNames[$v]) ? $this->quoteId($this->tableNames[$v]).' '.$v : $v);
if ($t)
$t .= " INNER JOIN $v";
else
$t = $v;
}
$tables = $t;
}
else
$tables = preg_replace_callback('/((?:,|JOIN)\s*`)([^`]+)/s', array($this, 'tables_builder_pregcb1'), $tables);
return $tables;
}
function tables_builder_pregcb1($m)
{
if (isset($this->tableNames[$m[2]]))
return $m[1].$this->tableNames[$m[2]];
return $m[1].$m[2];
}
/**
* Run a SELECT query and return results.
*
* Usage: either
* $this->select($tables, $fields, $where, $options, $format)
* using $this->select_builder() or
* $this->select($sql_text, $format)
* using query text.
*
* @param int $format Return format, bitmask of MS_XX constants:
* MS_RESULT = return mysqli result object to manually fetch from
* MS_LIST = return rows as indexed arrays
* MS_HASH = return rows as associative arrays (default)
* MS_ROW = only return the first row
* MS_COL = only return the first column
* MS_VALUE = only return the first cell (just 1 value)
*/
function select($tables, $fields = '*', $where = 1, $options = NULL, $format = 0)
{
if (is_int($fields) || is_string($fields) && ctype_digit($fields))
{
$sql = $tables;
$format = $fields;
}
else
$sql = $this->select_builder($tables, $fields, $where, $options);
if ($format & MS_RESULT)
return $this->query($sql, MYSQLI_USE_RESULT);
if ($format & MS_LIST)
$r = $this->get_rows($sql);
else
$r = $this->get_assocs($sql);
if (!$r)
$r = array();
if ($format & MS_ROW)
{
if (!count($r))
return NULL;
if ($format & MS_COL)
{
$r = $r[0];
$k = array_keys($r);
$r = $r[$k[0]];
}
else
$r = $r[0];
}
elseif ($format & MS_COL)
{
foreach ($r as $i => $v)
{
if (!$k)
{
$k = array_keys($v);
$k = $k[0];
}
$r[$i] = $v[$k];
}
}
return $r;
}
function found_rows()
{
return $this->select('SELECT FOUND_ROWS()', MS_VALUE);
}
/**
* Delete a set of rows.
* @param mixed $tables see $this->tables_builder()
* @param mixed $where see $this->where_builder()
* @param array $options Options for query:
* 'LIMIT' => array($limit, $offset) or array($limit) or just $limit
* 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit
*/
function delete($tables, $where, $options = NULL)
{
$tables = $this->tables_builder($tables);
$where = $this->where_builder($where);
$sql = "DELETE FROM $tables WHERE $where";
$sql .= $this->limit_option($options);
$this->query($sql);
return $this->link->affected_rows;
}
/**
* Builds an INSERT query.
* @param string $table Table name to insert rows to.
* @param array $rows Array of table rows to be inserted.
* @param boolean $onduplicatekey If true, include
* ON DUPLICATE KEY UPDATE column=VALUES(column) for all columns.
*/
function insert_builder($table, $rows, $onduplicatekey = false, $replace = false)
{
if (isset($this->tableNames[$table]))
{
$table = $this->tableNames[$table];
}
$key = array_keys($rows[0]);
foreach ($rows as &$r)
{
$rs = array();
foreach ($key as &$k)
$rs[] = $this->quote($r[$k]);
$r = "(".implode(",", $rs).")";
}
foreach ($key as &$k)
if (strpos($k, '`') === false)
$k = $this->quoteId($k);
$sql = ($replace ? "REPLACE" : "INSERT").
" INTO $table (".implode(",",$key).") VALUES ".implode(",",$rows);
if ($onduplicatekey)
{
foreach ($key as &$k)
$k = "$k=VALUES($k)";
$sql .= " ON DUPLICATE KEY UPDATE ".implode(",",$key);
}
return $sql;
}
/**
* Insert a single row into $table and return inserted ID.
* @param string $table Table name to insert row to.
* @param array $rows Row to be inserted.
* @return int $insert_id Autoincrement ID of inserted row (if appropriate).
*/
function insert_row($table, $row)
{
$sql = $this->insert_builder($table, array($row));
if ($this->query($sql))
return $this->link->insert_id;
return NULL;
}
function insert_id()
{
return $this->link->insert_id;
}
/**
* Update or insert rows into $table.
* Update query: $this->update($table, $set, $where, $options);
* Insert-or-update query: $this->update($table, $rows);
*
* @param string $table Table name to update.
* @param array $rows One row or array of rows for insert-or-update query.
* @param array $set Assoc array with values for update query.
* @param array $where Conditions for update query, see $this->where_builder().
* @param array $options Options for update query:
* 'LIMIT' => array($limit, $offset) or array($limit) or just $limit
* 'OFFSET' => $offset, for the case when 'LIMIT' is just $limit
* 'REPLACE' => true to use REPLACE instead of INSERT
*/
function update($table, $rows, $where = NULL, $options = NULL)
{
if (!$rows)
return false;
if (is_null($where))
{
if (!is_array($rows))
return false;
if (!is_array(@$rows[0]))
$rows = array($rows);
$sql = $this->insert_builder($table, $rows, true, !empty($options['replace']));
}
else
{
$sql = array();
if (!is_array($rows))
$rows = array($rows);
foreach ($rows as $k => $v)
{
if (!ctype_digit("$k"))
$sql[] = $k.'='.$this->quote($v);
else
$sql[] = $v;
}
$where = $this->where_builder($where);
$sql = 'UPDATE ' . $this->tables_builder($table) . ' SET ' . implode(', ', $sql) . ' WHERE ' . $where;
$sql .= $this->limit_option($options);
}
if ($this->query($sql))
return $this->link->affected_rows;
return false;
}
function get_rows($sql)
{
$r = array();
if ($res = $this->query($sql))
{
while ($row = $res->fetch_row())
$r[] = $row;
$res->free();
}
return $r;
}
function get_assocs($sql)
{
$r = array();
if ($res = $this->query($sql))
{
while ($row = $res->fetch_assoc())
$r[] = $row;
$res->free();
}
return $r;
}
}

View File

@ -1,24 +1,28 @@
<?php
# Всякие константы
# (c) VitaliF 2006-2010
# (c) VitaliF 2006-2013
define ('IN_CONFIG', true);
define('IN_CONFIG', true);
setlocale(LC_ALL, 'ru_RU.UTF-8');
setlocale(LC_NUMERIC, 'C');
mb_internal_encoding('utf-8');
include_once 'loconfig.php';
$db = array(
'host' => 'localhost',
'username' => 'root',
'password' => '',
'dbname' => '',
);
$image_path = '/images/';
$template_path = '/templates/';
$cache_path = '/tplcache';
$cfgTables = array();
$mysqlThrowError = true;
$mysqlNoSiteConfig = true;
include_once 'loconfig.php';
$LoginSession = 'RTV'.md5('http://' . $Domain);
require_once 'lib.php';

View File

@ -1,16 +1,19 @@
<?php
# Утилитные функции
# (c) VitaliF 2006-2010
# (c) VitaliF 2006+
$AutoloadClasses['DatabaseMysql'] = dirname(__FILE__).'/DatabaseMysql.php';
$AutoloadClasses['LinguaStemRu'] = dirname(__FILE__).'/stem_ru.php';
$AutoloadClasses['VMXTemplate'] = dirname(__FILE__).'/template.php';
$AutoloadClasses['Mailer'] = dirname(__FILE__).'/mailing.php';
function __autoload($class_name)
{
global $AutoloadClasses;
if ($f = $AutoloadClasses[$class_name])
if (isset($AutoloadClasses[$class_name]))
{
require_once $f;
require_once $AutoloadClasses[$class_name];
return true;
}
return false;
@ -52,34 +55,7 @@ class PopulatedRow implements ArrayAccess
}
}
function get_include_contents($filename)
{
if (is_file($filename))
{
ob_start();
include $filename;
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
return false;
}
function str_starts_with ($str, $sub)
{
if (strcmp (substr ($str, 0, strlen ($sub)), $sub) == 0)
return true;
return false;
}
function str_ends_with ($str, $sub)
{
if (strcmp (substr ($str, strlen ($str) - strlen ($sub)), $sub) == 0)
return true;
return false;
}
function normalize_domain ($st)
function normalize_domain($st)
{
$dom = '';
if (!empty($st))
@ -95,7 +71,7 @@ function normalize_domain ($st)
return $dom;
}
function normalize_link ($l, $pre)
function normalize_link($l, $pre)
{
if (!empty ($l) && !empty ($pre))
{
@ -109,7 +85,7 @@ function normalize_link ($l, $pre)
$pre .= '/';
$l = $pre . $l;
}
else if (($sch = strpos($pre, '://')) !== false)
elseif (($sch = strpos($pre, '://')) !== false)
$l = substr($pre, 0, strpos($pre, '/', $sch+3)) . $l;
}
return $l;
@ -119,57 +95,57 @@ function normalize_link ($l, $pre)
function strlimit($str, $maxlen)
{
if (!$maxlen || $maxlen < 1 || strlen($str) <= $maxlen)
if (!$maxlen || $maxlen < 1 || mb_strlen($str) <= $maxlen)
return $str;
$str = substr($str, 0, $maxlen);
$p = strrpos($str, ' ');
if (!$p || ($pt = strrpos($str, "\t")) > $ps)
$str = mb_substr($str, 0, $maxlen);
$p = mb_strrpos($str, ' ');
if (!$p || ($pt = mb_strrpos($str, "\t")) > $ps)
$p = $pt;
if ($p)
$str = substr($str, 0, $p);
$str = mb_substr($str, 0, $p);
return $str . '...';
}
function html_pbr($str)
{
return str_replace ("\n", "<br>", htmlspecialchars($str));
return nl2br(htmlspecialchars($str));
}
function html_strip_pbr ($str)
function html_strip_pbr($str)
{
global $allowed_html;
if (!($a = $allowed_html))
$a = '<div> <span> <a> <b> <i> <u> <p> <h1> <h2> <h3> <h4> <h5> <h6> <strike> <strong> <small> <big> <blink> <center> <ol> <pre> <sub> <sup> <font> <br> <table> <tr> <td> <th> <tbody> <tfoot> <thead> <tt> <ul> <li> <em> <img> <marquee>';
return nl2br (strip_tags ($str, $a));
return nl2br(strip_tags($str, $a));
}
function html_strip ($str)
function html_strip($str)
{
global $allowed_html;
if (!($a = $allowed_html))
$a = '<div> <span> <a> <b> <i> <u> <p> <h1> <h2> <h3> <h4> <h5> <h6> <strike> <strong> <small> <big> <blink> <center> <ol> <pre> <sub> <sup> <font> <br> <table> <tr> <td> <th> <tbody> <tfoot> <thead> <tt> <ul> <li> <em> <img> <marquee>';
return strip_tags ($str, $a);
return strip_tags($str, $a);
}
function unhtmlentity ($s)
function unhtmlentity($s)
{
$s = html_entity_decode ($s);
preg_match_all ('/&#([0-9]*);/', $s, $ce, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
$s = html_entity_decode($s);
preg_match_all('/&#([0-9]*);/', $s, $ce, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
$c = count($ce);
for ($i = $c-1; $i >= 0; $i--)
$s = substr ($s, 0, $ce[$i][0][1]) . chr($ce[$i][1][0]) . substr ($s, $ce[$i][0][1]+strlen($ce[$i][0][0]));
$s = substr($s, 0, $ce[$i][0][1]) . chr($ce[$i][1][0]) . substr($s, $ce[$i][0][1]+strlen($ce[$i][0][0]));
return $s;
}
function process_array ($arr, $kfunc, $vfunc)
function process_array($arr, $kfunc, $vfunc)
{
$na = array ();
$na = array();
foreach ($arr as $k => $v)
{
if (is_callable($kfunc))
$k = $kfunc ($k);
$k = $kfunc($k);
if (is_callable($vfunc))
$v = $vfunc ($v);
$v = $vfunc($v);
$na [$k] = $v;
}
return $na;
@ -214,57 +190,6 @@ function array_hashtree($a, $ka = NULL, $value = NULL)
return $r;
}
/**
* Составление HTML кода картинки или flash-файла (возможно со ссылкой и alt)
* Если $link начинается с popup:// - тогда запишется javascript-код для popup-а ссылки
* Если $link начинается с popupwh:// - тогда дальше идёт WIDTH:HEIGHT:
*/
function banner ($path, $alt = false, $link = false, $width = false, $height = false, $target = false)
{
if (!$path)
return '';
$code = '';
$wh = '';
if ($width !== false && $width > 0)
$wh .= ' width="' . $width . '"';
if ($height !== false && $height > 0)
$wh .= ' height="' . $height . '"';
if (str_ends_with ($path, ".swf")) // flash
{
$code .= "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0\"$wh>
<param name=\"movie\" value=\"$path\">
<param name=\"play\" value=\"true\">
<param name=\"loop\" value=\"true\">
<param name=\"quality\" value=\"high\">
<param name=\"WMode\" value=\"Window\">
<embed$wh src=\"$path\" play=\"true\" loop=\"true\" quality=\"high\" WMode=\"Window\" pluginspage=\"http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash\"></embed>
</object>";
}
else // картинка
{
if ($link !== false && str_starts_with ($link, 'popup://'))
{
$link = substr ($link, 8);
$link = "javascript: void window.open('$link', 'newWin', 'width=640,height=480,menubar=no,location=no,resizable=yes,scrollbars=yes').focus();";
}
else if ($link !== false && str_starts_with ($link, 'popupwh://'))
{
$link = substr ($link, 10);
list ($popupw, $popuph, $link) = explode (':', $link, 3);
$link = "javascript: void window.open('$link', 'newWin', 'width=$popupw,height=$popuph,menubar=no,location=no,resizable=yes,scrollbars=yes').focus();";
}
if ($link !== false)
$code .= '<a href="'.$link.'"'.($target !== false ? ' target="'.$target.'"' : '').'>';
if ($alt !== false)
$alttag = ' alt="'.htmlspecialchars($alt).'" title="'.htmlspecialchars($alt).'"';
else $alttag = '';
$code .= "<img src=\"$path\" border=\"0\"$alttag$wh />";
if ($link !== false)
$code .= '</a>';
}
return $code;
}
/* Уложить ширину и высоту изображения пропорционально в рамки maxw/maxh */
function fit_width_height($cw, $ch, $maxw, $maxh)
{
@ -275,12 +200,41 @@ function fit_width_height($cw, $ch, $maxw, $maxh)
$nw = $maxw;
$nh = $ch * $maxw / $cw;
}
else if ($maxh > 0 && ($maxw <= 0 || $maxh/$ch < $maxw/$cw))
elseif ($maxh > 0 && ($maxw <= 0 || $maxh/$ch < $maxw/$cw))
{
$nw = $cw * $maxh / $ch;
$nh = $maxh;
}
return array($nw, $nh);
return array(intval($nw), intval($nh));
}
function convertBMP($from, $to, $quality = 90)
{
static $class;
$fp = fopen($from, "rb");
if (!$fp)
{
return false;
}
$sign = fread($fp, 2);
fclose($fp);
if ($sign != 'BM')
{
return $from;
}
$class = class_exists('Gmagick') ? 'Gmagick' : 'Imagick';
try
{
$im = new $class();
$im->readImage($from);
$im->setCompressionQuality($quality);
$im->writeImage($to);
}
catch (Exception $e)
{
return $from;
}
return $to;
}
function loadimagebyext($from)
@ -290,9 +244,9 @@ function loadimagebyext($from)
$ext = strtolower(substr($from, strrpos($from, '.')+1));
if (function_exists("imagejpeg") && ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'jfif' || $ext == 'jpe'))
$img = @imagecreatefromjpeg($from);
else if (function_exists("imagepng") && ($ext == 'png'))
elseif (function_exists("imagepng") && ($ext == 'png'))
$img = @imagecreatefrompng($from);
else if (function_exists("imagegif") && ($ext == 'gif'))
elseif (function_exists("imagegif") && ($ext == 'gif'))
$img = @imagecreatefromgif($from);
}
return $img;
@ -309,9 +263,9 @@ function saveimagebyext($img, $to)
$ext = strtolower(substr($to, strrpos($to, '.')+1));
if ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'jfif' || $ext == 'jpe')
@imagejpeg($img, $to, $qual);
else if ($ext == 'png')
elseif ($ext == 'png')
@imagepng($img, $to);
else if ($ext == 'gif')
elseif ($ext == 'gif')
@imagegif($img, $to);
else
return false;
@ -410,9 +364,9 @@ function get_image_size($from, &$w, &$h)
$ext = strtolower (substr ($from, strrpos ($from, '.')+1));
if (function_exists ("imagejpeg") && ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'jfif' || $ext == 'jpe'))
$img = @imagecreatefromjpeg ($from);
else if (function_exists ("imagepng") && ($ext == 'png'))
elseif (function_exists ("imagepng") && ($ext == 'png'))
$img = @imagecreatefrompng ($from);
else if (function_exists ("imagegif") && ($ext == 'gif'))
elseif (function_exists ("imagegif") && ($ext == 'gif'))
$img = @imagecreatefromgif ($from);
if ($img)
{
@ -449,8 +403,10 @@ function load_image ($filevarname, $newfilename, &$imgvar, &$thumbvar, &$imgw, &
@unlink ($local_path . $fn_t); // !!!
if ((@move_uploaded_file ($_FILES [$filevarname]['tmp_name'], $local_path . $fn_i)))
{
$imgw = 0+config_var ($maxdim_config.'img_maxw');
$imgh = 0+config_var ($maxdim_config.'img_maxh');
if (!$imgw)
$imgw = 0+config_var ($maxdim_config.'img_maxw');
if (!$imgh)
$imgh = 0+config_var ($maxdim_config.'img_maxh');
if ($imgw <= 0)
$imgw = false;
if ($imgh <= 0)
@ -502,37 +458,55 @@ function load_file ($filevarname, $newfilename)
return $ret;
}
function file_size ($filename)
function mkpath($path, $throw_error = true)
{
if (is_dir($path) || @mkdir($path, 0777, true))
{
return true;
}
if ($throw_error)
{
$error = error_get_last();
throw new Exception("Failed to create path $path: ".$error['message']);
}
return false;
}
function file_size($filename)
{
global $local_path;
$r = false;
if ($filename{0} == '/' &&
file_exists($local_path . $filename))
$r = filesize ($local_path . $filename);
else if (str_starts_with ($filename, 'http://') ||
str_starts_with ($filename, 'ftp://'))
{
$a = @get_headers ($filename, 1);
if (isset ($a ['Content-Length']))
$r = $a ['Content-Length'];
$r = filesize($local_path . $filename);
}
elseif (str_starts_with($filename, 'http://') ||
str_starts_with($filename, 'ftp://'))
{
$a = @get_headers($filename, 1);
if (isset($a['Content-Length']))
{
$r = $a['Content-Length'];
}
}
return $r;
}
function file_size_string ($bytes)
function file_size_string($bytes)
{
$r = $bytes;
if (is_numeric ($r))
{
if ($r >= 0 && $r < 1024)
$r = $r . ' байт';
else if ($r >= 1024 && $r < 1024*1024)
elseif ($r >= 1024 && $r < 1024*1024)
$r = sprintf ('%.2f Кб', $r/1024);
else if ($r >= 1024*1024 && $r < 1024*1024*1024)
elseif ($r >= 1024*1024 && $r < 1024*1024*1024)
$r = sprintf ('%.2f Мб', $r/1024/1024);
else if ($r >= 1024*1024*1024)
elseif ($r >= 1024*1024*1024)
$r = sprintf ('%.2f Гб', $r/1024/1024/1024);
else if ($r < 0)
elseif ($r < 0)
$r = sprintf ('%.2f Гб', 2-($r/1024/1024/1024));
}
return $r;
@ -543,113 +517,109 @@ function file_size_string ($bytes)
* HTTP аутентификация с логином и паролем, указанными в $userpwd как login:password.
* Если данные являются gzip-сжатыми - они будут автоматически распакованы.
*/
function http_get_contents ($url, $userpwd = false)
function curl_get_contents($url, $options = array())
{
global $local_path, $cache_path;
$ch = curl_init();
curl_setopt ($ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_HEADER, 0);
curl_setopt ($ch, CURLOPT_USERAGENT, 'CommercialRealty/1.0');
mt_srand ((float)microtime() * 1000000);
$eh = $local_path . $cache_path . '/php-' . md5(mt_rand(0,mt_getrandmax())) . '.gz';
$fd = fopen ($eh, 'wb');
curl_setopt ($ch, CURLOPT_FILE, $fd);
if (!empty ($userpwd))
curl_setopt ($ch, CURLOPT_USERPWD, $userpwd);
@curl_exec ($ch);
curl_close ($ch);
fclose ($fd);
ob_start ();
readgzfile ($eh);
$data = ob_get_clean ();
unlink ($eh);
curl_setopt_array($ch, $options+array(
CURLOPT_URL => $url,
CURLOPT_ENCODING => 'gzip,deflate',
CURLOPT_RETURNTRANSFER => 1,
));
$data = @curl_exec($ch);
curl_close($ch);
return $data;
}
/* Преобразовать имена некоторых строк в ID некоторых строк */
/* Получить некоторые строки по именам с автоматическим добавлением,
если $add_unknown=true (например, теги) */
function get_tags($tags, $add_unknown = true, $table = 'tags', $id_field = 'id', $name_field = 'name')
{
$tagids = array_hashtree(mysql_select($table, "$id_field, LOWER($name_field) $name_field", array($name_field => $tags)), $name_field);
global $mysql, $_TagCache;
if (!$tags)
return $tags;
$unk = array();
foreach ($tags as $t)
if (!$tagids[mb_strtolower($t, 'utf-8')])
$unk[] = array($name_field => $t);
{
$t = mb_strtolower($t, 'utf-8');
if (!$_TagCache[$t])
$unk[] = $t;
}
if ($unk)
{
$tagids = array_hashtree($mysql->select($table, "$id_field, LOWER($name_field) $name_field", array($name_field => $unk)), $name_field);
$unk = array();
foreach ($tags as $t)
{
if (!$tagids[mb_strtolower($t, 'utf-8')])
$unk[] = array($name_field => $t);
}
}
if ($unk && $add_unknown)
{
mysql_update($table, $unk);
$tagids = array_hashtree(mysql_select($table, "$id_field, LOWER($name_field) $name_field", array($name_field => $tags)), $name_field);
$mysql->update($table, $unk);
$tagids = array_hashtree($mysql->select($table, "$id_field, LOWER($name_field) $name_field", array($name_field => $tags)), $name_field);
}
foreach ($tags as &$t)
if ($tt = $tagids[mb_strtolower($t, 'utf-8')])
{
$t = mb_strtolower($t, 'utf-8');
if ($_TagCache[$t])
$t = $_TagCache[$t];
elseif ($tt = $tagids[$t])
{
$_TagCache[$t] = $tt[0];
$t = $tt[0];
}
}
return $tags;
}
/**
* Функция возвращает ID значения $v поля $stringfield (по умолчанию поля name), при дополнительных
* требованиях $adds = array (поле => значение, ...) и, если такого значения нету, добавляет его в БД.
*
* Функция работает так:
* - если $v пусто (empty($v)==true) - функция возвращает 0 (ибо нефиг);
* - ищет, есть ли заданное значение там, где удовлетворены все требования, если есть - всё ок, возвращает его ID;
* - по одному с конца ослабляет требования, и если находит соответствующую строку - редактирует её таким образом,
* чтобы она соответствовала всем заданным требованиям, и возвращает её ID;
* - если такая строка не найдена - добавляет её и возвращает её ID.
* - если произошла какая-либо ошибка, она возвращает 0.
* - если в найденной записи поле stop != 0 => функция возвращает 0.
* полезно, если мы хотим "обучать" функцию не совершать ошибки повторно.
*/
function stringtable_get_id ($v, $table, $adds, $stringfield = 'name')
function site_setcookie($name, $value, $expire, $secure = false, $httponly = false)
{
$rid = 0;
if (!empty ($v))
{
$v = mysql_ecranize ($v);
$addset = array ();
if (is_array ($adds))
foreach ($adds as $pk => $pf)
$addset [] = "`$pk`=" . mysql_ecranize($pf);
$search = array_merge (array ("`$stringfield`=$v"), $addset);
$change = array ();
while (count ($search) > 0)
{
if (($rid = 0+amquery1x1 ("SELECT `id` FROM `$table` WHERE " . implode (' AND ', $search) . ' LIMIT 1')) > 0)
{
if (!empty ($change))
amysql_query ("UPDATE `$table` SET " . implode (', ', $change) . " WHERE `id`=$rid");
break;
}
$change [] = array_pop ($search);
}
if (!$rid && amysql_query ("INSERT INTO `$table` SET " . implode (', ', $change)))
{
$rid = mysql_insert_id ();
if (!$rid)
$rid = 0+amquery1x1 ("SELECT id FROM `$table` WHERE " . implode (' AND ', $change) . ' LIMIT 1');
}
if ($rid && acmget ($table, $rid, 'stop') > 0)
$rid = 0;
}
return $rid;
global $Domain;
list($dom, $path) = explode('/', $Domain, 2);
$path = "/$path";
return setcookie($name, $value, $expire ? time()+$expire : 0, $path, '.'.$dom, $secure, $httponly);
}
/**
* Начало сессии :)
* @param boolean $always Если true, то начинать сессию всегда, иначе - только если она существует
* @param int $lifetime Необязательное время жизни сессии
*/
function session_begin($lifetime = -1)
function session_begin($always = true, $lifetime = NULL)
{
global $Domain, $LoginSession;
session_name($LoginSession);
list($dom, $path) = explode('/', $Domain, 2);
$path = "/$path";
session_set_cookie_params($lifetime, $path, '.' . $dom);
@session_start();
if (isset($_COOKIE[$LoginSession]) || $always)
{
if ($lifetime === NULL)
{
$lifetime = 0;
}
session_name($LoginSession);
list($dom, $path) = explode('/', $Domain, 2);
$path = "/$path";
$dom = (strtolower($dom) == 'localhost' ? '' : ".$dom");
session_set_cookie_params($lifetime, $path, $dom);
return @session_start();
}
return false;
}
function session_terminate()
{
$_SESSION = array();
$params = session_get_cookie_params();
if (session_begin())
{
session_destroy();
}
setcookie(
session_name(), '', -1, $params['path'], $params['domain'],
$params['secure'], $params['httponly']
);
}
/**
@ -659,21 +629,22 @@ function session_begin($lifetime = -1)
* Функция предназначена для присваивания блоков <select>...</select>
* и присвоит "выделено" той опции, которая задана ID-ом $sel_id
*/
function option_block ($sql, $arr, $sel_id = NULL)
function option_block($sql, $arr, $sel_id = NULL)
{
global $mysql;
$vars = array();
if (is_array ($arr))
foreach ($arr as $id => $name)
$vars[] = array (
$vars[] = array(
'ID' => $id,
'NAME' => $name,
'SEL' => !is_null($sel_id) && $id == $sel_id ? 'selected' : '',
);
if (is_string($sql) && ($rows = mysql_get_rows($sql)))
if (is_string($sql) && ($rows = $mysql->get_rows($sql)))
{
foreach ($rows as $row)
{
$vars[] = array (
$vars[] = array(
'ID' => $row[0],
'NAME' => $row[1],
'SEL' => !is_null($sel_id) && $row[0] == $sel_id ? 'selected' : '',
@ -686,16 +657,15 @@ function option_block ($sql, $arr, $sel_id = NULL)
/**
* Данные постраничной навигации внутри $allcount элементов по
* $perpage на странице, при том условии что текущий первый отображённый - это $start.
* Ссылка на страницу будет getURL($component, $args+array(page => НОМЕР_СТРАНИЦЫ)).
* Ссылка на страницу будет App::url($component, $args+array(page => НОМЕР_СТРАНИЦЫ)).
* Если задано $eps, то отобразится не более 2*$eps соседних страниц.
*/
function page_nav3 ($total, $perpage, $start, $component, $args = array(), $eps = NULL, $metanext = 'METAS', $pagearg = 'page')
function page_nav3($total, $perpage, $curpage, $component, $args = array(), $eps = NULL, $metanext = 'METAS', $pagearg = 'page')
{
if ($perpage <= 0 && $total > 0)
$perpage = $total;
if ($perpage <= 0)
$perpage = 1;
$curpage = floor($start / $perpage);
$pagecount = ceil($total / $perpage);
if ($curpage >= $pagecount)
$curpage = $pagecount-1;
@ -705,21 +675,23 @@ function page_nav3 ($total, $perpage, $start, $component, $args = array(), $eps
$vars = array (
'total' => $total,
'perpage' => $perpage,
'list_start' => $start+1,
'list_start' => min($total, $curpage*$perpage+1),
'page_count' => $pagecount,
'list_end' => min($total, $start+$perpage),
'list_end' => min($total, $curpage*$perpage+$perpage),
'page' => array(),
'current_page' => $curpage+1,
);
if ($pagecount < 2)
{
return $vars;
}
if ($curpage > 0)
{
$args[$pagearg] = $curpage-1;
$vars['page'][] = array (
'name' => '<',
'href' => getURL($component, $args),
'href' => App::url($component, $args),
'nc' => true,
);
}
@ -727,13 +699,13 @@ function page_nav3 ($total, $perpage, $start, $component, $args = array(), $eps
$eps = config_var('page_nav_eps', 10);
if ($eps && $eps > 0)
{
$ppend = min ($pagecount, $curpage + $eps + 1);
for ($i = max (0, $curpage - $eps); $i < $ppend; $i++)
$ppend = min($pagecount, $curpage + $eps + 1);
for ($i = max(0, $curpage - $eps); $i < $ppend; $i++)
{
$args[$pagearg] = $i;
$vars['page'][] = array (
$vars['page'][] = array(
'name' => $i+1,
'href' => getURL($component, $args),
'href' => App::url($component, $args),
'nc' => $i != $curpage,
);
}
@ -743,9 +715,9 @@ function page_nav3 ($total, $perpage, $start, $component, $args = array(), $eps
for ($i = 0; $i < $pagecount; $i++)
{
$args[$pagearg] = $i;
$vars['page'][] = array (
$vars['page'][] = array(
'name' => $i+1,
'href' => getURL($component, $args),
'href' => App::url($component, $args),
'nc' => $i != $curpage,
);
}
@ -756,43 +728,17 @@ function page_nav3 ($total, $perpage, $start, $component, $args = array(), $eps
if ($metanext)
{
global $template;
$template->tpldata[$metanext] .= '<link rel="next" title="Следующая страница" href="'.getURL($component, $args).'"/>';
$template->tpldata[$metanext] .= '<link rel="next" title="Следующая страница" href="'.App::url($component, $args).'"/>';
}
$vars['page'][] = array (
$vars['page'][] = array(
'name' => '>',
'href' => getURL($component, $args),
'href' => App::url($component, $args),
'nc' => true,
);
}
return $vars;
}
function php2js($a)
{
if (is_null($a)) return 'null';
if ($a === false) return 'false';
if ($a === true) return 'true';
if (is_scalar($a)) {
$a = addslashes($a);
$a = str_replace("\n", '\n', $a);
$a = str_replace("\r", '\r', $a);
$a = preg_replace('{(</)(script)}i', "$1'+'$2", $a);
return "'$a'";
}
$isList = true;
for ($i=0, reset($a); $i<count($a); $i++, next($a))
if (key($a) !== $i) { $isList = false; break; }
$result = array();
if ($isList) {
foreach ($a as $v) $result[] = php2js($v);
return '[ ' . join(', ', $result) . ' ]';
} else {
foreach ($a as $k=>$v)
$result[] = php2js($k) . ': ' . php2js($v);
return '{ ' . join(', ', $result) . ' }';
}
}
if (!function_exists("quoted_printable_encode"))
{
define('QP_LINE_LENGTH', 75);
@ -862,11 +808,6 @@ if (!function_exists("quoted_printable_encode"))
}
}
function is_intval($a)
{
return (is_string($a) || is_int($a)) && !strcmp(intval($a), $a);
}
function litsplit($re, $s)
{
$r = array();
@ -932,3 +873,113 @@ function extract_video_frame($from, $time, $w, $h, $to)
scaleimage($to, $to, $w, $h);
return true;
}
function array_id_name($assoc)
{
$r = array();
foreach($assoc as $k => $v)
$r[] = array('id' => $k, 'name' => $v);
return $r;
}
function urandom($nbytes = 16, $hex = false)
{
$pr_bits = '';
// Unix/Linux platform?
$fp = @fopen('/dev/urandom', 'rb');
if ($fp !== FALSE)
{
$pr_bits = @fread($fp, $nbytes);
@fclose($fp);
}
// MS-Windows platform?
elseif (@class_exists('COM'))
{
// http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
try
{
$com = new COM('CAPICOM.Utilities.1');
if (method_exists($com, 'GetRandom'))
$pr_bits = base64_decode($com->GetRandom($nbytes,0));
else
{
$com = new COM('System.Security.Cryptography.RNGCryptoServiceProvider');
if (method_exists($com, 'GetBytes'))
$pr_bits = base64_decode($com->GetBytes($nbytes));
}
}
catch (Exception $ex)
{
}
}
if (!strlen($pr_bits))
for ($i = 0; $i < $nbytes; $i++)
$pr_bits .= chr(mt_rand(0, 255));
if ($hex)
{
$pr_bits = unpack('H*', $pr_bits);
$pr_bits = $pr_bits[1];
}
return $pr_bits;
}
// Convert all UTF8 characters except ASCII and &"'<>
// to HTML entities (&amp; &quot; &apos; &lt; &gt; &#xHEX;)
function htmlentities_utf8($s)
{
$s = unpack('C*', mb_convert_encoding($s, 'UTF-16BE', 'UTF-8'));
$r = '';
for ($i = 1, $n = count($s); $i <= $n; $i += 2)
{
if (!$s[$i])
{
if ($s[$i+1] == 0x26)
$r .= '&amp;';
elseif ($s[$i+1] == 0x22)
$r .= '&quot;';
elseif ($s[$i+1] == 0x27)
$r .= '&apos;';
elseif ($s[$i+1] == 0x3C)
$r .= '&lt;';
elseif ($s[$i+1] == 0x3E)
$r .= '&gt;';
else
$r .= chr($s[$i+1]);
}
else
$r .= sprintf("&#x%x;", ($s[$i]<<8) + ($s[$i+1]));
}
return $r;
}
function load_config()
{
global $mysql, $engine_configuration, $mysqlNoSiteConfig, $mysqlSiteConfigQuery;
if ($mysqlNoSiteConfig)
return;
$engine_configuration = array();
if ($mysqlSiteConfigQuery)
$rows = $mysql->select($mysqlSiteConfigQuery, MS_LIST);
else
$rows = $mysql->select('config', array('name', 'value'), 1, array(), MS_LIST);
foreach ($rows as $row)
$engine_configuration[$row[0]] = $row[1];
}
function is_intval($v)
{
return is_int($v) || is_string($v) && ctype_digit($v);
}
function config_var($name, $default = NULL)
{
global $engine_configuration;
if (!$engine_configuration)
load_config();
$v = @$engine_configuration[$name];
if (!is_null($default) && !strlen($v) || is_intval($default) && $v <= 0)
$v = $default;
elseif (is_intval($default))
$v = intval($v);
return $v;
}

View File

@ -1,521 +0,0 @@
<?php
# Утилитные функции работы с MySQL
# (c) VitaliF 2006-2010
require_once 'config.php';
define('MS_HASH', 0);
define('MS_LIST', 1);
define('MS_ROW', 2);
define('MS_ONE', 2);
define('MS_COL', 4);
define('MS_VALUE', 6);
define('MS_RESULT', 8);
$mysql_db_stack = array();
$mds_count = 0;
function mysql_start($allow_nodb = false)
{
global $mysql_primary_link, $dbhost, $dbuser, $dbpwd, $db;
if ($mysql_primary_link) // уже соединено
return true;
// Соединяемся с MySQL
$mysql_primary_link = mysql_pconnect ($dbhost, $dbuser, $dbpwd);
if (!$mysql_primary_link)
die ('Could not connect to the database: ' . mysql_errno() . ": " . mysql_error());
if (!mysql_usedb($db) && !$allow_nodb)
return false;
amysql_query("SET NAMES utf8");
return true;
}
function mysql_usedb($dbn)
{
global $mysql_db_stack, $mysql_primary_link;
$r = true;
if (!count($mysql_db_stack) || $mysql_db_stack[count($mysql_db_stack)-1] != $dbn)
$r = mysql_select_db ($dbn, $mysql_primary_link);
$mysql_db_stack[] = $dbn;
return $r;
}
function mysql_unusedb()
{
global $mysql_db_stack, $mysql_primary_link;
if (($n = count($mysql_db_stack)) < 1)
return false;
$pop = array_pop($mysql_db_stack);
if ($n > 1 && $pop != $mysql_db_stack[$n-2])
return mysql_select_db($mysql_db_stack[$n-2], $mysql_primary_link);
return false;
}
function mysql_started()
{
global $mysql_primary_link;
return is_resource($mysql_primary_link);
}
function amysql_query($q)
{
global $amy_query_count, $mysql_primary_link, $mysqlQueryLogFile, $mysqlThrowError;
if (!mysql_started())
mysql_start(false);
if (!isset($amy_query_count))
$amy_query_count = 0;
$amy_query_count++;
if ($mysqlQueryLogFile && ($fp = fopen($mysqlQueryLogFile, "ab")))
$start = microtime(true);
$result = mysql_query($q, $mysql_primary_link);
if (!$result && $mysqlThrowError)
{
debug_print_backtrace();
die(mysql_errno().': '.mysql_error());
}
if ($fp)
{
$t = microtime(true)-$start;
fwrite($fp, date("[Y-m-d H:i:s] ").sprintf("%.3f sec ", $t).$q.(mysql_errno() ? ': ERROR! '.mysql_errno().': '.mysql_error() : '')."\n");
fclose($fp);
}
return $result;
}
function amysql_queries($qs, $ignore_errors = true)
{
$r = true;
$qs = explode(';', $qs);
foreach ($qs as $q)
{
$q = trim($q);
if ($q != '' && !amysql_query($q))
{
$r = false;
if (!$ignore_errors)
break;
}
}
return $r;
}
function amquery1x1($s)
{
if (($r = amysql_query($s)) && ($r = mysql_fetch_row($r)))
return $r[0];
return false;
}
function mysql_ecranize($s)
{
global $mysql_primary_link;
if (!$mysql_primary_link)
mysql_start();
if (is_null($s))
return "NULL";
return "'" . mysql_real_escape_string($s) . "'";
}
function mysql_time($dat, $tim, $format)
{
return @date($format, mktime (substr ($tim, 0, 2), substr ($tim, 3, 2), substr ($tim, 6, 2), substr($dat,5,2), substr($dat,8,2), substr($dat,0,4)));
}
// Дополнить кэш таблицы $table с помощью запроса $q
function cmcache($table, $q)
{
global $mysql_php_cache;
$cnt = 0;
$r = amysql_query($q);
if ($r)
{
while ($row = mysql_fetch_assoc ($r))
{
$mysql_php_cache [$table][$row['id']] = $row;
$cnt++;
}
}
return $cnt;
}
// Функция кэширующего чтения поля field из записи номер id таблицы table: "table/id/field"
// Кэш можно заполнять предыдущей функцией, хотя эта и сама может кэшировать записи по одной
function acmget($table, $id, $field)
{
global $mysql_php_cache, $cfgTables;
if ($cfgTables[$table])
$table = $cfgTables[$table];
if (!isset ($mysql_php_cache[$table][$id][$field]) &&
cmcache ($table, "SELECT * FROM `$table` WHERE `id`=$id LIMIT 1") < 1)
return false;
return $mysql_php_cache[$table][$id][$field];
}
function cmchainmatch($table, $parent_field, $start, $field, $value)
{
while (($new = acmget($table, $start, $field)) != $value && $start > 0)
$start = $new;
if (acmget($table, $start, $field) == $value)
return true;
return false;
}
function mysql_where_builder($where)
{
if (!is_array($where))
return $where;
$wh = array();
foreach ($where as $k => $v)
{
if (is_numeric($k))
$wh[] = $v;
else if (is_array($v))
{
if (!$v)
{
debug_print_backtrace();
die("Error: empty array for '$k IN (...)', don't know what to do");
}
else
{
if (is_array($v[0]))
foreach ($v as &$l)
$l = "(" . implode(",", array_map('mysql_ecranize', $l)) . ")";
else
$v = array_map('mysql_ecranize', $v);
$wh[] = "$k IN (" . implode(",", $v) . ")";
}
}
elseif (!is_null($v))
$wh[] = "$k=".mysql_ecranize($v);
else
$wh[] = "$k IS NULL";
}
if (!$wh)
$where = '1';
else
$where = '(' . join(') AND (', $wh) . ')';
return $where;
}
function mysql_select_builder($tables, $fields, $where, $options = NULL)
{
if (!$options)
$options = array();
else
{
foreach ($options as $k => $v)
if (is_intval($k))
$options[$v] = true;
}
if (is_array($fields))
$fields = join(',', $fields);
$where = mysql_where_builder($where);
$tables = mysql_tables_builder($tables);
$sql = 'SELECT ';
if ($options['CALC_FOUND_ROWS'] || $options['SQL_CALC_FOUND_ROWS'])
$sql .= 'SQL_CALC_FOUND_ROWS ';
if ($options['NO_CACHE'] || $options['SQL_NO_CACHE'])
$sql .= 'SQL_NO_CACHE ';
elseif ($options['CACHE'] || $options['SQL_CACHE'])
$sql .= 'SQL_CACHE ';
$sql .= "$fields FROM $tables WHERE $where";
if ($g = $options['GROUP BY'])
{
if (is_array($g))
{
$g1 = array();
foreach ($g as $k => $v)
{
if (is_numeric($k))
$g1[] = $v;
else
$g1[] = "$k $v";
}
$g = join(',', $g1);
}
$sql .= " GROUP BY $g";
}
if ($g = $options['ORDER BY'])
{
if (is_array($g))
{
$g1 = array();
foreach ($g as $k => $v)
{
if (is_numeric($k))
$g1[] = $v;
else
$g1[] = "$k $v";
}
$g = join(',', $g1);
}
$sql .= " ORDER BY $g";
}
if ($g = $options['LIMIT'])
{
if (is_array($g))
$g = join(',', $g);
if ($g)
$sql .= " LIMIT $g";
}
if ($options['FOR UPDATE'])
$sql .= ' FOR UPDATE';
elseif ($options['LOCK IN SHARE MODE'])
$sql .= ' LOCK IN SHARE MODE';
return $sql;
}
function mysql_tables_builder($tables)
{
global $cfgTables;
if (is_array($tables))
{
$t = '';
foreach ($tables as $k => $v)
{
if (!is_numeric($k))
{
if (is_array($v))
{
$join = substr(strtolower($v[0]), 0, 4);
if ($join == 'righ')
$join = 'RIGHT';
elseif ($join == 'left')
$join = 'LEFT';
else /* if (!$join || $join == 'inne' || $join == 'join') */
$join = 'INNER';
if (is_array($v[1])) // nested join (left join (A join B on ...) on ...)
$table = '('.mysql_tables_builder($v[1]).')';
else
$table = ($cfgTables[$v[1]] ? '`'.$cfgTables[$v[1]].'`' : $v[1]) . ' ' . $k;
if ($t)
$t .= " $join JOIN $table ON ".mysql_where_builder($v[2]);
else
$t = $table;
continue;
}
else
$v = ($cfgTables[$v] ? '`'.$cfgTables[$v].'`' : $v) . ' ' . $k;
}
else
$v = ($cfgTables[$v] ? '`'.$cfgTables[$v].'` '.$v : $v);
if ($t)
$t .= " INNER JOIN $v";
else
$t = $v;
}
$tables = $t;
}
else
$tables = preg_replace_callback('/((?:,|JOIN)\s*`)([^`]+)/s', 'mysql_tables_builder_pregcb1', $tables);
return $tables;
}
function mysql_tables_builder_pregcb1($m)
{
global $cfgTables;
$t = $cfgTables[$m[2]];
if (!$t)
$t = $m[2];
return $m[1].$t;
}
function mysql_select($tables, $fields = '*', $where = 1, $options = NULL, $format = 0)
{
if (!strcmp(intval($fields), "$fields"))
{
$sql = $tables;
$format = $fields;
}
else
$sql = mysql_select_builder($tables, $fields, $where, $options);
if ($format & MS_RESULT)
return amysql_query($sql);
if ($format & MS_LIST)
$r = mysql_get_rows($sql);
else
$r = mysql_get_assocs($sql);
if (!$r)
$r = array();
if ($format & MS_ROW)
{
if ($format & MS_COL)
{
if (!count($r))
return NULL;
$r = $r[0];
$k = array_keys($r);
$r = $r[$k[0]];
}
else
$r = $r[0];
}
elseif ($format & MS_COL)
{
foreach ($r as $i => $v)
{
if (!$k)
{
$k = array_keys($v);
$k = $k[0];
}
$r[$i] = $v[$k];
}
}
return $r;
}
function mysql_found_rows()
{
return mysql_select('SELECT FOUND_ROWS()', MS_VALUE);
}
function mysql_delete($tables, $where, $options = NULL)
{
global $mysql_primary_link;
$tables = mysql_tables_builder($tables);
$where = mysql_where_builder($where);
$sql = "DELETE FROM $tables WHERE $where";
if (is_array($options))
{
if ($g = $options['LIMIT'])
{
if (is_array($g))
$g = join(',', $g);
if ($g)
$sql .= " LIMIT $g";
}
}
amysql_query($sql);
return mysql_affected_rows($mysql_primary_link);
}
function mysql_insert_builder($table, $rows, $onduplicatekey = false)
{
global $cfgTables;
if ($cfgTables[$table])
$table = $cfgTables[$table];
$key = array_keys($rows[0]);
foreach ($rows as &$r)
{
$rs = array();
foreach ($key as &$k)
$rs[] = mysql_ecranize($r[$k]);
$r = "(".implode(",", $rs).")";
}
foreach ($key as &$k)
if (strpos($k, '`') === false)
$k = "`$k`";
$sql = "INSERT INTO $table (".implode(",",$key).") VALUES ".implode(",",$rows);
if ($onduplicatekey)
{
foreach ($key as &$k)
$k = "$k=VALUES($k)";
$sql .= " ON DUPLICATE KEY UPDATE ".implode(",",$key);
}
return $sql;
}
function mysql_insert_row($table, $row)
{
global $mysql_primary_link;
$sql = mysql_insert_builder($table, array($row));
if (amysql_query($sql))
return mysql_insert_id($mysql_primary_link);
return NULL;
}
function mysql_update($table, $rows, $where = NULL, $options = NULL)
{
global $mysql_primary_link;
if (!$rows)
return false;
if (is_null($where))
{
if (!is_array($rows))
return false;
if (!is_array($rows[0]))
$rows = array($rows);
$sql = mysql_insert_builder($table, $rows, true);
}
else
{
$sql = array();
if (!is_array($rows))
$rows = array($rows);
foreach ($rows as $k => $v)
{
if (!is_intval($k))
$sql[] = $k.'='.mysql_ecranize($v);
else
$sql[] = $v;
}
$where = mysql_where_builder($where);
$sql = 'UPDATE ' . mysql_tables_builder($table) . ' SET ' . implode(', ', $sql) . ' WHERE ' . $where;
if (is_array($options))
{
if ($g = $options['LIMIT'])
{
if (is_array($g))
$g = join(',', $g);
if ($g)
$sql .= " LIMIT $g";
}
}
}
if (amysql_query($sql))
return mysql_affected_rows($mysql_primary_link);
return false;
}
function mysql_get_rows($sql)
{
$r = array();
if (is_resource($res = $sql) || ($res = amysql_query ($sql)))
while ($row = mysql_fetch_row ($res))
$r[] = $row;
return $r;
}
function mysql_get_assocs($sql)
{
$r = array();
if (is_resource($res = $sql) || ($res = amysql_query ($sql)))
while ($row = mysql_fetch_assoc ($res))
$r[] = $row;
return $r;
}
function load_config()
{
global $engine_configuration, $mysqlNoSiteConfig;
if ($mysqlNoSiteConfig)
return;
$engine_configuration = array();
if ($rows = mysql_select('config', array('name', 'value'), 1, array(), MS_LIST))
foreach ($rows as $row)
$engine_configuration[$row[0]] = $row[1];
}
function config_var($name, $default = NULL)
{
global $engine_configuration;
if (!$engine_configuration)
load_config();
$v = @$engine_configuration[$name];
if (!is_null($default) && !strlen($v) || is_intval($default) && $v <= 0)
$v = $default;
elseif (is_intval($default))
$v = intval($v);
return $v;
}
function mysql_finish()
{
global $mysql_primary_link;
if (mysql_started())
mysql_close($mysql_primary_link);
$mysql_primary_link = NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@
require_once 'config.php';
require_once 'template.php';
require_once 'mysql.php';
if (!defined('LIGHTWEIGHT'))
{
@ -17,10 +16,11 @@ if (!defined('LIGHTWEIGHT'))
$tpl = config_var('template');
if ($tpl == '')
$tpl = 'default';
$template = new Template (array(
$template = new VMXTemplate (array(
'root' => $local_path . $template_path . $tpl,
'cache_dir' => $local_path . $cache_path,
'print_error' => 1,
'strip_space' => true,
));
$template->vars(array(
'DOMAIN' => "http://$Domain",

103
olap.php
View File

@ -10,6 +10,7 @@ require_once 'lib/templating.php';
require_once 'olap_config.php';
$modified = false;
foreach ($_REQUEST as $k => $v)
{
if ($v === "")
@ -35,6 +36,18 @@ class OLAP
{
static $initialised;
static $sources;
static $mwTimestampSql = array(
'year' => 'SUBSTR($,1,4)',
'ym' => 'CONCAT(SUBSTR($,1,4),"-",SUBSTR($,5,2))',
'month' => 'SUBSTR($,5,2)',
'ymd' => 'CONCAT(SUBSTR($,1,4),"-",SUBSTR($,5,2),"-",SUBSTR($,7,2))',
'day' => 'SUBSTR($,7,2)',
'weekday' => 'WEEKDAY(CONCAT(SUBSTR($,1,4),"-",SUBSTR($,5,2),"-",SUBSTR($,7,2)))',
'week' => 'WEEK(CONCAT(SUBSTR($,1,4),"-",SUBSTR($,5,2),"-",SUBSTR($,7,2)))',
'hour' => 'SUBSTR($,9,2)',
'minute' => 'SUBSTR($,11,2)',
'second' => 'SUBSTR($,13,2)',
);
static $functions = array(
'year' => array('name' => 'Год', 'time_format' => 'Y', 'sql' => 'SUBSTR($,1,4)'),
'ym' => array('name' => 'Год-месяц', 'time_format' => 'Y-m', 'sql' => 'SUBSTR($,1,7)'),
@ -94,6 +107,7 @@ class OLAP
if (empty($s['where']))
$s['where'] = array();
}
$i = 0;
foreach (self::$group_types as $k => $v)
self::$group_types[$k] = array('id' => $k, 'num' => $i++, 'name' => $v);
foreach (self::$functions as $k => &$v)
@ -103,22 +117,28 @@ class OLAP
self::$initialised = true;
}
function mysql()
{
static $mysql;
if (!$mysql)
{
global $db;
$mysql = new DatabaseMysql(!empty($this->current_src['db']) ? $this->current_src['db'] : $db);
}
return $mysql;
}
function parse_request()
{
$request = &$this->request;
// выбираем источник данных
$this->current_srcid = $request['datasource'];
$this->current_src = self::$sources[$this->current_srcid];
$this->current_srcid = @$request['datasource'];
$this->current_src = @self::$sources[$this->current_srcid];
if (!$this->current_src)
return;
// задаём параметры соединения с БД из источника
global $db, $dbhost, $dbuser, $dbpwd;
if (!empty($this->current_src['db']))
list($db, $dbhost, $dbuser, $dbpwd) = $this->current_src['db'];
// Дополняем всяким бредом всякий бред
// дополняем всяким бредом всякий бред
foreach ($this->current_src['fielddescs'] as $k => &$v)
{
$v['id'] = $k;
@ -127,7 +147,7 @@ class OLAP
$f = $v['sql'];
if (!$f)
$f = $k;
$v['options'] = mysql_select(
$v['options'] = $this->mysql()->select(
$this->current_src['tables'],
"$f id, $f name",
$this->current_src['where'],
@ -148,7 +168,7 @@ class OLAP
$this->where = array();
$this->where_nosql = array();
$this->group_fields = array();
if ($request['graph_log'])
if (!empty($request['graph_log']))
{
$this->graph_logarithmic = true;
$this->request['graph_log'] = 1;
@ -179,7 +199,7 @@ class OLAP
if (empty($fd['nosql']))
{
$dn = !empty($fd['sql']) ? $fd['sql'] : $f;
$ve = mysql_ecranize($v);
$ve = $this->mysql()->quote($v);
if ($t == 'eq')
{
if ($fd['sql_eq'])
@ -199,8 +219,8 @@ class OLAP
$i = substr($k, 11);
$gf = array();
foreach(array('type', 'field', 'func', 'sort_dir', 'sort_field', 'sort_aggr', 'sort_func') as $k)
$gf[$k] = $request["group-$k-$i"];
if ($request['csv'] && $gf['type'] == 'graph')
$gf[$k] = @$request["group-$k-$i"];
if (!empty($request['csv']) && $gf['type'] == 'graph')
$gf['type'] = 'tr';
if (self::$group_types[$gf['type']] && $gf['field'] !== '')
{
@ -213,7 +233,7 @@ class OLAP
usort($this->group_fields, 'OLAP::group_type_cmp');
// Число мест под поля группировки
$this->nfields = intval($request['field_count']);
$this->nfields = intval(@$request['field_count']);
if ($this->nfields <= 0)
$this->nfields = 5;
if (count($this->group_fields) > $this->nfields)
@ -221,10 +241,10 @@ class OLAP
// Описание показателя
foreach(array('field', 'func', 'aggr') as $x)
$this->cell[$x] = $request["cell_$x"];
$this->cell['field_name'] = $this->current_src['fielddescs'][$this->cell['field']]['name'];
$this->cell['func_name'] = self::$functions[$this->cell['func']]['name'];
$this->cell['aggr_name'] = self::$aggregates[$this->cell['aggr']]['name'];
$this->cell[$x] = @$request["cell_$x"];
$this->cell['field_name'] = isset($this->cell['field']) ? $this->current_src['fielddescs'][$this->cell['field']]['name'] : NULL;
$this->cell['func_name'] = isset($this->cell['func']) ? self::$functions[$this->cell['func']]['name'] : NULL;
$this->cell['aggr_name'] = isset($this->cell['aggr']) ? self::$aggregates[$this->cell['aggr']]['name'] : NULL;
$this->cell['value_desc'] = $this->cell['field_name'];
if ($this->cell['func_name'])
$this->cell['value_desc'] = $this->cell['func_name'].'('.$this->cell['value_desc'].')';
@ -269,7 +289,7 @@ class OLAP
'field_count' => $this->nfields,
'srcid' => $this->current_srcid,
'src' => $this->current_src,
'fielddescs' => $this->current_src['fielddescs'],
'fielddescs' => array_values($this->current_src['fielddescs']),
'group_types' => array_values(self::$group_types),
'functions' => array_values(self::$functions),
'aggregates' => array_values(self::$aggregates),
@ -379,7 +399,7 @@ class OLAP
$sql = $this->sql_trans_field($field, $func);
$fields[] = str_replace('$', $sql, self::$aggregates[$aggr]['sql']).' c';
$code = 'while($r = mysql_fetch_assoc($result)) { ';
$code = 'while($r = $result->fetch_assoc()) { ';
foreach ($output as $k => $o)
{
$asgn = '$output['.intval($k).'][\'output\']';
@ -389,7 +409,7 @@ class OLAP
}
$code .= '}';
$result = mysql_select(
$result = $this->mysql()->select(
$this->current_src['tables'], $fields,
array_merge($this->current_src['where'], $this->where),
array('GROUP BY' => $group_by),
@ -433,12 +453,12 @@ class OLAP
// Важно, что функция потоковая, чтобы память не хавала
// В начале и конце дёргается с NULL'ом вместо строки:
// В начале типа "конструктора", в конце типа "выплюнуть запомненное"
$code = $call.'(NULL, $this); while($row = mysql_fetch_assoc($result)) { foreach('.
$code = $call.'(NULL, $this); while($row = $result->fetch_assoc()) { foreach('.
$call.'($row, $this) as $r) { '.$code.'} } '.
'foreach('.$call.'(NULL, $this) as $r) { '.$code.'}';
}
else
$code = 'while($r = mysql_fetch_assoc($result)) { ' . $code . '}';
$code = 'while($r = $result->fetch_assoc()) { ' . $code . '}';
// "Завершение" вычисления агрегата
if (is_callable('self::aggr_finish_'.$aggr))
@ -485,7 +505,7 @@ class OLAP
else
{
// SQL
$result = mysql_select(
$result = $this->mysql()->select(
$this->current_src['tables'], $fields,
array_merge($this->current_src['where'], $this->where),
$this->current_src['sql_options'],
@ -574,7 +594,7 @@ class OLAP
// Масштабируем график до целых чисел с точностью $this->graph_scale
if ($gr['max'] > $gr['min'])
{
if ($gr['bi_sign'])
if (!empty($gr['bi_sign']))
{
foreach ($gr['data'] as &$d)
{
@ -843,9 +863,14 @@ class OLAP
$field = '1';
$fd = &$this->current_src['fielddescs'][$field];
$c = "\$r['$field']";
if (!empty($fd['is_time']) && $fd['format'] == Template::TS_UNIX &&
(!self::$functions[$func] || self::$functions[$func]['time_format']))
$c = "date('Y-m-d',$c)";
if (!empty($fd['is_time']) &&
!self::$functions[$func] || self::$functions[$func]['time_format'])
{
if ($fd['format'] == VMXTemplate::TS_UNIX)
$c = "date('Y-m-d', $c)";
elseif ($fd['format'] == VMXTemplate::TS_MW)
$c = "VMXTemplate::timestamp($c, VMXTemplate::TS_DB)";
}
if ($func)
$c = "self::fn_$func($c)";
return $c;
@ -859,13 +884,22 @@ class OLAP
else
{
$sql = $field;
if ($fd['sql'])
if (isset($fd['sql']))
$sql = $fd['sql'];
}
if ($fd['is_time'] && $fd['format'] == TS_UNIX &&
(!self::$functions[$func] || self::$functions[$func]['time_format']))
$sql = "FROM_UNIXTIME($sql)";
if (self::$functions[$func])
if (!empty($fd['is_time']) &&
empty(self::$functions[$func]) || self::$functions[$func]['time_format'])
{
if ($fd['format'] == VMXTemplate::TS_UNIX)
$sql = "FROM_UNIXTIME($sql)";
elseif ($fd['format'] == VMXTemplate::TS_MW)
{
if (isset(self::$mwTimestampSql[$func]))
$sql = str_replace('$', $sql, self::$mwTimestampSql[$func]);
$func = NULL;
}
}
if (isset(self::$functions[$func]))
$sql = str_replace('$', $sql, self::$functions[$func]['sql']);
return $sql;
}
@ -880,7 +914,7 @@ class OLAP
$value = number_format($value, self::$decimal[0], self::$decimal[1], self::$decimal[2]);
return $value;
}
elseif (($proc = $this->current_src['fielddescs'][$field]['format_func']) &&
elseif (($proc = @$this->current_src['fielddescs'][$field]['format_func']) &&
is_callable($proc))
{
return $proc($value, $this->is_html_format);
@ -1016,6 +1050,7 @@ class OLAP
function tpl_jsgraphs($graphs)
{
$groups = array();
$group = false;
foreach ($graphs as $i => $graph)
{
$min_ne = count($graph['desc']);

View File

@ -6,6 +6,21 @@ function v2tags_get_tags()
}
OLAP::$sources = array(
'mediawiki_history' => array(
'id' => 'mediawiki_history',
'name' => 'Ревизии MediaWiki',
'tables' => array('r' => 'revision'),
'fields' => 't.rev_timestamp',
'where' => array(),
'fielddescs' => array(
'rev_timestamp' => array(
'name' => 'Время',
'le_ge' => true,
'is_time' => true,
'format' => TS_MW,
),
),
),
'v2tags' => array(
'id' => 'v2tags',
'name' => 'VitaphotoV2, статистика по тегам',

View File

@ -5,6 +5,6 @@ AjexFileManager.init({
});
</script>-->
</div>
<p id="admpoweredby" style="color: #d0d0d0; margin: 20px 0 0 20px">Powered by VitaliF, 2010</p>
<p id="admpoweredby" style="color: #d0d0d0; margin: 20px 0 0 20px">Powered by VitaliF, 2010-2013</p>
</body>
</html>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>{title/s}</title>
<title>{s title}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{METAS}
<style>

View File

@ -13,7 +13,7 @@
<script language="javascript" type="text/javascript" src="{DOMAIN}/tpldata/jqplot.logAxisRenderer.min.js"></script>
<link rel="stylesheet" type="text/css" href="{DOMAIN}/tpldata/jquery.jqplot.css" />
<!-- END -->
<!-- INCLUDE admin_header.tpl -->
<!-- INCLUDE "admin_header.tpl" -->
<!-- IF NOT srcid -->
<p>Добро пожаловать в простую OLAPообразную статистику. Выберите источник данных:</p>
<form action="?" method="GET">
@ -36,7 +36,7 @@
<!-- FOR f = aggregates -->
<option value="{s f.id}"<!-- IF eq(cell.aggr, f.id) --> selected="selected"<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
</select>
<select style="width:100px" name="cell_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
@ -61,7 +61,7 @@
<option value="{g.id}"<!-- IF eq(gf.type,g.id) --> selected="selected" <!-- END -->>{s g.name}</option>
<!-- END -->
</select>
<!-- INCLUDE admin_olap_field.tpl -->
<!-- INCLUDE "admin_olap_field.tpl" -->
</p>
<p><select style="width:100px" name="group-sort_dir-{fld}">
<option value="asc">По возрастанию</option>
@ -75,7 +75,7 @@
<!-- END -->
<!-- END -->
</select>
<!-- SET ft = 'sort_' --><!-- INCLUDE admin_olap_field.tpl --><!-- SET ft = '' -->
<!-- SET ft = 'sort_' --><!-- INCLUDE "admin_olap_field.tpl" --><!-- SET ft = '' -->
</p>
</li>
<!-- END -->
@ -122,11 +122,11 @@
<p>Отчёт занял {time_elapsed} сек. Использовано {memory} памяти для работы.</p>
<!-- END -->
<!-- END -->
<!-- INCLUDE admin_footer.tpl -->
<!-- INCLUDE "admin_footer.tpl" -->
<!-- FUNCTION block_groups -->
<!-- FOR group = groups -->
<div style="width: 900px; height: 400px; margin-top: 20px" id="group{group#}"></div>
<div style="width: 900px; height: 400px; margin-top: 20px" id="group{group_index}"></div>
<!-- END -->
<p>
<input type="checkbox" name="graph_log" id="graph_log" value="1"
@ -135,24 +135,24 @@
</p>
<script language="javascript" type="text/javascript">
var g = [
<!-- FOR group = groups -->{yesno(group#,',','')}
<!-- FOR group = groups -->{yesno(group_index,',','')}
{
id: "group{group#}",
id: "group{group_index}",
desc: "{q join(', ',group.full_desc)}",
numeric: {yesno(group.numeric,'true','false')},
data: [
<!-- FOR graph = group.graphs -->{yesno(graph#,',','')}
<!-- FOR graph = group.graphs -->{yesno(graph_index,',','')}
[
<!-- FOR g = graph.data -->
[ {yesno(group.numeric,g.0,concat("'",q(g.0),"'"))}, {g.1} ],
[ {yesno(group.numeric,g[0],concat("'",q(g[0]),"'"))}, {g[1]} ],
<!-- END -->
]
<!-- END -->
],
x: "{q group.graphs.0.field}<!-- IF group.graphs.0.func --> ({q group.graphs.0.func})<!-- END -->",
x: "{q group.graphs[0].field}<!-- IF group.graphs[0].func --> ({q group.graphs[0].func})<!-- END -->",
dateAxis: '{group.date_axis}',
titles: [
<!-- FOR graph = group.graphs -->{yesno(graph#,',','')}
<!-- FOR graph = group.graphs -->{yesno(graph_index,',','')}
'{q graph.last_desc}'
<!-- END -->
]
@ -236,9 +236,9 @@ function doPlot(logarithmic)
<!-- SET o = 0 -->
<!-- FOR d = graph.desc -->
<!--# Описание графика (поле: значение, ..., только значения, не равные значениям предыдущего графика) -->
<!-- SET o = or(o, not(graph#), ne(get(get(get(get(graphs,sub(graph#,1)),'desc'),d#),'value'),d.value)) -->
<!-- SET o = or(o, not(graph_index), ne(get(get(get(get(graphs,sub(graph_index,1)),'desc'),d_index),'value'),d.value)) -->
<!-- IF o -->
<p style="margin-left: {mul(d#,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<p style="margin-left: {mul(d_index,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<!-- END -->
<!-- END -->
<div style="margin-left: {mul(count(graph.desc),20)}px">
@ -248,15 +248,15 @@ function doPlot(logarithmic)
<tr><th colspan="4">- {graph.field}<!-- IF graph.func --> ({lc graph.func})<!-- END --> +</th></tr>
<!-- FOR g = graph.data -->
<tr>
<td><div style="float: right; background: red; height: 7px; width: {g.3}px"></div></td>
<td>{s g.0}:&nbsp;</td><td>{s g.1}</td>
<td><div style="background: blue; height: 7px; width: {g.2}px"></div></td>
<td><div style="float: right; background: red; height: 7px; width: {g[3]}px"></div></td>
<td>{s g[0]}:&nbsp;</td><td>{s g[1]}</td>
<td><div style="background: blue; height: 7px; width: {g[2]}px"></div></td>
</tr>
<!-- END -->
<!-- ELSE -->
<tr><th style="text-align: left" colspan="2">{graph.field}<!-- IF graph.func --> ({lc graph.func})<!-- END --></th></tr>
<!-- FOR g = graph.data -->
<tr><td>{s g.0}:&nbsp;</td><td>{s g.1}</td><td><div style="background: blue; height: 7px; width: {g.2}px"></div></td></tr>
<tr><td>{s g[0]}:&nbsp;</td><td>{s g[1]}</td><td><div style="background: blue; height: 7px; width: {g[2]}px"></div></td></tr>
<!-- END -->
<!-- END -->
</table>
@ -271,9 +271,9 @@ function doPlot(logarithmic)
<!-- IF table.desc -->
<!-- SET o = 0 -->
<!-- FOR d = table.desc -->
<!-- SET o = or(o, not(table#), ne(get(get(get(get(tables,sub(table#,1)),'desc'),d#),'value'),d.value)) -->
<!-- SET o = or(o, not(table_index), ne(get(get(get(get(tables,sub(table_index,1)),'desc'),d_index),'value'),d.value)) -->
<!-- IF o -->
<p style="margin-left: {mul(d#,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<p style="margin-left: {mul(d_index,20)}px">{d.field}<!-- IF d.func --> ({lc d.func})<!-- END -->: {d.value}</p>
<!-- END -->
<!-- END -->
<div style="margin-left: {mul(count(table.desc),20)}px">