From 2f84fec1a90593f59fd0efb200119af8dcf502b1 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Thu, 6 Oct 2022 18:31:38 +0200 Subject: [PATCH] Save hide state in db --- CMakeLists.txt | 1 + src/api/sqliteConnector.cpp | 136 +++++++++++++++++---- src/api/sqliteConnector.h | 6 + src/api/webDAV.cpp | 20 +-- src/api/webDAVModel.h | 8 ++ src/handler/eventHandler.cpp | 8 +- src/handler/fileHandler.cpp | 28 +++++ src/handler/fileHandler.h | 3 + src/ui/excludeFileView/excludeFileView.cpp | 9 +- src/ui/excludeFileView/excludeFileView.h | 2 +- src/ui/webDAVView/webDAVView.cpp | 8 +- 11 files changed, 182 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47b51e4..c020d50 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ include_directories( ) TARGET_LINK_LIBRARIES (Nextcloud.app PRIVATE inkview freetype curl sqlite3 stdc++fs) +target_compile_definitions(Nextcloud.app PRIVATE DBVERSION=2 PROGRAMVERSION="1.02") INSTALL (TARGETS Nextcloud.app) diff --git a/src/api/sqliteConnector.cpp b/src/api/sqliteConnector.cpp index 74824ea..8b464c0 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -21,6 +21,12 @@ using std::string; SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath) { _fileHandler = std::shared_ptr(new FileHandler()); + + // check if migration has to be run + int currentVersion = getDbVersion(); + if (currentVersion != DBVERSION) { + runMigration(currentVersion); + } } SqliteConnector::~SqliteConnector() @@ -30,6 +36,74 @@ SqliteConnector::~SqliteConnector() Log::writeInfoLog("closed DB"); } +void SqliteConnector::runMigration(int currentVersion) +{ + open(); + + char* log; + sprintf(log, "Running migration from db version %i to %i (Program version %s)", currentVersion, DBVERSION, PROGRAMVERSION); + Log::writeInfoLog(log); + + // currently there are no migrations + + // updating to current version + int rs; + sqlite3_stmt *stmt = 0; + + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'version' (dbversion) VALUES (?)", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, DBVERSION); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) { + // this is critical + Log::writeErrorLog(std::string("error inserting into version") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + + sqlite3_finalize(stmt); + sqlite3_close(_db); +} + +int SqliteConnector::getDbVersion() +{ + open(); + + int rs; + sqlite3_stmt *stmt = 0; + + int version; + rs = sqlite3_prepare_v2(_db, "SELECT MAX(dbversion) FROM 'version'", -1, &stmt, 0); + while (sqlite3_step(stmt) == SQLITE_ROW) + { + version = sqlite3_column_int(stmt, 0); + } + + if (version != 0) + { + sqlite3_finalize(stmt); + sqlite3_close(_db); + return version; + } else { + // this is probably the first start -> the version is up to date and insert the current version + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'version' (dbversion) VALUES (?)", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, DBVERSION); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) { + Log::writeErrorLog(std::string("error inserting into version") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + + // for compatibility alter the table because at this point db migrations doesn't exist + rs = sqlite3_exec(_db, "ALTER TABLE metadata ADD hide INT DEFAULT 0 NOT NULL", NULL, 0, NULL); + + sqlite3_finalize(stmt); + sqlite3_close(_db); + + return DBVERSION; + } +} + bool SqliteConnector::open() { int rs; @@ -42,7 +116,8 @@ bool SqliteConnector::open() return false; } - rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS metadata (title VARCHAR, localPath VARCHAR, size VARCHAR, fileType VARCHAR, lasteditDate VARCHAR, type INT, state INT, etag VARCHAR, path VARCHAR PRIMARY KEY, parentPath VARCHAR)", NULL, 0, NULL); + rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS metadata (title VARCHAR, localPath VARCHAR, size VARCHAR, fileType VARCHAR, lasteditDate VARCHAR, type INT, state INT, etag VARCHAR, path VARCHAR PRIMARY KEY, parentPath VARCHAR, hide INT DEFAULT 0 NOT NULL)", NULL, 0, NULL); + rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS version (dbversion INT)", NULL, 0, NULL); return true; } @@ -123,13 +198,16 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa int rs; sqlite3_stmt *stmt = 0; std::vector items; - std::vector itemsToRemove; - rs = sqlite3_prepare_v2(_db, "SELECT title, localPath, path, size, etag, fileType, lastEditDate, type, state FROM 'metadata' WHERE path=? OR parentPath=? ORDER BY parentPath;", -1, &stmt, 0); + rs = sqlite3_prepare_v2( + _db, + "SELECT title, localPath, path, size, etag, fileType, lastEditDate, type, state, hide FROM 'metadata' WHERE (path=? OR parentPath=?) AND hide <> 2 ORDER BY parentPath;", + -1, &stmt, 0 + ); rs = sqlite3_bind_text(stmt, 1, parentPath.c_str(), parentPath.length(), NULL); rs = sqlite3_bind_text(stmt, 2, parentPath.c_str(), parentPath.length(), NULL); - const int storageLocationLength = (NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/").length(); + const string storageLocation = NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/"; while (sqlite3_step(stmt) == SQLITE_ROW) { WebDAVItem temp; @@ -143,6 +221,7 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.lastEditDate = Util::webDAVStringToTm(reinterpret_cast(sqlite3_column_text(stmt, 6))); temp.type = static_cast(sqlite3_column_int(stmt,7)); temp.state = static_cast(sqlite3_column_int(stmt,8)); + temp.hide = static_cast(sqlite3_column_int(stmt,9)); if (iv_access(temp.localPath.c_str(), W_OK) != 0) { @@ -150,33 +229,15 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.state = FileState::ICLOUD; } - string direcotryPath = temp.path; - if (direcotryPath.length() >= storageLocationLength) { - direcotryPath = direcotryPath.substr(storageLocationLength); - } - if (temp.type == Itemtype::IFILE && ( _fileHandler->excludeFolder(direcotryPath) || _fileHandler->excludeFile(temp.title))) { - //The file was previously cached and should be excluded from now on - //TODO Maybe an SQL statement with REGEX match should be executed directly after changing the conifg - itemsToRemove.push_back(temp); - } else if (temp.type == Itemtype::IFOLDER && _fileHandler->excludeFolder(direcotryPath)) { - itemsToRemove.push_back(temp); - } - else { - items.push_back(temp); + if (temp.hide == HideState::INOTDEFINED) { + temp.hide = _fileHandler->getHideState(temp.type, storageLocation, temp.path, temp.title); } + items.push_back(temp); } sqlite3_finalize(stmt); sqlite3_close(_db); - for (WebDAVItem itemD : itemsToRemove) { - if (itemD.type == Itemtype::IFOLDER) { - deleteChildren(itemD.path); - } else { - deleteChild(itemD.path, itemD.title); - } - } - return items; } @@ -199,6 +260,28 @@ void SqliteConnector::deleteChild(const string &path, const string &title) } +bool SqliteConnector::resetHideState() +{ + open(); + int rs; + sqlite3_stmt *stmt = 0; + + rs = sqlite3_prepare_v2(_db, "UPDATE 'metadata' SET hide=0", -1, &stmt, 0); + rs = sqlite3_step(stmt); + + if (rs != SQLITE_DONE) + { + Log::writeErrorLog(sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + + sqlite3_finalize(stmt); + sqlite3_close(_db); + + return true; +} + void SqliteConnector::deleteChildren(const string &parentPath) { //TODO missing the onces where parentPath is one folder deeper and also destroyed @@ -232,7 +315,7 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) for (auto item : items) { - rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state) VALUES (?,?,?,?,?,?,?,?,?,?);", -1, &stmt, 0); + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state, hide) VALUES (?,?,?,?,?,?,?,?,?,?,?);", -1, &stmt, 0); rs = sqlite3_exec(_db, "BEGIN TRANSACTION;", NULL, NULL, NULL); rs = sqlite3_bind_text(stmt, 1, item.title.c_str(), item.title.length(), NULL); rs = sqlite3_bind_text(stmt, 2, item.localPath.c_str(), item.localPath.length(), NULL); @@ -245,6 +328,7 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) rs = sqlite3_bind_text(stmt, 8, lastEditDateString.c_str(), lastEditDateString.length(), NULL); rs = sqlite3_bind_int(stmt, 9, item.type); rs = sqlite3_bind_int(stmt, 10, item.state); + rs = sqlite3_bind_int(stmt, 11, item.hide); rs = sqlite3_step(stmt); if (rs == SQLITE_CONSTRAINT) diff --git a/src/api/sqliteConnector.h b/src/api/sqliteConnector.h index fe7545d..d3f207d 100644 --- a/src/api/sqliteConnector.h +++ b/src/api/sqliteConnector.h @@ -31,6 +31,10 @@ public: bool open(); + int getDbVersion(); + + void runMigration(int currentVersion); + std::string getEtag(const std::string &path); FileState getState(const std::string &path); @@ -43,6 +47,8 @@ public: void deleteChild(const std::string &path, const std::string &title); + bool resetHideState(); + bool saveItemsChildren(const std::vector &children); private: diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index b1ac336..8565c2a 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -115,6 +115,7 @@ vector WebDAV::getDataStructure(const string &pathUrl) size_t begin = xmlItem.find(beginItem); size_t end; + string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; while (begin != std::string::npos) { end = xmlItem.find(endItem); @@ -183,20 +184,11 @@ vector WebDAV::getDataStructure(const string &pathUrl) tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length()); Util::decodeUrl(tempItem.title); - string folderPath = tempItem.path; - string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; - if (tempItem.path.find(prefix) != string::npos) { - folderPath = tempItem.path.substr(prefix.length()); - if (tempItem.type == Itemtype::IFILE && folderPath.length() >= tempItem.title.length()) { - folderPath = folderPath.substr(0, folderPath.length() - tempItem.title.length()); - } - } - - if (tempItem.type == Itemtype::IFILE && ( !_fileHandler->excludeFolder(folderPath) && !_fileHandler->excludeFile(tempItem.title) )) - tempItems.push_back(tempItem); - else if (tempItem.type == Itemtype::IFOLDER && !_fileHandler->excludeFolder(folderPath)) - tempItems.push_back(tempItem); - + string &pathDecoded = tempItem.path; + Util::decodeUrl(pathDecoded); + tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,(pathDecoded), tempItem.title); + + tempItems.push_back(tempItem); xmlItem = xmlItem.substr(end + endItem.length()); begin = xmlItem.find(beginItem); } diff --git a/src/api/webDAVModel.h b/src/api/webDAVModel.h index c4b5ab0..10744ba 100644 --- a/src/api/webDAVModel.h +++ b/src/api/webDAVModel.h @@ -28,6 +28,13 @@ enum FileState IDOWNLOADED }; +enum HideState +{ + INOTDEFINED, + ISHOW, + IHIDE +}; + struct WebDAVItem : Entry{ std::string etag; std::string path; @@ -38,6 +45,7 @@ struct WebDAVItem : Entry{ tm lastEditDate = { 0 }; std::string size; std::string fileType; + HideState hide; }; #endif diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index 5c5b1a7..1d78cc5 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -251,7 +251,8 @@ void EventHandler::mainMenuHandler(const int index) //Info case 106: { - Message(ICON_INFORMATION, "Info", "Version 1.02 \n For support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues", 1200); + string message; + Message(ICON_INFORMATION, "Info", message.append("Version ").append(PROGRAMVERSION).append("\nFor support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues").c_str(), 1200); break; } //Exit @@ -388,6 +389,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) Util::accessConfig(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); Util::accessConfig(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); Util::accessConfig(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); + _sqllite.resetHideState(); _excludeFileView.reset(); ShowHourglassForce(); @@ -655,6 +657,10 @@ void EventHandler::getLocalFileStructure(std::vector &tempItems) void EventHandler::downloadFolder(vector &items, int itemID) { + //Don't sync hidden files + if (items.at(itemID).hide == HideState::IHIDE) + return; + //BanSleep(2000); string path = items.at(itemID).path; diff --git a/src/handler/fileHandler.cpp b/src/handler/fileHandler.cpp index 209b246..e3f385c 100644 --- a/src/handler/fileHandler.cpp +++ b/src/handler/fileHandler.cpp @@ -9,6 +9,7 @@ #include "fileHandler.h" #include "log.h" #include "util.h" +#include "webDAVModel.h" #include #include @@ -124,6 +125,33 @@ bool FileHandler::excludeFolder(std::string folderName) { return _invertMatch; } +HideState FileHandler::getHideState(Itemtype itemType, std::string prefix, std::string path, std::string title = "") { + + string folderPath = path; + if (path.find(prefix) != string::npos) { + folderPath = path.substr(prefix.length()); + if (itemType == Itemtype::IFILE && folderPath.length() >= title.length()) { + folderPath = folderPath.substr(0, folderPath.length() - title.length()); + } + } + folderPath = "/" + folderPath; + + if (itemType == Itemtype::IFILE) { + if (!excludeFolder(folderPath) && !excludeFile(title)) { + return HideState::ISHOW; + } else { + return HideState::IHIDE; + } + } else { + if (!excludeFolder(folderPath)) { + return HideState::ISHOW; + } else { + return HideState::IHIDE; + } + } +} + + void FileHandler::update(string regex, string folderRegex, string extensions, int invertMatch) { for (FileHandler *handler : _instances) { handler->parseConfig(regex, folderRegex, extensions, invertMatch); diff --git a/src/handler/fileHandler.h b/src/handler/fileHandler.h index 445a449..e31b1fe 100644 --- a/src/handler/fileHandler.h +++ b/src/handler/fileHandler.h @@ -9,6 +9,8 @@ #ifndef FILEHANDLER #define FILEHANDLER +#include "webDAVModel.h" + #include #include #include @@ -22,6 +24,7 @@ class FileHandler ~FileHandler(); bool excludeFile(std::string filename); bool excludeFolder(std::string foldername); + HideState getHideState(Itemtype itemType, std::string prefixToStripe, std::string path, std::string title); std::string getStorageLocation(); std::string getStorageUsername(); diff --git a/src/ui/excludeFileView/excludeFileView.cpp b/src/ui/excludeFileView/excludeFileView.cpp index 8c6da82..8bcd088 100644 --- a/src/ui/excludeFileView/excludeFileView.cpp +++ b/src/ui/excludeFileView/excludeFileView.cpp @@ -14,10 +14,10 @@ using std::string; -std::shared_ptr ExcludeFileView::_excludeFileViewStatic; +ExcludeFileView* ExcludeFileView::_excludeFileViewStatic; ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect) { - _excludeFileViewStatic.reset(this); + _excludeFileViewStatic = this; _extensionList = Util::getConfig("ex_extensionList", ""); _regex = Util::getConfig("ex_pattern", ""); @@ -77,6 +77,7 @@ ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(content ExcludeFileView::~ExcludeFileView() { CloseFont(_font); + // Pointer cannot be freeded... It would crash when optining the window >~ 2 times (as well as an shared_ptr) } int ExcludeFileView::excludeClicked(int x, int y) @@ -176,8 +177,8 @@ void ExcludeFileView::keyboardHandler(char *text) return; string s(text); - if (s.empty()) - return; + //if (s.empty()) + // return; if (_target == ExcludeFileKeyboardTarget::IEXTENSIONS) { diff --git a/src/ui/excludeFileView/excludeFileView.h b/src/ui/excludeFileView/excludeFileView.h index 0d85a20..b3e10c6 100644 --- a/src/ui/excludeFileView/excludeFileView.h +++ b/src/ui/excludeFileView/excludeFileView.h @@ -54,7 +54,7 @@ public: int getInvertMatch() { return _invertMatch; }; private: - static std::shared_ptr _excludeFileViewStatic; + static ExcludeFileView *_excludeFileViewStatic; int _fontHeight; ifont *_font; const irect _contentRect; diff --git a/src/ui/webDAVView/webDAVView.cpp b/src/ui/webDAVView/webDAVView.cpp index ab42922..67a7e51 100644 --- a/src/ui/webDAVView/webDAVView.cpp +++ b/src/ui/webDAVView/webDAVView.cpp @@ -17,8 +17,14 @@ using std::vector; -WebDAVView::WebDAVView(const irect &contentRect, vector &items, int page) : ListView(contentRect, page) +WebDAVView::WebDAVView(const irect &contentRect, vector &itemsUnfiltered, int page) : ListView(contentRect, page) { + vector items; + std::copy_if (itemsUnfiltered.begin(), itemsUnfiltered.end(), std::back_inserter(items), [](WebDAVItem i) + { + return i.hide != HideState::IHIDE; + }); + auto pageHeight = 0; auto contentHeight = _contentRect.h - _footerHeight; auto entrycount = items.size();