CHANGELOG | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/Changelog | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/rcube_sieve.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/localization/en_US.inc | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/localization/pl_PL.inc | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/managesieve.js | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/managesieve.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/default/managesieve.css | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/default/templates/filteredit.html | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/skins/default/templates/managesieve.html | ●●●●● patch | view | raw | blame | history |
CHANGELOG
@@ -1,6 +1,9 @@ CHANGELOG RoundCube Webmail =========================== - Managesieve: import from Horde-INGO - Managesieve: support for more than one match (#1486078) - Managesieve: support for selectively disabling rules within a single sieve script (#1485882) - Threaded message listing now available - Added sorting by ARRIVAL and CC - Message list columns configurable by the user plugins/managesieve/Changelog
@@ -1,3 +1,10 @@ * version 2.3 [2010-03-18] ----------------------------------------------------------- - Added import from Horde-INGO - Support for more than one match using if+stop instead of if+elsif structures (#1486078) - Support for selectively disabling rules within a single sieve script (#1485882) - Added vertical splitter * version 2.2 [2010-02-06] ----------------------------------------------------------- - Fix handling of "<>" characters in filter names (#1486477) plugins/managesieve/lib/rcube_sieve.php
@@ -58,6 +58,10 @@ $this->disabled = $disabled; } public function __destruct() { $this->sieve->disconnect(); } /** * Getter for error code */ @@ -221,13 +225,23 @@ // try to parse from Roundcube format $this->script = new rcube_sieve_script($script, $this->disabled); // ... else try Squirrelmail format if (empty($this->script->content) && $name == 'phpscript') { $script = $this->sieve->getScript('phpscript'); $script = $this->_convert_from_squirrel_rules($script); // ... else try to import from different formats if (empty($this->script->content)) { $script = $this->_import_rules($script); $this->script = new rcube_sieve_script($script, $this->disabled); } // replace all elsif with if+stop, we support only ifs foreach ($this->script->content as $idx => $rule) { if (!isset($this->script->content[$idx+1]) || preg_match('/^else|elsif$/', $this->script->content[$idx+1]['type'])) { // 'stop' not found? if (!preg_match('/^(stop|vacation)$/', $rule['actions'][count($rule['actions'])-1]['type'])) { $this->script->content[$idx]['actions'][] = array( 'type' => 'stop' ); } } } $this->current = $name; @@ -254,13 +268,13 @@ } private function _convert_from_squirrel_rules($script) private function _import_rules($script) { $i = 0; $name = array(); // tokenize rules if ($tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) // Squirrelmail (Avelsieve) if ($tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { foreach($tokens as $token) { if (preg_match('/^#START_SIEVE_RULE.*/', $token, $matches)) { $name[$i] = "unnamed rule ".($i+1); @@ -269,6 +283,21 @@ elseif (isset($name[$i])) { $content .= "if $token\n"; $i++; } } } // Horde (INGO) else if ($tokens = preg_split('/(# .+)\r?\n/i', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { foreach($tokens as $token) { if (preg_match('/^# (.+)/i', $token, $matches)) { $name[$i] = $matches[1]; $content .= "# rule:[" . $name[$i] . "]\n"; } elseif (isset($name[$i])) { $token = str_replace(":comparator \"i;ascii-casemap\" ", "", $token); $content .= $token . "\n"; $i++; } } } @@ -330,14 +359,15 @@ $result = false; // check existsing script rules names foreach ($this->content as $idx => $elem) foreach ($this->content as $idx => $elem) { $names[$elem['name']] = $idx; } foreach ($content as $elem) if (!isset($names[$elem['name']])) { foreach ($content as $elem) { if (!isset($names[$elem['name']])) { array_push($this->content, $elem); $result = true; } } return $result; @@ -358,8 +388,7 @@ public function delete_rule($index) { if(isset($this->content[$index])) { if(isset($this->content[$index])) { unset($this->content[$index]); return true; } @@ -374,8 +403,7 @@ public function update_rule($index, $content) { // TODO: check this->supported if ($this->content[$index]) { if ($this->content[$index]) { $this->content[$index] = $content; return $index; } @@ -392,8 +420,7 @@ $idx = 0; // rules foreach ($this->content as $rule) { foreach ($this->content as $rule) { $extension = ''; $tests = array(); $i = 0; @@ -402,11 +429,9 @@ $script .= '# rule:[' . $rule['name'] . "]\n"; // constraints expressions foreach ($rule['tests'] as $test) { foreach ($rule['tests'] as $test) { $tests[$i] = ''; switch ($test['test']) { switch ($test['test']) { case 'size': $tests[$i] .= ($test['not'] ? 'not ' : ''); $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; @@ -437,7 +462,10 @@ $i++; } $script .= ($idx>0 ? 'els' : '').($rule['join'] ? 'if allof (' : 'if anyof ('); // $script .= ($idx>0 ? 'els' : '').($rule['join'] ? 'if allof (' : 'if anyof ('); // disabled rule: if false #.... $script .= 'if' . ($rule['disabled'] ? ' false #' : ''); $script .= $rule['join'] ? ' allof (' : ' anyof ('; if (sizeof($tests) > 1) $script .= implode(",\n\t", $tests); elseif (sizeof($tests)) @@ -447,10 +475,8 @@ $script .= ")\n{\n"; // action(s) foreach ($rule['actions'] as $action) { switch ($action['type']) { foreach ($rule['actions'] as $action) { switch ($action['type']) { case 'fileinto': $extension = 'fileinto'; $script .= "\tfileinto \"" . $this->_escape_string($action['target']) . "\";\n"; @@ -540,22 +566,19 @@ $script = preg_replace('|/\*.*?\*/|sm', '', $script); // tokenize rules if ($tokens = preg_split('/(# rule:\[.*\])\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) foreach($tokens as $token) { if (preg_match('/^# rule:\[(.*)\]/', $token, $matches)) { if ($tokens = preg_split('/(# rule:\[.*\])\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) { foreach($tokens as $token) { if (preg_match('/^# rule:\[(.*)\]/', $token, $matches)) { $content[$i]['name'] = $matches[1]; } elseif (isset($content[$i]['name']) && sizeof($content[$i]) == 1) { if ($rule = $this->_tokenize_rule($token)) { elseif (isset($content[$i]['name']) && sizeof($content[$i]) == 1) { if ($rule = $this->_tokenize_rule($token)) { $content[$i] = array_merge($content[$i], $rule); $i++; } else // unknown rule format unset($content[$i]); } } } @@ -571,16 +594,29 @@ { $result = NULL; if (preg_match('/^(if|elsif|else)\s+((true|not\s+true|allof|anyof|exists|header|not|size)(.*))\s+\{(.*)\}$/sm', trim($content), $matches)) { list($tests, $join) = $this->_parse_tests(trim($matches[2])); if (preg_match('/^(if|elsif|else)\s+((true|false|not\s+true|allof|anyof|exists|header|not|size)(.*))\s+\{(.*)\}$/sm', trim($content), $matches)) { $tests = trim($matches[2]); // disabled rule (false + comment): if false #..... if ($matches[3] == 'false') { $tests = preg_replace('/^false\s+#\s+/', '', $tests); $disabled = true; } else $disabled = false; list($tests, $join) = $this->_parse_tests($tests); $actions = $this->_parse_actions(trim($matches[5])); if ($tests && $actions) $result = array( 'type' => $matches[1], 'tests' => $tests, 'actions' => $actions, 'join' => $join, 'disabled' => $disabled, ); } @@ -616,30 +652,23 @@ $pattern = '/(' . implode('$)|(', $patterns) . '$)/ms'; // parse actions body if (preg_match_all($pattern, $content, $mm, PREG_SET_ORDER)) { foreach ($mm as $m) { if (preg_match_all($pattern, $content, $mm, PREG_SET_ORDER)) { foreach ($mm as $m) { $content = trim($m[0]); if(preg_match('/^(discard|keep|stop)/', $content, $matches)) { if(preg_match('/^(discard|keep|stop)/', $content, $matches)) { $result[] = array('type' => $matches[1]); } elseif(preg_match('/^fileinto/', $content)) { elseif(preg_match('/^fileinto/', $content)) { $result[] = array('type' => 'fileinto', 'target' => $this->_parse_string($m[sizeof($m)-1])); } elseif(preg_match('/^redirect/', $content)) { elseif(preg_match('/^redirect/', $content)) { $result[] = array('type' => 'redirect', 'target' => $this->_parse_string($m[sizeof($m)-1])); } elseif(preg_match('/^(reject|ereject)\s+(.*);$/sm', $content, $matches)) { elseif(preg_match('/^(reject|ereject)\s+(.*);$/sm', $content, $matches)) { $result[] = array('type' => $matches[1], 'target' => $this->_parse_string($matches[2])); } elseif(preg_match('/^vacation\s+(.*);$/sm', $content, $matches)) { elseif(preg_match('/^vacation\s+(.*);$/sm', $content, $matches)) { $vacation = array('type' => 'vacation'); if (preg_match('/:(days)\s+([0-9]+)/', $content, $vm)) { @@ -662,9 +691,11 @@ $vacation['from'] = $vm[2]; $content = preg_replace('/:(from)\s+(".*?[^\\\]")/', '', $content); } $content = preg_replace('/^vacation/', '', $content); $content = preg_replace('/;$/', '', $content); $content = trim($content); if (preg_match('/^:(mime)/', $content, $vm)) { $vacation['mime'] = true; $content = preg_replace('/^:mime/', '', $content); @@ -685,14 +716,12 @@ * * @param string Text */ private function _parse_tests($content) { $result = NULL; // lists if (preg_match('/^(allof|anyof)\s+\((.*)\)$/sm', $content, $matches)) { if (preg_match('/^(allof|anyof)\s+\((.*)\)$/sm', $content, $matches)) { $content = $matches[2]; $join = $matches[1]=='allof' ? true : false; } @@ -714,14 +743,11 @@ $pattern = '/(' . implode(')|(', $patterns) . ')/'; // ...and parse tests list if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $size = sizeof($match); if (preg_match('/^(not\s+)?size/', $match[0])) { if (preg_match('/^(not\s+)?size/', $match[0])) { $result[] = array( 'test' => 'size', 'not' => $match[$size-4] ? true : false, @@ -729,8 +755,7 @@ 'arg' => $match[$size-1], // value ); } elseif (preg_match('/^(not\s+)?header/', $match[0])) { elseif (preg_match('/^(not\s+)?header/', $match[0])) { $result[] = array( 'test' => 'header', 'not' => $match[$size-5] ? true : false, @@ -739,16 +764,14 @@ 'arg2' => $this->_parse_list($match[$size-1]), // string(s) ); } elseif (preg_match('/^(not\s+)?exists/', $match[0])) { elseif (preg_match('/^(not\s+)?exists/', $match[0])) { $result[] = array( 'test' => 'exists', 'not' => $match[$size-3] ? true : false, 'arg' => $this->_parse_list($match[$size-1]), // header(s) ); } elseif (preg_match('/^(not\s+)?true/', $match[0])) { elseif (preg_match('/^(not\s+)?true/', $match[0])) { $result[] = array( 'test' => 'true', 'not' => $match[$size-2] ? true : false, @@ -787,8 +810,7 @@ { $replace['/"/'] = '\\"'; if (is_array($content)) { if (is_array($content)) { for ($x=0, $y=sizeof($content); $x<$y; $x++) $content[$x] = preg_replace(array_keys($replace), array_values($replace), $content[$x]); @@ -807,16 +829,13 @@ { $result = array(); for ($x=0, $len=strlen($content); $x<$len; $x++) { switch ($content[$x]) { for ($x=0, $len=strlen($content); $x<$len; $x++) { switch ($content[$x]) { case '\\': $str .= $content[++$x]; break; case '"': if (isset($str)) { if (isset($str)) { $result[] = $str; unset($str); } plugins/managesieve/localization/en_US.inc
@@ -46,6 +46,7 @@ $labels['active'] = 'active'; $labels['copyfromset'] = 'Copy filters from set'; $labels['none'] = '- none -'; $labels['filterdisabled'] = 'Filter disabled'; $messages = array(); $messages['filterunknownerror'] = 'Unknown server error'; plugins/managesieve/localization/pl_PL.inc
@@ -47,6 +47,7 @@ $labels['active'] = 'aktywny'; $labels['copyfromset'] = 'Skopiuj filtry ze zbioru'; $labels['none'] = '- brak -'; $labels['filterdisabled'] = 'Filtr wyłączony'; $messages = array(); $messages['filterunknownerror'] = 'Nieznany błąd serwera'; plugins/managesieve/managesieve.js
@@ -26,8 +26,9 @@ if (rcmail.env.action == 'plugin.managesieve') { if (rcmail.gui_objects.sieveform) if (rcmail.gui_objects.sieveform) { rcmail.enable_command('plugin.managesieve-save', true); } else { rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', 'plugin.managesieve-down', false); @@ -47,6 +48,8 @@ rcmail.filters_list.focus(); } } if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled) $('#disabled').attr('checked', true); }); /*********************************************************/ @@ -91,7 +94,7 @@ return i; } rcube_webmail.prototype.managesieve_updatelist = function(action, name, id) rcube_webmail.prototype.managesieve_updatelist = function(action, name, id, disabled) { this.set_busy(true); @@ -114,7 +117,7 @@ case 'down': var rows = this.filters_list.rows; var from; var from, fromstatus, status; // we need only to replace filter names... for (var i=0; i<rows.length; i++) @@ -122,11 +125,15 @@ if (rows[i]==null) { // removed row continue; } else if (rows[i].uid == id) { from = rows[i].obj.cells[0]; from = rows[i].obj; fromstatus = $(from).hasClass('disabled'); } else if (rows[i].uid == id+1){ name = rows[i].obj.cells[0].innerHTML; rows[i].obj.cells[0].innerHTML = from.innerHTML; from.innerHTML = name; status = $(rows[i].obj).hasClass('disabled'); rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML; from.cells[0].innerHTML = name; $(from)[status?'addClass':'removeClass']('disabled'); $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled'); this.filters_list.highlight_row(i); break; } @@ -137,7 +144,7 @@ case 'up': var rows = this.filters_list.rows; var from; var from, status, fromstatus; // we need only to replace filter names... for (var i=0; i<rows.length; i++) @@ -145,12 +152,16 @@ if (rows[i]==null) { // removed row continue; } else if (rows[i].uid == id-1) { from = rows[i].obj.cells[0]; from = rows[i].obj; fromstatus = $(from).hasClass('disabled'); this.filters_list.highlight_row(i); } else if (rows[i].uid == id) { name = rows[i].obj.cells[0].innerHTML; rows[i].obj.cells[0].innerHTML = from.innerHTML; from.innerHTML = name; status = $(rows[i].obj).hasClass('disabled'); rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML; from.cells[0].innerHTML = name; $(from)[status?'addClass':'removeClass']('disabled'); $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled'); break; } } @@ -164,6 +175,10 @@ if (rows[i] && rows[i].uid == id) { rows[i].obj.cells[0].innerHTML = name; if (disabled) $(rows[i].obj).addClass('disabled'); else $(rows[i].obj).removeClass('disabled'); break; } break; @@ -186,6 +201,8 @@ td = parent.document.createElement('td'); new_row.appendChild(td); list.insert_row(new_row, false); if (disabled) $(new_row).addClass('disabled'); if (row.cells[0].className) td.className = row.cells[0].className; plugins/managesieve/managesieve.php
@@ -7,7 +7,7 @@ * It's clickable interface which operates on text scripts and communicates * with server using managesieve protocol. Adds Filters tab in Settings. * * @version 2.2 * @version 2.3 * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl> * * Configuration (see config.inc.php.dist) @@ -46,9 +46,7 @@ function managesieve_start() { $rcmail = rcmail::get_instance(); $this->rc = &$rcmail; $this->rc = rcmail::get_instance(); $this->load_config(); // register UI objects @@ -302,6 +300,7 @@ foreach ($sizeitems as $item) $items[] = $item; $this->form['disabled'] = $_POST['_disabled'] ? true : false; $this->form['join'] = $join=='allof' ? true : false; $this->form['name'] = $name; $this->form['tests'] = array(); @@ -486,8 +485,10 @@ if ($save && $fid !== false) { $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); $this->rc->output->add_script(sprintf("rcmail.managesieve_updatelist('%s', '%s', %d);", isset($new) ? 'add' : 'update', Q($this->form['name']), $fid), 'foot'); $this->rc->output->add_script( sprintf("rcmail.managesieve_updatelist('%s', '%s', %d, %d);", isset($new) ? 'add' : 'update', Q($this->form['name']), $fid, $this->form['disabled']), 'foot'); } else { @@ -504,10 +505,12 @@ { // Handle form action if (isset($_GET['_framed']) || isset($_POST['_framed'])) { if (isset($_GET['_newset']) || isset($_POST['_newset'])) if (isset($_GET['_newset']) || isset($_POST['_newset'])) { $this->rc->output->send('managesieve.setedit'); else } else { $this->rc->output->send('managesieve.filteredit'); } } else { $this->rc->output->set_pagetitle($this->gettext('filters')); $this->rc->output->send('managesieve.managesieve'); @@ -525,7 +528,11 @@ $a_show_cols = array('managesieve.filtername'); foreach($this->script as $idx => $filter) $result[] = array('managesieve.filtername' => $filter['name'], 'id' => $idx); $result[] = array( 'managesieve.filtername' => $filter['name'], 'id' => $idx, 'class' => $filter['disabled'] ? 'disabled' : '', ); // create XHTML table $out = rcube_table_output($attrib, $result, $a_show_cols, 'id'); @@ -723,6 +730,9 @@ $out .= "</fieldset>\n"; if ($scr['disabled']) { $this->rc->output->set_env('rule_disabled', true); } $this->rc->output->add_label('managesieve.ruledeleteconfirm'); $this->rc->output->add_label('managesieve.actiondeleteconfirm'); $this->rc->output->add_gui_object('sieveform', 'filterform'); plugins/managesieve/skins/default/managesieve.css
@@ -5,7 +5,6 @@ { position: absolute; left: 20px; width: 220px; top: 120px; bottom: 30px; border: 1px solid #999999; @@ -28,6 +27,11 @@ cursor: pointer; } #filters-table tbody tr.disabled td { color: #999999; } #filtersbuttons { position: absolute; @@ -38,7 +42,7 @@ #filtersetsbuttons { position: absolute; left: 250px; left: 230px; top: 85px; } @@ -131,7 +135,7 @@ #filtersetselect { position: absolute; left: 380px; left: 360px; top: 90px; } @@ -139,7 +143,6 @@ { position: absolute; top: 120px; left: 250px; right: 20px; bottom: 30px; border: 1px solid #999999; @@ -168,12 +171,6 @@ white-space: nowrap; background-color: #F9F9F9; padding: 20px 10px 10px 10px; } #filter-form input, select { font-size: 10pt; font-family: inherit; } fieldset @@ -248,3 +245,24 @@ font-size: 10px; white-space: nowrap; } #footer { padding-top: 5px; width: 100%; } #footer .footerleft { padding-left: 2px; white-space: nowrap; float: left; } #footer .footerright { padding-right: 2px; white-space: nowrap; text-align: right; float: right; } plugins/managesieve/skins/default/templates/filteredit.html
@@ -99,9 +99,15 @@ <div id="filter-form"> <roundcube:object name="filterform" /> <p> <div id="footer"> <div class="footerleft"> <roundcube:button command="plugin.managesieve-save" type="input" class="button mainaction" label="save" /> </p> </div> <div class="footerright"> <label for="disabled"><roundcube:label name="managesieve.filterdisabled" /></label> <input type="checkbox" id="disabled" name="_disabled" value="1" /> </div> </div> </form> </div> plugins/managesieve/skins/default/templates/managesieve.html
@@ -5,6 +5,15 @@ <roundcube:include file="/includes/links.html" /> <link rel="stylesheet" type="text/css" href="/this/managesieve.css" /> <script type="text/javascript" src="/functions.js"></script> <script type="text/javascript" src="/splitter.js"></script> <style type="text/css"> #filterslist { width: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter-5 : 210" />px; } #filter-box { left: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220" />px; <roundcube:exp expression="browser:ie ? ('width:expression((parseInt(this.parentNode.offsetWidth)-'.(!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220).')+\\'px\\');') : ''" /> } </style> </head> <body> @@ -32,7 +41,10 @@ <div id="filterslist"> <roundcube:object name="filterslist" id="filters-table" class="records-table" cellspacing="0" summary="Filters list" /> </div> <script type="text/javascript"> var sieveviewsplit = new rcube_splitter({id:'sieveviewsplitter', p1: 'filterslist', p2: 'filter-box', orientation: 'v', relative: true, start: 215}); rcmail.add_onload('sieveviewsplit.init()'); </script> <div id="filter-box"> <roundcube:object name="filterframe" id="filter-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" /> </div>