<?php


function getSQLFromP0(){
    echo '+++wwwwwwwww++++++';
}



/**
 * Обвернуть строку в кавычки в соответствии с типом.
 * @param String $t Тип
 * @param String $v Значение
 * @return String Строка
 */
function getSQLValue($t,$v)
{
    if($t=='object' && gettype($v)=='string') $t='string'; //Если id шники uuid

    if($t=='object')
    {	if (($v=='-1')||($v=='')) $v='NULL';
    }else
        if($t=='i4' || $t=='integer')
        {	if($v=='') $v='NULL';
        }else
            if($t=='f8')
            {	if($v=='')$v='NULL';
                $v=str_replace(',','.',$v); //Разделитель целой и дробной части точка
            }else
                if($t=='b')
                {	if($v=='') $v='NULL'; else
                    if($v=='1') $v='true'; else
                        if($v=='0') $v='false';
                }else
                    if($t=='string' || $t=='dateTime' || $t=='date')
                    {	if($v=='')
                    {	$v='NULL';
                    }else
                    {	$v=str_replace('\'','\\\'',$v); //так как в SQL строку вставляется
                        $v='\''.$v.'\'';
                    }
                    }else
                        $v='\''.$v.'\'';
    return $v;
}


/** Перенести параметры из родительского в sql строку дочернего элемента
 * @param object $nParent Родительский узел
 * @param object $nChild Дочерний узел
 * @return string
 */
function getSQLFromP($nParent,$nChild)
{
    if($nChild==null) return '';
    $nPs=findNode($nParent, 'columns');
    $nFs=findNode($nChild, 'filter');
    //Переносим значения в фильтр
    if($nFs!=null)
    {
        if($nPs!=null) $nP=$nPs->firstChild; else $nP=null;
        while ($nP != null)
        {
            if ($nP->nodeName=='param')
            {
                $val=getCdata($nP)->nodeValue;

                $nF=findNodeOnAttribute($nFs, 'column', 'pn', $nP->getAttribute("n"));
                if($nF!=null) getCdata($nF)->nodeValue=$val;
            }
            $nP = $nP->nextSibling;
        }
    }
    //Переносим значения в SQL запрос из фильтра
    $sql=getCdataValue(findNode($nChild,'sql-query'));
    $nFs=findNode($nChild, 'filter');
    if($nFs!=null)
    {	$nF=$nFs->firstChild;
        while($nF != null)
        {
            if($nF->nodeName=='column')
            {
                $sql=str_replace('${'.$nF->getAttribute("n").'}',getSQLValue($nF->getAttribute("vt"),getCdata($nF)->nodeValue),$sql);
            }
            $nF=$nF->nextSibling;
        }
    }
    return $sql;
}

