<?php

define('TYPE_CATEGORY', 0);

$DownloadId=0;

RegisterPrefix("clsCategory","cat","kernel/include/category.php");

class clsCategory extends clsItem
{
    var $Permissions;
    function clsCategory($CategoryId=NULL)
    {
        global $objSession;
        $this->clsItem(TRUE);

        $this->tablename = GetTablePrefix()."Category";
        $this->type=1;
        $this->BasePermission ="CATEGORY";
        $this->id_field = "CategoryId";
        $this->TagPrefix = "cat";

        $this->debuglevel=0;
        /* keyword highlighting */
        $this->OpenTagVar = "Category_Highlight_OpenTag";
        $this->CloseTagVar = "Category_Highlight_CloseTag";

        if($CategoryId!=NULL)
        {        
            $this->LoadFromDatabase($CategoryId);
            $this->Permissions = new clsPermList($CategoryId,$objSession->Get("GroupId"));
            
        }
        else
        {        
           $this->Permissions = new clsPermList();

        }
     }

    function ClearCacheData()
    {
       /*$env = "':m".$this->Get("CategoryId")."%'";
       DeleteTagCache("m_list_cats","",$env); */
       DeleteTagCache("m_itemcount","Category%");           
       DeleteModuleTagCache('kernel');
    }
    
  function DetectChanges($name, $value)
  {  	
  	global $objSession;
  	//print_pre($_POST);

	if ($this->Data[$name] != $value) {		
		//echo "$name Modified tt ".$this->Data[$name]." tt $value<br>";
		if (!stristr($name, 'Modif') && !stristr($name, 'Created')) {
			if ($objSession->GetVariable("HasChanges") != 1) {
				$objSession->SetVariable("HasChanges", 2);		
			}
		}
	}
  }    

      
    function Delete()
    {
    	global $CatDeleteList;
		
    	if(!is_array($CatDeleteList))
    	  $CatDeleteList = array();    	 
    	if($this->UsingTempTable()==FALSE)
    	{
          $this->Permissions->Delete_CatPerms($this->Get("CategoryId"));
          
          // TODO: find way to delete specific category cache only
          /*$sql = "DELETE FROM ".GetTablePrefix()."CountCache WHERE CategoryId=".$this->Get("CategoryId");
          $this->adodbConnection->Execute($sql);*/
          
		  $CatDeleteList[] = $this->Get("CategoryId");          
      	  if($this->Get("CreatedById")>0)
         	$this->SendUserEventMail("CATEGORY.DELETE",$this->Get("CreatedById"));
      	  $this->SendAdminEventMail("CATEGORY.DELETE");          
		  
    	  parent::Delete();
          $this->ClearCacheData();          
    	}
    	else 
    	{
    		parent::Delete();    		
    	}
    }
    

    function Update($UpdatedBy=NULL)
    {
        parent::Update($UpdatedBy);
        if($this->tablename==GetTablePrefix()."Category")
            $this->ClearCacheData();
    }

    function Create()
    {
    	if( (int) $this->Get("CreatedOn") == 0) {
    		$this->Set("CreatedOn",date("U"));
    	}
    	parent::Create();
    	if($this->tablename == GetTablePrefix()."Category") {
    		$this->ClearCacheData();
    	}
    }

    function SetParentId($value) 
    {
        //Before we set a parent verify that propsed parent is not our child.
        //Otherwise it will cause recursion.
         
      $id = $this->Get("CategoryId");
      $path = $this->Get("ParentPath");
      $sql = sprintf("SELECT CategoryId From ".GetTablePrefix()."Category WHERE ParentPath LIKE '$path%' AND CategoryId = %d ORDER BY ParentPath",$value);     
      $rs = $this->adodbConnection->SelectLimit($sql,1,0);
      if(!$rs->EOF)
      {
         return;
        }
        $this->Set("ParentId",$value);
    } 

	function Approve()
	{
      global $objSession;

      if($this->Get("CreatedById")>0)
        $this->SendUserEventMail("CATEGORY.APPROVE",$this->Get("CreatedById"));
      $this->SendAdminEventMail("CATEGORY.APPROVE");
	  $this->Set("Status", 1);
	  $this->Update();
    }

    function Deny()
    {
        global $objSession;

        if($this->Get("CreatedById")>0)
        	$this->SendUserEventMail("CATEGORY.DENY",$this->Get("CreatedById"));
        $this->SendAdminEventMail("CATEGORY.DENY");

        $this->Set("Status", 0);
        $this->Update();
    }


    function IsEditorsPick() 
    {
        return $this->Is("EditorsPick");
    }
    
    function SetEditorsPick($value) 
    {
        $this->Set("EditorsPick", $value);
    }
       
   function GetSubCats($limit=NULL, $target_template=NULL, $separator=NULL, $anchor=NULL, $ending=NULL, $class=NULL)
   {
      global $m_var_list, $m_var_list_update, $var_list, $var_list_update;
      
      $sql = "SELECT CategoryId, Name from ".GetTablePrefix()."Category where ParentId=".$this->Get("CategoryId")." AND Status=1 ORDER BY Priority";
      if(isset($limit))
      {        
      	$rs = $this->adodbConnection->SelectLimit($sql, $limit, 0);
        }
        else
        {
            $rs = $this->adodbConnection->Execute($sql);
        }
      $count=1;
      
      $class_name = is_null($class)? "catsub" : $class;
      
      
      while($rs && !$rs->EOF)
      {      	
         $var_list_update["t"] = !is_null($target_template)? $target_template : $var_list["t"];
         $m_var_list_update["cat"] = $rs->fields["CategoryId"];
         $m_var_list_update["p"] = "1";
         $cat_name = $rs->fields['Name'];
		if (!is_null($anchor))
			$ret .= "<a class=\"$class_name\" href=\"". HREF_Wrapper() . "\">$cat_name</a>";            
		else
			$ret .= "<span=\"$class_name\">$cat_name</span>";       
        
            $rs->MoveNext();
            if(!$rs->EOF)
            {
                $ret.= is_null($separator)? ", " : $separator;
            }
      	}
        if(strlen($ret))
          $ret .= is_null($ending)? " ..." : $ending;
          
      	unset($var_list_update["t"], $m_var_list_update["cat"], $m_var_list_update["p"]);
      	
        return $ret;
   }

    function Validate()
    {
      global $objSession;
      
        $dataValid = true;             
      if(!isset($this->m_Type))
        {
            $Errors->AddError("error.fieldIsRequired",'Type',"","","clsCategory","Validate");
            $dataValid = false;
        }
        
        if(!isset($this->m_Name))
        {
            $Errors->AddError("error.fieldIsRequired",'Name',"","","clsCategory","Validate");
            $dataValid = false;
        }
        
        if(!isset($this->m_Description))
        {
            $Errors->AddError("error.fieldIsRequired",'Description',"","","clsCategory","Validate");
            $dataValid = false;
        }
        
        if(!isset($this->m_Visible))
        {
            $Errors->AddError("error.fieldIsRequired",'Visible',"","","clsCategory","Validate");
            $dataValid = false;
        }
        
        if(!isset($this->m_CreatedById))
        {
            $Errors->AddError("error.fieldIsRequired",'CreatedBy',"","","clsCategory","Validate");
            $dataValid = false;
        }
        return $dataValid;
    }
    
    function UpdateCachedPath()
   {
   	  if($this->UsingTempTable()==TRUE)
   	    return;
      $Id = $this->Get("CategoryId");
      $Id2 = $Id;
        $NavPath = "";
        $path = array();        
      do
      {  
         $rs = $this->adodbConnection->Execute("SELECT ParentId,Name from ".$this->tablename." where CategoryId='$Id2'");
            $path[] = $Id2;            
            $nav[] = $rs->fields["Name"];
         if ($rs && !$rs->EOF)
         {                    
             //echo $path;
            $Id2 = $rs->fields["ParentId"];
                
         }
         else
            $Id2="0"; //to prevent infinite loop
      } while ($Id2 != "0");
        $parentpath = "|".implode("|",array_reverse($path))."|";
        $NavBar = implode(">",array_reverse($nav));    
        //echo "<BR>\n";
      //$rs = $this->adodbConnection->Execute("update Category set ParentPath='$path' where CategoryId='$Id'");
        if($this->Get("ParentPath")!=$parentpath || $this->Get("CachedNavbar")!=$NavBar)
        {        
          $this->Set("ParentPath",$parentpath);
          $this->Set("CachedNavbar",$NavBar);
          $this->Update();
        }
   }

   function GetCachedNavBar()
   {
       $res = $this->Get("CachedNavbar");
       if(!strlen($res))
       {
           $this->UpdateCachedPath();
           $res = $this->Get("CachedNavbar");
       }
       return $res;
   }
   function Increment_Count()
   {
      $this->Increment("CachedDescendantCatsQty");
   }

   function Decrement_Count()
   {
      $this->Decrement("CachedDescendantCatsQty");
      $this->Update();
   }

