Roll call system, and update forms to method=post
authoradamch <adamch>
Sat, 1 Oct 2005 22:20:46 +0000 (22:20 +0000)
committeradamch <adamch>
Sat, 1 Oct 2005 22:20:46 +0000 (22:20 +0000)
23 files changed:
apps.mlt
apt.mlt
balances.mlt
contact.mlt
domain.mlt
groups.mlt
header.mlt
issue.mlt
kind.mlt
link.mlt
list.mlt
location.mlt
money.mlt
poll.mlt
portal.mlt
pref.mlt
roll.mlt [new file with mode: 0644]
roll.sig [new file with mode: 0644]
roll.sml [new file with mode: 0644]
support.mlt
tables.sql
users.mlt
util.sml

index a572697..4594588 100644 (file)
--- a/apps.mlt
+++ b/apps.mlt
@@ -88,9 +88,11 @@ elseif $"add" <> "" then
        App.add id %>
 
 First, create this UNIX user:
-<blockquote><tt>adduser <% #name appl %> "<% #rname appl %>"<% if #forward appl then %>" <% #email appl %>"<% end %></tt></blockquote>
+<blockquote><tt>portal_adduser <% #name appl %> "<% #rname appl %>"<% if #forward appl then %>" <% #email appl %>"<% end %></tt></blockquote>
 
-<form action="users">
+<p>You should then run <tt>visudo</tt> to add <% #name appl %> to the <tt>MEMBERS</tt> group.  If you're not transmitting <% #name appl %>'s password to him by other means, run <tt>savepass <% #name appl %> &lt;password&gt;</tt> to save it in <tt>~<% #name appl %>/.pass</tt>.</p>
+
+<form action="users" method="post">
 <input type="hidden" name="app" value="<% #id appl %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% Web.html (#name appl) %>"></td> </tr>
