use activitypub::collection::OrderedCollection; use rocket::{ request::LenientForm, response::{Redirect, Flash} }; use rocket_contrib::Template; use serde_json; use std::{collections::HashMap, borrow::Cow}; use validator::{Validate, ValidationError, ValidationErrors}; use plume_common::activity_pub::{ActivityStream, ApRequest}; use plume_common::utils; use plume_models::{ blog_authors::*, blogs::*, db_conn::DbConn, instance::Instance, posts::Post, users::User }; use routes::Page; #[get("/~/?", rank = 2)] fn paginated_details(name: String, conn: DbConn, user: Option, page: Page) -> Template { may_fail!(user, Blog::find_by_fqn(&*conn, name), "Requested blog couldn't be found", |blog| { let posts = Post::blog_page(&*conn, &blog, page.limits()); let articles = Post::get_for_blog(&*conn, &blog); let authors = &blog.list_authors(&*conn); Template::render("blogs/details", json!({ "blog": &blog.to_json(&*conn), "account": user, "is_author": user.map(|x| x.is_author_in(&*conn, blog.clone())), "posts": posts.into_iter().map(|p| p.to_json(&*conn)).collect::>(), "authors": authors.into_iter().map(|u| u.to_json(&*conn)).collect::>(), "n_authors": authors.len(), "n_articles": articles.len(), "page": page.page, "n_pages": Page::total(articles.len() as i32) })) }) } #[get("/~/", rank = 3)] fn details(name: String, conn: DbConn, user: Option) -> Template { paginated_details(name, conn, user, Page::first()) } #[get("/~/", rank = 1)] fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream { let blog = Blog::find_local(&*conn, name).unwrap(); ActivityStream::new(blog.into_activity(&*conn)) } #[get("/blogs/new")] fn new(user: User) -> Template { Template::render("blogs/new", json!({ "account": user, "errors": null, "form": null })) } #[get("/blogs/new", rank = 2)] fn new_auth() -> Flash{ utils::requires_login("You need to be logged in order to create a new blog", uri!(new)) } #[derive(FromForm, Validate, Serialize)] struct NewBlogForm { #[validate(custom(function = "valid_slug", message = "Invalid name"))] pub title: String } fn valid_slug(title: &str) -> Result<(), ValidationError> { let slug = utils::make_actor_id(title.to_string()); if slug.len() == 0 { Err(ValidationError::new("empty_slug")) } else { Ok(()) } } #[post("/blogs/new", data = "")] fn create(conn: DbConn, data: LenientForm, user: User) -> Result { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); let mut errors = match form.validate() { Ok(_) => ValidationErrors::new(), Err(e) => e }; if let Some(_) = Blog::find_local(&*conn, slug.clone()) { errors.add("title", ValidationError { code: Cow::from("existing_slug"), message: Some(Cow::from("A blog with the same name already exists.")), params: HashMap::new() }); } if errors.is_empty() { let blog = Blog::insert(&*conn, NewBlog::new_local( slug.clone(), form.title.to_string(), String::from(""), Instance::local_id(&*conn) )); blog.update_boxes(&*conn); BlogAuthor::insert(&*conn, NewBlogAuthor { blog_id: blog.id, author_id: user.id, is_owner: true }); Ok(Redirect::to(uri!(details: name = slug.clone()))) } else { println!("{:?}", errors); Err(Template::render("blogs/new", json!({ "account": user, "errors": errors.inner(), "form": form }))) } } #[get("/~//outbox")] fn outbox(name: String, conn: DbConn) -> ActivityStream { let blog = Blog::find_local(&*conn, name).unwrap(); blog.outbox(&*conn) }