2025-05-17 19:57:09 -04:00
use super ::* ;
use crate ::cache ::Cache ;
use crate ::model ::moderation ::AuditLogEntry ;
use crate ::model ::{ Error , Result , auth ::User , communities ::PostDraft , permissions ::FinePermission } ;
use crate ::{ auto_method , execute , get , query_row , query_rows , params } ;
#[ cfg(feature = " sqlite " ) ]
use rusqlite ::Row ;
#[ cfg(feature = " postgres " ) ]
use tokio_postgres ::Row ;
impl DataManager {
/// Get a [`PostDraft`] from an SQL row.
pub ( crate ) fn get_draft_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> PostDraft {
PostDraft {
id : get ! ( x ->0 ( i64 ) ) as usize ,
created : get ! ( x ->1 ( i64 ) ) as usize ,
content : get ! ( x ->2 ( String ) ) ,
owner : get ! ( x ->3 ( i64 ) ) as usize ,
}
}
auto_method! ( get_draft_by_id ( ) @ get_draft_from_row -> " SELECT * FROM drafts WHERE id = $1 " - - name = " draft " - - returns = PostDraft - - cache - key - tmpl = " atto.draft:{} " ) ;
/// Get all drafts from the given user (from most recent, paginated).
///
/// # Arguments
/// * `id` - the ID of the user the requested drafts belong to
/// * `batch` - the limit of posts in each page
/// * `page` - the page number
pub async fn get_drafts_by_user (
& self ,
id : usize ,
batch : usize ,
page : usize ,
) -> Result < Vec < PostDraft > > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
// ...
let res = query_rows! (
& conn ,
" SELECT * FROM drafts WHERE owner = $1 ORDER BY created DESC LIMIT $2 OFFSET $3 " ,
& [ & ( id as i64 ) , & ( batch as i64 ) , & ( ( page * batch ) as i64 ) ] ,
| x | { Self ::get_draft_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " draft " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
/// Get all drafts from the given user (from most recent).
///
/// # Arguments
/// * `id` - the ID of the user the requested drafts belong to
pub async fn get_drafts_by_user_all ( & self , id : usize ) -> Result < Vec < PostDraft > > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
// ...
let res = query_rows! (
& conn ,
" SELECT * FROM drafts WHERE owner = $1 ORDER BY created DESC " ,
& [ & ( id as i64 ) ] ,
| x | { Self ::get_draft_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " draft " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-05-17 20:02:55 -04:00
const MAXIMUM_FREE_DRAFTS : usize = 10 ;
2025-05-17 19:57:09 -04:00
/// Create a new post draft in the database.
///
/// # Arguments
/// * `data` - a mock [`PostDraft`] object to insert
pub async fn create_draft ( & self , data : PostDraft ) -> Result < usize > {
// check values
if data . content . len ( ) < 2 {
return Err ( Error ::DataTooShort ( " content " . to_string ( ) ) ) ;
} else if data . content . len ( ) > 4096 {
return Err ( Error ::DataTooLong ( " content " . to_string ( ) ) ) ;
}
2025-05-17 20:02:55 -04:00
// check number of stacks
let owner = self . get_user_by_id ( data . owner ) . await ? ;
if ! owner . permissions . check ( FinePermission ::SUPPORTER ) {
let stacks = self . get_stacks_by_owner ( data . owner ) . await ? ;
if stacks . len ( ) > = Self ::MAXIMUM_FREE_DRAFTS {
return Err ( Error ::MiscError (
" You already have the maximum number of drafts you can have " . to_string ( ) ,
) ) ;
}
}
2025-05-17 19:57:09 -04:00
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" INSERT INTO drafts VALUES ($1, $2, $3, $4) " ,
params! [
& ( data . id as i64 ) ,
& ( data . created as i64 ) ,
& data . content ,
& ( data . owner as i64 ) ,
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( data . id )
}
pub async fn delete_draft ( & self , id : usize , user : User ) -> Result < ( ) > {
let y = self . get_draft_by_id ( id ) . await ? ;
if user . id ! = y . owner {
if ! user . permissions . check ( FinePermission ::MANAGE_POSTS ) {
return Err ( Error ::NotAllowed ) ;
} else {
self . create_audit_log_entry ( AuditLogEntry ::new (
user . id ,
format! ( " invoked `delete_draft` with x value ` {id} ` " ) ,
) )
. await ?
}
}
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM drafts WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
self . 2. remove ( format! ( " atto.draft: {} " , id ) ) . await ;
Ok ( ( ) )
}
pub async fn update_draft_content ( & self , id : usize , user : User , x : String ) -> Result < ( ) > {
let y = self . get_draft_by_id ( id ) . await ? ;
if user . id ! = y . owner {
if ! user . permissions . check ( FinePermission ::MANAGE_POSTS ) {
return Err ( Error ::NotAllowed ) ;
} else {
self . create_audit_log_entry ( AuditLogEntry ::new (
user . id ,
format! ( " invoked `update_draft_content` with x value ` {id} ` " ) ,
) )
. await ?
}
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" UPDATE drafts SET content = $1 WHERE id = $2 " ,
params! [ & x , & ( id as i64 ) ]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( ( ) )
}
}