2001-01-11 10:52:33 +01:00
< ? php
2008-03-15 15:52:26 +01:00
/**
* API - Categories
2008-04-16 13:37:47 +02:00
*
2008-03-15 15:52:26 +01:00
* @ link http :// www . egroupware . org
* @ author Joseph Engo < jengo @ phpgroupware . org >
* @ author Bettina Gille < ceb @ phpgroupware . org >
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
* Copyright ( C ) 2000 , 2001 Joseph Engo , Bettina Gille
* Copyright ( C ) 2002 , 2003 Bettina Gille
* Reworked 11 / 2005 by RalfBecker - AT - outdoor - training . de
2008-12-17 21:05:19 +01:00
* Reworked 12 / 2008 by RalfBecker - AT - outdoor - training . de to operate only on a catergory cache , no longer the db direct
2008-03-15 15:52:26 +01:00
* @ license http :// opensource . org / licenses / lgpl - license . php LGPL - GNU Lesser General Public License
* @ package api
* @ subpackage categories
* @ access public
* @ version $Id $
*/
/**
* class to manage categories in eGroupWare
2009-02-25 13:59:28 +01:00
*
2008-12-17 21:05:19 +01:00
* Categories are read now once from the database into a static cache variable ( by the static init_cache method ) .
* The egw object fills that cache ones per session , stores it in a private var , from which it restores it for each
* request of that session .
2011-02-15 14:03:39 +01:00
*
2010-01-31 00:50:51 +01:00
* $cat [ 'data' ] array :
* ------------------
* $cat [ 'data' ] array is stored serialized in the database to allow applications to simply add all
* sorts of values there ( without the hassel of a DB schema change ) .
2011-02-15 14:03:39 +01:00
* Static methods categories :: read ( $cat_id ) and categories :: id2name now returns data already unserialized
* and add () or edit () methods automatically serialize $cat [ 'data' ], if it ' s not yet serialized .
2010-01-31 00:50:51 +01:00
* return * () methods still return $cat [ 'data' ] serialized by default , but have a parameter to return
2011-02-15 14:03:39 +01:00
* it as array () . That default might change in future too , so better check if it ' s
2010-01-31 00:50:51 +01:00
* not already an array , before unserialize !
2009-02-25 13:59:28 +01:00
*
2008-12-17 21:05:19 +01:00
* @ ToDo The cache now contains a backlink from the parent to it ' s children . Use that link to simplyfy return_all_children
* and other functions needing to know if a cat has children . Be aware a user might not see all children , as they can
* belong to other users .
2008-03-15 15:52:26 +01:00
*/
class categories
{
2008-12-17 21:05:19 +01:00
/**
2010-01-31 00:50:51 +01:00
* Account id this class is instanciated for ( self :: GLOBAL_ACCOUNT for global cats )
2008-12-17 21:05:19 +01:00
*
* @ var int
*/
2009-05-14 09:59:51 +02:00
public $account_id ;
2008-12-17 21:05:19 +01:00
/**
2010-01-31 00:50:51 +01:00
* Application this class is instancated for ( self :: GLOBAL_APPNAME for application global cats )
2008-12-17 21:05:19 +01:00
*
* @ var string
*/
2009-05-14 09:59:51 +02:00
public $app_name ;
2008-03-15 15:52:26 +01:00
/**
* @ var egw_db
*/
2009-05-14 09:59:51 +02:00
private $db ;
2008-12-17 21:05:19 +01:00
/**
* Total number of records of return_ ( sorted_ ) array ( returning only a limited number of rows )
*
* @ var int
*/
2009-05-14 09:59:51 +02:00
public $total_records ;
2008-12-17 21:05:19 +01:00
/**
* Grants from other users for account_id and app_name ( init by return array )
*
* @ var array
*/
2009-05-14 09:59:51 +02:00
public $grants ;
2008-12-17 21:05:19 +01:00
/**
* Name of the categories table
*/
const TABLE = 'egw_categories' ;
2009-02-25 15:35:43 +01:00
/**
2009-05-14 09:59:51 +02:00
* @ deprecated use categories :: TABLE
2009-02-25 15:35:43 +01:00
* @ var string
*/
2009-05-14 09:59:51 +02:00
public $table = self :: TABLE ;
2008-12-17 21:05:19 +01:00
/**
* Cache holding all categories , set via init_cache () method
*
2009-02-25 13:59:28 +01:00
* @ var array cat_id => array of data
2008-12-17 21:05:19 +01:00
*/
private static $cache ;
2012-02-10 21:14:44 +01:00
const CACHE_APP = 'phpgwapi' ;
const CACHE_NAME = 'cat_cache' ;
2009-02-25 13:59:28 +01:00
2010-01-31 00:50:51 +01:00
/**
* Appname for global categories
*/
const GLOBAL_APPNAME = 'phpgw' ;
2011-02-15 14:03:39 +01:00
2010-01-31 00:50:51 +01:00
/**
* account_id for global categories
*/
2010-01-31 04:36:01 +01:00
const GLOBAL_ACCOUNT = 0 ;
2011-02-15 14:03:39 +01:00
2010-01-31 04:36:01 +01:00
/**
* Owners for global accounts
2011-02-15 14:03:39 +01:00
*
2010-01-31 04:36:01 +01:00
* Usually the users group memberships and self :: GLOBAL_ACCOUNT
2011-02-15 14:03:39 +01:00
*
2010-01-31 04:36:01 +01:00
* @ var array
*/
private $global_owners = array ( self :: GLOBAL_ACCOUNT );
2011-02-15 14:03:39 +01:00
2013-02-05 12:24:02 +01:00
/**
* string to postfix global cats
*
* @ var string
*/
static public $global_marker ;
2005-11-05 23:58:51 +01:00
/**
2008-03-15 15:52:26 +01:00
* constructor for categories class
2005-11-05 23:58:51 +01:00
*
2010-01-31 00:50:51 +01:00
* @ param int | string $accountid = '' account id or lid , default to current user
2008-03-15 15:52:26 +01:00
* @ param string $app_name = '' app name defaults to current app
2005-11-05 23:58:51 +01:00
*/
2009-05-14 09:59:51 +02:00
function __construct ( $accountid = '' , $app_name = '' )
2001-03-12 13:16:14 +01:00
{
2008-03-15 15:52:26 +01:00
if ( ! $app_name ) $app_name = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ];
2010-01-31 04:36:01 +01:00
if ( $accountid === self :: GLOBAL_ACCOUNT ||
$accountid < 0 && $GLOBALS [ 'egw' ] -> accounts -> exists ( $accountid ) == 2 )
2010-01-31 00:50:51 +01:00
{
2010-01-31 04:36:01 +01:00
$this -> account_id = ( int ) $accountid ;
2010-01-31 00:50:51 +01:00
}
else
{
$this -> account_id = ( int ) get_account_id ( $accountid );
2012-08-08 12:47:47 +02:00
$this -> global_owners = $this -> account_id ? $GLOBALS [ 'egw' ] -> accounts -> memberships ( $this -> account_id , true ) : array ();
2010-01-31 04:36:01 +01:00
$this -> global_owners [] = self :: GLOBAL_ACCOUNT ;
2010-01-31 00:50:51 +01:00
}
2008-03-15 15:52:26 +01:00
$this -> app_name = $app_name ;
$this -> db = $GLOBALS [ 'egw' ] -> db ;
2002-01-03 18:16:56 +01:00
2012-08-08 18:25:03 +02:00
if ( is_null ( self :: $cache )) // should not be necessary, as cache is load and restored by egw object
2001-03-12 13:16:14 +01:00
{
2008-12-17 21:05:19 +01:00
self :: init_cache ();
2001-03-12 13:16:14 +01:00
}
2013-02-05 12:24:02 +01:00
if ( is_null ( self :: $global_marker ))
{
// as et2 adds options with .text(), it can't be entities, but php knows no string literals with utf-8
self :: $global_marker = html_entity_decode ( ' ♦' , ENT_NOQUOTES , 'utf-8' );
}
2008-03-15 15:52:26 +01:00
}
2001-03-12 13:16:14 +01:00
2009-05-14 09:59:51 +02:00
/**
* php4 constructor
*
* @ deprecated
*/
function categories ( $accountid = '' , $app_name = '' )
{
self :: __construct ( $accountid , $app_name );
}
2008-03-15 15:52:26 +01:00
/**
* returns array with id ' s of all children from $cat_id and $cat_id itself !
*
2008-12-17 21:05:19 +01:00
* @ param int | array $cat_id ( array of ) integer cat - id to search for
2008-03-15 15:52:26 +01:00
* @ return array of cat - id ' s
*/
function return_all_children ( $cat_id )
{
2008-12-17 21:05:19 +01:00
$all_children = ( array ) $cat_id ;
2004-04-05 00:21:26 +02:00
2008-03-15 15:52:26 +01:00
$children = $this -> return_array ( 'subs' , 0 , False , '' , '' , '' , True , $cat_id , - 1 , 'id' );
if ( is_array ( $children ) && count ( $children ))
{
2008-12-17 21:05:19 +01:00
$all_children = array_merge ( $all_children , $this -> return_all_children ( $children ));
2008-03-15 15:52:26 +01:00
}
//echo "<p>categories::return_all_children($cat_id)=(".implode(',',$all_children).")</p>\n";
return $all_children ;
}
2001-03-12 13:16:14 +01:00
2008-03-15 15:52:26 +01:00
/**
* return an array populated with categories
*
2008-12-17 21:05:19 +01:00
* @ param string $type = 'all' 'subs' , 'mains' , 'appandmains' , 'appandsubs' , 'noglobal' , 'noglobalapp' , defaults to 'all'
* @ param int $start = 0 see $limit
* @ param boolean | int $limit if true limited query to maxmatches rows ( starting with $start )
2008-03-15 15:52:26 +01:00
* @ param string $query = '' query - pattern
2008-12-17 21:05:19 +01:00
* @ param string $sort = 'ASC' sort order , defaults to 'ASC'
* @ param string $order = '' order by , default cat_main , cat_level , cat_name ASC
2011-06-24 15:35:39 +02:00
* @ param boolean | string $globals includes the global egroupware categories or not ,
* 'all_no_acl' to return global and all non - private user categories independent of ACL
2008-12-17 21:05:19 +01:00
* @ param array | int $parent_id = null return only subcats of $parent_id ( s )
2008-03-15 15:52:26 +01:00
* @ param int $lastmod = - 1 if > 0 return only cats modified since then
* @ param string $column = '' if column - name given only that column is returned , not the full array with all cat - data
2008-12-17 21:05:19 +01:00
* @ param array $filter = null array with column - name ( without cat_ - prefix ) => value pairs ( ! negates the value )
2010-01-31 00:50:51 +01:00
* @ param boolean $unserialize_data = false return $cat [ 'data' ] as array ( not serialized array )
2008-12-17 21:05:19 +01:00
* @ return array of cat - arrays or $column values
2008-03-15 15:52:26 +01:00
*/
2010-01-31 00:50:51 +01:00
function return_array ( $type = 'all' , $start = 0 , $limit = true , $query = '' , $sort = 'ASC' , $order = '' , $globals = false , $parent_id = null , $lastmod =- 1 , $column = '' , $filter = null , $unserialize_data = false )
2008-03-15 15:52:26 +01:00
{
2010-01-31 00:50:51 +01:00
//error_log(__METHOD__."($type,$start,$limit,$query,$sort,$order,globals=$globals,parent=".array2string($parent_id).",$lastmod,$column,filter=".array2string($filter).",$unserialize_data) account_id=$this->account_id, appname=$this->app_name: ".function_backtrace());
2008-12-17 21:05:19 +01:00
$cats = array ();
foreach ( self :: $cache as $cat_id => $cat )
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
if ( $filter ) foreach ( $filter as $col => $val )
{
if ( ! is_array ( $val ) && $val [ 0 ] === '!' )
{
2009-11-16 07:54:03 +01:00
// also match against trimmed database entry on name and description fields
if (( $col == 'name' || $col == 'description' ) && is_string ( $cat [ $col ]))
{
if ( $cat [ $col ] == substr ( $val , 1 ) || trim ( $cat [ $col ]) == substr ( $val , 1 )) continue 2 ;
}
else
{
if ( $cat [ $col ] == substr ( $val , 1 )) continue 2 ;
}
2008-12-17 21:05:19 +01:00
}
elseif ( is_array ( $val ))
{
2009-11-16 07:54:03 +01:00
// also match against trimmed database entry on name and description fields
if (( $col == 'name' || $col == 'description' ) && is_string ( $cat [ $col ]))
{
if ( ! in_array ( $cat [ $col ], $val ) && ! in_array ( trim ( $cat [ $col ]), $val )) continue 2 ;
}
else
{
if ( ! in_array ( $cat [ $col ], $val )) continue 2 ;
}
2008-12-17 21:05:19 +01:00
}
else
{
2009-11-16 07:54:03 +01:00
// also match against trimmed database entry on name and description fields
if (( $col == 'name' || $col == 'description' ) && is_string ( $cat [ $col ]))
{
if ( $cat [ $col ] != $val && trim ( $cat [ $col ]) != $val ) continue 2 ;
}
else
{
if ( $cat [ $col ] != $val ) continue 2 ;
}
2008-12-17 21:05:19 +01:00
}
}
// check if certain parent required
if ( $parent_id && ! in_array ( $cat [ 'parent' ],( array ) $parent_id )) continue ;
2009-02-25 13:59:28 +01:00
2009-11-18 08:42:14 +01:00
// return global categories just if $globals is set
2010-01-31 00:50:51 +01:00
if ( ! $globals && $cat [ 'appname' ] == self :: GLOBAL_APPNAME )
2001-03-27 09:19:28 +02:00
{
2008-12-17 21:05:19 +01:00
continue ;
2001-03-27 09:19:28 +02:00
}
2011-02-15 14:03:39 +01:00
2009-11-18 08:42:14 +01:00
// check for read permission
2011-06-24 15:35:39 +02:00
if ( ! $this -> check_perms ( EGW_ACL_READ , $cat , $globals === 'all_no_acl' ))
2009-11-18 08:42:14 +01:00
{
continue ;
}
2011-02-15 14:03:39 +01:00
2008-12-17 21:05:19 +01:00
// check if we have the correct type
switch ( $type )
2001-03-27 09:19:28 +02:00
{
2008-12-17 21:05:19 +01:00
case 'subs' :
if ( ! $cat [ 'parent' ]) continue 2 ; // 2 for switch AND foreach!
break ;
case 'mains' :
if ( $cat [ 'parent' ]) continue 2 ;
break ;
case 'appandmains' :
if ( $cat [ 'appname' ] != $this -> app_name || $cat [ 'parent' ]) continue 2 ;
break ;
case 'appandsubs' :
if ( $cat [ 'appname' ] != $this -> app_name || ! $cat [ 'parent' ]) continue 2 ;
break ;
case 'noglobal' :
if ( $cat [ 'appname' ] == $this -> app_name ) continue 2 ;
break ;
case 'noglobalapp' :
if ( $cat [ 'appname' ] != $this -> app_name || $cat [ 'owner' ] == ( int ) $this -> account_id ) continue 2 ;
break ;
2001-03-27 09:19:28 +02:00
}
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
// check name and description for $query
if ( $query && stristr ( $cat [ 'name' ], $query ) === false && stristr ( $cat [ 'description' ], $query ) === false ) continue ;
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
// check if last modified since
if ( $lastmod > 0 && $cat [ 'last_mod' ] <= $lastmod ) continue ;
2009-02-25 13:59:28 +01:00
2010-01-31 00:50:51 +01:00
if ( $unserialize_data ) $cat [ 'data' ] = $cat [ 'data' ] ? unserialize ( $cat [ 'data' ]) : array ();
2008-12-17 21:05:19 +01:00
$cats [] = $cat ;
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
if ( ! ( $this -> total_records = count ( $cats )))
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
//error_log(__METHOD__."($type,$start,$limit,$query,$sort,$order,$globals,parent=$parent_id,$lastmod,$column) account_id=$this->account_id, appname=$this->app_name = FALSE");
return false ;
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
if ( ! $sort ) $sort = 'ASC' ;
// order the entries if necessary (cache is already ordered in or default order: cat_main, cat_level, cat_name ASC)
if ( $this -> total_records > 1 && ! empty ( $order ) &&
preg_match ( '/^[a-zA-Z_(), ]+$/' , $order ) && preg_match ( '/^(ASC|DESC|asc|desc)$/' , $sort ))
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
if ( strstr ( $order , 'cat_data' ) !== false ) $order = 'cat_data' ; // sitemgr orders by round(cat_data)!
if ( substr ( $order , 0 , 4 ) == 'cat_' ) $order = substr ( $order , 4 );
$sign = strtoupper ( $sort ) == 'DESC' ? '-' : '' ;
usort ( $cats , create_function ( '$a,$b' , $func = ( in_array ( $order , array ( 'name' , 'description' , 'appname' , 'app_name' )) ?
" return ${ sign } strcasecmp( \$ a[' $order '], \$ b[' $order ']); " : " return $sign (int) \$ a[' $order '] - $sign (int) \$ b[' $order ']; " )));
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
// limit the number of returned rows
if ( $limit )
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
if ( ! is_int ( $limit )) $limit = ( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ];
$cats = array_slice ( $cats ,( int ) $start , $limit );
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
// return only a certain column (why not return is as value?)
if ( $column )
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
foreach ( $cats as $k => $cat )
2005-11-05 23:58:51 +01:00
{
2008-12-17 21:05:19 +01:00
$cats [ $k ] = $cat [ $column ];
2002-01-12 05:08:35 +01:00
}
2008-03-15 15:52:26 +01:00
}
2010-01-31 00:50:51 +01:00
//error_log(__METHOD__."($type,$start,$limit,$query,$sort,$order,$globals,parent=".array2string($parent_id).",$lastmod,$column,filter=".array2string($filter).",$unserialize_data) account_id=$this->account_id, appname=$this->app_name = ".array2string($cats));
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
reset ( $cats ); // some old code (eg. sitemgr) relies on the array-pointer!
2008-03-15 15:52:26 +01:00
return $cats ;
}
/**
2008-12-17 21:05:19 +01:00
* return a sorted array populated with categories ( main sorting criteria is hierachy ! )
2008-03-15 15:52:26 +01:00
*
2008-12-17 21:05:19 +01:00
* @ param int $start = 0 see $limit
* @ param boolean | int $limit if true limited query to maxmatches rows ( starting with $start )
2008-03-15 15:52:26 +01:00
* @ param string $query = '' query - pattern
2008-12-17 21:05:19 +01:00
* @ param string $sort = 'ASC' sort order , either defaults to 'ASC'
* @ param string $order = 'cat_name' order by
2011-06-24 15:35:39 +02:00
* @ param boolean | string $globals includes the global egroupware categories or not ,
* 'all_no_acl' to return global and all non - private user categories independent of ACL
2008-12-17 21:05:19 +01:00
* @ param array | int $parent_id = 0 return only subcats of $parent_id ( s )
2010-01-31 00:50:51 +01:00
* @ param boolean $unserialize_data = false return $cat [ 'data' ] as array ( not serialized array )
2008-03-15 15:52:26 +01:00
* @ return array with cats
*/
2011-06-16 00:32:52 +02:00
function return_sorted_array ( $start = 0 , $limit = True , $query = '' , $sort = 'ASC' , $order = 'cat_name' , $globals = False , $parent_id = 0 , $unserialize_data = false , $filter = null )
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
if ( ! $sort ) $sort = 'ASC' ;
if ( ! $order ) $order = 'cat_name' ;
2002-01-12 05:08:35 +01:00
2010-01-31 00:50:51 +01:00
//error_log(__METHOD__."($start,$limit,$query,$sort,$order,globals=$globals,parent=$parent_id,$unserialize_data) account_id=$this->account_id, appname=$this->app_name: ".function_backtrace());
2002-01-12 05:08:35 +01:00
2008-12-17 21:05:19 +01:00
$parents = $cats = array ();
2012-11-05 18:56:03 +01:00
// Cast parent_id to array, but only if there is one
if ( $parent_id !== false && $parent_id !== null ) $parent_id = ( array ) $parent_id ;
if ( ! ( $cats = $this -> return_array ( 'all' , 0 , false , $query , $sort , $order , $globals , $parent_id , - 1 , '' , $filter , $unserialize_data )))
2008-03-15 15:52:26 +01:00
{
2008-12-17 21:05:19 +01:00
$cats = array ();
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
foreach ( $cats as $cat )
2008-03-15 15:52:26 +01:00
{
$parents [] = $cat [ 'id' ];
}
2013-02-05 12:24:02 +01:00
2012-11-05 18:56:03 +01:00
if ( $parent_id || ! $cats ) // Avoid wiping search results
2008-03-15 15:52:26 +01:00
{
2012-11-05 18:56:03 +01:00
// Go find the children
while ( count ( $parents ))
2002-01-13 01:39:26 +01:00
{
2012-11-05 18:56:03 +01:00
if ( ! ( $subs = $this -> return_array ( 'all' , 0 , false , $query , $sort , $order , $globals , $parents , - 1 , '' , $filter , $unserialize_data )))
{
break ;
}
$parents = $children = array ();
foreach ( $subs as $cat )
{
$parents [] = $cat [ 'id' ];
$children [ $cat [ 'parent' ]][] = $cat ;
}
// sort the cats into the mains
if ( count ( $children ))
2003-08-28 16:31:11 +02:00
{
2012-11-05 18:56:03 +01:00
$cats2 = $cats ;
$cats = array ();
foreach ( $cats2 as $cat )
2005-11-24 00:01:28 +01:00
{
2012-11-05 18:56:03 +01:00
$cats [] = $cat ;
if ( isset ( $children [ $cat [ 'id' ]]))
2005-11-24 00:01:28 +01:00
{
2012-11-05 18:56:03 +01:00
foreach ( $children [ $cat [ 'id' ]] as $child )
{
$cats [] = $child ;
}
2005-11-24 00:01:28 +01:00
}
}
2002-01-12 05:08:35 +01:00
}
}
}
2008-03-15 15:52:26 +01:00
$this -> total_records = count ( $cats );
2008-04-16 13:37:47 +02:00
2008-12-17 21:05:19 +01:00
// limit the number of returned rows
2008-03-15 15:52:26 +01:00
if ( $limit )
{
2008-12-17 21:05:19 +01:00
if ( ! is_int ( $limit )) $limit = ( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ];
$cats = array_slice ( $cats ,( int ) $start , $limit );
2008-03-15 15:52:26 +01:00
}
2009-02-25 13:59:28 +01:00
reset ( $cats ); // some old code (eg. sitemgr) relies on the array-pointer!
2008-03-15 15:52:26 +01:00
return $cats ;
}
2002-01-12 05:08:35 +01:00
2008-03-15 15:52:26 +01:00
/**
2010-01-31 00:50:51 +01:00
* Read a category
2008-03-15 15:52:26 +01:00
*
* We use a shared cache together with id2name
2011-02-15 14:03:39 +01:00
*
2010-10-25 20:08:07 +02:00
* Data array get automatically unserialized , if it was serialized !
2008-03-15 15:52:26 +01:00
*
* @ param int $id id of category
2010-01-31 00:50:51 +01:00
* @ return array | boolean array with cat - data or false if cat not found
2008-03-15 15:52:26 +01:00
*/
2010-01-31 00:50:51 +01:00
static function read ( $id )
2008-03-15 15:52:26 +01:00
{
2012-02-10 21:14:44 +01:00
if ( is_null ( self :: $cache )) self :: init_cache ();
2010-01-31 00:50:51 +01:00
if ( ! isset ( self :: $cache [ $id ])) return false ;
2011-02-15 14:03:39 +01:00
2010-01-31 00:50:51 +01:00
$cat = self :: $cache [ $id ];
2011-02-15 14:03:39 +01:00
$cat [ 'data' ] = $cat [ 'data' ] ? ((( $arr = unserialize ( $cat [ 'data' ])) !== false || $cat [ 'data' ] === 'b:0;' ) ?
2010-10-25 20:08:07 +02:00
$arr : $cat [ 'data' ]) : array ();
2011-02-15 14:03:39 +01:00
2010-01-31 00:50:51 +01:00
return $cat ;
2008-03-15 15:52:26 +01:00
}
2002-10-12 19:23:56 +02:00
2008-03-15 15:52:26 +01:00
/**
2009-05-13 23:21:46 +02:00
* Add a category
*
* Owner and appname are set from the values used to instanciate the class !
2008-03-15 15:52:26 +01:00
*
* @ param array $value cat - data
* @ return int new cat - id
*/
function add ( $values )
{
if (( int ) $values [ 'parent' ] > 0 )
{
$values [ 'level' ] = $this -> id2name ( $values [ 'parent' ], 'level' ) + 1 ;
$values [ 'main' ] = $this -> id2name ( $values [ 'parent' ], 'main' );
2002-10-12 19:23:56 +02:00
}
2010-10-14 17:33:10 +02:00
else
{
$values [ 'level' ] = 0 ;
}
2012-02-14 18:36:35 +01:00
$this -> db -> insert ( self :: TABLE , $cat = array (
2008-03-15 15:52:26 +01:00
'cat_parent' => $values [ 'parent' ],
2011-07-06 17:09:08 +02:00
'cat_owner' => isset ( $values [ 'owner' ]) ? $values [ 'owner' ] : $this -> account_id ,
2009-05-13 23:21:46 +02:00
'cat_access' => isset ( $values [ 'access' ]) ? $values [ 'access' ] : 'public' ,
'cat_appname' => $this -> app_name ,
2008-03-15 15:52:26 +01:00
'cat_name' => $values [ 'name' ],
2009-05-13 23:21:46 +02:00
'cat_description' => isset ( $values [ 'description' ]) ? $values [ 'description' ] : $values [ 'descr' ], // support old name different from returned one
2010-01-31 00:50:51 +01:00
'cat_data' => is_array ( $values [ 'data' ]) ? serialize ( $values [ 'data' ]) : $values [ 'data' ],
2008-03-15 15:52:26 +01:00
'cat_main' => $values [ 'main' ],
2008-04-16 13:37:47 +02:00
'cat_level' => $values [ 'level' ],
2008-03-15 15:52:26 +01:00
'last_mod' => time (),
),( int ) $values [ 'id' ] > 0 ? array ( 'cat_id' => $values [ 'id' ]) : array (), __LINE__ , __FILE__ );
2012-02-14 18:36:35 +01:00
$cat [ 'cat_id' ] = $id = ( int ) $values [ 'id' ] > 0 ? ( int ) $values [ 'id' ] : $this -> db -> get_last_insert_id ( self :: TABLE , 'cat_id' );
2008-03-15 15:52:26 +01:00
2009-05-13 23:21:46 +02:00
if ( ! ( int ) $values [ 'parent' ] && $id != $values [ 'main' ])
2001-03-12 13:16:14 +01:00
{
2008-12-17 21:05:19 +01:00
$this -> db -> update ( self :: TABLE , array ( 'cat_main' => $id ), array ( 'cat_id' => $id ), __LINE__ , __FILE__ );
2012-02-14 18:36:35 +01:00
$cat [ 'cat_main' ] = $id ;
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
// update cache accordingly
2012-02-14 18:36:35 +01:00
self :: invalidate_cache ( egw_db :: strip_array_keys ( $cat , 'cat_' ));
2008-12-17 21:05:19 +01:00
2008-03-15 15:52:26 +01:00
return $id ;
}
2011-02-15 14:03:39 +01:00
2009-11-26 09:24:51 +01:00
/**
* Checks category permissions for a given list of commaseparated category ids
* and truncates it by the ones the user does not have the requested permission on
*
* @ param int $needed necessary ACL right : EGW_ACL_ { READ | EDIT | DELETE }
* @ param string $cat_list commaseparated list of category ids
* @ return string truncated commaseparated list of category ids
*/
function check_list ( $needed , $cat_list )
{
if ( empty ( $cat_list )) return $cat_list ;
2013-07-08 10:04:41 +02:00
if ( is_array ( $cat_list ))
{
$cat_list = implode ( ',' , $cat_list );
//error_log(__METHOD__.__LINE__.' string expected, array found for cat_list. Converted to:'.$cat_list);
}
2009-11-26 09:24:51 +01:00
$cat_arr = explode ( ',' , $cat_list );
if ( ! empty ( $cat_arr ) && is_array ( $cat_arr ) && count ( $cat_arr ) > 0 )
{
foreach ( $cat_arr as $id => $cat_id )
{
2013-03-20 12:06:23 +01:00
if ( ! $this -> check_perms ( $needed , $cat_id , false , $needed == EGW_ACL_READ )) // allow reading all global cats
2009-11-26 09:24:51 +01:00
{
unset ( $cat_arr [ $id ]);
}
}
$cat_list = implode ( ',' , $cat_arr );
}
2011-02-15 14:03:39 +01:00
2009-11-26 09:24:51 +01:00
return $cat_list ;
}
2011-02-15 14:03:39 +01:00
2009-11-18 08:42:14 +01:00
/**
* Checks if the current user has the necessary ACL rights
*
* If the access of a category is set to private , one needs a private grant for the application
*
* @ param int $needed necessary ACL right : EGW_ACL_ { READ | EDIT | DELETE }
* @ param mixed $category category as array or the category_id
2011-06-24 15:35:39 +02:00
* @ param boolean $no_acl_check = false if true , grants are NOT checked , gives access to all non - private categories of all users
2013-03-20 12:06:23 +01:00
* @ param boolean $allow_global_read if true , global cats are allowed ( independent of app ) for reading
2009-11-18 08:42:14 +01:00
* @ return boolean true permission granted , false for permission denied , null for category does not exist
*/
2013-03-20 12:06:23 +01:00
public function check_perms ( $needed , $category , $no_acl_check = false , $allow_global_read = false )
2009-11-18 08:42:14 +01:00
{
2010-01-31 04:36:01 +01:00
if ( ! is_array ( $category ) && ! ( $category = self :: read ( $category )))
2009-11-18 08:42:14 +01:00
{
return null ;
}
2010-01-31 00:50:51 +01:00
// The user for the global cats has id self::GLOBAL_ACCOUNT, this one has full access to all global cats
2010-01-31 04:36:01 +01:00
if ( $this -> account_id == self :: GLOBAL_ACCOUNT && ( $category [ 'appname' ] == self :: GLOBAL_APPNAME ||
$category [ 'appname' ] == $this -> app_name && self :: is_global ( $category )))
2009-11-18 08:42:14 +01:00
{
2010-01-31 04:36:01 +01:00
//echo "<p>".__METHOD__."($needed,$category[name]) access because class instanciated for GLOBAL ACCOUNT</p>\n";
2009-11-18 08:42:14 +01:00
return true ;
}
2011-02-15 14:03:39 +01:00
2009-11-18 08:42:14 +01:00
// Read access to global categories
2013-03-20 12:06:23 +01:00
if ( $needed == EGW_ACL_READ && (( $is_global = array_intersect ( explode ( ',' , $category [ 'owner' ]), $this -> global_owners )) ||
2012-06-27 18:04:51 +02:00
$no_acl_check && $category [ 'access' ] == 'public' ) && // no_acl_check only means public cats
2013-03-20 12:06:23 +01:00
( $category [ 'appname' ] == self :: GLOBAL_APPNAME || $category [ 'appname' ] == $this -> app_name ||
$is_global && $allow_global_read ))
2009-11-18 08:42:14 +01:00
{
2010-01-31 04:36:01 +01:00
//echo "<p>".__METHOD__."($needed,$category[name]) access because global via memberships</p>\n";
2009-11-18 08:42:14 +01:00
return true ;
}
2011-02-15 14:03:39 +01:00
2009-11-18 08:42:14 +01:00
// Full access to own categories
if ( $category [ 'appname' ] == $this -> app_name && $category [ 'owner' ] == $this -> account_id )
{
return true ;
}
2011-02-15 14:03:39 +01:00
2011-06-24 15:35:39 +02:00
// if $no_acl_check is set, allow access to all public (non-private) categories
if ( $no_acl_check && $category [ 'access' ] == 'public' && $this -> account_id != self :: GLOBAL_ACCOUNT && $category [ 'appname' ] == $this -> app_name )
{
return true ;
}
2009-11-18 08:42:14 +01:00
// Load the application grants
if ( $category [ 'appname' ] == $this -> app_name && is_null ( $this -> grants ))
{
2011-06-15 16:20:43 +02:00
$this -> grants = $GLOBALS [ 'egw' ] -> acl -> get_grants ( $this -> app_name , true );
2009-11-18 08:42:14 +01:00
}
2011-02-15 14:03:39 +01:00
2010-01-31 00:50:51 +01:00
// Check for ACL granted access, the self::GLOBAL_ACCOUNT user must not get access by ACL to keep old behaviour
2011-06-28 00:05:03 +02:00
$acl_grant = $this -> account_id != self :: GLOBAL_ACCOUNT && $category [ 'appname' ] == $this -> app_name ;
$owner_grant = false ;
foreach ( explode ( ',' , $category [ 'owner' ]) as $owner )
{
$owner_grant = $owner_grant || (( $this -> grants [ $owner ] & $needed ) &&
( $category [ 'access' ] == 'public' || ( $this -> grants [ $owner ] & EGW_ACL_PRIVATE )));
}
return $acl_grant && $owner_grant ;
2009-11-18 08:42:14 +01:00
}
2005-11-05 23:58:51 +01:00
2008-03-15 15:52:26 +01:00
/**
* delete a category
*
* @ param int $cat_id category id
* @ param boolean $drop_subs = false if true delete sub - cats too
* @ param boolean $modify_subs = false if true make the subs owned by the parent of $cat_id
*/
function delete ( $cat_id , $drop_subs = False , $modify_subs = False )
{
2012-03-17 11:54:36 +01:00
//error_log(__METHOD__."(".array2string($cat_id).', drop_subs='.array2string($drop_subs).', modify_subs='.array2string($modify_subs).') '.function_backtrace());
2008-03-15 15:52:26 +01:00
if ( $modify_subs )
{
$new_parent = $this -> id2name ( $cat_id , 'parent' );
foreach (( array ) $this -> return_sorted_array ( '' , False , '' , '' , '' , False , $cat_id ) as $cat )
2001-05-15 02:00:49 +02:00
{
2008-03-15 15:52:26 +01:00
if ( $cat [ 'level' ] == 1 )
2002-10-04 22:59:00 +02:00
{
2008-12-17 21:05:19 +01:00
$this -> db -> update ( self :: TABLE , array (
2008-04-16 13:37:47 +02:00
'cat_level' => 0 ,
'cat_parent' => 0 ,
2008-03-15 15:52:26 +01:00
'cat_main' => $cat [ 'id' ],
), array (
'cat_id' => $cat [ 'id' ],
'cat_appname' => $this -> app_name ,
), __LINE__ , __FILE__ );
$new_main = $cat [ 'id' ];
2002-10-04 22:59:00 +02:00
}
else
{
2008-03-15 15:52:26 +01:00
$update = array ( 'cat_level' => $cat [ 'level' ] - 1 );
2008-04-16 13:37:47 +02:00
2008-03-15 15:52:26 +01:00
if ( $new_main ) $update [ 'cat_main' ] = $new_main ;
2001-12-11 02:20:45 +01:00
2008-03-15 15:52:26 +01:00
if ( $cat [ 'parent' ] == $cat_id ) $update [ 'cat_parent' ] = $new_parent ;
2007-12-11 10:29:50 +01:00
2008-12-17 21:05:19 +01:00
$this -> db -> update ( self :: TABLE , $update , array (
2008-03-15 15:52:26 +01:00
'cat_id' => $cat [ 'id' ],
'cat_appname' => $this -> app_name ,
), __LINE__ , __FILE__ );
2007-12-11 10:29:50 +01:00
}
}
2008-03-15 15:52:26 +01:00
}
if ( $drop_subs )
{
2012-02-14 18:36:35 +01:00
$where [ 'cat_id' ] = $this -> return_all_children ( $cat_id );
2008-03-15 15:52:26 +01:00
}
else
{
$where [ 'cat_id' ] = $cat_id ;
}
$where [ 'cat_appname' ] = $this -> app_name ;
2007-12-11 10:29:50 +01:00
2010-10-13 09:39:53 +02:00
$GLOBALS [ 'hook_values' ] = array (
'cat_id' => $cat_id ,
2011-02-15 14:03:39 +01:00
'cat_name' => self :: id2name ( $cat_id ),
2010-10-13 09:39:53 +02:00
'drop_subs' => $drop_subs ,
'modify_subs' => $modify_subs ,
'location' => 'delete_category'
);
2011-02-15 14:03:39 +01:00
if ( $this -> is_global ( $cat_id , true )) // true = application global (otherwise eg. global addressbook categories call all apps)
2010-10-13 09:39:53 +02:00
{
$GLOBALS [ 'egw' ] -> hooks -> process ( $GLOBALS [ 'hook_values' ], False , True ); // called for every app now, not only enabled ones)
}
else
{
2011-02-15 14:03:39 +01:00
$GLOBALS [ 'egw' ] -> hooks -> single ( $GLOBALS [ 'hook_values' ], self :: id2name ( $cat_id , 'appname' ));
2010-10-13 09:39:53 +02:00
}
2008-12-17 21:05:19 +01:00
$this -> db -> delete ( self :: TABLE , $where , __LINE__ , __FILE__ );
// update cache accordingly
2012-02-14 18:36:35 +01:00
self :: invalidate_cache ( $modify_subs ? null : $where [ 'cat_id' ]);
2008-03-15 15:52:26 +01:00
}
2002-01-03 18:16:56 +01:00
2010-10-14 17:33:10 +02:00
/**
* adapt_level_in_subtree of a category
*
* Owner and appname are set from the values used to instanciate the class !
*
* @ param array $values array with cat - data ( it need to be complete , as everything get ' s written )
* @ return void
*/
function adapt_level_in_subtree ( $values )
{
foreach (( array ) $this -> return_sorted_array ( '' , False , '' , '' , '' , False , $values [ 'id' ]) as $cat )
{
if ( $cat [ 'parent' ] == $values [ 'id' ])
{
$this -> db -> update ( self :: TABLE , array (
'cat_level' => $values [ 'level' ] + 1 ,
'last_mod' => time (),
), array (
'cat_id' => $cat [ 'id' ],
'cat_appname' => $this -> app_name ,
), __LINE__ , __FILE__ );
$cat [ 'level' ] = $values [ 'level' ] + 1 ;
self :: invalidate_cache ( $cat [ 'id' ]);
$this -> adapt_level_in_subtree ( $cat );
}
else
{
continue ;
}
}
}
/**
* check_consistency4update - for edit
*
* @ param array $values array with cat - data ( it need to be complete , as everything get ' s written )
* @ return mixed string / boolean errorstring if consitency check failed / true if the consistency check did not fail
*/
function check_consistency4update ( $values )
{
// check if we try to move an element down its own subtree, which will fail
foreach ( $this -> return_sorted_array ( '' , False , '' , '' , '' , False , $values [ 'id' ]) as $cat ) if ( $cat [ 'id' ] == $values [ 'parent' ]) return lang ( 'Cannot set a category as parent, which is part of this categorys subtree!' );
// check if we try to be our own parent
if ( $values [ 'parent' ] == $values [ 'id' ]) return lang ( 'Cannot set this cat as its own parent!' ); // deny to be our own parent
// check if parent still exists
if (( int ) $values [ 'parent' ] > 0 && ! $this -> read ( $values [ 'parent' ])) return lang ( 'Chosen parent category no longer exists' );
return true ;
}
2008-03-15 15:52:26 +01:00
/**
* edit / update a category
*
2009-05-13 23:21:46 +02:00
* Owner and appname are set from the values used to instanciate the class !
*
2008-03-15 15:52:26 +01:00
* @ param array $values array with cat - data ( it need to be complete , as everything get ' s written )
2010-10-14 17:33:10 +02:00
* @ return int cat - id or false if it failed
2008-03-15 15:52:26 +01:00
*/
function edit ( $values )
{
if ( isset ( $values [ 'old_parent' ]) && ( int ) $values [ 'old_parent' ] != ( int ) $values [ 'parent' ])
{
2010-10-14 17:33:10 +02:00
$ret = $this -> check_consistency4update ( $values );
if ( $ret !== true ) throw new egw_exception_wrong_userinput ( $ret );
// everything seems in order -> proceed
$values [ 'level' ] = ( $values [ 'parent' ] ? $this -> id2name ( $values [ 'parent' ], 'level' ) + 1 : 0 );
$this -> adapt_level_in_subtree ( $values );
2011-02-15 14:03:39 +01:00
2008-03-15 15:52:26 +01:00
return $this -> add ( $values );
2001-04-25 20:12:10 +02:00
}
2008-03-15 15:52:26 +01:00
else
2001-03-12 13:16:14 +01:00
{
2010-10-14 17:33:10 +02:00
//echo "old parent not set <br>";
2008-03-15 15:52:26 +01:00
if ( $values [ 'parent' ] > 0 )
2008-02-07 03:40:43 +01:00
{
2010-10-14 17:33:10 +02:00
$ret = $this -> check_consistency4update ( $values );
if ( $ret !== true ) throw new egw_exception_wrong_userinput ( $ret );
// everything seems in order -> proceed
2008-03-15 15:52:26 +01:00
$values [ 'main' ] = $this -> id2name ( $values [ 'parent' ], 'main' );
$values [ 'level' ] = $this -> id2name ( $values [ 'parent' ], 'level' ) + 1 ;
2001-03-12 13:16:14 +01:00
}
2008-03-15 15:52:26 +01:00
else
2001-03-12 13:16:14 +01:00
{
2010-10-14 17:33:10 +02:00
//echo "new parent not set <br>";
2008-03-15 15:52:26 +01:00
$values [ 'main' ] = $values [ 'id' ];
$values [ 'level' ] = 0 ;
2001-07-02 04:25:26 +02:00
}
2010-10-14 17:33:10 +02:00
// adapt the level info in each child
$this -> adapt_level_in_subtree ( $values );
2001-04-25 20:12:10 +02:00
}
2012-02-14 18:36:35 +01:00
$this -> db -> update ( self :: TABLE , $cat = array (
2008-03-15 15:52:26 +01:00
'cat_name' => $values [ 'name' ],
2009-05-13 23:21:46 +02:00
'cat_description' => isset ( $values [ 'description' ]) ? $values [ 'description' ] : $values [ 'descr' ], // support old name different from the one read
2010-01-31 00:50:51 +01:00
'cat_data' => is_array ( $values [ 'data' ]) ? serialize ( $values [ 'data' ]) : $values [ 'data' ],
2008-03-15 15:52:26 +01:00
'cat_parent' => $values [ 'parent' ],
'cat_access' => $values [ 'access' ],
2011-06-15 16:20:43 +02:00
'cat_owner' => isset ( $values [ 'owner' ]) ? $values [ 'owner' ] : $this -> account_id ,
2008-03-15 15:52:26 +01:00
'cat_main' => $values [ 'main' ],
'cat_level' => $values [ 'level' ],
'last_mod' => time (),
), array (
'cat_id' => $values [ 'id' ],
'cat_appname' => $this -> app_name ,
), __LINE__ , __FILE__ );
2008-04-16 13:37:47 +02:00
2012-02-14 18:36:35 +01:00
$cat [ 'cat_id' ] = $values [ 'id' ];
$cat [ 'cat_appname' ] = $this -> app_name ;
2008-12-17 21:05:19 +01:00
// update cache accordingly
2012-02-14 18:36:35 +01:00
self :: invalidate_cache ( egw_db :: strip_array_keys ( $cat , 'cat_' ));
2008-12-17 21:05:19 +01:00
2008-03-15 15:52:26 +01:00
return ( int ) $values [ 'id' ];
}
/**
* return category id for a given name
2008-04-16 13:37:47 +02:00
*
2008-03-15 15:52:26 +01:00
* Cat ' s with the given name are returned in this order :
* - personal cats first
* - then application global categories
* - global categories
* - cat ' s of other user
*
* @ param string $cat_name cat - name
2008-12-17 21:05:19 +01:00
* @ param boolean | string $strip = false if true , strips 'X-' ( $strip ) from category names added by some SyncML clients
2008-03-15 15:52:26 +01:00
* @ return int cat - id or 0 if not found
*/
2008-12-17 21:05:19 +01:00
function name2id ( $cat_name , $strip = false )
2008-03-15 15:52:26 +01:00
{
static $cache = array (); // a litle bit of caching
2008-04-16 13:37:47 +02:00
2008-03-15 15:52:26 +01:00
if ( isset ( $cache [ $cat_name ])) return $cache [ $cat_name ];
2001-04-25 20:12:10 +02:00
2008-03-15 15:52:26 +01:00
if ( $strip === true )
2003-08-28 16:31:11 +02:00
{
2008-03-15 15:52:26 +01:00
$strip = 'X-' ;
2003-08-28 16:31:11 +02:00
}
2008-03-15 15:52:26 +01:00
$cats = array ( $cat_name );
2008-12-17 21:05:19 +01:00
if ( $strip && strncmp ( $strip , $cat_name , strlen ( $strip )) == 0 )
2001-03-12 13:16:14 +01:00
{
2008-03-15 15:52:26 +01:00
$stripped_cat_name = substr ( $cat_name , strlen ( $strip ));
if ( isset ( $cache [ $stripped_cat_name ]))
2001-05-15 02:00:49 +02:00
{
2008-03-15 15:52:26 +01:00
$cache [ $cat_name ] = $cache [ $stripped_cat_name ];
return $cache [ $stripped_cat_name ];
2001-05-15 02:00:49 +02:00
}
2008-03-15 15:52:26 +01:00
$cats [] = $stripped_cat_name ;
}
2008-04-16 13:37:47 +02:00
2008-12-17 21:05:19 +01:00
if ( ! ( $cats = $this -> return_array ( 'all' , 0 , false , '' , '' , '' , true , null , - 1 , '' , array (
'name' => $cats ,
2010-01-31 00:50:51 +01:00
'appname' => array ( $this -> app_name , self :: GLOBAL_APPNAME ),
2008-12-17 21:05:19 +01:00
))))
{
return 0 ; // cat not found, dont cache it, as it might be created in this request
}
if ( count ( $cats ) > 1 )
{
// if more the one cat matches we weight them by: exact name match; own, global, other users cat; appplication cats
foreach ( $cats as $k => $cat )
{
$cats [ $k ][ 'weight' ] = 100 * ( $cat [ 'name' ] == $cat_name ) +
2010-01-31 04:36:01 +01:00
10 * ( $cat [ 'owner' ] == $this -> account_id ? 3 : ( $cat [ 'owner' ] <= self :: GLOBAL_ACCOUNT ? 2 : 1 )) +
2010-01-31 00:50:51 +01:00
( $cat [ 'appname' ] != self :: GLOBAL_APPNAME );
2008-12-17 21:05:19 +01:00
}
// sort heighest weight to the top
usort ( $cats , create_function ( '$a,$b' , " return \$ b['weight'] - \$ a['weight']; " ));
}
return $cache [ $cat [ 'cat_name' ]] = ( int ) $cats [ 0 ][ 'id' ];
2008-03-15 15:52:26 +01:00
}
2011-02-15 14:03:39 +01:00
2010-01-31 04:36:01 +01:00
/**
2011-02-15 14:03:39 +01:00
* Check if category is global ( owner <= 0 || appname == 'phpgw' )
*
2010-01-31 04:36:01 +01:00
* @ param int | array $cat
2011-02-15 14:03:39 +01:00
* @ param boolean $application_global = false true check for application global categories only ( appname == 'phpgw' )
2010-01-31 04:36:01 +01:00
* @ return boolean
*/
2011-02-15 14:03:39 +01:00
static function is_global ( $cat , $application_global = false )
2010-01-31 04:36:01 +01:00
{
if ( ! is_array ( $cat ) && ! ( $cat = self :: read ( $cat ))) return null ; // cat not found
2011-02-15 14:03:39 +01:00
2011-06-28 00:05:03 +02:00
$global_owner = false ;
foreach ( explode ( ',' , $cat [ 'owner' ]) as $owner )
{
$global_owner = $global_owner || $owner <= self :: GLOBAL_ACCOUNT ;
}
return $global_owner && ! $application_global || $cat [ 'appname' ] == self :: GLOBAL_APPNAME ;
2010-01-31 04:36:01 +01:00
}
2008-03-15 15:52:26 +01:00
/**
* return category information for a given id
*
2010-01-31 00:50:51 +01:00
* We use a shared cache together with read
* $item == 'data' is returned as array ( not serialized array ) !
2008-03-15 15:52:26 +01:00
*
* @ param int $cat_id = 0 cat - id
2009-11-18 08:42:14 +01:00
* @ param string $item = 'name' requested information , 'path' = / delimited path of category names ( incl . parents )
2008-03-15 15:52:26 +01:00
* @ return string information or '--' if not found or ! $cat_id
*/
2008-12-17 21:05:19 +01:00
static function id2name ( $cat_id = 0 , $item = 'name' )
2008-03-15 15:52:26 +01:00
{
if ( ! $cat_id ) return '--' ;
if ( ! $item ) $item = 'parent' ;
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
if ( is_null ( self :: $cache )) self :: init_cache ();
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
$cat = self :: $cache [ $cat_id ];
2008-03-15 15:52:26 +01:00
if ( $item == 'path' )
{
if ( $cat [ 'parent' ])
2001-05-15 02:00:49 +02:00
{
2008-12-17 21:05:19 +01:00
return self :: id2name ( $cat [ 'parent' ], 'path' ) . ' / ' . $cat [ 'name' ];
2001-05-15 02:00:49 +02:00
}
2008-03-15 15:52:26 +01:00
$item = 'name' ;
}
2010-01-31 00:50:51 +01:00
if ( $item == 'data' )
{
return $cat [ 'data' ] ? unserialize ( $cat [ 'data' ]) : array ();
}
elseif ( $cat [ $item ])
2008-03-15 15:52:26 +01:00
{
return $cat [ $item ];
}
elseif ( $item == 'name' )
{
return '--' ;
}
return null ;
}
/**
* check if a category id and / or name exists , if id AND name are given the check is for a category with same name and different id ( ! )
*
* @ param string $type subs or mains
* @ param string $cat_name = '' category name
* @ param int $cat_id = 0 category id
* @ param int $parent = 0 category id of parent
* @ return int / boolean cat_id or false if cat not exists
*/
function exists ( $type , $cat_name = '' , $cat_id = 0 , $parent = 0 )
{
if ( $cat_name )
{
2008-12-17 21:05:19 +01:00
$filter [ 'name' ] = $cat_name ;
if ( $cat_id ) $filter [ 'id' ] = '!' . ( int ) $cat_id ;
2005-11-05 23:58:51 +01:00
}
2008-03-15 15:52:26 +01:00
elseif ( $cat_id )
{
2008-12-17 21:05:19 +01:00
$filter [ 'id' ] = $cat_id ;
2008-03-15 15:52:26 +01:00
}
2008-12-17 21:05:19 +01:00
if ( ! ( $cats = $this -> return_array ( $type , 0 , false , '' , '' , '' , true , $parent , - 1 , 'id' , $filter )))
{
$ret = false ;
}
else
{
$ret = $cats [ 0 ];
}
//error_log(__METHOD__."($type,$cat_name,$cat_id,$parent) = ".$ret);
return $ret ;
2008-03-15 15:52:26 +01:00
}
2009-02-25 13:59:28 +01:00
2008-03-15 15:52:26 +01:00
/**
* Change the owner of all cats owned by $owner to $to OR deletes them if ! $to
*
* @ param int $owner owner or the cats to delete or change
* @ param int $to = 0 new owner or 0 to delete the cats
* @ param string $app = '' if given only cats matching $app are modifed / deleted
*/
function change_owner ( $owner , $to = 0 , $app = '' )
{
$where = array ( 'cat_owner' => $owner );
2008-04-16 13:37:47 +02:00
2008-03-15 15:52:26 +01:00
if ( $app ) $where [ 'cat_appname' ] = $app ;
2001-05-15 02:00:49 +02:00
2008-03-15 15:52:26 +01:00
if (( int ) $to )
{
2008-12-17 21:05:19 +01:00
$this -> db -> update ( self :: TABLE , array ( 'cat_owner' => $to ), $where , __LINE__ , __FILE__ );
2008-03-15 15:52:26 +01:00
}
else
{
2008-12-17 21:05:19 +01:00
$this -> db -> delete ( self :: TABLE , $where , __LINE__ , __FILE__ );
}
// update cache accordingly
self :: invalidate_cache ();
}
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
/**
* Initialise or restore the categories cache
2009-02-25 13:59:28 +01:00
*
2008-12-17 21:05:19 +01:00
* We use the default ordering of return_array to avoid doing it again there
*/
2012-02-10 21:14:44 +01:00
public static function init_cache ()
2008-12-17 21:05:19 +01:00
{
2012-02-10 21:14:44 +01:00
self :: $cache = egw_cache :: getInstance ( self :: CACHE_APP , self :: CACHE_NAME );
if ( is_null ( self :: $cache ))
{
// check if we are already updated to global owner == 0, if not do it now
if ( ! $GLOBALS [ 'egw' ] -> db -> select ( self :: TABLE , 'COUNT(*)' , array ( 'cat_owner' => '0' ), __LINE__ , __FILE__ ) -> fetchColumn ())
2008-12-17 21:05:19 +01:00
{
2012-02-10 21:14:44 +01:00
$GLOBALS [ 'egw' ] -> db -> update ( self :: TABLE , array ( 'cat_owner' => '0' ), " (cat_owner='-1' OR cat_appname='phpgw') " , __LINE__ , __FILE__ );
$GLOBALS [ 'egw' ] -> db -> insert ( self :: TABLE , array (
'cat_main' => 0 ,
'cat_parent' => 0 ,
'cat_level' => 0 ,
'cat_owner' => 0 ,
'cat_appname' => '*update*' ,
'cat_name' => 'global=0' ,
'cat_description' => 'global=0' ,
), false , __LINE__ , __FILE__ );
2008-12-17 21:05:19 +01:00
}
2012-02-10 21:14:44 +01:00
self :: $cache = array ();
// read all cats (cant use $this->db!)
foreach ( $GLOBALS [ 'egw' ] -> db -> select ( self :: TABLE , '*' , false , __LINE__ , __FILE__ ,
false , 'ORDER BY cat_main, cat_level, cat_name ASC' ) as $cat )
2008-12-17 21:05:19 +01:00
{
2012-02-10 21:14:44 +01:00
$cat = egw_db :: strip_array_keys ( $cat , 'cat_' );
if ( $cat [ 'appname' ] == '*update*' ) continue ; // --> ignore update marker
$cat [ 'app_name' ] = $cat [ 'appname' ];
// backlink children to their parent
if ( $cat [ 'parent' ])
{
self :: $cache [ $cat [ 'parent' ]][ 'children' ][] = $cat [ 'id' ];
}
if ( isset ( self :: $cache [ $cat [ 'id' ]]))
{
$cat [ 'children' ] = self :: $cache [ $cat [ 'id' ]][ 'children' ];
unset ( self :: $cache [ $cat [ 'id' ]]); // otherwise the order gets messed up!
}
self :: $cache [ $cat [ 'id' ]] = $cat ;
2008-12-17 21:05:19 +01:00
}
2012-02-10 21:14:44 +01:00
egw_cache :: setInstance ( self :: CACHE_APP , self :: CACHE_NAME , self :: $cache );
2008-12-17 21:05:19 +01:00
}
//error_log(__METHOD__."() cache initialised: ".function_backtrace());
}
2009-02-25 13:59:28 +01:00
2008-12-17 21:05:19 +01:00
/**
* Invalidate the cache
2009-02-25 13:59:28 +01:00
*
2008-12-17 21:05:19 +01:00
* Currently we dont care for $cat_id , as changing cats happens very infrequently and
* also changes child categories ( ! )
*
2012-02-14 18:36:35 +01:00
* @ param int | array $cat concerned id ( s ) or array with cat - data or null for all cats
2008-12-17 21:05:19 +01:00
*/
2012-02-14 18:36:35 +01:00
public static function invalidate_cache ( $cat = null )
2008-12-17 21:05:19 +01:00
{
2012-03-17 11:54:36 +01:00
//error_log(__METHOD__."(".array2string($cat).') '.function_backtrace());
2012-02-14 18:36:35 +01:00
// allways invalidate instance-global cache, as updating our own cache is not perfect and does not help other sessions
2012-02-10 21:14:44 +01:00
egw_cache :: unsetInstance ( self :: CACHE_APP , self :: CACHE_NAME );
2012-02-14 18:36:35 +01:00
// if cat given update our own cache, to work around failed sitemgr install via setup (cant read just added categories)
if ( $cat )
{
if ( ! is_array ( $cat ) || isset ( $cat [ 0 ]))
{
foreach (( array ) $cat as $cat_id )
{
unset ( self :: $cache [ $cat_id ]);
}
}
elseif ( $cat [ 'id' ])
{
self :: $cache [ $cat [ 'id' ]] = $cat ;
}
}
2012-03-17 11:54:36 +01:00
else
{
self :: init_cache ();
}
2001-03-12 13:16:14 +01:00
}
2010-01-31 00:50:51 +01:00
2012-08-08 18:25:03 +02:00
/**
* Delete categories belonging to a given account , when account got deleted
*
* @ param int $account_id
* @ param int $new_owner = null for users data can be transfered to new owner
* @ return int number of deleted or modified categories
*/
public static function delete_account ( $account_id , $new_owner = null )
{
if ( is_null ( self :: $cache )) self :: init_cache ();
$deleted = 0 ;
foreach ( self :: $cache as $cat_id => $data )
{
if ( $data [ 'owner' ] && ( $owners = explode ( ',' , $data [ 'owner' ])) && ( $owner_key = array_search ( $account_id , $owners )) !== false )
{
// delete category if account_id is single owner and no new owner or owner is a group
if ( count ( $owners ) == 1 && ( ! $new_owner || $account_id < 0 ))
{
if ( ! isset ( $cat ))
{
$cat = new categories ( $new_owner , $data [ 'appname' ]);
}
$cat -> delete ( $cat_id , false , true );
}
else
{
unset ( $owners [ $owner_key ]);
if ( $new_owner && $account_id > 0 ) $owners [] = $new_owner ;
$data [ 'owner' ] = implode ( ',' , $owners );
// app_name have to match cat to update!
if ( ! isset ( $cat ) || $cat -> app_name != $data [ 'appname' ])
{
$cat = new categories ( $new_owner , $data [ 'appname' ]);
}
$cat -> add ( $data );
}
++ $deleted ;
}
}
return $deleted ;
}
/**
* Delete categories with not ( longer ) existing owners
*
* @ return int number of deleted categories
*/
public static function delete_orphans ()
{
if ( is_null ( self :: $cache )) self :: init_cache ();
$checked = array ();
$deleted = 0 ;
foreach ( self :: $cache as $cat_id => $data )
{
foreach ( explode ( ',' , $data [ 'owner' ]) as $owner )
{
if ( $owner && ! in_array ( $owner , $checked ))
{
if ( ! $GLOBALS [ 'egw' ] -> accounts -> exists ( $owner ))
{
$deleted += self :: delete_account ( $owner );
}
$checked [] = $owner ;
}
}
}
return $deleted ;
}
2010-01-31 00:50:51 +01:00
/**
******************************** old / deprecated functions ***********************************
*/
/**
* read a single category
*
* We use a shared cache together with id2name
*
* @ deprecated use read ( $id ) returning just the category array not an array with one element
* @ param int $id id of category
* @ return array | boolean array with one array of cat - data or false if cat not found
*/
static function return_single ( $id )
{
2012-02-10 21:14:44 +01:00
if ( is_null ( self :: $cache )) self :: init_cache ();
2010-01-31 00:50:51 +01:00
return isset ( self :: $cache [ $id ]) ? array ( self :: $cache [ $id ]) : false ;
}
2011-02-15 14:03:39 +01:00
2010-01-31 00:50:51 +01:00
/**
* return into a select box , list or other formats
*
* @ param string / array $format string 'select' or 'list' , or array with all params
* @ param string $type = '' subs or mains
* @ param int / array $selected - cat_id or array with cat_id values
* @ param boolean $globals True or False , includes the global egroupware categories or not
* @ deprecated use html class to create selectboxes
* @ return string populated with categories
*/
function formatted_list ( $format , $type = '' , $selected = '' , $globals = False , $site_link = 'site' )
{
if ( is_array ( $format ))
{
$type = ( $format [ 'type' ] ? $format [ 'type' ] : 'all' );
$selected = ( isset ( $format [ 'selected' ]) ? $format [ 'selected' ] : '' );
$self = ( isset ( $format [ 'self' ]) ? $format [ 'self' ] : '' );
$globals = ( isset ( $format [ 'globals' ]) ? $format [ 'globals' ] : True );
$site_link = ( isset ( $format [ 'site_link' ]) ? $format [ 'site_link' ] : 'site' );
$format = $format [ 'format' ] ? $format [ 'format' ] : 'select' ;
}
if ( ! is_array ( $selected ))
{
$selected = explode ( ',' , $selected );
}
if ( $type != 'all' )
{
$cats = $this -> return_array ( $type , 0 , False , '' , '' , '' , $globals );
}
else
{
$cats = $this -> return_sorted_array ( 0 , False , '' , '' , '' , $globals );
}
if ( ! $cats ) return '' ;
if ( $self )
{
foreach ( $cats as $key => $cat )
{
if ( $cat [ 'id' ] == $self )
{
unset ( $cats [ $key ]);
}
}
}
switch ( $format )
{
case 'select' :
foreach ( $cats as $cat )
{
$s .= '<option value="' . $cat [ 'id' ] . '"' ;
if ( in_array ( $cat [ 'id' ], $selected ))
{
$s .= ' selected="selected"' ;
}
$s .= '>' . str_repeat ( ' ' , $cat [ 'level' ]);
$s .= $GLOBALS [ 'egw' ] -> strip_html ( $cat [ 'name' ]);
2010-01-31 04:36:01 +01:00
if ( self :: is_global ( $cat ))
2010-01-31 00:50:51 +01:00
{
2013-02-05 12:24:02 +01:00
$s .= self :: $global_marker ;
2010-01-31 00:50:51 +01:00
}
$s .= '</option>' . " \n " ;
}
break ;
case 'list' :
$space = ' ' ;
$s = '<table border="0" cellpadding="2" cellspacing="2">' . " \n " ;
foreach ( $cats as $cat )
{
$image_set = ' ' ;
if ( in_array ( $cat [ 'id' ], $selected ))
{
$image_set = '<img src="' . EGW_IMAGES_DIR . '/roter_pfeil.gif">' ;
}
if (( $cat [ 'level' ] == 0 ) && ! in_array ( $cat [ 'id' ], $selected ))
{
$image_set = '<img src="' . EGW_IMAGES_DIR . '/grauer_pfeil.gif">' ;
}
$space_set = str_repeat ( $space , $cat [ 'level' ]);
$s .= '<tr>' . " \n " ;
$s .= '<td width="8">' . $image_set . '</td>' . " \n " ;
$s .= '<td>' . $space_set . '<a href="' . $GLOBALS [ 'egw' ] -> link ( $site_link , 'cat_id=' . $cat [ 'id' ]) . '">'
. $GLOBALS [ 'egw' ] -> strip_html ( $cat [ 'name' ])
. '</a></td>' . " \n "
. '</tr>' . " \n " ;
}
$s .= '</table>' . " \n " ;
break ;
}
return $s ;
}
/**
* return category name for a given id
*
* @ deprecated This is only a temp wrapper , use id2name () to keep things matching across the board . ( jengo )
* @ param int $cat_id
* @ return string cat_name category name
*/
function return_name ( $cat_id )
{
return $this -> id2name ( $cat_id );
}
2008-03-15 15:52:26 +01:00
}