1 <?php # vim:ts=2:sw=2:et:
2 include '../../inc/common.php';
4 MTrackACL::requireAllRights("Tickets", 'create');
7 $field_aliases = array(
11 'type' => 'classification',
13 $supported_fields = array(
29 foreach ($supported_fields as $i => $f) {
30 unset($supported_fields[$i]);
31 $supported_fields[$f] = $f;
34 $C = MTrackTicket_CustomFields::getInstance();
35 foreach ($C->fields as $f) {
36 $name = substr($f->name, 2);
37 $supported_fields[$f->name] = $f->name;
38 if (!isset($field_aliases[$name])) {
39 $field_aliases[$name] = $f->name;
43 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
45 if (isset($_FILES['csvfile']) && $_FILES['csvfile']['error'] == 0
46 && is_uploaded_file($_FILES['csvfile']['tmp_name'])) {
47 ini_set('auto_detect_line_endings', true);
48 $fp = fopen($_FILES['csvfile']['tmp_name'], 'r');
49 $header = fgetcsv($fp);
52 foreach ($header as $i => $name) {
53 $name = strtolower($name);
54 if (isset($field_aliases[$name])) {
55 $name = $field_aliases[$name];
57 if (!isset($supported_fields[$name])) {
58 $err[] = "Unsupported field: $name";
62 $db = MTrackDB::get();
63 $db->beginTransaction();
64 MTrackChangeset::$use_txn = false;
68 if ($line === false) break;
71 foreach ($header as $i => $name) {
72 $item[$name] = $line[$i];
75 if (isset($item['ticket'])) {
76 $id = $item['ticket'];
81 $tkt = MTrackIssue::loadByNSIdent($id);
83 $err[] = "No such ticket $id";
86 } catch (Exception $e) {
87 $err[] = $e->getMessage();
90 $output[] = "<b>Updating ticket $tkt->nsident</b><br>\n";
92 $tkt = new MTrackIssue;
93 $tkt->priority = 'normal';
94 list($tkt->nsident) = MTrackDB::q(
95 'select max(cast(nsident as integer)) + 1 from tickets')
96 ->fetchAll(PDO::FETCH_COLUMN, 0);
97 if ($tkt->nsident === null) {
100 $output[] = "<b>Creating ticket $tkt->nsident<b><br>\n";
102 $CS = MTrackChangeset::begin("ticket:X", $_POST['comment']);
103 if (strlen(trim($_POST['comment']))) {
104 $tkt->addComment($_POST['comment']);
106 foreach ($item as $name => $value) {
107 if ($name == 'ticket') {
110 $output[] = "$name => $value<br>\n";
115 case 'classification':
121 $tkt->$name = strlen($value) ? $value : null;
124 if (strlen($value)) {
125 foreach ($tkt->getMilestones() as $mid) {
126 $tkt->dissocMilestone($mid);
128 $tkt->assocMilestone($value);
132 if (strlen($value)) {
133 $tkt->assocMilestone($value);
137 if (strlen($value)) {
138 $tkt->dissocMilestone($value);
142 if (strlen($value)) {
143 foreach ($tkt->getComponents() as $mid) {
144 $tkt->dissocComponent($mid);
146 $tkt->assocComponent($value);
150 if (strlen($value)) {
151 $tkt->assocComponent($value);
155 if (strlen($value)) {
156 $tkt->dissocComponent($value);
160 if (!strncmp($name, 'x_', 2)) {
161 $tkt->{$name} = $value;
165 } catch (Exception $e) {
166 $err[] = $e->getMessage();
170 $CS->setObject("ticket:" . $tkt->tid);
173 $_SESSION['admin.import.result'] = array($err, $output);
180 header("Location: {$ABSWEB}admin/importcsv.php");
184 if (isset($_SESSION['admin.import.result'])) {
185 list($err, $info) = $_SESSION['admin.import.result'];
186 unset($_SESSION['admin.import.result']);
188 mtrack_head(count($err) ? 'Import Failed' : 'Import Complete');
190 foreach ($info as $line) {
195 echo "The following errors were encountered:<br>\n";
196 foreach ($err as $msg) {
197 echo htmlentities($msg) . "<br>\n";
199 echo "<br><b>No changes were committed</b><br>\n";
201 echo "<br><b>Done!</b>\n";
208 mtrack_head('Import');
211 <h1>Import/Update via CSV</h1>
214 You may use this facility to change ticket properties en-masse by uploading
219 <li>If a ticket column is present and non-empty,
220 that ticket will be updated</li>
221 <li>If there is no ticket column, or the ticket column is empty,
222 then a ticket will be created</li>
223 <li>If any errors are detected, none of the changes from the CSV file
228 The input file must be a CSV file with the field names on the first line.
232 The following fields are supported:
237 <dd>The ticket number</dd>
240 <dd>The value to use for the milestone. If updating an existing ticket,
241 this field will remove any other milestones in the ticket and set it to
246 <dd>Removes a milestone; if the ticket is associated with the named milestone,
247 it will be removed from that milestone.
251 <dd>Associates the ticket with the named milestone, preserving any other
252 milestones currently associated with the ticket.
256 <dd>Sets the summary for the ticket</dd>
258 <dt>status or state</dt>
259 <dd>Sets the state of the ticket; can be one of the configured ticket states
263 <dd>Sets the priority; can be one of the configured priorities</dd>
266 <dd>Sets the owner</dd>
269 <dd>Sets the ticket type</dd>
272 <dd>Sets the component, replacing all other component associations</dd>
275 <dd>Removes association with the named component</dd>
278 <dd>Associates with the named component, preserving existing associations</dd>
281 <dd>Sets the description of the ticket</dd>
285 foreach ($C->fields as $f) {
286 $name = substr($f->name, 2);
287 if (!isset($field_aliases[$name]) || $field_aliases[$name] != $f->name) {
289 echo "<dt>$name</dt>\n";
291 echo "<dt>$name</dt>\n";
292 echo "<dt>$f->name</dt>\n";
294 echo "<dd>" . htmlentities($f->label, ENT_QUOTES, 'utf-8') . "\n";
296 if ($f->type == 'select') {
297 echo "<br>Value may be one of:<br>";
298 $data = $f->ticketData();
299 foreach ($data['options'] as $opt) {
300 echo " <tt>" . htmlentities($opt, ENT_QUOTES, 'utf-8') . "</tt><br>";
313 <p>Enter a comment in the box below; it will be added as a comment to
314 all affected tickets</p>
316 <form method='post' enctype='multipart/form-data'>
317 <textarea name='comment' id='comment'
318 class='code wiki' rows='4' cols='78'></textarea>
319 <input type='file' name='csvfile'>
320 <input type='submit' value='Import'>