<?
//---------------------------------------------------------------------------------------------------------------------
//
// класс - регистрация событий на сайте и админе
//
//---------------------------------------------------------------------------------------------------------------------

class c_events_system
{
   public $table_name ;
   public $tkey ;
   public $log_enabled ;

   function __construct($options=array())
 	{  $this->table_name=($options['table_name'])? $options['table_name']:TM_LOG_EVENTS ;
       $this->tkey=$_SESSION['pkey_by_table'][$this->table_name] ;
       $this->log_enabled=1 ;
    }

   function register($finfo)
   {    if (!$GLOBALS['log_events_info']) return(0) ;
        if (!$this->log_enabled) return(0) ;
        if (!isset($_SESSION['list_db_tables'][$this->table_name])) return(0) ;
        if (!isset($finfo['c_data'])) 	$finfo['c_data']=time() ;
        if (!isset($finfo['clss'])) 	$finfo['clss']=40 ;
   		if (!isset($finfo['member'])) 	$finfo['member']=$_SESSION['member']->reffer ;
        								$finfo['ip']=IP ;
        if ($_COOKIE['GeoIP_info']) {$info['country']=$_COOKIE['GeoIP_country'] ; $info['city']=$_COOKIE['GeoIP_city'] ; }
        $_fields=array() ; $_values=array() ;
	    foreach ($finfo as $fname=>$fvalue) { $_fields[]=$fname ; $_values[]="'".addslashes($fvalue)."'" ; }
	    $sql='insert into '.$this->table_name.' ('.implode(',',$_fields).') values ('.implode(',',$_values).')' ; //echo $sql.'<br>' ;
        execSQL_update($sql) ;
	    $id=execSQL_value("SELECT LAST_INSERT_ID() as pkey") ;
        return $id  ;
   }

   // новая сокращенная форма регистрации событий
   // название события, комментарий, объект события, участниуи события
   function reg($name,$comment='',$reffer='',$links_1='',$links_2='',$links_3='',$links_4='',$links_5='',$links_6='',$links_7='',$links_8='',$links_9='',$links_10='')
   { global $log_events_info ;
     if (!$log_events_info) return ;
     if (!$this->log_enabled) return ;

     // стираем из событий события последних дней
     if ($GLOBALS['LS_log_events_day_live']>0)
     { $now=time() ;
       $act_date = mktime(0,0,0, date("m",$now), date("d",$now)-$GLOBALS['LS_log_events_day_live'], date("Y",$now)); // время начала следующего дня
       $cnt=execSQL_value('select count(pkey) as cnt from '.$this->table_name.' where c_data<'.$act_date) ;
       if ($cnt) { execSQL_update('delete from '.$this->table_name.' where c_data<'.$act_date) ;
                   execSQL_update('OPTIMIZE TABLE '.$this->table_name) ;
                   $this->reg('Автоочистка журнала событий','Срок хранения '.$GLOBALS['LS_log_events_day_live'].' дней, удалено '.$cnt.' записей') ;
                 }
     }


     if ($_SESSION['member']->reffer) $arr[]=$_SESSION['member']->reffer ;


     if ($reffer) 	$arr[]=$reffer ;
     if ($links_1)  $arr[]=$links_1 ;
     if ($links_2)  $arr[]=$links_2 ;
     if ($links_3)  $arr[]=$links_3 ;
     if ($links_4)  $arr[]=$links_4 ;
     if ($links_5)  $arr[]=$links_5 ;
     if ($links_6)  $arr[]=$links_6 ;
     if ($links_7)  $arr[]=$links_7 ;
     if ($links_8)  $arr[]=$links_8 ;
     if ($links_9)  $arr[]=$links_9 ;
     if ($links_10)  $arr[]=$links_10 ;

     //if (_sizeof($arr)) $all_links=implode(',',$arr) ;
     // 23.06.11 - новая схема сохранения информации по объектам события - сохраняем через пробем чтобы можно было жедать выборку по базе
     if (_sizeof($arr)) $all_links=' '.implode(' ',$arr).' ' ;

     $clss=_DOT($this->tkey)->jur_clss ;
     if (!$clss) $clss=40 ;

     $finfo=array(	'c_data'		=>  time(),
                    'clss'          =>  $clss,
                    'enabled'       =>  1,
     				'obj_name' 		=> 	$name,
		    		'member'		=>  $_SESSION['member']->reffer, // кто выполнил событие
		    		'reffer' 		=>  $reffer,         // предмет события
		    		'comment'		=>	$comment,
		    		'IP'			=>	IP,
		    		'links'			=>	$all_links       // объекты, имеющие отношение к событию
		   		  )
     ;
     if ($_COOKIE['GeoIP_info']) {$info['country']=$_COOKIE['GeoIP_country'] ; $info['city']=$_COOKIE['GeoIP_city'] ; }
	 foreach ($finfo as $fname=>$fvalue) { $_fields[]=$fname ; $_values[]="'".addslashes($fvalue)."'" ; }
	 $sql='insert into '.$this->table_name.' ('.implode(',',$_fields).') values ('.implode(',',$_values).')' ; //echo $sql.'<br>' ;
	 execSQL_update($sql) ;
     $new_pkey=execSQL_van("SELECT LAST_INSERT_ID() as pkey") ;
     return $new_pkey['pkey']  ;
   }

