Skip to content
Merged
Next Next commit
Renaming tag -> community_tag
This is a lot clearer since the tag table has a required community_id
column.

Tables / objects should be named by their ownership level, and these
tags are created and maintained by communities.

If in the future we want to add PersonTag, we can.

- Fixes #6292
  • Loading branch information
dessalines committed Jan 23, 2026
commit d8266555cd798550dfcd3ae3221207ef7ff8bd2f
26 changes: 13 additions & 13 deletions crates/api/api/src/community/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::source::{
community::Community,
tag::{Tag, TagInsertForm, TagUpdateForm},
community_tag::{CommunityTag, CommunityTagInsertForm, CommunityTagUpdateForm},
};
use lemmy_db_views_community::{
CommunityView,
Expand All @@ -29,7 +29,7 @@ pub async fn create_community_tag(
Json(data): Json<CreateCommunityTag>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<Tag>> {
) -> LemmyResult<Json<CommunityTag>> {
is_valid_actor_name(&data.name)?;

let community_view =
Expand All @@ -39,7 +39,7 @@ pub async fn create_community_tag(
// Verify that only mods can create tags
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;

check_api_elements_count(community_view.post_tags.0.len())?;
check_api_elements_count(community_view.tags.0.len())?;
if let Some(desc) = &data.description {
summary_length_check(desc)?;
check_slurs(desc, &slur_regex(&context).await?)?;
Expand All @@ -48,7 +48,7 @@ pub async fn create_community_tag(
let ap_id = Url::parse(&format!("{}/tag/{}", community.ap_id, &data.name))?;

// Create the tag
let tag_form = TagInsertForm {
let tag_form = CommunityTagInsertForm {
name: data.name.clone(),
display_name: data.display_name.clone(),
description: data.description.clone(),
Expand All @@ -57,7 +57,7 @@ pub async fn create_community_tag(
deleted: Some(false),
};

let tag = Tag::create(&mut context.pool(), &tag_form).await?;
let tag = CommunityTag::create(&mut context.pool(), &tag_form).await?;

ActivityChannel::submit_activity(
SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
Expand All @@ -71,8 +71,8 @@ pub async fn update_community_tag(
Json(data): Json<UpdateCommunityTag>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<Tag>> {
let tag = Tag::read(&mut context.pool(), data.tag_id).await?;
) -> LemmyResult<Json<CommunityTag>> {
let tag = CommunityTag::read(&mut context.pool(), data.tag_id).await?;
let community = Community::read(&mut context.pool(), tag.community_id).await?;

// Verify that only mods can update tags
Expand All @@ -84,36 +84,36 @@ pub async fn update_community_tag(
}

// Update the tag
let tag_form = TagUpdateForm {
let tag_form = CommunityTagUpdateForm {
display_name: diesel_string_update(data.display_name.as_deref()),
description: diesel_string_update(data.description.as_deref()),
updated_at: Some(Some(Utc::now())),
..Default::default()
};

let tag = Tag::update(&mut context.pool(), data.tag_id, &tag_form).await?;
let tag = CommunityTag::update(&mut context.pool(), data.tag_id, &tag_form).await?;
Ok(Json(tag))
}

pub async fn delete_community_tag(
Json(data): Json<DeleteCommunityTag>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<Tag>> {
let tag = Tag::read(&mut context.pool(), data.tag_id).await?;
) -> LemmyResult<Json<CommunityTag>> {
let tag = CommunityTag::read(&mut context.pool(), data.tag_id).await?;
let community = Community::read(&mut context.pool(), tag.community_id).await?;

// Verify that only mods can delete tags
check_community_mod_action(&local_user_view, &community, false, &mut context.pool()).await?;

// Soft delete the tag
let tag_form = TagUpdateForm {
let tag_form = CommunityTagUpdateForm {
updated_at: Some(Some(Utc::now())),
deleted: Some(true),
..Default::default()
};

let tag = Tag::update(&mut context.pool(), data.tag_id, &tag_form).await?;
let tag = CommunityTag::update(&mut context.pool(), data.tag_id, &tag_form).await?;

ActivityChannel::submit_activity(
SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
Expand Down
4 changes: 2 additions & 2 deletions crates/api/api_common/src/community.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
pub use lemmy_db_schema::{
newtypes::{CommunityId, MultiCommunityId, TagId},
newtypes::{CommunityId, CommunityTagId, MultiCommunityId},
source::{
community::{Community, CommunityActions},
community_tag::{CommunityTag, CommunityTagsView},
multi_community::{MultiCommunity, MultiCommunityFollow},
tag::{Tag, TagsView},
},
};
pub use lemmy_db_schema_file::enums::CommunityVisibility;
Expand Down
12 changes: 6 additions & 6 deletions crates/api/api_utils/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use chrono::{DateTime, Days, Local, TimeZone, Utc};
use enum_map::{EnumMap, enum_map};
use lemmy_db_schema::{
newtypes::{CommunityId, PostId, PostOrCommentId, TagId},
newtypes::{CommunityId, CommunityTagId, PostId, PostOrCommentId},
source::{
comment::{Comment, CommentActions, CommentLikeForm},
community::{Community, CommunityActions, CommunityUpdateForm},
community_tag::{CommunityTag, PostCommunityTag},
images::{ImageDetails, RemoteImage},
instance::InstanceActions,
local_site::LocalSite,
Expand All @@ -24,7 +25,6 @@ use lemmy_db_schema::{
private_message::PrivateMessage,
registration_application::RegistrationApplication,
site::Site,
tag::{PostTag, Tag},
},
traits::Likeable,
};
Expand Down Expand Up @@ -958,19 +958,19 @@ pub fn check_comment_depth(comment: &Comment) -> LemmyResult<()> {

pub async fn update_post_tags(
post: &Post,
tag_ids: &[TagId],
community_tag_ids: &[CommunityTagId],
context: &LemmyContext,
) -> LemmyResult<()> {
// validate tags
let community_tags = Tag::read_for_community(&mut context.pool(), post.community_id)
let community_tags = CommunityTag::read_for_community(&mut context.pool(), post.community_id)
.await?
.into_iter()
.map(|t| t.id)
.collect::<HashSet<_>>();
if !community_tags.is_superset(&tag_ids.iter().copied().collect()) {
if !community_tags.is_superset(&community_tag_ids.iter().copied().collect()) {
return Err(LemmyErrorType::TagNotInCommunity.into());
}
PostTag::update(&mut context.pool(), post, tag_ids).await?;
PostCommunityTag::update(&mut context.pool(), post, community_tag_ids).await?;
Ok(())
}

Expand Down
8 changes: 4 additions & 4 deletions crates/apub/apub/src/http/community.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ use lemmy_apub_objects::{
multi_community::ApubMultiCommunity,
multi_community_collection::ApubFeedCollection,
},
protocol::tags::CommunityTag,
protocol::tags::ApubCommunityTag,
};
use lemmy_db_schema::{
source::{community::Community, multi_community::MultiCommunity, tag::Tag},
source::{community::Community, community_tag::CommunityTag, multi_community::MultiCommunity},
traits::ApubActor,
};
use lemmy_db_schema_file::enums::CommunityVisibility;
Expand Down Expand Up @@ -216,10 +216,10 @@ pub(crate) async fn get_apub_community_tag_http(

check_community_fetchable(&community)?;

let tag = Tag::read_for_community(&mut context.pool(), community.id)
let tag = CommunityTag::read_for_community(&mut context.pool(), community.id)
.await?
.into_iter()
.map(CommunityTag::to_json)
.map(ApubCommunityTag::to_json)
.find(|t| t.preferred_username == info.tag_name)
.ok_or(LemmyErrorType::NotFound)?;

Expand Down
15 changes: 9 additions & 6 deletions crates/apub/objects/src/objects/community.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
objects::instance::fetch_instance_actor_for_object,
protocol::{group::Group, tags::CommunityTag},
protocol::{group::Group, tags::ApubCommunityTag},
utils::{
functions::{
GetActorType,
Expand Down Expand Up @@ -36,7 +36,7 @@ use lemmy_db_schema::{
source::{
actor_language::CommunityLanguage,
community::{Community, CommunityInsertForm, CommunityUpdateForm},
tag::Tag,
community_tag::CommunityTag,
},
traits::ApubActor,
};
Expand Down Expand Up @@ -117,7 +117,7 @@ impl Object for ApubCommunity {
let community_id = self.id;
let langs = CommunityLanguage::read(&mut data.pool(), community_id).await?;
let language = LanguageTag::new_multiple(langs, &mut data.pool()).await?;
let post_tags = Tag::read_for_community(&mut data.pool(), community_id).await?;
let community_tags = CommunityTag::read_for_community(&mut data.pool(), community_id).await?;
let group = Group {
kind: GroupType::Group,
id: self.id().clone().into(),
Expand Down Expand Up @@ -145,7 +145,10 @@ impl Object for ApubCommunity {
)),
manually_approves_followers: Some(self.visibility == CommunityVisibility::Private),
discoverable: Some(self.visibility != CommunityVisibility::Unlisted),
tag: post_tags.into_iter().map(CommunityTag::to_json).collect(),
tag: community_tags
.into_iter()
.map(ApubCommunityTag::to_json)
.collect(),
};
Ok(group)
}
Expand Down Expand Up @@ -245,8 +248,8 @@ impl Object for ApubCommunity {
.iter()
.map(|t| t.to_insert_form(community.id))
.collect();
let existing_tags = Tag::read_for_community(&mut context.pool(), community.id).await?;
Tag::update_many(&mut context.pool(), new_tags, existing_tags).await?;
let existing_tags = CommunityTag::read_for_community(&mut context.pool(), community.id).await?;
CommunityTag::update_many(&mut context.pool(), new_tags, existing_tags).await?;

let community: ApubCommunity = community.into();

Expand Down
11 changes: 6 additions & 5 deletions crates/apub/objects/src/objects/post.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
protocol::{
page::{Attachment, Page, PageType},
tags::{ApubTag, CommunityTag, Hashtag, HashtagType},
tags::{ApubCommunityTag, ApubTag, Hashtag, HashtagType},
},
utils::{
functions::{
Expand Down Expand Up @@ -39,10 +39,10 @@ use lemmy_api_utils::{
};
use lemmy_db_schema::source::{
community::Community,
community_tag::CommunityTag,
local_site::LocalSite,
person::Person,
post::{Post, PostInsertForm, PostUpdateForm},
tag::Tag,
};
use lemmy_db_views_community_moderator::CommunityModeratorView;
use lemmy_db_views_site::SiteView;
Expand Down Expand Up @@ -137,10 +137,10 @@ impl Object for ApubPost {
.collect();

// Add tags defined by community and applied to this post
let mut tags: Vec<ApubTag> = Tag::read_for_post(&mut context.pool(), self.id)
let mut tags: Vec<ApubTag> = CommunityTag::read_for_post(&mut context.pool(), self.id)
.await?
.into_iter()
.map(|tag| ApubTag::CommunityTag(CommunityTag::to_json(tag)))
.map(|tag| ApubTag::CommunityTag(ApubCommunityTag::to_json(tag)))
.collect();

// Add automatic hashtag based on community name
Expand Down Expand Up @@ -333,7 +333,8 @@ pub async fn update_apub_post_tags(
.iter()
.filter_map(ApubTag::community_tag_id)
.collect::<HashSet<_>>();
let community_tags = Tag::read_for_community(&mut context.pool(), post.community_id).await?;
let community_tags =
CommunityTag::read_for_community(&mut context.pool(), post.community_id).await?;
let post_tags = community_tags
.into_iter()
.filter(|t| post_tag_ap_ids.contains(&*t.ap_id.0))
Expand Down
4 changes: 2 additions & 2 deletions crates/apub/objects/src/protocol/group.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
objects::community::ApubCommunity,
protocol::tags::CommunityTag,
protocol::tags::ApubCommunityTag,
utils::protocol::{AttributedTo, Endpoints, ImageObject, LanguageTag, Source},
};
use activitypub_federation::{
Expand Down Expand Up @@ -63,5 +63,5 @@ pub struct Group {
/// https://docs.joinmastodon.org/spec/activitypub/#discoverable
pub(crate) discoverable: Option<bool>,
#[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) tag: Vec<CommunityTag>,
pub(crate) tag: Vec<ApubCommunityTag>,
}
16 changes: 8 additions & 8 deletions crates/apub/objects/src/protocol/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::objects::person::ApubPerson;
use activitypub_federation::{fetch::object_id::ObjectId, kinds::link::MentionType};
use lemmy_db_schema::{
newtypes::CommunityId,
source::tag::{Tag, TagInsertForm},
source::community_tag::{CommunityTag, CommunityTagInsertForm},
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand All @@ -15,7 +15,7 @@ use url::Url;
#[serde(untagged)]
pub enum ApubTag {
Hashtag(Hashtag),
CommunityTag(CommunityTag),
CommunityTag(ApubCommunityTag),
Mention(Mention),
Unknown(Value),
}
Expand Down Expand Up @@ -68,7 +68,7 @@ enum CommunityTagType {
/// A tag that a community owns, that is added to a post.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CommunityTag {
pub struct ApubCommunityTag {
#[serde(rename = "type")]
kind: CommunityTagType,
pub id: Url,
Expand All @@ -77,9 +77,9 @@ pub struct CommunityTag {
pub content: Option<String>,
}

impl CommunityTag {
pub fn to_json(tag: Tag) -> Self {
CommunityTag {
impl ApubCommunityTag {
pub fn to_json(tag: CommunityTag) -> Self {
ApubCommunityTag {
kind: Default::default(),
id: tag.ap_id.into(),
name: tag.display_name,
Expand All @@ -88,8 +88,8 @@ impl CommunityTag {
}
}

pub fn to_insert_form(&self, community_id: CommunityId) -> TagInsertForm {
TagInsertForm {
pub fn to_insert_form(&self, community_id: CommunityId) -> CommunityTagInsertForm {
CommunityTagInsertForm {
ap_id: self.id.clone().into(),
name: self.preferred_username.clone(),
display_name: self.name.clone(),
Expand Down
Loading