--- /dev/null
+<% 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
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 ()
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