PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/handlers/class.atkimporthandler.inc

https://github.com/ibuildingsnl/ATK
PHP | 1607 lines | 1137 code | 152 blank | 318 comment | 142 complexity | 0c0191c0d253472e2f26f3a92e34e0f4 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * This file is part of the Achievo ATK distribution.
  4. * Detailed copyright and licensing information can be found
  5. * in the doc/COPYRIGHT and doc/LICENSE files which should be
  6. * included in the distribution.
  7. *
  8. * @package atk
  9. * @subpackage handlers
  10. *
  11. * @copyright (c)2004 Ivo Jansch
  12. * @copyright (c)2004 Ibuildings.nl BV
  13. * @license http://www.achievo.org/atk/licensing ATK Open Source License
  14. *
  15. * @version $Revision: 6323 $
  16. * $Id$
  17. */
  18. /**
  19. * Handler for the 'import' action of a node. The import action is a
  20. * generic tool for importing CSV files into a table.
  21. *
  22. * @author Ivo Jansch <ivo@achievo.org>
  23. * @package atk
  24. * @subpackage handlers
  25. *
  26. */
  27. class atkImportHandler extends atkActionHandler
  28. {
  29. var $m_importNode;
  30. /**
  31. * The action handler.
  32. * @param bool Always true
  33. */
  34. function action_import()
  35. {
  36. global $ATK_VARS;
  37. //need to keep the postdata after a AF_LARGE selection in the allfield
  38. if(!isset($this->m_postvars["phase"]) && isset($ATK_VARS['atkformdata']))
  39. foreach($ATK_VARS['atkformdata'] as $key=>$value)
  40. $this->m_postvars[$key] = $value;
  41. $keys = array();
  42. //need to keep the selected item after an importerror
  43. if (is_array($ATK_VARS['allFields'])) $keys = array_keys($ATK_VARS['allFields']);
  44. foreach ($keys as $key)
  45. {
  46. if(!isset($ATK_VARS[$ATK_VARS['allFields'][$key]."_newsel"]))
  47. $ATK_VARS[$ATK_VARS['allFields'][$key]."_newsel"] = $ATK_VARS[$ATK_VARS['allFields'][$key]];
  48. }
  49. $phase = ($this->m_postvars["phase"]!=""?$this->m_postvars["phase"]:"init");
  50. switch ($phase)
  51. {
  52. case "init": $this->doInit(); break;
  53. case "upload": $this->doUpload(); break;
  54. case "process": $this->doProcess(); break;
  55. }
  56. }
  57. /**
  58. * Sets the node for this handler. Implicitly sets the
  59. * import node too!
  60. *
  61. * @param atkNode $node node instance
  62. *
  63. * @see setImportNode
  64. */
  65. function setNode(&$node)
  66. {
  67. parent::setNode($node);
  68. $this->setImportNode($node);
  69. }
  70. /**
  71. * Sets the import node. By default this is the same node
  72. * as set by setNode, but if you call this method after the setNode
  73. * call you can override the import node.
  74. *
  75. * @param atkNode $node node instance
  76. *
  77. * @see setNode
  78. */
  79. function setImportNode(&$node)
  80. {
  81. $this->m_importNode = &$node;
  82. }
  83. /**
  84. * Create import page for the given phase.
  85. *
  86. * @param string $phase import phase (init, upload, process)
  87. * @param string $content page content
  88. */
  89. function importPage($phase, $content)
  90. {
  91. $controller = &atkController::getInstance();
  92. $action = $controller->getPhpFile().'?'.SID;
  93. $formStart =
  94. '<form id="entryform" name="entryform" enctype="multipart/form-data" action="'.$action.'" method="post">'.
  95. session_form(atkLevel() == 0 ? SESSION_NESTED : SESSION_REPLACE).
  96. '<input type="hidden" name="atknodetype" value="'.$this->m_node->atkNodeType().'" />'.
  97. '<input type="hidden" name="atkaction" value="'.$this->m_node->m_action.'" />'.
  98. $controller->getHiddenVarsString();
  99. $buttons = $this->invoke('getImportButtons', $phase);
  100. $ui = &$this->m_node->getUi();
  101. $page = &$this->m_node->getPage();
  102. $this->m_node->addStyle("style.css");
  103. $params = $this->m_node->getDefaultActionParams(false);
  104. $params['header'] = $this->invoke('importHeader', $phase);
  105. $params['formstart'] = $formStart;
  106. $params['content'] = $content;
  107. $params['buttons'] = $buttons;
  108. $output = $ui->renderAction('import', $params);
  109. $params = array();
  110. $params['title'] = $this->m_node->actionTitle('import');
  111. $params['content'] = $output;
  112. $output = $ui->renderBox($params);
  113. $output = $this->m_node->renderActionPage('import', $output);
  114. $page->addContent($output);
  115. }
  116. /**
  117. * Import header.
  118. *
  119. * @param string $phase import phase ('init', 'upload', 'process', 'analyze')
  120. */
  121. function importHeader($phase)
  122. {
  123. return '';
  124. }
  125. /**
  126. * Get import buttons.
  127. *
  128. * @param string $phase import phase ('init', 'upload', 'process', 'analyze')
  129. */
  130. function getImportButtons($phase)
  131. {
  132. $result = array();
  133. if ($phase == 'init')
  134. {
  135. $result[] = '<input class="btn" type="submit" value="'.$this->m_node->text("import_upload").'">';
  136. }
  137. else if ($phase == 'analyze')
  138. {
  139. $result[] = '<input type="submit" class="btn" name="analyse" value="'.$this->m_node->text("import_analyse").'">';
  140. $result[] = '<input type="submit" class="btn" name="import" value="'.$this->m_node->text("import_import").'"> ';
  141. }
  142. if (atkLevel() > 0)
  143. {
  144. $result[] = atkButton($this->m_node->text("cancel","atk"), "", SESSION_BACK, true);
  145. }
  146. return $result;
  147. }
  148. /**
  149. * This function shows a form to upload a .csv
  150. * @param bool Always true
  151. */
  152. function doInit()
  153. {
  154. $content = '
  155. <input type="hidden" name="phase" value="upload">
  156. <table border="0">
  157. <tr>
  158. <td style="text-align: left">
  159. '.$this->m_node->text("import_upload_explanation").'
  160. <br /><br />
  161. <input type="file" name="csvfile">
  162. </td>
  163. </tr>
  164. </table>';
  165. $this->invoke('importPage', 'init', $content);
  166. }
  167. /**
  168. * This function takes care of uploaded file
  169. */
  170. function doUpload()
  171. {
  172. $fileid = uniqid("file_");
  173. $filename = $this->getTmpFileDestination($fileid);
  174. if (!move_uploaded_file($_FILES['csvfile']['tmp_name'], $filename))
  175. {
  176. $this->m_node->redirect($this->m_node->feedbackUrl("import", ACTION_FAILED));
  177. }
  178. else
  179. {
  180. // file uploaded
  181. $this->doAnalyze($fileid);
  182. }
  183. }
  184. /**
  185. * This function checks if there is enough information to import the date
  186. * else it wil shows a form to set how the file wil be imported
  187. */
  188. function doProcess()
  189. {
  190. $filename = $this->getTmpFileDestination($this->m_postvars["fileid"]);
  191. if ($this->m_postvars["import"]!="")
  192. {
  193. $this->doImport($filename);
  194. }
  195. else
  196. {
  197. // reanalyze
  198. $this->doAnalyze($this->m_postvars["fileid"]);
  199. }
  200. }
  201. /**
  202. * This function shows a form where the user can choose the mapping of the column,
  203. * an allfield and if the first record must be past over
  204. *
  205. * @param string $fileid the id of the uploaded file
  206. * @param array $importerrors An array with the import errors
  207. */
  208. function doAnalyze($fileid,$importerrors=array())
  209. {
  210. $sessionMgr = &atkGetSessionManager();
  211. $filename = $this->getTmpFileDestination($fileid);
  212. $rows = $this->getSampleRows($filename);
  213. $delimiter = $sessionMgr->pageVar("delimiter");
  214. if ($delimiter=="") $delimiter = $this->estimateDelimiter($rows);
  215. $enclosure = $sessionMgr->pageVar("enclosure");
  216. if ($enclosure=="") $enclosure = $this->estimateEnclosure($rows);
  217. $allFields = $sessionMgr->pageVar("allFields");
  218. if($allFields=="") $allFields = array();
  219. $skipfirstrow = $this->m_postvars['skipfirstrow'];
  220. $doupdate = $this->m_postvars['doupdate'];
  221. $updatekey1 = $this->m_postvars['updatekey1'];
  222. $onfalseidentifier = $this->m_postvars['onfalseid'];
  223. $novalidatefirst = $this->m_postvars['novalidatefirst'];
  224. $columncount = $this->estimateColumnCount($rows, $delimiter);
  225. $csv_data = $this->fgetcsvfromarray($rows, $columncount, $delimiter, $enclosure);
  226. $col_map = $this->m_postvars["col_map"];
  227. if (!is_array($col_map))
  228. {
  229. // init colmap
  230. $col_map = $this->initColmap($csv_data[0], $matchFound);
  231. }
  232. if ($skipfirstrow === null)
  233. {
  234. $skipfirstrow = $matchFound;
  235. }
  236. if ($columncount>count($col_map))
  237. {
  238. // fill with ignored
  239. for ($i=0, $_i=($columncount-count($col_map)); $i<$_i; $i++) $col_map[] = "-";
  240. }
  241. $rowCount = $this->getRowCount($filename, $skipfirstrow);
  242. // Display sample
  243. $sample =
  244. atktext("import_sample").':<br><br><table class="recordlist">'.
  245. $this->_getAnalyseSample($columncount, $col_map, $csv_data, $skipfirstrow);
  246. $content = '
  247. <input type="hidden" name="phase" value="process">
  248. <div style="text-align: left; margin-left: 10px;">
  249. '.$this->_getAnalyseHeader($fileid, $columncount, $delimiter, $enclosure, $rowCount).'
  250. <br />
  251. '.$this->_getErrors($importerrors).'
  252. '.$sample.'
  253. <br />
  254. '.$this->_getAnalyseExtraOptions($skipfirstrow, $doupdate, $updatekey1, $onfalseidentifier, $allFields, $novalidatefirst).'
  255. </div>';
  256. $page = &$this->m_node->getPage();
  257. $theme = &atkinstance("atk.ui.atktheme");
  258. $page->register_style($theme->stylePath("recordlist.css"));
  259. $this->invoke('importPage', 'analyze', $content);
  260. }
  261. /**
  262. * Transforms the $importerrors array into displayable HTML
  263. *
  264. * @todo make this use templates
  265. *
  266. * @param Array $importerrors A special array with arrays in it
  267. * $importerrors[0] are general errors, other than that
  268. * the numbers stand for recordnumbers
  269. * @return String HTML table with the errors
  270. */
  271. function _getErrors($importerrors)
  272. {
  273. if(is_array($importerrors))
  274. {
  275. $content.="\n<table>";
  276. $errorCount = 0;
  277. foreach ($importerrors as $record => $errors)
  278. {
  279. $errorCount++;
  280. if ($errorCount > atkconfig("showmaximporterrors",50)) break;
  281. if ($record==0 && atk_value_in_array($errors))
  282. {
  283. $content.="<tr><td colSpan=2>";
  284. foreach ($errors as $error)
  285. {
  286. if (!empty($error)) $content.= "<span class=\"error\">".text($error['msg']).$error['spec']."</span><br />";
  287. }
  288. $content.="</td></tr>";
  289. }
  290. else if (atk_value_in_array($errors))
  291. {
  292. $content.="<tr><td valign=\"top\" class=\"error\">";
  293. $content.="<b>Record $record:</b>&nbsp;";
  294. $content.="</td><td valign=\"top\" class=\"error\">";
  295. $counter = 0;
  296. for ($counter=0;$counter<count($errors)&&$counter<atkconfig("showmaximporterrors",50);$counter++)
  297. {
  298. $content.= $this->m_node->text($errors[$counter]['msg']).$errors[$counter]['spec']."<br />";
  299. }
  300. $content.="</td></tr>";
  301. }
  302. }
  303. $content.="</tr></table><br />";
  304. }
  305. return $content;
  306. }
  307. /**
  308. * Returns the HTML header for the 'analyse' mode of the import handler
  309. * @param String $fileid The 'id' (name) of the file we are importing
  310. * @param String $columncount The number of columns we have
  311. * @param String $delimiter The delimiter in the file
  312. * @param String $enclosure The enclosure in the file
  313. * @param int $rowcount The number of rows in the CSV file
  314. * @return String The HTML header
  315. */
  316. function _getAnalyseHeader($fileid, $columncount, $delimiter, $enclosure, $rowcount)
  317. {
  318. $content = '<br>';
  319. $content.= '<input type="hidden" name="fileid" value="'.$fileid.'">';
  320. $content.= '<input type="hidden" name="columncount" value="'.$columncount.'">';
  321. $content.= '<table border="0">';
  322. $content.= '<tr><td>'.text("delimiter").': </td><td><input type="text" size="2" name="delimiter" value="'.atk_htmlentities($delimiter).'"></td></tr>';
  323. $content.= '<tr><td>'.text("enclosure").': </td><td><input type="text" size="2" name="enclosure" value="'.atk_htmlentities($enclosure).'"></td></tr>';
  324. $content.= '<tr><td>'.atktext("import_detectedcolumns").': </td><td>'.$columncount.'</td></tr>';
  325. $content.= '<tr><td>'.atktext("import_detectedrows").': </td><td>'.$rowcount.'</td></tr>';
  326. $content.= '</table>';
  327. return $content;
  328. }
  329. /**
  330. * Returns a sample of the analysis
  331. * @param String $columncount The number of columns we have
  332. * @param String $col_map A mapping of the column
  333. * @param String $csv_data The CSV data
  334. * @param String $skipfirstrow Wether or not to skip the first row
  335. */
  336. function _getAnalyseSample($columncount, $col_map, $csv_data, $skipfirstrow)
  337. {
  338. // header
  339. $sample.= '<tr>';
  340. for ($j=1; $j<=$columncount; $j++)
  341. {
  342. $sample.='<th>';
  343. $sample.= ucfirst(atktext("column")).' '.$j;
  344. $sample.='</th>';
  345. }
  346. $sample.= '</tr>';
  347. // column assign
  348. $sample.= '<tr>';
  349. for ($j=0; $j<$columncount; $j++)
  350. {
  351. $sample.='<th>';
  352. $sample.=$this->getAttributeSelector($j, $col_map[$j]);
  353. $sample.='</th>';
  354. }
  355. $sample.= '</tr>';
  356. // sample data
  357. for ($i=0; $i<count($csv_data); $i++)
  358. {
  359. $line = $csv_data[$i];
  360. $sample.='<tr class="row'.(($i%2)+1).'">';
  361. for ($j=0; $j<$columncount; $j++)
  362. {
  363. if ($i==0&&$skipfirstrow)
  364. {
  365. $sample.='<th>';
  366. $sample.=atktext(trim($line[$j]));
  367. }
  368. else
  369. {
  370. $sample.='<td>';
  371. if ($col_map[$j]!="" && $col_map[$j]!="-")
  372. {
  373. $display = $this->_getSampleValue($col_map[$j],trim($line[$j]));
  374. if ($display) $sample.= $display;
  375. else $sample.= atktext($col_map[$j]);
  376. if ((string)$display!==(string)$line[$j])
  377. {
  378. // Also display raw value so we can verify
  379. $sample.= ' <i style="color: #777777">('.trim($line[$j]).")</i>";
  380. }
  381. }
  382. else if ($col_map[$j]=="-")
  383. {
  384. // ignoring.
  385. $sample.='<div style="color: #777777">'.trim($line[$j]).'</div>';
  386. }
  387. else
  388. {
  389. $sample.=trim($line[$j]);
  390. }
  391. }
  392. $sample.=($i==0&&$skipfirstrow)?'</th>':'</td>';
  393. }
  394. $sample.='</tr>';
  395. }
  396. $sample.= '</table>';
  397. return $sample;
  398. }
  399. /**
  400. * Gets the displayable value for the attribute
  401. * @param String $attributename The name of the attribute
  402. * @param String $value The value of the attribute
  403. * @return String The displayable value for the attribute
  404. */
  405. function _getSampleValue($attributename, $value)
  406. {
  407. $attr = &$this->getUsableAttribute($attributename);
  408. if(method_exists($attr, "parseTime"))
  409. $newval = $attr->parseTime($value);
  410. else
  411. $newval = $attr->parseStringValue($value);
  412. if (method_exists($attr, "createDestination"))
  413. {
  414. $attr->createDestination();
  415. // If we can create a destination, then we can be reasonably sure it's a relation
  416. // and importing in a relation is a different matter altogether
  417. $searchresults = $attr->m_destInstance->searchDb($newval);
  418. if (count($searchresults)==1)
  419. {
  420. $atkval = array($attributename=>array($attr->m_destInstance->primaryKeyField()=>$searchresults[0][$attr->m_destInstance->primaryKeyField()]));
  421. }
  422. }
  423. else
  424. {
  425. $atkval = array($attributename=>$newval);
  426. }
  427. return $attr->display($atkval);
  428. }
  429. /**
  430. * Returns the extra options of the importhandler
  431. * @param String $skipfirstrow Wether or not to skip the first row
  432. * @param String $doupdate Wether or not to do an update
  433. * @param String $updatekey1 The key to update on
  434. * @param String $onfalseidentifier What to do on a false identifier
  435. * @param String $allFields The fields to import
  436. * @param Bool $novalidatefirst Validate before the import
  437. * @return String The HTML with the extra options
  438. */
  439. function _getAnalyseExtraOptions($skipfirstrow, $doupdate, $updatekey1, $onfalseidentifier, $allFields, $novalidatefirst)
  440. {
  441. $content.= '<br /><table id="importoptions">';
  442. $content.= ' <tr>';
  443. $content.= ' <td>';
  444. foreach ($allFields as $allfield)
  445. {
  446. if (!$this->m_postvars[$allfield]) $noallfieldvalue=true;
  447. }
  448. if (empty($allFields) || !$noallfieldvalue) $allFields[] = '';
  449. foreach ($allFields as $allField)
  450. {
  451. $content.= atktext("import_allfield").': </td><td>'.$this->getAttributeSelector(0,$allField,"allFields[]");
  452. if ($allField!="")
  453. {
  454. $attr = $this->getUsableAttribute($allField);
  455. if(is_object($attr))
  456. {
  457. $fakeeditarray = array($allField=>$this->m_postvars[$allField]);
  458. $content.= ' '.atktext("value").': '.$attr->edit($fakeeditarray,"","edit").'<br/>';
  459. }
  460. }
  461. $content.= '</td></tr><tr><td>';
  462. }
  463. $content.= atktext("import_skipfirstrow").': </td><td><input type="checkbox" name="skipfirstrow" class="atkcheckbox" value="1" '.($skipfirstrow?"CHECKED":"").'/>';
  464. $content.= '</td></tr><tr><td>';
  465. $content.= atktext("import_doupdate").': </td><td> <input type="checkbox" name="doupdate" class="atkcheckbox" value="1" '.($doupdate?"CHECKED":"").'/>';
  466. $content.= '</td></tr><tr><td>';
  467. $content.= atktext("import_update_key").': </td><td>'.$this->getAttributeSelector(0,$updatekey1,"updatekey1",2).'</td>';
  468. $content.= '</td></tr><tr><td>';
  469. $content.= atktext("import_onfalseidentifier").': </td><td> <input type="checkbox" name="onfalseid" class="atkcheckbox" value="1" '.($onfalseidentifier?"CHECKED":"").'/>';
  470. $content.= '</td></tr><tr><td>';
  471. $content.= atktext("import_novalidatefirst").': </td><td> <input type="checkbox" name="novalidatefirst" class="atkcheckbox" value="1" '.($novalidatefirst?"CHECKED":"").'/>';
  472. $content.= ' </td>';
  473. $content.= ' </tr>';
  474. $content.= '</table><br /><br />';
  475. return $content;
  476. }
  477. /**
  478. * Get the destination of the uploaded csv-file
  479. * @param string $fileid The id of the file
  480. * @return string The path of the file
  481. */
  482. function getTmpFileDestination($fileid)
  483. {
  484. return atkconfig("atktempdir")."csv_import_$fileid.csv";
  485. }
  486. /**
  487. * Get data from each line
  488. * @param Array $arr An array with the lines from the CSV file
  489. * @param int $columncount The number of columns in the file
  490. * @param String $delimiterChar The delimeter character
  491. * @param String $enclosureChar The enclosure character
  492. * @return Array An array with the CSV data
  493. */
  494. function fgetcsvfromarray ($arr, $columncount, $delimiterChar = ',', $enclosureChar = '"')
  495. {
  496. $result = array();
  497. foreach ($arr as $line)
  498. {
  499. $result[] = $this->fgetcsvfromline($line, $columncount, $delimiterChar, $enclosureChar);
  500. }
  501. return $result;
  502. }
  503. /**
  504. * Gets the char which is used for enclosure in the csv-file
  505. * @param Array $rows The rows from the csv-file
  506. * @return String The enclosure
  507. */
  508. function estimateDelimiter($rows)
  509. {
  510. if (!is_array($rows)||count($rows)==0) return ",";
  511. if (strpos($rows[0], ";")!==false) return ";";
  512. if (strpos($rows[0], ",")!==false) return ",";
  513. if (strpos($rows[0], ":")!==false) return ":";
  514. else return ";";
  515. }
  516. /**
  517. * Gets the char which is used for enclosure in the csv-file
  518. * @param Array $rows The rows from the csv-file
  519. * @return String The enclosure
  520. */
  521. function estimateEnclosure($rows)
  522. {
  523. if (!is_array($rows)||count($rows)==0) return '"';
  524. if (substr_count($rows[0], '"')>=2) return '"';
  525. return '';
  526. }
  527. /**
  528. * Counts the number of columns in the first row
  529. * @param Array $rows The rows from the csv-file
  530. * @param String $delimiter The char which seperate the fields
  531. * @return int The number of columns
  532. */
  533. function estimateColumnCount($rows, $delimiter)
  534. {
  535. if (!is_array($rows)||count($rows)==0) return 0;
  536. if ($delimiter == "") return 1;
  537. return (substr_count($rows[0], $delimiter)+1);
  538. }
  539. /**
  540. * Get the first 5 lines from the csv-file
  541. * @param String $file The path to the csv-file
  542. * @return Array The 5 lines from the csv file
  543. */
  544. function getSampleRows($file)
  545. {
  546. $result = array();
  547. $fp = fopen($file, "r");
  548. for ($i=0; $i<5; $i++)
  549. {
  550. $line = fgets($fp);
  551. if ($line!==false)
  552. {
  553. $result[] = $line;
  554. }
  555. }
  556. fclose($fp);
  557. return $result;
  558. }
  559. /**
  560. * Returns the CSV line count.
  561. *
  562. * @param string $file the path to the csv-file
  563. * @param bool $skipFirstRow Skip the first row?
  564. * @return int row count
  565. */
  566. function getRowCount($file, $skipFirstRow)
  567. {
  568. $count = 0;
  569. $fp = fopen($file, "r");
  570. while ($line = fgets($fp))
  571. {
  572. if (trim($line) == "") continue;
  573. $count++;
  574. }
  575. return $count - ($count > 0 && $skipFirstRow ? 1 : 0);
  576. }
  577. function fgetcsvfromline ($line, $columncount, $delimiterChar = ',', $enclosureChar = '"')
  578. {
  579. $line = trim($line);
  580. // if we haven't got an enclosure char, the only thing we can do is
  581. // splitting it using the delimiterChar - no further action needed
  582. if (!$enclosureChar)
  583. {
  584. return explode($delimiterChar,$line);
  585. }
  586. if ($line{0} == $delimiterChar)
  587. {
  588. $line = $enclosureChar.$enclosureChar.$line;
  589. }
  590. if (substr($line, -1) == $delimiterChar)
  591. $line .= $enclosureChar.$enclosureChar;
  592. $reDelimiterChar = preg_quote($delimiterChar, '/');
  593. $reEnclosureChar = preg_quote($enclosureChar, '/');
  594. // Some exports don't enclose empty or numeric fields with the enclosureChar. Let's fix
  595. // that first so we can use one preg_split statement that works in those cases too.
  596. // loop until all occurrences are replaced. Contains an infinite loop prevention.
  597. for ($fix="", $i=0, $_i=substr_count($line, $delimiterChar); $fix!=$line && $i<$_i;$i++)
  598. {
  599. if ($fix!="") $line = $fix;
  600. $pattern = '/'.$reDelimiterChar.'([^\\\\'.$reDelimiterChar.$reEnclosureChar.']*)'.$reDelimiterChar.'/';
  601. $fix = preg_replace($pattern , $delimiterChar.$enclosureChar.'\\1'.$enclosureChar.$delimiterChar, $line);
  602. }
  603. $line = $fix;
  604. // fix an unquoted string at line end, if any
  605. $pattern = '/'.$reDelimiterChar.'([^\\\\'.$reDelimiterChar.$reEnclosureChar.']*)$/';
  606. $line = preg_replace($pattern ,
  607. $delimiterChar.$enclosureChar.'\\1'.$enclosureChar,$line);
  608. // chop the first and last enclosures so they aren't split at
  609. $start = (($line[0]==$enclosureChar)?1:0);
  610. if($line[strlen($line)-1]==$enclosureChar) {
  611. $line = substr($line, $start, -1);
  612. } else {
  613. $line = substr($line, $start);
  614. }
  615. // now split by delimiter
  616. $expression = '/'.$reEnclosureChar.' *'.$reDelimiterChar.'*'.$reEnclosureChar.'/';
  617. return preg_split($expression, $line);
  618. }
  619. /**
  620. * Gives all the attributes that can be used for the import
  621. * @param bool $obligatoryOnly if false then give all attributes, if true then give only the obligatory ones
  622. * defaults to false
  623. * @return Array the attributes
  624. */
  625. function getUsableAttributes($obligatoryOnly=false)
  626. {
  627. $attrs = array();
  628. foreach (array_keys($this->m_importNode->m_attribList) as $attribname)
  629. {
  630. $attrib = &$this->m_importNode->getAttribute($attribname);
  631. if($this->integrateAttribute($attrib))
  632. {
  633. $attrib->createDestination();
  634. foreach(array_keys($attrib->m_destInstance->m_attribList) as $relattribname)
  635. {
  636. $relattrib = &$attrib->m_destInstance->getAttribute($relattribname);
  637. if ($this->_usableForImport($obligatoryOnly, $relattrib))
  638. {
  639. $attrs[] = $relattribname;
  640. }
  641. }
  642. }
  643. else
  644. {
  645. if ($this->_usableForImport($obligatoryOnly, $attrib))
  646. {
  647. $attrs[] = $attribname;
  648. }
  649. }
  650. }
  651. return $attrs;
  652. }
  653. /**
  654. * Check if an attribute is usable for import.
  655. * @param bool $obligatoryOnly Wether or not we should concider obligatory attributes
  656. * @param Object &$attrib The attribute
  657. * @return bool Wether or not the attribute is usable for import
  658. */
  659. function _usableForImport($obligatoryOnly, &$attrib)
  660. {
  661. return ((!$obligatoryOnly || $this->isObligatory($attrib))
  662. && !$attrib->hasFlag(AF_AUTOINCREMENT) && !$this->isHide($attrib)
  663. && !is_a($attrib, 'atkdummyattribute'));
  664. }
  665. /**
  666. * Gives all obligatory attributes
  667. *
  668. * Same as getUsableAttributes with parameter true
  669. * @return Array An array with all the obligatory attributes
  670. */
  671. function getObligatoryAttributes()
  672. {
  673. return $this->getUsableAttributes(true);
  674. }
  675. /**
  676. * Checks whether the attribute is obligatory
  677. * @param Object $attr The attribute to check
  678. * @return boolean The result of the check
  679. */
  680. function isObligatory($attr)
  681. {
  682. return ($attr->hasFlag(AF_OBLIGATORY) && !$this->isHide($attr));
  683. }
  684. /**
  685. * Checks whether the attribute is hiden by a flag
  686. * @param Object $attr The attribute to check
  687. * @return boolean The result of the check
  688. */
  689. function isHide($attr)
  690. {
  691. return (($attr->hasFlag(AF_HIDE) || ($attr->hasFlag(AF_HIDE_ADD) && $attr->hasFlag(AF_HIDE_EDIT)))&& !$attr->hasFlag(AF_FORCE_LOAD));
  692. }
  693. /**
  694. * Checks whether the attribute has the flag AF_ONETOONE_INTEGRATE
  695. * @param Object $attr The attribute to check
  696. * @return boolean The result of the check
  697. */
  698. function integrateAttribute($attr)
  699. {
  700. return in_array(get_class($attr),array("atkonetoonerelation","atksecurerelation")) && $attr->hasFlag(AF_ONETOONE_INTEGRATE);
  701. }
  702. /**
  703. * Get al attributes from the import node that have the flag AF_ONETOONE_INTEGRATE
  704. * @return array A list with all attributes from the import node that have the flag AF_ONETOONE_INTEGRATE
  705. */
  706. function getIntegratedAttributes()
  707. {
  708. $attrs = array();
  709. foreach (array_keys($this->m_importNode->m_attribList) as $attribname)
  710. {
  711. $attrib = &$this->m_importNode->getAttribute($attribname);
  712. if($this->integrateAttribute($attrib))
  713. {
  714. $attrs[] = $attribname;
  715. }
  716. }
  717. return $attrs;
  718. }
  719. /**
  720. * Check whether the attribute is part of a relation
  721. * @param String $attrname name of the attribute
  722. * @return mixed false if not, relation name if yes
  723. */
  724. function isRelationAttribute($attrname)
  725. {
  726. if(array_key_exists($attrname,$this->m_importNode->m_attribList))
  727. return false;
  728. foreach($this->getIntegratedAttributes() as $attr)
  729. {
  730. $relattr = $this->m_importNode->getAttribute($attr);
  731. $relattr->createDestination();
  732. if(array_key_exists($attrname,$relattr->m_destInstance->m_attribList))
  733. return $attr;
  734. }
  735. return false;
  736. }
  737. /**
  738. * Check whether the attribute has a relation (only manytoonerelations)
  739. * @param String $attrname name of the attribute
  740. * @return boolean result of the check
  741. */
  742. function hasRelationAttribute($attrname)
  743. {
  744. return in_array(get_class($this->getUsableAttribute($attrname)),array("atkmanytoonerelation","atkmanytoonetreerelation"));
  745. }
  746. /**
  747. * Get the real attribute (instance) by his name
  748. * @param String $name name of the attribute
  749. * @return object instance of the attribute
  750. */
  751. function &getUsableAttribute($name)
  752. {
  753. if(array_key_exists($name,$this->m_importNode->m_attribList))
  754. return $this->m_importNode->getAttribute($name);
  755. foreach($this->getIntegratedAttributes() as $attr)
  756. {
  757. $relattr = $this->m_importNode->getAttribute($attr);
  758. $relattr->createDestination();
  759. if(array_key_exists($name,$relattr->m_destInstance->m_attribList))
  760. return $relattr->m_destInstance->getAttribute($name);
  761. }
  762. return null;
  763. }
  764. /**
  765. * Add one value to the record
  766. * @param Array $record the record wich will be changed
  767. * @param String $attrname the name of the attribute
  768. * @param String $value the value of that attribute
  769. */
  770. function addToRecord(&$record,$attrname,$value)
  771. {
  772. $attr = &$this->getUsableAttribute($attrname);
  773. if(!is_object($attr)) return;
  774. foreach($this->getIntegratedAttributes() as $intattr)
  775. {
  776. if(!isset($record[$intattr]))
  777. $record[$intattr] = array('mode'=>"add",'atkaction'=>"save");
  778. }
  779. $record[$attrname] = $value;
  780. }
  781. /**
  782. * Returns a dropdownlist with all possible field in the importnode
  783. * @param int $index the number of the column
  784. * @param String $value the name of the attribute that is selected in the list (if empty then select the last one)
  785. * @param String $othername if set, use a other name for the dropdown, else use the name "col_map[index]"
  786. * @param int $emptycol mode for empty column (0 = no empty column, 1= empty column, 2= an 'ignore this column' (default))
  787. * @return String the html-code for the dropdownlist (<select>...</sekect>)
  788. */
  789. function getAttributeSelector($index=0, $value="",$othername="", $emptycol=2)
  790. {
  791. if(!$othername)
  792. $res = '<select name="col_map['.$index.']">';
  793. else
  794. $res = '<select name="'.$othername.'" onchange="entryform.submit()">';
  795. $j=0;
  796. $hasoneselected = false;
  797. $attrs = $this->getUsableAttributes();
  798. foreach ($attrs as $attribname)
  799. {
  800. $attr = &$this->getUsableAttribute($attribname);
  801. $label = $attr->label();
  802. $selected = "";
  803. if ($value!="" && $value==$attribname)
  804. {
  805. // select the next.
  806. $selected="selected";
  807. $hasoneselected = true;
  808. }
  809. $res.= '<option value="'.$attribname.'" '.$selected.'>'.$label."\n";
  810. $j++;
  811. }
  812. if ($emptycol==2) $res.= '<option value="-" '.(($value=="-"||!$hasoneselected)?"selected":"").' style="font-style: italic">'.atktext("import_ignorecolumn");
  813. elseif ($emptycol==1) $res.= '<option value="" '.((!$value||!$hasoneselected)?"selected":"").'>';
  814. $res.= '</select>';
  815. return $res;
  816. }
  817. /**
  818. * The same als the php function array_search, but now much better.
  819. * This function is not case sensitive
  820. * @param Array $array The array to search through
  821. * @param mixed $value The value to search for
  822. * @return mixed The key if it is in the array, else false
  823. */
  824. function inArray($array,$value)
  825. {
  826. foreach($array as $key=>$item)
  827. {
  828. if(strtolower($item) == strtolower($value))
  829. return $key;
  830. if(strtolower($item) == strtolower(atktext($value, $this->m_node->m_module, $this->m_node->m_type)))
  831. return $key;
  832. }
  833. return false;
  834. }
  835. /**
  836. * Make a record of translations of the given attributes
  837. * @param Array $attributes The attributes to translate
  838. * @return Array The result of the translation
  839. */
  840. function getAttributesTranslation($attributes)
  841. {
  842. $result = array();
  843. foreach($attributes as $attribute)
  844. {
  845. $attr = &$this->getUsableAttribute($attribute);
  846. $label = $attr->label();
  847. $result[] = $label;
  848. }
  849. return $result;
  850. }
  851. /**
  852. * Tries to make a default col_map with the first record of the csv-file
  853. * @param Array $firstRecord The first record of the CSV file
  854. * @param Bool &$matchFound Found a match?
  855. * @return Array The default col_map
  856. */
  857. function initColmap($firstRecord, &$matchFound)
  858. {
  859. $result = array();
  860. $attributes = $this->getUsableAttributes();
  861. $translations = $this->getAttributesTranslation($attributes);
  862. $matchFound = false;
  863. foreach($firstRecord as $value)
  864. {
  865. $key = $this->inArray($attributes,$value);
  866. if($key)
  867. {
  868. $result[] = $attributes[$key];
  869. $matchFound = true;
  870. }
  871. else
  872. {
  873. //checks the translation
  874. $key = $this->inArray($translations,$value);
  875. if($key!==false)
  876. {
  877. $result[] = $attributes[$key];
  878. $matchFound = true;
  879. }
  880. else
  881. $result[] = "-";
  882. }
  883. }
  884. return $result;
  885. }
  886. /**
  887. * Add the allField to the col_map array
  888. * but only if a valid field is selected
  889. * @param Array $col_map The map of columns (!stub)
  890. * @return mixed The value for the field to use with all records
  891. */
  892. function getAllFieldsValues(&$col_map)
  893. {
  894. $allFields = $this->m_postvars["allFields"];
  895. foreach ($allFields as $key=>$allField)
  896. {
  897. if ($allField!="")
  898. {
  899. $attr = &$this->getUsableAttribute($allField);
  900. if($attr)
  901. {
  902. $col_map[] = $allField;
  903. }
  904. //get the value from the postvars
  905. $allFieldValue = $this->m_postvars[$allField];
  906. if(strstr($allFieldValue,"="))
  907. {
  908. $allFieldValue = substr(strstr($allFieldValue,"="),2);
  909. $allFieldValue = substr($allFieldValue,0,atk_strlen($allFieldValue)-1);
  910. }
  911. $allFieldsValues[$allField] = $allFieldValue;
  912. }
  913. }
  914. return $allFieldsValues;
  915. }
  916. /**
  917. * The real import function actually imports the importfile
  918. *
  919. * @param Bool $nopost
  920. */
  921. function doImport($nopost=false)
  922. {
  923. ini_set('max_execution_time',300);
  924. $db = &$this->m_importNode->getDb();
  925. $fileid = $this->m_postvars["fileid"];
  926. $file = $this->getTmpFileDestination($fileid);
  927. $validated = $this->getValidatedRecords($file);
  928. if (!$this->m_postvars['novalidatefirst'] && $this->showErrors($validated['importerrors']))
  929. {
  930. $db->rollback();
  931. return;
  932. }
  933. $this->addRecords($validated['importerrors'], $validated['validatedrecs']);
  934. if (!$this->m_postvars['novalidatefirst'] && $this->showErrors($validated['importerrors']))
  935. {
  936. $db->rollback();
  937. return;
  938. }
  939. $db->commit();
  940. // clean-up
  941. @unlink($file);
  942. // clear recordlist cache
  943. $this->clearCache();
  944. // register message
  945. atkimport('atk.utils.atkmessagequeue');
  946. $messageQueue = &atkMessageQueue::getInstance();
  947. $count = count((array)$validated['validatedrecs']['add']) + count((array)$validated['validatedrecs']['update']);
  948. if ($count == 0)
  949. {
  950. $messageQueue->addMessage(sprintf($this->m_node->text('no_records_to_import'), $count), AMQ_GENERAL);
  951. }
  952. else if ($count == 1)
  953. {
  954. $messageQueue->addMessage($this->m_node->text('successfully_imported_one_record'), AMQ_SUCCESS);
  955. }
  956. else
  957. {
  958. $messageQueue->addMessage(sprintf($this->m_node->text('successfully_imported_x_records'), $count), AMQ_SUCCESS);
  959. }
  960. $this->m_node->redirect();
  961. }
  962. /**
  963. * Get the validated records
  964. *
  965. * @param String $file The import csv file
  966. * @return Array with importerrors and validatedrecs
  967. */
  968. function getValidatedRecords($file)
  969. {
  970. $enclosure = $this->m_postvars["enclosure"];
  971. $delimiter = $this->m_postvars["delimiter"];
  972. $columncount = $this->m_postvars["columncount"];
  973. $skipfirstrow = $this->m_postvars['skipfirstrow'];
  974. $allFields = $this->m_postvars["allFields"];
  975. $col_map = $this->m_postvars["col_map"];
  976. $allFieldsValues = $this->getAllFieldsValues($col_map);
  977. $initial_values = $this->m_importNode->initial_values();
  978. $validatedrecs = array();
  979. $validatedrecs["add"]=array();
  980. $validatedrecs["update"] = array();
  981. $importerrors = array();
  982. $importerrors[0]=array();
  983. $importerrors[0] = array_merge($importerrors[0],$this->checkImport($col_map,$initial_values));
  984. $allfielderror = $this->checkAllFields($allFields,$allFieldsValues);
  985. if ($allfielderror)
  986. {
  987. $importerrors[0][] = $allfielderror;
  988. }
  989. if (count($importerrors[0]) > 0)
  990. {
  991. // don't start importing if even the minimum requirements haven't been met
  992. return array('importerrors'=>&$importerrors, 'validatedrecs'=>array());
  993. }
  994. static $mb_converting_exists = null;
  995. if(!isset($mb_converting_exists))
  996. {
  997. $mb_converting_exists = function_exists("mb_convert_encoding");
  998. atkdebug('Checking function_exists("mb_convert_encoding")');
  999. }
  1000. static $atkCharset = null;
  1001. if(!isset($atkCharset))
  1002. {
  1003. $atkCharset = atkGetCharset();
  1004. atkdebug('setting atkcharset static!');
  1005. }
  1006. atkimport("atk.utils.atkstringparser");
  1007. //copy the csv in a record and add it to the db
  1008. $fp = fopen($file, "r");
  1009. if($skipfirstrow == "1") $line = fgets($fp);
  1010. for($line = fgets($fp),$counter=1; $line!==false; $line = fgets($fp),$counter++)
  1011. {
  1012. atkdebug("Validating record nr. $counter");
  1013. //if we have an empty line, pass it
  1014. if(trim($line)=="")
  1015. continue;
  1016. //large import are a problem for the maximum execution time, so we want to set for each
  1017. //loop of the for-loop an maximum execution time
  1018. set_time_limit(60);
  1019. atkdebug('set_time_limit(60)');
  1020. if ($atkCharset != '' && $mb_converting_exists) $line = mb_convert_encoding($line, $atkCharset);
  1021. $data = $this->fgetcsvfromline($line, $columncount, $delimiter, $enclosure);
  1022. $rec = $initial_values;
  1023. for ($i=0, $_i=count($col_map); $i<$_i; $i++)
  1024. {
  1025. if ($col_map[$i]!="-")
  1026. {
  1027. if(!in_array($col_map[$i],$allFields))// column is mapped
  1028. {
  1029. $value = $this->_getAttributeValue($col_map[$i], $allFields, $data[$i], $importerrors,$counter,$rec);
  1030. }
  1031. else //this is the allField
  1032. {
  1033. $value = $allFieldsValues[$col_map[$i]];
  1034. }
  1035. $this->addToRecord($rec,$col_map[$i],$value);
  1036. }
  1037. }
  1038. $this->validateRecord($rec, $validatedrecs, $importerrors, $counter);
  1039. }
  1040. // close file
  1041. @fclose($fp);
  1042. return array('importerrors'=>&$importerrors, 'validatedrecs'=>&$validatedrecs);
  1043. }
  1044. /**
  1045. * Gets the ATK value of the attribute
  1046. *
  1047. * @param String $attributename The name of the attribute
  1048. * @param Array $allFields Array with all the fields
  1049. * @param mixed $value The value from the CSV file
  1050. * @param Array &$importerrors Any import errors which may occur or may have occured
  1051. * @param Integer $counter The counter of the validatedrecords
  1052. * @param Array $rec The record
  1053. *
  1054. * @return mixed The ATK value of the field
  1055. */
  1056. function _getAttributeValue($attributename, $allFields, $value, &$importerrors, $counter, $rec)
  1057. {
  1058. $updatekey1 = $this->m_postvars['updatekey1'];
  1059. $attr = &$this->getUsableAttribute($attributename);
  1060. if (method_exists($attr, "createDestination") && $attr->createDestination() && !in_array($attributename,$allFields))
  1061. {
  1062. $primaryKeyAttr = $attr->m_destInstance->getAttribute($attr->m_destInstance->primaryKeyField());
  1063. $isNumeric = $attr->hasFlag(AF_AUTO_INCREMENT) || is_a($primaryKeyAttr, 'atknumberattribute');
  1064. $relationselect = array();
  1065. // this check only works if either the primary key column is non-numeric or the given value is numeric
  1066. if (!$isNumeric || is_numeric($value))
  1067. {
  1068. $relationselect = $attr->m_destInstance->selectDb($attr->m_destInstance->m_table.".".$attr->m_destInstance->primaryKeyField().' = \''.escapeSQL($value)."'");
  1069. }
  1070. if (count($relationselect)==0 || count($relationselect)>1)
  1071. {
  1072. static $searchresults = array();
  1073. if (!array_key_exists($attributename, $searchresults) || (array_key_exists($attributename, $searchresults) && !array_key_exists($value, $searchresults[$attributename])))
  1074. {
  1075. atkdebug("Caching attributeValue result for $attributename ($value)");
  1076. $searchresults[$attributename][$value] = $attr->m_destInstance->searchDb($value);
  1077. }
  1078. if (count($searchresults[$attributename][$value])==1)
  1079. {
  1080. $value = array($attr->m_destInstance->primaryKeyField()=>$searchresults[$attributename][$value][0][$attr->m_destInstance->primaryKeyField()]);
  1081. }
  1082. else
  1083. {
  1084. $relation = $this->isRelationAttribute($attributename);
  1085. if ($relation) $rec[$relation][$attributename] = $value;
  1086. else $rec[$attributename] = $value;
  1087. $importerrors[$counter][] = array("msg"=>atktext("error_formdataerror"),
  1088. "spec"=>sprintf(atktext("import_nonunique_identifier"), $this->getValueFromRecord($rec, $attributename)));
  1089. }
  1090. }
  1091. }
  1092. else if (is_object($attr) && method_exists($attr,"parseStringValue"))
  1093. {
  1094. $value = $attr->parseStringValue($value);
  1095. }
  1096. else
  1097. {
  1098. $value = trim($value);
  1099. }
  1100. return $value;
  1101. }
  1102. /**
  1103. * Determines wether or not errors occurred and shows the analyze screen if errors occurred.
  1104. * @param Array $importerrors An array with the errors that occurred
  1105. * @param Array $extraerror An extra error, if we found errors
  1106. * @return bool Wether or not errors occurred
  1107. */
  1108. function showErrors($importerrors, $extraerror=null)
  1109. {
  1110. foreach ($importerrors as $importerror)
  1111. {
  1112. if (is_array($importerror) && !empty($importerror[0]))
  1113. {
  1114. $errorfound=true;
  1115. }
  1116. }
  1117. if($errorfound)
  1118. {
  1119. if ($extraerror) $importerrors[0][] = $extraerror;
  1120. $this->doAnalyze($this->m_postvars["fileid"],$importerrors);
  1121. return true;
  1122. }
  1123. }
  1124. /**
  1125. * Adds the validated records but checks for errors first
  1126. *
  1127. * @param Array $importerrors Errors that occurred during validation of importfile
  1128. * @param Array $validatedrecs Records that were validated
  1129. */
  1130. function addRecords(&$importerrors, &$validatedrecs)
  1131. {
  1132. $counter=0;
  1133. foreach ($validatedrecs as $action=>$validrecs)
  1134. {
  1135. foreach ($validrecs as $validrec)
  1136. {
  1137. $counter++;
  1138. atkdebug("Doing $action for record nr $counter");
  1139. $this->$action($validrec);
  1140. if (!empty($validrec['atkerror']))
  1141. {
  1142. foreach ($validrec['atkerror'] as $atkerror)
  1143. {
  1144. $importerrors[$counter][] = array("msg"=>"Fouten gedetecteerd op rij $counter: ",
  1145. "spec"=>$atkerror['msg']);
  1146. }
  1147. }
  1148. unset($validrec);
  1149. }
  1150. unset($validrecs);
  1151. }
  1152. unset($validatedrecs);
  1153. }
  1154. /**
  1155. * Add a valid record to the db
  1156. * @param Array $record The record to add
  1157. * @return bool Wether or not there were errors
  1158. */
  1159. function add(&$record)
  1160. {
  1161. $this->m_importNode->preAdd($record);
  1162. if(isset($record['atkerror']))
  1163. {
  1164. return false;
  1165. }
  1166. $this->m_importNode->addDb($record);
  1167. if(isset($record['atkerror']))
  1168. {
  1169. return false;
  1170. }
  1171. return true;
  1172. }
  1173. /**
  1174. * Update a record in the db
  1175. * @param Array $record the record to update
  1176. * @return bool Wether or not there were errors
  1177. */
  1178. function update(&$record)
  1179. {
  1180. $this->m_importNode->preUpdate($record);
  1181. if(isset($record['atkerror']))
  1182. {
  1183. return false;
  1184. }
  1185. $this->m_importNode->updateDb($record);
  1186. if(isset($record['atkerror']))
  1187. {
  1188. return false;
  1189. }
  1190. return true;
  1191. }
  1192. /**
  1193. * Check whether the record if valide to import
  1194. * @param array $record the record
  1195. * @return bool Wether or not there were errors
  1196. */
  1197. function validate(&$record)
  1198. {
  1199. if ($this->m_postvars['doupdate']) $mode = "update";
  1200. else $mode = "add";
  1201. $this->m_importNode->validate($record,$mode);
  1202. foreach (array_keys($record) as $key)
  1203. {
  1204. $error = $error || (is_array($record[$key]) && array_key_exists('atkerror', $record[$key]) && count($record[$key]['atkerror']) > 0);
  1205. }
  1206. if(isset($error))
  1207. {
  1208. return false;
  1209. }
  1210. else
  1211. {
  1212. return true;
  1213. }
  1214. }
  1215. /**
  1216. * Checks the import by the col_map and the initial_values
  1217. * Check if all obligatory fields are used in the col_map or the initial_values
  1218. * Check if there are no fields used twice
  1219. *
  1220. * @param array $col_map The use of the fields for the columns in the csv
  1221. * @param array $initial_values The initial_values of the importnode
  1222. * @return array An array with errors, if there are any
  1223. */
  1224. function checkImport($col_map,$initial_values)
  1225. {
  1226. $errors = array();
  1227. //get the unused obligatory fields
  1228. $unused = array_values(array_diff($this->getObligatoryAttributes(),$col_map));
  1229. $this->_returnErrors(array_values(array_diff($unused,array_keys($initial_values))),"import_error_fieldisobligatory","import_error_fieldsareobligatory",$errors);
  1230. $this->_returnErrors($this->_getDuplicateColumns($col_map),"import_error_fieldusedtwice","import_error_fieldsusedtwice",$errors);
  1231. return $errors;
  1232. }
  1233. /**
  1234. * Checks if there are errors and if there are then it adds it to the collection
  1235. * @param Array $errors The errors to check
  1236. * @param String $singleerror The language code to use for a single error
  1237. * @param String $doubleerror The language code to use for multiple errors
  1238. * @param Array &$collection The collection of errors thus far
  1239. */
  1240. function _returnErrors($errors, $singleerror,$doubleerror, &$collection)
  1241. {
  1242. if(count($errors) > 0)
  1243. {
  1244. $msg = atktext((count($errors)==1)?$singleerror:$doubleerror).": ";
  1245. foreach ($errors as $key=>$field)
  1246. {
  1247. $attr = &$this->getUsableAttribute($field);
  1248. $errors[$key] = $attr->label();
  1249. }
  1250. $collection[] = array("msg"=>$msg,"spec"=>implode(", ",$errors));
  1251. }
  1252. }
  1253. /**
  1254. * Array with columns
  1255. * @param Array $array The array the columns to check
  1256. * @return Array The duplicate columns
  1257. */
  1258. function _getDuplicateColumns($array)
  1259. {
  1260. $result = array();
  1261. $frequencies = array_count_values($array);
  1262. foreach ($frequencies as $key=>$count)
  1263. {
  1264. if ($count>1 && $key!="-") $result[] = $key;
  1265. }
  1266. return $result;
  1267. }
  1268. /**
  1269. * Checks the allfield for correct data
  1270. * @param Array $fields The fields
  1271. * @param Array &$values The values of the fields
  1272. * @return Array An array with an error message, if an error occurred
  1273. */
  1274. function checkAllFields($fields, &$values)
  1275. {
  1276. foreach ($fields as $field)
  1277. {
  1278. $attr = &$this->getUsableAttribute($field);
  1279. if(!$attr)
  1280. return;
  1281. $record = array();
  1282. $this->addToRecord($record,$field,$values[$field]);
  1283. $result = $attr->display($record);
  1284. if(!$result)
  1285. if(in_array($field,$this->getObligatoryAttributes()))
  1286. {
  1287. return array('msg' => sprintf(atktext("import_error_allfieldnocorrectdata"),atktext($field,$this->m_node->m_module,$this->m_node->m_type),var_export($values[$field],1)));
  1288. }
  1289. else
  1290. {
  1291. $value = "";
  1292. }
  1293. }
  1294. return;
  1295. }
  1296. /**
  1297. * Validates a record
  1298. * @param Array &$rec The record to validate
  1299. * @param Array &$validatedrecs The records thus far validated
  1300. * @param Array &$importerrors The errors so far in the import process
  1301. * @param int $counter The number that the record is
  1302. */
  1303. function validateRecord(&$rec, &$validatedrecs, &$importerrors, $counter)
  1304. {
  1305. // Update variables
  1306. $doupdate = $this->m_postvars['doupdate'];
  1307. $updatekey1 = $this->m_postvars['updatekey1'];
  1308. $onfalseidentifier = $this->m_postvars['onfalseid'];
  1309. $errors=array();
  1310. if (!$this->validate($rec))
  1311. {
  1312. if ($rec['atkerror'][0])
  1313. foreach ($rec['atkerror'] as $atkerror)
  1314. {
  1315. $errors[] = $atkerror;
  1316. }
  1317. foreach (array_keys($rec) as $key)
  1318. {
  1319. if (is_array($rec[$key]) && array_key_exists('atkerror', $rec[$key]) && count($rec[$key]['atkerror']) > 0)
  1320. {
  1321. foreach ($rec[$key]['atkerror'] as $atkerror)
  1322. {
  1323. $errors[] = $atkerror;
  1324. }
  1325. }
  1326. }
  1327. if ($errors[0])
  1328. {
  1329. foreach ($errors as $error)
  1330. {
  1331. $attr = &$this->getUsableAttribute($error['attrib_name']);
  1332. $importerrors[$counter][] = array("msg"=>$error['msg'].": ",
  1333. "spec"=>$attr->

Large files files are truncated, but you can click here to view the full file