master
vitalif 2010-07-27 04:51:32 +00:00
parent 56dc33acda
commit 311ce6b288
12 changed files with 3109 additions and 0 deletions

1
lib/.htaccess Normal file
View File

@ -0,0 +1 @@
Deny from all

23
lib/config.php Normal file
View File

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

934
lib/lib.php Normal file
View File

@ -0,0 +1,934 @@
<?php
# Утилитные функции
# (c) VitaliF 2006-2010
$AutoloadClasses['LinguaStemRu'] = dirname(__FILE__).'/stem_ru.php';
function __autoload($class_name)
{
global $AutoloadClasses;
if ($f = $AutoloadClasses[$class_name])
{
require_once $f;
return true;
}
return false;
}
class PopulatedRow implements ArrayAccess
{
public $row;
public function __construct($row)
{
$this->row = $row;
}
public function offsetExists($k)
{
return array_key_exists($k, $this->row) || self::$calckeys[$k];
}
public function offsetUnset($k)
{
unset($this->$k);
}
public function offsetSet($k, $v)
{
if (array_key_exists($k, $this->row))
$this->row[$k] = $v;
else
$this->$k = $v;
}
public function offsetGet($k)
{
if (array_key_exists($k, $this->row))
return $this->row[$k];
if (!is_null($this->$k))
return $this->$k;
$this->populate($k);
return $this->$k;
}
public function populate($k)
{
}
}
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)
{
$dom = '';
if (!empty($st))
{
if (($p = strpos ($st, '://')) !== false)
$dom = substr ($st, $p+3);
$dom = strtolower ($dom);
if (preg_match ('#^www[0-9]*\.#is', $dom, $m))
$dom = substr ($dom, strlen ($m[0]));
if (($p = strpos ($st, '/')) !== false)
$dom = substr ($st, 0, $p);
}
return $dom;
}
function normalize_link ($l, $pre)
{
if (!empty ($l) && !empty ($pre))
{
if (str_starts_with($l, './'))
$l = substr($l, 2);
if (!strpos($l, '://'))
{
if ($l{0} != '/')
{
if ($pre{strlen($pre)-1} != '/')
$pre .= '/';
$l = $pre . $l;
}
else if (($sch = strpos($pre, '://')) !== false)
$l = substr($pre, 0, strpos($pre, '/', $sch+3)) . $l;
}
return $l;
}
return $l;
}
function strlimit($str, $maxlen)
{
if (!$maxlen || $maxlen < 1 || strlen($str) <= $maxlen)
return $str;
$str = substr($str, 0, $maxlen);
$p = strrpos($str, ' ');
if (!$p || ($pt = strrpos($str, "\t")) > $ps)
$p = $pt;
if ($p)
$str = substr($str, 0, $p);
return $str . '...';
}
function html_pbr($str)
{
return str_replace ("\n", "<br>", htmlspecialchars($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));
}
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);
}
function unhtmlentity ($s)
{
$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]));
return $s;
}
function process_array ($arr, $kfunc, $vfunc)
{
$na = array ();
foreach ($arr as $k => $v)
{
if (is_callable($kfunc))
$k = $kfunc ($k);
if (is_callable($vfunc))
$v = $vfunc ($v);
$na [$k] = $v;
}
return $na;
}
/**
* Функция разворачивает массив во вложенный хеш
* Например [[0,1],[0,2],[1,1]] -> [0:[1:[[0,1]],2:[[0,2]]],1:[1:[[1,1]]]]
*/
function array_hashtree($a, $ka = NULL, $value = NULL)
{
$r = array();
if (!is_array($ka))
$ka = array($ka);
foreach ($a as $ca)
{
$c = &$r;
if (!is_null($ka))
{
foreach ($ka as $k)
{
$k = $ca[$k];
if (!array_key_exists($k, $c))
$c[$k] = array();
$c = &$c[$k];
}
}
else
{
foreach ($ca as $k)
{
if (!array_key_exists($k, $c))
$c[$k] = array();
$c = &$c[$k];
}
}
if (is_null($value))
$c[] = $ca;
else
$c = $value;
}
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)
{
if ($maxw <= 0 && $maxh <= 0 || $cw <= 0 || $ch <= 0)
return array($cw, $ch);
if ($maxw > 0 && ($maxh <= 0 || $maxh/$ch >= $maxw/$cw))
{
$nw = $maxw;
$nh = $ch * $maxw / $cw;
}
else if ($maxh > 0 && ($maxw <= 0 || $maxh/$ch < $maxw/$cw))
{
$nw = $cw * $maxh / $ch;
$nh = $maxh;
}
return array($nw, $nh);
}
function loadimagebyext($from)
{
if (strrpos($from, '.') !== false)
{
$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'))
$img = @imagecreatefrompng($from);
else if (function_exists("imagegif") && ($ext == 'gif'))
$img = @imagecreatefromgif($from);
}
return $img;
}
function saveimagebyext($img, $to)
{
global $default_jpeg_quality;
$qual = $default_jpeg_quality;
if (!$qual)
$qual = 85;
if (strrpos($to, '.') !== false)
{
$ext = strtolower(substr($to, strrpos($to, '.')+1));
if ($ext == 'jpg' || $ext == 'jpeg' || $ext == 'jfif' || $ext == 'jpe')
@imagejpeg($img, $to, $qual);
else if ($ext == 'png')
@imagepng($img, $to);
else if ($ext == 'gif')
@imagegif($img, $to);
else
return false;
return $to;
}
return false;
}
/**
* Масштабирование изображения, находящегося в файле $filename
* Поддерживаются JPG, GIF, и PNG, в зависимости от возможностей PHP
* В $maxw и $maxh возвратит результаты.
*/
function scaleimage ($from, $to, &$maxw, &$maxh, $ret_after_size = false)
{
if (is_string($from))
$img = loadimagebyext($from);
else
$img = $from;
if (!$img)
return false;
$cw = imagesx($img);
$ch = imagesy($img);
$maxw = intval($maxw);
$maxh = intval($maxh);
if (($maxw <= 0 || $cw <= $maxw) && ($maxh <= 0 || $ch <= $maxh))
{
$maxw = $cw;
$maxh = $ch;
copy($from, $to);
@imagedestroy($img);
return true;
}
list($nw, $nh) = fit_width_height($cw, $ch, $maxw, $maxh);
$thumb = imagecreatetruecolor($nw, $nh);
$f = function_exists('imagecopyresampled') ? 'imagecopyresampled' : 'imagecopyresized';
call_user_func($f, $thumb, $img, 0, 0, 0, 0, $nw, $nh, $cw, $ch);
imagedestroy($img);
$r = saveimagebyext($thumb, $to);
imagedestroy($thumb);
if ($ret_after_size)
{
$maxw = $nw;
$maxh = $nh;
}
else
{
$maxw = $cw;
$maxh = $ch;
}
return $r;
}
/* обрезание до нужного соотношения сторон $aspect = X/Y
и потом - масштабирование до ширины $forcewidth, если она задана */
function centercropimage($from, $to, $aspect, $forcewidth = 0)
{
$img = loadimagebyext($from);
if (!$img)
return false;
$cw = imagesx($img);
$ch = imagesy($img);
$ca = $cw/$ch;
if ($ca > $aspect)
{
$nw = $aspect*$ch;
$nh = $ch;
}
else
{
$nw = $cw;
$nh = $cw/$aspect;
}
$posx = intval(($cw-$nw)/2);
$posy = intval(($ch-$nh)/2);
$cw = $nw;
$ch = $nh;
if ($forcewidth > 0)
{
$nh = $nh*$forcewidth/$nw;
$nw = $forcewidth;
}
$thumb = imagecreatetruecolor($nw, $nh);
$f = function_exists('imagecopyresampled') ? 'imagecopyresampled' : 'imagecopyresized';
call_user_func($f, $thumb, $img, 0, 0, $posx, $posy, $nw, $nh, $cw, $ch);
imagedestroy($img);
$r = saveimagebyext($thumb, $to);
imagedestroy($thumb);
return $r;
}
function get_image_size($from, &$w, &$h)
{
if (strrpos ($from, '.') !== false)
{
$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'))
$img = @imagecreatefrompng ($from);
else if (function_exists ("imagegif") && ($ext == 'gif'))
$img = @imagecreatefromgif ($from);
if ($img)
{
$w = imagesx ($img);
$h = imagesy ($img);
imagedestroy ($img);
return true;
}
}
return false;
}
function load_image ($filevarname, $newfilename, &$imgvar, &$thumbvar, &$imgw, &$imgh, $maxdim_config = '')
{
global $local_path, $image_path, $file_creation_mode;
$ret = false;
$path = $image_path . $newfilename;
if (($pos = strrpos ($path, '/')) !== false)
$path = substr ($path, 0, $pos+1);
else $path = '/';
if (isset ($_FILES [$filevarname]['tmp_name']) &&
$_FILES [$filevarname]['name'] != '')
{
$ext = strtolower (substr ($_FILES [$filevarname]['name'], strrpos ($_FILES [$filevarname]['name'], '.')+1));
if ($ext == 'jpg' || $ext == 'jpeg' ||
$ext == 'jpe' || $ext == 'jfif' ||
$ext == 'png' || $ext == 'gif')
{
$fn_i = $image_path . $newfilename . '.' . $ext;
$fn_t = $image_path . $newfilename . '_t.' . $ext;
@unlink ($local_path . $fn_i); // !!! - предыдущее файло сотрётся
@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 <= 0)
$imgw = false;
if ($imgh <= 0)
$imgh = false;
if (is_numeric($file_creation_mode))
chmod($local_path . $fn_i, $file_creation_mode);
if (scaleimage ($local_path . $fn_i, $local_path . $fn_t, $imgw, $imgh))
{
$imgvar = $fn_i;
$thumbvar = $fn_t;
$ret = true;
if (is_numeric($file_creation_mode))
chmod($local_path . $fn_t, $file_creation_mode);
}
}
else
@unlink ($_FILES [$filevarname]['tmp_name']);
}
else
@unlink ($_FILES [$filevarname]['tmp_name']);
}
return $ret;
}
function load_file ($filevarname, $newfilename)
{
global $local_path, $file_creation_mode;
$ret = false;
$path = $newfilename;
if (($pos = strrpos ($path, '/')) !== false)
$path = substr ($path, 0, $pos+1);
else $path = '/';
if (isset ($_FILES [$filevarname]['tmp_name']))
{
$ext = strtolower (substr ($_FILES [$filevarname]['name'], strrpos ($_FILES [$filevarname]['name'], '.')+1));
$fn = $newfilename . '.' . $ext;
@unlink ($local_path . $fn);
if ((@move_uploaded_file ($_FILES [$filevarname]['tmp_name'], $local_path . $fn)))
{
$ret = $fn;
if (is_numeric($file_creation_mode))
chmod($local_path . $fn, $file_creation_mode);
}
else
@unlink ($_FILES [$filevarname]['tmp_name']);
}
return $ret;
}
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'];
}
return $r;
}
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)
$r = sprintf ('%.2f Кб', $r/1024);
else if ($r >= 1024*1024 && $r < 1024*1024*1024)
$r = sprintf ('%.2f Мб', $r/1024/1024);
else if ($r >= 1024*1024*1024)
$r = sprintf ('%.2f Гб', $r/1024/1024/1024);
else if ($r < 0)
$r = sprintf ('%.2f Гб', 2-($r/1024/1024/1024));
}
return $r;
}
/**
* Получить данные от URL $url. Если $userpwd задана - тогда будет использована
* HTTP аутентификация с логином и паролем, указанными в $userpwd как login:password.
* Если данные являются gzip-сжатыми - они будут автоматически распакованы.
*/
function http_get_contents ($url, $userpwd = false)
{
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);
return $data;
}
/* Преобразовать имена некоторых строк в ID некоторых строк */
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);
$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);
}
foreach ($tags as &$t)
if ($tt = $tagids[mb_strtolower($t, 'utf-8')])
$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')
{
$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;
}
/**
* Начало сессии :)
*/
function session_begin($lifetime = -1)
{
global $Domain, $LoginSession;
session_name($LoginSession);
list($dom, $path) = explode('/', $Domain, 2);
$path = "/$path";
session_set_cookie_params($lifetime, $path, '.' . $dom);
@session_start();
}
/**
* Определение нужного количества итераций блока $block на основе:
* sql-запроса $sql ($row[0] это ID и $row[1] это NAME) и
* массива $arr (массив сначала).
* Функция предназначена для присваивания блоков <select>...</select>
* и присвоит "выделено" той опции, которая задана ID-ом $sel_id
*/
function option_block ($sql, $arr, $sel_id = NULL)
{
$vars = array();
if (is_array ($arr))
foreach ($arr as $id => $name)
$vars[] = array (
'ID' => $id,
'NAME' => $name,
'SEL' => !is_null($sel_id) && $id == $sel_id ? 'selected' : '',
);
if (is_string($sql) && ($rows = mysql_get_rows($sql)))
{
foreach ($rows as $row)
{
$vars[] = array (
'ID' => $row[0],
'NAME' => $row[1],
'SEL' => !is_null($sel_id) && $row[0] == $sel_id ? 'selected' : '',
);
}
}
return $vars;
}
/**
* Данные постраничной навигации внутри $allcount элементов по
* $perpage на странице, при том условии что текущий первый отображённый - это $start.
* Ссылка на страницу будет getURL($component, $args+array(page => НОМЕР_СТРАНИЦЫ)).
* Если задано $eps, то отобразится не более 2*$eps соседних страниц.
*/
function page_nav3 ($total, $perpage, $start, $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;
if ($curpage < 0)
$curpage = 0;
$vars = array (
'total' => $total,
'perpage' => $perpage,
'list_start' => $start+1,
'page_count' => $pagecount,
'list_end' => min($total, $start+$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),
'nc' => true,
);
}
if (is_null($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++)
{
$args[$pagearg] = $i;
$vars['page'][] = array (
'name' => $i+1,
'href' => getURL($component, $args),
'nc' => $i != $curpage,
);
}
}
else
{
for ($i = 0; $i < $pagecount; $i++)
{
$args[$pagearg] = $i;
$vars['page'][] = array (
'name' => $i+1,
'href' => getURL($component, $args),
'nc' => $i != $curpage,
);
}
}
if ($curpage < $pagecount-1)
{
$args[$pagearg] = $curpage+1;
if ($metanext)
{
global $template;
$template->tpldata[$metanext] .= '<link rel="next" title="Следующая страница" href="'.getURL($component, $args).'"/>';
}
$vars['page'][] = array (
'name' => '>',
'href' => getURL($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);
define('QP_LINE_SEPARATOR', "\r\n");
function quoted_printable_encode($string, $encodedWord = false)
{
if(!preg_match('//u', $string)) {
throw new Exception('Input string is not valid UTF-8');
}
static $wordStart = '=?UTF-8?Q?';
static $wordEnd = '?=';
static $endl = QP_LINE_SEPARATOR;
$lineLength = $encodedWord
? QP_LINE_LENGTH - strlen($wordStart) - strlen($wordEnd)
: QP_LINE_LENGTH;
$string = $encodedWord
? preg_replace('~[\r\n]+~', ' ', $string) // we need encoded word to be single line
: preg_replace('~\r\n?~', "\n", $string); // normalize line endings
$string = preg_replace('~[\x00-\x08\x0B-\x1F]+~', '', $string); // remove control characters
$output = $encodedWord ? $wordStart : '';
$charsLeft = $lineLength;
$chr = isset($string{0}) ? $string{0} : null;
$ord = ord($chr);
for ($i = 0; isset($chr); $i++) {
$nextChr = isset($string{$i + 1}) ? $string{$i + 1} : null;
$nextOrd = ord($nextChr);
if (
$ord > 127 or // high byte value
$ord === 95 or // underscore "_"
$ord === 63 && $encodedWord or // "?" in encoded word
$ord === 61 or // equal sign "="
// space or tab in encoded word or at line end
$ord === 32 || $ord === 9 and $encodedWord || !isset($nextOrd) || $nextOrd === 10
) {
$chr = sprintf('=%02X', $ord);
}
if ($ord === 10) { // line feed
$output .= $endl;
$charsLeft = $lineLength;
} elseif (
strlen($chr) < $charsLeft or
strlen($chr) === $charsLeft and $nextOrd === 10 || $encodedWord
) { // add character
$output .= $chr;
$charsLeft-=strlen($chr);
} elseif (isset($nextOrd)) { // another line needed
$output .= $encodedWord
? $wordEnd . $endl . "\t" . $wordStart . $chr
: '=' . $endl . $chr;
$charsLeft = $lineLength - strlen($chr);
}
$chr = $nextChr;
$ord = $nextOrd;
}
return $output . ($encodedWord ? $wordEnd : '');
}
}
function is_intval($a)
{
return (is_string($a) || is_int($a)) && !strcmp(intval($a), $a);
}
function litsplit($re, $s)
{
$r = array();
$s = trim($s);
$lit = "\'(?:[^\'\\\\]+|\\.|\'\')+\'|\"(?:[^\"\\\\]+|\\.|\"\")+\"";
$offset = 0;
$re = "/((?:$lit|.+?)*?)($re)/is";
while (preg_match($re, $s, $m, PREG_OFFSET_CAPTURE, $offset))
{
$r[] = $m[1][0];
$offset = $m[0][1] + strlen($m[0][0]);
}
$s = substr($s, $offset);
if (strlen($s))
$r[] = $s;
return $r;
}
function probe_video($fn)
{
global $VideoProbe;
if (!$VideoProbe || !$VideoProbe['cmd'] || !$VideoProbe['size'] || !$VideoProbe['duration'])
die(__FUNCTION__.": \$VideoProbe=array('cmd'=>'shell command','size'=>'size regexp','duration'=>'duration regexp') isn't configured yet");
$cmd = $VideoProbe['cmd'];
$cmd = str_replace('$input', $fn, $cmd);
$out = shell_exec($cmd);
$db = array();
if (preg_match($VideoProbe['size'], $out, $m))
{
$db['width'] = $m[1];
$db['height'] = $m[2];
}
if (preg_match($VideoProbe['duration'], $out, $m))
{
$dur = $m[1];
$colon = explode(':', $dur);
if (count($colon) > 1)
{
$dur = 0;
for ($mul = 1, $i = 0; $i < count($colon); $i++, $mul *= 60)
$dur += $colon[count($colon)-1-$i] * $mul;
}
$db['duration'] = $dur;
}
return $db;
}
function extract_video_frame($from, $time, $w, $h, $to)
{
global $VideoConverter;
if (!$VideoConverter)
die(__FUNCTION.": \$VideoConverter = 'shell command' isn't configured yet");
$cmd = $VideoConverter;
$repl = array(
'$input' => $from,
'$position' => $time,
'$output' => $to,
);
$cmd = str_replace(array_keys($repl), array_values($repl), $cmd);
exec($cmd, $output, $exitcode);
if ($exitcode != 0)
return false;
scaleimage($to, $to, $w, $h);
return true;
}

493
lib/mysql.php Normal file
View File

@ -0,0 +1,493 @@
<?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] ").intval($t/1000).".".($t%1000)." sec $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)
{
$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);
if (is_array($where))
$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 ';
$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 = array();
foreach ($tables as $k => $v)
{
if (!is_numeric($k))
$v = ($cfgTables[$v] ? '`'.$cfgTables[$v].'`' : $v) . ' ' . $k;
else
$v = ($cfgTables[$v] ? '`'.$cfgTables[$v].'` '.$v : $v);
$t[] = $v;
}
$tables = join(',', $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);
if (is_array($where))
$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;
}
if (is_array($where))
$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;
}

