From 4b1798f0cdf87361869e7cf2b341acacd056c410 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 5 Apr 2017 15:04:00 +0200 Subject: Moved cpp code into src directory --- framework/src/domain/composercontroller.cpp | 307 ++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 framework/src/domain/composercontroller.cpp (limited to 'framework/src/domain/composercontroller.cpp') diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp new file mode 100644 index 00000000..3328d9eb --- /dev/null +++ b/framework/src/domain/composercontroller.cpp @@ -0,0 +1,307 @@ +/* + Copyright (c) 2016 Michael Bohlender + Copyright (c) 2016 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + + +#include "composercontroller.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "identitiesmodel.h" +#include "recepientautocompletionmodel.h" +#include "mailtemplates.h" + +SINK_DEBUG_AREA("composercontroller"); + +class IdentitySelector : public Selector { +public: + IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller) + { + } + + void setCurrent(const QModelIndex &index) Q_DECL_OVERRIDE + { + if (index.isValid()) { + auto currentAccountId = index.data(IdentitiesModel::AccountId).toByteArray(); + + KMime::Types::Mailbox mb; + mb.setName(index.data(IdentitiesModel::Username).toString()); + mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); + SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; + + mController.setIdentity(mb); + mController.setAccountId(currentAccountId); + } else { + SinkWarning() << "No valid identity for index: " << index; + mController.clearIdentity(); + mController.clearAccountId(); + } + + } +private: + ComposerController &mController; +}; + +class RecipientCompleter : public Completer { +public: + RecipientCompleter() : Completer(new RecipientAutocompletionModel) + { + } + + void setSearchString(const QString &s) { + static_cast(model())->setFilter(s); + Completer::setSearchString(s); + } +}; + + +ComposerController::ComposerController() + : Kube::Controller(), + action_send{new Kube::ControllerAction{this, &ComposerController::send}}, + action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, + mRecipientCompleter{new RecipientCompleter}, + mIdentitySelector{new IdentitySelector{*this}} +{ + updateSaveAsDraftAction(); + // mSendAction->monitorProperty(); + // mSendAction->monitorProperty([] (const QString &) -> bool{ + // //validate + // }); + // registerAction(&ComposerController::send); + // actionDepends(); + // TODO do in constructor + + QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); + QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); + QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction); + QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSaveAsDraftAction); + QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction); + QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction); + updateSendAction(); +} + +void ComposerController::clear() +{ + Controller::clear(); + //Reapply account and identity from selection + mIdentitySelector->reapplyCurrentIndex(); +} + +Completer *ComposerController::recipientCompleter() const +{ + return mRecipientCompleter.data(); +} + +Selector *ComposerController::identitySelector() const +{ + return mIdentitySelector.data(); +} + +void ComposerController::setMessage(const KMime::Message::Ptr &msg) +{ + setTo(msg->to(true)->asUnicodeString()); + setCc(msg->cc(true)->asUnicodeString()); + setSubject(msg->subject(true)->asUnicodeString()); + setBody(msg->body()); + setExistingMessage(msg); +} + +void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) +{ + using namespace Sink; + using namespace Sink::ApplicationDomain; + + Query query(*message.value()); + query.request(); + Store::fetchOne(query).then([this, loadAsDraft](const Mail &mail) { + setExistingMail(mail); + + //TODO this should probably happen as reaction to the property being set. + const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); + if (!mailData.isEmpty()) { + KMime::Message::Ptr mail(new KMime::Message); + mail->setContent(mailData); + mail->parse(); + if (loadAsDraft) { + setMessage(mail); + } else { + auto reply = MailTemplates::reply(mail); + //We assume reply + setMessage(reply); + } + } else { + qWarning() << "Retrieved empty message"; + } + }).exec(); +} + +void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName) +{ + if (auto model = static_cast(recipientCompleter()->model())) { + model->addEntry(addrSpec, displayName); + } +} + +void applyAddresses(const QString &list, std::function callback) +{ + for (const auto &to : KEmailAddress::splitAddressList(list)) { + QByteArray displayName; + QByteArray addrSpec; + QByteArray comment; + KEmailAddress::splitAddress(to.toUtf8(), displayName, addrSpec, comment); + callback(addrSpec, displayName); + } +} + +KMime::Message::Ptr ComposerController::assembleMessage() +{ + auto mail = mExistingMessage; + if (!mail) { + mail = KMime::Message::Ptr::create(); + } + applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + mail->to(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + mail->cc(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + mail->bcc(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + + mail->from(true)->addAddress(getIdentity()); + + mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); + mail->setBody(getBody().toUtf8()); + if (!mail->messageID()) { + mail->messageID(true)->generate("org.kde.kube"); + } + if (!mail->date(true)->dateTime().isValid()) { + mail->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); + } + + mail->assemble(); + return mail; +} + +void ComposerController::updateSendAction() +{ + auto enabled = !getTo().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty(); + sendAction()->setEnabled(enabled); +} + +void ComposerController::send() +{ + // verify() + // && verify(); + auto message = assembleMessage(); + + auto accountId = getAccountId(); + //SinkLog() << "Sending a mail: " << *this; + using namespace Sink; + using namespace Sink::ApplicationDomain; + + Q_ASSERT(!accountId.isEmpty()); + Query query; + query.containsFilter(ResourceCapabilities::Mail::transport); + query.filter(accountId); + auto job = Store::fetchAll(query) + .then([=](const QList &resources) { + if (!resources.isEmpty()) { + auto resourceId = resources[0]->identifier(); + SinkLog() << "Sending message via resource: " << resourceId; + Mail mail(resourceId); + mail.setMimeMessage(message->encodedContent()); + return Store::create(mail) + .then([=] { + //Trigger a sync, but don't wait for it. + Store::synchronize(Sink::SyncScope{}.resourceFilter(resourceId)).exec(); + }); + } + SinkWarning() << "Failed to find a mailtransport resource"; + return KAsync::error(0, "Failed to find a MailTransport resource."); + }) + .then([&] (const KAsync::Error &error) { + SinkLog() << "Message was sent: "; + emit done(); + }); + run(job); +} + +void ComposerController::updateSaveAsDraftAction() +{ + bool enabled = !getAccountId().isEmpty(); + sendAction()->setEnabled(enabled); +} + +void ComposerController::saveAsDraft() +{ + SinkLog() << "Save as draft"; + const auto accountId = getAccountId(); + auto existingMail = getExistingMail(); + + auto message = assembleMessage(); + //FIXME this is something for the validation + if (!message) { + SinkWarning() << "Failed to get the mail: "; + return; + } + + using namespace Sink; + using namespace Sink::ApplicationDomain; + + auto job = [&] { + if (existingMail.identifier().isEmpty()) { + SinkLog() << "Creating a new draft" << existingMail.identifier(); + Query query; + query.containsFilter(ResourceCapabilities::Mail::drafts); + query.filter(accountId); + return Store::fetchOne(query) + .then([=](const SinkResource &resource) { + Mail mail(resource.identifier()); + mail.setDraft(true); + mail.setMimeMessage(message->encodedContent()); + return Store::create(mail); + }) + .onError([] (const KAsync::Error &error) { + SinkWarning() << "Error while creating draft: " << error.errorMessage; + }); + } else { + SinkLog() << "Modifying an existing mail" << existingMail.identifier(); + existingMail.setDraft(true); + existingMail.setMimeMessage(message->encodedContent()); + return Store::modify(existingMail); + } + }(); + job = job.then([&] (const KAsync::Error &) { + emit done(); + }); + run(job); +} -- cgit v1.2.3