Replace client-side tag URLs with collection IDs
This commit is contained in:
parent
be67972760
commit
d09770913b
8 changed files with 55 additions and 19 deletions
|
@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Make `delete-emoji` command accept emoji name and hostname instead of ID.
|
- Make `delete-emoji` command accept emoji name and hostname instead of ID.
|
||||||
|
- Replaced client-side tag URLs with collection IDs.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::activitypub::{
|
||||||
local_actor_subscribers,
|
local_actor_subscribers,
|
||||||
local_emoji_id,
|
local_emoji_id,
|
||||||
local_object_id,
|
local_object_id,
|
||||||
|
local_tag_collection,
|
||||||
},
|
},
|
||||||
types::{Attachment, EmojiTag, EmojiTagImage, LinkTag, SimpleTag},
|
types::{Attachment, EmojiTag, EmojiTagImage, LinkTag, SimpleTag},
|
||||||
vocabulary::*,
|
vocabulary::*,
|
||||||
|
@ -25,7 +26,6 @@ use crate::models::{
|
||||||
users::types::User,
|
users::types::User,
|
||||||
};
|
};
|
||||||
use crate::utils::files::get_file_url;
|
use crate::utils::files::get_file_url;
|
||||||
use crate::web_client::urls::get_tag_page_url;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -136,11 +136,11 @@ pub fn build_note(
|
||||||
tags.push(Tag::SimpleTag(tag));
|
tags.push(Tag::SimpleTag(tag));
|
||||||
};
|
};
|
||||||
for tag_name in &post.tags {
|
for tag_name in &post.tags {
|
||||||
let tag_page_url = get_tag_page_url(instance_url, tag_name);
|
let tag_href = local_tag_collection(instance_url, tag_name);
|
||||||
let tag = SimpleTag {
|
let tag = SimpleTag {
|
||||||
name: format!("#{}", tag_name),
|
name: format!("#{}", tag_name),
|
||||||
tag_type: HASHTAG.to_string(),
|
tag_type: HASHTAG.to_string(),
|
||||||
href: tag_page_url,
|
href: tag_href,
|
||||||
};
|
};
|
||||||
tags.push(Tag::SimpleTag(tag));
|
tags.push(Tag::SimpleTag(tag));
|
||||||
};
|
};
|
||||||
|
@ -327,7 +327,11 @@ mod tests {
|
||||||
username: "author".to_string(),
|
username: "author".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let post = Post { author, ..Default::default() };
|
let post = Post {
|
||||||
|
author,
|
||||||
|
tags: vec!["test".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
let note = build_note(INSTANCE_HOSTNAME, INSTANCE_URL, &post);
|
let note = build_note(INSTANCE_HOSTNAME, INSTANCE_URL, &post);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -345,6 +349,13 @@ mod tests {
|
||||||
assert_eq!(note.cc, vec![
|
assert_eq!(note.cc, vec![
|
||||||
local_actor_followers(INSTANCE_URL, "author"),
|
local_actor_followers(INSTANCE_URL, "author"),
|
||||||
]);
|
]);
|
||||||
|
assert_eq!(note.tag.len(), 1);
|
||||||
|
let tag = match note.tag[0] {
|
||||||
|
Tag::SimpleTag(ref tag) => tag,
|
||||||
|
_ => panic!(),
|
||||||
|
};
|
||||||
|
assert_eq!(tag.name, "#test");
|
||||||
|
assert_eq!(tag.href, "https://example.com/collections/tags/test");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -24,6 +24,7 @@ impl LocalActorCollection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mastodon and Pleroma use the same actor ID format
|
||||||
pub fn local_actor_id(instance_url: &str, username: &str) -> String {
|
pub fn local_actor_id(instance_url: &str, username: &str) -> String {
|
||||||
format!("{}/users/{}", instance_url, username)
|
format!("{}/users/{}", instance_url, username)
|
||||||
}
|
}
|
||||||
|
@ -65,6 +66,10 @@ pub fn local_emoji_id(instance_url: &str, emoji_name: &str) -> String {
|
||||||
format!("{}/objects/emojis/{}", instance_url, emoji_name)
|
format!("{}/objects/emojis/{}", instance_url, emoji_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_tag_collection(instance_url: &str, tag_name: &str) -> String {
|
||||||
|
format!("{}/collections/tags/{}", instance_url, tag_name)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_local_actor_id(
|
pub fn parse_local_actor_id(
|
||||||
instance_url: &str,
|
instance_url: &str,
|
||||||
actor_id: &str,
|
actor_id: &str,
|
||||||
|
|
|
@ -18,7 +18,11 @@ use crate::models::{
|
||||||
posts::queries::{get_post_by_id, get_posts_by_author},
|
posts::queries::{get_post_by_id, get_posts_by_author},
|
||||||
users::queries::get_user_by_name,
|
users::queries::get_user_by_name,
|
||||||
};
|
};
|
||||||
use crate::web_client::urls::{get_post_page_url, get_profile_page_url};
|
use crate::web_client::urls::{
|
||||||
|
get_post_page_url,
|
||||||
|
get_profile_page_url,
|
||||||
|
get_tag_page_url,
|
||||||
|
};
|
||||||
use super::actors::types::{get_local_actor, get_instance_actor};
|
use super::actors::types::{get_local_actor, get_instance_actor};
|
||||||
use super::builders::create_note::{
|
use super::builders::create_note::{
|
||||||
build_emoji_tag,
|
build_emoji_tag,
|
||||||
|
@ -364,6 +368,18 @@ pub async fn emoji_view(
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/collections/tags/{tag_name}")]
|
||||||
|
pub async fn tag_view(
|
||||||
|
config: web::Data<Config>,
|
||||||
|
tag_name: web::Path<String>,
|
||||||
|
) -> Result<HttpResponse, HttpError> {
|
||||||
|
let page_url = get_tag_page_url(&config.instance_url(), &tag_name);
|
||||||
|
let response = HttpResponse::Found()
|
||||||
|
.append_header(("Location", page_url))
|
||||||
|
.finish();
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::http::{
|
use actix_web::http::{
|
||||||
|
|
|
@ -159,6 +159,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(activitypub::instance_actor_scope())
|
.service(activitypub::instance_actor_scope())
|
||||||
.service(activitypub::object_view)
|
.service(activitypub::object_view)
|
||||||
.service(activitypub::emoji_view)
|
.service(activitypub::emoji_view)
|
||||||
|
.service(activitypub::tag_view)
|
||||||
.service(atom::get_atom_feed)
|
.service(atom::get_atom_feed)
|
||||||
.service(nodeinfo::get_nodeinfo)
|
.service(nodeinfo::get_nodeinfo)
|
||||||
.service(nodeinfo::get_nodeinfo_2_0)
|
.service(nodeinfo::get_nodeinfo_2_0)
|
||||||
|
|
|
@ -285,7 +285,7 @@ pub async fn search(
|
||||||
posts,
|
posts,
|
||||||
).await?;
|
).await?;
|
||||||
let hashtags = tags.into_iter()
|
let hashtags = tags.into_iter()
|
||||||
.map(Tag::from_tag_name)
|
.map(|tag_name| Tag::from_tag_name(&config.instance_url(), tag_name))
|
||||||
.collect();
|
.collect();
|
||||||
Ok(SearchResults { accounts, statuses, hashtags })
|
Ok(SearchResults { accounts, statuses, hashtags })
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::activitypub::identifiers::local_tag_collection;
|
||||||
use crate::mastodon_api::accounts::types::Account;
|
use crate::mastodon_api::accounts::types::Account;
|
||||||
use crate::mastodon_api::media::types::Attachment;
|
use crate::mastodon_api::media::types::Attachment;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
|
@ -39,11 +40,11 @@ pub struct Tag {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
pub fn from_tag_name(tag_name: String) -> Self {
|
pub fn from_tag_name(instance_url: &str, tag_name: String) -> Self {
|
||||||
|
let tag_url = local_tag_collection(instance_url, &tag_name);
|
||||||
Tag {
|
Tag {
|
||||||
name: tag_name,
|
name: tag_name,
|
||||||
// TODO: add link to tag page
|
url: tag_url,
|
||||||
url: "".to_string(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ impl Status {
|
||||||
.map(|item| Mention::from_profile(item, instance_url))
|
.map(|item| Mention::from_profile(item, instance_url))
|
||||||
.collect();
|
.collect();
|
||||||
let tags: Vec<Tag> = post.tags.into_iter()
|
let tags: Vec<Tag> = post.tags.into_iter()
|
||||||
.map(Tag::from_tag_name)
|
.map(|tag_name| Tag::from_tag_name(instance_url, tag_name))
|
||||||
.collect();
|
.collect();
|
||||||
let emojis: Vec<CustomEmoji> = post.emojis.into_iter()
|
let emojis: Vec<CustomEmoji> = post.emojis.into_iter()
|
||||||
.map(|emoji| CustomEmoji::from_db(instance_url, emoji))
|
.map(|emoji| CustomEmoji::from_db(instance_url, emoji))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
|
|
||||||
|
use crate::activitypub::identifiers::local_tag_collection;
|
||||||
use crate::errors::ValidationError;
|
use crate::errors::ValidationError;
|
||||||
use crate::web_client::urls::get_tag_page_url;
|
|
||||||
use super::links::is_inside_code_block;
|
use super::links::is_inside_code_block;
|
||||||
|
|
||||||
const HASHTAG_RE: &str = r"(?m)(?P<before>^|\s|>|[\(])#(?P<tag>[^\s<]+)";
|
const HASHTAG_RE: &str = r"(?m)(?P<before>^|\s|>|[\(])#(?P<tag>[^\s<]+)";
|
||||||
|
@ -45,11 +45,11 @@ pub fn replace_hashtags(instance_url: &str, text: &str, tags: &[String]) -> Stri
|
||||||
let tag_name = tag.to_lowercase();
|
let tag_name = tag.to_lowercase();
|
||||||
let after = secondary_caps["after"].to_string();
|
let after = secondary_caps["after"].to_string();
|
||||||
if tags.contains(&tag_name) {
|
if tags.contains(&tag_name) {
|
||||||
let tag_page_url = get_tag_page_url(instance_url, &tag_name);
|
let tag_url = local_tag_collection(instance_url, &tag_name);
|
||||||
return format!(
|
return format!(
|
||||||
r#"{}<a class="hashtag" href="{}">#{}</a>{}"#,
|
r#"{}<a class="hashtag" href="{}">#{}</a>{}"#,
|
||||||
before,
|
before,
|
||||||
tag_page_url,
|
tag_url,
|
||||||
tag,
|
tag,
|
||||||
after,
|
after,
|
||||||
);
|
);
|
||||||
|
@ -101,13 +101,14 @@ mod tests {
|
||||||
let output = replace_hashtags(INSTANCE_URL, TEXT_WITH_TAGS, &tags);
|
let output = replace_hashtags(INSTANCE_URL, TEXT_WITH_TAGS, &tags);
|
||||||
|
|
||||||
let expected_output = concat!(
|
let expected_output = concat!(
|
||||||
r#"@user1@server1 some text <a class="hashtag" href="https://example.com/tag/testtag">#TestTag</a>."#, "\n",
|
r#"@user1@server1 some text <a class="hashtag" href="https://example.com/collections/tags/testtag">#TestTag</a>."#, "\n",
|
||||||
r#"<a class="hashtag" href="https://example.com/tag/tag1">#TAG1</a> <a class="hashtag" href="https://example.com/tag/tag1">#tag1</a> "#,
|
r#"<a class="hashtag" href="https://example.com/collections/tags/tag1">#TAG1</a> "#,
|
||||||
|
r#"<a class="hashtag" href="https://example.com/collections/tags/tag1">#tag1</a> "#,
|
||||||
r#"#test_underscore #test*special "#,
|
r#"#test_underscore #test*special "#,
|
||||||
r#"more text (<a class="hashtag" href="https://example.com/tag/tag2">#tag2</a>) text "#,
|
r#"more text (<a class="hashtag" href="https://example.com/collections/tags/tag2">#tag2</a>) text "#,
|
||||||
r#"<a class="hashtag" href="https://example.com/tag/tag3">#tag3</a>, "#,
|
r#"<a class="hashtag" href="https://example.com/collections/tags/tag3">#tag3</a>, "#,
|
||||||
r#"<a class="hashtag" href="https://example.com/tag/tag4">#tag4</a>:<br>"#,
|
r#"<a class="hashtag" href="https://example.com/collections/tags/tag4">#tag4</a>:<br>"#,
|
||||||
r#"end with <a class="hashtag" href="https://example.com/tag/tag5">#tag5</a>"#,
|
r#"end with <a class="hashtag" href="https://example.com/collections/tags/tag5">#tag5</a>"#,
|
||||||
);
|
);
|
||||||
assert_eq!(output, expected_output);
|
assert_eq!(output, expected_output);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue