//! # Groups helper //! //! @author Pierre Hubert use crate::constants::database_tables_names::{GROUPS_LIST_TABLE, GROUPS_MEMBERS_TABLE}; use crate::data::error::{ExecError, ResultBoxError}; use crate::data::group::{Group, GroupAccessLevel, GroupPostsCreationLevel, GroupRegistrationLevel, GroupVisibilityLevel}; use crate::data::group_id::GroupID; use crate::data::group_member::{GroupMember, GroupMembershipLevel}; use crate::data::new_group::NewGroup; use crate::data::user::UserID; use crate::helpers::database; use crate::utils::date_utils::time; impl GroupVisibilityLevel { pub fn to_db(&self) -> u64 { match self { GroupVisibilityLevel::OPEN_GROUP => 0, GroupVisibilityLevel::PRIVATE_GROUP => 1, GroupVisibilityLevel::SECRETE_GROUP => 2, } } pub fn from_db(level: u32) -> GroupVisibilityLevel { match level { 0 => GroupVisibilityLevel::OPEN_GROUP, 1 => GroupVisibilityLevel::PRIVATE_GROUP, 2 => GroupVisibilityLevel::SECRETE_GROUP, _ => GroupVisibilityLevel::SECRETE_GROUP } } } impl GroupMembershipLevel { pub fn to_db(&self) -> u32 { match self { GroupMembershipLevel::ADMINISTRATOR => 0, GroupMembershipLevel::MODERATOR => 1, GroupMembershipLevel::MEMBER => 2, GroupMembershipLevel::INVITED => 3, GroupMembershipLevel::PENDING => 4, GroupMembershipLevel::VISITOR => 5, } } pub fn from_db(level: u32) -> GroupMembershipLevel { match level { 0 => GroupMembershipLevel::ADMINISTRATOR, 1 => GroupMembershipLevel::MODERATOR, 2 => GroupMembershipLevel::MEMBER, 3 => GroupMembershipLevel::INVITED, 4 => GroupMembershipLevel::PENDING, 5 => GroupMembershipLevel::VISITOR, _ => GroupMembershipLevel::VISITOR } } } impl GroupRegistrationLevel { pub fn to_db(&self) -> u32 { match self { GroupRegistrationLevel::OPEN_REGISTRATION => 0, GroupRegistrationLevel::MODERATED_REGISTRATION => 1, GroupRegistrationLevel::CLOSED_REGISTRATION => 2, } } pub fn from_db(level: u32) -> GroupRegistrationLevel { match level { 0 => GroupRegistrationLevel::OPEN_REGISTRATION, 1 => GroupRegistrationLevel::MODERATED_REGISTRATION, 2 => GroupRegistrationLevel::CLOSED_REGISTRATION, _ => GroupRegistrationLevel::CLOSED_REGISTRATION, } } } impl GroupPostsCreationLevel { pub fn to_db(&self) -> u32 { match self { GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS => 0, GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS => 1, } } pub fn from_db(level: u32) -> GroupPostsCreationLevel { match level { 0 => GroupPostsCreationLevel::POSTS_LEVEL_MODERATORS, 1 => GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS, _ => GroupPostsCreationLevel::POSTS_LEVEL_ALL_MEMBERS, } } } /// Create a new group. Returns the ID of the new group pub fn create(group: &NewGroup) -> ResultBoxError { // First, create the group let group_id = database::InsertQuery::new(GROUPS_LIST_TABLE) .add_u64("time_create", time()) .add_user_id("userid_create", &group.owner_id) .add_str("name", &group.name) .insert()?.ok_or(ExecError::new("Could not get group ID!"))?; let group_id = GroupID::new(group_id); // Insert first member insert_member(&GroupMember { id: 0, user_id: group.owner_id.clone(), group_id: group_id.clone(), time_create: time(), level: GroupMembershipLevel::ADMINISTRATOR, following: true, })?; Ok(group_id) } /// Insert a new group into the database pub fn insert_member(m: &GroupMember) -> ResultBoxError<()> { database::InsertQuery::new(GROUPS_MEMBERS_TABLE) .add_group_id("groups_id", &m.group_id) .add_user_id("user_id", &m.user_id) .add_u64("time_create", m.time_create) .add_u32("level", m.level.to_db()) .insert_drop_result() } /// Get the list of groups of a user pub fn get_list_user(user_id: UserID, only_followed: bool) -> ResultBoxError> { let mut query = database::QueryInfo::new(GROUPS_MEMBERS_TABLE) .add_field("groups_id") .cond_user_id("user_id", &user_id); if only_followed { query = query.cond_legacy_bool("following", true); } query.exec(|row| row.get_group_id("groups_id")) } /// Get information about a group pub fn get_info(group_id: &GroupID) -> ResultBoxError { database::QueryInfo::new(GROUPS_LIST_TABLE) .cond_group_id("id", group_id) .query_row(db_to_group) } /// Check out whether a group exists or not pub fn exists(group_id: &GroupID) -> ResultBoxError { database::QueryInfo::new(GROUPS_LIST_TABLE) .cond_group_id("id", group_id) .exec_count() .map(|m| m > 0) } /// Find a group id by virtual directory pub fn find_by_virtual_directory(dir: &str) -> ResultBoxError { database::QueryInfo::new(GROUPS_LIST_TABLE) .cond("virtual_directory", dir) .add_field("id") .query_row(|res| res.get_group_id("id")) } /// Search for group pub fn search_group(query: &str, limit: u64) -> ResultBoxError> { database::QueryInfo::new(GROUPS_LIST_TABLE) .set_custom_where("name LIKE ? AND visibility != ?") .add_custom_where_argument_str(format!("%{}%", query).as_str()) .add_custom_where_argument_u64(GroupVisibilityLevel::SECRETE_GROUP.to_db()) .set_limit(limit) .add_field("id") .exec(|row| row.get_group_id("id")) } /// Get information about the membership of a user over a group pub fn get_membership(group_id: &GroupID, user_id: Option) -> ResultBoxError { let default_membership = GroupMember { id: 0, user_id: UserID::new(0), group_id: group_id.clone(), time_create: 0, level: GroupMembershipLevel::VISITOR, following: false, }; if user_id == None { return Ok(default_membership); } let user_id = user_id.unwrap(); Ok(database::QueryInfo::new(GROUPS_MEMBERS_TABLE) .cond_group_id("groups_id", group_id) .cond_user_id("user_id", &user_id) .query_row(db_to_group_member) .unwrap_or(default_membership)) } /// Get the membership level of a user for a group pub fn get_membership_level(group_id: &GroupID, user_id: Option) -> ResultBoxError { match user_id { None => Ok(GroupMembershipLevel::VISITOR), Some(user_id) => { let level = database::QueryInfo::new(GROUPS_MEMBERS_TABLE) .cond_group_id("groups_id", group_id) .cond_user_id("user_id", &user_id) .add_field("level") .query_row(|f| f.get_u32("level")) .unwrap_or(GroupMembershipLevel::VISITOR.to_db()); Ok(GroupMembershipLevel::from_db(level)) } } } /// Get the visibility level of a group pub fn get_visibility(group_id: &GroupID) -> ResultBoxError { let result = database::QueryInfo::new(GROUPS_LIST_TABLE) .cond_group_id("id", group_id) .add_field("visibility") .query_row(|f| f.get_u32("visibility"))?; Ok(GroupVisibilityLevel::from_db(result)) } /// Get the current access level of a user over a group pub fn get_access_level(group_id: &GroupID, user_id: Option) -> ResultBoxError { let membership_level = get_membership_level(group_id, user_id)?; // Check if the user is a confirmed member of group if membership_level == GroupMembershipLevel::ADMINISTRATOR { return Ok(GroupAccessLevel::ADMIN_ACCESS); } if membership_level == GroupMembershipLevel::MODERATOR { return Ok(GroupAccessLevel::MODERATOR_ACCESS); } if membership_level == GroupMembershipLevel::MEMBER { return Ok(GroupAccessLevel::MEMBER_ACCESS); } let visibility_level = get_visibility(group_id)?; //If the group is open, everyone has view access if visibility_level == GroupVisibilityLevel::OPEN_GROUP { return Ok(GroupAccessLevel::VIEW_ACCESS); } //Else, all pending and invited membership get limited access if membership_level == GroupMembershipLevel::PENDING || membership_level == GroupMembershipLevel::INVITED { return Ok(GroupAccessLevel::LIMITED_ACCESS); } //Private groups gives limited access if visibility_level == GroupVisibilityLevel::PRIVATE_GROUP { return Ok(GroupAccessLevel::LIMITED_ACCESS); } // Else the user can not see the group // Especially in the case of secrete group Ok(GroupAccessLevel::NO_ACCESS) } /// Count the number of members of a group pub fn count_members(group_id: &GroupID) -> ResultBoxError { database::QueryInfo::new(GROUPS_MEMBERS_TABLE) .cond_group_id("groups_id", group_id) .set_custom_where("level <= ?") .add_custom_where_argument_u32(GroupMembershipLevel::MEMBER.to_db()) .exec_count() } /// Check the availability of a virtual directory for a group pub fn check_directory_availability(dir: &str, group_id: Option) -> ResultBoxError { let group = find_by_virtual_directory(dir); match (group, group_id) { (Ok(_), None) => Ok(false), (Ok(g), Some(g2)) => Ok(g == g2), (Err(_), _) => Ok(true) } } /// Set new settings to the group, except group logo pub fn set_settings(g: &Group) -> ResultBoxError { database::UpdateInfo::new(GROUPS_LIST_TABLE) .cond_group_id("id", &g.id) // Updates .set_str("name", &g.name) .set_u64("visibility", g.visibility.to_db()) .set_u32("registration_level", g.registration_level.to_db()) .set_u32("posts_level", g.posts_creation_level.to_db()) .set_opt_str("virtual_directory", g.virtual_directory.clone()) .set_opt_str("description", g.description.clone()) .set_opt_str("url", g.url.clone()) .exec() } /// Set a new path for a logo pub fn set_logo_path(g: &GroupID, path: Option) -> ResultBoxError { database::UpdateInfo::new(GROUPS_LIST_TABLE) .cond_group_id("id", g) .set_opt_str("path_logo", path) .exec() } /// Delete the logo of a group pub fn delete_logo(g: &GroupID) -> ResultBoxError { let group = get_info(g)?; if !group.has_logo() { return Ok(()); } let logo_path = group.get_logo_sys_path(); if logo_path.exists() { std::fs::remove_file(logo_path)?; } set_logo_path(g, None) } /// Turn a database entry into a group struct fn db_to_group(row: &database::RowResult) -> ResultBoxError { let group_id = row.get_group_id("id")?; Ok(Group { id: group_id.clone(), name: row.get_str("name")?, visibility: GroupVisibilityLevel::from_db(row.get_u32("visibility")?), registration_level: GroupRegistrationLevel::from_db(row.get_u32("registration_level")?), posts_creation_level: GroupPostsCreationLevel::from_db(row.get_u32("posts_level")?), logo: row.get_optional_str("path_logo")?, virtual_directory: row.get_optional_str("virtual_directory")?, time_create: row.get_u64("time_create")?, description: row.get_optional_str("description")?, url: row.get_optional_str("url")?, }) } /// Turn a database entry into a group member struct fn db_to_group_member(row: &database::RowResult) -> ResultBoxError { Ok(GroupMember { id: row.get_u64("id")?, user_id: row.get_user_id("user_id")?, group_id: row.get_group_id("groups_id")?, time_create: row.get_u64("time_create")?, level: GroupMembershipLevel::from_db(row.get_u32("level")?), following: row.get_legacy_bool("following")?, }) }