Compare commits
No commits in common. "dd6b8c76bad4350b9e810f4144270aef77d99642" and "9fd75149270cf7fe7cc056e389a4b6c46222c5f2" have entirely different histories.
dd6b8c76ba
...
9fd7514927
6 changed files with 75 additions and 214 deletions
|
@ -82,7 +82,6 @@ impl CommentId {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_base64(s: &str) -> Result<Self, base64::DecodeError> {
|
pub fn from_base64(s: &str) -> Result<Self, base64::DecodeError> {
|
||||||
// TODO prevent panic when s is too long
|
|
||||||
let mut buf = [0; 16];
|
let mut buf = [0; 16];
|
||||||
base64::decode_config_slice(s, base64::URL_SAFE_NO_PAD, &mut buf).map(|_| Self(buf))
|
base64::decode_config_slice(s, base64::URL_SAFE_NO_PAD, &mut buf).map(|_| Self(buf))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{config::Config, db::*, queries::*};
|
use crate::{config::Config, db::*};
|
||||||
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::{net::IpAddr, str::FromStr};
|
use std::{net::IpAddr, str::FromStr};
|
||||||
|
@ -30,18 +30,13 @@ pub fn approve_comment(comment_id: CommentId, dbs: &Dbs) -> Result<(), sled::Err
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_comment(comment_id: CommentId, dbs: &Dbs) -> Result<Option<Comment>, sled::Error> {
|
pub fn remove_pending_comment(
|
||||||
|
comment_id: CommentId,
|
||||||
|
dbs: &Dbs,
|
||||||
|
) -> Result<Option<Comment>, sled::Error> {
|
||||||
if let Some(comment) = dbs.comment.remove(&comment_id)? {
|
if let Some(comment) = dbs.comment.remove(&comment_id)? {
|
||||||
dbs.comment_pending.remove(&(
|
dbs.comment_pending
|
||||||
comment.topic_hash.clone(),
|
.remove(&(comment.topic_hash.clone(), comment.post_time, comment_id))?;
|
||||||
comment.post_time,
|
|
||||||
comment_id.clone(),
|
|
||||||
))?;
|
|
||||||
dbs.comment_approved.remove(&(
|
|
||||||
comment.topic_hash.clone(),
|
|
||||||
comment.post_time,
|
|
||||||
comment_id,
|
|
||||||
))?;
|
|
||||||
return Ok(Some(comment));
|
return Ok(Some(comment));
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -168,30 +163,6 @@ pub fn get_client_addr<State>(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_comment(config: &Config, comment: &CommentForm, errors: &mut Vec<String>) {
|
|
||||||
if comment.author.len() > config.comment_author_max_len {
|
|
||||||
errors.push(format!(
|
|
||||||
"Author name length is {} but maximum is {}.",
|
|
||||||
comment.author.len(),
|
|
||||||
config.comment_author_max_len
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if comment.email.len() > config.comment_email_max_len {
|
|
||||||
errors.push(format!(
|
|
||||||
"E-mail length is {} but maximum is {}.",
|
|
||||||
comment.email.len(),
|
|
||||||
config.comment_email_max_len
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if comment.text.len() > config.comment_text_max_len {
|
|
||||||
errors.push(format!(
|
|
||||||
"Comment length is {} but maximum is {}.",
|
|
||||||
comment.text.len(),
|
|
||||||
config.comment_text_max_len
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct AdminLoginQuery {
|
pub struct AdminLoginQuery {
|
||||||
|
@ -8,35 +8,37 @@ pub struct AdminLoginQuery {
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct AdminEditCommentQuery {
|
pub struct AdminEditCommentQuery {
|
||||||
pub author: String,
|
pub author: String,
|
||||||
pub id: String,
|
pub comment_id: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct AdminRmCommentQuery {
|
pub struct AdminRmCommentQuery {
|
||||||
pub id: String,
|
pub comment_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct NewCommentQuery {
|
pub struct NewCommentQuery {
|
||||||
#[serde(flatten)]
|
pub author: String,
|
||||||
pub comment: CommentForm,
|
pub email: String,
|
||||||
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct EditCommentQuery {
|
pub struct EditCommentQuery {
|
||||||
#[serde(flatten)]
|
pub author: String,
|
||||||
pub comment: CommentForm,
|
pub comment_id: String,
|
||||||
pub id: String,
|
pub email: String,
|
||||||
//pub token: String,
|
pub text: String,
|
||||||
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct RmCommentQuery {
|
pub struct RmCommentQuery {
|
||||||
pub id: String,
|
pub comment_id: String,
|
||||||
//pub token: String,
|
pub token: String,
|
||||||
}*/
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(tag = "a")]
|
#[serde(tag = "a")]
|
||||||
|
@ -45,8 +47,8 @@ pub enum CommentQuery {
|
||||||
NewComment(NewCommentQuery),
|
NewComment(NewCommentQuery),
|
||||||
#[serde(rename = "edit_comment")]
|
#[serde(rename = "edit_comment")]
|
||||||
EditComment(EditCommentQuery),
|
EditComment(EditCommentQuery),
|
||||||
/*#[serde(rename = "rm_comment")]
|
#[serde(rename = "rm_comment")]
|
||||||
RmComment(RmCommentQuery),*/
|
RmComment(RmCommentQuery),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
@ -69,15 +71,3 @@ pub struct ApproveQuery {
|
||||||
pub struct RemoveQuery {
|
pub struct RemoveQuery {
|
||||||
pub remove: String,
|
pub remove: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct EditQuery {
|
|
||||||
pub edit: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct CommentForm {
|
|
||||||
pub author: String,
|
|
||||||
pub email: String,
|
|
||||||
pub text: String,
|
|
||||||
}
|
|
||||||
|
|
150
src/server.rs
150
src/server.rs
|
@ -18,14 +18,7 @@ pub async fn run_server(config: Arc<Config>, dbs: Dbs, templates: Arc<Templates>
|
||||||
let templates = templates.clone();
|
let templates = templates.clone();
|
||||||
let dbs = dbs.clone();
|
let dbs = dbs.clone();
|
||||||
move |req: tide::Request<()>| {
|
move |req: tide::Request<()>| {
|
||||||
serve_comments(
|
serve_comments(req, config.clone(), templates.clone(), dbs.clone(), &[])
|
||||||
req,
|
|
||||||
config.clone(),
|
|
||||||
templates.clone(),
|
|
||||||
dbs.clone(),
|
|
||||||
Context::new(),
|
|
||||||
200,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
app.at(&format!("{}t/:topic", config.root_url)).post({
|
app.at(&format!("{}t/:topic", config.root_url)).post({
|
||||||
|
@ -63,9 +56,10 @@ async fn serve_comments<'a>(
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
templates: Arc<Templates>,
|
templates: Arc<Templates>,
|
||||||
dbs: Dbs,
|
dbs: Dbs,
|
||||||
mut context: Context,
|
errors: &[String],
|
||||||
status_code: u16,
|
|
||||||
) -> tide::Result<tide::Response> {
|
) -> tide::Result<tide::Response> {
|
||||||
|
dbg!(req.peer_addr());
|
||||||
|
|
||||||
let Ok(topic) = req.param("topic") else {
|
let Ok(topic) = req.param("topic") else {
|
||||||
return Err(tide::Error::from_str(404, "No topic"))
|
return Err(tide::Error::from_str(404, "No topic"))
|
||||||
};
|
};
|
||||||
|
@ -76,8 +70,10 @@ async fn serve_comments<'a>(
|
||||||
|
|
||||||
let topic_hash = TopicHash::from_topic(topic);
|
let topic_hash = TopicHash::from_topic(topic);
|
||||||
|
|
||||||
|
let mut context = Context::new();
|
||||||
context.insert("config", &config);
|
context.insert("config", &config);
|
||||||
context.insert("admin", &admin);
|
context.insert("admin", &admin);
|
||||||
|
context.insert("new_comment_errors", errors);
|
||||||
|
|
||||||
if admin {
|
if admin {
|
||||||
if let Ok(query) = req.query::<ApproveQuery>() {
|
if let Ok(query) = req.query::<ApproveQuery>() {
|
||||||
|
@ -89,28 +85,17 @@ async fn serve_comments<'a>(
|
||||||
}
|
}
|
||||||
if let Ok(query) = req.query::<RemoveQuery>() {
|
if let Ok(query) = req.query::<RemoveQuery>() {
|
||||||
if let Ok(comment_id) = CommentId::from_base64(&query.remove) {
|
if let Ok(comment_id) = CommentId::from_base64(&query.remove) {
|
||||||
helpers::remove_comment(comment_id, &dbs)
|
helpers::remove_pending_comment(comment_id, &dbs)
|
||||||
.map_err(|e| error!("Removing comment: {:?}", e))
|
.map_err(|e| error!("Removing comment: {:?}", e))
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(query) = req.query::<EditQuery>() {
|
|
||||||
if let Ok(comment_id) = CommentId::from_base64(&query.edit) {
|
|
||||||
if let Some(comment) = dbs.comment.get(&comment_id).unwrap() {
|
|
||||||
context.insert("edit_comment", &comment_id.to_base64());
|
|
||||||
context.insert("edit_comment_author", &comment.author);
|
|
||||||
context.insert("edit_comment_email", &comment.email);
|
|
||||||
context.insert("edit_comment_text", &comment.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.insert(
|
context.insert(
|
||||||
"comments_pending",
|
"comments_pending",
|
||||||
&helpers::iter_pending_comments_by_topic(topic_hash.clone(), &dbs)
|
&helpers::iter_pending_comments_by_topic(topic_hash.clone(), &dbs)
|
||||||
.map(|(comment_id, comment)| CommentWithId {
|
.map(|(comment_id, comment)| CommentWithId {
|
||||||
author: comment.author,
|
author: comment.author,
|
||||||
editable: admin,
|
|
||||||
id: comment_id.to_base64(),
|
id: comment_id.to_base64(),
|
||||||
needs_approval: true,
|
needs_approval: true,
|
||||||
post_time: comment.post_time,
|
post_time: comment.post_time,
|
||||||
|
@ -125,7 +110,6 @@ async fn serve_comments<'a>(
|
||||||
&helpers::iter_approved_comments_by_topic(topic_hash, &dbs)
|
&helpers::iter_approved_comments_by_topic(topic_hash, &dbs)
|
||||||
.map(|(comment_id, comment)| CommentWithId {
|
.map(|(comment_id, comment)| CommentWithId {
|
||||||
author: comment.author,
|
author: comment.author,
|
||||||
editable: admin,
|
|
||||||
id: comment_id.to_base64(),
|
id: comment_id.to_base64(),
|
||||||
needs_approval: false,
|
needs_approval: false,
|
||||||
post_time: comment.post_time,
|
post_time: comment.post_time,
|
||||||
|
@ -134,10 +118,12 @@ async fn serve_comments<'a>(
|
||||||
.collect::<Vec<CommentWithId>>(),
|
.collect::<Vec<CommentWithId>>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(tide::Response::builder(status_code)
|
Ok(
|
||||||
|
tide::Response::builder(if errors.is_empty() { 200 } else { 400 })
|
||||||
.content_type(tide::http::mime::HTML)
|
.content_type(tide::http::mime::HTML)
|
||||||
.body(templates.tera.render("comments.html", &context)?)
|
.body(templates.tera.render("comments.html", &context)?)
|
||||||
.build())
|
.build(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn serve_admin<'a>(
|
async fn serve_admin<'a>(
|
||||||
|
@ -169,7 +155,6 @@ async fn serve_admin<'a>(
|
||||||
})?;
|
})?;
|
||||||
Some(CommentWithId {
|
Some(CommentWithId {
|
||||||
author: comment.author,
|
author: comment.author,
|
||||||
editable: true,
|
|
||||||
id: comment_id.to_base64(),
|
id: comment_id.to_base64(),
|
||||||
needs_approval: true,
|
needs_approval: true,
|
||||||
post_time: comment.post_time,
|
post_time: comment.post_time,
|
||||||
|
@ -206,11 +191,7 @@ async fn handle_post_comments(
|
||||||
dbs: Dbs,
|
dbs: Dbs,
|
||||||
notify_send: Sender<()>,
|
notify_send: Sender<()>,
|
||||||
) -> tide::Result<tide::Response> {
|
) -> tide::Result<tide::Response> {
|
||||||
let admin = req.cookie("admin").map_or(false, |psw| {
|
let client_addr = if config.antispam_enable {
|
||||||
check_admin_password_hash(&config, &String::from(psw.value()))
|
|
||||||
});
|
|
||||||
|
|
||||||
let client_addr = if !admin && config.antispam_enable {
|
|
||||||
match helpers::get_client_addr(&config, &req) {
|
match helpers::get_client_addr(&config, &req) {
|
||||||
Some(Ok(addr)) => {
|
Some(Ok(addr)) => {
|
||||||
if config.antispam_whitelist.contains(&addr) {
|
if config.antispam_whitelist.contains(&addr) {
|
||||||
|
@ -233,7 +214,6 @@ async fn handle_post_comments(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let mut context = Context::new();
|
|
||||||
|
|
||||||
match req.body_form::<CommentQuery>().await? {
|
match req.body_form::<CommentQuery>().await? {
|
||||||
CommentQuery::NewComment(query) => {
|
CommentQuery::NewComment(query) => {
|
||||||
|
@ -241,8 +221,27 @@ async fn handle_post_comments(
|
||||||
return Err(tide::Error::from_str(404, "No topic"))
|
return Err(tide::Error::from_str(404, "No topic"))
|
||||||
};
|
};
|
||||||
|
|
||||||
helpers::check_comment(&config, &query.comment, &mut errors);
|
if query.author.len() > config.comment_author_max_len {
|
||||||
|
errors.push(format!(
|
||||||
|
"Author name length is {} but maximum is {}.",
|
||||||
|
query.author.len(),
|
||||||
|
config.comment_author_max_len
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if query.email.len() > config.comment_email_max_len {
|
||||||
|
errors.push(format!(
|
||||||
|
"E-mail length is {} but maximum is {}.",
|
||||||
|
query.email.len(),
|
||||||
|
config.comment_email_max_len
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if query.text.len() > config.comment_text_max_len {
|
||||||
|
errors.push(format!(
|
||||||
|
"Comment length is {} but maximum is {}.",
|
||||||
|
query.text.len(),
|
||||||
|
config.comment_text_max_len
|
||||||
|
));
|
||||||
|
}
|
||||||
if let Some(client_addr) = &client_addr {
|
if let Some(client_addr) = &client_addr {
|
||||||
if let Some(antispam_timeout) =
|
if let Some(antispam_timeout) =
|
||||||
helpers::antispam_check_client_mutation(client_addr, &dbs, &config).unwrap()
|
helpers::antispam_check_client_mutation(client_addr, &dbs, &config).unwrap()
|
||||||
|
@ -268,95 +267,25 @@ async fn handle_post_comments(
|
||||||
|
|
||||||
let comment = Comment {
|
let comment = Comment {
|
||||||
topic_hash,
|
topic_hash,
|
||||||
author: query.comment.author,
|
author: query.author,
|
||||||
email: if query.comment.email.is_empty() {
|
email: if query.email.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(query.comment.email)
|
Some(query.email)
|
||||||
},
|
},
|
||||||
last_edit_time: None,
|
last_edit_time: None,
|
||||||
post_time: time,
|
post_time: time,
|
||||||
text: query.comment.text,
|
text: query.text,
|
||||||
};
|
};
|
||||||
helpers::new_pending_comment(&comment, &dbs)
|
helpers::new_pending_comment(&comment, &dbs)
|
||||||
.map_err(|e| error!("Adding pending comment: {:?}", e))
|
.map_err(|e| error!("Adding pending comment: {:?}", e))
|
||||||
.ok();
|
.ok();
|
||||||
notify_send.send(()).ok();
|
notify_send.send(()).ok();
|
||||||
} else {
|
|
||||||
context.insert("new_comment_author", &query.comment.author);
|
|
||||||
context.insert("new_comment_email", &query.comment.email);
|
|
||||||
context.insert("new_comment_text", &query.comment.text);
|
|
||||||
}
|
|
||||||
context.insert("new_comment_errors", &errors);
|
|
||||||
}
|
|
||||||
CommentQuery::EditComment(query) => {
|
|
||||||
if !admin {
|
|
||||||
return Err(tide::Error::from_str(403, "Forbidden"));
|
|
||||||
}
|
|
||||||
|
|
||||||
helpers::check_comment(&config, &query.comment, &mut errors);
|
|
||||||
|
|
||||||
let comment_id = if let Ok(comment_id) = CommentId::from_base64(&query.id) {
|
|
||||||
comment_id
|
|
||||||
} else {
|
|
||||||
return Err(tide::Error::from_str(400, "Invalid comment id"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut comment = if let Some(comment) = dbs.comment.get(&comment_id).unwrap() {
|
|
||||||
comment
|
|
||||||
} else {
|
|
||||||
return Err(tide::Error::from_str(404, "Not found"));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(client_addr) = &client_addr {
|
|
||||||
if let Some(antispam_timeout) =
|
|
||||||
helpers::antispam_check_client_mutation(client_addr, &dbs, &config).unwrap()
|
|
||||||
{
|
|
||||||
errors.push(format!(
|
|
||||||
"The edition quota from your IP is reached. You will be unblocked in {}s.",
|
|
||||||
antispam_timeout
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
if errors.is_empty() {
|
|
||||||
if let Some(client_addr) = &client_addr {
|
|
||||||
helpers::antispam_update_client_mutation(client_addr, &dbs).unwrap();
|
|
||||||
}
|
}
|
||||||
|
serve_comments(req, config, templates, dbs, &errors).await
|
||||||
let time = std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_secs();
|
|
||||||
|
|
||||||
comment.author = query.comment.author;
|
|
||||||
comment.email = if query.comment.email.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(query.comment.email)
|
|
||||||
};
|
|
||||||
comment.text = query.comment.text;
|
|
||||||
comment.last_edit_time = Some(time);
|
|
||||||
|
|
||||||
dbs.comment.insert(&comment_id, &comment).unwrap();
|
|
||||||
} else {
|
|
||||||
context.insert("edit_comment", &comment_id.to_base64());
|
|
||||||
context.insert("edit_comment_author", &query.comment.author);
|
|
||||||
context.insert("edit_comment_email", &query.comment.email);
|
|
||||||
context.insert("edit_comment_text", &query.comment.text);
|
|
||||||
}
|
|
||||||
context.insert("edit_comment_errors", &errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serve_comments(
|
|
||||||
req,
|
|
||||||
config,
|
|
||||||
templates,
|
|
||||||
dbs,
|
|
||||||
context,
|
|
||||||
if errors.is_empty() { 200 } else { 400 },
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_post_admin(
|
async fn handle_post_admin(
|
||||||
|
@ -367,7 +296,6 @@ async fn handle_post_admin(
|
||||||
) -> tide::Result<tide::Response> {
|
) -> tide::Result<tide::Response> {
|
||||||
if let Some(psw) = req.cookie("admin") {
|
if let Some(psw) = req.cookie("admin") {
|
||||||
if check_admin_password(&config, &String::from(psw.value())).is_some() {
|
if check_admin_password(&config, &String::from(psw.value())).is_some() {
|
||||||
#[allow(clippy::match_single_binding)]
|
|
||||||
match req.body_form::<AdminQuery>().await? {
|
match req.body_form::<AdminQuery>().await? {
|
||||||
_ => serve_admin(req, config, templates, dbs).await,
|
_ => serve_admin(req, config, templates, dbs).await,
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ impl Templates {
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct CommentWithId {
|
pub struct CommentWithId {
|
||||||
pub author: String,
|
pub author: String,
|
||||||
pub editable: bool,
|
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub needs_approval: bool,
|
pub needs_approval: bool,
|
||||||
pub post_time: Time,
|
pub post_time: Time,
|
||||||
|
|
|
@ -8,17 +8,14 @@
|
||||||
{% if comments_pending %}
|
{% if comments_pending %}
|
||||||
<div id="comments_pending">
|
<div id="comments_pending">
|
||||||
{% for comment in comments_pending %}
|
{% for comment in comments_pending %}
|
||||||
<div class="comment{% if comment.needs_approval %} comment_pending{% endif %}" id="comment-{{ comment.id | safe }}">
|
<div class="comment{% if comment.needs_approval %} comment_pending{% endif %}" id="comment-{{ comment.id }}">
|
||||||
<span class="comment-author">{{ comment.author }}</span>
|
<span class="comment-author">{{ comment.author }}</span>
|
||||||
<span class="comment-date">{{ comment.post_time | date(format="%F %R", locale=config.lang) }}</span>
|
<span class="comment-date">{{ comment.post_time | date(format="%F %R", locale=config.lang) }}</span>
|
||||||
{% if comment.editable %}
|
|
||||||
<a href="?edit={{ comment.id | safe }}#edit_comment-form">Edit</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if admin and comment.needs_approval %}
|
{% if admin and comment.needs_approval %}
|
||||||
<a href="?approve={{ comment.id | safe }}">Approve</a>
|
<a href="?approve={{ comment.id }}">Approve</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if admin %}
|
{% if admin %}
|
||||||
<a href="?remove={{ comment.id | safe }}">Remove</a>
|
<a href="?remove={{ comment.id }}">Remove</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p class="comment-text">{{ comment.text }}</p>
|
<p class="comment-text">{{ comment.text }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,17 +24,14 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="comments">
|
<div id="comments">
|
||||||
{% for comment in comments %}
|
{% for comment in comments %}
|
||||||
<div class="comment{% if comment.needs_approval %} comment_pending{% endif %}" id="comment-{{ comment.id | safe }}">
|
<div class="comment{% if comment.needs_approval %} comment_pending{% endif %}" id="comment-{{ comment.id }}">
|
||||||
<span class="comment-author">{{ comment.author }}</span>
|
<span class="comment-author">{{ comment.author }}</span>
|
||||||
<span class="comment-date">{{ comment.post_time | date(format="%F %R", locale=config.lang) }}</span>
|
<span class="comment-date">{{ comment.post_time | date(format="%F %R", locale=config.lang) }}</span>
|
||||||
{% if comment.editable %}
|
|
||||||
<a href="?edit={{ comment.id | safe }}#edit_comment-form">Edit</a>
|
|
||||||
{% endif %}
|
|
||||||
{% if admin and comment.needs_approval %}
|
{% if admin and comment.needs_approval %}
|
||||||
<a href="?approve={{ comment.id | safe }}">Approve</a>
|
<a href="?approve={{ comment.id }}">Approve</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if admin %}
|
{% if admin %}
|
||||||
<a href="?remove={{ comment.id | safe }}">Remove</a>
|
<a href="?remove={{ comment.id }}">Remove</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p class="comment-text">{{ comment.text }}</p>
|
<p class="comment-text">{{ comment.text }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,32 +47,12 @@
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<label for="new_comment-author">Your name:</label>
|
<label for="new_comment-author">Your name:</label>
|
||||||
<input type="text" id="new_comment-author" name="author" maxlength="{{ config.comment_author_max_len | safe }}"{% if new_comment_author %} value="{{ new_comment_author }}"{% endif %}/><br/>
|
<input type="text" id="new_comment-author" name="author" required maxlenth="{{ config.comment_author_max_len }}"/><br/>
|
||||||
<label for="new_comment-email">Your e-mail:</label>
|
<label for="new_comment-email">Your e-mail:</label>
|
||||||
<input type="email" id="new_comment-email" name="email" maxlength="{{ config.comment_email_max_len | safe }}"{% if new_comment_email %} value="{{ new_comment_email }}"{% endif %}/><br/>
|
<input type="email" id="new_comment-email" name="email" maxlength="{{ config.comment_email_max_len }}"/><br/>
|
||||||
<label for="new_comment-text">Your comment:</label><br/>
|
<label for="new_comment-text">Your comment:</label><br/>
|
||||||
<textarea id="new_comment-text" name="text" maxlength="{{ config.comment_text_max_len | safe }}">{% if new_comment_text %}{{ new_comment_text }}{% endif %}</textarea><br/>
|
<textarea id="new_comment-text" name="text" maxlength="{{ config.comment_text_max_len }}"></textarea><br/>
|
||||||
<button type="submit" name="a" value="new_comment">Post comment</button>
|
<button type="submit" name="a" value="new_comment">Post comment</button>
|
||||||
</form>
|
</form>
|
||||||
{% if edit_comment %}
|
|
||||||
<form id="edit_comment-form" action="#edit_comment-form" method="post">
|
|
||||||
{% if edit_comment_errors %}
|
|
||||||
<p>Whoops, the following error occurred:</p>
|
|
||||||
<ul id="edit_comment-errors" class="errors">
|
|
||||||
{% for error in edit_comment_errors %}
|
|
||||||
<li class="error">{{ error | safe }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
<input type="hidden" name="id" value="{{ edit_comment | safe }}" autocomplete="off"/>
|
|
||||||
<label for="edit_comment-author">Your name:</label>
|
|
||||||
<input type="text" id="edit_comment-author" name="author" maxlength="{{ config.comment_author_max_len | safe }}"{% if edit_comment_author %} value="{{ edit_comment_author }}"{% endif %}/><br/>
|
|
||||||
<label for="edit_comment-email">Your e-mail:</label>
|
|
||||||
<input type="email" id="edit_comment-email" name="email" maxlength="{{ config.comment_email_max_len | safe }}"{% if edit_comment_email %} value="{{ edit_comment_email }}"{% endif %}/><br/>
|
|
||||||
<label for="edit_comment-text">Your comment:</label><br/>
|
|
||||||
<textarea id="edit_comment-text" name="text" maxlength="{{ config.comment_text_max_len | safe }}">{% if edit_comment_text %}{{ edit_comment_text }}{% endif %}</textarea><br/>
|
|
||||||
<button type="submit" name="a" value="edit_comment">Edit comment</button>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue