diff --git a/CHANGELOG b/CHANGELOG index beb87c82d..7a51eefce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Managesieve: Added support for 'editheader' extension - RFC5293 (#5954) - Password: Added 'modoboa' driver (#6361) - Plugin API: Added 'raise_error' hook (#6199) - Fix so invalid smtp_helo_host is never used, fallback to localhost (#6408) diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog index 266771c69..ee94ca07c 100644 --- a/plugins/managesieve/Changelog +++ b/plugins/managesieve/Changelog @@ -1,3 +1,4 @@ +- Added support for 'editheader' extension - RFC5293 (#5954) - Fix bug where show_real_foldernames setting wasn't respected (#6422) * version 9.1 [2018-05-19] diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve.php b/plugins/managesieve/lib/Roundcube/rcube_sieve.php index 6c751696a..fb610e8da 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve.php @@ -313,14 +313,10 @@ class rcube_sieve if ($this->script) { $supported = $this->script->get_extensions(); - foreach ($ext as $idx => $ext_name) { - if (!in_array($ext_name, $supported)) { - unset($ext[$idx]); - } - } + $ext = array_values(array_intersect($ext, $supported)); } - return array_values($ext); + return $ext; } /** diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index 8ef58f65d..929701f89 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -688,6 +688,15 @@ class rcube_sieve_engine $notifymessages = rcube_utils::get_input_value('_action_notifymessage', rcube_utils::INPUT_POST, true); $notifyfrom = rcube_utils::get_input_value('_action_notifyfrom', rcube_utils::INPUT_POST); $notifyimp = rcube_utils::get_input_value('_action_notifyimportance', rcube_utils::INPUT_POST); + $addheader_name = rcube_utils::get_input_value('_action_addheader_name', rcube_utils::INPUT_POST); + $addheader_value = rcube_utils::get_input_value('_action_addheader_value', rcube_utils::INPUT_POST, true); + $addheader_pos = rcube_utils::get_input_value('_action_addheader_pos', rcube_utils::INPUT_POST); + $delheader_name = rcube_utils::get_input_value('_action_delheader_name', rcube_utils::INPUT_POST); + $delheader_value = rcube_utils::get_input_value('_action_delheader_value', rcube_utils::INPUT_POST, true); + $delheader_pos = rcube_utils::get_input_value('_action_delheader_pos', rcube_utils::INPUT_POST); + $delheader_index = rcube_utils::get_input_value('_action_delheader_index', rcube_utils::INPUT_POST); + $delheader_op = rcube_utils::get_input_value('_action_delheader_op', rcube_utils::INPUT_POST); + $delheader_comp = rcube_utils::get_input_value('_action_delheader_comp', rcube_utils::INPUT_POST); // we need a "hack" for radiobuttons foreach ($sizeitems as $item) @@ -1091,6 +1100,42 @@ class rcube_sieve_engine $this->form['actions'][$i]['target'] = $_target; break; + case 'addheader': + case 'deleteheader': + $this->form['actions'][$i]['name'] = trim($type == 'addheader' ? $addheader_name[$idx] : $delheader_name[$idx]); + $this->form['actions'][$i]['value'] = $type == 'addheader' ? $addheader_value[$idx] : $delheader_value[$idx]; + $this->form['actions'][$i]['last'] = ($type == 'addheader' ? $addheader_pos[$idx] : $delheader_pos[$idx]) == 'last'; + + if (empty($this->form['actions'][$i]['name'])) { + $this->errors['actions'][$i]['name'] = $this->plugin->gettext('cannotbeempty'); + } + else if (!preg_match('/^[0-9a-z_-]+$/i', $this->form['actions'][$i]['name'])) { + $this->errors['actions'][$i]['name'] = $this->plugin->gettext('forbiddenchars'); + } + + if ($type == 'deleteheader') { + foreach ((array) $this->form['actions'][$i]['value'] as $pidx => $pattern) { + if (empty($pattern)) { + unset($this->form['actions'][$i]['value'][$pidx]); + } + } + + $this->form['actions'][$i]['match-type'] = $delheader_op[$idx]; + $this->form['actions'][$i]['comparator'] = $delheader_comp[$idx]; + $this->form['actions'][$i]['index'] = $delheader_index[$idx]; + + if (!preg_match('/^[0-9]+$/i', $this->form['actions'][$i]['index'])) { + $this->errors['actions'][$i]['index'] = $this->plugin->gettext('forbiddenchars'); + } + } + else { + if (empty($this->form['actions'][$i]['value'])) { + $this->errors['actions'][$i]['value'] = $this->plugin->gettext('cannotbeempty'); + } + } + + break; + case 'vacation': $reason = $this->strip_value($reasons[$idx]); $interval_type = $interval_types[$idx] == 'seconds' ? 'seconds' : 'days'; @@ -1714,38 +1759,6 @@ class rcube_sieve_engine $aout .= $this->list_input($id, 'custom_var', $customv, isset($customv), $this->error_class($id, 'test', 'header', 'custom_var'), 15) . "\n"; - // matching type select (operator) - $select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id, - 'style' => 'display:' .(!in_array($rule['test'], array('size', 'duplicate')) ? 'inline' : 'none'), - 'class' => 'operator_selector', - 'onchange' => 'rule_op_select(this, '.$id.')')); - $select_op->add(rcube::Q($this->plugin->gettext('filtercontains')), 'contains'); - $select_op->add(rcube::Q($this->plugin->gettext('filternotcontains')), 'notcontains'); - $select_op->add(rcube::Q($this->plugin->gettext('filteris')), 'is'); - $select_op->add(rcube::Q($this->plugin->gettext('filterisnot')), 'notis'); - $select_op->add(rcube::Q($this->plugin->gettext('filterexists')), 'exists'); - $select_op->add(rcube::Q($this->plugin->gettext('filternotexists')), 'notexists'); - $select_op->add(rcube::Q($this->plugin->gettext('filtermatches')), 'matches'); - $select_op->add(rcube::Q($this->plugin->gettext('filternotmatches')), 'notmatches'); - if (in_array('regex', $this->exts)) { - $select_op->add(rcube::Q($this->plugin->gettext('filterregex')), 'regex'); - $select_op->add(rcube::Q($this->plugin->gettext('filternotregex')), 'notregex'); - } - if (in_array('relational', $this->exts)) { - $select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthan')), 'count-gt'); - $select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthanequal')), 'count-ge'); - $select_op->add(rcube::Q($this->plugin->gettext('countislessthan')), 'count-lt'); - $select_op->add(rcube::Q($this->plugin->gettext('countislessthanequal')), 'count-le'); - $select_op->add(rcube::Q($this->plugin->gettext('countequals')), 'count-eq'); - $select_op->add(rcube::Q($this->plugin->gettext('countnotequals')), 'count-ne'); - $select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthan')), 'value-gt'); - $select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthanequal')), 'value-ge'); - $select_op->add(rcube::Q($this->plugin->gettext('valueislessthan')), 'value-lt'); - $select_op->add(rcube::Q($this->plugin->gettext('valueislessthanequal')), 'value-le'); - $select_op->add(rcube::Q($this->plugin->gettext('valueequals')), 'value-eq'); - $select_op->add(rcube::Q($this->plugin->gettext('valuenotequals')), 'value-ne'); - } - $test = self::rule_test($rule); $target = ''; @@ -1796,7 +1809,7 @@ class rcube_sieve_engine $tout .= $select_msg->show($test); } - $tout .= $select_op->show($test); + $tout .= $this->match_type_selector('rule_op', $id, $test, $rule['test']); $tout .= $this->list_input($id, 'rule_target', $target, $rule['test'] != 'size' && $rule['test'] != 'exists' && $rule['test'] != 'duplicate', $this->error_class($id, 'test', 'target', 'rule_target')) . "\n"; @@ -1854,18 +1867,10 @@ class rcube_sieve_engine $mout .= ''; // Advanced modifiers (comparators) - $select_comp = new html_select(array('name' => "_rule_comp[]", 'id' => 'rule_comp_op'.$id)); - $select_comp->add(rcube::Q($this->plugin->gettext('default')), ''); - $select_comp->add(rcube::Q($this->plugin->gettext('octet')), 'i;octet'); - $select_comp->add(rcube::Q($this->plugin->gettext('asciicasemap')), 'i;ascii-casemap'); - if (in_array('comparator-i;ascii-numeric', $this->exts)) { - $select_comp->add(rcube::Q($this->plugin->gettext('asciinumeric')), 'i;ascii-numeric'); - } - $need_comp = $rule['test'] != 'size' && $rule['test'] != 'duplicate'; $mout .= ''; // Advanced modifiers (mime) @@ -2096,32 +2101,36 @@ class rcube_sieve_engine $select_action = new html_select(array('name' => "_action_type[$id]", 'id' => 'action_type'.$id, 'onchange' => 'action_type_select(' .$id .')')); if (in_array('fileinto', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagemoveto')), 'fileinto'); + $select_action->add($this->plugin->gettext('messagemoveto'), 'fileinto'); if (in_array('fileinto', $this->exts) && in_array('copy', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagecopyto')), 'fileinto_copy'); - $select_action->add(rcube::Q($this->plugin->gettext('messageredirect')), 'redirect'); + $select_action->add($this->plugin->gettext('messagecopyto'), 'fileinto_copy'); + $select_action->add($this->plugin->gettext('messageredirect'), 'redirect'); if (in_array('copy', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagesendcopy')), 'redirect_copy'); + $select_action->add($this->plugin->gettext('messagesendcopy'), 'redirect_copy'); if (in_array('reject', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagediscard')), 'reject'); + $select_action->add($this->plugin->gettext('messagediscard'), 'reject'); else if (in_array('ereject', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagediscard')), 'ereject'); + $select_action->add($this->plugin->gettext('messagediscard'), 'ereject'); if (in_array('vacation', $this->exts)) - $select_action->add(rcube::Q($this->plugin->gettext('messagereply')), 'vacation'); - $select_action->add(rcube::Q($this->plugin->gettext('messagedelete')), 'discard'); + $select_action->add($this->plugin->gettext('messagereply'), 'vacation'); + $select_action->add($this->plugin->gettext('messagedelete'), 'discard'); if (in_array('imapflags', $this->exts) || in_array('imap4flags', $this->exts)) { - $select_action->add(rcube::Q($this->plugin->gettext('setflags')), 'setflag'); - $select_action->add(rcube::Q($this->plugin->gettext('addflags')), 'addflag'); - $select_action->add(rcube::Q($this->plugin->gettext('removeflags')), 'removeflag'); + $select_action->add($this->plugin->gettext('setflags'), 'setflag'); + $select_action->add($this->plugin->gettext('addflags'), 'addflag'); + $select_action->add($this->plugin->gettext('removeflags'), 'removeflag'); + } + if (in_array('editheader', $this->exts)) { + $select_action->add($this->plugin->gettext('addheader'), 'addheader'); + $select_action->add($this->plugin->gettext('deleteheader'), 'deleteheader'); } if (in_array('variables', $this->exts)) { - $select_action->add(rcube::Q($this->plugin->gettext('setvariable')), 'set'); + $select_action->add($this->plugin->gettext('setvariable'), 'set'); } if (in_array('enotify', $this->exts) || in_array('notify', $this->exts)) { - $select_action->add(rcube::Q($this->plugin->gettext('notify')), 'notify'); + $select_action->add($this->plugin->gettext('notify'), 'notify'); } - $select_action->add(rcube::Q($this->plugin->gettext('messagekeep')), 'keep'); - $select_action->add(rcube::Q($this->plugin->gettext('rulestop')), 'stop'); + $select_action->add($this->plugin->gettext('messagekeep'), 'keep'); + $select_action->add($this->plugin->gettext('rulestop'), 'stop'); $select_type = $action['type']; if (in_array($action['type'], array('fileinto', 'redirect')) && $action['copy']) { @@ -2190,7 +2199,7 @@ class rcube_sieve_engine } } - $out .= '
'; + $out .= '
'; $out .= ''. rcube::Q($this->plugin->gettext('vacationreason')) .'
'; $out .= html::tag('textarea', array( 'name' => '_action_reason[' . $id . ']', @@ -2353,7 +2362,7 @@ class rcube_sieve_engine } // @TODO: nice UI for mailto: (other methods too) URI parameters - $out .= '
'; + $out .= '
'; $out .= '' .rcube::Q($this->plugin->gettext('notifytarget')) . '
'; $out .= '
'; $out .= $select_method->show($method); @@ -2393,6 +2402,85 @@ class rcube_sieve_engine $this->error_class($id, 'action', 'options', 'action_notifyoption'), 30) . '
'; $out .= '
'; + if (in_array('editheader', $this->exts)) { + $action['pos'] = $action['last'] ? 'last' : ''; + $pos1_selector = new html_select(array( + 'name' => "_action_addheader_pos[$id]", + 'id' => "action_addheader_pos$id", + 'class' => $this->error_class($id, 'action', 'pos', 'action_addheader_pos') + )); + $pos1_selector->add($this->plugin->gettext('headeratstart'), ''); + $pos1_selector->add($this->plugin->gettext('headeratend'), 'last'); + $pos2_selector = new html_select(array( + 'name' => "_action_delheader_pos[$id]", + 'id' => "action_delheader_pos$id", + 'class' => $this->error_class($id, 'action', 'pos', 'action_delheader_pos') + )); + $pos2_selector->add($this->plugin->gettext('headerfromstart'), ''); + $pos2_selector->add($this->plugin->gettext('headerfromend'), 'last'); + + // addheader + $out .= '
'; + $out .= '
'; + $out .= html::tag('input', array( + 'type' => 'text', + 'name' => '_action_addheader_name[' . $id . ']', + 'id' => 'action_addheader_name' . $id, + 'value' => $action['name'], + 'size' => 35, + 'class' => $this->error_class($id, 'action', 'name', 'action_addheader_name'), + )); + $out .= '

'; + $out .= html::tag('input', array( + 'type' => 'text', + 'name' => '_action_addheader_value[' . $id . ']', + 'id' => 'action_addheader_value' . $id, + 'value' => $action['value'], + 'size' => 35, + 'class' => $this->error_class($id, 'action', 'value', 'action_addheader_value'), + )); + $out .= '

'; + $out .= $pos1_selector->show($action['pos']); + $out .= '
'; + + // deleteheader + $out .= '
'; + $out .= '
'; + $out .= html::tag('input', array( + 'type' => 'text', + 'name' => '_action_delheader_name[' . $id . ']', + 'id' => 'action_delheader_name' . $id, + 'value' => $action['name'], + 'size' => 35, + 'class' => $this->error_class($id, 'action', 'name', 'action_delheader_name'), + )); + $out .= '

'; + $out .= $this->list_input($id, 'action_delheader_value', $action['value'], true, + $this->error_class($id, 'action', 'value', 'action_delheader_value')) . "\n"; + $out .= '
'; + $out .= html::span('label input-group-prepend', html::label(array( + 'class' => 'input-group-text', 'for' => 'action_delheader_op'.$id), rcube::Q($this->plugin->gettext('headermatchtype')))); + $out .= $this->match_type_selector('action_delheader_op', $id, $action['match-type'], null, 'basic'); + $out .= '
'; + $out .= '
'; + $out .= html::span('label input-group-prepend', html::label(array( + 'class' => 'input-group-text', 'for' => 'action_delheader_comp_op'.$id), rcube::Q($this->plugin->gettext('comparator')))); + $out .= $this->comparator_selector($action['comparator'], 'action_delheader_comp', $id); + $out .= '
'; + $out .= '

'; + $out .= '
'; + $out .= html::tag('input', array( + 'type' => 'text', + 'name' => '_action_delheader_index[' . $id . ']', + 'id' => 'action_delheader_index' . $id, + 'value' => $action['index'] ? intval($action['index']) : '', + 'size' => 5, + 'class' => $this->error_class($id, 'action', 'index', 'action_delheader_index'), + )); + $out .= $pos2_selector->show($action['pos']); + $out .= '
'; + } + // mailbox select if ($action['type'] == 'fileinto') { // make sure non-existing (or unsubscribed) mailbox is listed (#1489956) @@ -2934,4 +3022,63 @@ class rcube_sieve_engine return $headers; } + + /** + * Match type selector + */ + protected function match_type_selector($name, $id, $test, $rule = null, $mode = 'all') + { + // matching type select (operator) + $select_op = new html_select(array( + 'name' => "_{$name}[]", + 'id' => "{$name}{$id}", + 'style' => 'display:' .(!in_array($rule, array('size', 'duplicate')) ? 'inline' : 'none'), + 'class' => 'operator_selector', + 'onchange' => "{$name}_select(this, '{$id}')", + )); + + $select_op->add(rcube::Q($this->plugin->gettext('filtercontains')), 'contains'); + $select_op->add(rcube::Q($this->plugin->gettext('filternotcontains')), 'notcontains'); + $select_op->add(rcube::Q($this->plugin->gettext('filteris')), 'is'); + $select_op->add(rcube::Q($this->plugin->gettext('filterisnot')), 'notis'); + if ($mode == 'all') { + $select_op->add(rcube::Q($this->plugin->gettext('filterexists')), 'exists'); + $select_op->add(rcube::Q($this->plugin->gettext('filternotexists')), 'notexists'); + } + $select_op->add(rcube::Q($this->plugin->gettext('filtermatches')), 'matches'); + $select_op->add(rcube::Q($this->plugin->gettext('filternotmatches')), 'notmatches'); + if (in_array('regex', $this->exts)) { + $select_op->add(rcube::Q($this->plugin->gettext('filterregex')), 'regex'); + $select_op->add(rcube::Q($this->plugin->gettext('filternotregex')), 'notregex'); + } + if ($mode == 'all' && in_array('relational', $this->exts)) { + $select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthan')), 'count-gt'); + $select_op->add(rcube::Q($this->plugin->gettext('countisgreaterthanequal')), 'count-ge'); + $select_op->add(rcube::Q($this->plugin->gettext('countislessthan')), 'count-lt'); + $select_op->add(rcube::Q($this->plugin->gettext('countislessthanequal')), 'count-le'); + $select_op->add(rcube::Q($this->plugin->gettext('countequals')), 'count-eq'); + $select_op->add(rcube::Q($this->plugin->gettext('countnotequals')), 'count-ne'); + $select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthan')), 'value-gt'); + $select_op->add(rcube::Q($this->plugin->gettext('valueisgreaterthanequal')), 'value-ge'); + $select_op->add(rcube::Q($this->plugin->gettext('valueislessthan')), 'value-lt'); + $select_op->add(rcube::Q($this->plugin->gettext('valueislessthanequal')), 'value-le'); + $select_op->add(rcube::Q($this->plugin->gettext('valueequals')), 'value-eq'); + $select_op->add(rcube::Q($this->plugin->gettext('valuenotequals')), 'value-ne'); + } + + return $select_op->show($test); + } + + protected function comparator_selector($comparator, $name, $id) + { + $select_comp = new html_select(array('name' => "_{$name}[]", 'id' => "{$name}_op{$id}")); + $select_comp->add(rcube::Q($this->plugin->gettext('default')), ''); + $select_comp->add(rcube::Q($this->plugin->gettext('octet')), 'i;octet'); + $select_comp->add(rcube::Q($this->plugin->gettext('asciicasemap')), 'i;ascii-casemap'); + if (in_array('comparator-i;ascii-numeric', $this->exts)) { + $select_comp->add(rcube::Q($this->plugin->gettext('asciinumeric')), 'i;ascii-numeric'); + } + + return $select_comp->show($comparator); + } } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php index 369f1da9d..fdfbf6e25 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php @@ -31,6 +31,7 @@ class rcube_sieve_script 'copy', // RFC3894 'date', // RFC5260 'duplicate', // RFC7352 + 'editheader', // RFC5293 'enotify', // RFC5435 'envelope', // RFC5228 'ereject', // RFC5429 @@ -422,6 +423,26 @@ class rcube_sieve_script . self::escape_string($action['target']); break; + case 'addheader': + case 'deleteheader': + array_push($exts, 'editheader'); + $action_script .= $action['type']; + if (!empty($action['index'])) { + $action_script .= " :index " . intval($action['index']); + } + if (!empty($action['last']) && (!empty($action['index']) || $action['type'] == 'addheader')) { + $action_script .= " :last"; + } + if ($action['type'] == 'deleteheader') { + $action['type'] = $action['match-type']; + $this->add_operator($action, $action_script, $exts); + } + $action_script .= " " . self::escape_string($action['name']); + if ((is_string($action['value']) && strlen($action['value']) > 0) || (is_array($action['value']) && !empty($action['value']))) { + $action_script .= " " . self::escape_string($action['value']); + } + break; + case 'keep': case 'discard': case 'stop': @@ -908,6 +929,21 @@ class rcube_sieve_script $result[] = $action; break; + case 'addheader': + case 'deleteheader': + $args = $this->test_tokens($tokens); + if ($token == 'deleteheader') { + $args['match-type'] = $args['type']; + } + if (($index = array_search(':last', $tokens)) !== false) { + $args['last'] = true; + unset($tokens[$index]); + } + $action = array('type' => $token, 'name' => array_shift($tokens), 'value' => array_shift($tokens)); + + $result[] = $action + $args; + break; + case 'reject': case 'ereject': case 'setflag': diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc index 3fa9d89c8..80d4b2166 100644 --- a/plugins/managesieve/localization/en_US.inc +++ b/plugins/managesieve/localization/en_US.inc @@ -113,6 +113,18 @@ $labels['flagdeleted'] = 'Deleted'; $labels['flaganswered'] = 'Answered'; $labels['flagflagged'] = 'Flagged'; $labels['flagdraft'] = 'Draft'; +$labels['addheader'] = 'Add header to the message'; +$labels['deleteheader'] = 'Remove header from the message'; +$labels['headername'] = 'Header name'; +$labels['headervalue'] = 'Header value'; +$labels['headerpos'] = 'Header position'; +$labels['headeratstart'] = 'at the beginning'; +$labels['headeratend'] = 'at the end'; +$labels['headeroccurrence'] = 'Header occurrence'; +$labels['headerfromstart'] = 'from start'; +$labels['headerfromend'] = 'from end'; +$labels['headerpatterns'] = 'Header value patterns'; +$labels['headermatchtype'] = 'match type:'; $labels['setvariable'] = 'Set variable'; $labels['setvarname'] = 'Variable name:'; $labels['setvarvalue'] = 'Variable value:'; diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js index 6aad573e9..6775bb6c8 100644 --- a/plugins/managesieve/managesieve.js +++ b/plugins/managesieve/managesieve.js @@ -766,7 +766,9 @@ function action_type_select(id) vacation: document.getElementById('action_vacation' + id), forward: document.getElementById('action_forward' + id), set: document.getElementById('action_set' + id), - notify: document.getElementById('action_notify' + id) + notify: document.getElementById('action_notify' + id), + addheader: document.getElementById('action_addheader' + id), + deleteheader: document.getElementById('action_deleteheader' + id) }; if (v == 'fileinto' || v == 'fileinto_copy') { @@ -781,17 +783,8 @@ function action_type_select(id) else if (v.match(/^(add|set|remove)flag$/)) { enabled.flags = 1; } - else if (v == 'vacation') { - enabled.vacation = 1; - } - else if (v == 'forward') { - enabled.forward = 1; - } - else if (v == 'set') { - enabled.set = 1; - } - else if (v == 'notify') { - enabled.notify = 1; + else if (v.match(/^(vacation|forward|set|notify|addheader|deleteheader)$/)) { + enabled[v] = 1; } for (var x in elems) { diff --git a/plugins/managesieve/tests/src/parser_editheader b/plugins/managesieve/tests/src/parser_editheader new file mode 100644 index 000000000..ed713a53e --- /dev/null +++ b/plugins/managesieve/tests/src/parser_editheader @@ -0,0 +1,36 @@ +require ["editheader"]; +# rule:[test-addheader1] +if true +{ + addheader "X-Sieve-Filtered" ""; +} +# rule:[test-addheader2] +if not header :contains "X-Sieve-Filtered" "" +{ + addheader :last "X-Sieve-Filtered" ""; +} +# rule:[test-deleteheader1] +if true +{ + deleteheader :index 1 :contains "Delivered-To" ["bob@example.com","test@test.com"]; +} +# rule:[test-deleteheader2] +if true +{ + deleteheader :index 2 :last :contains :comparator "i;octet" "Delivered-To" "test@test.com"; +} +# rule:[test-deleteheader3] +if true +{ + deleteheader "Delivered-To"; +} +# rule:[test-deleteheader4] +if true +{ + deleteheader :index 3 :last :contains "Delivered-To"; +} +# rule:[test-deleteheader5] +if true +{ + deleteheader "Delivered-To" "test"; +} diff --git a/skins/elastic/styles/widgets/forms.less b/skins/elastic/styles/widgets/forms.less index 70eb0cc6c..6b1d37340 100644 --- a/skins/elastic/styles/widgets/forms.less +++ b/skins/elastic/styles/widgets/forms.less @@ -98,6 +98,22 @@ input.smart-upload { } td.rowtargets { + .composite { + input, textarea, select, .multi-input, .input-group { + margin-bottom: .5rem; + } + + .input-group { + input, textarea, select, .multi-input { + margin-bottom: 0; + } + } + + br { + display: block; + } + } + .input-group { margin-bottom: .25rem;