summaryrefslogtreecommitdiffstats
path: root/framework/domain
diff options
context:
space:
mode:
Diffstat (limited to 'framework/domain')
-rw-r--r--framework/domain/CMakeLists.txt3
-rw-r--r--framework/domain/actions/sinkactions.cpp74
-rw-r--r--framework/domain/completer.cpp26
-rw-r--r--framework/domain/completer.h40
-rw-r--r--framework/domain/composercontroller.cpp268
-rw-r--r--framework/domain/composercontroller.h118
-rw-r--r--framework/domain/controller.cpp55
-rw-r--r--framework/domain/controller.h75
-rw-r--r--framework/domain/selector.cpp26
-rw-r--r--framework/domain/selector.h50
10 files changed, 489 insertions, 246 deletions
diff --git a/framework/domain/CMakeLists.txt b/framework/domain/CMakeLists.txt
index 481d5908..bb522416 100644
--- a/framework/domain/CMakeLists.txt
+++ b/framework/domain/CMakeLists.txt
@@ -20,6 +20,9 @@ set(mailplugin_SRCS
20 identitiesmodel.cpp 20 identitiesmodel.cpp
21 recepientautocompletionmodel.cpp 21 recepientautocompletionmodel.cpp
22 settings/accountsettings.cpp 22 settings/accountsettings.cpp
23 selector.cpp
24 completer.cpp
25 controller.cpp
23) 26)
24find_package(KF5 REQUIRED COMPONENTS Package) 27find_package(KF5 REQUIRED COMPONENTS Package)
25 28
diff --git a/framework/domain/actions/sinkactions.cpp b/framework/domain/actions/sinkactions.cpp
index fd791a91..a2d4c02c 100644
--- a/framework/domain/actions/sinkactions.cpp
+++ b/framework/domain/actions/sinkactions.cpp
@@ -16,7 +16,7 @@
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA. 17 02110-1301, USA.
18*/ 18*/
19#include <actions/context.h> 19
20#include <actions/actionhandler.h> 20#include <actions/actionhandler.h>
21 21
22#include <KMime/Message> 22#include <KMime/Message>
@@ -78,12 +78,18 @@ static ActionHandlerHelper deleteHandler("org.kde.kube.actions.delete",
78 } 78 }
79); 79);
80 80
81class FolderContext : public Kube::ContextWrapper {
82 using Kube::ContextWrapper::ContextWrapper;
83 KUBE_CONTEXTWRAPPER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder)
84};
85
81static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize", 86static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize",
82 [](Context *context) -> bool { 87 [](Context *context) -> bool {
83 return true; 88 return true;
84 }, 89 },
85 [](Context *context) { 90 [](Context *context_) {
86 if (auto folder = context->property("folder").value<Folder::Ptr>()) { 91 auto context = FolderContext{*context_};
92 if (auto folder = context.getFolder()) {
87 SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); 93 SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier();
88 auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<Mail::Folder>(QVariant::fromValue(folder->identifier())); 94 auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<Mail::Folder>(QVariant::fromValue(folder->identifier()));
89 scope.setType<ApplicationDomain::Mail>(); 95 scope.setType<ApplicationDomain::Mail>();
@@ -110,65 +116,3 @@ static ActionHandlerHelper sendOutboxHandler("org.kde.kube.actions.sendOutbox",
110 }} 116 }}
111); 117);
112 118
113static ActionHandlerHelper sendMailHandler("org.kde.kube.actions.sendmail",
114 [](Context *context) -> bool {
115 auto accountId = context->property("accountId").value<QByteArray>();
116 return !accountId.isEmpty();
117 },
118 ActionHandlerHelper::JobHandler{[](Context *context) -> KAsync::Job<void> {
119 auto accountId = context->property("accountId").value<QByteArray>();
120 auto message = context->property("message").value<KMime::Message::Ptr>();
121 SinkLog() << "Sending a mail: " << *context;
122
123 Query query;
124 query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport);
125 query.filter<SinkResource::Account>(accountId);
126 return Store::fetchAll<ApplicationDomain::SinkResource>(query)
127 .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> {
128 if (!resources.isEmpty()) {
129 auto resourceId = resources[0]->identifier();
130 SinkTrace() << "Sending message via resource: " << resourceId;
131 Mail mail(resourceId);
132 mail.setBlobProperty("mimeMessage", message->encodedContent());
133 return Store::create(mail);
134 }
135 SinkWarning() << "Failed to find a mailtransport resource";
136 return KAsync::error<void>(0, "Failed to find a MailTransport resource.");
137 });
138 }}
139);
140
141static ActionHandlerHelper saveAsDraft("org.kde.kube.actions.save-as-draft",
142 [](Context *context) -> bool {
143 auto accountId = context->property("accountId").value<QByteArray>();
144 return !accountId.isEmpty();
145 },
146 ActionHandlerHelper::JobHandler([](Context *context) -> KAsync::Job<void> {
147 SinkLog() << "Executing the save-as-draft action";
148 SinkLog() << *context;
149 const auto accountId = context->property("accountId").value<QByteArray>();
150 const auto message = context->property("message").value<KMime::Message::Ptr>();
151 auto existingMail = context->property("existingMail").value<Mail>();
152 if (!message) {
153 SinkWarning() << "Failed to get the mail: " << context->property("mail");
154 return KAsync::error<void>(1, "Failed to get the mail: " + context->property("mail").toString());
155 }
156
157 if (existingMail.identifier().isEmpty()) {
158 Query query;
159 query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts);
160 query.filter<SinkResource::Account>(accountId);
161 return Store::fetchOne<SinkResource>(query)
162 .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> {
163 Mail mail(resource.identifier());
164 mail.setDraft(true);
165 mail.setMimeMessage(message->encodedContent());
166 return Store::create(mail);
167 });
168 } else {
169 SinkWarning() << "Modifying an existing mail" << existingMail.identifier();
170 existingMail.setMimeMessage(message->encodedContent());
171 return Store::modify(existingMail);
172 }
173 })
174);
diff --git a/framework/domain/completer.cpp b/framework/domain/completer.cpp
new file mode 100644
index 00000000..cacb4faa
--- /dev/null
+++ b/framework/domain/completer.cpp
@@ -0,0 +1,26 @@
1/*
2 Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#include "completer.h"
20
21#include <QQmlEngine>
22
23Completer::Completer(QAbstractItemModel *model) : mModel{model}
24{
25 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership);
26}
diff --git a/framework/domain/completer.h b/framework/domain/completer.h
new file mode 100644
index 00000000..a672b809
--- /dev/null
+++ b/framework/domain/completer.h
@@ -0,0 +1,40 @@
1/*
2 Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#pragma once
20
21#include <QAbstractItemModel>
22#include <QString>
23#include <QQmlEngine>
24
25class Completer : public QObject {
26 Q_OBJECT
27 Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT)
28 Q_PROPERTY (QString searchString WRITE setSearchString READ searchString)
29
30public:
31 Completer(QAbstractItemModel *model);
32 QAbstractItemModel *model() { return mModel; }
33 virtual void setSearchString(const QString &s) { mSearchString = s; }
34 QString searchString() const { return mSearchString; }
35
36private:
37 QAbstractItemModel *mModel = nullptr;
38 QString mSearchString;
39};
40
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}
diff --git a/framework/domain/composercontroller.h b/framework/domain/composercontroller.h
index 3e701ed1..c5046306 100644
--- a/framework/domain/composercontroller.h
+++ b/framework/domain/composercontroller.h
@@ -23,110 +23,74 @@
23#include <QString> 23#include <QString>
24#include <QStringList> 24#include <QStringList>
25#include <QVariant> 25#include <QVariant>
26#include <QQmlEngine>
27#include <QAbstractItemModel>
28#include <sink/applicationdomaintype.h> 26#include <sink/applicationdomaintype.h>
27#include <KMime/Message>
29 28
30#include <actions/context.h> 29#include "completer.h"
31#include <actions/action.h> 30#include "selector.h"
31#include "controller.h"
32 32
33namespace KMime { 33inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mailbox &r)
34class Message; 34{
35 return !(l.prettyAddress() == r.prettyAddress());
35} 36}
36 37
37class ComposerContext : public Kube::Context { 38Q_DECLARE_METATYPE(KMime::Types::Mailbox);
38 Q_OBJECT
39 KUBE_CONTEXT_PROPERTY(QString, To, to)
40 KUBE_CONTEXT_PROPERTY(QString, Cc, cc)
41 KUBE_CONTEXT_PROPERTY(QString, Bcc, bcc)
42 KUBE_CONTEXT_PROPERTY(QString, From, from)
43 KUBE_CONTEXT_PROPERTY(QString, Subject, subject)
44 KUBE_CONTEXT_PROPERTY(QString, Body, body)
45};
46
47class Completer : public QObject {
48 Q_OBJECT
49 Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT)
50 Q_PROPERTY (QString searchString WRITE setSearchString READ searchString)
51 39
52public: 40namespace KMime {
53 Completer(QAbstractItemModel *model) : mModel{model} 41class Message;
54 { 42}
55 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership);
56 }
57 QAbstractItemModel *model() { return mModel; }
58 virtual void setSearchString(const QString &s) { mSearchString = s; }
59 QString searchString() const { return mSearchString; }
60
61private:
62 QAbstractItemModel *mModel = nullptr;
63 QString mSearchString;
64};
65 43
66/** 44class ComposerController : public Kube::Controller
67 * Exposes a model and maintains a current index selection. 45{
68 */
69class Selector : public QObject {
70 Q_OBJECT 46 Q_OBJECT
71 Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex)
72 Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT)
73
74public:
75 Selector(QAbstractItemModel *model) : mModel{model}
76 {
77 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership);
78 }
79
80 virtual QAbstractItemModel *model() { return mModel; }
81
82 void setCurrentIndex(int i) {
83 mCurrentIndex = i;
84 Q_ASSERT(mModel);
85 setCurrent(mModel->index(mCurrentIndex, 0));
86 }
87 47
88 int currentIndex() { return mCurrentIndex; } 48 //Interface properties
49 KUBE_CONTROLLER_PROPERTY(QString, To, to)
50 KUBE_CONTROLLER_PROPERTY(QString, Cc, cc)
51 KUBE_CONTROLLER_PROPERTY(QString, Bcc, bcc)
52 KUBE_CONTROLLER_PROPERTY(QString, Subject, subject)
53 KUBE_CONTROLLER_PROPERTY(QString, Body, body)
89 54
90 virtual void setCurrent(const QModelIndex &) = 0; 55 //Set by identitySelector
91private: 56 KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity)
92 QAbstractItemModel *mModel = nullptr; 57 KUBE_CONTROLLER_PROPERTY(QByteArray, AccountId, accountId)
93 int mCurrentIndex = 0;
94};
95 58
96class ComposerController : public QObject 59 //Set by loadMessage
97{ 60 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage)
98 Q_OBJECT 61 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail)
99 Q_PROPERTY (Kube::Context* mailContext READ mailContext CONSTANT)
100 62
101 Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) 63 Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT)
102 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) 64 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT)
65 //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT)
103 66
104 Q_PROPERTY (Kube::Action* sendAction READ sendAction) 67 Q_PROPERTY (Kube::ControllerAction* sendAction READ sendAction CONSTANT)
105 Q_PROPERTY (Kube::Action* saveAsDraftAction READ saveAsDraftAction) 68 Q_PROPERTY (Kube::ControllerAction* saveAsDraftAction READ saveAsDraftAction CONSTANT)
106 69
107public: 70public:
108 explicit ComposerController(QObject *parent = Q_NULLPTR); 71 explicit ComposerController();
109
110 Kube::Context* mailContext();
111 72
112 Completer *recipientCompleter() const; 73 Completer *recipientCompleter() const;
113 Selector *identitySelector() const; 74 Selector *identitySelector() const;
114 75
115 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); 76 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft);
116 77
117 Kube::Action* sendAction(); 78 Kube::ControllerAction* sendAction();
118 Kube::Action* saveAsDraftAction(); 79 Kube::ControllerAction* saveAsDraftAction();
119
120public slots:
121 void clear();
122 80
123signals: 81private slots:
124 void done(); 82 void updateSendAction();
83 void send();
84 void updateSaveAsDraftAction();
85 void saveAsDraft();
125 86
126private: 87private:
127 Kube::ActionHandler *messageHandler();
128 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); 88 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName);
129 void setMessage(const QSharedPointer<KMime::Message> &msg); 89 void setMessage(const QSharedPointer<KMime::Message> &msg);
90 KMime::Message::Ptr assembleMessage();
130 91
131 ComposerContext mContext; 92 QScopedPointer<Kube::ControllerAction> mSendAction;
93 QScopedPointer<Kube::ControllerAction> mSaveAsDraftAction;
94 QScopedPointer<Completer> mRecipientCompleter;
95 QScopedPointer<Selector> mIdentitySelector;
132}; 96};
diff --git a/framework/domain/controller.cpp b/framework/domain/controller.cpp
new file mode 100644
index 00000000..fb971136
--- /dev/null
+++ b/framework/domain/controller.cpp
@@ -0,0 +1,55 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#include "controller.h"
20
21#include <QQmlEngine>
22#include <QMetaProperty>
23
24using namespace Kube;
25
26ControllerAction::ControllerAction()
27 : QObject()
28{
29 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
30}
31
32void ControllerAction::execute()
33{
34 emit triggered();
35}
36
37void Controller::clear()
38{
39 auto meta = metaObject();
40 for (auto i = meta->propertyOffset(); i < meta->propertyCount(); i++) {
41 auto property = meta->property(i);
42 setProperty(property.name(), QVariant());
43 }
44 for (const auto &p : dynamicPropertyNames()) {
45 setProperty(p, QVariant());
46 }
47}
48
49void Controller::run(const KAsync::Job<void> &job)
50{
51 auto jobToExec = job;
52 //TODO handle error
53 //TODO attach a log context to the execution that we can gather from the job?
54 jobToExec.exec();
55}
diff --git a/framework/domain/controller.h b/framework/domain/controller.h
new file mode 100644
index 00000000..c152a588
--- /dev/null
+++ b/framework/domain/controller.h
@@ -0,0 +1,75 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#pragma once
20
21#include <QObject>
22#include <QVariant>
23#include <Async/Async>
24
25#define KUBE_CONTROLLER_PROPERTY(TYPE, NAME, LOWERCASENAME) \
26 public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \
27 Q_SIGNALS: void LOWERCASENAME##Changed(); \
28 private: TYPE m##NAME; \
29 public: \
30 struct NAME { \
31 static constexpr const char *name = #LOWERCASENAME; \
32 typedef TYPE Type; \
33 }; \
34 void set##NAME(const TYPE &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \
35 void clear##NAME() { setProperty(NAME::name, QVariant{}); } \
36 TYPE get##NAME() const { return m##NAME; } \
37
38namespace Kube {
39
40class ControllerAction : public QObject {
41 Q_OBJECT
42 Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged)
43public:
44 ControllerAction();
45 ~ControllerAction() = default;
46
47 Q_INVOKABLE void execute();
48 void setEnabled(bool enabled) { setProperty("enabled", enabled); }
49
50signals:
51 void enabledChanged();
52 void triggered();
53
54private:
55 bool mEnabled = true;
56};
57
58class Controller : public QObject {
59 Q_OBJECT
60public:
61 Controller() = default;
62 virtual ~Controller() = default;
63
64public slots:
65 void clear();
66
67signals:
68 void done();
69 void error();
70
71protected:
72 void run(const KAsync::Job<void> &job);
73};
74
75}
diff --git a/framework/domain/selector.cpp b/framework/domain/selector.cpp
new file mode 100644
index 00000000..ddb23744
--- /dev/null
+++ b/framework/domain/selector.cpp
@@ -0,0 +1,26 @@
1/*
2 Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#include "selector.h"
20
21#include <QQmlEngine>
22
23Selector::Selector(QAbstractItemModel *model) : mModel{model}
24{
25 QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership);
26}
diff --git a/framework/domain/selector.h b/framework/domain/selector.h
new file mode 100644
index 00000000..77c47ba7
--- /dev/null
+++ b/framework/domain/selector.h
@@ -0,0 +1,50 @@
1/*
2 Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#pragma once
20
21#include <QAbstractItemModel>
22#include <QString>
23
24/**
25 * Exposes a model and maintains a current index selection.
26 */
27class Selector : public QObject {
28 Q_OBJECT
29 Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex)
30 Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT)
31
32public:
33 Selector(QAbstractItemModel *model);
34
35 virtual QAbstractItemModel *model() { return mModel; }
36
37 void setCurrentIndex(int i) {
38 mCurrentIndex = i;
39 Q_ASSERT(mModel);
40 setCurrent(mModel->index(mCurrentIndex, 0));
41 }
42
43 int currentIndex() { return mCurrentIndex; }
44
45 virtual void setCurrent(const QModelIndex &) = 0;
46private:
47 QAbstractItemModel *mModel = nullptr;
48 int mCurrentIndex = 0;
49};
50