Hide cw pictures behind a summary/details (#483)
* Hide cw pictures behind a summary/details * refactor md_to_html a bit and add cw support * use random id for cw checkbox
This commit is contained in:
		
							parent
							
								
									eabe73ddc0
								
							
						
					
					
						commit
						12c2078c89
					
				| @ -56,53 +56,117 @@ fn to_inline(tag: Tag) -> Tag { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn flatten_text<'a>(state: &mut Option<String>, evt: Event<'a>) -> Option<Vec<Event<'a>>> { | ||||||
|  |     let (s, res) = match evt { | ||||||
|  |         Event::Text(txt) => match state.take() { | ||||||
|  |             Some(mut prev_txt) => { | ||||||
|  |                 prev_txt.push_str(&txt); | ||||||
|  |                 (Some(prev_txt), vec![]) | ||||||
|  |             } | ||||||
|  |             None => (Some(txt.into_owned()), vec![]), | ||||||
|  |         }, | ||||||
|  |         e => match state.take() { | ||||||
|  |             Some(prev) => (None, vec![Event::Text(Cow::Owned(prev)), e]), | ||||||
|  |             None => (None, vec![e]), | ||||||
|  |         }, | ||||||
|  |     }; | ||||||
|  |     *state = s; | ||||||
|  |     Some(res) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn inline_tags<'a>( | ||||||
|  |     (state, inline): &mut (Vec<Tag<'a>>, bool), | ||||||
|  |     evt: Event<'a>, | ||||||
|  | ) -> Option<Event<'a>> { | ||||||
|  |     if *inline { | ||||||
|  |         let new_evt = match evt { | ||||||
|  |             Event::Start(t) => { | ||||||
|  |                 let tag = to_inline(t); | ||||||
|  |                 state.push(tag.clone()); | ||||||
|  |                 Event::Start(tag) | ||||||
|  |             } | ||||||
|  |             Event::End(t) => match state.pop() { | ||||||
|  |                 Some(other) => Event::End(other), | ||||||
|  |                 None => Event::End(t), | ||||||
|  |             }, | ||||||
|  |             e => e, | ||||||
|  |         }; | ||||||
|  |         Some(new_evt) | ||||||
|  |     } else { | ||||||
|  |         Some(evt) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type MediaProcessor<'a> = Box<'a + Fn(i32) -> Option<(String, Option<String>)>>; | ||||||
|  | 
 | ||||||
|  | fn process_image<'a, 'b>( | ||||||
|  |     evt: Event<'a>, | ||||||
|  |     inline: bool, | ||||||
|  |     processor: &Option<MediaProcessor<'b>>, | ||||||
|  | ) -> Event<'a> { | ||||||
|  |     if let Some(ref processor) = *processor { | ||||||
|  |         match evt { | ||||||
|  |             Event::Start(Tag::Image(id, title)) => { | ||||||
|  |                 if let Some((url, cw)) = id.parse::<i32>().ok().and_then(processor.as_ref()) { | ||||||
|  |                     if inline || cw.is_none() { | ||||||
|  |                         Event::Start(Tag::Image(Cow::Owned(url), title)) | ||||||
|  |                     } else { | ||||||
|  |                         // there is a cw, and where are not inline
 | ||||||
|  |                         Event::Html(Cow::Owned(format!( | ||||||
|  |                             r#"<label for="postcontent-cw-{id}">
 | ||||||
|  |   <input type="checkbox" id="postcontent-cw-{id}" checked="checked" class="cw-checkbox"> | ||||||
|  |   <span class="cw-container"> | ||||||
|  |     <span class="cw-text"> | ||||||
|  |         {cw} | ||||||
|  |     </span> | ||||||
|  |   <img src="{url}" alt=""#, | ||||||
|  |                             id = random_hex(), | ||||||
|  |                             cw = cw.unwrap(), | ||||||
|  |                             url = url | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     Event::Start(Tag::Image(id, title)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Event::End(Tag::Image(id, title)) => { | ||||||
|  |                 if let Some((url, cw)) = id.parse::<i32>().ok().and_then(processor.as_ref()) { | ||||||
|  |                     if inline || cw.is_none() { | ||||||
|  |                         Event::End(Tag::Image(Cow::Owned(url), title)) | ||||||
|  |                     } else { | ||||||
|  |                         Event::Html(Cow::Borrowed( | ||||||
|  |                             r#""/> | ||||||
|  |   </span> | ||||||
|  | </label>"#,
 | ||||||
|  |                         )) | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     Event::End(Tag::Image(id, title)) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             e => e, | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         evt | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Returns (HTML, mentions, hashtags)
 | /// Returns (HTML, mentions, hashtags)
 | ||||||
| pub fn md_to_html( | pub fn md_to_html<'a>( | ||||||
|     md: &str, |     md: &str, | ||||||
|     base_url: &str, |     base_url: &str, | ||||||
|     inline: bool, |     inline: bool, | ||||||
|  |     media_processor: Option<MediaProcessor<'a>>, | ||||||
| ) -> (String, HashSet<String>, HashSet<String>) { | ) -> (String, HashSet<String>, HashSet<String>) { | ||||||
|     let parser = Parser::new_ext(md, Options::all()); |     let parser = Parser::new_ext(md, Options::all()); | ||||||
| 
 | 
 | ||||||
|     let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser |     let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser | ||||||
|         .scan(None, |state: &mut Option<String>, evt| { |         // Flatten text because pulldown_cmark break #hashtag in two individual text elements
 | ||||||
|             let (s, res) = match evt { |         .scan(None, flatten_text) | ||||||
|                 Event::Text(txt) => match state.take() { |  | ||||||
|                     Some(mut prev_txt) => { |  | ||||||
|                         prev_txt.push_str(&txt); |  | ||||||
|                         (Some(prev_txt), vec![]) |  | ||||||
|                     } |  | ||||||
|                     None => (Some(txt.into_owned()), vec![]), |  | ||||||
|                 }, |  | ||||||
|                 e => match state.take() { |  | ||||||
|                     Some(prev) => (None, vec![Event::Text(Cow::Owned(prev)), e]), |  | ||||||
|                     None => (None, vec![e]), |  | ||||||
|                 }, |  | ||||||
|             }; |  | ||||||
|             *state = s; |  | ||||||
|             Some(res) |  | ||||||
|         }) |  | ||||||
|         .flat_map(IntoIterator::into_iter) |         .flat_map(IntoIterator::into_iter) | ||||||
|  |         .map(|evt| process_image(evt, inline, &media_processor)) | ||||||
|         // Ignore headings, images, and tables if inline = true
 |         // Ignore headings, images, and tables if inline = true
 | ||||||
|         .scan(vec![], |state: &mut Vec<Tag>, evt| { |         .scan((vec![], inline), inline_tags) | ||||||
|             if inline { |  | ||||||
|                 let new_evt = match evt { |  | ||||||
|                     Event::Start(t) => { |  | ||||||
|                         let tag = to_inline(t); |  | ||||||
|                         state.push(tag.clone()); |  | ||||||
|                         Event::Start(tag) |  | ||||||
|                     } |  | ||||||
|                     Event::End(t) => match state.pop() { |  | ||||||
|                         Some(other) => Event::End(other), |  | ||||||
|                         None => Event::End(t), |  | ||||||
|                     }, |  | ||||||
|                     e => e, |  | ||||||
|                 }; |  | ||||||
|                 Some(new_evt) |  | ||||||
|             } else { |  | ||||||
|                 Some(evt) |  | ||||||
|             } |  | ||||||
|         }) |  | ||||||
|         .map(|evt| match evt { |         .map(|evt| match evt { | ||||||
|             Event::Text(txt) => { |             Event::Text(txt) => { | ||||||
|                 let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold( |                 let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold( | ||||||
| @ -273,7 +337,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|         for (md, mentions) in tests { |         for (md, mentions) in tests { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 md_to_html(md, "", false).1, |                 md_to_html(md, "", false, None).1, | ||||||
|                 mentions |                 mentions | ||||||
|                     .into_iter() |                     .into_iter() | ||||||
|                     .map(|s| s.to_string()) |                     .map(|s| s.to_string()) | ||||||
| @ -298,7 +362,7 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|         for (md, mentions) in tests { |         for (md, mentions) in tests { | ||||||
|             assert_eq!( |             assert_eq!( | ||||||
|                 md_to_html(md, "", false).2, |                 md_to_html(md, "", false, None).2, | ||||||
|                 mentions |                 mentions | ||||||
|                     .into_iter() |                     .into_iter() | ||||||
|                     .map(|s| s.to_string()) |                     .map(|s| s.to_string()) | ||||||
| @ -310,11 +374,11 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_inline() { |     fn test_inline() { | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             md_to_html("# Hello", "", false).0, |             md_to_html("# Hello", "", false, None).0, | ||||||
|             String::from("<h1>Hello</h1>\n") |             String::from("<h1>Hello</h1>\n") | ||||||
|         ); |         ); | ||||||
|         assert_eq!( |         assert_eq!( | ||||||
|             md_to_html("# Hello", "", true).0, |             md_to_html("# Hello", "", true, None).0, | ||||||
|             String::from("<p>Hello</p>\n") |             String::from("<p>Hello</p>\n") | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ use std::collections::HashSet; | |||||||
| 
 | 
 | ||||||
| use comment_seers::{CommentSeers, NewCommentSeers}; | use comment_seers::{CommentSeers, NewCommentSeers}; | ||||||
| use instance::Instance; | use instance::Instance; | ||||||
|  | use medias::Media; | ||||||
| use mentions::Mention; | use mentions::Mention; | ||||||
| use notifications::*; | use notifications::*; | ||||||
| use plume_common::activity_pub::{ | use plume_common::activity_pub::{ | ||||||
| @ -102,14 +103,16 @@ impl Comment { | |||||||
|                 .unwrap_or(false) |                 .unwrap_or(false) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn to_activity(&self, conn: &Connection) -> Result<Note> { |     pub fn to_activity<'b>(&self, conn: &'b Connection) -> Result<Note> { | ||||||
|  |         let author = User::get(conn, self.author_id)?; | ||||||
|  | 
 | ||||||
|         let (html, mentions, _hashtags) = utils::md_to_html( |         let (html, mentions, _hashtags) = utils::md_to_html( | ||||||
|             self.content.get().as_ref(), |             self.content.get().as_ref(), | ||||||
|             &Instance::get_local(conn)?.public_domain, |             &Instance::get_local(conn)?.public_domain, | ||||||
|             true, |             true, | ||||||
|  |             Some(Media::get_media_processor(conn, vec![&author])), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let author = User::get(conn, self.author_id)?; |  | ||||||
|         let mut note = Note::default(); |         let mut note = Note::default(); | ||||||
|         let to = vec![Id::new(PUBLIC_VISIBILTY.to_string())]; |         let to = vec![Id::new(PUBLIC_VISIBILTY.to_string())]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; | |||||||
| use std::iter::Iterator; | use std::iter::Iterator; | ||||||
| 
 | 
 | ||||||
| use ap_url; | use ap_url; | ||||||
|  | use medias::Media; | ||||||
| use plume_common::utils::md_to_html; | use plume_common::utils::md_to_html; | ||||||
| use safe_string::SafeString; | use safe_string::SafeString; | ||||||
| use schema::{instances, users}; | use schema::{instances, users}; | ||||||
| @ -128,8 +129,18 @@ impl Instance { | |||||||
|         short_description: SafeString, |         short_description: SafeString, | ||||||
|         long_description: SafeString, |         long_description: SafeString, | ||||||
|     ) -> Result<()> { |     ) -> Result<()> { | ||||||
|         let (sd, _, _) = md_to_html(short_description.as_ref(), &self.public_domain, true); |         let (sd, _, _) = md_to_html( | ||||||
|         let (ld, _, _) = md_to_html(long_description.as_ref(), &self.public_domain, false); |             short_description.as_ref(), | ||||||
|  |             &self.public_domain, | ||||||
|  |             true, | ||||||
|  |             Some(Media::get_media_processor(conn, vec![])), | ||||||
|  |         ); | ||||||
|  |         let (ld, _, _) = md_to_html( | ||||||
|  |             long_description.as_ref(), | ||||||
|  |             &self.public_domain, | ||||||
|  |             false, | ||||||
|  |             Some(Media::get_media_processor(conn, vec![])), | ||||||
|  |         ); | ||||||
|         diesel::update(self) |         diesel::update(self) | ||||||
|             .set(( |             .set(( | ||||||
|                 instances::name.eq(name), |                 instances::name.eq(name), | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ use guid_create::GUID; | |||||||
| use reqwest; | use reqwest; | ||||||
| use std::{fs, path::Path}; | use std::{fs, path::Path}; | ||||||
| 
 | 
 | ||||||
| use plume_common::activity_pub::Id; | use plume_common::{activity_pub::Id, utils::MediaProcessor}; | ||||||
| 
 | 
 | ||||||
| use instance::Instance; | use instance::Instance; | ||||||
| use safe_string::SafeString; | use safe_string::SafeString; | ||||||
| @ -124,10 +124,9 @@ impl Media { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn markdown(&self, conn: &Connection) -> Result<SafeString> { |     pub fn markdown(&self, conn: &Connection) -> Result<SafeString> { | ||||||
|         let url = self.url(conn)?; |  | ||||||
|         Ok(match self.category() { |         Ok(match self.category() { | ||||||
|             MediaCategory::Image => { |             MediaCategory::Image => { | ||||||
|                 SafeString::new(&format!("", escape(&self.alt_text), url)) |                 SafeString::new(&format!("", escape(&self.alt_text), self.id)) | ||||||
|             } |             } | ||||||
|             MediaCategory::Audio | MediaCategory::Video => self.html(conn)?, |             MediaCategory::Audio | MediaCategory::Video => self.html(conn)?, | ||||||
|             MediaCategory::Unknown => SafeString::new(""), |             MediaCategory::Unknown => SafeString::new(""), | ||||||
| @ -225,6 +224,19 @@ impl Media { | |||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_media_processor<'a>(conn: &'a Connection, user: Vec<&User>) -> MediaProcessor<'a> { | ||||||
|  |         let uid = user.iter().map(|u| u.id).collect::<Vec<_>>(); | ||||||
|  |         Box::new(move |id| { | ||||||
|  |             let media = Media::get(conn, id).ok()?; | ||||||
|  |             // if owner is user or check is disabled
 | ||||||
|  |             if uid.contains(&media.owner_id) || uid.is_empty() { | ||||||
|  |                 Some((media.url(conn).ok()?, media.content_warning)) | ||||||
|  |             } else { | ||||||
|  |                 None | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | |||||||
| @ -207,17 +207,19 @@ impl<'a> Provider<(&'a Connection, &'a Worker, &'a Searcher, Option<i32>)> for P | |||||||
|         let domain = &Instance::get_local(&conn) |         let domain = &Instance::get_local(&conn) | ||||||
|             .map_err(|_| ApiError::NotFound("posts::update: Error getting local instance".into()))? |             .map_err(|_| ApiError::NotFound("posts::update: Error getting local instance".into()))? | ||||||
|             .public_domain; |             .public_domain; | ||||||
|         let (content, mentions, hashtags) = md_to_html( |  | ||||||
|             query.source.clone().unwrap_or_default().clone().as_ref(), |  | ||||||
|             domain, |  | ||||||
|             false, |  | ||||||
|         ); |  | ||||||
| 
 |  | ||||||
|         let author = User::get( |         let author = User::get( | ||||||
|             conn, |             conn, | ||||||
|             user_id.expect("<Post as Provider>::create: no user_id error"), |             user_id.expect("<Post as Provider>::create: no user_id error"), | ||||||
|         ) |         ) | ||||||
|         .map_err(|_| ApiError::NotFound("Author not found".into()))?; |         .map_err(|_| ApiError::NotFound("Author not found".into()))?; | ||||||
|  | 
 | ||||||
|  |         let (content, mentions, hashtags) = md_to_html( | ||||||
|  |             query.source.clone().unwrap_or_default().clone().as_ref(), | ||||||
|  |             domain, | ||||||
|  |             false, | ||||||
|  |             Some(Media::get_media_processor(conn, vec![&author])), | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|         let blog = match query.blog_id { |         let blog = match query.blog_id { | ||||||
|             Some(x) => x, |             Some(x) => x, | ||||||
|             None => { |             None => { | ||||||
| @ -757,7 +759,7 @@ impl Post { | |||||||
|             post.license = license; |             post.license = license; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let mut txt_hashtags = md_to_html(&post.source, "", false) |         let mut txt_hashtags = md_to_html(&post.source, "", false, None) | ||||||
|             .2 |             .2 | ||||||
|             .into_iter() |             .into_iter() | ||||||
|             .map(|s| s.to_camel_case()) |             .map(|s| s.to_camel_case()) | ||||||
| @ -995,7 +997,7 @@ impl<'a> FromActivity<LicensedArticle, (&'a Connection, &'a Searcher)> for Post | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // save mentions and tags
 |             // save mentions and tags
 | ||||||
|             let mut hashtags = md_to_html(&post.source, "", false) |             let mut hashtags = md_to_html(&post.source, "", false, None) | ||||||
|                 .2 |                 .2 | ||||||
|                 .into_iter() |                 .into_iter() | ||||||
|                 .map(|s| s.to_camel_case()) |                 .map(|s| s.to_camel_case()) | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ lazy_static! { | |||||||
|     static ref CLEAN: Builder<'static> = { |     static ref CLEAN: Builder<'static> = { | ||||||
|         let mut b = Builder::new(); |         let mut b = Builder::new(); | ||||||
|         b.add_generic_attributes(iter::once("id")) |         b.add_generic_attributes(iter::once("id")) | ||||||
|             .add_tags(&["iframe", "video", "audio"]) |             .add_tags(&["iframe", "video", "audio", "label", "input"]) | ||||||
|             .id_prefix(Some("postcontent-")) |             .id_prefix(Some("postcontent-")) | ||||||
|             .url_relative(UrlRelative::Custom(Box::new(url_add_prefix))) |             .url_relative(UrlRelative::Custom(Box::new(url_add_prefix))) | ||||||
|             .add_tag_attributes( |             .add_tag_attributes( | ||||||
| @ -27,7 +27,23 @@ lazy_static! { | |||||||
|                 ["width", "height", "src", "frameborder"].iter().cloned(), |                 ["width", "height", "src", "frameborder"].iter().cloned(), | ||||||
|             ) |             ) | ||||||
|             .add_tag_attributes("video", ["src", "title", "controls"].iter()) |             .add_tag_attributes("video", ["src", "title", "controls"].iter()) | ||||||
|             .add_tag_attributes("audio", ["src", "title", "controls"].iter()); |             .add_tag_attributes("audio", ["src", "title", "controls"].iter()) | ||||||
|  |             .add_tag_attributes("label", ["for"].iter()) | ||||||
|  |             .add_tag_attributes("input", ["type", "checked"].iter()) | ||||||
|  |             .add_allowed_classes("input", ["cw-checkbox"].iter()) | ||||||
|  |             .add_allowed_classes("span", ["cw-container", "cw-text"].iter()) | ||||||
|  |             .attribute_filter(|elem, att, val| match (elem, att) { | ||||||
|  |                 ("input", "type") => Some("checkbox".into()), | ||||||
|  |                 ("input", "checked") => Some("checked".into()), | ||||||
|  |                 ("label", "for") => { | ||||||
|  |                     if val.starts_with("postcontent-cw-") { | ||||||
|  |                         Some(val.into()) | ||||||
|  |                     } else { | ||||||
|  |                         None | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 _ => Some(val.into()), | ||||||
|  |             }); | ||||||
|         b |         b | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  | |||||||
| @ -209,7 +209,13 @@ impl User { | |||||||
|             .set(( |             .set(( | ||||||
|                 users::display_name.eq(name), |                 users::display_name.eq(name), | ||||||
|                 users::email.eq(email), |                 users::email.eq(email), | ||||||
|                 users::summary_html.eq(utils::md_to_html(&summary, "", false).0), |                 users::summary_html.eq(utils::md_to_html( | ||||||
|  |                     &summary, | ||||||
|  |                     "", | ||||||
|  |                     false, | ||||||
|  |                     Some(Media::get_media_processor(conn, vec![self])), | ||||||
|  |                 ) | ||||||
|  |                 .0), | ||||||
|                 users::summary.eq(summary), |                 users::summary.eq(summary), | ||||||
|             )) |             )) | ||||||
|             .execute(conn)?; |             .execute(conn)?; | ||||||
| @ -868,7 +874,7 @@ impl NewUser { | |||||||
|                 display_name, |                 display_name, | ||||||
|                 is_admin, |                 is_admin, | ||||||
|                 summary: summary.to_owned(), |                 summary: summary.to_owned(), | ||||||
|                 summary_html: SafeString::new(&utils::md_to_html(&summary, "", false).0), |                 summary_html: SafeString::new(&utils::md_to_html(&summary, "", false, None).0), | ||||||
|                 email: Some(email), |                 email: Some(email), | ||||||
|                 hashed_password: Some(password), |                 hashed_password: Some(password), | ||||||
|                 instance_id: Instance::get_local(conn)?.id, |                 instance_id: Instance::get_local(conn)?.id, | ||||||
|  | |||||||
| @ -280,7 +280,21 @@ pub fn update( | |||||||
| 
 | 
 | ||||||
|                 blog.title = form.title.clone(); |                 blog.title = form.title.clone(); | ||||||
|                 blog.summary = form.summary.clone(); |                 blog.summary = form.summary.clone(); | ||||||
|                 blog.summary_html = SafeString::new(&utils::md_to_html(&form.summary, "", true).0); |                 blog.summary_html = SafeString::new( | ||||||
|  |                     &utils::md_to_html( | ||||||
|  |                         &form.summary, | ||||||
|  |                         "", | ||||||
|  |                         true, | ||||||
|  |                         Some(Media::get_media_processor( | ||||||
|  |                             &conn, | ||||||
|  |                             blog.list_authors(&conn) | ||||||
|  |                                 .expect("Couldn't get list of authors") | ||||||
|  |                                 .iter() | ||||||
|  |                                 .collect(), | ||||||
|  |                         )), | ||||||
|  |                     ) | ||||||
|  |                     .0, | ||||||
|  |                 ); | ||||||
|                 blog.icon_id = form.icon; |                 blog.icon_id = form.icon; | ||||||
|                 blog.banner_id = form.banner; |                 blog.banner_id = form.banner; | ||||||
|                 blog.save_changes::<Blog>(&*conn) |                 blog.save_changes::<Blog>(&*conn) | ||||||
|  | |||||||
| @ -15,8 +15,8 @@ use plume_common::{ | |||||||
|     utils, |     utils, | ||||||
| }; | }; | ||||||
| use plume_models::{ | use plume_models::{ | ||||||
|     blogs::Blog, comments::*, db_conn::DbConn, instance::Instance, mentions::Mention, posts::Post, |     blogs::Blog, comments::*, db_conn::DbConn, instance::Instance, medias::Media, | ||||||
|     safe_string::SafeString, tags::Tag, users::User, |     mentions::Mention, posts::Post, safe_string::SafeString, tags::Tag, users::User, | ||||||
| }; | }; | ||||||
| use routes::errors::ErrorPage; | use routes::errors::ErrorPage; | ||||||
| use Worker; | use Worker; | ||||||
| @ -49,6 +49,7 @@ pub fn create( | |||||||
|                     .expect("comments::create: local instance error") |                     .expect("comments::create: local instance error") | ||||||
|                     .public_domain, |                     .public_domain, | ||||||
|                 true, |                 true, | ||||||
|  |                 Some(Media::get_media_processor(&conn, vec![&user])), | ||||||
|             ); |             ); | ||||||
|             let comm = Comment::insert( |             let comm = Comment::insert( | ||||||
|                 &*conn, |                 &*conn, | ||||||
|  | |||||||
| @ -264,6 +264,13 @@ pub fn update( | |||||||
|                     .expect("posts::update: Error getting local instance") |                     .expect("posts::update: Error getting local instance") | ||||||
|                     .public_domain, |                     .public_domain, | ||||||
|                 false, |                 false, | ||||||
|  |                 Some(Media::get_media_processor( | ||||||
|  |                     &conn, | ||||||
|  |                     b.list_authors(&conn) | ||||||
|  |                         .expect("Could not get author list") | ||||||
|  |                         .iter() | ||||||
|  |                         .collect(), | ||||||
|  |                 )), | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             // update publication date if when this article is no longer a draft
 |             // update publication date if when this article is no longer a draft
 | ||||||
| @ -424,6 +431,13 @@ pub fn create( | |||||||
|                 .expect("post::create: local instance error") |                 .expect("post::create: local instance error") | ||||||
|                 .public_domain, |                 .public_domain, | ||||||
|             false, |             false, | ||||||
|  |             Some(Media::get_media_processor( | ||||||
|  |                 &conn, | ||||||
|  |                 blog.list_authors(&conn) | ||||||
|  |                     .expect("Could not get author list") | ||||||
|  |                     .iter() | ||||||
|  |                     .collect(), | ||||||
|  |             )), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         let searcher = rockets.searcher; |         let searcher = rockets.searcher; | ||||||
|  | |||||||
| @ -322,3 +322,36 @@ main .article-meta { | |||||||
|     right: 0px; |     right: 0px; | ||||||
|     bottom: 0px; |     bottom: 0px; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // content warning | ||||||
|  | .cw-container { | ||||||
|  |   position: relative; | ||||||
|  |   display: inline-block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .cw-text { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | input[type="checkbox"].cw-checkbox { | ||||||
|  |   display: none; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | input:checked ~ .cw-container:before { | ||||||
|  |   content: " "; | ||||||
|  |   position: absolute; | ||||||
|  |   height: 100%; | ||||||
|  |   width: 100%; | ||||||
|  |   background: rgba(0, 0, 0, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | input:checked ~ .cw-container > .cw-text { | ||||||
|  |   display: inline; | ||||||
|  |   position: absolute; | ||||||
|  |   color: white; | ||||||
|  |   width: 100%; | ||||||
|  |   text-align: center; | ||||||
|  |   top: 50%; | ||||||
|  |   transform: translateY(-50%); | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user