From 75f5b605ac2417cef6d132c1fd2a7d5dae418c52 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Mon, 5 Mar 2018 21:56:03 +0100 Subject: [PATCH] Allow multiple imported fields, replace existing --- app/Http/Controllers/SettingsController.php | 2 +- app/Jobs/AddContactFromVCard.php | 113 ++++++++++-------- resources/lang/en/settings.php | 4 + .../views/settings/imports/index.blade.php | 8 +- .../views/settings/imports/upload.blade.php | 10 +- 5 files changed, 84 insertions(+), 53 deletions(-) diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index ac3a12b7a7b..22ea3853844 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -217,7 +217,7 @@ public function storeImport(ImportsRequest $request) 'filename' => $filename, ]); - dispatch(new AddContactFromVCard($importJob)); + dispatch(new AddContactFromVCard($importJob, $request->get('behaviour'))); return redirect()->route('settings.import'); } diff --git a/app/Jobs/AddContactFromVCard.php b/app/Jobs/AddContactFromVCard.php index 8b831449cfa..97cdbe455a8 100644 --- a/app/Jobs/AddContactFromVCard.php +++ b/app/Jobs/AddContactFromVCard.php @@ -30,7 +30,11 @@ class AddContactFromVCard implements ShouldQueue const ERROR_CONTACT_EXIST = 'import_vcard_contact_exist'; const ERROR_CONTACT_DOESNT_HAVE_FIRSTNAME = 'import_vcard_contact_no_firstname'; + const BEHAVIOUR_ADD = 'behaviour_add'; + const BEHAVIOUR_REPLACE = 'behaviour_replace'; + protected $importJob; + protected $behaviour; protected $importedContacts = 0; protected $skippedContacts = 0; protected $gender; @@ -40,9 +44,10 @@ class AddContactFromVCard implements ShouldQueue * * @return void */ - public function __construct(ImportJob $importJob) + public function __construct(ImportJob $importJob, $behaviour = self::BEHAVIOUR_ADD) { $this->importJob = $importJob; + $this->behaviour = $behaviour; } /** @@ -68,23 +73,27 @@ public function handle() collect($matches[0])->map(function ($vcard) { return Reader::read($vcard); })->each(function (VCard $vcard) { - if ($this->contactExists($vcard, $this->importJob->account_id)) { + + // Skip contact if there isn't a first name or a nickname + if (! $this->contactHasName($vcard)) { $this->skippedContacts++; - $this->fileImportJobReport($vcard, self::VCARD_SKIPPED, self::ERROR_CONTACT_EXIST); + $this->fileImportJobReport($vcard, self::VCARD_SKIPPED, self::ERROR_CONTACT_DOESNT_HAVE_FIRSTNAME); return; } - // Skip contact if there isn't a first name or a nickname - if (! $this->contactHasName($vcard)) { + $contact = $this->existingContact($vcard, $this->importJob->account_id); + if ($contact && $this->behaviour === self::BEHAVIOUR_ADD) { $this->skippedContacts++; - $this->fileImportJobReport($vcard, self::VCARD_SKIPPED, self::ERROR_CONTACT_DOESNT_HAVE_FIRSTNAME); + $this->fileImportJobReport($vcard, self::VCARD_SKIPPED, self::ERROR_CONTACT_EXIST); return; } - $contact = new Contact(); - $contact->account_id = $this->importJob->account_id; + if (! $contact) { + $contact = new Contact(); + $contact->account_id = $this->importJob->account_id; + } if ($vcard->N && ! empty($vcard->N->getParts()[1])) { $contact->first_name = $this->formatValue($vcard->N->getParts()[1]); @@ -110,54 +119,60 @@ public function handle() } if ($vcard->ADR) { - $address = new Address(); - $address->street = $this->formatValue($vcard->ADR->getParts()[2]); - $address->city = $this->formatValue($vcard->ADR->getParts()[3]); - $address->province = $this->formatValue($vcard->ADR->getParts()[4]); - $address->postal_code = $this->formatValue($vcard->ADR->getParts()[5]); - - $country = Country::where('country', $vcard->ADR->getParts()[6]) - ->orWhere('iso', strtolower($vcard->ADR->getParts()[6])) - ->first(); - - if ($country) { - $address->country_id = $country->id; + foreach ($vcard->ADR as $adr) { + + $country = Country::where('country', $adr->getParts()[6]) + ->orWhere('iso', strtolower($adr->getParts()[6])) + ->first(); + + Address::firstOrCreate([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + 'street' => $this->formatValue($adr->getParts()[2]), + 'city' => $this->formatValue($adr->getParts()[3]), + 'province' => $this->formatValue($adr->getParts()[4]), + 'postal_code' => $this->formatValue($adr->getParts()[5]), + 'country_id' => $country ? $country->id : null, + ]); } - $address->contact_id = $contact->id; - $address->account_id = $contact->account_id; - $address->save(); } - if (! is_null($this->formatValue($vcard->EMAIL))) { + if ($vcard->EMAIL) { // Saves the email $contactFieldType = ContactFieldType::where('type', 'email') ->where('account_id', $contact->account_id) ->first(); - if (! empty($contactFieldType)) { - $contactField = new ContactField; - $contactField->account_id = $contact->account_id; - $contactField->contact_id = $contact->id; - $contactField->data = $this->formatValue($vcard->EMAIL); - $contactField->contact_field_type_id = $contactFieldType->id; - $contactField->save(); + if ($contactFieldType) { + foreach ($vcard->EMAIL as $data) { + ContactField::firstOrCreate([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + 'data' => $this->formatValue($data), + 'contact_field_type_id' => $contactFieldType->id, + ]); + } } + } - if (! is_null($this->formatValue($vcard->TEL))) { + if ($vcard->TEL) { // Saves the phone number $contactFieldType = ContactFieldType::where('type', 'phone') ->where('account_id', $contact->account_id) ->first(); if (! empty($contactFieldType)) { - $contactField = new ContactField; - $contactField->account_id = $contact->account_id; - $contactField->contact_id = $contact->id; - $contactField->data = $this->formatValue($vcard->TEL); - $contactField->contact_field_type_id = $contactFieldType->id; - $contactField->save(); + foreach ($vcard->TEL as $data) { + ContactField::firstOrCreate([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + 'data' => $this->formatValue($data), + 'contact_field_type_id' => $contactFieldType->id, + ]); + } + } } @@ -175,10 +190,15 @@ public function handle() $this->importJob->save(); } catch (\Exception $e) { $this->importJob->contacts_found = $numberOfContactsInTheFile; + $this->importJob->contacts_skipped = $this->skippedContacts; + $this->importJob->contacts_imported = $this->importedContacts; $this->importJob->failed = 1; $this->importJob->failed_reason = $e->getMessage(); + $this->importJob->ended_at = \Carbon\Carbon::now(); $this->importJob->save(); + logger($e); + Storage::disk('public')->delete($this->importJob->filename); } @@ -214,28 +234,23 @@ private function formatValue($value) * * @param VCard $vcard * @param User $user - * @return bool + * @return Contact|null */ - private function contactExists(VCard $vcard, $account_id) + private function existingContact(VCard $vcard, $account_id) { - $email = (string) $vcard->EMAIL; - $contactFieldType = ContactFieldType::where([ ['account_id', $account_id], ['type', 'email'], ])->first(); - $contactField = null; - - if ($contactFieldType) { + if ($vcard->EMAIL && $contactFieldType) { $contactField = ContactField::where([ ['account_id', $account_id], - ['data', $email], ['contact_field_type_id', $contactFieldType->id], - ])->first(); - } + ])->whereIn('data', iterator_to_array($vcard->EMAIL))->first(); - return $email && $contactField; + return $contactField->contact; + } } private function fileImportJobReport(VCard $vcard, $status, $reason = null) diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index 2eea61ee1b5..4ed966c86c6 100644 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -139,6 +139,10 @@ 'import_upload_rule_time' => 'It might take up to 1 minute to upload the contacts and process them. Be patient.', 'import_upload_rule_cant_revert' => 'Make sure data is accurate before uploading, as you can\'t undo the upload.', 'import_upload_form_file' => 'Your .vcf or .vCard file:', + 'import_upload_behaviour' => 'Import behaviour:', + 'import_upload_behaviour_add' => 'Add new contacts, skip existing', + 'import_upload_behaviour_replace' => 'Replace existing contacts', + 'import_upload_behaviour_help' => 'Note: Replacing will replace all data found in the vCard, but will keep existing contact fields', 'import_report_title' => 'Importing report', 'import_report_date' => 'Date of the import', 'import_report_type' => 'Type of import', diff --git a/resources/views/settings/imports/index.blade.php b/resources/views/settings/imports/index.blade.php index b5fa68c46fe..d9c2c66b9ed 100644 --- a/resources/views/settings/imports/index.blade.php +++ b/resources/views/settings/imports/index.blade.php @@ -44,7 +44,9 @@
  • @if (! is_null($importJob->ended_at)) - @if ($importJob->contacts_found != $importJob->contacts_imported) + @if ($importJob->failed) + + @elseif ($importJob->contacts_found != $importJob->contacts_imported) @else @@ -55,7 +57,9 @@ {{ \App\Helpers\DateHelper::getShortDateWithTime($importJob->created_at) }}
    - @if (! is_null($importJob->ended_at)) + @if($importJob->failed_reason) + {{ $importJob->failed_reason }} + @elseif (! is_null($importJob->ended_at)) {{ trans('settings.import_result_stat', ['total_contacts' => $importJob->contacts_found, 'total_imported' => $importJob->contacts_imported, 'total_skipped' => $importJob->contacts_skipped]) }} @endif
    diff --git a/resources/views/settings/imports/upload.blade.php b/resources/views/settings/imports/upload.blade.php index 1be71ae5c09..f15910d2632 100644 --- a/resources/views/settings/imports/upload.blade.php +++ b/resources/views/settings/imports/upload.blade.php @@ -42,7 +42,6 @@
  • {!! trans('settings.import_upload_rule_format') !!}
  • {{ trans('settings.import_upload_rule_vcard') }}
  • {!! trans('settings.import_upload_rule_instructions') !!}
  • -
  • {{ trans('settings.import_upload_rule_multiple') }}
  • {{ trans('settings.import_upload_rule_limit') }}
  • {{ trans('settings.import_upload_rule_time') }}
  • {{ trans('settings.import_upload_rule_cant_revert') }}
  • @@ -60,6 +59,15 @@ {{ trans('people.information_edit_max_size', ['size' => 10]) }} +
    + + + {{ trans('settings.import_upload_behaviour_help') }} +
    +
    {{ trans('app.cancel') }}