Improved Sieve Script editor with lists of available Actions, Controls and Tests

This commit is contained in:
the-djmaze
2022-03-17 10:21:23 +01:00
parent ff9a89380c
commit f47eb61aee
14 changed files with 269 additions and 164 deletions

181
dev/Sieve/Commands.js Normal file
View File

@@ -0,0 +1,181 @@
import { capa } from 'Sieve/Utils';
import {
ActionCommand,
ControlCommand,
TestCommand
} from 'Sieve/Grammar';
import {
DiscardCommand,
FileIntoCommand,
KeepCommand,
RedirectCommand
} from 'Sieve/Commands/Actions';
import {
ConditionalCommand,
ElsIfCommand,
ElseCommand,
IfCommand,
RequireCommand,
StopCommand
} from 'Sieve/Commands/Controls';
import {
AddressTest,
AllOfTest,
AnyOfTest,
EnvelopeTest,
ExistsTest,
FalseTest,
HeaderTest,
NotTest,
SizeTest,
TrueTest
} from 'Sieve/Commands/Tests';
import { BodyTest } from 'Sieve/Extensions/rfc5173';
import { EnvironmentTest } from 'Sieve/Extensions/rfc5183';
import { SetCommand, StringTest } from 'Sieve/Extensions/rfc5229';
import { VacationCommand } from 'Sieve/Extensions/rfc5230';
import { SetFlagCommand, AddFlagCommand, RemoveFlagCommand, HasFlagTest } from 'Sieve/Extensions/rfc5232';
import { SpamTestTest, VirusTestTest } from 'Sieve/Extensions/rfc5235';
import { DateTest, CurrentDateTest } from 'Sieve/Extensions/rfc5260';
import { AddHeaderCommand, DeleteHeaderCommand } from 'Sieve/Extensions/rfc5293';
import { ErejectCommand, RejectCommand } from 'Sieve/Extensions/rfc5429';
import { NotifyCommand, ValidNotifyMethodTest, NotifyMethodCapabilityTest } from 'Sieve/Extensions/rfc5435';
import { IHaveTest, ErrorCommand } from 'Sieve/Extensions/rfc5463';
import { MailboxExistsTest, MetadataTest, MetadataExistsTest } from 'Sieve/Extensions/rfc5490';
import { ForEveryPartCommand, BreakCommand, ReplaceCommand, EncloseCommand, ExtractTextCommand } from 'Sieve/Extensions/rfc5703';
import { IncludeCommand, ReturnCommand, GlobalCommand } from 'Sieve/Extensions/rfc6609';
export const
getIdentifier = (cmd, type) => {
const obj = new cmd, requires = obj.require;
return (
(!type || obj instanceof type)
&& (!requires || (Array.isArray(requires) ? requires : [requires]).every(string => capa.includes(string)))
)
? obj.identifier
: null;
},
AllCommands = [
// Control commands
IfCommand,
ElsIfCommand,
ElseCommand,
ConditionalCommand,
RequireCommand,
StopCommand,
// Action commands
DiscardCommand,
FileIntoCommand,
KeepCommand,
RedirectCommand,
// Test commands
AddressTest,
AllOfTest,
AnyOfTest,
EnvelopeTest,
ExistsTest,
FalseTest,
HeaderTest,
NotTest,
SizeTest,
TrueTest,
// rfc5173
BodyTest,
// rfc5183
EnvironmentTest,
// rfc5229
SetCommand,
StringTest,
// rfc5230
VacationCommand,
// rfc5232
SetFlagCommand,
AddFlagCommand,
RemoveFlagCommand,
HasFlagTest,
// rfc5235
SpamTestTest,
VirusTestTest,
// rfc5260
DateTest,
CurrentDateTest,
// rfc5293
AddHeaderCommand,
DeleteHeaderCommand,
// rfc5429
ErejectCommand,
RejectCommand,
// rfc5435
NotifyCommand,
ValidNotifyMethodTest,
NotifyMethodCapabilityTest,
// rfc5463
IHaveTest,
ErrorCommand,
// rfc5490
MailboxExistsTest,
MetadataTest,
MetadataExistsTest,
// rfc5703
ForEveryPartCommand,
BreakCommand,
ReplaceCommand,
EncloseCommand,
ExtractTextCommand,
// rfc6609
IncludeCommand,
ReturnCommand,
GlobalCommand
],
availableCommands = () => {
let commands = {}, id;
AllCommands.forEach(cmd => {
id = getIdentifier(cmd);
if (id) {
commands[id] = cmd;
}
});
return commands;
},
availableActions = () => {
let actions = {}, id;
AllCommands.forEach(cmd => {
id = getIdentifier(cmd, ActionCommand);
if (id) {
actions[id] = cmd;
}
});
return actions;
},
availableControls = () => {
let controls = {}, id;
AllCommands.forEach(cmd => {
id = getIdentifier(cmd, ControlCommand);
if (id) {
controls[id] = cmd;
}
});
return controls;
},
availableTests = () => {
let tests = {}, id;
AllCommands.forEach(cmd => {
id = getIdentifier(cmd, TestCommand);
if (id) {
tests[id] = cmd;
}
});
return tests;
};

