diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -44,6 +44,7 @@
configure_file(twitter/contents/code/main.js.in ${CMAKE_CURRENT_BINARY_DIR}/twitter/contents/code/main.js @ONLY)
kpackage_install_package(${CMAKE_CURRENT_BINARY_DIR}/twitter Twitter Purpose)
kaccounts_add_service(${CMAKE_CURRENT_SOURCE_DIR}/twitter-microblog.service.in)
+ add_subdirectory(nextcloud)
endif()
add_subdirectory(kdeconnect)
add_subdirectory(reviewboard)
diff --git a/src/plugins/nextcloud/CMakeLists.txt b/src/plugins/nextcloud/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_definitions(-DTRANSLATION_DOMAIN=\"purpose_nextcloud\")
+
+kaccounts_add_service(${CMAKE_CURRENT_SOURCE_DIR}/nextcloud-upload.service.in)
+
+add_share_plugin(nextcloudplugin nextcloudplugin.cpp nextcloudjob.cpp)
+target_link_libraries(nextcloudplugin KF5::KIOCore KF5::I18n KF5::Purpose KAccounts)
+
+
diff --git a/src/plugins/nextcloud/Messages.sh b/src/plugins/nextcloud/Messages.sh
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/Messages.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+$EXTRACTRC `find . -name \*.rc` `find . -name \*.ui` >> rc.cpp
+$XGETTEXT `find . -not -path \*/tests/\* -name \*.cpp -o -name \*.cc -o -name \*.h` -o $podir/purpose_nextcloud.pot
+rm -f rc.cpp
diff --git a/src/plugins/nextcloud/nextcloud-upload.service.in b/src/plugins/nextcloud/nextcloud-upload.service.in
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloud-upload.service.in
@@ -0,0 +1,8 @@
+
+
+ nextcloud-upload
+ NextCloud Upload
+ owncloud
+ owncloud
+ kaccounts-providers
+
diff --git a/src/plugins/nextcloud/nextcloudjob.h b/src/plugins/nextcloud/nextcloudjob.h
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloudjob.h
@@ -0,0 +1,45 @@
+/*
+ Copyright 2017 Lim Yuen Hoe
+
+ 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) any later version.
+
+ 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 NEXTCLOUDJOB_H
+#define NEXTCLOUDJOB_H
+
+#include
+#include
+#include
+
+class NextcloudJob : public Purpose::Job
+{
+ Q_OBJECT
+ public:
+ NextcloudJob(QObject* parent)
+ : Purpose::Job(parent), m_pendingJobs(0)
+ {}
+ void start() override;
+
+ private Q_SLOTS:
+ void fileUploaded(KJob*);
+ void checkTargetFolder(KJob*);
+
+ private:
+ void checkTargetFile(const QUrl& local, KJob* job);
+ void fileFetched(const QUrl& uploadUrl, KJob*);
+ QUrl createTargetUrl(QString filename, const QUrl baseUrl);
+ QUrl m_davUrl;
+ int m_pendingJobs;
+};
+#endif /* NEXTCLOUDJOB_H */
diff --git a/src/plugins/nextcloud/nextcloudjob.cpp b/src/plugins/nextcloud/nextcloudjob.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloudjob.cpp
@@ -0,0 +1,144 @@
+/*
+ Copyright 2017 Lim Yuen Hoe
+
+ 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) any later version.
+
+ 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 "nextcloudjob.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void NextcloudJob::start()
+{
+ // get all the info we need
+ const QString folder = data().value(QStringLiteral("folder")).toString();
+ const Accounts::AccountId id = data().value(QStringLiteral("accountId")).toInt();
+ Accounts::Account* acc = Accounts::Account::fromId(KAccounts::accountsManager(), id);
+ auto job = new GetCredentialsJob(id, this);
+ bool b = job->exec();
+ if (!b) {
+ qWarning() << "Couldn't fetch credentials";
+ setError(job->error());
+ setErrorText(job->errorText());
+ emitResult();
+ return;
+ }
+ Q_FOREACH(const Accounts::Service &service, acc->services()) {
+ if (service.name() == QStringLiteral("nextcloud-upload")) {
+ acc->selectService(service);
+ }
+ }
+ m_davUrl = QUrl(acc->valueAsString(QStringLiteral("server")) +
+ QStringLiteral("remote.php/webdav/") + folder + QStringLiteral("/"));
+ m_davUrl.setUserName(job->credentialsData().value(QStringLiteral("UserName")).toString());
+ m_davUrl.setPassword(job->credentialsData().value(QStringLiteral("Secret")).toString());
+
+ // first we check that the folder exists
+ KIO::DavJob* davjob = KIO::davPropFind(m_davUrl, QDomDocument(), QStringLiteral("0"), KIO::HideProgressInfo);
+ connect(davjob, &KJob::finished, this, &NextcloudJob::checkTargetFolder);
+}
+
+void NextcloudJob::checkTargetFolder(KJob* j)
+{
+ QString responseString = qobject_cast(j)->response().toString();
+ // TODO: prob a better way to do this
+ if (responseString.contains(QStringLiteral(""))) {
+ const QJsonArray urls = data().value(QStringLiteral("urls")).toArray();
+
+ foreach(const QJsonValue& url, urls) {
+ // before uploading, we try to avoid overwrite by checking for existing file on nextcloud
+ QUrl local = QUrl(url.toString());
+ QUrl uploadTarget = m_davUrl;
+ // disallow giant filenames
+ // TODO: this doesn't currently deal well with say image clipboard data. might see if we can somehow add the right extension.
+ uploadTarget.setPath(uploadTarget.path() + KStringHandler::csqueeze(local.fileName(), 100));
+ KIO::DavJob* davjob = KIO::davPropFind(uploadTarget, QDomDocument(), QStringLiteral("0"), KIO::HideProgressInfo);
+ connect(davjob, &KJob::finished, this, [=](KJob* job) { NextcloudJob::checkTargetFile(local, job); });
+ m_pendingJobs++;
+ }
+
+ } else {
+ qWarning() << "invalid folder";
+ setError(KIO::Error::ERR_CANNOT_ENTER_DIRECTORY);
+ setErrorText(i18n("Invalid folder!"));
+ emitResult();
+ }
+}
+
+void NextcloudJob::checkTargetFile(const QUrl& local, KJob* j)
+{
+ if (j->error()) {
+ setError(j->error());
+ setErrorText(j->errorText());
+ emitResult();
+ return;
+ }
+
+ KIO::DavJob* job = qobject_cast(j);
+ QString responseString = job->response().toString();
+
+ // TODO: better way to do this
+ if (responseString.contains(QStringLiteral("DAV\\Exception\\NotFound"))) {
+ // okay file doesn't exist on nextcloud, we'll fetch the file, then upload it
+ KIO::StoredTransferJob* next = KIO::storedGet(local);
+ QUrl targetUrl = job->url();
+ connect(next, &KJob::finished, this, [=](KJob* jj) { NextcloudJob::fileFetched(targetUrl, jj); });
+ } else {
+ // file already exists! we try successive suggestions until we find a free name
+ QUrl uploadTarget = m_davUrl;
+ uploadTarget.setPath(uploadTarget.path() + KIO::suggestName(m_davUrl, job->url().fileName()));
+ qDebug() << "Trying: " << uploadTarget.toString();
+ KIO::DavJob* davjob = KIO::davPropFind(uploadTarget, QDomDocument(), QStringLiteral("0"), KIO::HideProgressInfo);
+ connect(davjob, &KJob::finished, this, [=](KJob* jj) { NextcloudJob::checkTargetFile(local, jj); });
+ }
+}
+
+void NextcloudJob::fileFetched(const QUrl& uploadUrl, KJob* j)
+{
+ if (j->error()) {
+ setError(j->error());
+ setErrorText(j->errorText());
+ emitResult();
+ return;
+ }
+
+ KIO::StoredTransferJob* job = qobject_cast(j);
+
+ // we fetched our file and we have a place to upload to. Time to upload!
+ KIO::StoredTransferJob *tJob = KIO::storedPut(job->data(), uploadUrl, KIO::HideProgressInfo);
+ connect(tJob, &KJob::result, this, &NextcloudJob::fileUploaded);
+}
+
+void NextcloudJob::fileUploaded(KJob* j)
+{
+ if (j->error()) {
+ setError(j->error());
+ setErrorText(j->errorText());
+ emitResult();
+ return;
+ }
+
+ //KIO::StoredTransferJob *sjob = qobject_cast(j);
+ m_pendingJobs--;
+ if (m_pendingJobs == 0) {
+ setOutput( {{ QStringLiteral("url"), QString() }});
+ emitResult();
+ }
+}
diff --git a/src/plugins/nextcloud/nextcloudplugin.cpp b/src/plugins/nextcloud/nextcloudplugin.cpp
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloudplugin.cpp
@@ -0,0 +1,44 @@
+/*
+ Copyright 2017 Lim Yuen Hoe
+
+ 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) any later version.
+
+ 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
+#include "nextcloudjob.h"
+
+#include
+#include
+
+class NextcloudPlugin : public Purpose::PluginBase
+{
+Q_OBJECT
+public:
+ NextcloudPlugin(QObject* parent, const QVariantList& args)
+ : Purpose::PluginBase(parent)
+ {
+ Q_UNUSED(args);
+ }
+
+ virtual Purpose::Job* createJob() const override
+ {
+ return new NextcloudJob(nullptr);
+ }
+};
+
+K_PLUGIN_FACTORY_WITH_JSON(NextcloudShare, "nextcloudplugin.json", registerPlugin();)
+
+EXPORT_SHARE_VERSION
+
+#include "nextcloudplugin.moc"
diff --git a/src/plugins/nextcloud/nextcloudplugin.json b/src/plugins/nextcloud/nextcloudplugin.json
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloudplugin.json
@@ -0,0 +1,23 @@
+{
+ "KPlugin": {
+ "Authors": [
+ {
+ "Name": "Lim Yuen Hoe",
+ "Name[x-test]": "xxLim Yuen Hoexx"
+ }
+ ],
+ "Category": "Utilities",
+ "Description": "Upload files to Nextcloud",
+ "Icon": "edit-paste",
+ "License": "GPL",
+ "Name": "NextCloud",
+ "Name[x-test]": "xxNextCloudxx"
+ },
+ "X-Purpose-Configuration": [
+ "folder",
+ "accountId"
+ ],
+ "X-Purpose-PluginTypes": [
+ "Export"
+ ]
+}
diff --git a/src/plugins/nextcloud/nextcloudplugin_config.qml b/src/plugins/nextcloud/nextcloudplugin_config.qml
new file mode 100644
--- /dev/null
+++ b/src/plugins/nextcloud/nextcloudplugin_config.qml
@@ -0,0 +1,76 @@
+/*
+ Copyright 2017 Lim Yuen Hoe
+
+ 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) any later version.
+
+ 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 .
+*/
+
+import QtQuick 2.2
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.1
+import org.kde.kquickcontrolsaddons 2.0 as KQCA
+import Ubuntu.OnlineAccounts 0.1 as OA
+
+ColumnLayout
+{
+ id: root
+
+ property alias folder: folderField.text
+ property var accountId
+ property var urls
+ property var mimeType
+
+ function accountChanged()
+ {
+ var valid = accountsCombo.enabled && accountsCombo.currentIndex>=0;
+ if (valid) { root.accountId = serviceModel.get(accountsCombo.currentIndex, "accountId"); }
+ refreshConfigReady();
+ }
+
+ // without manually refreshing, auto-filled values don't seem to activate the "Run" button
+ function refreshConfigReady()
+ {
+ var jobData = configuration.data;
+ jobData['accountId'] = root.accountId;
+ jobData['folder'] = root.folder;
+ configuration.data = jobData;
+ }
+
+ Label { text: i18n("Account:") }
+ RowLayout {
+ Layout.fillWidth: true
+ ComboBox {
+ id: accountsCombo
+
+ Layout.fillWidth: true
+ textRole: "displayName"
+ enabled: count>0
+ model: OA.AccountServiceModel {
+ id: serviceModel
+ serviceType: "nextcloud-upload"
+ }
+ onCurrentIndexChanged: root.accountChanged()
+ Component.onCompleted: root.accountChanged()
+ }
+ Button {
+ iconName: "settings-configure"
+ onClicked: KQCA.KCMShell.open("kcm_kaccounts");
+ }
+ }
+ Label { text: i18n("Upload to folder:") }
+ TextField {
+ id: folderField
+ Layout.fillWidth: true
+ text: "/"
+ }
+}