validate($this->request->files(), [ 'file' => 'mimetypes:' . implode(',', fluentcrmCsvMimes()) ], [ 'file.mimetypes' => __('The file must be a valid CSV.', 'fluent-crm') ]); $delimeter = $request->get('delimiter', 'comma'); if ($delimeter == 'comma') { $delimeter = ','; } else { $delimeter = ';'; } $uploadedFiles = FileSystem::put($files); try { $csv = $this->getCsvReader(FileSystem::get($uploadedFiles[0]['file'])); $csv->setDelimiter($delimeter); $headers = $csv->fetchOne(); } catch (\Exception $exception) { return $this->sendError([ 'message' => $exception->getMessage() ]); } if (count($headers) != count(array_unique($headers))) { return $this->sendError([ 'message' => __('Looks like your csv has same name header multiple times. Please fix your csv first and remove any duplicate header column', 'fluent-crm') ]); } if ($request->get('type') == 'company') { $mappables = Company::mappables(); } else { $mappables = Subscriber::mappables(); } $headerItems = array_values(array_filter($headers)); $subscriberColumns = array_keys($mappables); $maps = []; $customFields = fluentcrm_get_custom_contact_fields(); $fieldsMap = []; if ($customFields) { foreach ($customFields as $field) { $fieldsMap[$field['slug']] = $field['label']; } } foreach ($headerItems as $headerItem) { $tableMap = (in_array($headerItem, $subscriberColumns)) ? $headerItem : null; if (!$tableMap) { $santizedItem = str_replace(' ', '_', strtolower($headerItem)); if (in_array($santizedItem, $subscriberColumns)) { $tableMap = $santizedItem; } } if (!empty($fieldsMap) && in_array($headerItem, $fieldsMap)) { $tableMap = array_search($headerItem, $fieldsMap); } $maps[] = [ 'csv' => $headerItem, 'table' => $tableMap ]; } if ($request->get('type') == 'company') { /** * Determine the columns of the company table in FluentCRM. * * This filter allows you to modify the columns of the company table in the CSV export. * * @since 2.8.0 * * @param array $subscriberColumns An array of default subscriber columns. */ $columns = apply_filters( 'fluent_crm/company_table_columns', $subscriberColumns ); } else { /** * Determine the columns of the subscriber table in FluentCRM. * * This filter allows you to modify the columns displayed in the subscriber table. * * @since 2.8.0 * * @param array $subscriberColumns An array of default subscriber table columns. */ $columns = apply_filters( 'fluent_crm/subscriber_table_columns', $subscriberColumns ); } return $this->send([ 'file' => $uploadedFiles[0]['file'], 'headers' => $headerItems, 'fields' => $mappables, 'columns' => $columns, 'map' => $maps ]); } public function import() { $inputs = $this->request->only([ 'map', 'tags', 'lists', 'file', 'update', 'new_status', 'double_optin_email', 'import_silently', 'force_update_status' ]); if (Arr::get($inputs, 'import_silently') == 'yes') { if (!defined('FLUENTCRM_DISABLE_TAG_LIST_EVENTS')) { define('FLUENTCRM_DISABLE_TAG_LIST_EVENTS', true); } } $forceStatusChange = Arr::get($inputs, 'force_update_status') == 'yes'; $delimeter = $this->request->get('delimiter', 'comma'); if ($delimeter == 'comma') { $delimeter = ','; } else { $delimeter = ';'; } $status = $inputs['new_status']; try { $reader = $this->getCsvReader(FileSystem::get($inputs['file'])); $reader->setDelimiter($delimeter); if (method_exists($reader, 'getRecords')) { $aHeaders = $reader->fetchOne(0); $allRecords = $reader->getRecords($aHeaders); if (!is_array($allRecords)) { $allRecords = iterator_to_array($allRecords, true); } unset($allRecords[0]); $allRecords = array_values($allRecords); } else { $aHeaders = $reader->fetchOne(0); $allRecords = $reader->fetchAssoc($aHeaders); if (!is_array($allRecords)) { $allRecords = iterator_to_array($allRecords, true); } unset($allRecords[0]); $allRecords = array_values($allRecords); } } catch (\Exception $exception) { return $this->sendError([ 'message' => $exception->getMessage() ]); } $page = $this->request->get('importing_page', 1); $processPerRequest = apply_filters('fluent_crm/csv_import_contact_limit_per_request', 100); $offset = ($page - 1) * $processPerRequest; $records = array_slice($allRecords, $offset, $processPerRequest); $customFieldKeys = $this->customFieldKeys(); $subscribers = []; $skipped = []; $isCompanyEnabled = Helper::isCompanyEnabled(); foreach ($records as $record) { if (!array_filter($record)) { continue; } $subscriber = [ 'custom_values' => [] ]; foreach ($inputs['map'] as $map) { if (!$map['table']) { continue; } if (isset($map['csv'], $map['table'])) { if (in_array($map['table'], ['tags', 'lists'])) { //if tags or lists are mapped to be imported if ($map['table'] == 'tags') { $subscriber['tags'] = !empty($record[$map['csv']]) ? explode(',', $record[$map['csv']]) : []; } else { $subscriber['lists'] = !empty($record[$map['csv']]) ? explode(',', $record[$map['csv']]) : []; } } else if (in_array($map['table'], $customFieldKeys)) { $subscriber['custom_values'][$map['table']] = $record[$map['csv']]; } else { $subscriber[$map['table']] = $record[$map['csv']]; } } } if (!array_key_exists('email', $subscriber)) { return $this->sendError(['email' => __('The email field is required.', 'fluent-crm')], 422); } $subscriber['email'] = is_string($subscriber['email']) ? trim($subscriber['email']) : $subscriber['email']; if ($subscriber['email'] && is_email($subscriber['email'])) { if (isset($subscriber['company_id']) && $subscriber['company_id'] && $isCompanyEnabled) { $companyNameOrId = $subscriber['company_id']; if (is_string($companyNameOrId)) { $company = Company::query()->firstOrCreate([ 'name' => $subscriber['company_id'] ], [ 'name' => $subscriber['company_id'] ]); if ($company) { $subscriber['company_id'] = $company->id; } else { unset($subscriber['company_id']); } } else { $company = Company::find($subscriber['company_id']); if (!$company) { unset($subscriber['company_id']); } } } $subscribers[] = Sanitize::contact($subscriber); } else { $skipped[] = $subscriber; } } if (!isset($inputs['tags'])) { $inputs['tags'] = []; } if (!isset($inputs['lists'])) { $inputs['lists'] = []; } $sendDoubleOptin = Arr::get($inputs, 'double_optin_email') == 'yes'; $result = Subscriber::import( $subscribers, $inputs['tags'], $inputs['lists'], $inputs['update'], $status, $sendDoubleOptin, $forceStatusChange, 'csv' ); $totalSkipped = count($result['skips']) + count($skipped); $completed = $offset + count($records); $totalCount = count($allRecords); $hasMore = $completed < $totalCount; if (!$hasMore) { FileSystem::delete($inputs['file']); } return $this->sendSuccess([ 'total' => $totalCount, 'completed' => $completed, 'total_page' => ceil($totalCount / $processPerRequest), 'skipped' => $totalSkipped, 'invalid_contacts' => $skipped, 'skipped_contacts' => $result['skips'], 'invalid_email_counts' => count($skipped), 'inserted' => count($result['inserted']), 'updated' => count($result['updated']), 'has_more' => $hasMore, 'last_page' => $page, 'tags' => $inputs['tags'], 'lists' => $inputs['lists'], 'offset' => $offset, 'result' => $result ]); } public function importCompanies() { $inputs = $this->request->only([ 'map', 'file', 'update', 'create_owner' ]); $delimeter = $this->request->get('delimiter', 'comma'); if ($delimeter == 'comma') { $delimeter = ','; } else { $delimeter = ';'; } try { $reader = $this->getCsvReader(FileSystem::get($inputs['file'])); $reader->setDelimiter($delimeter); if (method_exists($reader, 'getRecords')) { $aHeaders = $reader->fetchOne(0); $allRecords = $reader->getRecords($aHeaders); if (!is_array($allRecords)) { $allRecords = iterator_to_array($allRecords, true); } unset($allRecords[0]); $allRecords = array_values($allRecords); } else { $aHeaders = $reader->fetchOne(0); $allRecords = $reader->fetchAssoc($aHeaders); if (!is_array($allRecords)) { $allRecords = iterator_to_array($allRecords, true); } unset($allRecords[0]); $allRecords = array_values($allRecords); } } catch (\Exception $exception) { return $this->sendError([ 'message' => $exception->getMessage() ]); } $page = $this->request->get('importing_page', 1); $processPerRequest = 100; $offset = ($page - 1) * $processPerRequest; $records = array_slice($allRecords, $offset, $processPerRequest); $willCreateOwner = $this->request->get('create_owner') == 'yes'; $willUpdate = $this->request->get('update') == 'yes'; $customFields = fluentcrm_get_custom_company_fields(); $companies = []; $skipped = []; foreach ($records as $record) { if (!array_filter($record)) { continue; } $company = []; foreach ($inputs['map'] as $map) { if (!$map['table']) { continue; } if (isset($map['csv'], $map['table'])) { $company[$map['table']] = trim($record[$map['csv']]); } } if (empty($company['name'])) { return $this->sendError(['email' => __('The company name field is required.', 'fluent-crm')], 422); } if (!$willUpdate) { // check if exists if (Company::where('name', $company['name'])->first()) { $skipped[] = $company; continue; } } if ($customFields) { $customValues = []; foreach ($company as $dataKey => $dataValue) { if (strpos($dataKey, '_custom_') === 0) { $customKey = str_replace('_custom_', '', $dataKey); $customValues[$customKey] = $dataValue; unset($company[$dataKey]); } } $company['custom_values'] = $customValues; } $company = Sanitize::company($company); if (!empty($company['owner_email']) && is_email($company['owner_email'])) { $ownerEmail = sanitize_email($company['owner_email']); } else { $ownerEmail = null; } if ($ownerEmail) { $owner = FluentCrmApi('contacts')->getContact($ownerEmail); if ($owner) { $company['owner_id'] = $owner->id; } else if ($willCreateOwner) { $owner = FluentCrmApi('contacts')->createOrUpdate([ 'full_name' => sanitize_text_field(Arr::get($company, 'owner_name')), 'email' => $ownerEmail, 'status' => 'subscribed' ]); if ($owner) { $company['owner_id'] = $owner->id; } } } $createdCompany = FluentCrmApi('companies')->createOrUpdate($company); $companies[] = $createdCompany; } $completed = $offset + count($companies); $totalCount = count($allRecords); $hasMore = $completed < $totalCount; if (!$hasMore) { FileSystem::delete($inputs['file']); } return $this->sendSuccess([ 'total' => $totalCount, 'completed' => count($companies), 'total_page' => ceil($totalCount / $processPerRequest), 'skipped' => count($skipped), 'has_more' => $hasMore, 'last_page' => $page, 'offset' => $offset ]); } protected function customFieldKeys() { $fields = fluentcrm_get_option('contact_custom_fields', []); $keys = []; foreach ($fields as $field) { $keys[] = $field['slug']; } return $keys; } private function getCsvReader($file) { if (!class_exists(' \League\Csv\Reader')) { include FLUENTCRM_PLUGIN_PATH . 'app/Services/Libs/csv/autoload.php'; } return \League\Csv\Reader::createFromString($file); } }