   function add_event_comment($id,$text,$options=array())
   { $alter_text=execSQL_van('select comment from '.$this->table_name.' where pkey='.$id) ;
     $time_info=($options['no_fixed_time'])? '':date('d.m.Y H:i:s').' :' ;
     $text=($alter_text['comment'])? $alter_text['comment'].'<br>'.$time_info.$text:$text ;
   	 update_rec_in_table($this->tkey,array('comment'=>$text),'pkey='.$id) ;
   }

}

//---------------------------------------------------------------------------------------------------------------------
//
// класс - базовый объект
//
//---------------------------------------------------------------------------------------------------------------------


class c_obj
{
  public $clss ;
  public $parent ;    // родитель -> pkey
  public $pkey ;
  public $name ;
  public $enabled ;
  public $level ;
  public $catalog ;  // имя массива, в котором храняться все объекты данного класса

  public $tkey ;
  public $reffer ;
  public $list_obj = array(); // список подразделов - > pkey
  public $flag ;
  public $_fast_edit_icon ;

    public $image_name ;

    public $manual ;
    public $add_name ;

    public $page_href ;
    public $url_dir_name ;
    public $url_name ;
    public $href ;
    public $subdomain ; // собственный поддомен
    public $mount_dir ;  // точка монтирования к корню каталога
    public $all_cnt_clss ;
    public $cur_cnt_clss ;


    public $rec = array() ;

  // системные функции -------------------------------------------------------------------------------------------------
  // новый формат конструктора - в объект сразу передается вся запись
  function __construct(&$rec,$system)
  {
    $this->clss=$rec['clss'] ;
    $this->pkey=$rec['pkey'] ;
    $this->parent=$rec['parent'] ;
    $this->name=$rec['obj_name'];  //echo $this->clss.' '.$this->name.'<br>' ;
    $this->enabled=$rec['enabled'] ;
    $this->indx=$rec['indx'] ;
    $this->system=$system ;
    $this->level=0 ;
    $this->is_used=0 ;
    $this->save_info($rec) ;
  }


  // запись дополнительной информации в объекты класса
  function save_info($rec,$options=array())
  { // сохраняем общую информацию
    $this->enabled=($rec['enabled'])? 1:0 ;
    $this->tkey=$rec['tkey'] ;
    $this->reffer=$rec['pkey'].'.'.$rec['tkey'] ;
    if (isset($rec['_fast_edit_icon'])) $this->_fast_edit_icon=$rec['_fast_edit_icon'] ;

    // сохраняем информацию по фото
    if (isset($rec['_image_name'])) $this->image_name=$rec['_image_name'] ;

    // формируем имя страницы (только имя, не путь, путь будет сформирован позднее, когда буду загружены все элемнты дерева)
    if (is_object($this->system) and method_exists($this->system,'get_url_dir_name')) { $this->url_dir_name=$this->system->get_url_dir_name($rec) ; $this->url_name=$this->url_dir_name ; }
    if ($rec['subdomain']) { $this->subdomain=$rec['subdomain'] ; if (is_object($this->system)) $this->system->list_subdomain[$rec['subdomain']]=$this->pkey ; }
    if ($rec['href'] and !$rec['subdomain']) { $this->mount_dir=$rec['href'] ;}  // будет использовано только на разделах 2-го уровня
    if (!$rec['parent'])  $this->mount_dir=$this->system->root_dir ; // для корневого раздела
    if ((!isset($options['no_upload_rec']) or !$options['no_upload_rec']) and _ENGINE_MODE!='admin') $this->rec=$rec ; // else echo '!no_upload_rec для '.$rec['obj_name'].'<br>' ;
    if (is_object($this->system) and method_exists($this->system,'dop_save_info')) $this->system->dop_save_info($this,$rec) ;
  }

  // рекурсивное создание пути к текущему подразделу каталога
  // вызывается после создания модели из объекта c_system
  // проходит рекрсивно по всем элементам дерева, начиная с root
  function update_page_patch()
  { if (!is_object($this->system)) return ;
    // получаем url объекта на основе данных текущего элемента
    /*
    if (!$this->subdomain) { $this->host=_MAIN_DOMAIN ;
                             $this->path=$this->system->get_url_section($this) ; ;
                             $this->href=$this->path // создаем относительную ссылку, используется только для вывода URL на сайте
                           }
    else                   { $this->host=_MAIN_DOMAIN ;
                             $this->path=$this->system->get_url_section($this) ; ;
                             $this->href=_MAIN_PROTOCOL.$this->subdomain.'.'._BASE_DOMAIN.'/' ; //  создаем асолютную ссылку, используется только для вывода URL на сайте

                           }
    */
    // получаем url объекта на основе данных текущего элемента
    /*
    if (!$this->subdomain) $this->href=$this->system->get_url_section($this) ;
    else                   $this->href=_MAIN_PROTOCOL.$this->subdomain.'.'._BASE_DOMAIN.'/' ;
    */
    $this->href=$this->system->get_url_section($this) ;

    //echo $this->name.' - '.$this->href.'<br>' ;
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->update_page_patch() ;
  }

