2007-06-08 00:31:08 +02:00
< ? php
/**
* eGroupWare
*
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package importexport
* @ link http :// www . egroupware . org
* @ author Cornelius Weiss < nelius @ cwtech . de >
* @ copyright Cornelius Weiss < nelius @ cwtech . de >
* @ version $Id : $
*/
/**
* class import_csv for addressbook
*/
2010-03-22 16:11:12 +01:00
class addressbook_import_contacts_csv implements importexport_iface_import_plugin {
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
private static $plugin_options = array (
2007-06-09 17:38:30 +02:00
'fieldsep' , // char
'charset' , // string
'contact_owner' , // int
2008-05-10 14:02:49 +02:00
'update_cats' , // string {override|add} overides record
2007-06-23 14:06:08 +02:00
// with cat(s) from csv OR add the cat from
// csv file to exeisting cat(s) of record
'num_header_lines' , // int number of header lines
2007-06-09 17:38:30 +02:00
'field_conversion' , // array( $csv_col_num => conversion)
2007-06-08 00:31:08 +02:00
'field_mapping' , // array( $csv_col_num => adb_filed)
2008-05-10 14:02:49 +02:00
'conditions' , /* => array containing condition arrays :
2007-06-21 16:02:28 +02:00
'type' => exists , // exists
2007-06-08 00:31:08 +02:00
'string' => '#kundennummer' ,
'true' => array (
'action' => update ,
'last' => true ,
),
'false' => array (
2008-05-10 14:02:49 +02:00
'action' => insert ,
2007-06-08 00:31:08 +02:00
'last' => true ,
), */
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
);
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* actions wich could be done to data entries
*/
2010-02-26 00:18:45 +01:00
protected static $actions = array ( 'none' , 'update' , 'insert' , 'delete' , );
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* conditions for actions
*
* @ var array
*/
2010-02-26 00:18:45 +01:00
protected static $conditions = array ( 'exists' , 'greater' , 'greater or equal' , );
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
/**
* @ var definition
*/
private $definition ;
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* @ var bocontacts
*/
private $bocontacts ;
2008-05-10 14:02:49 +02:00
2010-02-26 00:18:45 +01:00
/**
* For figuring out if a contact has changed
*/
protected $tracking ;
2007-06-08 00:31:08 +02:00
/**
2007-06-21 20:08:17 +02:00
* @ var bool
*/
private $dry_run = false ;
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
/**
* @ var bool is current user admin ?
*/
private $is_admin = false ;
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
/**
* @ var int
*/
private $user = null ;
2008-05-10 14:02:49 +02:00
2011-12-15 17:24:47 +01:00
/**
* List of import warnings
*/
protected $warnings = array ();
2010-02-26 00:18:45 +01:00
/**
* List of import errors
*/
protected $errors = array ();
2010-03-03 17:29:25 +01:00
/**
* List of actions , and how many times that action was taken
*/
protected $results = array ();
2007-06-08 00:31:08 +02:00
/**
* imports entries according to given definition object .
* @ param resource $_stream
* @ param string $_charset
* @ param definition $_definition
*/
2010-03-22 16:11:12 +01:00
public function import ( $_stream , importexport_definition $_definition ) {
$import_csv = new importexport_import_csv ( $_stream , array (
2007-06-08 00:31:08 +02:00
'fieldsep' => $_definition -> plugin_options [ 'fieldsep' ],
'charset' => $_definition -> plugin_options [ 'charset' ],
));
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
$this -> definition = $_definition ;
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
// user, is admin ?
$this -> is_admin = isset ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ] ) && $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ];
$this -> user = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ];
2008-05-10 14:02:49 +02:00
2007-06-21 20:08:17 +02:00
// dry run?
2008-05-10 14:02:49 +02:00
$this -> dry_run = isset ( $_definition -> plugin_options [ 'dry_run' ] ) ? $_definition -> plugin_options [ 'dry_run' ] : false ;
2011-12-09 18:49:05 +01:00
// Needed for categories to work right
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] = 'addressbook' ;
2007-06-08 00:31:08 +02:00
// fetch the addressbook bo
2008-05-10 14:02:49 +02:00
$this -> bocontacts = new addressbook_bo ();
2010-02-26 00:18:45 +01:00
// Get the tracker for changes
$this -> tracking = new addressbook_tracking ( $this -> bocontacts );
2007-06-09 17:38:30 +02:00
// set FieldMapping.
$import_csv -> mapping = $_definition -> plugin_options [ 'field_mapping' ];
2008-05-10 14:02:49 +02:00
2007-06-09 17:38:30 +02:00
// set FieldConversion
$import_csv -> conversion = $_definition -> plugin_options [ 'field_conversion' ];
2008-05-10 14:02:49 +02:00
2007-06-23 14:06:08 +02:00
//check if file has a header lines
2010-02-26 00:18:45 +01:00
if ( isset ( $_definition -> plugin_options [ 'num_header_lines' ] ) && $_definition -> plugin_options [ 'num_header_lines' ] > 0 ) {
2007-06-23 14:06:08 +02:00
$import_csv -> skip_records ( $_definition -> plugin_options [ 'num_header_lines' ]);
2010-02-26 00:18:45 +01:00
} elseif ( isset ( $_definition -> plugin_options [ 'has_header_line' ]) && $_definition -> plugin_options [ 'has_header_line' ]) {
// First method is preferred
$import_csv -> skip_records ( 1 );
2007-06-08 00:31:08 +02:00
}
2008-05-10 14:02:49 +02:00
2011-05-12 20:01:34 +02:00
$_lookups = array ();
2011-07-29 01:26:08 +02:00
// set contact owner
$contact_owner = isset ( $_definition -> plugin_options [ 'contact_owner' ] ) ?
2007-06-23 14:06:08 +02:00
$_definition -> plugin_options [ 'contact_owner' ] : $this -> user ;
2011-07-29 01:26:08 +02:00
// Import into importer's personal addressbook
if ( $contact_owner == 'personal' )
{
$contact_owner = $this -> user ;
}
2008-05-10 14:02:49 +02:00
2010-02-26 00:18:45 +01:00
// Start counting successes
$count = 0 ;
2010-03-03 17:29:25 +01:00
$this -> results = array ();
2010-02-26 00:18:45 +01:00
// Failures
$this -> errors = array ();
2007-06-08 00:31:08 +02:00
while ( $record = $import_csv -> get_record () ) {
2010-02-26 00:18:45 +01:00
$success = false ;
2007-06-08 00:31:08 +02:00
// don't import empty contacts
if ( count ( array_unique ( $record ) ) < 2 ) continue ;
2008-05-10 14:02:49 +02:00
2011-10-25 20:49:14 +02:00
importexport_import_csv :: convert ( $record , addressbook_egw_record :: $types , 'addressbook' , $_lookups , $_definition -> plugin_options [ 'convert' ]);
2010-09-23 21:49:07 +02:00
// Set owner, unless it's supposed to come from CSV file
2011-08-08 16:44:53 +02:00
if ( $_definition -> plugin_options [ 'owner_from_csv' ] && $record [ 'owner' ]) {
if ( ! is_numeric ( $record [ 'owner' ])) {
2011-03-15 17:20:11 +01:00
// Automatically handle text owner without explicit translation
$new_owner = importexport_helper_functions :: account_name2id ( $record [ 'owner' ]);
if ( $new_owner == '' ) {
$this -> errors [ $import_csv -> get_current_position ()] = lang (
'Unable to convert "%1" to account ID. Using plugin setting (%2) for owner.' ,
$record [ 'owner' ],
2011-07-29 01:26:08 +02:00
common :: grab_owner_name ( $contact_owner )
2011-03-15 17:20:11 +01:00
);
2011-07-29 01:26:08 +02:00
$record [ 'owner' ] = $contact_owner ;
2011-03-15 17:20:11 +01:00
} else {
$record [ 'owner' ] = $new_owner ;
}
2010-09-23 21:49:07 +02:00
}
} else {
2011-07-29 01:26:08 +02:00
$record [ 'owner' ] = $contact_owner ;
2010-09-23 21:49:07 +02:00
}
2010-03-02 00:08:50 +01:00
2011-03-17 00:18:48 +01:00
// Also handle categories in their own field
$more_categories = array ();
foreach ( $_definition -> plugin_options [ 'field_mapping' ] as $number => $field_name ) {
2011-03-21 20:51:43 +01:00
if ( substr ( $field_name , 0 , 3 ) != 'cat' || ! $record [ $field_name ] || $field_name == 'cat_id' ) continue ;
2011-03-17 00:18:48 +01:00
list ( $cat , $cat_id ) = explode ( '-' , $field_name );
if ( is_numeric ( $record [ $field_name ]) && $record [ $field_name ] != 1 ) {
// Column has a single category ID
$more_categories [] = $record [ $field_name ];
} elseif ( $record [ $field_name ] == '1' ||
( ! is_numeric ( $record [ $field_name ]) && strtolower ( $record [ $field_name ]) == strtolower ( lang ( 'Yes' )))) {
// Each category got its own column. '1' is the database value, lang('yes') is the human value
$more_categories [] = $cat_id ;
} else {
// Text categories
2011-03-17 00:30:12 +01:00
$more_categories = array_merge ( $more_categories , importexport_helper_functions :: cat_name2id ( is_array ( $record [ $field_name ]) ? $record [ $field_name ] : explode ( ',' , $record [ $field_name ]), $cat_id ));
2011-03-17 00:18:48 +01:00
}
}
if ( count ( $more_categories ) > 0 ) $record [ 'cat_id' ] = array_merge ( is_array ( $record [ 'cat_id' ]) ? $record [ 'cat_id' ] : explode ( ',' , $record [ 'cat_id' ]), $more_categories );
2011-03-04 17:57:25 +01:00
2011-08-09 21:14:03 +02:00
// Private set but missing causes hidden entries
if ( array_key_exists ( 'private' , $record ) && ( ! isset ( $record [ 'private' ]) || $record [ 'private' ] == '' )) unset ( $record [ 'private' ]);
2011-10-25 20:49:14 +02:00
// Format birthday as backend requires - converter should give timestamp
if ( $record [ 'bday' ] && is_numeric ( $record [ 'bday' ]))
{
$time = new egw_time ( $record [ 'bday' ]);
$record [ 'bday' ] = $time -> format ( 'Y-m-d' );
}
2007-06-08 00:31:08 +02:00
if ( $_definition -> plugin_options [ 'conditions' ] ) {
foreach ( $_definition -> plugin_options [ 'conditions' ] as $condition ) {
2011-03-15 20:12:10 +01:00
$contacts = array ();
2007-06-08 00:31:08 +02:00
switch ( $condition [ 'type' ] ) {
// exists
case 'exists' :
2011-03-14 21:08:00 +01:00
if ( $record [ $condition [ 'string' ]]) {
2011-09-19 12:15:32 +02:00
$searchcondition = array ( $condition [ 'string' ] => $record [ $condition [ 'string' ]]);
// if we use account_id for the condition, we need to set the owner for filtering, as this
// enables addressbook_so to decide what backend is to be used
if ( $condition [ 'string' ] == 'account_id' ) $searchcondition [ 'owner' ] = 0 ;
2011-03-14 21:08:00 +01:00
$contacts = $this -> bocontacts -> search (
//array( $condition['string'] => $record[$condition['string']],),
'' ,
$_definition -> plugin_options [ 'update_cats' ] == 'add' ? false : true ,
'' , '' , '' , false , 'AND' , false ,
2011-09-19 12:15:32 +02:00
$searchcondition
2011-03-14 21:08:00 +01:00
);
}
2011-03-15 20:12:10 +01:00
if ( is_array ( $contacts ) && count ( array_keys ( $contacts ) ) >= 1 ) {
2007-06-08 00:31:08 +02:00
// apply action to all contacts matching this exists condition
$action = $condition [ 'true' ];
foreach ( ( array ) $contacts as $contact ) {
$record [ 'id' ] = $contact [ 'id' ];
2007-06-23 14:06:08 +02:00
if ( $_definition -> plugin_options [ 'update_cats' ] == 'add' ) {
2007-06-21 16:02:28 +02:00
if ( ! is_array ( $contact [ 'cat_id' ] ) ) $contact [ 'cat_id' ] = explode ( ',' , $contact [ 'cat_id' ] );
if ( ! is_array ( $record [ 'cat_id' ] ) ) $record [ 'cat_id' ] = explode ( ',' , $record [ 'cat_id' ] );
$record [ 'cat_id' ] = implode ( ',' , array_unique ( array_merge ( $record [ 'cat_id' ], $contact [ 'cat_id' ] ) ) );
}
2010-02-26 00:18:45 +01:00
$success = $this -> action ( $action [ 'action' ], $record , $import_csv -> get_current_position () );
2007-06-08 00:31:08 +02:00
}
} else {
2007-06-21 20:08:17 +02:00
$action = $condition [ 'false' ];
2010-02-26 00:18:45 +01:00
$success = ( $this -> action ( $action [ 'action' ], $record , $import_csv -> get_current_position () ));
2007-06-08 00:31:08 +02:00
}
break ;
2008-05-10 14:02:49 +02:00
// not supported action
2007-06-08 00:31:08 +02:00
default :
die ( 'condition / action not supported!!!' );
break ;
}
if ( $action [ 'last' ]) break ;
}
} else {
// unconditional insert
2010-02-26 00:18:45 +01:00
$success = $this -> action ( 'insert' , $record , $import_csv -> get_current_position () );
2007-06-08 00:31:08 +02:00
}
2011-12-13 17:07:49 +01:00
$count ++ ;
2007-06-08 00:31:08 +02:00
}
2010-02-26 00:18:45 +01:00
return $count ;
2007-06-08 00:31:08 +02:00
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* perform the required action
*
* @ param int $_action one of $this -> actions
* @ param array $_data contact data for the action
* @ return bool success or not
*/
2010-02-26 00:18:45 +01:00
private function action ( $_action , $_data , $record_num = 0 ) {
2007-06-08 00:31:08 +02:00
switch ( $_action ) {
case 'none' :
return true ;
case 'update' :
2010-02-26 00:18:45 +01:00
// Only update if there are changes
$old = $this -> bocontacts -> read ( $_data [ 'id' ]);
2010-10-29 09:29:40 +02:00
// if we get countrycodes as countryname, try to translate them -> the rest should be handled by bo classes.
foreach ( array ( 'adr_one_' , 'adr_two_' ) as $c_prefix ) {
if ( strlen ( trim ( $_data [ $c_prefix . 'countryname' ])) == 2 ) $_data [ $c_prefix . 'countryname' ] = $GLOBALS [ 'egw' ] -> country -> get_full_name ( trim ( $_data [ $c_prefix . 'countryname' ]), $translated = true );
}
2010-03-24 16:43:52 +01:00
// Don't change a user account into a contact
if ( $old [ 'owner' ] == 0 ) {
unset ( $_data [ 'owner' ]);
2010-09-23 21:49:07 +02:00
} elseif ( ! $this -> definition -> plugin_options [ 'change_owner' ]) {
// Don't change addressbook of an existing contact
unset ( $_data [ 'owner' ]);
2010-03-24 16:43:52 +01:00
}
2010-02-26 00:18:45 +01:00
// Merge to deal with fields not in import record
$_data = array_merge ( $old , $_data );
$changed = $this -> tracking -> changed_fields ( $_data , $old );
if ( count ( $changed ) == 0 ) {
return true ;
2010-10-29 09:29:40 +02:00
} else {
//error_log(__METHOD__.__LINE__.array2string($changed).' Old:'.$old['adr_one_countryname'].' ('.$old['adr_one_countrycode'].') New:'.$_data['adr_one_countryname'].' ('.$_data['adr_one_countryname'].')');
2010-09-23 17:38:28 +02:00
}
// Make sure n_fn gets updated
unset ( $_data [ 'n_fn' ]);
2010-02-26 00:18:45 +01:00
// Fall through
2007-06-08 00:31:08 +02:00
case 'insert' :
2010-03-02 00:08:50 +01:00
if ( $_action == 'insert' ) {
// Addressbook backend doesn't like inserting with ID specified, it screws up the owner & etag
unset ( $_data [ 'id' ]);
}
2010-09-23 17:38:28 +02:00
if ( ! isset ( $_data [ 'org_name' ])) {
// org_name is a trigger to update n_fileas
$_data [ 'org_name' ] = '' ;
}
2007-06-21 20:08:17 +02:00
if ( $this -> dry_run ) {
2010-08-16 16:56:14 +02:00
//print_r($_data);
2010-03-03 17:29:25 +01:00
$this -> results [ $_action ] ++ ;
2010-02-26 00:18:45 +01:00
return true ;
2007-06-21 20:08:17 +02:00
} else {
2010-02-26 00:18:45 +01:00
$result = $this -> bocontacts -> save ( $_data , $this -> is_admin );
if ( ! $result ) {
$this -> errors [ $record_num ] = $this -> bocontacts -> error ;
2010-03-03 17:29:25 +01:00
} else {
$this -> results [ $_action ] ++ ;
2010-02-26 00:18:45 +01:00
}
return $result ;
2007-06-21 20:08:17 +02:00
}
2010-03-22 16:11:12 +01:00
default :
throw new egw_exception ( 'Unsupported action' );
2007-06-08 00:31:08 +02:00
}
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* returns translated name of plugin
*
* @ return string name
*/
public static function get_name () {
2010-02-26 00:18:45 +01:00
return lang ( 'Addressbook CSV import' );
2007-06-08 00:31:08 +02:00
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* returns translated ( user ) description of plugin
*
* @ return string descriprion
*/
public static function get_description () {
return lang ( " Imports contacts into your Addressbook from a CSV File. CSV means 'Comma Seperated Values'. However in the options Tab you can also choose other seperators. " );
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* retruns file suffix ( s ) plugin can handle ( e . g . csv )
*
* @ return string suffix ( comma seperated )
*/
public static function get_filesuffix () {
return 'csv' ;
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* return etemplate components for options .
* @ abstract We can ' t deal with etemplate objects here , as an uietemplate
* objects itself are scipt orientated and not " dialog objects "
2008-05-10 14:02:49 +02:00
*
2007-06-08 00:31:08 +02:00
* @ return array (
* name => string ,
* content => array ,
* sel_options => array ,
* preserv => array ,
* )
*/
public function get_options_etpl () {
// lets do it!
}
2008-05-10 14:02:49 +02:00
2007-06-08 00:31:08 +02:00
/**
* returns etemplate name for slectors of this plugin
*
* @ return string etemplate name
*/
public function get_selectors_etpl () {
// lets do it!
}
2011-12-15 17:24:47 +01:00
/**
* Returns warnings that were encountered during importing
* Maximum of one warning message per record , but you can append if you need to
*
* @ return Array (
* record_ # => warning message
* )
*/
public function get_warnings () {
return $this -> warnings ;
}
2010-02-26 00:18:45 +01:00
/**
* Returns errors that were encountered during importing
* Maximum of one error message per record , but you can append if you need to
*
* @ return Array (
* record_ # => error message
* )
*/
public function get_errors () {
return $this -> errors ;
}
2010-03-03 17:29:25 +01:00
/**
* Returns a list of actions taken , and the number of records for that action .
* Actions are things like 'insert' , 'update' , 'delete' , and may be different for each plugin .
*
* @ return Array (
* action => record count
* )
*/
public function get_results () {
return $this -> results ;
}
2007-06-08 00:31:08 +02:00
} // end of iface_export_plugin
?>