diff --git a/autotests/drive/data/file1_copy_request.txt b/autotests/drive/data/file1_copy_request.txt index e92a647..e339415 100644 --- a/autotests/drive/data/file1_copy_request.txt +++ b/autotests/drive/data/file1_copy_request.txt @@ -1,40 +1,40 @@ -POST https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/copy?convert=false&ocr=false&pinned=false&prettyPrint=false +POST https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/copy?convert=false&ocr=false&pinned=false&supportsAllDrives=true&prettyPrint=false Content-Type: application/json { "mimeType": "application/vnd.google-apps.spreadsheet", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUyMzM1MTc5MDUyNA\"", "lastModifyingUserName": "John Doe", "writersCanShare": true, "sharedWithMeDate": "2018-03-14T13:35:25Z", "title": "Super mega secret KDE PIM plans for world domination", "ownerNames": [ "Konqui Dev" ], "id": "abcdefghijklmnopqrstuvwxyz", "parents": [ { "id": "completelynewparentreference", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/parents/completelynewparentreference", "parentLink": "https://www.googleapis.com/drive/v2/files/completelynewparentreference" } ], "shared": true, "editable": false, "kind": "drive#file", "modifiedDate": "2018-04-10T09:16:30Z", "createdDate": "2018-01-05T11:35:58Z", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.spreadsheet", "embedLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/htmlembed?ouid=116901758143213967333", "alternateLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/edit?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz" } diff --git a/autotests/drive/data/file1_create_request.txt b/autotests/drive/data/file1_create_request.txt index b2fe5d8..0a3dcf1 100644 --- a/autotests/drive/data/file1_create_request.txt +++ b/autotests/drive/data/file1_create_request.txt @@ -1,40 +1,40 @@ -POST https://www.googleapis.com/drive/v2/files?convert=false&ocr=false&pinned=false&useContentAsIndexableText=false&supportsAllDrives=true&prettyPrint=false +POST https://www.googleapis.com/drive/v2/files?convert=false&ocr=false&pinned=false&supportsAllDrives=true&useContentAsIndexableText=false&prettyPrint=false Content-Type: application/json { "mimeType": "application/vnd.google-apps.spreadsheet", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUyMzM1MTc5MDUyNA\"", "lastModifyingUserName": "John Doe", "writersCanShare": true, "sharedWithMeDate": "2018-03-14T13:35:25Z", "title": "Super mega secret KDE PIM plans for world domination", "ownerNames": [ "Konqui Dev" ], "id": "abcdefghijklmnopqrstuvwxyz", "parents": [ { "id": "zyxwvutsrqponmlkjihgfedcba", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/parents/zyxwvutsrqponmlkjihgfedcba", "parentLink": "https://www.googleapis.com/drive/v2/files/zyxwvutsrqponmlkjihgfedcba" } ], "shared": true, "editable": false, "kind": "drive#file", "modifiedDate": "2018-04-10T09:16:30Z", "createdDate": "2018-01-05T11:35:58Z", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.spreadsheet", "embedLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/htmlembed?ouid=116901758143213967333", "alternateLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/edit?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz" } diff --git a/autotests/drive/data/file2_copy_request.txt b/autotests/drive/data/file2_copy_request.txt index 5f52016..918b0b5 100644 --- a/autotests/drive/data/file2_copy_request.txt +++ b/autotests/drive/data/file2_copy_request.txt @@ -1,43 +1,43 @@ -POST https://www.googleapis.com/drive/v2/files/abc123def456ghi789/copy?convert=false&ocr=false&pinned=false&prettyPrint=false +POST https://www.googleapis.com/drive/v2/files/abc123def456ghi789/copy?convert=false&ocr=false&pinned=false&supportsAllDrives=true&prettyPrint=false Content-Type: application/json { "mimeType": "image/jpeg", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUxODk3NjU5MzAyMA\"", "lastModifyingUserName": "Konqui Dev", "md5Checksum": "dc74e95b97efce962ac3f31a0cdecccb", "writersCanShare": true, "fileExtension": "JPG", "title": "DSC_1287.JPG", "ownerNames": [ "Konqui Dev" ], "fileSize": 5106501, "id": "abc123def456ghi789", "parents": [ { "id": "someparentfolderid", "selfLink": "https://www.googleapis.com/drive/v2/files/abc123def456ghi789/parents/someparentfolderid", "parentLink": "https://www.googleapis.com/drive/v2/files/someparentfolderid" } ], "shared": true, "kind": "drive#file", "modifiedDate": "2018-02-18T17:56:33Z", "createdDate": "2018-02-18T17:56:33Z", "thumbnailLink": "https://lh3.googleusercontent.com/abc123def456ghi789=s220", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/image/jpeg", "embedLink": "https://drive.google.com/file/d/abc123def456ghi789/preview?usp=drivesdk", "alternateLink": "https://drive.google.com/file/d/abc123def456ghi789/view?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abc123def456ghi789", "downloadUrl": "https://doc-0o-5g-docs.googleusercontent.com/docs/securesc/abc123def456ghi789?e=download&gd=true", "webContentLink": "https://drive.google.com/uc?id=abc123def456ghi789&export=download" } diff --git a/autotests/drive/data/file2_create_request.txt b/autotests/drive/data/file2_create_request.txt index ef1d98e..4200f10 100644 Binary files a/autotests/drive/data/file2_create_request.txt and b/autotests/drive/data/file2_create_request.txt differ diff --git a/src/core/job.cpp b/src/core/job.cpp index 333bee5..e4f1a40 100644 --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -1,540 +1,539 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "job.h" #include "job_p.h" #include "account.h" #include "networkaccessmanagerfactory_p.h" #include "../debug.h" #include "authjob.h" #include #include #include #include #include using namespace KGAPI2; FileLogger *FileLogger::sInstance = nullptr; FileLogger::FileLogger() { if (!qEnvironmentVariableIsSet("KGAPI_SESSION_LOGFILE")) { return; } QString filename = QString::fromLocal8Bit(qgetenv("KGAPI_SESSION_LOGFILE")) + QLatin1Char('.') + QString::number(QCoreApplication::applicationPid()); mFile.reset(new QFile(filename)); if (!mFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(KGAPIDebug) << "Failed to open logging file" << filename << ":" << mFile->errorString(); mFile.reset(); } } FileLogger::~FileLogger() {} FileLogger *FileLogger::self() { if (!sInstance) { sInstance = new FileLogger(); } return sInstance; } void FileLogger::logRequest(const QNetworkRequest &request, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "C: " << request.url().toDisplayString() << "\n"; const auto headers = request.rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << request.rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } void FileLogger::logReply(const QNetworkReply *reply, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "S: " << reply->url().toDisplayString() << "\n"; const auto headers = reply->rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << reply->rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } Job::Private::Private(Job *parent): isRunning(false), error(KGAPI2::NoError), accessManager(nullptr), maxTimeout(0), prettyPrint(false), q(parent) { } void Job::Private::init() { QTimer::singleShot(0, q, [this]() { _k_doStart(); }); accessManager = NetworkAccessManagerFactory::instance()->networkAccessManager(q); connect(accessManager, &QNetworkAccessManager::finished, q, [this](QNetworkReply *reply) { _k_replyReceived(reply); }); dispatchTimer = new QTimer(q); connect(dispatchTimer, &QTimer::timeout, q, [this]() { _k_dispatchTimeout(); }); } QString Job::Private::parseErrorMessage(const QByteArray &json) { QJsonDocument document = QJsonDocument::fromJson(json); if (!document.isNull()) { QVariantMap map = document.toVariant().toMap(); QString message; if (map.contains(QStringLiteral("error"))) { map = map.value(QStringLiteral("error")).toMap(); } if (map.contains(QStringLiteral("message"))) { message.append(map.value(QStringLiteral("message")).toString()); } else { message = QLatin1String(json); } return message; } else { return QLatin1String(json); } } void Job::Private::_k_doStart() { isRunning = true; q->aboutToStart(); q->start(); } void Job::Private::_k_doEmitFinished() { Q_EMIT q->finished(q); } void Job::Private::_k_replyReceived(QNetworkReply* reply) { int replyCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (replyCode == 0) { /* Workaround for a bug (??), when QNetworkReply does not report HTTP/1.1 401 Unauthorized * as an error. */ if (!reply->rawHeaderList().isEmpty()) { QString status = QLatin1String(reply->rawHeaderList().first()); if (status.startsWith(QLatin1String("HTTP/1.1 401"))) replyCode = KGAPI2::Unauthorized; } } const QByteArray rawData = reply->readAll(); qCDebug(KGAPIDebug) << "Received reply from" << reply->url(); qCDebug(KGAPIDebug) << "Status code: " << replyCode; FileLogger::self()->logReply(reply, rawData); switch (replyCode) { case KGAPI2::NoError: case KGAPI2::OK: /** << OK status (fetched, updated, removed) */ case KGAPI2::Created: /** << OK status (created) */ case KGAPI2::NoContent: /** << OK status (removed task using Tasks API) */ break; case KGAPI2::TemporarilyMoved: { /** << Temporarily moved - Google provides a new URL where to send the request */ qCDebug(KGAPIDebug) << "Google says: Temporarily moved to " << reply->header(QNetworkRequest::LocationHeader).toUrl(); QNetworkRequest request = currentRequest.request; request.setUrl(reply->header(QNetworkRequest::LocationHeader).toUrl()); q->enqueueRequest(request, currentRequest.rawData, currentRequest.contentType); return; } case KGAPI2::BadRequest: /** << Bad request - malformed data, API changed, something went wrong... */ qCWarning(KGAPIDebug) << "Bad request, Google replied '" << rawData << "'"; q->setError(KGAPI2::BadRequest); q->setErrorString(tr("Bad request.")); q->emitFinished(); return; case KGAPI2::Unauthorized: /** << Unauthorized - Access token has expired, request a new token */ qCWarning(KGAPIDebug) << "Unauthorized. Access token has expired or is invalid."; q->setError(KGAPI2::Unauthorized); q->setErrorString(tr("Invalid authentication.")); q->emitFinished(); return; case KGAPI2::Forbidden: { qCWarning(KGAPIDebug) << "Requested resource is forbidden."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Forbidden); q->setErrorString(tr("Requested resource is forbidden.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::NotFound: { qCWarning(KGAPIDebug) << "Requested resource does not exist"; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::NotFound); q->setErrorString(tr("Requested resource does not exist.\n\nGoogle replied '%1'").arg(msg)); // don't emit finished() here, we can get 404 when fetching contact photos or so, // in that case 404 is not fatal. Let subclass decide whether to terminate or not. q->handleReply(reply, rawData); if (requestQueue.isEmpty()) { q->emitFinished(); } return; } case KGAPI2::Conflict: { qCWarning(KGAPIDebug) << "Conflict. Remote resource is newer then local."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Conflict); q->setErrorString(tr("Conflict. Remote resource is newer than local.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::Gone: { qCWarning(KGAPIDebug) << "Requested resource does not exist anymore."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Gone); q->setErrorString(tr("Requested resource does not exist anymore.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::InternalError: { qCWarning(KGAPIDebug) << "Internal server error."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::InternalError); q->setErrorString(tr("Internal server error. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::QuotaExceeded: { qCWarning(KGAPIDebug) << "User quota exceeded."; // Extend the interval (if possible) and enqueue the request again int interval = dispatchTimer->interval() / 1000; if (interval == 0) { interval = 1; } else if (interval == 1) { interval = 2; } else if ((interval > maxTimeout) && (maxTimeout > 0)) { const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::QuotaExceeded); q->setErrorString(tr("Maximum quota exceeded. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } else { interval = interval ^ 2; } qCDebug(KGAPIDebug) << "Increasing dispatch interval to" << interval * 1000 << "msecs"; dispatchTimer->setInterval(interval * 1000); const QNetworkRequest request = reply->request(); q->enqueueRequest(request); if (!dispatchTimer->isActive()) { dispatchTimer->start(); } return; } default:{ /** Something went wrong, there's nothing we can do about it */ qCWarning(KGAPIDebug) << "Unknown error" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::UnknownError); q->setErrorString(tr("Unknown error.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } } q->handleReply(reply, rawData); // handleReply has terminated the job, don't continue if (!q->isRunning()) { return; } qCDebug(KGAPIDebug) << requestQueue.length() << "requests in requestQueue."; if (requestQueue.isEmpty()) { q->emitFinished(); return; } if (!dispatchTimer->isActive()) { dispatchTimer->start(); } } void Job::Private::_k_dispatchTimeout() { if (requestQueue.isEmpty()) { dispatchTimer->stop(); return; } const Request r = requestQueue.dequeue(); currentRequest = r; QNetworkRequest authorizedRequest = r.request; if (account) { authorizedRequest.setRawHeader("Authorization", "Bearer " + account->accessToken().toLatin1()); } QUrl url = authorizedRequest.url(); QUrlQuery standardParamQuery(url); if (!fields.isEmpty()) { standardParamQuery.addQueryItem(Job::StandardParams::Fields, fields.join(QStringLiteral(","))); } if (!standardParamQuery.hasQueryItem(Job::StandardParams::PrettyPrint)) { standardParamQuery.addQueryItem(Job::StandardParams::PrettyPrint, prettyPrint ? QStringLiteral("true") : QStringLiteral("false")); } url.setQuery(standardParamQuery); authorizedRequest.setUrl(url); qCDebug(KGAPIDebug) << q << "Dispatching request to" << r.request.url(); FileLogger::self()->logRequest(authorizedRequest, r.rawData); q->dispatchRequest(accessManager, authorizedRequest, r.rawData, r.contentType); if (requestQueue.isEmpty()) { dispatchTimer->stop(); } } /************************* PUBLIC **********************/ const QString Job::StandardParams::PrettyPrint = QStringLiteral("prettyPrint"); const QString Job::StandardParams::Fields = QStringLiteral("fields"); Job::Job(QObject* parent): QObject(parent), d(new Private(this)) { d->init(); } Job::Job(const AccountPtr& account, QObject* parent): QObject(parent), d(new Private(this)) { d->account = account; d->init(); } Job::~Job() { delete d; } void Job::setError(Error error) { d->error = error; } Error Job::error() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called error() on running job, returning nothing"; return KGAPI2::NoError; } return d->error; } void Job::setErrorString(const QString& errorString) { d->errorString = errorString; } QString Job::errorString() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called errorString() on running job, returning nothing"; return QString(); } return d->errorString; } bool Job::isRunning() const { return d->isRunning; } int Job::maxTimeout() const { return d->maxTimeout; } void Job::setMaxTimeout(int maxTimeout) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setMaxTimeout() on running job. Ignoring."; return; } d->maxTimeout = maxTimeout; } AccountPtr Job::account() const { return d->account; } void Job::setAccount(const AccountPtr& account) { if (d->isRunning) { qCWarning(KGAPIDebug) << "Called setAccount() on running job. Ignoring."; return; } d->account = account; } bool Job::prettyPrint() const { return d->prettyPrint; } void Job::setPrettyPrint(bool prettyPrint) { if (d->isRunning) { qCWarning(KGAPIDebug) << "Called setPrettyPrint() on running job. Ignoring."; return; } d->prettyPrint = prettyPrint; } QStringList Job::fields() const { return d->fields; } void Job::setFields(const QStringList &fields) { d->fields = fields; } QString Job::buildSubfields(const QString &field, const QStringList &fields) { return QStringLiteral("%1(%2)").arg(field).arg(fields.join(QStringLiteral(","))); } void Job::restart() { if (d->isRunning) { qCWarning(KGAPIDebug) << "Running job cannot be restarted."; return; } QTimer::singleShot(0, this, [this]() { d->_k_doStart();}); } void Job::emitFinished() { - qCDebug(KGAPIDebug); aboutToFinish(); d->isRunning = false; d->dispatchTimer->stop(); d->requestQueue.clear(); // Emit in next event loop iteration so that the method caller can finish // before user is notified QTimer::singleShot(0, this, [this]() { d->_k_doEmitFinished(); }); } void Job::emitProgress(int processed, int total) { Q_EMIT progress(this, processed, total); } void Job::enqueueRequest(const QNetworkRequest& request, const QByteArray& data, const QString& contentType) { if (!isRunning()) { qCDebug(KGAPIDebug) << "Can't enqueue requests when job is not running."; qCDebug(KGAPIDebug) << "Not enqueueing" << request.url(); return; } qCDebug(KGAPIDebug) << "Queued" << request.url(); Request r_; r_.request = request; r_.rawData = data; r_.contentType = contentType; d->requestQueue.enqueue(r_); if (!d->dispatchTimer->isActive()) { d->dispatchTimer->start(); } } void Job::aboutToFinish() { } void Job::aboutToStart() { d->error = KGAPI2::NoError; d->errorString.clear(); d->currentRequest.contentType.clear(); d->currentRequest.rawData.clear(); d->currentRequest.request = QNetworkRequest(); d->dispatchTimer->setInterval(0); } #include "moc_job.cpp" diff --git a/src/drive/fileabstractdatajob.cpp b/src/drive/fileabstractdatajob.cpp index 42cf286..c0a1790 100644 --- a/src/drive/fileabstractdatajob.cpp +++ b/src/drive/fileabstractdatajob.cpp @@ -1,186 +1,201 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "fileabstractdatajob.h" #include "../debug.h" #include "utils.h" #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN FileAbstractDataJob::Private { public: Private(); bool convert; bool ocr; QString ocrLanguage; bool pinned; QString timedTextLanguage; QString timedTextTrackName; + bool supportsAllDrives; }; FileAbstractDataJob::Private::Private(): convert(false), ocr(false), - pinned(false) + pinned(false), + supportsAllDrives(true) { } FileAbstractDataJob::FileAbstractDataJob(const AccountPtr &account, QObject *parent): Job(account, parent), d(new Private) { } FileAbstractDataJob::~FileAbstractDataJob() { delete d; } bool FileAbstractDataJob::convert() const { return d->convert; } void FileAbstractDataJob::setConvert(bool convert) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify setConvert property when job is running"; return; } d->convert = convert; } bool FileAbstractDataJob::ocr() const { return d->ocr; } void FileAbstractDataJob::setOcr(bool ocr) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify ocr property when job is running"; return; } d->ocr = ocr; } QString FileAbstractDataJob::ocrLanguage() const { return d->ocrLanguage; } void FileAbstractDataJob::setOcrLanguage(const QString &ocrLanguage) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify ocrLanguage property when job is running"; return; } d->ocrLanguage = ocrLanguage; } bool FileAbstractDataJob::pinned() const { return d->pinned; } void FileAbstractDataJob::setPinned(bool pinned) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify pinned property when job is running"; return; } d->pinned = pinned; } QString FileAbstractDataJob::timedTextLanguage() const { return d->timedTextLanguage; } void FileAbstractDataJob::setTimedTextLanguage(const QString &timedTextLanguage) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify timedTextLanguage property when job is running"; return; } d->timedTextLanguage = timedTextLanguage; } QString FileAbstractDataJob::timedTextTrackName() const { return d->timedTextLanguage; } void FileAbstractDataJob::setTimedTextTrackName(const QString &timedTextTrackName) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify timedTextTrackName property when job is running"; return; } d->timedTextTrackName = timedTextTrackName; } +bool FileAbstractDataJob::supportsAllDrives() const +{ + return d->supportsAllDrives; +} + +void FileAbstractDataJob::setSupportsAllDrives(bool supportsAllDrives) +{ + d->supportsAllDrives = supportsAllDrives; +} + QUrl FileAbstractDataJob::updateUrl(QUrl &url) { QUrlQuery query(url); query.removeQueryItem(QStringLiteral("convert")); query.addQueryItem(QStringLiteral("convert"), Utils::bool2Str(d->convert)); query.removeQueryItem(QStringLiteral("ocr")); query.removeQueryItem(QStringLiteral("ocrLanguage")); query.addQueryItem(QStringLiteral("ocr"), Utils::bool2Str(d->ocr)); if (d->ocr && !d->ocrLanguage.isEmpty()) { query.addQueryItem(QStringLiteral("ocrLanguage"), d->ocrLanguage); } query.removeQueryItem(QStringLiteral("pinned")); query.addQueryItem(QStringLiteral("pinned"), Utils::bool2Str(d->pinned)); query.removeQueryItem(QStringLiteral("timedTextLanguage")); if (!d->timedTextLanguage.isEmpty()) { query.addQueryItem(QStringLiteral("timedTextLanguage"), d->timedTextLanguage); } query.removeQueryItem(QStringLiteral("timedTextTrackName")); if (!d->timedTextTrackName.isEmpty()) { query.addQueryItem(QStringLiteral("timedTextTrackName"), d->timedTextTrackName); } - url.setQuery(query); + query.removeQueryItem(QStringLiteral("supportsAllDrives")); + query.addQueryItem(QStringLiteral("supportsAllDrives"), Utils::bool2Str(d->supportsAllDrives)); + + url.setQuery(query); return url; } #include "moc_fileabstractdatajob.cpp" diff --git a/src/drive/fileabstractdatajob.h b/src/drive/fileabstractdatajob.h index 095b955..1e67500 100644 --- a/src/drive/fileabstractdatajob.h +++ b/src/drive/fileabstractdatajob.h @@ -1,138 +1,169 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KGAPI2_DRIVEFILEABSTRACTDATAJOB_H #define KGAPI2_DRIVEFILEABSTRACTDATAJOB_H #include "createjob.h" #include "kgapidrive_export.h" namespace KGAPI2 { namespace Drive { class KGAPIDRIVE_EXPORT FileAbstractDataJob : public KGAPI2::Job { Q_OBJECT /** * Whether to convert this file to the corresponding Google Docs format. * * Default value is false. * * This property can be modified only when the job is not running. */ Q_PROPERTY(bool convert READ convert WRITE setConvert) /** * Whether to attempt OCR on .jpg, .png, .gif, or .pdf uploads. * * Default value is false. * * This property can be modified only when the job is not running. */ Q_PROPERTY(bool ocr READ ocr WRITE setOcr) /** * If ocr is true, hints at the language to use. Valid values are * ISO 639-1 codes. * * This property can be modified only when the job is not running. */ Q_PROPERTY(QString ocrLanguage READ ocrLanguage WRITE setOcrLanguage) /** * Whether to pin the head revision of the new file. * * Default value is false. * * This property can be modified only when the job is not running. */ Q_PROPERTY(bool pinned READ pinned WRITE setPinned) /** * The language of timed text, * * This property can be modified only when the job is not running. */ Q_PROPERTY(QString timedTextLanguage READ timedTextLanguage WRITE setTimedTextLanguage) /** * The timed text track name. * * This property can be modified only when the job is not running. */ Q_PROPERTY(QString timedTextTrackName READ timedTextTrackName WRITE setTimedTextTrackName) + /** + * Sets whether the request supports both My Drives and shared drives. + * + * Set to true by default as LibKGAPI supports Team Drives. + * + * This property can be modified only when the job is not running. + */ + Q_PROPERTY(bool supportsAllDrives + READ supportsAllDrives + WRITE setSupportsAllDrives) + public: explicit FileAbstractDataJob(const AccountPtr &account, QObject *parent = nullptr); ~FileAbstractDataJob() override; bool convert() const; void setConvert(bool convert); bool ocr() const; void setOcr(bool ocr); QString ocrLanguage() const; void setOcrLanguage(const QString &ocrLanguage); bool pinned() const; void setPinned(bool pinned); QString timedTextLanguage() const; void setTimedTextLanguage(const QString &timedTextLanguage); QString timedTextTrackName() const; void setTimedTextTrackName(const QString &timedTextTrackName); + /** + * @brief Whether the request supports both My Drives and shared drives. + * + * Set to true by default as LibKGAPI supports Team Drives. + * + * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications + * are assumed to support shared drives. + */ + KGAPIDRIVE_DEPRECATED bool supportsAllDrives() const; + + /** + * @brief Sets whether the request supports both My Drives and shared drives. + * + * Set to true by default as LibKGAPI supports Team Drives. + * + * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications + * are assumed to support shared drives. + */ + KGAPIDRIVE_DEPRECATED void setSupportsAllDrives(bool supportsAllDrives); + protected: QUrl updateUrl(QUrl &url); private: class Private; Private *const d; friend class Private; }; } // namespace Drive } // namespace KGAPI2 #endif // KGAPI2_DRIVEFILEABSTRACTDATAJOB_H diff --git a/src/drive/fileabstractmodifyjob.cpp b/src/drive/fileabstractmodifyjob.cpp index e966642..49e7153 100644 --- a/src/drive/fileabstractmodifyjob.cpp +++ b/src/drive/fileabstractmodifyjob.cpp @@ -1,136 +1,154 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "fileabstractmodifyjob.h" #include "account.h" #include "driveservice.h" #include "file.h" #include "utils.h" #include #include +#include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN FileAbstractModifyJob::Private { public: Private(FileAbstractModifyJob *parent); void processNext(); QStringList filesIds; + bool supportsAllDrives; + private: FileAbstractModifyJob *q; }; FileAbstractModifyJob::Private::Private(FileAbstractModifyJob *parent): + supportsAllDrives(true), q(parent) { } void FileAbstractModifyJob::Private::processNext() { if (filesIds.isEmpty()) { q->emitFinished(); return; } const QString fileId = filesIds.takeFirst(); - const QUrl url = q->url(fileId); + QUrl url = q->url(fileId); + + QUrlQuery query(url); + query.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); + url.setQuery(query); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentLengthHeader, 0); q->enqueueRequest(request); } FileAbstractModifyJob::FileAbstractModifyJob(const QString &fileId, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << fileId; } FileAbstractModifyJob::FileAbstractModifyJob(const QStringList &filesIds, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << filesIds; } FileAbstractModifyJob::FileAbstractModifyJob(const FilePtr &file, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { d->filesIds << file->id(); } FileAbstractModifyJob::FileAbstractModifyJob(const FilesList &files, const AccountPtr &account, QObject *parent): ModifyJob(account, parent), d(new Private(this)) { for (const FilePtr & file : qAsConst(files)) { d->filesIds << file->id(); } } FileAbstractModifyJob::~FileAbstractModifyJob() { delete d; } void FileAbstractModifyJob::start() { d->processNext(); } +bool FileAbstractModifyJob::supportsAllDrives() const +{ + return d->supportsAllDrives; +} + +void FileAbstractModifyJob::setSupportsAllDrives(bool supportsAllDrives) +{ + d->supportsAllDrives = supportsAllDrives; +} + ObjectsList FileAbstractModifyJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); ObjectsList items; if (ct == KGAPI2::JSON) { items << File::fromJSON(rawData); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); } d->processNext(); return items; } #include "moc_fileabstractmodifyjob.cpp" diff --git a/src/drive/fileabstractmodifyjob.h b/src/drive/fileabstractmodifyjob.h index 35eacb1..95fc76e 100644 --- a/src/drive/fileabstractmodifyjob.h +++ b/src/drive/fileabstractmodifyjob.h @@ -1,70 +1,90 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KGAPI2_DRIVEFILEABSTRACTMODIFYJOB_H #define KGAPI2_DRIVEFILEABSTRACTMODIFYJOB_H #include "modifyjob.h" #include "kgapidrive_export.h" #include namespace KGAPI2 { namespace Drive { class KGAPIDRIVE_EXPORT FileAbstractModifyJob : public KGAPI2::ModifyJob { Q_OBJECT public: explicit FileAbstractModifyJob(const QString &fileId, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractModifyJob(const QStringList &filesIds, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractModifyJob(const FilePtr &file, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractModifyJob(const FilesList &files, const AccountPtr &account, QObject *parent = nullptr); ~FileAbstractModifyJob() override; + /** + * @brief Whether the request supports both My Drives and shared drives. + * + * Set to true by default as LibKGAPI supports Team Drives. + * + * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications + * are assumed to support shared drives. + */ + KGAPIDRIVE_DEPRECATED bool supportsAllDrives() const; + + /** + * @brief Sets whether the request supports both My Drives and shared drives. + * + * Set to true by default as LibKGAPI supports Team Drives. + * + * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications + * are assumed to support shared drives. + */ + KGAPIDRIVE_DEPRECATED void setSupportsAllDrives(bool supportsAllDrives); + protected: void start() override; KGAPI2::ObjectsList handleReplyWithItems(const QNetworkReply *reply, const QByteArray &rawData) override; virtual QUrl url(const QString &fileId) = 0; private: class Private; Private *const d; friend class Private; }; } // namespace Drive } // namespace KGAPI2 #endif // KGAPI2_DRIVEFILEABSTRACTMODIFYJOB_H diff --git a/src/drive/fileabstractuploadjob.cpp b/src/drive/fileabstractuploadjob.cpp index b74497d..86f2ed0 100644 --- a/src/drive/fileabstractuploadjob.cpp +++ b/src/drive/fileabstractuploadjob.cpp @@ -1,366 +1,352 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "fileabstractuploadjob.h" #include "account.h" #include "../debug.h" #include "driveservice.h" #include "file.h" #include "utils.h" #include #include #include #include #include #include #include #include using namespace KGAPI2; using namespace KGAPI2::Drive; class Q_DECL_HIDDEN FileAbstractUploadJob::Private { public: Private(FileAbstractUploadJob *parent); void processNext(); QByteArray buildMultipart(const QString &filePath, const FilePtr &metaData, QString &boundary); QByteArray readFile(const QString &filePath, QString &contentType); void _k_uploadProgress(qint64 bytesSent, qint64 totalBytes); int originalFilesCount; QMap files; QMap uploadedFiles; - bool supportsAllDrives; - bool useContentAsIndexableText; File::SerializationOptions serializationOptions = File::NoOptions; private: FileAbstractUploadJob *const q; }; FileAbstractUploadJob::Private::Private(FileAbstractUploadJob *parent): originalFilesCount(0), - supportsAllDrives(true), useContentAsIndexableText(false), q(parent) { } QByteArray FileAbstractUploadJob::Private::readFile(const QString &filePath, QString &contentType) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qCWarning(KGAPIDebug) << "Failed to access" << filePath; return QByteArray(); } const QMimeDatabase db; const QMimeType mime = db.mimeTypeForFileNameAndData(filePath, &file); contentType = mime.name(); file.reset(); QByteArray output = file.readAll(); file.close(); return output; } QByteArray FileAbstractUploadJob::Private::buildMultipart(const QString &filePath, const FilePtr &metaData, QString &boundary) { QString fileContentType; QByteArray fileContent; fileContent = readFile(filePath, fileContentType); if (fileContent.isEmpty()) { return QByteArray(); } // Wannabe implementation of RFC2387, i.e. multipart/related QByteArray body; QFileInfo finfo(filePath); const QByteArray md5 = QCryptographicHash::hash(finfo.fileName().toLatin1(), QCryptographicHash::Md5); boundary = QString::fromLatin1(md5.toHex()); body += "--" + boundary.toLatin1() + '\n'; body += "Content-Type: application/json; charset=UTF-8\n"; body += '\n'; body += File::toJSON(metaData); body += '\n'; body += '\n'; body += "--" + boundary.toLatin1() + '\n'; body += "Content-Type: " + fileContentType.toLatin1() + '\n'; body += '\n'; body += fileContent; body += '\n'; body += "--" + boundary.toLatin1() + "--"; return body; } void FileAbstractUploadJob::Private::processNext() { if (files.isEmpty()) { q->emitFinished(); return; } const QString filePath = files.cbegin().key(); if (!filePath.startsWith(QLatin1String("?=")) && !QFile::exists(filePath)) { qCWarning(KGAPIDebug) << filePath << "is not a valid file path"; processNext(); return; } const FilePtr metaData = files.take(filePath); QUrl url; if (filePath.startsWith(QLatin1String("?="))) { url = q->createUrl(QString(), metaData); } else { url = q->createUrl(filePath, metaData); } q->updateUrl(url); QUrlQuery query(url); query.addQueryItem(QStringLiteral("useContentAsIndexableText"), Utils::bool2Str(useContentAsIndexableText)); QByteArray rawData; QString contentType; // just to be sure query.removeQueryItem(QStringLiteral("uploadType")); if (metaData.isNull()) { query.addQueryItem(QStringLiteral("uploadType"), QStringLiteral("media")); rawData = readFile(filePath, contentType); if (rawData.isEmpty()) { processNext(); return; } } else if (!filePath.startsWith(QLatin1String("?="))) { query.addQueryItem(QStringLiteral("uploadType"), QStringLiteral("multipart")); QString boundary; rawData = buildMultipart(filePath, metaData, boundary); contentType = QStringLiteral("multipart/related; boundary=%1").arg(boundary); if (rawData.isEmpty()) { processNext(); return; } } else { rawData = File::toJSON(metaData, q->serializationOptions()); contentType = QStringLiteral("application/json"); } - query.addQueryItem(QStringLiteral("supportsAllDrives"), supportsAllDrives ? QStringLiteral("true") : QStringLiteral("false")); url.setQuery(query); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentLengthHeader, rawData.length()); request.setHeader(QNetworkRequest::ContentTypeHeader, contentType); request.setAttribute(QNetworkRequest::User, filePath); q->enqueueRequest(request, rawData, contentType); } void FileAbstractUploadJob::Private::_k_uploadProgress(qint64 bytesSent, qint64 totalBytes) { // Each file consists of 100 units, so if we have two files, one already // uploaded and the other one uploaded from 50%, the values are (150, 200) int processedParts = (originalFilesCount - files.count()) * 100; int currentFileParts = 100.0 * ((qreal) bytesSent / (qreal) totalBytes); q->emitProgress(processedParts + currentFileParts, originalFilesCount * 100); } FileAbstractUploadJob::FileAbstractUploadJob(const FilePtr &metadata, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { d->files.insert(QStringLiteral("?=0"), metadata); d->originalFilesCount = 1; } FileAbstractUploadJob::FileAbstractUploadJob(const FilesList &metadata, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { int i = 0; for (const FilePtr &file : metadata) { d->files.insert(QStringLiteral("?=%1").arg(i), file); ++i; } d->originalFilesCount = d->files.count(); } FileAbstractUploadJob::FileAbstractUploadJob(const QString &filePath, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { d->files.insert(filePath, FilePtr()); d->originalFilesCount = 1; } FileAbstractUploadJob::FileAbstractUploadJob(const QString &filePath, const FilePtr &metaData, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { d->files.insert(filePath, metaData); d->originalFilesCount = 1; } FileAbstractUploadJob::FileAbstractUploadJob(const QStringList &filePaths, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { for (const QString & filePath : filePaths) { d->files.insert(filePath, FilePtr()); } d->originalFilesCount = d->files.count(); } FileAbstractUploadJob::FileAbstractUploadJob(const QMap< QString, FilePtr > &files, const AccountPtr &account, QObject *parent): FileAbstractDataJob(account, parent), d(new Private(this)) { d->files = files; d->originalFilesCount = d->files.count(); } FileAbstractUploadJob::~FileAbstractUploadJob() { delete d; } void FileAbstractUploadJob::setUseContentAsIndexableText(bool useContentAsIndexableText) { if (isRunning()) { qCWarning(KGAPIDebug) << "Can't modify useContentAsIndexableText property when job is running"; return; } d->useContentAsIndexableText = useContentAsIndexableText; } bool FileAbstractUploadJob::useContentAsIndexableText() const { return d->useContentAsIndexableText; } void FileAbstractUploadJob::start() { d->processNext(); } QMap FileAbstractUploadJob::files() const { return d->uploadedFiles; } -bool FileAbstractUploadJob::supportsAllDrives() const -{ - return d->supportsAllDrives; -} - -void FileAbstractUploadJob::setSupportsAllDrives(bool supportsAllDrives) -{ - d->supportsAllDrives = supportsAllDrives; -} - void FileAbstractUploadJob::dispatchRequest(QNetworkAccessManager *accessManager, const QNetworkRequest &request, const QByteArray &data, const QString &contentType) { Q_UNUSED(contentType) QNetworkReply *reply = dispatch(accessManager, request, data); connect(reply, &QNetworkReply::uploadProgress, this, [this](qint64 bytesSent, qint64 totalBytes) {d->_k_uploadProgress(bytesSent, totalBytes); }); } void FileAbstractUploadJob::handleReply(const QNetworkReply *reply, const QByteArray &rawData) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { const QNetworkRequest request = reply->request(); const QString filePath = request.attribute(QNetworkRequest::User).toString(); FilePtr file = File::fromJSON(rawData); d->uploadedFiles.insert(filePath, file); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return; } d->processNext(); } void FileAbstractUploadJob::setSerializationOptions(File::SerializationOptions options) { d->serializationOptions = options; } File::SerializationOptions FileAbstractUploadJob::serializationOptions() const { return d->serializationOptions; } #include "moc_fileabstractuploadjob.cpp" diff --git a/src/drive/fileabstractuploadjob.h b/src/drive/fileabstractuploadjob.h index 639899b..1d88af2 100644 --- a/src/drive/fileabstractuploadjob.h +++ b/src/drive/fileabstractuploadjob.h @@ -1,130 +1,110 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KGAPI2_DRIVEFILEABSTRACTUPLOADJOB_H #define KGAPI2_DRIVEFILEABSTRACTUPLOADJOB_H #include "file.h" #include "fileabstractdatajob.h" #include "kgapidrive_export.h" #include #include namespace KGAPI2 { namespace Drive { class KGAPIDRIVE_EXPORT FileAbstractUploadJob : public KGAPI2::Drive::FileAbstractDataJob { Q_OBJECT /** * Whether to use the content as indexable text. * * Default value is false. * * This property can be modified only when the job is not running. */ Q_PROPERTY(bool useContentAsIndexableText READ useContentAsIndexableText WRITE setUseContentAsIndexableText) public: explicit FileAbstractUploadJob(const FilePtr &metadata, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractUploadJob(const FilesList &metadata, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractUploadJob(const QString &filePath, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractUploadJob(const QString &filePath, const FilePtr &metaData, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractUploadJob(const QStringList &filePaths, const AccountPtr &account, QObject *parent = nullptr); explicit FileAbstractUploadJob(const QMap < QString /* file path */, FilePtr /* metadata */ > &files, const AccountPtr &account, QObject *parent = nullptr); ~FileAbstractUploadJob() override; bool useContentAsIndexableText() const; void setUseContentAsIndexableText(bool useContentAsIndexableText); - /** - * @brief Whether the request supports both My Drives and shared drives. - * - * Set to true by default as LibKGAPI supports Team Drives. - * - * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications - * are assumed to support shared drives. - */ - KGAPIDRIVE_DEPRECATED bool supportsAllDrives() const; - - /** - * @brief Sets whether the request supports both My Drives and shared drives. - * - * Set to true by default as LibKGAPI supports Team Drives. - * - * @deprecated This parameter will only be effective until June 1, 2020. Afterwards all applications - * are assumed to support shared drives. - */ - KGAPIDRIVE_DEPRECATED void setSupportsAllDrives(bool supportsAllDrives); - QMap < QString /* file path */, FilePtr /* metadata */ > files() const; protected: void start() override; void dispatchRequest(QNetworkAccessManager *accessManager, const QNetworkRequest &request, const QByteArray &data, const QString &contentType) override; void handleReply(const QNetworkReply *reply, const QByteArray &rawData) override; virtual QUrl createUrl(const QString &filePath, const FilePtr &metaData) = 0; virtual QNetworkReply *dispatch(QNetworkAccessManager *accessManager, const QNetworkRequest &request, const QByteArray &data) = 0; void setSerializationOptions(File::SerializationOptions options); File::SerializationOptions serializationOptions() const; private: class Private; Private *const d; friend class Private; Q_PRIVATE_SLOT(d, void _k_uploadProgress(qint64 uploadedBytes, qint64 totalBytes)) }; } // namespace Drive } // namespace KGAPI2 #endif // KGAPI2_DRIVEFILEABSTRACTUPLOADJOB_H