  function to_array($fields=array())
  { $res=array() ;
  	if (_sizeof($this->rec)) foreach($this->rec as $indx=>$value)
        if (!_sizeof($fields)) $res[$indx]=$value ;
        elseif (in_array($indx,$fields)) $res[$indx]=$value ;
  	return($res) ;
  }

  function get_child_recs($fields=array())
  { $_res=array() ;
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $_res[$pkey]=$this->system->tree[$pkey]->to_array($fields) ;
    return($_res) ;
  }

  function href()
  { return $this->system->check_href_by_domain($this) ;
  }

  function add_child($pkey)
     {
       $this->list_obj[]=$pkey ;
       $this->system->tree[$pkey]->level=$this->level+1 ;
     }

  // метод используется только один раз - при select_obj_info в c_site админки
  // на удаление
  function get_obj_info()
  { $arr['tkey']=$this->tkey;
    $arr['pkey']=$this->pkey ;
    $arr['_reffer']=$this->reffer;
    $arr['obj_name']=$this->name ;
    $arr['clss']=$this->clss ;
    $arr['parent']=$this->parent ;
    $arr['level']=$this->level ;
    $arr['enabled']=$this->enabled ;

    if ($this->manual) 		$arr['manual']=$this->manual ;
    if ($this->add_name) 	$arr['add_name']=$this->add_name ; // пережиток прошлого, убрать

    // 05.02.12 - отключено, все metatags вынесены в sitemap
    //if ($this->title) 		$arr['title']=$this->title ;
    //if ($this->description) $arr['description']=$this->description ;
    //if ($this->keywords) 	$arr['keywords']=$this->keywords ;

    return($arr) ;
  }

  // функции работы с предками/потомками -------------------------------------------------------------------------------
  function get_prev_obj_id()
  { $indx=array_search($this->pkey,$this->system->tree[$this->parent]->list_obj) ;
    if ($indx!==false) return($this->system->tree[$this->parent]->list_obj[$indx-1]) ;
    else       return(0) ;
  }

  function get_next_obj_id()
  { $indx=array_search($this->pkey,$this->system->tree[$this->parent]->list_obj) ;
    if ($indx<_sizeof($this->system->tree[$this->parent]->list_obj)) return($this->system->tree[$this->parent]->list_obj[$indx+1]) ;
    else  return(0) ;

  }


  // возвращает код родительского объекта заданного уровня
  function get_parent_obj($level)
  {
    if ($this->level<$level) return $this->pkey ;
    if ($this->level==$level) return($this->pkey) ;
     else return(($this->system->tree[$this->parent]->get_parent_obj($level))) ;
  }

  function get_patch($options=array()) {$this->get_path($options);}
  function get_path($options=array())
  {  if ($options['show_tree']) $this->system->tree['root']->show_tree() ;
     $obj=$this ;
     if ($options['last_element']) $str=$options['last_element'] ;
     if (!$options['cur_obj_pkey'] or $options['cur_obj_pkey']!=$this->pkey)
     { if ($options['only_clss']) { if (in_array($obj->clss,$options['only_clss'])) $str[$obj->href]=strip_tags($obj->name) ; } else $str[$obj->href]=strip_tags($obj->name) ;}
     if (!$options['to_level']) $options['to_level']=0 ;
     while ($obj->parent>0 and $obj->level>$options['to_level'])
      { $obj=$this->system->tree[$obj->parent] ;
        $name=strip_tags($obj->name) ;
        if ($options['only_clss']) { if (in_array($obj->clss,$options['only_clss'])) $str[$obj->href]=$name ; }
        else $str[$obj->href]=$name ;
      }
     //trace() ;
     if ($options['include_index'] and is_object(_CUR_PAGE())) $str['/']=_INDEX_PAGE_NAME ;
     $str=array_reverse($str) ;
     return($str) ;
  }

