2018-09-25 22:55:14 +03:00
# include "storage.h"
# include <QVariant>
# include <assert.h>
# include <encryption.h>
2019-06-23 18:46:41 +03:00
# include <iostream>
2018-09-25 22:55:14 +03:00
# ifdef TARGET_WIN
# include <WinSock2.h>
# endif
enum class DbProperty
{
2018-12-21 23:11:26 +02:00
Version = 1 ,
InternalCipher = 2
2018-09-25 22:55:14 +03:00
} ;
# define CURRENT_DBVERSION "0"
// Function to make encryption. Input is raw content blob, output is blob with IV, original length and encrypted data.
2018-12-21 23:11:26 +02:00
/*static void EncryptBlob(const QByteArray& input, QByteArray& output)
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
TwofishCipher cipher ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Generate 16 bytes IV
QByteArray iv ( 16 , 0 ) ;
IV : : Generate ( iv ) ;
cipher . setIV ( iv ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Make padding
int padded = ( input . length ( ) % cipher . blocksize ( ) ) ? ( cipher . blocksize ( ) - input . length ( ) % cipher . blocksize ( ) ) : 0 ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Prepare output buffer
output . resize ( iv . length ( ) + 4 + input . length ( ) + padded ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Copy IV
memcpy ( output . data ( ) , iv . data ( ) , iv . length ( ) ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Copy original length in network byte order
uint32_t lengthNbo = htonl ( input . length ( ) ) ;
memcpy ( output . data ( ) + iv . length ( ) , & lengthNbo , 4 ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Encrypt
cipher . encrypt ( input , 0 , output , iv . length ( ) + 4 ) ;
} */
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
/*static void DecryptBlob(const QByteArray& input, QByteArray& output)
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
TwofishCipher cipher ;
assert ( input . length ( ) > = cipher . blocksize ( ) + 4 ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Read IV
QByteArray iv ( input . data ( ) , cipher . blocksize ( ) ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Read original length
uint32_t lengthNbo ;
memcpy ( & lengthNbo , input . data ( ) + iv . length ( ) , 4 ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Prepare output buffer
output . resize ( input . length ( ) - iv . length ( ) - 4 ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Set IV
cipher . setIV ( iv ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Decrypt data
cipher . decrypt ( input , iv . length ( ) + 4 , output , 0 ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Truncate to original size
output . truncate ( ntohl ( lengthNbo ) ) ;
} */
2018-09-25 22:55:14 +03:00
Storage : : Storage ( )
{
}
Storage : : ~ Storage ( )
{
2018-12-21 23:11:26 +02:00
close ( ) ;
2018-09-25 22:55:14 +03:00
}
QString Storage : : path ( )
{
2018-12-21 23:11:26 +02:00
return mPath ;
2018-09-25 22:55:14 +03:00
}
void Storage : : setPath ( const QString & path )
{
2018-12-21 23:11:26 +02:00
mPath = path ;
2018-09-25 22:55:14 +03:00
}
QString Storage : : key ( )
{
2018-12-21 23:11:26 +02:00
return mKey ;
2018-09-25 22:55:14 +03:00
}
void Storage : : setKey ( const QString & key )
{
2018-12-21 23:11:26 +02:00
mKey = key ;
2018-09-25 22:55:14 +03:00
}
SQLite : : Database & Storage : : database ( )
{
2018-12-21 23:11:26 +02:00
return * mDatabase ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : create ( )
{
2018-12-21 23:11:26 +02:00
assert ( ! mPath . isEmpty ( ) ) ;
try
{
mDatabase = QSharedPointer < SQLite : : Database > ( new SQLite : : Database ( mPath . toStdString ( ) . c_str ( ) , SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ) ) ;
}
catch ( std : : exception & e )
{
return false ;
}
2018-09-25 22:55:14 +03:00
# ifdef USE_ENCRYPTED_DB
2018-12-21 23:11:26 +02:00
try
{
std : : string keyQuery = " pragma key=' " + mKey . toStdString ( ) + " ' " ;
mDatabase - > exec ( keyQuery . c_str ( ) ) ;
mDatabase - > exec ( " pragma locking_mode=EXCLUSIVE " ) ;
mDatabase - > exec ( " pragma journal_mode=MEMORY " ) ;
mDatabase - > exec ( " pragma temp_store=MEMORY " ) ;
}
catch ( std : : exception & e )
{
2019-06-23 18:46:41 +03:00
std : : cout < < e . what ( ) < < std : : endl ;
2018-12-21 23:11:26 +02:00
return false ;
}
2018-09-25 22:55:14 +03:00
# endif
2018-12-21 23:11:26 +02:00
try
{
2019-06-23 18:46:41 +03:00
// Synctime & timestamp are always milliseconds from the start of UNIX epoch.
2018-12-21 23:11:26 +02:00
// Timeline
2019-06-23 18:46:41 +03:00
mDatabase - > exec ( " CREATE TABLE timeline (id INTEGER PRIMARY KEY, removed INTEGER, "
" taskid INTEGER, starttime TEXT, endtime TEXT, timestamp INTEGER) " ) ;
2018-12-21 23:11:26 +02:00
mDatabase - > exec ( " CREATE INDEX timeline_taskid_index ON timeline(taskid ASC) " ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Tasks tree
2019-06-23 18:46:41 +03:00
mDatabase - > exec ( " CREATE TABLE task (type INTEGER, removed INTEGER, "
" id INTEGER PRIMARY KEY, parentid INTEGER, orderid INTEGER, title TEXT, html TEXT, "
" flags INTEGER, timestamp INTEGER) " ) ;
2018-12-21 23:11:26 +02:00
mDatabase - > exec ( " CREATE INDEX task_parentid_index ON task(parentid ASC) " ) ;
2018-09-25 22:55:14 +03:00
2019-06-23 18:46:41 +03:00
// Tasks history
mDatabase - > exec ( " CREATE TABLE history_task (removed_old INTEGER, removed_new INTEGER, "
" timestamp_old INTEGER, timestamp_new INTEGER, "
" id INTEGER PRIMARY KEY, "
" parent_id_old INTEGER, parent_id_new INTEGER, "
" order_id_old INTEGER, order_id_new INTEGER, "
" diff_title TEXT, diff_html TEXT, "
" flags_old INTEGER, flags_new INTEGER) " ) ;
2018-12-21 23:11:26 +02:00
// Attachments
2019-06-23 18:46:41 +03:00
mDatabase - > exec ( " CREATE TABLE file (id INTEGER PRIMARY KEY, removed INTEGER, taskid INTEGER, "
" filename TEXT, content BLOB, orderid INTEGER, timestamp INTEGER) " ) ;
mDatabase - > exec ( " CREATE TABLE history_file (id INTEGER PRIMARY KEY, "
" removed_old INTEGER, removed_new INTEGER, "
" taskid_old INTEGER, taskid_new INTEGER, "
" filename_old TEXT, filename_new TEXT, "
" content BLOB, "
" order_id_old INTEGER, order_id_new INTEGER, "
" timestamp_old INTEGER, timestamp_new INTEGER) " ) ;
2018-12-21 23:11:26 +02:00
mDatabase - > exec ( " CREATE INDEX file_taskid_index ON file(taskid ASC) " ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Sync status
2019-06-23 18:46:41 +03:00
mDatabase - > exec ( " CREATE TABLE syncs (timestamp INT, status TEXT) " ) ;
2018-12-21 23:11:26 +02:00
}
catch ( std : : exception & e )
{
2019-06-23 18:46:41 +03:00
std : : cerr < < e . what ( ) < < std : : endl ;
2018-12-21 23:11:26 +02:00
return false ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
return true ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : open ( )
{
2018-12-21 23:11:26 +02:00
assert ( ! mPath . isEmpty ( ) ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
try
{
mDatabase = QSharedPointer < SQLite : : Database > ( new SQLite : : Database ( mPath . toStdString ( ) . c_str ( ) , SQLITE_OPEN_READWRITE ) ) ;
mDatabase - > exec ( " pragma key=' " + mKey . toStdString ( ) + " ' " ) ;
SQLite : : Statement q ( * mDatabase , " select count(*) from sqlite_master " ) ;
if ( ! q . executeStep ( ) )
return false ;
mDatabase - > exec ( " pragma locking_mode=EXCLUSIVE " ) ;
mDatabase - > exec ( " pragma journal_mode=MEMORY " ) ;
mDatabase - > exec ( " pragma temp_store=MEMORY " ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
}
catch ( std : : exception & e )
{
return false ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
loadTaskTree ( ) ;
return true ;
2018-09-25 22:55:14 +03:00
}
void Storage : : close ( )
{
2018-12-21 23:11:26 +02:00
if ( mDatabase )
{
mDatabase . clear ( ) ;
}
2018-09-25 22:55:14 +03:00
}
bool Storage : : hasTable ( const QString & tablename )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement tableQuery ( * mDatabase , " SELECT name FROM sqlite_master WHERE type='table' AND name=:tablename " ) ;
tableQuery . bind ( " :tablename " , tablename . toStdString ( ) ) ;
return tableQuery . executeStep ( ) ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : upgrade ( )
{
2018-12-21 23:11:26 +02:00
int currentVersion = 0 ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Check if properties table is here
if ( ! mDatabase )
return false ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
if ( ! hasTable ( " properties " ) )
{
// If not - create it and consider database version 0
SQLite : : Statement createQuery ( * mDatabase , " create table properties(type INTEGER, value TEXT) " ) ;
if ( ! createQuery . exec ( ) )
return false ;
// Insert version 0
SQLite : : Statement insertVersionQuery ( * mDatabase , " insert into properties(type, value) values(:proptype, :proptext) " ) ;
insertVersionQuery . bind ( " :proptype " , ( int ) DbProperty : : Version ) ;
insertVersionQuery . bind ( " :proptext " , " 0 " ) ;
}
else
{
// If yes - get database version from 'dbversion' key
SQLite : : Statement versionQuery ( * mDatabase , " select value from properties where type=:proptype " ) ;
versionQuery . bind ( " :proptype " , ( int ) DbProperty : : Version ) ;
if ( ! versionQuery . executeStep ( ) )
return false ;
QString dbVersionText = versionQuery . getColumn ( 0 ) . getText ( " " ) ;
if ( dbVersionText . isEmpty ( ) )
return false ;
bool ok = false ;
currentVersion = dbVersionText . toInt ( & ok ) ;
if ( ! ok )
return false ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Depending on obtained version upgrade database
switch ( currentVersion )
{
case 0 :
if ( ! upgradeFromVersion0 ( ) )
return false ;
break ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Update version number after upgrade
SQLite : : Statement updateVersion ( * mDatabase , " update table properties set value=:proptext where type=:proptype " ) ;
updateVersion . bind ( " :proptext " , CURRENT_DBVERSION ) ;
updateVersion . bind ( " :proptype " , ( int ) DbProperty : : Version ) ;
updateVersion . exec ( ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
return true ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : upgradeFromVersion0 ( )
{
2018-12-21 23:11:26 +02:00
/*
2018-09-25 22:55:14 +03:00
// Upgrade tasks table
SQLite : : Statement addEncryptedTitle ( * mDatabase , " alter table task add column title_encrypted BLOB " ) ;
if ( ! addEncryptedTitle . exec ( ) )
return false ;
SQLite : : Statement addEncryptedContent ( * mDatabase , " alter table task add column content_encrypted BLOB " ) ;
if ( ! addEncryptedContent . exec ( ) )
return false ;
SQLite : : Statement addEncryptedAttachments ( * mDatabase , " alter table attachment add column content_encrypted BLOB " ) ;
if ( ! addEncryptedAttachments . exec ( ) )
return false ;
// Database scheme is upgraded here, convert the data
SQLite : : Statement tasks ( * mDatabase , " select id, title, html from task " ) ;
while ( tasks . executeStep ( ) )
{
// TODO
}
// Iterate all notes and attachments and make them Twofish encrypted
mTaskToUpgrade . resize ( topOfTaskTree ( ) . size ( ) ) ;
std : : copy ( topOfTaskTree ( ) . begin ( ) , topOfTaskTree ( ) . end ( ) , mTaskToUpgrade ) ;
for ( PTask task : mTaskToUpgrade )
{
encryptTaskContent ( task ) ;
//encryptTaskAttachment(task);
// Add children to list
int tl = mTaskToUpgrade . length ( ) ;
mTaskToUpgrade . resize ( mTaskToUpgrade . size ( ) + task - > children ( ) . size ( ) ) ;
std : : copy ( task - > children ( ) . begin ( ) , task - > children ( ) . end ( ) , mTaskToUpgrade . begin ( ) + tl ) ;
}
*/
2018-12-21 23:11:26 +02:00
return true ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : encryptTask ( PTask task )
{
2018-12-21 23:11:26 +02:00
return false ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : encryptTaskContent ( PTask task )
{
2018-12-21 23:11:26 +02:00
return false ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : encryptTaskAttachment ( PAttachment attachment )
{
2018-12-21 23:11:26 +02:00
return false ;
2018-09-25 22:55:14 +03:00
}
PTask Storage : : createTask ( int index )
{
2018-12-21 23:11:26 +02:00
if ( ! mDatabase )
return PTask ( ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
SQLite : : Statement insertQuery ( * mDatabase , " insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, NULL, :orderid, 0, 0) " ) ;
insertQuery . bind ( " :title " , " " ) ;
insertQuery . bind ( " :html " , " " ) ;
insertQuery . bind ( " :orderid " , index ) ;
if ( insertQuery . exec ( ) )
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
PTask result ( new Task ( ) ) ;
result - > setId ( database ( ) . getLastInsertRowid ( ) ) ;
result - > setIndex ( index ) ;
mTaskModelIdMap [ result - > modelId ( ) ] = result ;
mTaskIdMap [ result - > id ( ) ] = result ;
if ( index > mTopTasks . size ( ) )
mTopTasks . push_back ( result ) ;
else
{
mTopTasks . insert ( mTopTasks . begin ( ) + index , result ) ;
// Assign new indexes for top tasks
for ( int i = 0 ; i < mTopTasks . size ( ) ; i + + )
{
mTopTasks [ i ] - > setIndex ( i ) ;
mTopTasks [ i ] - > save ( ) ;
}
}
return result ;
2018-09-25 22:55:14 +03:00
}
2018-12-21 23:11:26 +02:00
else
return PTask ( ) ;
2018-09-25 22:55:14 +03:00
}
PTask Storage : : createTask ( PTask parent , int index )
{
2018-12-21 23:11:26 +02:00
if ( ! parent )
return createTask ( index ) ;
if ( ! mDatabase )
return PTask ( ) ;
SQLite : : Statement insertQuery ( database ( ) , " insert into task(id, title, html, parentid, orderid, type, flags) values(NULL, :title, :html, :parentid, :orderid, 0, 0) " ) ;
insertQuery . bind ( " :title " , " " ) ;
insertQuery . bind ( " :html " , " " ) ;
insertQuery . bind ( " :parentid " , ( sqlite3_int64 ) parent - > id ( ) ) ;
insertQuery . bind ( " :orderid " , index ) ;
if ( insertQuery . exec ( ) )
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
PTask result ( new Task ( ) ) ;
result - > setId ( database ( ) . getLastInsertRowid ( ) ) ;
result - > setIndex ( index ) ;
result - > setParent ( parent ) ;
if ( index > parent - > children ( ) . size ( ) )
parent - > children ( ) . push_back ( result ) ;
else
{
parent - > children ( ) . insert ( parent - > children ( ) . begin ( ) + index , result ) ;
for ( int i = 0 ; i < parent - > children ( ) . size ( ) ; i + + )
{
parent - > children ( ) [ i ] - > setIndex ( i ) ;
parent - > children ( ) [ i ] - > save ( ) ;
}
}
mTaskModelIdMap [ result - > modelId ( ) ] = result ;
mTaskIdMap [ result - > id ( ) ] = result ;
return result ;
2018-09-25 22:55:14 +03:00
}
2018-12-21 23:11:26 +02:00
return PTask ( ) ;
2018-09-25 22:55:14 +03:00
}
/*
PTask Storage : : loadTask ( Task : : Id id , PTask parent )
{
PTask result ( new Task ( ) ) ;
result - > setId ( id ) ;
result - > load ( ) ;
result - > setParent ( parent ) ;
mTaskIdMap [ result - > modelId ( ) ] = result ;
return result ;
}
*/
void Storage : : saveTask ( PTask task , Depth depth )
{
2018-12-21 23:11:26 +02:00
if ( depth = = depthSingleTask )
saveSingleTask ( task ) ;
else
{
saveSingleTask ( task ) ;
TaskArray & children = task - > children ( ) ;
foreach ( PTask child , children )
saveTask ( child , depthRecursive ) ;
}
2018-09-25 22:55:14 +03:00
}
bool Storage : : moveTask ( PTask task , PTask newParent , int indexToInsert )
{
2018-12-21 23:11:26 +02:00
bool result = false ;
task - > setParent ( newParent ) ;
if ( newParent )
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement updateParent ( database ( ) , " update task set parentid=:parentid where id=:id " ) ;
updateParent . bind ( " :parentid " , ( sqlite3_int64 ) newParent - > id ( ) ) ;
updateParent . bind ( " :id " , ( sqlite3_int64 ) task - > id ( ) ) ;
result = updateParent . exec ( ) ;
// Insert into children list
TaskArray & children = newParent - > children ( ) ;
if ( indexToInsert > = children . size ( ) )
children . push_back ( task ) ;
else
children . insert ( indexToInsert , task ) ;
for ( int i = 0 ; i < children . size ( ) ; i + + )
{
children [ i ] - > setIndex ( i ) ;
children [ i ] - > save ( ) ;
}
2018-09-25 22:55:14 +03:00
}
else
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement updateToRoot ( database ( ) , " update task set parentid = NULL where id=:id " ) ;
updateToRoot . bind ( " :id " , ( sqlite3_int64 ) task - > id ( ) ) ;
result = updateToRoot . exec ( ) ;
if ( result )
task - > setParentId ( 0 ) ;
// Insert into root list
if ( topOfTaskTree ( ) . size ( ) > indexToInsert )
topOfTaskTree ( ) . insert ( indexToInsert , task ) ;
else
topOfTaskTree ( ) . push_back ( task ) ;
for ( int i = 0 ; i < topOfTaskTree ( ) . size ( ) ; i + + )
{
topOfTaskTree ( ) [ i ] - > setIndex ( i ) ;
topOfTaskTree ( ) [ i ] - > save ( ) ;
}
2018-09-25 22:55:14 +03:00
}
2018-12-21 23:11:26 +02:00
if ( result )
{
task - > save ( ) ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
return result ;
2018-09-25 22:55:14 +03:00
}
bool Storage : : isOpened ( )
{
2018-12-21 23:11:26 +02:00
return mDatabase ! = nullptr ;
2018-09-25 22:55:14 +03:00
}
void Storage : : save ( )
{
2018-12-21 23:11:26 +02:00
foreach ( PTask task , mTopTasks )
saveTask ( task , depthRecursive ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
database ( ) . exec ( " delete from task where removed = 1 " ) ;
database ( ) . exec ( " delete from timeline where removed = 1 " ) ;
database ( ) . exec ( " delete from file where removed = 1 " ) ;
//database().exec("delete from change where removed = 1");
2018-09-25 22:55:14 +03:00
}
void Storage : : saveSingleTask ( PTask task )
{
2018-12-21 23:11:26 +02:00
task - > save ( ) ;
2018-09-25 22:55:14 +03:00
}
void Storage : : loadTaskTree ( )
{
2018-12-21 23:11:26 +02:00
mTopTasks . clear ( ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (parentid is null) and ((removed != 1) or (removed is null)) order by orderid " ) ;
int currentIndex = 0 ;
while ( q . executeStep ( ) )
{
PTask t ( new Task ( ) ) ;
t - > load ( q ) ;
t - > setIndex ( currentIndex + + ) ;
mTaskModelIdMap [ t - > modelId ( ) ] = t ;
mTaskIdMap [ t - > id ( ) ] = t ;
mTopTasks . push_back ( t ) ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
foreach ( PTask task , mTopTasks )
loadTaskChildren ( task ) ;
2018-09-25 22:55:14 +03:00
}
void Storage : : loadTaskChildren ( PTask task )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " select id, title, orderid, flags, (select count(*) from file where (file.taskid = task.id) and ((file.removed is null) or (file.removed = 0))) from task where (task.parentid = :parentid) and ((task.removed != 1) or (task.removed is null)) order by task.orderid " ) ;
q . bind ( " :parentid " , ( sqlite3_int64 ) task - > id ( ) ) ;
int currentIndex = 0 ;
while ( q . executeStep ( ) )
{
PTask t ( new Task ( ) ) ;
t - > load ( q ) ;
t - > setIndex ( currentIndex + + ) ;
t - > setParent ( task , false ) ;
loadTaskChildren ( t ) ;
mTaskModelIdMap [ t - > modelId ( ) ] = t ;
mTaskIdMap [ t - > id ( ) ] = t ;
task - > children ( ) . push_back ( t ) ;
}
2018-09-25 22:55:14 +03:00
}
PTask Storage : : findTaskByModelId ( Task : : ModelId id )
{
2018-12-21 23:11:26 +02:00
auto taskIter = mTaskModelIdMap . find ( id ) ;
if ( taskIter ! = mTaskModelIdMap . end ( ) )
return * taskIter ;
else
return PTask ( ) ;
2018-09-25 22:55:14 +03:00
}
PTask Storage : : findTaskById ( Task : : Id id )
{
2018-12-21 23:11:26 +02:00
auto taskIter = mTaskIdMap . find ( id ) ;
if ( taskIter ! = mTaskIdMap . end ( ) )
return * taskIter ;
else
return PTask ( ) ;
2018-09-25 22:55:14 +03:00
}
int Storage : : findTaskIndexInParent ( PTask task )
{
2018-12-21 23:11:26 +02:00
if ( task - > parent ( ) )
return task - > parent ( ) - > children ( ) . indexOf ( task ) ;
else
return topOfTaskTree ( ) . indexOf ( task ) ;
2018-09-25 22:55:14 +03:00
}
TaskArray & Storage : : topOfTaskTree ( )
{
2018-12-21 23:11:26 +02:00
return mTopTasks ;
2018-09-25 22:55:14 +03:00
}
void Storage : : loadAttachments ( PTask task , AttachmentArray & output )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " select id, filename, orderid from file where (taskid = :taskid) and ((removed != 1) or (removed is null)) order by orderid " ) ;
q . bind ( " :taskid " , ( sqlite3_int64 ) task - > id ( ) ) ;
while ( q . executeStep ( ) )
{
PAttachment att ( new Attachment ( ) ) ;
att - > setId ( q . getColumn ( 0 ) . getInt64 ( ) ) ;
att - > setFilename ( q . getColumn ( 1 ) . getText ( ) ) ;
att - > setTaskId ( task - > id ( ) ) ;
att - > setIndex ( q . getColumn ( 2 ) . getInt ( ) ) ;
output . push_back ( att ) ;
}
2018-09-25 22:55:14 +03:00
}
void Storage : : deleteAttachment ( PAttachment att )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " update file set removed=1 where id=:id " ) ;
q . bind ( " :id " , ( sqlite3_int64 ) att - > id ( ) ) ;
q . exec ( ) ;
2018-09-25 22:55:14 +03:00
}
void Storage : : undeleteAttachment ( PAttachment att )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " update file set removed=0 where id=:id " ) ;
q . bind ( " :id " , ( sqlite3_int64 ) att - > id ( ) ) ;
q . exec ( ) ;
2018-09-25 22:55:14 +03:00
}
Storage & Storage : : instance ( )
{
2018-12-21 23:11:26 +02:00
static Storage _instance ;
return _instance ;
2018-09-25 22:55:14 +03:00
}
void Storage : : deleteTask ( PTask task , DeleteOption option )
{
2018-12-21 23:11:26 +02:00
if ( option ! = DeleteOption_FromParent )
{
// Remove from hash
removeTask ( task ) ;
}
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
TaskArray & siblings = task - > parent ( ) ? task - > parent ( ) - > children ( ) : mTopTasks ;
siblings . removeOne ( task ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Remove from database
if ( option = = DeleteOption_Total )
{
SQLite : : Statement q ( database ( ) , " update task set removed = 1 where id = :id " ) ;
q . bind ( " :id " , ( sqlite3_int64 ) task - > id ( ) ) ;
q . exec ( ) ;
}
2018-09-25 22:55:14 +03:00
#if 0
2018-12-21 23:11:26 +02:00
int taskIndex = - 1 ;
// Update indexes
for ( int i = 0 ; i < siblings . size ( ) ; i + + )
2018-09-25 22:55:14 +03:00
{
2018-12-21 23:11:26 +02:00
if ( siblings [ i ] = = task )
taskIndex = i ;
Task & t = * siblings [ i ] ;
if ( t . index ( ) > task - > index ( ) )
{
t . setIndex ( t . index ( ) - 1 ) ;
t . save ( ) ;
}
2018-09-25 22:55:14 +03:00
}
2018-12-21 23:11:26 +02:00
// Remove from tree
if ( taskIndex > = 0 )
siblings . remove ( taskIndex ) ;
2018-09-25 22:55:14 +03:00
# endif
}
void Storage : : undeleteTask ( PTask task )
{
2018-12-21 23:11:26 +02:00
SQLite : : Statement q ( database ( ) , " update task set removed = 0 where id = :id " ) ;
q . bind ( " :id " , ( sqlite3_int64 ) task - > id ( ) ) ;
q . exec ( ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
// Find place where to insert task
TaskArray & siblings = task - > parent ( ) ? task - > parent ( ) - > children ( ) : mTopTasks ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
TaskArray : : iterator taskIter = std : : lower_bound ( siblings . begin ( ) , siblings . end ( ) , task - > index ( ) , [ ] ( const PTask & t , int index ) { return t - > index ( ) < index ; } ) ;
if ( taskIter ! = siblings . end ( ) )
siblings . insert ( taskIter , task ) ;
else
siblings . append ( task ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
mTaskModelIdMap [ task - > modelId ( ) ] = task ;
mTaskIdMap [ task - > id ( ) ] = task ;
2018-09-25 22:55:14 +03:00
}
void Storage : : removeTask ( PTask task )
{
2018-12-21 23:11:26 +02:00
auto taskModelIter = mTaskModelIdMap . find ( task - > modelId ( ) ) ;
if ( taskModelIter ! = mTaskModelIdMap . end ( ) )
mTaskModelIdMap . erase ( taskModelIter ) ;
2018-09-25 22:55:14 +03:00
2018-12-21 23:11:26 +02:00
auto taskIter = mTaskIdMap . find ( task - > id ( ) ) ;
if ( taskIter ! = mTaskIdMap . end ( ) )
mTaskIdMap . erase ( taskIter ) ;
2018-09-25 22:55:14 +03:00
}