summaryrefslogtreecommitdiffstats
path: root/framework/domain/composercontroller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/domain/composercontroller.cpp')
-rw-r--r--framework/domain/composercontroller.cpp268
1 files changed, 164 insertions, 104 deletions
diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp
index 57d386c6..4ce356a9 100644
--- a/framework/domain/composercontroller.cpp
+++ b/framework/domain/composercontroller.cpp
@@ -19,9 +19,6 @@
19 19
20 20
21#include "composercontroller.h" 21#include "composercontroller.h"
22#include <actions/context.h>
23#include <actions/action.h>
24#include <actions/actionhandler.h>
25#include <settings/settings.h> 22#include <settings/settings.h>
26#include <KMime/Message> 23#include <KMime/Message>
27#include <KCodecs/KEmailAddress> 24#include <KCodecs/KEmailAddress>
@@ -40,41 +37,9 @@
40 37
41SINK_DEBUG_AREA("composercontroller"); 38SINK_DEBUG_AREA("composercontroller");
42 39
43Q_DECLARE_METATYPE(KMime::Types::Mailbox)
44
45ComposerController::ComposerController(QObject *parent) : QObject(parent)
46{
47 QQmlEngine::setObjectOwnership(&mContext, QQmlEngine::CppOwnership);
48}
49
50
51Kube::Context* ComposerController::mailContext()
52{
53 return &mContext;
54}
55
56class RecipientCompleter : public Completer {
57public:
58 RecipientCompleter() : Completer(new RecipientAutocompletionModel)
59 {
60 }
61
62 void setSearchString(const QString &s) {
63 static_cast<RecipientAutocompletionModel*>(model())->setFilter(s);
64 Completer::setSearchString(s);
65 }
66};
67
68Completer *ComposerController::recipientCompleter() const
69{
70 static auto selector = new RecipientCompleter();
71 QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership);
72 return selector;
73}
74
75class IdentitySelector : public Selector { 40class IdentitySelector : public Selector {
76public: 41public:
77 IdentitySelector(ComposerContext &context) : Selector(new IdentitiesModel), mContext(context) 42 IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller)
78 { 43 {
79 } 44 }
80 45
@@ -88,33 +53,72 @@ public:
88 mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); 53 mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8());
89 SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; 54 SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId;
90 55
91 mContext.setProperty("identity", QVariant::fromValue(mb)); 56 mController.setIdentity(mb);
92 mContext.setProperty("accountId", QVariant::fromValue(currentAccountId)); 57 mController.setAccountId(currentAccountId);
93 } else { 58 } else {
94 SinkWarning() << "No valid identity for index: " << index; 59 SinkWarning() << "No valid identity for index: " << index;
95 mContext.setProperty("identity", QVariant{}); 60 mController.clearIdentity();
96 mContext.setProperty("accountId", QVariant{}); 61 mController.clearAccountId();
97 } 62 }
98 63
99 } 64 }
100private: 65private:
101 ComposerContext &mContext; 66 ComposerController &mController;
102}; 67};
103 68
69class RecipientCompleter : public Completer {
70public:
71 RecipientCompleter() : Completer(new RecipientAutocompletionModel)
72 {
73 }
74
75 void setSearchString(const QString &s) {
76 static_cast<RecipientAutocompletionModel*>(model())->setFilter(s);
77 Completer::setSearchString(s);
78 }
79};
80
81
82ComposerController::ComposerController()
83 : Kube::Controller(),
84 mSendAction{new Kube::ControllerAction},
85 mSaveAsDraftAction{new Kube::ControllerAction},
86 mRecipientCompleter{new RecipientCompleter},
87 mIdentitySelector{new IdentitySelector{*this}}
88{
89 QObject::connect(mSaveAsDraftAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::saveAsDraft);
90 updateSaveAsDraftAction();
91 // mSendAction->monitorProperty<To>();
92 // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{
93 // //validate
94 // });
95 // registerAction<ControllerAction>(&ComposerController::send);
96 // actionDepends<ControllerAction, To, Subject>();
97 // TODO do in constructor
98 QObject::connect(mSendAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::send);
99
100 QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction);
101 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction);
102 updateSendAction();
103}
104
105Completer *ComposerController::recipientCompleter() const
106{
107 return mRecipientCompleter.data();
108}
109
104Selector *ComposerController::identitySelector() const 110Selector *ComposerController::identitySelector() const
105{ 111{
106 static auto selector = new IdentitySelector(*const_cast<ComposerContext*>(&mContext)); 112 return mIdentitySelector.data();
107 QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership);
108 return selector;
109} 113}
110 114
111void ComposerController::setMessage(const KMime::Message::Ptr &msg) 115void ComposerController::setMessage(const KMime::Message::Ptr &msg)
112{ 116{
113 mContext.setTo(msg->to(true)->asUnicodeString()); 117 setTo(msg->to(true)->asUnicodeString());
114 mContext.setCc(msg->cc(true)->asUnicodeString()); 118 setCc(msg->cc(true)->asUnicodeString());
115 mContext.setSubject(msg->subject(true)->asUnicodeString()); 119 setSubject(msg->subject(true)->asUnicodeString());
116 mContext.setBody(msg->body()); 120 setBody(msg->body());
117 mContext.setProperty("existingMessage", QVariant::fromValue(msg)); 121 setExistingMessage(msg);
118} 122}
119 123
120void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) 124void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft)
@@ -122,18 +126,20 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft)
122 Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); 126 Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>());
123 query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); 127 query.request<Sink::ApplicationDomain::Mail::MimeMessage>();
124 Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { 128 Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) {
125 mContext.setProperty("existingMail", QVariant::fromValue(mail)); 129 setExistingMail(mail);
130
131 //TODO this should probably happen as reaction to the property being set.
126 const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); 132 const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage());
127 if (!mailData.isEmpty()) { 133 if (!mailData.isEmpty()) {
128 KMime::Message::Ptr mail(new KMime::Message); 134 KMime::Message::Ptr mail(new KMime::Message);
129 mail->setContent(mailData); 135 mail->setContent(mailData);
130 mail->parse(); 136 mail->parse();
131 if (loadAsDraft) { 137 if (loadAsDraft) {
138 setMessage(mail);
139 } else {
132 auto reply = MailTemplates::reply(mail); 140 auto reply = MailTemplates::reply(mail);
133 //We assume reply 141 //We assume reply
134 setMessage(reply); 142 setMessage(reply);
135 } else {
136 setMessage(mail);
137 } 143 }
138 } else { 144 } else {
139 qWarning() << "Retrieved empty message"; 145 qWarning() << "Retrieved empty message";
@@ -159,68 +165,122 @@ void applyAddresses(const QString &list, std::function<void(const QByteArray &,
159 } 165 }
160} 166}
161 167
162void ComposerController::clear() 168Kube::ControllerAction* ComposerController::saveAsDraftAction()
169{
170 return mSaveAsDraftAction.data();
171}
172
173Kube::ControllerAction* ComposerController::sendAction()
163{ 174{
164 mContext.clear(); 175 return mSendAction.data();
165} 176}
166 177
178KMime::Message::Ptr ComposerController::assembleMessage()
179{
180 auto mail = mExistingMessage;
181 if (!mail) {
182 mail = KMime::Message::Ptr::create();
183 }
184 applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
185 mail->to(true)->addAddress(addrSpec, displayName);
186 recordForAutocompletion(addrSpec, displayName);
187 });
188 applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
189 mail->cc(true)->addAddress(addrSpec, displayName);
190 recordForAutocompletion(addrSpec, displayName);
191 });
192 applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
193 mail->bcc(true)->addAddress(addrSpec, displayName);
194 recordForAutocompletion(addrSpec, displayName);
195 });
196
197 mail->from(true)->addAddress(getIdentity());
198
199 mail->subject(true)->fromUnicodeString(getSubject(), "utf-8");
200 mail->setBody(getBody().toUtf8());
201 mail->assemble();
202 return mail;
203}
167 204
168Kube::ActionHandler *ComposerController::messageHandler() 205void ComposerController::updateSendAction()
169{ 206{
170 return new Kube::ActionHandlerHelper( 207 auto enabled = !getTo().isEmpty() && !getSubject().isEmpty();
171 [](Kube::Context *context) { 208 mSendAction->setEnabled(enabled);
172 auto identity = context->property("identity"); 209}
173 return identity.isValid(); 210
174 }, 211void ComposerController::send()
175 [this](Kube::Context *context) { 212{
176 auto mail = context->property("existingMessage").value<KMime::Message::Ptr>(); 213 // verify<To, Subject>()
177 if (!mail) { 214 // && verify<Subject>();
178 mail = KMime::Message::Ptr::create(); 215 auto message = assembleMessage();
216
217 auto accountId = getAccountId();
218 //SinkLog() << "Sending a mail: " << *this;
219 using namespace Sink;
220 using namespace Sink::ApplicationDomain;
221
222 Query query;
223 query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport);
224 query.filter<SinkResource::Account>(accountId);
225 auto job = Store::fetchAll<ApplicationDomain::SinkResource>(query)
226 .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> {
227 if (!resources.isEmpty()) {
228 auto resourceId = resources[0]->identifier();
229 SinkTrace() << "Sending message via resource: " << resourceId;
230 Mail mail(resourceId);
231 mail.setBlobProperty("mimeMessage", message->encodedContent());
232 return Store::create(mail);
179 } 233 }
180 applyAddresses(context->property(ComposerContext::To::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 234 return KAsync::error<void>(0, "Failed to find a MailTransport resource.");
181 mail->to(true)->addAddress(addrSpec, displayName); 235 });
182 recordForAutocompletion(addrSpec, displayName); 236 run(job);
183 }); 237 job = job.syncThen<void>([&] {
184 applyAddresses(context->property(ComposerContext::Cc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 238 emit done();
185 mail->cc(true)->addAddress(addrSpec, displayName); 239 });
186 recordForAutocompletion(addrSpec, displayName);
187 });
188 applyAddresses(context->property(ComposerContext::Bcc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
189 mail->bcc(true)->addAddress(addrSpec, displayName);
190 recordForAutocompletion(addrSpec, displayName);
191 });
192
193 mail->from(true)->addAddress(context->property("identity").value<KMime::Types::Mailbox>());
194
195 mail->subject(true)->fromUnicodeString(context->property(ComposerContext::Subject::name).toString(), "utf-8");
196 mail->setBody(context->property(ComposerContext::Body::name).toString().toUtf8());
197 mail->assemble();
198
199 context->setProperty("message", QVariant::fromValue(mail));
200 }
201 );
202} 240}
203 241
204Kube::Action* ComposerController::saveAsDraftAction() 242void ComposerController::updateSaveAsDraftAction()
205{ 243{
206 auto action = new Kube::Action("org.kde.kube.actions.save-as-draft", mContext); 244 mSendAction->setEnabled(true);
207 action->addPreHandler(messageHandler());
208 action->addPostHandler(new Kube::ActionHandlerHelper(
209 [this](Kube::Context *context) {
210 emit done();
211 }));
212 return action;
213} 245}
214 246
215Kube::Action* ComposerController::sendAction() 247void ComposerController::saveAsDraft()
216{ 248{
217 auto action = new Kube::Action("org.kde.kube.actions.sendmail", mContext); 249 const auto accountId = getAccountId();
218 // action->addPreHandler(identityHandler()); 250 auto existingMail = getExistingMail();
219 action->addPreHandler(messageHandler()); 251
220 // action->addPreHandler(encryptionHandler()); 252 auto message = assembleMessage();
221 action->addPostHandler(new Kube::ActionHandlerHelper( 253 //FIXME this is something for the validation
222 [this](Kube::Context *context) { 254 if (!message) {
223 emit done(); 255 SinkWarning() << "Failed to get the mail: ";
224 })); 256 return;
225 return action; 257 // return KAsync::error<void>(1, "Failed to get the mail.");
258 }
259
260 using namespace Sink;
261 using namespace Sink::ApplicationDomain;
262
263 auto job = [&] {
264 if (existingMail.identifier().isEmpty()) {
265 Query query;
266 query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts);
267 query.filter<SinkResource::Account>(accountId);
268 return Store::fetchOne<SinkResource>(query)
269 .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> {
270 Mail mail(resource.identifier());
271 mail.setDraft(true);
272 mail.setMimeMessage(message->encodedContent());
273 return Store::create(mail);
274 });
275 } else {
276 SinkWarning() << "Modifying an existing mail" << existingMail.identifier();
277 existingMail.setDraft(true);
278 existingMail.setMimeMessage(message->encodedContent());
279 return Store::modify(existingMail);
280 }
281 }();
282 job = job.syncThen<void>([&] {
283 emit done();
284 });
285 run(job);
226} 286}