  // возвращает список pkey (через запятую) всех дочерних подразделов в том числе и свой
  // clss - возвращать тока разделы определенного класса, по умолчанию все
  function get_list_child($clss=0,$debug=0)
  {

     if ($debug) { echo '<strong><br>(# '.$this->pkey.') Вход : '.$this->name.' level '.$this->level.'  </strong>'; print_r($this->list_obj) ;  echo '<br>' ;}
     $res=array() ;
     $ret='' ;

     //echo $this->name.'<br>' ; print_r($this->list_obj) ; echo $this->level ; //if ($this->level==2) exit ;
     if (_sizeof($this->list_obj)>0)
       {    if ($debug) echo '<div style="border:1px solid #C0C0C0;padding:10px;">' ;
       		foreach ($this->list_obj as $pkey) 	{ $_res=$this->system->tree[$pkey]->get_list_child($clss,$debug) ;
       											  if ($_res) $res[]=$_res ;
       										    }
       		if ($debug) echo '</div>' ;

       }

     if (!$clss) $res[]=$this->pkey ;
     else if ($this->clss==$clss) $res[]=$this->pkey ;


     if (_sizeof($res)) $ret=implode(',',$res) ; else $ret=null ;
     if ($debug) { echo '<strong>Возвращаем: ' ; print_r($ret) ; echo '</strong><br>' ; }

     //if ($this->level==1) exit ;

     return($ret) ;
  }

