File.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. <?php
  2. namespace Dotdigitalgroup\Email\Helper;
  3. /**
  4. * Creates the csv files in export folder and move to archive when it's complete.
  5. * Log info and debug to a custom log file connector.log
  6. */
  7. class File
  8. {
  9. /**
  10. * @var string
  11. */
  12. private $outputFolder;
  13. /**
  14. * @var string
  15. */
  16. private $outputArchiveFolder;
  17. /**
  18. * @var string
  19. */
  20. private $delimiter;
  21. /**
  22. * @var string
  23. */
  24. private $enclosure;
  25. /**
  26. * @var string
  27. */
  28. private $logFileName = 'connector.log';
  29. /**
  30. * @var \Magento\Framework\App\Filesystem\DirectoryList
  31. */
  32. private $directoryList;
  33. /**
  34. * @var \Dotdigitalgroup\Email\Model\ResourceModel\Consent
  35. */
  36. private $consentResource;
  37. /**
  38. * @var \Magento\Framework\File\Csv
  39. */
  40. private $csv;
  41. /**
  42. * File constructor.
  43. *
  44. * @param \Magento\Framework\App\Filesystem\DirectoryList $directoryList
  45. * @param \Dotdigitalgroup\Email\Model\ResourceModel\Consent $consentResource
  46. * @param \Magento\Framework\File\Csv $csv
  47. *
  48. * @throws \Magento\Framework\Exception\FileSystemException
  49. */
  50. public function __construct(
  51. \Magento\Framework\App\Filesystem\DirectoryList $directoryList,
  52. \Dotdigitalgroup\Email\Model\ResourceModel\Consent $consentResource,
  53. \Magento\Framework\File\Csv $csv
  54. ) {
  55. $this->csv = $csv;
  56. $this->consentResource = $consentResource;
  57. $this->directoryList = $directoryList;
  58. $varPath = $directoryList->getPath('var');
  59. $this->outputFolder = $varPath . DIRECTORY_SEPARATOR . 'export' . DIRECTORY_SEPARATOR . 'email';
  60. $this->outputArchiveFolder = $this->outputFolder . DIRECTORY_SEPARATOR . 'archive';
  61. // tab character
  62. $this->delimiter = ',';
  63. $this->enclosure = '"';
  64. $logDir = $directoryList->getPath('log');
  65. if (! is_dir($logDir)) {
  66. mkdir($directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'log');
  67. }
  68. $writer = new \Zend\Log\Writer\Stream($logDir . DIRECTORY_SEPARATOR . $this->logFileName);
  69. $logger = new \Zend\Log\Logger();
  70. $logger->addWriter($writer);
  71. $this->connectorLogger = $logger;
  72. }
  73. /**
  74. * @return string
  75. */
  76. private function getOutputFolder()
  77. {
  78. $this->createDirectoryIfNotExists($this->outputFolder);
  79. return $this->outputFolder;
  80. }
  81. /**
  82. * @return string
  83. */
  84. public function getArchiveFolder()
  85. {
  86. $this->createDirectoryIfNotExists($this->outputArchiveFolder);
  87. return $this->outputArchiveFolder;
  88. }
  89. /**
  90. * Return the full filepath.
  91. *
  92. * @param string $filename
  93. *
  94. * @return string
  95. */
  96. public function getFilePath($filename)
  97. {
  98. return $this->getOutputFolder() . DIRECTORY_SEPARATOR . $filename;
  99. }
  100. /**
  101. * Move file to archive dir.
  102. *
  103. * @param string $filename
  104. *
  105. * @return null
  106. */
  107. public function archiveCSV($filename)
  108. {
  109. $this->moveFile(
  110. $this->getOutputFolder(),
  111. $this->getArchiveFolder(),
  112. $filename
  113. );
  114. }
  115. /**
  116. * Moves the output file from one folder to the next.
  117. *
  118. * @param string $sourceFolder
  119. * @param string $destFolder
  120. * @param string $filename
  121. *
  122. * @return null
  123. */
  124. private function moveFile($sourceFolder, $destFolder, $filename)
  125. {
  126. // generate the full file paths
  127. $sourceFilepath = $sourceFolder . DIRECTORY_SEPARATOR . $filename;
  128. $destFilepath = $destFolder . DIRECTORY_SEPARATOR . $filename;
  129. // rename the file
  130. rename($sourceFilepath, $destFilepath);
  131. }
  132. /**
  133. * Output an array to the output file FORCING Quotes around all fields.
  134. *
  135. * @param string $filepath
  136. * @param mixed $csv
  137. *
  138. * @throws \Exception
  139. *
  140. * @return null
  141. */
  142. public function outputForceQuotesCSV($filepath, $csv)
  143. {
  144. $fqCsv = $this->arrayToCsv($csv, chr(9), '"', true, false);
  145. // Open for writing only; place the file pointer at the end of the file.
  146. // If the file does not exist, attempt to create it.
  147. $fp = fopen($filepath, 'a');
  148. // for some reason passing the preset delimiter/enclosure variables results in error
  149. // $this->delimiter $this->enclosure
  150. if (fwrite($fp, $fqCsv) == 0) {
  151. throw new \Exception('Problem writing CSV file');
  152. }
  153. fclose($fp);
  154. }
  155. /**
  156. * @param string $filepath
  157. * @param array $csv
  158. *
  159. * @return null
  160. */
  161. public function outputCSV($filepath, $csv)
  162. {
  163. /*
  164. * Open for writing only; place the file pointer at the end of the file.
  165. * If the file does not exist, attempt to create it.
  166. */
  167. $handle = fopen($filepath, 'a');
  168. fputcsv($handle, $csv, ',', '"');
  169. fclose($handle);
  170. }
  171. /**
  172. * If the path does not exist then create it.
  173. *
  174. * @param string $path
  175. *
  176. * @return null
  177. */
  178. private function createDirectoryIfNotExists($path)
  179. {
  180. if (!is_dir($path)) {
  181. mkdir($path, 0750, true);
  182. }
  183. }
  184. /**
  185. * Convert array into the csv.
  186. *
  187. * @param array $fields
  188. * @param string $delimiter
  189. * @param string $enclosure
  190. * @param bool $encloseAll
  191. * @param bool $nullToMysqlNull
  192. *
  193. * @return string
  194. */
  195. private function arrayToCsv(
  196. array &$fields,
  197. $delimiter,
  198. $enclosure,
  199. $encloseAll = false,
  200. $nullToMysqlNull = false
  201. ) {
  202. $delimiterEsc = preg_quote($delimiter, '/');
  203. $enclosureEsc = preg_quote($enclosure, '/');
  204. $output = [];
  205. foreach ($fields as $field) {
  206. if ($field === null && $nullToMysqlNull) {
  207. $output[] = 'NULL';
  208. continue;
  209. }
  210. // Enclose fields containing $delimiter, $enclosure or whitespace
  211. if ($encloseAll
  212. || preg_match(
  213. "/(?:$delimiterEsc|$enclosureEsc|\s)/",
  214. $field
  215. )
  216. ) {
  217. $output[] = $enclosure . str_replace(
  218. $enclosure,
  219. $enclosure . $enclosure,
  220. $field
  221. ) . $enclosure;
  222. } else {
  223. $output[] = $field;
  224. }
  225. }
  226. return implode($delimiter, $output) . "\n";
  227. }
  228. /**
  229. * Delete file or directory.
  230. *
  231. * @param string $path
  232. *
  233. * @return bool
  234. */
  235. public function deleteDir($path)
  236. {
  237. if (strpos($path, $this->directoryList->getPath('var')) === false) {
  238. return sprintf("Failed to delete directory - '%s'", $path);
  239. }
  240. $classFunc = [__CLASS__, __FUNCTION__];
  241. return is_file($path)
  242. ?
  243. @unlink($path)
  244. :
  245. array_map($classFunc, glob($path . '/*')) == @rmdir($path);
  246. }
  247. /**
  248. * Get log file content.
  249. *
  250. * @param string $filename
  251. *
  252. * @return string
  253. */
  254. public function getLogFileContent($filename = 'connector')
  255. {
  256. switch ($filename) {
  257. case "connector":
  258. $filename = 'connector.log';
  259. break;
  260. case "system":
  261. $filename = 'system.log';
  262. break;
  263. case "exception":
  264. $filename = 'exception.log';
  265. break;
  266. case "debug":
  267. $filename = 'debug.log';
  268. break;
  269. default:
  270. return "Log file is not valid. Log file name is " . $filename;
  271. }
  272. $pathLogfile = $this->directoryList->getPath('log') . DIRECTORY_SEPARATOR . $filename;
  273. //tail the length file content
  274. $lengthBefore = 500000;
  275. try {
  276. $contents = '';
  277. $handle = fopen($pathLogfile, 'r');
  278. fseek($handle, -$lengthBefore, SEEK_END);
  279. if (!$handle) {
  280. return "Log file is not readable or does not exist at this moment. File path is "
  281. . $pathLogfile;
  282. }
  283. if (filesize($pathLogfile) > 0) {
  284. $contents = fread($handle, filesize($pathLogfile));
  285. if ($contents === false) {
  286. return "Log file is not readable or does not exist at this moment. File path is "
  287. . $pathLogfile;
  288. }
  289. fclose($handle);
  290. }
  291. return $contents;
  292. } catch (\Exception $e) {
  293. return $e->getMessage() . $pathLogfile;
  294. }
  295. }
  296. /**
  297. * @param string|array|mixed $data
  298. *
  299. * @return null
  300. */
  301. public function info($data)
  302. {
  303. $this->connectorLogger->info($data);
  304. }
  305. /**
  306. * @param string $message
  307. * @param array $extra
  308. *
  309. * @return null
  310. */
  311. public function debug($message, $extra)
  312. {
  313. $this->connectorLogger->debug($message, $extra);
  314. }
  315. /**
  316. * @param string $file full path to the csv file.
  317. * @return bool|string
  318. */
  319. public function cleanProcessedConsent($file)
  320. {
  321. //read file and get the email addresses
  322. $index = $this->csv->getDataPairs($file, 0, 0);
  323. //remove header data for Email
  324. unset($index['Email']);
  325. $emails = array_values($index);
  326. $log = false;
  327. try {
  328. $result = $this->consentResource
  329. ->deleteConsentByEmails($emails);
  330. if ($count = count($result)) {
  331. $log = 'Consent data removed : ' . $count;
  332. }
  333. } catch (\Exception $e) {
  334. $log = $e->getMessage();
  335. }
  336. return $log;
  337. }
  338. /**
  339. * Return the full file path with checking in archive as fallback.
  340. *
  341. * @param string $filename
  342. * @return string
  343. */
  344. public function getFilePathWithFallback($filename)
  345. {
  346. $emailPath = $this->getOutputFolder() . DIRECTORY_SEPARATOR . $filename;
  347. $archivePath = $this->getArchiveFolder() . DIRECTORY_SEPARATOR . $filename;
  348. return is_file($emailPath) ? $emailPath : $archivePath;
  349. }
  350. /**
  351. * Check if file exist in email or archive folder
  352. *
  353. * @param string $filename
  354. * @return boolean
  355. */
  356. public function isFilePathExistWithFallback($filename)
  357. {
  358. $emailPath = $this->getOutputFolder() . DIRECTORY_SEPARATOR . $filename;
  359. $archivePath = $this->getArchiveFolder() . DIRECTORY_SEPARATOR . $filename;
  360. return is_file($emailPath) ? true : (is_file($archivePath) ? true : false);
  361. }
  362. /**
  363. * @param string $filename
  364. * @return bool
  365. */
  366. public function isFileAlreadyArchived($filename)
  367. {
  368. return is_file($this->getArchiveFolder() . DIRECTORY_SEPARATOR . $filename);
  369. }
  370. }