//Функция по заданному пути выдаёт все под пути в виде асоциативного массива (тоесть + 1 уровень дерева)
//$db - PDO соединение с базой данных
//$path - путь "id дерева из XML; id записи из базы; id дерева из XML; id записи из базы;"
//$nParent - родительский узел нужен для рекурсии по умолчанию ставить null
function GetSubNodes($path,$nParent,&$title)
{
    global $v_db;
    if($nParent==null) return array();

    $dt=''; //Разделитель для заголовка смотри ниже по тексту "->"

    //По заданному пути спускаемся к нужному узлу дерева
    $mas=split(';',$path);
    for($i=0;$i<(count($mas)-1)/2;$i++)
    {
        $nChild=findNodeOnAttribute($nParent, 'type', 'id', $mas[$i*2]); //Ищем нужную ветку XML дерева
        $sql=getSQLFromP($nParent,$nChild); //Выбираем параметры из родительского в дочерний потом из текущего в sql запрос
        //Выполняем и ищем запись с нужным id
        $res = $v_db->query($sql);


        while ($row = $res->fetch(PDO::FETCH_ASSOC))// $row - ассоциативный массив значений, ключи - названия столбцов
        {
            if($row[$nChild->getAttribute('ObjectID')]==$mas[$i*2+1])
            {
                $title.=$dt.$row[$nChild->getAttribute('c')]; //Чтоб отобразить заголовок в виде пути
                $dt='->';

                //Записываем параметры из текущей записи в параметры для передачи в в следующую итерацию
                $nPs=findNode($nChild,'columns');
                $nP=$nPs->firstChild;
                while ($nP != null)
                {
                    if ($nP->nodeName=='param')
                        getCdata($nP)->nodeValue=$row[$nP->getAttribute("n")];
                    $nP = $nP->nextSibling;
                }
                $nParent=$nChild;
                break;
            }
        }
    }

    //Добрались до самого последнего элемента пути теперь выбераем из всех дочерних узлов
    $result=array();
    $nChild=$nParent->firstChild;
    while ($nChild != null)
    {
        if($nChild->nodeName=='goto')
        {
            $nType=findFirstNodeOnAttribute($nChild->ownerDocument->documentElement,"type","id",$nChild->getAttribute("id"));
            if($nType==null) { $nChild = $nChild->nextSibling; continue; }
        }else $nType=$nChild;

        if ($nType->nodeName=='type')
        {
            $sql=getSQLFromP($nParent,$nType);//Получаем SQL запрос перенося параметры из родительского в дочерний а потом в SQL дочернего
            $res = $v_db->query($sql);
            while ($row = $res->fetch(PDO::FETCH_ASSOC))// $row - ассоциативный массив значений, ключи - названия столбцов
            {
                $cnt=count($result);
                $result[$cnt]['name']=$row[$nType->getAttribute('c')];
                $result[$cnt]['path']=$path.$nType->getAttribute('id').';'.$row[$nChild->getAttribute('ObjectID')].';';
            }
        }
        $nChild = $nChild->nextSibling;
    }
    return 	$result;
}

//Из пути получить асоциативный массив заголовков + путь
//$path - путь "id дерева из XML; id записи из базы; id дерева из XML; id записи из базы;" итд
//$nParent - XML узел с которого будет начинаться поиск
//$db - База данных PDO
function getTXTPath($path,$nParent,$db)
{
    $result=array(); $pos=0;
    $mas=explode(';',$path);
    for($i=0;$i<(count($mas)-1)/2;$i++)
    {
        $nChild=findNodeOnAttribute($nParent, 'type', 'id', $mas[$i*2]); //Ищем нужную ветку XML дерева
        if($nChild==null) $nChild=findFirstNodeOnAttribute($nParent->ownerDocument->documentElement, 'type', 'id', $mas[$i*2]);
        if($nChild==null) continue;


//print $nParent->ownerDocument->saveXML($nParent).'<br>';
        $sql=getSQLFromP($nParent,$nChild); //Выбираем параметры из родительского в дочерний потом из текущего в sql запрос
        if(gettype($_SESSION['USER_ID'])=='string')
            $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : '\''.$_SESSION['USER_ID'].'\'',$sql);
        else
            $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : $_SESSION['USER_ID'],$sql);
//print $sql.'<br>';


        //Выполняем и ищем запись с нужным id
        $res = $db->query($sql);
        while ($row = $res->fetch(PDO::FETCH_ASSOC))// $row - ассоциативный массив значений, ключи - названия столбцов
        {
            if($row[$nChild->getAttribute('ObjectID')]==$mas[$i*2+1])
            {
                $result[$pos]['name']=$row[$nChild->getAttribute('c')];
                $result[$pos]['path']=$mas[$i*2].';'.$mas[$i*2+1].';';
                $result[$pos]['url']=$row['url'];

                //Записываем параметры из текущей записи в параметры для передачи в в следующую итерацию
                $nP=findNode($nChild, 'columns');
                if($nP!=null) $nP=$nP->firstChild;
                while ($nP != null)
                {
                    if ($nP->nodeName=='param')
                    {
                        if(isset($row[$nP->getAttribute("n")]))
                            getCdata($nP)->nodeValue=$row[$nP->getAttribute("n")];
                        if(isset($row[$nP->getAttribute("n")]))
                            $result[$pos][$nP->getAttribute("n")]=$row[$nP->getAttribute("n")];

                        //getCdata($nP)->nodeValue=$row[$nP->getAttribute("n")];
                        //$result[$pos][$nP->getAttribute("n")]=$row[$nP->getAttribute("n")];
                    }
                    $nP = $nP->nextSibling;
                }
                $nParent=$nChild;

                $pos++;
                break;
            }
        }
    }
    return $result;
}