diff --git a/apt.mlt b/apt.mlt
index 519040b..65c9d76 100644 (file)
--- a/apt.mlt
+++ b/apt.mlt
@@ -150,7 +150,7 @@ elseif $"mod" <> "" then
        val user = Init.lookupUser (#usr req) %>
 <h3><b>Handle request</b></h3>
 
-<form action="apt">
+<form action="apt" method="post">
 <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>
@@ -202,7 +202,7 @@ elseif $"mod" <> "" then
 
 List the package names you'd like, separated by any whitespace characters.
 
-<form action="apt">
+<form action="apt" method="post">
 <table>
 <tr> <td align="right" valign="top"><b>Packages</b>:</td> <td><textarea name="new" rows="10" cols="40" wrap="soft"></textarea></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>
index 250d28c..a6b9352 100644 (file)
@@ -20,7 +20,7 @@ elseif $"mod" <> "" then
        val balance = Balance.lookupBalance (Web.stoi ($"mod")) %>
 <h3><b>Modify balance record</b></h3>
 
-<form action="balances">
+<form action="balances" method="post">
 <input type="hidden" name="id" value="<% $"mod" %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% #name balance %>"></td> </tr>
@@ -50,7 +50,7 @@ elseif $"mod" <> "" then
        ref total = 0.0 %>
 <h3><b>New balance</b></h3>
 
-<form action="balances">
+<form action="balances" method="post">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name"></td> </tr>
 <tr> <td><input type="submit" name="cmd" value="Create"></td> </tr>
index eed7986..d7d0fd8 100644 (file)
@@ -13,7 +13,7 @@ if $"cmd" = "add" then
 
 <h3><b>Modify contact entry</b></h3>
 
-<form action="contact">
+<form action="contact" method="post">
 <input type="hidden" name="save" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Kind</b>:</td> <td><select name="knd">
@@ -56,7 +56,7 @@ if showNormal then %>
 
 <h3><b>Add new contact information</b></h3>
 
-<form action="contact">
+<form action="contact" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Kind</b>:</td> <td><select name="knd">
index b5f07de..eb4c4b5 100644 (file)
@@ -63,7 +63,7 @@ elseif $"mod" <> "" then
        val user = Init.lookupUser (#usr req) %>
 <h3><b>Handle request</b></h3>
 
-<form action="domain">
+<form action="domain" method="post">
 <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>
@@ -115,7 +115,7 @@ elseif $"mod" <> "" then
 
 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">
+<form action="domain" method="post">
 <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>
index d0a1a28..25f86b7 100644 (file)
@@ -20,7 +20,7 @@ elseif $"mod" <> "" then
        val group = Group.lookupGroup (Web.stoi ($"mod")) %>
 <h3><b>Modify group record</b></h3>
 
-<form action="groups">
+<form action="groups" method="post">
 <input type="hidden" name="id" value="<% $"mod" %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% #name group %>"></td> </tr>
@@ -58,7 +58,7 @@ elseif $"mod" <> "" then
 
 <h3><b>Create group</b></h3>
 
-<form action="groups">
+<form action="groups" method="post">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name"></td> </tr>
 <tr> <td><input type="submit" name="cmd" value="Create"></td> </tr>
@@ -67,7 +67,7 @@ elseif $"mod" <> "" then
 
 <h3><b>Grant membership</b></h3>
 
-<form action="groups">
+<form action="groups" method="post">
 <table>
 <tr> <td align="right"><b>Group</b>:</td> <td><select name="grp">
 <% foreach group in Group.listGroups () do %>
index ef04ddd..12510cc 100644 (file)
@@ -1,8 +1,8 @@
 <html><head>
 <% val title =
        case $"title" of
-                 "" => "Hcoop Portal"
-               | t => ("Hcoop Portal: " ^ t) %>
+                 "" => "HCoop Portal"
+               | t => ("HCoop Portal: " ^ t) %>
 <title><% Web.html title %></title>
 </head><body>
 
index fdfface..0a5414e 100644 (file)
--- a/issue.mlt
+++ b/issue.mlt
@@ -14,7 +14,7 @@ if $"cmd" = "new" then
 
 <h3><b>New issue</b></h3>
 
-<form action="issue">
+<form action="issue" method="post">
 <input type="hidden" name="cat" value="<% catId %>">
 <input type="hidden" name="cmd" value="add">
 <table>
@@ -70,7 +70,7 @@ end %>
        else %>
 <h3><b>Modify issue</b></h3>
 
-<form action="issue">
+<form action="issue" method="post">
 <input type="hidden" name="cat" value="<% catId %>">
 <input type="hidden" name="save" value="<% id %>">
 <table>
@@ -180,7 +180,7 @@ elseif $"modPost" <> "" then
        else %>
 <h3><b>Modify post</b></h3>
 
-<form action="issue">
+<form action="issue" method="post">
 <input type="hidden" name="cat" value="<% catId %>">
 <input type="hidden" name="savePost" value="<% id %>">
 <textarea name="body" rows="10" cols="80" wrap="soft"><% Web.htmlNl (#body post) %></textarea>
@@ -283,7 +283,7 @@ foreach (name, post) in Support.listPosts id do %>
 
 <h3><b>Post to this thread</b></h3>
 
-<form action="issue">
+<form action="issue" method="post">
 <input type="hidden" name="cat" value="<% catId %>">
 <input type="hidden" name="iss" value="<% id %>">
 <input type="hidden" name="cmd" value="post">
index 2f8f08c..11ce043 100644 (file)
--- a/kind.mlt
+++ b/kind.mlt
@@ -13,7 +13,7 @@ if $"cmd" = "add" then
        val id = Web.stoi ($"mod");
        val kind = Contact.lookupKind id %>
 
-<form action="kind">
+<form action="kind" method="post">
 <input type="hidden" name="save" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% Web.html (#name kind) %>"></td> </tr>
@@ -53,7 +53,7 @@ if $"cmd" = "add" then
 
 <h3><b>Add a contact kind</b></h3>
 
-<form action="kind">
+<form action="kind" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name"></td> </tr>
index ddfea35..e2943a9 100644 (file)
--- a/link.mlt
+++ b/link.mlt
@@ -19,7 +19,7 @@ if $"cmd" = "add" then
 
 <h3><b>Modify link</b></h3>
 
-<form action="link">
+<form action="link" method="post">
 <input type="hidden" name="save" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Title</b>:</td> <td><input name="title" value="<% Web.html (#title link) %>"></td> </tr>
@@ -78,7 +78,7 @@ if showNormal then %>
 
 <h3><b>Add a link to a site you host with Hcoop</b></h3>
 
-<form action="link">
+<form action="link" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Title</b>:</td> <td><input name="title"></td> </tr>
index 44d8e26..6ea2716 100644 (file)
--- a/list.mlt
+++ b/list.mlt
@@ -64,7 +64,7 @@ elseif $"mod" <> "" then
        val user = Init.lookupUser (#usr req) %>
 <h3><b>Handle request</b></h3>
 
-<form action="list">
+<form action="list" method="post">
 <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>
@@ -118,7 +118,7 @@ elseif $"mod" <> "" then
 
 <p>If you want to use the Mailman web interface on your new list, and you want this to appear on a different web virtual host than hcoop.net, you should create a file <tt>.mailman</tt> in the domtool directory for this domain, before submitting a request. In that file, put the hostname of the vhost you want to use, and be sure to include a <tt>Mailman</tt> directive in that vhost's configuration.</p>
 
-<form action="list">
+<form action="list" method="post">
 <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>
index ce8c799..1390955 100644 (file)
@@ -23,7 +23,7 @@ elseif $"mod" <> "" then
 
 <h3><b>Modify a location</b></h3>
 
-<form action="location">
+<form action="location" method="post">
 <input type="hidden" name="save" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Parent</b>:</td> <td><select name="parent">
@@ -126,7 +126,7 @@ end %>
 
 <h3><b>Add a new location</b></h3>
 
-<form action="location">
+<form action="location" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Parent</b>:</td> <td><select name="parent">
@@ -156,7 +156,7 @@ end %>
 
 Adding yourself to a location automatically adds you to all more general loations.
 
-<form action="location">
+<form action="location" method="post">
 <select name="addLoc">
 <% ref indent = 0;
 foreach item in withUser do
@@ -177,7 +177,7 @@ end %>
 
 <h3><b>Remove yourself from a location</b></h3>
 
-<form action="location">
+<form action="location" method="post">
 <select name="remLoc">
 <% ref indent = 0;
 foreach item in withUser do
index 02e7233..3048309 100644 (file)
--- a/money.mlt
+++ b/money.mlt
@@ -59,7 +59,7 @@ end %></td> </tr>
 
 <h3><b>New hosting bill</b></h3>
 
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="cmd" value="hosting2">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr"></td> </tr>
@@ -86,7 +86,7 @@ end %></td> </tr>
 
 <h3><b>Modify hosting bill</b></h3>
 
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="saveHosting" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr" value="<% Web.html (#descr trn) %>"></td> </tr>
@@ -116,7 +116,7 @@ end %></textarea></td> </tr>
        Group.requireGroupName "money";
        showNormal := false %>
 <h3><b>New generic/even transaction</b></h3>
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="cmd" value="even">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr"></td> </tr>
@@ -137,7 +137,7 @@ end %></textarea></td> </tr>
 
 <h3><b>New member payment</b></h3>
 
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="cmd" value="pay2">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr"></td> </tr>
@@ -169,7 +169,7 @@ end %></textarea></td> </tr>
 
 <h3><b>Modify member payment</b></h3>
 
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="savePay" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr" value="<% Web.html (#descr trn) %>"></td> </tr>
@@ -200,7 +200,7 @@ end %></textarea></td> </tr>
        Group.requireGroupName "money";
        showNormal := false %>
 <h3><b>New generic/even transaction</b></h3>
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="cmd" value="even">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr"></td> </tr>
@@ -228,7 +228,7 @@ end %></textarea></td> </tr>
        val trn = Money.lookupTransaction (Web.stoi ($"modEven")) %>
 <h3><b>Modify even transaction</b></h3>
 
-<form action="money">
+<form action="money" method="post">
 <input type="hidden" name="saveEven" value="<% $"modEven" %>">
 <table>
 <tr> <td align="right"><b>Description</b>:</td> <td><input name="descr" value="<% Web.html (#descr trn) %>"></td> </tr>
index b216a4f..834f22d 100644 (file)
--- a/poll.mlt
+++ b/poll.mlt
@@ -32,7 +32,7 @@ elseif $"vote" <> "" then
 
 <h3><b>Choices</b></h3>
 
-<form action="poll">
+<form action="poll" method="post">
 <input type="hidden" name="vote2" value="<% id %>">
 <% val choices = Poll.listChoicesWithMyVotes id;
 if #votes poll = 1 then %>
@@ -91,7 +91,7 @@ elseif $"mod" <> "" then
        Poll.requireCanModify poll %>
 <h3><b>Modify poll</b></h3>
 
-<form action="poll">
+<form action="poll" method="post">
 <input type="hidden" name="id" value="<% $"mod" %>">
 <table>
 <tr> <td align="right"><b>Title</b>:</td> <td><input name="title" value="<% Web.html (#title poll) %>"></td> </tr>
@@ -157,7 +157,7 @@ elseif $"modChoice" <> "" then
        val poll = Poll.lookupPoll (#pol cho);
        Poll.requireCanModify poll %>
 
-<form action="poll">
+<form action="poll" method="post">
 <input type="hidden" name="saveChoice" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Text</b>:</td> <td><input name="descr" value="<% Web.html (#descr cho) %>"></td> </tr>
@@ -302,7 +302,7 @@ end %>
 <br><hr><br>
 <h3><b>Add a new choice</b></h3>
 
-<form action="poll">
+<form action="poll" method="post">
 <input type="hidden" name="addChoice" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Text</b>:</td> <td><input name="descr"></td> </tr>
@@ -319,7 +319,7 @@ if showNormal then %>
 
 <h3><b>Create a poll</b></h3>
 
-<form action="poll">
+<form action="poll" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Title</b>:</td> <td><input name="title"></td> </tr>
index 2223f91..0528e2f 100644 (file)
@@ -4,10 +4,12 @@ val bal = Balance.lookupBalance (#bal you);
 
 <% if Group.inGroupNum 0 then %>
 <b><h3>Admin</h3></b>
+<a href="apps?cmd=approved">Approved applicants waiting for accounts</a><br>
 <a href="users">Members</a><br>
 <a href="groups">Groups</a><br>
 <a href="balances">Balances</a><br>
 <a href="kind">Contact kinds</a><br>
+<a href="roll">Roll call!</a><br>
 <% end %>
 
 <b><h3>Your account</h3></b>
index 0f908d6..ebba9ab 100644 (file)
--- a/pref.mlt
+++ b/pref.mlt
@@ -28,7 +28,7 @@ if $"cmd" = "mod" then
        %><h3><b>Preferences updated</b></h3><%
 end %>
 
-<form action="pref">
+<form action="pref" method="post">
 <input type="hidden" name="cmd" value="mod">
 <table>
 <tr> <td align="right"><input type="checkbox" name="dir"<% if Pref.hasDirectory you then %> checked<% end %>></td> <td>Include me in the public member directory.</td> </tr>
diff --git a/roll.mlt b/roll.mlt
new file mode 100644 (file)
index 0000000..16db145
--- /dev/null
+++ b/roll.mlt
@@ -0,0 +1,144 @@
+<% @header [("title", ["Roll call!"])];
+
+val you = Init.getUserId ();
+
+ref viewingCall = NONE;
+ref showNormal = true;
+
+if $"cmd" = "respond" then
+       showNormal := false;
+
+       val rol = Web.stoi ($"rol");
+       val code = $"code";
+       val rc = Roll.lookupEntry (rol, you);
+
+       if code = #code rc then
+               Roll.respond (rol, you);
+               %><h3><b>Your response has been saved.  Thank you!</b></h3><%
+       else
+               %><h3><b>Incorrect code!</b></h3><%
+       end
+elseif $"cmd" = "add" then
+       Group.requireGroupNum 0;
+       val title = $"title";
+       val msg = $"msg";
+       if title = "" then
+               %><h3><b>Your roll call must have a title.</b></h3><%
+       else
+               val id = Roll.addRollCall (title, msg);
+               viewingCall := SOME id;
+               %><h3><b>Roll call added!</b></h3><%
+       end
+
+elseif $"mod" <> "" then
+       Group.requireGroupNum 0;
+       showNormal := false;
+       val rc = Roll.lookupRollCall (Web.stoi ($"mod")) %>
+<h3><b>Modify roll call</b></h3>
+
+<form action="roll" method="post">
+<input type="hidden" name="id" value="<% $"mod" %>">
+<table>
+<tr> <td align="right"><b>Title</b>:</td> <td><input name="title" value="<% Web.html (#title rc) %>"></td> </tr>
+<tr> <td align="right" valign="top"><b>Message</b>:</td> <td><textarea name="msg" wrap="soft" rows="5" cols="80"><% Web.html (#msg rc) %></textarea></td> </tr>
+<tr> <td><input type="submit" name="cmd" value="Save"></td> </tr>
+</table>
+</form>
+
+<% elseif $"cmd" = "Save" then
+       Group.requireGroupNum 0;
+       val rc = Roll.lookupRollCall (Web.stoi ($"id"));
+
+       val title = $"title";
+       val msg = $"msg";
+       if title = "" then
+               %><h3><b>Your rol call must have a title.</b></h3><%
+       else
+               Roll.modRollCall {rc with title = title, msg = msg};
+               viewingCall := SOME (#id rc);
+               %><h3><b>Roll call saved.</b></h3><%
+       end
+
+elseif $"del" <> "" then
+       Group.requireGroupNum 0;
+       showNormal := false;
+       val rc = Roll.lookupRollCall (Web.stoi ($"del")) %>
+       <h3><b>Are you sure you want to delete roll call "<a href="roll?view=<% #id rc %>"><% Web.html (#title rc) %></a>"?</b></h3>
+       <a href="roll?del2=<% $"del" %>">Yes, delete <% Web.html (#title rc) %>!</a>
+
+<% elseif $"del2" <> "" then
+       Group.requireGroupNum 0;
+       val rc = Roll.lookupRollCall (Web.stoi ($"del2"));
+       Roll.deleteRollCall (Web.stoi ($"del2")) %>
+       <h3><b><% Web.html (#title rc) %> deleted!</b></h3>
+
+<% elseif $"cmd" = "mailall" then
+       Group.requireGroupNum 0;
+       showNormal := false;
+       ref first = true %>
+
+<a href="mailto:<% foreach uname in Roll.activeUsernames () do
+       if first then
+               first := false
+       else
+               %>,<%
+       end
+       uname; Init.emailSuffix
+end %>">Mail everyone!</a>
+
+<% elseif $"view" <> "" then
+       Group.requireGroupNum 0;
+       val id = Web.stoi ($"view");
+       viewingCall := SOME id
+end;
+
+if showNormal then
+       Group.requireGroupNum 0;
+
+       switch viewingCall of
+                 NONE => %>
+<h3><b>Existing roll calls</b></h3>
+
+<% foreach rc in Roll.listRollCalls () do %>
+<li> <a href="roll?view=<% #id rc %>"><% Web.html (#title rc) %></a> (<% #started rc %>)</li>
+<% end %>
+
+<h3><b>New roll call</b></h3>
+
+<form action="roll" method="post">
+<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" valign="top"><b>Message</b>:</td> <td><textarea name="msg" wrap="soft" rows="5" cols="80"></textarea></td> </tr>
+<tr> <td><input type="submit" value="Create"></td> </tr>
+</table>
+</form>
+
+<a href="roll?cmd=mailall">Mail everyone! (provides mailto: link)</a>
+
+<%             | SOME id =>
+       val rc = Roll.lookupRollCall id %>
+       <h3><b>Roll call "<% Web.html (#title rc) %>"</b></h3>
+       <table border="0">
+       <tr> <td align="right"><b>Started:</b></td> <td><% #started rc %></td> </tr>
+       <tr> <td align="right" valign="top"><b>Message:</b></td> <td><% Web.htmlNl (#msg rc) %></td> </tr>
+       </table>
+       [ <a href="roll?mod=<% id %>">Modify</a> | <a href="roll?del=<% id %>">Delete</a> ]<br>
+
+<% val (didnt, did) = Roll.listEntries id %>
+
+<h2><b>Didn't respond yet:</b></h2>
+<% foreach (usr, ent) in didnt do %>
+<li> <a href="user?id=<% #id usr %>"><% Web.html (#name usr) %></a></li>
+<% end %>
+
+<h2><b>Responded:</b></h2>
+<% foreach (usr, ent) in did do %>
+<li> <a href="user?id=<% #id usr %>"><% Web.html (#name usr) %></a> (<%
+       switch #responded ent of SOME st => st end %>)</li>
+<% end %>
+
+<% end
+end %>
+
+<% @footer[] %>
\ No newline at end of file
diff --git a/roll.sig b/roll.sig
new file mode 100644 (file)
index 0000000..bbadf2d
--- /dev/null
+++ b/roll.sig
@@ -0,0 +1,28 @@
+signature ROLL =
+sig
+    val activeUsernames : unit -> string list
+
+    type roll_call = {
+        id : int,
+        title : string,
+        msg : string,
+        started : Init.C.timestamp
+    }
+
+    val addRollCall : string * string -> int
+    val modRollCall : roll_call -> unit
+    val deleteRollCall : int -> unit
+    val lookupRollCall : int -> roll_call
+    val listRollCalls : unit -> roll_call list
+
+    type roll_call_entry = {
+        rol : int,
+        usr : int,
+        code : string,
+        responded : Init.C.timestamp option
+    }
+
+    val listEntries : int -> (Init.user * roll_call_entry) list * (Init.user * roll_call_entry) list
+    val lookupEntry : int * int -> roll_call_entry
+    val respond : int * int -> unit
+end
diff --git a/roll.sml b/roll.sml
new file mode 100644 (file)
index 0000000..31eb9e8
--- /dev/null
+++ b/roll.sml
@@ -0,0 +1,136 @@
+structure Roll :> ROLL = struct
+
+open Init Sql Util
+
+fun activeUsernames () =
+    let
+       fun mkRow [name] = C.stringFromSql name
+         | mkRow row = rowError ("activeUsernames", row)
+    in
+       C.map (getDb ()) mkRow "SELECT name FROM WebUserActive ORDER BY name"
+    end
+
+type roll_call = {
+     id : int,
+     title : string,
+     msg : string,
+     started : C.timestamp
+}
+
+fun mkRollRow [id, title, msg, started] =
+    {id = C.intFromSql id, title = C.stringFromSql title,
+     msg = C.stringFromSql msg, started = C.timestampFromSql started}
+  | mkRollRow row = rowError ("roll", row)
+
+fun addRollCall (title, msg) =
+    let
+       val db = getDb ()
+       val id = nextSeq (db, "RollCallSeq")
+
+       fun addUser [uid, name] =
+           let
+               val uid = C.intFromSql uid
+               val name = C.stringFromSql name
+               val code = randomPassword ()
+
+               val _ = C.dml db ($`INSERT INTO RollCallEntry (rol, usr, code, responded)
+                                   VALUES (^(C.intToSql id), ^(C.intToSql uid), ^(C.stringToSql code), NULL)`)
+
+               val mail = Mail.mopen ()
+           in
+               Mail.mwrite (mail, "From: Hcoop Portal <hcoop");
+               Mail.mwrite (mail, emailSuffix);
+               Mail.mwrite (mail, ">\nTo: ");
+               Mail.mwrite (mail, name);
+               Mail.mwrite (mail, emailSuffix);
+               Mail.mwrite (mail, "\nSubject: HCoop Roll Call: ");
+               Mail.mwrite (mail, title);
+               Mail.mwrite (mail, "\n\n");
+               Mail.mwrite (mail, "The admins want to make sure that they can reach everyone by e-mail.  To let them know that you're here, please visit this URL:\n\t");
+               Mail.mwrite (mail, urlPrefix);
+               Mail.mwrite (mail, "roll?cmd=respond&rol=");
+               Mail.mwrite (mail, Int.toString id);
+               Mail.mwrite (mail, "&code=");
+               Mail.mwrite (mail, code);
+               Mail.mwrite (mail, "\nYou may have to hit \"reload\" in your web browser, if you aren't already logged into the portal.\n\n");
+               Mail.mwrite (mail, msg);
+               ignore (Mail.mclose mail)
+           end
+         | addUser row = rowError ("add roll entry", row)
+    in
+       C.dml db ($`INSERT INTO RollCall (id, title, msg, started)
+                   VALUES (^(C.intToSql id), ^(C.stringToSql title), ^(C.stringToSql msg),
+                           CURRENT_TIMESTAMP)`);
+       C.app db addUser "SELECT id, name FROM WebUserActive";
+       id
+    end
+
+fun modRollCall (rc : roll_call) =
+    let
+       val db = getDb ()
+    in
+       ignore (C.dml db ($`UPDATE RollCall SET
+                           title = ^(C.stringToSql (#title rc)),
+                           msg = ^(C.stringToSql (#msg rc))
+                           WHERE id = ^(C.intToSql (#id rc))`))
+    end
+
+fun deleteRollCall id =
+    ignore (C.dml (getDb ()) ($`DELETE FROM RollCall WHERE id = ^(C.intToSql id)`))
+
+fun lookupRollCall id =
+    case C.oneOrNoRows (getDb ()) ($`SELECT id, title, msg, started
+                                    FROM RollCall
+                                    WHERE id = ^(C.intToSql id)`) of
+       NONE => raise Fail "Roll call not found"
+      | SOME row => mkRollRow row
+
+fun listRollCalls id =
+    C.map (getDb ()) mkRollRow "SELECT id, title, msg, started FROM RollCall ORDER BY started DESC"
+
+type roll_call_entry = {
+     rol : int,
+     usr : int,
+     code : string,
+     responded : C.timestamp option
+}
+
+fun mkEntryRow [rol, usr, code, responded] =
+    {rol = C.intFromSql rol, usr = C.intFromSql usr,
+     code = C.stringFromSql code,
+     responded = (if C.isNull responded then NONE else SOME (C.timestampFromSql responded))}
+  | mkEntryRow row = rowError ("roll entry", row)
+
+fun lookupEntry (rol, usr) =
+    case C.oneOrNoRows (getDb ()) ($`SELECT rol, usr, code, responded
+                                    FROM RollCallEntry
+                                    WHERE rol = ^(C.intToSql rol) AND usr = ^(C.intToSql usr)`) of
+       NONE => raise Fail "Roll call entry not found"
+      | SOME row => mkEntryRow row
+
+fun listEntries id =
+    let
+       fun folder (_ :: row, (didnt, did)) =
+           let
+               val ent = mkEntryRow row
+               val uent = (Init.lookupUser (#usr ent), ent)
+           in
+               case #responded ent of
+                   NONE => (uent :: didnt, did)
+                 | SOME _ => (didnt, uent :: did)
+           end
+         | folder (row, _) = rowError ("listEntries folder", row)
+    in
+       C.fold (getDb ()) folder ([], [])
+              ($`SELECT name, rol, usr, code, responded
+                 FROM RollCallEntry JOIN WebUser ON id = usr
+                 WHERE rol = ^(C.intToSql id)
+                 ORDER BY responded, name DESC`)
+    end
+
+fun respond (rol, usr) =
+    ignore (C.dml (getDb ()) ($`UPDATE RollCallEntry
+                               SET responded = CURRENT_TIMESTAMP
+                               WHERE rol = ^(C.intToSql rol) AND usr = ^(C.intToSql usr)`))
+
+end
index 2af6750..a9c64c9 100644 (file)
@@ -26,7 +26,7 @@ if $"sub" <> "" then
 
 <h3><b>Modify category</b></h3>
 
-<form action="support">
+<form action="support" method="post">
 <input type="hidden" name="save" value="<% id %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% Web.html (#name cat) %>"></td> </tr>
@@ -86,7 +86,7 @@ if showNormal then %>
 
 <h3><b>Add new category</b></h3>
 
-<form action="support">
+<form action="support" method="post">
 <input type="hidden" name="cmd" value="add">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name"></td> </tr>
index 2f40212..555d81b 100644 (file)
@@ -251,8 +251,23 @@ CREATE VIEW WebUserActive
        AS SELECT id, name, rname, bal, joined, app
                FROM WebUser
                        LEFT OUTER JOIN (SELECT usr FROM Membership JOIN WebGroup
-                               ON grp = WebGroup.id AND WebGroup.name = 'retired') AS mem
+                               ON grp = WebGroup.id AND (WebGroup.name IN ('retired', 'phantom'))) AS mem
                                ON usr = WebUser.id 
-
                WHERE usr IS NULL;
 
+CREATE TABLE RollCall(
+       id INTEGER PRIMARY KEY,
+       title TEXT NOT NULL,
+       msg TEXT NOT NULL,
+       started TIMESTAMP NOT NULL);
+
+CREATE SEQUENCE RollCallSeq START 1;
+
+CREATE TABLE RollCallEntry(
+       rol INTEGER NOT NULL,
+       usr INTEGER NOT NULL,
+       code TEXT NOT NULL,
+       responded TIMESTAMP,
+       PRIMARY KEY (rol, usr),
+       FOREIGN KEY (rol) REFERENCES RollCall(id) ON DELETE CASCADE,
+       FOREIGN KEY (usr) REFERENCES WebUser(id) ON DELETE CASCADE);
index 1a60143..fbd60aa 100644 (file)
--- a/users.mlt
+++ b/users.mlt
@@ -40,7 +40,7 @@ elseif $"mod" <> "" then
        val user = Init.lookupUser (Web.stoi ($"mod")) %>
 <h3><b>Modify member record</b></h3>
 
-<form action="users">
+<form action="users" method="post">
 <input type="hidden" name="id" value="<% $"mod" %>">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name" value="<% #name user %>"></td> </tr>
@@ -79,7 +79,7 @@ elseif $"mod" <> "" then
 <% if showNormal then %>
 <h3><b>New member</b></h3>
 
-<form action="users">
+<form action="users" method="post">
 <table>
 <tr> <td align="right"><b>Name</b>:</td> <td><input name="name"></td> </tr>
 <tr> <td align="right"><b>Real name</b>:</td> <td><input name="rname"></td> </tr>
index 801044e..9ac38d0 100644 (file)
--- a/util.sml
+++ b/util.sml
@@ -30,7 +30,7 @@ fun makeSet f items =
 fun neg (r : real) = ~r
 fun add (r1 : real, r2) = r1 + r2
 
-fun isIdent ch = Char.isLower ch orelse Char.isDigit ch
+fun isIdent ch = Char.isLower ch orelse Char.isDigit ch orelse ch = #"-"
 
 fun validHost s =
     size s > 0 andalso size s < 20 andalso List.all isIdent (String.explode s)