Save hide state in db

master
Jonas Letzbor 2022-10-06 18:31:38 +02:00
parent 6f54ccd44c
commit 2f84fec1a9
11 changed files with 182 additions and 47 deletions

View File

@ -85,6 +85,7 @@ include_directories(
) )
TARGET_LINK_LIBRARIES (Nextcloud.app PRIVATE inkview freetype curl sqlite3 stdc++fs) 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) INSTALL (TARGETS Nextcloud.app)

View File

@ -21,6 +21,12 @@ using std::string;
SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath) SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath)
{ {
_fileHandler = std::shared_ptr<FileHandler>(new FileHandler()); _fileHandler = std::shared_ptr<FileHandler>(new FileHandler());
// check if migration has to be run
int currentVersion = getDbVersion();
if (currentVersion != DBVERSION) {
runMigration(currentVersion);
}
} }
SqliteConnector::~SqliteConnector() SqliteConnector::~SqliteConnector()
@ -30,6 +36,74 @@ SqliteConnector::~SqliteConnector()
Log::writeInfoLog("closed DB"); 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() bool SqliteConnector::open()
{ {
int rs; int rs;
@ -42,7 +116,8 @@ bool SqliteConnector::open()
return false; 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; return true;
} }
@ -123,13 +198,16 @@ std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPa
int rs; int rs;
sqlite3_stmt *stmt = 0; sqlite3_stmt *stmt = 0;
std::vector<WebDAVItem> items; std::vector<WebDAVItem> items;
std::vector<WebDAVItem> 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, 1, parentPath.c_str(), parentPath.length(), NULL);
rs = sqlite3_bind_text(stmt, 2, 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) while (sqlite3_step(stmt) == SQLITE_ROW)
{ {
WebDAVItem temp; WebDAVItem temp;
@ -143,6 +221,7 @@ std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPa
temp.lastEditDate = Util::webDAVStringToTm(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 6))); temp.lastEditDate = Util::webDAVStringToTm(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 6)));
temp.type = static_cast<Itemtype>(sqlite3_column_int(stmt,7)); temp.type = static_cast<Itemtype>(sqlite3_column_int(stmt,7));
temp.state = static_cast<FileState>(sqlite3_column_int(stmt,8)); temp.state = static_cast<FileState>(sqlite3_column_int(stmt,8));
temp.hide = static_cast<HideState>(sqlite3_column_int(stmt,9));
if (iv_access(temp.localPath.c_str(), W_OK) != 0) if (iv_access(temp.localPath.c_str(), W_OK) != 0)
{ {
@ -150,33 +229,15 @@ std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPa
temp.state = FileState::ICLOUD; temp.state = FileState::ICLOUD;
} }
string direcotryPath = temp.path; if (temp.hide == HideState::INOTDEFINED) {
if (direcotryPath.length() >= storageLocationLength) { temp.hide = _fileHandler->getHideState(temp.type, storageLocation, temp.path, temp.title);
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);
} }
items.push_back(temp);
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
sqlite3_close(_db); sqlite3_close(_db);
for (WebDAVItem itemD : itemsToRemove) {
if (itemD.type == Itemtype::IFOLDER) {
deleteChildren(itemD.path);
} else {
deleteChild(itemD.path, itemD.title);
}
}
return items; 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) void SqliteConnector::deleteChildren(const string &parentPath)
{ {
//TODO missing the onces where parentPath is one folder deeper and also destroyed //TODO missing the onces where parentPath is one folder deeper and also destroyed
@ -232,7 +315,7 @@ bool SqliteConnector::saveItemsChildren(const std::vector<WebDAVItem> &items)
for (auto item : 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_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, 1, item.title.c_str(), item.title.length(), NULL);
rs = sqlite3_bind_text(stmt, 2, item.localPath.c_str(), item.localPath.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<WebDAVItem> &items)
rs = sqlite3_bind_text(stmt, 8, lastEditDateString.c_str(), lastEditDateString.length(), NULL); 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, 9, item.type);
rs = sqlite3_bind_int(stmt, 10, item.state); rs = sqlite3_bind_int(stmt, 10, item.state);
rs = sqlite3_bind_int(stmt, 11, item.hide);
rs = sqlite3_step(stmt); rs = sqlite3_step(stmt);
if (rs == SQLITE_CONSTRAINT) if (rs == SQLITE_CONSTRAINT)

View File

@ -31,6 +31,10 @@ public:
bool open(); bool open();
int getDbVersion();
void runMigration(int currentVersion);
std::string getEtag(const std::string &path); std::string getEtag(const std::string &path);
FileState getState(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); void deleteChild(const std::string &path, const std::string &title);
bool resetHideState();
bool saveItemsChildren(const std::vector<WebDAVItem> &children); bool saveItemsChildren(const std::vector<WebDAVItem> &children);
private: private:

View File

@ -115,6 +115,7 @@ vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
size_t begin = xmlItem.find(beginItem); size_t begin = xmlItem.find(beginItem);
size_t end; size_t end;
string prefix = NEXTCLOUD_ROOT_PATH + _username + "/";
while (begin != std::string::npos) while (begin != std::string::npos)
{ {
end = xmlItem.find(endItem); end = xmlItem.find(endItem);
@ -183,20 +184,11 @@ vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length()); tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length());
Util::decodeUrl(tempItem.title); Util::decodeUrl(tempItem.title);
string folderPath = tempItem.path; string &pathDecoded = tempItem.path;
string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; Util::decodeUrl(pathDecoded);
if (tempItem.path.find(prefix) != string::npos) { tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,(pathDecoded), tempItem.title);
folderPath = tempItem.path.substr(prefix.length());
if (tempItem.type == Itemtype::IFILE && folderPath.length() >= tempItem.title.length()) { tempItems.push_back(tempItem);
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);
xmlItem = xmlItem.substr(end + endItem.length()); xmlItem = xmlItem.substr(end + endItem.length());
begin = xmlItem.find(beginItem); begin = xmlItem.find(beginItem);
} }

