File indexing completed on 2024-05-26 05:24:59

0001 /*
0002   SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "collectionexpirywidget.h"
0008 #include "attributes/expirecollectionattribute.h"
0009 #include "collectionexpiryjob.h"
0010 #include "folder/foldersettings.h"
0011 #include "folderrequester.h"
0012 #include "kernel/mailkernel.h"
0013 #include "util/mailutil.h"
0014 
0015 #include <Akonadi/CollectionModifyJob>
0016 
0017 #include <KMessageBox>
0018 #include <KPluralHandlingSpinBox>
0019 #include <QCheckBox>
0020 #include <QFormLayout>
0021 #include <QGroupBox>
0022 #include <QHBoxLayout>
0023 #include <QPushButton>
0024 #include <QRadioButton>
0025 
0026 using namespace MailCommon;
0027 
0028 class DaysSpinBox : public KPluralHandlingSpinBox
0029 {
0030 public:
0031     DaysSpinBox(QWidget *parent)
0032         : KPluralHandlingSpinBox(parent)
0033     {
0034         setMaximum(999999);
0035         setSuffix(ki18ncp("Expire messages after %1", " day", " days"));
0036         setSpecialValueText(i18n("Never"));
0037     }
0038 
0039     [[nodiscard]] QString textFromValue(int value) const override
0040     {
0041         if (value == 0) {
0042             return i18n("Never");
0043         }
0044         return KPluralHandlingSpinBox::textFromValue(value);
0045     }
0046 
0047     [[nodiscard]] int valueFromText(const QString &text) const override
0048     {
0049         return KPluralHandlingSpinBox::valueFromText(text);
0050     }
0051 
0052     QValidator::State validate(QString &text, int &pos) const override
0053     {
0054         if (text == i18n("Never")) {
0055             return QValidator::Acceptable;
0056         }
0057         return KPluralHandlingSpinBox::validate(text, pos);
0058     }
0059 };
0060 
0061 CollectionExpiryWidget::CollectionExpiryWidget(QWidget *parent)
0062     : QWidget(parent)
0063     , mExpireReadMailSB(new DaysSpinBox(this))
0064     , mExpireUnreadMailSB(new DaysSpinBox(this))
0065     , mFolderSelector(new FolderRequester(this))
0066     , mExpireNowPB(new QPushButton(i18n("Save Settings and Expire Now"), this))
0067     , mExpireMailWithInvalidDateCB(new QCheckBox(i18n("Expire messages with invalid date"), this))
0068 {
0069     auto formLayout = new QFormLayout(this);
0070     formLayout->setContentsMargins({});
0071 
0072     connect(mExpireReadMailSB, &KPluralHandlingSpinBox::valueChanged, this, &CollectionExpiryWidget::slotChanged);
0073     formLayout->addRow(i18n("Expire read messages after:"), mExpireReadMailSB);
0074 
0075     connect(mExpireUnreadMailSB, &KPluralHandlingSpinBox::valueChanged, this, &CollectionExpiryWidget::slotChanged);
0076     formLayout->addRow(i18n("Expire unread messages after:"), mExpireUnreadMailSB);
0077 
0078     connect(mExpireMailWithInvalidDateCB, &QCheckBox::toggled, this, &CollectionExpiryWidget::slotChanged);
0079     formLayout->addRow(QString(), mExpireMailWithInvalidDateCB);
0080 
0081     auto actionsGroup = new QGroupBox(this);
0082     actionsGroup->setFlat(true); // for mutual exclusion of the radio buttons
0083     formLayout->addRow(actionsGroup);
0084 
0085     auto moveToHBox = new QHBoxLayout();
0086     moveToHBox->setContentsMargins({});
0087     moveToHBox->setSpacing(6);
0088 
0089     mMoveToRB = new QRadioButton(actionsGroup);
0090     mMoveToRB->setText(i18n("Move expired messages to:"));
0091     connect(mMoveToRB, &QRadioButton::toggled, this, &CollectionExpiryWidget::slotChanged);
0092     moveToHBox->addWidget(mMoveToRB);
0093 
0094     mFolderSelector->setMustBeReadWrite(true);
0095     mFolderSelector->setShowOutbox(false);
0096     moveToHBox->addWidget(mFolderSelector);
0097     formLayout->addRow(QString(), moveToHBox);
0098     connect(mFolderSelector, &FolderRequester::folderChanged, this, &CollectionExpiryWidget::slotChanged);
0099 
0100     mDeletePermanentlyRB = new QRadioButton(actionsGroup);
0101     mDeletePermanentlyRB->setText(i18n("Delete expired messages permanently"));
0102     connect(mDeletePermanentlyRB, &QRadioButton::toggled, this, &CollectionExpiryWidget::slotChanged);
0103 
0104     formLayout->addRow(QString(), mDeletePermanentlyRB);
0105 
0106     connect(mExpireNowPB, &QPushButton::clicked, this, &CollectionExpiryWidget::saveAndExpireRequested);
0107     formLayout->addRow(QString(), mExpireNowPB);
0108 
0109     mDeletePermanentlyRB->setChecked(true);
0110     slotChanged();
0111 }
0112 
0113 CollectionExpiryWidget::~CollectionExpiryWidget() = default;
0114 
0115 void CollectionExpiryWidget::hideExpireNowButton()
0116 {
0117     mExpireNowPB->setVisible(false);
0118 }
0119 
0120 void CollectionExpiryWidget::slotChanged()
0121 {
0122     const bool showExpiryActions = mExpireReadMailSB->value() != 0 || mExpireUnreadMailSB->value() != 0;
0123     mMoveToRB->setEnabled(showExpiryActions);
0124     mFolderSelector->setEnabled(showExpiryActions && mMoveToRB->isChecked());
0125     mDeletePermanentlyRB->setEnabled(showExpiryActions);
0126     mExpireNowPB->setEnabled(showExpiryActions);
0127 
0128     Q_EMIT configChanged();
0129 }
0130 
0131 void CollectionExpiryWidget::load(const MailCommon::CollectionExpirySettings &settings)
0132 {
0133     if (settings.isValid()) {
0134         bool expiryGloballyOn = settings.expiryGloballyOn;
0135         if (expiryGloballyOn && settings.mReadExpireUnits != ExpireCollectionAttribute::ExpireNever && settings.daysToExpireRead >= 0) {
0136             mExpireReadMailSB->setValue(settings.daysToExpireRead);
0137         } else {
0138             mExpireReadMailSB->setValue(0);
0139         }
0140         if (expiryGloballyOn && settings.mUnreadExpireUnits != ExpireCollectionAttribute::ExpireNever && settings.daysToExpireUnread >= 0) {
0141             mExpireUnreadMailSB->setValue(settings.daysToExpireUnread);
0142         } else {
0143             mExpireUnreadMailSB->setValue(0);
0144         }
0145 
0146         if (settings.mExpireAction == ExpireCollectionAttribute::ExpireDelete) {
0147             mDeletePermanentlyRB->setChecked(true);
0148         } else {
0149             mMoveToRB->setChecked(true);
0150         }
0151 
0152         mExpireMailWithInvalidDateCB->setChecked(settings.expiryMessagesWithInvalidDate);
0153         Akonadi::Collection::Id destFolderID = settings.mExpireToFolderId;
0154         if (destFolderID > 0) {
0155             Akonadi::Collection destFolder = Kernel::self()->collectionFromId(destFolderID);
0156             if (destFolder.isValid()) {
0157                 mFolderSelector->setCollection(destFolder);
0158             }
0159         }
0160     } else {
0161         mDeletePermanentlyRB->setChecked(true);
0162     }
0163     slotChanged();
0164 }
0165 
0166 bool CollectionExpiryWidget::validateExpireFolder(bool expireNow)
0167 {
0168     const bool enableGlobally = mExpireReadMailSB->value() != 0 || mExpireUnreadMailSB->value() != 0;
0169     const Akonadi::Collection expireToFolder = mFolderSelector->collection();
0170     if (enableGlobally && mMoveToRB->isChecked() && !expireToFolder.isValid()) {
0171         KMessageBox::error(this,
0172                            i18n("Please select a folder to expire messages into.\nIf this is not done, expired messages will be permanently deleted."),
0173                            i18n("No Folder Selected"));
0174         mDeletePermanentlyRB->setChecked(true);
0175         expireNow = false; // settings are not valid
0176     }
0177     return expireNow;
0178 }
0179 
0180 MailCommon::ExpireCollectionAttribute *CollectionExpiryWidget::assignFolderAttribute(Akonadi::Collection &collection, bool &expireNow)
0181 {
0182     const Akonadi::Collection expireToFolder = mFolderSelector->collection();
0183     MailCommon::ExpireCollectionAttribute *attribute = nullptr;
0184     if (expireToFolder.isValid() && mMoveToRB->isChecked()) {
0185         if (expireToFolder.id() == collection.id()) {
0186             KMessageBox::error(this,
0187                                i18n("Please select a different folder than the current folder to expire messages into.\nIf this is not done, expired messages "
0188                                     "will be permanently deleted."),
0189                                i18n("Wrong Folder Selected"));
0190             mDeletePermanentlyRB->setChecked(true);
0191             expireNow = false; // settings are not valid
0192         } else {
0193             attribute = collection.attribute<MailCommon::ExpireCollectionAttribute>(Akonadi::Collection::AddIfMissing);
0194             attribute->setExpireToFolderId(expireToFolder.id());
0195         }
0196     }
0197     if (!attribute) {
0198         attribute = collection.attribute<MailCommon::ExpireCollectionAttribute>(Akonadi::Collection::AddIfMissing);
0199     }
0200     return attribute;
0201 }
0202 
0203 CollectionExpirySettings CollectionExpiryWidget::settings() const
0204 {
0205     CollectionExpirySettings settings;
0206     settings.expiryGloballyOn = mExpireReadMailSB->value() != 0 || mExpireUnreadMailSB->value() != 0;
0207     settings.expiryMessagesWithInvalidDate = mExpireMailWithInvalidDateCB->isChecked();
0208     // we always write out days now
0209     settings.daysToExpireRead = mExpireReadMailSB->value();
0210     settings.daysToExpireUnread = mExpireUnreadMailSB->value();
0211     settings.mReadExpireUnits =
0212         mExpireReadMailSB->value() != 0 ? MailCommon::ExpireCollectionAttribute::ExpireDays : MailCommon::ExpireCollectionAttribute::ExpireNever;
0213     settings.mUnreadExpireUnits =
0214         mExpireUnreadMailSB->value() != 0 ? MailCommon::ExpireCollectionAttribute::ExpireDays : MailCommon::ExpireCollectionAttribute::ExpireNever;
0215 
0216     if (mDeletePermanentlyRB->isChecked()) {
0217         settings.mExpireAction = ExpireCollectionAttribute::ExpireDelete;
0218     } else {
0219         settings.mExpireAction = ExpireCollectionAttribute::ExpireMove;
0220     }
0221     return settings;
0222 }
0223 
0224 void CollectionExpiryWidget::save(const CollectionExpirySettings &collectionExpirySettings, Akonadi::Collection &collection, bool saveSettings, bool expireNow)
0225 {
0226     expireNow = validateExpireFolder(expireNow);
0227     MailCommon::ExpireCollectionAttribute *attribute = assignFolderAttribute(collection, expireNow);
0228     attribute->setAutoExpire(collectionExpirySettings.expiryGloballyOn);
0229     // we always write out days now
0230     attribute->setReadExpireAge(collectionExpirySettings.daysToExpireRead);
0231     attribute->setUnreadExpireAge(collectionExpirySettings.daysToExpireUnread);
0232     attribute->setReadExpireUnits(collectionExpirySettings.mReadExpireUnits);
0233     attribute->setUnreadExpireUnits(collectionExpirySettings.mUnreadExpireUnits);
0234     attribute->setExpireAction(collectionExpirySettings.mExpireAction);
0235 
0236     if (saveSettings) {
0237         auto job = new CollectionExpiryJob;
0238         job->setExpireNow(expireNow);
0239         job->setCollection(collection);
0240         job->start();
0241     } else {
0242         if (expireNow) {
0243             MailCommon::Util::expireOldMessages(collection, true /*immediate*/);
0244         }
0245     }
0246     Q_EMIT configChanged(false);
0247 }
0248 
0249 void CollectionExpiryWidget::save(Akonadi::Collection &collection, bool saveSettings, bool expireNow)
0250 {
0251     const CollectionExpirySettings collectionExpirySettings = settings();
0252     save(collectionExpirySettings, collection, saveSettings, expireNow);
0253 }
0254 
0255 bool CollectionExpiryWidget::canHandle(const Akonadi::Collection &col)
0256 {
0257     QSharedPointer<FolderSettings> fd = FolderSettings::forCollection(col, false);
0258     return fd->canDeleteMessages() && !fd->isStructural() && !MailCommon::Util::isVirtualCollection(col);
0259 }
0260 
0261 bool CollectionExpirySettings::isValid() const
0262 {
0263     const bool valid = daysToExpireRead != -1 || daysToExpireUnread != -1 || mUnreadExpireUnits != ExpireCollectionAttribute::ExpireNever
0264         || mReadExpireUnits != ExpireCollectionAttribute::ExpireNever || mExpireAction != ExpireCollectionAttribute::ExpireDelete || mExpireToFolderId != -1;
0265     return valid;
0266 }
0267 
0268 QDebug operator<<(QDebug d, const CollectionExpirySettings &t)
0269 {
0270     d << " expiryGloballyOn " << t.expiryGloballyOn;
0271     d << " expiryMessagesWithInvalidDate " << t.expiryMessagesWithInvalidDate;
0272     d << " daysToExpireRead " << t.daysToExpireRead;
0273     d << " daysToExpireUnread " << t.daysToExpireUnread;
0274     d << " mUnreadExpireUnits " << t.mUnreadExpireUnits;
0275     d << " mReadExpireUnits " << t.mReadExpireUnits;
0276     d << " mExpireAction " << t.mExpireAction;
0277     d << " mExpireToFolderId " << t.mExpireToFolderId;
0278     return d;
0279 }
0280 
0281 #include "moc_collectionexpirywidget.cpp"