commit
						fc5acac861
					
				| @ -0,0 +1,2 @@ | ||||
| -- This file should undo anything in `up.sql` | ||||
| ALTER TABLE tags RENAME COLUMN is_hashtag TO is_hastag; | ||||
| @ -0,0 +1 @@ | ||||
| ALTER TABLE tags RENAME COLUMN is_hastag TO is_hashtag; | ||||
							
								
								
									
										10
									
								
								migrations/sqlite/2018-10-20-164036_fix_hastag_typo/down.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								migrations/sqlite/2018-10-20-164036_fix_hastag_typo/down.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| CREATE TABLE tags2 ( | ||||
|     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||
|     tag TEXT NOT NULL DEFAULT '', | ||||
|     is_hastag BOOLEAN NOT NULL DEFAULT 'f', | ||||
|     post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE NOT NULL | ||||
| ); | ||||
| 
 | ||||
| INSERT INTO tags2 SELECT * FROM tags; | ||||
| DROP TABLE tags; | ||||
| ALTER TABLE tags2 RENAME TO tags; | ||||
							
								
								
									
										10
									
								
								migrations/sqlite/2018-10-20-164036_fix_hastag_typo/up.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								migrations/sqlite/2018-10-20-164036_fix_hastag_typo/up.sql
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| CREATE TABLE tags2 ( | ||||
|     id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||
|     tag TEXT NOT NULL DEFAULT '', | ||||
|     is_hashtag BOOLEAN NOT NULL DEFAULT 'f', | ||||
|     post_id INTEGER REFERENCES posts(id) ON DELETE CASCADE NOT NULL | ||||
| ); | ||||
| 
 | ||||
| INSERT INTO tags2 SELECT * FROM tags; | ||||
| DROP TABLE tags; | ||||
| ALTER TABLE tags2 RENAME TO tags; | ||||
| @ -20,58 +20,117 @@ pub fn requires_login(message: &str, url: Uri) -> Flash<Redirect> { | ||||
|     Flash::new(Redirect::to(format!("/login?m={}", gettext(message.to_string()))), "callback", url.to_string()) | ||||
| } | ||||
| 
 | ||||
| /// Returns (HTML, mentions)
 | ||||
