Specific requests
- Join, with display of pending applications for all members to read
- - New domain requests
+ - Mailing list requests
end;
if ok then
- val id = Apt.addRequest (Init.getUserId(), $"req", $"msg");
+ val id = Apt.add (Init.getUserId(), $"req", $"msg");
if not (Apt.notifyNew id) then
%><h3><b>Error sending e-mail notification</b></h3><%
end
%><h3><b>Open requests</b></h3>
<a href="apt?cmd=list">List all requests</a><%
- foreach (name, req) in Apt.listOpenRequests () do %>
+ foreach (name, req) in Apt.listOpen () do %>
<br><hr><br>
<table>
<tr> <td align="right"><b>By</b>:</td> <td colspan="2"><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
<tr> <td align="right"><b>Packages</b>:</td><%
ref first = true;
- val pkgs = String.tokens Char.isSpace (#pkgs req);
+ val pkgs = String.tokens Char.isSpace (#data req);
foreach pkg in pkgs do
if first then
elseif $"cmd" = "list" then
%><h3><b>All requests</b></h3><%
- foreach (name, req) in Apt.listRequests () do %>
+ foreach (name, req) in Apt.list () do %>
<br><hr><br>
<table>
<tr> <td align="right"><b>By</b>:</td> <td colspan="2"><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
<tr> <td align="right"><b>Packages</b>:</td><%
ref first = true;
- val pkgs = String.tokens Char.isSpace (#pkgs req);
+ val pkgs = String.tokens Char.isSpace (#data req);
foreach pkg in pkgs do
if first then
elseif $"mod" <> "" then
Group.requireGroupName "server";
val id = Web.stoi ($"mod");
- val req = Apt.lookupRequest id;
+ val req = Apt.lookup id;
val user = Init.lookupUser (#usr req) %>
<h3><b>Handle request</b></h3>
<option value="1"<% if #status req = Apt.INSTALLED then %> selected<% end %>>Installed</option>
<option value="2"<% if #status req = Apt.REJECTED then %> selected<% end %>>Rejected</option>
</select></td> </tr>
-<tr> <td align="right" valign="top"><b>Packages</b>:</td> <td><textarea name="pkgs" rows="5" cols="40" wrap="soft"><% Web.html (#pkgs req) %></textarea></td> </tr>
+<tr> <td align="right" valign="top"><b>Packages</b>:</td> <td><textarea name="pkgs" rows="5" cols="40" wrap="soft"><% Web.html (#data req) %></textarea></td> </tr>
<tr> <td align="right" valign="top"><b>Message</b>:</td> <td><textarea name="msg" rows="10" cols="80" wrap="soft"><% Web.html (#msg req) %></textarea></td> </tr>
<tr> <td><input type="submit" value="Save"></td> </tr>
</table>
<% elseif $"save" <> "" then
Group.requireGroupName "server";
val id = Web.stoi ($"save");
- val req = Apt.lookupRequest id;
+ val req = Apt.lookup id;
val oldStatus = #status req;
val newStatus = Apt.statusFromInt (Web.stoi ($"status"));
- Apt.modRequest {req with pkgs = $"pkgs", msg = $"msg", status = newStatus};
+ Apt.modify {req with data = $"pkgs", msg = $"msg", status = newStatus};
if oldStatus <> newStatus then
if not (Apt.notifyMod (oldStatus, newStatus, Init.getUserName(), id)) then
%><h3><b>Error sending e-mail notification</b></h3><%
<% elseif $"del" <> "" then
Group.requireGroupName "server";
val id = Web.stoi ($"del");
- val req = Apt.lookupRequest id;
+ val req = Apt.lookup id;
val user = Init.lookupUser (#usr req)
- %><h3><b>Are you sure you want to delete request by <% #name user %> for <tt><% #pkgs req %></tt>?</b></h3>
+ %><h3><b>Are you sure you want to delete request by <% #name user %> for <tt><% #data req %></tt>?</b></h3>
<a href="apt?del2=<% id %>">Yes, I'm sure!</a>
<% elseif $"del2" <> "" then
Group.requireGroupName "server";
val id = Web.stoi ($"del2");
- Apt.deleteRequest id
+ Apt.delete id
%><h3><b>Request deleted</b><h3>
Back to: <a href="apt?cmd=open">open requests</a>, <a href="apt?cmd=list">all requests</a>
+++ /dev/null
-signature APT =
-sig
- datatype status =
- NEW
- | INSTALLED
- | REJECTED
-
- type request = { id : int, usr : int, pkgs : string, msg : string, status : status, stamp : Init.C.timestamp }
-
- val statusFromInt : int -> status
-
- val addRequest : int * string * string -> int
- val lookupRequest : int -> request
- val modRequest : request -> unit
- val deleteRequest : int -> unit
- val listRequests : unit -> (string * request) list
- val listOpenRequests : unit -> (string * request) list
-
- val notifyNew : int -> bool
- val notifyMod : status * status * string * int -> bool
-end
\ No newline at end of file
-structure Apt :> APT =
-struct
-
-open Util Sql Init
-
-datatype status =
- NEW
- | INSTALLED
- | REJECTED
-
-type request = { id : int, usr : int, pkgs : string, msg : string, status : status, stamp : C.timestamp }
-
-val statusFromInt =
- fn 0 => NEW
- | 1 => INSTALLED
- | 2 => REJECTED
- | _ => raise C.Sql "Bad APT request status"
-
-val statusToInt =
- fn NEW => 0
- | INSTALLED => 1
- | REJECTED => 2
-
-fun statusFromSql v = statusFromInt (C.intFromSql v)
-fun statusToSql s = C.intToSql (statusToInt s)
-
-fun mkRequestRow [id, usr, pkgs, msg, status, stamp] =
- {id = C.intFromSql id, usr = C.intFromSql usr, pkgs = C.stringFromSql pkgs,
- msg = C.stringFromSql msg, status = statusFromSql status, stamp = C.timestampFromSql stamp}
- | mkRequestRow r = rowError ("APT request", r)
-
-fun addRequest (usr, pkgs, msg) =
- let
- val db = getDb ()
- val id = nextSeq (db, "AptSeq")
- in
- C.dml db ($`INSERT INTO Apt (id, usr, pkgs, msg, status, stamp)
- VALUES (^(C.intToSql id), ^(C.intToSql usr), ^(C.stringToSql pkgs), ^(C.stringToSql msg),
- 0, CURRENT_TIMESTAMP)`);
- id
- end
-
-fun modRequest (req : request) =
- let
- val db = getDb ()
- in
- ignore (C.dml db ($`UPDATE Apt SET
- usr = ^(C.intToSql (#usr req)), pkgs = ^(C.stringToSql (#pkgs req)),
- msg = ^(C.stringToSql (#msg req)), status = ^(statusToSql (#status req))
- WHERE id = ^(C.intToSql (#id req))`))
- end
-
-fun deleteRequest id =
- ignore (C.dml (getDb ()) ($`DELETE FROM Apt WHERE id = ^(C.intToSql id)`))
-
-fun lookupRequest id =
- case C.oneOrNoRows (getDb ()) ($`SELECT id, usr, pkgs, msg, status, stamp
- FROM Apt
- WHERE id = ^(C.intToSql id)`) of
- SOME row => mkRequestRow row
- | NONE => raise Fail "APT request not found"
-
-fun mkRequestRow' (name :: rest) = (C.stringFromSql name, mkRequestRow rest)
- | mkRequestRow' r = rowError ("Apt.request'", r)
-
-fun listRequests () =
- C.map (getDb ()) mkRequestRow' ($`SELECT name, Apt.id, usr, pkgs, msg, status, stamp
- FROM Apt JOIN WebUser ON usr = WebUser.id
- ORDER BY stamp DESC`)
-
-fun listOpenRequests () =
- C.map (getDb ()) mkRequestRow' ($`SELECT name, Apt.id, usr, pkgs, msg, status, stamp
- FROM Apt JOIN WebUser ON usr = WebUser.id
- WHERE status = 0
- ORDER BY stamp DESC`)
-
-fun notify f req =
- let
- val grp =
- case Group.groupNameToId "server" of
- NONE => 0
- | SOME grp => grp
-
- val req = lookupRequest req
- val user = Init.lookupUser (#usr req)
-
- val mail = Mail.mopen ()
-
- fun doOne [name] =
- let
- val name = C.stringFromSql name
- in
- if name = #name user then
- ()
- else
- (Mail.mwrite (mail, name);
- Mail.mwrite (mail, ","))
- end
- | doOne r = rowError ("apt.doOne", r)
-
- fun rightJustify (n, s) =
- let
- fun pad n =
- if n <= 0 then
- ()
- else
- (Mail.mwrite (mail, " ");
- pad (n-1))
- in
- pad (n - size s);
- Mail.mwrite (mail, s)
- end
-
- val pkgs = String.tokens Char.isSpace (#pkgs req)
- val infos = map (valOf o AptQuery.query) pkgs
- in
- Mail.mwrite (mail, "From: Hcoop Portal <portal@hcoop.net>\nTo: ");
- Mail.mwrite (mail, #name user);
- Mail.mwrite (mail, "@hcoop.net\n");
- Mail.mwrite (mail, "Bcc: ");
- C.app (getDb ()) doOne ($`SELECT name
- FROM WebUser JOIN Membership ON (usr = id AND grp = ^(C.intToSql grp))`);
- Mail.mwrite (mail, "\nSubject: Apt package installation request\n\n");
-
- f (user, mail);
-
- app (fn info =>
- (rightJustify (10, #name info);
- Mail.mwrite (mail, " ");
- Mail.mwrite (mail, #descr info);
- Mail.mwrite (mail, "\n"))) infos;
-
- Mail.mwrite (mail, "\n");
- Mail.mwrite (mail, #msg req);
-
- Mail.mwrite (mail, "\n\nOpen requests: ");
- Mail.mwrite (mail, urlPrefix);
- Mail.mwrite (mail, "apt?cmd=open\n");
-
- OS.Process.isSuccess (Mail.mclose mail)
- end
-
-val notifyNew = notify (fn (user, mail) =>
- (Mail.mwrite (mail, #name user);
- Mail.mwrite (mail, " has requested the following packages:\n\n")))
-
-val statusToString =
- fn NEW => "New"
- | INSTALLED => "Installed"
- | REJECTED => "Rejected"
-
-fun notifyMod (oldStatus, newStatus, changer, req) =
- notify (fn (_, mail) =>
- (Mail.mwrite (mail, changer);
- Mail.mwrite (mail, " has changed the status of this request from ");
- Mail.mwrite (mail, statusToString oldStatus);
- Mail.mwrite (mail, " to ");
- Mail.mwrite (mail, statusToString newStatus);
- Mail.mwrite (mail, ".\n\n"))) req
-
-end
\ No newline at end of file
+structure Apt = Request(struct
+ val table = "Apt"
+ val adminGroup = "server"
+ fun subject _ = "Apt package installation request"
+ val template = "apt"
+ val descr = "packages"
+
+ fun body (mail, pkgs) =
+ let
+ val pkgs = String.tokens Char.isSpace pkgs
+ val infos = map (valOf o AptQuery.query) pkgs
+
+ fun rightJustify (n, s) =
+ let
+ fun pad n =
+ if n <= 0 then
+ ()
+ else
+ (Mail.mwrite (mail, " ");
+ pad (n-1))
+ in
+ pad (n - size s);
+ Mail.mwrite (mail, s)
+ end
+ in
+ app (fn info =>
+ (rightJustify (10, #name info);
+ Mail.mwrite (mail, " ");
+ Mail.mwrite (mail, #descr info);
+ Mail.mwrite (mail, "\n"))) infos
+ end
+ end)
--- /dev/null
+<% @header [("title", ["Domain set-up requests"])];
+
+val admin = Group.inGroupName "server";
+
+if $"req" <> "" then
+ val dom = $"req";
+ if Util.validDomain dom then
+ val id = Domain.add (Init.getUserId(), dom, $"msg");
+ if not (Domain.notifyNew id) then
+ %><h3><b>Error sending e-mail notification</b></h3><%
+ end
+ %><h3><b>Request added</b></h3><%
+ else
+ %><h3><b>Invalid domain name</b></h3><%
+ end
+
+elseif $"cmd" = "open" then
+ %><h3><b>Open requests</b></h3>
+ <a href="domain?cmd=list">List all requests</a><%
+
+ foreach (name, req) in Domain.listOpen () do %>
+<br><hr><br>
+<table>
+<tr> <td align="right"><b>By</b>:</td> <td><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
+<tr> <td align="right"><b>Time</b>:</td> <td><% #stamp req %></td> </tr>
+<tr> <td align="right"><b>Domain</b>:</td> <td><a href="<% Util.whoisUrl (#data req) %>"><% #data req %></a></td> </tr>
+<tr> <td align="right" valign="top"><b>Reason</b>:</td> <td colspan="2"><% Web.html (#msg req) %></td> </tr>
+</table>
+
+<% if admin then %>
+ <br>
+ <a href="domain?mod=<% #id req %>">[Modify]</a>
+ <a href="domain?del=<% #id req %>">[Delete]</a><br>
+ To set up, run: <tt>domtool.real mkdom <% #data req %> <% name %></tt>
+<% end %>
+
+<% end
+
+elseif $"cmd" = "list" then
+ %><h3><b>All requests</b></h3><%
+
+ foreach (name, req) in Domain.list () do %>
+<br><hr><br>
+<table>
+<tr> <td align="right"><b>By</b>:</td> <td colspan="2"><a href="user?id=<% #usr req %>"><% name %></a></td> </tr>
+<tr> <td align="right"><b>Time</b>:</td> <td colspan="2"><% #stamp req %></td> </tr>
+<tr> <td align="right"><b>Domain</b>:</td> <td><a href="<% Util.whoisUrl (#data req) %>"><% #data req %></a></td> </tr>
+<tr> <td align="right" valign="top"><b>Reason</b>:</td> <td colspan="2"><% Web.html (#msg req) %></td> </tr>
+</table>
+
+<% if admin then %>
+ <br>
+ <a href="domain?mod=<% #id req %>">[Modify]</a>
+ <a href="domain?del=<% #id req %>">[Delete]</a>
+<% end %>
+
+<% end
+
+elseif $"mod" <> "" then
+ Group.requireGroupName "server";
+ val id = Web.stoi ($"mod");
+ val req = Domain.lookup id;
+ val user = Init.lookupUser (#usr req) %>
+<h3><b>Handle request</b></h3>
+
+<form action="domain">
+<input type="hidden" name="save" value="<% id %>">
+<table>
+<tr> <td align="right"><b>Requestor</b>:</td> <td><a href="user?id=<% #usr req %>"><% #name user %></a></td> </tr>
+<tr> <td align="right"><b>Time</b>:</td> <td><% #stamp req %></td> </tr>
+<tr> <td align="right"><b>Status</b>:</td> <td><select name="status">
+ <option value="0"<% if #status req = Domain.NEW then %> selected<% end %>>New</option>
+ <option value="1"<% if #status req = Domain.INSTALLED then %> selected<% end %>>Installed</option>
+ <option value="2"<% if #status req = Domain.REJECTED then %> selected<% end %>>Rejected</option>
+</select></td> </tr>
+<tr> <td align="right"><b>Domain</b>:</td> <td><input name="dom" value="<% #data req %>"></td> </tr>
+<tr> <td align="right" valign="top"><b>Message</b>:</td> <td><textarea name="msg" rows="10" cols="80" wrap="soft"><% Web.html (#msg req) %></textarea></td> </tr>
+<tr> <td><input type="submit" value="Save"></td> </tr>
+</table>
+</form>
+
+<% elseif $"save" <> "" then
+ Group.requireGroupName "server";
+ val id = Web.stoi ($"save");
+ val req = Domain.lookup id;
+ val oldStatus = #status req;
+ val newStatus = Domain.statusFromInt (Web.stoi ($"status"));
+ Domain.modify {req with data = $"dom", msg = $"msg", status = newStatus};
+ if oldStatus <> newStatus then
+ if not (Domain.notifyMod (oldStatus, newStatus, Init.getUserName(), id)) then
+ %><h3><b>Error sending e-mail notification</b></h3><%
+ end
+ end
+ %><h3><b>Request modified</b></h3>
+ Back to: <a href="domain?cmd=open">open requests</a>, <a href="domain?cmd=list">all requests</a>
+
+<% elseif $"del" <> "" then
+ Group.requireGroupName "server";
+ val id = Web.stoi ($"del");
+ val req = Domain.lookup id;
+ val user = Init.lookupUser (#usr req)
+ %><h3><b>Are you sure you want to delete request by <% #name user %> for <tt><% #data req %></tt>?</b></h3>
+ <a href="domain?del2=<% id %>">Yes, I'm sure!</a>
+
+<% elseif $"del2" <> "" then
+ Group.requireGroupName "server";
+ val id = Web.stoi ($"del2");
+ Domain.delete id
+ %><h3><b>Request deleted</b><h3>
+ Back to: <a href="domain?cmd=open">open requests</a>, <a href="domain?cmd=list">all requests</a>
+
+<% else %>
+
+<h3><b>Request new domain</b></h3>
+
+Enter the full Internet domain name that you own and would like set up here. We don't do domain registration, so it is your responsibility to register this name with your registrar of choice before submitting it here.
+
+<form action="domain">
+<table>
+<tr> <td align="right" valign="top"><b>Domain</b>:</td> <td><input name="req"></td> </tr>
+<tr> <td align="right" valign="top"><b>Reason</b>:</td> <td><textarea name="msg" rows="5" cols="80" wrap="soft"></textarea></td> </tr>
+<tr> <td><input type="submit" value="Request"></td> </tr>
+</table>
+</form>
+
+<% end %>
+
+<% @footer[] %>
\ No newline at end of file
--- /dev/null
+structure Domain = Request(struct
+ val table = "Domain"
+ val adminGroup = "server"
+ fun subject dom = "Domain set-up request: " ^ dom
+ val template = "domain"
+ val descr = "domain"
+ fun body (mail, dom) =
+ (Mail.mwrite (mail, "Domain: ");
+ Mail.mwrite (mail, dom);
+ Mail.mwrite (mail, "\n"))
+ end)
--- /dev/null
+signature REQUEST_IN =
+sig
+ val table : string
+ val adminGroup : string
+ val subject : string -> string
+ val body : Mail.session * string -> unit
+ val template : string
+ val descr : string
+end
+
+signature REQUEST_OUT =
+sig
+ datatype status =
+ NEW
+ | INSTALLED
+ | REJECTED
+
+ type request = { id : int, usr : int, data : string, msg : string, status : status, stamp : Init.C.timestamp }
+
+ val statusFromInt : int -> status
+
+ val add : int * string * string -> int
+ val lookup : int -> request
+ val modify : request -> unit
+ val delete : int -> unit
+ val list : unit -> (string * request) list
+ val listOpen : unit -> (string * request) list
+
+ val notifyNew : int -> bool
+ val notifyMod : status * status * string * int -> bool
+end
\ No newline at end of file
-structure Apt :> APT =
+functor Request (T : REQUEST_IN) :> REQUEST_OUT =
struct
open Util Sql Init
+val table = T.table
+val seq = table ^ "Seq"
+
datatype status =
NEW
| INSTALLED
| REJECTED
-type request = { id : int, usr : int, pkgs : string, msg : string, status : status, stamp : C.timestamp }
+type request = { id : int, usr : int, data : string, msg : string, status : status, stamp : C.timestamp }
val statusFromInt =
fn 0 => NEW
fun statusFromSql v = statusFromInt (C.intFromSql v)
fun statusToSql s = C.intToSql (statusToInt s)
-fun mkRequestRow [id, usr, pkgs, msg, status, stamp] =
- {id = C.intFromSql id, usr = C.intFromSql usr, pkgs = C.stringFromSql pkgs,
+fun mkRow [id, usr, data, msg, status, stamp] =
+ {id = C.intFromSql id, usr = C.intFromSql usr, data = C.stringFromSql data,
msg = C.stringFromSql msg, status = statusFromSql status, stamp = C.timestampFromSql stamp}
- | mkRequestRow r = rowError ("APT request", r)
+ | mkRow r = rowError ("APT request", r)
-fun addRequest (usr, pkgs, msg) =
+fun add (usr, data, msg) =
let
val db = getDb ()
- val id = nextSeq (db, "AptSeq")
+ val id = nextSeq (db, seq)
in
- C.dml db ($`INSERT INTO Apt (id, usr, pkgs, msg, status, stamp)
- VALUES (^(C.intToSql id), ^(C.intToSql usr), ^(C.stringToSql pkgs), ^(C.stringToSql msg),
+ C.dml db ($`INSERT INTO ^table (id, usr, data, msg, status, stamp)
+ VALUES (^(C.intToSql id), ^(C.intToSql usr), ^(C.stringToSql data), ^(C.stringToSql msg),
0, CURRENT_TIMESTAMP)`);
id
end
-fun modRequest (req : request) =
+fun modify (req : request) =
let
val db = getDb ()
in
- ignore (C.dml db ($`UPDATE Apt SET
- usr = ^(C.intToSql (#usr req)), pkgs = ^(C.stringToSql (#pkgs req)),
+ ignore (C.dml db ($`UPDATE ^table SET
+ usr = ^(C.intToSql (#usr req)), data = ^(C.stringToSql (#data req)),
msg = ^(C.stringToSql (#msg req)), status = ^(statusToSql (#status req))
WHERE id = ^(C.intToSql (#id req))`))
end
-fun deleteRequest id =
- ignore (C.dml (getDb ()) ($`DELETE FROM Apt WHERE id = ^(C.intToSql id)`))
+fun delete id =
+ ignore (C.dml (getDb ()) ($`DELETE FROM ^table WHERE id = ^(C.intToSql id)`))
-fun lookupRequest id =
- case C.oneOrNoRows (getDb ()) ($`SELECT id, usr, pkgs, msg, status, stamp
- FROM Apt
+fun lookup id =
+ case C.oneOrNoRows (getDb ()) ($`SELECT id, usr, data, msg, status, stamp
+ FROM ^table
WHERE id = ^(C.intToSql id)`) of
- SOME row => mkRequestRow row
- | NONE => raise Fail "APT request not found"
+ SOME row => mkRow row
+ | NONE => raise Fail ($`^table request not found`)
-fun mkRequestRow' (name :: rest) = (C.stringFromSql name, mkRequestRow rest)
- | mkRequestRow' r = rowError ("Apt.request'", r)
+fun mkRow' (name :: rest) = (C.stringFromSql name, mkRow rest)
+ | mkRow' r = rowError ("Apt.request'", r)
-fun listRequests () =
- C.map (getDb ()) mkRequestRow' ($`SELECT name, Apt.id, usr, pkgs, msg, status, stamp
- FROM Apt JOIN WebUser ON usr = WebUser.id
- ORDER BY stamp DESC`)
+fun list () =
+ C.map (getDb ()) mkRow' ($`SELECT name, ^table.id, usr, data, msg, status, stamp
+ FROM ^table JOIN WebUser ON usr = WebUser.id
+ ORDER BY stamp DESC`)
-fun listOpenRequests () =
- C.map (getDb ()) mkRequestRow' ($`SELECT name, Apt.id, usr, pkgs, msg, status, stamp
- FROM Apt JOIN WebUser ON usr = WebUser.id
- WHERE status = 0
- ORDER BY stamp DESC`)
+fun listOpen () =
+ C.map (getDb ()) mkRow' ($`SELECT name, ^table.id, usr, data, msg, status, stamp
+ FROM ^table JOIN WebUser ON usr = WebUser.id
+ WHERE status = 0
+ ORDER BY stamp DESC`)
fun notify f req =
let
val grp =
- case Group.groupNameToId "server" of
+ case Group.groupNameToId T.adminGroup of
NONE => 0
| SOME grp => grp
- val req = lookupRequest req
+ val req = lookup req
val user = Init.lookupUser (#usr req)
val mail = Mail.mopen ()
(Mail.mwrite (mail, name);
Mail.mwrite (mail, ","))
end
- | doOne r = rowError ("apt.doOne", r)
-
- fun rightJustify (n, s) =
- let
- fun pad n =
- if n <= 0 then
- ()
- else
- (Mail.mwrite (mail, " ");
- pad (n-1))
- in
- pad (n - size s);
- Mail.mwrite (mail, s)
- end
-
- val pkgs = String.tokens Char.isSpace (#pkgs req)
- val infos = map (valOf o AptQuery.query) pkgs
+ | doOne r = rowError (table ^ ".doOne", r)
in
Mail.mwrite (mail, "From: Hcoop Portal <portal@hcoop.net>\nTo: ");
Mail.mwrite (mail, #name user);
Mail.mwrite (mail, "Bcc: ");
C.app (getDb ()) doOne ($`SELECT name
FROM WebUser JOIN Membership ON (usr = id AND grp = ^(C.intToSql grp))`);
- Mail.mwrite (mail, "\nSubject: Apt package installation request\n\n");
+ Mail.mwrite (mail, "\nSubject: ");
+ Mail.mwrite (mail, T.subject (#data req));
+ Mail.mwrite (mail, "\n\n");
f (user, mail);
- app (fn info =>
- (rightJustify (10, #name info);
- Mail.mwrite (mail, " ");
- Mail.mwrite (mail, #descr info);
- Mail.mwrite (mail, "\n"))) infos;
+ T.body (mail, #data req);
Mail.mwrite (mail, "\n");
Mail.mwrite (mail, #msg req);
Mail.mwrite (mail, "\n\nOpen requests: ");
Mail.mwrite (mail, urlPrefix);
- Mail.mwrite (mail, "apt?cmd=open\n");
+ Mail.mwrite (mail, T.template);
+ Mail.mwrite (mail, "?cmd=open\n");
OS.Process.isSuccess (Mail.mclose mail)
end
val notifyNew = notify (fn (user, mail) =>
(Mail.mwrite (mail, #name user);
- Mail.mwrite (mail, " has requested the following packages:\n\n")))
+ Mail.mwrite (mail, " has requested the following ");
+ Mail.mwrite (mail, T.descr);
+ Mail.mwrite (mail, " packages:\n\n")))
val statusToString =
fn NEW => "New"
CREATE TABLE Apt(
id INTEGER PRIMARY KEY,
usr INTEGER NOT NULL,
- pkgs TEXT NOT NULL,
+ data TEXT NOT NULL,
msg TEXT NOT NULL,
status INTEGER NOT NULL,
stamp TIMESTAMP NOT NULL,
FOREIGN KEY (usr) REFERENCES WebUser(id) ON DELETE CASCADE);
CREATE SEQUENCE AptSeq START 1;
+
+CREATE TABLE Domain(
+ id INTEGER PRIMARY KEY,
+ usr INTEGER NOT NULL,
+ data TEXT NOT NULL,
+ msg TEXT NOT NULL,
+ status INTEGER NOT NULL,
+ stamp TIMESTAMP NOT NULL,
+ FOREIGN KEY (usr) REFERENCES WebUser(id) ON DELETE CASCADE);
+
+CREATE SEQUENCE DomainSeq START 1;
val id : 'a -> 'a
val makeSet : ('a -> string) -> 'a list -> string
val neg : real -> real
+
+ val validHost : string -> bool
+ val validDomain : string -> bool
+ val whoisUrl : string -> string
end
\ No newline at end of file
fun neg (r : real) = ~r
+fun isIdent ch = Char.isLower ch orelse Char.isDigit ch
+
+fun validHost s =
+ size s > 0 andalso size s < 20 andalso List.all isIdent (String.explode s)
+
+fun validDomain s =
+ size s > 0 andalso size s < 100 andalso List.all validHost (String.fields (fn ch => ch = #".") s)
+
+fun whoisUrl dom = String.concat ["http://reports.internic.net/cgi/whois?whois_nic=", dom, "&type=domain"]
+
end
\ No newline at end of file