diff --git a/agents/unifiedmailboxagent/CMakeLists.txt b/agents/unifiedmailboxagent/CMakeLists.txt
index 8a21518b9..1680f57b8 100644
--- a/agents/unifiedmailboxagent/CMakeLists.txt
+++ b/agents/unifiedmailboxagent/CMakeLists.txt
@@ -1,49 +1,51 @@
add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_unifiedmailbox_agent\")
set(CMAKE_CXX_STANDARD 14)
if(BUILD_TESTING)
add_subdirectory(autotests)
endif()
set(unifiedmailbox_agent_SRCS
unifiedmailbox.cpp
unifiedmailboxagent.cpp
unifiedmailboxmanager.cpp
unifiedmailboxeditor.cpp
settingsdialog.cpp
mailkernel.cpp
)
ecm_qt_declare_logging_category(unifiedmailbox_agent_SRCS HEADER unifiedmailboxagent_debug.h IDENTIFIER agent_log CATEGORY_NAME org.kde.pim.unifiedmailboxagent)
kconfig_add_kcfg_files(unifiedmailbox_agent_SRCS
settings.kcfgc
)
+qt5_add_dbus_adaptor(unifiedmailbox_agent_SRCS org.freedesktop.Akonadi.UnifiedMailboxAgent.xml unifiedmailboxagent.h UnifiedMailboxAgent)
add_executable(akonadi_unifiedmailbox_agent ${unifiedmailbox_agent_SRCS})
target_link_libraries(akonadi_unifiedmailbox_agent
KF5::AkonadiAgentBase
KF5::AkonadiMime
KF5::AkonadiWidgets
KF5::Mime
KF5::I18n
KF5::IdentityManagement
KF5::WidgetsAddons
KF5::IconThemes
KF5::ItemModels
KF5::MailCommon
+ KF5::DBusAddons
)
if( APPLE )
set_target_properties(akonadi_unifiedmailbox_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${kmail_SOURCE_DIR}/agents/Info.plist.template)
set_target_properties(akonadi_unifiedmailbox_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.KF5::UnifiedMailbox")
set_target_properties(akonadi_unifiedmailbox_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE PIM Unified Mailbox")
endif ()
install(TARGETS akonadi_unifiedmailbox_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} )
install(FILES unifiedmailboxagent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents")
diff --git a/agents/unifiedmailboxagent/org.freedesktop.Akonadi.UnifiedMailboxAgent.xml b/agents/unifiedmailboxagent/org.freedesktop.Akonadi.UnifiedMailboxAgent.xml
new file mode 100644
index 000000000..3db29beef
--- /dev/null
+++ b/agents/unifiedmailboxagent/org.freedesktop.Akonadi.UnifiedMailboxAgent.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/agents/unifiedmailboxagent/settings.kcfg b/agents/unifiedmailboxagent/settings.kcfg
index 45f8fb87a..cf85f8ca3 100644
--- a/agents/unifiedmailboxagent/settings.kcfg
+++ b/agents/unifiedmailboxagent/settings.kcfg
@@ -1,12 +1,15 @@
-
+
false
-
+
+
+ false
+
diff --git a/agents/unifiedmailboxagent/unifiedmailboxagent.cpp b/agents/unifiedmailboxagent/unifiedmailboxagent.cpp
index bfdf9e6ad..a20c7d5cf 100644
--- a/agents/unifiedmailboxagent/unifiedmailboxagent.cpp
+++ b/agents/unifiedmailboxagent/unifiedmailboxagent.cpp
@@ -1,239 +1,292 @@
/*
Copyright (C) 2018 Daniel Vrátil
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "unifiedmailboxagent.h"
#include "unifiedmailbox.h"
#include "unifiedmailboxagent_debug.h"
+#include "unifiedmailboxagentadaptor.h"
#include "settingsdialog.h"
#include "settings.h"
#include "common.h"
#include
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
#include
UnifiedMailboxAgent::UnifiedMailboxAgent(const QString &id)
: Akonadi::ResourceBase(id)
, mBoxManager(config())
{
setAgentName(i18n("Unified Mailboxes"));
+ new UnifiedMailboxAgentAdaptor(this);
+ KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/UnifiedMailboxAgent"), this, QDBusConnection::ExportAdaptors);
+ const auto service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Resource, identifier());
+ KDBusConnectionPool::threadConnection().registerService(service);
+
connect(&mBoxManager, &UnifiedMailboxManager::updateBox,
this, [this](const UnifiedMailbox *box) {
if (!box->collectionId()) {
qCWarning(agent_log) << "MailboxManager wants us to update Box but does not have its CollectionId!?";
return;
}
// Schedule collection sync for the box
synchronizeCollection(box->collectionId().value());
});
auto &ifs = changeRecorder()->itemFetchScope();
ifs.setAncestorRetrieval(Akonadi::ItemFetchScope::None);
ifs.setCacheOnly(true);
ifs.fetchFullPayload(false);
- QTimer::singleShot(0, this, [this]() {
- qCDebug(agent_log) << "delayed init";
-
- fixSpecialCollections();
- mBoxManager.loadBoxes([this]() {
- // boxes loaded, let's sync up
- synchronize();
- });
- });
+ if (Settings::self()->enabled()) {
+ QTimer::singleShot(0, this, &UnifiedMailboxAgent::delayedInit);
+ }
}
void UnifiedMailboxAgent::configure(WId windowId)
{
QPointer agent(this);
if (SettingsDialog(config(), mBoxManager, windowId).exec() && agent) {
mBoxManager.saveBoxes();
synchronize();
Q_EMIT configurationDialogAccepted();
} else {
mBoxManager.loadBoxes();
}
}
+void UnifiedMailboxAgent::delayedInit()
+{
+ qCDebug(agent_log) << "delayed init";
+
+ fixSpecialCollections();
+ mBoxManager.loadBoxes([this]() {
+ // boxes loaded, let's sync up
+ synchronize();
+ });
+}
+
+
+bool UnifiedMailboxAgent::enabledAgent() const
+{
+ return Settings::self()->enabled();
+}
+
+void UnifiedMailboxAgent::setEnableAgent(bool enabled)
+{
+ if (enabled != Settings::self()->enabled()) {
+ Settings::self()->setEnabled(enabled);
+ Settings::self()->save();
+ if (!enabled) {
+ setOnline(false);
+ auto fetch = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this);
+ fetch->fetchScope().setResource(identifier());
+ connect(fetch, &Akonadi::CollectionFetchJob::collectionsReceived,
+ this, [this](const Akonadi::Collection::List &cols) {
+ for (const auto &col : cols) {
+ new Akonadi::CollectionDeleteJob(col, this);
+ }
+ });
+ } else {
+ setOnline(true);
+ delayedInit();
+ }
+ }
+}
+
+
void UnifiedMailboxAgent::retrieveCollections()
{
+ if (!Settings::self()->enabled()) {
+ collectionsRetrieved({});
+ return;
+ }
+
Akonadi::Collection::List collections;
Akonadi::Collection topLevel;
topLevel.setName(identifier());
topLevel.setRemoteId(identifier());
topLevel.setParentCollection(Akonadi::Collection::root());
topLevel.setContentMimeTypes({Akonadi::Collection::mimeType()});
topLevel.setRights(Akonadi::Collection::ReadOnly);
auto displayAttr = topLevel.attribute(Akonadi::Collection::AddIfMissing);
displayAttr->setDisplayName(i18n("Unified Mailboxes"));
displayAttr->setActiveIconName(QStringLiteral("globe"));
collections.push_back(topLevel);
for (const auto &boxIt : mBoxManager) {
const auto &box = boxIt.second;
Akonadi::Collection col;
col.setName(box->id());
col.setRemoteId(box->id());
col.setParentCollection(topLevel);
col.setContentMimeTypes({Common::MailMimeType});
col.setRights(Akonadi::Collection::CanChangeItem | Akonadi::Collection::CanDeleteItem);
col.setVirtual(true);
auto displayAttr = col.attribute(Akonadi::Collection::AddIfMissing);
displayAttr->setDisplayName(box->name());
displayAttr->setIconName(box->icon());
collections.push_back(std::move(col));
}
collectionsRetrieved(std::move(collections));
// Add mapping between boxes and collections
mBoxManager.discoverBoxCollections();
}
void UnifiedMailboxAgent::retrieveItems(const Akonadi::Collection &c)
{
+ if (!Settings::self()->enabled()) {
+ itemsRetrieved({});
+ return;
+ }
+
// First check that we have all Items from all source collections
Q_EMIT status(Running, i18n("Synchronizing unified mailbox %1", c.displayName()));
const auto unifiedBox = mBoxManager.unifiedMailboxFromCollection(c);
if (!unifiedBox) {
qCWarning(agent_log) << "Failed to retrieve box ID for collection " << c.id();
itemsRetrievedIncremental({}, {}); // fake incremental retrieval
return;
}
const auto lastSeenEvent = QDateTime::fromSecsSinceEpoch(c.remoteRevision().toLongLong());
const auto sources = unifiedBox->sourceCollections();
for (auto source : sources) {
auto fetch = new Akonadi::ItemFetchJob(Akonadi::Collection(source), this);
fetch->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches);
fetch->fetchScope().setFetchVirtualReferences(true);
fetch->fetchScope().setCacheOnly(true);
connect(fetch, &Akonadi::ItemFetchJob::itemsReceived,
this, [this, c](const Akonadi::Item::List &items) {
Akonadi::Item::List toLink;
std::copy_if(items.cbegin(), items.cend(), std::back_inserter(toLink),
[&c](const Akonadi::Item &item) {
return !item.virtualReferences().contains(c);
});
if (!toLink.isEmpty()) {
new Akonadi::LinkJob(c, toLink, this);
}
});
}
auto fetch = new Akonadi::ItemFetchJob(c, this);
fetch->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches);
fetch->fetchScope().setCacheOnly(true);
fetch->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
connect(fetch, &Akonadi::ItemFetchJob::itemsReceived,
this, [this, unifiedBox, c](const Akonadi::Item::List &items) {
Akonadi::Item::List toUnlink;
std::copy_if(items.cbegin(), items.cend(), std::back_inserter(toUnlink),
[&unifiedBox](const Akonadi::Item &item) {
return !unifiedBox->sourceCollections().contains(item.storageCollectionId());
});
if (!toUnlink.isEmpty()) {
new Akonadi::UnlinkJob(c, toUnlink, this);
}
});
connect(fetch, &Akonadi::ItemFetchJob::result,
this, [this]() {
itemsRetrievedIncremental({}, {}); // fake incremental retrieval
});
}
bool UnifiedMailboxAgent::retrieveItem(const Akonadi::Item &item, const QSet &parts)
{
// This method should never be called by Akonadi
Q_UNUSED(parts);
qCWarning(agent_log) << "retrieveItem() for item" << item.id() << "called but we can't own any items! This is a bug in Akonadi";
return false;
}
void UnifiedMailboxAgent::fixSpecialCollection(const QString &colId, Akonadi::SpecialMailCollections::Type type)
{
if (colId.isEmpty()) {
return;
}
const auto id = colId.toLongLong();
// SpecialMailCollection requires the Collection to have a Resource set as well, so
// we have to retrieve it first.
connect(new Akonadi::CollectionFetchJob(Akonadi::Collection(id), Akonadi::CollectionFetchJob::Base, this),
&Akonadi::CollectionFetchJob::collectionsReceived,
this, [type](const Akonadi::Collection::List &cols) {
if (cols.count() != 1) {
qCWarning(agent_log) << "Identity special collection retrieval did not find a valid collection";
return;
}
Akonadi::SpecialMailCollections::self()->registerCollection(type, cols.first());
});
}
void UnifiedMailboxAgent::fixSpecialCollections()
{
// This is a tiny hack to assign proper SpecialCollectionAttribute to special collections
// assigned trough Identities. This should happen automatically in KMail when user changes
// the special collections on the identity page, but until recent master (2018-07-24) this
// wasn't the case and there's no automatic migration, so we need to fix up manually here.
if (Settings::self()->fixedSpecialCollections()) {
return;
}
qCDebug(agent_log) << "Fixing special collections assigned from Identities";
for (const auto &identity : *KIdentityManagement::IdentityManager::self()) {
if (!identity.disabledFcc()) {
fixSpecialCollection(identity.fcc(), Akonadi::SpecialMailCollections::SentMail);
}
fixSpecialCollection(identity.drafts(), Akonadi::SpecialMailCollections::Drafts);
fixSpecialCollection(identity.templates(), Akonadi::SpecialMailCollections::Templates);
}
Settings::self()->setFixedSpecialCollections(true);
}
AKONADI_RESOURCE_MAIN(UnifiedMailboxAgent)
diff --git a/agents/unifiedmailboxagent/unifiedmailboxagent.h b/agents/unifiedmailboxagent/unifiedmailboxagent.h
index 62f229fbb..5774389c1 100644
--- a/agents/unifiedmailboxagent/unifiedmailboxagent.h
+++ b/agents/unifiedmailboxagent/unifiedmailboxagent.h
@@ -1,59 +1,63 @@
/*
Copyright (C) 2018 Daniel Vrátil
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef UNIFIEDMAILBOXAGENT_H
#define UNIFIEDMAILBOXAGENT_H
#include
#include
#include "unifiedmailboxmanager.h"
#include
#include
/* Despite its name, this is actually a Resource, but it acts as an Agent: it
* listens to notifications about Items that belong to other resources and acts
* on them.
* The only reason this agent has to implement ResourceBase is to be able to own
* the virtual unified collections into which content of other collections is
* linked.
*/
class UnifiedMailboxAgent : public Akonadi::ResourceBase
{
Q_OBJECT
public:
explicit UnifiedMailboxAgent(const QString &id);
~UnifiedMailboxAgent() override = default;
void configure(WId windowId) override;
+ void setEnableAgent(bool enable);
+ bool enabledAgent() const;
+
void retrieveCollections() override;
void retrieveItems(const Akonadi::Collection &collection) override;
bool retrieveItem(const Akonadi::Item &item, const QSet &parts) override;
-
private:
+ void delayedInit();
+
void fixSpecialCollections();
void fixSpecialCollection(const QString &colId, Akonadi::SpecialMailCollections::Type type);
UnifiedMailboxManager mBoxManager;
};
#endif
diff --git a/agents/unifiedmailboxagent/unifiedmailboxmanager.cpp b/agents/unifiedmailboxagent/unifiedmailboxmanager.cpp
index f3f959c4a..2aa8854bf 100644
--- a/agents/unifiedmailboxagent/unifiedmailboxmanager.cpp
+++ b/agents/unifiedmailboxagent/unifiedmailboxmanager.cpp
@@ -1,428 +1,433 @@
/*
Copyright (C) 2018 Daniel Vrátil
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "unifiedmailboxmanager.h"
#include "unifiedmailbox.h"
#include "unifiedmailboxagent_debug.h"
#include "utils.h"
#include "common.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
namespace {
/**
* A little RAII helper to make sure changeProcessed() and replayNext() gets
* called on the ChangeRecorder whenever we are done with handling a change.
*/
class ReplayNextOnExit
{
public:
ReplayNextOnExit(Akonadi::ChangeRecorder &recorder)
: mRecorder(recorder)
{}
~ReplayNextOnExit()
{
mRecorder.changeProcessed();
mRecorder.replayNext();
}
private:
Akonadi::ChangeRecorder &mRecorder;
};
}
// static
bool UnifiedMailboxManager::isUnifiedMailbox(const Akonadi::Collection &col)
{
#ifdef UNIT_TESTS
return col.parentCollection().name() == Common::AgentIdentifier;
#else
return col.resource() == Common::AgentIdentifier;
#endif
}
UnifiedMailboxManager::UnifiedMailboxManager(KSharedConfigPtr config, QObject* parent)
: QObject(parent)
, mConfig(std::move(config))
{
mMonitor.setObjectName(QStringLiteral("UnifiedMailboxChangeRecorder"));
mMonitor.setConfig(&mMonitorSettings);
mMonitor.setChangeRecordingEnabled(true);
mMonitor.setTypeMonitored(Akonadi::Monitor::Items);
mMonitor.setTypeMonitored(Akonadi::Monitor::Collections);
mMonitor.itemFetchScope().setCacheOnly(true);
mMonitor.itemFetchScope().setFetchRemoteIdentification(false);
mMonitor.itemFetchScope().setFetchModificationTime(false);
mMonitor.collectionFetchScope().fetchAttribute();
connect(&mMonitor, &Akonadi::Monitor::itemAdded,
this, [this](const Akonadi::Item &item, const Akonadi::Collection &collection) {
ReplayNextOnExit replayNext(mMonitor);
qCDebug(agent_log) << "Item" << item.id() << "added to collection" << collection.id();
const auto box = unifiedMailboxForSource(collection.id());
if (!box) {
qCWarning(agent_log) << "Failed to find unified mailbox for source collection " << collection.id();
return;
}
if (!box->collectionId()) {
qCWarning(agent_log) << "Missing box->collection mapping for unified mailbox" << box->id();
return;
}
new Akonadi::LinkJob(Akonadi::Collection{box->collectionId().value()}, {item}, this);
});
connect(&mMonitor, &Akonadi::Monitor::itemsRemoved,
this, [this](const Akonadi::Item::List &items) {
ReplayNextOnExit replayNext(mMonitor);
// Monitor did the heavy lifting for us and already figured out that
// we only monitor the source collection of the Items and translated
// it into REMOVE change.
// This relies on Akonadi never mixing Items from different sources or
// destination during batch-moves.
const auto parentId = items.first().parentCollection().id();
const auto box = unifiedMailboxForSource(parentId);
if (!box) {
qCWarning(agent_log) << "Received Remove notification for Items belonging to" << parentId << "which we don't monitor";
return;
}
if (!box->collectionId()) {
qCWarning(agent_log) << "Missing box->collection mapping for unified mailbox" << box->id();
return;
}
new Akonadi::UnlinkJob(Akonadi::Collection{box->collectionId().value()}, items, this);
});
connect(&mMonitor, &Akonadi::Monitor::itemsMoved,
this, [this](const Akonadi::Item::List &items, const Akonadi::Collection &srcCollection,
const Akonadi::Collection &dstCollection) {
ReplayNextOnExit replayNext(mMonitor);
if (const auto srcBox = unifiedMailboxForSource(srcCollection.id())) {
// Move source collection was our source, unlink the Item from a box
new Akonadi::UnlinkJob(Akonadi::Collection{srcBox->collectionId().value()}, items, this);
}
if (const auto dstBox = unifiedMailboxForSource(dstCollection.id())) {
// Move destination collection is our source, link the Item into a box
new Akonadi::LinkJob(Akonadi::Collection{dstBox->collectionId().value()}, items, this);
}
});
connect(&mMonitor, &Akonadi::Monitor::collectionRemoved,
this, [this](const Akonadi::Collection &col) {
ReplayNextOnExit replayNext(mMonitor);
if (auto box = unifiedMailboxForSource(col.id())) {
box->removeSourceCollection(col.id());
mMonitor.setCollectionMonitored(col, false);
if (box->sourceCollections().isEmpty()) {
removeBox(box->id());
}
saveBoxes();
// No need to resync the box collection, the linked Items got removed by Akonadi
} else {
qCWarning(agent_log) << "Received notification about removal of Collection" << col.id() << "which we don't monitor";
}
});
connect(&mMonitor, qOverload &>(&Akonadi::Monitor::collectionChanged),
this, [this](const Akonadi::Collection &col, const QSet &parts) {
ReplayNextOnExit replayNext(mMonitor);
+
qCDebug(agent_log) << "Collection changed:" << parts;
if (!parts.contains(Akonadi::SpecialCollectionAttribute().type())) {
return;
}
if (col.hasAttribute()) {
const auto srcBox = unregisterSpecialSourceCollection(col.id());
const auto dstBox = registerSpecialSourceCollection(col);
if (srcBox == dstBox) {
return;
}
saveBoxes();
if (srcBox && srcBox->sourceCollections().isEmpty()) {
removeBox(srcBox->id());
return;
}
if (srcBox) {
Q_EMIT updateBox(srcBox);
}
if (dstBox) {
Q_EMIT updateBox(dstBox);
}
} else {
if (const auto box = unregisterSpecialSourceCollection(col.id())) {
saveBoxes();
if (box->sourceCollections().isEmpty()) {
removeBox(box->id());
} else {
Q_EMIT updateBox(box);
}
}
}
});
}
UnifiedMailboxManager::~UnifiedMailboxManager()
{
}
Akonadi::ChangeRecorder &UnifiedMailboxManager::changeRecorder()
{
return mMonitor;
}
void UnifiedMailboxManager::loadBoxes(FinishedCallback &&finishedCb)
{
+ qCDebug(agent_log) << "loading boxes";
const auto group = mConfig->group("UnifiedMailboxes");
const auto boxGroups = group.groupList();
for (const auto &boxGroupName : boxGroups) {
const auto boxGroup = group.group(boxGroupName);
auto box = std::make_unique();
box->load(boxGroup);
insertBox(std::move(box));
}
const auto cb = [this, finishedCb = std::move(finishedCb)]() {
+ qCDebug(agent_log) << "Finished callback: enabling change recorder";
// Only now start processing changes from change recorder
connect(&mMonitor, &Akonadi::ChangeRecorder::changesAdded, &mMonitor, &Akonadi::ChangeRecorder::replayNext, Qt::QueuedConnection);
// And start replaying any potentially pending notification
- mMonitor.replayNext();
+ QTimer::singleShot(0, &mMonitor, &Akonadi::ChangeRecorder::replayNext);
if (finishedCb) {
finishedCb();
}
};
+ qCDebug(agent_log) << "Loaded" << mMailboxes.size() << "boxes from config";
+
if (mMailboxes.empty()) {
createDefaultBoxes(std::move(cb));
} else {
discoverBoxCollections(std::move(cb));
}
}
void UnifiedMailboxManager::saveBoxes()
{
auto group = mConfig->group("UnifiedMailboxes");
const auto currentGroups = group.groupList();
for (const auto &groupName : currentGroups) {
group.deleteGroup(groupName);
}
for (const auto &boxIt : mMailboxes) {
auto boxGroup = group.group(boxIt.second->id());
boxIt.second->save(boxGroup);
}
mConfig->sync();
}
void UnifiedMailboxManager::insertBox(std::unique_ptr box)
{
auto it = mMailboxes.emplace(std::make_pair(box->id(), std::move(box)));
it.first->second->attachManager(this);
}
void UnifiedMailboxManager::removeBox(const QString &id)
{
auto box = std::find_if(mMailboxes.begin(), mMailboxes.end(),
[&id](const std::pair> &box) {
return box.second->id() == id;
});
if (box == mMailboxes.end()) {
return;
}
box->second->attachManager(nullptr);
mMailboxes.erase(box);
}
UnifiedMailbox *UnifiedMailboxManager::unifiedMailboxForSource(qint64 source) const
{
const auto box = mSourceToBoxMap.find(source);
if (box == mSourceToBoxMap.cend()) {
return {};
}
return box->second;
}
UnifiedMailbox * UnifiedMailboxManager::unifiedMailboxFromCollection(const Akonadi::Collection &col) const
{
if (!isUnifiedMailbox(col)) {
return nullptr;
}
const auto box = mMailboxes.find(col.name());
if (box == mMailboxes.cend()) {
return {};
}
return box->second.get();
}
void UnifiedMailboxManager::createDefaultBoxes(FinishedCallback &&finishedCb)
{
// First build empty boxes
auto inbox = std::make_unique();
inbox->attachManager(this);
inbox->setId(Common::InboxBoxId);
inbox->setName(i18n("Inbox"));
inbox->setIcon(QStringLiteral("mail-folder-inbox"));
insertBox(std::move(inbox));
auto sent = std::make_unique();
sent->attachManager(this);
sent->setId(Common::SentBoxId);
sent->setName(i18n("Sent"));
sent->setIcon(QStringLiteral("mail-folder-sent"));
insertBox(std::move(sent));
auto drafts = std::make_unique();
drafts->attachManager(this);
drafts->setId(Common::DraftsBoxId);
drafts->setName(i18n("Drafts"));
drafts->setIcon(QStringLiteral("document-properties"));
insertBox(std::move(drafts));
auto list = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this);
list->fetchScope().fetchAttribute();
list->fetchScope().setContentMimeTypes({QStringLiteral("message/rfc822")});
#ifdef UNIT_TESTS
list->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::Parent);
#else
list->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::None);
#endif
connect(list, &Akonadi::CollectionFetchJob::collectionsReceived,
this, [this](const Akonadi::Collection::List &list) {
for (const auto &col : list) {
if (isUnifiedMailbox(col)) {
continue;
}
try {
switch (Akonadi::SpecialMailCollections::self()->specialCollectionType(col)) {
case Akonadi::SpecialMailCollections::Inbox:
mMailboxes.at(Common::InboxBoxId)->addSourceCollection(col.id());
break;
case Akonadi::SpecialMailCollections::SentMail:
mMailboxes.at(Common::SentBoxId)->addSourceCollection(col.id());
break;
case Akonadi::SpecialMailCollections::Drafts:
mMailboxes.at(Common::DraftsBoxId)->addSourceCollection(col.id());
break;
default:
continue;
}
} catch (const std::out_of_range &) {
qCWarning(agent_log) << "Failed to find a special unified mailbox for source collection" << col.id();
continue;
}
}
});
- connect(list, &Akonadi::CollectionFetchJob::finished,
+ connect(list, &Akonadi::CollectionFetchJob::result,
this, [this, finishedCb = std::move(finishedCb)]() {
saveBoxes();
if (finishedCb) {
finishedCb();
}
});
}
void UnifiedMailboxManager::discoverBoxCollections(FinishedCallback &&finishedCb)
{
auto list = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this);
#ifdef UNIT_TESTS
list->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::Parent);
#else
list->fetchScope().setResource(Common::AgentIdentifier);
#endif
connect(list, &Akonadi::CollectionFetchJob::collectionsReceived,
this, [this](const Akonadi::Collection::List &list) {
for (const auto &col : list) {
if (!isUnifiedMailbox(col) || col.parentCollection() == Akonadi::Collection::root()) {
continue;
}
mMailboxes.at(col.name())->setCollectionId(col.id());
}
});
if (finishedCb) {
- connect(list, &Akonadi::CollectionFetchJob::finished,
- this, finishedCb);
+ connect(list, &Akonadi::CollectionFetchJob::result, this, finishedCb);
}
}
const UnifiedMailbox *UnifiedMailboxManager::registerSpecialSourceCollection(const Akonadi::Collection& col)
{
// This is slightly awkward, wold be better if we could use SpecialMailCollections,
// but it also relies on Monitor internally, so there's a possible race condition
// between our ChangeRecorder and SpecialMailCollections' Monitor
auto attr = col.attribute();
Q_ASSERT(attr);
if (!attr) {
return {};
}
decltype(mMailboxes)::iterator box;
if (attr->collectionType() == Common::SpecialCollectionInbox) {
box = mMailboxes.find(Common::InboxBoxId);
} else if (attr->collectionType() == Common::SpecialCollectionSentMail) {
box = mMailboxes.find(Common::SentBoxId);
} else if (attr->collectionType() == Common::SpecialCollectionDrafts) {
box = mMailboxes.find(Common::DraftsBoxId);
}
if (box == mMailboxes.end()) {
return {};
}
box->second->addSourceCollection(col.id());
return box->second.get();
}
const UnifiedMailbox *UnifiedMailboxManager::unregisterSpecialSourceCollection(qint64 colId)
{
auto box = unifiedMailboxForSource(colId);
if (!box) {
return {};
}
if (!box->isSpecial()) {
qDebug() << colId << "does not belong to a special unified box" << box->id();
return {};
}
box->removeSourceCollection(colId);
return box;
}
diff --git a/src/configuredialog/configureplugins/configurepluginslistwidget.cpp b/src/configuredialog/configureplugins/configurepluginslistwidget.cpp
index c3b1929a9..27e8a7115 100644
--- a/src/configuredialog/configureplugins/configurepluginslistwidget.cpp
+++ b/src/configuredialog/configureplugins/configurepluginslistwidget.cpp
@@ -1,357 +1,359 @@
/*
Copyright (C) 2016-2018 Montel Laurent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
This program 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
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "configurepluginslistwidget.h"
#include "kmail_debug.h"
#include "util.h"
#include "../../plugininterface/kmailplugininterface.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
namespace {
QString pluginEditorGroupName()
{
return QStringLiteral("plugineditorgroupname");
}
QString viewerPluginGroupName()
{
return QStringLiteral("viewerplugingroupname");
}
QString pluginEditorCheckBeforeGroupName()
{
return QStringLiteral("plugineditorcheckbeforegroupname");
}
QString pluginEditorInitGroupName()
{
return QStringLiteral("plugineditorinitgroupname");
}
QString pluginEditorConvertTextGroupName()
{
return QStringLiteral("plugineditorconvertTextgroupname");
}
QString kmailPluginToolsGroupName()
{
return QStringLiteral("kmailplugintoolsgroupname");
}
QString networkUrlInterceptorGroupName()
{
return QStringLiteral("networkurlinterceptorgroupname");
}
QString headerStyleGroupName()
{
return QStringLiteral("headerstylegroupname");
}
QString agentAkonadiGroupName()
{
return QStringLiteral("agentakonadigroupname");
}
}
ConfigurePluginsListWidget::ConfigurePluginsListWidget(QWidget *parent)
: PimCommon::ConfigurePluginsListWidget(parent)
{
connect(this, &ConfigurePluginsListWidget::configureClicked, this, &ConfigurePluginsListWidget::slotConfigureClicked);
}
ConfigurePluginsListWidget::~ConfigurePluginsListWidget()
{
}
void ConfigurePluginsListWidget::save()
{
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageComposer::PluginEditorManager::self()->configGroupName(),
MessageComposer::PluginEditorManager::self()->configPrefixSettingKey(),
mPluginEditorItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageViewer::ViewerPluginManager::self()->configGroupName(),
MessageViewer::ViewerPluginManager::self()->configPrefixSettingKey(),
mPluginMessageViewerItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageComposer::PluginEditorInitManager::self()->configGroupName(),
MessageComposer::PluginEditorInitManager::self()->configPrefixSettingKey(),
mPluginEditorInitItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageComposer::PluginEditorCheckBeforeSendManager::self()->configGroupName(),
MessageComposer::PluginEditorCheckBeforeSendManager::self()->configPrefixSettingKey(),
mPluginCheckBeforeSendItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(KMailPluginInterface::self()->configGroupName(),
KMailPluginInterface::self()->configPrefixSettingKey(),
mPluginGenericItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->configGroupName(),
WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->configPrefixSettingKey(),
mPluginWebEngineItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageViewer::HeaderStylePluginManager::self()->configGroupName(),
MessageViewer::HeaderStylePluginManager::self()->configPrefixSettingKey(),
mPluginHeaderStyleItems);
PimCommon::ConfigurePluginsListWidget::savePlugins(MessageComposer::PluginEditorConvertTextManager::self()->configGroupName(),
MessageComposer::PluginEditorConvertTextManager::self()->configPrefixSettingKey(),
mPluginConvertTextItems);
saveAkonadiAgent();
}
void ConfigurePluginsListWidget::saveAkonadiAgent()
{
for (PluginItem *item : qAsConst(mAgentPluginsItems)) {
for (const PimCommon::PluginUtilData &data : qAsConst(mPluginUtilDataList)) {
if (item->mIdentifier == data.mIdentifier) {
changeAgentActiveState(data.mExtraInfo.at(0), data.mExtraInfo.at(1), item->checkState(0) == Qt::Checked);
break;
}
}
}
}
void ConfigurePluginsListWidget::doLoadFromGlobalSettings()
{
initialize();
}
void ConfigurePluginsListWidget::doResetToDefaultsOther()
{
changeState(mPluginEditorItems);
changeState(mPluginMessageViewerItems);
changeState(mPluginCheckBeforeSendItems);
changeState(mPluginGenericItems);
changeState(mPluginWebEngineItems);
changeState(mPluginHeaderStyleItems);
changeState(mAgentPluginsItems);
changeState(mPluginEditorInitItems);
changeState(mPluginConvertTextItems);
}
void ConfigurePluginsListWidget::initialize()
{
mListWidget->clear();
//Load CheckBeforeSend
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageComposer::PluginEditorCheckBeforeSendManager::self()->pluginsDataList(),
i18n("Check Before Send Plugins"),
MessageComposer::PluginEditorCheckBeforeSendManager::self()->configGroupName(),
MessageComposer::PluginEditorCheckBeforeSendManager::self()->configPrefixSettingKey(),
mPluginCheckBeforeSendItems,
pluginEditorCheckBeforeGroupName());
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageComposer::PluginEditorInitManager::self()->pluginsDataList(),
i18n("Composer Plugins"),
MessageComposer::PluginEditorInitManager::self()->configGroupName(),
MessageComposer::PluginEditorInitManager::self()->configPrefixSettingKey(),
mPluginEditorInitItems,
pluginEditorInitGroupName());
//Load generic plugins
//Necessary to initialize pluging when we load it outside kmail
KMailPluginInterface::self()->initializePlugins();
PimCommon::ConfigurePluginsListWidget::fillTopItems(KMailPluginInterface::self()->pluginsDataList(),
i18n("Tools Plugins"),
KMailPluginInterface::self()->configGroupName(),
KMailPluginInterface::self()->configPrefixSettingKey(),
mPluginGenericItems,
kmailPluginToolsGroupName());
//Load plugin editor
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageComposer::PluginEditorManager::self()->pluginsDataList(),
i18n("Editor Plugins"),
MessageComposer::PluginEditorManager::self()->configGroupName(),
MessageComposer::PluginEditorManager::self()->configPrefixSettingKey(),
mPluginEditorItems,
pluginEditorGroupName());
//Load messageviewer plugin
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageViewer::ViewerPluginManager::self()->pluginsDataList(),
i18n("Message Viewer"),
MessageViewer::ViewerPluginManager::self()->configGroupName(),
MessageViewer::ViewerPluginManager::self()->configPrefixSettingKey(),
mPluginMessageViewerItems,
viewerPluginGroupName());
//Load webengineplugin
PimCommon::ConfigurePluginsListWidget::fillTopItems(WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->pluginsDataList(),
i18n("Webengine Plugins"),
WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->configGroupName(),
WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->configPrefixSettingKey(),
mPluginWebEngineItems,
networkUrlInterceptorGroupName());
//Load headerstyle
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageViewer::HeaderStylePluginManager::self()->pluginsDataList(),
i18n("Header Style Plugins"),
MessageViewer::HeaderStylePluginManager::self()->configGroupName(),
MessageViewer::HeaderStylePluginManager::self()->configPrefixSettingKey(),
mPluginHeaderStyleItems,
headerStyleGroupName());
// //Load headerstyle
PimCommon::ConfigurePluginsListWidget::fillTopItems(MessageComposer::PluginEditorConvertTextManager::self()->pluginsDataList(),
i18n("Convertor Text Plugins"),
MessageComposer::PluginEditorConvertTextManager::self()->configGroupName(),
MessageComposer::PluginEditorConvertTextManager::self()->configPrefixSettingKey(),
mPluginConvertTextItems,
pluginEditorConvertTextGroupName());
//Load Agent Plugin
initializeAgentPlugins();
mListWidget->expandAll();
}
void ConfigurePluginsListWidget::initializeAgentPlugins()
{
mPluginUtilDataList.clear();
mPluginUtilDataList.reserve(4);
mPluginUtilDataList << createAgentPluginData(QStringLiteral("akonadi_sendlater_agent"), QStringLiteral("/SendLaterAgent"));
mPluginUtilDataList << createAgentPluginData(QStringLiteral("akonadi_archivemail_agent"), QStringLiteral("/ArchiveMailAgent"));
mPluginUtilDataList << createAgentPluginData(QStringLiteral("akonadi_newmailnotifier_agent"), QStringLiteral("/NewMailNotifierAgent"));
mPluginUtilDataList << createAgentPluginData(QStringLiteral("akonadi_followupreminder_agent"), QStringLiteral("/FollowUpReminder"));
+ mPluginUtilDataList << createAgentPluginData(QStringLiteral("akonadi_unifiedmailbox_agent"), QStringLiteral("/UnifiedMailboxAgent"));
PimCommon::ConfigurePluginsListWidget::fillTopItems(mPluginUtilDataList,
i18n("Akonadi Agents"),
QString(),
QString(),
mAgentPluginsItems,
agentAkonadiGroupName());
}
PimCommon::PluginUtilData ConfigurePluginsListWidget::createAgentPluginData(const QString &agentIdentifier, const QString &path)
{
PimCommon::PluginUtilData data;
data.mEnableByDefault = true;
data.mHasConfigureDialog = true;
const Akonadi::AgentType::List lstAgent = Akonadi::AgentManager::self()->types();
for (const Akonadi::AgentType &type : lstAgent) {
if (type.identifier() == agentIdentifier) {
data.mExtraInfo << agentIdentifier;
data.mExtraInfo << path;
const bool enabled = agentActivateState(agentIdentifier, path);
data.mEnableByDefault = enabled;
data.mName = type.name();
data.mDescription = type.description();
data.mIdentifier = type.identifier();
break;
}
}
return data;
}
bool ConfigurePluginsListWidget::agentActivateState(const QString &agentIdentifier, const QString &pathName)
{
const QString service
= Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, agentIdentifier);
QDBusInterface interface(service, pathName);
if (interface.isValid()) {
QDBusReply enabled = interface.call(QStringLiteral("enabledAgent"));
if (enabled.isValid()) {
return enabled;
} else {
qCDebug(KMAIL_LOG) << agentIdentifier << "doesn't have enabledAgent function";
return false;
}
} else {
qCDebug(KMAIL_LOG) << agentIdentifier << "does not exist when trying to activate the agent state";
}
return false;
}
void ConfigurePluginsListWidget::changeAgentActiveState(const QString &agentIdentifier, const QString &path, bool enable)
{
if (!agentIdentifier.isEmpty() && !path.isEmpty()) {
const QString service
= Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, agentIdentifier);
QDBusInterface interface(service, path);
if (interface.isValid()) {
interface.call(QStringLiteral("setEnableAgent"), enable);
} else {
qCDebug(KMAIL_LOG) << agentIdentifier << "does not exist when trying to change the agent active state";
}
}
}
void ConfigurePluginsListWidget::slotConfigureClicked(const QString &configureGroupName, const QString &identifier)
{
if (!configureGroupName.isEmpty() && !identifier.isEmpty()) {
if (configureGroupName == headerStyleGroupName()) {
MessageViewer::HeaderStylePlugin *plugin = MessageViewer::HeaderStylePluginManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == networkUrlInterceptorGroupName()) {
WebEngineViewer::NetworkPluginUrlInterceptor *plugin = WebEngineViewer::NetworkUrlInterceptorPluginManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == viewerPluginGroupName()) {
MessageViewer::ViewerPlugin *plugin = MessageViewer::ViewerPluginManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == pluginEditorGroupName()) {
MessageComposer::PluginEditor *plugin = MessageComposer::PluginEditorManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == kmailPluginToolsGroupName()) {
PimCommon::GenericPlugin *plugin = KMailPluginInterface::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == pluginEditorInitGroupName()) {
MessageComposer::PluginEditorInit *plugin = MessageComposer::PluginEditorInitManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == pluginEditorCheckBeforeGroupName()) {
MessageComposer::PluginEditorCheckBeforeSend *plugin = MessageComposer::PluginEditorCheckBeforeSendManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == pluginEditorConvertTextGroupName()) {
MessageComposer::PluginEditorConvertText *plugin = MessageComposer::PluginEditorConvertTextManager::self()->pluginFromIdentifier(identifier);
plugin->showConfigureDialog(this);
} else if (configureGroupName == agentAkonadiGroupName()) {
for (const PimCommon::PluginUtilData &data : qAsConst(mPluginUtilDataList)) {
if (data.mIdentifier == identifier) {
const QString service = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, data.mExtraInfo.at(0));
QDBusInterface interface(service, data.mExtraInfo.at(1));
if (interface.isValid()) {
interface.call(QStringLiteral("showConfigureDialog"), static_cast(winId()));
} else {
qCDebug(KMAIL_LOG) << " interface does not exist when trying to configure the plugin";
}
break;
}
}
} else {
qCWarning(KMAIL_LOG) << "Unknown configureGroupName" << configureGroupName;
}
}
}
void ConfigurePluginsListWidget::defaults()
{
doResetToDefaultsOther();
}