File indexing completed on 2024-12-22 05:01:00

0001 /*
0002   This file is part of KMail, the KDE mail client.
0003   SPDX-FileCopyrightText: 2005 Till Adam <adam@kde.org>
0004   SPDX-FileCopyrightText: 2011-2024 Laurent Montel <montel@kde.org>
0005   SPDX-FileCopyrightText: 2012 Jonathan Marten <jjm@keelhaul.me.uk>
0006 
0007   SPDX-License-Identifier: GPL-2.0-only
0008 */
0009 
0010 #include "collectionmailinglistpage.h"
0011 #include "util.h"
0012 #include <MailCommon/MailKernel>
0013 #include <MailCommon/MailUtil>
0014 
0015 #include <Akonadi/ItemFetchJob>
0016 #include <Akonadi/ItemFetchScope>
0017 #include <Akonadi/MessageParts>
0018 
0019 #include <QCheckBox>
0020 #include <QFormLayout>
0021 #include <QHBoxLayout>
0022 #include <QLabel>
0023 #include <QLineEdit>
0024 #include <QPushButton>
0025 
0026 #include "kmail_debug.h"
0027 #include <KEditListWidget>
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 #include <KSqueezedTextLabel>
0031 #include <QComboBox>
0032 
0033 using namespace MailCommon;
0034 
0035 CollectionMailingListPage::CollectionMailingListPage(QWidget *parent)
0036     : CollectionPropertiesPage(parent)
0037 {
0038     setObjectName(QLatin1StringView("KMail::CollectionMailingListPage"));
0039     setPageTitle(i18nc("@title:tab Mailing list settings for a folder.", "Mailing List"));
0040 }
0041 
0042 CollectionMailingListPage::~CollectionMailingListPage() = default;
0043 
0044 void CollectionMailingListPage::slotConfigChanged()
0045 {
0046     changed = true;
0047 }
0048 
0049 bool CollectionMailingListPage::canHandle(const Akonadi::Collection &col) const
0050 {
0051     QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false);
0052     return !CommonKernel->isSystemFolderCollection(col) && !fd->isStructural() && !MailCommon::Util::isVirtualCollection(col);
0053 }
0054 
0055 void CollectionMailingListPage::init(const Akonadi::Collection &col)
0056 {
0057     mCurrentCollection = col;
0058     mFolder = FolderSettings::forCollection(col, false);
0059 
0060     auto topLayout = new QFormLayout(this);
0061 
0062     mHoldsMailingList = new QCheckBox(i18n("Folder holds a mailing list"), this);
0063     connect(mHoldsMailingList, &QCheckBox::toggled, this, &CollectionMailingListPage::slotHoldsML);
0064     connect(mHoldsMailingList, &QCheckBox::toggled, this, &CollectionMailingListPage::slotConfigChanged);
0065     topLayout->addRow(QString(), mHoldsMailingList);
0066 
0067     mDetectButton = new QPushButton(i18n("Detect Automatically"), this);
0068     connect(mDetectButton, &QPushButton::pressed, this, &CollectionMailingListPage::slotDetectMailingList);
0069     topLayout->addRow(QString(), mDetectButton);
0070 
0071     mMLId = new KSqueezedTextLabel(QString(), this);
0072     mMLId->setTextElideMode(Qt::ElideRight);
0073     topLayout->addRow(i18n("Mailing list description:"), mMLId);
0074 
0075     // FIXME: add QWhatsThis
0076     mMLHandlerCombo = new QComboBox(this);
0077     mMLHandlerCombo->addItem(i18n("KMail"), MailingList::KMail);
0078     mMLHandlerCombo->addItem(i18n("Browser"), MailingList::Browser);
0079     connect(mMLHandlerCombo, &QComboBox::activated, this, &CollectionMailingListPage::slotMLHandling);
0080 
0081     topLayout->addRow(i18n("Preferred handler:"), mMLHandlerCombo);
0082 
0083     auto addressWidget = new QWidget(this);
0084     addressWidget->setContentsMargins({});
0085     auto addressTypeLayout = new QHBoxLayout(addressWidget);
0086     addressTypeLayout->setContentsMargins({});
0087     mAddressCombo = new QComboBox(this);
0088     addressTypeLayout->addWidget(mAddressCombo);
0089 
0090     // FIXME: if the mailing list actions have either QAction's or toolbar buttons
0091     //       associated with them - remove this button since it's really silly
0092     //       here
0093     mHandleButton = new QPushButton(i18n("Invoke Handler"), this);
0094     if (mFolder) {
0095         connect(mHandleButton, &QPushButton::clicked, this, &CollectionMailingListPage::slotInvokeHandler);
0096     } else {
0097         mHandleButton->setEnabled(false);
0098     }
0099     addressTypeLayout->addWidget(mHandleButton);
0100     topLayout->addRow(i18n("Address type:"), addressWidget);
0101 
0102     topLayout->addRow(new QLabel(i18n("URL for mailing list posting:"), this));
0103 
0104     mEditList = new KEditListWidget(this);
0105     mEditList->lineEdit()->setClearButtonEnabled(true);
0106     connect(mEditList, &KEditListWidget::changed, this, &CollectionMailingListPage::slotConfigChanged);
0107     topLayout->addRow(mEditList);
0108 
0109     // Order is important because the activate handler and fillMLFromWidgets
0110     // depend on it
0111     const QStringList el{i18n("Post to List"), i18n("Subscribe to List"), i18n("Unsubscribe From List"), i18n("List Archives"), i18n("List Help")};
0112     mAddressCombo->addItems(el);
0113     connect(mAddressCombo, &QComboBox::activated, this, &CollectionMailingListPage::slotAddressChanged);
0114 
0115     mMLId->setEnabled(false);
0116     mMLHandlerCombo->setEnabled(false);
0117     mAddressCombo->setEnabled(false);
0118     mHandleButton->setEnabled(false);
0119     mEditList->setEnabled(false);
0120 }
0121 
0122 void CollectionMailingListPage::load(const Akonadi::Collection &col)
0123 {
0124     init(col);
0125 
0126     if (mFolder) {
0127         mMailingList = mFolder->mailingList();
0128     }
0129 
0130     mMLId->setText((mMailingList.id().isEmpty() ? i18n("Not available") : mMailingList.id()));
0131     mMLHandlerCombo->setCurrentIndex(mMailingList.handler());
0132     mEditList->insertStringList(QUrl::toStringList(mMailingList.postUrls()));
0133 
0134     mAddressCombo->setCurrentIndex(mLastItem);
0135     mHoldsMailingList->setChecked(mFolder && mFolder->isMailingListEnabled());
0136     slotHoldsML(mHoldsMailingList->isChecked());
0137     changed = false;
0138 }
0139 
0140 void CollectionMailingListPage::save(Akonadi::Collection &col)
0141 {
0142     Q_UNUSED(col)
0143     if (changed) {
0144         if (mFolder) {
0145             // settings for mailingList
0146             mFolder->setMailingListEnabled(mHoldsMailingList && mHoldsMailingList->isChecked());
0147             fillMLFromWidgets();
0148             mFolder->setMailingList(mMailingList);
0149         }
0150     }
0151 }
0152 
0153 //----------------------------------------------------------------------------
0154 void CollectionMailingListPage::slotHoldsML(bool holdsML)
0155 {
0156     mMLId->setEnabled(holdsML);
0157     mMLHandlerCombo->setEnabled(holdsML);
0158     mAddressCombo->setEnabled(holdsML);
0159     if (mFolder) {
0160         mHandleButton->setEnabled(holdsML);
0161     }
0162     mEditList->setEnabled(holdsML);
0163     mDetectButton->setEnabled(mFolder && mFolder->count() != 0);
0164 }
0165 
0166 //----------------------------------------------------------------------------
0167 void CollectionMailingListPage::slotDetectMailingList()
0168 {
0169     if (!mFolder) {
0170         return; // in case the folder was just created
0171     }
0172 
0173     qCDebug(KMAIL_LOG) << "Detecting mailing list";
0174 
0175     // next try the 5 most recently added messages
0176     if (!(mMailingList.features() & MailingList::Post)) {
0177         // FIXME not load all folder
0178         auto job = new Akonadi::ItemFetchJob(mCurrentCollection, this);
0179         job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Header);
0180         connect(job, &Akonadi::ItemFetchJob::result, this, &CollectionMailingListPage::slotFetchDone);
0181         // Don't allow to reactive it
0182         mDetectButton->setEnabled(false);
0183     } else {
0184         mMLId->setText((mMailingList.id().isEmpty() ? i18n("Not available.") : mMailingList.id()));
0185         fillEditBox();
0186     }
0187 }
0188 
0189 void CollectionMailingListPage::slotFetchDone(KJob *job)
0190 {
0191     mDetectButton->setEnabled(true);
0192     if (MailCommon::Util::showJobErrorMessage(job)) {
0193         return;
0194     }
0195     auto fjob = qobject_cast<Akonadi::ItemFetchJob *>(job);
0196     Q_ASSERT(fjob);
0197     Akonadi::Item::List items = fjob->items();
0198     const int maxchecks = 5;
0199     int num = items.size();
0200     for (int i = --num; (i > num - maxchecks) && (i >= 0); --i) {
0201         Akonadi::Item item = items[i];
0202         if (item.hasPayload<KMime::Message::Ptr>()) {
0203             auto message = item.payload<KMime::Message::Ptr>();
0204             mMailingList = MessageCore::MailingList::detect(message);
0205             if (mMailingList.features() & MailingList::Post) {
0206                 break;
0207             }
0208         }
0209     }
0210     if (!(mMailingList.features() & MailingList::Post)) {
0211         if (mMailingList.features() == MailingList::None) {
0212             KMessageBox::error(this, i18n("KMail was unable to detect any mailing list in this folder."));
0213         } else {
0214             KMessageBox::error(this,
0215                                i18n("KMail was unable to fully detect a mailing list in this folder. "
0216                                     "Please fill in the addresses by hand."));
0217         }
0218     } else {
0219         mMLId->setText((mMailingList.id().isEmpty() ? i18n("Not available.") : mMailingList.id()));
0220         fillEditBox();
0221     }
0222 }
0223 
0224 //----------------------------------------------------------------------------
0225 void CollectionMailingListPage::slotMLHandling(int element)
0226 {
0227     mMailingList.setHandler(static_cast<MailingList::Handler>(element));
0228     slotConfigChanged();
0229 }
0230 
0231 //----------------------------------------------------------------------------
0232 void CollectionMailingListPage::slotAddressChanged(int i)
0233 {
0234     fillMLFromWidgets();
0235     fillEditBox();
0236     mLastItem = i;
0237     slotConfigChanged();
0238 }
0239 
0240 //----------------------------------------------------------------------------
0241 void CollectionMailingListPage::fillMLFromWidgets()
0242 {
0243     if (!mHoldsMailingList->isChecked()) {
0244         return;
0245     }
0246 
0247     // make sure that email addresses are prepended by "mailto:"
0248     bool listChanged = false;
0249     const QStringList oldList = mEditList->items();
0250     QStringList newList; // the correct string list
0251     QStringList::ConstIterator end = oldList.constEnd();
0252     for (QStringList::ConstIterator it = oldList.constBegin(); it != end; ++it) {
0253         if (!(*it).startsWith(QLatin1StringView("http:")) && !(*it).startsWith(QLatin1StringView("https:")) && !(*it).startsWith(QLatin1StringView("mailto:"))
0254             && ((*it).contains(QLatin1Char('@')))) {
0255             listChanged = true;
0256             newList << QStringLiteral("mailto:") + *it;
0257         } else {
0258             newList << *it;
0259         }
0260     }
0261     if (listChanged) {
0262         mEditList->clear();
0263         mEditList->insertStringList(newList);
0264     }
0265 
0266     // mMailingList.setHandler( static_cast<MailingList::Handler>( mMLHandlerCombo->currentIndex() ) );
0267     switch (mLastItem) {
0268     case 0:
0269         mMailingList.setPostUrls(QUrl::fromStringList(mEditList->items()));
0270         break;
0271     case 1:
0272         mMailingList.setSubscribeUrls(QUrl::fromStringList(mEditList->items()));
0273         break;
0274     case 2:
0275         mMailingList.setUnsubscribeUrls(QUrl::fromStringList(mEditList->items()));
0276         break;
0277     case 3:
0278         mMailingList.setArchiveUrls(QUrl::fromStringList(mEditList->items()));
0279         break;
0280     case 4:
0281         mMailingList.setHelpUrls(QUrl::fromStringList(mEditList->items()));
0282         break;
0283     default:
0284         qCWarning(KMAIL_LOG) << "Wrong entry in the mailing list entry combo!";
0285     }
0286 }
0287 
0288 void CollectionMailingListPage::fillEditBox()
0289 {
0290     mEditList->clear();
0291     switch (mAddressCombo->currentIndex()) {
0292     case 0:
0293         mEditList->insertStringList(QUrl::toStringList(mMailingList.postUrls()));
0294         break;
0295     case 1:
0296         mEditList->insertStringList(QUrl::toStringList(mMailingList.subscribeUrls()));
0297         break;
0298     case 2:
0299         mEditList->insertStringList(QUrl::toStringList(mMailingList.unsubscribeUrls()));
0300         break;
0301     case 3:
0302         mEditList->insertStringList(QUrl::toStringList(mMailingList.archiveUrls()));
0303         break;
0304     case 4:
0305         mEditList->insertStringList(QUrl::toStringList(mMailingList.helpUrls()));
0306         break;
0307     default:
0308         qCWarning(KMAIL_LOG) << "Wrong entry in the mailing list entry combo!";
0309     }
0310 }
0311 
0312 void CollectionMailingListPage::slotInvokeHandler()
0313 {
0314     save(mCurrentCollection);
0315     switch (mAddressCombo->currentIndex()) {
0316     case 0:
0317         if (!KMail::Util::mailingListPost(mFolder, mCurrentCollection)) {
0318             qCWarning(KMAIL_LOG) << "invalid folder";
0319         }
0320         break;
0321     case 1:
0322         if (!KMail::Util::mailingListSubscribe(mFolder, mCurrentCollection)) {
0323             qCWarning(KMAIL_LOG) << "invalid folder";
0324         }
0325         break;
0326     case 2:
0327         if (!KMail::Util::mailingListUnsubscribe(mFolder, mCurrentCollection)) {
0328             qCWarning(KMAIL_LOG) << "invalid folder";
0329         }
0330         break;
0331     case 3:
0332         if (!KMail::Util::mailingListArchives(mFolder, mCurrentCollection)) {
0333             qCWarning(KMAIL_LOG) << "invalid folder";
0334         }
0335         break;
0336     case 4:
0337         if (!KMail::Util::mailingListHelp(mFolder, mCurrentCollection)) {
0338             qCWarning(KMAIL_LOG) << "invalid folder";
0339         }
0340         break;
0341     default:
0342         qCWarning(KMAIL_LOG) << "Wrong entry in the mailing list entry combo!";
0343     }
0344 }
0345 
0346 #include "moc_collectionmailinglistpage.cpp"