Git commit c1f10614d33602200694d38a737b494a0918eb44 by Dan Vr?til. Committed on 10/10/2014 at 11:32. Pushed by dvratil into branch 'master'.
KSieve/Vacation: Add start date/end date when out-of-office reply script should be active When the Sieve server supports "date" extension, we will show a date edits that allow users to specify date range in which the out-of-office reply script should be active. This is achieved by generating additional condition in the Sieve script. REVIEW: 120540 GUI: New input fields in Tools -> Edit "Out of Office" Replies... dialog M +9 -2 libksieve/ksieveui/vacation/vacation.cpp M +22 -1 libksieve/ksieveui/vacation/vacationcheckjob.cpp M +25 -0 libksieve/ksieveui/vacation/vacationdialog.cpp M +8 -0 libksieve/ksieveui/vacation/vacationdialog.h M +63 -0 libksieve/ksieveui/vacation/vacationeditwidget.cpp M +16 -0 libksieve/ksieveui/vacation/vacationeditwidget.h M +10 -2 libksieve/ksieveui/vacation/vacationpagewidget.cpp M +86 -0 libksieve/ksieveui/vacation/vacationscriptextractor.h M +34 -4 libksieve/ksieveui/vacation/vacationutils.cpp M +8 -2 libksieve/ksieveui/vacation/vacationutils.h http://commits.kde.org/kdepim/c1f10614d33602200694d38a737b494a0918eb44 diff --git a/libksieve/ksieveui/vacation/vacation.cpp b/libksieve/ksieveui/vacation/vacation.cpp index 8702f5c..2842500 100644 --- a/libksieve/ksieveui/vacation/vacation.cpp +++ b/libksieve/ksieveui/vacation/vacation.cpp @@ -112,11 +112,13 @@ void Vacation::slotGetResult(KManageSieve::SieveJob *job, bool success, QStringList aliases = VacationUtils::defaultMailAliases(); bool sendForSpam = VacationUtils::defaultSendForSpam(); QString domainName = VacationUtils::defaultDomainName(); + QDate startDate = VacationUtils::defaultStartDate(); + QDate endDate = VacationUtils::defaultEndDate(); if (!success) { active = false; // default to inactive } - if (!mCheckOnly && (!success || !KSieveUi::VacationUtils::parseScript(script, messageText, notificationInterval, aliases, sendForSpam, domainName))) + if (!mCheckOnly && (!success || !KSieveUi::VacationUtils::parseScript(script, messageText, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate))) KMessageBox::information(0, i18n("Someone (probably you) changed the " "vacation script on the server.\n" "KMail is no longer able to determine " @@ -132,6 +134,9 @@ void Vacation::slotGetResult(KManageSieve::SieveJob *job, bool success, mDialog->setSendForSpam(sendForSpam); mDialog->setDomainName(domainName); mDialog->enableDomainAndSendForSpam(!VacationSettings::allowOutOfOfficeUploadButNoSettings()); + mDialog->enableDates(job->sieveCapabilities().contains(QLatin1String("date"))); + mDialog->setStartDate(startDate); + mDialog->setEndDate(endDate); connect(mDialog, &VacationDialog::okClicked, this, &Vacation::slotDialogOk); connect(mDialog, &VacationDialog::cancelClicked, this, &Vacation::slotDialogCancel); @@ -158,7 +163,9 @@ void Vacation::slotDialogOk() mDialog->notificationInterval(), mDialog->mailAliases(), mDialog->sendForSpam(), - mDialog->domainName()); + mDialog->domainName(), + mDialog->startDate(), + mDialog->endDate()); const bool active = mDialog->activateVacation(); emit scriptActive(active, mServerName); diff --git a/libksieve/ksieveui/vacation/vacationcheckjob.cpp b/libksieve/ksieveui/vacation/vacationcheckjob.cpp index 8eeab6c..e83c960 100644 --- a/libksieve/ksieveui/vacation/vacationcheckjob.cpp +++ b/libksieve/ksieveui/vacation/vacationcheckjob.cpp @@ -16,9 +16,12 @@ */ #include "vacationcheckjob.h" +#include "vacationutils.h" #include <kmanagesieve/sievejob.h> +#include <QDate> + using namespace KSieveUi; VacationCheckJob::VacationCheckJob(const QUrl &url, const QString &serverName, QObject *parent) : QObject(parent), @@ -39,11 +42,29 @@ VacationCheckJob::~VacationCheckJob() mSieveJob = 0; } -void VacationCheckJob::slotGetResult(KManageSieve::SieveJob */*job*/, bool success, const QString &/*script*/, bool active) +void VacationCheckJob::slotGetResult(KManageSieve::SieveJob */*job*/, bool success, const QString &script, bool active) { mSieveJob = 0; if (!success) { active = false; // default to inactive } + + QDate startDate, endDate; + + QString dummyStr; + QStringList dummyStrList; + int dummyInt; + bool dummyBool; + + // If the script is active then parse it, and verify whether it has date range set + if (active) { + bool valid = VacationUtils::parseScript(script, dummyStr, dummyInt, dummyStrList, dummyBool, dummyStr, startDate, endDate); + // If the date range is set, mark the script as active only if the date range + // includes now/today + if (valid && startDate.isValid() && endDate.isValid()) { + active = (startDate <= QDate::currentDate() && endDate >= QDate::currentDate()); + } + } + emit scriptActive(active, mServerName); } diff --git a/libksieve/ksieveui/vacation/vacationdialog.cpp b/libksieve/ksieveui/vacation/vacationdialog.cpp index bbc858f..a8a86a1 100644 --- a/libksieve/ksieveui/vacation/vacationdialog.cpp +++ b/libksieve/ksieveui/vacation/vacationdialog.cpp @@ -184,3 +184,28 @@ void VacationDialog::slotDialogDefaults() { mVacationEditWidget->setDefault(); } + +void VacationDialog::enableDates(bool enable) +{ + mVacationEditWidget->enableDates(enable); +} + +QDate VacationDialog::endDate() const +{ + return mVacationEditWidget->endDate(); +} + +void VacationDialog::setEndDate(const QDate &endDate) +{ + mVacationEditWidget->setEndDate(endDate); +} + +QDate VacationDialog::startDate() const +{ + return mVacationEditWidget->startDate(); +} + +void VacationDialog::setStartDate(const QDate &startDate) +{ + mVacationEditWidget->setStartDate(startDate); +} diff --git a/libksieve/ksieveui/vacation/vacationdialog.h b/libksieve/ksieveui/vacation/vacationdialog.h index bbb30eb..ae90800 100644 --- a/libksieve/ksieveui/vacation/vacationdialog.h +++ b/libksieve/ksieveui/vacation/vacationdialog.h @@ -17,6 +17,7 @@ #include <QDialog> template <typename T> class QList; +class QDate; namespace KMime { @@ -40,6 +41,7 @@ public: ~VacationDialog(); void enableDomainAndSendForSpam(bool enable = true); + void enableDates(bool enable = true); bool activateVacation() const; void setActivateVacation(bool activate); @@ -63,6 +65,12 @@ public: bool sendForSpam() const; void setSendForSpam(bool enable); + QDate startDate() const; + void setStartDate(const QDate &startDate); + + QDate endDate() const; + void setEndDate(const QDate &endDate); + Q_SIGNALS: void okClicked(); void cancelClicked(); diff --git a/libksieve/ksieveui/vacation/vacationeditwidget.cpp b/libksieve/ksieveui/vacation/vacationeditwidget.cpp index ad8d61e..9206243 100644 --- a/libksieve/ksieveui/vacation/vacationeditwidget.cpp +++ b/libksieve/ksieveui/vacation/vacationeditwidget.cpp @@ -19,6 +19,7 @@ #include "vacationutils.h" #include <KLocalizedString> +#include <KDateComboBox> #include <QSpinBox> #include <QLineEdit> @@ -66,6 +67,32 @@ VacationEditWidget::VacationEditWidget(QWidget *parent) mTextEdit->setAcceptRichText(false); glay->addWidget(mTextEdit, row, 0, 1, 2); + // Start date + ++row; + mStartDate = new KDateComboBox(this); + mStartDate->setObjectName(QLatin1String("mStartDate")); + mStartDate->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords); + mStartDateLabel = new QLabel(i18n("&Start date:"), this); + mStartDateLabel->setObjectName(QLatin1String("mStartDateLabel")); + mStartDateLabel->setBuddy(mStartDate); + glay->addWidget(mStartDateLabel, row, 0); + glay->addWidget(mStartDate, row, 1); + + // End date + ++row; + mEndDate = new KDateComboBox(this); + mEndDate->setObjectName(QLatin1String("mEndDate")); + mEndDate->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords); + mEndDateLabel = new QLabel(i18n("&End date:"), this); + mEndDateLabel->setObjectName(QLatin1String("mStartDateLabel")); + mEndDateLabel->setBuddy(mEndDate); + glay->addWidget(mEndDateLabel, row, 0); + glay->addWidget(mEndDate, row, 1); + + // Hide the date edits by default - they must be enabled by caller when the + // server supports this feature + enableDates(false); + // "Resent only after" spinbox and label: ++row; int defDayInterval = 7; //default day interval @@ -222,6 +249,42 @@ void VacationEditWidget::setSendForSpam(bool enable) mSpamCheck->setChecked(!enable); } +QDate VacationEditWidget::endDate() const +{ + if (mEndDate->isEnabled()) { + return mEndDate->date(); + } else { + return QDate(); + } +} + +void VacationEditWidget::setEndDate(const QDate &endDate) +{ + mEndDate->setDate(endDate); +} + +QDate VacationEditWidget::startDate() const +{ + if (mStartDate->isEnabled()) { + return mStartDate->date(); + } else { + return QDate(); + } +} + +void VacationEditWidget::setStartDate(const QDate &startDate) +{ + mStartDate->setDate(startDate); +} + +void VacationEditWidget::enableDates(bool enable) +{ + mStartDate->setVisible(enable); + mStartDateLabel->setVisible(enable); + mEndDate->setVisible(enable); + mEndDateLabel->setVisible(enable); +} + void VacationEditWidget::enableDomainAndSendForSpam(bool enable) { mDomainCheck->setEnabled(enable); diff --git a/libksieve/ksieveui/vacation/vacationeditwidget.h b/libksieve/ksieveui/vacation/vacationeditwidget.h index 71513e3..b54759c 100644 --- a/libksieve/ksieveui/vacation/vacationeditwidget.h +++ b/libksieve/ksieveui/vacation/vacationeditwidget.h @@ -19,8 +19,13 @@ #define VACATIONEDITWIDGET_H #include <QWidget> + +class QLabel; class QSpinBox; class QLineEdit; +class KDateComboBox; + +class QDate; namespace PimCommon { @@ -49,6 +54,7 @@ public: ~VacationEditWidget(); void enableDomainAndSendForSpam(bool enable = true); + void enableDates(bool enable = true); bool activateVacation() const; void setActivateVacation(bool activate); @@ -72,6 +78,12 @@ public: bool sendForSpam() const; void setSendForSpam(bool enable); + QDate startDate() const; + void setStartDate(const QDate &startDate); + + QDate endDate() const; + void setEndDate(const QDate &endDate); + void setDefault(); private Q_SLOTS: @@ -85,6 +97,10 @@ protected: QCheckBox *mSpamCheck; QCheckBox *mDomainCheck; QLineEdit *mDomainEdit; + KDateComboBox *mStartDate; + QLabel *mStartDateLabel; + KDateComboBox *mEndDate; + QLabel *mEndDateLabel; }; } diff --git a/libksieve/ksieveui/vacation/vacationpagewidget.cpp b/libksieve/ksieveui/vacation/vacationpagewidget.cpp index 529ac14..f9be363 100644 --- a/libksieve/ksieveui/vacation/vacationpagewidget.cpp +++ b/libksieve/ksieveui/vacation/vacationpagewidget.cpp @@ -109,17 +109,20 @@ void VacationPageWidget::slotGetResult(KManageSieve::SieveJob *job, bool success mStackWidget->setCurrentIndex(ScriptNotSupported); return; } + mVacationEditWidget->setEnabled(true); QString messageText = VacationUtils::defaultMessageText(); int notificationInterval = VacationUtils::defaultNotificationInterval(); QStringList aliases = VacationUtils::defaultMailAliases(); bool sendForSpam = VacationUtils::defaultSendForSpam(); QString domainName = VacationUtils::defaultDomainName(); + QDate startDate = VacationUtils::defaultStartDate(); + QDate endDate = VacationUtils::defaultEndDate(); if (!success) { active = false; // default to inactive } - if ((!success || !KSieveUi::VacationUtils::parseScript(script, messageText, notificationInterval, aliases, sendForSpam, domainName))) { + if ((!success || !KSieveUi::VacationUtils::parseScript(script, messageText, notificationInterval, aliases, sendForSpam, domainName, startDate, endDate))) { mVacationWarningWidget->setVisible(true); } @@ -131,6 +134,9 @@ void VacationPageWidget::slotGetResult(KManageSieve::SieveJob *job, bool success mVacationEditWidget->setSendForSpam(sendForSpam); mVacationEditWidget->setDomainName(domainName); mVacationEditWidget->enableDomainAndSendForSpam(!VacationSettings::allowOutOfOfficeUploadButNoSettings()); + mVacationEditWidget->enableDates(job->sieveCapabilities().contains(QLatin1String("date"))); + mVacationEditWidget->setStartDate(startDate); + mVacationEditWidget->setEndDate(endDate); //emit scriptActive( mWasActive, mServerName ); } @@ -145,7 +151,9 @@ KSieveUi::VacationCreateScriptJob *VacationPageWidget::writeScript() mVacationEditWidget->notificationInterval(), mVacationEditWidget->mailAliases(), mVacationEditWidget->sendForSpam(), - mVacationEditWidget->domainName()); + mVacationEditWidget->domainName(), + mVacationEditWidget->startDate(), + mVacationEditWidget->endDate()); const bool active = mVacationEditWidget->activateVacation(); createJob->setStatus(active, mWasActive); //Q_EMIT scriptActive( active, mServerName); diff --git a/libksieve/ksieveui/vacation/vacationscriptextractor.h b/libksieve/ksieveui/vacation/vacationscriptextractor.h index 9d3ac40..d4cda95 100644 --- a/libksieve/ksieveui/vacation/vacationscriptextractor.h +++ b/libksieve/ksieveui/vacation/vacationscriptextractor.h @@ -65,6 +65,18 @@ public: mBuilders[2] = sb3; assert(sb1); assert(sb2); assert(sb3); } + MultiScriptBuilder(KSieve::ScriptBuilder * sb1, + KSieve::ScriptBuilder * sb2, + KSieve::ScriptBuilder * sb3, + KSieve::ScriptBuilder * sb4) + : KSieve::ScriptBuilder(), mBuilders(4) + { + mBuilders[0] = sb1; + mBuilders[1] = sb2; + mBuilders[2] = sb3; + mBuilders[3] = sb4; + assert(sb1); assert(sb2); assert(sb3); assert(sb4); + } ~MultiScriptBuilder() {} private: #ifdef FOREACH @@ -440,6 +452,80 @@ public: } }; + +// if not allof (currentDate :value "ge" date "YYYY-MM-DD", +// currentDate :value "le" date "YYYY-MM-DD") { keep; stop; } +static const GenericInformationExtractor::StateNode datesNodes[] = { + { 0, GIE::CommandStart, "if", 1, 0, 0 }, // 0 + { 0, GIE::TestStart, "not", 2, 0, 0 }, // 1 + { 0, GIE::TestStart, "allof", 3, 0, 0 }, // 2 + + // handle startDate and endDate in arbitrary order + { 0, GIE::TestListStart, 0, 4, 0, 0 }, // 3 + { 0, GIE::TestStart, "currentdate", 5, 0, 0 }, // 4 + { 0, GIE::TaggedArgument, "value", 6, 0, 0 }, // 5 + { 0, GIE::StringArgument, "ge", 7, 9, 0 }, // 6 + { 0, GIE::StringArgument, "date", 8, 0, 0 }, // 7 + { 0, GIE::StringArgument, 0, 12, 0, "startDate" }, // 8 + { 0, GIE::StringArgument, "le", 10, 0, 0 }, // 9 + { 0, GIE::StringArgument, "date", 11, 0, 0 }, // 10 + { 0, GIE::StringArgument, 0, 12, 0, "endDate" }, // 11 + { 0, GIE::TestEnd, 0, 13, 0, 0 }, // 12 + + { 0, GIE::TestStart, "currentdate", 14, 0, 0 }, // 13 + { 0, GIE::TaggedArgument, "value", 15, 0, 0 }, // 14 + { 0, GIE::StringArgument, "le", 16, 18, 0 }, // 15 + { 0, GIE::StringArgument, "date", 17, 0, 0 }, // 16 + { 0, GIE::StringArgument, 0, 21, 0, "endDate" }, // 17 + { 0, GIE::StringArgument, "ge", 19, 0, 0 }, // 18 + { 0, GIE::StringArgument, "date", 20, 0, 0 }, // 19 + { 0, GIE::StringArgument, 0, 21, 0, "startDate" }, // 20 + { 0, GIE::TestEnd, 0, 22, 0, 0 }, // 21 + { 0, GIE::TestListEnd, 0, 23, 0, 0 }, // 22 + + { 0, GIE::TestEnd, 0, 24, 0, 0 }, // 23 + { 0, GIE::TestEnd, 0, 25, 0, 0 }, // 24 + + // block of commands, find "stop", take nested if's into account: + { 0, GIE::BlockStart, 0, 26, 0, 0 }, // 25 + { 1, GIE::CommandStart, "stop", 29, 28, "stop" }, // 26 + { -1, GIE::Any, 0, 26, 0, 0 }, // 27 + { 0, GIE::BlockEnd, 0, 0, 27, 0 }, // 28 + + { -1, GIE::Any, 0, 27, 27, 0 } // 29 end state +}; + +static const unsigned int numDatesNodes = sizeof datesNodes / sizeof *datesNodes; + +class DateExtractor : public GenericInformationExtractor +{ +public: + DateExtractor() + : GenericInformationExtractor(std::vector<StateNode>(datesNodes, datesNodes+numDatesNodes)) + { + } + + QDate endDate() const + { + return date(QLatin1String("endDate")); + } + + QDate startDate() const + { + return date(QLatin1String("startDate")); + } + +private: + QDate date(const QString &name) const { + if (mResults.count(name) == 0) { + return QDate(); + } else { + return QDate::fromString(mResults.at(name), Qt::ISODate); + } + } +}; + + class VacationDataExtractor : public KSieve::ScriptBuilder { enum Context { diff --git a/libksieve/ksieveui/vacation/vacationutils.cpp b/libksieve/ksieveui/vacation/vacationutils.cpp index 24accb9..c1e124d 100644 --- a/libksieve/ksieveui/vacation/vacationutils.cpp +++ b/libksieve/ksieveui/vacation/vacationutils.cpp @@ -80,9 +80,20 @@ QString KSieveUi::VacationUtils::defaultDomainName() return VacationSettings::outOfOfficeDomain(); } +QDate KSieveUi::VacationUtils::defaultStartDate() +{ + return QDate::currentDate(); +} + +QDate KSieveUi::VacationUtils::defaultEndDate() +{ + return defaultStartDate().addDays(7); +} + bool KSieveUi::VacationUtils::parseScript(const QString &script, QString &messageText, int ¬ificationInterval, QStringList &aliases, - bool &sendForSpam, QString &domainName) + bool &sendForSpam, QString &domainName, + QDate &startDate, QDate &endDate) { if (script.trimmed().isEmpty()) { messageText = VacationUtils::defaultMessageText(); @@ -103,7 +114,8 @@ bool KSieveUi::VacationUtils::parseScript(const QString &script, QString &messag VacationDataExtractor vdx; SpamDataExtractor sdx; DomainRestrictionDataExtractor drdx; - KSieveExt::MultiScriptBuilder tsb(&vdx, &sdx, &drdx); + DateExtractor dtx; + KSieveExt::MultiScriptBuilder tsb(&vdx, &sdx, &drdx, &dtx); parser.setScriptBuilder(&tsb); if (!parser.parse()) { return false; @@ -115,13 +127,16 @@ bool KSieveUi::VacationUtils::parseScript(const QString &script, QString &messag sendForSpam = !sdx.found(); domainName = drdx.domainName(); } + startDate = dtx.startDate(); + endDate = dtx.endDate(); return true; } QString KSieveUi::VacationUtils::composeScript(const QString &messageText, int notificationInterval, const AddrSpecList &addrSpecs, - bool sendForSpam, const QString &domain) + bool sendForSpam, const QString &domain, + const QDate &startDate, const QDate &endDate) { QString addressesArgument; QStringList aliases; @@ -135,7 +150,15 @@ QString KSieveUi::VacationUtils::composeScript(const QString &messageText, } addressesArgument += sl.join(QLatin1String(", ")) + QLatin1String(" ] "); } - QString script = QString::fromLatin1("require \"vacation\";\n\n"); + + QString script = QString::fromLatin1("require \"vacation\";\n"); + if (startDate.isValid() && endDate.isValid()) { + script += QString::fromLatin1("require \"relational\";\n" + "require \"date\";\n\n"); + } else { + script += QString::fromLatin1("\n"); + } + if (!sendForSpam) script += QString::fromLatin1("if header :contains \"X-Spam-Flag\" \"YES\"" " { keep; stop; }\n"); // FIXME? @@ -144,6 +167,13 @@ QString KSieveUi::VacationUtils::composeScript(const QString &messageText, script += QString::fromLatin1("if not address :domain :contains \"from\" \"%1\" { keep; stop; }\n").arg(domain); } + if (startDate.isValid() && endDate.isValid()) { + script += QString::fromLatin1("if not allof(currentdate :value \"ge\" \"date\" \"%1\"," + " currentdate :value \"le\" \"date\" \"%2\")" + " { keep; stop; }\n").arg(startDate.toString(Qt::ISODate), + endDate.toString(Qt::ISODate)); + } + script += QLatin1String("vacation "); script += addressesArgument; if (notificationInterval > 0) { diff --git a/libksieve/ksieveui/vacation/vacationutils.h b/libksieve/ksieveui/vacation/vacationutils.h index 84b54c8..a0c2f20 100644 --- a/libksieve/ksieveui/vacation/vacationutils.h +++ b/libksieve/ksieveui/vacation/vacationutils.h @@ -20,6 +20,8 @@ #include <QStringList> #include <QString> +class QDate; + namespace KMime { namespace Types @@ -38,14 +40,18 @@ int defaultNotificationInterval(); QStringList defaultMailAliases(); bool defaultSendForSpam(); QString defaultDomainName(); +QDate defaultStartDate(); +QDate defaultEndDate(); QString composeScript(const QString &messageText, int notificationInterval, const KMime::Types::AddrSpecList &aliases, - bool sendForSpam, const QString &excludeDomain); + bool sendForSpam, const QString &excludeDomain, + const QDate &startDate, const QDate &endDate); bool parseScript(const QString &script, QString &messageText, int ¬ificationInterval, QStringList &aliases, - bool &sendForSpam, QString &domainName); + bool &sendForSpam, QString &domainName, + QDate &startDate, QDate &endDate); } }