  function get_arr_child($clss=0)
  {  $res=array() ;
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey)
      { $_res=$this->system->tree[$pkey]->get_arr_child($clss) ;
       	if (_sizeof($_res)) $res=array_merge($res,$_res) ;
      }
     if (!$clss) $res[]=(int)$this->pkey ; elseif ($this->clss==$clss) $res[]=(int)$this->pkey ;
     return($res) ;
  }

  // возвращает список pkey (через запятую) всех родительских подразделов в том числе и свой
  function get_list_parent()
  {  $res=$this->get_arr_parent() ;
     if (_sizeof($res)) $ret=implode(',',$res) ; else $ret='' ;
     return($ret) ;
  }

  function get_arr_parent($options=array())
  {  $res_arr=array() ;
     if ($this->parent and $this->level) $res_arr=$this->system->tree[$this->parent]->get_arr_parent($options) ;
      $res_arr[]=($options['not_int'])?(string)$this->pkey:(int)$this->pkey;
     return($res_arr) ;
  }

  // возвращает в массиве список имен всех родительских подразделов по уровням
  function get_arr_parent_name($options=array())
  { $res_arr=array() ;
    if ($this->parent) $res_arr=$this->system->tree[$this->parent]->get_arr_parent_name($options) ;
    if (!$options['to_level'] or $this->level>=$options['to_level']) $res_arr[$this->level]=$this->name ;
    return($res_arr) ;
  }

  // возвращает в массиве список имен всех дочерних подразделов
  function get_arr_child_name()
  { $res_arr=array() ;
    if (_sizeof($this->list_obj)) foreach($this->list_obj as $id) $res_arr[]=$this->system->tree[$id]->name ;
    return($res_arr) ;
  }


  // возвращает ближайщий родительский Pkey класс clss
  function get_parent_by_clss($clss) { return($this->get_obj_by_clss($clss));}
  function get_obj_by_clss($clss)
  {
    //echo 'Ищем '.$clss.'<br>' ;
    //echo $this->name.' '.$this->clss.'<br>' ;
    if ($this->clss==$clss) {  return($this->pkey) ; }
    else if ($this->level!=0) return($this->system->tree[$this->parent]->get_obj_by_clss($clss)) ;
         else return(0) ;
  }

  //  возращает список элеметов дерева у которых есть в массиве rec поле fname отличное от 0
  function get_obj_by_props($fname,&$arr)
  {

    //echo 'name='.$this->name.'<br>';
    if (!$this->level) $arr=array() ;

  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->get_obj_by_props($fname,$arr) ;


  	if ($this->rec[$fname]) $arr[$this->pkey]=$this->rec[$fname] ;

    if (!$this->level) { asort($arr) ; $arr=array_keys($arr) ; }
  }

  // возвращает условие для отбора объектов по parent для текущего и дочернего объектов
  // если работаем с корневым разделом, то для исколючения вывода списка всех разделом и как следствие утяжеления SQL-запроса
  // выводим просто parent>0 ;
  function get_usl_parent_child()
  {
  	if ($this->level) $res='parent in ('.$this->get_list_child().') ' ;
  	else			  $res='parent>0' ;
  	return($res) ;
  }

  // функции работы с деревом -------------------------------------------------------------------------------

  function update_field_value($fname)
  {
    $cnt=0 ;
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $cnt+=$this->system->tree[$pkey]->update_field_value($fname) ;
    $cur_fname='_'.$fname ;
    $this->{$cur_fname}=$this->$fname ;
    $this->$fname=$this->$cur_fname+$cnt ;
    return($this->$fname) ;
  }

  function set_field_value($fname,$value)
    {
      if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->set_field_value($fname,$value) ;
      $this->$fname=$value ;
    }

  function unset_field($fname)
    { if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->unset_field($fname) ;
      unset($this->$fname) ;
    }


  function update_cnt_value()
  {
    $cnt=0 ;
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $cnt+=$this->system->tree[$pkey]->update_cnt_value() ;
    $this->cnt=$this->_cnt+$cnt ;

    return($this->cnt) ;
  }

  function update_cnt_fname_value($cur_fname,$all_fname)
  { // пусть подразделы обновлят свои значения cnt_clss
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->update_cnt_fname_value($cur_fname,$all_fname) ; // рекурсивный вызов функции
    // перерь проходим по подрезделам и суммируем значения по классам
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey)
     if (_sizeof($this->system->tree[$pkey]->$all_fname)) foreach($this->system->tree[$pkey]->$all_fname as $clss=>$cnt) $this->{$all_fname}[$clss]+=$cnt ;
    // перерь суммируем свои значения
    if (_sizeof($this->$cur_fname)) foreach($this->$cur_fname as $indx=>$cnt) $this->{$all_fname}[$indx]+=$cnt ;
  }

  function exec_func_recursive($func_name,$options=array())
  { if ($options['params_is_obj'])  $func_name($this) ;
    else                            $func_name($this->pkey) ;
    // пусть подразделы обновлят свои значения cnt_clss
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->exec_func_recursive($func_name,$options); // рекурсивный вызов функции
  }

  function print_template($func_name,$options=array())
  { print_template($this,$func_name,$options) ;
    // пусть подразделы обновлят свои значения cnt_clss
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->print_template($func_name); // рекурсивный вызов функции
  }

  // удаляем пустые подразелы - у которых число cnt=0
  function delete_space_obj($debug=0)
  {
    // сначала пусть дочерние объекты проверт себя
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->delete_space_obj() ;
  	// а теперь проверим дочерние объекты
  	if ($debug) echo '<strong>Проверяем '.$this->name.'</strong><br>' ;


  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $indx=>$pkey)
     if (!_sizeof($this->system->tree[$pkey]->all_cnt_clss))
  	  { if ($debug) echo 'Удален '.$this->system->tree[$pkey]->name.'<br>' ;
  	    unset($this->system->tree[$pkey]) ;
  	    unset($this->list_obj[$indx]) ;
	  }
  }

  // удаляем отключенные подразелы - у которых enadled=0
  function delete_disable_obj()
  {
    // сначала пусть дочерние объекты проверт себя
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->delete_disable_obj() ;
  	// а теперь проверим дочерние объекты
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $indx=>$pkey)
  	 if (!$this->system->tree[$pkey]->enabled)
  	  { //echo 'Удаляем '.$this->system->tree[$pkey]->name.'('.$pkey.')<br>' ;
  	    $this->system->tree[$pkey]->delete_child_obj() ;
  	    unset($this->system->tree[$pkey]) ;
  	    unset($this->list_obj[$indx]) ;
	  }
  }

  // удаляем все подразелы
  function delete_child_obj()
  {
    // сначала пусть дочерние объекты удаляют своих потомков
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->delete_child_obj() ;
  	// а теперь удаляем дочерние объекты
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $indx=>$pkey)
  	  { //echo 'Удаляем '.$this->system->tree[$pkey]->name.'('.$pkey.')<br>' ;
  	    unset($this->system->tree[$pkey]) ;
  	    unset($this->list_obj[$indx]) ;
	  }
  }

  function update_child_level()
  { $this->is_used=1 ;
  	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey)
  	 { $this->system->tree[$pkey]->level=$this->level+1 ;
  	   $this->system->tree[$pkey]->update_child_level() ;
  	 }
  }


  // функции вывода дерева в различных реализациях ---------------------------------------------------------------------
  function get_select_option($sel_pkey=0,$max_level=100,$show_start_element=1)
  {  $sel=($sel_pkey==$this->pkey)? 'selected':'' ;
     $pref=($this->pkey)? str_repeat("&nbsp;&nbsp;",$this->level):"" ;
     if ($show_start_element) $str='<option value="'.$this->pkey.'"'.$sel.'>'.$pref.$this->name.'</option>' ;
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) if ($this->system->tree[$pkey]->level<=$max_level) $str.=$this->system->tree[$pkey]->get_select_option($sel_pkey,$max_level) ;
     return($str) ;
  }

  // используется для подгрузки select при реактировании parent2,parent3,parent4
  function get_path_array(&$res_arr,$pref='')
  {  if ($this->level)
     { $path=($pref)? $pref.'/'.$this->name:$this->name ;
       $res_arr[$this->pkey]=$path ;
     }
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) if ($this->system->tree[$pkey]->level<=100) $this->system->tree[$pkey]->get_path_array($res_arr,$path) ;
  }

  // вывод каталога в формате маркета
  function to_obj_XML(&$obj_XML,$tag_name,$options=array())
  {  if (!$options['from_level']) $options['from_level']=0;
     if (!$this->name) $this->name='Раздел #'.$this->pkey ;
     if (!$options['from_level'] or $this->level>=$options['from_level'])
     { if ($this->parent and $this->system->tree[$this->parent]->level>=$options['from_level']) $obj_XML->add_child($tag_name,$this->name,array('id'=>$this->pkey,'parentId'=>$this->parent)) ;
       else               $obj_XML->add_child($tag_name,$this->name,array('id'=>$this->pkey)) ;
     }
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->to_obj_XML($obj_XML,$tag_name,$options) ;
  }

  function to_arr(&$arr)
  { $arr[$this->pkey]=array('parentId'=>$this->parent,'name'=>$this->name,'level'=>$this->level) ;
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->to_arr($arr) ;
  }

  function tree_to_array(&$arr)
  { $arr[$this->pkey]=$this->rec ;
    $arr[$this->pkey]['_level']=$this->level ;
    if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $this->system->tree[$pkey]->tree_to_array($arr) ;
  }

 function get_name_patch($to_level=0,$options=array())
  {
     $pkey=$this->pkey ;
     $level=$this->level ;
     $str=($options['use_class'])? '<span class="'.$options['use_class'].'">'.$this->name.'</span>':$this->name ;
     while ($pkey>1 and $level>$to_level)
      { $pkey=$this->system->tree[$pkey]->parent ;
        $name=$this->system->tree[$pkey]->name ;
        $level=$this->system->tree[$pkey]->level ;
        //if (!$level) $class='class=start' ;
        $str=$name." / ".$str ;
      }
     return($str) ;
  }

 function get_name_patch_span($to_level=0,$options=array())
  {
     $pkey=$this->pkey ;
     $level=$this->level ;
     $str=($options['use_class'])? '<span class="'.$options['use_class'].'" id='.$this->pkey.'>'.$this->name.'</span>':'<span id='.$this->pkey.'>'.$this->name.'</span>' ;
     while ($pkey>1 and $level>$to_level)
      { $pkey=$this->system->tree[$pkey]->parent ;
        $name='<span id='.$pkey.'>'.$this->system->tree[$pkey]->name.'</span>' ;
        $level=$this->system->tree[$pkey]->level ;
        //if (!$level) $class='class=start' ;
        $str=$name." / ".$str ;
      }
     return($str) ;
  }


  // ОТЛАДОЧНЫЕ ФУНКЦИИ --------------------------------------------------------------------------------------------------------

  // !!! Отладочный показ дерева объектов
  function show_tree($show_field='')
  {
     //echo "<span style='color:#008000'>".$this->name." </span> ".$this->pkey.' (cnt_search='.$this->cnt_search.',_cnt_search='.$this->_cnt_search.')  clss='.$this->clss.'  image= '.$this->image_name.'<br> ' ;
     echo "<span style='color:#008000'>".$this->name." </span> ".$this->pkey.' <span style="color:#800000">'.$this->href.' </span>' ;
     print_r($this->cur_cnt_clss) ;
     if ($show_field) if (!is_array($show_field)) echo $show_field.'='.$this->$show_field.' ' ;
     else foreach($show_field as $field) echo $field.'='.$this->$field.' ' ;
     echo '<br>';
     if (_sizeof($this->list_obj))
      { echo "<div style='border:1px solid #C0C0C0;margin:10px;padding:4px;'>" ;
        foreach ($this->list_obj as $pkey) if (isset($this->system->tree[$pkey])) echo $this->system->tree[$pkey]->show_tree($show_field) ;
        echo "</div>" ;
      }
  }


}