| pub fn md_to_html(md: &str) -> (String, Vec<String>) { | ||||
| #[derive(Debug)] | ||||
| enum State { | ||||
|     Mention, | ||||
|     Hashtag, | ||||
|     Word, | ||||
|     Ready, | ||||
| } | ||||
| 
 | ||||
| /// Returns (HTML, mentions, hashtags)
 | ||||
| pub fn md_to_html(md: &str) -> (String, Vec<String>, Vec<String>) { | ||||
|     let parser = Parser::new_ext(md, Options::all()); | ||||
| 
 | ||||
|     let (parser, mentions): (Vec<Vec<Event>>, Vec<Vec<String>>) = parser.map(|evt| match evt { | ||||
|     let (parser, mentions, hashtags): (Vec<Vec<Event>>, Vec<Vec<String>>, Vec<Vec<String>>) = parser.map(|evt| match evt { | ||||
|         Event::Text(txt) => { | ||||
|             let (evts, _, _, _, new_mentions) = txt.chars().fold((vec![], false, String::new(), 0, vec![]), |(mut events, in_mention, text_acc, n, mut mentions), c| { | ||||
|                 if in_mention { | ||||
|                     let char_matches = c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_'; | ||||
|                     if char_matches && (n < (txt.chars().count() - 1)) { | ||||
|                         (events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions) | ||||
|                     } else { | ||||
|                         let mention = if char_matches { | ||||
|                             text_acc + c.to_string().as_ref() | ||||
|             let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold((vec![], State::Ready, String::new(), 0, vec![], vec![]), |(mut events, state, text_acc, n, mut mentions, mut hashtags), c| { | ||||
|                 match state { | ||||
|                     State::Mention => { | ||||
|                         let char_matches = c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_'; | ||||
|                         if char_matches && (n < (txt.chars().count() - 1)) { | ||||
|                             (events, State::Mention, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags) | ||||
|                         } else { | ||||
|                             text_acc | ||||
|                         }; | ||||
|                         let short_mention = mention.clone(); | ||||
|                         let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or(""); | ||||
|                         let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into()); | ||||
|                             let mention = if char_matches { | ||||
|                                 text_acc + c.to_string().as_ref() | ||||
|                             } else { | ||||
|                                 text_acc | ||||
|                             }; | ||||
|                             let short_mention = mention.clone(); | ||||
|                             let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or(""); | ||||
|                             let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into()); | ||||
| 
 | ||||
|                         mentions.push(mention); | ||||
|                         events.push(Event::Start(link.clone())); | ||||
|                         events.push(Event::Text(format!("@{}", short_mention).into())); | ||||
|                         events.push(Event::End(link)); | ||||
|                             mentions.push(mention); | ||||
|                             events.push(Event::Start(link.clone())); | ||||
|                             events.push(Event::Text(format!("@{}", short_mention).into())); | ||||
|                             events.push(Event::End(link)); | ||||
| 
 | ||||
|                         (events, false, c.to_string(), n + 1, mentions) | ||||
|                     } | ||||
|                 } else { | ||||
|                     if c == '@' { | ||||
|                         events.push(Event::Text(text_acc.into())); | ||||
|                         (events, true, String::new(), n + 1, mentions) | ||||
|                     } else { | ||||
|                         if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
 | ||||
|                             events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) | ||||
|                             (events, State::Ready, c.to_string(), n + 1, mentions, hashtags) | ||||
|                         } | ||||
|                     } | ||||
|                     State::Hashtag => { | ||||
|                         let char_matches = c.is_alphanumeric(); | ||||
|                         if char_matches && (n < (txt.chars().count() -1)) { | ||||
|                             (events, State::Hashtag, text_acc + c.to_string().as_ref(), n+1, mentions, hashtags) | ||||
|                         } else { | ||||
|                             let hashtag = if char_matches { | ||||
|                                 text_acc + c.to_string().as_ref() | ||||
|                             } else { | ||||
|                                 text_acc | ||||
|                             }; | ||||
|                             let link = Tag::Link(format!("/tag/{}", hashtag.to_camel_case()).into(), hashtag.to_string().into()); | ||||
| 
 | ||||
|                             hashtags.push(hashtag.clone()); | ||||
|                             events.push(Event::Start(link.clone())); | ||||
|                             events.push(Event::Text(format!("#{}", hashtag).into())); | ||||
|                             events.push(Event::End(link)); | ||||
| 
 | ||||
|                             (events, State::Ready, c.to_string(), n + 1, mentions, hashtags) | ||||
|                         } | ||||
|                     } | ||||
|                     State::Ready => { | ||||
|                         if c == '@' { | ||||
|                             events.push(Event::Text(text_acc.into())); | ||||
|                             (events, State::Mention, String::new(), n + 1, mentions, hashtags) | ||||
|                         } else if c == '#' { | ||||
|                             events.push(Event::Text(text_acc.into())); | ||||
|                             (events, State::Hashtag, String::new(), n + 1, mentions, hashtags) | ||||
|                         } else if c.is_alphanumeric() { | ||||
|                             if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
 | ||||
|                                 events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) | ||||
|                             } | ||||
|                             (events, State::Word, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags) | ||||
|                         } else { | ||||
|                             if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
 | ||||
|                                 events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) | ||||
|                             } | ||||
|                             (events, State::Ready, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags) | ||||
|                         } | ||||
|                     } | ||||
|                     State::Word => { | ||||
|                         if c.is_alphanumeric() { | ||||
|                             if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
 | ||||
|                                 events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) | ||||
|                             } | ||||
|                             (events, State::Word, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags) | ||||
|                         } else { | ||||
|                             if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
 | ||||
|                                 events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) | ||||
|                             } | ||||
|                             (events, State::Ready, text_acc + c.to_string().as_ref(), n + 1, mentions, hashtags) | ||||
|                         } | ||||
|                         (events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions) | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             (evts, new_mentions) | ||||
|             (evts, new_mentions, new_hashtags) | ||||
|         }, | ||||
|         _ => (vec![evt], vec![]) | ||||
|     }).unzip(); | ||||
|         _ => (vec![evt], vec![], vec![]) | ||||
|     }).fold((vec![],vec![],vec![]), |(mut parser, mut mention, mut hashtag), (p, m, h)| { | ||||
|         parser.push(p); | ||||
|         mention.push(m); | ||||
|         hashtag.push(h); | ||||
|         (parser, mention, hashtag) | ||||
|     }); | ||||
|     let parser = parser.into_iter().flatten(); | ||||
|     let mentions = mentions.into_iter().flatten().map(|m| String::from(m.trim())); | ||||
|     let hashtags = hashtags.into_iter().flatten().map(|h| String::from(h.trim())); | ||||
| 
 | ||||