    function LoadFromDatabase($Id)
    {
      global $objSession, $Errors, $objConfig;
        if($Id==0)
            return FALSE;
        
        if(!isset($Id))
        {
            $Errors->AddError("error.AppError",NULL,'Internal error: LoadFromDatabase id',"",get_class($this),"LoadFromDatabase");
            return false;
        }        
        $sql = sprintf("SELECT * FROM ".$this->tablename." WHERE CategoryId = '%s'",$Id);
        $result = $this->adodbConnection->Execute($sql);
        if ($result === false)
        {
            $Errors->AddError("error.DatabaseError",NULL,$this->adodbConnection->ErrorMsg(),"",get_class($this),"LoadFromDatabase");
            return false;
        }
        $data = $result->fields;
        if(is_array($data))
        {
           $this->SetFromArray($data);
           $this->Clean();
        }
        else
           return false;
      return true;
    }

    function SetNewItem()
    {
        global $objConfig;

        $value = $this->Get("CreatedOn");

        $cutoff = adodb_date("U") - ($objConfig->Get("Category_DaysNew") * 86400);
        $this->IsNew = FALSE;
        if($value>$cutoff)
            $this->IsNew = TRUE;
        return $this->IsNew;      
    }

    
    function LoadFromResourceId($Id)
    {
        global $objSession, $Errors;
        if(!isset($Id))
        {
            $Errors->AddError("error.AppError",NULL,'Internal error: LoadFromDatabase id',"",get_class($this),"LoadFromResourceId");
            return false;
        }        
        $sql = sprintf("SELECT * FROM ".$this->tablename." WHERE ResourceId = '%s'",$Id); 
        $result = $this->adodbConnection->Execute($sql);
        if ($result === false)
        {
            $Errors->AddError("error.DatabaseError",NULL,$adodbConnection->ErrorMsg(),"",get_class($this),"LoadFromResourceId");
            return false;
        }        
        $data = $result->fields;
        if(is_array($data))
            $this->SetFromArray($data);
        else
            return false;  
        return true;
    }

    function GetParentField($fieldname,$skipvalue,$default)
    {
        /* this function moves up the category tree until a value for $field other than
           $skipvalue is returned.  if no matches are made, then $default is returned */

        $path = $this->Get("ParentPath");
        $path = substr($path,1,-1); //strip off the first & last tokens
        $aPath = explode("|",$path);
        $aPath = array_slice($aPath,0,-1);
        $ParentPath = implode("|",$aPath);
        $sql = "SELECT $fieldname FROM ".GetTablePrefix()."Category WHERE $fieldname != '$skipvalue' AND ParentPath LIKE '$ParentPath' ORDER BY ParentPath DESC";
        $rs = $this->adodbConnection->execute($sql);
        if($rs && !$rs->EOF)
        {
            $ret = $rs->fields[$fieldname];
        }
        else
        {
            $ret = $default;
        }
        $update = "UPDATE ".$this->tablename." SET $fieldname='$ret' WHERE CategoryId=".$this->Get("CategoryId");
        $this->adodbConnection->execute($update);
        return $ret;
    }

    

    function GetCustomField($fieldName)
    {
      global $objSession, $Errors;

        if(!isset($this->m_CategoryId))
        {
           $Errors->AddError("error.appError","Get field is required in order to set custom field values","","",get_class($this),"GetCustomField");
           return false;
        }

        return GetCustomFieldValue($this->m_CategoryId,"category",$fieldName);
    }

    function SetCustomField($fieldName, $value)
    {
      global $objSession, $Errors;

        if(!isset($this->m_CategoryId))
        {
           $Errors->AddError("error.appError","Set field is required in order to set custom field values","","",get_class($this),"SetCustomField");
           return false;
        }
        return SetCustomFieldValue($this->m_CategoryId,"category",$fieldName,$value);
    }
    
    function LoadPermissions($first=1)
    {        
        /* load all permissions for group on this category */
        $this->Permissions->CatId=$this->Get("CategoryId");
        if($this->Permissions->NumItems()==0)
        {      
          $cats = explode("|",substr($this->Get("ParentPath"),1,-1));
          if(is_array($cats))
          {             
            $cats = array_reverse($cats);    
            $cats[] = 0;
            foreach($cats as $catid)
            { 
              $this->Permissions->LoadCategory($catid);
            }
          }
        }
        if($this->Permissions->NumItems()==0)
        {        
          if($first==1)
          {
            $this->Permissions->GroupId=NULL;
            $this->LoadPermissions(0);
          }
        }
    }

    function PermissionObject()
    {
        return $this->Permissions;
    }

    function PermissionItemObject($PermissionName)
    {
        $p = $this->Permissions->GetPermByName($PermissionName);
        return $p;
    }

    function HasPermission($PermissionName,$GroupID)
    { 
        global $objSession;

        $perm = $this->PermissionValue($PermissionName,$GroupID);
//        echo "Permission $PermissionName for $GroupID is $perm in ".$this->Get("CategoryId")."<br>\n";
        if(!$perm)
        {
            $perm=$objSession->HasSystemPermission("ROOT");
        }
        return ($perm==1);
    }

    function PermissionValue($PermissionName,$GroupID)
    {
      //$this->LoadPermissions();            
      $ret=NULL;
      //echo "Looping though ".count($this->Permissions)." permissions Looking for $PermissionName of $GroupID<br>\n";
      if($this->Permissions->GroupId != $GroupID)
      {
          $this->Permissions->Clear();
          $this->Permissions->GroupId = $GroupID;
      }

      $this->Permissions->CatId=$this->Get("CategoryId");
      $ret = $this->Permissions->GetPermissionValue($PermissionName);
      if($ret == NULL)
      {
         $cats = explode("|",substr($this->Get("ParentPath"),1,-1));

       if(is_array($cats))
        { 
          $cats = array_reverse($cats);          
          $cats[] = 0;
          foreach($cats as $catid)
          {  
            $this->Permissions->LoadCategory($catid);
            $ret = $this->Permissions->GetPermissionValue($PermissionName);
            if(is_numeric($ret))
              break;
          }
        }
      }
      return $ret;
    }

    function SetPermission($PermName,$GroupID,$Value,$Type=0)
    {
        global $objSession, $objPermissions, $objGroups;
        
        if($this->Permissions->GroupId != $GroupID)
        {
            $this->Permissions->Clear();
            $this->Permissions->GroupId = $GroupID;
        }

        if($objSession->HasSystemPermission("GRANT"))
        {
            $current = $this->PermissionValue($PermName,$GroupID);

            if($current == NULL)
            {
                $this->Permissions->Add_Permission($this->Get("CategoryId"),$GroupId,$PermName,$Value,$Type);
            }
            else
            {
                $p = $this->Permissions->GetPermByName($PermName);
                if($p->Inherited==FALSE)
                {
                    $p->Set("PermissionValue",$Value);
                    $p->Update();
                }
                else
                   $this->Permissions->Add_Permission($this->Get("CategoryId"),$GroupId,$PermName,$Value,$Type);
            }
            if($PermName == "CATEGORY.VIEW")
            {           
              $Groups = $objGroups->GetAllGroupList();
              $ViewList = $this->Permissions->GetGroupPermList($this,"CATEGORY.VIEW",$Groups);
              $this->SetViewPerms("CATEGORY.VIEW",$ViewList,$Groups);
              $this->Update();
            }
       }
    }

    function SetViewPerms($PermName,$acl,$allgroups)
    {
       global $objPermCache;

       $dacl = array();
       if(!is_array($allgroups))
       {
           global $objGroups;
           $allgroups = $objGroups->GetAllGroupList();
       }
       
       for($i=0;$i<count($allgroups);$i++)
       {
           $g = $allgroups[$i];
           if(!in_array($g,$acl))
               $dacl[] = $g;
       }
        if(count($acl)<count($dacl))
        {
            $aval = implode(",",$acl);
            $dval = "";
        }
        else
        {            
            $dval = implode(",",$dacl);
            $aval = "";
        }
        if(strlen($aval)==0 && strlen($dval)==0)
        {
         $aval = implode(",",$allgroups);
        }
        $PermId = $this->Permissions->GetPermId($PermName);
        $pc = $objPermCache->GetPerm($this->Get("CategoryId"),$PermId);
        if(is_object($pc))
        {            
            $pc->Set("ACL",$aval);
            $pc->Set("DACL",$dval);
            $pc->Update();
        }
        else
            $objPermCache->AddPermCache($this->Get("CategoryId"),$PermId,$aval,$dval);

        //$this->Update();
    }

    function GetACL($PermName)
    {
        global $objPermCache;

        $ret = "";
        $PermId = $this->Permissions->GetPermId($PermName);
        $pc = $objPermCache->GetPerm($this->Get("CategoryId"),$PermId);
        if(is_object($pc))
        {            
            $ret = $this->Get("ACL");
        }
        return $ret;
    }


    function UpdateACL()
    {
       global $objGroups, $objPermCache;

       $glist = $objGroups->GetAllGroupList();

       $ViewList = $this->Permissions->GetGroupPermList($this,"CATEGORY.VIEW",$glist);       
       $perms = $this->Permissions->GetAllViewPermGroups($this,$glist);
       //echo "<PRE>";print_r($perms); echo "</PRE>";
       foreach($perms as $PermName => $l)
       {                
         $this->SetViewPerms($PermName,$l,$glist);
       }
    }

    function Cat_Link()
    {
        global $m_var_list_update;

        $m_var_list_update["cat"] = $this->Get("CategoryId");
        $ret =  HREF_Wrapper();
        unset($m_var_list_update["cat"]);
        return $ret;
    }

    function Parent_Link()
    {
        global $m_var_list_update;

        $m_var_list_update["cat"] = $this->Get("ParentId");
        $ret =  HREF_Wrapper();
        unset($m_var_list_update["cat"]);
        return $ret;
    }

