. * */ namespace OCA\Contacts\Connector; use Sabre\VObject\Component; use \SplFileObject as SplFileObject; use Sabre\VObject\StringUtil; /** * @brief Implementation of the Import class for CSV format * Doesn't really like csv that has fields with new lines in it, beware ! */ class ImportCsvConnector extends ImportConnector { /** * @brief separates elements from the input stream according to the entry_separator value in config * ignoring the first line if mentionned in the config * @param $file the input file to import * @param $limit the number of elements to return (-1 = no limit) * @return array(array(data), array(titles)) */ public function getElementsFromInput($file, $limit=-1) { $linesAndTitles = $this->getSourceElementsFromFile($file, $limit); $lines = $linesAndTitles[0]; $titles = $linesAndTitles[1]; $elements = array(); foreach ($lines as $line) { $elements[] = $this->convertElementToVCard($line, $titles); } return array_values($elements); } /** * @brief parses the file in csv format * @param $file the input file to import * @param $limit the number of elements to return (-1 = no limit) * @return array(array(data), array(titles)) */ private function getSourceElementsFromFile($file, $limit=-1) { if (file_put_contents($file, StringUtil::convertToUTF8(file_get_contents($file)))) { $csv = new SplFileObject($file, 'r'); $csv->setFlags(SplFileObject::READ_CSV); $delimiter = ''; if (isset($this->configContent->import_core->delimiter)) { $delimiter = (string)$this->configContent->import_core->delimiter; } else { // Look for the delimiter in the first line, should be the most present character between ',', ';' and '\t' $firstLine = (new SplFileObject($file))->fgets(); $nbComma = substr_count($firstLine, ','); $nbSemicolon = substr_count($firstLine, ';'); $nbTab = substr_count($firstLine, "\t"); if ($nbComma > $nbSemicolon && $nbComma > $nbTab) { // Comma it is $delimiter = ','; } else if ($nbSemicolon > $nbComma && $nbSemicolon > $nbTab) { // Semicolon it is $delimiter = ';'; } else if ($nbTab > $nbComma && $nbTab > $nbSemicolon) { // Tab it is $delimiter = "\t"; } else if ($nbTab == 0 && $nbComma == 0 && $nbSemicolon == 0) { // We have a problem, no delimiter found return array(array(), array()); } } $csv->setCsvControl($delimiter, "\"", "\\"); $ignore_first_line = (isset($this->configContent->import_core->ignore_first_line) && (((string)$this->configContent->import_core->ignore_first_line) == 'true') || ((string)$this->configContent->import_core->ignore_first_line) == '1'); $titles = false; $lines = array(); $index = 0; foreach($csv as $line) { if (!($ignore_first_line && $index == 0) && count($line) > 1) { // Ignore first line $lines[] = $line; if (count($lines) == $limit) { break; } } else if ($ignore_first_line && $index == 0) { $titles = $line; } $index++; } return array($lines, $titles); } else { error_log("Error converting file to utf8"); return array(array(), array()); } } /** * @brief converts a unique element into a owncloud VCard * @param $element the element to convert * @return VCard, all unconverted elements are stored in X-Unknown-Element parameters */ public function convertElementToVCard($element, $title = null) { $vcard = \Sabre\VObject\Component::create('VCARD'); for ($i=0; $i < count($element); $i++) { if ($element[$i] != '') { //error_log("element at $i, value:'".$element[$i]."'"); // Look for the right import_entry if (isset($this->configContent->import_core->base_parsing)) { if (strcasecmp((string)$this->configContent->import_core->base_parsing, 'position') == 0) { $importEntry = $this->getImportEntryFromPosition((String)$i); } else if (strcasecmp((string)$this->configContent->import_core->base_parsing, 'name') == 0 && isset($title[$i])) { $importEntry = $this->getImportEntryFromName($title[$i]); } } if ($importEntry) { // Create a new property and attach it to the vcard $value = $element[$i]; if (isset($importEntry['remove'])) { $value = str_replace($importEntry['remove'], '', $element[$i]); } $values = array($value); if (isset($importEntry['separator'])) { $values = explode($importEntry['separator'], $value); } foreach ($values as $oneValue) { if (isset($importEntry->vcard_favourites)) { foreach ($importEntry->vcard_favourites as $vcardFavourite) { if (strcasecmp((string)$vcardFavourite, trim($oneValue)) == 0) { $property = \Sabre\VObject\Property::create("X-FAVOURITES", 'yes'); $vcard->add($property); } else { $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else { $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else if (isset($element[$i]) && isset($title[$i])) { $property = \Sabre\VObject\Property::create("X-Unknown-Element", StringUtil::convertToUTF8($element[$i])); $property->parameters[] = new \Sabre\VObject\Parameter('TYPE', ''.StringUtil::convertToUTF8($title[$i])); $vcard->add($property); } } } $vcard->validate(\Sabre\VObject\Component\VCard::REPAIR); return $vcard; } /** * @brief gets the import entry corresponding to the position given in parameter * @param $position the position to look for in the connector * @return int|false */ private function getImportEntryFromPosition($position) { for ($i=0; $i < $this->configContent->import_entry->count(); $i++) { if ($this->configContent->import_entry[$i]['position'] == $position && $this->configContent->import_entry[$i]['enabled'] == 'true') { return $this->configContent->import_entry[$i]; } } return false; } /** * @brief gets the import entry corresponding to the name given in parameter * @param $name the parameter name to look for in the connector * @return string|false */ private function getImportEntryFromName($name) { for ($i=0; $i < $this->configContent->import_entry->count(); $i++) { if ($this->configContent->import_entry[$i]['name'] == StringUtil::convertToUTF8($name) && $this->configContent->import_entry[$i]['enabled'] == 'true') { return $this->configContent->import_entry[$i]; } if (isset($this->configContent->import_entry[$i]->altname)) { foreach ($this->configContent->import_entry[$i]->altname as $altname) { if ($altname == StringUtil::convertToUTF8($name) && $this->configContent->import_entry[$i]['enabled'] == 'true') { return $this->configContent->import_entry[$i]; } } } } return false; } /** * @brief returns the probability that the first element is a match for this format * @param $file the file to examine * @return 0 if not a valid csv file * 1 - 0.5*(number of untranslated elements/total number of elements) * The more the first element has untranslated elements, the more the result is close to 0.5 */ public function getFormatMatch($file) { // Examining the first element only $partsAndTitle = $this->getSourceElementsFromFile($file, 1); $parts = $partsAndTitle[0]; $titles = $partsAndTitle[1]; if (!$parts || ($parts && isset($this->configContent->import_core->expected_columns) && count($parts[0]) != (string)$this->configContent->import_core->expected_columns) ) { // Doesn't look like a csv file return 0; } else { $element = $this->convertElementToVCard($parts[0], $titles); $unknownElements = $element->select("X-Unknown-Element"); return (1 - (0.5 * count($unknownElements)/count($parts[0]))); } } } ?>