parent
66b0ddbbc5
commit
6bc49bdd70
28 changed files with 526 additions and 10 deletions
|
@ -15,7 +15,7 @@ use lemmy_db_schema::{
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::SiteView;
|
use lemmy_db_views::structs::{CustomEmojiView, SiteView};
|
||||||
use lemmy_db_views_actor::structs::PersonView;
|
use lemmy_db_views_actor::structs::PersonView;
|
||||||
use lemmy_utils::{error::LemmyError, version, ConnectionId};
|
use lemmy_utils::{error::LemmyError, version, ConnectionId};
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@ impl Perform for LeaveAdmin {
|
||||||
|
|
||||||
let all_languages = Language::read_all(context.pool()).await?;
|
let all_languages = Language::read_all(context.pool()).await?;
|
||||||
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
||||||
let taglines_res = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
let taglines = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||||
let taglines = taglines_res.is_empty().then_some(taglines_res);
|
let custom_emojis = CustomEmojiView::get_all(context.pool(), site_view.local_site.id).await?;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view,
|
site_view,
|
||||||
|
@ -77,6 +77,7 @@ impl Perform for LeaveAdmin {
|
||||||
all_languages,
|
all_languages,
|
||||||
discussion_languages,
|
discussion_languages,
|
||||||
taglines,
|
taglines,
|
||||||
|
custom_emojis,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
crates/api_common/src/custom_emoji.rs
Normal file
42
crates/api_common/src/custom_emoji.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use crate::sensitive::Sensitive;
|
||||||
|
use lemmy_db_schema::newtypes::CustomEmojiId;
|
||||||
|
use lemmy_db_views::structs::CustomEmojiView;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct CreateCustomEmoji {
|
||||||
|
pub category: String,
|
||||||
|
pub shortcode: String,
|
||||||
|
pub image_url: Url,
|
||||||
|
pub alt_text: String,
|
||||||
|
pub keywords: Vec<String>,
|
||||||
|
pub auth: Sensitive<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct EditCustomEmoji {
|
||||||
|
pub id: CustomEmojiId,
|
||||||
|
pub category: String,
|
||||||
|
pub image_url: Url,
|
||||||
|
pub alt_text: String,
|
||||||
|
pub keywords: Vec<String>,
|
||||||
|
pub auth: Sensitive<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
|
pub struct DeleteCustomEmoji {
|
||||||
|
pub id: CustomEmojiId,
|
||||||
|
pub auth: Sensitive<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct DeleteCustomEmojiResponse {
|
||||||
|
pub id: CustomEmojiId,
|
||||||
|
pub success: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct CustomEmojiResponse {
|
||||||
|
pub custom_emoji: CustomEmojiView,
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ pub mod comment;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod custom_emoji;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
pub mod post;
|
pub mod post;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
|
|
|
@ -14,6 +14,7 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{
|
use lemmy_db_views::structs::{
|
||||||
CommentView,
|
CommentView,
|
||||||
|
CustomEmojiView,
|
||||||
LocalUserView,
|
LocalUserView,
|
||||||
PostView,
|
PostView,
|
||||||
RegistrationApplicationView,
|
RegistrationApplicationView,
|
||||||
|
@ -224,7 +225,8 @@ pub struct GetSiteResponse {
|
||||||
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
|
pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
|
||||||
pub all_languages: Vec<Language>,
|
pub all_languages: Vec<Language>,
|
||||||
pub discussion_languages: Vec<LanguageId>,
|
pub discussion_languages: Vec<LanguageId>,
|
||||||
pub taglines: Option<Vec<Tagline>>,
|
pub taglines: Vec<Tagline>,
|
||||||
|
pub custom_emojis: Vec<CustomEmojiView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -118,6 +118,10 @@ pub enum UserOperationCrud {
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
EditPrivateMessage,
|
EditPrivateMessage,
|
||||||
DeletePrivateMessage,
|
DeletePrivateMessage,
|
||||||
|
//Emojis
|
||||||
|
CreateCustomEmoji,
|
||||||
|
EditCustomEmoji,
|
||||||
|
DeleteCustomEmoji,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, Display, Debug, Clone)]
|
#[derive(EnumString, Display, Debug, Clone)]
|
||||||
|
|
54
crates/api_crud/src/custom_emoji/create.rs
Normal file
54
crates/api_crud/src/custom_emoji/create.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::PerformCrud;
|
||||||
|
use actix_web::web::Data;
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
|
||||||
|
utils::{get_local_user_view_from_jwt, is_admin},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
custom_emoji::{CustomEmoji, CustomEmojiInsertForm},
|
||||||
|
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||||
|
local_site::LocalSite,
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::CustomEmojiView;
|
||||||
|
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl PerformCrud for CreateCustomEmoji {
|
||||||
|
type Response = CustomEmojiResponse;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
_websocket_id: Option<ConnectionId>,
|
||||||
|
) -> Result<CustomEmojiResponse, LemmyError> {
|
||||||
|
let data: &CreateCustomEmoji = self;
|
||||||
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
|
let local_site = LocalSite::read(context.pool()).await?;
|
||||||
|
// Make sure user is an admin
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
|
let emoji_form = CustomEmojiInsertForm::builder()
|
||||||
|
.local_site_id(local_site.id)
|
||||||
|
.shortcode(data.shortcode.to_lowercase().trim().to_string())
|
||||||
|
.alt_text(data.alt_text.to_string())
|
||||||
|
.category(data.category.to_string())
|
||||||
|
.image_url(data.clone().image_url.into())
|
||||||
|
.build();
|
||||||
|
let emoji = CustomEmoji::create(context.pool(), &emoji_form).await?;
|
||||||
|
let mut keywords = vec![];
|
||||||
|
for keyword in &data.keywords {
|
||||||
|
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||||
|
.custom_emoji_id(emoji.id)
|
||||||
|
.keyword(keyword.to_lowercase().trim().to_string())
|
||||||
|
.build();
|
||||||
|
keywords.push(keyword_form);
|
||||||
|
}
|
||||||
|
CustomEmojiKeyword::create(context.pool(), keywords).await?;
|
||||||
|
let view = CustomEmojiView::get(context.pool(), emoji.id).await?;
|
||||||
|
Ok(CustomEmojiResponse { custom_emoji: view })
|
||||||
|
}
|
||||||
|
}
|
33
crates/api_crud/src/custom_emoji/delete.rs
Normal file
33
crates/api_crud/src/custom_emoji/delete.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::PerformCrud;
|
||||||
|
use actix_web::web::Data;
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse},
|
||||||
|
utils::{get_local_user_view_from_jwt, is_admin},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::custom_emoji::CustomEmoji;
|
||||||
|
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl PerformCrud for DeleteCustomEmoji {
|
||||||
|
type Response = DeleteCustomEmojiResponse;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
_websocket_id: Option<ConnectionId>,
|
||||||
|
) -> Result<DeleteCustomEmojiResponse, LemmyError> {
|
||||||
|
let data: &DeleteCustomEmoji = self;
|
||||||
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
|
// Make sure user is an admin
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
CustomEmoji::delete(context.pool(), data.id).await?;
|
||||||
|
Ok(DeleteCustomEmojiResponse {
|
||||||
|
id: data.id,
|
||||||
|
success: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
3
crates/api_crud/src/custom_emoji/mod.rs
Normal file
3
crates/api_crud/src/custom_emoji/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
mod create;
|
||||||
|
mod delete;
|
||||||
|
mod update;
|
54
crates/api_crud/src/custom_emoji/update.rs
Normal file
54
crates/api_crud/src/custom_emoji/update.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use crate::PerformCrud;
|
||||||
|
use actix_web::web::Data;
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
|
||||||
|
utils::{get_local_user_view_from_jwt, is_admin},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
custom_emoji::{CustomEmoji, CustomEmojiUpdateForm},
|
||||||
|
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||||
|
local_site::LocalSite,
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::CustomEmojiView;
|
||||||
|
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||||
|
|
||||||
|
#[async_trait::async_trait(?Send)]
|
||||||
|
impl PerformCrud for EditCustomEmoji {
|
||||||
|
type Response = CustomEmojiResponse;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self, context, _websocket_id))]
|
||||||
|
async fn perform(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
_websocket_id: Option<ConnectionId>,
|
||||||
|
) -> Result<CustomEmojiResponse, LemmyError> {
|
||||||
|
let data: &EditCustomEmoji = self;
|
||||||
|
let local_user_view =
|
||||||
|
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||||
|
|
||||||
|
let local_site = LocalSite::read(context.pool()).await?;
|
||||||
|
// Make sure user is an admin
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
|
let emoji_form = CustomEmojiUpdateForm::builder()
|
||||||
|
.local_site_id(local_site.id)
|
||||||
|
.alt_text(data.alt_text.to_string())
|
||||||
|
.category(data.category.to_string())
|
||||||
|
.image_url(data.clone().image_url.into())
|
||||||
|
.build();
|
||||||
|
let emoji = CustomEmoji::update(context.pool(), data.id, &emoji_form).await?;
|
||||||
|
CustomEmojiKeyword::delete(context.pool(), data.id).await?;
|
||||||
|
let mut keywords = vec![];
|
||||||
|
for keyword in &data.keywords {
|
||||||
|
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||||
|
.custom_emoji_id(emoji.id)
|
||||||
|
.keyword(keyword.to_lowercase().trim().to_string())
|
||||||
|
.build();
|
||||||
|
keywords.push(keyword_form);
|
||||||
|
}
|
||||||
|
CustomEmojiKeyword::create(context.pool(), keywords).await?;
|
||||||
|
let view = CustomEmojiView::get(context.pool(), emoji.id).await?;
|
||||||
|
Ok(CustomEmojiResponse { custom_emoji: view })
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||||
|
|
||||||
mod comment;
|
mod comment;
|
||||||
mod community;
|
mod community;
|
||||||
|
mod custom_emoji;
|
||||||
mod post;
|
mod post;
|
||||||
mod private_message;
|
mod private_message;
|
||||||
mod site;
|
mod site;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use lemmy_db_schema::source::{
|
||||||
language::Language,
|
language::Language,
|
||||||
tagline::Tagline,
|
tagline::Tagline,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::SiteView;
|
use lemmy_db_views::structs::{CustomEmojiView, SiteView};
|
||||||
use lemmy_db_views_actor::structs::{
|
use lemmy_db_views_actor::structs::{
|
||||||
CommunityBlockView,
|
CommunityBlockView,
|
||||||
CommunityFollowerView,
|
CommunityFollowerView,
|
||||||
|
@ -88,8 +88,8 @@ impl PerformCrud for GetSite {
|
||||||
|
|
||||||
let all_languages = Language::read_all(context.pool()).await?;
|
let all_languages = Language::read_all(context.pool()).await?;
|
||||||
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
let discussion_languages = SiteLanguage::read_local(context.pool()).await?;
|
||||||
let taglines_res = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
let taglines = Tagline::get_all(context.pool(), site_view.local_site.id).await?;
|
||||||
let taglines = (!taglines_res.is_empty()).then_some(taglines_res);
|
let custom_emojis = CustomEmojiView::get_all(context.pool(), site_view.local_site.id).await?;
|
||||||
|
|
||||||
Ok(GetSiteResponse {
|
Ok(GetSiteResponse {
|
||||||
site_view,
|
site_view,
|
||||||
|
@ -101,6 +101,7 @@ impl PerformCrud for GetSite {
|
||||||
all_languages,
|
all_languages,
|
||||||
discussion_languages,
|
discussion_languages,
|
||||||
taglines,
|
taglines,
|
||||||
|
custom_emojis,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,13 @@ use lemmy_api_common::{
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
TransferCommunity,
|
TransferCommunity,
|
||||||
},
|
},
|
||||||
|
custom_emoji::{
|
||||||
|
CreateCustomEmoji,
|
||||||
|
CustomEmojiResponse,
|
||||||
|
DeleteCustomEmoji,
|
||||||
|
DeleteCustomEmojiResponse,
|
||||||
|
EditCustomEmoji,
|
||||||
|
},
|
||||||
person::{
|
person::{
|
||||||
AddAdmin,
|
AddAdmin,
|
||||||
AddAdminResponse,
|
AddAdminResponse,
|
||||||
|
@ -354,3 +361,15 @@ impl SendActivity for ListCommentReports {
|
||||||
impl SendActivity for ResolveCommentReport {
|
impl SendActivity for ResolveCommentReport {
|
||||||
type Response = CommentReportResponse;
|
type Response = CommentReportResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SendActivity for CreateCustomEmoji {
|
||||||
|
type Response = CustomEmojiResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SendActivity for EditCustomEmoji {
|
||||||
|
type Response = CustomEmojiResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SendActivity for DeleteCustomEmoji {
|
||||||
|
type Response = DeleteCustomEmojiResponse;
|
||||||
|
}
|
||||||
|
|
60
crates/db_schema/src/impls/custom_emoji.rs
Normal file
60
crates/db_schema/src/impls/custom_emoji.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use crate::{
|
||||||
|
newtypes::CustomEmojiId,
|
||||||
|
schema::{
|
||||||
|
custom_emoji::dsl::custom_emoji,
|
||||||
|
custom_emoji_keyword::dsl::{custom_emoji_id, custom_emoji_keyword},
|
||||||
|
},
|
||||||
|
source::{
|
||||||
|
custom_emoji::{CustomEmoji, CustomEmojiInsertForm, CustomEmojiUpdateForm},
|
||||||
|
custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
|
||||||
|
},
|
||||||
|
utils::{get_conn, DbPool},
|
||||||
|
};
|
||||||
|
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
|
impl CustomEmoji {
|
||||||
|
pub async fn create(pool: &DbPool, form: &CustomEmojiInsertForm) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
insert_into(custom_emoji)
|
||||||
|
.values(form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
pub async fn update(
|
||||||
|
pool: &DbPool,
|
||||||
|
emoji_id: CustomEmojiId,
|
||||||
|
form: &CustomEmojiUpdateForm,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
diesel::update(custom_emoji.find(emoji_id))
|
||||||
|
.set(form)
|
||||||
|
.get_result::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
pub async fn delete(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<usize, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
diesel::delete(custom_emoji.find(emoji_id))
|
||||||
|
.execute(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomEmojiKeyword {
|
||||||
|
pub async fn create(
|
||||||
|
pool: &DbPool,
|
||||||
|
form: Vec<CustomEmojiKeywordInsertForm>,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
insert_into(custom_emoji_keyword)
|
||||||
|
.values(form)
|
||||||
|
.get_results::<Self>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
pub async fn delete(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<usize, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
diesel::delete(custom_emoji_keyword.filter(custom_emoji_id.eq(emoji_id)))
|
||||||
|
.execute(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ pub mod comment_reply;
|
||||||
pub mod comment_report;
|
pub mod comment_report;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
pub mod community_block;
|
pub mod community_block;
|
||||||
|
pub mod custom_emoji;
|
||||||
pub mod email_verification;
|
pub mod email_verification;
|
||||||
pub mod federation_allowlist;
|
pub mod federation_allowlist;
|
||||||
pub mod federation_blocklist;
|
pub mod federation_blocklist;
|
||||||
|
|
|
@ -108,6 +108,10 @@ pub struct InstanceId(i32);
|
||||||
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||||
pub struct LocalSiteId(i32);
|
pub struct LocalSiteId(i32);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||||
|
#[cfg_attr(feature = "full", derive(DieselNewType))]
|
||||||
|
pub struct CustomEmojiId(i32);
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||||
#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))]
|
#[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))]
|
||||||
|
|
|
@ -751,6 +751,27 @@ table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
custom_emoji (id) {
|
||||||
|
id -> Int4,
|
||||||
|
local_site_id -> Int4,
|
||||||
|
shortcode -> Varchar,
|
||||||
|
image_url -> Text,
|
||||||
|
alt_text -> Text,
|
||||||
|
category -> Text,
|
||||||
|
published -> Timestamp,
|
||||||
|
updated -> Nullable<Timestamp>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
custom_emoji_keyword (id) {
|
||||||
|
id -> Int4,
|
||||||
|
custom_emoji_id -> Int4,
|
||||||
|
keyword -> Varchar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
joinable!(person_block -> person (person_id));
|
joinable!(person_block -> person (person_id));
|
||||||
|
|
||||||
joinable!(comment -> person (creator_id));
|
joinable!(comment -> person (creator_id));
|
||||||
|
@ -836,6 +857,8 @@ joinable!(federation_blocklist -> instance (instance_id));
|
||||||
joinable!(local_site -> site (site_id));
|
joinable!(local_site -> site (site_id));
|
||||||
joinable!(local_site_rate_limit -> local_site (local_site_id));
|
joinable!(local_site_rate_limit -> local_site (local_site_id));
|
||||||
joinable!(tagline -> local_site (local_site_id));
|
joinable!(tagline -> local_site (local_site_id));
|
||||||
|
joinable!(custom_emoji -> local_site (local_site_id));
|
||||||
|
joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id));
|
||||||
|
|
||||||
allow_tables_to_appear_in_same_query!(
|
allow_tables_to_appear_in_same_query!(
|
||||||
activity,
|
activity,
|
||||||
|
@ -896,5 +919,7 @@ allow_tables_to_appear_in_same_query!(
|
||||||
federation_blocklist,
|
federation_blocklist,
|
||||||
local_site,
|
local_site,
|
||||||
local_site_rate_limit,
|
local_site_rate_limit,
|
||||||
person_follower
|
person_follower,
|
||||||
|
custom_emoji,
|
||||||
|
custom_emoji_keyword,
|
||||||
);
|
);
|
||||||
|
|
44
crates/db_schema/src/source/custom_emoji.rs
Normal file
44
crates/db_schema/src/source/custom_emoji.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use crate::newtypes::{CustomEmojiId, DbUrl, LocalSiteId};
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use crate::schema::custom_emoji;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
diesel(belongs_to(crate::source::local_site::LocalSite))
|
||||||
|
)]
|
||||||
|
pub struct CustomEmoji {
|
||||||
|
pub id: CustomEmojiId,
|
||||||
|
pub local_site_id: LocalSiteId,
|
||||||
|
pub shortcode: String,
|
||||||
|
pub image_url: DbUrl,
|
||||||
|
pub alt_text: String,
|
||||||
|
pub category: String,
|
||||||
|
pub published: chrono::NaiveDateTime,
|
||||||
|
pub updated: Option<chrono::NaiveDateTime>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||||
|
pub struct CustomEmojiInsertForm {
|
||||||
|
pub local_site_id: LocalSiteId,
|
||||||
|
pub shortcode: String,
|
||||||
|
pub image_url: DbUrl,
|
||||||
|
pub alt_text: String,
|
||||||
|
pub category: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||||
|
pub struct CustomEmojiUpdateForm {
|
||||||
|
pub local_site_id: LocalSiteId,
|
||||||
|
pub image_url: DbUrl,
|
||||||
|
pub alt_text: String,
|
||||||
|
pub category: String,
|
||||||
|
}
|
26
crates/db_schema/src/source/custom_emoji_keyword.rs
Normal file
26
crates/db_schema/src/source/custom_emoji_keyword.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::newtypes::CustomEmojiId;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
use crate::schema::custom_emoji_keyword;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji_keyword))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
diesel(belongs_to(crate::source::custom_emoji::CustomEmoji))
|
||||||
|
)]
|
||||||
|
pub struct CustomEmojiKeyword {
|
||||||
|
pub id: i32,
|
||||||
|
pub custom_emoji_id: CustomEmojiId,
|
||||||
|
pub keyword: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
|
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji_keyword))]
|
||||||
|
pub struct CustomEmojiKeywordInsertForm {
|
||||||
|
pub custom_emoji_id: CustomEmojiId,
|
||||||
|
pub keyword: String,
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ pub mod comment_reply;
|
||||||
pub mod comment_report;
|
pub mod comment_report;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
pub mod community_block;
|
pub mod community_block;
|
||||||
|
pub mod custom_emoji;
|
||||||
|
pub mod custom_emoji_keyword;
|
||||||
pub mod email_verification;
|
pub mod email_verification;
|
||||||
pub mod federation_allowlist;
|
pub mod federation_allowlist;
|
||||||
pub mod federation_blocklist;
|
pub mod federation_blocklist;
|
||||||
|
|
82
crates/db_views/src/custom_emoji_view.rs
Normal file
82
crates/db_views/src/custom_emoji_view.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::structs::CustomEmojiView;
|
||||||
|
use diesel::{result::Error, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
newtypes::{CustomEmojiId, LocalSiteId},
|
||||||
|
schema::{custom_emoji, custom_emoji_keyword},
|
||||||
|
source::{custom_emoji::CustomEmoji, custom_emoji_keyword::CustomEmojiKeyword},
|
||||||
|
utils::{get_conn, DbPool},
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
type CustomEmojiTuple = (CustomEmoji, Option<CustomEmojiKeyword>);
|
||||||
|
|
||||||
|
impl CustomEmojiView {
|
||||||
|
pub async fn get(pool: &DbPool, emoji_id: CustomEmojiId) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
let emojis = custom_emoji::table
|
||||||
|
.find(emoji_id)
|
||||||
|
.left_join(
|
||||||
|
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||||
|
)
|
||||||
|
.select((
|
||||||
|
custom_emoji::all_columns,
|
||||||
|
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||||
|
))
|
||||||
|
.load::<CustomEmojiTuple>(conn)
|
||||||
|
.await?;
|
||||||
|
if let Some(emoji) = CustomEmojiView::from_tuple_to_vec(emojis)
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
Ok(emoji)
|
||||||
|
} else {
|
||||||
|
Err(diesel::result::Error::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all(pool: &DbPool, for_local_site_id: LocalSiteId) -> Result<Vec<Self>, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
let emojis = custom_emoji::table
|
||||||
|
.filter(custom_emoji::local_site_id.eq(for_local_site_id))
|
||||||
|
.left_join(
|
||||||
|
custom_emoji_keyword::table.on(custom_emoji_keyword::custom_emoji_id.eq(custom_emoji::id)),
|
||||||
|
)
|
||||||
|
.order(custom_emoji::category)
|
||||||
|
.then_order_by(custom_emoji::id)
|
||||||
|
.select((
|
||||||
|
custom_emoji::all_columns,
|
||||||
|
custom_emoji_keyword::all_columns.nullable(), // (or all the columns if you want)
|
||||||
|
))
|
||||||
|
.load::<CustomEmojiTuple>(conn)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(CustomEmojiView::from_tuple_to_vec(emojis))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_tuple_to_vec(items: Vec<CustomEmojiTuple>) -> Vec<Self> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut hash: HashMap<CustomEmojiId, Vec<CustomEmojiKeyword>> = HashMap::new();
|
||||||
|
for item in &items {
|
||||||
|
let emoji_id: CustomEmojiId = item.0.id;
|
||||||
|
if let std::collections::hash_map::Entry::Vacant(e) = hash.entry(emoji_id) {
|
||||||
|
e.insert(Vec::new());
|
||||||
|
result.push(CustomEmojiView {
|
||||||
|
custom_emoji: item.0.clone(),
|
||||||
|
keywords: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if let Some(item_keyword) = &item.1 {
|
||||||
|
if let Some(keywords) = hash.get_mut(&emoji_id) {
|
||||||
|
keywords.push(item_keyword.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for emoji in &mut result {
|
||||||
|
if let Some(keywords) = hash.get_mut(&emoji.custom_emoji.id) {
|
||||||
|
emoji.keywords = keywords.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ pub mod comment_report_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod comment_view;
|
pub mod comment_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
pub mod custom_emoji_view;
|
||||||
|
#[cfg(feature = "full")]
|
||||||
pub mod local_user_view;
|
pub mod local_user_view;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod post_report_view;
|
pub mod post_report_view;
|
||||||
|
|
|
@ -4,6 +4,8 @@ use lemmy_db_schema::{
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
comment_report::CommentReport,
|
comment_report::CommentReport,
|
||||||
community::Community,
|
community::Community,
|
||||||
|
custom_emoji::CustomEmoji,
|
||||||
|
custom_emoji_keyword::CustomEmojiKeyword,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
local_site_rate_limit::LocalSiteRateLimit,
|
||||||
local_user::LocalUser,
|
local_user::LocalUser,
|
||||||
|
@ -113,3 +115,8 @@ pub struct SiteView {
|
||||||
pub local_site_rate_limit: LocalSiteRateLimit,
|
pub local_site_rate_limit: LocalSiteRateLimit,
|
||||||
pub counts: SiteAggregates,
|
pub counts: SiteAggregates,
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct CustomEmojiView {
|
||||||
|
pub custom_emoji: CustomEmoji,
|
||||||
|
pub keywords: Vec<CustomEmojiKeyword>,
|
||||||
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
pictrs:
|
pictrs:
|
||||||
image: asonix/pictrs:0.3.1
|
image: asonix/pictrs:0.4.0-beta.19
|
||||||
# this needs to match the pictrs url in lemmy.hjson
|
# this needs to match the pictrs url in lemmy.hjson
|
||||||
hostname: pictrs
|
hostname: pictrs
|
||||||
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
|
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
|
||||||
|
@ -82,6 +82,11 @@ services:
|
||||||
- PICTRS__API_KEY=API_KEY
|
- PICTRS__API_KEY=API_KEY
|
||||||
- RUST_LOG=debug
|
- RUST_LOG=debug
|
||||||
- RUST_BACKTRACE=full
|
- RUST_BACKTRACE=full
|
||||||
|
- PICTRS__MEDIA__VIDEO_CODEC=vp9
|
||||||
|
- PICTRS__MEDIA__GIF__MAX_WIDTH=256
|
||||||
|
- PICTRS__MEDIA__GIF__MAX_HEIGHT=256
|
||||||
|
- PICTRS__MEDIA__GIF__MAX_AREA=65536
|
||||||
|
- PICTRS__MEDIA__GIF__MAX_FRAME_COUNT=400
|
||||||
user: 991:991
|
user: 991:991
|
||||||
volumes:
|
volumes:
|
||||||
- ./volumes/pictrs:/mnt
|
- ./volumes/pictrs:/mnt
|
||||||
|
|
|
@ -22,7 +22,7 @@ services:
|
||||||
|
|
||||||
pictrs:
|
pictrs:
|
||||||
restart: always
|
restart: always
|
||||||
image: asonix/pictrs:0.3.1
|
image: asonix/pictrs:0.4.0-beta.19
|
||||||
user: 991:991
|
user: 991:991
|
||||||
volumes:
|
volumes:
|
||||||
- ./volumes/pictrs_alpha:/mnt
|
- ./volumes/pictrs_alpha:/mnt
|
||||||
|
|
2
migrations/2023-02-11-173347_custom_emojis/down.sql
Normal file
2
migrations/2023-02-11-173347_custom_emojis/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
drop table custom_emoji_keyword;
|
||||||
|
drop table custom_emoji;
|
19
migrations/2023-02-11-173347_custom_emojis/up.sql
Normal file
19
migrations/2023-02-11-173347_custom_emojis/up.sql
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
create table custom_emoji (
|
||||||
|
id serial primary key,
|
||||||
|
local_site_id int references local_site on update cascade on delete cascade not null,
|
||||||
|
shortcode varchar(128) not null UNIQUE,
|
||||||
|
image_url text not null UNIQUE,
|
||||||
|
alt_text text not null,
|
||||||
|
category text not null,
|
||||||
|
published timestamp without time zone default now() not null,
|
||||||
|
updated timestamp without time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
create table custom_emoji_keyword (
|
||||||
|
id serial primary key,
|
||||||
|
custom_emoji_id int references custom_emoji on update cascade on delete cascade not null,
|
||||||
|
keyword varchar(128) not null,
|
||||||
|
UNIQUE (custom_emoji_id, keyword)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index idx_custom_emoji_category on custom_emoji (id,category);
|
|
@ -31,6 +31,7 @@ use lemmy_api_common::{
|
||||||
TransferCommunity,
|
TransferCommunity,
|
||||||
},
|
},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
|
||||||
person::{
|
person::{
|
||||||
AddAdmin,
|
AddAdmin,
|
||||||
BanPerson,
|
BanPerson,
|
||||||
|
@ -352,6 +353,16 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
|
||||||
.route("/community", web::post().to(route_post::<PurgeCommunity>))
|
.route("/community", web::post().to(route_post::<PurgeCommunity>))
|
||||||
.route("/post", web::post().to(route_post::<PurgePost>))
|
.route("/post", web::post().to(route_post::<PurgePost>))
|
||||||
.route("/comment", web::post().to(route_post::<PurgeComment>)),
|
.route("/comment", web::post().to(route_post::<PurgeComment>)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::scope("/custom_emoji")
|
||||||
|
.wrap(rate_limit.message())
|
||||||
|
.route("", web::post().to(route_post_crud::<CreateCustomEmoji>))
|
||||||
|
.route("", web::put().to(route_post_crud::<EditCustomEmoji>))
|
||||||
|
.route(
|
||||||
|
"/delete",
|
||||||
|
web::post().to(route_post_crud::<DeleteCustomEmoji>),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ use lemmy_api_common::{
|
||||||
TransferCommunity,
|
TransferCommunity,
|
||||||
},
|
},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
custom_emoji::{CreateCustomEmoji, DeleteCustomEmoji, EditCustomEmoji},
|
||||||
person::{
|
person::{
|
||||||
AddAdmin,
|
AddAdmin,
|
||||||
BanPerson,
|
BanPerson,
|
||||||
|
@ -389,6 +390,16 @@ pub async fn match_websocket_operation_crud(
|
||||||
UserOperationCrud::GetComment => {
|
UserOperationCrud::GetComment => {
|
||||||
do_websocket_operation_crud::<GetComment>(context, id, op, data).await
|
do_websocket_operation_crud::<GetComment>(context, id, op, data).await
|
||||||
}
|
}
|
||||||
|
// Emojis
|
||||||
|
UserOperationCrud::CreateCustomEmoji => {
|
||||||
|
do_websocket_operation_crud::<CreateCustomEmoji>(context, id, op, data).await
|
||||||
|
}
|
||||||
|
UserOperationCrud::EditCustomEmoji => {
|
||||||
|
do_websocket_operation_crud::<EditCustomEmoji>(context, id, op, data).await
|
||||||
|
}
|
||||||
|
UserOperationCrud::DeleteCustomEmoji => {
|
||||||
|
do_websocket_operation_crud::<DeleteCustomEmoji>(context, id, op, data).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue