Journal

Contents

1. Introduction

A Journal pattern for QM.  Based on Blog.r by Carl Sassenrath.  Requires the User Management pattern.

2. Model

2.1. models/journal.r

REBOL [
Title: "Journal Model"
Author: "Christopher Ross-Gill"
Type: 'roughcut
]
 
locate: func [id][pad id 4]
 
record: make record [
on-create: does [
set 'date now
set 'title ""
set 'comments 0
set 'tags []
]
 
title: link: when?: next?: previous?: none
 
on-load: does [
title: get 'title
link: pad id 4
when?: get 'date
next?: if 1 < length? owner [pad id + 1 4]
previous?: if id > 1 [pad id - 1 4]
]
 
insert-post: injects [
title: string! length is between [1 52] else "No Title"
date: date! else "Journal entries require a date"
text: string! length is less-than 150'000
]
 
insert-input: injects [title: opt string! date: opt string! text: opt string!]
 
on-save: does [
save-text
]
 
save-text: has [text][
if text: get 'text [
unset 'text
write path/entry.txt text
]
]
 
get-text: does [
either new? [""][
any [read path/entry.txt ""]
]
]
 
comment-count: comments?: has [out /short][
pluralize either short ["Cmt"]["Comment"] any [get 'comments 0]
]
 
age?: does [
how-far? any [get 'date now/date]
]
 
rank: 0
 
finds?: func [query /local content] [
; Show the results of a blog search, listed by search-hit rank:
unless string? query [return none]
 
rank: 0
query: parse query none
 
foreach term query [
if find/any title term [rank: rank + 4]
content: get-text
while [content: find/any/tail content term][rank: rank + 1]
]
rank > 0
]
]
 
how-far?: func [
"Convert date/time to a friendly format."
[catch] date [date!]
/local diff
][
diff: now/date - date/date
return case [
diff < 2 ["Today"]
diff < 3 ["Yesterday"]
diff < 7 ["This week"]
diff < 14 ["This fortnight"]
date/month = now/month ["This month"]
date/month = (now/month - 1) ["Last month"]
date/month = (now/month + 11) ["Last month"]
date/year = now/year ["This year"]
date/year = (now/year - 1) ["Last year"]
date ["A while back"]
]
]

3. Controller

3.1. controllers/journal.r

REBOL [
Title: "QM Journal"
Type: 'controller
Default: "main"
]
 
event "prepare" does [
id: edit: none date: now
format: func [text][
rejoin ["<p>" replace/all copy sanitize text newline <br /> "</p>"]
]
]
 
protect "new" "create" "edit" "store" (not user/has-role? 'editor) [
redirect-to %/user/sign-in
]
 
action "main" does [
title: header/title
reverse entries: copy skip tail journal -2
date: now + 1
]
 
action "index" [page: opt integer!] does [
entries: paginate journal page
title: reform ["Page" entries/current "of" entries/last]
]
 
action "show" [id: integer!] does [
entry: select journal id
 
if assert-all [
entry [render/status "Not Found" 404]
][
title: entry/title
]
]
 
action "new" does [
entry: select journal 'new [
title: "New Journal Entry"
]
]
 
action "create" does [
entry: select journal 'new
details: get-param/body 'entry
 
either entry/insert-post details [
entry/set 'id 1 + length? journal
entry/set 'author user/id
entry/store
redirect-to journal/show/(entry/id)
][
entry/insert-input details
render %new.rsp
]
]
 
action "edit" [id: integer!] does [
either entry: select journal id [
title: entry/title
][
title: "Not Found"
render/status "Not Found" 404
]
]
 
action "store" [id: integer!] does [
entry: select journal id
details: get-param/body 'entry
 
if assert-all [
entry [
title: "Not Found"
render/status "Not Found" 404
]
entry/insert-post details [
title: entry/title
entry/insert-input details
render %edit.rsp
]
][
entry/set 'editor user/id
entry/store
redirect-to journal/show/(entry/id)
]
]
 