    function Admin_Parent_Link($page=NULL)
    {
        global $m_var_list_update;

        if(!strlen($page))
            $page = $_SERVER["PHP_SELF"];
        $m_var_list_update["cat"] = $this->Get("ParentId");
        $ret =  $page."?env=".BuildEnv();
        unset($m_var_list_update["cat"]);
        return $ret;
    }

    function StatusIcon()
    {
        global $imagesURL;

        $ret = $imagesURL."/itemicons/";

        switch($this->Get("Status"))
        {
          case STATUS_DISABLED:
            $ret .= "icon16_cat_disabled.gif";
            break;
          case STATUS_PENDING:
            $ret .= "icon16_cat_pending.gif";  
            break;
          case STATUS_ACTIVE:
            $img = "icon16_cat.gif";
            if($this->IsPopItem())
                $img = "icon16_cat_pop.gif";
            if($this->IsHotItem())
                $img = "icon16_cat_hot.gif";
            if($this->IsNewItem())           
                $img = "icon16_cat_new.gif";
            if($this->Is("EditorsPick"))
                $img = "icon16_car_pick.gif";
            $ret .= $img;
            break;
        }
        return $ret;
    }

    function SubCatCount()
    {
        $ret = $this->Get("CachedDescendantCatsQty");

            $sql = "SELECT COUNT(*) as SubCount FROM ".$this->tablename." WHERE ParentPath LIKE '".$this->Get("ParentPath")."%' AND CategoryId !=".$this->Get("CategoryId");
            $rs = $this->adodbConnection->Execute($sql);
            if($rs && !$rs->EOF)
            {
                $val = $rs->fields["SubCount"];
                if($val != $this->Get("CachedDescendantCatsQty"))
                {               
                  $this->Set("CachedDescendantCatsQty",$val);
                  $this->Update();
                }
                $ret = $this->Get("CachedDescendantCatsQty");                
            }                       
        return $ret;
    }

    function GetSubCatIds()
    {
        $sql = "SELECT CategoryId FROM ".$this->tablename." WHERE ParentPath LIKE '".$this->Get("ParentPath")."%' AND CategoryId !=".$this->Get("CategoryId");
        $rs = $this->adodbConnection->Execute($sql);
        $ret = array();
        while($rs && !$rs->EOF)
        {
            $ret[] = $rs->fields["CategoryId"];
            $rs->MoveNext();
        }
        return $ret;
    }
    
    function GetParentIds()
    {
    	$Parents = array();
    	$ParentPath = $this->Get("ParentPath");
    	if(strlen($ParentPath))
    	{
    		$ParentPath = substr($ParentPath,1,-1);
    		$Parents = explode("|",$ParentPath);
    	}
    	return $Parents;
    }
    
    function ItemCount($ItemType="")
    {
		global $objItemTypes,$objCatList,$objCountCache;
	
		if(!is_numeric($ItemType))
		{
      		$TypeId = $objItemTypes->GetItemTypeValue($ItemType); 
		}
		else
	  		$TypeId = (int)$ItemType;      		 		
	  	  
	  	//$path = $this->Get("ParentPath");
	  	//$path = substr($path,1,-1);
	  	//$path = str_replace("|",",",$path);
	  	$path = implode(",",$this->GetSubCatIds());
	  	if(strlen($path))
	  	{
	  		$path = $this->Get("CategoryId").",".$path;
	  	}
	  	else
	  	  $path = $this->Get("CategoryId");
	  	
	  	$res = TableCount(GetTablePrefix()."CategoryItems","CategoryId IN ($path)",FALSE);    
	  	
	  	return $res;	  	
    }

