forked from extern/egroupware
* Add vcard import/export plugin
This commit is contained in:
parent
c1e2403ab7
commit
c8eb6c5e24
129
addressbook/inc/class.addressbook_export_vcard.inc.php
Normal file
129
addressbook/inc/class.addressbook_export_vcard.inc.php
Normal 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',
|
||||
);
|
||||
}
|
||||
}
|
326
addressbook/inc/class.addressbook_import_vcard.inc.php
Normal file
326
addressbook/inc/class.addressbook_import_vcard.inc.php
Normal 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
|
||||
?>
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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',);
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user