Mailing list creation requests
authoradamch <adamch>
Sun, 24 Apr 2005 23:49:49 +0000 (23:49 +0000)
committeradamch <adamch>
Sun, 24 Apr 2005 23:49:49 +0000 (23:49 +0000)
TODO
domain.mlt
init.sml
list.mlt [new file with mode: 0644]
mailinglist.sml [new file with mode: 0644]
request.sml
tables.sql
util.sig
util.sml

diff --git a/TODO b/TODO
index f7e0653..d2e1f0d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -3,4 +3,3 @@ Member data
 
 Specific requests
        - Join, with display of pending applications for all members to read
-       - Mailing list requests
index b47367e..b5f07de 100644 (file)
@@ -113,7 +113,7 @@ elseif $"mod" <> "" then
 
 <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.
+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.  The "Reason" field is optional.
 
 <form action="domain">
 <table>
index 4c39832..722f1b8 100644 (file)
--- a/init.sml
+++ b/init.sml
@@ -33,6 +33,8 @@ fun mkUserRow [id, name, rname, bal, joined] =
 
 fun init () =
     let
+       val _ = Util.init ()
+
        val c = conn ()
     in
        C.dml c "BEGIN";
diff --git a/list.mlt b/list.mlt
new file mode 100644 (file)
index 0000000..44ffab0
--- /dev/null
+++ b/list.mlt
@@ -0,0 +1,128 @@
+<% @header [("title", ["Mailing list creation requests"])];
+
+val admin = Group.inGroupName "lists";
+
+if $"req" <> "" then
+       val dom = $"req";
+       if Util.validEmail dom then
+               val id = MailingList.add (Init.getUserId(), dom, $"msg");
+               if not (MailingList.notifyNew id) then
+                       %><h3><b>Error sending e-mail notification</b></h3><%
+               end
+               %><h3><b>Request added</b></h3><%
+       else
+               %><h3><b>Invalid list e-mail address</b></h3><%
+       end
+
+elseif $"cmd" = "open" then
+       %><h3><b>Open requests</b></h3>
+       <a href="list?cmd=list">List all requests</a><%
+
+       foreach (name, req) in MailingList.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>List name</b>:</td> <td><% #data req %></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="list?mod=<% #id req %>">[Modify]</a>
+       <a href="list?del=<% #id req %>">[Delete]</a><br>
+       To set up, run: <tt>newlist <% #data req %>&nbsp;<% name %>@hcoop.net <% Util.randomPassword () %></tt>
+<% end %>
+
+<%     end
+
+elseif $"cmd" = "list" then
+       %><h3><b>All requests</b></h3><%
+
+       foreach (name, req) in MailingList.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>List name</b>:</td> <td><% #data req %></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="list?mod=<% #id req %>">[Modify]</a>
+       <a href="list?del=<% #id req %>">[Delete]</a>
+<% end %>
+
+<%     end
+
+elseif $"mod" <> "" then
+       Group.requireGroupName "lists";
+       val id = Web.stoi ($"mod");
+       val req = MailingList.lookup id;
+       val user = Init.lookupUser (#usr req) %>
+<h3><b>Handle request</b></h3>
+
+<form action="list">
+<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 = MailingList.NEW then %> selected<% end %>>New</option>
+       <option value="1"<% if #status req = MailingList.INSTALLED then %> selected<% end %>>Installed</option>
+       <option value="2"<% if #status req = MailingList.REJECTED then %> selected<% end %>>Rejected</option>
+</select></td> </tr>
+<tr> <td align="right"><b>List name</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 "lists";
+       val id = Web.stoi ($"save");
+       val req = MailingList.lookup id;
+       val oldStatus = #status req;
+       val newStatus = MailingList.statusFromInt (Web.stoi ($"status"));
+       MailingList.modify {req with data = $"dom", msg = $"msg", status = newStatus};
+       if oldStatus <> newStatus then
+               if not (MailingList.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="list?cmd=open">open requests</a>, <a href="list?cmd=list">all requests</a>
+
+<% elseif $"del" <> "" then
+       Group.requireGroupName "lists";
+       val id = Web.stoi ($"del");
+       val req = MailingList.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="list?del2=<% id %>">Yes, I'm sure!</a>
+
+<% elseif $"del2" <> "" then
+       Group.requireGroupName "lists";
+       val id = Web.stoi ($"del2");
+       MailingList.delete id
+       %><h3><b>Request deleted</b><h3>
+       Back to: <a href="list?cmd=open">open requests</a>, <a href="list?cmd=list">all requests</a>
+
+<% else %>
+
+<h3><b>Request new mailing list</b></h3>
+
+Enter here the e-mail address you would like for your list.  Please keep in mind that the part of your list address before the "@" must be unique across all mailing lists we run.  We may reject applications with overly general names here, especially names of current users or UNIX usernames that are likely to be chosen by future members.  The "Reason" field is optional.
+
+<form action="list">
+<table>
+<tr> <td align="right" valign="top"><b>List name</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
diff --git a/mailinglist.sml b/mailinglist.sml
new file mode 100644 (file)
index 0000000..a74132b
--- /dev/null
@@ -0,0 +1,12 @@
+structure MailingList = Request(struct
+                               val table = "MailingList"
+                               val adminGroup = "lists"
+                               fun subject list = "Mailman list request: " ^ list
+                               val template = "list"
+                               val descr = "mailing list"
+
+                               fun body (mail, lst) =
+                                   (Mail.mwrite (mail, "List name: ");
+                                    Mail.mwrite (mail, lst);
+                                    Mail.mwrite (mail, "\n"))
+                               end)
index 08b7853..395bab4 100644 (file)
@@ -130,7 +130,7 @@ val notifyNew = notify (fn (user, mail) =>
                           (Mail.mwrite (mail, #name user);
                            Mail.mwrite (mail, " has requested the following ");
                            Mail.mwrite (mail, T.descr);
-                           Mail.mwrite (mail, " packages:\n\n")))
+                           Mail.mwrite (mail, ":\n\n")))
 
 val statusToString =
     fn NEW => "New"
index feaf3db..20419d3 100644 (file)
@@ -195,3 +195,14 @@ CREATE TABLE Domain(
        FOREIGN KEY (usr) REFERENCES WebUser(id) ON DELETE CASCADE);
 
 CREATE SEQUENCE DomainSeq START 1;
+
+CREATE TABLE MailingList(
+       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 MailingListSeq START 1;
index 55b05c3..3bfaccc 100644 (file)
--- a/util.sig
+++ b/util.sig
@@ -1,5 +1,7 @@
 signature UTIL =
 sig
+    val init : unit -> unit
+
     datatype 'a flat_element =
             BEGIN
           | END
@@ -15,5 +17,8 @@ sig
 
     val validHost : string -> bool
     val validDomain : string -> bool
+    val validEmail : string -> bool
     val whoisUrl : string -> string
+
+    val randomPassword : unit -> string
 end
\ No newline at end of file
index 44895db..a5ae19e 100644 (file)
--- a/util.sml
+++ b/util.sml
@@ -37,6 +37,28 @@ fun validHost s =
 fun validDomain s =
     size s > 0 andalso size s < 100 andalso List.all validHost (String.fields (fn ch => ch = #".") s)
 
+fun validUser s =
+    size s > 0 andalso size s < 50 andalso List.all
+                                              (fn ch => isIdent ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+")
+                                              (String.explode s)
+
+fun validEmailUser s =
+    size s > 0 andalso size s < 50 andalso List.all
+                                              (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-" orelse ch = #"+")
+                                              (String.explode s)
+
+fun validEmail s =
+    (case String.fields (fn ch => ch = #"@") s of
+        [user, host] => validEmailUser user andalso validDomain host
+       | _ => false)
+
 fun whoisUrl dom = String.concat ["http://reports.internic.net/cgi/whois?whois_nic=", dom, "&type=domain"]
 
+val rnd = ref (Random.rand (0, 0))
+
+fun init () = rnd := Random.rand (SysWord.toInt (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())),
+                                 SysWord.toInt (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())))
+
+fun randomPassword () = Int.toString (Int.abs (Random.randInt (!rnd)))
+
 end
\ No newline at end of file