From c2303db1aa632091dbdaf3d59057d531b8a1267e Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 1 Dec 2019 17:21:19 -0800 Subject: [PATCH] Some RSS work. - Display rss buttons on front end for user, /c/all, and community pages. Fixes #348. - Some clean up and additions to RSS feeds. --- docker/dev/dev_deploy.sh | 11 ++ server/src/feeds.rs | 203 ++++++++++++++++++++++---------- server/src/lib.rs | 2 +- server/src/main.rs | 2 +- ui/src/components/community.tsx | 7 ++ ui/src/components/main.tsx | 9 +- ui/src/components/symbols.tsx | 4 + ui/src/components/user.tsx | 7 ++ 8 files changed, 177 insertions(+), 68 deletions(-) create mode 100755 docker/dev/dev_deploy.sh diff --git a/docker/dev/dev_deploy.sh b/docker/dev/dev_deploy.sh new file mode 100755 index 000000000..91ac6c5d9 --- /dev/null +++ b/docker/dev/dev_deploy.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Building from the dev branch for dev servers +git checkout dev + +# Rebuilding dev docker +docker-compose build +docker tag dev_lemmy:latest dessalines/lemmy:dev +docker push dessalines/lemmy:dev + +git checkout master diff --git a/server/src/feeds.rs b/server/src/feeds.rs index 4cd3d9cbb..8a1db28bc 100644 --- a/server/src/feeds.rs +++ b/server/src/feeds.rs @@ -1,20 +1,19 @@ -extern crate rss; extern crate htmlescape; +extern crate rss; use super::*; -use crate::Settings; -use crate::db::{establish_connection, ListingType, SortType}; +use crate::db::community::Community; use crate::db::community_view::SiteView; use crate::db::post_view::PostView; use crate::db::user::User_; -use crate::db::community::Community; -use actix_web::{HttpResponse, web, Result}; +use crate::db::{establish_connection, ListingType, SortType}; +use crate::Settings; use actix_web::body::Body; -use rss::{ChannelBuilder, Item, ItemBuilder}; +use actix_web::{web, HttpResponse, Result}; use diesel::result::Error; -use std::str::FromStr; -use self::rss::Guid; +use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder}; use serde::Deserialize; +use std::str::FromStr; use strum::ParseError; #[derive(Deserialize)] @@ -29,65 +28,104 @@ enum RequestType { } pub fn get_all_feed(info: web::Query) -> HttpResponse { - let sort_type = get_sort_type(info); - if sort_type.is_err() { - return HttpResponse::BadRequest().finish(); - } - - let result = get_feed_internal(&sort_type.unwrap(), RequestType::All, None); - return match result { - Ok(rss) => HttpResponse::Ok() - .content_type("application/rss+xml") - .body(rss), - Err(_) => HttpResponse::InternalServerError().finish(), + let sort_type = match get_sort_type(info) { + Ok(sort_type) => sort_type, + Err(_) => return HttpResponse::BadRequest().finish(), }; + + match get_feed_internal(&sort_type, RequestType::All, None) { + Ok(rss) => HttpResponse::Ok() + .content_type("application/rss+xml") + .body(rss), + Err(_) => HttpResponse::InternalServerError().finish(), + } } pub fn get_feed(path: web::Path<(char, String)>, info: web::Query) -> HttpResponse { - let sort_type = get_sort_type(info); - if sort_type.is_err() { - return HttpResponse::BadRequest().finish(); - } + let sort_type = match get_sort_type(info) { + Ok(sort_type) => sort_type, + Err(_) => return HttpResponse::BadRequest().finish(), + }; let request_type = match path.0 { 'u' => RequestType::User, - 'c' => RequestType::Community, + 'c' => RequestType::Community, _ => return HttpResponse::NotFound().finish(), }; - let result = get_feed_internal(&sort_type.unwrap(), request_type, Some(path.1.clone())); - if result.is_ok() { - let rss = result.unwrap(); - return HttpResponse::Ok() + match get_feed_internal(&sort_type, request_type, Some(path.1.to_owned())) { + Ok(rss) => HttpResponse::Ok() .content_type("application/rss+xml") - .body(rss); - } else { - let error = result.err().unwrap(); - return match error { - Error::NotFound => HttpResponse::NotFound().finish(), - _ => HttpResponse::InternalServerError().finish(), - } + .body(rss), + Err(_) => HttpResponse::NotFound().finish(), } } fn get_sort_type(info: web::Query) -> Result { - let sort_query = info.sort.clone().unwrap_or(SortType::Hot.to_string()); - return SortType::from_str(&sort_query); + let sort_query = info.sort.to_owned().unwrap_or(SortType::Hot.to_string()); + SortType::from_str(&sort_query) } -fn get_feed_internal(sort_type: &SortType, request_type: RequestType, name: Option) - -> Result { +fn get_feed_internal( + sort_type: &SortType, + request_type: RequestType, + name: Option, +) -> Result { let conn = establish_connection(); let mut community_id: Option = None; let mut creator_id: Option = None; + + let site_view = SiteView::read(&conn)?; + + let mut channel_builder = ChannelBuilder::default(); + + // TODO do channel image, need to externalize + match request_type { - RequestType::All =>(), - RequestType::Community => community_id = Some(Community::read_from_name(&conn,name.unwrap())?.id), - RequestType::User => creator_id = Some(User_::find_by_email_or_username(&conn,&name.unwrap())?.id), + RequestType::All => { + channel_builder + .title(htmlescape::encode_minimal(&site_view.name)) + .link(format!("https://{}", Settings::get().hostname)); + + if let Some(site_desc) = site_view.description { + channel_builder.description(htmlescape::encode_minimal(&site_desc)); + } + } + RequestType::Community => { + let community = Community::read_from_name(&conn, name.unwrap())?; + community_id = Some(community.id); + + let community_url = format!("https://{}/c/{}", Settings::get().hostname, community.name); + + channel_builder + .title(htmlescape::encode_minimal(&format!( + "{} - {}", + site_view.name, community.name + ))) + .link(community_url); + + if let Some(community_desc) = community.description { + channel_builder.description(htmlescape::encode_minimal(&community_desc)); + } + } + RequestType::User => { + let creator = User_::find_by_email_or_username(&conn, &name.unwrap())?; + creator_id = Some(creator.id); + + let creator_url = format!("https://{}/u/{}", Settings::get().hostname, creator.name); + + channel_builder + .title(htmlescape::encode_minimal(&format!( + "{} - {}", + site_view.name, creator.name + ))) + .link(creator_url); + } } - let post = PostView::list(&conn, + let posts = PostView::list( + &conn, ListingType::All, sort_type, community_id, @@ -99,41 +137,76 @@ fn get_feed_internal(sort_type: &SortType, request_type: RequestType, name: Opti false, false, None, - None,)?; + None, + )?; let mut items: Vec = Vec::new(); - for p in post { - let dt = DateTime::::from_utc(p.published, Utc); + + for p in posts { let mut i = ItemBuilder::default(); + i.title(htmlescape::encode_minimal(&p.name)); + + let author_url = format!("https://{}/u/{}", Settings::get().hostname, p.creator_name); + i.author(format!( + "/u/{} (link)", + p.creator_name, author_url + )); + + let dt = DateTime::::from_utc(p.published, Utc); i.pub_date(htmlescape::encode_minimal(&dt.to_rfc2822())); let post_url = format!("https://{}/post/{}", Settings::get().hostname, p.id); - let mut guid = Guid::default(); - guid.set_permalink(true); - guid.set_value(&post_url); - i.guid(guid); - i.comments(post_url); + i.comments(post_url.to_owned()); + let guid = GuidBuilder::default() + .permalink(true) + .value(&post_url) + .build(); + i.guid(guid.unwrap()); - if p.url.is_some() { - i.link(p.url.unwrap()); + let community_url = format!( + "https://{}/c/{}", + Settings::get().hostname, + p.community_name + ); + + let category = CategoryBuilder::default() + .name(format!( + "/c/{} (link)", + p.community_name, community_url + )) + .domain(Settings::get().hostname) + .build(); + i.categories(vec![category.unwrap()]); + + if let Some(url) = p.url { + i.link(url); } - if p.body.is_some() { - i.content(p.body.unwrap()); + + // TODO find a markdown to html parser here, do images, etc + let mut description = format!(" + submitted by {} to {}
{} points | {} comments", + author_url, + p.creator_name, + community_url, + p.community_name, + p.score, + post_url, + p.number_of_comments); + + if let Some(body) = p.body { + description.push_str(&format!("

