definition = $_definition; // 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']; // set contact owner $contact_owner = isset( $_definition->plugin_options['contact_owner'] ) ? $_definition->plugin_options['contact_owner'] : $this->user; // Import into importer's personal addressbook if($contact_owner == 'personal') { $contact_owner = $this->user; } // dry run? $this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] : false; // Needed for categories to work right $GLOBALS['egw_info']['flags']['currentapp'] = 'addressbook'; // fetch the addressbook bo $this->bocontacts = new addressbook_vcal(); $charset = $_definition->plugin_options['charset']; if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; // Start counting successes $this->current = 0; $count = 0; $this->results = array(); // Failures $this->errors = array(); // Fix for Apple Addressbook $vCard = preg_replace('/item\d\.(ADR|TEL|EMAIL|URL)/', '\1', stream_get_contents($_stream)); $contacts = new Api\CalDAV\IcalIterator($vCard, '', $charset, array($this, '_vcard'),array( // Owner (addressbook) $contact_owner )); $contacts->next(); while($contacts->valid()) { $this->current++; $contact = $contacts->current(); $this->action('insert', $contact, $this->current); // Stop if we have enough records for a preview if($this->dry_run) { $egw_record = new addressbook_egw_record(); $egw_record->set_record($contact); $this->preview_records[] = $egw_record; if($count >= $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']) break; } $count++; $contacts->next(); } return $count; } /** * Changes a vcard object into egw data array */ public function _vcard($_vcard, $owner) { $charset = $this->definition->plugin_options['charset']; if($charset == 'user') $charset = $GLOBALS['egw_info']['user']['preferences']['addressbook']['vcard_charset']; $record = $this->bocontacts->vcardtoegw($_vcard,$charset); $record['owner'] = $owner; // Check that owner (addressbook) is allowed if(!array_key_exists($record['owner'], $this->bocontacts->get_addressbooks())) { $this->errors[$this->current] = lang("Unable to import into %1, using %2", Api\Accounts::username($record['owner']), Api\Accounts::username($this->user) ); $record['owner'] = $this->user; } // Do not allow owner == 0 (accounts) without an account_id // It causes the contact to be filed as an account, and can't delete if(!$record['owner'] && !$record['account_id']) { $record['owner'] = $this->user; } if (is_array($record['cat_id'])) { $record['cat_id'] = implode(',',$this->bocontacts->find_or_add_categories($record['cat_id'], -1)); } // Make sure picture is loaded/updated if($record['jpegphoto']) { $record['photo_unchanged'] = false; } return $record; } /** * perform the required action * * @param int $_action one of $this->actions * @param array $_data contact data for the action * @return bool success or not */ private function action ( $_action, $_data, $record_num = 0 ) { switch ($_action) { case 'none' : return true; case 'update' : // Only update if there are changes $old = $this->bocontacts->read($_data['id']); // 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'] = Api\Country::get_full_name(trim($_data[$c_prefix.'countryname']), true); } } // Don't change a user account into a contact if($old['owner'] == 0) { unset($_data['owner']); } elseif(!$this->definition->plugin_options['change_owner']) { // Don't change addressbook of an existing contact unset($_data['owner']); } // 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; } 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'].')'); } // Make sure n_fn gets updated unset($_data['n_fn']); // Fall through case 'insert' : if($_action == 'insert') { // Addressbook backend doesn't like inserting with ID specified, it screws up the owner & etag unset($_data['id']); } if(!isset($_data['org_name'])) { // org_name is a trigger to update n_fileas $_data['org_name'] = ''; } if ( $this->dry_run ) { //print_r($_data); $this->results[$_action]++; return true; } else { $result = $this->bocontacts->save( $_data, $this->is_admin); if(!$result) { $this->errors[$record_num] = $this->bocontacts->error; } else { $this->results[$_action]++; } return $result; } default: throw new Api\Exception('Unsupported action: '. $_action); } } public function preview( $_stream, importexport_definition $_definition ) { $rows = array('h1'=>array(),'f1'=>array(),'.h1'=>'class=th'); // Set this so plugin doesn't do any data changes $_definition->plugin_options = (array)$_definition->plugin_options + array('dry_run' => true); $this->import($_stream, $_definition); rewind($_stream); // Get field labels $rows['h1'] = $labels = $this->bocontacts->contact_fields; $record_class = get_class($this->preview_records[0]); foreach($this->preview_records as $record) { // Convert to human-friendly importexport_export_csv::convert($record,$record_class::$types,$_definition->application); $record = $record->get_record_array(); $row = array(); foreach(array_keys($labels) as $field) { $row[$field] = $record[$field]; // Don't scare users, do something with jpeg if($field == 'jpegphoto' && $row[$field]) { $row[$field] = ''; } unset($record[$field]); } $row += $record; $rows[] = $row; } return Api\Html::table($rows); } /** * returns translated name of plugin * * @return string name */ public static function get_name() { return lang('Addressbook vCard import'); } /** * returns translated (user) description of plugin * * @return string descriprion */ public static function get_description() { return lang("Imports contacts into your Addressbook from a vCard File. "); } /** * retruns file suffix(s) plugin can handle (e.g. csv) * * @return string suffix (comma seperated) */ public static function get_filesuffix() { return 'vcf'; } /** * 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" * * @return array ( * name => string, * content => array, * sel_options => array, * preserv => array, * ) */ public function get_options_etpl() { // lets do it! } /** * returns etemplate name for slectors of this plugin * * @return string etemplate name */ public function get_selectors_etpl() { // lets do it! } /** * 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; } /** * 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; } /** * 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; } }