File indexing completed on 2024-10-13 05:07:24

0001 // SPDX-FileCopyrightText: 2021 Simon Persson <simon.persson@mykolab.com>
0002 //
0003 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "purger.h"
0006 #include "kuppurger_debug.h"
0007 
0008 #include <KFormat>
0009 #include <KLocalizedString>
0010 #include <KStandardAction>
0011 #include <KToolBar>
0012 
0013 #include <QDateTime>
0014 #include <QGuiApplication>
0015 #include <QHash>
0016 #include <QSplitter>
0017 
0018 Purger::Purger(QString pRepoPath, QString pBranchName, QWidget *pParent)
0019     : KMainWindow(pParent), mRepoPath(std::move(pRepoPath)), 
0020       mBranchName(std::move(pBranchName))
0021 {
0022     setWindowIcon(QIcon::fromTheme(QStringLiteral("kup")));
0023     KToolBar *lAppToolBar = toolBar();
0024     lAppToolBar->addAction(KStandardAction::quit(this, SLOT(close()), this));
0025     mDeleteAction = KStandardAction::deleteFile(this, SLOT(purge()), this);
0026     lAppToolBar->addAction(mDeleteAction);
0027 
0028     mListWidget = new QListWidget();
0029     auto lSplitter = new QSplitter();
0030     lSplitter->addWidget(mListWidget);
0031     mTextEdit = new QTextEdit();
0032     mTextEdit->setReadOnly(true);
0033     lSplitter->addWidget(mTextEdit);
0034     setCentralWidget(lSplitter);
0035     
0036     mCollectProcess = new KProcess();
0037     mCollectProcess->setOutputChannelMode(KProcess::SeparateChannels);
0038     *mCollectProcess << QStringLiteral("bup");
0039     *mCollectProcess << QStringLiteral("-d") << mRepoPath;
0040     *mCollectProcess << QStringLiteral("gc") << QStringLiteral("--unsafe") << QStringLiteral("--verbose");
0041     connect(mCollectProcess, &KProcess::readyReadStandardError, this, [this] {
0042         auto lLogText = QString::fromUtf8(mCollectProcess->readAllStandardError());
0043         qCInfo(KUPPURGER) << lLogText;
0044         mTextEdit->append(lLogText);
0045     });
0046     connect(mCollectProcess, SIGNAL(finished(int,QProcess::ExitStatus)), 
0047             SLOT(purgeDone(int,QProcess::ExitStatus)));
0048 
0049     mListProcess = new KProcess();
0050     mListProcess->setOutputChannelMode(KProcess::SeparateChannels);
0051     *mListProcess << QStringLiteral("bup");
0052     *mListProcess << QStringLiteral("-d") << mRepoPath;
0053     *mListProcess << QStringLiteral("ls") << QStringLiteral("--hash") << mBranchName;
0054     connect(mListProcess, &KProcess::readyReadStandardOutput, this, [this] {
0055         KFormat lFormat;
0056         const auto lLines = QString::fromUtf8(mListProcess->readAllStandardOutput()).split(QChar::LineFeed);
0057         for(const QString &lLine: lLines) {
0058             qCDebug(KUPPURGER) << lLine;
0059             const auto lHash = lLine.left(40);
0060             if(!lHash.isEmpty() && lHash != QStringLiteral("0000000000000000000000000000000000000000")) {
0061                 const auto lTimeStamp = lLine.mid(41);
0062                 if(mHashes.contains(lHash)) {
0063                     auto lItem = mHashes.value(lHash);
0064                     lItem->setWhatsThis(lItem->whatsThis() + QChar::LineFeed + lTimeStamp);
0065                 } else {
0066                     const auto lDateTime = QDateTime::fromString(lTimeStamp, QStringLiteral("yyyy-MM-dd-HHmmss"));
0067                     const auto lDisplayText = lFormat.formatRelativeDateTime(lDateTime, QLocale::ShortFormat);
0068                     auto lItem = new QListWidgetItem(lDisplayText, mListWidget);
0069                     lItem->setWhatsThis(lTimeStamp); //misuse of field, for later use when removing
0070                     lItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
0071                     lItem->setCheckState(Qt::Unchecked);
0072                     mHashes.insert(lHash, lItem);
0073                 }
0074             }
0075         }
0076     });
0077     connect(mListProcess, SIGNAL(finished(int,QProcess::ExitStatus)), 
0078             SLOT(listDone(int,QProcess::ExitStatus)));
0079 
0080     fillListWidget();
0081 }
0082 
0083 QSize Purger::sizeHint() const {
0084     return {800, 600};
0085 }
0086 
0087 void Purger::fillListWidget() {
0088     QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0089     mListWidget->clear();
0090     mHashes.clear();
0091     mDeleteAction->setEnabled(false);
0092     mListProcess->start();
0093 }
0094 
0095 void Purger::listDone(int, QProcess::ExitStatus) {
0096     QGuiApplication::restoreOverrideCursor();
0097     mDeleteAction->setEnabled(true);
0098 }
0099 
0100 void Purger::purge() {
0101     qCInfo(KUPPURGER)  << "Starting purge operation.";
0102     mDeleteAction->setEnabled(false);
0103     bool lAnythingRemoved = false;
0104     for(int i=0; i < mListWidget->count(); ++i) {
0105         auto lItem = mListWidget->item(i);
0106         qCInfo(KUPPURGER)  << lItem->text() << lItem->whatsThis() << lItem->checkState();
0107         if(lItem->checkState() == Qt::Checked) {
0108             const auto lTimeStamps = lItem->whatsThis().split(QChar::LineFeed);
0109             for(const QString &lTimeStamp: lTimeStamps) {
0110                 KProcess lRemoveProcess;
0111                 lRemoveProcess.setOutputChannelMode(KProcess::SeparateChannels);
0112                 lRemoveProcess << QStringLiteral("bup");
0113                 lRemoveProcess << QStringLiteral("-d") << mRepoPath << QStringLiteral("rm");
0114                 lRemoveProcess << QStringLiteral("--unsafe") << QStringLiteral("--verbose");
0115                 lRemoveProcess << QString("%1/%2").arg(mBranchName, lTimeStamp);
0116                 qCInfo(KUPPURGER)  << lRemoveProcess.program();
0117                 if(lRemoveProcess.execute() == 0) {
0118                     lAnythingRemoved = true;
0119                     qCInfo(KUPPURGER) << "Successfully removed snapshot";
0120                 }
0121                 const auto lLogText = QString::fromUtf8(lRemoveProcess.readAllStandardError());
0122                 qCInfo(KUPPURGER) << lLogText;
0123                 mTextEdit->append(lLogText);
0124             }
0125         }
0126     }
0127     if(lAnythingRemoved) {
0128         QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0129         qCInfo(KUPPURGER)  << mCollectProcess->program();
0130         mCollectProcess->start();
0131     } else {
0132         mDeleteAction->setEnabled(true);
0133     }
0134 }
0135 
0136 void Purger::purgeDone(int pExitCode, QProcess::ExitStatus pExitStatus) {
0137     qCInfo(KUPPURGER)  << pExitCode << pExitStatus;
0138     QGuiApplication::restoreOverrideCursor();
0139     mDeleteAction->setEnabled(true);
0140     const auto lLogText = QString::fromUtf8(mCollectProcess->readAllStandardError());
0141     qCInfo(KUPPURGER) << lLogText;
0142     mTextEdit->append(lLogText);
0143     fillListWidget();
0144 }
0145