1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 die("FIXME - I do not use milestones at present.. so this will be done later");
7 $pi = urldecode(mtrack_get_pathinfo());
9 function parse_date_string($str)
14 return MTrackDB::unixtime(strtotime($str));
17 if ($_GET['new'] == 1 || $_GET['edit'] == 1) {
20 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
21 if (isset($_POST['cancel'])) {
22 header("Location: {$ABSWEB}roadmap.php");
26 if ($_GET['new'] == 1) {
27 MTrackACL::requireAllRights("Roadmap", 'create');
28 $ms = new MTrack_Milestone;
30 MTrackACL::requireAllRights("Roadmap", 'modify');
31 $ms = MTrack_Milestone::loadById($_POST['mid']);
34 if (strlen($_POST['name'])) {
35 $ms->name = $_POST['name'];
36 $ms->description = $_POST['desc'];
38 $pmid = (int)$_POST['pmid'];
40 $pm = MTrack_Milestone::loadById($pmid);
42 $error = "There is no milestone with a parent of $pmid";
50 $ms->duedate = parse_date_string($_POST['duedate']);
51 $ms->startdate = parse_date_string($_POST['startdate']);
53 $compdate = parse_date_string($_POST['compdate']);
54 if ($ms->completed === null && $compdate !== null) {
55 $description = "$ms->name completed";
57 $description = $ms->description;
59 $ms->completed = $compdate;
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);
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'];
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);
86 $kid->duedate = MTrackDB::unixtime($due);
87 $kid->pmid = $ms->mid;
91 $start = strtotime("+1 day", $due);
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");
108 header("Location: {$ABSWEB}milestone.php/$ms->name");
113 } else if (strlen($pi)) {
114 MTrackACL::requireAllRights("Roadmap", 'modify');
115 $ms = MTrack_Milestone::loadByName($pi);
117 MTrackACL::requireAllRights("Roadmap", 'create');
118 $ms = new MTrack_Milestone;
120 mtrack_head($_GET['new'] == 1 ? "New Milestone" : "Edit Milestone");
123 $error = htmlentities($error, ENT_QUOTES, 'utf-8');
125 <div class='ui-state-error ui-corner-all'>
126 <span class='ui-icon ui-icon-alert'></span> $error
132 $name = htmlentities($ms->name, ENT_COMPAT, 'utf-8');
133 $desc = htmlentities($ms->description, ENT_COMPAT, 'utf-8');
136 $duedate = date('m/d/y', strtotime($ms->duedate));
140 if ($ms->startdate) {
141 $startdate = date('m/d/y', strtotime($ms->startdate));
146 if ($ms->completed != null) {
147 $compdate = date('m/d/y', strtotime($ms->completed));
152 if ($_GET['new'] == 1) {
153 echo "<h1>New Milestone</h1>";
156 echo "<h1>Edit Milestone</h1>";
162 <input type='hidden' name='mid' value='{$ms->mid}'>
164 <label>Name of the milestone:</label><br>
165 <input type='text' id='name' name='name' size='32' value='$name'>
169 $kids = MTrackDB::q('select name from milestones where pmid = ?', $ms->mid)->fetchAll(PDO::FETCH_COLUMN, 0);
174 <label>Children:</label> <em>Effort expended against the following milestones is also counted towards the burndown of this milestone</em><br>
177 foreach ($kids as $name) {
178 echo "<a href='{$ABSWEB}milestone.php/$name'>$name</a><br>\n";
186 foreach (MTrackDB::q('select mid, name from milestones where
187 pmid is null and ((deleted != 1 and mid != ? and completed is null)
190 $ms->mid, $ms->pmid)->fetchAll(PDO::FETCH_ASSOC) as $row) {
191 $parents[$row['mid']] = $row['name'];
193 $parents[''] = '(none)';
194 $parent = mtrack_select_box('pmid', $parents, $ms->pmid);
199 <label>Parent:</label> <em>Effort expended against a milestone is also counted towards the burndown of its parent</em><br>
205 $open_milestones = MTrack_Milestone::enumMilestones();
206 $open_milestones[''] = '(none)';
208 $compmilestone = mtrack_select_box('compmilestone', $open_milestones);
212 <legend>Schedule</legend>
215 <input type='text' id='startdate' name='startdate' size='0'
216 value='$startdate' class='dateinput'>
217 <em>Format: MM/DD/YY</em>
222 <input type='text' id='duedate' name='duedate' size='0'
223 value='$duedate' class='dateinput'>
224 <em>Format: MM/DD/YY</em>
231 <input type='text' id='compdate' name='compdate'
232 size='0' value='$compdate' class='dateinput'>
233 <em>Format: MM/DD/YY</em>
235 <em>Re-target open tickets to milestone:</em> $compmilestone
239 if (count($kids) == 0 && !$ms->pmid) {
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'
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.
266 <textarea id='desc' name='desc' class='code wiki' rows='10' cols='78'>$desc</textarea>
269 <div class='buttons'>
270 <button type='submit' name='save'>$save Milestone</button>
271 <button type='submit' name='cancel'>Cancel</button>
274 <script type='text/javascript'>
275 $(document).ready(function() {
277 $('.dateinput').datepicker({
279 dateFormat: 'mm/dd/yy'
284 } else if (strlen($pi)) {
288 <div style="float:right">
289 <button onclick="document.location.href='{$ABSWEB}milestone.php/$pi?edit=1';return false;">Edit Milestone</button>
293 echo MTrack_Milestone::macro_MilestoneSummary($pi);
295 $kids = MTrackDB::q('select name from milestones where pmid =
296 (select mid from milestones where name = ?)', $pi)
297 ->fetchAll(PDO::FETCH_ASSOC);
299 echo "<h2>Related milestones:</h2>";
300 foreach ($kids as $row) {
301 echo MTrack_Milestone::macro_MilestoneSummary($row['name']);
306 throw new Exception("no such milestone $pi");