{}", body)); } + + i.description(description); + items.push(i.build().unwrap()); } - let site_view = SiteView::read(&conn)?; - let mut channel_builder = ChannelBuilder::default(); - channel_builder.title(htmlescape::encode_minimal(&site_view.name)) - .link(format!("https://{}", Settings::get().hostname)) - .items(items); - if site_view.description.is_some() { - channel_builder.description(htmlescape::encode_minimal(&site_view.description.unwrap())); - } + channel_builder.items(items); + let channel = channel_builder.build().unwrap(); channel.write_to(::std::io::sink()).unwrap(); - return Ok(channel.to_string()); -} \ No newline at end of file + Ok(channel.to_string()) +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 5eaa99831..1ff13aab1 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -25,11 +25,11 @@ pub extern crate strum; pub mod api; pub mod apub; pub mod db; +pub mod feeds; pub mod nodeinfo; pub mod schema; pub mod version; pub mod websocket; -pub mod feeds; use chrono::{DateTime, NaiveDateTime, Utc}; use dotenv::dotenv; diff --git a/server/src/main.rs b/server/src/main.rs index 976f5f5d0..9b06f9807 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,8 +7,8 @@ use actix_files::NamedFile; use actix_web::*; use actix_web_actors::ws; use lemmy_server::db::establish_connection; -use lemmy_server::nodeinfo; use lemmy_server::feeds; +use lemmy_server::nodeinfo; use lemmy_server::websocket::server::*; use std::env; use std::time::{Duration, Instant}; diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index cfeff0852..a8ec9331a 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -174,6 +174,13 @@ export class Community extends Component { return ( ); } diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index c871db72b..403368ab7 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -431,9 +431,16 @@ export class Main extends Component { type_={this.state.type_} onChange={this.handleTypeChange} /> - + + {this.state.type_ == ListingType.All && ( + + + # + + + )} ); } diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 5506b58f5..c7f8232f5 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,10 @@ export class Symbols extends Component { xmlnsXlink="http://www.w3.org/1999/xlink" > + + rss + + arrow-down diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 84656ce7e..6fff538fa 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -249,6 +249,13 @@ export class User extends Component { hideHot /> + + + # + + ); }