    function ParseObject($element)
    {
        global $objConfig, $objCatList, $rootURL, $var_list, $var_list_update, $m_var_list_update, $objItemTypes,$objCountCache, $objUsers;
        
        $extra_attribs = ExtraAttributes($element->attributes);
        
        //print_r($element);
        if(strtolower($element->name)==$this->TagPrefix)
        {          
            $field = strtolower( $element->GetAttributeByName('_field') );
            switch($field)
            {
            	case 'm_language':
            		$ret = language( $element->GetAttributeByName('_Phrase') );
            		break;
            	
            	case 'parentcategorylink':
	            	$m_var_list_update['cat'] = $this->Get('ParentId');
	                $m_var_list_update['p'] = 1;
	                $ret = str_replace('advanced_view.php','browse.php',$_SERVER['PHP_SELF']).'?env='.BuildEnv();
	                unset($m_var_list_update['cat']);
	                unset($m_var_list_update['p']);
	                return $ret;
	                break;

	            case 'primarycategory':
            		$ret = $this->Get('CachedNavbar');
            		if($ret) // category not in root
            		{
            			$ret = explode('>',$ret);
            			array_pop($ret);
            			$ret = implode('>',$ret);
            		}
            		
            		$ret = prompt_language($objConfig->Get("Root_Name")).($ret ? '>' : '').$ret;
            		break;
            	
            	case "name":
            case "Name":
            	/*
            	@field:cat.name
            	@description:Category name
            	*/
                $ret = $this->HighlightField("Name");
            break;
            case "description":
            	/*
            	@field:cat.description
            	@description:Category Description
            	*/            
                $ret = ($this->Get("Description"));
                $ret = $this->HighlightText($ret);
            break;
            case "cachednavbar":
            	/*
            	@field:cat.cachednavbar
            	@description: Category cached navbar
            	*/
                $ret = $this->HighlightField("CachedNavbar");
                if(!strlen($ret))
                {
                    $this->UpdateCachedPath();
                    $ret = $this->HighlightField("CachedNavbar");
                }
            break;
            case "image":
            	/*
 				@field:cat.image
 				@description:Return an image associated with the category
  				@attrib:_default:bool:If true, will return the default image if the requested image does not exist
  				@attrib:_name::Return the image with this name
  				@attrib:_thumbnail:bool:If true, return the thumbnail version of the image
  				@attrib:_imagetag:bool:If true, returns a complete image tag. exta html attributes are passed to the image tag
               */            
               $default = $element->GetAttributeByName('_primary');
               $name = $element->GetAttributeByName('_name');
               if(strlen($name))
               {
                   $img = $this->GetImageByName($name);
               }
               else
               {
                   if($default)
                     $img = $this->GetDefaultImage();
               }
               if($img)
               {
                   if( $element->GetAttributeByName('_thumbnail') )
                   {
                     $url = $img->parsetag("thumb_url");
                   }
                   else
                     $url = $img->parsetag("image_url");

               }
               else
               {
                  $url = $element->GetAttributeByName('_defaulturl');
               }

               if( $element->GetAttributeByName('_imagetag') )
               {
                   if(strlen($url))
                   {
                     $ret = "<IMG src=\"$url\" $extra_attribs >";
                   }
                   else
                       $ret = "";
               }
               else
                   $ret = $url;
            break;
            case "createdby":
				/*
				@field:cat.createdby
				@description:parse a user field of the user that created the category
  				@attrib:_usertag::User field to return (defaults to login ID)            
  				*/            
                $field = $element->GetAttributeByName('_usertag');
                if(!strlen($field))
                {
                    $field = "user_login";
                }
                 
                $userId = $this->Get("CreatedById");                
                if (!empty($userId) && ($userId > 0))
                {
	                $u =& $objUsers->GetItem($userId);
	                if (is_object($u))
	                {                	
	                	$ret = $u->parsetag($field);	
	                }
                }
	            else
	            	$ret = " ";        
	            	
            break;
            case "custom":
                /*
                @field:cat.custom
                @description:Returns a custom field
  				@attrib:_customfield::field name to return
  				@attrib:_default::default value
  				*/            
                $field =  $element->GetAttributeByName('_customfield'); 
                $default = $element->GetAttributeByName('_default');
                $ret = $this->GetCustomFieldValue($field,$default);
            break;
            
            case "catsubcats":
                /*
                @field:cat.catsubcats
                @description:Returns a list of subcats of current category
  				@attrib:_limit:int:Number of categories to return
  				@attrib:_separator::Separator between categories
  				@attrib:_anchor:bool:Make an anchor (only if template is not specified)
  				@attrib:_TargetTemplate:tpl:Target template
  				@attrib:_Ending::Add special text at the end of subcategory list
  				@attrib:_Class::Specify stly sheet class for anchors (if used) or "span" object			
  				*/            
                $limit =  ((int)$element->attributes["_limit"]>0)? $element->attributes["_limit"] : NULL; 
                $separator = $element->attributes["_separator"];
                $anchor = (int)($element->attributes["_anchor"])? 1 : NULL;                
                $templ = $element->GetAttributeByName("_TargetTemplate");               
                $template = !empty($templ)? $templ : NULL;                       
                $ending = strlen($element->attributes["_ending"])? $element->attributes["_ending"] : NULL;
                $class = strlen($element->attributes["_class"])? $element->attributes["_class"] : NULL;
                
                $ret = $this->GetSubCats($limit, $template, $separator, $anchor, $ending, $class);
                
            break;
            
            case "date":
            	/*
  				@field:cat.date
  				@description:Returns the date/time the category was created
  				@attrib:_tz:bool:Convert the date to the user's local time
  				@attrib:_part::Returns part of the date.  The following options are available: month,day,year,time_24hr,time_12hr
            	*/            
                $d = $this->Get("CreatedOn");

                if( $element->GetAttributeByName('_tz') )
                {
                    $d = GetLocalTime($d,$objSession->Get("tz"));
                }

                $part = strtolower( $element->GetAttributeByName('_part') );
                if(strlen($part))
                {
                    $ret = ExtractDatePart($part,$d);
                }
                else
                {                                        
                  if(!is_numeric($d))
                  {                  
                    $ret = "";
                  }
                  else
                    $ret = LangDate($d);
                }
            break;
            case "link":
            	/*
  				@field:cat.link
  				@description:Returns a URL setting the category to the current category
  				@attrib:_template:tpl:Template URL should point to   
  				@attrib:_mod_template:tpl:Template INSIDE a module to which the category belongs URL should point to
  				*/        
  				if ( strlen( $element->GetAttributeByName('_mod_template') ) ){ 
  					//will prefix the template with module template root path depending on category
            			$ids = $this->GetParentIds();
            			$tpath = GetModuleArray("template");
            			$roots = GetModuleArray("rootcat");
            			
            			// get template path of module, by searching for moudle name 
            			// in this categories first parent category 
            			// and then using found moudle name as a key for module template paths array
            			$path = $tpath[array_search ($ids[0], $roots)];
            			
            			$module_templates = explode(',', $element->GetAttributeByName('_mod_template'));
            			$m_templates = Array();
            			foreach($module_templates as $module_t)
            			{
            				$module_t = explode(':', $module_t);
            				if( isset($module_t[1]) )
            				{
            					$m_templates[$module_t[0]] = $module_t[1];
            				}
            				else 
            				{
            					$m_templates['default'] = $module_t[0];
            				}
            			}
            			$db =& GetADODBConnection();
            			$sql = 'SELECT Name FROM '.GetTablePrefix().'Modules WHERE RootCat = '.$ids[0];
            			$module = $db->GetOne($sql);
            			if( isset($m_templates[$module]) )
            			{
            				$target_template = $m_templates[$module];
            			}
            			else 
            			{
            				$target_template = $m_templates['default'];
            			}
            			$t = $path . $target_template;
            		}
            		else 
            			$t = $element->GetAttributeByName('_template');
            		  
                
                if(strlen($t))
                {                
                    $var_list_update["t"] = $t;
                }
                else
                {
                    $var_list_update["t"] = $var_list["t"];
                }
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $ret = HREF_Wrapper();
                unset($m_var_list_update["cat"], $var_list_update["t"]);
            break;
            case "adminlink":
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $m_var_list_update["p"] = 1;
                $ret = $_SERVER["PHP_SELF"]."?env=" . BuildEnv();
                unset($m_var_list_update["cat"]);
                unset($m_var_list_update["p"]);
                return $ret;
                break;            
            case "customlink":
                $t = $this->GetCustomFieldValue("indextemplate","");
                if(strlen($t))
                {                
                    $var_list_update["t"] = $t;
                }
                else
                {
                    $var_list_update["t"] = $var_list["t"];
                }
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $ret = HREF_Wrapper();
                unset($m_var_list_update["cat"], $var_list_update["t"]);
			break;            
            case "link_selector":
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $ret = $_SERVER["PHP_SELF"]."?env=" . BuildEnv();

                
				$tmp = GetVar('Selector'); if($tmp) $ret .= '&Selector='.$tmp;
                $tmp = GetVar('new'); if($tmp) $ret .= '&new='.$tmp;
                $tmp = GetVar('destform'); if($tmp) $ret .= '&destform='.$tmp;
                
                unset($m_var_list_update["cat"]);
                return $ret;
                break;
            case "admin_icon":
                if( $element->GetAttributeByName('fulltag') )
                {
                    $ret = "<IMG $extra_attribs SRC=\"".$this->StatusIcon()."\">";
                }
                else
                    $ret = $this->StatusIcon();
            break;
            case "subcats":
            	/*
            	@field:cat.subcats
            	@description: Loads category's subcategories into a list and renders using the m_list_cats global tag           	
            	@attrib:_subcattemplate:tpl:Template used to render subcategory list elements
  				@attrib: _columns:int: Numver of columns to display the categories in (defaults to 1)
  				@attrib: _maxlistcount:int: Maximum number of categories to list
  				@attrib: _FirstItemTemplate:tpl: Template used for the first category listed
  				@attrib: _LastItemTemplate:tpl: Template used for the last category listed  				
  				@attrib: _NoTable:bool: If set to 1, the categories will not be listed in a table. If a table is used, all HTML attributes are passed to the TABLE tag
				*/            	
                $attr = array();
                $attr["_catid"] = $this->Get("CategoryId");
                $attr["_itemtemplate"] = $element->GetAttributeByName('_subcattemplate');
                if( $element->GetAttributeByName('_notable') )
                    $attr["_notable"]=1;
                $ret = m_list_cats($attr);
            break;
            case "subcatcount":
            	/*
            	@field:cat.subcatcount
            	@description:returns number of subcategories
            	*/
            	$GroupOnly = $element->GetAttributeByName('_grouponly') ? 1 : 0;
            	$txt = "<inp:m_itemcount _CatId=\"".$this->Get("CategoryId")."\" _SubCats=\"1\" ";
            	$txt .="_CategoryCount=\"1\" _ItemType=\"1\" _GroupOnly=\"$GroupOnly\" />";
            	$tag = new clsHtmlTag($txt);
            	$ret = $tag->Execute();	            	
            break;    
            case "itemcount":
               /*
               @field:cat.itemcount
               @description:returns the number of items in the category
               @attrib:_itemtype::name of item type to count, or all items if not set
               */
               $typestr = $element->GetAttributeByName('_itemtype');
               if(strlen($typestr))
               {
                 $type = $objItemTypes->GetTypeByName($typestr);
                 if(is_object($type))
                 {
                   $ForceUpdate = 1;
                   $TypeId = $type->Get("ItemType");
            	   $GroupOnly = $element->GetAttributeByName('_grouponly') ? 1 : 0;
            	   $txt = "<inp:m_itemcount _CatId=\"".$this->Get("CategoryId")."\" _SubCats=\"1\" ";
            	   $txt .=" _ListType=\"category\" _CountCurrent=\"1\" _ForceUpdate=\"$ForceUpdate\" _ItemType=\"$TypeId\" _GroupOnly=\"$GroupOnly\" />";
            	   $tag = new clsHtmlTag($txt);
            	   $ret = $tag->Execute();	
            	   //echo "Category parseobject: $ret<br>";
                 }
                 else
                   $ret = "";
               }
               else
               {
               	   $ret = (int)$objCountCache->GetCatListTotal($this->Get("CategoryId"));
               }
            break;
            case "totalitems":
               /*
               @field:cat.totalitems
               @description:returns the number of items in the category and all subcategories               
               */           
                $ret = $this->ItemCount();               
            break;
            
            case 'modified':
            	$ret = '';
            	$date = $this->Get('Modified');
            	if(!$date) $date = $this->Get('CreatedOn');
            	if( $element->GetAttributeByName('_tz') )
            	{
            		$date = GetLocalTime($date,$objSession->Get("tz"));
            	}

            	$part = strtolower($element->GetAttributeByName('_part') );
            	if(strlen($part))
            	{
            		$ret = ExtractDatePart($part,$date);
            	}
            	else
            	{
            		$ret = ($date <= 0) ? '' : LangDate($date);
            	}
            	break;
            
            case "itemdate":
            	/*
            	@field:cat.itemdate
            	@description:Returns the date the cache count was last updated
            	@attrib:_itemtype:Item name to check
  				@attrib:_tz:bool:Convert the date to the user's local time
  				@attrib:_part::Returns part of the date.  The following options are available: month,day,year,time_24hr,time_12hr            	
            	*/
                $typestr = $element->GetAttributeByName('_itemtype');
                $type = $objItemTypes->GetTypeByName($typestr);
                if(is_object($type))
                {
                    $TypeId = $type->Get("ItemType");
                    $cc = $objCountCache->GetCountObject(1,$TypeId,$this->get("CategoryId"),0);
                    if(is_object($cc))
                    {
                    	$date = $cc->Get("LastUpdate");
                    }
                    else  
                      $date = "";
                      
                    //$date = $this->GetCacheCountDate($TypeId);
                    if( $element->GetAttributeByName('_tz') )
                    {
                        $date = GetLocalTime($date,$objSession->Get("tz"));
                    }

                    $part = strtolower($element->GetAttributeByName('_part') );
                    if(strlen($part))
                    {
                        $ret = ExtractDatePart($part,$date);
                    }
                    else
                    {
                      if($date<=0)
                      {
                        $ret = "";
                      }
                      else
                        $ret = LangDate($date);
                    }
                }
                else
                  $ret = "";
            break;
            case "new":
            	/*
 				@field:cat.new
 				@description:returns text if category's status is "new"
  				@attrib:_label:lang: Text to return if status is new            
  				*/            
                if($this->IsNewItem())
                {
                  $ret = $element->GetAttributeByName('_label');
                  if(!strlen($ret))
                    $ret = "lu_new";
                  $ret = language($ret);
                }
                else
                 $ret = "";
           		break;
            case "pick":
            	/*
 				@field:cat.pick
 				@description:returns text if article's status is "hot"
  				@attrib:_label:lang: Text to return if status is "hot"            
  				*/               
                if($this->Get("EditorsPick")==1)
                {
                  $ret = $element->GetAttributeByName('_label');
                  if(!strlen($ret))
                    $ret = "lu_pick";
                  $ret = language($ret);
                }
                else
                   $ret = "";
            	break;
                        
            case "parsetag":
            	/*
 				@field:cat.parsetag
 				@description:returns a tag output with this categoriy set as a current category
  				@attrib:_tag:: tag name            
  				*/               
                
  				$tag = new clsHtmlTag();
                $tag->name = $element->GetAttributeByName('_tag');
                $tag->attributes = $element->attributes;               
                $tag->attributes["_catid"] = $this->Get("CategoryId"); 
                $ret = $tag->Execute();               
            	break;
            	
           
            /*
            @field:cat.relevance
           	@description:Displays the category relevance in search results
  			@attrib:_displaymode:: How the relevance should be displayed<br>
  				<UL>
  					<LI>"Numerical": Show the decimal value 
  				    <LI>"Bar": Show the HTML representing the relevance. Returns two HTML cells &lg;td&lt; with specified background colors
  				    <LI>"Graphical":Show image representing the relevance
  				</UL>  
			@attrib:_onimage::Zero relevance image shown in graphical display mode. Also used as prefix to build other images (i.e. prefix+"_"+percentage+".file_extension"
			@attrib:_OffBackGroundColor::Off background color of HTML cell in bar display mode
			@attrib:_OnBackGroundColor::On background color of HTML cell in bar display mode           
            */  
            
            }
            if( !isset($ret) ) $ret = parent::ParseObject($element);
            
        }
        return $ret;
    }


