File indexing completed on 2024-05-12 05:20:45

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "messageactions.h"
0008 
0009 #include "kmcommands.h"
0010 #include "kmkernel.h"
0011 #include "kmreaderwin.h"
0012 #include "settings/kmailsettings.h"
0013 #include "util.h"
0014 #include <MailCommon/MailKernel>
0015 #include <TemplateParser/CustomTemplatesMenu>
0016 
0017 #include <MessageCore/MailingList>
0018 #include <MessageCore/MessageCoreSettings>
0019 #include <MessageCore/StringUtil>
0020 #include <MessageViewer/HeaderStylePlugin>
0021 #include <MessageViewer/MessageViewerSettings>
0022 #include <PimCommonAkonadi/AnnotationDialog>
0023 
0024 #include <Akonadi/ChangeRecorder>
0025 #include <Akonadi/ItemFetchJob>
0026 #include <Akonadi/MessageParts>
0027 #include <Debug/akonadisearchdebugdialog.h>
0028 #include <KIO/KUriFilterSearchProviderActions>
0029 #include <QAction>
0030 
0031 #include "job/createfollowupreminderonexistingmessagejob.h"
0032 #include <MessageComposer/FollowUpReminderSelectDateDialog>
0033 
0034 #include "kmail_debug.h"
0035 #include <Akonadi/ItemFetchJob>
0036 #include <KActionCollection>
0037 #include <KActionMenu>
0038 #include <KIO/JobUiDelegateFactory>
0039 #include <KIO/OpenUrlJob>
0040 #include <KLocalizedString>
0041 #include <KStringHandler>
0042 #include <KUriFilter>
0043 #include <KXMLGUIClient>
0044 #include <QFileDialog>
0045 #include <QIcon>
0046 #include <QMenu>
0047 
0048 #include "folderarchive/folderarchivemanager.h"
0049 #include <Akonadi/Collection>
0050 #include <Akonadi/EntityAnnotationsAttribute>
0051 #include <Akonadi/StandardMailActionManager>
0052 #include <MailCommon/MailUtil>
0053 #include <MessageViewer/MessageViewerUtil>
0054 #include <QVariant>
0055 #include <QWidget>
0056 
0057 using namespace KMail;
0058 
0059 MessageActions::MessageActions(KActionCollection *ac, QWidget *parent)
0060     : QObject(parent)
0061     , mParent(parent)
0062     , mReplyActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18nc("Message->", "&Reply"), this))
0063     , mReplyAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("&Reply..."), this))
0064     , mReplyAllAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-all")), i18n("Reply to &All..."), this))
0065     , mReplyAuthorAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-sender")), i18n("Reply to A&uthor..."), this))
0066     , mReplyListAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-reply-list")), i18n("Reply to Mailing-&List..."), this))
0067     , mNoQuoteReplyAction(new QAction(i18n("Reply Without &Quote..."), this))
0068     , mForwardInlineAction(new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("@action:inmenu Message->Forward->", "&Inline..."), this))
0069     , mForwardAttachedAction(
0070           new QAction(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("@action:inmenu Message->Forward->", "As &Attachment..."), this))
0071     , mRedirectAction(new QAction(i18nc("Message->Forward->", "&Redirect..."), this))
0072     , mNewToRecipientsAction(new QAction(i18n("New Message to Recipients..."), this))
0073     , mStatusMenu(new KActionMenu(i18n("Mar&k Message"), this))
0074     , mForwardActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-forward")), i18nc("Message->", "&Forward"), this))
0075     , mMailingListActionMenu(new KActionMenu(QIcon::fromTheme(QStringLiteral("mail-message-new-list")), i18nc("Message->", "Mailing-&List"), this))
0076     , mAnnotateAction(new QAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")), i18n("Add Note..."), this))
0077     , mEditAsNewAction(new QAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18n("&Edit As New"), this))
0078     , mListFilterAction(new QAction(i18n("Filter on Mailing-&List..."), this))
0079     , mAddFollowupReminderAction(new QAction(i18n("Add Followup Reminder..."), this))
0080     , mDebugAkonadiSearchAction(new QAction(QStringLiteral("Debug Akonadi Search..."), this)) /* dont translate it*/
0081     , mSendAgainAction(new QAction(i18n("Send A&gain..."), this))
0082     , mNewMessageFromTemplateAction(new QAction(QIcon::fromTheme(QStringLiteral("document-new")), i18n("New Message From &Template"), this))
0083     , mWebShortcutMenuManager(new KIO::KUriFilterSearchProviderActions(this))
0084     , mExportToPdfAction(new QAction(QIcon::fromTheme(QStringLiteral("application-pdf")), i18n("Export to PDF..."), this))
0085     , mArchiveMessageAction(new QAction(i18nc("@action:inmenu", "Archive Message"), this))
0086 
0087 {
0088     ac->addAction(QStringLiteral("message_reply_menu"), mReplyActionMenu);
0089     connect(mReplyActionMenu, &KActionMenu::triggered, this, &MessageActions::slotReplyToMsg);
0090 
0091     ac->addAction(QStringLiteral("reply"), mReplyAction);
0092     ac->setDefaultShortcut(mReplyAction, Qt::Key_R);
0093     connect(mReplyAction, &QAction::triggered, this, &MessageActions::slotReplyToMsg);
0094     mReplyActionMenu->addAction(mReplyAction);
0095 
0096     ac->addAction(QStringLiteral("reply_author"), mReplyAuthorAction);
0097     ac->setDefaultShortcut(mReplyAuthorAction, Qt::SHIFT | Qt::Key_A);
0098     connect(mReplyAuthorAction, &QAction::triggered, this, &MessageActions::slotReplyAuthorToMsg);
0099     mReplyActionMenu->addAction(mReplyAuthorAction);
0100 
0101     ac->addAction(QStringLiteral("reply_all"), mReplyAllAction);
0102     ac->setDefaultShortcut(mReplyAllAction, Qt::Key_A);
0103     connect(mReplyAllAction, &QAction::triggered, this, &MessageActions::slotReplyAllToMsg);
0104     mReplyActionMenu->addAction(mReplyAllAction);
0105 
0106     ac->addAction(QStringLiteral("reply_list"), mReplyListAction);
0107 
0108     ac->setDefaultShortcut(mReplyListAction, Qt::Key_L);
0109     connect(mReplyListAction, &QAction::triggered, this, &MessageActions::slotReplyListToMsg);
0110     mReplyActionMenu->addAction(mReplyListAction);
0111 
0112     ac->addAction(QStringLiteral("noquotereply"), mNoQuoteReplyAction);
0113     ac->setDefaultShortcut(mNoQuoteReplyAction, Qt::SHIFT | Qt::Key_R);
0114     connect(mNoQuoteReplyAction, &QAction::triggered, this, &MessageActions::slotNoQuoteReplyToMsg);
0115 
0116     ac->addAction(QStringLiteral("mlist_filter"), mListFilterAction);
0117     connect(mListFilterAction, &QAction::triggered, this, &MessageActions::slotMailingListFilter);
0118 
0119     ac->addAction(QStringLiteral("set_status"), mStatusMenu);
0120 
0121     ac->addAction(QStringLiteral("annotate"), mAnnotateAction);
0122     connect(mAnnotateAction, &QAction::triggered, this, &MessageActions::annotateMessage);
0123 
0124     ac->addAction(QStringLiteral("editasnew"), mEditAsNewAction);
0125     connect(mEditAsNewAction, &QAction::triggered, this, &MessageActions::editCurrentMessage);
0126     ac->setDefaultShortcut(mEditAsNewAction, Qt::Key_T);
0127 
0128     mPrintAction = KStandardAction::print(this, &MessageActions::slotPrintMessage, ac);
0129     mPrintPreviewAction = KStandardAction::printPreview(this, &MessageActions::slotPrintPreviewMsg, ac);
0130 
0131     ac->addAction(QStringLiteral("message_forward"), mForwardActionMenu);
0132 
0133     connect(mForwardAttachedAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardAttachedMessage()));
0134 
0135     ac->addAction(QStringLiteral("message_forward_as_attachment"), mForwardAttachedAction);
0136 
0137     connect(mForwardInlineAction, SIGNAL(triggered(bool)), parent, SLOT(slotForwardInlineMsg()));
0138 
0139     ac->addAction(QStringLiteral("message_forward_inline"), mForwardInlineAction);
0140 
0141     setupForwardActions(ac);
0142 
0143     ac->addAction(QStringLiteral("new_to_recipients"), mNewToRecipientsAction);
0144     connect(mNewToRecipientsAction, SIGNAL(triggered(bool)), parent, SLOT(slotNewMessageToRecipients()));
0145 
0146     ac->addAction(QStringLiteral("message_forward_redirect"), mRedirectAction);
0147     connect(mRedirectAction, SIGNAL(triggered(bool)), parent, SLOT(slotRedirectMessage()));
0148 
0149     ac->setDefaultShortcut(mRedirectAction, QKeySequence(Qt::Key_E));
0150     mForwardActionMenu->addAction(mRedirectAction);
0151 
0152     connect(mMailingListActionMenu->menu(), &QMenu::triggered, this, &MessageActions::slotRunUrl);
0153     ac->addAction(QStringLiteral("mailing_list"), mMailingListActionMenu);
0154     mMailingListActionMenu->setEnabled(false);
0155 
0156     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemChanged, this, &MessageActions::slotItemModified);
0157     connect(kmkernel->folderCollectionMonitor(), &Akonadi::Monitor::itemRemoved, this, &MessageActions::slotItemRemoved);
0158 
0159     mCustomTemplatesMenu = new TemplateParser::CustomTemplatesMenu(parent, ac);
0160 
0161     connect(mCustomTemplatesMenu, SIGNAL(replyTemplateSelected(QString)), parent, SLOT(slotCustomReplyToMsg(QString)));
0162     connect(mCustomTemplatesMenu, SIGNAL(replyAllTemplateSelected(QString)), parent, SLOT(slotCustomReplyAllToMsg(QString)));
0163     connect(mCustomTemplatesMenu, SIGNAL(forwardTemplateSelected(QString)), parent, SLOT(slotCustomForwardMsg(QString)));
0164     connect(KMKernel::self(), &KMKernel::customTemplatesChanged, mCustomTemplatesMenu, &TemplateParser::CustomTemplatesMenu::update);
0165 
0166     forwardMenu()->addSeparator();
0167     forwardMenu()->addAction(mCustomTemplatesMenu->forwardActionMenu());
0168     replyMenu()->addSeparator();
0169     replyMenu()->addAction(mCustomTemplatesMenu->replyActionMenu());
0170     replyMenu()->addAction(mCustomTemplatesMenu->replyAllActionMenu());
0171 
0172     // Don't translate it. Shown only when we set env variable AKONADI_SEARCH_DEBUG
0173     connect(mDebugAkonadiSearchAction, &QAction::triggered, this, &MessageActions::slotDebugAkonadiSearch);
0174 
0175     ac->addAction(QStringLiteral("message_followup_reminder"), mAddFollowupReminderAction);
0176     connect(mAddFollowupReminderAction, &QAction::triggered, this, &MessageActions::slotAddFollowupReminder);
0177 
0178     ac->addAction(QStringLiteral("send_again"), mSendAgainAction);
0179     connect(mSendAgainAction, &QAction::triggered, this, &MessageActions::slotResendMessage);
0180 
0181     ac->addAction(QStringLiteral("use_template"), mNewMessageFromTemplateAction);
0182     connect(mNewMessageFromTemplateAction, &QAction::triggered, this, &MessageActions::slotUseTemplate);
0183     ac->setDefaultShortcut(mNewMessageFromTemplateAction, QKeySequence(Qt::SHIFT | Qt::Key_N));
0184 
0185     ac->addAction(QStringLiteral("file_export_pdf"), mExportToPdfAction);
0186     connect(mExportToPdfAction, &QAction::triggered, this, &MessageActions::slotExportToPdf);
0187 
0188     ac->addAction(QStringLiteral("archive_message"), mArchiveMessageAction);
0189     connect(mArchiveMessageAction, &QAction::triggered, this, &MessageActions::slotArchiveMessage);
0190 
0191     updateActions();
0192 }
0193 
0194 MessageActions::~MessageActions()
0195 {
0196     delete mCustomTemplatesMenu;
0197 }
0198 
0199 void MessageActions::fillAkonadiStandardAction(Akonadi::StandardMailActionManager *akonadiStandardActionManager)
0200 {
0201     QAction *action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsRead);
0202     mStatusMenu->addAction(action);
0203 
0204     action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsUnread);
0205     mStatusMenu->addAction(action);
0206 
0207     mStatusMenu->addSeparator();
0208     action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsImportant);
0209     mStatusMenu->addAction(action);
0210 
0211     action = akonadiStandardActionManager->action(Akonadi::StandardMailActionManager::MarkMailAsActionItem);
0212     mStatusMenu->addAction(action);
0213 }
0214 
0215 TemplateParser::CustomTemplatesMenu *MessageActions::customTemplatesMenu() const
0216 {
0217     return mCustomTemplatesMenu;
0218 }
0219 
0220 void MessageActions::slotUseTemplate()
0221 {
0222     if (!mCurrentItem.isValid()) {
0223         return;
0224     }
0225     KMCommand *command = new KMUseTemplateCommand(mParent, mCurrentItem);
0226     command->start();
0227 }
0228 
0229 QAction *MessageActions::editAsNewAction() const
0230 {
0231     return mEditAsNewAction;
0232 }
0233 
0234 void MessageActions::setCurrentMessage(const Akonadi::Item &msg, const Akonadi::Item::List &items)
0235 {
0236     mCurrentItem = msg;
0237 
0238     if (!items.isEmpty()) {
0239         if (msg.isValid()) {
0240             mVisibleItems = items;
0241         } else {
0242             mVisibleItems.clear();
0243         }
0244     }
0245 
0246     if (!msg.isValid()) {
0247         mVisibleItems.clear();
0248         clearMailingListActions();
0249     }
0250 
0251     updateActions();
0252 }
0253 
0254 KActionMenu *MessageActions::replyMenu() const
0255 {
0256     return mReplyActionMenu;
0257 }
0258 
0259 QAction *MessageActions::replyListAction() const
0260 {
0261     return mReplyListAction;
0262 }
0263 
0264 QAction *MessageActions::forwardInlineAction() const
0265 {
0266     return mForwardInlineAction;
0267 }
0268 
0269 QAction *MessageActions::forwardAttachedAction() const
0270 {
0271     return mForwardAttachedAction;
0272 }
0273 
0274 QAction *MessageActions::redirectAction() const
0275 {
0276     return mRedirectAction;
0277 }
0278 
0279 QAction *MessageActions::newToRecipientsAction() const
0280 {
0281     return mNewToRecipientsAction;
0282 }
0283 
0284 KActionMenu *MessageActions::messageStatusMenu() const
0285 {
0286     return mStatusMenu;
0287 }
0288 
0289 KActionMenu *MessageActions::forwardMenu() const
0290 {
0291     return mForwardActionMenu;
0292 }
0293 
0294 QAction *MessageActions::annotateAction() const
0295 {
0296     return mAnnotateAction;
0297 }
0298 
0299 QAction *MessageActions::printAction() const
0300 {
0301     return mPrintAction;
0302 }
0303 
0304 QAction *MessageActions::printPreviewAction() const
0305 {
0306     return mPrintPreviewAction;
0307 }
0308 
0309 QAction *MessageActions::listFilterAction() const
0310 {
0311     return mListFilterAction;
0312 }
0313 
0314 KActionMenu *MessageActions::mailingListActionMenu() const
0315 {
0316     return mMailingListActionMenu;
0317 }
0318 
0319 void MessageActions::slotItemRemoved(const Akonadi::Item &item)
0320 {
0321     if (item == mCurrentItem) {
0322         mCurrentItem = Akonadi::Item();
0323         updateActions();
0324     }
0325 }
0326 
0327 void MessageActions::slotItemModified(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers)
0328 {
0329     Q_UNUSED(partIdentifiers)
0330     if (item == mCurrentItem) {
0331         mCurrentItem = item;
0332         const int numberOfVisibleItems = mVisibleItems.count();
0333         for (int i = 0; i < numberOfVisibleItems; ++i) {
0334             Akonadi::Item it = mVisibleItems.at(i);
0335             if (item == it) {
0336                 mVisibleItems[i] = item;
0337             }
0338         }
0339         updateActions();
0340     }
0341 }
0342 
0343 void MessageActions::updateActions()
0344 {
0345     const bool hasPayload = mCurrentItem.hasPayload<KMime::Message::Ptr>();
0346     bool itemValid = mCurrentItem.isValid();
0347     Akonadi::Collection parent;
0348     if (itemValid) { //=> valid
0349         parent = mCurrentItem.parentCollection();
0350     }
0351     if (parent.isValid()) {
0352         if (CommonKernel->folderIsTemplates(parent)) {
0353             itemValid = false;
0354         }
0355     }
0356 
0357     const bool multiVisible = !mVisibleItems.isEmpty() || mCurrentItem.isValid();
0358     const bool uniqItem = (itemValid || hasPayload) && (mVisibleItems.count() <= 1);
0359     mReplyActionMenu->setEnabled(hasPayload);
0360     mReplyAction->setEnabled(hasPayload);
0361     mNoQuoteReplyAction->setEnabled(hasPayload);
0362     mReplyAuthorAction->setEnabled(hasPayload);
0363     mReplyAllAction->setEnabled(hasPayload);
0364     mReplyListAction->setEnabled(hasPayload);
0365     mNoQuoteReplyAction->setEnabled(hasPayload);
0366     mSendAgainAction->setEnabled(hasPayload);
0367 
0368     mAnnotateAction->setEnabled(uniqItem);
0369     mAddFollowupReminderAction->setEnabled(uniqItem);
0370     if (!mCurrentItem.hasAttribute<Akonadi::EntityAnnotationsAttribute>()) {
0371         mAnnotateAction->setText(i18n("Add Note..."));
0372     } else {
0373         mAnnotateAction->setText(i18n("Edit Note..."));
0374     }
0375 
0376     mStatusMenu->setEnabled(multiVisible);
0377 
0378     mPrintAction->setEnabled(mMessageView != nullptr);
0379     mPrintPreviewAction->setEnabled(mMessageView != nullptr);
0380     mExportToPdfAction->setEnabled(uniqItem);
0381     if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
0382         if (mCurrentItem.loadedPayloadParts().contains("RFC822")) {
0383             updateMailingListActions(mCurrentItem);
0384         } else {
0385             auto job = new Akonadi::ItemFetchJob(mCurrentItem);
0386             job->fetchScope().fetchAllAttributes();
0387             job->fetchScope().fetchFullPayload(true);
0388             job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header);
0389             job->fetchScope().fetchAttribute<Akonadi::EntityAnnotationsAttribute>();
0390             connect(job, &Akonadi::ItemFetchJob::result, this, &MessageActions::slotUpdateActionsFetchDone);
0391         }
0392     }
0393     mEditAsNewAction->setEnabled(uniqItem);
0394     mArchiveMessageAction->setEnabled(hasPayload);
0395 }
0396 
0397 void MessageActions::slotUpdateActionsFetchDone(KJob *job)
0398 {
0399     if (job->error()) {
0400         return;
0401     }
0402 
0403     auto fetchJob = static_cast<Akonadi::ItemFetchJob *>(job);
0404     if (fetchJob->items().isEmpty()) {
0405         return;
0406     }
0407     const Akonadi::Item messageItem = fetchJob->items().constFirst();
0408     if (messageItem == mCurrentItem) {
0409         mCurrentItem = messageItem;
0410         updateMailingListActions(messageItem);
0411     }
0412 }
0413 
0414 void MessageActions::clearMailingListActions()
0415 {
0416     mMailingListActionMenu->setEnabled(false);
0417     mListFilterAction->setEnabled(false);
0418     mListFilterAction->setText(i18n("Filter on Mailing-List..."));
0419 }
0420 
0421 void MessageActions::updateMailingListActions(const Akonadi::Item &messageItem)
0422 {
0423     if (!messageItem.hasPayload<KMime::Message::Ptr>()) {
0424         return;
0425     }
0426     auto message = messageItem.payload<KMime::Message::Ptr>();
0427     const MessageCore::MailingList mailList = MessageCore::MailingList::detect(message);
0428 
0429     if (mailList.features() == MessageCore::MailingList::None) {
0430         clearMailingListActions();
0431     } else {
0432         // A mailing list menu with only a title is pretty boring
0433         // so make sure theres at least some content
0434         QString listId;
0435         if (mailList.features() & MessageCore::MailingList::Id) {
0436             // From a list-id in the form, "Birds of France <bof.yahoo.com>",
0437             // take "Birds of France" if it exists otherwise "bof.yahoo.com".
0438             listId = mailList.id();
0439             const int start = listId.indexOf(QLatin1Char('<'));
0440             if (start > 0) {
0441                 listId.truncate(start - 1);
0442             } else if (start == 0) {
0443                 const int end = listId.lastIndexOf(QLatin1Char('>'));
0444                 if (end < 1) { // shouldn't happen but account for it anyway
0445                     listId.remove(0, 1);
0446                 } else {
0447                     listId = listId.mid(1, end - 1);
0448                 }
0449             }
0450         }
0451         mMailingListActionMenu->menu()->clear();
0452         qDeleteAll(mMailListActionList);
0453         mMailListActionList.clear();
0454         mMailingListActionMenu->menu()->setTitle(KStringHandler::rsqueeze(i18n("Mailing List Name: %1", (listId.isEmpty() ? i18n("<unknown>") : listId)), 40));
0455         if (mailList.features() & MessageCore::MailingList::ArchivedAt) {
0456             // IDEA: this may be something you want to copy - "Copy in submenu"?
0457             addMailingListActions(i18n("Open Message in List Archive"), mailList.archivedAtUrls());
0458         }
0459         if (mailList.features() & MessageCore::MailingList::Post) {
0460             addMailingListActions(i18n("Post New Message"), mailList.postUrls());
0461         }
0462         if (mailList.features() & MessageCore::MailingList::Archive) {
0463             addMailingListActions(i18n("Go to Archive"), mailList.archiveUrls());
0464         }
0465         if (mailList.features() & MessageCore::MailingList::Help) {
0466             addMailingListActions(i18n("Request Help"), mailList.helpUrls());
0467         }
0468         if (mailList.features() & MessageCore::MailingList::Owner) {
0469             addMailingListActions(i18nc("Contact the owner of the mailing list", "Contact Owner"), mailList.ownerUrls());
0470         }
0471         if (mailList.features() & MessageCore::MailingList::Subscribe) {
0472             addMailingListActions(i18n("Subscribe to List"), mailList.subscribeUrls());
0473         }
0474         if (mailList.features() & MessageCore::MailingList::Unsubscribe) {
0475             addMailingListActions(i18n("Unsubscribe from List"), mailList.unsubscribeUrls());
0476         }
0477         mMailingListActionMenu->setEnabled(true);
0478 
0479         QByteArray name;
0480         QString value;
0481         const QString lname = MailingList::name(message, name, value);
0482         if (!lname.isEmpty()) {
0483             mListFilterAction->setEnabled(true);
0484             mListFilterAction->setText(i18n("Filter on Mailing-List %1...", lname));
0485         }
0486     }
0487 }
0488 
0489 void MessageActions::replyCommand(MessageComposer::ReplyStrategy strategy)
0490 {
0491     if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
0492         return;
0493     }
0494 
0495     const QString text = mMessageView ? mMessageView->copyText() : QString();
0496     auto command = new KMReplyCommand(mParent, mCurrentItem, strategy, text);
0497     command->setReplyAsHtml(mMessageView ? mMessageView->viewer()->htmlMail() : false);
0498     connect(command, &KMCommand::completed, this, &MessageActions::replyActionFinished);
0499     command->start();
0500 }
0501 
0502 void MessageActions::setMessageView(KMReaderWin *msgView)
0503 {
0504     mMessageView = msgView;
0505 }
0506 
0507 void MessageActions::setupForwardActions(KActionCollection *ac)
0508 {
0509     disconnect(mForwardActionMenu, SIGNAL(triggered(bool)), nullptr, nullptr);
0510     mForwardActionMenu->removeAction(mForwardInlineAction);
0511     mForwardActionMenu->removeAction(mForwardAttachedAction);
0512 
0513     if (KMailSettings::self()->forwardingInlineByDefault()) {
0514         mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
0515         mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
0516         ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
0517         ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT | Qt::Key_F));
0518         QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)), mParent, SLOT(slotForwardInlineMsg()));
0519     } else {
0520         mForwardActionMenu->insertAction(mRedirectAction, mForwardAttachedAction);
0521         mForwardActionMenu->insertAction(mRedirectAction, mForwardInlineAction);
0522         ac->setDefaultShortcut(mForwardInlineAction, QKeySequence(Qt::Key_F));
0523         ac->setDefaultShortcut(mForwardAttachedAction, QKeySequence(Qt::SHIFT | Qt::Key_F));
0524         QObject::connect(mForwardActionMenu, SIGNAL(triggered(bool)), mParent, SLOT(slotForwardAttachedMessage()));
0525     }
0526 }
0527 
0528 void MessageActions::setupForwardingActionsList(KXMLGUIClient *guiClient)
0529 {
0530     QList<QAction *> forwardActionList;
0531     guiClient->unplugActionList(QStringLiteral("forward_action_list"));
0532     if (KMailSettings::self()->forwardingInlineByDefault()) {
0533         forwardActionList.append(mForwardInlineAction);
0534         forwardActionList.append(mForwardAttachedAction);
0535     } else {
0536         forwardActionList.append(mForwardAttachedAction);
0537         forwardActionList.append(mForwardInlineAction);
0538     }
0539     forwardActionList.append(mRedirectAction);
0540     guiClient->plugActionList(QStringLiteral("forward_action_list"), forwardActionList);
0541 }
0542 
0543 void MessageActions::slotReplyToMsg()
0544 {
0545     replyCommand(MessageComposer::ReplySmart);
0546 }
0547 
0548 void MessageActions::slotReplyAuthorToMsg()
0549 {
0550     replyCommand(MessageComposer::ReplyAuthor);
0551 }
0552 
0553 void MessageActions::slotReplyListToMsg()
0554 {
0555     replyCommand(MessageComposer::ReplyList);
0556 }
0557 
0558 void MessageActions::slotReplyAllToMsg()
0559 {
0560     replyCommand(MessageComposer::ReplyAll);
0561 }
0562 
0563 void MessageActions::slotNoQuoteReplyToMsg()
0564 {
0565     if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
0566         return;
0567     }
0568     auto command = new KMReplyCommand(mParent, mCurrentItem, MessageComposer::ReplySmart, QString(), true);
0569     command->setReplyAsHtml(mMessageView ? mMessageView->viewer()->htmlMail() : false);
0570 
0571     command->start();
0572 }
0573 
0574 void MessageActions::slotRunUrl(QAction *urlAction)
0575 {
0576     const QVariant q = urlAction->data();
0577     if (q.userType() == QMetaType::QUrl) {
0578         auto job = new KIO::OpenUrlJob(q.toUrl());
0579         job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, mParent));
0580         job->start();
0581     }
0582 }
0583 
0584 void MessageActions::slotMailingListFilter()
0585 {
0586     if (!mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
0587         return;
0588     }
0589 
0590     KMCommand *command = new KMMailingListFilterCommand(mParent, mCurrentItem);
0591     command->start();
0592 }
0593 
0594 void MessageActions::printMessage(bool preview)
0595 {
0596     if (mMessageView) {
0597         bool result = false;
0598         if (MessageViewer::MessageViewerSettings::self()->printSelectedText()) {
0599             result = mMessageView->printSelectedText(preview);
0600         }
0601         if (!result) {
0602             const bool useFixedFont = MessageViewer::MessageViewerSettings::self()->useFixedFont();
0603             const QString overrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding();
0604 
0605             const Akonadi::Item message = mCurrentItem;
0606             KMPrintCommandInfo commandInfo;
0607             commandInfo.mMsg = message;
0608             commandInfo.mHeaderStylePlugin = mMessageView->viewer()->headerStylePlugin();
0609             commandInfo.mFormat = mMessageView->viewer()->displayFormatMessageOverwrite();
0610             commandInfo.mHtmlLoadExtOverride = mMessageView->viewer()->htmlLoadExternal();
0611             commandInfo.mPrintPreview = preview;
0612             commandInfo.mUseFixedFont = useFixedFont;
0613             commandInfo.mOverrideFont = overrideEncoding;
0614             commandInfo.mShowSignatureDetails =
0615                 mMessageView->viewer()->showSignatureDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
0616             commandInfo.mShowEncryptionDetails =
0617                 mMessageView->viewer()->showEncryptionDetails() || MessageViewer::MessageViewerSettings::self()->alwaysShowEncryptionSignatureDetails();
0618 
0619             auto command = new KMPrintCommand(mParent, commandInfo);
0620             command->start();
0621         }
0622     } else {
0623         qCWarning(KMAIL_LOG) << "MessageActions::printMessage impossible to do it if we don't have a viewer";
0624     }
0625 }
0626 
0627 void MessageActions::slotPrintPreviewMsg()
0628 {
0629     printMessage(true);
0630 }
0631 
0632 void MessageActions::slotPrintMessage()
0633 {
0634     printMessage(false);
0635 }
0636 
0637 /**
0638  * This adds a list of actions to mMailingListActionMenu mapping the identifier item to
0639  * the url.
0640  *
0641  * e.g.: item = "Contact Owner"
0642  * "Contact Owner (email)" -> KRun( "mailto:bob@arthouseflowers.example.com" )
0643  * "Contact Owner (web)" -> KRun( "http://arthouseflowers.example.com/contact-owner.php" )
0644  */
0645 void MessageActions::addMailingListActions(const QString &item, const QList<QUrl> &list)
0646 {
0647     for (const QUrl &url : list) {
0648         addMailingListAction(item, url);
0649     }
0650 }
0651 
0652 /**
0653  * This adds a action to mMailingListActionMenu mapping the identifier item to
0654  * the url. See addMailingListActions above.
0655  */
0656 void MessageActions::addMailingListAction(const QString &item, const QUrl &url)
0657 {
0658     QString protocol = url.scheme().toLower();
0659     QString prettyUrl = url.toDisplayString();
0660     if (protocol == QLatin1StringView("mailto")) {
0661         protocol = i18n("email");
0662         prettyUrl.remove(0, 7); // length( "mailto:" )
0663     } else if (protocol.startsWith(QLatin1StringView("http"))) {
0664         protocol = i18n("web");
0665     }
0666     // item is a mailing list url description passed from the updateActions method above.
0667     auto act =
0668         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",
0669                           "%1 (%2)",
0670                           item,
0671                           protocol),
0672                     this);
0673     mMailListActionList.append(act);
0674     const QVariant v(url);
0675     act->setData(v);
0676     KMail::Util::addQActionHelpText(act, prettyUrl);
0677     mMailingListActionMenu->addAction(act);
0678 }
0679 
0680 void MessageActions::editCurrentMessage()
0681 {
0682     KMCommand *command = nullptr;
0683     if (mCurrentItem.isValid()) {
0684         Akonadi::Collection col = mCurrentItem.parentCollection();
0685         qCDebug(KMAIL_LOG) << " mCurrentItem.parentCollection()" << mCurrentItem.parentCollection();
0686         // edit, unlike send again, removes the message from the folder
0687         // we only want that for templates and drafts folders
0688         if (col.isValid() && (CommonKernel->folderIsDraftOrOutbox(col) || CommonKernel->folderIsTemplates(col))) {
0689             command = new KMEditItemCommand(mParent, mCurrentItem, true);
0690         } else {
0691             command = new KMEditItemCommand(mParent, mCurrentItem, false);
0692         }
0693         command->start();
0694     } else if (mCurrentItem.hasPayload<KMime::Message::Ptr>()) {
0695         command = new KMEditMessageCommand(mParent, mCurrentItem.payload<KMime::Message::Ptr>());
0696         command->start();
0697     }
0698 }
0699 
0700 void MessageActions::annotateMessage()
0701 {
0702     if (!mCurrentItem.isValid()) {
0703         return;
0704     }
0705 
0706     auto dialog = new PimCommon::AnnotationEditDialog(mCurrentItem, mParent);
0707     dialog->exec();
0708     delete dialog;
0709 }
0710 
0711 void MessageActions::addWebShortcutsMenu(QMenu *menu, const QString &text)
0712 {
0713     mWebShortcutMenuManager->setSelectedText(text);
0714     mWebShortcutMenuManager->addWebShortcutsToMenu(menu);
0715 }
0716 
0717 QAction *MessageActions::debugAkonadiSearchAction() const
0718 {
0719     return mDebugAkonadiSearchAction;
0720 }
0721 
0722 QAction *MessageActions::addFollowupReminderAction() const
0723 {
0724     return mAddFollowupReminderAction;
0725 }
0726 
0727 void MessageActions::slotDebugAkonadiSearch()
0728 {
0729     if (!mCurrentItem.isValid()) {
0730         return;
0731     }
0732     QPointer<Akonadi::Search::AkonadiSearchDebugDialog> dlg = new Akonadi::Search::AkonadiSearchDebugDialog;
0733     dlg->setAkonadiId(mCurrentItem.id());
0734     dlg->setAttribute(Qt::WA_DeleteOnClose);
0735     dlg->setSearchType(Akonadi::Search::AkonadiSearchDebugSearchPathComboBox::Emails);
0736     dlg->doSearch();
0737     dlg->show();
0738 }
0739 
0740 void MessageActions::slotResendMessage()
0741 {
0742     // mCurrentItem.isValid() may be false here if message was imported via 'File' -> 'Open...'
0743     KMCommand *command = new KMResendMessageCommand(mParent, mCurrentItem);
0744     command->start();
0745 }
0746 
0747 QAction *MessageActions::newMessageFromTemplateAction() const
0748 {
0749     return mNewMessageFromTemplateAction;
0750 }
0751 
0752 QAction *MessageActions::sendAgainAction() const
0753 {
0754     return mSendAgainAction;
0755 }
0756 
0757 void MessageActions::slotAddFollowupReminder()
0758 {
0759     if (!mCurrentItem.isValid()) {
0760         return;
0761     }
0762 
0763     QPointer<MessageComposer::FollowUpReminderSelectDateDialog> dlg = new MessageComposer::FollowUpReminderSelectDateDialog(mParent);
0764     if (dlg->exec()) {
0765         const QDate date = dlg->selectedDate();
0766         auto job = new CreateFollowupReminderOnExistingMessageJob(this);
0767         job->setDate(date);
0768         job->setCollection(dlg->collection());
0769         job->setMessageItem(mCurrentItem);
0770         job->start();
0771     }
0772     delete dlg;
0773 }
0774 
0775 void MessageActions::slotExportToPdf()
0776 {
0777     QString fileName = MessageViewer::Util::generateFileNameForExtension(mCurrentItem, QStringLiteral(".pdf"));
0778     fileName = QFileDialog::getSaveFileName(mParent, i18n("Export to PDF"), QDir::homePath() + QLatin1Char('/') + fileName, i18n("PDF document (*.pdf)"));
0779     if (!fileName.isEmpty()) {
0780         mMessageView->viewer()->exportToPdf(fileName);
0781     }
0782 }
0783 
0784 void MessageActions::slotArchiveMessage()
0785 {
0786     if (!mCurrentItem.isValid() || !mCurrentItem.parentCollection().isValid()) {
0787         return;
0788     }
0789     Akonadi::Item::List items;
0790     items << mCurrentItem;
0791     auto resource = CommonKernel->collectionFromId(mCurrentItem.parentCollection().id()).resource();
0792     KMKernel::self()->folderArchiveManager()->setArchiveItems(items, resource);
0793 }
0794 
0795 Akonadi::Item MessageActions::currentItem() const
0796 {
0797     return mCurrentItem;
0798 }
0799 
0800 QAction *MessageActions::exportToPdfAction() const
0801 {
0802     return mExportToPdfAction;
0803 }
0804 
0805 QAction *MessageActions::archiveMessageAction() const
0806 {
0807     return mArchiveMessageAction;
0808 }
0809 
0810 #include "moc_messageactions.cpp"