action "search" does [
title: "Search Results"
entries: select journal [finds? get-param 'q]
sort/compare entries func [a b][a/rank > b/rank]
]

4. Views

4.1. views/journal/main.rsp

<div>
<% foreach entry entries [ %><% if date <> date: entry/when?/date [ %>
<h2><%= form-date date "%A, %B %e%i, %Y" %></h2>
<% ] %>
<!-- \entry --><div class="entry">
<!-- \content --><h3 class="entry-title"><%: a journal/show/(entry/link) %><%= entry/title %></a></h3>
<div class="entry-content">
<%= format entry/get-text %>
<!-- /content --></div>
<!-- /entry --></div>
<% ] %>
</div>

4.2. views/journal/index.rsp

<%: form get %/journal/search %><fieldset>
<legend>Search</legend>
<input type="text" size="25" name="q"></p>
</fieldset></form>
 
<p><a href="/journal">Main page</a>
|| <a href="/journal/index">Index</a><% if entries/previous [ %>
|| <%: a (link-to journal/index/(entries/previous)) %>Previous Page</a><% ] if entries/next [ %>
|| <%: a (link-to journal/index/(entries/next)) %>Next Page</a><% ] %></p>
 
<table class="index"><% foreach entry entries/records [ %><tr>
<td><%= form-date/gmt entry/get 'date "%e-%b-%Y" %></td>
<td><%: a journal/show/(entry/link) %><%= entry/title %></a></td>
<td>[<%= entry/link %>]</td>
<td><%= entry/comment-count/short %></td>
</tr><% ] %></table>

4.3. views/journal/show.rsp

<div class="hfeed">
<div class="hentry" id="post-<% entry/link %>">
 
<h3>Article #<%= entry/link %>&#8212;<%= form-date entry/when? "%A, %B %e%i, %Y" %></h3>
 
<div class="entry-content">
<%= format entry/get-text %>
</div>
 
<p><a href="/journal">Main page</a>
|| <a href="/journal/index">Index</a><% if user/has-role? 'editor [ %>
|| <%: a journal/edit/(entry/link) %>Edit</a><% ] if entry/previous? [ %>
|| <%: a journal/show/(entry/previous?) %>Previous</a><% ] if entry/next? [ %>
|| <%: a journal/show/(entry/next?) %>Next</a><% ] %>
|| <%: a journal/comments/(entry/link) %><%= entry/comments? %></p>
 
</div>
</div>

4.4. views/journal/edit.rsp

<h2>Edit Entry #<%= entry/link %></h2>
<%: form journal/store/(entry/link) %><fieldset>
<legend>Edit Journal Entry</legend>
<table><tr>
<th><label for="entry/date">Date:</label></th>
<td><%: field entry/date (entry/get 'date) %></td>
</tr><tr>
<th><label for="entry/title">Title:</label></th>
<td><%: field entry/title (entry/get 'title) %></td>
</tr><tr>
<th><label for="entry/text">Text:</label></th>
<td><%: area entry/text (sanitize entry/get-text) 25x10 %></td>
</tr><tr>
<td></td>
<td><%: submit do "Save" %>
</tr></table>
</fieldset></form>

4.5. views/journal/search.rsp

<%: form get %/journal/search %><fieldset>
<legend>Search</legend>
<table><tr><input type="text" size="25" name="q"></tr></table>
</fieldset></form>
 
<p><a href="/journal">Main page</a>
|| <a href="/journal/index">Index</a></p>
 
<% either empty? entries [ %><p>No Matches</p><% ][
%><table class="index"><% foreach entry entries [ %><tr>
<td><%= entry/age? %></td>
<td><%: a journal/show/(entry/link) %><%= entry/title %></a></td>
<td>[<%= entry/link %>]</td>
<td><%= entry/comment-count/short %></td>
</tr><% ] %></table><% ] %>