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_redirect_resource;
|
||||||
mod article_resource;
|
mod article_resource;
|
||||||
mod assets;
|
mod assets;
|
||||||
|
mod changes_resource;
|
||||||
mod db;
|
mod db;
|
||||||
mod mimes;
|
mod mimes;
|
||||||
mod models;
|
mod models;
|
||||||
|
|
|
@ -14,3 +14,17 @@ pub struct ArticleRevision {
|
||||||
|
|
||||||
pub latest: bool,
|
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> {
|
pub fn lookup_slug(&self, slug: String) -> CpuFuture<SlugLookup, Error> {
|
||||||
#[derive(Queryable)]
|
#[derive(Queryable)]
|
||||||
struct ArticleRevisionStub {
|
struct ArticleRevisionStub {
|
||||||
|
|
|
@ -17,6 +17,16 @@ lazy_static! {
|
||||||
static ref LOOKUP_MAP: HashMap<String, ResourceFn> = {
|
static ref LOOKUP_MAP: HashMap<String, ResourceFn> = {
|
||||||
let mut lookup_map = HashMap::new();
|
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(
|
lookup_map.insert(
|
||||||
"/_new".to_string(),
|
"/_new".to_string(),
|
||||||
Box::new(|state: &State|
|
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