final move of files
[web.mtrack] / MTrackWeb / Milestone.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3
4 die("FIXME - I do not use milestones at present.. so this will be done later");
5
6
7 $pi = urldecode(mtrack_get_pathinfo());
8
9 function parse_date_string($str)
10 {
11   if (!strlen($str)) {
12     return null;
13   }
14   return MTrackDB::unixtime(strtotime($str));
15 }
16
17 if ($_GET['new'] == 1 || $_GET['edit'] == 1) {
18   $error = null;
19
20   if ($_SERVER['REQUEST_METHOD'] == 'POST') {
21     if (isset($_POST['cancel'])) {
22       header("Location: {$ABSWEB}roadmap.php");
23       exit;
24     }
25
26     if ($_GET['new'] == 1) {
27       MTrackACL::requireAllRights("Roadmap", 'create');
28       $ms = new MTrack_Milestone;
29     } else {
30       MTrackACL::requireAllRights("Roadmap", 'modify');
31       $ms = MTrack_Milestone::loadById($_POST['mid']);
32     }
33
34     if (strlen($_POST['name'])) {
35       $ms->name = $_POST['name'];
36       $ms->description = $_POST['desc'];
37
38       $pmid = (int)$_POST['pmid'];
39       if ($pmid > 0) {
40         $pm = MTrack_Milestone::loadById($pmid);
41         if (!$pm) {
42           $error = "There is no milestone with a parent of $pmid";
43         } else {
44           $ms->pmid = $pmid;
45         }
46       } else {
47         $ms->pmid = null;
48       }
49
50       $ms->duedate = parse_date_string($_POST['duedate']);
51       $ms->startdate = parse_date_string($_POST['startdate']);
52
53       $compdate = parse_date_string($_POST['compdate']);
54       if ($ms->completed === null && $compdate !== null) {
55         $description = "$ms->name completed";
56       } else {
57         $description = $ms->description;
58       }
59       $ms->completed = $compdate;
60
61       $other = MTrack_Milestone::loadByName($_POST['name']);
62       if ($other && ($_GET['new'] == 1 || $ms->mid != $other->mid)) {
63         $error = "a milestone named \"$ms->name\" already exists";
64       } else if ($error === null) {
65         $CS = MTrackChangeset::begin("milestone:$ms->name", $description);
66         $ms->save($CS);
67
68         if ($pmid < 1 && $_POST['additers'] == 'on') {
69           /* add children for iterations (not allowed for milestones
70            * that are themselves a child of another */
71           $start = strtotime($ms->startdate);
72           $end = strtotime($ms->duedate);
73           $days = (int)$_POST['iterduration'];
74
75           $n = 1;
76           $link = rawurlencode($ms->name);
77           while ($start < $end) {
78             $kid = new MTrack_Milestone;
79             $kid->name = $ms->name . " ($n)";
80             $kid->description = "Iteration $n of [milestone:$link]";
81             $kid->startdate = MTrackDB::unixtime($start);
82             $due = strtotime("+$days day", $start);
83             if ($due > $end) {
84               $due = $end;
85             }
86             $kid->duedate = MTrackDB::unixtime($due);
87             $kid->pmid = $ms->mid;
88
89             $kid->save($CS);
90
91             $start = strtotime("+1 day", $due);
92             $n++;
93           }
94         }
95
96         if ($ms->completed !== null && $_POST['compmilestone'] != '') {
97           $TM = MTrack_Milestone::loadById($_POST['compmilestone']);
98           foreach (MTrackDB::q("select t.tid from ticket_milestones tm left join tickets t on (tm.tid = t.tid) where mid = ? and status != 'closed'", $ms->mid)->fetchAll(PDO::FETCH_COLUMN, 0) as $tid) {
99             $T = MTrackIssue::loadById($tid);
100             $T->dissocMilestone($ms);
101             $T->assocMilestone($TM);
102             $T->addComment("$ms->name completed, moving ticket to $TM->name");
103             $T->save($CS);
104           }
105         }
106
107         $CS->commit();
108         header("Location: {$ABSWEB}milestone.php/$ms->name");
109         exit;
110       }
111     }
112     var_export($_POST);
113   } else if (strlen($pi)) {
114     MTrackACL::requireAllRights("Roadmap", 'modify');
115     $ms = MTrack_Milestone::loadByName($pi);
116   } else {
117     MTrackACL::requireAllRights("Roadmap", 'create');
118     $ms = new MTrack_Milestone;
119   }
120   mtrack_head($_GET['new'] == 1 ? "New Milestone" : "Edit Milestone");
121
122   if ($error) {
123     $error = htmlentities($error, ENT_QUOTES, 'utf-8');
124     echo <<<HTML
125 <div class='ui-state-error ui-corner-all'>
126     <span class='ui-icon ui-icon-alert'></span> $error
127 </div>
128
129 HTML;
130   }
131
132   $name = htmlentities($ms->name, ENT_COMPAT, 'utf-8');
133   $desc = htmlentities($ms->description, ENT_COMPAT, 'utf-8');
134   
135   if ($ms->duedate) {
136     $duedate = date('m/d/y', strtotime($ms->duedate));
137   } else {
138     $duedate = '';
139   }
140   if ($ms->startdate) {
141     $startdate = date('m/d/y', strtotime($ms->startdate));
142   } else {
143     $startdate = '';
144   }
145
146   if ($ms->completed != null) {
147     $compdate = date('m/d/y', strtotime($ms->completed));
148   } else {
149     $compdate = null;
150   }
151
152   if ($_GET['new'] == 1) {
153     echo "<h1>New Milestone</h1>";
154     $save = 'Add';
155   } else {
156     echo "<h1>Edit Milestone</h1>";
157     $save = 'Save';
158   }
159
160   echo <<<HTML
161 <form method='post'>
162 <input type='hidden' name='mid' value='{$ms->mid}'>
163 <div class='field'>
164   <label>Name of the milestone:</label><br>
165   <input type='text' id='name' name='name' size='32' value='$name'>
166 </div>
167 HTML;
168
169   $kids = MTrackDB::q('select name from milestones where pmid = ?', $ms->mid)->fetchAll(PDO::FETCH_COLUMN, 0);
170   if (count($kids)) {
171
172     echo <<<HTML
173 <div class='field'>
174   <label>Children:</label> <em>Effort expended against the following milestones is also counted towards the burndown of this milestone</em><br>
175 HTML;
176
177     foreach ($kids as $name) {
178       echo "<a href='{$ABSWEB}milestone.php/$name'>$name</a><br>\n";
179     }
180
181     echo "</div>\n";
182   
183   } else {
184
185     $parents = array();
186     foreach (MTrackDB::q('select mid, name from milestones where
187         pmid is null and ((deleted != 1 and mid != ? and completed is null)
188         or (mid = ?))
189         order by name',
190         $ms->mid, $ms->pmid)->fetchAll(PDO::FETCH_ASSOC) as $row) {
191       $parents[$row['mid']] = $row['name'];
192     }
193     $parents[''] = '(none)';
194     $parent = mtrack_select_box('pmid', $parents, $ms->pmid);
195
196
197     echo <<<HTML
198 <div class='field'>
199   <label>Parent:</label> <em>Effort expended against a milestone is also counted towards the burndown of its parent</em><br>
200   $parent
201 </div>
202 HTML;
203   }
204
205   $open_milestones = MTrack_Milestone::enumMilestones();
206   $open_milestones[''] = '(none)';
207
208   $compmilestone = mtrack_select_box('compmilestone', $open_milestones);
209
210   echo <<<HTML
211 <fieldset>
212   <legend>Schedule</legend>
213   <div class='field'>
214     <label>Start:<br>
215       <input type='text' id='startdate' name='startdate' size='0'
216         value='$startdate' class='dateinput'>
217       <em>Format: MM/DD/YY</em>
218     </label>
219   </div>
220   <div class='field'>
221     <label>Due:<br>
222       <input type='text' id='duedate' name='duedate' size='0'
223         value='$duedate' class='dateinput'>
224       <em>Format: MM/DD/YY</em>
225     </label>
226   </div>
227   <br>
228   <div class='field'>
229     <label>
230       Completed:<br>
231       <input type='text' id='compdate' name='compdate'
232         size='0' value='$compdate' class='dateinput'>
233       <em>Format: MM/DD/YY</em>
234     </label><br>
235     <em>Re-target open tickets to milestone:</em> $compmilestone
236   </div>
237 HTML;
238
239   if (count($kids) == 0 && !$ms->pmid) {
240     echo <<<HTML
241   <br>
242   <div class='field'>
243     <label>
244       <input type='checkbox' id='additers' name='additers'>
245       Add child milestones for iteration tracking<br>
246       <em>Iteration duration of
247       <input type='text' id='iterduration' name='iterduration'
248         size='3' value='7'>
249       days</em>
250     </label>
251   </div>
252 HTML;
253   }
254
255   echo <<<HTML
256 </fieldset>
257 <div class='field'>
258   <fieldset class='iefix'>
259     <label for='desc'>Description</label><br/>
260     <em>By default, the milestone summary will display a burndown chart
261       as though you had added <tt>[[BurnDown(milestone=name,width=50%,height=150)]]</tt> into the description field below.<br>
262       If you wish to change the size and position of the chart, explicitly
263       enter the burndown macro in the description field.<br>
264       To turn off the burndown for this milestone, enter <tt>[[BurnDown()]]</tt> in the description field.
265     </em>
266     <textarea id='desc' name='desc' class='code wiki' rows='10' cols='78'>$desc</textarea>
267   </fieldset>
268 </div>
269 <div class='buttons'>
270   <button type='submit' name='save'>$save Milestone</button>
271   <button type='submit' name='cancel'>Cancel</button>
272 </div>
273 </form>
274 <script type='text/javascript'>
275 $(document).ready(function() {
276   $('#name').focus();
277   $('.dateinput').datepicker({
278     // minDate: 0,
279     dateFormat: 'mm/dd/yy'
280   });
281 });
282 </script>
283 HTML;
284 } else if (strlen($pi)) {
285
286   mtrack_head($pi);
287 echo <<<HTML
288 <div style="float:right">
289 <button onclick="document.location.href='{$ABSWEB}milestone.php/$pi?edit=1';return false;">Edit Milestone</button>
290 </div>
291 HTML;
292
293   echo MTrack_Milestone::macro_MilestoneSummary($pi);
294
295   $kids = MTrackDB::q('select name from milestones where pmid = 
296     (select mid from milestones where name = ?)', $pi)
297     ->fetchAll(PDO::FETCH_ASSOC);
298   if (count($kids)) {
299     echo "<h2>Related milestones:</h2>";
300     foreach ($kids as $row) {
301       echo MTrack_Milestone::macro_MilestoneSummary($row['name']);
302     }
303   } 
304
305 }  else {
306   throw new Exception("no such milestone $pi");
307 }
308
309 mtrack_foot();