    function parsetag($tag)
    { 
        global $objConfig,$objUsers, $m_var_list, $m_var_list_update;
        if(is_object($tag))
        {        
            $tagname = $tag->name;
        }
        else
            $tagname = $tag;

        switch($tagname)
        {
          case "cat_id":
                return $this->Get("CategoryId");
                break;        
          case "cat_parent":
                return $this->Get("ParentId");
                break;  
          case "cat_fullpath":
                return $this->Get("CachedNavbar");
                break;
          case "cat_name":
                return $this->Get("Name");
                break;
          case "cat_desc":
                return $this->Get("Description");
                break;
          case "cat_priority":
                if($this->Get("Priority")!=0)
                {               
                  return (int)$this->Get("Priority");
                }
                else
                  return "";
                break;
          case "cat_pick":
                if ($this->Get("EditorsPick"))
                    return "pick";
                break;
          case "cat_status":
                return $this->Get("Status");
                break;
          case "cat_Pending":
                return $this->Get("Name");
                break;

          case "cat_pop":
                if($this->IsPopItem())
                    return "pop";
                break;
          case "cat_new":
                if($this->IsNewItem())
                    return "new";
                break;
          case "cat_hot":
                if($this->IsHotItem())
                    return "hot";
                break;
          case "cat_metakeywords":
                return $this->Get("MetaKeywords");
                break;
          case "cat_metadesc":
                return $this->Get("MetaDescription");
                break;
          case "cat_createdby":
                return $objUsers->GetUserName($this->Get("CreatedById"));
                break;
          case "cat_resourceid":
                return $this->Get("ResourceId");
                break;
          case "cat_sub_cats":
                return $this->GetSubCats($objConfig->Get("SubCat_ListCount"));
                break;
            case "cat_link":
                return $this->Cat_Link();
                break;
            case "subcat_count":
                return $this->SubCatCount();
                break;
            case "cat_itemcount":
                return (int)$this->GetTotalItemCount();
                break;
            case "cat_link_admin":
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $m_var_list_update["p"] = 1;
                $ret = $_SERVER["PHP_SELF"]."?env=" . BuildEnv();
                unset($m_var_list_update["cat"]);
                unset($m_var_list_update["p"]);
                return $ret;
                break;
            case "cat_admin_icon":
                $ret = $this->StatusIcon();
                return $ret;
                break;
            case "cat_link_selector":
                $m_var_list_update["cat"] = $this->Get("CategoryId");
                $ret = $_SERVER["PHP_SELF"]."?env=" . BuildEnv();
                unset($m_var_list_update["cat"]);
                return $ret;
                break;

            case "cat_link_edit":
                $m_var_list_update["id"] = $this->Get("CategoryId");
                $ret = "addcategory.php?env=" . BuildEnv();
                unset($m_var_list_update["id"]);
                return $ret;
                break;        

            case "cat_date":               
                return LangDate($this->Get("CreatedOn"));
                break;
            case "cat_num_cats":
                return $this->Get("CachedDescendantCatsQty");
                break;
         case "cell_back":
             if ($m_var_list_update["cat_cell"]=="#cccccc")
             {
                 $m_var_list_update["cat_cell"]="#ffffff";
                 return "#ffffff";
             }
             else
             {
                 $m_var_list_update["cat_cell"]="#cccccc";
                 return "#cccccc";
             }
             break;
          default:
              return "Undefined:$tagname";
        }
    }

    function ParentNames()
    {
        global $objCatList;

        if(strlen($this->Get("CachedNavbar"))==0)
        {
          $nav = "";
          //echo "Rebuilding Navbar..<br>\n";
          if(strlen($this->Get("ParentPath"))==0)
          {   
            $this->UpdateCachedPath();
          }
          $cats = explode("|",substr($this->Get("ParentPath"),1,-1));
          
          foreach($cats as $catid)
          { 
              $cat =& $objCatList->GetCategory($catid);
              if(is_object($cat))
              { 
                  if(strlen($cat->Get("Name")))
                      $names[] = $cat->Get("Name");   
                
              }
          }       
          $nav = implode(">", $names);
          $this->Set("CachedNavbar",$nav);
          $this->Update();
        }
        $res = explode(">",$this->Get("CachedNavbar"));
        return $res;
    }

// not used anywhere
    /*    function UpdateCacheCounts()
    {
    	global $objItemTypes;

    	$CatId = $this->Get("CategoryId");

    	if($CatId>0)
    	{
    		//echo "Updating count for ".$this->Get("CachedNavbar")."<br>\n";
    		UpdateCategoryCount(0,$CatId);
    	}
    }*/
    
    /**
    * @return void
    * @param int $date
    * @desc Set Modified field for category & all it's parent categories
    */
    function SetLastUpdate($date)
    {
    	$parents = $this->Get('ParentPath');
    	$parents = substr($parents, 1, strlen($parents) - 2 );
    	$parents = explode('|', $parents);
    	
    	$db =&GetADODBConnection();
    	$sql = 'UPDATE '.$this->tablename.' SET Modified = '.$date.' WHERE CategoryId IN ('.implode(',', $parents).')';
    	$db->Execute($sql);
    }
    
      
}

class clsCatList extends clsItemList //clsItemCollection
{
  	//var $Page; // no need because clsItemList class used instead of clsItemCollection
  	//var $PerPageVar;

  	function clsCatList()
  	{
  	    global $m_var_list;
  	    $this->clsItemCollection();
  	    $this->classname="clsCategory";
  	    $this->AdminSearchFields = array("Name","Description");
  	    $this->Page = (int)$m_var_list["p"];
  	    $this->PerPageVar = "Perpage_Category";
  	    $this->SourceTable = GetTablePrefix()."Category";
  	    $this->BasePermission="CATEGORY";
  	    $this->DefaultPerPage = 20;
  	}

	function SaveNewPage()
    {
    	global $m_var_list;
    	$m_var_list["p"] = $this->Page;
    }

  function GetCountSQL($PermName,$CatId=NULL, $GroupId=NULL, $AdditonalWhere="")
  {
  	global $objSession, $objPermissions, $objCatList;
  	
  	$ltable = $this->SourceTable;
    $acl = $objSession->GetACLClause();
    $cattable = GetTablePrefix()."CategoryItems";
    $CategoryTable = GetTablePrefix()."Category";
    $ptable = GetTablePrefix()."PermCache";  	
    $VIEW = $objPermissions->GetPermId($PermName);
  	
  	$sql = "SELECT count(*) as CacheVal FROM $ltable ";
    $sql .="INNER JOIN $ptable ON ($ltable.CategoryId=$ptable.CategoryId) ";
    $sql .="WHERE ($acl AND PermId=$VIEW AND $ltable.Status=1) ";

	if(strlen($AdditonalWhere)>0)
    {
      $sql .= "AND (".$AdditonalWhere.")";
    }
    return $sql;
  }  

  function CountCategories($attribs)
  {
  	global $objSession;
  	$ParentWhere='';
  	$cat = getArrayValue($attribs,'_catid');
    if(!is_numeric($cat))
    {
        $cat = $this->CurrentCategoryID();
    }
    if((int)$cat>0)
        $c = $this->GetCategory($cat);
        
  	if( getArrayValue($attribs,'_subcats') && $cat>0)
    {
       $ParentWhere = "(ParentPath LIKE '".$c->Get("ParentPath")."%' AND ".$this->SourceTable.".CategoryId != $cat)";
    }
    if( getArrayValue($attribs,'_today') )
    {
      $today = mktime(0,0,0,date("m"),date("d"),date("Y"));             
      $TodayWhere = "(CreatedOn>=$today)";	
    }
    else
    {
    	$TodayWhere = '';
    }
    if( getArrayValue($attribs,'_grouponly') )
	{
	   $GroupList = $objSession->Get("GroupList");	    	  
	}
	else        		
	   $GroupList = NULL;
	       	
	$where = "";
	if(strlen($ParentWhere))
	{
	  $where = $ParentWhere;    
	}
	if(strlen($TodayWhere))
	{
	  if(strlen($where))	       		
	     $where .=" AND ";
	   $where .= $TodayWhere;
	}
    $sql = $this->GetCountSQL("CATEGORY.VIEW",$cat,$GroupList,$where);
    
//    echo "SQL: ".$sql."<BR>";
    
	$rs = $this->adodbConnection->Execute($sql);  
	if($rs && !$rs->EOF)
	{
	   $ret = $rs->fields["CacheVal"];		  
	}
	else
	  $ret = 0;
	  
	return $ret;  
  }         
    