//получить путь по id дерева и по id записи в виде строки (поднимается вверх по дереву)
// $tid - id дерева
// $fid - id записи
// $nParent - родительский XML узел
// $db - Открытая база данных
function getPathOnId($tid,$fid,$nParent,$db)
{
    if($nParent==null || $db==null) return '';
    $str='';

    $nChild=findFirstNodeOnAttribute($nParent, 'type', 'id', $tid);
    if($nChild==null) return '';
    //Пробуем найти родительский id по переданому
    $nFs=findNode($nChild, 'filter');
    $sql=findNode($nChild,'sql-query');
    if($sql==null) return '';
    $sql=getCdata($sql)->nodeValue;
    if($nFs!=null)
    {	$nF=$nFs->firstChild;
        while($nF != null)
        {
            if($nF->nodeName=='column')
            {
                if($nF->getAttribute('n')==$nChild->getAttribute('ObjectID'))
                    getCdata($nF)->nodeValue=$fid;
                else getCdata($nF)->nodeValue=''; //Очищаем фильтр

                $sql=str_replace('${'.$nF->getAttribute("n").'}',getSQLValue($nF->getAttribute("vt"),getCdata($nF)->nodeValue),$sql);
            }
            $nF=$nF->nextSibling;
        }
    }

    $res = $db->query($sql);
    if($res->rowCount()>1) return ''; //В результ сете может быть только 1 запись так как фильтруем по id
    while ($row = $res->fetch(PDO::FETCH_ASSOC))// $row - ассоциативный массив значений, ключи - названия столбцов
    {
        $fid=$row[$nChild->getAttribute('ParentID')];

        //Мы не знаем id шник какого родителя ведь есть goto! (Для них должно выполняться условие их родитель всегда должен быть не пустым те. быть не на первом уровне вложенности)
        $nGoto=findFirstNodeOnAttribute($nParent, 'goto', 'id', $tid);
        if($nGoto!=null) $nGoto=$nGoto->parentNode;
        if($nGoto!=null && $nGoto->nodeName=='type')
        {
            $sTmp=getPathOnId($nGoto->getAttribute('id'),$fid,$nParent,$db);
            if($sTmp!='') //Не тупик ли
            {
                $str=$nChild->getAttribute('id').';'.$fid.';'.$str;
                $str=$sTmp.$str;
                break;
            }
        }

        //Для случаяв если по goto не удалось пройтись
        $nChild=$nChild->parentNode;
        if($nChild!=null && $nChild->nodeName=='type' && $nChild->getAttribute('id')!='-1')
        {
            $str=$fid.';'.$str;
            $str=$nChild->getAttribute('id').';'.$str;
            $str=getPathOnId($nChild->getAttribute('id'),$fid,$nParent,$db).$str;
        }
    }
    return $str;
}

