2005-11-04 00:47:52 +01:00
< ? php
2006-06-13 06:30:16 +02:00
/**
* Addressbook - General business object
*
* @ link http :// www . egroupware . org
* @ author Cornelius Weiss < egw @ von - und - zu - weiss . de >
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
2009-07-15 21:44:09 +02:00
* @ author Joerg Lehrke < jlehrke @ noc . de >
2006-06-13 06:30:16 +02:00
* @ package addressbook
2015-07-23 19:40:48 +02:00
* @ copyright ( c ) 2005 - 15 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2008-05-10 14:02:49 +02:00
* @ copyright ( c ) 2005 / 6 by Cornelius Weiss < egw @ von - und - zu - weiss . de >
2006-06-13 06:30:16 +02:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
2008-04-25 21:06:15 +02:00
* @ version $Id $
2006-06-13 06:30:16 +02:00
*/
2005-11-04 00:47:52 +01:00
/**
2006-04-23 16:40:31 +02:00
* General business object of the adressbook
*/
2008-05-10 14:02:49 +02:00
class addressbook_bo extends addressbook_so
2005-11-04 00:47:52 +01:00
{
2006-03-07 23:43:08 +01:00
/**
* @ var int $now_su actual user ( ! ) time
*/
var $now_su ;
2008-04-25 21:06:15 +02:00
2006-03-07 23:43:08 +01:00
/**
* @ var array $timestamps timestamps
*/
2006-04-23 16:40:31 +02:00
var $timestamps = array ( 'modified' , 'created' );
2008-04-25 21:06:15 +02:00
2006-04-23 16:40:31 +02:00
/**
* @ var array $fileas_types
*/
var $fileas_types = array (
'org_name: n_family, n_given' ,
'org_name: n_family, n_prefix' ,
'org_name: n_given n_family' ,
'org_name: n_fn' ,
2010-11-10 10:35:10 +01:00
'org_name, org_unit: n_family, n_given' ,
'org_name, adr_one_locality: n_family, n_given' ,
'org_name, org_unit, adr_one_locality: n_family, n_given' ,
2006-04-23 16:40:31 +02:00
'n_family, n_given: org_name' ,
2008-10-10 13:25:35 +02:00
'n_family, n_given (org_name)' ,
2006-04-23 16:40:31 +02:00
'n_family, n_prefix: org_name' ,
'n_given n_family: org_name' ,
'n_prefix n_family: org_name' ,
'n_fn: org_name' ,
'org_name' ,
2007-06-28 08:11:58 +02:00
'org_name - org_unit' ,
2006-04-23 16:40:31 +02:00
'n_given n_family' ,
'n_prefix n_family' ,
'n_family, n_given' ,
'n_family, n_prefix' ,
'n_fn' ,
);
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
/**
* @ var array $org_fields fields belonging to the ( virtual ) organisation entry
*/
var $org_fields = array (
'org_name' ,
'org_unit' ,
'adr_one_street' ,
'adr_one_street2' ,
'adr_one_locality' ,
'adr_one_region' ,
'adr_one_postalcode' ,
'adr_one_countryname' ,
2011-03-07 19:32:20 +01:00
'adr_one_countrycode' ,
2006-04-30 11:34:24 +02:00
'label' ,
'tel_work' ,
'tel_fax' ,
'tel_assistent' ,
'assistent' ,
'email' ,
'url' ,
'tz' ,
);
2006-09-15 09:16:07 +02:00
/**
* Which fields is a ( non - admin ) user allowed to edit in his own account
*
* @ var array
*/
var $own_account_acl ;
2006-04-30 11:34:24 +02:00
/**
* @ var double $org_common_factor minimum percentage of the contacts with identical values to construct the " common " ( virtual ) org - entry
*/
var $org_common_factor = 0.6 ;
2008-04-25 21:06:15 +02:00
2006-04-26 23:23:38 +02:00
var $contact_fields = array ();
var $business_contact_fields = array ();
var $home_contact_fields = array ();
2006-04-23 16:40:31 +02:00
2010-02-09 22:56:39 +01:00
/**
* Set Logging
*
* @ var boolean
*/
var $log = false ;
2010-04-21 19:41:34 +02:00
var $logfile = '/tmp/log-addressbook_bo' ;
2010-02-09 22:56:39 +01:00
2006-06-13 23:53:00 +02:00
/**
* Number and message of last error or false if no error , atm . only used for saving
*
* @ var string / boolean
*/
var $error ;
2006-10-19 10:50:25 +02:00
/**
* Addressbook preferences of the user
*
* @ var array
*/
var $prefs ;
2007-12-15 00:20:18 +01:00
/**
* Default addressbook for new contacts , if no addressbook is specified ( user preference )
*
* @ var int
*/
var $default_addressbook ;
/**
* Default addressbook is the private one
*
* @ var boolean
*/
var $default_private ;
2010-12-02 14:00:01 +01:00
/**
* Use a separate private addressbook ( former private flag ), for contacts not shareable via regular read acl
*
* @ var boolean
*/
var $private_addressbook = false ;
2009-11-20 10:05:23 +01:00
/**
* Categories object
*
2011-08-31 14:24:50 +02:00
* @ var categories
2009-11-20 10:05:23 +01:00
*/
var $categories ;
2006-06-13 23:53:00 +02:00
2010-02-08 18:53:47 +01:00
/**
* Tracking changes
2010-02-09 22:56:39 +01:00
*
2011-08-31 14:24:50 +02:00
* @ var addressbook_tracking
2010-02-08 18:53:47 +01:00
*/
protected $tracking ;
2010-02-11 00:07:43 +01:00
/**
* Keep deleted addresses , or really delete them
* Set in Admin -> Addressbook -> Site Configuration
2010-11-22 18:10:11 +01:00
* '' = really delete , 'history' = keep , only admins delete , 'userpurge' = keep , users delete
2010-02-11 00:07:43 +01:00
*
* @ var string
*/
protected $delete_history = '' ;
2010-07-30 17:30:46 +02:00
/**
* Constructor
*
* @ param string $contact_app = 'addressbook' used for acl -> get_grants ()
* @ param egw_db $db = null
*/
function __construct ( $contact_app = 'addressbook' , egw_db $db = null )
2005-11-04 00:47:52 +01:00
{
2010-07-30 17:30:46 +02:00
parent :: __construct ( $contact_app , $db );
2010-04-21 19:41:34 +02:00
if ( $this -> log )
{
$this -> logfile = $GLOBALS [ 'egw_info' ][ 'server' ][ 'temp_dir' ] . '/log-addressbook_bo' ;
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__ . " ( $contact_app ) \n " , 3 , $this -> logfile );
}
2010-02-16 11:42:27 +01:00
$this -> now_su = egw_time :: to ( 'now' , 'ts' );
2006-03-07 23:43:08 +01:00
2006-10-19 10:50:25 +02:00
$this -> prefs =& $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ];
2007-12-15 00:20:18 +01:00
// get the default addressbook from the users prefs
2008-04-25 21:06:15 +02:00
$this -> default_addressbook = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'add_default' ] ?
2008-01-25 13:57:13 +01:00
( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'add_default' ] : $this -> user ;
2008-04-25 21:06:15 +02:00
$this -> default_private = substr ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'add_default' ], - 1 ) == 'p' ;
2007-12-15 00:20:18 +01:00
if ( $this -> default_addressbook > 0 && $this -> default_addressbook != $this -> user &&
2008-04-25 21:06:15 +02:00
( $this -> default_private ||
2008-04-21 18:11:59 +02:00
$this -> default_addressbook == ( int ) $GLOBALS [ 'egw' ] -> preferences -> forced [ 'addressbook' ][ 'add_default' ] ||
$this -> default_addressbook == ( int ) $GLOBALS [ 'egw' ] -> preferences -> default [ 'addressbook' ][ 'add_default' ]))
2007-12-15 00:20:18 +01:00
{
$this -> default_addressbook = $this -> user ; // admin set a default or forced pref for personal addressbook
}
2014-07-23 16:00:20 +02:00
$this -> private_addressbook = self :: private_addressbook ( $this -> contact_repository == 'sql' , $this -> prefs );
2005-11-04 00:47:52 +01:00
2006-04-26 23:23:38 +02:00
$this -> contact_fields = array (
'id' => lang ( 'Contact ID' ),
2006-10-07 11:16:04 +02:00
'tid' => lang ( 'Type' ),
2006-04-26 23:23:38 +02:00
'owner' => lang ( 'Addressbook' ),
'private' => lang ( 'private' ),
'cat_id' => lang ( 'Category' ),
'n_prefix' => lang ( 'prefix' ),
'n_given' => lang ( 'first name' ),
'n_middle' => lang ( 'middle name' ),
'n_family' => lang ( 'last name' ),
'n_suffix' => lang ( 'suffix' ),
'n_fn' => lang ( 'full name' ),
'n_fileas' => lang ( 'own sorting' ),
'bday' => lang ( 'birthday' ),
2012-05-15 18:53:19 +02:00
'org_name' => lang ( 'Organisation' ),
2006-04-26 23:23:38 +02:00
'org_unit' => lang ( 'Department' ),
2011-12-05 20:32:12 +01:00
'title' => lang ( 'title' ),
'role' => lang ( 'role' ),
2006-04-26 23:23:38 +02:00
'assistent' => lang ( 'Assistent' ),
'room' => lang ( 'Room' ),
2013-04-23 22:08:51 +02:00
'adr_one_street' => lang ( 'business street' ),
'adr_one_street2' => lang ( 'business address line 2' ),
'adr_one_locality' => lang ( 'business city' ),
'adr_one_region' => lang ( 'business state' ),
'adr_one_postalcode' => lang ( 'business zip code' ),
'adr_one_countryname' => lang ( 'business country' ),
'adr_one_countrycode' => lang ( 'business country code' ),
2006-04-26 23:23:38 +02:00
'label' => lang ( 'label' ),
2013-04-23 22:08:51 +02:00
'adr_two_street' => lang ( 'street (private)' ),
'adr_two_street2' => lang ( 'address line 2 (private)' ),
'adr_two_locality' => lang ( 'city (private)' ),
'adr_two_region' => lang ( 'state (private)' ),
'adr_two_postalcode' => lang ( 'zip code (private)' ),
'adr_two_countryname' => lang ( 'country (private)' ),
'adr_two_countrycode' => lang ( 'country code (private)' ),
2006-04-26 23:23:38 +02:00
'tel_work' => lang ( 'work phone' ),
'tel_cell' => lang ( 'mobile phone' ),
2013-04-23 22:08:51 +02:00
'tel_fax' => lang ( 'business fax' ),
2006-04-26 23:23:38 +02:00
'tel_assistent' => lang ( 'assistent phone' ),
'tel_car' => lang ( 'car phone' ),
'tel_pager' => lang ( 'pager' ),
'tel_home' => lang ( 'home phone' ),
2013-04-23 22:08:51 +02:00
'tel_fax_home' => lang ( 'fax (private)' ),
'tel_cell_private' => lang ( 'mobile phone (private)' ),
2006-04-26 23:23:38 +02:00
'tel_other' => lang ( 'other phone' ),
'tel_prefer' => lang ( 'preferred phone' ),
2013-04-23 22:08:51 +02:00
'email' => lang ( 'business email' ),
'email_home' => lang ( 'email (private)' ),
'url' => lang ( 'url (business)' ),
'url_home' => lang ( 'url (private)' ),
2006-04-26 23:23:38 +02:00
'freebusy_uri' => lang ( 'Freebusy URI' ),
'calendar_uri' => lang ( 'Calendar URI' ),
'note' => lang ( 'note' ),
'tz' => lang ( 'time zone' ),
'geo' => lang ( 'geo' ),
'pubkey' => lang ( 'public key' ),
'created' => lang ( 'created' ),
'creator' => lang ( 'created by' ),
'modified' => lang ( 'last modified' ),
'modifier' => lang ( 'last modified by' ),
'jpegphoto' => lang ( 'photo' ),
2009-11-27 14:35:43 +01:00
'account_id' => lang ( 'Account ID' ),
2006-04-26 23:23:38 +02:00
);
$this -> business_contact_fields = array (
'org_name' => lang ( 'Company' ),
'org_unit' => lang ( 'Department' ),
'title' => lang ( 'Title' ),
'role' => lang ( 'Role' ),
'n_prefix' => lang ( 'prefix' ),
'n_given' => lang ( 'first name' ),
'n_middle' => lang ( 'middle name' ),
'n_family' => lang ( 'last name' ),
'n_suffix' => lang ( 'suffix' ),
'adr_one_street' => lang ( 'street' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_one_street2' => lang ( 'address line 2' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_one_locality' => lang ( 'city' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_one_region' => lang ( 'state' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_one_postalcode' => lang ( 'zip code' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_one_countryname' => lang ( 'country' ) . ' (' . lang ( 'business' ) . ')' ,
);
$this -> home_contact_fields = array (
'org_name' => lang ( 'Company' ),
'org_unit' => lang ( 'Department' ),
'title' => lang ( 'Title' ),
'role' => lang ( 'Role' ),
'n_prefix' => lang ( 'prefix' ),
'n_given' => lang ( 'first name' ),
'n_middle' => lang ( 'middle name' ),
'n_family' => lang ( 'last name' ),
'n_suffix' => lang ( 'suffix' ),
'adr_two_street' => lang ( 'street' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_two_street2' => lang ( 'address line 2' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_two_locality' => lang ( 'city' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_two_region' => lang ( 'state' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_two_postalcode' => lang ( 'zip code' ) . ' (' . lang ( 'business' ) . ')' ,
'adr_two_countryname' => lang ( 'country' ) . ' (' . lang ( 'business' ) . ')' ,
);
//_debug_array($this->contact_fields);
2011-08-27 18:11:10 +02:00
$this -> own_account_acl = $GLOBALS [ 'egw_info' ][ 'server' ][ 'own_account_acl' ];
2015-05-20 23:20:10 +02:00
if ( ! is_array ( $this -> own_account_acl )) $this -> own_account_acl = json_php_unserialize ( $this -> own_account_acl , true );
2006-09-15 09:16:07 +02:00
// we have only one acl (n_fn) for the whole name, as not all backends store every part in an own field
if ( $this -> own_account_acl && in_array ( 'n_fn' , $this -> own_account_acl ))
{
$this -> own_account_acl = array_merge ( $this -> own_account_acl , array ( 'n_prefix' , 'n_given' , 'n_middle' , 'n_family' , 'n_suffix' ));
}
2008-03-05 11:31:26 +01:00
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'org_fileds_to_update' ])
{
2011-08-26 15:50:20 +02:00
$this -> org_fields = $GLOBALS [ 'egw_info' ][ 'server' ][ 'org_fileds_to_update' ];
if ( ! is_array ( $this -> org_fields )) $this -> org_fields = unserialize ( $this -> org_fields );
2011-03-07 19:32:20 +01:00
// Set country code if country name is selected
$supported_fields = $this -> get_fields ( 'supported' , null , 0 );
if ( in_array ( 'adr_one_countrycode' , $supported_fields ) && in_array ( 'adr_one_countryname' , $this -> org_fields ))
{
$this -> org_fields [] = 'adr_one_countrycode' ;
}
if ( in_array ( 'adr_two_countrycode' , $supported_fields ) && in_array ( 'adr_two_countryname' , $this -> org_fields ))
{
$this -> org_fields [] = 'adr_two_countrycode' ;
}
2008-03-05 11:31:26 +01:00
}
2009-11-26 09:30:22 +01:00
$this -> categories = new categories ( $this -> user , 'addressbook' );
2010-02-08 18:53:47 +01:00
2011-08-26 15:50:20 +02:00
$this -> delete_history = $GLOBALS [ 'egw_info' ][ 'server' ][ 'history' ];
2005-11-04 00:47:52 +01:00
}
2008-04-25 21:06:15 +02:00
2014-07-23 16:00:20 +02:00
/**
* Do we use a private addressbook ( in comparison to a personal one )
*
* Used to set $this -> private_addressbook for current user .
*
* @ param string $contact_repository
* @ param array $prefs addressbook preferences
* @ return boolean
*/
2014-07-24 09:04:59 +02:00
public static function private_addressbook ( $contact_repository , array $prefs = null )
2014-07-23 16:00:20 +02:00
{
return $contact_repository == 'sql' && $prefs [ 'private_addressbook' ];
}
2009-05-02 11:37:19 +02:00
/**
* Get the availible addressbooks of the user
*
2009-08-21 09:12:07 +02:00
* @ param int $required = EGW_ACL_READ required rights on the addressbook or multiple rights or ' ed together ,
* to return only addressbooks fullfilling all the given rights
2009-05-02 11:37:19 +02:00
* @ param string $extra_label first label if given ( already translated )
2014-07-23 16:00:20 +02:00
* @ param int $user = null account_id or null for current user
2009-05-02 11:37:19 +02:00
* @ return array with owner => label pairs
*/
2014-07-23 16:00:20 +02:00
function get_addressbooks ( $required = EGW_ACL_READ , $extra_label = null , $user = null )
2009-05-02 11:37:19 +02:00
{
//echo "uicontacts::get_addressbooks($required,$include_all) grants="; _debug_array($this->grants);
2014-07-23 16:00:20 +02:00
if ( is_null ( $user ))
{
$user = $this -> user ;
$preferences = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ];
$grants = $this -> grants ;
}
else
{
$prefs_obj = new preferences ( $user );
$preferences = $prefs_obj -> read_repository ();
$grants = $this -> get_grants ( $user , 'addressbook' , $preferences );
}
2010-03-05 15:54:07 +01:00
$addressbooks = $to_sort = array ();
2009-05-02 11:37:19 +02:00
if ( $extra_label ) $addressbooks [ '' ] = $extra_label ;
2014-07-23 16:00:20 +02:00
$addressbooks [ $user ] = lang ( 'Personal' );
2009-05-02 11:37:19 +02:00
// add all group addressbooks the user has the necessary rights too
2014-07-23 16:00:20 +02:00
foreach ( $grants as $uid => $rights )
2009-05-02 11:37:19 +02:00
{
2009-08-21 09:12:07 +02:00
if (( $rights & $required ) == $required && $GLOBALS [ 'egw' ] -> accounts -> get_type ( $uid ) == 'g' )
2009-05-02 11:37:19 +02:00
{
2010-03-05 15:54:07 +01:00
$to_sort [ $uid ] = lang ( 'Group %1' , $GLOBALS [ 'egw' ] -> accounts -> id2name ( $uid ));
2009-05-02 11:37:19 +02:00
}
}
2010-03-05 15:54:07 +01:00
if ( $to_sort )
{
asort ( $to_sort );
$addressbooks += $to_sort ;
}
2015-07-23 19:40:48 +02:00
if ( $required != EGW_ACL_ADD && // do NOT allow to set accounts as default addressbook (AB can add accounts)
! $preferences [ 'addressbook' ][ 'hide_accounts' ] && (
2014-07-23 16:00:20 +02:00
( $grants [ 0 ] & $required ) == $required ||
$preferences [ 'common' ][ 'account_selection' ] == 'groupmembers' &&
2010-11-05 09:56:41 +01:00
$this -> account_repository != 'ldap' && ( $required & EGW_ACL_READ )))
2009-05-02 11:37:19 +02:00
{
$addressbooks [ 0 ] = lang ( 'Accounts' );
}
// add all other user addressbooks the user has the necessary rights too
2010-03-05 15:54:07 +01:00
$to_sort = array ();
2014-07-23 16:00:20 +02:00
foreach ( $grants as $uid => $rights )
2009-05-02 11:37:19 +02:00
{
2014-07-23 16:00:20 +02:00
if ( $uid != $user && ( $rights & $required ) == $required && $GLOBALS [ 'egw' ] -> accounts -> get_type ( $uid ) == 'u' )
2009-05-02 11:37:19 +02:00
{
2014-07-23 16:00:20 +02:00
$to_sort [ $uid ] = common :: grab_owner_name ( $uid );
2009-05-02 11:37:19 +02:00
}
}
2010-03-05 15:54:07 +01:00
if ( $to_sort )
{
asort ( $to_sort );
$addressbooks += $to_sort ;
}
2014-07-23 16:00:20 +02:00
if ( $user > 0 && self :: private_addressbook ( $this -> contact_repository , $preferences [ 'addressbook' ]))
2009-05-02 11:37:19 +02:00
{
2014-07-23 16:00:20 +02:00
$addressbooks [ $user . 'p' ] = lang ( 'Private' );
2009-05-02 11:37:19 +02:00
}
2010-11-05 09:56:41 +01:00
//echo "<p>".__METHOD__."($required,'$extra_label')"; _debug_array($addressbooks);
2009-05-02 11:37:19 +02:00
return $addressbooks ;
}
2006-04-23 16:40:31 +02:00
/**
* calculate the file_as string from the contact and the file_as type
*
* @ param array $contact
* @ param string $type = null file_as type , default null to read it from the contact , unknown / not set type default to the first one
2010-06-28 16:05:33 +02:00
* @ param boolean $update = false If true , reads the old record for any not set fields
2006-04-23 16:40:31 +02:00
* @ return string
*/
2010-06-28 16:05:33 +02:00
function fileas ( $contact , $type = null , $isUpdate = false )
2006-04-23 16:40:31 +02:00
{
if ( is_null ( $type )) $type = $contact [ 'fileas_type' ];
2008-10-10 13:25:35 +02:00
if ( ! $type ) $type = $this -> prefs [ 'fileas_default' ] ? $this -> prefs [ 'fileas_default' ] : $this -> fileas_types [ 0 ];
2008-04-25 21:06:15 +02:00
2007-04-29 16:28:47 +02:00
if ( strpos ( $type , 'n_fn' ) !== false ) $contact [ 'n_fn' ] = $this -> fullname ( $contact );
2008-04-25 21:06:15 +02:00
2010-06-28 16:05:33 +02:00
if ( $isUpdate )
{
$fileas_fields = array ( 'n_prefix' , 'n_given' , 'n_middle' , 'n_family' , 'n_suffix' , 'n_fn' , 'org_name' , 'org_unit' , 'adr_one_locality' );
$old = null ;
foreach ( $fileas_fields as $field )
{
if ( ! isset ( $contact [ $field ]))
{
if ( is_null ( $old )) $old = $this -> read ( $contact [ 'id' ]);
$contact [ $field ] = $old [ $field ];
}
}
unset ( $old );
}
2006-10-19 10:50:25 +02:00
$fileas = str_replace ( array ( 'n_prefix' , 'n_given' , 'n_middle' , 'n_family' , 'n_suffix' , 'n_fn' , 'org_name' , 'org_unit' , 'adr_one_locality' ),
array ( $contact [ 'n_prefix' ], $contact [ 'n_given' ], $contact [ 'n_middle' ], $contact [ 'n_family' ], $contact [ 'n_suffix' ],
$contact [ 'n_fn' ], $contact [ 'org_name' ], $contact [ 'org_unit' ], $contact [ 'adr_one_locality' ]), $type );
2008-04-25 21:06:15 +02:00
2006-10-19 10:50:25 +02:00
// removing empty delimiters, caused by empty contact fields
2009-05-02 11:37:19 +02:00
$fileas = str_replace ( array ( ', , : ' , ', : ' , ': , ' , ', , ' , ': : ' , ' ()' ), array ( ': ' , ': ' , ': ' , ', ' , ': ' , '' ), $fileas );
while ( $fileas [ 0 ] == ':' || $fileas [ 0 ] == ',' ) $fileas = substr ( $fileas , 2 );
2006-10-19 10:50:25 +02:00
while ( substr ( $fileas , - 2 ) == ': ' || substr ( $fileas , - 2 ) == ', ' ) $fileas = substr ( $fileas , 0 , - 2 );
//echo "<p align=right>bocontacts::fileas(,$type)='$fileas'</p>\n";
return $fileas ;
2006-04-23 16:40:31 +02:00
}
/**
* determine the file_as type from the file_as string and the contact
*
* @ param array $contact
* @ param string $type = null file_as type , default null to read it from the contact , unknown / not set type default to the first one
* @ return string
*/
function fileas_type ( $contact , $file_as = null )
{
if ( is_null ( $file_as )) $file_as = $contact [ 'n_fileas' ];
2008-04-25 21:06:15 +02:00
2006-04-23 16:40:31 +02:00
if ( $file_as )
{
foreach ( $this -> fileas_types as $type )
{
if ( $this -> fileas ( $contact , $type ) == $file_as )
{
return $type ;
}
}
}
2008-10-10 13:25:35 +02:00
return $this -> prefs [ 'fileas_default' ] ? $this -> prefs [ 'fileas_default' ] : $this -> fileas_types [ 0 ];
2006-04-23 16:40:31 +02:00
}
2013-05-10 10:52:06 +02:00
/**
* get selectbox options for the customfields
*
* @ param array $field = null
* @ return array with options :
*/
public static function cf_options ()
{
$cf_fields = config :: get_customfields ( 'addressbook' , TRUE );
foreach ( $cf_fields as $key => $value )
{
$options [ $key ] = $value [ 'label' ];
}
return $options ;
}
2006-04-23 16:40:31 +02:00
/**
* get selectbox options for the fileas types with translated labels , or real content
*
* @ param array $contact = null real content to use , default none
* @ return array with options : fileas type => label pairs
*/
function fileas_options ( $contact = null )
{
$labels = array (
'n_prefix' => lang ( 'prefix' ),
'n_given' => lang ( 'first name' ),
'n_middle' => lang ( 'middle name' ),
'n_family' => lang ( 'last name' ),
'n_suffix' => lang ( 'suffix' ),
'n_fn' => lang ( 'full name' ),
2006-04-25 14:43:41 +02:00
'org_name' => lang ( 'company' ),
2006-10-19 10:50:25 +02:00
'org_unit' => lang ( 'department' ),
'adr_one_locality' => lang ( 'city' ),
2006-04-23 16:40:31 +02:00
);
foreach ( $labels as $name => $label )
{
if ( $contact [ $name ]) $labels [ $name ] = $contact [ $name ];
}
foreach ( $this -> fileas_types as $fileas_type )
{
$options [ $fileas_type ] = $this -> fileas ( $labels , $fileas_type );
}
return $options ;
}
2009-05-09 09:58:16 +02:00
/**
* Set n_fileas ( and n_fn ) in contacts of all users ( called by Admin >> Addressbook >> Site configuration ( Admin only )
*
* If $all all fileas fields will be set , if ! $all only empty ones
*
* @ param string $fileas_type '' or type of $this -> fileas_types
* @ param int $all = false update all contacts or only ones with empty values
* @ param int & $errors = null on return number of errors
* @ return int | boolean number of contacts updated , false for wrong fileas type
*/
function set_all_fileas ( $fileas_type , $all = false , & $errors = null , $ignore_acl = false )
{
2010-02-09 22:56:39 +01:00
if ( $fileas_type != '' && ! in_array ( $fileas_type , $this -> fileas_types ))
2009-05-09 09:58:16 +02:00
{
return false ;
}
if ( $ignore_acl )
{
unset ( $this -> somain -> grants ); // to NOT limit search to contacts readable by current user
}
// to be able to work on huge contact repositories we read the contacts in chunks of 100
for ( $n = $updated = $errors = 0 ; ( $contacts = parent :: search ( $all ? array () : array (
'n_fileas IS NULL' ,
" n_fileas='' " ,
'n_fn IS NULL' ,
" n_fn='' " ,
), false , '' , '' , '' , false , 'OR' , array ( $n * 100 , 100 ))); ++ $n )
{
foreach ( $contacts as $contact )
{
$old_fn = $contact [ 'n_fn' ];
$old_fileas = $contact [ 'n_fileas' ];
$contact [ 'n_fn' ] = $this -> fullname ( $contact );
// only update fileas if type is given AND (all should be updated or n_fileas is empty)
if ( $fileas_type && ( $all || empty ( $contact [ 'n_fileas' ])))
{
$contact [ 'n_fileas' ] = $this -> fileas ( $contact , $fileas_type );
}
if ( $old_fileas != $contact [ 'n_fileas' ] || $old_fn != $contact [ 'n_fn' ])
{
//echo "<p>('$old_fileas' != '{$contact['n_fileas']}' || '$old_fn' != '{$contact['n_fn']}')=".array2string($old_fileas != $contact['n_fileas'] || $old_fn != $contact['n_fn'])."</p>\n";
// only specify/write updated fields plus "keys"
$contact = array_intersect_key ( $contact , array (
'id' => true ,
'owner' => true ,
'private' => true ,
'account_id' => true ,
'uid' => true ,
) + ( $old_fileas != $contact [ 'n_fileas' ] ? array ( 'n_fileas' => true ) : array ()) + ( $old_fn != $contact [ 'n_fn' ] ? array ( 'n_fn' => true ) : array ()));
if ( $this -> save ( $contact , $ignore_acl ))
{
$updated ++ ;
}
else
{
$errors ++ ;
}
}
}
}
return $updated ;
}
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
/**
* Cleanup all contacts db fields of all users ( called by Admin >> Addressbook >> Site configuration ( Admin only )
2009-12-01 11:24:55 +01:00
*
2009-11-11 09:44:51 +01:00
* Cleanup means to truncate all unnecessary chars like whitespaces or tabs ,
* remove unneeded carriage returns or set empty fields to NULL
*
* @ param int & $errors = null on return number of errors
* @ return int | boolean number of contacts updated
*/
function set_all_cleanup ( & $errors = null , $ignore_acl = false )
{
if ( $ignore_acl )
{
unset ( $this -> somain -> grants ); // to NOT limit search to contacts readable by current user
}
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
// fields that must not be touched
$fields_exclude = array (
2010-02-26 16:47:51 +01:00
'id' => true ,
'tid' => true ,
2009-11-11 09:44:51 +01:00
'owner' => true ,
'private' => true ,
'created' => true ,
'creator' => true ,
'modified' => true ,
'modifier' => true ,
'account_id' => true ,
'etag' => true ,
2010-02-26 16:47:51 +01:00
'uid' => true ,
2009-11-11 09:44:51 +01:00
'freebusy_uri' => true ,
2010-02-26 16:47:51 +01:00
'calendar_uri' => true ,
2009-11-11 09:44:51 +01:00
'photo' => true ,
);
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
// to be able to work on huge contact repositories we read the contacts in chunks of 100
for ( $n = $updated = $errors = 0 ; ( $contacts = parent :: search ( array (), false , '' , '' , '' , false , 'OR' , array ( $n * 100 , 100 ))); ++ $n )
{
foreach ( $contacts as $contact )
{
$fields_to_update = array ();
foreach ( $contact as $field_name => $field_value )
{
if ( $fields_exclude [ $field_name ] === true ) continue ; // dont touch specified field
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
if ( is_string ( $field_value ) && $field_name != 'pubkey' && $field_name != 'jpegphoto' )
{
// check if field has to be trimmed
if ( strlen ( $field_value ) != strlen ( trim ( $field_value )))
{
$fields_to_update [ $field_name ] = $field_value = trim ( $field_value );
}
// check if field contains a carriage return - exclude notes
if ( $field_name != 'note' && strpos ( $field_value , " \x0D \x0A " ) !== false )
{
$fields_to_update [ $field_name ] = $field_value = str_replace ( " \x0D \x0A " , " " , $field_value );;
}
}
// check if a field contains an empty string
if ( is_string ( $field_value ) && strlen ( $field_value ) == 0 )
{
$fields_to_update [ $field_name ] = $field_value = null ;
}
2009-12-02 12:06:01 +01:00
// check for valid birthday date
if ( $field_name == 'bday' && $field_value != null &&
2009-12-02 12:18:20 +01:00
! preg_match ( '/^(18|19|20|21|22)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/' , $field_value ))
2009-12-02 12:06:01 +01:00
{
$fields_to_update [ $field_name ] = $field_value = null ;
}
2009-11-11 09:44:51 +01:00
}
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
if ( count ( $fields_to_update ) > 0 )
{
$contact_to_save = array (
'id' => $contact [ 'id' ],
'owner' => $contact [ 'owner' ],
'private' => $contact [ 'private' ],
'account_id' => $contact [ 'account_id' ],
'uid' => $contact [ 'uid' ]) + $fields_to_update ;
2009-12-01 11:24:55 +01:00
2009-11-11 09:44:51 +01:00
if ( $this -> save ( $contact_to_save , $ignore_acl ))
{
$updated ++ ;
}
else
{
$errors ++ ;
}
}
}
}
return $updated ;
}
2009-05-09 09:58:16 +02:00
2006-04-23 16:40:31 +02:00
/**
* get full name from the name - parts
*
* @ param array $contact
* @ return string full name
*/
function fullname ( $contact )
{
2009-07-15 21:44:09 +02:00
if ( empty ( $contact [ 'n_family' ]) && empty ( $contact [ 'n_given' ])) {
$cpart = array ( 'org_name' );
} else {
$cpart = array ( 'n_prefix' , 'n_given' , 'n_middle' , 'n_family' , 'n_suffix' );
}
2006-04-23 16:40:31 +02:00
$parts = array ();
2009-07-15 21:44:09 +02:00
foreach ( $cpart as $n )
2006-04-23 16:40:31 +02:00
{
if ( $contact [ $n ]) $parts [] = $contact [ $n ];
}
return implode ( ' ' , $parts );
}
2006-03-07 23:43:08 +01:00
/**
* changes the data from the db - format to your work - format
*
* it gets called everytime when data is read from the db
* This function needs to be reimplemented in the derived class
*
2008-04-25 21:06:15 +02:00
* @ param array $data
2010-02-09 22:56:39 +01:00
* @ param $date_format = 'ts' date - formats : 'ts' = timestamp , 'server' = timestamp in server - time , 'array' = array or string with date - format
*
* @ return array updated data
2006-03-07 23:43:08 +01:00
*/
2010-02-09 22:56:39 +01:00
function db2data ( $data , $date_format = 'ts' )
2006-03-07 23:43:08 +01:00
{
2010-06-26 13:35:11 +02:00
static $fb_url = false ;
2010-11-04 21:40:33 +01:00
2006-03-07 23:43:08 +01:00
// convert timestamps from server-time in the db to user-time
2010-02-09 22:56:39 +01:00
foreach ( $this -> timestamps as $name )
2006-03-07 23:43:08 +01:00
{
2010-02-09 22:56:39 +01:00
if ( isset ( $data [ $name ]))
2006-03-07 23:43:08 +01:00
{
2010-02-09 22:56:39 +01:00
$data [ $name ] = egw_time :: server2user ( $data [ $name ], $date_format );
2006-03-07 23:43:08 +01:00
}
}
2014-03-06 16:10:22 +01:00
$data [ 'photo' ] = $this -> photo_src ( $data [ 'id' ], $data [ 'jpegphoto' ], '' , $data [ 'etag' ]);
2008-04-25 21:06:15 +02:00
2006-07-15 01:25:52 +02:00
// set freebusy_uri for accounts
2006-07-22 20:02:15 +02:00
if ( ! $data [ 'freebusy_uri' ] && ! $data [ 'owner' ] && $data [ 'account_id' ] && ! is_object ( $GLOBALS [ 'egw_setup' ]))
2006-07-15 01:25:52 +02:00
{
2010-06-26 13:35:11 +02:00
if ( $fb_url || @ is_dir ( EGW_SERVER_ROOT . '/calendar/inc' ))
{
$fb_url = true ;
$user = isset ( $data [ 'account_lid' ]) ? $data [ 'account_lid' ] : $GLOBALS [ 'egw' ] -> accounts -> id2name ( $data [ 'account_id' ]);
$data [ 'freebusy_uri' ] = calendar_bo :: freebusy_url ( $user );
}
2006-07-15 01:25:52 +02:00
}
2006-03-07 23:43:08 +01:00
return $data ;
}
2008-04-25 21:06:15 +02:00
2006-04-23 16:40:31 +02:00
/**
* src for photo : returns array with linkparams if jpeg exists or the $default image - name if not
* @ param int $id contact_id
* @ param boolean $jpeg = false jpeg exists or not
* @ param string $default = '' image - name to use if ! $jpeg , eg . 'template'
2014-03-06 16:10:22 +01:00
* @ param string $etag = null etag to set in url to allow caching with Expires header
2006-04-23 16:40:31 +02:00
* @ return string / array
*/
2014-03-06 16:10:22 +01:00
function photo_src ( $id , $jpeg , $default = '' , $etag = null )
2006-04-23 16:40:31 +02:00
{
2014-03-18 16:10:57 +01:00
//error_log(__METHOD__."($id, ..., etag=$etag) ". function_backtrace());
2006-04-23 16:40:31 +02:00
return $jpeg ? array (
2008-05-10 14:02:49 +02:00
'menuaction' => 'addressbook.addressbook_ui.photo' ,
2006-04-23 16:40:31 +02:00
'contact_id' => $id ,
2014-03-06 16:10:22 +01:00
) + ( isset ( $etag ) ? array (
'etag' => $etag ,
) : array ()) : $default ;
2006-04-23 16:40:31 +02:00
}
2006-03-07 23:43:08 +01:00
/**
* changes the data from your work - format to the db - format
*
* It gets called everytime when data gets writen into db or on keys for db - searches
* this needs to be reimplemented in the derived class
*
* @ param array $data
2010-02-09 22:56:39 +01:00
* @ param $date_format = 'ts' date - formats : 'ts' = timestamp , 'server' = timestamp in server - time , 'array' = array or string with date - format
*
* @ return array upated data
2006-03-07 23:43:08 +01:00
*/
2010-02-09 22:56:39 +01:00
function data2db ( $data , $date_format = 'ts' )
2006-03-07 23:43:08 +01:00
{
// convert timestamps from user-time to server-time in the db
2010-02-09 22:56:39 +01:00
foreach ( $this -> timestamps as $name )
2006-03-07 23:43:08 +01:00
{
2010-02-09 22:56:39 +01:00
if ( isset ( $data [ $name ]))
2006-03-07 23:43:08 +01:00
{
2012-03-14 16:22:51 +01:00
$data [ $name ] = egw_time :: user2server ( $data [ $name ], $date_format );
2006-03-07 23:43:08 +01:00
}
}
return $data ;
}
2005-11-04 00:47:52 +01:00
/**
* deletes contact in db
*
2006-04-23 16:40:31 +02:00
* @ param mixed & $contact contact array with key id or ( array of ) id ( s )
2007-10-11 08:24:57 +02:00
* @ param boolean $deny_account_delete = true if true never allow to delete accounts
2008-05-06 21:58:15 +02:00
* @ param int $check_etag = null
* @ return boolean | int true on success or false on failiure , 0 if etag does not match
2005-11-04 00:47:52 +01:00
*/
2008-05-06 21:58:15 +02:00
function delete ( $contact , $deny_account_delete = true , $check_etag = null )
2005-11-04 00:47:52 +01:00
{
2006-04-23 16:40:31 +02:00
if ( is_array ( $contact ) && isset ( $contact [ 'id' ]))
2005-11-04 00:47:52 +01:00
{
2006-04-23 16:40:31 +02:00
$contact = array ( $contact );
2005-11-04 00:47:52 +01:00
}
2006-04-23 16:40:31 +02:00
elseif ( ! is_array ( $contact ))
2005-11-04 00:47:52 +01:00
{
2006-04-23 16:40:31 +02:00
$contact = array ( $contact );
2005-11-04 00:47:52 +01:00
}
2006-04-23 16:40:31 +02:00
foreach ( $contact as $c )
2005-11-04 00:47:52 +01:00
{
2006-04-23 16:40:31 +02:00
$id = is_array ( $c ) ? $c [ 'id' ] : $c ;
2005-12-23 12:54:51 +01:00
2008-05-06 21:58:15 +02:00
$ok = false ;
2010-02-11 00:07:43 +01:00
if ( $this -> check_perms ( EGW_ACL_DELETE , $c , $deny_account_delete ))
2006-04-23 16:40:31 +02:00
{
2010-02-26 16:47:51 +01:00
if ( ! ( $old = $this -> read ( $id ))) return false ;
2012-03-07 13:05:02 +01:00
// check if we only mark contacts as deleted, or really delete them
// already marked as deleted item and accounts are always really deleted
// we cant mark accounts as deleted, as no such thing exists for accounts!
if ( $old [ 'owner' ] && $this -> delete_history != '' && $old [ 'tid' ] != addressbook_so :: DELETED_TYPE )
2010-02-11 00:07:43 +01:00
{
$delete = $old ;
$delete [ 'tid' ] = addressbook_so :: DELETED_TYPE ;
2012-10-01 19:23:44 +02:00
if ( $check_etag ) $delete [ 'etag' ] = $check_etag ;
if (( $ok = $this -> save ( $delete ))) $ok = true ; // we have to return true or false
2010-09-27 18:39:54 +02:00
egw_link :: unlink ( 0 , 'addressbook' , $id , '' , '' , '' , true );
2010-02-26 16:47:51 +01:00
}
elseif (( $ok = parent :: delete ( $id , $check_etag )))
2010-02-11 00:07:43 +01:00
{
egw_link :: unlink ( 0 , 'addressbook' , $id );
}
// Don't notify of final purge
2010-02-26 16:47:51 +01:00
if ( $ok && $old [ 'tid' ] != addressbook_so :: DELETED_TYPE )
{
2012-06-17 12:03:28 +02:00
if ( ! isset ( $this -> tracking )) $this -> tracking = new addressbook_tracking ( $this );
2010-02-11 00:07:43 +01:00
$this -> tracking -> track ( array ( 'id' => $id ), array ( 'id' => $id ), null , true );
}
2006-04-23 16:40:31 +02:00
}
else
{
2012-03-07 13:05:02 +01:00
break ;
2006-04-23 16:40:31 +02:00
}
}
2012-03-07 13:05:02 +01:00
//error_log(__METHOD__.'('.array2string($contact).', deny_account_delete='.array2string($deny_account_delete).', check_etag='.array2string($check_etag).' returning '.array2string($ok));
return $ok ;
2005-11-04 00:47:52 +01:00
}
2008-04-25 21:06:15 +02:00
2005-11-04 00:47:52 +01:00
/**
* saves contact to db
*
2006-04-25 14:43:41 +02:00
* @ param array & $contact contact array from etemplate :: exec
2006-06-22 01:12:45 +02:00
* @ param boolean $ignore_acl = false should the acl be checked or not
2006-07-10 00:58:04 +02:00
* @ return int / string / boolean id on success , false on failure , the error - message is in $this -> error
2005-11-04 00:47:52 +01:00
*/
2006-06-22 01:12:45 +02:00
function save ( & $contact , $ignore_acl = false )
2005-11-04 00:47:52 +01:00
{
2006-08-22 10:14:35 +02:00
// remember if we add or update a entry
if (( $isUpdate = $contact [ 'id' ]))
2005-11-04 00:47:52 +01:00
{
2007-10-11 08:24:57 +02:00
if ( ! isset ( $contact [ 'owner' ]) || ! isset ( $contact [ 'private' ])) // owner/private not set on update, eg. SyncML
2006-08-22 10:14:35 +02:00
{
2006-10-26 06:01:41 +02:00
if (( $old = $this -> read ( $contact [ 'id' ]))) // --> try reading the old entry and set it from there
2006-08-22 10:14:35 +02:00
{
2007-10-11 08:24:57 +02:00
if ( ! isset ( $contact [ 'owner' ]))
{
$contact [ 'owner' ] = $old [ 'owner' ];
}
2007-07-04 21:13:00 +02:00
if ( ! isset ( $contact [ 'private' ]))
{
$contact [ 'private' ] = $old [ 'private' ];
}
2006-08-22 10:14:35 +02:00
}
else // entry not found --> create a new one
{
$isUpdate = $contact [ 'id' ] = null ;
}
}
}
2008-03-10 08:00:32 +01:00
else
2007-06-25 18:16:55 +02:00
{
// if no owner/addressbook set use the setting of the add_default prefs (if set, otherwise the users personal addressbook)
2007-12-15 00:20:18 +01:00
if ( ! isset ( $contact [ 'owner' ])) $contact [ 'owner' ] = $this -> default_addressbook ;
if ( ! isset ( $contact [ 'private' ])) $contact [ 'private' ] = ( int ) $this -> default_private ;
2015-07-23 19:40:48 +02:00
// do NOT allow to create new accounts via addressbook, they are broken without an account_id
if ( ! $contact [ 'owner' ] && empty ( $contact [ 'account_id' ]))
{
$contact [ 'owner' ] = $this -> default_addressbook ? $this -> default_addressbook : $this -> user ;
}
2007-11-23 18:19:38 +01:00
// allow admins to import contacts with creator / created date set
if ( ! $contact [ 'creator' ] || ! $this -> is_admin ( $contact )) $contact [ 'creator' ] = $this -> user ;
if ( ! $contact [ 'created' ] || ! $this -> is_admin ( $contact )) $contact [ 'created' ] = $this -> now_su ;
2008-04-25 21:06:15 +02:00
2006-04-26 23:23:38 +02:00
if ( ! $contact [ 'tid' ]) $contact [ 'tid' ] = 'n' ;
2005-11-04 00:47:52 +01:00
}
2011-09-09 08:36:16 +02:00
// ensure accounts and group addressbooks are never private!
if ( $contact [ 'owner' ] <= 0 )
2008-03-10 08:00:32 +01:00
{
2011-09-09 08:36:16 +02:00
$contact [ 'private' ] = 0 ;
2008-03-10 08:00:32 +01:00
}
2006-06-22 01:12:45 +02:00
if ( ! $ignore_acl && ! $this -> check_perms ( $isUpdate ? EGW_ACL_EDIT : EGW_ACL_ADD , $contact ))
2005-11-04 00:47:52 +01:00
{
2006-06-13 23:53:00 +02:00
$this -> error = 'access denied' ;
2006-04-23 16:40:31 +02:00
return false ;
2005-11-04 00:47:52 +01:00
}
2010-11-04 21:40:33 +01:00
// resize image to 60px width
if ( ! empty ( $contact [ 'jpegphoto' ]))
{
2010-11-05 08:51:12 +01:00
$contact [ 'jpegphoto' ] = $this -> resize_photo ( $contact [ 'jpegphoto' ]);
2010-11-04 21:40:33 +01:00
}
2005-11-04 00:47:52 +01:00
// convert categories
2009-12-01 11:24:55 +01:00
if ( is_array ( $contact [ 'cat_id' ]))
{
2006-10-31 10:26:40 +01:00
$contact [ 'cat_id' ] = implode ( ',' , $contact [ 'cat_id' ]);
}
2010-09-28 21:50:50 +02:00
// Update country codes
2010-09-28 21:55:55 +02:00
foreach ( array ( 'adr_one_' , 'adr_two_' ) as $c_prefix ) {
2010-12-02 14:00:01 +01:00
if ( $contact [ $c_prefix . 'countryname' ] && ! $contact [ $c_prefix . 'countrycode' ] &&
2010-11-15 17:29:31 +01:00
$code = $GLOBALS [ 'egw' ] -> country -> country_code ( $contact [ $c_prefix . 'countryname' ]))
2010-09-28 21:50:50 +02:00
{
2010-09-28 21:55:55 +02:00
if ( strlen ( $code ) == 2 )
{
$contact [ $c_prefix . 'countrycode' ] = $code ;
}
else
{
$contact [ $c_prefix . 'countrycode' ] = null ;
}
2010-09-28 21:50:50 +02:00
}
2010-09-28 21:55:55 +02:00
if ( $contact [ $c_prefix . 'countrycode' ] != null )
2010-09-28 21:50:50 +02:00
{
2010-09-28 21:55:55 +02:00
$contact [ $c_prefix . 'countryname' ] = null ;
2010-09-28 21:50:50 +02:00
}
}
2005-11-04 00:47:52 +01:00
// last modified
2006-04-23 16:40:31 +02:00
$contact [ 'modifier' ] = $this -> user ;
$contact [ 'modified' ] = $this -> now_su ;
// set full name and fileas from the content
2010-02-26 16:47:51 +01:00
if ( ! isset ( $contact [ 'n_fn' ]))
{
$contact [ 'n_fn' ] = $this -> fullname ( $contact );
2006-06-17 20:50:07 +02:00
}
2010-06-28 16:05:33 +02:00
if ( isset ( $contact [ 'org_name' ])) $contact [ 'n_fileas' ] = $this -> fileas ( $contact , null , false );
2015-07-27 18:52:55 +02:00
// Get old record for tracking changes
if ( ! isset ( $old ) && $isUpdate )
{
$old = $this -> read ( $contact [ 'id' ]);
}
2006-09-15 09:16:07 +02:00
$to_write = $contact ;
// (non-admin) user editing his own account, make sure he does not change fields he is not allowed to (eg. via SyncML or xmlrpc)
2015-08-19 13:21:50 +02:00
if ( ! $ignore_acl && ! $contact [ 'owner' ] && ! ( $this -> is_admin ( $contact ) || $this -> allow_account_edit ()))
2006-09-15 09:16:07 +02:00
{
foreach ( $contact as $field => $value )
{
if ( ! in_array ( $field , $this -> own_account_acl ) && ! in_array ( $field , array ( 'id' , 'owner' , 'account_id' , 'modified' , 'modifier' )))
{
2015-07-27 18:52:55 +02:00
// user is not allowed to change that
if ( $old )
{
2015-08-19 13:21:50 +02:00
$to_write [ $field ] = $contact [ $field ] = $old [ $field ];
2015-07-27 18:52:55 +02:00
}
else
{
unset ( $to_write [ $field ]);
}
2006-09-15 09:16:07 +02:00
}
}
}
2010-02-08 18:53:47 +01:00
2010-10-06 17:30:07 +02:00
// IF THE OLD ENTRY IS A ACCOUNT, dont allow to change the owner/location
// maybe we need that for id and account_id as well.
if ( is_array ( $old ) && ( ! isset ( $old [ 'owner' ]) || empty ( $old [ 'owner' ])))
{
2010-10-06 17:42:32 +02:00
if ( isset ( $to_write [ 'owner' ]) && ! empty ( $to_write [ 'owner' ]))
{
error_log ( __METHOD__ . __LINE__ . " Trying to change account to owner: " . $to_write [ 'owner' ] . ' Account affected:' . array2string ( $old ) . ' Data send:' . array2string ( $to_write ));
unset ( $to_write [ 'owner' ]);
}
2010-10-06 17:30:07 +02:00
}
2015-08-18 13:45:59 +02:00
if ( ! ( $this -> error = parent :: save ( $to_write )))
2005-12-23 12:54:51 +01:00
{
2006-09-20 06:30:13 +02:00
$contact [ 'id' ] = $to_write [ 'id' ];
2008-04-26 10:46:44 +02:00
$contact [ 'uid' ] = $to_write [ 'uid' ];
2008-04-25 21:06:15 +02:00
$contact [ 'etag' ] = $to_write [ 'etag' ];
2013-01-31 13:45:43 +01:00
// if contact is an account and account-relevant data got updated, handle it like account got updated
2014-09-06 14:09:25 +02:00
if ( $contact [ 'account_id' ] && $isUpdate &&
2013-01-31 13:45:43 +01:00
( $old [ 'email' ] != $contact [ 'email' ] || $old [ 'n_family' ] != $contact [ 'n_family' ] || $old [ 'n_given' ] != $contact [ 'n_given' ]))
2006-06-22 01:12:45 +02:00
{
2013-01-31 13:45:43 +01:00
// invalidate the cache of the accounts class
2006-06-22 01:12:45 +02:00
$GLOBALS [ 'egw' ] -> accounts -> cache_invalidate ( $contact [ 'account_id' ]);
2013-01-31 13:45:43 +01:00
// call edit-accout hook, to let other apps know about changed account (names or email)
$GLOBALS [ 'hook_values' ] = $GLOBALS [ 'egw' ] -> accounts -> read ( $contact [ 'account_id' ]);
$GLOBALS [ 'egw' ] -> hooks -> process ( $GLOBALS [ 'hook_values' ] + array (
'location' => 'editaccount' ,
), False , True ); // called for every app now, not only enabled ones)
2006-06-22 01:12:45 +02:00
}
2006-09-15 09:16:07 +02:00
// notify interested apps about changes in the account-contact data
2014-09-06 14:09:25 +02:00
if ( ! $to_write [ 'owner' ] && $to_write [ 'account_id' ] && $isUpdate )
2006-09-15 09:16:07 +02:00
{
$to_write [ 'location' ] = 'editaccountcontact' ;
$GLOBALS [ 'egw' ] -> hooks -> process ( $to_write , False , True ); // called for every app now, not only enabled ones));
}
2010-03-24 17:23:12 +01:00
// Notify linked apps about changes in the contact data
egw_link :: notify_update ( 'addressbook' , $contact [ 'id' ], $contact );
2010-02-08 18:53:47 +01:00
2010-09-27 18:39:54 +02:00
// Check for restore of deleted contact, restore held links
2011-08-10 15:03:51 +02:00
if ( $old && $old [ 'tid' ] == addressbook_so :: DELETED_TYPE && $contact [ 'tid' ] != addressbook_so :: DELETED_TYPE )
2010-09-27 18:39:54 +02:00
{
egw_link :: restore ( 'addressbook' , $contact [ 'id' ]);
}
2010-04-16 18:06:37 +02:00
// Record change history for sql - doesn't work for LDAP accounts
2012-06-17 12:03:28 +02:00
if ( ! $contact [ 'account_id' ] || $contact [ 'account_id' ] && $this -> account_repository == 'sql' )
{
2010-04-16 18:06:37 +02:00
$deleted = ( $old [ 'tid' ] == addressbook_so :: DELETED_TYPE || $contact [ 'tid' ] == addressbook_so :: DELETED_TYPE );
2012-06-17 12:03:28 +02:00
if ( ! isset ( $this -> tracking )) $this -> tracking = new addressbook_tracking ( $this );
2011-08-10 15:03:51 +02:00
$this -> tracking -> track ( $to_write , $old ? $old : null , null , $deleted );
2010-04-16 18:06:37 +02:00
}
2005-12-23 12:54:51 +01:00
}
2007-12-11 10:29:50 +01:00
2006-07-10 00:58:04 +02:00
return $this -> error ? false : $contact [ 'id' ];
2005-11-04 00:47:52 +01:00
}
2008-04-25 21:06:15 +02:00
2010-11-04 21:40:33 +01:00
/**
* Resizes photo to 60 * 80 pixel and returns it
*
* @ param string | FILE $photo string with image or open filedescribtor
2014-01-28 12:24:47 +01:00
* @ param int $dst_w = 240 max width to resize to
2010-11-04 21:40:33 +01:00
* @ return string with resized jpeg photo , null on error
*/
2014-01-28 12:24:47 +01:00
public static function resize_photo ( $photo , $dst_w = 240 )
2010-11-04 21:40:33 +01:00
{
if ( is_resource ( $photo ))
{
$photo = stream_get_contents ( $photo );
}
if ( empty ( $photo ) || ! ( $image = imagecreatefromstring ( $photo )))
{
error_log ( __METHOD__ . " () invalid image! " );
return null ;
}
$src_w = imagesx ( $image );
$src_h = imagesy ( $image );
//error_log(__METHOD__."() got image $src_w * $src_h, is_jpeg=".array2string(substr($photo,0,2) === "\377\330"));
// if $photo is to width or not a jpeg image --> resize it
2012-01-30 20:48:24 +01:00
if ( $src_w > $dst_w || cut_bytes ( $photo , 0 , 2 ) !== " \377 \330 " )
2010-11-04 21:40:33 +01:00
{
2012-01-30 20:48:24 +01:00
//error_log(__METHOD__."(,dst_w=$dst_w) src_w=$src_w, cut_bytes(photo,0,2)=".array2string(cut_bytes($photo,0,2)).' --> resizing');
2010-11-04 21:40:33 +01:00
// scale the image to a width of 60 and a height according to the proportion of the source image
$resized = imagecreatetruecolor ( $dst_w , $dst_h = round ( $src_h * $dst_w / $src_w ));
imagecopyresized ( $resized , $image , 0 , 0 , 0 , 0 , $dst_w , $dst_h , $src_w , $src_h );
ob_start ();
2012-07-17 15:17:19 +02:00
imagejpeg ( $resized , null , 90 );
2010-11-04 21:40:33 +01:00
$photo = ob_get_contents ();
ob_end_clean ();
imagedestroy ( $resized );
//error_log(__METHOD__."() resized image $src_w*$src_h to $dst_w*$dst_h");
}
2012-01-30 20:48:24 +01:00
//else error_log(__METHOD__."(,dst_w=$dst_w) src_w=$src_w, cut_bytes(photo,0,2)=".array2string(cut_bytes($photo,0,2)).' --> NOT resizing');
2010-11-04 21:40:33 +01:00
imagedestroy ( $image );
return $photo ;
}
2005-11-04 00:47:52 +01:00
/**
* reads contacts matched by key and puts all cols in the data array
*
2010-03-07 00:06:43 +01:00
* @ param int | string $contact_id
2016-01-20 15:39:16 +01:00
* @ param boolean $ignore_acl = false true : no acl check
2010-03-07 00:06:43 +01:00
* @ return array | boolean array with contact data , null if not found or false on no view perms
2005-11-04 00:47:52 +01:00
*/
2016-01-20 15:39:16 +01:00
function read ( $contact_id , $ignore_acl = false )
2005-11-04 00:47:52 +01:00
{
2006-06-24 18:03:00 +02:00
if ( ! ( $data = parent :: read ( $contact_id )))
2005-11-04 00:47:52 +01:00
{
2012-02-29 10:58:15 +01:00
$data = null ; // not found
2006-06-24 18:03:00 +02:00
}
2016-01-20 15:39:16 +01:00
elseif ( ! $ignore_acl && ! $this -> check_perms ( EGW_ACL_READ , $data ))
2006-06-24 18:03:00 +02:00
{
2012-02-29 10:58:15 +01:00
$data = false ; // no view perms
2005-11-04 00:47:52 +01:00
}
2012-02-29 10:58:15 +01:00
else
{
// determine the file-as type
$data [ 'fileas_type' ] = $this -> fileas_type ( $data );
2008-04-25 21:06:15 +02:00
2012-02-29 10:58:15 +01:00
// Update country name from code
if ( $data [ 'adr_one_countrycode' ] != null ) {
$data [ 'adr_one_countryname' ] = $GLOBALS [ 'egw' ] -> country -> get_full_name ( $data [ 'adr_one_countrycode' ], true );
}
if ( $data [ 'adr_two_countrycode' ] != null ) {
$data [ 'adr_two_countryname' ] = $GLOBALS [ 'egw' ] -> country -> get_full_name ( $data [ 'adr_two_countrycode' ], true );
}
2010-09-28 21:50:50 +02:00
}
2012-02-29 10:58:15 +01:00
//error_log(__METHOD__.'('.array2string($contact_id).') returning '.array2string($data));
2005-11-04 00:47:52 +01:00
return $data ;
}
2008-04-25 21:06:15 +02:00
2005-11-04 00:47:52 +01:00
/**
2011-06-26 15:55:25 +02:00
* Checks if the current user has the necessary ACL rights
*
* If the access of a contact is set to private , one need a private grant for a personal addressbook
* or the group membership for a group - addressbook
*
* @ param int $needed necessary ACL right : EGW_ACL_ { READ | EDIT | DELETE }
* @ param mixed $contact contact as array or the contact - id
* @ param boolean $deny_account_delete = false if true never allow to delete accounts
* @ param int $user = null for which user to check , default current user
* @ return boolean true permission granted , false for permission denied , null for contact does not exist
*/
function check_perms ( $needed , $contact , $deny_account_delete = false , $user = null )
2005-11-04 00:47:52 +01:00
{
2011-06-26 15:55:25 +02:00
if ( ! $user ) $user = $this -> user ;
if ( $user == $this -> user )
{
$grants = $this -> grants ;
$memberships = $this -> memberships ;
}
else
{
$grants = $this -> get_grants ( $user );
$memberships = $GLOBALS [ 'egw' ] -> accounts -> memberships ( $user , true );
}
2008-04-25 21:06:15 +02:00
if (( ! is_array ( $contact ) || ! isset ( $contact [ 'owner' ])) &&
2006-07-09 22:07:09 +02:00
! ( $contact = parent :: read ( is_array ( $contact ) ? $contact [ 'id' ] : $contact )))
2006-04-23 16:40:31 +02:00
{
2007-10-11 08:24:57 +02:00
return null ;
2006-04-23 16:40:31 +02:00
}
$owner = $contact [ 'owner' ];
2008-04-25 21:06:15 +02:00
2006-06-14 18:53:14 +02:00
// allow the user to edit his own account
2011-06-26 15:55:25 +02:00
if ( ! $owner && $needed == EGW_ACL_EDIT && $contact [ 'account_id' ] == $user && $this -> own_account_acl )
2006-04-23 16:40:31 +02:00
{
2011-06-26 15:55:25 +02:00
$access = true ;
2006-04-23 16:40:31 +02:00
}
2006-06-14 18:53:14 +02:00
// dont allow to delete own account (as admin handels it too)
2011-06-26 15:55:25 +02:00
elseif ( ! $owner && $needed == EGW_ACL_DELETE && ( $deny_account_delete || $contact [ 'account_id' ] == $user ))
2006-06-14 18:53:14 +02:00
{
2011-06-26 15:55:25 +02:00
$access = false ;
2006-06-14 18:53:14 +02:00
}
2010-11-05 09:56:41 +01:00
// for reading accounts (owner == 0) and account_selection == groupmembers, check if current user and contact are groupmembers
2011-06-26 15:55:25 +02:00
elseif ( $owner == 0 && $needed == EGW_ACL_READ &&
2011-07-18 11:46:32 +02:00
$GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'account_selection' ] == 'groupmembers' &&
! isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ]))
2010-11-05 09:56:41 +01:00
{
2011-06-26 15:55:25 +02:00
$access = !! array_intersect ( $memberships , $GLOBALS [ 'egw' ] -> accounts -> memberships ( $contact [ 'account_id' ], true ));
}
else
{
$access = ( $grants [ $owner ] & $needed ) &&
( ! $contact [ 'private' ] || ( $grants [ $owner ] & EGW_ACL_PRIVATE ) || in_array ( $owner , $memberships ));
2010-11-05 09:56:41 +01:00
}
2011-06-26 15:55:25 +02:00
//error_log(__METHOD__."($needed,$contact[id],$deny_account_delete,$user) returning ".array2string($access));
return $access ;
}
/**
* Check access to the file store
*
* @ param int | array $id id of entry or entry array
* @ param int $check EGW_ACL_READ for read and EGW_ACL_EDIT for write or delete access
* @ param string $rel_path = null currently not used in InfoLog
* @ param int $user = null for which user to check , default current user
* @ return boolean true if access is granted or false otherwise
*/
function file_access ( $id , $check , $rel_path = null , $user = null )
{
return $this -> check_perms ( $check , $id , false , $user );
2006-04-23 16:40:31 +02:00
}
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
/**
* Read ( virtual ) org - entry ( values " common " for most contacts in the given org )
*
* @ param string $org_id org_name : oooooo ||| org_unit : uuuuuuuuu ||| adr_one_locality : lllllll ( org_unit and adr_one_locality are optional )
* @ return array / boolean array with common org fields or false if org not found
*/
function read_org ( $org_id )
{
if ( ! $org_id ) return false ;
2009-07-20 14:11:05 +02:00
if ( strpos ( $org_id , '*AND*' ) !== false ) $org_id = str_replace ( '*AND*' , '&' , $org_id );
2006-04-30 11:34:24 +02:00
$org = array ();
foreach ( explode ( '|||' , $org_id ) as $part )
{
2010-02-25 14:42:27 +01:00
list ( $name , $value ) = explode ( ':' , $part , 2 );
2006-04-30 11:34:24 +02:00
$org [ $name ] = $value ;
}
2008-03-30 10:53:40 +02:00
$csvs = array ( 'cat_id' ); // fields with comma-separated-values
// split regular fields and custom fields
$custom_fields = $regular_fields = array ();
foreach ( $this -> org_fields as $name )
{
if ( $name [ 0 ] != '#' )
{
$regular_fields [] = $name ;
}
else
{
$custom_fields [] = $name = substr ( $name , 1 );
$regular_fields [ 'id' ] = 'id' ;
if ( substr ( $this -> customfields [ $name ][ 'type' ], 0 , 6 ) == 'select' && $this -> customfields [ $name ][ 'rows' ] || // multiselection
$this -> customfields [ $name ][ 'type' ] == 'radio' )
{
$csvs [] = '#' . $name ;
}
}
}
// read the regular fields
$contacts = parent :: search ( '' , $regular_fields , '' , '' , '' , false , 'AND' , false , $org );
2006-04-30 11:34:24 +02:00
if ( ! $contacts ) return false ;
2008-03-30 10:53:40 +02:00
// if we have custom fields, read and merge them in
if ( $custom_fields )
{
foreach ( $contacts as $contact )
{
$ids [] = $contact [ 'id' ];
}
if (( $cfs = $this -> read_customfields ( $ids , $custom_fields )))
{
foreach ( $contacts as & $contact )
{
$id = $contact [ 'id' ];
if ( isset ( $cfs [ $id ]))
{
foreach ( $cfs [ $id ] as $name => $value )
{
$contact [ '#' . $name ] = $value ;
}
}
}
unset ( $contact );
}
}
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
// create a statistic about the commonness of each fields values
$fields = array ();
foreach ( $contacts as $contact )
{
foreach ( $contact as $name => $value )
{
2008-03-30 10:53:40 +02:00
if ( ! in_array ( $name , $csvs ))
{
$fields [ $name ][ $value ] ++ ;
}
else
{
// for comma separated fields, we have to use each single value
foreach ( explode ( ',' , $value ) as $val )
{
$fields [ $name ][ $val ] ++ ;
}
}
2006-04-30 11:34:24 +02:00
}
}
foreach ( $fields as $name => $values )
{
if ( ! in_array ( $name , $this -> org_fields )) continue ;
arsort ( $values , SORT_NUMERIC );
list ( $value , $num ) = each ( $values );
//echo "<p>$name: '$value' $num/".count($contacts)."=".($num / (double) count($contacts))." >= $this->org_common_factor = ".($num / (double) count($contacts) >= $this->org_common_factor ? 'true' : 'false')."</p>\n";
if ( $value && $num / ( double ) count ( $contacts ) >= $this -> org_common_factor )
{
2008-03-30 10:53:40 +02:00
if ( ! in_array ( $name , $csvs ))
2008-03-05 11:31:26 +01:00
{
$org [ $name ] = $value ;
}
else
{
$org [ $name ] = array ();
2008-03-30 10:53:40 +02:00
foreach ( $values as $value => $num )
2008-03-05 11:31:26 +01:00
{
2008-03-30 10:53:40 +02:00
if ( $value && $num / ( double ) count ( $contacts ) >= $this -> org_common_factor )
2008-03-05 11:31:26 +01:00
{
2008-03-30 10:53:40 +02:00
$org [ $name ][] = $value ;
2008-03-05 11:31:26 +01:00
}
}
$org [ $name ] = implode ( ',' , $org [ $name ]);
}
2006-04-30 11:34:24 +02:00
}
}
return $org ;
}
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
/**
* Return all org - members with same content in one or more of the given fields ( only org_fields are counting )
*
* @ param string $org_name
* @ param array $fields field - name => value pairs
* @ return array with contacts
*/
function org_similar ( $org_name , $fields )
{
$criteria = array ();
foreach ( $this -> org_fields as $name )
{
if ( isset ( $fields [ $name ]))
{
2012-05-15 17:52:11 +02:00
if ( empty ( $fields [ $name ]))
{
$criteria [] = " ( $name IS NULL OR $name ='') " ;
}
else
{
$criteria [ $name ] = $fields [ $name ];
}
2006-04-30 11:34:24 +02:00
}
}
return parent :: search ( $criteria , false , 'n_family,n_given' , '' , '' , false , 'OR' , false , array ( 'org_name' => $org_name ));
}
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
/**
* Return the changed fields from two versions of a contact ( not modified or modifier )
*
* @ param array $from original / old version of the contact
* @ param array $to changed / new version of the contact
* @ param boolean $onld_org_fields = true check and return only org_fields , default true
* @ return array with field - name => value from $from
*/
function changed_fields ( $from , $to , $only_org_fields = true )
{
2012-05-15 17:52:11 +02:00
// we only care about countryname, if contrycode is empty
foreach ( array (
'adr_one_countryname' => 'adr_one_countrycode' ,
'adr_two_countryname' => 'adr_one_countrycode' ,
) as $name => $code )
{
if ( ! empty ( $from [ $code ])) $from [ $name ] = '' ;
if ( ! empty ( $to [ $code ])) $to [ $name ] = '' ;
}
2006-04-30 11:34:24 +02:00
$changed = array ();
foreach ( $only_org_fields ? $this -> org_fields : array_keys ( $this -> contact_fields ) as $name )
{
2008-06-12 16:28:25 +02:00
if ( in_array ( $name , array ( 'modified' , 'modifier' ))) // never count these
2006-04-30 11:34:24 +02:00
{
continue ;
}
if (( string ) $from [ $name ] != ( string ) $to [ $name ])
{
$changed [ $name ] = $from [ $name ];
}
}
return $changed ;
}
2008-04-25 21:06:15 +02:00
2006-04-30 11:34:24 +02:00
/**
* Change given fields in all members of the org with identical content in the field
*
* @ param string $org_name
* @ param array $from original / old version of the contact
* @ param array $to changed / new version of the contact
* @ param array $members = null org - members to change , default null --> function queries them itself
* @ return array / boolean ( changed - members , changed - fields , failed - members ) or false if no org_fields changed or no ( other ) members matching that fields
2008-04-25 21:06:15 +02:00
*/
2006-04-30 11:34:24 +02:00
function change_org ( $org_name , $from , $to , $members = null )
{
if ( ! ( $changed = $this -> changed_fields ( $from , $to , true ))) return false ;
if ( is_null ( $members ) || ! is_array ( $members ))
{
$members = $this -> org_similar ( $org_name , $changed );
}
if ( ! $members ) return false ;
2008-04-25 21:06:15 +02:00
2008-06-12 19:40:19 +02:00
$ids = array ();
foreach ( $members as $member )
{
$ids [] = $member [ 'id' ];
}
$customfields = $this -> read_customfields ( $ids );
2006-04-30 11:34:24 +02:00
$changed_members = $changed_fields = $failed_members = 0 ;
foreach ( $members as $member )
{
2008-06-12 19:40:19 +02:00
if ( isset ( $customfields [ $member [ 'id' ]]))
{
foreach ( $this -> customfields as $name => $data )
{
$member [ '#' . $name ] = $customfields [ $member [ 'id' ]][ $name ];
}
}
2006-04-30 11:34:24 +02:00
$fields = 0 ;
foreach ( $changed as $name => $value )
{
if (( string ) $value == ( string ) $member [ $name ])
{
$member [ $name ] = $to [ $name ];
//echo "<p>$member[n_family], $member[n_given]: $name='{$to[$name]}'</p>\n";
++ $fields ;
}
}
if ( $fields )
{
if ( ! $this -> check_perms ( EGW_ACL_EDIT , $member ) || ! $this -> save ( $member ))
{
++ $failed_members ;
}
else
{
++ $changed_members ;
$changed_fields += $fields ;
}
}
}
return array ( $changed_members , $changed_fields , $failed_members );
}
2006-04-23 16:40:31 +02:00
/**
* get title for a contact identified by $contact
2008-04-25 21:06:15 +02:00
*
2006-10-19 10:50:25 +02:00
* Is called as hook to participate in the linking . The format is determined by the link_title preference .
2006-04-23 16:40:31 +02:00
*
* @ param int / string / array $contact int / string id or array with contact
2008-03-08 23:45:01 +01:00
* @ return string / boolean string with the title , null if contact does not exitst , false if no perms to view it
2006-04-23 16:40:31 +02:00
*/
function link_title ( $contact )
{
if ( ! is_array ( $contact ) && $contact )
2005-11-04 00:47:52 +01:00
{
2006-04-23 16:40:31 +02:00
$contact = $this -> read ( $contact );
2005-11-04 00:47:52 +01:00
}
2006-04-23 16:40:31 +02:00
if ( ! is_array ( $contact ))
2005-11-04 00:47:52 +01:00
{
2006-06-24 18:03:00 +02:00
return $contact ;
2005-11-04 00:47:52 +01:00
}
2006-10-19 10:50:25 +02:00
$type = $this -> prefs [ 'link_title' ];
if ( ! $type || $type === 'n_fileas' )
{
if ( $contact [ 'n_fileas' ]) return $contact [ 'n_fileas' ];
$type = null ;
}
2013-05-10 10:52:06 +02:00
$title = $this -> fileas ( $contact , $type );
if ( $this -> prefs [ 'link_title_cf' ] && $contact [ '#' . $this -> prefs [ 'link_title_cf' ]])
{
$title .= ' ' . $contact [ '#' . $this -> prefs [ 'link_title_cf' ]];
}
return $title ;
2006-04-23 16:40:31 +02:00
}
2008-03-08 23:45:01 +01:00
/**
* get title for multiple contacts identified by $ids
2008-04-25 21:06:15 +02:00
*
2008-03-08 23:45:01 +01:00
* Is called as hook to participate in the linking . The format is determined by the link_title preference .
*
* @ param array $ids array with contact - id ' s
* @ return array with titles , see link_title
*/
function link_titles ( array $ids )
{
$titles = array ();
if (( $contacts =& $this -> search ( array ( 'contact_id' => $ids ), false )))
{
2013-05-10 10:52:06 +02:00
$ids = array ();
foreach ( $contacts as $contact )
{
$ids [] = $contact [ 'id' ];
}
$cfs = $this -> read_customfields ( $ids );
2008-03-08 23:45:01 +01:00
foreach ( $contacts as $contact )
{
2013-05-10 10:52:06 +02:00
$titles [ $contact [ 'id' ]] = $this -> link_title ( $contact + ( array ) $cfs [ $contact [ 'id' ]]);
2008-03-08 23:45:01 +01:00
}
}
// we assume all not returned contacts are not readable for the user (as we report all deleted contacts to egw_link)
foreach ( $ids as $id )
{
if ( ! isset ( $titles [ $id ]))
{
$titles [ $id ] = false ;
}
}
return $titles ;
}
2006-04-23 16:40:31 +02:00
/**
* query addressbook for contacts matching $pattern
*
* Is called as hook to participate in the linking
*
2008-10-10 12:55:52 +02:00
* @ param string | array $pattern pattern to search , or an array with a 'search' key
2009-12-08 22:24:46 +01:00
* @ param array $options Array of options for the search
2006-04-23 16:40:31 +02:00
* @ return array with id - title pairs of the matching entries
*/
2009-12-08 22:24:46 +01:00
function link_query ( $pattern , Array & $options = array ())
2006-04-23 16:40:31 +02:00
{
2015-06-08 16:26:25 +02:00
$result = $criteria = array ();
2009-12-08 22:24:46 +01:00
$limit = false ;
2006-04-23 16:40:31 +02:00
if ( $pattern )
2005-11-04 00:47:52 +01:00
{
2010-04-26 16:31:55 +02:00
$criteria = is_array ( $pattern ) ? $pattern [ 'search' ] : $pattern ;
2006-04-23 16:40:31 +02:00
}
2010-09-22 11:36:26 +02:00
if ( $options [ 'start' ] || $options [ 'num_rows' ])
{
2009-12-08 22:24:46 +01:00
$limit = array ( $options [ 'start' ], $options [ 'num_rows' ]);
}
2010-07-30 17:30:46 +02:00
$filter = ( array ) $options [ 'filter' ];
2010-06-16 16:09:52 +02:00
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'hide_accounts' ]) $filter [ 'account_id' ] = null ;
2010-12-06 11:04:29 +01:00
if (( $contacts =& parent :: search ( $criteria , false , 'org_name,n_family,n_given,cat_id,contact_email' , '' , '%' , false , 'OR' , $limit , $filter )))
2006-04-23 16:40:31 +02:00
{
2013-05-10 10:52:06 +02:00
$ids = array ();
foreach ( $contacts as $contact )
{
$ids [] = $contact [ 'id' ];
}
$cfs = $this -> read_customfields ( $ids );
2006-10-02 08:45:46 +02:00
foreach ( $contacts as $contact )
{
2015-06-08 16:26:25 +02:00
$result [ $contact [ 'id' ]] = $this -> link_title ( $contact + ( array ) $cfs [ $contact [ 'id' ]]);
// make sure to return a correctly quoted rfc822 address, if requested
if ( $options [ 'type' ] === 'email' )
{
2015-06-09 10:54:23 +02:00
$args = explode ( '@' , $contact [ 'email' ]);
$args [] = $result [ $contact [ 'id' ]];
$result [ $contact [ 'id' ]] = call_user_func_array ( 'imap_rfc822_write_address' , $args );
2015-06-08 16:26:25 +02:00
}
2008-02-15 02:36:43 +01:00
// show category color
if ( $contact [ 'cat_id' ] && ( $color = etemplate :: cats2color ( $contact [ 'cat_id' ])))
{
$result [ $contact [ 'id' ]] = array (
'label' => $result [ $contact [ 'id' ]],
'style.backgroundColor' => $color ,
);
}
2006-10-02 08:45:46 +02:00
}
2006-04-23 16:40:31 +02:00
}
2009-12-08 22:24:46 +01:00
$options [ 'total' ] = $this -> total ;
2006-04-23 16:40:31 +02:00
return $result ;
}
2006-12-11 08:35:49 +01:00
2010-09-22 11:36:26 +02:00
/**
* Query for subtype email ( returns only contacts with email address set )
*
* @ param string | array $pattern
* @ param array $options
* @ return Ambigous < multitype : , string , multitype : Ambigous < multitype : , string > string >
*/
function link_query_email ( $pattern , Array & $options = array ())
{
if ( isset ( $options [ 'filter' ]) && ! is_array ( $options [ 'filter' ]))
{
$options [ 'filter' ] = ( array ) $options [ 'filter' ];
}
// return only contacts with email set
2013-05-06 17:20:11 +02:00
$options [ 'filter' ][] = " contact_email " . $this -> db -> capabilities [ egw_db :: CAPABILITY_CASE_INSENSITIV_LIKE ] . " '%@%' " ;
2010-11-04 21:40:33 +01:00
2010-09-22 11:36:26 +02:00
// let link query know, to append email to list
$options [ 'type' ] = 'email' ;
2010-11-04 21:40:33 +01:00
2010-09-22 11:36:26 +02:00
return $this -> link_query ( $pattern , $options );
}
2010-11-04 21:40:33 +01:00
2007-06-19 19:06:32 +02:00
/**
* returns info about contacts for calender
*
* @ param int / array $ids single contact - id or array of id ' s
2008-04-25 21:06:15 +02:00
* @ return array
2007-06-19 19:06:32 +02:00
*/
function calendar_info ( $ids )
{
if ( ! $ids ) return null ;
2008-04-25 21:06:15 +02:00
2007-06-19 19:06:32 +02:00
$data = array ();
foreach ( ! is_array ( $ids ) ? array ( $ids ) : $ids as $id )
{
if ( ! ( $contact = $this -> read ( $id ))) continue ;
2008-04-25 21:06:15 +02:00
2007-06-19 19:06:32 +02:00
$data [] = array (
'res_id' => $id ,
'email' => $contact [ 'email' ] ? $contact [ 'email' ] : $contact [ 'email_home' ],
'rights' => EGW_ACL_READ_FOR_PARTICIPANTS ,
'name' => $this -> link_title ( $contact ),
2008-05-08 22:19:20 +02:00
'cn' => trim ( $contact [ 'n_given' ] . ' ' . $contact [ 'n_family' ]),
2007-06-19 19:06:32 +02:00
);
}
//echo "<p>calendar_info(".print_r($ids,true).")="; _debug_array($data);
return $data ;
}
2006-04-23 16:40:31 +02:00
2008-06-05 14:04:00 +02:00
/**
* Read the next and last event of given contacts
*
* @ param array $ids contact_id ' s
* @ param boolean $extra_title = true if true , use a short date only title and put the full title as extra_title ( tooltip )
* @ return array
*/
function read_calendar ( $ids , $extra_title = true )
{
2009-04-01 12:59:56 +02:00
if ( ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'calendar' ]) return array ();
2008-06-05 14:04:00 +02:00
$uids = array ();
foreach ( $ids as $id )
{
if ( is_numeric ( $id )) $uids [] = 'c' . $id ;
}
if ( ! $uids ) return array ();
2008-06-07 19:45:33 +02:00
$bocal = new calendar_bo ();
2008-06-05 14:04:00 +02:00
$events = $bocal -> search ( array (
'users' => $uids ,
'enum_recuring' => true ,
));
if ( ! $events ) return array ();
//_debug_array($events);
$calendars = array ();
foreach ( $events as $event )
{
foreach ( $event [ 'participants' ] as $uid => $status )
{
2009-05-02 11:37:19 +02:00
if ( $uid [ 0 ] != 'c' || ( $status == 'R' && ! $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'calendar' ][ 'show_rejected' ]))
2008-06-05 14:04:00 +02:00
{
continue ;
}
$id = ( int ) substr ( $uid , 1 );
if ( $event [ 'start' ] < $this -> now_su ) // past event --> check for last event
{
if ( ! isset ( $calendars [ $id ][ 'last_event' ]) || $event [ 'start' ] > $calendars [ $id ][ 'last_event' ])
{
$calendars [ $id ][ 'last_event' ] = $event [ 'start' ];
$link = array (
'id' => $event [ 'id' ],
'app' => 'calendar' ,
'title' => $bocal -> link_title ( $event ),
2010-07-07 12:11:52 +02:00
'extra_args' => array (
'date' => date ( 'Ymd' , $event [ 'start' ]),
),
2008-06-05 14:04:00 +02:00
);
if ( $extra_title )
{
$link [ 'extra_title' ] = $link [ 'title' ];
$link [ 'title' ] = date ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'dateformat' ], $event [ 'start' ]);
}
$calendars [ $id ][ 'last_link' ] = $link ;
}
}
else // future event --> check for next event
{
if ( ! isset ( $calendars [ $id ][ 'next_event' ]) || $event [ 'start' ] < $calendars [ $id ][ 'next_event' ])
{
$calendars [ $id ][ 'next_event' ] = $event [ 'start' ];
$link = array (
'id' => $event [ 'id' ],
'app' => 'calendar' ,
'title' => $bocal -> link_title ( $event ),
2010-07-07 12:11:52 +02:00
'extra_args' => array (
'date' => date ( 'Ymd' , $event [ 'start' ]),
),
2008-06-05 14:04:00 +02:00
);
if ( $extra_title )
{
$link [ 'extra_title' ] = $link [ 'title' ];
$link [ 'title' ] = date ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'dateformat' ], $event [ 'start' ]);
}
$calendars [ $id ][ 'next_link' ] = $link ;
}
}
}
}
return $calendars ;
}
2006-04-23 16:40:31 +02:00
/**
2006-09-18 10:15:08 +02:00
* Called by delete - account hook , when an account get deleted --> deletes / moves the personal addressbook
2006-04-23 16:40:31 +02:00
*
* @ param array $data
*/
function deleteaccount ( $data )
{
// delete/move personal addressbook
parent :: deleteaccount ( $data );
2005-11-04 00:47:52 +01:00
}
2006-04-25 14:43:41 +02:00
2011-02-04 18:09:37 +01:00
/**
* Called by delete_category hook , when a category gets deleted .
* Removes the category from addresses
*/
function delete_category ( $data )
{
2011-02-15 17:05:39 +01:00
// get all cats if you want to drop sub cats
$drop_subs = ( $data [ 'drop_subs' ] && ! $data [ 'modify_subs' ]);
if ( $drop_subs )
{
$cats = new categories ( '' , 'addressbook' );
$cat_ids = $cats -> return_all_children ( $data [ 'cat_id' ]);
}
else
{
$cat_ids = array ( $data [ 'cat_id' ]);
}
// Get addresses that use the category
@ set_time_limit ( 0 );
foreach ( $cat_ids as $cat_id )
{
2012-08-09 10:25:53 +02:00
if (( $ids = $this -> search ( array ( 'cat_id' => $cat_id ), false )))
2011-02-15 17:05:39 +01:00
{
2012-08-09 10:25:53 +02:00
foreach ( $ids as & $info )
{
$info [ 'cat_id' ] = implode ( ',' , array_diff ( explode ( ',' , $info [ 'cat_id' ]), $cat_ids ));
$this -> save ( $info );
}
2011-02-15 17:05:39 +01:00
}
}
2011-02-04 18:09:37 +01:00
}
2006-04-25 14:43:41 +02:00
/**
2006-09-18 10:15:08 +02:00
* Called by edit - account hook , when an account get edited --> not longer used
2008-04-25 21:06:15 +02:00
*
2006-09-18 10:15:08 +02:00
* This function is still there , to not give a fatal error , if the hook still exists .
* Can be removed after the next db - update , which also reloads the hooks . RalfBecker 2006 / 09 / 18
2006-04-25 14:43:41 +02:00
*
* @ param array $data
*/
function editaccount ( $data )
{
2006-09-18 10:15:08 +02:00
// just force a new registration of the addressbook hooks
include ( EGW_INCLUDE_ROOT . '/addressbook/setup/setup.inc.php' );
$GLOBALS [ 'egw' ] -> hooks -> register_hooks ( 'addressbook' , $setup_info [ 'addressbook' ][ 'hooks' ]);
2006-04-25 14:43:41 +02:00
}
2008-04-25 21:06:15 +02:00
2006-12-19 12:25:44 +01:00
/**
* Merges some given addresses into the first one and delete the others
2008-04-25 21:06:15 +02:00
*
2006-12-19 12:25:44 +01:00
* If one of the other addresses is an account , everything is merged into the account .
* If two accounts are in $ids , the function fails ( returns false ) .
*
* @ param array $ids contact - id ' s to merge
* @ return int number of successful merged contacts , false on a fatal error ( eg . cant merge two accounts )
*/
function merge ( $ids )
{
$this -> error = false ;
2010-02-09 22:56:39 +01:00
$account = null ;
2010-11-25 21:24:22 +01:00
$custom_fields = config :: get_customfields ( 'addressbook' , true );
$custom_field_list = $this -> read_customfields ( $ids );
2007-06-22 14:42:56 +02:00
foreach ( parent :: search ( array ( 'id' => $ids ), false ) as $contact ) // $this->search calls the extended search from ui!
2006-12-19 12:25:44 +01:00
{
if ( $contact [ 'account_id' ])
{
if ( ! is_null ( $account ))
{
echo $this -> error = 'Can not merge more then one account!' ;
2008-04-25 21:06:15 +02:00
return false ; // we dont deal with two accounts!
2006-12-19 12:25:44 +01:00
}
$account = $contact ;
continue ;
}
2010-11-25 21:24:22 +01:00
// Add in custom fields
2011-01-20 17:45:12 +01:00
if ( is_array ( $custom_field_list [ $contact [ 'id' ]])) $contact = array_merge ( $contact , $custom_field_list [ $contact [ 'id' ]]);
2010-11-25 21:24:22 +01:00
2006-12-19 12:25:44 +01:00
$pos = array_search ( $contact [ 'id' ], $ids );
$contacts [ $pos ] = $contact ;
}
if ( ! is_null ( $account )) // we found an account, so we merge the contacts into it
{
$target = $account ;
unset ( $account );
}
else // we found no account, so we merge all but the first into the first
{
$target = $contacts [ 0 ];
unset ( $contacts [ 0 ]);
}
if ( ! $this -> check_perms ( EGW_ACL_EDIT , $target ))
{
echo $this -> error = 'No edit permission for the target contact!' ;
return 0 ;
}
foreach ( $contacts as $contact )
{
foreach ( $contact as $name => $value )
{
if ( ! $value ) continue ;
switch ( $name )
{
case 'id' :
case 'tid' :
case 'owner' :
case 'private' :
2008-05-17 08:44:17 +02:00
case 'etag' ;
2006-12-19 12:25:44 +01:00
break ; // ignored
case 'cat_id' : // cats are all merged together
if ( ! is_array ( $target [ 'cat_id' ])) $target [ 'cat_id' ] = $target [ 'cat_id' ] ? explode ( ',' , $target [ 'cat_id' ]) : array ();
$target [ 'cat_id' ] = array_unique ( array_merge ( $target [ 'cat_id' ], is_array ( $value ) ? $value : explode ( ',' , $value )));
break ;
2008-04-25 21:06:15 +02:00
2006-12-19 12:25:44 +01:00
default :
2010-11-25 21:24:22 +01:00
// Multi-select custom fields can also be merged
if ( $name [ 0 ] == '#' ) {
$c_name = substr ( $name , 1 );
if ( $custom_fields [ $c_name ][ 'type' ] == 'select' && $custom_fields [ $c_name ][ 'rows' ] > 1 ) {
if ( ! is_array ( $target [ $name ])) $target [ $name ] = $target [ $name ] ? explode ( ',' , $target [ $name ]) : array ();
$target [ $name ] = implode ( ',' , array_unique ( array_merge ( $target [ $name ], is_array ( $value ) ? $value : explode ( ',' , $value ))));
}
}
2006-12-19 12:25:44 +01:00
if ( ! $target [ $name ]) $target [ $name ] = $value ;
break ;
}
}
}
if ( ! $this -> save ( $target )) return 0 ;
2008-04-25 21:06:15 +02:00
2006-12-19 12:25:44 +01:00
$success = 1 ;
foreach ( $contacts as $contact )
{
if ( ! $this -> check_perms ( EGW_ACL_DELETE , $contact ))
{
continue ;
}
2008-03-08 23:45:01 +01:00
foreach ( egw_link :: get_links ( 'addressbook' , $contact [ 'id' ]) as $data )
2006-12-19 12:25:44 +01:00
{
2011-02-15 15:46:09 +01:00
//_debug_array(array('function'=>__METHOD__,'line'=>__LINE__,'app'=>'addressbook','id'=>$contact['id'],'data:'=>$data,'target'=>$target['id']));
// info_from and info_link_id (main link)
$newlinkID = egw_link :: link ( 'addressbook' , $target [ 'id' ], $data [ 'app' ], $data [ 'id' ], $data [ 'remark' ], $target [ 'owner' ]);
//_debug_array(array('newLinkID'=>$newlinkID));
2011-04-07 21:37:37 +02:00
if ( $newlinkID )
2011-02-15 15:46:09 +01:00
{
// update egw_infolog set info_link_id=$newlinkID where info_id=$data['id'] and info_link_id=$data['link_id']
2011-04-07 21:37:37 +02:00
if ( $data [ 'app' ] == 'infolog' )
2011-02-15 15:46:09 +01:00
{
$this -> db -> update ( 'egw_infolog' , array (
'info_link_id' => $newlinkID
), array (
'info_id' => $data [ 'id' ],
'info_link_id' => $data [ 'link_id' ]
), __LINE__ , __FILE__ , 'infolog' );
}
unset ( $newlinkID );
}
2006-12-19 12:25:44 +01:00
}
if ( $this -> delete ( $contact [ 'id' ])) $success ++ ;
}
return $success ;
}
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
/**
* Some caching for lists within request
*
* @ var array
*/
private static $list_cache = array ();
2007-03-13 14:38:15 +01:00
/**
* Check if user has required rights for a list or list - owner
*
* @ param int $list
* @ param int $required
* @ param int $owner = null
* @ return boolean
*/
function check_list ( $list , $required , $owner = null )
{
if ( $list && ( $list_data = $this -> read_list ( $list )))
2008-04-25 21:06:15 +02:00
{
2007-03-13 14:38:15 +01:00
$owner = $list_data [ 'list_owner' ];
}
2012-01-31 20:47:52 +01:00
//error_log(__METHOD__."($list, $required, $owner) grants[$owner]=".$this->grants[$owner]." returning ".array2string(!!($this->grants[$owner] & $required)));
2007-03-13 14:38:15 +01:00
return !! ( $this -> grants [ $owner ] & $required );
}
2008-04-25 21:06:15 +02:00
2007-03-13 14:38:15 +01:00
/**
2012-01-31 10:57:59 +01:00
* Adds / updates a distribution list
2007-03-13 14:38:15 +01:00
*
2012-01-31 10:57:59 +01:00
* @ param string | array $keys list - name or array with column - name => value pairs to specify the list
2007-03-13 14:38:15 +01:00
* @ param int $owner user - or group - id
2012-01-31 10:57:59 +01:00
* @ param array $contacts = array () contacts to add ( only for not yet existing lists ! )
* @ param array & $data = array () values for keys 'list_uid' , 'list_carddav_name' , 'list_name'
* @ return int | boolean integer list_id or false on error
2007-03-13 14:38:15 +01:00
*/
2012-01-31 10:57:59 +01:00
function add_list ( $keys , $owner , $contacts = array (), array & $data = array ())
2007-03-13 14:38:15 +01:00
{
2012-01-31 20:47:52 +01:00
if ( ! $this -> check_list ( null , EGW_ACL_ADD | EGW_ACL_EDIT , $owner )) return false ;
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
try {
$ret = parent :: add_list ( $keys , $owner , $contacts , $data );
if ( $ret ) unset ( self :: $list_cache [ $ret ]);
}
// catch sql error, as creating same name&owner list gives a sql error doublicate key
catch ( egw_exception_db_invalid_sql $e ) {
return false ;
}
return $ret ;
2007-03-13 14:38:15 +01:00
}
2008-04-25 21:06:15 +02:00
2007-03-13 14:38:15 +01:00
/**
2012-01-31 10:57:59 +01:00
* Adds contacts to a distribution list
2007-03-13 14:38:15 +01:00
*
2012-01-31 10:57:59 +01:00
* @ param int | array $contact contact_id ( s )
2007-03-13 14:38:15 +01:00
* @ param int $list list - id
2012-01-31 10:57:59 +01:00
* @ param array $existing = null array of existing contact - id ( s ) of list , to not reread it , eg . array ()
2007-03-13 14:38:15 +01:00
* @ return false on error
*/
2012-01-31 10:57:59 +01:00
function add2list ( $contact , $list , array $existing = null )
2007-03-13 14:38:15 +01:00
{
if ( ! $this -> check_list ( $list , EGW_ACL_EDIT )) return false ;
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
unset ( self :: $list_cache [ $list ]);
2012-01-31 10:57:59 +01:00
return parent :: add2list ( $contact , $list , $existing );
2007-03-13 14:38:15 +01:00
}
2008-04-25 21:06:15 +02:00
2007-03-13 14:38:15 +01:00
/**
* Removes one contact from distribution list ( s )
*
2012-01-31 10:57:59 +01:00
* @ param int | array $contact contact_id ( s )
2007-03-13 14:38:15 +01:00
* @ param int $list list - id
* @ return false on error
*/
2007-05-01 10:17:22 +02:00
function remove_from_list ( $contact , $list = null )
2007-03-13 14:38:15 +01:00
{
2007-05-01 10:17:22 +02:00
if ( $list && ! $this -> check_list ( $list , EGW_ACL_EDIT )) return false ;
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
if ( $list )
{
unset ( self :: $list_cache [ $list ]);
}
else
{
self :: $list_cache = array ();
}
2007-03-13 14:38:15 +01:00
return parent :: remove_from_list ( $contact , $list );
}
/**
* Deletes a distribution list ( incl . it ' s members )
*
* @ param int / array $list list_id ( s )
* @ return number of members deleted or false if list does not exist
*/
function delete_list ( $list )
{
2007-04-18 17:33:22 +02:00
if ( ! $this -> check_list ( $list , EGW_ACL_DELETE )) return false ;
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
foreach (( array ) $list as $l )
{
unset ( self :: $list_cache [ $l ]);
}
2007-03-13 14:38:15 +01:00
return parent :: delete_list ( $list );
}
2008-04-25 21:06:15 +02:00
2007-03-13 14:38:15 +01:00
/**
* Read data of a distribution list
*
* @ param int $list list_id
* @ return array of data or false if list does not exist
*/
function read_list ( $list )
{
2013-08-02 18:31:54 +02:00
if ( isset ( self :: $list_cache [ $list ])) return self :: $list_cache [ $list ];
2008-04-25 21:06:15 +02:00
2013-08-02 18:31:54 +02:00
return self :: $list_cache [ $list ] = parent :: read_list ( $list );
2007-05-03 10:17:31 +02:00
}
2008-04-25 21:06:15 +02:00
2007-05-03 10:17:31 +02:00
/**
* Get the address - format of a country
*
2007-06-12 18:31:58 +02:00
* This is a good reference where I got nearly all information , thanks to mikaelarhelger - AT - gmail . com
* http :// www . bitboost . com / ref / international - address - formats . html
2008-04-25 21:06:15 +02:00
*
2007-06-12 18:31:58 +02:00
* Mail me ( RalfBecker - AT - outdoor - training . de ) if you want your nation added or fixed .
2008-04-25 21:06:15 +02:00
*
2007-05-03 10:17:31 +02:00
* @ param string $country
* @ return string 'city_state_postcode' ( eg . US ) or 'postcode_city' ( eg . DE )
*/
function addr_format_by_country ( $country )
{
$code = $GLOBALS [ 'egw' ] -> country -> country_code ( $country );
switch ( $code )
{
2007-06-12 18:31:58 +02:00
case 'AU' :
2007-05-03 10:17:31 +02:00
case 'CA' :
2007-06-12 18:31:58 +02:00
case 'GB' : // not exactly right, postcode is in separate line
case 'HK' : // not exactly right, they have no postcode
case 'IN' :
case 'ID' :
case 'IE' : // not exactly right, they have no postcode
case 'JP' : // not exactly right
case 'KR' :
case 'LV' :
case 'NZ' :
case 'TW' :
case 'SA' : // not exactly right, postcode is in separate line
case 'SG' :
case 'US' :
2007-05-03 10:17:31 +02:00
$adr_format = 'city_state_postcode' ;
break ;
2008-04-25 21:06:15 +02:00
2007-06-12 18:31:58 +02:00
case 'AR' :
case 'AT' :
case 'BE' :
case 'CH' :
case 'CZ' :
case 'DK' :
case 'EE' :
case 'ES' :
case 'FI' :
case 'FR' :
2007-05-03 10:17:31 +02:00
case 'DE' :
2007-06-12 18:31:58 +02:00
case 'GL' :
case 'IS' :
case 'IL' :
case 'IT' :
case 'LT' :
case 'LU' :
case 'MY' :
case 'MX' :
case 'NL' :
case 'NO' :
case 'PL' :
case 'PT' :
case 'RO' :
case 'RU' :
case 'SE' :
2007-05-03 10:17:31 +02:00
$adr_format = 'postcode_city' ;
break ;
2008-04-25 21:06:15 +02:00
2007-05-03 10:17:31 +02:00
default :
$adr_format = $this -> prefs [ 'addr_format' ] ? $this -> prefs [ 'addr_format' ] : 'postcode_city' ;
}
//echo "<p>bocontacts::addr_format_by_country('$country'='$code') = '$adr_format'</p>\n";
return $adr_format ;
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
2009-11-18 08:46:01 +01:00
/**
* Find existing categories in database by name or add categories that do not exist yet
* currently used for vcard import
*
* @ param array $catname_list names of the categories which should be found or added
* @ param int $contact_id = null match against existing contact and expand the returned category ids
* by the ones the user normally does not see due to category permissions - used to preserve categories
* @ return array category ids ( found , added and preserved categories )
*/
function find_or_add_categories ( $catname_list , $contact_id = null )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2012-02-10 22:29:50 +01:00
if ( $contact_id && $contact_id > 0 && ( $old_contact = $this -> read ( $contact_id )))
2009-11-18 08:46:01 +01:00
{
// preserve categories without users read access
$old_categories = explode ( ',' , $old_contact [ 'cat_id' ]);
$old_cats_preserve = array ();
2010-06-14 09:45:25 +02:00
if ( is_array ( $old_categories ) && count ( $old_categories ) > 0 )
2009-11-18 08:46:01 +01:00
{
2010-06-14 09:45:25 +02:00
foreach ( $old_categories as $cat_id )
2009-11-18 08:46:01 +01:00
{
2010-06-14 09:45:25 +02:00
if ( ! $this -> categories -> check_perms ( EGW_ACL_READ , $cat_id ))
2009-11-18 08:46:01 +01:00
{
$old_cats_preserve [] = $cat_id ;
}
}
}
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
$cat_id_list = array ();
2010-06-14 09:45:25 +02:00
foreach (( array ) $catname_list as $cat_name )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
$cat_name = trim ( $cat_name );
2007-12-11 10:29:50 +01:00
$cat_id = $this -> categories -> name2id ( $cat_name , 'X-' );
if ( ! $cat_id )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-11-18 12:11:16 +01:00
// some SyncML clients (mostly phones) add an X- to the category names
2007-12-11 10:29:50 +01:00
if ( strncmp ( $cat_name , 'X-' , 2 ) == 0 )
{
$cat_name = substr ( $cat_name , 2 );
}
2009-07-15 21:44:09 +02:00
$cat_id = $this -> categories -> add ( array ( 'name' => $cat_name , 'descr' => $cat_name , 'access' => 'private' ));
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
2007-12-11 10:29:50 +01:00
if ( $cat_id )
{
$cat_id_list [] = $cat_id ;
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
2009-12-01 11:24:55 +01:00
2010-06-14 09:45:25 +02:00
if ( is_array ( $old_cats_preserve ) && count ( $old_cats_preserve ) > 0 )
2009-11-18 08:46:01 +01:00
{
$cat_id_list = array_merge ( $cat_id_list , $old_cats_preserve );
}
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
if ( count ( $cat_id_list ) > 1 )
{
2007-12-11 10:29:50 +01:00
$cat_id_list = array_unique ( $cat_id_list );
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
sort ( $cat_id_list , SORT_NUMERIC );
}
2009-11-18 08:46:01 +01:00
2012-02-10 22:29:50 +01:00
//error_log(__METHOD__."(".array2string($catname_list).", $contact_id) returning ".array2string($cat_id_list));
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
return $cat_id_list ;
}
function get_categories ( $cat_id_list )
{
2007-12-11 10:29:50 +01:00
if ( ! is_object ( $this -> categories ))
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-11-18 10:55:31 +01:00
$this -> categories = new categories ( $this -> user , 'addressbook' );
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
2007-12-11 10:29:50 +01:00
if ( ! is_array ( $cat_id_list ))
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2007-12-11 10:29:50 +01:00
$cat_id_list = explode ( ',' , $cat_id_list );
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
$cat_list = array ();
2007-12-11 10:29:50 +01:00
foreach ( $cat_id_list as $cat_id )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-12-01 11:24:55 +01:00
if ( $cat_id && $this -> categories -> check_perms ( EGW_ACL_READ , $cat_id ) &&
2009-11-18 08:46:01 +01:00
( $cat_name = $this -> categories -> id2name ( $cat_id )) && $cat_name != '--' )
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
2009-07-16 18:05:40 +02:00
$cat_list [] = $cat_name ;
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
}
}
return $cat_list ;
}
function fixup_contact ( & $contact )
{
2009-07-15 21:44:09 +02:00
if ( empty ( $contact [ 'n_fn' ]))
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
$contact [ 'n_fn' ] = $this -> fullname ( $contact );
}
2008-04-25 21:06:15 +02:00
2009-07-15 21:44:09 +02:00
if ( empty ( $contact [ 'n_fileas' ]))
SyncML patches from patrick.bihan-faou-AT-mindstep.com (without
logout+mbstring stuff), small modification to use the already exiting
methodes to generate full name and fileas)
The code is commited to trunk only at the moment to allow testing of it.
If everything goes well, we intend to commit it to 1.4 branch too.
Here's the original description of the patch by Patrick:
- handles the default config for current versions of funambol (i.e. the
scard/stask/snote/scal locations)
- tries to be a bit smarter on how the data content should be encoded
based on what the client specified (sif+base64/vcard, / fragmented or
not, etc.)
- workaround a bug in some versions of funambol, where funambol does not
specify the proper sif type for the type of requested data
- imported patch #117 from egw's tracker
- make sure that the logs generated by the horde code go to stderr so
they can be view in the webserver's logs
- as much as possible reduce code duplication. For example, the
categories are handled in the parent classes for both the SIF avn VCAL
formats for each type of data (addressbook,infolog,calendar).
- make sure the code can handle more than one categories in each
direction
- treat the 'sony ericsson' vendor string just like 'sonyericsson', the
newer phones apparently have a space in the vendor string... (this
touches some files in the icalsrv as well)
- handle notes: these should now work with everything (funambol or
other)
- remove more code duplication: the syncml "api" for the various data
types (calendar, contacts, infolog) is now common for both the vcard and
sif data formats (cf the files that need to be removed)
- handle the "privat" filter in infolog like the "private" filter (some
part of the code use the name without the trailing e)
- imported patch # 267 from egw's tracker
2007-09-29 12:29:48 +02:00
{
$contact [ 'n_fileas' ] = $this -> fileas ( $contact );
}
}
2009-07-15 21:44:09 +02:00
/**
* Try to find a matching db entry
*
* @ param array $contact the contact data we try to find
* @ param boolean $relax = false if asked to relax , we only match against some key fields
2010-02-09 22:56:39 +01:00
* @ return array od matching contact_ids
2009-07-15 21:44:09 +02:00
*/
function find_contact ( $contact , $relax = false )
{
2010-04-21 19:41:34 +02:00
$empty_addr_one = $empty_addr_two = true ;
2010-02-09 22:56:39 +01:00
if ( $this -> log )
2009-12-01 11:24:55 +01:00
{
2010-02-09 22:56:39 +01:00
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
. '(' . ( $relax ? 'RELAX' : 'EXACT' ) . ')[ContactData]:'
2010-04-21 19:41:34 +02:00
. array2string ( $contact )
. " \n " , 3 , $this -> logfile );
2009-12-01 11:24:55 +01:00
}
2010-02-09 22:56:39 +01:00
$matchingContacts = array ();
2009-07-15 21:44:09 +02:00
if ( $contact [ 'id' ] && ( $found = $this -> read ( $contact [ 'id' ])))
{
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
2010-04-21 19:41:34 +02:00
. '()[ContactID]: ' . $contact [ 'id' ]
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
2009-07-15 21:44:09 +02:00
// We only do a simple consistency check
2010-02-26 16:47:51 +01:00
if ( ! $relax || (( empty ( $found [ 'n_family' ]) || $found [ 'n_family' ] == $contact [ 'n_family' ])
2009-07-15 21:44:09 +02:00
&& ( empty ( $found [ 'n_given' ]) || $found [ 'n_given' ] == $contact [ 'n_given' ])
2010-02-26 16:47:51 +01:00
&& ( empty ( $found [ 'org_name' ]) || $found [ 'org_name' ] == $contact [ 'org_name' ])))
2009-07-15 21:44:09 +02:00
{
2010-02-09 22:56:39 +01:00
return array ( $found [ 'id' ]);
2009-07-15 21:44:09 +02:00
}
}
unset ( $contact [ 'id' ]);
2010-02-09 22:56:39 +01:00
if ( ! $relax && ! empty ( $contact [ 'uid' ]))
{
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
2010-04-21 19:41:34 +02:00
. '()[ContactUID]: ' . $contact [ 'uid' ]
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
// Try the given UID first
$criteria = array ( 'contact_uid' => $contact [ 'uid' ]);
if (( $foundContacts = parent :: search ( $criteria )))
{
foreach ( $foundContacts as $egwContact )
{
$matchingContacts [] = $egwContact [ 'id' ];
}
}
return $matchingContacts ;
}
unset ( $contact [ 'uid' ]);
2009-12-01 11:24:55 +01:00
$columns_to_search = array ( 'n_family' , 'n_given' , 'n_middle' , 'n_prefix' , 'n_suffix' ,
2009-07-15 21:44:09 +02:00
'bday' , 'org_name' , 'org_unit' , 'title' , 'role' ,
2009-11-04 17:22:36 +01:00
'email' , 'email_home' );
2009-12-01 11:24:55 +01:00
$tolerance_fields = array ( 'n_middle' , 'n_prefix' , 'n_suffix' ,
2009-11-06 15:43:46 +01:00
'bday' , 'org_unit' , 'title' , 'role' ,
'email' , 'email_home' );
2012-05-23 11:19:58 +02:00
$addr_one_fields = array ( 'adr_one_street' , 'adr_one_locality' ,
'adr_one_region' , 'adr_one_postalcode' );
$addr_two_fields = array ( 'adr_two_street' , 'adr_two_locality' ,
'adr_two_region' , 'adr_two_postalcode' );
2009-07-15 21:44:09 +02:00
2009-08-22 16:22:55 +02:00
if ( ! empty ( $contact [ 'owner' ]))
{
$columns_to_search += array ( 'owner' );
}
2009-07-15 21:44:09 +02:00
$result = false ;
$criteria = array ();
2010-04-21 19:41:34 +02:00
2009-07-15 21:44:09 +02:00
foreach ( $columns_to_search as $field )
{
2010-04-21 19:41:34 +02:00
if ( $relax && in_array ( $field , $tolerance_fields )) continue ;
if ( empty ( $contact [ $field ]))
{
2009-07-15 21:44:09 +02:00
// Not every device supports all fields
if ( ! in_array ( $field , $tolerance_fields ))
{
2010-04-21 19:41:34 +02:00
$criteria [ $field ] = '' ;
2009-07-15 21:44:09 +02:00
}
}
else
{
2010-04-21 19:41:34 +02:00
$criteria [ $field ] = $contact [ $field ];
2009-07-15 21:44:09 +02:00
}
}
if ( ! $relax )
{
// We use addresses only for strong matching
2010-04-21 19:41:34 +02:00
foreach ( $addr_one_fields as $field )
2009-07-15 21:44:09 +02:00
{
2010-04-21 19:41:34 +02:00
if ( empty ( $contact [ $field ]))
2009-07-15 21:44:09 +02:00
{
2010-04-21 19:41:34 +02:00
$criteria [ $field ] = '' ;
}
else
{
$empty_addr_one = false ;
$criteria [ $field ] = $contact [ $field ];
2009-07-15 21:44:09 +02:00
}
}
2010-04-21 19:41:34 +02:00
foreach ( $addr_two_fields as $field )
2009-07-15 21:44:09 +02:00
{
2010-04-21 19:41:34 +02:00
if ( empty ( $contact [ $field ]))
2009-07-15 21:44:09 +02:00
{
2010-04-21 19:41:34 +02:00
$criteria [ $field ] = '' ;
}
else
{
$empty_addr_two = false ;
$criteria [ $field ] = $contact [ $field ];
2009-07-15 21:44:09 +02:00
}
}
}
2010-04-21 19:41:34 +02:00
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
. '()[Addressbook FIND Step 1]: '
2010-04-21 19:41:34 +02:00
. 'CRITERIA = ' . array2string ( $criteria )
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
2009-07-15 21:44:09 +02:00
// first try full match
2010-04-21 19:41:34 +02:00
if (( $foundContacts = parent :: search ( $criteria , true , '' , '' , '' , true )))
2009-07-15 21:44:09 +02:00
{
2010-02-09 22:56:39 +01:00
foreach ( $foundContacts as $egwContact )
{
$matchingContacts [] = $egwContact [ 'id' ];
}
2009-07-15 21:44:09 +02:00
}
// No need for more searches for relaxed matching
2010-04-21 19:41:34 +02:00
if ( $relax || count ( $matchingContacts )) return $matchingContacts ;
2010-02-09 22:56:39 +01:00
2010-04-21 19:41:34 +02:00
if ( ! $empty_addr_one && $empty_addr_two )
2009-07-15 21:44:09 +02:00
{
// try given address and ignore the second one in EGW
2010-04-21 19:41:34 +02:00
foreach ( $addr_two_fields as $field )
{
unset ( $criteria [ $field ]);
}
2009-07-15 21:44:09 +02:00
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
. '()[Addressbook FIND Step 2]: '
2010-04-21 19:41:34 +02:00
. 'CRITERIA = ' . array2string ( $criteria )
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
2010-04-21 19:41:34 +02:00
if (( $foundContacts = parent :: search ( $criteria , true , '' , '' , '' , true )))
2009-07-15 21:44:09 +02:00
{
2010-02-09 22:56:39 +01:00
foreach ( $foundContacts as $egwContact )
{
$matchingContacts [] = $egwContact [ 'id' ];
}
2009-07-15 21:44:09 +02:00
}
else
{
// try address as home address -- some devices don't qualify addresses
foreach ( $addr_two_fields as $key => $field )
{
2010-04-21 19:41:34 +02:00
$criteria [ $field ] = $criteria [ $addr_one_fields [ $key ]];
unset ( $criteria [ $addr_one_fields [ $key ]]);
2009-07-15 21:44:09 +02:00
}
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
. '()[Addressbook FIND Step 3]: '
2010-04-21 19:41:34 +02:00
. 'CRITERIA = ' . array2string ( $criteria )
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
2010-04-21 19:41:34 +02:00
if (( $foundContacts = parent :: search ( $criteria , true , '' , '' , '' , true )))
2009-07-15 21:44:09 +02:00
{
2010-02-09 22:56:39 +01:00
foreach ( $foundContacts as $egwContact )
{
$matchingContacts [] = $egwContact [ 'id' ];
}
2009-07-15 21:44:09 +02:00
}
}
}
2010-04-21 19:41:34 +02:00
elseif ( ! $empty_addr_one && ! $empty_addr_two )
2010-02-09 22:56:39 +01:00
{ // try again after address swap
2009-07-15 21:44:09 +02:00
foreach ( $addr_one_fields as $key => $field )
{
2010-04-21 19:41:34 +02:00
$_temp = $criteria [ $field ];
$criteria [ $field ] = $criteria [ $addr_two_fields [ $key ]];
$criteria [ $addr_two_fields [ $key ]] = $_temp ;
2009-07-15 21:44:09 +02:00
}
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
. '()[Addressbook FIND Step 4]: '
2010-04-21 19:41:34 +02:00
. 'CRITERIA = ' . array2string ( $criteria )
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
2010-04-21 19:41:34 +02:00
if (( $foundContacts = parent :: search ( $criteria , true , '' , '' , '' , true )))
2009-07-15 21:44:09 +02:00
{
2010-02-09 22:56:39 +01:00
foreach ( $foundContacts as $egwContact )
{
$matchingContacts [] = $egwContact [ 'id' ];
}
2009-07-15 21:44:09 +02:00
}
}
2010-02-09 22:56:39 +01:00
if ( $this -> log )
{
error_log ( __FILE__ . '[' . __LINE__ . '] ' . __METHOD__
2010-04-21 19:41:34 +02:00
. '()[FOUND]: ' . array2string ( $matchingContacts )
. " \n " , 3 , $this -> logfile );
2010-02-09 22:56:39 +01:00
}
return $matchingContacts ;
2009-07-15 21:44:09 +02:00
}
2010-12-02 20:42:03 +01:00
/**
* Get a ctag ( collection tag ) for one addressbook or all addressbooks readable by a user
*
* Currently implemented as maximum modification date ( 1 seconde granularity ! )
*
* We have to include deleted entries , as otherwise the ctag will not change if an entry gets deleted !
* ( Only works if tracking of deleted entries / history is switched on ! )
*
2011-04-07 21:37:37 +02:00
* @ param int | array $owner = null 0 = accounts , null = all addressbooks or integer account_id of user or group
2010-12-02 20:42:03 +01:00
* @ return string
*/
public function get_ctag ( $owner = null )
{
$filter = array ( 'tid' => null ); // tid=null --> use all entries incl. deleted (tid='D')
// show addressbook of a single user?
2012-02-29 10:58:15 +01:00
if ( ! is_null ( $owner )) $filter [ 'owner' ] = $owner ;
2010-12-02 20:42:03 +01:00
// should we hide the accounts addressbook
if ( ! $owner && $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'addressbook' ][ 'hide_accounts' ])
{
$filter [ 'account_id' ] = null ;
}
2012-02-29 10:58:15 +01:00
$result = $this -> search ( array (), 'contact_modified' , 'contact_modified DESC' , '' , '' , false , 'AND' , array ( 0 , 1 ), $filter );
2010-12-02 20:42:03 +01:00
2012-02-29 10:58:15 +01:00
if ( ! $result || ! isset ( $result [ 0 ][ 'modified' ]))
2010-12-02 20:42:03 +01:00
{
$ctag = 'empty' ; // ctag for empty addressbook
}
else
{
2013-08-02 16:41:31 +02:00
// need to convert modified time back to server-time (was converted to user-time by search)
// as we use it direct in server-queries eg. CardDAV sync-report and to be consistent with CalDAV
$ctag = egw_time :: user2server ( $result [ 0 ][ 'modified' ]);
2010-12-02 20:42:03 +01:00
}
//error_log(__METHOD__.'('.array2string($owner).') returning '.array2string($ctag));
return $ctag ;
}
2015-05-20 23:20:10 +02:00
static public $pgp_key_regexp = '/-----BEGIN PGP PUBLIC KEY BLOCK-----.*-----END PGP PUBLIC KEY BLOCK-----\r?\n/s' ;
/**
* Search addressbook for PGP public keys of given recipients
*
* EMail addresses are lowercased to make search case - insensitive
*
* @ param string | int | array $recipients ( array of ) email addresses or numeric account - ids
* @ return array email | account_id => key pairs
*/
2015-05-25 13:25:26 +02:00
public function get_pgp_keys ( $recipients )
2015-05-20 23:20:10 +02:00
{
if ( ! $recipients ) return array ();
if ( ! is_array ( $recipients )) $recipients = array ( $recipients );
$criteria = $result = array ();
foreach ( $recipients as & $recipient )
{
if ( is_numeric ( $recipient ))
{
2015-05-23 16:04:28 +02:00
$criteria [ 'egw_addressbook.account_id' ][] = ( int ) $recipient ;
2015-05-20 23:20:10 +02:00
}
else
{
$criteria [ 'contact_email' ][] = $recipient = strtolower ( $recipient );
}
}
foreach ( $this -> search ( $criteria , array ( 'account_id' , 'contact_email' , 'contact_pubkey' ), '' , '' , '' , false , 'OR' , false ,
" contact_pubkey LIKE '%-----BEGIN PGP PUBLIC KEY BLOCK-----%' " ) as $contact )
{
$matches = null ;
if ( preg_match ( self :: $pgp_key_regexp , $contact [ 'pubkey' ], $matches ))
{
$contact [ 'email' ] = strtolower ( $contact [ 'email' ]);
if ( empty ( $criteria [ 'account_id' ]) || in_array ( $contact [ 'email' ], $recipients ))
{
$result [ $contact [ 'email' ]] = $matches [ 0 ];
}
else
{
$result [ $contact [ 'account_id' ]] = $matches [ 0 ];
}
}
}
2015-05-25 13:25:26 +02:00
return $result ;
}
/**
* Keyserver URL and CA to verify ssl connection
*/
const KEYSERVER = 'https://hkps.pool.sks-keyservers.net/pks/lookup?op=get&exact=on&search=' ;
const KEYSERVER_CA = '/addressbook/doc/sks-keyservers.netCA.pem' ;
/**
* Search keyserver for PGP public keys
*
* @ param int | string | array $recipients ( array of ) email addresses or numeric account - ids
* @ param array $result = array ()
*/
public static function get_pgp_keyserver ( $recipients , array $result = array ())
{
foreach ( $recipients as $recipient )
{
$id = $recipient ;
if ( is_numeric ( $recipient ))
{
$recipient = $GLOBALS [ 'egw' ] -> accounts -> id2name ( $recipient , 'account_email' );
}
$matches = null ;
if (( $response = file_get_contents ( self :: KEYSERVER . urlencode ( $recipient ), false , stream_context_create ( array (
'ssl' => array (
'verify_peer' => true ,
'cafile' => EGW_SERVER_ROOT . self :: KEYSERVER_CA ,
)
)))) && preg_match ( self :: $pgp_key_regexp , $response , $matches ))
{
$result [ $id ] = $matches [ 0 ];
}
}
return $result ;
}
/**
* Search addressbook for PGP public keys of given recipients
*
* EMail addresses are lowercased to make search case - insensitive
*
* @ param string | int | array $recipients ( array of ) email addresses or numeric account - ids
* @ return array email | account_id => key pairs
*/
public function ajax_get_pgp_keys ( $recipients )
{
if ( ! $recipients ) return array ();
if ( ! is_array ( $recipients )) $recipients = array ( $recipients );
$result = $this -> get_pgp_keys ( $recipients );
if (( $missing = array_diff ( $recipients , array_keys ( $result ))))
{
$result = self :: get_pgp_keyserver ( $missing , $result );
}
2015-05-20 23:20:10 +02:00
//error_log(__METHOD__."(".array2string($recipients).") returning ".array2string($result));
egw_json_response :: get () -> data ( $result );
}
/**
* Set PGP keys for given email or account_id , if user has necessary rights
*
* @ param array $keys email | account_id => public key pairs to store
* @ param boolean $allow_user_updates = null for admins , set config to allow regular users to store their pgp key
* @ return int number of pgp keys stored
*/
public function ajax_set_pgp_keys ( $keys , $allow_user_updates = null )
{
if ( isset ( $allow_user_updates ) && isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ]))
{
$update = false ;
2015-05-27 07:11:38 +02:00
if ( $allow_user_updates && ! in_array ( 'pubkey' , $this -> own_account_acl ))
2015-05-20 23:20:10 +02:00
{
2015-05-27 07:11:38 +02:00
$this -> own_account_acl [] = 'pubkey' ;
2015-05-20 23:20:10 +02:00
$update = true ;
}
2015-05-27 07:11:38 +02:00
elseif ( ! $allow_user_updates && ( $key = array_search ( 'pubkey' , $this -> own_account_acl )) !== false )
2015-05-20 23:20:10 +02:00
{
unset ( $this -> own_account_acl [ $key ]);
$update = true ;
}
if ( $update )
{
config :: save_value ( 'own_account_acl' , $this -> own_account_acl , 'phpgwapi' );
}
}
$criteria = array ();
foreach ( $keys as $recipient => $key )
{
if ( ! preg_match ( self :: $pgp_key_regexp , $key )) continue ;
if ( is_numeric ( $recipient ))
{
2015-05-23 16:04:28 +02:00
$criteria [ 'egw_addressbook.account_id' ][] = ( int ) $recipient ;
2015-05-20 23:20:10 +02:00
}
else
{
2015-05-23 16:04:28 +02:00
$criteria [ 'contact_email' ][] = $recipient ;
2015-05-20 23:20:10 +02:00
}
}
if ( ! $criteria ) return 0 ;
$updated = 0 ;
foreach ( $this -> search ( $criteria , false , '' , '' , '' , false , 'OR' ) as $contact )
{
if ( $contact [ 'account_id' ] && isset ( $keys [ $contact [ 'account_id' ]]))
{
$key = $keys [ $contact [ 'account_id' ]];
}
elseif ( isset ( $keys [ $contact [ 'email' ]]))
{
$key = $keys [ $contact [ 'email' ]];
}
if ( empty ( $contact [ 'pubkey' ]) || ! preg_match ( self :: $pgp_key_regexp , $contact [ 'pubkey' ]))
{
$contact [ 'pubkey' ] .= $key ;
}
else
{
$contact [ 'pubkey' ] = preg_replace ( self :: $pgp_key_regexp , $key , $contact [ 'pubkey' ]);
}
if ( $this -> check_perms ( EGW_ACL_EDIT , $contact ) && $this -> save ( $contact ))
{
++ $updated ;
}
}
2015-05-23 16:04:28 +02:00
if ( $criteria == array ( 'egw.addressbook.account_id' => array (( int ) $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ])))
{
$message = ! $updated ? lang ( 'Permissiong denied! Ask your administrator to allow regular uses to update their public keys.' ) :
lang ( 'Your new public key has been stored in accounts addressbook.' );
}
else
{
$message = lang ( '%1 public keys added.' , $updated );
}
2015-05-25 13:25:26 +02:00
// add all keys to public keyserver too
$message .= " \n " . lang ( '%1 key(s) added to public keyserver "%2".' ,
self :: set_pgp_keyserver ( $keys ), PARSE_URL ( self :: KEYSERVER_ADD , PHP_URL_HOST ));
2015-05-23 16:04:28 +02:00
egw_json_response :: get () -> data ( $message );
2015-05-20 23:20:10 +02:00
}
2015-05-25 13:25:26 +02:00
/**
* Keyserver add URL
*/
const KEYSERVER_ADD = 'https://hkps.pool.sks-keyservers.net/pks/add' ;
/**
* Upload PGP keys to public keyserver
*
* @ param array $keys email | account_id => public key pairs to store
* @ return int number of pgp keys stored
*/
public static function set_pgp_keyserver ( $keys )
{
$added = 0 ;
foreach ( $keys as $email => $cert )
{
if ( is_numeric ( $email ))
{
$email = $GLOBALS [ 'egw' ] -> accounts -> id2name ( $email , 'account_email' );
}
if (( $response = file_get_contents ( self :: KEYSERVER_ADD , false , stream_context_create ( array (
'ssl' => array (
'verify_peer' => true ,
'cafile' => EGW_SERVER_ROOT . self :: KEYSERVER_CA ,
),
'http' => array (
'header' => " Content-type: text/plain " ,
'method' => 'POST' ,
'content' => http_build_query ( array (
'keytext' => $cert ,
)),
),
)))))
{
$added ++ ;
}
}
return $added ;
}
2006-02-16 12:50:16 +01:00
}