|     // TODO: fetch mentionned profiles in background, if needed
 | ||||
| 
 | ||||
|     let mut buf = String::new(); | ||||
|     html::push_html(&mut buf, parser); | ||||
|     (buf, mentions.collect()) | ||||
|     let hashtags = hashtags.collect(); | ||||
|     (buf, mentions.collect(), hashtags) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| @ -90,10 +149,30 @@ mod tests { | ||||
|             ("between parenthesis (@test)", vec!["test"]), | ||||
|             ("with some punctuation @test!", vec!["test"]), | ||||
|             ("      @spaces     ", vec!["spaces"]), | ||||
|             ("not_a@mention", vec![]), | ||||
|         ]; | ||||
| 
 | ||||
|         for (md, mentions) in tests { | ||||
|             assert_eq!(md_to_html(md).1, mentions.into_iter().map(|s| s.to_string()).collect::<Vec<String>>()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_hashtags() { | ||||
|         let tests = vec![ | ||||
|             ("nothing", vec![]), | ||||
|             ("#hashtag", vec!["hashtag"]), | ||||
|             ("#many #hashtags", vec!["many", "hashtags"]), | ||||
|             ("#start with a hashtag", vec!["start"]), | ||||
|             ("hashtag at #end", vec!["end"]), | ||||
|             ("between parenthesis (#test)", vec!["test"]), | ||||
|             ("with some punctuation #test!", vec!["test"]), | ||||
|             ("      #spaces     ", vec!["spaces"]), | ||||
|             ("not_a#hashtag", vec![]), | ||||
|         ]; | ||||
| 
 | ||||
|         for (md, mentions) in tests { | ||||
|             assert_eq!(md_to_html(md).2, mentions.into_iter().map(|s| s.to_string()).collect::<Vec<String>>()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -100,7 +100,7 @@ impl Comment { | ||||
|     } | ||||
| 
 | ||||
|     pub fn into_activity(&self, conn: &Connection) -> Note { | ||||
|         let (html, mentions) = utils::md_to_html(self.content.get().as_ref()); | ||||
|         let (html, mentions, _hashtags) = utils::md_to_html(self.content.get().as_ref()); | ||||
| 
 | ||||
|         let author = User::get(conn, self.author_id).expect("Comment::into_activity: author error"); | ||||
|         let mut note = Note::default(); | ||||
|  | ||||
| @ -117,8 +117,8 @@ impl Instance { | ||||
|     } | ||||
| 
 | ||||
|     pub fn update(&self, conn: &Connection, name: String, open_registrations: bool, short_description: SafeString, long_description: SafeString) { | ||||
|         let (sd, _) = md_to_html(short_description.as_ref()); | ||||
|         let (ld, _) = md_to_html(long_description.as_ref()); | ||||
|         let (sd, _, _) = md_to_html(short_description.as_ref()); | ||||
|         let (ld, _, _) = md_to_html(long_description.as_ref()); | ||||
|         diesel::update(self) | ||||
|             .set(( | ||||
|                 instances::name.eq(name), | ||||
|  | ||||
| @ -144,7 +144,7 @@ table! { | ||||
|     tags (id) { | ||||
|         id -> Int4, | ||||
|         tag -> Text, | ||||
|         is_hastag -> Bool, | ||||
|         is_hashtag -> Bool, | ||||
|         post_id -> Int4, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ use schema::tags; | ||||
| pub struct Tag { | ||||
|     pub id: i32, | ||||
|     pub tag: String, | ||||
|     pub is_hastag: bool, | ||||
|     pub is_hashtag: bool, | ||||
|     pub post_id: i32 | ||||
| } | ||||
| 
 | ||||
| @ -17,7 +17,7 @@ pub struct Tag { | ||||
| #[table_name = "tags"] | ||||
| pub struct NewTag { | ||||
|     pub tag: String, | ||||
|     pub is_hastag: bool, | ||||
|     pub is_hashtag: bool, | ||||
|     pub post_id: i32 | ||||
| } | ||||
| 
 | ||||
| @ -40,7 +40,7 @@ impl Tag { | ||||
|     pub fn from_activity(conn: &Connection, tag: Hashtag, post: i32) -> Tag { | ||||
|         Tag::insert(conn, NewTag { | ||||
|             tag: tag.name_string().expect("Tag::from_activity: name error"), | ||||
|             is_hastag: false, | ||||
|             is_hashtag: false, | ||||
|             post_id: post | ||||
|         }) | ||||
|     } | ||||
|  | ||||
							
								
								
									
										3
									
								
								po/en.po
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								po/en.po
									
									
									
									
									
								
							| @ -611,3 +611,6 @@ msgstr "" | ||||
| 
 | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "" | ||||
|  | ||||
							
								
								
									
										3
									
								
								po/fr.po
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								po/fr.po
									
									
									
									
									
								
							| @ -627,3 +627,6 @@ msgstr "Utilisateurs" | ||||
| 
 | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "Cet article n’est pas encore publié." | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "Il n'y a pas encore d'article avec ce tag" | ||||
|  | ||||
							
								
								
									
										3
									
								
								po/gl.po
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								po/gl.po
									
									
									
									
									
								
							| @ -614,3 +614,6 @@ msgstr "Usuarias" | ||||
| #, fuzzy | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "Esto é un borrador, non publicar por agora." | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "" | ||||
|  | ||||
							
								
								
									
										3
									
								
								po/nb.po
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								po/nb.po
									
									
									
									
									
								
							| @ -636,6 +636,9 @@ msgstr "Brukernavn" | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "" | ||||
| 
 | ||||
| #~ msgid "One reshare" | ||||
| #~ msgid_plural "{{ count }} reshares" | ||||
| #~ msgstr[0] "Én deling" | ||||
|  | ||||
							
								
								
									
										3
									
								
								po/pl.po
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								po/pl.po
									
									
									
									
									
								
							| @ -626,6 +626,9 @@ msgstr "Użytkownicy" | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "Ten wpis nie został jeszcze opublikowany." | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "" | ||||
| 
 | ||||
| #~ msgid "One reshare" | ||||
| #~ msgid_plural "{{ count }} reshares" | ||||
| #~ msgstr[0] "Jedno udostępnienie" | ||||
|  | ||||
| @ -594,3 +594,6 @@ msgstr "" | ||||
| 
 | ||||
| msgid "This post isn't published yet." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "There is currently no article with that tag" | ||||
| msgstr "" | ||||
|  | ||||
| @ -35,7 +35,7 @@ fn create(blog_name: String, slug: String, data: LenientForm<NewCommentForm>, us | ||||
|     let form = data.get(); | ||||
|     form.validate() | ||||
|         .map(|_| { | ||||
|             let (html, mentions) = utils::md_to_html(form.content.as_ref()); | ||||
|             let (html, mentions, _hashtags) = utils::md_to_html(form.content.as_ref()); | ||||
|             let comm = Comment::insert(&*conn, NewComment { | ||||
|                 content: SafeString::new(html.as_ref()), | ||||
|                 in_response_to_id: form.responding_to.clone(), | ||||
|  | ||||
| @ -139,7 +139,7 @@ fn edit(blog: String, slug: String, user: User, conn: DbConn) -> Option<Template | ||||
|                 content: source, | ||||
|                 tags: Tag::for_post(&*conn, post.id) | ||||
|                     .into_iter() | ||||
|                     .map(|t| t.tag) | ||||
|                     .filter_map(|t| if !t.is_hashtag {Some(t.tag)} else {None}) | ||||
|                     .collect::<Vec<String>>() | ||||
|                     .join(", "), | ||||
|                 license: post.license.clone(), | ||||
| @ -183,7 +183,7 @@ fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientFor | ||||
|             // actually it's not "Ok"…
 | ||||
|             Ok(Redirect::to(uri!(super::blogs::details: name = blog))) | ||||
|         } else { | ||||
|             let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref()); | ||||
|             let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); | ||||
| 
 | ||||
|             let license = if form.license.len() > 0 { | ||||
|                 form.license.to_string() | ||||
| @ -215,16 +215,32 @@ fn update(blog: String, slug: String, user: User, conn: DbConn, data: LenientFor | ||||
|             let old_tags = Tag::for_post(&*conn, post.id).into_iter().collect::<Vec<_>>(); | ||||
|             let tags = form.tags.split(",").map(|t| t.trim().to_camel_case()).filter(|t| t.len() > 0).collect::<Vec<_>>(); | ||||
|             for tag in tags.iter() { | ||||
|                 if old_tags.iter().all(|ot| &ot.tag!=tag) { | ||||
|                 if old_tags.iter().all(|ot| &ot.tag!=tag || ot.is_hashtag) { | ||||
|                     Tag::insert(&*conn, NewTag { | ||||
|                         tag: tag.clone(), | ||||
|                         is_hastag: false, | ||||
|                         is_hashtag: false, | ||||
|                         post_id: post.id | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             for ot in old_tags.iter() { | ||||
|                 if !tags.contains(&ot.tag) && !ot.is_hashtag { | ||||
|                     ot.delete(&conn); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             let hashtags = hashtags.into_iter().map(|h| h.to_camel_case()).collect::<Vec<_>>(); | ||||
|             for hashtag in hashtags.iter() { | ||||
|                 if old_tags.iter().all(|ot| &ot.tag!=hashtag || !ot.is_hashtag) { | ||||
|                     Tag::insert(&*conn, NewTag { | ||||
|                         tag: hashtag.clone(), | ||||
|                         is_hashtag: true, | ||||
|                         post_id: post.id, | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|             for ot in old_tags { | ||||
|                 if !tags.contains(&ot.tag) { | ||||
|                 if !hashtags.contains(&ot.tag) && ot.is_hashtag { | ||||
|                     ot.delete(&conn); | ||||
|                 } | ||||
|             } | ||||
| @ -294,7 +310,7 @@ fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: D | ||||
|             // actually it's not "Ok"…
 | ||||
|             Ok(Redirect::to(uri!(super::blogs::details: name = blog_name))) | ||||
|         } else { | ||||
|             let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref()); | ||||
|             let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); | ||||
| 
 | ||||
|             let post = Post::insert(&*conn, NewPost { | ||||
|                 blog_id: blog.id, | ||||
| @ -322,7 +338,14 @@ fn create(blog_name: String, data: LenientForm<NewPostForm>, user: User, conn: D | ||||
|             for tag in tags { | ||||
|                 Tag::insert(&*conn, NewTag { | ||||
|                     tag: tag, | ||||
|                     is_hastag: false, | ||||
|                     is_hashtag: false, | ||||
|                     post_id: post.id | ||||
|                 }); | ||||
|             } | ||||
|             for hashtag in hashtags { | ||||
|                 Tag::insert(&*conn, NewTag { | ||||
|                     tag: hashtag.to_camel_case(), | ||||
|                     is_hashtag: true, | ||||
|                     post_id: post.id | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
| @ -4,25 +4,23 @@ use serde_json; | ||||
| use plume_models::{ | ||||
|     db_conn::DbConn, | ||||
|     posts::Post, | ||||
|     tags::Tag, | ||||
|     users::User, | ||||
| }; | ||||
| use routes::Page; | ||||
| 
 | ||||
| #[get("/tag/<name>")] | ||||
| fn tag(user: Option<User>, conn: DbConn, name: String) -> Option<Template> { | ||||
| fn tag(user: Option<User>, conn: DbConn, name: String) -> Template { | ||||
|     paginated_tag(user, conn, name, Page::first()) | ||||
| } | ||||
| 
 | ||||
| #[get("/tag/<name>?<page>")] | ||||
| fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page) -> Option<Template> { | ||||
|     let tag = Tag::find_by_name(&*conn, name)?; | ||||
|     let posts = Post::list_by_tag(&*conn, tag.tag.clone(), page.limits()); | ||||
|     Some(Template::render("tags/index", json!({ | ||||
|         "tag": tag.clone(), | ||||
| fn paginated_tag(user: Option<User>, conn: DbConn, name: String, page: Page) -> Template { | ||||
|     let posts = Post::list_by_tag(&*conn, name.clone(), page.limits()); | ||||
|     Template::render("tags/index", json!({ | ||||
|         "tag": name.clone(), | ||||
|         "account": user.map(|u| u.to_json(&*conn)), | ||||
|         "articles": posts.into_iter().map(|p| p.to_json(&*conn)).collect::<Vec<serde_json::Value>>(), | ||||
|         "page": page.page, | ||||
|         "n_pages": Page::total(Post::count_for_tag(&*conn, tag.tag) as i32) | ||||
|     }))) | ||||
|         "n_pages": Page::total(Post::count_for_tag(&*conn, name) as i32) | ||||
|     })) | ||||
| } | ||||
|  | ||||
| @ -43,7 +43,9 @@ | ||||
|         <p>{{ "This article is under the {{ license }} license." | _(license=article.post.license) }}</p> | ||||
|         <ul class="tags"> | ||||
|             {% for tag in article.tags %} | ||||
|                 <li><a href="/tag/{{ tag.tag }}">{{ tag.tag }}</a></li> | ||||
| 		{% if not tag.is_hashtag %} | ||||
|                     <li><a href="/tag/{{ tag.tag }}">{{ tag.tag }}</a></li> | ||||
|                 {% endif %} | ||||
|             {% endfor %} | ||||
|         </ul> | ||||
|         <div class="flex"> | ||||
|  | ||||
| @ -2,16 +2,22 @@ | ||||
| {% import "macros" as macros %} | ||||
| 
 | ||||
| {% block title %} | ||||
| {{ 'Articles tagged "{{ tag }}"' | _(tag=tag.tag) }} | ||||
| {{ 'Articles tagged "{{ tag }}"' | _(tag=tag) }} | ||||
| {% endblock title %} | ||||
| 
 | ||||
| {% block content %} | ||||
|     <h1>{{ 'Articles tagged "{{ tag }}"' | _(tag=tag.tag) }}</h1> | ||||
|     <h1>{{ 'Articles tagged "{{ tag }}"' | _(tag=tag) }}</h1> | ||||
| 
 | ||||
|     <div class="cards"> | ||||
|         {% for article in articles %} | ||||
|             {{ macros::post_card(article=article) }} | ||||
|         {% endfor %} | ||||
|     </div> | ||||
|     {% if articles| length != 0 %} | ||||
|         <div class="cards"> | ||||
|             {% for article in articles %} | ||||
|                 {{ macros::post_card(article=article) }} | ||||
|             {% endfor %} | ||||
|         </div> | ||||
|     {% else %} | ||||
|         <section> | ||||
|             <h2>{{ "There is currently no article with that tag" | _ }}</h2> | ||||
|         </section> | ||||
|     {% endif %} | ||||
|     {{ macros::paginate(page=page, total=n_pages) }} | ||||
| {% endblock content %} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user