summaryrefslogtreecommitdiffstats
path: root/framework/src
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-12-04 00:02:58 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-12-04 10:19:23 +0100
commit243f18fed2ee9b8f2778e83ee4fbe2b3275b0370 (patch)
treeac60f61ad7aaf9c997410ae2f7dcab1a128e157a /framework/src
parent8455e38d9269cfeeb33cbf123688e19186c8be4c (diff)
downloadkube-243f18fed2ee9b8f2778e83ee4fbe2b3275b0370.tar.gz
kube-243f18fed2ee9b8f2778e83ee4fbe2b3275b0370.zip
Subcontrollers for list properties
Diffstat (limited to 'framework/src')
-rw-r--r--framework/src/domain/composercontroller.cpp426
-rw-r--r--framework/src/domain/composercontroller.h57
-rw-r--r--framework/src/domain/controller.cpp108
-rw-r--r--framework/src/domain/controller.h68
-rw-r--r--framework/src/frameworkplugin.cpp2
5 files changed, 309 insertions, 352 deletions
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp
index f1e84019..d09dcd79 100644
--- a/framework/src/domain/composercontroller.cpp
+++ b/framework/src/domain/composercontroller.cpp
@@ -41,7 +41,11 @@
41#include "mime/mailtemplates.h" 41#include "mime/mailtemplates.h"
42#include "mime/mailcrypto.h" 42#include "mime/mailcrypto.h"
43 43
44Q_DECLARE_METATYPE(GpgME::Key); 44std::vector<GpgME::Key> &operator+=(std::vector<GpgME::Key> &list, const std::vector<GpgME::Key> &add)
45{
46 list.insert(std::end(list), std::begin(add), std::end(add));
47 return list;
48}
45 49
46class IdentitySelector : public Selector { 50class IdentitySelector : public Selector {
47public: 51public:
@@ -93,14 +97,6 @@ public:
93 } 97 }
94}; 98};
95 99
96static void traverse(const QStandardItemModel *model, const std::function<void(QStandardItem *item)> &f)
97{
98 auto root = model->invisibleRootItem();
99 for (int row = 0; row < root->rowCount(); row++) {
100 f(root->child(row, 0));
101 }
102}
103
104template<typename T> 100template<typename T>
105void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> continuation) 101void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> continuation)
106{ 102{
@@ -116,33 +112,22 @@ void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> co
116 watcher->setFuture(future); 112 watcher->setFuture(future);
117} 113}
118 114
119class AddresseeModel : public QStandardItemModel 115class AddresseeController : public Kube::ListPropertyController
120{ 116{
121public: 117public:
122 AddresseeModel()
123 :QStandardItemModel()
124 {
125 setItemRoleNames({{ComposerController::AddresseeNameRole, "addresseeName"},
126 {ComposerController::KeyFoundRole, "keyFound"},
127 {ComposerController::KeyMissingRole, "keyMissing"},
128 {ComposerController::KeyRole, "key"}});
129 }
130 118
131 void add(const QString &addressee) 119 QSet<QByteArray> mMissingKeys;
120 AddresseeController()
121 : Kube::ListPropertyController{{"name", "keyFound", "key"}}
132 { 122 {
133 auto item = new QStandardItem; 123 QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) {
134 item->setText(addressee); 124 findKey(id, map.value("name").toString());
135 item->setData(QVariant::fromValue(addressee), ComposerController::AddresseeNameRole); 125 });
136 item->setData(false, ComposerController::KeyFoundRole);
137 item->setData(mCryptoEnabled, ComposerController::KeyMissingRole);
138 appendRow(QList<QStandardItem*>() << item);
139 if (mCryptoEnabled) {
140 findKey(addressee);
141 }
142 } 126 }
143 127
144 void findKey(const QString &addressee) 128 void findKey(const QByteArray &id, const QString &addressee)
145 { 129 {
130 mMissingKeys << id;
146 KMime::Types::Mailbox mb; 131 KMime::Types::Mailbox mb;
147 mb.fromUnicodeString(addressee); 132 mb.fromUnicodeString(addressee);
148 133
@@ -150,126 +135,88 @@ public:
150 asyncRun<std::vector<GpgME::Key>>(this, [mb] { 135 asyncRun<std::vector<GpgME::Key>>(this, [mb] {
151 return MailCrypto::findKeys(QStringList{} << mb.address(), false, false, MailCrypto::OPENPGP); 136 return MailCrypto::findKeys(QStringList{} << mb.address(), false, false, MailCrypto::OPENPGP);
152 }, 137 },
153 [this, addressee](const std::vector<GpgME::Key> &keys) { 138 [this, addressee, id](const std::vector<GpgME::Key> &keys) {
154 if (!keys.empty()) { 139 if (!keys.empty()) {
155 if (keys.size() > 1 ) { 140 if (keys.size() > 1 ) {
156 SinkWarning() << "Found more than one key, encrypting to all of them."; 141 SinkWarning() << "Found more than one key, encrypting to all of them.";
157 } 142 }
158 SinkLog() << "Found key: " << keys.front().primaryFingerprint(); 143 SinkLog() << "Found key: " << keys.front().primaryFingerprint();
159 for (auto item : findItems(addressee)) { 144 setValue(id, "keyFound", true);
160 item->setData(true, ComposerController::KeyFoundRole); 145 setValue(id, "key", QVariant::fromValue(keys));
161 item->setData(QVariant::fromValue(keys), ComposerController::KeyRole); 146 mMissingKeys.remove(id);
162 } 147 setProperty("foundAllKeys", mMissingKeys.isEmpty());
163 } else { 148 } else {
164 SinkWarning() << "Failed to find key for recipient."; 149 SinkWarning() << "Failed to find key for recipient.";
165 } 150 }
166 }); 151 });
167 } 152 }
168 153
169 void remove(const QString &addressee) 154 void set(const QStringList &list)
170 { 155 {
171 auto root = invisibleRootItem(); 156 for (const auto &email: list) {
172 for (int row = 0; row < root->rowCount(); row++) { 157 add({{"name", email}});
173 if (root->child(row, 0)->text() == addressee) {
174 root->removeRow(row);
175 return;
176 }
177 } 158 }
178 } 159 }
160};
179 161
180 bool foundAllKeys() 162class AttachmentController : public Kube::ListPropertyController
181 { 163{
182 std::vector<GpgME::Key> keys; 164public:
183 auto root = invisibleRootItem();
184 for (int row = 0; row < root->rowCount(); row++) {
185 auto item = root->child(row, 0);
186 if (!item->data(ComposerController::KeyFoundRole).toBool()) {
187 return false;
188 }
189 }
190 return true;
191 }
192 165
193 std::vector<GpgME::Key> getKeys() 166 AttachmentController()
167 : Kube::ListPropertyController{{"name", "filename", "content", "mimetype", "description", "iconname", "url", "inline"}}
194 { 168 {
195 std::vector<GpgME::Key> keys; 169 QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) {
196 traverse(this, [&] (QStandardItem *item) { 170 auto url = map.value("url").toUrl();
197 auto l = item->data(ComposerController::KeyRole).value<std::vector<GpgME::Key>>(); 171 setAttachmentProperties(id, url);
198 keys.insert(std::end(keys), std::begin(l), std::end(l));
199 }); 172 });
200 return keys;
201 } 173 }
202 174
203 void setStringList(const QStringList &list) 175 void setAttachmentProperties(const QByteArray &id, const QUrl &url)
204 { 176 {
205 clear(); 177 QMimeDatabase db;
206 for (const auto &s : list) { 178 auto mimeType = db.mimeTypeForUrl(url);
207 add(s); 179 if (mimeType.name() == QLatin1String("inode/directory")) {
208 } 180 qWarning() << "Can't deal with directories yet.";
209 } 181 } else {
210 182 if (!url.isLocalFile()) {
211 QStringList stringList() const 183 qWarning() << "Cannot attach remote file: " << url;
212 { 184 return;
213 QStringList list; 185 }
214 traverse(this, [&] (QStandardItem *item) {
215 list << item->text();
216 });
217 return list;
218 }
219 186
220 void setCryptoEnabled(bool enabled) 187 QFileInfo fileInfo(url.toLocalFile());
221 { 188 if (!fileInfo.exists()) {
222 mCryptoEnabled = enabled; 189 qWarning() << "The file doesn't exist: " << url;
223 traverse(this, [&] (QStandardItem *item) {
224 item->setData(mCryptoEnabled, ComposerController::KeyMissingRole);
225 if (mCryptoEnabled) {
226 findKey(item->text());
227 } 190 }
228 });
229 }
230 191
231 bool mCryptoEnabled = false; 192 QFile file{fileInfo.filePath()};
193 file.open(QIODevice::ReadOnly);
194 const auto data = file.readAll();
195 QVariantMap map;
196 map.insert("filename", fileInfo.fileName());
197 map.insert("mimetype", mimeType.name().toLatin1());
198 map.insert("filename", fileInfo.fileName().toLatin1());
199 map.insert("inline", false);
200 map.insert("iconname", mimeType.iconName());
201 map.insert("url", url);
202 map.insert("content", data);
203 setValues(id, map);
204 }
205 }
232}; 206};
233 207
234
235ComposerController::ComposerController() 208ComposerController::ComposerController()
236 : Kube::Controller(), 209 : Kube::Controller(),
210 controller_to{new AddresseeController},
211 controller_cc{new AddresseeController},
212 controller_bcc{new AddresseeController},
213 controller_attachments{new AttachmentController},
237 action_send{new Kube::ControllerAction{this, &ComposerController::send}}, 214 action_send{new Kube::ControllerAction{this, &ComposerController::send}},
238 action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, 215 action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}},
239 mRecipientCompleter{new RecipientCompleter}, 216 mRecipientCompleter{new RecipientCompleter},
240 mIdentitySelector{new IdentitySelector{*this}}, 217 mIdentitySelector{new IdentitySelector{*this}}
241 mToModel{new AddresseeModel},
242 mCcModel{new AddresseeModel},
243 mBccModel{new AddresseeModel},
244 mAttachmentModel{new QStandardItemModel}
245{ 218{
246 mAttachmentModel->setItemRoleNames({{NameRole, "name"},
247 {FilenameRole, "filename"},
248 {ContentRole, "content"},
249 {MimeTypeRole, "mimetype"},
250 {DescriptionRole, "description"},
251 {IconNameRole, "iconName"},
252 {UrlRole, "url"},
253 {InlineRole, "inline"}});
254 updateSaveAsDraftAction();
255 // mSendAction->monitorProperty<To>();
256 // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{
257 // //validate
258 // });
259 // registerAction<ControllerAction>(&ComposerController::send);
260 // actionDepends<ControllerAction, To, Subject>();
261 // TODO do in constructor
262
263 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction);
264 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction);
265 QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction);
266 QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction);
267 QObject::connect(this, &ComposerController::identityChanged, &ComposerController::findPersonalKey); 219 QObject::connect(this, &ComposerController::identityChanged, &ComposerController::findPersonalKey);
268 updateSendAction();
269
270 QObject::connect(this, &ComposerController::encryptChanged, [&] () { mToModel->setCryptoEnabled(getEncrypt()); });
271 QObject::connect(this, &ComposerController::encryptChanged, [&] () { mCcModel->setCryptoEnabled(getEncrypt()); });
272 QObject::connect(this, &ComposerController::encryptChanged, [&] () { mBccModel->setCryptoEnabled(getEncrypt()); });
273} 220}
274 221
275void ComposerController::findPersonalKey() 222void ComposerController::findPersonalKey()
@@ -280,18 +227,17 @@ void ComposerController::findPersonalKey()
280 return MailCrypto::findKeys(QStringList{} << identity.address(), true); 227 return MailCrypto::findKeys(QStringList{} << identity.address(), true);
281 }, 228 },
282 [this](const std::vector<GpgME::Key> &keys) { 229 [this](const std::vector<GpgME::Key> &keys) {
283 mPersonalKeys = keys; 230 if (keys.empty()) {
284 if (mPersonalKeys.empty()) {
285 SinkWarning() << "Failed to find a personal key."; 231 SinkWarning() << "Failed to find a personal key.";
286 } 232 }
287 if (mPersonalKeys.size() > 1) { 233 if (keys.size() > 1) {
288 SinkWarning() << "Found multiple keys, using first one:"; 234 SinkWarning() << "Found multiple keys, using first one:";
289 SinkWarning() << " " << mPersonalKeys.front().primaryFingerprint(); 235 SinkWarning() << " " << keys.front().primaryFingerprint();
290 } else { 236 } else {
291 SinkLog() << "Found personal key: " << mPersonalKeys.front().primaryFingerprint(); 237 SinkLog() << "Found personal key: " << keys.front().primaryFingerprint();
292 } 238 }
293 updateSendAction(); 239 setPersonalKeys(QVariant::fromValue(keys));
294 setEncryptionAvailable(!mPersonalKeys.empty()); 240 setFoundPersonalKeys(!keys.empty());
295 }); 241 });
296} 242}
297 243
@@ -300,111 +246,10 @@ void ComposerController::clear()
300 Controller::clear(); 246 Controller::clear();
301 //Reapply account and identity from selection 247 //Reapply account and identity from selection
302 mIdentitySelector->reapplyCurrentIndex(); 248 mIdentitySelector->reapplyCurrentIndex();
303 mToModel->clear(); 249 //FIXME implement in Controller::clear instead
304 mCcModel->clear(); 250 toController()->clear();
305 mBccModel->clear(); 251 ccController()->clear();
306} 252 bccController()->clear();
307
308QAbstractItemModel *ComposerController::toModel() const
309{
310 return mToModel.data();
311}
312
313void ComposerController::addTo(const QString &s)
314{
315 mToModel->add(s);
316 updateSendAction();
317}
318
319void ComposerController::removeTo(const QString &s)
320{
321 mToModel->remove(s);
322 updateSendAction();
323}
324
325QAbstractItemModel *ComposerController::ccModel() const
326{
327 return mCcModel.data();
328}
329
330void ComposerController::addCc(const QString &s)
331{
332 mCcModel->add(s);
333 updateSendAction();
334}
335
336void ComposerController::removeCc(const QString &s)
337{
338 mCcModel->remove(s);
339 updateSendAction();
340}
341
342QAbstractItemModel *ComposerController::bccModel() const
343{
344 return mBccModel.data();
345}
346
347void ComposerController::addBcc(const QString &s)
348{
349 mBccModel->add(s);
350 updateSendAction();
351}
352
353void ComposerController::removeBcc(const QString &s)
354{
355 mBccModel->remove(s);
356 updateSendAction();
357}
358
359QAbstractItemModel *ComposerController::attachmentModel() const
360{
361 return mAttachmentModel.data();
362}
363
364void ComposerController::addAttachment(const QUrl &url)
365{
366 QMimeDatabase db;
367 auto mimeType = db.mimeTypeForUrl(url);
368 if (mimeType.name() == QLatin1String("inode/directory")) {
369 qWarning() << "Can't deal with directories yet.";
370 } else {
371 if (!url.isLocalFile()) {
372 qWarning() << "Cannot attach remote file: " << url;
373 return;
374 }
375
376 QFileInfo fileInfo(url.toLocalFile());
377 if (!fileInfo.exists()) {
378 qWarning() << "The file doesn't exist: " << url;
379 }
380
381 QFile file{fileInfo.filePath()};
382 file.open(QIODevice::ReadOnly);
383 const auto data = file.readAll();
384 auto item = new QStandardItem;
385 item->setData(fileInfo.fileName(), NameRole);
386 item->setData(mimeType.name().toLatin1(), MimeTypeRole);
387 item->setData(fileInfo.fileName(), FilenameRole);
388 item->setData(false, InlineRole);
389 item->setData(mimeType.iconName(), IconNameRole);
390 item->setData(url, UrlRole);
391 item->setData(data, ContentRole);
392 mAttachmentModel->appendRow(item);
393 }
394}
395
396void ComposerController::removeAttachment(const QUrl &url)
397{
398 auto root = mAttachmentModel->invisibleRootItem();
399 if (root->hasChildren()) {
400 for (int row = 0; row < root->rowCount(); row++) {
401 auto item = root->child(row, 0);
402 if (url == item->data(UrlRole).toUrl()) {
403 root->removeRow(row);
404 return;
405 }
406 }
407 }
408} 253}
409 254
410Completer *ComposerController::recipientCompleter() const 255Completer *ComposerController::recipientCompleter() const
@@ -450,24 +295,23 @@ static QStringList getStringListFromAddresses(const KMime::Types::Mailbox::List
450 295
451void ComposerController::addAttachmentPart(KMime::Content *partToAttach) 296void ComposerController::addAttachmentPart(KMime::Content *partToAttach)
452{ 297{
453 auto item = new QStandardItem; 298 QVariantMap map;
454
455 if (partToAttach->contentType()->mimeType() == "multipart/digest" || 299 if (partToAttach->contentType()->mimeType() == "multipart/digest" ||
456 partToAttach->contentType()->mimeType() == "message/rfc822") { 300 partToAttach->contentType()->mimeType() == "message/rfc822") {
457 // if it is a digest or a full message, use the encodedContent() of the attachment, 301 // if it is a digest or a full message, use the encodedContent() of the attachment,
458 // which already has the proper headers 302 // which already has the proper headers
459 item->setData(partToAttach->encodedContent(), ContentRole); 303 map.insert("content", partToAttach->encodedContent());
460 } else { 304 } else {
461 item->setData(partToAttach->decodedContent(), ContentRole); 305 map.insert("content", partToAttach->decodedContent());
462 } 306 }
463 item->setData(partToAttach->contentType()->mimeType(), MimeTypeRole); 307 map.insert("mimetype", partToAttach->contentType()->mimeType());
464 308
465 QMimeDatabase db; 309 QMimeDatabase db;
466 auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType()); 310 auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType());
467 item->setData(mimeType.iconName(), IconNameRole); 311 map.insert("iconname", mimeType.iconName());
468 312
469 if (partToAttach->contentDescription(false)) { 313 if (partToAttach->contentDescription(false)) {
470 item->setData(partToAttach->contentDescription()->asUnicodeString(), DescriptionRole); 314 map.insert("description", partToAttach->contentDescription()->asUnicodeString());
471 } 315 }
472 QString name; 316 QString name;
473 QString filename; 317 QString filename;
@@ -478,7 +322,7 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach)
478 } 322 }
479 if (partToAttach->contentDisposition(false)) { 323 if (partToAttach->contentDisposition(false)) {
480 filename = partToAttach->contentDisposition()->filename(); 324 filename = partToAttach->contentDisposition()->filename();
481 item->setData(partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline, InlineRole); 325 map.insert("inline", partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline);
482 } 326 }
483 327
484 if (name.isEmpty() && !filename.isEmpty()) { 328 if (name.isEmpty() && !filename.isEmpty()) {
@@ -489,20 +333,19 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach)
489 } 333 }
490 334
491 if (!filename.isEmpty()) { 335 if (!filename.isEmpty()) {
492 item->setData(filename, FilenameRole); 336 map.insert("filename", filename);
493 } 337 }
494 if (!name.isEmpty()) { 338 if (!name.isEmpty()) {
495 item->setData(name, NameRole); 339 map.insert("name", name);
496 } 340 }
497 341 attachmentsController()->add(map);
498 mAttachmentModel->appendRow(item);
499} 342}
500 343
501void ComposerController::setMessage(const KMime::Message::Ptr &msg) 344void ComposerController::setMessage(const KMime::Message::Ptr &msg)
502{ 345{
503 mToModel->setStringList(getStringListFromAddresses(msg->to(true)->mailboxes())); 346 static_cast<AddresseeController*>(toController())->set(getStringListFromAddresses(msg->to(true)->mailboxes()));
504 mCcModel->setStringList(getStringListFromAddresses(msg->cc(true)->mailboxes())); 347 static_cast<AddresseeController*>(ccController())->set(getStringListFromAddresses(msg->cc(true)->mailboxes()));
505 mBccModel->setStringList(getStringListFromAddresses(msg->bcc(true)->mailboxes())); 348 static_cast<AddresseeController*>(bccController())->set(getStringListFromAddresses(msg->bcc(true)->mailboxes()));
506 349
507 setSubject(msg->subject(true)->asUnicodeString()); 350 setSubject(msg->subject(true)->asUnicodeString());
508 bool isHtml = false; 351 bool isHtml = false;
@@ -563,75 +406,66 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con
563 } 406 }
564} 407}
565 408
409std::vector<GpgME::Key> ComposerController::getRecipientKeys()
410{
411 std::vector<GpgME::Key> keys;
412 {
413 const auto list = toController()->getList<std::vector<GpgME::Key>>("key");
414 for (const auto &l: list) {
415 keys.insert(std::end(keys), std::begin(l), std::end(l));
416 }
417 }
418 {
419 const auto list = ccController()->getList<std::vector<GpgME::Key>>("key");
420 for (const auto &l: list) {
421 keys.insert(std::end(keys), std::begin(l), std::end(l));
422 }
423 }
424 {
425 const auto list = bccController()->getList<std::vector<GpgME::Key>>("key");
426 for (const auto &l: list) {
427 keys.insert(std::end(keys), std::begin(l), std::end(l));
428 }
429 }
430 return keys;
431}
432
566KMime::Message::Ptr ComposerController::assembleMessage() 433KMime::Message::Ptr ComposerController::assembleMessage()
567{ 434{
568 applyAddresses(mToModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 435 auto toAddresses = toController()->getList<QString>("name");
569 recordForAutocompletion(addrSpec, displayName); 436 auto ccAddresses = toController()->getList<QString>("name");
570 }); 437 auto bccAddresses = toController()->getList<QString>("name");
571 applyAddresses(mCcModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { 438 applyAddresses(toAddresses + ccAddresses + bccAddresses, [&](const QByteArray &addrSpec, const QByteArray &displayName) {
572 recordForAutocompletion(addrSpec, displayName);
573 });
574 applyAddresses(mBccModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) {
575 recordForAutocompletion(addrSpec, displayName); 439 recordForAutocompletion(addrSpec, displayName);
576 }); 440 });
577 441
578 QList<Attachment> attachments; 442 QList<Attachment> attachments;
579 auto root = mAttachmentModel->invisibleRootItem(); 443 attachmentsController()->traverse([&](const QVariantMap &value) {
580 for (int row = 0; row < root->rowCount(); row++) {
581 auto item = root->child(row, 0);
582 attachments << Attachment{ 444 attachments << Attachment{
583 item->data(NameRole).toString(), 445 value["name"].toString(),
584 item->data(FilenameRole).toString(), 446 value["filename"].toString(),
585 item->data(MimeTypeRole).toByteArray(), 447 value["mimetype"].toByteArray(),
586 item->data(InlineRole).toBool(), 448 value["inline"].toBool(),
587 item->data(ContentRole).toByteArray() 449 value["content"].toByteArray()
588 }; 450 };
589 } 451 });
590 452
591 std::vector<GpgME::Key> signingKeys; 453 std::vector<GpgME::Key> signingKeys;
592 if (getSign()) { 454 if (getSign()) {
593 signingKeys = mPersonalKeys; 455 signingKeys = getPersonalKeys().value<std::vector<GpgME::Key>>();
594 } 456 }
595 std::vector<GpgME::Key> encryptionKeys; 457 std::vector<GpgME::Key> encryptionKeys;
596 if (getEncrypt()) { 458 if (getEncrypt()) {
597 if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) {
598 qWarning() << "Can't encrypt with missing keys";
599 return nullptr;
600 }
601 //Encrypt to self so we can read the sent message 459 //Encrypt to self so we can read the sent message
602 encryptionKeys.insert(std::end(encryptionKeys), std::begin(mPersonalKeys), std::end(mPersonalKeys)); 460 encryptionKeys += getPersonalKeys().value<std::vector<GpgME::Key>>();
603 auto toKeys = mToModel->getKeys(); 461 encryptionKeys += getRecipientKeys();
604 encryptionKeys.insert(std::end(encryptionKeys), std::begin(toKeys), std::end(toKeys));
605 auto ccKeys = mCcModel->getKeys();
606 encryptionKeys.insert(std::end(encryptionKeys), std::begin(ccKeys), std::end(ccKeys));
607 auto bccKeys = mBccModel->getKeys();
608 encryptionKeys.insert(std::end(encryptionKeys), std::begin(bccKeys), std::end(bccKeys));
609 } 462 }
610 463
611 return MailTemplates::createMessage(mExistingMessage, mToModel->stringList(), mCcModel->stringList(), mBccModel->stringList(), getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); 464 return MailTemplates::createMessage(mExistingMessage, toAddresses, ccAddresses, bccAddresses, getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys);
612}
613
614void ComposerController::updateSendAction()
615{
616 auto enabled = !mToModel->stringList().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty();
617 if (getEncrypt()) {
618 if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) {
619 SinkWarning() << "Don't have all keys";
620 enabled = false;
621 }
622 }
623 if (getSign()) {
624 if (mPersonalKeys.empty()) {
625 enabled = false;
626 }
627 }
628 sendAction()->setEnabled(enabled);
629} 465}
630 466
631void ComposerController::send() 467void ComposerController::send()
632{ 468{
633 // verify<To, Subject>()
634 // && verify<Subject>();
635 auto message = assembleMessage(); 469 auto message = assembleMessage();
636 if (!message) { 470 if (!message) {
637 SinkWarning() << "Failed to assemble the message."; 471 SinkWarning() << "Failed to assemble the message.";
@@ -670,12 +504,6 @@ void ComposerController::send()
670 run(job); 504 run(job);
671} 505}
672 506
673void ComposerController::updateSaveAsDraftAction()
674{
675 bool enabled = !getAccountId().isEmpty();
676 sendAction()->setEnabled(enabled);
677}
678
679void ComposerController::saveAsDraft() 507void ComposerController::saveAsDraft()
680{ 508{
681 SinkLog() << "Save as draft"; 509 SinkLog() << "Save as draft";
@@ -683,9 +511,8 @@ void ComposerController::saveAsDraft()
683 auto existingMail = getExistingMail(); 511 auto existingMail = getExistingMail();
684 512
685 auto message = assembleMessage(); 513 auto message = assembleMessage();
686 //FIXME this is something for the validation
687 if (!message) { 514 if (!message) {
688 SinkWarning() << "Failed to get the mail: "; 515 SinkWarning() << "Failed to assemble the message.";
689 return; 516 return;
690 } 517 }
691 518
@@ -720,3 +547,4 @@ void ComposerController::saveAsDraft()
720 }); 547 });
721 run(job); 548 run(job);
722} 549}
550
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h
index 995f4a2e..70a88900 100644
--- a/framework/src/domain/composercontroller.h
+++ b/framework/src/domain/composercontroller.h
@@ -41,6 +41,8 @@ inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mail
41 41
42Q_DECLARE_METATYPE(KMime::Types::Mailbox); 42Q_DECLARE_METATYPE(KMime::Types::Mailbox);
43 43
44Q_DECLARE_METATYPE(GpgME::Key);
45
44namespace KMime { 46namespace KMime {
45class Message; 47class Message;
46} 48}
@@ -57,7 +59,6 @@ class ComposerController : public Kube::Controller
57 KUBE_CONTROLLER_PROPERTY(bool, HtmlBody, htmlBody) 59 KUBE_CONTROLLER_PROPERTY(bool, HtmlBody, htmlBody)
58 KUBE_CONTROLLER_PROPERTY(bool, Encrypt, encrypt) 60 KUBE_CONTROLLER_PROPERTY(bool, Encrypt, encrypt)
59 KUBE_CONTROLLER_PROPERTY(bool, Sign, sign) 61 KUBE_CONTROLLER_PROPERTY(bool, Sign, sign)
60 KUBE_CONTROLLER_PROPERTY(bool, EncryptionAvailable, encryptionAvailable)
61 62
62 //Set by identitySelector 63 //Set by identitySelector
63 KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) 64 KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity)
@@ -67,26 +68,21 @@ class ComposerController : public Kube::Controller
67 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) 68 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage)
68 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) 69 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail)
69 70
71 KUBE_CONTROLLER_PROPERTY(/*std::vector<GpgME::Key>*/QVariant, PersonalKeys, personalKeys)
72 KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys)
73
74 KUBE_CONTROLLER_LISTCONTROLLER(to)
75 KUBE_CONTROLLER_LISTCONTROLLER(cc)
76 KUBE_CONTROLLER_LISTCONTROLLER(bcc)
77 KUBE_CONTROLLER_LISTCONTROLLER(attachments)
78
70 Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) 79 Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT)
71 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) 80 Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT)
72 //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT)
73
74 Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT)
75 Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT)
76 Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT)
77 Q_PROPERTY (QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT)
78 81
79 KUBE_CONTROLLER_ACTION(send) 82 KUBE_CONTROLLER_ACTION(send)
80 KUBE_CONTROLLER_ACTION(saveAsDraft) 83 KUBE_CONTROLLER_ACTION(saveAsDraft)
81 84
82public: 85public:
83 enum AddresseeRoles {
84 KeyFoundRole = Qt::UserRole + 1,
85 KeyMissingRole,
86 KeyRole,
87 AddresseeNameRole
88 };
89
90 explicit ComposerController(); 86 explicit ComposerController();
91 87
92 Completer *recipientCompleter() const; 88 Completer *recipientCompleter() const;
@@ -94,50 +90,19 @@ public:
94 90
95 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); 91 Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft);
96 92
97 QAbstractItemModel *toModel() const;
98 QAbstractItemModel *ccModel() const;
99 QAbstractItemModel *bccModel() const;
100 QAbstractItemModel *attachmentModel() const;
101
102 Q_INVOKABLE void addTo(const QString &);
103 Q_INVOKABLE void removeTo(const QString &);
104 Q_INVOKABLE void addCc(const QString &);
105 Q_INVOKABLE void removeCc(const QString &);
106 Q_INVOKABLE void addBcc(const QString &);
107 Q_INVOKABLE void removeBcc(const QString &);
108 Q_INVOKABLE void addAttachment(const QUrl &);
109 Q_INVOKABLE void removeAttachment(const QUrl &);
110
111public slots: 93public slots:
112 virtual void clear() Q_DECL_OVERRIDE; 94 virtual void clear() Q_DECL_OVERRIDE;
113 95
114private slots: 96private slots:
115 void updateSendAction();
116 void updateSaveAsDraftAction();
117 void findPersonalKey(); 97 void findPersonalKey();
118 98
119private: 99private:
120 enum AttachmentRoles {
121 NameRole = Qt::UserRole + 1,
122 FilenameRole,
123 ContentRole,
124 MimeTypeRole,
125 DescriptionRole,
126 InlineRole,
127 IconNameRole,
128 UrlRole
129 };
130
131 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); 100 void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName);
132 void setMessage(const QSharedPointer<KMime::Message> &msg); 101 void setMessage(const QSharedPointer<KMime::Message> &msg);
133 void addAttachmentPart(KMime::Content *partToAttach); 102 void addAttachmentPart(KMime::Content *partToAttach);
134 KMime::Message::Ptr assembleMessage(); 103 KMime::Message::Ptr assembleMessage();
104 std::vector<GpgME::Key> getRecipientKeys();
135 105
136 QScopedPointer<Completer> mRecipientCompleter; 106 QScopedPointer<Completer> mRecipientCompleter;
137 QScopedPointer<Selector> mIdentitySelector; 107 QScopedPointer<Selector> mIdentitySelector;
138 QSharedPointer<AddresseeModel> mToModel;
139 QSharedPointer<AddresseeModel> mCcModel;
140 QSharedPointer<AddresseeModel> mBccModel;
141 QScopedPointer<QStandardItemModel> mAttachmentModel;
142 std::vector<GpgME::Key> mPersonalKeys;
143}; 108};
diff --git a/framework/src/domain/controller.cpp b/framework/src/domain/controller.cpp
index 52f4cd1f..82a761f6 100644
--- a/framework/src/domain/controller.cpp
+++ b/framework/src/domain/controller.cpp
@@ -20,16 +20,24 @@
20 20
21#include <QQmlEngine> 21#include <QQmlEngine>
22#include <QMetaProperty> 22#include <QMetaProperty>
23#include <QStandardItemModel>
24#include <QStandardItem>
25#include <QUuid>
23#include <sink/log.h> 26#include <sink/log.h>
24 27
25using namespace Kube; 28using namespace Kube;
26 29
27ControllerAction::ControllerAction() 30ControllerState::ControllerState()
28 : QObject() 31 : QObject()
29{ 32{
30 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); 33 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
31} 34}
32 35
36ControllerAction::ControllerAction()
37 : ControllerState()
38{
39}
40
33void ControllerAction::execute() 41void ControllerAction::execute()
34{ 42{
35 emit triggered(); 43 emit triggered();
@@ -57,3 +65,101 @@ void Controller::run(const KAsync::Job<void> &job)
57 //TODO attach a log context to the execution that we can gather from the job? 65 //TODO attach a log context to the execution that we can gather from the job?
58 jobToExec.exec(); 66 jobToExec.exec();
59} 67}
68
69
70static void traverse(const QStandardItemModel *model, const std::function<bool(QStandardItem *item)> &f)
71{
72 auto root = model->invisibleRootItem();
73 for (int row = 0; row < root->rowCount(); row++) {
74 if (!f(root->child(row, 0))) {
75 return;
76 }
77 }
78}
79
80ListPropertyController::ListPropertyController(const QStringList &roles)
81 : QObject(),
82 mModel(new QStandardItemModel)
83{
84 //Generate a set of roles for the names. We're not using any enum, so the actual role value doesn't matter.
85 int role = Qt::UserRole + 1;
86 mRoles.insert("id", role);
87 role++;
88 for (const auto &r : roles) {
89 mRoles.insert(r, role);
90 role++;
91 }
92
93 QHash<int, QByteArray> roleNames;
94 for (const auto r : mRoles.keys()) {
95 roleNames.insert(mRoles[r], r.toLatin1());
96 }
97 mModel->setItemRoleNames(roleNames);
98}
99
100void ListPropertyController::add(const QVariantMap &value)
101{
102 auto item = new QStandardItem;
103 auto id = QUuid::createUuid().toByteArray();
104 item->setData(id, mRoles["id"]);
105 for (const auto &k : value.keys()) {
106 item->setData(value.value(k), mRoles[k]);
107 }
108 mModel->appendRow(QList<QStandardItem*>() << item);
109 emit added(id, value);
110}
111
112void ListPropertyController::remove(const QByteArray &id)
113{
114 auto root = mModel->invisibleRootItem();
115 const auto idRole = mRoles["id"];
116 for (int row = 0; row < root->rowCount(); row++) {
117 if (root->child(row, 0)->data(idRole).toByteArray() == id) {
118 root->removeRow(row);
119 return;
120 }
121 }
122}
123
124void ListPropertyController::clear()
125{
126 mModel->clear();
127}
128
129QAbstractItemModel *ListPropertyController::model()
130{
131 QQmlEngine::setObjectOwnership(mModel.data(), QQmlEngine::CppOwnership);
132 return mModel.data();
133}
134
135void ListPropertyController::setValue(const QByteArray &id, const QString &key, const QVariant &value)
136{
137 setValues(id, {{key, value}});
138}
139
140void ListPropertyController::setValues(const QByteArray &id, const QVariantMap &values)
141{
142 const auto idRole = mRoles["id"];
143 ::traverse(mModel.data(), [&] (QStandardItem *item) {
144 if (item->data(idRole).toByteArray() == id) {
145 for (const auto &key : values.keys()) {
146 item->setData(values.value(key), mRoles[key]);
147 }
148 return false;
149 }
150 return true;
151 });
152}
153
154void ListPropertyController::traverse(const std::function<void(const QVariantMap &)> &f)
155{
156 ::traverse(mModel.data(), [&] (QStandardItem *item) {
157 QVariantMap map;
158 for (const auto &key : mRoles.keys()) {
159 map.insert(key, item->data(mRoles[key]));
160 }
161 f(map);
162 return true;
163 });
164}
165
diff --git a/framework/src/domain/controller.h b/framework/src/domain/controller.h
index bf2a7ace..ba7ac8fe 100644
--- a/framework/src/domain/controller.h
+++ b/framework/src/domain/controller.h
@@ -42,13 +42,36 @@
42 public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \ 42 public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \
43 private slots: void NAME(); \ 43 private slots: void NAME(); \
44 44
45#define KUBE_CONTROLLER_LISTCONTROLLER(NAME) \
46 Q_PROPERTY (Kube::ListPropertyController* NAME READ NAME##Controller CONSTANT) \
47 private: QScopedPointer<Kube::ListPropertyController> controller_##NAME; \
48 public: Kube::ListPropertyController* NAME##Controller() const { Q_ASSERT(controller_##NAME); return controller_##NAME.data(); } \
49
50
51class QAbstractItemModel;
52class QStandardItemModel;
45 53
46namespace Kube { 54namespace Kube {
47 55
48class ControllerAction : public QObject { 56class ControllerState : public QObject {
49 Q_OBJECT 57 Q_OBJECT
50 Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) 58 Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged)
51public: 59public:
60 ControllerState();
61 ~ControllerState() = default;
62
63 void setEnabled(bool enabled) { setProperty("enabled", enabled); }
64
65signals:
66 void enabledChanged();
67
68private:
69 bool mEnabled = false;
70};
71
72class ControllerAction : public ControllerState {
73 Q_OBJECT
74public:
52 ControllerAction(); 75 ControllerAction();
53 template <typename Func> 76 template <typename Func>
54 ControllerAction(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func slot) 77 ControllerAction(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func slot)
@@ -60,14 +83,9 @@ public:
60 ~ControllerAction() = default; 83 ~ControllerAction() = default;
61 84
62 Q_INVOKABLE void execute(); 85 Q_INVOKABLE void execute();
63 void setEnabled(bool enabled) { setProperty("enabled", enabled); }
64 86
65signals: 87signals:
66 void enabledChanged();
67 void triggered(); 88 void triggered();
68
69private:
70 bool mEnabled = true;
71}; 89};
72 90
73class Controller : public QObject { 91class Controller : public QObject {
@@ -87,4 +105,42 @@ protected:
87 void run(const KAsync::Job<void> &job); 105 void run(const KAsync::Job<void> &job);
88}; 106};
89 107
108class ListPropertyController : public QObject
109{
110 Q_OBJECT
111 Q_PROPERTY(QAbstractItemModel* model READ model CONSTANT)
112
113public:
114 ListPropertyController(const QStringList &roles);
115 Q_INVOKABLE virtual void add(const QVariantMap &value);
116 Q_INVOKABLE virtual void remove(const QByteArray &id);
117 Q_INVOKABLE void clear();
118
119 QAbstractItemModel *model();
120
121 void setValue(const QByteArray &id, const QString &key, const QVariant &);
122 void setValues(const QByteArray &id, const QVariantMap &values);
123 void traverse(const std::function<void(const QVariantMap &)> &f);
124
125 template<typename T>
126 QList<T> getList(const QString &property)
127 {
128 QList<T> list;
129 traverse([&] (const QVariantMap &map) {
130 list << map[property].value<T>();
131 });
132 return list;
133 }
134
135Q_SIGNALS:
136 void added(QByteArray, QVariantMap);
137
138protected:
139 QScopedPointer<QStandardItemModel> mModel;
140
141private:
142 QHash<QString, int> mRoles;
143};
144
145
90} 146}
diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp
index 9f7b03c1..4aff5708 100644
--- a/framework/src/frameworkplugin.cpp
+++ b/framework/src/frameworkplugin.cpp
@@ -39,6 +39,7 @@
39#include "webengineprofile.h" 39#include "webengineprofile.h"
40#include "startupcheck.h" 40#include "startupcheck.h"
41#include "keyring.h" 41#include "keyring.h"
42#include "controller.h"
42 43
43#include <QtQml> 44#include <QtQml>
44 45
@@ -70,6 +71,7 @@ void FrameworkPlugin::registerTypes (const char *uri)
70 qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel"); 71 qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel");
71 qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel"); 72 qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel");
72 qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController"); 73 qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController");
74 qmlRegisterType<Kube::ControllerAction>(uri, 1, 0, "ControllerAction");
73 qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser"); 75 qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser");
74 qmlRegisterType<Retriever>(uri, 1, 0, "Retriever"); 76 qmlRegisterType<Retriever>(uri, 1, 0, "Retriever");
75 qmlRegisterType<OutboxModel>(uri, 1, 0, "OutboxModel"); 77 qmlRegisterType<OutboxModel>(uri, 1, 0, "OutboxModel");