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"