1099
lib/template.php Normal file

File diff suppressed because it is too large Load Diff

27
lib/templating.php Normal file
View File

@ -0,0 +1,27 @@
<?php
# Создание глобального объекта шаблона и определение наиболее общих его переменных
# (c) VitaliF 2006-2010
require_once 'config.php';
require_once 'template.php';
require_once 'mysql.php';
if (!defined('LIGHTWEIGHT'))
{
header("Cache-Control: no-cache");
header("Pragma: no-cache");
header("Expires: Thu, 01 Jan 1970 12:00:00 GMT");
}
$tpl = config_var('template');
if ($tpl == '')
$tpl = 'default';
$template = new Template (array(
'root' => $local_path . $template_path . $tpl,
'cache_dir' => $local_path . $cache_path,
'print_error' => 1,
));
$template->vars(array(
'DOMAIN' => "http://$Domain",
));

288
olap.php Normal file
View File

@ -0,0 +1,288 @@
<?php
# Простая OLAPообразная статистика
# admin_olap.tpl, admin_olap_csv.tpl
# (c) Vitaliy Filippov 2010
$is_adm = true;
require_once 'lib/templating.php';
require_once 'olap_config.php';
OLAP::execute($_REQUEST);
exit;
class OLAP
{
static $sources;
static $current_srcid, $current_src;
static $functions = array(
'year' => array('name' => 'Год', 'time' => true),
'month' => array('name' => 'Месяц', 'time' => true),
'day' => array('name' => 'Дата', 'time' => true),
'weekday' => array('name' => 'День недели', 'time' => true),
'week' => array('name' => '№ недели', 'time' => true),
);
static $aggregates = array(
'distinct' => array('name' => 'Значения', 'cell_only' => true),
'count' => array('name' => 'Количество'),
'sum' => array('name' => 'Сумма'),
'avg' => array('name' => 'Среднее'),
'min' => array('name' => 'Минимум'),
'max' => array('name' => 'Максимум'),
);
static $specf = array('v_field', 'v_func', 'h_field', 'h_func', 'cell_field', 'cell_aggr', 'cell_func', 'v_sort_dir', 'v_sort_field', 'v_sort_aggr', 'v_sort_func', 'h_sort_dir', 'h_sort_field', 'h_sort_aggr', 'h_sort_func');
static function execute($request)
{
global $template;
set_time_limit(300);
$time_start = microtime(true);
$template->compiletime_functions['fformat'] = "OLAP::tpl_field_format";
self::$current_srcid = $request['datasource'];
self::$current_src = self::$sources[self::$current_srcid];
$template->vars('sources', array_values(self::$sources));
if (!self::$current_src)
{
$template->vars('select', true);
print $template->parse('admin_olap.tpl');
exit;
}
foreach (self::$current_src['fielddescs'] as $k => &$v)
{
$v['id'] = $k;
if ($v['options'])
foreach ($v['options'] as &$o)
$v['options_hash'][$o['id']] = $o['name'];
}
foreach (self::$functions as $k => &$v)
$v['id'] = $k;
foreach (self::$aggregates as $k => &$v)
$v['id'] = $k;
unset($v);
$wh = array();
$where = array();
foreach ($request as $k => $t)
{
if (substr($k, 0, 5) == 'type-')
{
$i = substr($k, 5);
$f = $request["field-$i"];
$v = $request["value-$i"];
$fd = self::$current_src['fielddescs'][$f];
if (!strlen($v))
continue;
$wh["type-$i"] = $t;
$wh["field-$i"] = $f;
$wh["value-$i"] = $v;
if ($fd['is_time'])
$v = Template::timestamp($v, self::$current_src['fielddefs'][$f]['format']);
$dn = $fd['dbname'];
if (!$dn)
$dn = $f;
if ($t == 'eq')
$where[$dn] = $v;
elseif ($t == 'ge')
$where[] = "$dn >= ".mysql_ecranize($v);
elseif ($t == 'le')
$where[] = "$dn <= ".mysql_ecranize($v);
}
}
$template->vars(array(
'wh' => $wh,
'srcid' => self::$current_srcid,
'src' => self::$current_src,
'fielddescs' => array_values(self::$current_src['fielddescs']),
'functions' => array_values(self::$functions),
'aggregates' => array_values(self::$aggregates),
));
if (!$request['build'])
{
$vars = array();
foreach (self::$specf as $f)
$vars[$f] = $$f;
$template->vars($vars);
print $template->parse('admin_olap.tpl');
exit;
}
$result = mysql_select(self::$current_src['tables'], self::$current_src['fields'], array_merge(self::$current_src['where'], $where), self::$current_src['options'], MS_RESULT);
foreach(self::$specf as $x)
$$x = $request[$x];
$group = array();
$hkeys = array();
$v_v = '';
$v_h = '';
$fetch = self::$current_src['fetch_row'] ? 'mysql_fetch_row' : 'mysql_fetch_assoc';
$code = 'while($r = '.$fetch.'($result)) {';
if ($v_field !== '')
{
$code .= '$v_v = ';
if ($v_func !== '')
$code .= 'self::transform_'.$v_func.'(&$r, $v_field);';
else
$code .= '$r[$v_field];';
}
if ($h_field !== '')
{
$code .= '$v_h = ';
if ($h_func !== '')
$code .= 'self::transform_'.$h_func.'(&$r, $h_field);';
else
$code .= '$r[$h_field];';
}
$code .= '$v_c = ';
if ($cell_func !== '')
$code .= 'self::transform_'.$cell_func.'(&$r, $cell_field);';
else
$code .= '$r[$cell_field];';
$code .= '$hkeys[$v_h] = 1;';
$code .= 'self::aggr_update_'.$cell_aggr.'(&$group[$v_v][$v_h], &$v_c);';
if ($v_field !== '' && $v_sort_aggr !== '' && $v_sort_field !== '')
{
$code .= '$v_vs = ';
if ($v_sort_func !== '')
$code .= 'self::transform_'.$v_sort_func.'(&$r, $v_sort_field);';
else
$code .= '$r[$v_sort_field];';
$code .= 'self::aggr_update_'.$v_sort_aggr.'(&$vsort[$v_v], &$v_vs);';
}
if ($h_field !== '' && $h_sort_aggr !== '' && $h_sort_field !== '')
{
$code .= '$v_hs = ';
if ($h_sort_func !== '')
$code .= 'self::transform_'.$h_sort_func.'(&$r, $h_sort_field);';
else
$code .= '$r[$h_sort_field];';
$code .= 'self::aggr_update_'.$h_sort_aggr.'(&$vsort[$v_h], &$v_hs);';
}
$code .= '}';
eval($code);
if (is_callable("OLAP::aggr_finish_$cell_aggr"))
foreach ($group as $i => &$r)
foreach ($r as $j => &$c)
call_user_func_array("OLAP::aggr_finish_$cell_aggr", array(&$c));
if ($v_field !== '' && $v_sort_aggr !== '' && $v_sort_field !== '' && is_callable("OLAP::aggr_finish_$v_sort_aggr"))
foreach ($vsort as $v => &$c)
call_user_func_array("OLAP::aggr_finish_$v_sort_aggr", array(&$c));
if ($h_field !== '' && $h_sort_aggr !== '' && $h_sort_field !== '' && is_callable("OLAP::aggr_finish_$h_sort_aggr"))
foreach ($hsort as $v => &$c)
call_user_func_array("OLAP::aggr_finish_$h_sort_aggr", array(&$c));
$hkeys = array_keys($hkeys);
self::do_sort($hkeys, $hsort, $h_sort_dir);
$vkeys = array_keys($group);
self::do_sort($vkeys, $vsort, $v_sort_dir);
$vars = array(
'build' => 1,
'hkeys' => $hkeys,
'vkeys' => $vkeys,
'hsort' => $hsort,
'vsort' => $vsort,
'data' => $group,
'rpt_link' => "?".http_build_query($_GET+$_POST),
'csv_link' => "?csv=1&".http_build_query($_GET+$_POST),
'memory' => file_size_string(memory_get_usage()),
'time_elapsed' => sprintf("%.3f", microtime(true)-$time_start),
);
foreach (self::$specf as $f)
$vars[$f] = $$f;
$template->vars($vars);
if ($request['csv'])
{
self::$is_html_format = false;
header("Content-Type: text/csv; charset=windows-1251");
header("Content-Disposition: inline; filename=OLAP-".date('YmdHis').".csv");
$out = $template->parse('admin_olap_csv.tpl');
print iconv("utf-8", "windows-1251", $out);
}
else
print $template->parse('admin_olap.tpl');
}
static function do_sort(&$values, &$sortvalues, &$sort_dir)
{
$fn = 'sort';
$sort_dir = strtolower($sort_dir);
if ($sort_dir == 'desc')
$fn = 'rsort';
if ($sortvalues)
{
$ufn = $sort_dir == 'desc' ? "OLAP::custom_reverse_cmp" : "OLAP::custom_sort_cmp";
self::$sort = &$sortvalues;
usort($values, $ufn);
}
else
$fn($values);
}
static $sort;
static function custom_sort_cmp($a, $b)
{
return self::$sort[$a] < self::$sort[$b] ? -1 : (self::$sort[$a] > self::$sort[$b] ? 1 : 0);
}
static function custom_reverse_cmp($a, $b)
{
return self::$sort[$a] > self::$sort[$b] ? -1 : (self::$sort[$a] < self::$sort[$b] ? 1 : 0);
}
static $is_html_format = true;
static function field_format($field, $func, $aggr, $value)
{
$o = &self::$current_src['fielddescs'][$field]['options_hash'];
if ($aggr == 'distinct')
{
$r = '';
foreach($value as $k => $n)
$r .= self::field_format($field, $func, false, $k).': '.$n.(self::$is_html_format ? "<br />" : "")."\n";
return $r;
}
elseif ($o && $o[$value])
return $o[$value];
$fn = self::$is_html_format ? 'htmlspecialchars' : 'addslashes';
return $fn($value);
}
static function tpl_field_format($field, $func, $aggr, $value)
{
return "OLAP::field_format($field, $func, $aggr, $value)";
}
static function o_tsformat($f, &$row, $field)
{
$tf = "_t$field";
if (!$row[$tf])
$row[$tf] = Template::timestamp($row[$field]);
return date($f, $row[$tf]);
}
/* Агрегатные функции и преобразования */
static function transform_year(&$row, $field) { return self::o_tsformat('Y', $row, $field); }
static function transform_month(&$row, $field) { return self::o_tsformat('Y-m', $row, $field); }
static function transform_day(&$row, $field) { return self::o_tsformat('Y-m-d', $row, $field); }
static function transform_weekday(&$row, $field) { return self::o_tsformat('N', $row, $field); }
static function transform_week(&$row, $field) { return self::o_tsformat('W', $row, $field); }
static function aggr_update_count(&$n, &$v) { $n++; }
static function aggr_update_sum(&$sum, &$v) { $sum += $v; }
static function aggr_update_avg(&$avg, &$v) { $avg['n']++; $avg['s'] += $v; }
static function aggr_finish_avg(&$avg) { $avg = $avg['s']/$avg['n']; }
static function aggr_update_min(&$min, &$v) { if ($min > $v) $min = $v; }
static function aggr_update_max(&$max, &$v) { if ($max < $v) $max = $v; }
static function aggr_update_distinct(&$d, &$v) { $d[$v]++; }
}

