signature INPUT = sig
con paper :: {Type}
constraint [Id, Document] ~ paper
type userId
val userId_inj : sql_injectable_prim userId
table user : {Id : userId, Nam : string, Password : string, Chair : bool, OnPc : bool}
PRIMARY KEY Id,
CONSTRAINT Nam UNIQUE Nam
type paperId
val paperId_inj : sql_injectable_prim paperId
val paperId_show : show paperId
val paperId_read : read paperId
val paperId_eq : eq paperId
table paper : ([Id = paperId, Document = blob] ++ paper)
PRIMARY KEY Id
con review :: {Type}
constraint [Paper, User] ~ review
table review : ([Paper = paperId, User = userId] ++ review)
PRIMARY KEY (Paper, User)
val checkLogin : transaction (option {Id : userId, Nam : string, Chair : bool, OnPc : bool})
val getLogin : transaction {Id : userId, Nam : string, Chair : bool, OnPc : bool}
val getPcLogin : transaction {Id : userId, Nam : string, Chair : bool}
val checkChair : transaction unit
val summarizePaper : ctx ::: {Unit} -> [[Body] ~ ctx] => $paper -> xml ([Body] ++ ctx) [] []
end
signature OUTPUT = sig
con paper :: {Type}
type userId
type paperId
val linksForPc : xbody
val linksForChair : xbody
con yourPaperTables :: {{Type}}
constraint [Paper] ~ yourPaperTables
val joinYourPaper : tabs ::: {{Type}} -> paper ::: {Type}
-> [[Paper] ~ tabs] => [[Paper] ~ yourPaperTables] => [tabs ~ yourPaperTables] => [[Id] ~ paper] =>
userId
-> sql_from_items ([Paper = [Id = paperId] ++ paper] ++ tabs)
-> sql_from_items (yourPaperTables ++ [Paper = [Id = paperId] ++ paper] ++ tabs)
end
open Meta
functor Make(M : sig
con paper :: {(Type * Type)}
constraint [Id, Document, Authors] ~ paper
val paper : $(map Meta.meta paper)
val paperFolder : folder paper
con paperPrivate :: {Type}
constraint [Id, Document, Authors] ~ paperPrivate
constraint paper ~ paperPrivate
val paperPrivate : $(map Meta.private paperPrivate)
val paperPrivateFolder : folder paperPrivate
con review :: {(Type * Type)}
constraint [Paper, User] ~ review
val review : $(map Meta.meta review)
val reviewFolder : folder review
val submissionDeadline : time
val summarizePaper : ctx ::: {Unit} -> [[Body] ~ ctx] => $(map fst paper ++ paperPrivate)
-> xml ([Body] ++ ctx) [] []
functor Make (M : INPUT where con paper = map fst paper ++ paperPrivate
where con review = map fst review)
: OUTPUT where con paper = map fst paper ++ paperPrivate
where con userId = M.userId
where con paperId = M.paperId
end) = struct
table user : {Id : int, Nam : string, Password : string, Chair : bool, OnPc : bool}
PRIMARY KEY Id,
CONSTRAINT Nam UNIQUE Nam
sequence userId
con paper = [Id = int, Document = blob] ++ map fst M.paper ++ M.paperPrivate
table paper : paper
PRIMARY KEY Id
sequence paperId
table authorship : {Paper : int, User : int}
PRIMARY KEY (Paper, User),
CONSTRAINT Paper FOREIGN KEY Paper REFERENCES paper(Id) ON DELETE CASCADE,
CONSTRAINT User FOREIGN KEY User REFERENCES user(Id)
con review = [Paper = int, User = int] ++ map fst M.review
table review : review
PRIMARY KEY (Paper, User),
CONSTRAINT Paper FOREIGN KEY Paper REFERENCES paper(Id),
CONSTRAINT User FOREIGN KEY User REFERENCES user(Id)
sequence reviewId
cookie login : {Id : int, Password : string}
val checkLogin =
r <- getCookie login;
case r of
None => return None
| Some r =>
oneOrNoRows1 (SELECT user.Id, user.Nam, user.Chair, user.OnPc
FROM user
WHERE user.Id = {[r.Id]}
AND user.Password = {[r.Password]})
val getLogin =
ro <- checkLogin;
case ro of
None => error You must be logged in to do that.
| Some r => return r
val getPcLogin =
r <- getLogin;
if r.OnPc then
return (r -- #OnPc)
else
error You are not on the PC.
val checkChair =
r <- getLogin;
if r.Chair then
return ()
else
error You are not a chair.
structure O = M.Make(struct
val user = user
val paper = paper
val review = review
val checkLogin = checkLogin
val getLogin = getLogin
val getPcLogin = getPcLogin
val checkChair = checkChair
val summarizePaper = @@M.summarizePaper
end)
val checkOnPc =
r <- getLogin;
if r.OnPc then
return ()
else
error You aren't authorized to do that.
fun checkPaper id =
r <- getLogin;
if r.OnPc then
return ()
else
error You aren't authorized to see that paper.
structure Users = BulkEdit.Make(struct
con keyName = #Id
val visible = {Nam = string "Name",
Chair = bool "Chair?",
OnPc = bool "On PC?"}
val title = "Users"
val isAllowed =
me <- checkLogin;
return (Option.isSome me)
val t = user
end)
fun doRegister r =
n <- oneRowE1 (SELECT COUNT( * ) AS N
FROM user
WHERE user.Nam = {[r.Nam]});
if n > 0 then
register (Some "Sorry; that username is taken.")
else
id <- nextval userId;
dml (INSERT INTO user (Id, Nam, Password, Chair, OnPc)
VALUES ({[id]}, {[r.Nam]}, {[r.Password]}, FALSE, FALSE));
setCookie login {Id = id, Password = r.Password};
main ()
and register msg = return
Registering a New Account
{case msg of
None =>
| Some msg =>
{[msg]}
}
and signin r =
ro <- oneOrNoRowsE1 (SELECT user.Id AS N
FROM user
WHERE user.Nam = {[r.Nam]}
AND user.Password = {[r.Password]});
(case ro of
None => return ()
| Some id => setCookie login {Id = id, Password = r.Password});
m <- main' ();
return
{case ro of
None =>
Invalid username or password.
| _ => }
{m}
and main' () =
me <- checkLogin;
now <- now;
return
and main () =
m <- main' ();
return {m}
and submit () =
let
fun doSubmit r =
me <- getLogin;
coauthors <- List.mapM (fn name => oneOrNoRowsE1 (SELECT user.Id AS N
FROM user
WHERE user.Nam = {[name.Nam]})) r.Authors;
if List.exists Option.isNone coauthors then
error At least one of those coauthor usernames isn't registered.
else
id <- nextval paperId;
dml (insert paper ({Id = sql_inject id, Document = sql_inject (fileData r.Document)}
++ ensql M.paper (r -- #Authors -- #Document) M.paperFolder
++ initialize M.paperPrivate M.paperPrivateFolder));
List.app (fn uid =>
case uid of
None => error Impossible empty uid!
| Some uid => dml (INSERT INTO authorship (Paper, User)
VALUES ({[id]}, {[uid]})))
(Some me.Id :: coauthors);
return
Thanks for submitting!
in
me <- getLogin;
numAuthors <- Dnat.zero;
return
and all () =
checkOnPc;
listPapers (SELECT paper.Id, paper.{{map fst M.paper ++ M.paperPrivate}} FROM paper)
and your () =
me <- getLogin;
listPapers (sql_query {Rows = sql_query1 {Distinct = False,
From = O.joinYourPaper me.Id (sql_from_table [#Paper] paper),
Where = (WHERE TRUE),
GroupBy = sql_subset_all [_],
Having = (WHERE TRUE),
SelectFields = sql_subset [[Paper =
([Id = _]
++ map fst M.paper
++ M.paperPrivate, _)]
++ map (fn ts => ([], ts))
O.yourPaperTables],
SelectExps = {}},
OrderBy = sql_order_by_Nil [_],
Limit = sql_no_limit,
Offset = sql_no_offset})
and one id =
let
fun newReview r =
me <- getLogin;
checkPaper id;
dml (insert review ({Paper = sql_inject id, User = sql_inject me.Id}
++ ensql M.review r M.reviewFolder));
one id
fun saveReview r =
me <- getLogin;
checkPaper id;
dml (update [map fst M.review] ! (ensql M.review r M.reviewFolder)
review (WHERE T.Paper = {[id]} AND T.User = {[me.Id]}));
one id
in
me <- getLogin;
checkPaper id;
ro <- oneOrNoRows (SELECT paper.{{map fst M.paper}}, octet_length(paper.Document) AS N
FROM paper
WHERE paper.Id = {[id]});
authors <- queryX (SELECT user.Nam
FROM authorship
JOIN user ON authorship.User = user.Id
WHERE authorship.Paper = {[id]})
(fn r =>
{[r.User.Nam]}
);
myReview <- oneOrNoRows1 (SELECT review.{{map fst M.review}}
FROM review
WHERE review.User = {[me.Id]}
AND review.Paper = {[id]});
otherReviews <- queryX (SELECT user.Nam, review.{{map fst M.review}}
FROM review JOIN user ON review.User = user.Id
WHERE review.Paper = {[id]}
AND review.User <> {[me.Id]})
(fn r => User: {[r.User.Nam]}
{allContent M.review r.Review M.reviewFolder}
);
case ro of
None => error Paper not found!
| Some r => return
Paper #{[id]}
Authors:
{authors}
{allContent M.paper r.Paper M.paperFolder}
{if r.N = 0 then
No paper uploaded yet.
else
Download paper ({[r.N]} bytes)}
{case myReview of
None =>
Add Your Review
| Some myReview =>
Edit Your Review
}
Other reviews
{otherReviews}
end
and download id =
checkPaper id;
ro <- oneOrNoRows (SELECT paper.Document
FROM paper
WHERE paper.Id = {[id]});
case ro of
None => error Paper not found!
| Some r => returnBlob r.Paper.Document (blessMime "application/pdf")
end
functor Join(M : sig
structure O1 : OUTPUT
structure O2 : OUTPUT where con paper = O1.paper
where con userId = O1.userId
where con paperId = O1.paperId
constraint O1.yourPaperTables ~ O2.yourPaperTables
end)
= struct
open M
open O1
val linksForPc = {O1.linksForPc}{O2.linksForPc}
val linksForChair = {O1.linksForChair}{O2.linksForChair}
con yourPaperTables = O1.yourPaperTables ++ O2.yourPaperTables
constraint [Paper] ~ yourPaperTables
fun joinYourPaper [tabs] [paper] [[Paper] ~ tabs] [[Paper] ~ _] [tabs ~ yourPaperTables] [[Id] ~ paper]
uid (fi : sql_from_items ([Paper = [Id = paperId] ++ paper] ++ tabs)) =
O2.joinYourPaper uid (O1.joinYourPaper uid fi)
end