open User sequence eventIds policy sendOwnIds eventIds type event = int table event : {Id : event, Creator : user, Time : time, Title : string, Desc : string} PRIMARY KEY Id, CONSTRAINT Creator FOREIGN KEY Creator REFERENCES user(Id) policy sendClient (SELECT * FROM event, user WHERE event.Creator = user.Id AND known(user.Pass)) policy mayInsert (SELECT * FROM event AS New, user WHERE New.Creator = user.Id AND known(user.Pass) AND known(New.Title) AND known(New.Desc)) policy mayUpdate (SELECT * FROM event AS New, event AS Old, user WHERE Old.Id = New.Id AND Old.Creator = user.Id AND New.Creator = Old.Creator AND known(user.Pass) AND known(New.Title) AND known(New.Desc)) policy mayDelete (SELECT * FROM event AS Old, user WHERE Old.Creator = user.Id AND known(user.Pass)) table attendee : {Event : event, User : user} PRIMARY KEY (Event, User), CONSTRAINT Event FOREIGN KEY Event REFERENCES event(Id) ON DELETE CASCADE, CONSTRAINT User FOREIGN KEY User REFERENCES user(Id) ON DELETE CASCADE fun aclPolicy tab = also (mayInsert (SELECT * FROM tab AS New, event, user WHERE known(user.Pass) AND New.Event = event.Id AND event.Creator = user.Id)) (mayDelete (SELECT * FROM tab AS Old, event, user WHERE known(user.Pass) AND Old.Event = event.Id AND event.Creator = user.Id)) policy sendClient (SELECT event.Id, event.Creator, event.Time, event.Title, event.Desc FROM event, user, attendee WHERE known(user.Pass) AND attendee.Event = event.Id AND attendee.User = user.Id) policy aclPolicy attendee table timeOnly : {Event : event, User : user} PRIMARY KEY (Event, User), CONSTRAINT Event FOREIGN KEY Event REFERENCES event(Id) ON DELETE CASCADE, CONSTRAINT User FOREIGN KEY User REFERENCES user(Id) ON DELETE CASCADE policy sendClient (SELECT event.Id, event.Time FROM event, user, timeOnly WHERE known(user.Pass) AND timeOnly.Event = event.Id AND timeOnly.User = user.Id) policy aclPolicy timeOnly val forceUserId = u <- User.userId (); case u of None => error <xml>You must log in to proceed further.</xml> | Some u => return u fun wrap titl bod = bl <- User.blurb (); return <xml> <head> <title>Calendar - {[titl]}</title> </head> <body> {bl} <h1>{[titl]}</h1> {bod} </body> </xml> fun idOf uname = uo <- oneOrNoRowsE1 (SELECT (user.Id) FROM user WHERE user.Nam = {[uname]}); case uo of None => error <xml>Username not found</xml> | Some u => return u fun display id = let fun delete () = u <- forceUserId; dml (DELETE FROM event WHERE Id = {[id]} AND Creator = {[u]}); main () fun update r = u <- forceUserId; dml (UPDATE event SET Time = {[readError r.Time]}, Title = {[r.Title]}, Desc = {[r.Desc]} WHERE Id = {[id]} AND Creator = {[u]}); display id fun allowed () = u <- forceUserId; b <- oneRowE1 (SELECT COUNT( * ) > 0 FROM event WHERE event.Id = {[id]} AND event.Creator = {[u]}); if b then return () else error <xml>You are not the creator of that event!</xml> fun adder tab r = allowed (); u <- idOf r.Nam; dml (DELETE FROM tab WHERE Event = {[id]} AND User = {[u]}); dml (INSERT INTO tab (Event, User) VALUES ({[id]}, {[u]})); display id fun addAttendee r = adder attendee r fun addTimeOnly r = adder timeOnly r in u <- forceUserId; ro <- oneOrNoRows (SELECT user.Nam, event.Time, event.Title, event.Desc, event.Creator FROM event, user WHERE event.Id = {[id]} AND event.Creator = user.Id AND event.Creator = {[u]} UNION SELECT user.Nam, event.Time, event.Title, event.Desc, event.Creator FROM event, user, attendee WHERE event.Id = {[id]} AND event.Creator = user.Id AND attendee.Event = event.Id AND attendee.User = {[u]}); case ro of None => ro <- oneOrNoRows (SELECT event.Time FROM event, timeOnly WHERE event.Id = {[id]} AND timeOnly.Event = event.Id AND timeOnly.User = {[u]}); (case ro of None => error <xml>Event not found</xml> | Some r => wrap ("Event: [title unavailable]") <xml> Time: {[r.Event.Time]}<br/> </xml>) | Some r => atts <- queryX1 (SELECT user.Id, user.Nam FROM attendee, user WHERE attendee.User = user.Id AND attendee.Event = {[id]} ORDER BY user.Nam) (fn r => <xml><li><a link={byId r.Id}>{[r.Nam]}</a></li></xml>); tos <- queryX1 (SELECT user.Id, user.Nam FROM timeOnly, user WHERE timeOnly.User = user.Id AND timeOnly.Event = {[id]} ORDER BY user.Nam) (fn r => <xml><li><a link={byId r.Id}>{[r.Nam]}</a></li></xml>); wrap ("Event: " ^ r.Event.Title) <xml> Creator: {[r.User.Nam]}<br/> Time: {[r.Event.Time]}<br/> <br/> {[r.Event.Desc]} <br/> <h4>Attendees:</h4> {atts} <h4>People who may know something is happening at this time:</h4> {tos} {if r.Event.Creator = u then <xml> <form> <submit value="Delete" action={delete}/> </form> <h2>Change event details</h2> <form><table> <tr> <th>Title:</th> <td><textbox{#Title} value={r.Event.Title}/></td> </tr> <tr> <th>Time:</th> <td><textbox{#Time} value={show r.Event.Time}/></td> </tr> <tr> <th>Description:</th> <td><textbox{#Desc} value={r.Event.Desc}/></td> </tr> <tr> <td><submit action={update}/></td> </tr> </table></form> <h2>Add attendee</h2> <form> <textbox{#Nam}/> <submit action={addAttendee}/> </form> <h2>Add time-only access</h2> <form> <textbox{#Nam}/> <submit action={addTimeOnly}/> </form> </xml> else <xml/>} <hr/> <a link={main ()}>Back to your calendar</a> </xml> end and add r = u <- forceUserId; id <- nextval eventIds; dml (INSERT INTO event (Id, Creator, Time, Title, Desc) VALUES ({[id]}, {[u]}, {[readError r.Time]}, {[r.Title]}, {[r.Desc]})); display id and main () = u <- userId (); case u of None => wrap "Main" <xml>You must log in to proceed further.</xml> | Some u => evs <- queryX (SELECT event.Id AS Id, event.Time AS Time, event.Title AS Title FROM event WHERE event.Creator = {[u]} UNION SELECT event.Id AS Id, event.Time AS Time, event.Title AS Title FROM event, attendee WHERE attendee.Event = event.Id AND attendee.User = {[u]} ORDER BY Time) (fn r => <xml> <hr/> <h3><a link={display r.Id}>{[r.Title]}</a></h3> Time: {[r.Time]}<br/> </xml>); wrap "Main" <xml> {evs} <hr/> <h2>Add event</h2> <form><table> <tr> <th>Title:</th> <td><textbox{#Title}/></td> </tr> <tr> <th>Time:</th> <td><textbox{#Time}/></td> </tr> <tr> <th>Description:</th> <td><textbox{#Desc}/></td> </tr> <tr> <td><submit action={add}/></td> </tr> </table></form> <hr/> <h2>View another user's calendar</h2> <form> <textbox{#Nam}/> <submit value="View" action={byName}/> </form> </xml> and byName r = uname <- return r.Nam; u <- idOf uname; other uname u and byId u = uname <- oneRowE1 (SELECT (user.Nam) FROM user WHERE user.Id = {[u]}); other uname u and other uname u = u' <- userId (); case u' of None => wrap "Someone else's calendar" <xml>You must log in to proceed further.</xml> | Some u' => evs <- queryX' (SELECT event.Id AS Id, event.Time AS Time FROM event WHERE event.Creator = {[u]} AND event.Creator = {[u']} UNION SELECT event.Id AS Id, event.Time AS Time FROM event, attendee AS You WHERE event.Creator = {[u]} AND You.Event = event.Id AND You.User = {[u']} UNION SELECT event.Id AS Id, event.Time AS Time FROM event, timeOnly AS You WHERE event.Creator = {[u]} AND You.Event = event.Id AND You.User = {[u']} UNION SELECT event.Id AS Id, event.Time AS Time FROM event, attendee AS Other WHERE event.Creator = {[u']} AND Other.Event = event.Id AND Other.User = {[u]} UNION SELECT event.Id AS Id, event.Time AS Time FROM event, attendee AS You, attendee AS Other WHERE You.Event = event.Id AND You.User = {[u']} AND Other.Event = event.Id AND Other.User = {[u]} UNION SELECT event.Id AS Id, event.Time AS Time FROM event, timeOnly AS You, attendee AS Other WHERE You.Event = event.Id AND You.User = {[u']} AND Other.Event = event.Id AND Other.User = {[u]} ORDER BY Time) (fn r => title <- oneOrNoRowsE1 (SELECT (event.Title) FROM event WHERE event.Id = {[r.Id]} AND event.Creator = {[u']} UNION SELECT (event.Title) FROM event, attendee WHERE event.Id = {[r.Id]} AND attendee.Event = event.Id AND attendee.User = {[u']}); return <xml> <hr/> <h3>{case title of None => <xml><i>Busy</i></xml> | Some title => <xml><a link={display r.Id}>{[title]}</a></xml>}</h3> Time: {[r.Time]}<br/> </xml>); wrap (uname ^ "'s Calendar") <xml> {evs} <hr/> <a link={main ()}>Back to your calendar</a> </xml>