//---------------------------------------------------------------------------------------------------------------------
//
// класс - объектная таблица
//
//---------------------------------------------------------------------------------------------------------------------

class c_obj_table extends c_obj // объект - описание объектной таблицы
{
    public $pkey ;
    public $enabled ;
    public $multilang ;
    public $pattern ;

    public $table_name ;			 // название объектной таблицы
    public $table_code ;          // тоже название, но без 'obj_'
    public $clss ;	 			 // класс таблицы - 101 - объектная, 106 - индексная, 108 - журнал
    public $mode ; 				 // тип таблицы (main,out,ext,indx,jurl)

    public $dir_to_image ;
    public $patch_to_image ;
    public $img_pattern ;
    public $system ;
    public $dir_name ;
    public $dir_to_file ;
    public $patch_to_file ;

    public $used_dir ;
    public $used_patch ;
    public $setting=array() ;
    public $rules=array() ;



  public $title ;

  public $values = array() ; // индексные значения таблицы

  public $list_clss = array() ; // массив объектов, поддерживаемых объектной таблицей, в формате array(clss1,clss2,clss3....)
  public $list_clss_ext = array() ; // массив объектов, поддерживаемых объектной таблицей, в формате array(clss1=array(...),clss2=array(...),clss3=array(...),....)

  public $list_OUT = array() ; // массив дочерних внешних таблиц
  public $list_connect = array() ; // массив информации по связям с другими таблицами

  // системные функции класса -------------------------------------------------------------------------------------------