  function CurrentCategoryID()
  {
      global $m_var_list;      
      return (int)$m_var_list["cat"];
  }

  function NumCategories()
  {
      return $this->NumItems();
  }

  function &CurrentCat()
  {
      //return $this->GetCategory($this->CurrentCategoryID());
      return $this->GetItem($this->CurrentCategoryID());
  }

  function &GetCategory($CatID)
  {
      return $this->GetItem($CatID);
  }

  function GetByResource($ResId)
  {
      return $this->GetItemByField("ResourceId",$ResId);
  }

  function QueryOrderByClause($EditorsPick=FALSE,$Priority=FALSE,$UseTableName=FALSE)  
  {
  	global $objSession;
  	
    if($UseTableName)
    {
      $TableName = $this->SourceTable.".";
    }
    else
      $TableName = "";	

    $Orders = array();

    if($EditorsPick)  
    {
    	$Orders[] = $TableName."EditorsPick DESC";
    }
    if($Priority)
    {
       $Orders[] = $TableName."Priority DESC";  	
    }
  
  	$FieldVar = "Category_Sortfield";
    $OrderVar = "Category_Sortorder";
       	 	   
    if(is_object($objSession))
    {
      if(strlen($objSession->GetPersistantVariable($FieldVar))>0)  
      {  	
      		$Orders[] = trim($TableName.$objSession->GetPersistantVariable($FieldVar) . " ". 
          		       $objSession->GetPersistantVariable($OrderVar));            	
      }
    }

  	$FieldVar = "Category_Sortfield2";
    $OrderVar = "Category_Sortorder2";
       	 	   
    if(is_object($objSession))
    {
      if(strlen($objSession->GetPersistantVariable($FieldVar))>0)  
      {  		
      		$Orders[] = trim($TableName.$objSession->GetPersistantVariable($FieldVar) . " ". 
          		       $objSession->GetPersistantVariable($OrderVar));            	
      }
    }
    
    
    if(count($Orders)>0)
    {
    	$OrderBy = "ORDER BY ".implode(", ",$Orders);
    }
    else   
      $OrderBy="";
    return $OrderBy; 
  }
  

	function LoadCategories($where="", $orderBy = "", $no_limit = true, $fix_method = 'set_first')
  	{
      	// load category list using $where clause
      	// apply ordering specified in $orderBy
      	// show all cats ($no_limit = true) or only from current page ($no_limit = false)
      	// in case if stored page is greather then page count issue page_fixing with
      	// method specified (see "FixInvalidPage" method for details)
      	$PerPage = $this->GetPerPage();
      
      	$this->QueryItemCount = TableCount($this->SourceTable,$where,0);
      	if($no_limit == false)
      	{
      	    $this->FixInvalidPage($fix_method);
      	    $Start = !empty($this->Page)? (($this->Page-1) * $PerPage) : 0;
      	    $limit = "LIMIT ".$Start.",".$PerPage;
      	}
      	else
      		$limit = NULL;           
      
      	return $this->Query_Category($where, $orderBy, $limit);
  	}

  function Query_Category($whereClause="",$orderByClause="",$limit=NULL)
  {   
    global $m_var_list, $objSession, $Errors, $objPermissions;
    $GroupID = $objSession->Get("GroupID");
    $resultset = array();

   $table = $this->SourceTable;
   $ptable = GetTablePrefix()."PermCache";
   $CAT_VIEW = $objPermissions->GetPermId("CATEGORY.VIEW");
   if(!$objSession->HasSystemPermission("ADMIN"))
   {   
     $sql = "SELECT * FROM $table INNER JOIN $ptable ON ($ptable.CategoryId=$table.CategoryId)";
     $acl_where = $objSession->GetACLClause(); 
     if(strlen($whereClause))
     {    
        $sql .= " WHERE ($acl_where) AND PermId=$CAT_VIEW AND ".$whereClause;
     }
     else
        $sql .= " WHERE ($acl_where) AND PermId=$CAT_VIEW ";
   }
   else
   {
       $sql ="SELECT * FROM $table ".($whereClause ? "WHERE $whereClause" : '');
   }
   $sql .=" ".$orderByClause;

   if(isset($limit) && strlen(trim($limit)))
       $sql .= " ".$limit;
    if($objSession->HasSystemPermission("DEBUG.LIST"))
      echo $sql;
    //echo "SQL: $sql<br>";
    return $this->Query_item($sql);
  }
  
  function CountPending()
  {
      return TableCount($this->SourceTable,"Status=".STATUS_PENDING,0);
  }

  function GetPageLinkList($dest_template=NULL,$page="",$PagesToList=10,$HideEmpty=TRUE)
  {
      global $objConfig, $m_var_list_update, $var_list_update, $var_list;

//      if(!strlen($page)) $page = GetIndexURL(2);

      $PerPage = $this->GetPerPage();
      $NumPages = ceil( $this->GetNumPages($PerPage) );
      
      if($NumPages == 1 && $HideEmpty) return '';

      if(strlen($dest_template))
      {
          $var_list_update["t"] = $dest_template;
      }
      else
          $var_list_update["t"] = $var_list["t"];

      $o = "";
      if($this->Page>$NumPages)
          $this->Page=$NumPages;

      $StartPage = (int)$this->Page - ($PagesToList/2);
      if($StartPage<1)
          $StartPage=1;

      $EndPage = $StartPage+($PagesToList-1);
      if($EndPage>$NumPages)
      {
          $EndPage = $NumPages;
          $StartPage = $EndPage-($PagesToList-1);
          if($StartPage<1)
              $StartPage=1;
      }

      $o = "";
      if($StartPage>1)
      {
        $m_var_list_update["p"] = $this->Page-$PagesToList;
        $prev_url = HREF_Wrapper();
        $o .= "<A HREF=\"$prev_url\">&lt;&lt;</A>";
      }


      for($p=$StartPage;$p<=$EndPage;$p++)
      {
          if($p!=$this->Page)
          {
              $m_var_list_update["p"]=$p;
              $href = HREF_Wrapper();
              $o .= " <A HREF=\"$href\" >$p</A> ";
          }
          else
          {
              $o .= "$p";
          }
      }
      if($EndPage<$NumPages && $EndPage>0)
      {
        $m_var_list_update["p"]=$this->Page+$PagesToList;
        $next_url = HREF_Wrapper();
        $o .= "<A HREF=\"$next_url\"> &gt;&gt;</A>";
      }
      unset($m_var_list_update,$var_list_update["t"] );
      return $o;
  }
	
  	function GetAdminPageLinkList($url)
  	{
      	global $objConfig, $m_var_list_update, $var_list_update, $var_list;
		
		$PerPage = $this->GetPerPage();
      	$NumPages = ceil($this->GetNumPages($PerPage));
      	$o = "";

      	if($this->Page>1)
      	{
        	$m_var_list_update["p"]=$this->Page-1;
        	$prev_url = $url."?env=".BuildEnv();
        	unset($m_var_list_update["p"]);
         	$o .= "<A HREF=\"$prev_url\" class=\"NAV_URL\"><<</A>";
      	}

      	if($this->Page<$NumPages)
      	{
        	$m_var_list_update["p"]=$this->Page+1;
        	$next_url = $url."?env=".BuildEnv();
        	unset($m_var_list_update["p"]);
      	}

      	for($p=1;$p<=$NumPages;$p++)
      	{
          	if($p != $this->Page)
          	{
              	$m_var_list_update["p"]=$p;
              	$href = $url."?env=".BuildEnv();
              	unset($m_var_list_update["p"]);
              	$o .=  " <A HREF=\"$href\" class=\"NAV_URL\">$p</A> ";
          	}
          	else
              $o .= "<SPAN class=\"CURRENT_PAGE\">$p</SPAN>";
      	}
      	
      	if($this->Page < $NumPages)
         	$o .= "<A HREF=\"$next_url\" class=\"NAV_URL\">>></A>";

      	return $o;        
  	}

  function Search_Category($orderByClause)
  {
    global $objSession, $objConfig, $Errors;    

    $PerPage = $this->GetPerPage();
    $Start = ($this->Page-1) * $PerPage;
    $objResults = new clsSearchResults("Category","clsCategory");
    $this->Clear();
    $this->Categories = $objResults->LoadSearchResults($Start,$PerPage);
    
    return $this->Categories;
  }


  function GetSubCats($ParentCat)
  {   
      return $this->Query_Category("ParentId=".$ParentCat,"");
  }

