File indexing completed on 2024-05-12 05:25:37

0001 /*
0002    SPDX-FileCopyrightText: 2012-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "pimdataexporterwindow.h"
0008 #include "dialog/showarchivestructuredialog.h"
0009 #include "importexportprogressindicatorgui.h"
0010 #include "job/fullsynchronizeresourcesjob.h"
0011 #include "pimdataexportgui_debug.h"
0012 #include "trayicon/pimdatatrayicon.h"
0013 #include "widgets/logwidget.h"
0014 
0015 #include "dialog/selectiontypedialog.h"
0016 #include "dialog/synchronizeresourcedialog.h"
0017 #include "pimdatabackuprestoreui.h"
0018 #include "pimdataexporterkernel.h"
0019 #include "pimdataimportdatainfofile.h"
0020 
0021 #include "dialog/backupfilestructureinfodialog.h"
0022 
0023 #include <MailCommon/FilterManager>
0024 #include <MailCommon/MailKernel>
0025 
0026 #include <PimCommon/PimUtil>
0027 
0028 #include <Akonadi/ControlGui>
0029 
0030 #include <KAboutData>
0031 #include <KActionCollection>
0032 #include <KFileWidget>
0033 #include <KLocalizedString>
0034 #include <KMessageBox>
0035 #include <KRecentDirs>
0036 #include <KRecentFilesMenu>
0037 #include <KSharedConfig>
0038 #include <KStandardAction>
0039 #include <QCommandLineParser>
0040 #include <QFileDialog>
0041 #include <QPointer>
0042 #include <QStandardPaths>
0043 #include <QStatusBar>
0044 #include <QVBoxLayout>
0045 
0046 #include "dialog/pimdataexporterconfiguredialog.h"
0047 #ifdef WITH_KUSERFEEDBACK
0048 #include "userfeedback/userfeedbackmanager.h"
0049 #include <KUserFeedback/NotificationPopup>
0050 #include <KUserFeedback/Provider>
0051 #endif
0052 
0053 #include <PimCommon/NeedUpdateVersionUtils>
0054 #include <PimCommon/NeedUpdateVersionWidget>
0055 
0056 // signal handler for SIGINT & SIGTERM
0057 #ifdef Q_OS_UNIX
0058 #include <KSignalHandler>
0059 #include <signal.h>
0060 #include <unistd.h>
0061 #endif
0062 
0063 PimDataExporterWindow::PimDataExporterWindow(QWidget *parent)
0064     : KXmlGuiWindow(parent)
0065     , mLogWidget(new LogWidget(this))
0066 {
0067 #ifdef Q_OS_UNIX
0068     /**
0069      * Set up signal handler for SIGINT and SIGTERM
0070      */
0071     KSignalHandler::self()->watchSignal(SIGINT);
0072     KSignalHandler::self()->watchSignal(SIGTERM);
0073     connect(KSignalHandler::self(), &KSignalHandler::signalReceived, this, [this](int signal) {
0074         if (signal == SIGINT || signal == SIGTERM) {
0075             // Intercept console.
0076             printf("Shutting down...\n");
0077             if (!mInProgress) {
0078                 close();
0079             }
0080         }
0081     });
0082 #endif
0083     // Initialize filtermanager
0084     (void)MailCommon::FilterManager::instance();
0085 #ifdef WITH_KUSERFEEDBACK
0086     // Initialize
0087     (void)UserFeedBackManager::self();
0088 #endif
0089     auto kernel = new PimDataExporterKernel(this);
0090     CommonKernel->registerKernelIf(kernel); // register KernelIf early, it is used by the Filter classes
0091     CommonKernel->registerSettingsIf(kernel); // SettingsIf is used in FolderTreeWidget
0092 
0093     setupActions(true);
0094     setupGUI(Keys | StatusBar | Save | Create, QStringLiteral("pimdataexporter.rc"));
0095 
0096     auto mainWidget = new QWidget(this);
0097     auto mainWidgetLayout = new QVBoxLayout(mainWidget);
0098     mainWidgetLayout->setContentsMargins({});
0099     mainWidgetLayout->setSpacing(0);
0100     if (PimCommon::NeedUpdateVersionUtils::checkVersion()) {
0101         const auto status = PimCommon::NeedUpdateVersionUtils::obsoleteVersionStatus(KAboutData::applicationData().version(), QDate::currentDate());
0102         if (status != PimCommon::NeedUpdateVersionUtils::ObsoleteVersion::NotObsoleteYet) {
0103             auto needUpdateVersionWidget = new PimCommon::NeedUpdateVersionWidget(this);
0104             mainWidgetLayout->addWidget(needUpdateVersionWidget);
0105             needUpdateVersionWidget->setObsoleteVersion(status);
0106         }
0107     }
0108     mainWidgetLayout->addWidget(mLogWidget);
0109 
0110     setCentralWidget(mainWidget);
0111     resize(800, 600);
0112     Akonadi::ControlGui::widgetNeedsAkonadi(this);
0113     statusBar()->hide();
0114     mTrayIcon = new PimDataTrayIcon(this);
0115 #ifdef WITH_KUSERFEEDBACK
0116     auto userFeedBackNotificationPopup = new KUserFeedback::NotificationPopup(this);
0117     userFeedBackNotificationPopup->setFeedbackProvider(UserFeedBackManager::self()->userFeedbackProvider());
0118 #endif
0119 }
0120 
0121 PimDataExporterWindow::~PimDataExporterWindow()
0122 {
0123     MailCommon::FilterManager::instance()->cleanup();
0124 }
0125 
0126 void PimDataExporterWindow::initializeBackupRestoreUi()
0127 {
0128     mPimDataBackupRestoreUI = new PimDataBackupRestoreUI(this, this);
0129     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::addInfo, this, &PimDataExporterWindow::slotAddInfo);
0130     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::addEndLine, this, &PimDataExporterWindow::slotAddEndLine);
0131     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::addError, this, &PimDataExporterWindow::slotAddError);
0132     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::addTitle, this, &PimDataExporterWindow::slotAddTitle);
0133     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::updateActions, this, &PimDataExporterWindow::slotUpdateActions);
0134     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::jobFinished, this, &PimDataExporterWindow::slotJobFinished);
0135     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::backupDone, this, &PimDataExporterWindow::slotShowBackupFinishDialogInformation);
0136     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::jobFailed, this, &PimDataExporterWindow::slotJobFailed);
0137     connect(mPimDataBackupRestoreUI, &PimDataBackupRestoreUI::needSyncResource, this, &PimDataExporterWindow::slotAddResourceToSync);
0138     connect(mPimDataBackupRestoreUI, &PimDataBackupRestore::restoreDone, this, &PimDataExporterWindow::slotRestoreDone);
0139 }
0140 
0141 void PimDataExporterWindow::slotAddResourceToSync(const QString &name, const QString &identifier)
0142 {
0143     mNeedToSyncResources.insert(name, identifier);
0144 }
0145 
0146 void PimDataExporterWindow::slotJobFinished()
0147 {
0148     mPimDataBackupRestoreUI->nextStep();
0149 }
0150 
0151 void PimDataExporterWindow::slotJobFailed()
0152 {
0153     mPimDataBackupRestoreUI->closeArchive();
0154 }
0155 
0156 void PimDataExporterWindow::slotRestoreDone()
0157 {
0158     if (!mNeedToSyncResources.isEmpty()) {
0159         QPointer<SynchronizeResourceDialog> dlg = new SynchronizeResourceDialog(this);
0160         dlg->setResources(mNeedToSyncResources);
0161         QStringList list;
0162         if (dlg->exec()) {
0163             list = dlg->resources();
0164         }
0165         delete dlg;
0166         if (!list.isEmpty()) {
0167             slotAddInfo(i18n("Full sync starts..."));
0168             auto job = new FullSynchronizeResourcesJob(this);
0169             job->setWindowParent(this);
0170             job->setResources(list);
0171             connect(job, &FullSynchronizeResourcesJob::synchronizeFinished, this, &PimDataExporterWindow::slotFullSyncFinished);
0172             connect(job, &FullSynchronizeResourcesJob::synchronizeInstanceDone, this, &PimDataExporterWindow::slotFullSyncInstanceDone);
0173             connect(job, &FullSynchronizeResourcesJob::synchronizeInstanceFailed, this, &PimDataExporterWindow::slotFullSyncInstanceFailed);
0174             job->start();
0175         } else {
0176             importDone();
0177         }
0178     } else {
0179         importDone();
0180     }
0181 }
0182 
0183 void PimDataExporterWindow::slotShowBackupFinishDialogInformation()
0184 {
0185     showFinishInformation();
0186 }
0187 
0188 void PimDataExporterWindow::importDone()
0189 {
0190     slotUpdateActions(false);
0191     mTrayIcon->setStatus(KStatusNotifierItem::Passive);
0192 }
0193 
0194 void PimDataExporterWindow::slotFullSyncFinished()
0195 {
0196     importDone();
0197     const QString str = i18n("Full sync finished.");
0198     slotAddInfo(str);
0199     mTrayIcon->setToolTipSubTitle(str);
0200 }
0201 
0202 void PimDataExporterWindow::slotFullSyncInstanceDone(const QString &identifier)
0203 {
0204     slotAddInfo(i18n("Full sync for \"%1\" done.", identifier));
0205 }
0206 
0207 void PimDataExporterWindow::slotFullSyncInstanceFailed(const QString &identifier)
0208 {
0209     slotAddError(i18n("Full sync for \"%1\" failed.", identifier));
0210 }
0211 
0212 void PimDataExporterWindow::showFinishInformation()
0213 {
0214     KMessageBox::information(this,
0215                              i18n("For restoring data, you must use \"pimdataexporter\". "
0216                                   "Be careful as it can overwrite your existing settings and data."),
0217                              i18n("Backup"),
0218                              QStringLiteral("setProgressDialogLabelBackupInfos"));
0219     mTrayIcon->setStatus(KStatusNotifierItem::Passive);
0220 }
0221 
0222 void PimDataExporterWindow::handleCommandLine(const QCommandLineParser &parser)
0223 {
0224     QString templateFile;
0225     if (parser.isSet(QStringLiteral("template"))) {
0226         templateFile = parser.value(QStringLiteral("template"));
0227     }
0228     if (parser.isSet(QStringLiteral("import"))) {
0229         if (!parser.positionalArguments().isEmpty()) {
0230             loadData(parser.positionalArguments().at(0), templateFile);
0231         }
0232     } else if (parser.isSet(QStringLiteral("export"))) {
0233         if (!parser.positionalArguments().isEmpty()) {
0234             backupData(parser.positionalArguments().at(0), templateFile);
0235         }
0236     }
0237 }
0238 
0239 void PimDataExporterWindow::setupActions(bool canZipFile)
0240 {
0241     KActionCollection *ac = actionCollection();
0242 
0243     mBackupAction = ac->addAction(QStringLiteral("backup"), this, &PimDataExporterWindow::slotBackupData);
0244     mBackupAction->setText(i18n("Export Data..."));
0245     mBackupAction->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
0246     mBackupAction->setEnabled(canZipFile);
0247 
0248     mRestoreAction = ac->addAction(QStringLiteral("restore"), this, &PimDataExporterWindow::slotRestoreData);
0249     mRestoreAction->setText(i18n("Import Data..."));
0250     mRestoreAction->setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
0251     mRestoreAction->setEnabled(canZipFile);
0252 
0253     mSaveLogAction = ac->addAction(QStringLiteral("save_log"), this, &PimDataExporterWindow::slotSaveLog);
0254     mSaveLogAction->setText(i18n("Save log..."));
0255 
0256     mArchiveStructureInfo = ac->addAction(QStringLiteral("show_structure_info"), this, &PimDataExporterWindow::slotShowStructureInfos);
0257     mArchiveStructureInfo->setText(i18n("Show Archive Structure Information..."));
0258 
0259     mShowArchiveInformationsAction = ac->addAction(QStringLiteral("show_archive_info"), this, &PimDataExporterWindow::slotShowArchiveInformations);
0260     mShowArchiveInformationsAction->setText(i18n("Show Archive Information..."));
0261 
0262     mShowArchiveInformationsAboutCurrentArchiveAction =
0263         ac->addAction(QStringLiteral("show_current_archive_info"), this, &PimDataExporterWindow::slotShowCurrentArchiveInformations);
0264     mShowArchiveInformationsAboutCurrentArchiveAction->setText(i18n("Show Information on current Archive..."));
0265     mShowArchiveInformationsAboutCurrentArchiveAction->setEnabled(false);
0266 
0267     KStandardAction::quit(this, &PimDataExporterWindow::close, ac);
0268     mRecentFilesMenu = new KRecentFilesMenu(this);
0269     actionCollection()->addAction(QStringLiteral("pimdataexporter_file_open_recent"), mRecentFilesMenu->menuAction());
0270     connect(mRecentFilesMenu, &KRecentFilesMenu::urlTriggered, this, &PimDataExporterWindow::slotRestoreFile);
0271 
0272     KStandardAction::preferences(this, &PimDataExporterWindow::slotConfigure, ac);
0273 }
0274 
0275 void PimDataExporterWindow::slotConfigure()
0276 {
0277     PimDataExporterConfigureDialog dlg(this);
0278     dlg.exec();
0279 }
0280 
0281 void PimDataExporterWindow::slotUpdateActions(bool inAction)
0282 {
0283     mBackupAction->setEnabled(!inAction);
0284     mRestoreAction->setEnabled(!inAction);
0285     mSaveLogAction->setEnabled(!inAction);
0286     mArchiveStructureInfo->setEnabled(!inAction);
0287     mShowArchiveInformationsAction->setEnabled(!inAction);
0288     mShowArchiveInformationsAboutCurrentArchiveAction->setEnabled(!inAction && !mLastArchiveFileName.isEmpty());
0289     mInProgress = inAction;
0290 }
0291 
0292 void PimDataExporterWindow::slotRestoreFile(const QUrl &url)
0293 {
0294     if (!url.isEmpty()) {
0295         loadData(url.toLocalFile());
0296     }
0297 }
0298 
0299 void PimDataExporterWindow::slotShowArchiveInformations()
0300 {
0301     const QString filename = QFileDialog::getOpenFileName(this, i18n("Select Archive"), QString(), QStringLiteral("%1 (*.zip)").arg(i18n("Zip file")));
0302     if (filename.isEmpty()) {
0303         return;
0304     }
0305 
0306     ShowArchiveStructureDialog dlg(filename, this);
0307     dlg.exec();
0308 }
0309 
0310 void PimDataExporterWindow::slotSaveLog()
0311 {
0312     if (mLogWidget->isEmpty()) {
0313         KMessageBox::information(this, i18n("Log is empty."), i18n("Save log"));
0314         return;
0315     }
0316     const QString log = mLogWidget->toHtml();
0317     const QString filter(i18n("HTML Files (*.html)"));
0318     PimCommon::Util::saveTextAs(log, filter, this);
0319 }
0320 
0321 void PimDataExporterWindow::slotBackupData()
0322 {
0323     if (KMessageBox::warningContinueCancel(this, i18n("Please quit all kdepim applications before backing up your data."), i18n("Backup"))
0324         == KMessageBox::Cancel) {
0325         return;
0326     }
0327     backupData();
0328 }
0329 
0330 void PimDataExporterWindow::backupData(const QString &filename, const QString &templateFile)
0331 {
0332     QString currentFileName = filename;
0333     QPointer<SelectionTypeDialog> dialog = new SelectionTypeDialog(true, this);
0334     dialog->loadTemplate(templateFile);
0335     if (dialog->exec()) {
0336         mLogWidget->clear();
0337         initializeBackupRestoreUi();
0338         mPimDataBackupRestoreUI->setStoredParameters(dialog->storedType());
0339         mPimDataBackupRestoreUI->setExportedInfoFileName(dialog->exportedFileInfo());
0340         delete dialog;
0341 
0342         if (currentFileName.isEmpty()) {
0343             QString recentDirClass;
0344             currentFileName =
0345                 QFileDialog::getSaveFileName(this,
0346                                              i18n("Create backup"),
0347                                              KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///pimsettingexporter")), recentDirClass).toLocalFile(),
0348                                              i18n("Zip file (*.zip)"));
0349             if (currentFileName.isEmpty()) {
0350                 return;
0351             }
0352             if (!recentDirClass.isEmpty()) {
0353                 KRecentDirs::add(recentDirClass, currentFileName);
0354             }
0355             mRecentFilesMenu->addUrl(QUrl::fromLocalFile(currentFileName));
0356         }
0357         mTrayIcon->setStatus(KStatusNotifierItem::Active);
0358         mTrayIcon->setToolTipSubTitle(i18n("Backup in progress..."));
0359         if (!mPimDataBackupRestoreUI->backupStart(currentFileName)) {
0360             qCDebug(PIMDATAEXPORTERGUI_LOG) << " backup Start failed";
0361         }
0362         mLastArchiveFileName = currentFileName;
0363     } else {
0364         delete dialog;
0365     }
0366 }
0367 
0368 void PimDataExporterWindow::slotAddInfo(const QString &info)
0369 {
0370     mLogWidget->addInfoLogEntry(info);
0371     qApp->processEvents();
0372 }
0373 
0374 void PimDataExporterWindow::slotAddError(const QString &info)
0375 {
0376     mLogWidget->addErrorLogEntry(info);
0377     qApp->processEvents();
0378 }
0379 
0380 void PimDataExporterWindow::slotAddTitle(const QString &info)
0381 {
0382     mLogWidget->addTitleLogEntry(info);
0383     qApp->processEvents();
0384 }
0385 
0386 void PimDataExporterWindow::slotAddEndLine()
0387 {
0388     mLogWidget->addEndLineLogEntry();
0389     qApp->processEvents();
0390 }
0391 
0392 void PimDataExporterWindow::slotRestoreData()
0393 {
0394     loadData();
0395 }
0396 
0397 void PimDataExporterWindow::loadData(const QString &filename, const QString &templateFile)
0398 {
0399     const int answer = KMessageBox::warningTwoActions(this,
0400                                                       i18n("Before restoring data you must close all kdepim applications. Do you want to continue?"),
0401                                                       i18n("Backup"),
0402                                                       KStandardGuiItem::cont(),
0403                                                       KStandardGuiItem::cancel());
0404     if (answer == KMessageBox::ButtonCode::SecondaryAction) {
0405         return;
0406     }
0407 
0408     // First select filename.
0409     QString currentFileName = filename;
0410     if (currentFileName.isEmpty()) {
0411         QString recentDirClass;
0412         currentFileName =
0413             QFileDialog::getOpenFileName(this,
0414                                          i18n("Restore backup"),
0415                                          KFileWidget::getStartUrl(QUrl(QStringLiteral("kfiledialog:///pimdataexporter")), recentDirClass).toLocalFile(),
0416                                          QStringLiteral("%1 (*.zip)").arg(i18n("Zip File")));
0417         if (currentFileName.isEmpty()) {
0418             return;
0419         }
0420         if (!recentDirClass.isEmpty()) {
0421             KRecentDirs::add(recentDirClass, currentFileName);
0422         }
0423     }
0424     // Don't put it in 'if' otherwise temporary file will be removed after that.
0425     QString templateFileName;
0426     PimDataImportDataInfoFile dataInfo;
0427     bool cleanupItems = false;
0428     if (templateFile.isEmpty()) {
0429         dataInfo.setCurrentFileName(currentFileName);
0430         templateFileName = dataInfo.importDataInfoPath();
0431         cleanupItems = true;
0432     } else {
0433         templateFileName = templateFile;
0434     }
0435     QPointer<SelectionTypeDialog> dialog = new SelectionTypeDialog(false, this);
0436     dialog->loadTemplate(templateFileName);
0437     if (cleanupItems) {
0438         dialog->removeNotSelectedItems();
0439     }
0440     if (dialog->exec()) {
0441         mLogWidget->clear();
0442         mNeedToSyncResources.clear();
0443         initializeBackupRestoreUi();
0444         mPimDataBackupRestoreUI->setStoredParameters(dialog->storedType());
0445         delete dialog;
0446 
0447         mTrayIcon->setStatus(KStatusNotifierItem::Active);
0448         mTrayIcon->setToolTipSubTitle(i18n("Restore in progress..."));
0449         if (!mPimDataBackupRestoreUI->restoreStart(currentFileName)) {
0450             qCDebug(PIMDATAEXPORTERGUI_LOG) << " PimDataExporterWindow restore failed";
0451         }
0452     } else {
0453         delete dialog;
0454     }
0455 }
0456 
0457 void PimDataExporterWindow::slotShowStructureInfos()
0458 {
0459     BackupFileStructureInfoDialog dlg(this);
0460     dlg.exec();
0461 }
0462 
0463 void PimDataExporterWindow::slotShowCurrentArchiveInformations()
0464 {
0465     if (!mLastArchiveFileName.isEmpty()) {
0466         ShowArchiveStructureDialog dlg(mLastArchiveFileName, this);
0467         dlg.exec();
0468     }
0469 }
0470 
0471 #include "moc_pimdataexporterwindow.cpp"