Skip to content

Commit

Permalink
Implement upload to webdav folder funcationality
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jan 5, 2025
1 parent 4151281 commit df0df4f
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 29 deletions.
178 changes: 157 additions & 21 deletions src/core/webdavconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,18 +233,18 @@ void WebdavConnection::processDirParserFinished()
{
applyStoredPassword();

QDir localDir( mGetLocalPath );
QDir localDir( mProcessLocalPath );
for ( const QWebdavItem &item : list )
{
if ( item.isDir() )
{
localDir.mkpath( item.path().mid( mGetRemotePath.size() ) );
localDir.mkpath( item.path().mid( mProcessRemotePath.size() ) );
}
else
{
if ( mIsDownloadingPath )
{
QFileInfo fileInfo( mGetLocalPath + item.path().mid( mGetRemotePath.size() ) );
QFileInfo fileInfo( mProcessLocalPath + item.path().mid( mProcessRemotePath.size() ) );
if ( !fileInfo.exists() || ( fileInfo.fileTime( QFileDevice::FileModificationTime ) != item.lastModified() ) )
{
mWebdavItems << item;
Expand All @@ -263,6 +263,40 @@ void WebdavConnection::processDirParserFinished()

getWebdavItems();
}
else if ( mIsUploadingPath )
{
applyStoredPassword();

for ( const QWebdavItem &item : list )
{
if ( !item.isDir() )
{
QFileInfo fileInfo( mProcessLocalPath + item.path().mid( mProcessRemotePath.size() ) );
if ( fileInfo.exists() )
{
auto localFileInfo = std::find_if( mLocalItems.begin(), mLocalItems.end(), [&fileInfo]( const QFileInfo &entry ) {
return entry.absoluteFilePath() == fileInfo.absoluteFilePath();
} );

if ( localFileInfo != mLocalItems.end() )
{
if ( localFileInfo->fileTime( QFileDevice::FileModificationTime ) == item.lastModified() )
{
mLocalItems.remove( localFileInfo - mLocalItems.begin(), 1 );
}
}
}
}
}

for ( const QFileInfo &fileInfo : mLocalItems )
{
mBytesTotal += fileInfo.size();
}
emit progressChanged();

putLocalItems();
}
}

void WebdavConnection::getWebdavItems()
Expand All @@ -273,22 +307,22 @@ void WebdavConnection::getWebdavItems()
const QDateTime itemLastModified = mWebdavItems.first().lastModified();
QNetworkReply *reply = mWebdavConnection.get( itemPath );
QTemporaryFile *temporaryFile = new QTemporaryFile( reply );
temporaryFile->setFileTemplate( QStringLiteral( "%1%2.XXXXXXXXXXXX" ).arg( mGetLocalPath, itemPath.mid( mGetRemotePath.size() ) ) );
temporaryFile->setFileTemplate( QStringLiteral( "%1%2.XXXXXXXXXXXX" ).arg( mProcessLocalPath, itemPath.mid( mProcessRemotePath.size() ) ) );
temporaryFile->open();

connect( reply, &QNetworkReply::downloadProgress, this, [=]( int bytesReceived, int bytesTotal ) {
mCurrentBytesReceived = bytesReceived;
mCurrentBytesProcessed = bytesReceived;
emit progressChanged();

temporaryFile->write( reply->readAll() );
} );

connect( reply, &QNetworkReply::finished, this, [=]() {
mBytesReceived += mCurrentBytesReceived;
mCurrentBytesReceived = 0;
mBytesProcessed += mCurrentBytesProcessed;
mCurrentBytesProcessed = 0;
if ( reply->error() == QNetworkReply::NoError )
{
QFile file( mGetLocalPath + itemPath.mid( mGetRemotePath.size() ) );
QFile file( mProcessLocalPath + itemPath.mid( mProcessRemotePath.size() ) );
if ( file.exists() )
{
// Remove pre-existing file
Expand All @@ -297,7 +331,7 @@ void WebdavConnection::getWebdavItems()

temporaryFile->write( reply->readAll() );
temporaryFile->setAutoRemove( false );
temporaryFile->rename( mGetLocalPath + itemPath.mid( mGetRemotePath.size() ) );
temporaryFile->rename( mProcessLocalPath + itemPath.mid( mProcessRemotePath.size() ) );
temporaryFile->close();
delete temporaryFile;

Expand Down Expand Up @@ -326,10 +360,10 @@ void WebdavConnection::getWebdavItems()
QVariantMap webdavConfiguration;
webdavConfiguration[QStringLiteral( "url" )] = mUrl;
webdavConfiguration[QStringLiteral( "username" )] = mUsername;
webdavConfiguration[QStringLiteral( "remote_path" )] = mGetRemotePath;
webdavConfiguration[QStringLiteral( "remote_path" )] = mProcessRemotePath;

QJsonDocument jsonDocument = QJsonDocument::fromVariant( webdavConfiguration );
QFile jsonFile( QStringLiteral( "%1qfield_webdav_configuration.json" ).arg( mGetLocalPath ) );
QFile jsonFile( QStringLiteral( "%1qfield_webdav_configuration.json" ).arg( mProcessLocalPath ) );
jsonFile.open( QFile::WriteOnly );
jsonFile.write( jsonDocument.toJson() );
jsonFile.close();
Expand All @@ -345,6 +379,49 @@ void WebdavConnection::getWebdavItems()
}
}

void WebdavConnection::putLocalItems()
{
if ( !mLocalItems.isEmpty() )
{
const QString itemPath = mLocalItems.first().absoluteFilePath();
qDebug() << "itemPath " << itemPath;
const QString remoteItemPath = mProcessRemotePath + itemPath.mid( mProcessLocalPath.size() ).replace( QDir::separator(), "/" );
qDebug() << "remoteItemPath " << remoteItemPath;

QFile *file = new QFile( itemPath );
file->open( QFile::ReadOnly );
QNetworkReply *reply = mWebdavConnection.put( remoteItemPath, file );
file->setParent( reply );

connect( reply, &QNetworkReply::uploadProgress, this, [=]( int bytesSent, int bytesTotal ) {
mCurrentBytesProcessed = bytesSent;
emit progressChanged();
} );

connect( reply, &QNetworkReply::finished, this, [=]() {
mBytesProcessed += mCurrentBytesProcessed;
mCurrentBytesProcessed = 0;
emit progressChanged();
if ( reply->error() != QNetworkReply::NoError )
{
mLastError = tr( "Failed to upload file %1 due to network error (%1)" ).arg( reply->error() );
}

mLocalItems.removeFirst();
putLocalItems();
reply->deleteLater();
} );
}
else
{
if ( mIsUploadingPath )
{
mIsUploadingPath = false;
emit isUploadingPathChanged();
}
}
}

void WebdavConnection::importPath( const QString &remotePath, const QString &localPath )
{
if ( mUrl.isEmpty() || mUsername.isEmpty() || ( mPassword.isEmpty() && mStoredPassword.isEmpty() ) )
Expand All @@ -358,18 +435,18 @@ void WebdavConnection::importPath( const QString &remotePath, const QString &loc
QDir localDir( localPath );
localDir.mkpath( localFolder );

mGetRemotePath = remotePath;
mGetLocalPath = QDir::cleanPath( localPath + QDir::separator() + localFolder ) + QDir::separator();
mProcessRemotePath = remotePath;
mProcessLocalPath = QDir::cleanPath( localPath + QDir::separator() + localFolder ) + QDir::separator();

mWebdavItems.clear();
mBytesReceived = 0;
mBytesProcessed = 0;
mBytesTotal = 0;
emit progressChanged();

mIsImportingPath = true;
emit isImportingPathChanged();

mWebdavDirParser.listDirectory( &mWebdavConnection, mGetRemotePath, true );
mWebdavDirParser.listDirectory( &mWebdavConnection, mProcessRemotePath, true );
}

void WebdavConnection::downloadPath( const QString &localPath )
Expand Down Expand Up @@ -400,22 +477,81 @@ void WebdavConnection::downloadPath( const QString &localPath )
{
setupConnection();

mGetRemotePath = webdavConfiguration["remote_path"].toString();
mProcessRemotePath = webdavConfiguration["remote_path"].toString();
if ( !remoteChildrenPath.isEmpty() )
{
mGetRemotePath = mGetRemotePath + remoteChildrenPath.join( "/" ) + QStringLiteral( "/" );
mProcessRemotePath = mProcessRemotePath + remoteChildrenPath.join( "/" ) + QStringLiteral( "/" );
}
mGetLocalPath = QDir::cleanPath( localPath ) + QDir::separator();
mProcessLocalPath = QDir::cleanPath( localPath ) + QDir::separator();

mWebdavItems.clear();
mBytesReceived = 0;
mBytesProcessed = 0;
mBytesTotal = 0;
emit progressChanged();

mIsDownloadingPath = true;
emit isDownloadingPathChanged();

mWebdavDirParser.listDirectory( &mWebdavConnection, mGetRemotePath, true );
mWebdavDirParser.listDirectory( &mWebdavConnection, mProcessRemotePath, true );
}
}
}
}

void WebdavConnection::uploadPath( const QString &localPath )
{
QDir dir( localPath );
bool webdavConfigurationExists = dir.exists( "qfield_webdav_configuration.json" );
QStringList remoteChildrenPath;
while ( !webdavConfigurationExists )
{
remoteChildrenPath << dir.dirName();
if ( !dir.cdUp() )
break;

webdavConfigurationExists = dir.exists( "qfield_webdav_configuration.json" );
}

if ( webdavConfigurationExists )
{
QFile webdavConfigurationFile( dir.absolutePath() + QDir::separator() + QStringLiteral( "qfield_webdav_configuration.json" ) );
webdavConfigurationFile.open( QFile::ReadOnly );
QJsonDocument jsonDocument = QJsonDocument::fromJson( webdavConfigurationFile.readAll() );
if ( !jsonDocument.isEmpty() )
{
QVariantMap webdavConfiguration = jsonDocument.toVariant().toMap();
setUrl( webdavConfiguration["url"].toString() );
setUsername( webdavConfiguration["username"].toString() );
if ( isPasswordStored() )
{
setupConnection();

mProcessRemotePath = webdavConfiguration["remote_path"].toString();
if ( !remoteChildrenPath.isEmpty() )
{
mProcessRemotePath = mProcessRemotePath + remoteChildrenPath.join( "/" ) + QStringLiteral( "/" );
}
mProcessLocalPath = QDir::cleanPath( localPath ) + QDir::separator();

mLocalItems.clear();
QDirIterator it( mProcessLocalPath, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
while ( it.hasNext() )
{
it.next();
if ( it.fileName() != QStringLiteral( "qfield_webdav_configuration.json" ) )
{
mLocalItems << it.fileInfo();
}
}

mBytesProcessed = 0;
mBytesTotal = 0;
emit progressChanged();

mIsUploadingPath = true;
emit isUploadingPathChanged();

mWebdavDirParser.listDirectory( &mWebdavConnection, mProcessRemotePath, true );
}
}
}
Expand All @@ -425,7 +561,7 @@ double WebdavConnection::progress() const
{
if ( ( mIsImportingPath || mIsDownloadingPath ) && mBytesTotal > 0 )
{
return static_cast<double>( mBytesReceived + mCurrentBytesReceived ) / mBytesTotal;
return static_cast<double>( mBytesProcessed + mCurrentBytesProcessed ) / mBytesTotal;
}

return 0;
Expand Down
18 changes: 13 additions & 5 deletions src/core/webdavconnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class WebdavConnection : public QObject
Q_PROPERTY( bool isFetchingAvailablePaths READ isFetchingAvailablePaths NOTIFY isFetchingAvailablePathsChanged )
Q_PROPERTY( bool isImportingPath READ isImportingPath NOTIFY isImportingPathChanged )
Q_PROPERTY( bool isDownloadingPath READ isDownloadingPath NOTIFY isDownloadingPathChanged )
Q_PROPERTY( bool isUploadingPath READ isUploadingPath NOTIFY isUploadingPathChanged )

Q_PROPERTY( QStringList availablePaths READ availablePaths NOTIFY availablePathsChanged )
Q_PROPERTY( double progress READ progress NOTIFY progressChanged )
Expand Down Expand Up @@ -76,6 +77,8 @@ class WebdavConnection : public QObject

bool isDownloadingPath() const { return mIsDownloadingPath; }

bool isUploadingPath() const { return mIsUploadingPath; }

double progress() const;

QString lastError() const { return mLastError; }
Expand All @@ -84,6 +87,7 @@ class WebdavConnection : public QObject

Q_INVOKABLE void importPath( const QString &remotePath, const QString &localPath );
Q_INVOKABLE void downloadPath( const QString &localPath );
Q_INVOKABLE void uploadPath( const QString &localPath );

signals:
void urlChanged();
Expand All @@ -94,6 +98,7 @@ class WebdavConnection : public QObject
void isFetchingAvailablePathsChanged();
void isImportingPathChanged();
void isDownloadingPathChanged();
void isUploadingPathChanged();
void availablePathsChanged();
void progressChanged();
void lastErrorChanged();
Expand All @@ -108,6 +113,7 @@ class WebdavConnection : public QObject
void applyStoredPassword();
void setupConnection();
void getWebdavItems();
void putLocalItems();

QString mUrl;
QString mUsername;
Expand All @@ -121,13 +127,15 @@ class WebdavConnection : public QObject

bool mIsImportingPath = false;
bool mIsDownloadingPath = false;

QString mGetRemotePath;
QString mGetLocalPath;
bool mIsUploadingPath = false;

QList<QWebdavItem> mWebdavItems;
qint64 mCurrentBytesReceived = 0;
qint64 mBytesReceived = 0;
QList<QFileInfo> mLocalItems;

QString mProcessRemotePath;
QString mProcessLocalPath;
qint64 mCurrentBytesProcessed = 0;
qint64 mBytesProcessed = 0;
qint64 mBytesTotal = 0;

QWebdav mWebdavConnection;
Expand Down
17 changes: 14 additions & 3 deletions src/qml/QFieldLocalDataPickerScreen.qml
Original file line number Diff line number Diff line change
Expand Up @@ -499,9 +499,10 @@ Page {
leftPadding: Theme.menuItemLeftPadding

text: qsTr("Upload folder to WebDAV server...")
onTriggered:
//platformUtilities.sendCompressedFolderTo(itemMenu.itemPath);
{
onTriggered: {
if (webdavConnectionLoader.item) {
webdavConnectionLoader.item.uploadPath(itemMenu.itemPath);
}
}
}

Expand Down Expand Up @@ -792,6 +793,16 @@ Page {
}
}

onIsUploadingPathChanged: {
if (isUploadingPath) {
busyOverlay.text = qsTr("Uploading WebDAV folder");
busyOverlay.progress = 0;
busyOverlay.state = "visible";
} else {
busyOverlay.state = "hidden";
}
}

onProgressChanged: {
if (isImportingPath || isDownloadingPath) {
busyOverlay.progress = progress;
Expand Down

0 comments on commit df0df4f

Please sign in to comment.