  function AllSubCats($ParentCat)
  {
      $c =& $this->GetCategory($ParentCat);
      $sql = "SELECT * FROM ".$this->SourceTable." WHERE ParentPath LIKE '".$c->Get("ParentPath")."%'";
      $rs = $this->adodbConnection->Execute($sql);
      $subcats = array();
      while($rs && !$rs->EOF)
      {
      	  if($rs->fields["CategoryId"]!=$ParentCat)
      	  {
            $subcats[] = $rs->fields["CategoryId"];
      	  }
          $rs->MoveNext();
      }
	  if($ParentCat>0)
	  {
        if($c->Get("CachedDescendantCatsQty")!=count($subcats))
        {
          $c->Set("CachedDescendantCatsQty",count($subcats));
        }
	  }
      return $subcats;
  }

  function cat_navbar($admin=0, $cat, $target_template, $separator = " > ", $LinkLeaf = FALSE,
  					  $root = 0,$RootTemplate="",$modcat=0, $ModTemplate="", $LinkRoot = FALSE)  					 
  {
    // draw category navigation bar (at top)
    global $Errors, $var_list_update, $var_list, $m_var_list_update, $m_var_list, $objConfig;

    $url_params = Array('reset' => 1);
	if( GetVar('Selector') ) $url_params['Selector'] = GetVar('Selector');
	if( GetVar('new') ) $url_params['new'] = GetVar('new');
	if( GetVar('destform') ) $url_params['destform'] = GetVar('destform');
	
    $nav = "";
    $m_var_list_update["p"]=1;   
    if(strlen($target_template)==0)
      $target_template = $var_list["t"];
   
   
    if($cat == 0)
    {
        $cat_name = language($objConfig->Get("Root_Name"));
        if ($LinkRoot)
        {
        	$var_list_update["t"] = strlen($RootTemplate)? $RootTemplate : $target_template;
        	$nav = "<a class=\"navbar\" href=\"" . HREF_Wrapper('', $url_params) . "\">$cat_name</a>";	}
        else
        	$nav = "<span class=\"NAV_CURRENT_ITEM\">$cat_name</span>";
    }
    else
    {
      $nav = array();
      $c =& $this->GetCategory($cat);
      $nav_unparsed = $c->Get("ParentPath");    
      if(strlen($nav_unparsed)==0)
      {  
        $c->UpdateCachedPath();
        $nav_unparsed = $c->Get("ParentPath");
      }
      //echo " Before $nav_unparsed ";
      if($root)
      {      
        $r =& $this->GetCategory($root);
        $rpath = $r->Get("ParentPath");
        $nav_unparsed = substr($nav_unparsed,strlen($rpath),-1);
        $cat_name = $r->Get("Name");
        $m_var_list_update["cat"] = $root;
        if($cat == $catid && !$LinkLeaf)
        {
          $nav[] = "<span class=\"NAV_CURRENT_ITEM\" >".$cat_name."</span>";         //href=\"browse.php?env=". BuildEnv() ."\"
        }
        else
        {          
          if ($admin == 1)       
          {
            $nav[] = "<a class=\"control_link\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
          }
          else
          {
            if(strlen($RootTemplate))
            {
                $var_list_update["t"] = $RootTemplate;
            }
            else
            {
              $var_list_update["t"] = $target_template;
            }
            $nav[] = "<a class=\"navbar\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
          }
        }
      }
      else
      {      
        $nav_unparsed = substr($nav_unparsed,1,-1);
        $cat_name = language($objConfig->Get("Root_Name"));
        $m_var_list_update["cat"] = 0;
        if($cat == 0)
        {
          $nav[] = "<span class=\"NAV_CURRENT_ITEM\" >".$cat_name."</span>";         //href=\"browse.php?env=". BuildEnv() ."\"
        }
        else
        {          
          if ($admin == 1)       
          {
            $nav[] = "<a class=\"control_link\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
          }
          else
          {
            if(strlen($RootTemplate))
            {
              $var_list_update["t"] = $RootTemplate;
            }
            else
              $var_list_update["t"] = $target_template;
            $nav[] = "<a class=\"navbar\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
          }
        }
        
      }
      //echo " After $nav_unparsed <br>\n";
      if(strlen($target_template)==0)
        $target_template = $var_list["t"];    
        
      $cats = explode("|", $nav_unparsed);
      foreach($cats as $catid)
      {
          if($catid)
          {          
            $c =& $this->GetCategory($catid);
            if(is_object($c))
            {          
              $cat_name = $c->Get("Name");
          
              $m_var_list_update["cat"] = $catid;
              if($catid==$modcat && strlen($ModTemplate)>0)
              {
              	$t = $ModTemplate;
              }
              else 
              	$t = $target_template;
              if($cat == $catid && !$LinkLeaf)
              {
                $nav[] = "<span class=\"NAV_CURRENT_ITEM\" >".$cat_name."</span>";
              }
              else
              {          
                if ($admin == 1)       
                {
                  $nav[] = "<a class=\"control_link\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
                }
                else
                {
                  $var_list_update["t"] = $t;
                  $nav[] = "<a class=\"navbar\" href=\"".HREF_Wrapper('', $url_params)."\">".$cat_name."</a>";
                  unset($var_list_update["t"]);
                }
              }
             unset($m_var_list_update["cat"]); 
            }
          }          
      }
      $nav = implode($separator, $nav);
    }
    return $nav;
  }

  function &Add($ParentId, $Name, $Description, $CreatedOn, $EditorsPick, $Status, $Hot, $New, $Pop,
                $Priority, $MetaKeywords,$MetaDesc)  
  {
      global $objSession;

      $UserId = $objSession->Get("UserId");

      $d = new clsCategory(NULL);
      $d->tablename = $this->SourceTable;
      if($d->UsingTempTable())
        $d->Set("CategoryId",-1);
      $d->idfield = "CategoryId";
      $d->Set(array("ParentId", "Name", "Description", "CreatedOn",  "EditorsPick", "Status", "HotItem", 
                    "NewItem","PopItem", "Priority", "MetaKeywords", "MetaDescription", "CreatedById"), 
              array($ParentId, $Name, $Description, $CreatedOn, $EditorsPick, $Status, $Hot, $New,
                    $Pop, $Priority, $MetaKeywords,$MetaDesc, $UserId));

      $d->Create();
      if($Status==1)
      {      
        $d->SendUserEventMail("CATEGORY.ADD",$objSession->Get("PortalUserId"));
        $d->SendAdminEventMail("CATEGORY.ADD");
      }
      else
      {
          $d->SendUserEventMail("CATEGORY.ADD.PENDING",$objSession->Get("PortalUserId"));
          $d->SendAdminEventMail("CATEGORY.ADD.PENDING");
      }
      $d->UpdateCachedPath();
      //RunUp($ParentId, "Increment_Count");

      return $d;
  }

  function &Edit_Category($CategoryId, $Name, $Description, $CreatedOn, $EditorsPick, $Status, $Hot, 
                         $NewItem, $Pop, $Priority, $MetaKeywords,$MetaDesc) 
  {
      $d =& $this->GetCategory($CategoryId);
      $d->Set(array("Name", "Description", "CreatedOn",  "EditorsPick", "Status", "HotItem",
                    "NewItem", "PopItem", "Priority", "MetaKeywords","MetaDescription"), 
              array($Name, $Description, $CreatedOn,  $EditorsPick, $Status, $Hot, $NewItem, 
                    $Pop, $Priority, $MetaKeywords,$MetaDesc));           
      $d->Update();
      $d->UpdateCachedPath();
      return $d;

  }

  function Move_Category($Id, $ParentTo)
  {
      global $objCatList;
      
      $d =& $this->GetCategory($Id);
      $oldparent = $d->Get("ParentId");      
      $ChildList = $d->GetSubCatIds();
      
      /*
      echo "Target Parent Id: $ParentTo <br>\n";
      echo "<PRE>";print_r($ChildList); echo "</PRE>";
      echo "Old Parent: $oldparent <br>\n";
      echo "ID: $Id <br>\n";
      */
      /* sanity checks */
      
      if(!in_array($ParentTo,$ChildList) && $oldparent != $ParentTo && $oldparent != $Id &&
      	 $Id != $ParentTo)
      {
          $d->Set("ParentId", $ParentTo);
          $d->Update();
          $d->UpdateCachedPath();
          RunUp($oldparent, "Decrement_Count");
          RunUp($ParentTo, "Increment_Count");
          RunDown($ParentTo, "UpdateCachedPath");
          return TRUE; 
      }
      else 
      {
		   	global $Errors;
      		$Errors->AddAdminUserError("la_error_move_subcategory");
        	return FALSE;
      }      
      die();  
  }

  function Copy_CategoryTree($Id, $ParentTo)
  {
      global $PastedCatIds;

      $new = $this->Copy_Category($Id, $ParentTo);
      if($new)
      {
        $PastedCatIds[$Id] = $new;
        $sql = "SELECT CategoryId from ".GetTablePrefix()."Category where ParentId=$Id";
        $result = $this->adodbConnection->Execute($sql);
        if ($result && !$result->EOF)
        {
          while(!$result->EOF)
          {       
              $this->Copy_CategoryTree($result->fields["CategoryId"], $new);
              $result->MoveNext();
          }
        }
      }
      return $new;
  }

