diff options
-rwxr-xr-x | docker/dev/dev_deploy.sh | 11 | ||||
-rw-r--r-- | server/src/feeds.rs | 201 | ||||
-rw-r--r-- | server/src/lib.rs | 2 | ||||
-rw-r--r-- | server/src/main.rs | 2 | ||||
-rw-r--r-- | ui/src/components/community.tsx | 7 | ||||
-rw-r--r-- | ui/src/components/main.tsx | 9 | ||||
-rw-r--r-- | ui/src/components/symbols.tsx | 4 | ||||
-rw-r--r-- | ui/src/components/user.tsx | 7 |
8 files changed, 176 insertions, 67 deletions
diff --git a/docker/dev/dev_deploy.sh b/docker/dev/dev_deploy.sh new file mode 100755 index 00000000..91ac6c5d --- /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 4cd3d9cb..8a1db28b 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<Params>) -> HttpResponse<Body> { - 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 result = get_feed_internal(&sort_type.unwrap(), RequestType::All, None); - return match result { + match get_feed_internal(&sort_type, RequestType::All, None) { Ok(rss) => HttpResponse::Ok() - .content_type("application/rss+xml") - .body(rss), + .content_type("application/rss+xml") + .body(rss), Err(_) => HttpResponse::InternalServerError().finish(), - }; + } } pub fn get_feed(path: web::Path<(char, String)>, info: web::Query<Params>) -> HttpResponse<Body> { - 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<Params>) -> Result<SortType, ParseError> { - 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<String>) - -> Result<String, Error> { +fn get_feed_internal( + sort_type: &SortType, + request_type: RequestType, + name: Option<String>, +) -> Result<String, Error> { let conn = establish_connection(); let mut community_id: Option<i32> = None; let mut creator_id: Option<i32> = 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<Item> = Vec::new(); - for p in post { - let dt = DateTime::<Utc>::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/{} <a href=\"{}\">(link)</a>", + p.creator_name, author_url + )); + + let dt = DateTime::<Utc>::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); - - if p.url.is_some() { - i.link(p.url.unwrap()); + i.comments(post_url.to_owned()); + let guid = GuidBuilder::default() + .permalink(true) + .value(&post_url) + .build(); + i.guid(guid.unwrap()); + + let community_url = format!( + "https://{}/c/{}", + Settings::get().hostname, + p.community_name + ); + + let category = CategoryBuilder::default() + .name(format!( + "/c/{} <a href=\"{}\">(link)</a>", + 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 <a href=\"{}\">{}</a> to <a href=\"{}\">{}</a><br>{} points | <a href=\"{}\">{} comments</a>", + 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!("<br><br>{}", 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 5eaa9983..1ff13aab 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 976f5f5d..9b06f980 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 cfeff085..a8ec9331 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -174,6 +174,13 @@ export class Community extends Component<any, State> { return ( <div class="mb-2"> <SortSelect sort={this.state.sort} onChange={this.handleSortChange} /> + <a + href={`${document.location.origin}/feeds/c/${this.state.communityName}.xml`} + > + <svg class="icon mx-2 text-muted small"> + <use xlinkHref="#icon-rss">#</use> + </svg> + </a> </div> ); } diff --git a/ui/src/components/main.tsx b/ui/src/components/main.tsx index c871db72..403368ab 100644 --- a/ui/src/components/main.tsx +++ b/ui/src/components/main.tsx @@ -431,9 +431,16 @@ export class Main extends Component<any, MainState> { type_={this.state.type_} onChange={this.handleTypeChange} /> - <span class="ml-2"> + <span class="mx-2"> <SortSelect sort={this.state.sort} onChange={this.handleSortChange} /> </span> + {this.state.type_ == ListingType.All && ( + <a href={`${document.location.origin}/feeds/all.xml`}> + <svg class="icon mx-1 text-muted small"> + <use xlinkHref="#icon-rss">#</use> + </svg> + </a> + )} </div> ); } diff --git a/ui/src/components/symbols.tsx b/ui/src/components/symbols.tsx index 5506b58f..c7f8232f 100644 --- a/ui/src/components/symbols.tsx +++ b/ui/src/components/symbols.tsx @@ -15,6 +15,10 @@ export class Symbols extends Component<any, any> { xmlnsXlink="http://www.w3.org/1999/xlink" > <defs> + <symbol id="icon-rss" viewBox="0 0 32 32"> + <title>rss</title> + <path d="M4.259 23.467c-2.35 0-4.259 1.917-4.259 4.252 0 2.349 1.909 4.244 4.259 4.244 2.358 0 4.265-1.895 4.265-4.244-0-2.336-1.907-4.252-4.265-4.252zM0.005 10.873v6.133c3.993 0 7.749 1.562 10.577 4.391 2.825 2.822 4.384 6.595 4.384 10.603h6.16c-0-11.651-9.478-21.127-21.121-21.127zM0.012 0v6.136c14.243 0 25.836 11.604 25.836 25.864h6.152c0-17.64-14.352-32-31.988-32z"></path> + </symbol> <symbol id="icon-arrow-down" viewBox="0 0 26 28"> <title>arrow-down</title> <path d="M25.172 13c0 0.531-0.219 1.047-0.578 1.406l-10.172 10.187c-0.375 0.359-0.891 0.578-1.422 0.578s-1.047-0.219-1.406-0.578l-10.172-10.187c-0.375-0.359-0.594-0.875-0.594-1.406s0.219-1.047 0.594-1.422l1.156-1.172c0.375-0.359 0.891-0.578 1.422-0.578s1.047 0.219 1.406 0.578l4.594 4.594v-11c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v11l4.594-4.594c0.359-0.359 0.875-0.578 1.406-0.578s1.047 0.219 1.422 0.578l1.172 1.172c0.359 0.375 0.578 0.891 0.578 1.422z"></path> diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 84656ce7..6fff538f 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -249,6 +249,13 @@ export class User extends Component<any, UserState> { hideHot /> </span> + <a + href={`${document.location.origin}/feeds/u/${this.state.username}.xml`} + > + <svg class="icon mx-2 text-muted small"> + <use xlinkHref="#icon-rss">#</use> + </svg> + </a> </div> ); } |