Add basic /_changes page
This commit is contained in:
parent
370cbd0c24
commit
f22ffc112a
6 changed files with 173 additions and 0 deletions
82
src/changes_resource.rs
Normal file
82
src/changes_resource.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use futures::{self, Future};
|
||||
use hyper;
|
||||
use hyper::header::ContentType;
|
||||
use hyper::server::*;
|
||||
|
||||
use assets::StyleCss;
|
||||
use mimes::*;
|
||||
use site::Layout;
|
||||
use state::State;
|
||||
use web::{Resource, ResponseFuture};
|
||||
|
||||
pub struct ChangesResource {
|
||||
state: State,
|
||||
before: Option<i32>,
|
||||
}
|
||||
|
||||
impl ChangesResource {
|
||||
pub fn new(state: State, before: Option<i32>) -> Self {
|
||||
Self { state, before }
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for ChangesResource {
|
||||
fn allow(&self) -> Vec<hyper::Method> {
|
||||
use hyper::Method::*;
|
||||
vec![Options, Head, Get]
|
||||
}
|
||||
|
||||
fn head(&self) -> ResponseFuture {
|
||||
Box::new(futures::finished(Response::new()
|
||||
.with_status(hyper::StatusCode::Ok)
|
||||
.with_header(ContentType(TEXT_HTML.clone()))
|
||||
))
|
||||
}
|
||||
|
||||
fn get(self: Box<Self>) -> ResponseFuture {
|
||||
use chrono::{TimeZone, Local};
|
||||
|
||||
struct Row {
|
||||
article_id: i32,
|
||||
revision: i32,
|
||||
created: String,
|
||||
|
||||
slug: String,
|
||||
title: String,
|
||||
|
||||
latest: bool,
|
||||
}
|
||||
|
||||
#[derive(BartDisplay)]
|
||||
#[template="templates/changes.html"]
|
||||
struct Template<'a> {
|
||||
changes: &'a [Row],
|
||||
}
|
||||
|
||||
let data = self.state.get_article_revision_stubs(self.before, 30);
|
||||
let head = self.head();
|
||||
|
||||
Box::new(data.join(head)
|
||||
.and_then(move |(data, head)| {
|
||||
use std::iter::Iterator;
|
||||
|
||||
let changes = &data.into_iter().map(|x| {
|
||||
Row {
|
||||
article_id: x.article_id,
|
||||
revision: x.revision,
|
||||
created: Local.from_utc_datetime(&x.created).to_string(),
|
||||
slug: x.slug,
|
||||
title: x.title,
|
||||
latest: x.latest,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
Ok(head
|
||||
.with_body(Layout {
|
||||
title: "Changes",
|
||||
body: &Template { changes },
|
||||
style_css_checksum: StyleCss::checksum(),
|
||||
}.to_string()))
|
||||
}))
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ use std::net::SocketAddr;
|
|||
mod article_redirect_resource;
|
||||
mod article_resource;
|
||||
mod assets;
|
||||
mod changes_resource;
|
||||
mod db;
|
||||
mod mimes;
|
||||
mod models;
|
||||
|
|
|
@ -14,3 +14,17 @@ pub struct ArticleRevision {
|
|||
|
||||
pub latest: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Queryable)]
|
||||
pub struct ArticleRevisionStub {
|
||||
pub sequence_number: i32,
|
||||
|
||||
pub article_id: i32,
|
||||
pub revision: i32,
|
||||
pub created: chrono::NaiveDateTime,
|
||||
|
||||
pub slug: String,
|
||||
pub title: String,
|
||||
|
||||
pub latest: bool,
|
||||
}
|
||||
|
|
29
src/state.rs
29
src/state.rs
|
@ -100,6 +100,35 @@ impl State {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_article_revision_stubs(&self, before: Option<i32>, limit: i32) -> CpuFuture<Vec<models::ArticleRevisionStub>, Error> {
|
||||
let connection_pool = self.connection_pool.clone();
|
||||
|
||||
self.cpu_pool.spawn_fn(move || {
|
||||
use schema::article_revisions;
|
||||
|
||||
let query = article_revisions::table
|
||||
.order(article_revisions::sequence_number.desc())
|
||||
.limit(limit as i64)
|
||||
.select((
|
||||
article_revisions::sequence_number,
|
||||
article_revisions::article_id,
|
||||
article_revisions::revision,
|
||||
article_revisions::created,
|
||||
article_revisions::slug,
|
||||
article_revisions::title,
|
||||
article_revisions::latest,
|
||||
))
|
||||
.into_boxed();
|
||||
|
||||
let query = match before {
|
||||
Some(before) => query.filter(article_revisions::sequence_number.lt(before)),
|
||||
None => query
|
||||
};
|
||||
|
||||
Ok(query.load(&*connection_pool.get()?)?)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lookup_slug(&self, slug: String) -> CpuFuture<SlugLookup, Error> {
|
||||
#[derive(Queryable)]
|
||||
struct ArticleRevisionStub {
|
||||
|
|
|
@ -17,6 +17,16 @@ lazy_static! {
|
|||
static ref LOOKUP_MAP: HashMap<String, ResourceFn> = {
|
||||
let mut lookup_map = HashMap::new();
|
||||
|
||||
use changes_resource::ChangesResource;
|
||||
|
||||
lookup_map.insert(
|
||||
"/_changes".to_string(),
|
||||
Box::new(|state: &State|
|
||||
// TODO Use query arguments to fill in the `before` parameter below
|
||||
Box::new(ChangesResource::new(state.clone(), None)) as BoxResource
|
||||
) as ResourceFn
|
||||
);
|
||||
|
||||
lookup_map.insert(
|
||||
"/_new".to_string(),
|
||||
Box::new(|state: &State|
|
||||
|
|
37
templates/changes.html
Normal file
37
templates/changes.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
<div class="container">
|
||||
<header>
|
||||
<h1>Changes</h1>
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Slug</th>
|
||||
<th>Article ID</th>
|
||||
<th>Revision</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
{{#changes}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#.latest?}}<a href="{{#..slug.is_empty()?}}.{{/..slug.is_empty()}}{{..slug}}">{{/.latest}}
|
||||
{{.title}}
|
||||
{{#.latest?}}</a>{{/.latest}}
|
||||
</td>
|
||||
<td>{{#.slug.is_empty()?}}/{{/.slug.is_empty()}}{{.slug}}</td>
|
||||
<td>{{.article_id}}</td>
|
||||
<td>{{.revision}}</td>
|
||||
<td>{{.created}}</td>
|
||||
</tr>
|
||||
{{/changes}}
|
||||
</table>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<ul>
|
||||
<li><a href="_new">Create article</a></li>
|
||||
</ul>
|
||||
<p>Powered by <a href="https://github.com/maghoff/sausagewiki">Sausagewiki</a></p>
|
||||
</footer>
|
Loading…
Reference in a new issue