diff --git a/addressbook/inc/class.addressbook_export_vcard.inc.php b/addressbook/inc/class.addressbook_export_vcard.inc.php new file mode 100644 index 0000000000..aab5728f85 --- /dev/null +++ b/addressbook/inc/class.addressbook_export_vcard.inc.php @@ -0,0 +1,129 @@ +export_limit = $export_limit; // we may not need that after all + if($export_limit == 'no' && !$limit_exception) { + return; + } + + // Need to switch the app to get the same results + $old_app = $GLOBALS['egw_info']['flags']['currentapp']; + $GLOBALS['egw_info']['flags']['currentapp'] = 'addressbook'; + + if ($options['selection'] == 'use_all' || $options['selection'] == 'all') { + // uicontacts selection with checkbox 'use_all' + $query = $GLOBALS['egw']->session->appsession('index','addressbook'); + $query['num_rows'] = -1; // all + $query['csv_export'] = true; // so get_rows method _can_ produce different content or not store state in the session + if(!array_key_exists('filter',$query)) $query['filter'] = $GLOBALS['egw_info']['user']['account_id']; + $uicontacts->get_rows($query,$selection,$readonlys, true); // only return the ids + } + elseif ( $options['selection'] == 'all_contacts' ) { + if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) { + $col_filter['account_id'] = null; + } + $selection = ExecMethod2('addressbook.addressbook_bo.search', array(), true, '', '','',false,'AND',false,$col_filter); + //$uicontacts->get_rows($query,$selection,$readonlys,true); + } else { + $selection = explode(',',$options['selection']); + } + $GLOBALS['egw_info']['flags']['currentapp'] = $old_app; + + if(bo_merge::hasExportLimit($export_limit) && !$limit_exception) { + $selection = array_slice($selection, 0, $export_limit); + } + + foreach ($selection as &$_contact) { + if(is_array($_contact) && ($_contact['id'] || $_contact['contact_id'])) + { + $_contact = $_contact[$_contact['id'] ? 'id' : 'contact_id']; + } + } + + // vCard opens & closes the resource itself, but this doesn't seem to matter + $meta = stream_get_meta_data($_stream); + + $vcard = new addressbook_vcal(); + $vcard->export($selection, $meta['uri']); + } + + /** + * returns translated name of plugin + * + * @return string name + */ + public static function get_name() { + return lang('Addressbook vCard export'); + } + + /** + * returns translated (user) description of plugin + * + * @return string descriprion + */ + public static function get_description() { + return lang("Exports contacts from your Addressbook into a vCard File."); + } + + /** + * retruns file suffix for exported file + * + * @return string suffix + */ + public static function get_filesuffix() { + return 'vcf'; + } + + public static function get_mimetype() { + return 'text/x-vcard'; + } + + /** + * return html for options. + * this way the plugin has all opertunities for options tab + * + * @return string html + */ + public function get_options_etpl() { + } + + /** + * returns slectors of this plugin via xajax + * + */ + public function get_selectors_etpl() { + return array( + 'name' => 'addressbook.export_csv_selectors', + 'content' => 'use_all', + ); + } +} diff --git a/addressbook/inc/class.addressbook_import_vcard.inc.php b/addressbook/inc/class.addressbook_import_vcard.inc.php new file mode 100644 index 0000000000..c5b5b05302 --- /dev/null +++ b/addressbook/inc/class.addressbook_import_vcard.inc.php @@ -0,0 +1,326 @@ +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(); + + // Start counting successes + $this->current = 0; + $count = 0; + $this->results = array(); + + // Failures + $this->errors = array(); + + $contacts = new egw_ical_iterator($_stream, '', $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); + $count++; + $contacts->next(); + } + + return $count; + } + + /** + * Changes a vcard object into egw data array + */ + public function _vcard($_vcard, $owner) + { + $record = $this->bocontacts->vcardtoegw($_vcard,$this->definition->plugin_options['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", + common::grab_owner_name($record['owner']), + common::grab_owner_name($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)); + } + 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'] = $GLOBALS['egw']->country->get_full_name(trim($_data[$c_prefix.'countryname']),$translated=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 egw_exception('Unsupported action: '. $_action); + + } + } + + /** + * 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; + } +} // end of iface_export_plugin +?> diff --git a/addressbook/inc/class.addressbook_wizard_import_vcard.inc.php b/addressbook/inc/class.addressbook_wizard_import_vcard.inc.php new file mode 100644 index 0000000000..5c9ca22b87 --- /dev/null +++ b/addressbook/inc/class.addressbook_wizard_import_vcard.inc.php @@ -0,0 +1,68 @@ +steps = array( + 'wizard_step60' => lang('Choose owner of imported data'), + ); + + } + + function wizard_step60(&$content, &$sel_options, &$readonlys, &$preserv) + { + if($this->debug) error_log(__METHOD__.print_r($content,true)); + + // return from step60 + if ($content['step'] == 'wizard_step60') + { + switch (array_search('pressed', $content['button'])) + { + case 'next': + return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],1); + case 'previous' : + return $GLOBALS['egw']->importexport_definitions_ui->get_step($content['step'],-1); + case 'finish': + return 'wizard_finish'; + default : + return $this->wizard_step60($content,$sel_options,$readonlys,$preserv); + } + } + // init step60 + else + { + $content['msg'] = $this->steps['wizard_step60']; + $content['step'] = 'wizard_step60'; + if(!array_key_exists($content['contact_owner']) && $content['plugin_options']) { + $content['contact_owner'] = $content['plugin_options']['contact_owner']; + } + if(!array_key_exists($content['change_owner']) && $content['plugin_options']) { + $content['change_owner'] = $content['plugin_options']['change_owner']; + } + + $bocontacts = new addressbook_bo(); + $sel_options['contact_owner'] = array('personal' => lang("Importer's personal")) + $bocontacts->get_addressbooks(EGW_ACL_ADD); + + $preserv = $content; + unset ($preserv['button']); + return 'addressbook.importexport_wizard_vcard_chooseowner'; + } + + } +} diff --git a/addressbook/setup/etemplates.inc.php b/addressbook/setup/etemplates.inc.php index 3a6d242c3f..cdbc0d01cd 100755 --- a/addressbook/setup/etemplates.inc.php +++ b/addressbook/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * EGroupware - eTemplates for Application addressbook * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2012-09-06 14:21 + * generated by soetemplate::dump4setup() 2012-09-18 09:00 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package addressbook @@ -72,6 +72,8 @@ $templ_data[] = array('name' => 'addressbook.export_explode_fields','template' = $templ_data[] = array('name' => 'addressbook.importexport_wizard_chooseowner','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:1:{s:2:"h2";s:14:",@no_owner_map";}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:30:"Use field from CSV if possible";s:4:"name";s:14:"owner_from_csv";}}i:3;a:1:{s:1:"A";a:2:{s:4:"type";s:6:"select";s:4:"name";s:13:"contact_owner";}}i:4;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}i:5;a:1:{s:1:"A";a:3:{s:4:"type";s:11:"select-bool";s:5:"label";s:32:"Change addressbook when updating";s:4:"name";s:12:"change_owner";}}}s:4:"rows";i:5;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1320351347',); +$templ_data[] = array('name' => 'addressbook.importexport_wizard_vcard_chooseowner','template' => '','lang' => '','group' => '0','version' => '1.9.001','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";}}i:2;a:1:{s:1:"A";a:2:{s:4:"type";s:6:"select";s:4:"name";s:13:"contact_owner";}}i:3;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:3;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1347980440',); + $templ_data[] = array('name' => 'addressbook.importexport_wizzard_chooseowner','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:6:"select";s:4:"name";s:13:"contact_owner";s:4:"size";s:4:"None";}}}s:4:"rows";i:2;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1146646360',); $templ_data[] = array('name' => 'addressbook.importexport_wizzard_choosesepncharset','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:1:{s:1:"B";s:5:"180px";}i:1;a:2:{s:1:"A";a:4:{s:4:"type";s:5:"label";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:14:"Fieldseperator";}s:1:"B";a:4:{s:4:"type";s:4:"text";s:7:"no_lang";s:1:"1";s:4:"name";s:8:"fieldsep";s:4:"size";s:1:"1";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:15:"Charset of file";}s:1:"B";a:4:{s:4:"type";s:6:"select";s:7:"no_lang";s:1:"1";s:4:"name";s:7:"charset";s:4:"span";s:9:",width180";}}i:4;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:20:"Header lines to skip";}s:1:"B";a:3:{s:4:"type";s:3:"int";s:4:"name";s:16:"num_header_lines";s:4:"size";s:1:"0";}}}s:4:"rows";i:4;s:4:"cols";i:2;}}','size' => '','style' => '.width180 select { width:150px;}','modified' => '1145979153',); diff --git a/addressbook/templates/default/importexport_wizard_vcard_chooseowner.xet b/addressbook/templates/default/importexport_wizard_vcard_chooseowner.xet new file mode 100644 index 0000000000..17bbeb34c8 --- /dev/null +++ b/addressbook/templates/default/importexport_wizard_vcard_chooseowner.xet @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file