diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ include (GenerateExportHeader) include (ECMGenerateHeaders) include (ECMAddQch) +include (ECMQtDeclareLoggingCategory) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") @@ -54,7 +55,6 @@ include (CMakePackageConfigHelpers) include (ECMSetupVersion) - # Adding local CMake modules set ( CMAKE_MODULE_PATH diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,10 @@ ) +find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS + KIO + I18n +) add_library ( KF5ActivitiesStats SHARED @@ -148,4 +152,5 @@ DESTINATION ${ECM_MKSPECS_INSTALL_DIR} ) +add_subdirectory( ioslave ) diff --git a/src/ioslave/CMakeLists.txt b/src/ioslave/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/src/ioslave/CMakeLists.txt @@ -0,0 +1,25 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kio5_recentdocumentsactivities\") + + +set( + KActivitiesStats_IOSLAVE_SRS + + recentdocumentsactivities.cpp +) + +ecm_qt_declare_logging_category(KActivitiesStats_IOSLAVE_SRS + HEADER kactivity-ioslave-logsettings.h + IDENTIFIER KACTIVITY_STAT_IOSLAVE_LOG +CATEGORY_NAME kf5.kactivity.stat.ioslave) + +add_library(recentdocumentsactivities MODULE ${KActivitiesStats_IOSLAVE_SRS}) + +target_link_libraries(recentdocumentsactivities + KF5::KIOCore + KF5::I18n + KF5::Activities + KF5::ActivitiesStats) +set_target_properties(recentdocumentsactivities PROPERTIES OUTPUT_NAME "recentdocumentsactivities") +install(TARGETS recentdocumentsactivities DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kio) + +install( FILES recentdocumentsactivities.protocol DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/src/ioslave/recentdocumentsactivities.h b/src/ioslave/recentdocumentsactivities.h new file mode 100644 --- /dev/null +++ b/src/ioslave/recentdocumentsactivities.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Méven Car + * + * 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 RECENTDOCUMENTACTIVITIES_H +#define RECENTDOCUMENTACTIVITIES_H + +#include +#include + +class RecentDocumentActivities : public KIO::ForwardingSlaveBase +{ +public: + RecentDocumentActivities(const QByteArray &pool, const QByteArray &app); + ~RecentDocumentActivities() override; + +protected: + bool rewriteUrl(const QUrl &url, QUrl &newUrl) override; + void listDir(const QUrl &url) override; + void stat(const QUrl& url) override; + void mimetype(const QUrl& url) override; + // void del(const QUrl& url, bool isfile) override; + +private: + + KIO::UDSEntry udsEntryFromResource(const QString& resource); +}; + +#endif diff --git a/src/ioslave/recentdocumentsactivities.cpp b/src/ioslave/recentdocumentsactivities.cpp new file mode 100644 --- /dev/null +++ b/src/ioslave/recentdocumentsactivities.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2019 Méven Car + * + * 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 "kactivity-ioslave-logsettings.h" +#include "recentdocumentsactivities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include + +#include +#include +#include + +namespace KAStats = KActivities::Stats; + +using namespace KAStats; +using namespace KAStats::Terms; + +extern "C" int Q_DECL_EXPORT kdemain(int argc, char **argv) +{ + // necessary to use other kio slaves + QCoreApplication app(argc, argv); + app.setApplicationName(QStringLiteral("kio_recentdocumentsactivities")); + if (argc != 4) { + fprintf(stderr, "Usage: kio_recentdocumentsactivities protocol domain-socket1 domain-socket2\n"); + exit(-1); + } + // start the slave + RecentDocumentActivities slave(argv[2], argv[3]); + slave.dispatchLoop(); + return 0; +} + +bool isRootUrl(const QUrl &url) +{ + const QString path = url.adjusted(QUrl::StripTrailingSlash).path(); + return (path.isEmpty() || path == QLatin1String("/")); +} + + +RecentDocumentActivities::RecentDocumentActivities(const QByteArray& pool, const QByteArray& app): + ForwardingSlaveBase("recentdocumentsactivities", pool, app) +{} + +RecentDocumentActivities::~RecentDocumentActivities() +{} + + +bool RecentDocumentActivities::rewriteUrl(const QUrl& url, QUrl& newUrl) +{ + Q_UNUSED(url) + Q_UNUSED(newUrl); + return false; +} + +ResultModel* runQuery(const QUrl& url) { + auto query = UsedResources + | RecentlyUsedFirst + | Agent::any() + | Limit(30); + + // Parse url query parameter + auto urlQuery = QUrlQuery(url); + + // handles type aka mimetype + if (urlQuery.hasQueryItem(QStringLiteral("type"))) { + auto typeValue = urlQuery.queryItemValue(QStringLiteral("type")); + if (typeValue.contains(QLatin1Char(','))) { + // multiple mimetypes were passed + query = query | Type(typeValue.split(QLatin1Char(','))); + } else { + query = query | Type(typeValue); + } + } + + // limit parameter + if (urlQuery.hasQueryItem(QStringLiteral("limit"))) { + auto limitValue = urlQuery.queryItemValue(QStringLiteral("limit")); + bool parseOk; + auto limitInt = limitValue.toInt(&parseOk); + if (parseOk) { + query = query | Limit(limitInt); + } + } + + // url parameter for exact path match or folders, supports wildcard pattern matching + if (urlQuery.hasQueryItem(QStringLiteral("url"))) { + query = query | Url(urlQuery.queryItemValue(QStringLiteral("url"))); + } else { + query = query | Url::file(); + } + + qDebug(KACTIVITY_STAT_IOSLAVE_LOG) << "running query" << query; + + return new ResultModel(query); +} + +KIO::UDSEntry RecentDocumentActivities::udsEntryFromResource(const QString& resource){ + + // the query only returns files and folders + QUrl resourceUrl = QUrl::fromLocalFile(resource); + + KIO::UDSEntry uds; + KIO::StatJob* job = KIO::stat(resourceUrl, KIO::HideProgressInfo); + + // we do not want to wait for the event loop to delete the job + QScopedPointer sp(job); + job->setAutoDelete(false); + if (job->exec()) { + uds = job->statResult(); + } + uds.fastInsert(KIO::UDSEntry::UDS_URL, resourceUrl.toString()); + return uds; +} + +void RecentDocumentActivities::listDir(const QUrl& url) +{ + if (!isRootUrl(url)) { + error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); + return; + } + + auto model = runQuery(url); + + KIO::UDSEntryList udslist; + udslist.reserve(model->rowCount()); + + for(int r = 0; r < model->rowCount(); ++r) { + QModelIndex index = model->index(r, 0); + QString resource = model->data(index, ResultModel::ResourceRole).toString(); + + udslist << udsEntryFromResource(resource); + } + + listEntries(udslist); + finished(); +} + +void RecentDocumentActivities::stat(const QUrl& url) +{ + qDebug(KACTIVITY_STAT_IOSLAVE_LOG) << "stating" << " " << url; + + if (isRootUrl(url)) { + // + // stat the root path + // + + QString dirName = i18n("Recent Documents"); + KIO::UDSEntry uds; + uds.reserve(5); + uds.fastInsert(KIO::UDSEntry::UDS_NAME, dirName); + uds.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, dirName); + uds.fastInsert(KIO::UDSEntry::UDS_DISPLAY_TYPE, dirName); + uds.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("document-open-recent")); + uds.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); + uds.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); + + statEntry(uds); + finished(); + } else { + auto model = runQuery(url); + if (model->rowCount() != 1) { + error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); + return ; + } + + QModelIndex index = model->index(0, 0); + QString resource = model->data(index, ResultModel::ResourceRole).toString(); + statEntry(udsEntryFromResource(resource)); + + finished(); + } +} + +void RecentDocumentActivities::mimetype(const QUrl& url) +{ + // the root url is always a folder + if (isRootUrl(url)) { + mimeType(QString::fromLatin1("inode/directory")); + finished(); + } + // results are forwarded + else { + ForwardingSlaveBase::mimetype(url); + } +} diff --git a/src/ioslave/recentdocumentsactivities.protocol b/src/ioslave/recentdocumentsactivities.protocol new file mode 100644 --- /dev/null +++ b/src/ioslave/recentdocumentsactivities.protocol @@ -0,0 +1,14 @@ +[Protocol] +X-DocPath=kioslave5/recentdocumentsactivities/index.html +exec=kf5/kio/recentdocumentsactivities +protocol=recentdocumentsactivities +Icon=document-open-recent +input=none +output=filesystem +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group,Link +reading=true +opening=true +writing=true +deleting=true +maxInstances=1 +Class=:local