CHANGELOG | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/Changelog | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/composer.json | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/Roundcube/rcube_sieve_script.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/localization/en_US.inc | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/managesieve.js | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/classic/managesieve.css | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/classic/managesieve_mail.css | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/larry/managesieve.css | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/tests/src/parser_duplicate | ●●●●● patch | view | raw | blame | history |
CHANGELOG
@@ -3,6 +3,9 @@ - Implemented message/rfc822 attachment preview - Update to jsTimezoneDetect 1.0.6 - Managesieve: Support 'duplicate' extension [RFC 7352] - Managesieve: Unhide advanced rule controls if there are inputs with errors - Managesieve: Display warning message when filter form contains errors - Enigma: Added enigma_debug option - Fix message list multi-select/deselect issue (#5219) - Fix bug where getting HTML editor content could steal focus from other form controls (#5223) plugins/managesieve/Changelog
@@ -1,3 +1,9 @@ * version 8.7 [2016-] ----------------------------------------------------------- - Support 'duplicate' extension [RFC 7352] - Unhide advanced rule controls if there are inputs with errors - Display warning message when filter form contains errors * version 8.6 [2016-04-06] ----------------------------------------------------------- - Refactored script parser to be 100x faster plugins/managesieve/composer.json
@@ -3,7 +3,7 @@ "type": "roundcube-plugin", "description": "Adds a possibility to manage Sieve scripts (incoming mail filters). It's clickable interface which operates on text scripts and communicates with server using managesieve protocol. Adds Filters tab in Settings.", "license": "GPLv3+", "version": "8.6", "version": "8.7", "authors": [ { "name": "Aleksander Machniak", plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -63,7 +63,7 @@ 1 => 'notifyimportancehigh' ); const VERSION = '8.6'; const VERSION = '8.7'; const PROGNAME = 'Roundcube (Managesieve)'; const PORT = 4190; @@ -609,6 +609,12 @@ $lastindexes = rcube_utils::get_input_value('_rule_index_last', rcube_utils::INPUT_POST); $dateheaders = rcube_utils::get_input_value('_rule_date_header', rcube_utils::INPUT_POST); $dateparts = rcube_utils::get_input_value('_rule_date_part', rcube_utils::INPUT_POST); $message = rcube_utils::get_input_value('_rule_message', rcube_utils::INPUT_POST); $dup_handles = rcube_utils::get_input_value('_rule_duplicate_handle', rcube_utils::INPUT_POST, true); $dup_headers = rcube_utils::get_input_value('_rule_duplicate_header', rcube_utils::INPUT_POST, true); $dup_uniqueids = rcube_utils::get_input_value('_rule_duplicate_uniqueid', rcube_utils::INPUT_POST, true); $dup_seconds = rcube_utils::get_input_value('_rule_duplicate_seconds', rcube_utils::INPUT_POST); $dup_lasts = rcube_utils::get_input_value('_rule_duplicate_last', rcube_utils::INPUT_POST); $act_types = rcube_utils::get_input_value('_action_type', rcube_utils::INPUT_POST, true); $mailboxes = rcube_utils::get_input_value('_action_mailbox', rcube_utils::INPUT_POST, true); $act_targets = rcube_utils::get_input_value('_action_target', rcube_utils::INPUT_POST, true); @@ -816,6 +822,34 @@ $this->form['tests'][$i]['part'] = $trans; if ($trans == 'content') { $this->form['tests'][$i]['content'] = $trans_type; } } else if ($header == 'message') { $test = $this->strip_value($message[$idx]); if (preg_match('/^not/', $test)) { $this->form['tests'][$i]['not'] = true; $test = substr($test, 3); } $this->form['tests'][$i]['test'] = $test; if ($test == 'duplicate') { $this->form['tests'][$i]['last'] = !empty($dup_lasts[$idx]); $this->form['tests'][$i]['handle'] = trim($dup_handles[$idx]); $this->form['tests'][$i]['header'] = trim($dup_headers[$idx]); $this->form['tests'][$i]['uniqueid'] = trim($dup_uniqueids[$idx]); $this->form['tests'][$i]['seconds'] = trim($dup_seconds[$idx]); if ($this->form['tests'][$i]['seconds'] && preg_match('/[^0-9]/', $this->form['tests'][$i]['seconds']) ) { $this->errors['tests'][$i]['duplicate_seconds'] = $this->plugin->gettext('forbiddenchars'); } if ($this->form['tests'][$i]['header'] && $this->form['tests'][$i]['uniqueid']) { $this->errors['tests'][$i]['duplicate_uniqueid'] = $this->plugin->gettext('duplicate.conflict.err'); } } } else { @@ -1094,6 +1128,9 @@ $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); // $this->rc->output->send(); } } else { $this->rc->output->show_message('managesieve.filterformerror', 'warning'); } } @@ -1406,12 +1443,16 @@ $select_header->add($header, $index); } $select_header->add($this->plugin->gettext('...'), '...'); if (in_array('body', $this->exts)) if (in_array('body', $this->exts)) { $select_header->add($this->plugin->gettext('body'), 'body'); } $select_header->add($this->plugin->gettext('size'), 'size'); if (in_array('date', $this->exts)) { $select_header->add($this->plugin->gettext('datetest'), 'date'); $select_header->add($this->plugin->gettext('currdate'), 'currentdate'); } if (in_array('duplicate', $this->exts)) { $select_header->add($this->plugin->gettext('message'), 'message'); } if (isset($rule['test'])) { @@ -1433,6 +1474,9 @@ } else if (in_array($rule['test'], array('size', 'body', 'date', 'currentdate'))) { $test = $rule['test']; } else if (in_array($rule['test'], array('duplicate'))) { $test = 'message'; } else if ($rule['test'] != 'true') { $test = '...'; @@ -1460,7 +1504,7 @@ // matching type select (operator) $select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id, 'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'), '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'); @@ -1527,9 +1571,22 @@ $tout .= $select_dp->show($rule['test'] == 'currentdate' || $rule['test'] == 'date' ? $rule['part'] : ''); } // message test select (e.g. duplicate) if (in_array('duplicate', $this->exts)) { $select_msg = new html_select(array('name' => "_rule_message[]", 'id' => 'rule_message'.$id, 'style' => in_array($rule['test'], array('duplicate')) ? '' : 'display:none', 'class' => 'message_selector', )); $select_msg->add(rcube::Q($this->plugin->gettext('duplicate')), 'duplicate'); $select_msg->add(rcube::Q($this->plugin->gettext('notduplicate')), 'notduplicate'); $tout .= $select_msg->show($test); } $tout .= $select_op->show($test); $tout .= $this->list_input($id, 'rule_target', $target, $rule['test'] != 'size' && $rule['test'] != 'exists', $rule['test'] != 'size' && $rule['test'] != 'exists' && $rule['test'] != 'duplicate', $this->error_class($id, 'test', 'target', 'rule_target')) . "\n"; $select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id)); @@ -1555,8 +1612,9 @@ 'onchange' => 'rule_mod_select(' .$id .')')); $select_mod->add(rcube::Q($this->plugin->gettext('none')), ''); $select_mod->add(rcube::Q($this->plugin->gettext('address')), 'address'); if (in_array('envelope', $this->exts)) if (in_array('envelope', $this->exts)) { $select_mod->add(rcube::Q($this->plugin->gettext('envelope')), 'envelope'); } $select_type = new html_select(array('name' => "_rule_mod_type[]", 'id' => 'rule_mod_type'.$id)); $select_type->add(rcube::Q($this->plugin->gettext('allparts')), 'all'); @@ -1567,7 +1625,7 @@ $select_type->add(rcube::Q($this->plugin->gettext('detail')), 'detail'); } $need_mod = !in_array($rule['test'], array('size', 'body', 'date', 'currentdate')); $need_mod = !in_array($rule['test'], array('size', 'body', 'date', 'currentdate', 'duplicate')); $mout = '<div id="rule_mod' .$id. '" class="adv"' . (!$need_mod ? ' style="display:none"' : '') . '>'; $mout .= ' <span class="label">' . rcube::Q($this->plugin->gettext('modifier')) . ' </span>'; $mout .= $select_mod->show($rule['test']); @@ -1604,7 +1662,8 @@ } // Comparators $mout .= '<div id="rule_comp' .$id. '" class="adv"' . ($rule['test'] == 'size' ? ' style="display:none"' : '') . '>'; $need_comp = $rule['test'] != 'size' && $rule['test'] != 'duplicate'; $mout .= '<div id="rule_comp' .$id. '" class="adv"' . (!$need_comp ? ' style="display:none"' : '') . '>'; $mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('comparator')) . '</span>'; $mout .= $select_comp->show($rule['comparator']); $mout .= '</div>'; @@ -1633,6 +1692,32 @@ $mout .= '</div>'; } // Duplicate if (in_array('duplicate', $this->exts)) { $need_duplicate = $rule['test'] == 'duplicate'; $mout .= '<div id="rule_duplicate_div' .$id. '" class="adv"'. (!$need_duplicate ? ' style="display:none"' : '') .'>'; $mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('duplicate.handle')) . '</span>'; $mout .= '<input type="text" name="_rule_duplicate_handle[]" id="rule_duplicate_handle'.$id . '" value="'. ($rule['handle'] ? rcube::JQ($rule['handle']) : '') . '" size="30"' . $this->error_class($id, 'test', 'duplicate_handle', 'rule_duplicate_handle') .' /><br>'; $mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('duplicate.header')) . '</span>'; $mout .= '<input type="text" name="_rule_duplicate_header[]" id="rule_duplicate_header'.$id . '" value="'. ($rule['header'] ? rcube::JQ($rule['header']) : '') . '" size="30"' . $this->error_class($id, 'test', 'duplicate_header', 'rule_duplicate_header') .' /><br>'; $mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('duplicate.uniqueid')) . '</span>'; $mout .= '<input type="text" name="_rule_duplicate_uniqueid[]" id="rule_duplicate_uniqueid'.$id . '" value="'. ($rule['uniqueid'] ? rcube::JQ($rule['uniqueid']) : '') . '" size="30"' . $this->error_class($id, 'test', 'duplicate_uniqueid', 'rule_duplicate_uniqueid') .' /><br>'; $mout .= '<span class="label">' . rcube::Q($this->plugin->gettext('duplicate.seconds')) . '</span>'; $mout .= '<input type="text" name="_rule_duplicate_seconds[]" id="rule_duplicate_seconds'.$id . '" value="'. rcube::JQ($rule['seconds']) . '" size="6"' . $this->error_class($id, 'test', 'duplicate_seconds', 'rule_duplicate_seconds') .' />'; $mout .= ' <input type="checkbox" name="_rule_duplicate_last['.$id.']" id="rule_duplicate_last'.$id . '" value="1"' . (!empty($rule['last']) ? ' checked="checked"' : '') . ' />' . '<label for="rule_duplicate_last'.$id.'">'.rcube::Q($this->plugin->gettext('duplicate.last')).'</label>'; $mout .= '</div>'; } // Build output table $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : ''; $out .= '<table><tr>'; plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
@@ -26,19 +26,20 @@ private $vars = array(); // "global" variables private $prefix = ''; // script header (comments) private $supported = array( // Sieve extensions supported by class private $supported = array( // supported Sieve extensions: 'body', // RFC5173 'copy', // RFC3894 'date', // RFC5260 'duplicate', // RFC7352 'enotify', // RFC5435 'envelope', // RFC5228 'ereject', // RFC5429 'fileinto', // RFC5228 'imapflags', // draft-melnikov-sieve-imapflags-06 'imap4flags', // RFC5232 'include', // draft-ietf-sieve-include-12 'include', // RFC6609 'index', // RFC5260 'notify', // draft-martin-sieve-notify-01, 'notify', // RFC5435 'regex', // draft-ietf-sieve-regex-01 'reject', // RFC5429 'relational', // RFC3431 @@ -317,7 +318,29 @@ $tests[$i] .= ' ' . self::escape_string($test['arg']); break; case 'duplicate': array_push($exts, 'duplicate'); $tests[$i] .= ($test['not'] ? 'not ' : '') . $test['test']; $tokens = array('handle', 'uniqueid', 'header'); foreach ($tokens as $token) if ($test[$token] !== null && $test[$token] !== '') { $tests[$i] .= " :$token " . self::escape_string($test[$token]); } if (!empty($test['seconds'])) { $tests[$i] .= ' :seconds ' . intval($test['seconds']); } if (!empty($test['last'])) { $tests[$i] .= ' :last'; } break; } $i++; } } @@ -749,6 +772,23 @@ $tests[] = $test; break; case 'duplicate': $test = array('test' => $token, 'not' => $not); for ($i=0, $len=count($tokens); $i<$len; $i++) { if (!is_array($tokens[$i])) { if (preg_match('/^:(handle|header|uniqueid|seconds)$/i', $tokens[$i], $m)) { $test[strtolower($m[1])] = $tokens[++$i]; } else if (preg_match('/^:last$/i', $tokens[$i])) { $test['last'] = true; } } } $tests[] = $test; break; case 'exists': $tests[] = array('test' => 'exists', 'not' => $not, 'arg' => array_pop($tokens)); @@ -966,33 +1006,25 @@ $result = array(); for ($i=0, $len=count($tokens); $i<$len; $i++) { if (!is_array($tokens[$i]) && $tokens[$i][0] == ':') { if (preg_match('/^:comparator$/i', $tokens[$i])) { if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { $test['comparator'] = $tokens[++$i]; continue; } if (preg_match('/^:(count|value)$/i', $tokens[$i])) { else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) { $test['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i]; continue; } if (preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { $test['type'] = strtolower(substr($tokens[$i], 1)); continue; } if (preg_match('/^:index$/i', $tokens[$i])) { else if (!is_array($tokens[$i]) && preg_match('/^:index$/i', $tokens[$i])) { $test['index'] = intval($tokens[++$i]); if ($tokens[$i+1] && preg_match('/^:last$/i', $tokens[$i+1])) { $test['last'] = true; $i++; } continue; } } else { $result[] = $tokens[$i]; } } $tokens = $result; @@ -1098,7 +1130,6 @@ { $result = array(); $length = strlen($str); $mask = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:_'; // remove spaces from the beginning of the string while ($position < $length && (!$num || $num === true || count($result) < $num)) { @@ -1184,11 +1215,18 @@ if ($position == $length) { break 2; } if ($length - $position < 2) { $result[] = substr($str, $position); $position = $length; break; } // tag/identifier/number if ($len = strspn($str, $mask, $position)) { $atom = substr($str, $position, $len); $position += $len; if (preg_match('/[a-zA-Z0-9:_]+/', $str, $m, PREG_OFFSET_CAPTURE, $position) && $m[0][1] == $position ) { $atom = $m[0][0]; $position += strlen($atom); if ($atom != 'text:') { $result[] = $atom; plugins/managesieve/localization/en_US.inc
@@ -56,7 +56,7 @@ $labels['del'] = 'Delete'; $labels['sender'] = 'Sender'; $labels['recipient'] = 'Recipient'; $labels['vacationaddr'] = 'My email addresses:'; $labels['vacationaddr'] = 'My e-mail addresses:'; $labels['vacationdays'] = 'How often send messages (in days):'; $labels['vacationinterval'] = 'How often send messages:'; $labels['vacationreason'] = 'Message body (vacation reason):'; @@ -174,7 +174,7 @@ $labels['vacation.status'] = 'Status'; $labels['vacation.on'] = 'On'; $labels['vacation.off'] = 'Off'; $labels['vacation.addresses'] = 'My email addresses'; $labels['vacation.addresses'] = 'My e-mail addresses'; $labels['vacation.interval'] = 'Reply interval'; $labels['vacation.after'] = 'Put vacation rule after'; $labels['vacation.saving'] = 'Saving data...'; @@ -191,6 +191,14 @@ $labels['ariasummaryfiltersetslist'] = 'List of filter sets'; $labels['filterstitle'] = 'Edit incoming mail filters'; $labels['vacationtitle'] = 'Edit out-of-office rule'; $labels['message'] = 'Message'; $labels['duplicate'] = 'is duplicate'; $labels['notduplicate'] = 'is not duplicate'; $labels['duplicate.handle'] = 'handle:'; $labels['duplicate.header'] = 'header:'; $labels['duplicate.uniqueid'] = 'identifier:'; $labels['duplicate.seconds'] = 'timeout (seconds):'; $labels['duplicate.last'] = 'relative to the last execution'; $messages = array(); $messages['filterunknownerror'] = 'Unknown server error.'; @@ -199,6 +207,7 @@ $messages['filterdeleted'] = 'Filter deleted successfully.'; $messages['filtersaved'] = 'Filter saved successfully.'; $messages['filtersaveerror'] = 'Unable to save filter. Server error occurred.'; $messages['filterformerror'] = 'Filter form contains errors.'; $messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?'; $messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?'; $messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?'; @@ -228,5 +237,6 @@ $messages['saveerror'] = 'Unable to save data. Server error occurred.'; $messages['vacationsaved'] = 'Vacation data saved successfully.'; $messages['emptyvacationbody'] = 'Body of vacation message is required!'; $messages['duplicate.conflict.err'] = 'Both header and unique identifier are not alowed.'; ?> plugins/managesieve/managesieve.js
@@ -599,6 +599,7 @@ { var obj = document.getElementById('header' + id), size = document.getElementById('rule_size' + id), msg = document.getElementById('rule_message' + id), op = document.getElementById('rule_op' + id), header = document.getElementById('custom_header' + id + '_list'), mod = document.getElementById('rule_mod' + id), @@ -610,7 +611,11 @@ if (h == 'size') { size.style.display = 'inline'; $.each([op, header, mod, trans, comp], function() { this.style.display = 'none'; }); $.each([op, header, mod, trans, comp, msg], function() { this.style.display = 'none'; }); } else if (h == 'message') { msg.style.display = 'inline'; $.each([op, header, mod, trans, comp, size], function() { this.style.display = 'none'; }); } else { header.style.display = h != '...' ? 'none' : 'inline-block'; @@ -619,6 +624,7 @@ comp.style.display = ''; mod.style.display = h == 'body' || h == 'currentdate' || h == 'date' ? 'none' : 'block'; trans.style.display = h == 'body' ? 'block' : 'none'; msg.style.display = h == 'message' ? 'block' : 'none'; } if (datepart) @@ -638,7 +644,7 @@ if (!header) header = document.getElementById('header' + id).value; target.style.display = obj.value == 'exists' || obj.value == 'notexists' || header == 'size' ? 'none' : 'inline-block'; target.style.display = obj.value.match(/^(exists|notexists)$/) || header.match(/^(size|message)$/) ? 'none' : 'inline-block'; }; function rule_trans_select(id) @@ -653,6 +659,7 @@ { var obj = document.getElementById('rule_mod_op' + id), target = document.getElementById('rule_mod_type' + id), duplicate = document.getElementById('rule_duplicate_div' + id), index = document.getElementById('rule_index_div' + id); if (!header) @@ -661,7 +668,10 @@ target.style.display = obj.value != 'address' && obj.value != 'envelope' ? 'none' : 'inline'; if (index) index.style.display = header != 'body' && header != 'currentdate' && header != 'size' && obj.value != 'envelope' ? '' : 'none'; index.style.display = !header.match(/^(body|currentdate|size|message)$/) && obj.value != 'envelope' ? '' : 'none'; if (duplicate) duplicate.style.display = header == 'message' ? '' : 'none'; }; function rule_join_radio(value) @@ -954,6 +964,13 @@ .click(function() { // show drop-down upon clicks $(this).autocomplete('search', $(this).val() || ' '); }) // display advanced controls when contain errors $('input.error').each(function() { if (String(this.id).match(/([0-9]+)$/)) { $('#ruleadv' + RegExp.$1 + '.show').click(); } }); } plugins/managesieve/skins/classic/managesieve.css
@@ -172,12 +172,12 @@ td.advbutton a.show { background: url(images/down_small.gif) center no-repeat; background: url(images/down_small.gif?v=8629.106) center no-repeat; } td.advbutton a.hide { background: url(images/up_small.gif) center no-repeat; background: url(images/up_small.gif?v=c56c.106) center no-repeat; } td.rowbuttons @@ -225,6 +225,11 @@ margin-left: 10px; } td.rowtargets div.adv input { margin-bottom: 1px; } html.mozilla #filter-form select { padding-top: 3px; @@ -255,6 +260,7 @@ } td.rowtargets span, td.rowtargets label, span.label { color: #666666; @@ -305,7 +311,7 @@ a.button.add { background: url(images/add.png) no-repeat; background: url(images/add.png?v=a165.280) no-repeat; width: 30px; height: 20px; margin-right: 4px; @@ -314,7 +320,7 @@ a.button.del { background: url(images/del.png) no-repeat; background: url(images/del.png?v=3c27.247) no-repeat; width: 30px; height: 20px; display: inline-block; @@ -400,7 +406,7 @@ display: inline-block; width: 16px; height: 16px; background: url(images/erase.png) -1px 0 no-repeat #eee; background: url(images/erase.png?v=3052.453) -1px 0 no-repeat #eee; cursor: pointer; } plugins/managesieve/skins/classic/managesieve_mail.css
@@ -1,5 +1,5 @@ #messagemenu li a.filterlink { background-image: url(images/filter.png); background-image: url(images/filter.png?v=b0fe.547); background-position: 7px 1px; } plugins/managesieve/skins/larry/managesieve.css
@@ -93,6 +93,7 @@ #filter-form legend, #filter-form label { color: #666666; vertical-align: middle; } #rules, #actions @@ -146,12 +147,12 @@ td.advbutton a.show { background: url(images/down_small.gif) center no-repeat; background: url(images/down_small.gif?v=8629.106) center no-repeat; } td.advbutton a.hide { background: url(images/up_small.gif) center no-repeat; background: url(images/up_small.gif?v=c56c.106) center no-repeat; } td.rowbuttons @@ -197,6 +198,11 @@ td.rowtargets div a { margin-left: 10px; } td.rowtargets div.adv input { margin-bottom: 1px; } input.disabled, input.disabled:hover @@ -282,7 +288,7 @@ a.button.add { background: url(images/add.png) no-repeat; background: url(images/add.png?v=a165.280) no-repeat; width: 30px; height: 20px; margin-right: 4px; @@ -291,7 +297,7 @@ a.button.del { background: url(images/del.png) no-repeat; background: url(images/del.png?v=3c27.247) no-repeat; width: 30px; height: 20px; display: inline-block; @@ -393,7 +399,7 @@ display: inline-block; width: 16px; height: 16px; background: url(images/erase.png) -1px -1px no-repeat #eee; background: url(images/erase.png?v=3052.453) -1px -1px no-repeat #eee; cursor: pointer; } @@ -414,7 +420,7 @@ /* vacation form */ #settings-sections .vacation a { background-image: url(images/vacation_icons.png); background-image: url(images/vacation_icons.png?v=e738.767); background-repeat: no-repeat; background-position: 7px 1px; } plugins/managesieve/tests/src/parser_duplicate
New file @@ -0,0 +1,16 @@ require ["duplicate","fileinto"]; # rule:[test-duplicate] if duplicate { fileinto "urgent"; } # rule:[test-duplicate-2] if allof (duplicate :handle "support" :header "X-Ticket-ID", header :contains "subject" "fileserver") { fileinto "test"; } # rule:[test-duplicate-3] if not duplicate :uniqueid "test" :seconds 1800 { discard; }