  function Copy_Category($Id, $ParentTo)
  {        
      global $objGroups;

      
      $src = $this->GetCategory($Id);
      $Children = $src->GetSubCatIds();
      if($Id==$ParentTo || in_array($ParentTo,$Children))
      {
      	/* sanity error here */
      	global $Errors;
      	$Errors->AddAdminUserError("la_error_copy_subcategory");      	
      	return 0;
      }      
      $dest = $src;
      $dest->Set("ParentId", $ParentTo);
      if ($src->get("ParentId") == $ParentTo)
      {
   		$OldName = $src->Get("Name");
    	if(substr($OldName,0,5)=="Copy ")
    	{
    		$parts = explode(" ",$OldName,4);
    		if($parts[2]=="of" && is_numeric($parts[1]))
    		{
    			$Name = $parts[3];
    		}
    		else
    		  if($parts[1]=="of")
    		  {
    		    $Name = $parts[2]." ".$parts[3];
    		  }
    		  else
    		    $Name = $OldName; 
    	}
    	else
    	  $Name = $OldName;      	
    	//echo "New Name: $Name<br>";
		$dest->Set("Name", $Name);    	  
		$Names = CategoryNameCount($ParentTo,$Name); 
		//echo "Names Count: ".count($Names)."<br>";
    	if(count($Names)>0)
    	{    		
    		$NameCount = count($Names);
    		$found = FALSE;
    		$NewName = "Copy of $Name";
    		
    		if(!in_array($NewName,$Names))
    		{
    			//echo "Matched on $NewName in:<br>\n";
    			$found = TRUE;
    		}
    		else
    		{
    		  for($x=2;$x<$NameCount+2;$x++)
    		  {

    				$NewName = "Copy ".$x." of ".$Name;
    				if(!in_array($NewName,$Names))
    				{
    					$found = TRUE;
    					break;
    				}
    				
    			}
    		}    	
    		if(!$found)
    		{
    			$NameCount++;
    			$NewName = "Copy $NameCount of $Name";
    		} 
    		//echo "New Name: $NewName<br>";
            $dest->Set("Name",$NewName);      
    	}
    	
      }
      $dest->UnsetIdField();
      $dest->Set("CachedDescendantCatsQty",0);
      $dest->Set("ResourceId",NULL);
      $dest->Create();      
      $dest->UpdateCachedPath();     
      $p = new clsPermList();
      $p->Copy_Permissions($src->Get("CategoryId"),$dest->Get("CategoryId"));   
      $glist =  $objGroups->GetAllGroupList();
      $view = $p->GetGroupPermList($dest, "CATEGORY.VIEW", $glist);
      $dest->SetViewPerms("CATEGORY.VIEW",$view,$glist);
      RunUp($ParentTo, "Increment_Count");
      return $dest->Get("CategoryId");     
  }

  function Delete_Category($Id)
  {
      global $objSession;

      $d =& $this->GetCategory($Id);
      
      if(is_object($d))
      {      
        if($d->Get("CategoryId")==$Id)
        {    
          $d->SendUserEventMail("CATEGORY.DELETE",$objSession->Get("PortalUserId"));
          $d->SendAdminEventMail("CATEGORY.DELETE");
          $p =& $this->GetCategory($d->Get("ParentId"));        
          RunDown($d->Get("CategoryId"), "Delete");
          RunUp($p->Get("CategoryId"), "Decrement_Count");   
          RunUp($d->Get("CategoryId"),"ClearCacheData");

        }
      }
  }
  
  function PasteFromClipboard($TargetCat)
  {
        global $objSession;

        $clip = $objSession->GetVariable("ClipBoard");
        if(strlen($clip))
        {
            $ClipBoard = ParseClipboard($clip);
            $IsCopy = (substr($ClipBoard["command"],0,4)=="COPY") || ($ClipBoard["source"] == $TargetCat);

            $item_ids = explode(",",$ClipBoard["ids"]);
            for($i=0;$i<count($item_ids);$i++)
            {   
                $ItemId = $item_ids[$i];
                $item = $this->GetItem($item_ids[$i]);
                if(!$IsCopy)
                { 
                    $this->Move_Category($ItemId, $TargetCat);                    
                    $clip = str_replace("CUT","COPY",$clip);
                    $objSession->SetVariable("ClipBoard",$clip);
                }
                else
                {              
                  $this->Copy_CategoryTree($ItemId,$TargetCat);
                }
            }
        }
      }


  function NumChildren($ParentID)
  {   
   $cat_filter = "m_cat_filter";
   global $$cat_filter;

   $filter = $$cat_filter;
   $adodbConnection = &GetADODBConnection();

   $sql = "SELECT COUNT(Name) as children from ".$this->SourceTable." where ParentId=" . $ParentID . $filter;
   $result = $adodbConnection->Execute($sql);
    return $result->fields["children"];
  }

  function UpdateMissingCacheData()
  {
      $rs = $this->adodbConnection->Execute("SELECT * FROM ".$this->SourceTable." WHERE ParentPath IS NULL or ParentPath=''");
      while($rs && !$rs->EOF)
      {
          $c = new clsCategory(NULL);
          $data = $rs->fields;
          $c->SetFromArray($data);
          $c->UpdateCachedPath();
          $rs->MoveNext();
      }

      $rs = $this->adodbConnection->Execute("SELECT * FROM ".$this->SourceTable." WHERE CachedNavbar IS NULL or CachedNavBar=''");
      while($rs && !$rs->EOF)
      {
          $c = new clsCategory(NULL);
          $data = $rs->fields;
          $c->SetFromArray($data);
          $c->UpdateCachedPath();
          $rs->MoveNext();
      }      
  }
 
  function CopyFromEditTable($idfield)
  {
      global $objGroups, $objSession, $objPermList;
		$GLOBALS['_CopyFromEditTable']=1;
      $objPermList = new clsPermList();
      $edit_table = $objSession->GetEditTable($this->SourceTable);

      $sql = "SELECT * FROM $edit_table";
      $rs = $this->adodbConnection->Execute($sql);
      $item_ids = Array();
      while($rs && !$rs->EOF)
      {
          $data = $rs->fields;
          $c = new $this->classname;
          $c->SetFromArray($data);          
          $c->Dirty();          
          
          if($c->Get("CategoryId")>0)
          {          
            $c->Update();
          }
          else
          {
          	  $c->UnsetIdField();
              $c->Create();
              $sql = "UPDATE ".GetTablePrefix()."Permissions SET CatId=".$c->Get("CategoryId")." WHERE CatId=-1";
              $this->adodbConnection->Execute($sql);
          }
          $c->UpdateCachedPath();                     
          $c->UpdateACL();
          $c->SendUserEventMail("CATEGORY.MODIFY",$objSession->Get("PortalUserId"));
          $c->SendAdminEventMail("CATEGORY.MODIFY");
          $c->Related = new clsRelationshipList();
          if(is_object($c->Related))
          {            
            $r = $c->Related;
            $r->CopyFromEditTable($c->Get("ResourceId"));
          }
          $item_ids[] = $c->UniqueId();
          //RunDown($c->Get("CategoryId"),"UpdateCachedPath");
          //RunDown($c->Get("CategoryId"),"UpdateACL");
          unset($c);
          unset($r);
          $rs->MoveNext();
      }
      @$this->adodbConnection->Execute("DROP TABLE IF EXISTS $edit_table");
		unset($GLOBALS['_CopyFromEditTable']);
      return $item_ids;
      //$this->UpdateMissingCacheData();
  }   
  
  function PurgeEditTable($idfield)
  {
  	 parent::PurgeEditTable($idfield);
  	 $sql = "DELETE FROM ".GetTablePrefix()."Permissions WHERE CatId=-1";
  	 $this->adodbConnection->Execute($sql);
  }

  function GetExclusiveType($CatId)
  {
      global $objItemTypes, $objConfig;

      $itemtype = NULL;
      $c =& $this->GetItem($CatId);
      $path = $c->Get("ParentPath");
      foreach($objItemTypes->Items as $Type)
      {
          $RootVar = $Type->Get("ItemName")."_Root";
          $RootId = $objConfig->Get($RootVar);
          if((int)$RootId)
          {
              $rcat = $this->GetItem($RootId);
              $rpath = $rcat->Get("ParentPath");
              $p = substr($path,0,strlen($rpath));
              //echo $rpath." vs. .$p [$path]<br>\n";
              if($rpath==$p)
              {              
                  $itemtype = $Type;
                  break;
              }
          }      
      }
      return $itemtype;
  }
}

function RunUp($Id, $function, $Param=NULL)
{     
    global $objCatList;

   $d =  $objCatList->GetCategory($Id);
   $ParentId = $d->Get("ParentId");
   if ($ParentId == 0)
    {
        if($Param == NULL)
        {        
        	$d->$function();
        }
        else
        {            
            $d->$function($Param);
        }
    }
   else
   {
      	RunUp($ParentId, $function, $Param);
        if($Param == NULL)
        {        
        $d->$function();
        }
        else
        {               
            $d->$function($Param);
        }

   }
   
}

function RunDown($Id, $function, $Param=NULL)
{
    global $objCatList;

   $adodbConnection = &GetADODBConnection(); 
   $sql = "select CategoryId from ".GetTablePrefix()."Category where ParentId='$Id'";
   $rs = $adodbConnection->Execute($sql);
  
   while($rs && !$rs->EOF) 
   {  
      RunDown($rs->fields["CategoryId"], $function, $Param);           
      $rs->MoveNext();
   }
   $d = $objCatList->GetCategory($Id);
    if($Param == NULL)
    {        
      $d->$function();
    }
    else
        $d->$function($Param);
}
?>