  // запись дополнительной информации в объекты класса
  function save_info($rec,$no_check_table=0)
  { //print_r($rec) ; echo '<hr>' ;
    $this->enabled=($rec['enabled'])? 1:0 ; // состояние
    $this->table_name=$rec['table_name'] ;  // имя таблицы
    $this->title=$rec['obj_name'] ;
    $this->clss=$rec['clss'] ;
    if ($rec['setting']) $this->setting=unserialize($rec['setting']) ;
    if (isset($rec['multilang'])) $this->multilang=$rec['multilang'] ;
    $this->table_code=str_replace('obj_'.SITE_CODE.'_','',$this->table_name) ;
    if ($this->table_code=='site_menu') $this->table_code='menu' ;
    //if (isset($rec['master_field'])) $this->system=$rec['master_field'] ;   // поле используется для хранения имени подсистемы
    if (isset($rec['img_pattern'])) $this->img_pattern=$rec['img_pattern'] ;  // название используемого клона изрбражений

    // 05.09.2010 изменения в параметр МОDE
    // mode - теперь или main или out. main - основные таблицы, out - дочерние таблицы.
    // изначально у всех таблиц, кроме jur и indx mode=main. Значение потом измениться на out, если окажется, что таблица - дочерняя
    if ($this->clss==101) $this->mode='main' ;

    // пути к директории с файлами
    // исторически в obj_descr_obj_tables для этих целей было два поля: dir_to_image и patch_to_image
    // со временем названия полей потеряли свой смысл
    // теперь в поле dir_to_image - относительный HTTP путь к месторасположению файлов каталога
    // также там можно указатьабсолютный HTTP путь к месторасположению файлов каталога - но, только если фото находиться на другом домене
    if ($rec['dir_to_image']) // если задан относительный или абсолютный HTTP путь
     { $arr=parse_url($rec['dir_to_image']);
       if ($arr['host']) $this->patch_to_file=$rec['dir_to_image'] ;
       else
       {  $this->dir_name='/'.$rec['dir_to_image'] ;
          $this->dir_name=str_replace('//','/',$this->dir_name) ;
          $this->dir_to_file=_DIR_TO_ROOT.$this->dir_name ;
          $this->patch_to_file=_PATH_TO_SITE.$this->dir_name ;
       }
     }

    $this->rules=array() ;
    // устарено. Правило теперь необходимо задавать через _DOT('obj_site_artikle')->rules[]=array('to_pkey'=>1,'only_clss'=>11) ;
    //if (_sizeof($_SESSION['descr_tables'][$this->pkey])) $this->rules=$_SESSION['descr_tables'][$this->pkey] ;
    //if (_sizeof($_SESSION['descr_tables'][$this->table_name])) $this->rules=$_SESSION['descr_tables'][$this->table_name] ;
  }

  // функции вывода -----------------------------------------------------------------------------------------------------
  function get_select_option($self_pkey=-1,$sel_pkey=-1,$sel_clss=-1)
  {  global ${$this->catalog} ;
     //echo $this->catalog ;
     $sel=($sel_pkey==$this->pkey)? 'selected':'' ;
     $pref=($this->pkey)? str_repeat("&nbsp;&nbsp;",$this->level):"" ;
     if ($self_pkey!=$this->pkey)
      if ($sel_clss==-1 xor $this->clss==$sel_clss) $str[]="<option value='".$this->pkey."' $sel>".$pref.$this->name."</option>" ;
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $str[]=${$this->catalog}[$pkey]->get_select_option($self_pkey,$sel_pkey) ;
     if (_sizeof($str)) $ret=implode('',$str) ; else $ret=null ;
     return($ret) ;
  }

  // выводит в выпадающем списке перечень классов, поддерживаемых текущей таблицей
  // имя списка - select_clss
  //
  // опции: no_show_clss - класс, который не показывать
  //        parent_clss  - класс объекта, в котом будут создаваться дочерние объекты
  //        parent_reffer  - код объекта, в котом будут создаваться дочерние объекты
  function show_select_to_list_clss($parent_reffer,$parent_clss,$options=array())
  { list($pkey,$tkey)=explode('.',$parent_reffer) ;
    $arr_clss=_CLSS($parent_clss)->get_adding_clss($tkey,$pkey) ;
    ?><select size="1" name="select_clss"><?if (_sizeof($arr_clss)) foreach($arr_clss as $clss=>$name)
    if (!_sizeof($options['no_show_clss']) or (_sizeof($options['no_show_clss']) and !in_array($clss,$options['no_show_clss'])))
    {?><option value="<?echo $clss?>"><?echo $name?></option><?}?></select><?
  }

  function list_clone_img($name='')
  { if (!$this->img_pattern) return(array()) ;
    if (!isset($_SESSION['img_pattern'][$this->img_pattern])) return(array()) ;
    if ($name and !isset($_SESSION['img_pattern'][$this->img_pattern][$name])) return(array()) ;
    if ($name and isset($_SESSION['img_pattern'][$this->img_pattern][$name])) return($_SESSION['img_pattern'][$this->img_pattern][$name]) ;
    return($_SESSION['img_pattern'][$this->img_pattern]) ;
  }


