* Add vcard import/export plugin

This commit is contained in:
Nathan Gray 2012-09-18 15:09:48 +00:00
parent c1e2403ab7
commit c8eb6c5e24
5 changed files with 550 additions and 1 deletions

View File

@ -0,0 +1,129 @@
<?php
/**
* vCard export plugin for importexport framework
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package addressbook
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
* @version $Id$
*/
/**
* export addressbook contacts as vcard
*/
class addressbook_export_vcard implements importexport_iface_export_plugin {
/**
* Exports records as defined in $_definition
*
* @param egw_record $_definition
*/
public function export( $_stream, importexport_definition $_definition) {
$uicontacts = new addressbook_ui();
$selection = array();
// Addressbook defines its own export imits
$limit_exception = bo_merge::is_export_limit_excepted();
$export_limit = bo_merge::getExportLimit($app='addressbook');
if (!$limit_exception) $export_object->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',
);
}
}

View File

@ -0,0 +1,326 @@
<?php
/**
* importexport plugin to import vCard files
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
* @version $Id$
*/
/**
* Plugin to import vCard files
*/
class addressbook_import_vcard implements importexport_iface_import_plugin {
private static $plugin_options = array(
'contact_owner'
);
/**
* actions wich could be done to data entries
*/
protected static $actions = array('insert');
/**
* conditions for actions
*
* @var array
*/
protected static $conditions = array( );
/**
* @var definition
*/
private $definition;
/**
* @var bocontacts
*/
private $bocontacts;
/**
* For figuring out if a contact has changed
*/
protected $tracking;
/**
* @var bool
*/
private $dry_run = false;
/**
* @var bool is current user admin?
*/
private $is_admin = false;
/**
* @var int
*/
private $user = null;
/**
* List of import warnings
*/
protected $warnings = array();
/**
* List of import errors
*/
protected $errors = array();
/**
* List of actions, and how many times that action was taken
*/
protected $results = array();
/**
* imports entries according to given definition object.
* @param resource $_stream
* @param string $_charset
* @param definition $_definition
*/
public function import( $_stream, importexport_definition $_definition ) {
$this->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
?>

View File

@ -0,0 +1,68 @@
<?php
/**
* eGroupWare - Wizard for Adressbook vCard import
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package addressbook
* @subpackage importexport
* @link http://www.egroupware.org
* @author Nathan Gray
* @version $Id$
*/
class addressbook_wizard_import_vcard extends addressbook_import_vcard
{
/**
* constructor
*/
function __construct()
{
$this->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';
}
}
}

View File

@ -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',);

View File

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="addressbook.importexport_wizard_vcard_chooseowner" template="" lang="" group="0" version="1.9.001">
<grid>
<columns>
<column/>
</columns>
<rows>
<row>
<description id="msg" no_lang="1"/>
</row>
<row>
<menulist>
<menupopup id="contact_owner"/>
</menulist>
</row>
<row>
<description/>
</row>
</rows>
</grid>
</template>
</overlay>