28
olap_config.php Normal file
View File

@ -0,0 +1,28 @@
<?php
OLAP::$sources = array(
'v2tags' => array(
'id' => 'v2tags',
'name' => 'VitaphotoV2, статистика по тегам',
'tables' => array('t' => 'stats_tag_all'),
'fields' => 't.ts, t.tag',
'where' => array(),
'fetch_row' => true,
'fielddescs' => array(
#'ts'
0 => array(
'name' => 'Время',
'le_ge' => true,
'is_time' => true,
'format' => TS_UNIX,
'dbname' => 'ts',
),
#'tag'
1 => array(
'name' => 'Тег',
'dbname' => 'tag',
'options' => array_merge(array('id' => 0, 'name' => '*'), mysql_select('tags', 'id, name', array('type' => 0))),
),
),
),
);

View File

@ -0,0 +1,10 @@
<!--# script language="JavaScript">
AjexFileManager.init({
path: '/admin/afm/',
editor: CKEDITOR
});
</script>-->
</div>
<p id="admpoweredby" style="color: #d0d0d0; margin: 20px 0 0 20px">Powered by VitaliF, 2010</p>
</body>
</html>

View File

@ -0,0 +1,36 @@
<html>
<head>
<title>{title/s}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{METAS}
<style>
<!--
html, body, form, table, input, select, textarea { margin: 0; font-family: sans-serif; font-size: 13px; }
html, form, table, input, select { padding: 0; }
img { border: 0; }
body { padding: 8px; }
select, textarea { border: 1px solid black; }
input:not([type="radio"]):not([type="checkbox"]) { border: 1px solid black; }
h1 { text-indent: 20px; font-family: Trebuchet MS; font-size: 20px; font-weight: bold; }
p { margin: 4px 0; }
.rbborder { border-width: 0 1px 1px 0; border-color: gray; border-style: solid; }
.themes { padding: 8px; text-align: left; }
.themes ul { list-style-type: none; padding: 0; margin: 4px 0; display: block; }
.themes li { padding: 0; display: inline; margin-right: 8px; }
.simpletable, .simpletable td { border: 1px solid black; }
.simpletable { border-collapse: collapse; }
.simpletable th { padding: 3px 8px; }
.simpletable td { padding: 3px; }
#middle ul.list div.right { float: left; }
-->
</style>
<script language="JavaScript" type="text/javascript" src="{DOMAIN}/tpldata/lightbox.js"></script>
<!-- IF NOT nock -->
<script type="text/javascript" language="JavaScript" src="{DOMAIN}/ckeditor/ckeditor.js"></script>
<!--#<script type="text/javascript" language="JavaScript" src="{DOMAIN}/admin/afm/ajex.js"></script> -->
<!-- END -->
<link rel="stylesheet" type="text/css" href="{DOMAIN}/tpldata/lightbox.css" />
</head>
<body {body_args}>
<div id="admcontent">
<!-- IF NOT notitle --><h1><!-- IF detailtitle -->{detailtitle}<!-- ELSE -->{title}<!-- END --></h1><!-- END -->

