PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/common/lib/Form/Class.ImportView.inc.php

https://github.com/xrg/a2billing
PHP | 1061 lines | 904 code | 109 blank | 48 comment | 110 complexity | 577655ca8305145bad0d3184dad5a110 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. require_once("Class.FormViews.inc.php");
  3. /** Upload View! Set this up to enable importing CSV data into entity.
  4. */
  5. //TODO: use $form->prefix for fields
  6. //TODO: Locales!!
  7. class AskImportView extends FormView {
  8. public $common = array(); ///< Common fields, for which add-style elements appear
  9. public $mandatory = array(); ///< Mandatory fields to import
  10. public $optional = array(); ///< Optional fields
  11. public $multiple = array();
  12. public $distinct = true;
  13. public $multi_sep = '|';
  14. public $delimiter = ';';
  15. public $csvmode = true; ///< Defines some behaviour, like optional fields.
  16. public $fmtcomment; ///< The text describing the import format.
  17. public $bodyfield; ///< For mail mode, the body field
  18. function RenderHead(){
  19. ?>
  20. <script type="text/javascript">
  21. // <!--
  22. function MM_openBrWindow(theURL,winName,features) { //v2.0
  23. window.open(theURL,winName,features);
  24. }
  25. function importValidate(tform){
  26. if (tform.the_file.value.length < 2){
  27. alert ('<?= _("Please, you must first select a file !") ?>');
  28. tform.the_file.focus ();
  29. return false;
  30. }
  31. }
  32. //-->
  33. </script>
  34. <script language="JavaScript" type="text/javascript">
  35. <!--
  36. function deselectHeaders(tform)
  37. {
  38. // tform.unselected_search_sources[0].selected = false;
  39. // tform.prefs.selected_search_sources[0].selected = false;
  40. }
  41. function resetHidden(tform)
  42. {
  43. var tmp = '';
  44. for (i = 0; i < tform.selected_search_sources.length; i++) {
  45. tmp += tform.selected_search_sources[i].value;
  46. if (i < tform.selected_search_sources.length - 1)
  47. tmp += ":";
  48. }
  49. tform.search_sources.value = tmp;
  50. }
  51. function addSource(tform)
  52. {
  53. for (i = 0; i < tform.unselected_search_sources.length; i++) {
  54. if (tform.unselected_search_sources[i].selected) {
  55. tform.selected_search_sources[tform.selected_search_sources.length] = new Option(tform.unselected_search_sources[i].text, tform.unselected_search_sources[i].value);
  56. tform.unselected_search_sources[i] = null;
  57. i--;
  58. }
  59. }
  60. resetHidden(tform);
  61. }
  62. function removeSource(tform)
  63. {
  64. for (i = 0; i < tform.selected_search_sources.length; i++) {
  65. if (tform.selected_search_sources[i].selected) {
  66. tform.unselected_search_sources[tform.unselected_search_sources.length] = new Option(tform.selected_search_sources[i].text, tform.selected_search_sources[i].value)
  67. tform.selected_search_sources[i] = null;
  68. i--;
  69. }
  70. }
  71. resetHidden(tform);
  72. }
  73. function moveSourceUp(tform)
  74. {
  75. var sel = tform.selected_search_sources.selectedIndex;
  76. //var sel = tform["selected_search_sources[]"].selectedIndex;
  77. if (sel == -1 || tform.selected_search_sources.length <= 1) return;
  78. // deselect everything but the first selected item
  79. tform.selected_search_sources.selectedIndex = sel;
  80. if (sel == 1) {
  81. tmp = tform.selected_search_sources[sel];
  82. tform.selected_search_sources[sel] = null;
  83. tform.selected_search_sources[tform.selected_search_sources.length] = tmp;
  84. tform.selected_search_sources.selectedIndex = tform.selected_search_sources.length - 1;
  85. } else {
  86. tmp = new Array();
  87. for (i = 1; i < tform.selected_search_sources.length; i++) {
  88. tmp[i - 1] = new Option(tform.selected_search_sources[i].text, tform.selected_search_sources[i].value)
  89. }
  90. for (i = 0; i < tmp.length; i++) {
  91. if (i + 1 == sel - 1) {
  92. tform.selected_search_sources[i + 1] = tmp[i + 1];
  93. } else if (i + 1 == sel) {
  94. tform.selected_search_sources[i + 1] = tmp[i - 1];
  95. } else {
  96. tform.selected_search_sources[i + 1] = tmp[i];
  97. }
  98. }
  99. tform.selected_search_sources.selectedIndex = sel - 1;
  100. }
  101. resetHidden(tform);
  102. }
  103. function moveSourceDown(tform)
  104. {
  105. var sel = tform.selected_search_sources.selectedIndex;
  106. if (sel == -1 || tform.selected_search_sources.length <= 1) return;
  107. // deselect everything but the first selected item
  108. tform.selected_search_sources.selectedIndex = sel;
  109. if (sel == tform.selected_search_sources.length - 1) {
  110. tmp = new Array();
  111. for (i = 1; i < tform.selected_search_sources.length; i++) {
  112. tmp[i - 1] = new Option(tform.selected_search_sources[i].text, tform.selected_search_sources[i].value)
  113. }
  114. tform.selected_search_sources[1] = tmp[tmp.length - 1];
  115. for (i = 0; i < tmp.length - 1; i++) {
  116. tform.selected_search_sources[i + 2] = tmp[i];
  117. }
  118. tform.selected_search_sources.selectedIndex = 1;
  119. } else {
  120. tmp = new Array();
  121. for (i = 1; i < tform.selected_search_sources.length; i++) {
  122. tmp[i - 1] = new Option(tform.selected_search_sources[i].text, tform.selected_search_sources[i].value)
  123. }
  124. for (i = 0; i < tmp.length; i++) {
  125. if (i + 1 == sel) {
  126. tform.selected_search_sources[i + 1] = tmp[i + 1];
  127. } else if (i + 1 == sel + 1) {
  128. tform.selected_search_sources[i + 1] = tmp[i - 1];
  129. } else {
  130. tform.selected_search_sources[i + 1] = tmp[i];
  131. }
  132. }
  133. tform.selected_search_sources.selectedIndex = sel + 1;
  134. }
  135. resetHidden(tform);
  136. }
  137. // -->
  138. </script>
  139. <?php
  140. } //end function
  141. function Render(&$form){
  142. $this->RenderHead();
  143. if (empty($this->fmtcomment) &&($this->csvmode)){
  144. $this->fmtcomment = str_params( _("Use the example below to format the CSV file. Fields are separated by '%1' , </br>".
  145. ". and , are used for decimal format."),
  146. array($this->delimiter),1);
  147. }
  148. if ($this->csvmode)
  149. $impMsg= str_params(_("New %1 have to be imported from a CSV file."),
  150. array($form->model_name),1);
  151. else
  152. $impMsg= str_params(_("New %1 have to be imported from a file."),
  153. array($form->model_name),1);
  154. $fldIndex = array();
  155. foreach ($form->model as $key => $fld)
  156. if ($fld->fieldname)
  157. $fldIndex[$fld->fieldname]=$key;
  158. $formname = $form->prefix .'Imp' ;
  159. ?>
  160. <div class='impMsg'><?= $impMsg ?>
  161. </div>
  162. <table cellspacing="2" align="center" class="importForm">
  163. <form name="<?= $formname ?>" enctype="multipart/form-data" action="<?= $_SERVER['PHP_SELF']?>" method="post" onsubmit="return importValidate(this)">
  164. <input type="hidden" name="action" value="import-analyze">
  165. <?php if (count($this->common)){ ?>
  166. <tr> <td colspan="3" align=center class='title'> <?= _("Common fields");?></td></tr>
  167. <?php foreach($this->common as $fldname) {
  168. $fld = &$form->model[$fldIndex[$fldname]];
  169. ?><tr><td class='field'><?php $fld->RenderAddTitle($form) ?></td>
  170. <td colspan="2" class='value'><?php $fld->DispAdd($form);?> </td>
  171. </tr>
  172. <?php }
  173. }
  174. if (count($this->mandatory)){ ?>
  175. <tr> <td colspan="3" align=center class='title'> <?= _("Mandatory fields");?></td></tr>
  176. <?php foreach($this->mandatory as $fldname) {
  177. if ((!$this->csvmode) && $fldname == $this->bodyfield)
  178. continue;
  179. $fld = &$form->model[$fldIndex[$fldname]];
  180. ?><tr><td class='field'><?php $fld->RenderAddTitle($form) ?></td>
  181. <td class='value'><?php if (!$this->csvmode) echo ucfirst($fldname); ?></td>
  182. <td class='value'><div class="descr"><?= $fld->editDescr?></div></td>
  183. </tr>
  184. <?php }
  185. }
  186. if (count($this->optional)){ ?>
  187. <tr> <td colspan="3" align=center class='title'> <?= _("Optional fields");?></td></tr>
  188. <?php if ($this->csvmode) { ?>
  189. <tr><td colspan="3" align=center>
  190. <input name="search_sources" value="" type="hidden">
  191. <table><tbody><tr>
  192. <td><?php echo gettext("Unselected Fields...");?><br>
  193. <select name="unselected_search_sources" multiple="multiple" size="<?= count($this->optional)?>" width="50" onchange="deselectHeaders(document.forms['<?= $formname?>'])">
  194. <?php foreach($this->optional as $fldname) {
  195. $fld = &$form->model[$fldIndex[$fldname]]; ?>
  196. <option value="<?= $fldname ?>"><?= htmlspecialchars($fld->fieldtitle) ?></option><?php
  197. }
  198. ?>
  199. </select>
  200. </td>
  201. <td> <a onclick="addSource(document.forms['<?= $formname?>']); return false;"><img src="./Images/forward.png" alt="<?= _("Add field")?>" title="<?= _("Add field")?>" border="0"></a>
  202. <br>
  203. <a onclick="removeSource(document.forms['<?= $formname?>']); return false;"><img src="./Images/back.png" alt="<?= _("Remove field")?>" title="<?= _("Remove field")?>" border="0"></a>
  204. </td>
  205. <td>
  206. <?= _("Selected Fields...");?><br>
  207. <select name="selected_search_sources" multiple="multiple" size="<?= count($this->optional)?>" width="50" onchange="deselectHeaders(document.forms['<?= $formname?>']);">
  208. </select>
  209. </td>
  210. <td>
  211. <a onclick="moveSourceUp(document.forms['<?= $formname?>']); return false;"><img src="./Images/up_black.png" alt="<?= _("Move up")?>" title="<?= _("Move up")?>" border="0"></a>
  212. <br>
  213. <a onclick="moveSourceDown(document.forms['<?= $formname?>']); return false;"><img src="./Images/down_black.png" alt="<?= _("Move down")?>" title="<?= _("Move down")?>" border="0"></a>
  214. </td>
  215. </tr>
  216. </tbody></table>
  217. </td></tr>
  218. <?php }else { //non-csvmode
  219. foreach($this->optional as $fldname) {
  220. $fld = &$form->model[$fldIndex[$fldname]];
  221. ?><tr><td class='field'><?php $fld->RenderAddTitle($form) ?></td>
  222. <td class='value'><?php if (!$this->csvmode) echo ucfirst($fldname); ?></td>
  223. <td class='value'><div class="descr"><?= $fld->editDescr?></div></td>
  224. </tr>
  225. <?php }
  226. }
  227. }
  228. ?>
  229. <tr> <td colspan="3" align=center class='title'> <?= _("Upload File");?></td></tr>
  230. <tr> <td class="field"><?= _("File") ?></td>
  231. <td colspan="2" class="value">
  232. <p align="center"><span class="textcomment">
  233. <?= str_params(_("The maximum file size is %1 KB"),
  234. array($my_max_file_size / 1024),1) ?>
  235. </span><br>
  236. <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $my_max_file_size?>">
  237. <input type="hidden" name="task" value="upload">
  238. <input name="the_file" type="file" size="50" onFocus="this.select()" >
  239. </p>
  240. </td> </tr>
  241. <tr><td class="field"><?= _("Submit")?></td>
  242. <td class='value' colspan="2" align='right'>
  243. <input type="submit" value="<?= str_params(_("Import %1"),array($form->model_name),1) ?>" class="btnsubmit" >
  244. </td></tr>
  245. <?php if (count($this->examples)) {
  246. ?>
  247. <tr> <td colspan="3" align="center">&nbsp; </td> </tr>
  248. <tr> <td colspan="3" align=center class='title'> <?= _("Examples");?></td></tr>
  249. <tr> <td colspan="3">
  250. <div align="center"><span class="textcomment">
  251. <?= $this->fmtcomment ?>
  252. </span>
  253. <br/>
  254. <?php foreach ($this->examples as $exmpl){ ?>
  255. <a href="<?= $exmpl[1]?>" target="superframe"><?= $exmpl[0] ?></a> -
  256. <?php } ?>
  257. </div>
  258. <iframe name="superframe" src="<?= $this->examples[0][1]?>" bgcolor="white" width=500 height=120 marginWidth=10 marginHeight=10 frameBorder=1 scrolling="auto">
  259. </iframe>
  260. </td> </tr>
  261. <?php } ?>
  262. </form>
  263. </table>
  264. <?php
  265. }
  266. };
  267. /** This class retrieves the file and analyzes it */
  268. class ImportAView extends FormView {
  269. protected $askImport;
  270. protected $movedFile;
  271. protected $fields;
  272. public function ImportAView(AskImportView &$ai){
  273. $this->askImport = &$ai;
  274. }
  275. public function PerformAction(&$form){
  276. $dbg_elem = new DbgElem();
  277. $dbhandle = $form->a2billing->DBHandle();
  278. if ($form->FG_DEBUG>0)
  279. array_unshift($form->pre_elems,$dbg_elem);
  280. $dbg_elem->content .= print_r($_POST,true) . "\n";
  281. $fil = $_FILES[$form->prefix.'the_file'];
  282. if (!isset($fil)){
  283. $form->pre_elems[] = new ErrorElem(_("File has not been posted at all!"));
  284. $form->setAction('ask-import');
  285. return;
  286. }
  287. switch($fil['error']){
  288. case UPLOAD_ERR_OK:
  289. $dbg_elem->content .= "File uploaded OK.\n";
  290. break;
  291. case UPLOAD_ERR_INI_SIZE:
  292. $form->pre_elems[] = new ErrorElem(str_params(_("File size exceeds %1 limit of the system!"),array(123),1));
  293. $dbg_elem->content .="Error!\n";
  294. $form->setAction('ask-import');
  295. return;
  296. case UPLOAD_ERR_FORM_SIZE:
  297. $form->pre_elems[] = new ErrorElem(str_params(_("File size exceeds %1 limit for this action!"),array(123),1));
  298. $dbg_elem->content .="Error!\n";
  299. $form->setAction('ask-import');
  300. return;
  301. case UPLOAD_ERR_PARTIAL:
  302. $form->pre_elems[] = new ErrorElem(_("The uploaded file was only partially uploaded."));
  303. $dbg_elem->content .="Error!\n";
  304. $form->setAction('ask-import');
  305. return;
  306. case UPLOAD_ERR_NO_FILE:
  307. $form->pre_elems[] = new ErrorElem(_("No file was uploaded."));
  308. $dbg_elem->content .="Error!\n";
  309. $form->setAction('ask-import');
  310. return;
  311. case UPLOAD_ERR_NO_TMP_DIR:
  312. $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  313. $dbg_elem->content .="Missing a temporary folder. \n";
  314. $form->setAction('ask-import');
  315. return;
  316. case UPLOAD_ERR_CANT_WRITE:
  317. $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  318. $dbg_elem->content .="Failed to write file to disk.\n";
  319. $form->setAction('ask-import');
  320. return;
  321. default:
  322. $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  323. $dbg_elem->content .="Unknown error:".$fil['error']. "\n";
  324. $form->setAction('ask-import');
  325. return;
  326. }
  327. if (isset($this->allowed_mimetypes) && !in_array($fil['type'],$this->allowed_mimetypes)){
  328. $form->pre_elems[] = new ErrorElem(str_params(_("Cannot accept file of type %1. Allowed types are: %2."),
  329. array($fil['type'],implode(', ',$this->allowed_mimetypes)),1));
  330. $dbg_elem->content .="Cannot accept file type \n";
  331. $form->setAction('ask-import');
  332. return;
  333. }
  334. //Now, check the given fields
  335. $this->fields = $this->askImport->mandatory;
  336. if($this->askImport->csvmode){
  337. $optionals= explode(':',$form->getpost_dirty('search_sources'));
  338. foreach ($optionals as $opt)
  339. if (!empty($opt))
  340. if (!in_array($opt,$this->askImport->optional)){
  341. $form->pre_elems[] = new ErrorElem(_("Error in submitted form."));
  342. $dbg_elem->content .= "You tried to pass $opt as a field.\n";
  343. $form->setAction('ask-import');
  344. return;
  345. }else
  346. $this->fields[] = $opt;
  347. }
  348. $tmpdir = DynConf::GetCfg('global','upload_tmpdir','/tmp');
  349. $tmpname = str_replace('/','-',basename($fil['name']));
  350. $tmpname = tempnam($tmpdir,$tmpname);
  351. if ($tmpname ===false) {
  352. $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  353. $dbg_elem->content .="Cannot make temp file in $tmpdir \n";
  354. $form->setAction('ask-import');
  355. return;
  356. }
  357. if (move_uploaded_file($fil['tmp_name'],$tmpname)){
  358. $dbg_elem->content .="moved \"".$fil['tmp_name'] ."\" to \"". $tmpname ."\"\n";
  359. $this->movedFile = $tmpname;
  360. }else {
  361. $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  362. $dbg_elem->content .="Cannot move uploaded file to temporary. \n";
  363. $form->setAction('ask-import');
  364. return;
  365. }
  366. // $dbg_elem->content .="copying data from \"".$fil['tmp_name'] ."\" to \"". $tmpname ."\"\n";
  367. // $this->tmpFile = @fopen($fil['tmp_name'],'rb');
  368. // if ($this->tmpFile === false){
  369. // $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  370. // $dbg_elem->content .="Cannot open uploaded file. \n";
  371. // $form->setAction('ask-import');
  372. // return;
  373. // }
  374. //
  375. // $this->copyFile = fopen($tmpname,'w');
  376. // if ($this->copyFile === false){
  377. // $form->pre_elems[] = new ErrorElem(_("Internal error, could not upload."));
  378. // $dbg_elem->content .="Cannot open copy file. \n";
  379. // $form->setAction('ask-import');
  380. // return;
  381. // }
  382. }
  383. public function Render(&$form){
  384. ?><div class='impA-progress' name="<?= $form->prefix?>iprogress">
  385. <?= _("Analyzing uploaded data...") ?>
  386. <div>
  387. <?php
  388. $fp = fopen($this->movedFile, "r");
  389. if (!$fp){
  390. ?><div class="error">
  391. <?= _("Error: Cannot open uploaded file") ?>
  392. </div>
  393. <?php
  394. return;
  395. }
  396. // Construct an array of the fields to be imported,
  397. // structure: $fields2[<num>] = array(<name>,<key>,<bool:!aggregate>)
  398. $fldIndex = array();
  399. foreach ($form->model as $key => $fld)
  400. if ($fld->fieldname)
  401. $fldIndex[$fld->fieldname]=$key;
  402. $fields2= array();
  403. foreach($this->fields as $fld)
  404. if (in_array($fld,$this->askImport->multiple))
  405. $fields2[] = array($fld,$fldIndex[$fld],false);
  406. else
  407. $fields2[] = array($fld,$fldIndex[$fld],true);
  408. unset($fldIndex);
  409. ?> <table cellSpacing="2" align='center' class="<?= $form->list_class?>">
  410. <?php
  411. $this->RenderListHeads($fields2,$form);
  412. // echo nl2br(print_r($fields2,true));
  413. $nrows = 0;
  414. $delimiter = $this->askImport->delimiter;
  415. $multi_sep = $this->askImport->multi_sep;
  416. $max_rows = 10; //TODO
  417. $last_a = null;
  418. $last_b = null;
  419. ?><tbody>
  420. <?php
  421. while (($larr = fgetcsv($fp,4096,$delimiter))!==false){
  422. if ($larr===null)
  423. continue;
  424. if(count($larr)<count($fields2)){
  425. if ($form->FG_DEBUG)
  426. echo "Less fields came!<br>\n";
  427. if ($form->FG_DEBUG>2)
  428. echo nl2br(print_r($larr,true). "\n");
  429. break;
  430. }
  431. $arr_a = array();
  432. $arr_b = array();
  433. //split the data into 2 arrays.
  434. foreach($fields2 as $fld){
  435. $val = $form->model[$fld[1]]->buildValue(current($larr),$form);
  436. if($fld[2])
  437. $arr_a[] = $val;
  438. else
  439. $arr_b[] = $val;
  440. next($larr);
  441. }
  442. //If non-multiple data matches, we reuse the line
  443. if ($this->askImport->distinct && ($arr_a == $last_a)){
  444. foreach($arr_b as $kb => $b)
  445. $last_b[$kb] = array_merge($last_b[$kb],
  446. explode($multi_sep, $b));
  447. }
  448. else{
  449. if (!empty($last_a))
  450. $this->RenderRow($fields2,$last_a,$last_b,$form);
  451. $last_a = $arr_a;
  452. $last_b = array();
  453. foreach($arr_b as $b)
  454. $last_b[] =explode($multi_sep,$b);
  455. $nrows++;
  456. }
  457. if ($nrows > $max_rows)
  458. break;
  459. // Will actually skip the latest $last_a
  460. }
  461. ?></tbody> </table>
  462. <form action=<?= $_SERVER['PHP_SELF']?> method=post name="<?= $form->prefix?>Imp" id="<?= $form->prefix ?>Imp">
  463. <?php // The uploaded file should never be revealed to the client. Thus, we keep that
  464. // in _SESSION.
  465. $_SESSION[$form->prefix.'importFile'] = $this->movedFile;
  466. $_SESSION[$form->prefix.'importFields'] = implode(',',$this->fields);
  467. // Also, protect against multiple uploads in the same session
  468. $str ='';
  469. for ($i=0;$i<6;$i++)
  470. $str .= mt_rand(0,9);
  471. $_SESSION[$form->prefix.'importRnd'] = $str;
  472. $hidden_arr = array( 'action' => 'import', 'sub_action' => '', 'rnd' => $str);
  473. foreach ($this->askImport->common as $co)
  474. $hidden_arr[$co] = $form->getpost_dirty($co);
  475. if (strlen($form->prefix)>0){
  476. $arr2= array();
  477. foreach($hidden_arr as $key => $val)
  478. $arr2[$form->prefix.$key] = $val;
  479. $hidden_arr = $arr2;
  480. }
  481. $form->gen_PostParams($hidden_arr,true);
  482. ?>
  483. <button type=submit>
  484. <?= str_params(_("Import these %1"),array($form->model_name),1) ?>
  485. <img src="./Images/icon_arrow_orange.png" ></button>
  486. </form>
  487. <?php
  488. fclose($fp);
  489. }
  490. protected function RenderListHeads($fields2,$form){
  491. ?> <thead><tr> <?php
  492. foreach ($fields2 as $fld){
  493. $mfld = &$form->model[$fld[1]];
  494. echo "<td";
  495. if ($mfld->listWidth)
  496. echo ' width="'.$mfld->listWidth .'"';
  497. echo '>';
  498. if ($mfld->fieldacr){
  499. echo '<acronym title="'.htmlspecialchars($mfld->fieldtitle).'" >';
  500. echo htmlspecialchars($mfld->fieldacr);
  501. echo '<acronym>';
  502. }else
  503. echo htmlspecialchars($mfld->fieldtitle);
  504. echo "</td>";
  505. }
  506. ?></tr></thead> <?php
  507. }
  508. protected function RenderRow($fields2,$last_a,$last_b,&$form){
  509. reset($last_a);
  510. reset($last_b);
  511. echo "<tr>";
  512. foreach($fields2 as $fld){
  513. echo "<td>";
  514. $mfld = &$form->model[$fld[1]];
  515. if ($fld[2]){
  516. echo htmlspecialchars(current($last_a));
  517. next($last_a);
  518. }else{
  519. echo htmlspecialchars(implode(', ',current($last_b)));
  520. next($last_b);
  521. }
  522. echo "</td>";
  523. }
  524. echo "</tr>\n";
  525. }
  526. };
  527. /** Import a file in mail-like format */
  528. class ImportMailAView extends ImportAView {
  529. public $comment_char = '#';
  530. public $delim_line = '-------- Mail --------';
  531. public function Render(&$form){
  532. ?><div class='impA-progress' name="<?= $form->prefix?>iprogress">
  533. <?= _("Analyzing uploaded data...") ?>
  534. <div>
  535. <?php
  536. $fp = fopen($this->movedFile, "r");
  537. if (!$fp){
  538. ?><div class="error">
  539. <?= _("Error: Cannot open uploaded file") ?>
  540. </div>
  541. <?php
  542. return;
  543. }
  544. $all_fields = array();
  545. foreach ($this->askImport->mandatory as $fld)
  546. $all_fields[ucfirst($fld)] = $fld;
  547. foreach ($this->askImport->optional as $fld)
  548. $all_fields[ucfirst($fld)] = $fld;
  549. // echo nl2br(print_r($fields2,true));
  550. $nrows = 0;
  551. $commentc = $this->comment_char;
  552. $comment_len = strlen($this->comment_char);
  553. //$multi_sep = $this->askImport->multi_sep;
  554. $max_rows = 10; //TODO
  555. $bodyfld=$this->askImport->bodyfield;
  556. if (empty($bodyfld))
  557. $bodyfld="message";
  558. if (!feof($fp)){ // only one, not "while"
  559. $temail=array();
  560. // Sub-loop: get headers
  561. while (!feof($fp)){
  562. $line =fgets($fp);
  563. if (! $line)
  564. break;
  565. $line2=trim($line); // but also leave $line intact
  566. if (substr($line2,0,$comment_len)== $commentc)
  567. continue;
  568. if (($pos=strpos($line2,':'))===false)
  569. break;
  570. $fld = substr($line2,0,$pos);
  571. if (!isset($all_fields[$fld])){
  572. if ($form->FG_DEBUG>1)
  573. echo "Skipping tag \"$fld\"<br>\n";
  574. continue;
  575. }
  576. $temail[$all_fields[$fld]] = substr($line2,$pos+1);
  577. }
  578. // skip the first line of the message, if it's whitespace
  579. if ($line && trim($line)=="")
  580. $line="";
  581. $temail[$bodyfld] = "";
  582. // Second loop: message body
  583. do{
  584. if ($line == '')
  585. continue;
  586. if ($line == $this->delim_line."\n")
  587. break;
  588. $temail[$bodyfld] .=$line;
  589. }while (($line = fgets($fp))!==false);
  590. if ($form->FG_DEBUG>2)
  591. echo "Got one mail!\n";
  592. $this->RenderMail($temail,$form);
  593. }
  594. ?>
  595. <form action=<?= $_SERVER['PHP_SELF']?> method=post name="<?= $form->prefix?>Imp" id="<?= $form->prefix ?>Imp">
  596. <?php // The uploaded file should never be revealed to the client. Thus, we keep that
  597. // in _SESSION.
  598. $_SESSION[$form->prefix.'importFile'] = $this->movedFile;
  599. // Also, protect against multiple uploads in the same session
  600. $str ='';
  601. for ($i=0;$i<6;$i++)
  602. $str .= mt_rand(0,9);
  603. $_SESSION[$form->prefix.'importRnd'] = $str;
  604. $hidden_arr = array( 'action' => 'import', 'sub_action' => '', 'rnd' => $str);
  605. foreach ($this->askImport->common as $co)
  606. $hidden_arr[$co] = $form->getpost_dirty($co);
  607. if (strlen($form->prefix)>0){
  608. $arr2= array();
  609. foreach($hidden_arr as $key => $val)
  610. $arr2[$form->prefix.$key] = $val;
  611. $hidden_arr = $arr2;
  612. }
  613. $form->gen_PostParams($hidden_arr,true);
  614. ?>
  615. <button type=submit>
  616. <?= str_params(_("Import these %1"),array($form->model_name),1) ?>
  617. <img src="./Images/icon_arrow_orange.png" ></button>
  618. </form>
  619. <?php
  620. fclose($fp);
  621. }
  622. public function RenderMail($temail,$form){
  623. $fldIndex = array();
  624. foreach ($form->model as $key => $fld)
  625. if ($fld->fieldname)
  626. $fldIndex[$fld->fieldname]=$key;
  627. $bodyfld=$this->askImport->bodyfield;
  628. if (empty($bodyfld))
  629. $bodyfld="message";
  630. ?>
  631. <table cellspacing="2" align="center" class="importMailForm">
  632. <tbody>
  633. <tr><td colspan="3" align=center class='title'>&nbsp;&nbsp;</td><tr>
  634. <?php
  635. foreach($this->askImport->mandatory as $fld){
  636. if ($fld == $bodyfld)
  637. continue;
  638. ?>
  639. <tr><td class="field"><?php
  640. $form->model[$fldIndex[$fld]]->RenderAddTitle($form);
  641. ?></td><td class="value"><?php
  642. if(isset($temail[$fld]))
  643. echo htmlspecialchars($temail[$fld]);
  644. ?></td></tr>
  645. <?php
  646. }
  647. ?>
  648. <tr><td class="field">&nbsp;</td><td class= "value">&nbsp;</td></tr>
  649. <?php
  650. foreach($this->askImport->optional as $fld){
  651. if ($fld == $bodyfld)
  652. continue;
  653. ?>
  654. <tr><td class="field"><?php
  655. $form->model[$fldIndex[$fld]]->RenderAddTitle($form);
  656. ?></td><td class="value"><?php
  657. if(isset($temail[$fld]))
  658. $form->model[$fldIndex[$fld]]->DispList($temail,$form);
  659. ?></td></tr>
  660. <?php
  661. }
  662. ?>
  663. <tr><td colspan="3" align=center class='title'><?= $form->model[$fldIndex[$bodyfld]]->fieldtitle ?></td></tr>
  664. <tr><td class="message" colspan="2">
  665. <?= nl2br(htmlspecialchars($temail[$bodyfld])) ?>
  666. </td></tr></tbody>
  667. </table>
  668. <?
  669. }
  670. };
  671. /** This class performs the SQL import */
  672. class ImportView extends FormView {
  673. protected $askImport;
  674. protected $movedFile;
  675. public function ImportView(AskImportView &$ai){
  676. $this->askImport = &$ai;
  677. }
  678. public function PerformAction(&$form){
  679. $dbg_elem = new DbgElem();
  680. if ($form->FG_DEBUG>0)
  681. array_unshift($form->pre_elems,$dbg_elem);
  682. $dbg_elem->content .= print_r($_POST,true) . "\n";
  683. if ((!isset($_SESSION[$form->prefix.'importRnd'])) ||
  684. ($_SESSION[$form->prefix.'importRnd'] != $form->getpost_single('rnd'))){
  685. $form->pre_elems[] = new ErrorElem(_("Session Error, cannot import!"));
  686. $dbg_elem->content .="Random didn't match!\n";
  687. if ($form->FG_DEBUG>3) $dbg_elem->content .= print_r($_SESSION,true);
  688. $form->setAction('idle');
  689. return;
  690. }
  691. if (!isset($_SESSION[$form->prefix.'importFile']) ||
  692. !is_readable($_SESSION[$form->prefix.'importFile'])){
  693. $form->pre_elems[] = new ErrorElem(_("Session Error, file vanished!"));
  694. $dbg_elem->content .="Cannot read ".$_SESSION[$form->prefix.'importFile'] ." \n";
  695. $form->setAction('idle');
  696. return;
  697. }
  698. $dbg_elem->content .="Ready to open ".$_SESSION[$form->prefix.'importFile'] ." \n";
  699. $this->movedFile= $_SESSION[$form->prefix.'importFile'];
  700. if (!isset($_SESSION[$form->prefix.'importFields'])) {
  701. $form->pre_elems[] = new ErrorElem(_("Session Error !"));
  702. $dbg_elem->content .="Fields vanished from session. \n";
  703. $form->setAction('idle');
  704. return;
  705. }
  706. }
  707. public function Render(&$form){
  708. $dbhandle = $form->a2billing->DBHandle();
  709. $fldIndex = array();
  710. ?><div class='impA-progress' name="<?= $form->prefix?>iprogress">
  711. <?= _("Importing uploaded data...") ?>
  712. <span name="<?= $form->prefix?>icount"> </span>
  713. <div>
  714. <?php
  715. // Construct, again, the list of fields
  716. foreach ($form->model as $key => $fld)
  717. if ($fld->fieldname)
  718. $fldIndex[$fld->fieldname]=$key;
  719. $fields2= array();
  720. $returning = array();
  721. $fields = explode(',',$_SESSION[$form->prefix.'importFields']);
  722. foreach($fields as $fld){
  723. $retk = null;
  724. $ext = false;
  725. //does it aggregate over CSV rows?
  726. $aggr = in_array($fld,$this->askImport->multiple);
  727. // does it belong to the primary INSERT or to
  728. // some subsequent?
  729. if ($form->model[$fldIndex[$fld]] instanceof RevRef){
  730. $ext = true;
  731. $retk = $form->model[$fldIndex[$fld]]->localkey;
  732. }
  733. $fields2[] = array($fld,$fldIndex[$fld],$aggr,$ext, $retk);
  734. }
  735. unset($fields);
  736. if ($form->FG_DEBUG >4) {
  737. echo nl2br(htmlspecialchars(print_r($fields2,true)));
  738. echo "<br>\n";
  739. }
  740. // Build primary INSERT
  741. $ins_keys = array();
  742. //$ins_values = array();
  743. $ins_qm = array();
  744. $ins_returning = array();
  745. // Find
  746. foreach($this->askImport->common as $fld){
  747. $ins_keys[] = $fld;
  748. $ins_qm[] = str_dbparams($dbhandle, "%!1",
  749. array( $form->model[$fldIndex[$fld]]->
  750. buildValue( $form->getpost_dirty($fld),$form)));
  751. }
  752. foreach($fields2 as $fld)
  753. if (!$fld[3]){
  754. $ins_keys[]=$fld[0];
  755. $ins_qm[] = '?';
  756. }else
  757. $ins_returning[] = $fld[4];
  758. $insert_pri = "INSERT INTO ". $form->model_table ."(" .
  759. implode(', ',$ins_keys) . ") VALUES(".
  760. implode(',', $ins_qm).")";
  761. if (count($ins_returning))
  762. $insert_pri .= " RETURNING ".implode(', ',$ins_returning);
  763. $insert_pri .=";";
  764. if ($form->FG_DEBUG >1) {
  765. echo "Insert query: ". htmlspecialchars($insert_pri) . "<br>\n";
  766. }
  767. $fp = fopen($this->movedFile, "r");
  768. if (!$fp){
  769. ?><div class="error">
  770. <?= _("Error: Cannot open uploaded file") ?>
  771. </div>
  772. <?php
  773. return;
  774. }
  775. $nrows = 0;
  776. $nlines = 0;
  777. $delimiter = $this->askImport->delimiter;
  778. $multi_sep = $this->askImport->multi_sep;
  779. $last_a = null;
  780. $reted = null;
  781. // $last_b = null;
  782. //Everything must be in one transaction, to avoid partially imported
  783. //data
  784. $dbhandle->StartTrans();
  785. // The actual import loop!
  786. while (($larr = fgetcsv($fp,4096,$delimiter))!==false){
  787. if ($larr===null)
  788. continue;
  789. if(count($larr)<count($fields2)){
  790. if ($form->FG_DEBUG)
  791. echo "Less fields came!<br>\n";
  792. if ($form->FG_DEBUG>2)
  793. echo nl2br(print_r($larr,true). "\n");
  794. $dbhandle->FailTrans();
  795. break;
  796. }
  797. $nlines++;
  798. $arr_a = array();
  799. $arr_b = array();
  800. $arr_ext = array();
  801. //split the data into 2 arrays.
  802. foreach($fields2 as $fld){
  803. $val = $form->model[$fld[1]]->buildValue(current($larr),$form);
  804. if(!$fld[2])
  805. $arr_a[] = $val;
  806. else if (!$fld[3])
  807. $arr_b[] = $val;
  808. else
  809. $arr_c[$fld[0]] =$val;
  810. next($larr);
  811. }
  812. //If non-multiple data matches, we reuse the line
  813. if ($this->askImport->distinct && ($arr_a == $last_a)){
  814. }
  815. else{
  816. if ($form->FG_DEBUG>2 && ($nrows<100)) {
  817. echo "Data:" . htmlspecialchars(implode(', ',$arr_a)) . "<br>\n";
  818. }
  819. $res = $dbhandle->Execute($insert_pri,$arr_a);
  820. if(!$res){ ?>
  821. <div class="error">
  822. <?= _("Database error, cannot import!"); ?>
  823. </div>
  824. <?php if ($form->FG_DEBUG) {
  825. echo $dbhandle->ErrorMsg();
  826. echo "<br>\n";
  827. }
  828. $dbhandle->FailTrans();
  829. return;
  830. }elseif(count($ins_returning) && $res->EOF){ ?>
  831. <div class="error">
  832. <?= _("Database error, rows not imported!"); ?>
  833. </div>
  834. <?php if ($form->FG_DEBUG) {
  835. echo "No result from insert operation!";
  836. echo "<br>\n";
  837. }
  838. $dbhandle->FailTrans();
  839. return;
  840. }else
  841. $reted = $res->fetchRow();
  842. if ($form->FG_DEBUG && !$res->EOF)
  843. echo "Second result after INSERT? weird..<br>\n";
  844. if ($form->FG_DEBUG>2 && ($nlines <10) && count($ins_returning))
  845. echo "Returned: " . print_r($reted,true) . "<br>\n";
  846. $last_a = $arr_a;
  847. $nrows++;
  848. }
  849. if (count($arr_c))
  850. foreach ($fields2 as $fld) {
  851. if(!$fld[3])
  852. continue;
  853. $mfld = &$form->model[$fld[1]];
  854. $cqry = "INSERT INTO $mfld->reftable ($mfld->refid, $mfld->refname) VALUES ";
  855. $data = explode($multi_sep,$arr_c[$fld[0]]);
  856. if (! count($data)) continue;
  857. $cqry_val = array();
  858. foreach($data as $dat)
  859. $cqry_val[] = str_dbparams($dbhandle,"(%1, %2)",array($reted[$mfld->localkey],$dat));
  860. $cqry .= implode(",\n",$cqry_val) . ";";
  861. if ($form->FG_DEBUG>2 && ($nlines<100)) {
  862. echo "Extra: " . htmlspecialchars($cqry) ."<br>\n";
  863. }
  864. $res = $dbhandle->Execute($cqry);
  865. if (!$res){ ?>
  866. <div class="error">
  867. <?= _("Database error, secondary rows not imported!"); ?>
  868. </div>
  869. <?php if ($form->FG_DEBUG) {
  870. if ($form->FG_DEBUG>2)
  871. echo "Query: " . htmlspecialchars($cqry) ."<br>\n";
  872. echo $dbhandle->ErrorMsg();
  873. echo "<br>\n";
  874. }
  875. $dbhandle->FailTrans();
  876. return;
  877. }
  878. }
  879. if (($nlines %1000) == 0) {
  880. // reset the timer and give us another 20sec
  881. set_time_limit(20);
  882. if ($form->FG_DEBUG>1)
  883. echo "Rows found: $nrows<br>\n";
  884. ?>
  885. <script language="JavaScript" type="text/javascript">
  886. document.getElementsByName("<?= $form->prefix?>icount")[0].innerHTML = "<?=
  887. str_params(_("%1 lines processed: %2 rows"), array($nlines,$nrows),1) ?>";
  888. window.status = "<?=
  889. str_params(_("%1 lines processed: %2 rows"), array($nlines,$nrows),1) ?>";
  890. </script>
  891. <?php
  892. @ob_end_flush(); // Make sure we flush the http data
  893. }
  894. } //while fgets
  895. if ($dbhandle->CompleteTrans() ){
  896. ?>
  897. <script language="JavaScript" type="text/javascript">
  898. document.getElementsByName("<?= $form->prefix?>icount")[0].innerHTML = "<?=
  899. str_params(_("%1 lines processed: %2 rows"), array($nlines,$nrows),1) ?>";
  900. window.status = "<?=
  901. str_params(_("%1 lines processed: %2 rows"), array($nlines,$nrows),1) ?>";
  902. </script>
  903. <?php
  904. }
  905. else{
  906. echo _("Import of data aborted.");
  907. echo "<br>\n";
  908. }
  909. unset($_SESSION[$form->prefix.'importFile']);
  910. unset($_SESSION[$form->prefix.'importFields']);
  911. unset($_SESSION[$form->prefix.'importRnd']);
  912. @unlink($this->movedFile);
  913. } // Render
  914. };
  915. ?>