1 /*
2 SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "messageactions.h"
8
9 #include "kmcommands.h"
10 #include "kmkernel.h"
11 #include "kmmainwidget.h"
12 #include "kmreaderwin.h"
13 #include "settings/kmailsettings.h"
14 #include "util.h"
15 #include <MailCommon/MailKernel>
16 #include <TemplateParser/CustomTemplatesMenu>
17
18 #include <MessageCore/MailingList>
19 #include <MessageCore/MessageCoreSettings>
20 #include <MessageCore/StringUtil>
21 #include <MessageViewer/HeaderStylePlugin>
22 #include <MessageViewer/MessageViewerSettings>
23 #include <PimCommonAkonadi/AnnotationDialog>
24
25 #include <Akonadi/ChangeRecorder>
26 #include <Akonadi/ItemFetchJob>
27 #include <Akonadi/KMime/MessageParts>
28 #include <AkonadiSearch/Debug/akonadisearchdebugdialog.h>
29 #include <KIO/KUriFilterSearchProviderActions>
30 #include <QAction>
31
32 #include "job/createfollowupreminderonexistingmessagejob.h"
33 #include <MessageComposer/FollowUpReminderSelectDateDialog>
34
35 #include "kmail_debug.h"
36 #include <Akonadi/ItemFetchJob>
37 #include <KActionCollection>
38 #include <KActionMenu>
39 #include <KIO/JobUiDelegate>
40 #include <KIO/OpenUrlJob>
41 #include <KLocalizedString>
42 #include <KStringHandler>
43 #include <KUriFilter>
44 #include <KXMLGUIClient>
45 #include <QFileDialog>
46 #include <QIcon>
47 #include <QMenu>
48
49 #include <Akonadi/Collection>
50 #include <Akonadi/EntityAnnotationsAttribute>
51 #include <MailCommon/MailUtil>
52 #include <MessageViewer/MessageViewerUtil>
53 #include <QVariant>
54 #include <QWidget>
55
56 using namespace KMail;
57
MessageActions(KActionCollection * ac,QWidget * parent)58 MessageActions::MessageActions(KActionCollection *ac, QWidget *parent)
59 : QObject(parent)
60 , mParent(parent)
61 , mReplyActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18nc("Message->", "&Reply"), this))
62 , mReplyAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("&Reply..."), this))
63 , mReplyAllAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-all")), i18n("Reply to &All..."), this))
64 , mReplyAuthorAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply to A&uthor..."), this))
65 , mReplyListAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-list")), i18n("Reply to Mailing-&List..."), this))
66 , mNoQuoteReplyAction(new QAction(i18n("Reply Without &Quote..."), this))
67 , mForwardInlineAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("@action:inmenu Message->Forward->", "&Inline..."), this))
68 , mForwardAttachedAction(
69 new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("@action:inmenu Message->Forward->", "As &Attachment..."), this))
70 , mRedirectAction(new QAction(i18nc("Message->Forward->", "&Redirect..."), this))
71 , mNewToRecipientsAction(new QAction(i18n("New Message to Recipients..."), this))
72 , mStatusMenu(new KActionMenu(i18n("Mar&k Message"), this))
73 , mForwardActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("Message->", "&Forward"), this))
74 , mMailingListActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-message-new-list")), i18nc("Message->", "Mailing-&List"), this))
75 , mAnnotateAction(new QAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")), i18n("Add Note..."), this))
76 , mEditAsNewAction(new QAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18n("&Edit As New"), this))
77 , mListFilterAction(new QAction(i18n("Filter on Mailing-&List..."), this))
78 , mAddFollowupReminderAction(new QAction(i18n("Add Followup Reminder..."), this))
79 , mDebugAkonadiSearchAction(new QAction(QStringLiteral("Debug Akonadi Search..."), this)) /* dont translate it*/
80 , mSendAgainAction(new QAction(i18n("Send A&gain..."), this))
81 , mNewMessageFromTemplateAction(new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Message From &Template"), this))
82 , mWebShortcutMenuManager(new KIO::KUriFilterSearchProviderActions(this))
83 , mExportToPdfAction(new QAction(QIcon::fromTheme(QStringLiteral("application-pdf")), i18n("Export to PDF..."), this))
84
85 {
86 ac->addAction(QStringLiteral("message_reply_menu"), mReplyActionMenu);
87 connect(mReplyActionMenu, &KActionMenu::triggered, this, &MessageActions::slotReplyToMsg);
88
89 ac->addAction(QStringLiteral("reply"), mReplyAction);
90 ac->setDefaultShortcut(mReplyAction, Qt::Key_R);
91 connect(mReplyAction, &QAction::triggered, this, &MessageActions::slotReplyToMsg);
92 mReplyActionMenu->addAction(mReplyAction);
93
94 ac->addAction(QStringLiteral("reply_author"), mReplyAuthorAction);
95 ac->setDefaultShortcut(mReplyAuthorAction, Qt::SHIFT | Qt::Key_A);
96 connect(mReplyAuthorAction, &QAction::triggered, this, &MessageActions::slotReplyAuthorToMsg);
97 mReplyActionMenu->addAction(mReplyAuthorAction);
98
99 ac->addAction(QStringLiteral("reply_all"), mReplyAllAction);
100 ac->setDefaultShortcut(mReplyAllAction, Qt::Key_A);
101 connect(mReplyAllAction, &QAction::triggered, this, &MessageActions::slotReplyAllToMsg);
102 mReplyActionMenu->addAction(mReplyAllAction);
103
104 ac->addAction(QStringLiteral("reply_list"), mReplyListAction);
105
106 ac->setDefaultShortcut(mReplyListAction, Qt::Key_L);
107 connect(mReplyListAction, &QAction::triggered, this, &MessageActions::slotReplyListToMsg);
108 mReplyActionMenu->addAction(mReplyListAction);
109
110 ac->addAction(QStringLiteral("noquotereply"), mNoQuoteReplyAction);
111 ac->setDefaultShortcut(mNoQuoteReplyAction, Qt::SHIFT | Qt::Key_R);
112 connect(mNoQuoteReplyAction, &QAction::triggered, this, &MessageActions::slotNoQuoteReplyToMsg);
113
114 ac->addAction(QStringLiteral("mlist_filter"), mListFilterAction);
115 connect(mListFilterAction, &QAction::triggered, this, &MessageActions::slotMailingListFilter);
116
117 ac->addAction(QStringLiteral("set_status"), mStatusMenu);
118
119 ac->addAction(QStringLiteral("annotate"), mAnnotateAction);
120 connect(mAnnotateAction, &QAction::triggered, this, &MessageActions::annotateMessage);
121
122 ac->addAction(QStringLiteral("editasnew"), mEditAsNewAction);
123 connect(mEditAsNewAction, &QAction::triggered, this, &MessageActions::editCurrentMessage);
124 ac->setDefaultShortcut(mEditAsNewAction, Qt::Key_T);
125
126 mPrintAction = KStandardAction::print(this, &MessageActions::slotPrintMessage, ac);
127 mPrintPreviewAction = KStandardAction::printPreview(this, &MessageActions::slotPrintPreviewMsg, ac);
128
129 ac->addAction(QStringLiteral("message_forward"), mForwardActionMenu);
130
131 connect(mForwardAttachedAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardAttachedMessage()));
132
133 ac->addAction(QStringLiteral("message_forward_as_attachment"), mForwardAttachedAction);
134
135 connect(mForwardInlineAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardInlineMsg()));
136
137 ac->addAction(QStringLiteral("message_forward_inline"), mForwardInlineAction);
138
139 setupForwardActions(ac);
140
141 ac->addAction(QStringLiteral("new_to_recipients"), mNewToRecipientsAction);
142 connect(mNewToRecipientsAction, SIGNAL(triggered(bool)), parent, SLOT(slotNewMessageToRecipients()));
143
144 ac->addAction(QStringLiteral("message_forward_redirect"), mRedirectAction);
145 connect(mRedirectAction, SIGNAL(triggered(bool)), parent, SLOT(slotRedirectMessage()));
146
147 ac->setDefaultShortcut(mRedirectAction, QKeySequence(Qt::Key_E));
148 mForwardActionMenu->addAction(mRedirectAction);
149
150 connect(mMailingListActionMenu->menu(), &QMenu::triggered, this, &MessageActions::slotRunUrl);
151 ac->addAction(QStringLiteral("mailing_list"), mMailingListActionMenu);
152 mMailingListActionMenu->setEnabled(false);
153
154 connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemChanged, this, &MessageActions::slotItemModified);
155 connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemRemoved, this, &MessageActions::slotItemRemoved);
156
157 mCustomTemplatesMenu = new TemplateParser::CustomTemplatesMenu(parent, ac);
158
159 connect(mCustomTemplatesMenu, SIGNAL(replyTemplateSelected(QString)), parent, SLOT(slotCustomReplyToMsg(QString)));
160 connect(mCustomTemplatesMenu, SIGNAL(replyAllTemplateSelected(QString)), parent, SLOT(slotCustomReplyAllToMsg(QString)));
161 connect(mCustomTemplatesMenu, SIGNAL(forwardTemplateSelected(QString)), parent, SLOT(slotCustomForwardMsg(QString)));
162 connect(KMKernel::self(), &KMKernel::customTemplatesChanged, mCustomTemplatesMenu, &TemplateParser::CustomTemplatesMenu::update);
163
164 forwardMenu()->addSeparator();
165 forwardMenu()->addAction(mCustomTemplatesMenu->forwardActionMenu());
166 replyMenu()->addSeparator();
167 replyMenu()->addAction(mCustomTemplatesMenu->replyActionMenu());
168 replyMenu()->addAction(mCustomTemplatesMenu->replyAllActionMenu());
169
170 // Don't translate it. Shown only when we set env variable AKONADI_SEARCH_DEBUG
171 connect(mDebugAkonadiSearchAction, &QAction::triggered, this, &MessageActions::slotDebugAkonadiSearch);
172
173 ac->addAction(QStringLiteral("message_followup_reminder"), mAddFollowupReminderAction);
174 connect(mAddFollowupReminderAction, &QAction::triggered, this, &MessageActions::slotAddFollowupReminder);
175
176 ac->addAction(QStringLiteral("send_again"), mSendAgainAction);
177 connect(mSendAgainAction, &QAction::triggered, this, &MessageActions::slotResendMessage);
178
179 ac->addAction(QStringLiteral("use_template"), mNewMessageFromTemplateAction);
180 connect(mNewMessageFromTemplateAction, &QAction::triggered, this, &MessageActions::slotUseTemplate);
181 ac->setDefaultShortcut(mNewMessageFromTemplateAction, QKeySequence(Qt::SHIFT | Qt::Key_N));
182
183 ac->addAction(QStringLiteral("file_export_pdf"), mExportToPdfAction);
184 connect(mExportToPdfAction, &QAction::triggered, this, &MessageActions::slotExportToPdf);
185
186 updateActions();
187 }
188
~MessageActions()189 MessageActions::~MessageActions()
190 {
191 delete mCustomTemplatesMenu;
192 }
193
fillAkonadiStandardAction(Akonadi::StandardMailActionManager * akonadiStandardActionManager)194 void MessageActions::fillAkonadiStandardAction(Akonadi::StandardMailActionManager *akonadiStandardActionManager)
195 {
196 QAction *action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsRead);
197 mStatusMenu->addAction(action);
198
199 action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsUnread);
200 mStatusMenu->addAction(action);
201
202 mStatusMenu->addSeparator();
203 action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsImportant);
204 mStatusMenu->addAction(action);
205
206 action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsActionItem);
207 mStatusMenu->addAction(action);
208 }
209
customTemplatesMenu() const210 TemplateParser::CustomTemplatesMenu *MessageActions::customTemplatesMenu() const
211 {
212 return mCustomTemplatesMenu;
213 }
214
slotUseTemplate()215 void MessageActions::slotUseTemplate()
216 {
217 if (!mCurrentItem.isValid()) {
218 return;
219 }
220 KMCommand *command = new KMUseTemplateCommand(mParent, mCurrentItem);
221 command->start();
222 }
223
editAsNewAction() const224 QAction *MessageActions::editAsNewAction() const
225 {
226 return mEditAsNewAction;
227 }
228
setCurrentMessage(const Akonadi::Item & msg,const Akonadi::Item::List & items)229 void MessageActions::setCurrentMessage(const Akonadi::Item &msg, const Akonadi::Item::List &items)
230 {
231 mCurrentItem = msg;
232
233 if (!items.isEmpty()) {
234 if (msg.isValid()) {
235 mVisibleItems = items;
236 } else {
237 mVisibleItems.clear();
238 }
239 }
240
241 if (!msg.isValid()) {
242 mVisibleItems.clear();
243 clearMailingListActions();
244 }
245
246 updateActions();
247 }
248
replyMenu() const249 KActionMenu *MessageActions::replyMenu() const
250 {
251 return mReplyActionMenu;
252 }
253
replyListAction() const254 QAction *MessageActions::replyListAction() const
255 {
256 return mReplyListAction;
257 }
258
forwardInlineAction() const259 QAction *MessageActions::forwardInlineAction() const
260 {
261 return mForwardInlineAction;
262 }
263
forwardAttachedAction() const264 QAction *MessageActions::forwardAttachedAction() const
265 {
266 return mForwardAttachedAction;
267 }
268
redirectAction() const269 QAction *MessageActions::redirectAction() const
270 {
271 return mRedirectAction;
272 }
273
newToRecipientsAction() const274 QAction *MessageActions::newToRecipientsAction() const
275 {
276 return mNewToRecipientsAction;
277 }
278
messageStatusMenu() const279 KActionMenu *MessageActions::messageStatusMenu() const
280 {
281 return mStatusMenu;
282 }
283
forwardMenu() const284 KActionMenu *MessageActions::forwardMenu() const
285 {
286 return mForwardActionMenu;
287 }
288
annotateAction() const289 QAction *MessageActions::annotateAction() const
290 {
291 return mAnnotateAction;
292 }
293
printAction() const294 QAction *MessageActions::printAction() const
295 {
296 return mPrintAction;
297 }
298
printPreviewAction() const299 QAction *MessageActions::printPreviewAction() const
300 {
301 return mPrintPreviewAction;
302 }
303
listFilterAction() const304 QAction *MessageActions::listFilterAction() const
305 {
306 return mListFilterAction;
307 }
308
mailingListActionMenu() const309 KActionMenu *MessageActions::mailingListActionMenu() const
310 {
311 return mMailingListActionMenu;
312 }
313
slotItemRemoved(const Akonadi::Item & item)314 void MessageActions::slotItemRemoved(const Akonadi::Item &item)
315 {
316 if (item == mCurrentItem) {
317 mCurrentItem = Akonadi::Item();
318 updateActions();
319 }
320 }
321
slotItemModified(const Akonadi::Item & item,const QSet<QByteArray> & partIdentifiers)322 void MessageActions::slotItemModified(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
323 {
324 Q_UNUSED(partIdentifiers)
325 if (item == mCurrentItem) {
326 mCurrentItem = item;
327 const int numberOfVisibleItems = mVisibleItems.count();
328 for (int i = 0; i < numberOfVisibleItems; ++i) {
329 Akonadi::Item it = mVisibleItems.at(i);
330 if (item == it) {
331 mVisibleItems[i] = item;
332 }
333 }
334 updateActions();
335 }
336 }
337
updateActions()338 void MessageActions::updateActions()
339 {
340 const bool hasPayload = mCurrentItem.hasPayload<KMime::Message::Ptr>();
341 bool itemValid = mCurrentItem.isValid();
342 Akonadi::Collection parent;
343 if (itemValid) { //=> valid
344 parent = mCurrentItem.parentCollection();
345 }
346 if (parent.isValid()) {
347 if (CommonKernel->folderIsTemplates(parent)) {
348 itemValid = false;
349 }
350 }
351
352 const bool multiVisible = !mVisibleItems.isEmpty() || mCurrentItem.isValid();
353 const bool uniqItem = (itemValid || hasPayload) && (mVisibleItems.count() <= 1);
354 mReplyActionMenu->setEnabled(hasPayload);
355 mReplyAction->setEnabled(hasPayload);
356 mNoQuoteReplyAction->setEnabled(hasPayload);
357 mReplyAuthorAction->setEnabled(hasPayload);
358 mReplyAllAction->setEnabled(hasPayload);
359 mReplyListAction->setEnabled(hasPayload);
360 mNoQuoteReplyAction->setEnabled(hasPayload);
361 mSendAgainAction->setEnabled(itemValid);
362
363 mAnnotateAction->setEnabled(uniqItem);
364 mAddFollowupReminderAction->setEnabled(uniqItem);
365 if (!mCurrentItem.hasAttribute<Akonadi::EntityAnnotationsAttribute>()) {
366 mAnnotateAction->setText(i18n("Add Note..."));
367 } else {
368 mAnnotateAction->setText(i18n("Edit Note..."));
369 }
370
371 mStatusMenu->setEnabled(multiVisible);
372
373 mPrintAction->setEnabled(mMessageView != nullptr);
374 mPrintPreviewAction->setEnabled(mMessageView != nullptr);
375 mExportToPdfAction->setEnabled(uniqItem);
376 if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
377 if (mCurrentItem.loadedPayloadParts().contains("RFC822")) {
378 updateMailingListActions(mCurrentItem);
379 } else {
380 auto job = new Akonadi::ItemFetchJob(mCurrentItem);
381 job->fetchScope().fetchAllAttributes();
382 job->fetchScope().fetchFullPayload(true);
383 job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header);
384 job->fetchScope().fetchAttribute<Akonadi::EntityAnnotationsAttribute>();
385 connect(job, &Akonadi::ItemFetchJob::result, this, &MessageActions::slotUpdateActionsFetchDone);
386 }
387 }
388 mEditAsNewAction->setEnabled(uniqItem);
389 }
390
slotUpdateActionsFetchDone(KJob * job)391 void MessageActions::slotUpdateActionsFetchDone(KJob *job)
392 {
393 if (job->error()) {
394 return;
395 }
396
397 auto fetchJob = static_cast<Akonadi::ItemFetchJob *>(job);
398 if (fetchJob->items().isEmpty()) {
399 return;
400 }
401 const Akonadi::Item messageItem = fetchJob->items().constFirst();
402 if (messageItem == mCurrentItem) {
403 mCurrentItem = messageItem;
404 updateMailingListActions(messageItem);
405 }
406 }
407
clearMailingListActions()408 void MessageActions::clearMailingListActions()
409 {
410 mMailingListActionMenu->setEnabled(false);
411 mListFilterAction->setEnabled(false);
412 mListFilterAction->setText(i18n("Filter on Mailing-List..."));
413 }
414
updateMailingListActions(const Akonadi::Item & messageItem)415 void MessageActions::updateMailingListActions(const Akonadi::Item &messageItem)
416 {
417 if (!messageItem.hasPayload<KMime::Message::Ptr>()) {
418 return;
419 }
420 auto message = messageItem.payload<KMime::Message::Ptr>();
421 const MessageCore::MailingList mailList = MessageCore::MailingList::detect(message);
422
423 if (mailList.features() == MessageCore::MailingList::None) {
424 clearMailingListActions();
425 } else {
426 // A mailing list menu with only a title is pretty boring
427 // so make sure theres at least some content
428 QString listId;
429 if (mailList.features() & MessageCore::MailingList::Id) {
430 // From a list-id in the form, "Birds of France <bof.yahoo.com>",
431 // take "Birds of France" if it exists otherwise "bof.yahoo.com".
432 listId = mailList.id();
433 const int start = listId.indexOf(QLatin1Char('<'));
434 if (start > 0) {
435 listId.truncate(start - 1);
436 } else if (start == 0) {
437 const int end = listId.lastIndexOf(QLatin1Char('>'));
438 if (end < 1) { // shouldn't happen but account for it anyway
439 listId.remove(0, 1);
440 } else {
441 listId = listId.mid(1, end - 1);
442 }
443 }
444 }
445 mMailingListActionMenu->menu()->clear();
446 qDeleteAll(mMailListActionList);
447 mMailListActionList.clear();
448 mMailingListActionMenu->menu()->setTitle(KStringHandler::rsqueeze(i18n("Mailing List Name: %1", (listId.isEmpty() ? i18n("<unknown>") : listId)), 40));
449 if (mailList.features() & MessageCore::MailingList::ArchivedAt) {
450 // IDEA: this may be something you want to copy - "Copy in submenu"?
451 addMailingListActions(i18n("Open Message in List Archive"), mailList.archivedAtUrls());
452 }
453 if (mailList.features() & MessageCore::MailingList::Post) {
454 addMailingListActions(i18n("Post New Message"), mailList.postUrls());
455 }
456 if (mailList.features() & MessageCore::MailingList::Archive) {
457 addMailingListActions(i18n("Go to Archive"), mailList.archiveUrls());
458 }
459 if (mailList.features() & MessageCore::MailingList::Help) {
460 addMailingListActions(i18n("Request Help"), mailList.helpUrls());
461 }
462 if (mailList.features() & MessageCore::MailingList::Owner) {
463 addMailingListActions(i18nc("Contact the owner of the mailing list", "Contact Owner"), mailList.ownerUrls());
464 }
465 if (mailList.features() & MessageCore::MailingList::Subscribe) {
466 addMailingListActions(i18n("Subscribe to List"), mailList.subscribeUrls());
467 }
468 if (mailList.features() & MessageCore::MailingList::Unsubscribe) {
469 addMailingListActions(i18n("Unsubscribe from List"), mailList.unsubscribeUrls());
470 }
471 mMailingListActionMenu->setEnabled(true);
472
473 QByteArray name;
474 QString value;
475 const QString lname = MailingList::name(message, name, value);
476 if (!lname.isEmpty()) {
477 mListFilterAction->setEnabled(true);
478 mListFilterAction->setText(i18n("Filter on Mailing-List %1...", lname));
479 }
480 }
481 }
482
replyCommand(MessageComposer::ReplyStrategy strategy)483 void MessageActions::replyCommand(MessageComposer::ReplyStrategy strategy)
484 {
485 if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
486 return;
487 }
488
489 const QString text = mMessageView ? mMessageView->copyText() : QString();
490 auto command = new KMReplyCommand(mParent, mCurrentItem, strategy, text);
491 command->setReplyAsHtml(mMessageView ? mMessageView->viewer()->htmlMail() : false);
492 connect(command, &KMCommand::completed, this, &MessageActions::replyActionFinished);
493 command->start();
494 }
495
setMessageView(KMReaderWin * msgView)496 void MessageActions::setMessageView(KMReaderWin *msgView)
497 {
498 mMessageView = msgView;
499 }
500
setupForwardActions(KActionCollection * ac)501 void MessageActions::setupForwardActions(KActionCollection *ac)
502 {
503 disconnect(mForwardActionMenu, SIGNAL(triggered(bool)), nullptr, nullptr);
504 mForwardActionMenu->removeAction(mForwardInlineAction);
505 mForwardActionMenu->removeAction(mForwardAttachedAction);
506
507 if (KMailSettings::self()->forwardingInlineByDefault()) {
508 mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
509 mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
510 ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
511 ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT | Qt::Key_F));
512 QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)), mParent, SLOT(slotForwardInlineMsg()));
513 } else {
514 mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
515 mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
516 ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
517 ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT | Qt::Key_F));
518 QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)), mParent, SLOT(slotForwardAttachedMessage()));
519 }
520 }
521
setupForwardingActionsList(KXMLGUIClient * guiClient)522 void MessageActions::setupForwardingActionsList(KXMLGUIClient *guiClient)
523 {
524 QList<QAction *> forwardActionList;
525 guiClient->unplugActionList(QStringLiteral("forward_action_list"));
526 if (KMailSettings::self()->forwardingInlineByDefault()) {
527 forwardActionList.append(mForwardInlineAction);
528 forwardActionList.append(mForwardAttachedAction);
529 } else {
530 forwardActionList.append(mForwardAttachedAction);
531 forwardActionList.append(mForwardInlineAction);
532 }
533 forwardActionList.append(mRedirectAction);
534 guiClient->plugActionList(QStringLiteral("forward_action_list"), forwardActionList);
535 }
536
slotReplyToMsg()537 void MessageActions::slotReplyToMsg()
538 {
539 replyCommand(MessageComposer::ReplySmart);
540 }
541
slotReplyAuthorToMsg()542 void MessageActions::slotReplyAuthorToMsg()
543 {
544 replyCommand(MessageComposer::ReplyAuthor);
545 }
546
slotReplyListToMsg()547 void MessageActions::slotReplyListToMsg()
548 {
549 replyCommand(MessageComposer::ReplyList);
550 }
551
slotReplyAllToMsg()552 void MessageActions::slotReplyAllToMsg()
553 {
554 replyCommand(MessageComposer::ReplyAll);
555 }
556
slotNoQuoteReplyToMsg()557 void MessageActions::slotNoQuoteReplyToMsg()
558 {
559 if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
560 return;
561 }
562 auto command = new KMReplyCommand(mParent, mCurrentItem, MessageComposer::ReplySmart, QString(), true);
563 command->setReplyAsHtml(mMessageView ? mMessageView->viewer()->htmlMail() : false);
564
565 command->start();
566 }
567
slotRunUrl(QAction * urlAction)568 void MessageActions::slotRunUrl(QAction *urlAction)
569 {
570 const QVariant q = urlAction->data();
571 if (q.type() == QVariant::Url) {
572 auto job = new KIO::OpenUrlJob(q.toUrl());
573 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, mParent));
574 job->start();
575 }
576 }
577
slotMailingListFilter()578 void MessageActions::slotMailingListFilter()
579 {
580 if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
581 return;
582 }
583
584 KMCommand *command = new KMMailingListFilterCommand(mParent, mCurrentItem);
585 command->start();
586 }
587
printMessage(bool preview)588 void MessageActions::printMessage(bool preview)
589 {
590 if (mMessageView) {
591 bool result = false;
592 if (MessageViewer::MessageViewerSettings::self()->printSelectedText()) {
593 result = mMessageView->printSelectedText(preview);
594 }
595 if (!result) {
596 const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
597 const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
598
599 const Akonadi::Item message = mCurrentItem;
600 KMPrintCommandInfo commandInfo;
601 commandInfo.mMsg = message;
602 commandInfo.mHeaderStylePlugin = mMessageView->viewer()->headerStylePlugin();
603 commandInfo.mFormat = mMessageView->viewer()->displayFormatMessageOverwrite();
604 commandInfo.mHtmlLoadExtOverride = mMessageView->viewer()->htmlLoadExternal();
605 commandInfo.mPrintPreview = preview;
606 commandInfo.mUseFixedFont = useFixedFont;
607 commandInfo.mOverrideFont = overrideEncoding;
608 commandInfo.mShowSignatureDetails =
609 mMessageView->viewer()->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
610 commandInfo.mShowEncryptionDetails =
611 mMessageView->viewer()->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
612
613 auto command = new KMPrintCommand(mParent, commandInfo);
614 command->start();
615 }
616 } else {
617 qCWarning(KMAIL_LOG) << "MessageActions::printMessage impossible to do it if we don't have a viewer";
618 }
619 }
620
slotPrintPreviewMsg()621 void MessageActions::slotPrintPreviewMsg()
622 {
623 printMessage(true);
624 }
625
slotPrintMessage()626 void MessageActions::slotPrintMessage()
627 {
628 printMessage(false);
629 }
630
631 /**
632 * This adds a list of actions to mMailingListActionMenu mapping the identifier item to
633 * the url.
634 *
635 * e.g.: item = "Contact Owner"
636 * "Contact Owner (email)" -> KRun( "mailto:bob@arthouseflowers.example.com" )
637 * "Contact Owner (web)" -> KRun( "http://arthouseflowers.example.com/contact-owner.php" )
638 */
addMailingListActions(const QString & item,const QList<QUrl> & list)639 void MessageActions::addMailingListActions(const QString &item, const QList<QUrl> &list)
640 {
641 for (const QUrl &url : list) {
642 addMailingListAction(item, url);
643 }
644 }
645
646 /**
647 * This adds a action to mMailingListActionMenu mapping the identifier item to
648 * the url. See addMailingListActions above.
649 */
addMailingListAction(const QString & item,const QUrl & url)650 void MessageActions::addMailingListAction(const QString &item, const QUrl &url)
651 {
652 QString protocol = url.scheme().toLower();
653 QString prettyUrl = url.toDisplayString();
654 if (protocol == QLatin1String("mailto")) {
655 protocol = i18n("email");
656 prettyUrl.remove(0, 7); // length( "mailto:" )
657 } else if (protocol.startsWith(QLatin1String("http"))) {
658 protocol = i18n("web");
659 }
660 // item is a mailing list url description passed from the updateActions method above.
661 auto *act =
662 new QAction(i18nc("%1 is a 'Contact Owner' or similar action. %2 is a protocol normally web or email though could be irc/ftp or other url variant",
663 "%1 (%2)",
664 item,
665 protocol),
666 this);
667 mMailListActionList.append(act);
668 const QVariant v(url);
669 act->setData(v);
670 KMail::Util::addQActionHelpText(act, prettyUrl);
671 mMailingListActionMenu->addAction(act);
672 }
673
editCurrentMessage()674 void MessageActions::editCurrentMessage()
675 {
676 KMCommand *command = nullptr;
677 if (mCurrentItem.isValid()) {
678 Akonadi::Collection col = mCurrentItem.parentCollection();
679 qCDebug(KMAIL_LOG) << " mCurrentItem.parentCollection()" << mCurrentItem.parentCollection();
680 // edit, unlike send again, removes the message from the folder
681 // we only want that for templates and drafts folders
682 if (col.isValid() && (CommonKernel->folderIsDraftOrOutbox(col) || CommonKernel->folderIsTemplates(col))) {
683 command = new KMEditItemCommand(mParent, mCurrentItem, true);
684 } else {
685 command = new KMEditItemCommand(mParent, mCurrentItem, false);
686 }
687 command->start();
688 } else if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
689 command = new KMEditMessageCommand(mParent, mCurrentItem.payload<KMime::Message::Ptr>());
690 command->start();
691 }
692 }
693
annotateMessage()694 void MessageActions::annotateMessage()
695 {
696 if (!mCurrentItem.isValid()) {
697 return;
698 }
699
700 PimCommon::AnnotationEditDialog dialog(mCurrentItem, mParent);
701 dialog.setAttribute(Qt::WA_DeleteOnClose);
702 dialog.exec();
703 }
704
addWebShortcutsMenu(QMenu * menu,const QString & text)705 void MessageActions::addWebShortcutsMenu(QMenu *menu, const QString &text)
706 {
707 mWebShortcutMenuManager->setSelectedText(text);
708 mWebShortcutMenuManager->addWebShortcutsToMenu(menu);
709 }
710
debugAkonadiSearchAction() const711 QAction *MessageActions::debugAkonadiSearchAction() const
712 {
713 return mDebugAkonadiSearchAction;
714 }
715
addFollowupReminderAction() const716 QAction *MessageActions::addFollowupReminderAction() const
717 {
718 return mAddFollowupReminderAction;
719 }
720
slotDebugAkonadiSearch()721 void MessageActions::slotDebugAkonadiSearch()
722 {
723 if (!mCurrentItem.isValid()) {
724 return;
725 }
726 QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
727 dlg->setAkonadiId(mCurrentItem.id());
728 dlg->setAttribute(Qt::WA_DeleteOnClose);
729 dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Emails);
730 dlg->doSearch();
731 dlg->show();
732 }
733
slotResendMessage()734 void MessageActions::slotResendMessage()
735 {
736 if (!mCurrentItem.isValid()) {
737 return;
738 }
739 KMCommand *command = new KMResendMessageCommand(mParent, mCurrentItem);
740 command->start();
741 }
742
newMessageFromTemplateAction() const743 QAction *MessageActions::newMessageFromTemplateAction() const
744 {
745 return mNewMessageFromTemplateAction;
746 }
747
sendAgainAction() const748 QAction *MessageActions::sendAgainAction() const
749 {
750 return mSendAgainAction;
751 }
752
slotAddFollowupReminder()753 void MessageActions::slotAddFollowupReminder()
754 {
755 if (!mCurrentItem.isValid()) {
756 return;
757 }
758
759 QPointer<MessageComposer::FollowUpReminderSelectDateDialog> dlg = new MessageComposer::FollowUpReminderSelectDateDialog(mParent);
760 if (dlg->exec()) {
761 const QDate date = dlg->selectedDate();
762 auto job = new CreateFollowupReminderOnExistingMessageJob(this);
763 job->setDate(date);
764 job->setCollection(dlg->collection());
765 job->setMessageItem(mCurrentItem);
766 job->start();
767 }
768 delete dlg;
769 }
770
slotExportToPdf()771 void MessageActions::slotExportToPdf()
772 {
773 QString fileName = MessageViewer::Util::generateFileNameForExtension(mCurrentItem, QStringLiteral(".pdf"));
774 fileName = QFileDialog::getSaveFileName(mParent, i18n("Export to PDF"), QDir::homePath() + QLatin1Char('/') + fileName, i18n("PDF document (*.pdf)"));
775 if (!fileName.isEmpty()) {
776 mMessageView->viewer()->exportToPdf(fileName);
777 }
778 }
779
currentItem() const780 Akonadi::Item MessageActions::currentItem() const
781 {
782 return mCurrentItem;
783 }
784
exportToPdfAction() const785 QAction *MessageActions::exportToPdfAction() const
786 {
787 return mExportToPdfAction;
788 }
789