View File

@ -0,0 +1,166 @@
<!-- SET title -->Статистика<!-- END -->
<!-- INCLUDE admin_header.tpl -->
<!-- IF NOT srcid -->
<p>Добро пожаловать в простую OLAPообразную статистику. Выберите источник данных:</p>
<form action="?" method="GET">
<select style="width:100px" name="datasource">
<!-- FOR s = sources -->
<option value="{s s.id}">{s s.name}</option>
<!-- END -->
</select>
<input type="submit" value=" Продолжить " />
</form>
<!-- ELSE -->
<p>Выбранный источник данных: <b>{s src.name}</b>. <a href="?">Выбрать другой источник данных</a>.</p>
<p>Настройте желаемый отчёт:</p>
<form action="?" method="GET">
<input type="hidden" name="datasource" value="{s src.id}" />
<input type="hidden" name="build" value="1" />
<table>
<tr><td style="text-align: center; vertical-align: middle"><input type="submit" value=" Построить отчёт " /></td><td style="background-color: #e0e0e0; padding: 8px">
По горизонтали:<br />
<select style="width:100px" name="h_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(h_field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="h_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(h_func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select><br />
Сортировка:<br />
<select style="width:100px" name="h_sort_dir">
<option value="asc">по возрастанию</option>
<option value="desc"<!-- IF eq(h_sort_dir, 'desc') --> selected<!-- END -->>по убыванию</option>
</select>
<select style="width:100px" name="h_sort_aggr">
<option value="">по значениям поля</option>
<!-- FOR f = aggregates -->
<!-- IF NOT f.cell_only -->
<option value="{s f.id}"<!-- IF eq(h_sort_aggr, f.id) --> selected<!-- END -->>{s f.name} другого поля:</option>
<!-- END -->
<!-- END -->
</select>
<select style="width:100px" name="h_sort_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(h_sort_field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="h_sort_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(h_sort_func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select>
</td></tr>
<tr><td style="background-color: #e0e0e0; padding: 8px">
По вертикали:<br />
<select style="width:100px" name="v_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(v_field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="v_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(v_func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select><br />
Сортировка:<br />
<select style="width:100px" name="v_sort_dir">
<option value="asc">по возрастанию</option>
<option value="desc"<!-- IF eq(v_sort_dir, 'desc') --> selected<!-- END -->>по убыванию</option>
</select>
<select style="width:100px" name="v_sort_aggr">
<option value="">по значениям поля</option>
<!-- FOR f = aggregates -->
<!-- IF NOT f.cell_only -->
<option value="{s f.id}"<!-- IF eq(v_sort_aggr, f.id) --> selected<!-- END -->>{s f.name} другого поля:</option>
<!-- END -->
<!-- END -->
</select>
<select style="width:100px" name="v_sort_field">
<option value="">---</option>
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(v_sort_field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="v_sort_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(v_sort_func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select>
</td><td style="background-color: #ffe0e0; padding: 8px">
В таблице:<br />
<select style="width:100px" name="cell_aggr">
<!-- FOR f = aggregates -->
<option value="{s f.id}"<!-- IF eq(cell_aggr, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="cell_field">
<!-- FOR f = fielddescs -->
<option value="{s f.id}"<!-- IF eq(cell_field, f.id) --> selected<!-- END -->>{s f.name}</option>
<!-- END -->
</select>
<select style="width:100px" name="cell_func">
<option value="">без преобразования</option>
<!-- FOR f = functions -->
<option value="{s f.id}"<!-- IF eq(cell_func, f.id) --> selected<!-- END -->><!-- IF f.time -->(время) <!-- END -->{s f.name}</option>
<!-- END -->
</select>
</td></tr>
</table>
Условия выборки данных:
<table>
<!-- SET i = "0" -->
<!-- FOR f = fielddescs -->
<tr><th style="text-align: left; vertical-align: top">{s f.name}</th>
<td style="text-align: left; vertical-align: top">
= <input type="hidden" name="field-{i}" value="{s f.id}" /><input type="hidden" name="type-{i}" value="eq" />
<!-- IF f.options -->
<select style="width: 100px" name="value-{i}">
<option value="">любой</option>
<!-- FOR o = f.options -->
<option value="{s o.id}">{s o.name}</option>
<!-- END -->
</select>
<!-- ELSE -->
<input style="width: 100px" type="text" name="value-{i}" value="{s get(wh,concat('value-',i))}" />
<!-- END -->
<!-- SET i = add(i,1) -->
<!-- IF f.le_ge -->
или &ge; <input type="hidden" name="field-{i}" value="{s f.id}" /><input type="hidden" name="type-{i}" value="ge" /><input style="width: 100px" type="text" name="value-{i}" value="{s get(wh,concat('value-',i))}" /><!-- SET i = add(i,1) -->
и &le; <input type="hidden" name="field-{i}" value="{s f.id}" /><input type="hidden" name="type-{i}" value="le" /><input style="width: 100px" type="text" name="value-{i}" value="{s get(wh,concat('value-',i))}" /><!-- SET i = add(i,1) -->
<!-- END -->
<!-- IF f.is_time --> (YYYY-MM-DD HH:MM:SS)<!-- END -->
<!-- IF eq(concat('',f.id),'in_trust') --> (0-9)<!-- END -->
</td></tr>
<!-- END -->
</table>
</form>
<!-- IF build -->
<h1>Отчёт</h1>
<p><a href="{s rpt_link}">Ссылка на данный отчёт</a> | <a href="{s csv_link}">В формате CSV</a></p>
<!-- IF NOT data -->
<p>Нет данных для показа.</p>
<!-- ELSE -->
<table class="simpletable">
<tr><th></th><!-- FOR k = hkeys --><th>{fformat(h_field,h_func,'',k)}</th><!-- END --><!-- IF vsort --><th></th><!-- END --></tr>
<!-- FOR v = vkeys -->
<tr><th>{fformat(v_field,v_func,'',v)}</th><!-- FOR k = hkeys --><td>{fformat(cell_field,cell_func,cell_aggr,get(get(data,v),k))}</td><!-- END --><!-- IF vsort --><th>{get(vsort,v)}</th><!-- END --></tr>
<!-- END -->
<!-- IF hsort -->
<tr><th></th><!-- FOR k = hkeys --><th>{get(hsort,k)}</th><!-- END --><!-- IF vsort --><th></th><!-- END --></tr>
<!-- END -->
</table>
<p>Отчёт занял {time_elapsed} сек. Использовано {memory} памяти для работы.</p>
<!-- END -->
<!-- END -->
<!-- END -->
<!-- INCLUDE admin_footer.tpl -->

View File

@ -0,0 +1,4 @@
<!-- FOR k = hkeys -->;"{fformat(h_field,h_func,'',k)}"<!-- END -->
<!-- FOR v = vkeys -->
"{fformat(v_field,v_func,'',v)}"<!-- FOR k = hkeys -->;"{fformat(cell_field,cell_func,cell_aggr,get(get(data,v),k))}"<!-- END -->
<!-- END -->