  /*==================================================================================================================================*/
  //
  // ЗАМЕНА ОБЪЕКТНОЙ МОДЕЛИ
  //
  /*==================================================================================================================================*/


  function update_levels_parents($options=array())
   {
     TREE($this->table_name)->update_levels_parents();
  }

  function convert_list_to_tree(&$arr_table,&$arr_tree,$parent=0)
  { if (_sizeof($arr_table))  foreach($arr_table as $id=>$rec_parent) if ($rec_parent==$parent) { $arr_tree[$id]=array() ; unset($arr_table[$id]) ; }
    if (_sizeof($arr_tree)) foreach ($arr_tree as $child_pkey=>$arr_child_ids) $this->convert_list_to_tree($arr_table,$arr_tree[$child_pkey],$child_pkey) ;
  }

}

//---------------------------------------------------------------------------------------------------------------------
//
// класс - класс объектов
//
//---------------------------------------------------------------------------------------------------------------------

class c_obj_clss //extends c_obj  // объект - описание класса
{
  public $kod ;                // устаревшее, теперь надо использовать pkey
  public $list_obj =array () ;

  function __construct($init_pkey,$init_parent,$init_name,$system,$debug=0)
  {
    if ($debug) echo 'создаем класс: '.$init_pkey.', '.$init_name.',parent='.$init_parent.'<br>' ;
    $this->clss=$init_pkey ; // надо оставить из всех этих полей одно
    $this->pkey=$init_pkey ;
    $this->kod=$init_pkey ;

    $this->parent=$init_parent ;
    $this->name=$init_name ;
    $this->system=$system ;
    $this->enabled=1 ;
    $this->level=0 ;

    //unset($this->image_name) ;
  }

    function add_child($pkey)
     {
       $this->list_obj[]=$pkey ;
       $this->system->tree[$pkey]->level=$this->level+1 ;
     }

    function update_child_level()
     { $this->is_used=1 ;
     	if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey)
     	 { $this->system->tree[$pkey]->level=$this->level+1 ;
     	   $this->system->tree[$pkey]->update_child_level() ;
     	 }
     }

 function get_list_parent_clss()
  {  $res = array() ;
     //echo "<br>Класс: (".$this->kod.") '".$this->name."', pkey= ".$this->pkey." родитель: ".$this->parent  ;
     //trace() ;
      $parent=($this->parent<0)? 0:$this->parent ;
     if ($this->pkey and isset($this->system->tree[$parent])) $res=$this->system->tree[$parent]->get_list_parent_clss() ;
     $res[]=$this->pkey ;
     //echo "<br>Класс: (".$this->kod.") '".$this->name."' clss классов=" ; print_r($res)  ;
     return($res) ;
  }

 function get_list_child_clss()
  {  if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) $_res[]=$this->system->tree[$pkey]->get_list_child_clss() ;
     $_res[]=$this->kod ;
     $res=implode(',',$_res) ;
     return($res) ;
  }

 function get_arr_child_clss()
  {  $res_arr=array() ;
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey)
     { $child_arr=$this->system->tree[$pkey]->get_arr_child_clss() ;
       $res_arr=array_merge($res_arr,$child_arr) ;
     }
     $res_arr[]=$this->kod ;
     return($res_arr) ;
  }

 function get_name_patch($to_level=0,$options=array())
  {  //print_rr($this) ; echo '<hr>' ;
     $pkey=$this->pkey ;
     $level=$this->level ;
     $_str[]=$this->system->tree[$pkey]->name.' ('.$pkey.')' ;
     while ($pkey>0 and $level>$to_level)
       { $pkey=$this->system->tree[$pkey]->parent ;
         $level=$this->system->tree[$pkey]->level ;
         $_str[]=$this->system->tree[$pkey]->name.' ('.$pkey.')' ;
       }
     return(implode(' => ',array_reverse($_str))) ;
  }

  function get_select_option($self_pkey=-1,$sel_pkey=-1,$max_level=100)
  {  $sel=($sel_pkey==$this->pkey)? 'selected':'' ;
     $pref=($this->pkey)? str_repeat("&nbsp;&nbsp;",$this->level):"" ;
     if ($self_pkey!=$this->pkey) $str[]="<option value='".$this->pkey."'" .$sel.">".$pref.'('.$this->pkey.') '.$this->name."</option>" ;
     if (_sizeof($this->list_obj)) foreach ($this->list_obj as $pkey) if ($this->system->tree[$pkey]->level<=$max_level) $str[]=$this->system->tree[$pkey]->get_select_option($self_pkey,$sel_pkey) ;
     if (_sizeof($str)) $ret=implode('',$str) ; else $ret=null ;
     return($ret) ;
  }



}
?>