View File

@@ -4,7 +4,7 @@
*/
import {
GrammarCommand,
ControlCommand,
GrammarStringList,
GrammarQuotedString
} from 'Sieve/Grammar';
@@ -16,7 +16,7 @@ import {
* elsif <test2: test> <block2: block>
* else <block3: block>
*/
export class ConditionalCommand extends GrammarCommand
export class ConditionalCommand extends ControlCommand
{
constructor()
{
@@ -61,7 +61,7 @@ export class ElseCommand extends ConditionalCommand
/**
* https://tools.ietf.org/html/rfc5228#section-3.2
*/
export class RequireCommand extends GrammarCommand
export class RequireCommand extends ControlCommand
{
constructor()
{
@@ -87,6 +87,6 @@ export class RequireCommand extends GrammarCommand
/**
* https://tools.ietf.org/html/rfc5228#section-3.3
*/
export class StopCommand extends GrammarCommand
export class StopCommand extends ControlCommand
{
}

View File

@@ -3,13 +3,13 @@
*/
import {
GrammarCommand,
ActionCommand,
GrammarQuotedString,
GrammarStringList,
TestCommand
} from 'Sieve/Grammar';
export class SetCommand extends GrammarCommand
export class SetCommand extends ActionCommand
{
constructor()
{

View File

@@ -6,13 +6,13 @@
import { capa } from 'Sieve/Utils';
import {
GrammarCommand,
ActionCommand,
GrammarNumber,
GrammarQuotedString,
GrammarStringList
} from 'Sieve/Grammar';
export class VacationCommand extends GrammarCommand
export class VacationCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,14 +3,14 @@
*/
import {
GrammarCommand,
ActionCommand,
GrammarQuotedString,
GrammarString,
GrammarStringList,
TestCommand
} from 'Sieve/Grammar';
class FlagCommand extends GrammarCommand
class FlagCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,14 +3,14 @@
*/
import {
GrammarCommand,
ActionCommand,
GrammarNumber,
GrammarQuotedString,
GrammarString,
GrammarStringList
} from 'Sieve/Grammar';
export class AddHeaderCommand extends GrammarCommand
export class AddHeaderCommand extends ActionCommand
{
constructor()
{
@@ -38,7 +38,7 @@ export class AddHeaderCommand extends GrammarCommand
}
}
export class DeleteHeaderCommand extends GrammarCommand
export class DeleteHeaderCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,7 +3,7 @@
*/
import {
GrammarCommand,
ActionCommand,
GrammarQuotedString,
GrammarString
} from 'Sieve/Grammar';
@@ -11,7 +11,7 @@ import {
/**
* https://tools.ietf.org/html/rfc5429#section-2.1
*/
export class ErejectCommand extends GrammarCommand
export class ErejectCommand extends ActionCommand
{
constructor()
{
@@ -47,7 +47,7 @@ export class ErejectCommand extends GrammarCommand
/**
* https://tools.ietf.org/html/rfc5429#section-2.2
*/
export class RejectCommand extends GrammarCommand
export class RejectCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,7 +3,7 @@
*/
import {
GrammarCommand,
ActionCommand,
GrammarNumber,
GrammarQuotedString,
GrammarStringList,
@@ -13,7 +13,7 @@ import {
/**
* https://datatracker.ietf.org/doc/html/rfc5435#section-3
*/
export class NotifyCommand extends GrammarCommand
export class NotifyCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,7 +3,7 @@
*/
import {
GrammarCommand,
ControlCommand,
TestCommand,
GrammarQuotedString,
GrammarStringList
@@ -36,7 +36,7 @@ export class IHaveTest extends TestCommand
/**
* https://datatracker.ietf.org/doc/html/rfc5463#section-5
*/
export class ErrorCommand extends GrammarCommand
export class ErrorCommand extends ControlCommand
{
constructor()
{

View File

@@ -3,7 +3,8 @@
*/
import {
GrammarCommand,
ActionCommand,
ControlCommand,
GrammarNumber,
GrammarQuotedString,
GrammarString,
@@ -13,7 +14,7 @@ import {
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-3
*/
export class ForEveryPartCommand extends GrammarCommand
export class ForEveryPartCommand extends ControlCommand
{
constructor()
{
@@ -57,7 +58,7 @@ export class BreakCommand extends ForEveryPartCommand
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-5
*/
export class ReplaceCommand extends GrammarCommand
export class ReplaceCommand extends ActionCommand
{
constructor()
{
@@ -103,7 +104,7 @@ export class ReplaceCommand extends GrammarCommand
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-6
*/
export class EncloseCommand extends GrammarCommand
export class EncloseCommand extends ActionCommand
{
constructor()
{
@@ -140,7 +141,7 @@ export class EncloseCommand extends GrammarCommand
/**
* https://datatracker.ietf.org/doc/html/rfc5703#section-7
*/
export class ExtractTextCommand extends GrammarCommand
export class ExtractTextCommand extends ActionCommand
{
constructor()
{

View File

@@ -3,11 +3,12 @@
*/
import {
GrammarCommand,
GrammarQuotedString
ControlCommand,
GrammarQuotedString,
GrammarStringList
} from 'Sieve/Grammar';
export class IncludeCommand extends GrammarCommand
export class IncludeCommand extends ControlCommand
{
constructor()
{
@@ -22,7 +23,7 @@ export class IncludeCommand extends GrammarCommand
toString()
{
return this.identifier
return 'include'
+ (this.global ? ' :global' : '')
+ (this.once ? ' :once' : '')
+ (this.optional ? ' :optional' : '')
@@ -41,7 +42,28 @@ export class IncludeCommand extends GrammarCommand
}
}
export class ReturnCommand extends GrammarCommand
export class ReturnCommand extends ControlCommand
{
get require() { return 'include'; }
}
export class GlobalCommand extends ControlCommand
{
constructor()
{
super();
this.value = new GrammarStringList;
}
get require() { return ['include', 'variables']; }
toString()
{
return 'global ' + this.value + ';';
}
pushArguments(args)
{
this.value = args.pop();
}
}

View File

@@ -2,7 +2,7 @@
* https://tools.ietf.org/html/rfc5228#section-8
*/
import { capa, getMatchTypes } from 'Sieve/Utils';
import { getMatchTypes } from 'Sieve/Utils';
import {
BRACKET_COMMENT,
@@ -27,123 +27,11 @@ import {
GrammarTestList
} from 'Sieve/Grammar';
import {
DiscardCommand,
FileIntoCommand,
KeepCommand,
RedirectCommand
} from 'Sieve/Commands/Actions';
import {
ConditionalCommand,
ElsIfCommand,
ElseCommand,
IfCommand,
RequireCommand,
StopCommand
} from 'Sieve/Commands/Controls';
import {
AddressTest,
AllOfTest,
AnyOfTest,
EnvelopeTest,
ExistsTest,
FalseTest,
HeaderTest,
NotTest,
SizeTest,
TrueTest
} from 'Sieve/Commands/Tests';
import { BodyTest } from 'Sieve/Extensions/rfc5173';
import { EnvironmentTest } from 'Sieve/Extensions/rfc5183';
import { SetCommand, StringTest } from 'Sieve/Extensions/rfc5229';
import { VacationCommand } from 'Sieve/Extensions/rfc5230';
import { SetFlagCommand, AddFlagCommand, RemoveFlagCommand, HasFlagTest } from 'Sieve/Extensions/rfc5232';
import { SpamTestTest, VirusTestTest } from 'Sieve/Extensions/rfc5235';
import { DateTest, CurrentDateTest } from 'Sieve/Extensions/rfc5260';
import { AddHeaderCommand, DeleteHeaderCommand } from 'Sieve/Extensions/rfc5293';
import { ErejectCommand, RejectCommand } from 'Sieve/Extensions/rfc5429';
import { NotifyCommand, ValidNotifyMethodTest, NotifyMethodCapabilityTest } from 'Sieve/Extensions/rfc5435';
import { IHaveTest, ErrorCommand } from 'Sieve/Extensions/rfc5463';
import { MailboxExistsTest, MetadataTest, MetadataExistsTest } from 'Sieve/Extensions/rfc5490';
import { ForEveryPartCommand, BreakCommand, ReplaceCommand, EncloseCommand, ExtractTextCommand } from 'Sieve/Extensions/rfc5703';
import { IncludeCommand, ReturnCommand } from 'Sieve/Extensions/rfc6609';
import { availableCommands } from 'Sieve/Commands';
import { ConditionalCommand, RequireCommand } from 'Sieve/Commands/Controls';
import { NotTest } from 'Sieve/Commands/Tests';
const
AllCommands = [
// Control commands
IfCommand,
ElsIfCommand,
ElseCommand,
ConditionalCommand,
RequireCommand,
StopCommand,
// Action commands
DiscardCommand,
FileIntoCommand,
KeepCommand,
RedirectCommand,
// Test commands
AddressTest,
AllOfTest,
AnyOfTest,
EnvelopeTest,
ExistsTest,
FalseTest,
HeaderTest,
NotTest,
SizeTest,
TrueTest,
// rfc5173
BodyTest,
// rfc5183
EnvironmentTest,
// rfc5229
SetCommand,
StringTest,
// rfc5230
VacationCommand,
// rfc5232
SetFlagCommand,
AddFlagCommand,
RemoveFlagCommand,
HasFlagTest,
// rfc5235
SpamTestTest,
VirusTestTest,
// rfc5260
DateTest,
CurrentDateTest,
// rfc5293
AddHeaderCommand,
DeleteHeaderCommand,
// rfc5429
ErejectCommand,
RejectCommand,
// rfc5435
NotifyCommand,
ValidNotifyMethodTest,
NotifyMethodCapabilityTest,
// rfc5463
IHaveTest,
ErrorCommand,
// rfc5490
MailboxExistsTest,
MetadataTest,
MetadataExistsTest,
// rfc5703
ForEveryPartCommand,
BreakCommand,
ReplaceCommand,
EncloseCommand,
ExtractTextCommand,
// rfc6609
IncludeCommand,
ReturnCommand
],
T_UNKNOWN = 0,
T_STRING_LIST = 1,
T_QUOTED_STRING = 2,
@@ -184,15 +72,7 @@ export const parseScript = (script, name = 'script.sieve') => {
script = script.replace(/\r?\n/g, '\r\n');
// Only activate available commands
const Commands = {};
AllCommands.forEach(cmd => {
const obj = new cmd, requires = obj.require;
if (!requires
|| (Array.isArray(requires) ? requires : [requires]).every(string => capa.includes(string))
) {
Commands[obj.identifier] = cmd;
}
});
const Commands = availableCommands();
let match,
line = 1,

View File

@@ -5,6 +5,8 @@ import { FilterPopupView } from 'Sieve/View/Filter';
import { parseScript } from 'Sieve/Parser';
import { availableActions, availableControls, availableTests } from 'Sieve/Commands';
import {
capa,
scripts,
@@ -21,10 +23,14 @@ export class SieveScriptPopupView extends rl.pluginPopupView {
errorText: '',
rawActive: false,
allowToggle: false,
script: null
script: null,
sieveCapabilities: '',
availableActions: '',
availableControls: '',
availableTests: ''
});
this.sieveCapabilities = capa.join(' ');
this.saving = false;
this.filterForDeletion = ko.observable(null).askDeleteHelper();
@@ -137,6 +143,11 @@ export class SieveScriptPopupView extends rl.pluginPopupView {
beforeShow(oScript) {
// onShow(oScript) {
this.sieveCapabilities(capa.join(' '));
this.availableActions([...Object.keys(availableActions())].join(', '));
this.availableControls([...Object.keys(availableControls())].join(', '));
this.availableTests([...Object.keys(availableTests())].join(', '));
oScript = oScript || new SieveScriptModel();
let raw = !oScript.allowFilters();
this.script(oScript);

View File

@@ -20,13 +20,23 @@
<div class="alert alert-error g-ui-user-select-none" style="white-space:pre" data-bind="visible: $root.errorText, text: $root.errorText" data-icon="⚠"></div>
<div class="control-group" data-bind="visible: $root.rawActive">
<div>
<pre>
<b data-i18n="POPUPS_SIEVE_SCRIPT/CAPABILITY_LABEL"></b>:
<span data-bind="text: $root.sieveCapabilities"></span>
</pre>
<textarea style="width:100%;height:60vh" data-bind="value: body, valueUpdate: 'input'"></textarea>
</div>
<details>
<summary data-i18n="POPUPS_SIEVE_SCRIPT/CAPABILITY_LABEL"></summary>
<pre data-bind="text: $root.sieveCapabilities"></pre>
</details>
<details>
<summary>Actions</summary>
<pre data-bind="text: $root.availableActions"></pre>
</details>
<details>
<summary>Controls</summary>
<pre data-bind="text: $root.availableControls"></pre>
</details>
<details>
<summary>Tests</summary>
<pre data-bind="text: $root.availableTests"></pre>
</details>
<textarea style="width:100%;height:60vh" data-bind="value: body, valueUpdate: 'input'"></textarea>
</div>
<div data-bind="visible: !$root.rawActive()">
<table class="table table-hover list-table filters-list g-ui-user-select-none"