PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/web/admin/importcsv.php

https://bitbucket.org/yoander/mtrack
PHP | 326 lines | 282 code | 44 blank | 0 comment | 43 complexity | e8876ee907f4395fca622466948ac12e MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. <?php # vim:ts=2:sw=2:et:
  2. include '../../inc/common.php';
  3. MTrackACL::requireAllRights("Tickets", 'create');
  4. session_start();
  5. $field_aliases = array(
  6. 'state' => 'status',
  7. 'pri' => 'priority',
  8. 'id' => 'ticket',
  9. 'type' => 'classification',
  10. );
  11. $supported_fields = array(
  12. 'classification',
  13. 'ticket',
  14. 'milestone',
  15. '-milestone',
  16. '+milestone',
  17. 'summary',
  18. 'status',
  19. 'priority',
  20. 'estimated',
  21. 'owner',
  22. 'type',
  23. 'component',
  24. '-component',
  25. '+component',
  26. 'description'
  27. );
  28. foreach ($supported_fields as $i => $f) {
  29. unset($supported_fields[$i]);
  30. $supported_fields[$f] = $f;
  31. }
  32. $C = MTrackTicket_CustomFields::getInstance();
  33. foreach ($C->getFields() as $f) {
  34. $name = substr($f->name, 2);
  35. $supported_fields[$f->name] = $f->name;
  36. if (!isset($field_aliases[$name])) {
  37. $field_aliases[$name] = $f->name;
  38. }
  39. }
  40. if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  41. if (isset($_FILES['csvfile']) && $_FILES['csvfile']['error'] == 0
  42. && is_uploaded_file($_FILES['csvfile']['tmp_name'])) {
  43. ini_set('auto_detect_line_endings', true);
  44. $fp = fopen($_FILES['csvfile']['tmp_name'], 'r');
  45. $header = fgetcsv($fp);
  46. $err = array();
  47. $output = array();
  48. foreach ($header as $i => $name) {
  49. $name = strtolower($name);
  50. if (isset($field_aliases[$name])) {
  51. $name = $field_aliases[$name];
  52. }
  53. if (!isset($supported_fields[$name])) {
  54. $err[] = "Unsupported field: $name";
  55. }
  56. $header[$i] = $name;
  57. }
  58. $db = MTrackDB::get();
  59. $db->beginTransaction();
  60. MTrackChangeset::$use_txn = false;
  61. $todo = array();
  62. do {
  63. $line = fgetcsv($fp);
  64. if ($line === false) break;
  65. $item = array();
  66. foreach ($header as $i => $name) {
  67. $item[$name] = $line[$i];
  68. }
  69. if (isset($item['ticket']) && strlen($item['ticket'])) {
  70. $id = $item['ticket'];
  71. if ($id[0] == '#') {
  72. $id = substr($id, 1);
  73. }
  74. try {
  75. $tkt = MTrackIssue::loadByNSIdent($id);
  76. if ($tkt == null) {
  77. $err[] = "No such ticket $id";
  78. continue;
  79. }
  80. } catch (Exception $e) {
  81. $err[] = $e->getMessage();
  82. continue;
  83. }
  84. $output[] = "<b>Updating ticket $tkt->nsident</b><br>\n";
  85. } else {
  86. $tkt = new MTrackIssue;
  87. $tkt->priority = 'normal';
  88. $tkt->get_next_nsident();
  89. $output[] = "<b>Creating ticket $tkt->nsident<b><br>\n";
  90. }
  91. $CS = MTrackChangeset::begin("ticket:X", $_POST['comment']);
  92. if (strlen(trim($_POST['comment']))) {
  93. $tkt->addComment($_POST['comment']);
  94. }
  95. foreach ($item as $name => $value) {
  96. if ($name == 'ticket') {
  97. continue;
  98. }
  99. $output[] = "$name => $value<br>\n";
  100. try {
  101. switch ($name) {
  102. case 'summary':
  103. case 'description':
  104. case 'classification':
  105. case 'priority':
  106. case 'severity':
  107. case 'changelog':
  108. case 'owner':
  109. case 'cc':
  110. case 'estimated':
  111. case 'status':
  112. $tkt->$name = strlen($value) ? $value : null;
  113. break;
  114. case 'milestone':
  115. if (strlen($value)) {
  116. foreach ($tkt->getMilestones() as $mid) {
  117. $tkt->dissocMilestone($mid);
  118. }
  119. $tkt->assocMilestone($value);
  120. }
  121. break;
  122. case '+milestone':
  123. if (strlen($value)) {
  124. $tkt->assocMilestone($value);
  125. }
  126. break;
  127. case '-milestone':
  128. if (strlen($value)) {
  129. $tkt->dissocMilestone($value);
  130. }
  131. break;
  132. case 'component':
  133. if (strlen($value)) {
  134. foreach ($tkt->getComponents() as $mid) {
  135. $tkt->dissocComponent($mid);
  136. }
  137. $tkt->assocComponent($value);
  138. }
  139. break;
  140. case '+component':
  141. if (strlen($value)) {
  142. $tkt->assocComponent($value);
  143. }
  144. break;
  145. case '-component':
  146. if (strlen($value)) {
  147. $tkt->dissocComponent($value);
  148. }
  149. break;
  150. default:
  151. if (!strncmp($name, 'x_', 2)) {
  152. $tkt->{$name} = $value;
  153. }
  154. break;
  155. }
  156. } catch (Exception $e) {
  157. $err[] = $e->getMessage();
  158. }
  159. }
  160. $tkt->save($CS);
  161. $CS->setObject("ticket:" . $tkt->tid);
  162. } while (true);
  163. $_SESSION['admin.import.result'] = array($err, $output);
  164. if (count($err)) {
  165. $db->rollback();
  166. } else {
  167. $db->commit();
  168. }
  169. }
  170. header("Location: {$ABSWEB}admin/importcsv.php");
  171. exit;
  172. }
  173. if (isset($_SESSION['admin.import.result'])) {
  174. list($err, $info) = $_SESSION['admin.import.result'];
  175. unset($_SESSION['admin.import.result']);
  176. mtrack_head(count($err) ? 'Import Failed' : 'Import Complete');
  177. foreach ($info as $line) {
  178. echo $line;
  179. }
  180. if (count($err)) {
  181. echo "The following errors were encountered:<br>\n";
  182. foreach ($err as $msg) {
  183. echo htmlentities($msg) . "<br>\n";
  184. }
  185. echo "<br><b>No changes were committed</b><br>\n";
  186. } else {
  187. echo "<br><b>Done!</b>\n";
  188. }
  189. mtrack_foot();
  190. exit;
  191. }
  192. mtrack_head('Import');
  193. ?>
  194. <h1>Import/Update via CSV</h1>
  195. <p>
  196. You may use this facility to change ticket properties en-masse by uploading
  197. a CSV file.
  198. </p>
  199. <ul>
  200. <li>If a ticket column is present and non-empty,
  201. that ticket will be updated</li>
  202. <li>If there is no ticket column, or the ticket column is empty,
  203. then a ticket will be created</li>
  204. <li>If any errors are detected, none of the changes from the CSV file
  205. will be applied</li>
  206. </ul>
  207. <p>
  208. The input file must be a CSV file with the field names on the first line.
  209. </p>
  210. <p>
  211. The following fields are supported:
  212. </p>
  213. <dl>
  214. <dt>ticket</dt>
  215. <dd>The ticket number</dd>
  216. <dt>milestone</dt>
  217. <dd>The value to use for the milestone. If updating an existing ticket,
  218. this field will remove any other milestones in the ticket and set it to
  219. only this value.
  220. </dd>
  221. <dt>-milestone</dt>
  222. <dd>Removes a milestone; if the ticket is associated with the named milestone,
  223. it will be removed from that milestone.
  224. </dd>
  225. <dt>+milestone</dt>
  226. <dd>Associates the ticket with the named milestone, preserving any other
  227. milestones currently associated with the ticket.
  228. </dd>
  229. <dt>summary</dt>
  230. <dd>Sets the summary for the ticket</dd>
  231. <dt>status or state</dt>
  232. <dd>Sets the state of the ticket; can be one of the configured ticket states
  233. </dd>
  234. <dt>priority</dt>
  235. <dd>Sets the priority; can be one of the configured priorities</dd>
  236. <dt>owner</dt>
  237. <dd>Sets the owner</dd>
  238. <dt>type</dt>
  239. <dd>Sets the ticket type</dd>
  240. <dt>estimated</dt>
  241. <dd>Estimated time (hours)</dd>
  242. <dt>component</dt>
  243. <dd>Sets the component, replacing all other component associations</dd>
  244. <dt>-component</dt>
  245. <dd>Removes association with the named component</dd>
  246. <dt>+component</dt>
  247. <dd>Associates with the named component, preserving existing associations</dd>
  248. <dt>description</dt>
  249. <dd>Sets the description of the ticket</dd>
  250. <?php
  251. foreach ($C->getFields() as $f) {
  252. $name = substr($f->name, 2);
  253. if (!isset($field_aliases[$name]) || $field_aliases[$name] != $f->name) {
  254. $name = $f->name;
  255. echo "<dt>$name</dt>\n";
  256. } else {
  257. echo "<dt>$name</dt>\n";
  258. echo "<dt>$f->name</dt>\n";
  259. }
  260. echo "<dd>" . htmlentities($f->label, ENT_QUOTES, 'utf-8') . "\n";
  261. if ($f->type == 'select') {
  262. echo "<br>Value may be one of:<br>";
  263. $data = $f->ticketData();
  264. foreach ($data['options'] as $opt) {
  265. echo " <tt>" . htmlentities($opt, ENT_QUOTES, 'utf-8') . "</tt><br>";
  266. }
  267. }
  268. echo "</dd>\n";
  269. }
  270. ?>
  271. </dl>
  272. <h2>Import</h2>
  273. <p>Enter a comment in the box below; it will be added as a comment to
  274. all affected tickets</p>
  275. <form method='post' enctype='multipart/form-data'>
  276. <textarea name='comment' id='comment'
  277. class='code wiki' rows='4' cols='78'></textarea>
  278. <input type='file' name='csvfile'>
  279. <input type='submit' value='Import'>
  280. </form>
  281. <?php
  282. mtrack_foot();