pull/7045/merge
johndoh 4 years ago committed by GitHub
commit 9847afbde0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1425,6 +1425,11 @@ function rcube_webmail()
var dialog = $('<iframe>').attr('src', this.url('import', {_framed: 1, _target: this.env.source})),
import_func = function(e) {
var win = dialog[0].contentWindow,
form = null;
if (win.rcmail.gui_objects.importformmap)
form = win.rcmail.gui_objects.importformmap;
else
form = win.rcmail.gui_objects.importform;
if (form) {

@ -408,9 +408,12 @@ class rcube_csv2vcard
/**
* Import contacts from CSV file
*
* @param string $csv Content of the CSV file
* @param string $csv Content of the CSV file
* @param boolean $dry_run Generate automatic field mapping
*
* @return array Field mapping info (dry run only)
*/
public function import($csv)
public function import($csv, $dry_run = false)
{
// convert to UTF-8
$head = substr($csv, 0, 4096);
@ -420,9 +423,6 @@ class rcube_csv2vcard
$head = '';
$prev_line = false;
$this->map = array();
$this->gmail_map = array();
// Parse file
foreach (preg_split("/[\r\n]+/", $csv) as $line) {
if (!empty($prev_line)) {
@ -438,6 +438,11 @@ class rcube_csv2vcard
// Parse header
if (empty($this->map)) {
$this->parse_header($elements);
if ($dry_run) {
return array('source' => $elements, 'destination' => $this->map);
}
if (empty($this->map)) {
break;
}
@ -470,6 +475,47 @@ class rcube_csv2vcard
}
}
/**
* Set field mapping info
*
* @param array Field mapping
*/
public function set_map($elements)
{
// sanitize input
$elements = array_filter($elements, function($val) {
return in_array($val, $this->csv2vcard_map);
});
$this->map = $elements;
}
/**
* Set field mapping info
*
* @return array Array of vcard fields and localized names
*/
public function get_fields()
{
// get all vcard fields
$fields = array_unique($this->csv2vcard_map);
$local_field_names = $this->local_label_map ?: $this->label_map;
$local_field_names = array_flip($local_field_names);
// translate with the local map
$map = array();
foreach ($fields as $csv => $vcard) {
$map[$vcard] = $local_field_names[$csv];
}
// small fix to prevent "Groups" displaying as "Categories"
$map['groups'] = $local_field_names['groups'];
asort($map);
return $map;
}
/**
* Export vCards
*

@ -488,6 +488,8 @@ $labels['importgroups'] = 'Import group assignments';
$labels['importgroupsall'] = 'All (create groups if necessary)';
$labels['importgroupsexisting'] = 'Only for existing groups';
$labels['importdesc'] = 'You can upload contacts from an existing address book.<br/>We currently support importing addresses from the <a href="https://en.wikipedia.org/wiki/VCard">vCard</a> or CSV (comma-separated) data format.';
$labels['importmapdesc'] = 'Confirm the field mapping information below is correct before proceeding with CSV (comma-separated) data import.';
$labels['fieldnotmapped'] = 'Field not mapped (do not import)';
$labels['done'] = 'Done';
// settings
@ -655,6 +657,7 @@ $labels['installedplugins'] = 'Installed plugins';
$labels['plugin'] = 'Plugin';
$labels['version'] = 'Version';
$labels['source'] = 'Source';
$labels['destination'] = 'Destination';
$labels['license'] = 'License';
$labels['support'] = 'Get support';
$labels['savedsearches'] = 'Saved searches';

@ -125,6 +125,7 @@ $messages['converting'] = 'Removing formatting...';
$messages['messageopenerror'] = 'Could not load message from server.';
$messages['filelinkerror'] = 'Attaching the file failed.';
$messages['fileuploaderror'] = 'File upload failed.';
$messages['csvfilemismatch'] = 'Upload of multiple CSV files with different fields is not supported.';
$messages['filesizeerror'] = 'The uploaded file exceeds the maximum size of $size.';
$messages['filecounterror'] = 'You can upload maximum $count files at once.';
$messages['msgsizeerror'] = 'Failed to attach a file. Maximum size of a message ($size) exceeded.';

@ -20,14 +20,23 @@
/** The import process **/
const UPLOAD_ERR_CSV_FIELDS = 101;
$importstep = 'rcmail_import_form';
if (is_array($_FILES['_file'])) {
if (is_array($_FILES['_file']) || is_array($_POST['_map'])) {
$replace = (bool)rcube_utils::get_input_value('_replace', rcube_utils::INPUT_GPC);
$target = rcube_utils::get_input_value('_target', rcube_utils::INPUT_GPC);
$with_groups = intval(rcube_utils::get_input_value('_groups', rcube_utils::INPUT_GPC));
// reload params for CSV field mapping screen
if (is_array($_POST['_map']) && $params = $_SESSION['contactcsvimport']['params']) {
list('replace' => $replace, 'target' => $target, 'with_groups' => $with_groups) = $params;
}
$vcards = array();
$csvs = array();
$map = array();
$upload_error = null;
$CONTACTS = $RCMAIL->get_address_book($target, true);
@ -40,54 +49,101 @@ if (is_array($_FILES['_file'])) {
$OUTPUT->show_message('addresswriterror', 'error');
}
else {
foreach ((array)$_FILES['_file']['tmp_name'] as $i => $filepath) {
// Process uploaded file if there is no error
$err = $_FILES['_file']['error'][$i];
$filepaths = array();
if (is_array($_POST['_map'])) {
$filepaths = $_SESSION['contactcsvimport']['files'];
}
else {
foreach ((array)$_FILES['_file']['tmp_name'] as $i => $filepath) {
// Process uploaded file if there is no error
$err = $_FILES['_file']['error'][$i];
if ($err) {
$upload_error = $err;
if ($err) {
$upload_error = $err;
}
else {
$filepaths[] = $filepath;
}
}
else {
$file_content = file_get_contents($filepath);
}
// let rcube_vcard do the hard work :-)
$vcard_o = new rcube_vcard();
$vcard_o->extend_fieldmap($CONTACTS->vcard_map);
$v_list = $vcard_o->import($file_content);
foreach ($filepaths as $filepath) {
$file_content = file_get_contents($filepath);
if (!empty($v_list)) {
$vcards = array_merge($vcards, $v_list);
continue;
}
// let rcube_vcard do the hard work :-)
$vcard_o = new rcube_vcard();
$vcard_o->extend_fieldmap($CONTACTS->vcard_map);
$v_list = $vcard_o->import($file_content);
if (!empty($v_list)) {
$vcards = array_merge($vcards, $v_list);
continue;
}
// no vCards found, try CSV
$csv = new rcube_csv2vcard($_SESSION['language']);
// no vCards found, try CSV
$csv = new rcube_csv2vcard($_SESSION['language']);
if (is_array($_POST['_map'])) {
$map = rcube_utils::get_input_value('_map', rcube_utils::INPUT_GPC);
$map = array_filter($map);
$csv->set_map($map);
$csv->import($file_content);
$v_list = $csv->export();
if (!empty($v_list)) {
$vcards = array_merge($vcards, $v_list);
unlink($filepath);
}
else {
// save uploaded file for the real import in the next step
$temp_csv = rcube_utils::temp_filename('csvimpt');
if (move_uploaded_file($filepath, $temp_csv) && file_exists($temp_csv)) {
$fields = $csv->get_fields();
$last_map = $map;
$map = $csv->import($file_content, true);
// when multiple CSV files are uploaded check they all have the same structure
if ($last_map && $last_map !== $map) {
$csvs = array();
$upload_error = UPLOAD_ERR_CSV_FIELDS;
break;
}
$csvs[] = $temp_csv;
}
else {
$upload_error = UPLOAD_ERR_CANT_WRITE;
}
continue;
}
$v_list = $csv->export();
if (!empty($v_list)) {
$vcards = array_merge($vcards, $v_list);
}
}
}
// no vcards detected
if (!count($vcards)) {
if ($upload_error == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$size = $RCMAIL->show_bytes(rcube_utils::max_upload_size());
$OUTPUT->show_message('filesizeerror', 'error', array('size' => $size));
}
else if ($upload_error) {
$OUTPUT->show_message('fileuploaderror', 'error');
}
else {
$OUTPUT->show_message('importformaterror', 'error');
}
if (count($csvs) > 0) {
// csv import, show field mapping options
$importstep = 'rcmail_import_map';
$_SESSION['contactcsvimport']['files'] = $csvs;
$_SESSION['contactcsvimport']['params'] = array(
'replace' => $replace,
'target' => $target,
'with_groups' => $with_groups,
'fields' => $fields
);
// Stored separately due to nested array limitations in session
$_SESSION['contactcsvimport']['map'] = $map;
// Re-enable the import button
$OUTPUT->command('parent.import_state_set', 'error');
}
else {
elseif (count($vcards) > 0) {
// import vcards
$IMPORT_STATS = new stdClass;
$IMPORT_STATS->names = array();
$IMPORT_STATS->skipped_names = array();
@ -172,9 +228,28 @@ if (is_array($_FILES['_file'])) {
}
$importstep = 'rcmail_import_confirm';
$_SESSION['contactcsvimport'] = null;
$OUTPUT->command('parent.import_state_set', $IMPORT_STATS->inserted ? 'reload' : 'ok');
}
else {
// no data to import
if ($upload_error == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$size = $RCMAIL->show_bytes(rcube_utils::max_upload_size());
$OUTPUT->show_message('filesizeerror', 'error', array('size' => $size));
}
else if ($upload_error == UPLOAD_ERR_CSV_FIELDS) {
$OUTPUT->show_message('csvfilemismatch', 'error');
}
else if ($upload_error) {
$OUTPUT->show_message('fileuploaderror', 'error');
}
else {
$OUTPUT->show_message('importformaterror', 'error');
}
$OUTPUT->command('parent.import_state_set', 'error');
}
}
@ -270,6 +345,56 @@ function rcmail_import_form($attrib)
'enctype' => 'multipart/form-data') + $attrib,
$form);
// remove any info left over info from previous import attempts
$_SESSION['contactcsvimport'] = null;
return $out;
}
/**
* Render the field mapping page for the CSV import process
*/
function rcmail_import_map($attrib)
{
global $RCMAIL, $OUTPUT;
$params = $_SESSION['contactcsvimport']['params'];
// hide groups field from list when group import disabled
if ($params['with_groups'] == 0) {
unset($params['fields']['groups']);
}
$fieldlist = new html_select(array('name' => '_map[]'));
$fieldlist->add($RCMAIL->gettext('fieldnotmapped'), '');
foreach ($params['fields'] as $id => $name) {
$fieldlist->add($name, $id);
}
$field_table = new html_table(array('cols' => 2) + $attrib);
$field_table->add_header(null, $RCMAIL->gettext('source'));
$field_table->add_header(null, $RCMAIL->gettext('destination'));
$map = $_SESSION['contactcsvimport']['map'];
foreach ($map['source'] as $i => $name) {
$field_table->add('title', html::label('rcmimportmap' . $i, rcube::Q($name)));
$field_table->add(null, $fieldlist->show(array_key_exists($i, $map['destination']) ? $map['destination'][$i] : '', array('id' => 'rcmimportmap' . $i)));
}
$form = '';
$form .= html::tag('input', array('type' => 'hidden', 'name' => '_unlock', 'value' => ''));
$form .= $field_table->show();
$attrib += array('id' => "rcmImportFormMap");
$OUTPUT->add_gui_object('importformmap', $attrib['id']);
$out = html::p(null, rcube::Q($RCMAIL->gettext('importmapdesc'), 'show'))
. $OUTPUT->form_tag(array(
'action' => $RCMAIL->url('import'),
'method' => 'post') + $attrib,
$form);
return $out;
}

@ -934,6 +934,22 @@ function rcube_elastic_ui()
});
});
// Contact import CSV mapping table
$('#rcmImportFormMap table.propform', context).each(function() {
var col_sizes = ['sm', 4, 8];
var first, last, row = $(this).find('> thead > tr'),
row_classes = ['form-group', 'row', 'd-none', 'd-sm-flex'],
cells = row.children('th');
first = cells.first();
last = cells.last();
first.addClass('col-' + col_sizes[0] + '-' + col_sizes[1]);
last.addClass('col-' + col_sizes[0] + '-' + col_sizes[2]);
row.addClass(row_classes.join(' '));
});
// Advanced options form
$('fieldset.advanced', context).each(function() {
var table = $(this).children('.propform').first();

Loading…
Cancel
Save