File indexing completed on 2024-12-29 04:54:49

0001 /*
0002    SPDX-FileCopyrightText: 2015-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "sievescriptdebuggerfrontendwidget.h"
0008 #include "sievescriptdebuggerresulteditor.h"
0009 #include "sievescriptdebuggertextedit.h"
0010 #include "sievescriptdebuggerwarning.h"
0011 
0012 #include "editor/sievetextedit.h"
0013 #include "editor/sievetexteditwidget.h"
0014 #include <KLineEdit>
0015 #include <KLocalizedString>
0016 #include <TextCustomEditor/PlainTextEditorWidget>
0017 #ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
0018 #include <TextEditTextToSpeech/TextToSpeechContainerWidget>
0019 #endif
0020 #include <KLineEditEventHandler>
0021 #include <KUrlRequester>
0022 #include <QDate>
0023 #include <QFormLayout>
0024 #include <QLabel>
0025 #include <QProcess>
0026 #include <QScrollBar>
0027 #include <QSplitter>
0028 #include <QTemporaryFile>
0029 #include <QTextStream>
0030 #include <QVBoxLayout>
0031 
0032 using namespace KSieveUi;
0033 
0034 SieveScriptDebuggerFrontEndWidget::SieveScriptDebuggerFrontEndWidget(QWidget *parent)
0035     : QWidget(parent)
0036 {
0037     auto mainLayout = new QVBoxLayout(this);
0038     mainLayout->setContentsMargins({});
0039 
0040     auto formLayout = new QFormLayout;
0041     mainLayout->addLayout(formLayout);
0042 
0043     auto emailLab = new QLabel(i18n("Email path:"), this);
0044     emailLab->setObjectName(QLatin1StringView("emaillab"));
0045 
0046     mEmailPath = new KUrlRequester(this);
0047     mEmailPath->setObjectName(QLatin1StringView("emailpath"));
0048     mEmailPath->lineEdit()->setTrapReturnKey(true);
0049     mEmailPath->lineEdit()->setClearButtonEnabled(true);
0050     connect(mEmailPath->lineEdit(), &KLineEdit::textChanged, this, &SieveScriptDebuggerFrontEndWidget::slotEmailChanged);
0051 
0052     formLayout->addRow(emailLab, mEmailPath);
0053 
0054     auto extensionLayout = new QHBoxLayout;
0055     mainLayout->addLayout(extensionLayout);
0056 
0057     auto extensionLab = new QLabel(i18n("Extension:"));
0058     extensionLab->setObjectName(QLatin1StringView("extensionlab"));
0059 
0060     mExtension = new QLineEdit(this);
0061     mExtension->setObjectName(QLatin1StringView("extension"));
0062     mExtension->setPlaceholderText(i18n("Activate extension with \"+<name of extension>\", deactivate it with \"-<name of extension>\""));
0063     mExtension->setClearButtonEnabled(true);
0064     KLineEditEventHandler::catchReturnKey(mExtension);
0065 
0066     formLayout->addRow(extensionLab, mExtension);
0067 
0068     mSplitter = new QSplitter(Qt::Vertical);
0069     mSplitter->setObjectName(QLatin1StringView("splitter"));
0070     mainLayout->addWidget(mSplitter);
0071 
0072     auto sieveEditorWidget = new QWidget(this);
0073     auto vboxSieveEditorLayout = new QVBoxLayout;
0074     sieveEditorWidget->setLayout(vboxSieveEditorLayout);
0075     vboxSieveEditorLayout->setContentsMargins({});
0076 #ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
0077     auto textToSpeechWidget = new TextEditTextToSpeech::TextToSpeechContainerWidget(this);
0078     textToSpeechWidget->setObjectName(QLatin1StringView("texttospeechwidget"));
0079     vboxSieveEditorLayout->addWidget(textToSpeechWidget);
0080 #endif
0081     auto textEdit = new KSieveUi::SieveScriptDebuggerTextEdit(this);
0082     connect(textEdit, &KSieveUi::SieveScriptDebuggerTextEdit::textChanged, this, &SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged);
0083     mSieveTextEditWidget = new KSieveUi::SieveTextEditWidget(textEdit, this);
0084     mSieveTextEditWidget->setObjectName(QLatin1StringView("sievetexteditwidget"));
0085     vboxSieveEditorLayout->addWidget(mSieveTextEditWidget);
0086 #ifdef HAVE_KTEXTADDONS_TEXT_TO_SPEECH_SUPPORT
0087     connect(mSieveTextEditWidget->textEdit(), &SieveTextEdit::say, textToSpeechWidget, &TextEditTextToSpeech::TextToSpeechContainerWidget::say);
0088 #endif
0089     mSplitter->addWidget(sieveEditorWidget);
0090     mSplitter->setChildrenCollapsible(false);
0091 
0092     mSieveTestResult = new TextCustomEditor::PlainTextEditorWidget(new KSieveUi::SieveScriptDebuggerResultEditor(this), this);
0093     mSieveTestResult->setObjectName(QLatin1StringView("sievetextresult"));
0094     mSieveTestResult->editor()->setPlaceholderText(i18n("Display check script result..."));
0095     mSieveTestResult->setReadOnly(true);
0096     mSplitter->addWidget(mSieveTestResult);
0097 
0098     mSieveScriptDebuggerWarning = new KSieveUi::SieveScriptDebuggerWarning(this);
0099     mSieveScriptDebuggerWarning->setObjectName(QLatin1StringView("sievescriptdebuggerwarning"));
0100     mainLayout->addWidget(mSieveScriptDebuggerWarning);
0101 
0102     connect(this, &SieveScriptDebuggerFrontEndWidget::debugScriptButtonClicked, this, &SieveScriptDebuggerFrontEndWidget::slotDebugScript);
0103 }
0104 
0105 SieveScriptDebuggerFrontEndWidget::~SieveScriptDebuggerFrontEndWidget()
0106 {
0107     disconnect(mSieveTextEditWidget->textEdit(),
0108                &KSieveUi::SieveScriptDebuggerTextEdit::textChanged,
0109                this,
0110                &SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged);
0111 }
0112 
0113 void SieveScriptDebuggerFrontEndWidget::updateDebugButton()
0114 {
0115     Q_EMIT debugButtonEnabled(!mSieveTextEditWidget->textEdit()->document()->isEmpty() && !mEmailPath->lineEdit()->text().trimmed().isEmpty());
0116 }
0117 
0118 void SieveScriptDebuggerFrontEndWidget::slotScriptTextChanged()
0119 {
0120     updateDebugButton();
0121     Q_EMIT scriptTextChanged();
0122 }
0123 
0124 void SieveScriptDebuggerFrontEndWidget::setSplitterSizes(const QList<int> &sizes)
0125 {
0126     mSplitter->setSizes(sizes);
0127 }
0128 
0129 QList<int> SieveScriptDebuggerFrontEndWidget::splitterSizes() const
0130 {
0131     return mSplitter->sizes();
0132 }
0133 
0134 void SieveScriptDebuggerFrontEndWidget::slotEmailChanged(const QString &text)
0135 {
0136     Q_UNUSED(text)
0137     updateDebugButton();
0138 }
0139 
0140 void SieveScriptDebuggerFrontEndWidget::slotDebugScript()
0141 {
0142     if (mSieveTextEditWidget->textEdit()->toPlainText().trimmed().isEmpty()) {
0143         mSieveScriptDebuggerWarning->setErrorMessage(i18n("Script text is empty."));
0144         return;
0145     }
0146     if (!mEmailPath->url().isLocalFile()) {
0147         mSieveScriptDebuggerWarning->setWarningMessage(i18n("Email file must be installed locally."));
0148         return;
0149     }
0150 
0151     auto temporaryFile = new QTemporaryFile();
0152     if (!temporaryFile->open()) {
0153         mSieveScriptDebuggerWarning->setErrorMessage(i18n("Failed to open temporary file."));
0154         delete temporaryFile;
0155         return;
0156     }
0157     Q_EMIT debugButtonEnabled(false);
0158     QTextStream stream(temporaryFile);
0159     stream << mSieveTextEditWidget->textEdit()->toPlainText();
0160     temporaryFile->flush();
0161     mProcess = new QProcess(this);
0162     temporaryFile->setParent(mProcess);
0163 
0164     QString extensionList;
0165     if (!mExtension->text().trimmed().isEmpty()) {
0166         extensionList = QStringLiteral("-x \"%1\"").arg(mExtension->text());
0167     }
0168 
0169     QStringList arguments;
0170     if (!extensionList.isEmpty()) {
0171         arguments << extensionList;
0172     }
0173 
0174     arguments << temporaryFile->fileName() << mEmailPath->url().toLocalFile();
0175     mProcess->start(QStringLiteral("sieve-test"), QStringList() << arguments);
0176     connect(mProcess, &QProcess::readyReadStandardOutput, this, &SieveScriptDebuggerFrontEndWidget::slotReadStandardOutput);
0177     connect(mProcess, &QProcess::readyReadStandardError, this, &SieveScriptDebuggerFrontEndWidget::slotReadErrorOutput);
0178     connect(mProcess, &QProcess::finished, this, &SieveScriptDebuggerFrontEndWidget::slotDebugFinished);
0179     mSieveTestResult->editor()->appendPlainText(QStringLiteral("--------------------------------------"));
0180     mSieveTestResult->editor()->appendPlainText(QLocale().toString(QDateTime::currentDateTime()));
0181     mSieveTestResult->editor()->appendPlainText(QStringLiteral("\n"));
0182     if (!mProcess->waitForStarted()) {
0183         delete mProcess;
0184         mProcess = nullptr;
0185         Q_EMIT debugButtonEnabled(true);
0186     }
0187 }
0188 
0189 void SieveScriptDebuggerFrontEndWidget::slotReadStandardOutput()
0190 {
0191     const QByteArray result = mProcess->readAllStandardOutput();
0192     mSieveTestResult->editor()->appendPlainText(QString::fromLocal8Bit(result));
0193 }
0194 
0195 void SieveScriptDebuggerFrontEndWidget::slotReadErrorOutput()
0196 {
0197     const QByteArray result = mProcess->readAllStandardError();
0198     mSieveTestResult->editor()->appendPlainText(QString::fromLocal8Bit(result));
0199 }
0200 
0201 void SieveScriptDebuggerFrontEndWidget::slotDebugFinished()
0202 {
0203     delete mProcess;
0204     mProcess = nullptr;
0205     Q_EMIT debugButtonEnabled(true);
0206 }
0207 
0208 QString SieveScriptDebuggerFrontEndWidget::script() const
0209 {
0210     return mSieveTextEditWidget->textEdit()->toPlainText();
0211 }
0212 
0213 void SieveScriptDebuggerFrontEndWidget::setScript(const QString &script)
0214 {
0215     mSieveTextEditWidget->textEdit()->setPlainText(script);
0216     mSieveTextEditWidget->textEdit()->verticalScrollBar()->setValue(mSieveTextEditWidget->textEdit()->verticalScrollBar()->maximum());
0217 }
0218 
0219 bool SieveScriptDebuggerFrontEndWidget::canAccept() const
0220 {
0221     const QString script = mSieveTextEditWidget->textEdit()->toPlainText();
0222     if (script.contains(QLatin1StringView("debug_log")) || script.contains(QLatin1StringView("vnd.dovecot.debug"))) {
0223         mSieveScriptDebuggerWarning->setErrorMessage(i18n("Script still contains debug method. Remove it please."));
0224         return false;
0225     }
0226     mSieveScriptDebuggerWarning->hide();
0227     return true;
0228 }
0229 
0230 #include "moc_sievescriptdebuggerfrontendwidget.cpp"