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"