//Получить XML дерево в виде строки (рекурсией в массив с заданным уровнем вложенности)
// $nParent - родительский XML узел
// $nParams - Первоначальные параметры фильтра (и для рекурсии)
// $db - соединение с базой данных
// $first - как вызванно через рекурсию или нет
// $path - путь к нужному узлу в виде "id node tree;id data;id node tree;id data;..."
function getTreeArray($nParent,$nParams,$db,$first,$path)
{
    if($nParent==null || $db==null) return null;
    $result='';
    if($first) $result.='<?xml version="1.0" encoding="utf-8"?><metadata fn="2" htmlid="0">'."\n";

    //Если параметры переданны как объект то парсим их в DOM дерево
    if(gettype($nParams)=='string')
    {
        $doc = new DOMDocument();
        try
        {	$doc->loadXML('<metadata>'.$nParams.'</metadata>');
        } catch (Exception $e)
        {	$nParams=null;
        }
        $nParams = $doc->documentElement;
    }

    $mpath = explode(';', $path); //Путь к заветному узлу в виде массива

    //перебераем все дочерние элементы и для каждого выполняем запрос c фильтрацией
    $currNode = $nParent->firstChild; //из tree.xml
    while ($currNode != null)
    {
        $tmpNode=$currNode; //если узел goto

        if($tmpNode->nodeName=="goto") //если встретилась "зацикливалка"
        {
            $treeid=$tmpNode->getAttribute("id");
            $tmpNode=findFirstNodeOnAttribute($tmpNode->ownerDocument->documentElement,"type","id",$treeid);
            if($tmpNode==null) { $currNode = $currNode->nextSibling; continue; }
        }

        if($tmpNode->nodeName=="type" && $tmpNode->getAttribute("hide") != '1')
        {
            $treeid=$tmpNode->getAttribute("id"); //Идентификатор XML узла
            //Пропускаем узлы которые не в заданном пути
            if(!(count($mpath)>=3 && $mpath[0]!=$treeid))
            {
                $caption=$tmpNode->getAttribute("c"); //Название поля для заголовка

                //$sql=getSQLFromP($nParent,$tmpNode);
                $sql=getSQLFromP($nParams,$tmpNode);

                if(gettype($_SESSION['USER_ID'])=='string')
                    $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : '\''.$_SESSION['USER_ID'].'\'',$sql);
                else
                    $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : $_SESSION['USER_ID'],$sql);


                $res=null;
                try
                {	$res=$db->query($sql);
                } catch (Exception $e)
                {	return null;
                }


                while ($row = $res->fetch(PDO::FETCH_ASSOC))
                {
                    if(array_key_exists('id', $row)) $fid=$row['id']; else $fid=''; //id записи
                    if(count($mpath)>=3 && $mpath[1]!=$fid) continue; //Пропускаем записи которые не в пути
                    if(array_key_exists('icon_id', $row)) $iid=$row['icon_id']; else $iid=''; //id значка
                    if(array_key_exists($caption, $row)) $val=$row[$caption]; else $val=''; //Заголовок


                    $visible='';
                    if($tmpNode->getAttribute("visible")=="0") $visible=' visible="0" ';
                    //Для проверки есть ли дети составляем XML запрос и отправляем в вункцию как будто он пришел от клиента
                    //c - Есть ли под узлы по умолчанию есть
                    //fid - id записи
                    //iid - id иконки
                    //treeid - id ветки дерева
                    //ObjectID - название поля с уникальным идентификатором записи



                    //сохраняем параметры фильтра для дочерних элементов с текщим состоянием
                    //перебираем фильтры которые должны быть заполненны для каждого узла даные для фильтра беруться из результ сета
                    $rСol='<columns>'."\n";
                    //считываем название поля и находим данные в результсете
                    $nColumn = findFirstNode($tmpNode, 'columns'); //tree.xml
                    if($nColumn!=null) $nColumn=$nColumn->firstChild;
                    while ($nColumn != null)
                    {
                        if($nColumn->nodeName=="param")
                        {
                            $fname=$nColumn->getAttribute("n");

                            if(array_key_exists($fname, $row)) //Если поле есть в результсете
                            {	//getCdata($nColumn)->nodeValue=$row[$fname]; //Заполняем парамерт значением для рекурсии при следующей итерации затрётся
                                $fval=$row[$fname];
                            }else $fval=getCdataValue($nColumn);

                            $rСol.='<param n="'.$fname.'"><![CDATA['.$fval.']]></param>'."\n";
                        }
                        $nColumn = $nColumn->nextSibling;
                    }
                    $rСol.='</columns>'."\n";

                    $path='';
                    for($i=1;$i<(count($mpath)-1)/2;$i++) $path.=$mpath[i].';'.$mpath[i].';'; //Оставшийся путь минус текущий
                    // !!! Рекурсия !!!

                    $rSub=getTreeArray($tmpNode,$rСol,$db,false,$path);

                    $result.='<tree c="'.($rSub == '' ? '0' : '1').'" fid="'.$fid.'" iid="'.$iid.'" treeid="'.$treeid.'" t="'.$tmpNode->getAttribute("n").'" ObjectID="'.$tmpNode->getAttribute("ObjectID").'"'.$visible.'>'."\n";
                    $result.='<![CDATA['.$val.']]>'."\n";
                    $result.=$rСol;
                    $result.=$rSub;
                    $result.='</tree>'."\n";

                    $rSub='';
                }
                $res->closeCursor();
            }
        }
        $currNode = $currNode->nextSibling;
    }

    if($first) $result.='</metadata>';
    return $result; //XML строка
}

