summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lock.ur27
-rw-r--r--lock.urs16
-rw-r--r--main.ur156
-rw-r--r--urwiki.urp2
4 files changed, 115 insertions, 86 deletions
diff --git a/lock.ur b/lock.ur
new file mode 100644
index 0000000..9accf81
--- /dev/null
+++ b/lock.ur
@@ -0,0 +1,27 @@
+(* Copyright 2015 the Massachusetts Institute of Technology
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License. *)
+
+table lock : { Title : string,
+ Holder : client }
+ PRIMARY KEY Title
+
+fun take page_title =
+ new_holder <- self;
+ result <- tryDml (INSERT INTO lock (Title, Holder)
+ VALUES ({[page_title]}, {[new_holder]}));
+ return (Option.isNone result)
+
+fun release page_title =
+ requestor <- self;
+ dml (DELETE FROM lock
+ WHERE Holder = {[requestor]})
diff --git a/lock.urs b/lock.urs
new file mode 100644
index 0000000..f7282cc
--- /dev/null
+++ b/lock.urs
@@ -0,0 +1,16 @@
+(* Copyright 2015 the Massachusetts Institute of Technology
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License. *)
+
+val take : string -> transaction bool
+
+val release : string -> transaction unit
diff --git a/main.ur b/main.ur
index 4110c96..6478dd1 100644
--- a/main.ur
+++ b/main.ur
@@ -20,16 +20,11 @@ type id = int
table commit : { Id : id,
Created : time,
+ Title : string,
Content : string }
PRIMARY KEY Id
sequence commit_id_next
-table article : { Title : string,
- Head : id }
- PRIMARY KEY Title,
- CONSTRAINT Head
- FOREIGN KEY Head REFERENCES commit(Id)
-
datatype mode = View | Edit
val eq_mode =
mkEq (fn x y =>
@@ -44,90 +39,79 @@ fun only_in mode_source target_mode =
then null
else Style.invisible)
-fun commit_article title text : transaction unit =
+fun current_revision title =
+ commits <- queryL1 (SELECT commit.Created, commit.Title, commit.Content
+ FROM commit
+ WHERE commit.Title = {[title]}
+ ORDER BY commit.Created ASC);
+ return (case commits of
+ Nil => {Title = title, Body = "Not found."}
+ | art :: _ => {Title = art.Title,
+ Body = show art.Content})
+
+fun create_commit title text : transaction unit =
id <- nextval commit_id_next;
creation_time <- now;
- dml (INSERT INTO commit (Id, Created, Content)
- VALUES ({[id]}, {[creation_time]}, {[text]}));
- (* TODO(bbaren): This is ugly. Use CAS instead? *)
- sql_error <- tryDml (INSERT INTO article (Title, Head)
- VALUES ({[title]}, {[id]}));
- case sql_error of
- None =>
- (* We created a new article. *)
- return ()
- | Some _ =>
- (* The article already exists. *)
- dml (UPDATE article
- SET Head = {[id]}
- WHERE Title = {[title]})
+ dml (INSERT INTO commit (Id, Created, Title, Content)
+ VALUES ({[id]}, {[creation_time]}, {[title]}, {[text]}))
fun wiki requested_article_title =
(* Look up the article. *)
- extant_articles <-
- queryL (SELECT article.Title, commit.Content
- FROM article LEFT JOIN commit ON article.Head = commit.Id
- WHERE article.Title = {[requested_article_title]});
- let
- val article =
- case extant_articles of
- Nil => {Title = requested_article_title, Body = "Not found."}
- | art :: Nil => {Title = art.Article.Title,
- Body = show art.Commit.Content}
- | _ :: _ :: _ => error
- <xml>
- Multiple articles with title
- ‘{[requested_article_title]}’
- </xml>
- in
- (* Stuff the article text in a source so we can live-update it as the user
- edits. *)
- article_body_source <- source article.Body;
- (* Initially, we're in View mode, and we can switch to Edit mode on user
- request. *)
- page_mode <- source View;
- return
- <xml>
- <head>
- <title>
- {[if article.Title = Configuration.main_page_name
- then Configuration.wiki_title
- else article.Title ^ " – " ^ Configuration.wiki_title]}
- </title>
- <link rel="stylesheet" href="/urwiki.css" />
- </head>
- <body>
- (* Page headings *)
- <div>
- <h1>{[Configuration.wiki_title]}</h1>
- <ul>
- <li>
- <a link={wiki Configuration.main_page_name}>
- {[Configuration.main_page_name]}
- </a>
- </li>
- </ul>
+ article <- current_revision requested_article_title;
+ (* Stuff the article text in a source so we can live-update it as the user
+ edits. *)
+ article_body_source <- source article.Body;
+ (* Initially, we're in View mode, and we can switch to Edit mode on user
+ request. *)
+ page_mode <- source View;
+ return
+ <xml>
+ <head>
+ <title>
+ {[if article.Title = Configuration.main_page_name
+ then Configuration.wiki_title
+ else article.Title ^ " – " ^ Configuration.wiki_title]}
+ </title>
+ <link rel="stylesheet" href="/urwiki.css" />
+ </head>
+ <body>
+ (* Page headings *)
+ <div>
+ <h1>{[Configuration.wiki_title]}</h1>
+ <ul>
+ <li>
+ <a link={wiki Configuration.main_page_name}>
+ {[Configuration.main_page_name]}
+ </a>
+ </li>
+ </ul>
+ </div>
+ (* Article *)
+ <dyn signal={text <- signal article_body_source;
+ return <xml>{[text]}</xml>} /><br />
+ (* Editing panel *)
+ <div>
+ (* Controls for View mode *)
+ <div dynClass={only_in page_mode View}>
+ <button
+ value="Edit"
+ onclick={fn _ =>
+ lock_result <- rpc (Lock.take article.Title);
+ if lock_result
+ then set page_mode Edit
+ else return ()} />
</div>
- (* Article *)
- <dyn signal={text <- signal article_body_source;
- return <xml>{[text]}</xml>} /><br />
- (* Editing panel *)
- <div>
- (* Controls for View mode *)
- <div dynClass={only_in page_mode View}>
- <button value="Edit" onclick={fn _ => set page_mode Edit} />
- </div>
- (* Controls for Edit mode *)
- <div dynClass={only_in page_mode Edit}>
- <ctextarea source={article_body_source} /><br />
- <button
- value="Commit"
- onclick={fn _ =>
- text <- get article_body_source;
- rpc (commit_article article.Title text);
- set page_mode View} />
- </div>
+ (* Controls for Edit mode *)
+ <div dynClass={only_in page_mode Edit}>
+ <ctextarea source={article_body_source} /><br />
+ <button
+ value="Commit"
+ onclick={fn _ =>
+ text <- get article_body_source;
+ rpc (create_commit article.Title text);
+ rpc (Lock.release article.Title);
+ set page_mode View} />
</div>
- </body>
- </xml>
- end
+ </div>
+ </body>
+ </xml>
diff --git a/urwiki.urp b/urwiki.urp
index 48ffebb..066918c 100644
--- a/urwiki.urp
+++ b/urwiki.urp
@@ -1,5 +1,7 @@
allow url /urwiki.css
file /urwiki.css urwiki.css
+$/option
style
+lock
main