Basic issue management
authoradamch <adamch>
Sun, 17 Apr 2005 20:52:53 +0000 (20:52 +0000)
committeradamch <adamch>
Sun, 17 Apr 2005 20:52:53 +0000 (20:52 +0000)
issue.mlt [new file with mode: 0644]
support.mlt
support.sig
support.sml

diff --git a/issue.mlt b/issue.mlt
new file mode 100644 (file)
index 0000000..48c78ef
--- /dev/null
+++ b/issue.mlt
@@ -0,0 +1,183 @@
+<% val catId = Web.stoi ($"cat");
+val cat = Support.lookupCategory catId;
+val admin = Group.inGroupNum (#grp cat);
+val you = Init.getUserId()
+
+ref viewingIssue = NONE;
+
+@header[("title", ["Support: " ^ Web.html (#name cat)])];
+
+ref showNormal = true;
+
+if $"cmd" = "new" then
+       showNormal := false %>
+
+<h3><b>New issue</b></h3>
+
+<form action="issue">
+<input type="hidden" name="cat" value="<% catId %>">
+<input type="hidden" name="cmd" value="add">
+<table>
+<tr> <td align="right"><b>Title</b>:</td> <td><input name="title"></td> </tr>
+<tr> <td align="right"><input type="checkbox" name="priv"></td> <td>Only make this issue accessible to the admins for this support category.</td> </tr>
+<tr> <td><input type="submit" value="Add"></td> </tr>
+</table>
+</form>
+
+<% elseif $"cmd" = "add" then
+       val title = $"title";
+       if not (Support.validTitle title) then
+               %><h3><b>Invalid title</b></h3><%
+       else
+               val id = Support.addIssue (you, catId, title, $"priv" = "on", Support.NEW);
+               viewingIssue := SOME id
+       end
+
+elseif $"mod" <> "" then
+       showNormal := false;
+       val id = Web.stoi ($"mod");
+       val issue = Support.lookupIssue id;
+       if catId <> #cat issue then
+               %><h3><b>Inconsistent cat field</b></h3><%
+       elseif not admin then
+               %><h3><b>You aren't authorized to modify that.</b></h3><%
+       else %>
+<h3><b>Modify issue</b></h3>
+
+<form action="issue">
+<input type="hidden" name="cat" value="<% catId %>">
+<input type="hidden" name="save" value="<% id %>">
+<table>
+<tr> <td align="right"><b>Category</b>:</td> <td><select name="newCat">
+<% foreach cat in Support.listCategories () do %>
+       <option value="<% #id cat %>"<% if #id cat = catId then %> selected<% end %>><% Web.html (#name cat) %></option>
+<% end %></select></td> </tr>
+<tr> <td align="right"><b>Title</b>:</td> <td><input name="title" value="<% Web.html (#title issue) %>"></td> </tr>
+<tr> <td align="right"><input type="checkbox" name="priv"<% if #priv issue then %> checked<% end %>></td> <td>Only make this issue accessible to the admins for this support category.</td> </tr>
+<tr> <td align="right"><b>Status</b>:</td> <td><select name="status">
+       <option value="0"<% if #status issue = Support.NEW then %> selected<% end %>>New</option>
+       <option value="1"<% if #status issue = Support.PENDING then %> selected<% end %>>Pending</option>
+       <option value="2"<% if #status issue = Support.CLOSED then %> selected<% end %>>Closed</option>
+</select></td> </tr>
+<tr> <td><input type="submit" value="Add"></td> </tr>
+</table>
+</form>
+<%     end
+
+elseif $"save" <> "" then
+       val id = Web.stoi ($"save");
+       val issue = Support.lookupIssue id;
+       val title = $"title";
+       val status = Web.stoi ($"status");
+       val newCat = Support.lookupCategory (Web.stoi ($"newCat"));
+
+       if catId <> #cat issue then
+               %><h3><b>Inconsistent cat field</b></h3><%
+       elseif (iff admin then not (Group.inGroupNum (#grp newCat)) else false) then
+               %><h3><b>Authorization failure</b></h3><%
+       elseif not (Support.validTitle title) then
+               %><h3><b>Invalid title</b></h3><%
+       elseif (iff status < 0 then false else status > 2) then
+               %><h3><b>Invalid status</b></h3><%
+       else
+               Support.modIssue {issue with cat = #id newCat, title = title,
+                               priv = ($"priv" = "on"),
+                               status = (case status of
+                                                 0 => Support.NEW
+                                               | 1 => Support.PENDING
+                                               | _ => Support.CLOSED)};
+               viewingIssue := SOME id
+               %><h3><b>Issue saved</b></h3<%
+       end
+
+elseif $"del" <> "" then
+       showNormal := false;
+       val id = Web.stoi ($"del");
+       val issue = Support.lookupIssue id;
+
+       if catId <> #cat issue then
+               %><h3><b>Inconsistent cat field</b></h3><%
+       elseif not admin then
+               %><h3><b>Authorization failure</b></h3><%
+       else
+               %><h3><b>Are you sure you want to delete "<% Web.html (#title issue) %>"?</b></h3>
+               <a href="issue?cat=<% catId %>&del2=<% id %>">Yes, delete "<% Web.html (#title issue) %>"!</a><%
+       end
+
+elseif $"del2" <> "" then
+       val id = Web.stoi ($"del2");
+       val issue = Support.lookupIssue id;
+
+       if catId <> #cat issue then
+               %><h3><b>Inconsistent cat field</b></h3><%
+       elseif not admin then
+               %><h3><b>Authorization failure</b></h3><%
+       else
+               Support.deleteIssue id
+               %><h3><b>Issue "<% Web.html (#title issue) %>" deleted</b></h3><%
+       end
+
+elseif $"id" <> "" then
+       viewingIssue := SOME (Web.stoi ($"id"))
+end;
+
+switch viewingIssue of
+         SOME id => 
+               val issue = Support.lookupIssue id;
+               val canEdit = (iff #usr issue = you then true else admin);
+               val canView = (iff #priv issue then canEdit else true);
+               if catId <> #cat issue then
+                       %><h3><b>Inconsistent cat field</b></h3><%
+               elseif not canView then
+                       %><h3><b>You aren't authorized to view that.</b></h3><%
+               else
+                       val user = Init.lookupUser (#usr issue) %>
+
+<table>
+<tr> <td align="right"><b>Title</b>:</td> <td><% Web.html (#title issue) %></td> </tr>
+<tr> <td align="right"><b>Created by</b>:</td> <td><a href="user?id=<% #usr issue %>"><% #name user %></a></td> </tr>
+<tr> <td align="right"><b>At</b>:</td> <td><% #stamp issue %></td> </tr>
+<tr> <td align="right"><b>Private</b>:</td> <td><% if #priv issue then %>yes<% else %>no<% end %></td> </tr>
+<tr> <td align="right"><b>Status</b>:</td> <td><% switch #status issue of
+         Support.NEW => %>New<%
+       | Support.PENDING => %>Pending<%
+       | Support.CLOSED => %>Closed<%
+       end %></td> </tr>
+</table>
+
+<% if admin then %>
+<a href="issue?cat=<% catId %>&mod=<% id %>">Modify this issue</a><br>
+<a href="issue?cat=<% catId %>&del=<% id %>">Delete this issue</a><br>
+<% end %>
+
+<%             end
+               
+       | NONE =>
+if showNormal then %>
+
+<a href="issue?cat=<% catId %>&cmd=new">New issue</a>
+
+<h3><b>Open issues</b></h3>
+
+<table>
+<% val issues = iff admin then Support.listOpenCategoryIssuesAdmin catId
+                       else Support.listOpenCategoryIssues (catId, you);
+
+foreach issue in issues do
+       val user = Init.lookupUser (#usr issue) %>
+       <tr> <td><a href="issue?cat=<% catId %>&id=<% #id issue %>"><% Web.html (#title issue) %></a>
+<% if #priv issue then %><i>(private)</i><% end %>
+       </td>
+       <td><a href="user?id=<% #usr issue %>"><% #name user %></a></td>
+       <td><% #stamp issue %></td>
+       <td><% switch #status issue of
+                         Support.NEW => %>New<%
+                       | Support.PENDING => %>Pending<%
+                       | Support.CLOSED => %>Closed<%
+               end %></td> </tr>
+<% end %>
+
+<% end
+end %>
+
+<% @footer[] %>
\ No newline at end of file
index 3c87e04..2af6750 100644 (file)
@@ -66,10 +66,10 @@ if $"sub" <> "" then
 
 if showNormal then %>
 
-<h3><b>New issue in:</b></h3>
+<h3><b>Choose a category:</b></h3>
 
 <% foreach (sub, cat) in Support.listCategoriesWithSubscriptions you do %>
-<a href="issue?new=<% #id cat %>"><% Web.html (#name cat) %></a>: <% Web.html (#descr cat) %>
+<a href="issue?cat=<% #id cat %>"><% Web.html (#name cat) %></a>: <% Web.html (#descr cat) %>
 <% if sub then %>
 <a href="support?unsub=<% #id cat %>">[Unsubscribe]</a>
 <% else %>
index 2c5e127..e36295d 100644 (file)
@@ -19,6 +19,9 @@ sig
 
     val lookupIssue : int -> issue
     val listIssues : unit -> issue list
+    val listCategoryIssues : int -> issue list
+    val listOpenCategoryIssues : int * int -> issue list
+    val listOpenCategoryIssuesAdmin : int -> issue list
     val addIssue : int * int * string * bool * status -> int
     val modIssue : issue -> unit
     val deleteIssue : int -> unit
@@ -32,4 +35,6 @@ sig
     val subscribed : subscription -> bool
     val subscribe : subscription -> unit
     val unsubscribe : subscription -> unit
+                                     
+    val validTitle : string -> bool
 end
\ No newline at end of file
index 5b9c714..5370150 100644 (file)
@@ -95,6 +95,27 @@ fun listIssues () =
                                   FROM SupIssue
                                   ORDER BY stamp DESC`)
 
+fun listCategoryIssues cat =
+    C.map (getDb ()) mkIssueRow ($`SELECT id, usr, cat, title, priv, status, stamp
+                                  FROM SupIssue
+                                  WHERE cat = ^(C.intToSql cat)
+                                  ORDER BY stamp DESC`)
+
+fun listOpenCategoryIssues (cat, usr) =
+    C.map (getDb ()) mkIssueRow ($`SELECT id, usr, cat, title, priv, status, stamp
+                                  FROM SupIssue
+                                  WHERE cat = ^(C.intToSql cat)
+                                     AND status < 2
+                                     AND (NOT priv OR usr = ^(C.intToSql usr))
+                                  ORDER BY stamp DESC`)
+
+fun listOpenCategoryIssuesAdmin cat =
+    C.map (getDb ()) mkIssueRow ($`SELECT id, usr, cat, title, priv, status, stamp
+                                  FROM SupIssue
+                                  WHERE cat = ^(C.intToSql cat)
+                                     AND status < 2
+                                  ORDER BY stamp DESC`)
+
 fun addIssue (usr, cat, title, priv, status) =
     let
        val db = getDb ()
@@ -188,5 +209,6 @@ fun unsubscribe {usr, cat} =
     ignore (C.dml (getDb ()) ($`DELETE FROM SupSubscription
                                WHERE usr = ^(C.intToSql usr) AND cat = ^(C.intToSql cat)`))
 
+fun validTitle s = CharVector.exists (fn ch => not (Char.isSpace ch)) s
 
 end
\ No newline at end of file