//Похожее на верхнее только получаем XML карту сайта
// $nParent - родительский XML узел
// $nParams - Первоначальные параметры фильтра (и для рекурсии)
// $db - соединение с базой данных
// $first - как вызванно через рекурсию или нет
function getSiteMap($nParent,$nParams,$db,$first,$path)
{
    if($nParent==null || $db==null) return null;
    $result='';
    if($first) $result.='<?xml version="1.0" encoding="utf-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'."\n";

    //Если параметры переданны как объект то парсим их в DOM дерево
    if(gettype($nParams)=='string')
    {
        $doc = new DOMDocument();
        try
        {	$doc->loadXML('<metadata>'.$nParams.'</metadata>');
        } catch (Exception $e)
        {	$nParams=null;
        }
        $nParams = $doc->documentElement;
    }

    //перебераем все дочерние элементы и для каждого выполняем запрос c фильтрацией
    $currNode = $nParent->firstChild; //из tree.xml
    while ($currNode != null)
    {
        $tmpNode=$currNode; //если узел goto

        if($tmpNode->nodeName=="goto") //если встретилась "зацикливалка"
        {
            $treeid=$tmpNode->getAttribute("id");
            $tmpNode=findFirstNodeOnAttribute($tmpNode->ownerDocument->documentElement,"type","id",$treeid);
            if($tmpNode==null) { $currNode = $currNode->nextSibling; continue; }
        }

        if($tmpNode->nodeName=="type")
        {
            $treeid=$tmpNode->getAttribute("id"); //Идентификатор XML узла

            $sql=getSQLFromP($nParams,$tmpNode);

            if(isset($_SESSION['USER_ID']))
            {
                if(gettype($_SESSION['USER_ID'])=='string')
                    $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : '\''.$_SESSION['USER_ID'].'\'',$sql);
                else
                    $sql=str_replace('${user_id}',$_SESSION['USER_ID']=='' ? 'null' : $_SESSION['USER_ID'],$sql);
            }

            $res=null;
            try
            {	$res=$db->query($sql);
            } catch (Exception $e)
            {	return null;
            }

            while ($row = $res->fetch(PDO::FETCH_ASSOC))
            {
                if(array_key_exists('id', $row)) { $fid=$row['id']; } else { $fid=''; } //id записи
                if(array_key_exists('seq', $row)) { $date=date('Y-m-d',946620000+$row['seq']); } else { $date=date('Y-m-d'); } //Дата модификации страницы
                if(array_key_exists('url', $row)) { $url=$row['url']; } else { $url=''; } //id записи

                //сохраняем параметры фильтра для дочерних элементов с текщим состоянием
                //перебираем фильтры которые должны быть заполненны для каждого узла даные для фильтра беруться из результ сета
                $rСol='<columns>'."\n";
                //считываем название поля и находим данные в результсете
                $nColumn = findFirstNode($tmpNode, 'columns'); //tree.xml
                if($nColumn!=null) $nColumn=$nColumn->firstChild;
                while ($nColumn != null)
                {
                    if($nColumn->nodeName=="param")
                    {
                        $fname=$nColumn->getAttribute("n");

                        if(array_key_exists($fname, $row)) //Если поле есть в результсете
                        {	//getCdata($nColumn)->nodeValue=$row[$fname]; //Заполняем парамерт значением для рекурсии при следующей итерации затрётся
                            $fval=$row[$fname];
                        }else
                        {	$fval=getCdataValue($nColumn);
                        }

                        $rСol.='<param n="'.$fname.'"><![CDATA['.$fval.']]></param>'."\n";
                    }
                    $nColumn = $nColumn->nextSibling;
                }
                $rСol.='</columns>'."\n";

                $result.='	<url>'."\n";
                $result.='		<loc>https://'.$_SERVER["SERVER_NAME"].$url.'</loc>'."\n";
                $result.='		<lastmod>'.$date.'</lastmod>'."\n";
                $result.='		<changefreq>daily</changefreq>'."\n";
                $result.='	</url>'."\n";

                // !!! Рекурсия !!!
                $result.=getSiteMap($tmpNode,$rСol,$db,false,$path.$treeid.';'.$fid.';');
            }
            $res->closeCursor();

        }
        $currNode = $currNode->nextSibling;
    }

    if($first) $result.='</urlset>';
    return $result; //XML строка
}

