1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 include '../inc/common.php';
5 $pi = urldecode(mtrack_get_pathinfo());
7 function parse_date_string($str)
12 return MTrackDB::unixtime(strtotime($str));
15 if ($_GET['new'] == 1 || $_GET['edit'] == 1) {
18 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
19 if (isset($_POST['cancel'])) {
20 header("Location: {$ABSWEB}roadmap.php");
24 if ($_GET['new'] == 1) {
25 MTrackACL::requireAllRights("Roadmap", 'create');
26 $ms = new MTrackMilestone;
28 MTrackACL::requireAllRights("Roadmap", 'modify');
29 $ms = MTrackMilestone::loadById($_POST['mid']);
32 if (strlen($_POST['name'])) {
33 $ms->name = $_POST['name'];
34 $ms->description = $_POST['desc'];
36 $pmid = (int)$_POST['pmid'];
38 $pm = MTrackMilestone::loadById($pmid);
40 $error = "There is no milestone with a parent of $pmid";
48 $ms->duedate = parse_date_string($_POST['duedate']);
49 $ms->startdate = parse_date_string($_POST['startdate']);
51 $compdate = parse_date_string($_POST['compdate']);
52 if ($ms->completed === null && $compdate !== null) {
53 $description = "$ms->name completed";
55 $description = $ms->description;
57 $ms->completed = $compdate;
59 $other = MTrackMilestone::loadByName($_POST['name']);
60 if ($other && ($_GET['new'] == 1 || $ms->mid != $other->mid)) {
61 $error = "a milestone named \"$ms->name\" already exists";
62 } else if ($error === null) {
63 $CS = MTrackChangeset::begin("milestone:$ms->name", $description);
66 if ($pmid < 1 && $_POST['additers'] == 'on') {
67 /* add children for iterations (not allowed for milestones
68 * that are themselves a child of another */
69 $start = strtotime($ms->startdate);
70 $end = strtotime($ms->duedate);
71 $days = (int)$_POST['iterduration'];
74 $link = rawurlencode($ms->name);
75 while ($start < $end) {
76 $kid = new MTrackMilestone;
77 $kid->name = $ms->name . " ($n)";
78 $kid->description = "Iteration $n of [milestone:$link]";
79 $kid->startdate = MTrackDB::unixtime($start);
80 $due = strtotime("+$days day", $start);
84 $kid->duedate = MTrackDB::unixtime($due);
85 $kid->pmid = $ms->mid;
89 $start = strtotime("+1 day", $due);
94 if ($ms->completed !== null && $_POST['compmilestone'] != '') {
95 $TM = MTrackMilestone::loadById($_POST['compmilestone']);
96 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) {
97 $T = MTrackIssue::loadById($tid);
98 $T->dissocMilestone($ms);
99 $T->assocMilestone($TM);
100 $T->addComment("$ms->name completed, moving ticket to $TM->name");
106 header("Location: {$ABSWEB}milestone.php/$ms->name");
111 } else if (strlen($pi)) {
112 MTrackACL::requireAllRights("Roadmap", 'modify');
113 $ms = MTrackMilestone::loadByName($pi);
115 MTrackACL::requireAllRights("Roadmap", 'create');
116 $ms = new MTrackMilestone;
118 mtrack_head($_GET['new'] == 1 ? "New Milestone" : "Edit Milestone");
121 $error = htmlentities($error, ENT_QUOTES, 'utf-8');
123 <div class='ui-state-error ui-corner-all'>
124 <span class='ui-icon ui-icon-alert'></span> $error
130 $name = htmlentities($ms->name, ENT_COMPAT, 'utf-8');
131 $desc = htmlentities($ms->description, ENT_COMPAT, 'utf-8');
134 $duedate = date('m/d/y', strtotime($ms->duedate));
138 if ($ms->startdate) {
139 $startdate = date('m/d/y', strtotime($ms->startdate));
144 if ($ms->completed != null) {
145 $compdate = date('m/d/y', strtotime($ms->completed));
150 if ($_GET['new'] == 1) {
151 echo "<h1>New Milestone</h1>";
154 echo "<h1>Edit Milestone</h1>";
160 <input type='hidden' name='mid' value='{$ms->mid}'>
162 <label>Name of the milestone:</label><br>
163 <input type='text' id='name' name='name' size='32' value='$name'>
167 $kids = MTrackDB::q('select name from milestones where pmid = ?', $ms->mid)->fetchAll(PDO::FETCH_COLUMN, 0);
172 <label>Children:</label> <em>Effort expended against the following milestones is also counted towards the burndown of this milestone</em><br>
175 foreach ($kids as $name) {
176 echo "<a href='{$ABSWEB}milestone.php/$name'>$name</a><br>\n";
184 foreach (MTrackDB::q('select mid, name from milestones where
185 pmid is null and ((deleted != 1 and mid != ? and completed is null)
188 $ms->mid, $ms->pmid)->fetchAll(PDO::FETCH_ASSOC) as $row) {
189 $parents[$row['mid']] = $row['name'];
191 $parents[''] = '(none)';
192 $parent = mtrack_select_box('pmid', $parents, $ms->pmid);
197 <label>Parent:</label> <em>Effort expended against a milestone is also counted towards the burndown of its parent</em><br>
203 $open_milestones = MTrackMilestone::enumMilestones();
204 $open_milestones[''] = '(none)';
206 $compmilestone = mtrack_select_box('compmilestone', $open_milestones);
210 <legend>Schedule</legend>
213 <input type='text' id='startdate' name='startdate' size='0'
214 value='$startdate' class='dateinput'>
215 <em>Format: MM/DD/YY</em>
220 <input type='text' id='duedate' name='duedate' size='0'
221 value='$duedate' class='dateinput'>
222 <em>Format: MM/DD/YY</em>
229 <input type='text' id='compdate' name='compdate'
230 size='0' value='$compdate' class='dateinput'>
231 <em>Format: MM/DD/YY</em>
233 <em>Re-target open tickets to milestone:</em> $compmilestone
237 if (count($kids) == 0 && !$ms->pmid) {
242 <input type='checkbox' id='additers' name='additers'>
243 Add child milestones for iteration tracking<br>
244 <em>Iteration duration of
245 <input type='text' id='iterduration' name='iterduration'
256 <fieldset class='iefix'>
257 <label for='desc'>Description</label><br/>
258 <em>By default, the milestone summary will display a burndown chart
259 as though you had added <tt>[[BurnDown(milestone=name,width=50%,height=150)]]</tt> into the description field below.<br>
260 If you wish to change the size and position of the chart, explicitly
261 enter the burndown macro in the description field.<br>
262 To turn off the burndown for this milestone, enter <tt>[[BurnDown()]]</tt> in the description field.
264 <textarea id='desc' name='desc' class='code wiki' rows='10' cols='78'>$desc</textarea>
267 <div class='buttons'>
268 <button type='submit' name='save'>$save Milestone</button>
269 <button type='submit' name='cancel'>Cancel</button>
272 <script type='text/javascript'>
273 $(document).ready(function() {
275 $('.dateinput').datepicker({
277 dateFormat: 'mm/dd/yy'
282 } else if (strlen($pi)) {
286 <div style="float:right">
287 <button onclick="document.location.href='{$ABSWEB}milestone.php/$pi?edit=1';return false;">Edit Milestone</button>
291 echo MTrackMilestone::macro_MilestoneSummary($pi);
293 $kids = MTrackDB::q('select name from milestones where pmid =
294 (select mid from milestones where name = ?)', $pi)
295 ->fetchAll(PDO::FETCH_ASSOC);
297 echo "<h2>Related milestones:</h2>";
298 foreach ($kids as $row) {
299 echo MTrackMilestone::macro_MilestoneSummary($row['name']);
304 throw new Exception("no such milestone $pi");