View File

@ -28,6 +28,13 @@ enum FileState
IDOWNLOADED IDOWNLOADED
}; };
enum HideState
{
INOTDEFINED,
ISHOW,
IHIDE
};
struct WebDAVItem : Entry{ struct WebDAVItem : Entry{
std::string etag; std::string etag;
std::string path; std::string path;
@ -38,6 +45,7 @@ struct WebDAVItem : Entry{
tm lastEditDate = { 0 }; tm lastEditDate = { 0 };
std::string size; std::string size;
std::string fileType; std::string fileType;
HideState hide;
}; };
#endif #endif

View File

@ -251,7 +251,8 @@ void EventHandler::mainMenuHandler(const int index)
//Info //Info
case 106: 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; break;
} }
//Exit //Exit
@ -388,6 +389,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2)
Util::accessConfig<string>(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); Util::accessConfig<string>(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex());
Util::accessConfig<string>(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); Util::accessConfig<string>(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex());
Util::accessConfig<int>(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); Util::accessConfig<int>(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch());
_sqllite.resetHideState();
_excludeFileView.reset(); _excludeFileView.reset();
ShowHourglassForce(); ShowHourglassForce();
@ -655,6 +657,10 @@ void EventHandler::getLocalFileStructure(std::vector<WebDAVItem> &tempItems)
void EventHandler::downloadFolder(vector<WebDAVItem> &items, int itemID) void EventHandler::downloadFolder(vector<WebDAVItem> &items, int itemID)
{ {
//Don't sync hidden files
if (items.at(itemID).hide == HideState::IHIDE)
return;
//BanSleep(2000); //BanSleep(2000);
string path = items.at(itemID).path; string path = items.at(itemID).path;

View File

@ -9,6 +9,7 @@
#include "fileHandler.h" #include "fileHandler.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#include "webDAVModel.h"
#include <sstream> #include <sstream>
#include <regex> #include <regex>
@ -124,6 +125,33 @@ bool FileHandler::excludeFolder(std::string folderName) {
return _invertMatch; 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) { void FileHandler::update(string regex, string folderRegex, string extensions, int invertMatch) {
for (FileHandler *handler : _instances) { for (FileHandler *handler : _instances) {
handler->parseConfig(regex, folderRegex, extensions, invertMatch); handler->parseConfig(regex, folderRegex, extensions, invertMatch);

View File

@ -9,6 +9,8 @@
#ifndef FILEHANDLER #ifndef FILEHANDLER
#define FILEHANDLER #define FILEHANDLER
#include "webDAVModel.h"
#include <regex> #include <regex>
#include <string> #include <string>
#include <vector> #include <vector>
@ -22,6 +24,7 @@ class FileHandler
~FileHandler(); ~FileHandler();
bool excludeFile(std::string filename); bool excludeFile(std::string filename);
bool excludeFolder(std::string foldername); bool excludeFolder(std::string foldername);
HideState getHideState(Itemtype itemType, std::string prefixToStripe, std::string path, std::string title);
std::string getStorageLocation(); std::string getStorageLocation();
std::string getStorageUsername(); std::string getStorageUsername();

View File

@ -14,10 +14,10 @@
using std::string; using std::string;
std::shared_ptr<ExcludeFileView> ExcludeFileView::_excludeFileViewStatic; ExcludeFileView* ExcludeFileView::_excludeFileViewStatic;
ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect) ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect)
{ {
_excludeFileViewStatic.reset(this); _excludeFileViewStatic = this;
_extensionList = Util::getConfig<string>("ex_extensionList", ""); _extensionList = Util::getConfig<string>("ex_extensionList", "");
_regex = Util::getConfig<string>("ex_pattern", ""); _regex = Util::getConfig<string>("ex_pattern", "");
@ -77,6 +77,7 @@ ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(content
ExcludeFileView::~ExcludeFileView() ExcludeFileView::~ExcludeFileView()
{ {
CloseFont(_font); 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) int ExcludeFileView::excludeClicked(int x, int y)
@ -176,8 +177,8 @@ void ExcludeFileView::keyboardHandler(char *text)
return; return;
string s(text); string s(text);
if (s.empty()) //if (s.empty())
return; // return;
if (_target == ExcludeFileKeyboardTarget::IEXTENSIONS) if (_target == ExcludeFileKeyboardTarget::IEXTENSIONS)
{ {

View File

@ -54,7 +54,7 @@ public:
int getInvertMatch() { return _invertMatch; }; int getInvertMatch() { return _invertMatch; };
private: private:
static std::shared_ptr<ExcludeFileView> _excludeFileViewStatic; static ExcludeFileView *_excludeFileViewStatic;
int _fontHeight; int _fontHeight;
ifont *_font; ifont *_font;
const irect _contentRect; const irect _contentRect;

View File

@ -17,8 +17,14 @@
using std::vector; using std::vector;
WebDAVView::WebDAVView(const irect &contentRect, vector<WebDAVItem> &items, int page) : ListView(contentRect, page) WebDAVView::WebDAVView(const irect &contentRect, vector<WebDAVItem> &itemsUnfiltered, int page) : ListView(contentRect, page)
{ {
vector<WebDAVItem> items;
std::copy_if (itemsUnfiltered.begin(), itemsUnfiltered.end(), std::back_inserter(items), [](WebDAVItem i)
{
return i.hide != HideState::IHIDE;
});
auto pageHeight = 0; auto pageHeight = 0;
auto contentHeight = _contentRect.h - _footerHeight; auto contentHeight = _contentRect.h - _footerHeight;
auto entrycount = items.size(); auto entrycount = items.size();