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

0001 /*
0002    SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
0003    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0004 
0005    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "archivefolderdialog.h"
0009 
0010 #include "kmmainwidget.h"
0011 #include <MailCommon/BackupJob>
0012 #include <MailCommon/FolderRequester>
0013 #include <MessageViewer/MessageViewerUtil>
0014 
0015 #include <Akonadi/Collection>
0016 
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <KSeparator>
0020 #include <KUrlRequester>
0021 #include <QComboBox>
0022 
0023 #include <QCheckBox>
0024 #include <QDialogButtonBox>
0025 #include <QGridLayout>
0026 #include <QLabel>
0027 #include <QMimeDatabase>
0028 #include <QPushButton>
0029 #include <QStandardPaths>
0030 #include <QVBoxLayout>
0031 
0032 using namespace KMail;
0033 using namespace MailCommon;
0034 
0035 QString ArchiveFolderDialog::standardArchivePath(const QString &folderName)
0036 {
0037     QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
0038     const QDir dir(currentPath);
0039     if (!dir.exists()) {
0040         currentPath = QDir::homePath();
0041     }
0042     return currentPath + QLatin1Char('/') + i18nc("Start of the filename for a mail archive file", "Archive") + QLatin1Char('_') + folderName + QLatin1Char('_')
0043         + QDate::currentDate().toString(Qt::ISODate) + QLatin1StringView(".tar.bz2");
0044 }
0045 
0046 ArchiveFolderDialog::ArchiveFolderDialog(QWidget *parent)
0047     : QDialog(parent)
0048     , mParentWidget(parent)
0049 {
0050     setObjectName(QLatin1StringView("archive_folder_dialog"));
0051     setWindowTitle(i18nc("@title:window for archiving a folder", "Archive Folder"));
0052     auto topLayout = new QVBoxLayout(this);
0053     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
0054     mOkButton = buttonBox->button(QDialogButtonBox::Ok);
0055     mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0056     connect(buttonBox, &QDialogButtonBox::accepted, this, &ArchiveFolderDialog::slotAccepted);
0057     connect(buttonBox, &QDialogButtonBox::rejected, this, &ArchiveFolderDialog::reject);
0058     mOkButton->setDefault(true);
0059     mOkButton->setText(i18nc("@action", "Archive"));
0060     setModal(true);
0061     auto mainWidget = new QWidget(this);
0062     topLayout->addWidget(mainWidget);
0063     topLayout->addWidget(buttonBox);
0064     auto mainLayout = new QGridLayout(mainWidget);
0065     mainLayout->setContentsMargins({});
0066 
0067     int row = 0;
0068 
0069     // TODO: Explanation label
0070 
0071     auto folderLabel = new QLabel(i18n("&Folder:"), mainWidget);
0072     mainLayout->addWidget(folderLabel, row, 0);
0073     mFolderRequester = new FolderRequester(mainWidget);
0074     mFolderRequester->setMustBeReadWrite(false);
0075     mFolderRequester->setNotAllowToCreateNewFolder(true);
0076     connect(mFolderRequester, &FolderRequester::folderChanged, this, &ArchiveFolderDialog::slotFolderChanged);
0077     folderLabel->setBuddy(mFolderRequester);
0078     mainLayout->addWidget(mFolderRequester, row, 1);
0079     row++;
0080 
0081     auto formatLabel = new QLabel(i18n("F&ormat:"), mainWidget);
0082     mainLayout->addWidget(formatLabel, row, 0);
0083     mFormatComboBox = new QComboBox(mainWidget);
0084     formatLabel->setBuddy(mFormatComboBox);
0085 
0086     // These combobox values have to stay in sync with the ArchiveType enum from BackupJob!
0087     mFormatComboBox->addItem(i18n("Compressed Zip Archive (.zip)"));
0088     mFormatComboBox->addItem(i18n("Uncompressed Archive (.tar)"));
0089     mFormatComboBox->addItem(i18n("BZ2-Compressed Tar Archive (.tar.bz2)"));
0090     mFormatComboBox->addItem(i18n("GZ-Compressed Tar Archive (.tar.gz)"));
0091     mFormatComboBox->setCurrentIndex(2);
0092     connect(mFormatComboBox, &QComboBox::activated, this, &ArchiveFolderDialog::slotFixFileExtension);
0093     mainLayout->addWidget(mFormatComboBox, row, 1);
0094     row++;
0095 
0096     auto fileNameLabel = new QLabel(i18n("&Archive File:"), mainWidget);
0097     mainLayout->addWidget(fileNameLabel, row, 0);
0098     mUrlRequester = new KUrlRequester(mainWidget);
0099     mUrlRequester->setMode(KFile::LocalOnly | KFile::File);
0100     mUrlRequester->setNameFilter(i18n("Archive file (*.tar *.zip *.tar.gz *.tar.bz2)"));
0101     fileNameLabel->setBuddy(mUrlRequester);
0102     connect(mUrlRequester, &KUrlRequester::urlSelected, this, &ArchiveFolderDialog::slotFixFileExtension);
0103     connect(mUrlRequester, &KUrlRequester::textChanged, this, &ArchiveFolderDialog::slotUrlChanged);
0104     mainLayout->addWidget(mUrlRequester, row, 1);
0105     row++;
0106 
0107     // TODO: Make this appear more dangerous!
0108     mDeleteCheckBox = new QCheckBox(i18n("&Delete folder and subfolders after completion"), mainWidget);
0109     mainLayout->addWidget(mDeleteCheckBox, row, 0, 1, 2, Qt::AlignLeft);
0110     row++;
0111 
0112     mRecursiveCheckBox = new QCheckBox(i18n("Archive all subfolders"), mainWidget);
0113     connect(mRecursiveCheckBox, &QCheckBox::clicked, this, &ArchiveFolderDialog::slotRecursiveCheckboxClicked);
0114     mainLayout->addWidget(mRecursiveCheckBox, row, 0, 1, 2, Qt::AlignLeft);
0115     mRecursiveCheckBox->setChecked(true);
0116     row++;
0117 
0118     // TODO: what's this, tooltips
0119 
0120     // TODO: Warn that user should do mail check for online IMAP and possibly cached IMAP as well
0121 
0122     mainLayout->addWidget(new KSeparator(), row, 0, 1, 2);
0123     row++;
0124     mainLayout->setColumnStretch(1, 1);
0125     mainLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Expanding), row, 0);
0126 
0127     // Make it a bit bigger, else the folder requester cuts off the text too early
0128     resize(500, minimumSize().height());
0129 }
0130 
0131 bool canRemoveFolder(const Akonadi::Collection &col)
0132 {
0133     const QSharedPointer<FolderSettings> folder = FolderSettings::forCollection(col, false);
0134     return !folder.isNull() && col.isValid() && !col.isVirtual() && (col.rights() & Akonadi::Collection::CanDeleteCollection) && !folder->isStructural()
0135         && !folder->isSystemFolder();
0136 }
0137 
0138 void ArchiveFolderDialog::slotRecursiveCheckboxClicked()
0139 {
0140     slotFolderChanged(mFolderRequester->collection());
0141 }
0142 
0143 void ArchiveFolderDialog::slotFolderChanged(const Akonadi::Collection &folder)
0144 {
0145     mDeleteCheckBox->setEnabled(allowToDeleteFolders(folder));
0146 }
0147 
0148 bool ArchiveFolderDialog::allowToDeleteFolders(const Akonadi::Collection &folder) const
0149 {
0150     return canRemoveFolder(folder) && mRecursiveCheckBox->isChecked();
0151 }
0152 
0153 void ArchiveFolderDialog::setFolder(const Akonadi::Collection &defaultCollection)
0154 {
0155     mFolderRequester->setCollection(defaultCollection);
0156     // TODO: what if the file already exists?
0157     mUrlRequester->setUrl(QUrl::fromLocalFile(standardArchivePath(defaultCollection.name())));
0158     const QSharedPointer<FolderSettings> folder = FolderSettings::forCollection(defaultCollection, false);
0159     mDeleteCheckBox->setEnabled(allowToDeleteFolders(defaultCollection));
0160     mOkButton->setEnabled(defaultCollection.isValid() && folder && !folder->isStructural());
0161 }
0162 
0163 void ArchiveFolderDialog::slotAccepted()
0164 {
0165     if (!MessageViewer::Util::checkOverwrite(mUrlRequester->url(), this)) {
0166         return;
0167     }
0168 
0169     if (!mFolderRequester->hasCollection()) {
0170         KMessageBox::information(this, i18n("Please select the folder that should be archived."), i18nc("@title:window", "No folder selected"));
0171         return;
0172     }
0173 
0174     auto backupJob = new MailCommon::BackupJob(mParentWidget);
0175     backupJob->setRootFolder(mFolderRequester->collection());
0176     backupJob->setSaveLocation(mUrlRequester->url());
0177     backupJob->setArchiveType(static_cast<BackupJob::ArchiveType>(mFormatComboBox->currentIndex()));
0178     backupJob->setDeleteFoldersAfterCompletion(mDeleteCheckBox->isEnabled() && mDeleteCheckBox->isChecked());
0179     backupJob->setRecursive(mRecursiveCheckBox->isChecked());
0180     backupJob->start();
0181     accept();
0182 }
0183 
0184 void ArchiveFolderDialog::slotFixFileExtension()
0185 {
0186     const int numExtensions = 4;
0187     // The extensions here are also sorted, like the enum order of BackupJob::ArchiveType
0188     const char *extensions[numExtensions] = {".zip", ".tar", ".tar.bz2", ".tar.gz"};
0189 
0190     QString fileName = mUrlRequester->url().path();
0191     if (fileName.isEmpty()) {
0192         fileName = standardArchivePath(mFolderRequester->hasCollection() ? mFolderRequester->collection().name() : QString());
0193     }
0194 
0195     QMimeDatabase db;
0196     const QString extension = db.suffixForFileName(fileName);
0197     if (!extension.isEmpty()) {
0198         fileName.truncate(fileName.length() - extension.length() - 1);
0199     }
0200 
0201     // Now, we've got a filename without an extension, simply append the correct one
0202     fileName += QLatin1StringView(extensions[mFormatComboBox->currentIndex()]);
0203     mUrlRequester->setUrl(QUrl::fromLocalFile(fileName));
0204 }
0205 
0206 void ArchiveFolderDialog::slotUrlChanged(const QString &url)
0207 {
0208     mOkButton->setEnabled(!url.isEmpty());
0209 }
0210 
0211 #include "moc_archivefolderdialog.cpp"