feat: add parent and update permission calculators
This commit is contained in:
@@ -100,6 +100,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
nsfw: bool,
|
nsfw: bool,
|
||||||
@@ -131,6 +135,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[serde(skip_serializing_if = "crate::if_false", default)]
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
nsfw: bool,
|
nsfw: bool,
|
||||||
@@ -161,6 +169,8 @@ auto_derived!(
|
|||||||
pub default_permissions: Option<OverrideField>,
|
pub default_permissions: Option<OverrideField>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub last_message_id: Option<String>,
|
pub last_message_id: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parent: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on channel object
|
/// Optional fields on channel object
|
||||||
@@ -219,6 +229,7 @@ impl Channel {
|
|||||||
default_permissions: None,
|
default_permissions: None,
|
||||||
role_permissions: HashMap::new(),
|
role_permissions: HashMap::new(),
|
||||||
nsfw: data.nsfw.unwrap_or(false),
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
parent: None
|
||||||
},
|
},
|
||||||
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
v0::LegacyServerChannelType::Voice => Channel::VoiceChannel {
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
@@ -229,6 +240,7 @@ impl Channel {
|
|||||||
default_permissions: None,
|
default_permissions: None,
|
||||||
role_permissions: HashMap::new(),
|
role_permissions: HashMap::new(),
|
||||||
nsfw: data.nsfw.unwrap_or(false),
|
nsfw: data.nsfw.unwrap_or(false),
|
||||||
|
parent: None
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -437,6 +449,13 @@ impl Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Channel::TextChannel { parent, .. } | Channel::VoiceChannel { parent, .. } => parent.as_deref(),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set role permission on a channel
|
/// Set role permission on a channel
|
||||||
pub async fn set_role_permission(
|
pub async fn set_role_permission(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => Channel::TextChannel {
|
} => Channel::TextChannel {
|
||||||
id,
|
id,
|
||||||
@@ -197,6 +198,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
crate::Channel::VoiceChannel {
|
crate::Channel::VoiceChannel {
|
||||||
@@ -207,6 +209,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
icon,
|
icon,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => Channel::VoiceChannel {
|
} => Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
@@ -216,6 +219,7 @@ impl From<crate::Channel> for Channel {
|
|||||||
icon: icon.map(|file| file.into()),
|
icon: icon.map(|file| file.into()),
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -267,6 +271,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => crate::Channel::TextChannel {
|
} => crate::Channel::TextChannel {
|
||||||
id,
|
id,
|
||||||
@@ -277,6 +282,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
last_message_id,
|
last_message_id,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
Channel::VoiceChannel {
|
Channel::VoiceChannel {
|
||||||
@@ -287,6 +293,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
icon,
|
icon,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
} => crate::Channel::VoiceChannel {
|
} => crate::Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
@@ -296,6 +303,7 @@ impl From<Channel> for crate::Channel {
|
|||||||
icon: icon.map(|file| file.into()),
|
icon: icon.map(|file| file.into()),
|
||||||
default_permissions,
|
default_permissions,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
|
parent,
|
||||||
nsfw,
|
nsfw,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -315,6 +323,7 @@ impl From<crate::PartialChannel> for PartialChannel {
|
|||||||
role_permissions: value.role_permissions,
|
role_permissions: value.role_permissions,
|
||||||
default_permissions: value.default_permissions,
|
default_permissions: value.default_permissions,
|
||||||
last_message_id: value.last_message_id,
|
last_message_id: value.last_message_id,
|
||||||
|
parent: value.parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,6 +341,7 @@ impl From<PartialChannel> for crate::PartialChannel {
|
|||||||
role_permissions: value.role_permissions,
|
role_permissions: value.role_permissions,
|
||||||
default_permissions: value.default_permissions,
|
default_permissions: value.default_permissions,
|
||||||
last_message_id: value.last_message_id,
|
last_message_id: value.last_message_id,
|
||||||
|
parent: value.parent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use revolt_permissions::{
|
|||||||
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
DEFAULT_PERMISSION_DIRECT_MESSAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Channel, Database, Member, Server, User};
|
use crate::{Category, Channel, Database, Member, Server, User};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BulkDatabasePermissionQuery<'a> {
|
pub struct BulkDatabasePermissionQuery<'a> {
|
||||||
@@ -197,7 +197,7 @@ async fn calculate_members_permissions<'a>(
|
|||||||
) -> HashMap<String, PermissionValue> {
|
) -> HashMap<String, PermissionValue> {
|
||||||
let mut resp = HashMap::new();
|
let mut resp = HashMap::new();
|
||||||
|
|
||||||
let (_, channel_role_permissions, channel_default_permissions) = match query
|
let (_, channel_role_permissions, channel_default_permissions, parent) = match query
|
||||||
.channel
|
.channel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("A channel must be assigned to calculate channel permissions")
|
.expect("A channel must be assigned to calculate channel permissions")
|
||||||
@@ -207,14 +207,16 @@ async fn calculate_members_permissions<'a>(
|
|||||||
id,
|
id,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
|
parent,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| Channel::VoiceChannel {
|
| Channel::VoiceChannel {
|
||||||
id,
|
id,
|
||||||
role_permissions,
|
role_permissions,
|
||||||
default_permissions,
|
default_permissions,
|
||||||
|
parent,
|
||||||
..
|
..
|
||||||
} => (id, role_permissions, default_permissions),
|
} => (id, role_permissions, default_permissions, parent),
|
||||||
_ => panic!("Calculation of member permissions must be done on a server channel"),
|
_ => panic!("Calculation of member permissions must be done on a server channel"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -268,6 +270,8 @@ async fn calculate_members_permissions<'a>(
|
|||||||
.map(|m| (&m.id.user, m)),
|
.map(|m| (&m.id.user, m)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let category = parent.map(|parent| query.server.categories.get(&parent).expect("Channel parent doesnt exist"));
|
||||||
|
|
||||||
for user in users {
|
for user in users {
|
||||||
let member = members.get(&user.id);
|
let member = members.get(&user.id);
|
||||||
|
|
||||||
@@ -298,11 +302,39 @@ async fn calculate_members_permissions<'a>(
|
|||||||
// Get the user's server permissions
|
// Get the user's server permissions
|
||||||
let mut permission = calculate_server_permissions(&query.server, user, member);
|
let mut permission = calculate_server_permissions(&query.server, user, member);
|
||||||
|
|
||||||
|
// Apply category overrides
|
||||||
|
if let Some(category) = category {
|
||||||
|
if let Some(defaults) = category.default_permissions {
|
||||||
|
permission.apply(defaults.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the applicable role overrides for the category
|
||||||
|
let mut roles = category.role_permissions
|
||||||
|
.iter()
|
||||||
|
.filter(|(id, _)| member.roles.contains(id))
|
||||||
|
.filter_map(|(id, permission)| {
|
||||||
|
query.server.roles.get(id).map(|role| {
|
||||||
|
let v: Override = (*permission).into();
|
||||||
|
(role.rank, v)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<(i64, Override)>>();
|
||||||
|
|
||||||
|
roles.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
let overrides = roles.into_iter().map(|(_, v)| v);
|
||||||
|
|
||||||
|
for role_override in overrides {
|
||||||
|
permission.apply(role_override)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply channel overrides
|
||||||
|
|
||||||
if let Some(defaults) = channel_default_permissions {
|
if let Some(defaults) = channel_default_permissions {
|
||||||
permission.apply(defaults.into());
|
permission.apply(defaults.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the applicable role overrides
|
// Get the applicable role overrides for the channel
|
||||||
let mut roles = channel_role_permissions
|
let mut roles = channel_role_permissions
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(id, _)| member.roles.contains(id))
|
.filter(|(id, _)| member.roles.contains(id))
|
||||||
|
|||||||
@@ -413,7 +413,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
|
|
||||||
async fn set_category_from_channel(&mut self) {
|
async fn set_category_from_channel(&mut self) {
|
||||||
if let Some((server, channel)) = self.server.as_ref().zip(self.channel.as_ref()) {
|
if let Some((server, channel)) = self.server.as_ref().zip(self.channel.as_ref()) {
|
||||||
let category = server.categories.values().find(|c| c.channels.contains(&channel.id().to_string())).cloned();
|
let category = channel.parent().and_then(|id| server.categories.get(id)).cloned();
|
||||||
|
|
||||||
self.category = category.map(Cow::Owned);
|
self.category = category.map(Cow::Owned);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
@@ -138,6 +142,10 @@ auto_derived!(
|
|||||||
)]
|
)]
|
||||||
role_permissions: HashMap<String, OverrideField>,
|
role_permissions: HashMap<String, OverrideField>,
|
||||||
|
|
||||||
|
/// Category the channel is in
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
parent: Option<String>,
|
||||||
|
|
||||||
/// Whether this channel is marked as not safe for work
|
/// Whether this channel is marked as not safe for work
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
@@ -170,6 +178,8 @@ auto_derived!(
|
|||||||
pub default_permissions: Option<OverrideField>,
|
pub default_permissions: Option<OverrideField>,
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
pub last_message_id: Option<String>,
|
pub last_message_id: Option<String>,
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
|
||||||
|
pub parent: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on channel object
|
/// Optional fields on channel object
|
||||||
|
|||||||
@@ -192,6 +192,7 @@ mod test {
|
|||||||
d: ChannelPermission::ViewChannel as i64,
|
d: ChannelPermission::ViewChannel as i64,
|
||||||
}),
|
}),
|
||||||
last_message_id: None,
|
last_message_id: None,
|
||||||
|
parent: None,
|
||||||
};
|
};
|
||||||
locked_channel
|
locked_channel
|
||||||
.update(&harness.db, partial, vec![])
|
.update(&harness.db, partial, vec![])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_config::config;
|
use revolt_config::config;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference}, Category, Database, PartialCategory, Role, User
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, Category, Channel, Database, PartialCategory, PartialChannel, Role, User
|
||||||
};
|
};
|
||||||
use revolt_models::v0::{self, DataEditCategory};
|
use revolt_models::v0::{self, DataEditCategory};
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
@@ -41,16 +41,34 @@ pub async fn edit(
|
|||||||
|
|
||||||
let DataEditCategory {
|
let DataEditCategory {
|
||||||
title,
|
title,
|
||||||
channels,
|
mut channels,
|
||||||
remove
|
remove
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
|
// remove the channels from any existing categories to avoid it having two
|
||||||
|
for category in server.categories.values_mut() {
|
||||||
|
category.channels.retain(|c| channels.as_ref().map_or(false, |cs| cs.contains(c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// only keep channels which exist in the server
|
||||||
|
if let Some(channels) = &mut channels {
|
||||||
|
channels.retain(|c| server.channels.contains(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
// unset parent from all channels which are removed from the category
|
||||||
|
for channel_id in &category.channels {
|
||||||
|
if channels.as_ref().is_some_and(|cs| !cs.contains(channel_id)) {
|
||||||
|
db.update_channel(channel_id, &PartialChannel { parent: None, ..Default::default() }, Vec::new()).await?;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// update the category with the new values
|
||||||
category.update(
|
category.update(
|
||||||
db,
|
db,
|
||||||
&mut server,
|
&mut server,
|
||||||
PartialCategory {
|
PartialCategory {
|
||||||
title,
|
title,
|
||||||
channels,
|
channels: channels.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
remove
|
remove
|
||||||
@@ -58,5 +76,16 @@ pub async fn edit(
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
).await?;
|
).await?;
|
||||||
|
|
||||||
|
let channels = db.fetch_channels(&channels.unwrap_or_default()).await?;
|
||||||
|
|
||||||
|
// update all channels to have the parent set
|
||||||
|
for channel in channels {
|
||||||
|
if let Channel::TextChannel { ref parent, .. } | Channel::VoiceChannel { ref parent, .. } = channel {
|
||||||
|
if parent.as_ref() != Some(&category.id) {
|
||||||
|
db.update_channel(channel.id(), &PartialChannel { parent: Some(category.id.clone()), ..Default::default() }, Vec::new()).await?;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Json(category.into()))
|
Ok(Json(category.into()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user