diff options
-rw-r--r-- | CHANGELOG | 3 | ||||
-rw-r--r-- | plugins/managesieve/Changelog | 7 | ||||
-rw-r--r-- | plugins/managesieve/lib/rcube_sieve.php | 847 | ||||
-rw-r--r-- | plugins/managesieve/localization/en_US.inc | 1 | ||||
-rw-r--r-- | plugins/managesieve/localization/pl_PL.inc | 1 | ||||
-rw-r--r-- | plugins/managesieve/managesieve.js | 39 | ||||
-rw-r--r-- | plugins/managesieve/managesieve.php | 32 | ||||
-rw-r--r-- | plugins/managesieve/skins/default/managesieve.css | 38 | ||||
-rw-r--r-- | plugins/managesieve/skins/default/templates/filteredit.html | 10 | ||||
-rw-r--r-- | plugins/managesieve/skins/default/templates/managesieve.html | 14 |
10 files changed, 543 insertions, 449 deletions
@@ -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 diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog index 2a6ef09fc..c84a49512 100644 --- a/plugins/managesieve/Changelog +++ b/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) diff --git a/plugins/managesieve/lib/rcube_sieve.php b/plugins/managesieve/lib/rcube_sieve.php index fb7645240..649967f57 100644 --- a/plugins/managesieve/lib/rcube_sieve.php +++ b/plugins/managesieve/lib/rcube_sieve.php @@ -58,6 +58,10 @@ class rcube_sieve $this->disabled = $disabled; } + public function __destruct() { + $this->sieve->disconnect(); + } + /** * Getter for error code */ @@ -221,15 +225,25 @@ class rcube_sieve // 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; return true; @@ -254,23 +268,38 @@ class rcube_sieve } - 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); - $content .= "# rule:[".$name[$i]."]\n"; + $content .= "# rule:[".$name[$i]."]\n"; } 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++; + } + } + } return $content; } @@ -293,563 +322,553 @@ class rcube_sieve class rcube_sieve_script { - public $content = array(); // script rules array + public $content = array(); // script rules array - private $supported = array( // extensions supported by class - 'fileinto', - 'reject', - 'ereject', - 'vacation', // RFC5230 + private $supported = array( // extensions supported by class + 'fileinto', + 'reject', + 'ereject', + 'vacation', // RFC5230 // TODO: (most wanted first) body, imapflags, notify, regex ); - /** + /** * Object constructor * * @param string Script's text content * @param array Disabled extensions */ - public function __construct($script, $disabled=NULL) + public function __construct($script, $disabled=NULL) { - if (!empty($disabled)) - foreach ($disabled as $ext) - if (($idx = array_search($ext, $this->supported)) !== false) - unset($this->supported[$idx]); + if (!empty($disabled)) + foreach ($disabled as $ext) + if (($idx = array_search($ext, $this->supported)) !== false) + unset($this->supported[$idx]); - $this->content = $this->_parse_text($script); + $this->content = $this->_parse_text($script); } - /** + /** * Adds script contents as text to the script array (at the end) * * @param string Text script contents */ - public function add_text($script) + public function add_text($script) { - $content = $this->_parse_text($script); - $result = false; - - // check existsing script rules names - foreach ($this->content as $idx => $elem) - $names[$elem['name']] = $idx; - - foreach ($content as $elem) - if (!isset($names[$elem['name']])) - { - array_push($this->content, $elem); - $result = true; - } - - return $result; + $content = $this->_parse_text($script); + $result = false; + + // check existsing script rules names + foreach ($this->content as $idx => $elem) { + $names[$elem['name']] = $idx; + } + + foreach ($content as $elem) { + if (!isset($names[$elem['name']])) { + array_push($this->content, $elem); + $result = true; + } + } + + return $result; } - /** + /** * Adds rule to the script (at the end) * * @param string Rule name * @param array Rule content (as array) */ - public function add_rule($content) + public function add_rule($content) { - // TODO: check this->supported - array_push($this->content, $content); - return sizeof($this->content)-1; + // TODO: check this->supported + array_push($this->content, $content); + return sizeof($this->content)-1; } - public function delete_rule($index) + public function delete_rule($index) { - if(isset($this->content[$index])) - { - unset($this->content[$index]); - return true; + if(isset($this->content[$index])) { + unset($this->content[$index]); + return true; } - return false; + return false; } - public function size() + public function size() { - return sizeof($this->content); + return sizeof($this->content); } - public function update_rule($index, $content) + public function update_rule($index, $content) { - // TODO: check this->supported - if ($this->content[$index]) - { - $this->content[$index] = $content; - return $index; + // TODO: check this->supported + if ($this->content[$index]) { + $this->content[$index] = $content; + return $index; } - return false; + return false; } - /** + /** * Returns script as text */ - public function as_text() + public function as_text() { - $script = ''; - $exts = array(); - $idx = 0; - - // rules - foreach ($this->content as $rule) - { - $extension = ''; - $tests = array(); - $i = 0; - - // header - $script .= '# rule:[' . $rule['name'] . "]\n"; + $script = ''; + $exts = array(); + $idx = 0; + + // rules + foreach ($this->content as $rule) { + $extension = ''; + $tests = array(); + $i = 0; + + // header + $script .= '# rule:[' . $rule['name'] . "]\n"; - // constraints expressions - foreach ($rule['tests'] as $test) - { - $tests[$i] = ''; - switch ($test['test']) - { - case 'size': - $tests[$i] .= ($test['not'] ? 'not ' : ''); - $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; - break; - case 'true': - $tests[$i] .= ($test['not'] ? 'not true' : 'true'); - break; - case 'exists': - $tests[$i] .= ($test['not'] ? 'not ' : ''); - if (is_array($test['arg'])) - $tests[$i] .= 'exists ["' . implode('", "', $this->_escape_string($test['arg'])) . '"]'; - else - $tests[$i] .= 'exists "' . $this->_escape_string($test['arg']) . '"'; - break; - case 'header': - $tests[$i] .= ($test['not'] ? 'not ' : ''); - $tests[$i] .= 'header :' . $test['type']; - if (is_array($test['arg1'])) - $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg1'])) . '"]'; - else - $tests[$i] .= ' "' . $this->_escape_string($test['arg1']) . '"'; - if (is_array($test['arg2'])) - $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg2'])) . '"]'; - else - $tests[$i] .= ' "' . $this->_escape_string($test['arg2']) . '"'; - break; + // constraints expressions + foreach ($rule['tests'] as $test) { + $tests[$i] = ''; + switch ($test['test']) { + case 'size': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; + break; + case 'true': + $tests[$i] .= ($test['not'] ? 'not true' : 'true'); + break; + case 'exists': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + if (is_array($test['arg'])) + $tests[$i] .= 'exists ["' . implode('", "', $this->_escape_string($test['arg'])) . '"]'; + else + $tests[$i] .= 'exists "' . $this->_escape_string($test['arg']) . '"'; + break; + case 'header': + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'header :' . $test['type']; + if (is_array($test['arg1'])) + $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg1'])) . '"]'; + else + $tests[$i] .= ' "' . $this->_escape_string($test['arg1']) . '"'; + if (is_array($test['arg2'])) + $tests[$i] .= ' ["' . implode('", "', $this->_escape_string($test['arg2'])) . '"]'; + else + $tests[$i] .= ' "' . $this->_escape_string($test['arg2']) . '"'; + break; } - $i++; + $i++; } + +// $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); + else if (sizeof($tests)) + $script .= $tests[0]; + else + $script .= 'true'; + $script .= ")\n{\n"; - $script .= ($idx>0 ? 'els' : '').($rule['join'] ? 'if allof (' : 'if anyof ('); - if (sizeof($tests) > 1) - $script .= implode(",\n\t", $tests); - elseif (sizeof($tests)) - $script .= $tests[0]; - else - $script .= 'true'; - $script .= ")\n{\n"; - - // action(s) - foreach ($rule['actions'] as $action) - { - switch ($action['type']) - { - case 'fileinto': - $extension = 'fileinto'; - $script .= "\tfileinto \"" . $this->_escape_string($action['target']) . "\";\n"; - break; - case 'redirect': - $script .= "\tredirect \"" . $this->_escape_string($action['target']) . "\";\n"; - break; - case 'reject': - case 'ereject': - $extension = $action['type']; - if (strpos($action['target'], "\n")!==false) - $script .= "\t".$action['type']." text:\n" . $action['target'] . "\n.\n;\n"; - else - $script .= "\t".$action['type']." \"" . $this->_escape_string($action['target']) . "\";\n"; - break; - case 'keep': - case 'discard': - case 'stop': - $script .= "\t" . $action['type'] .";\n"; - break; - case 'vacation': - $extension = 'vacation'; - $script .= "\tvacation"; - if ($action['days']) - $script .= " :days " . $action['days']; - if ($action['addresses']) - $script .= " :addresses " . $this->_print_list($action['addresses']); - if ($action['subject']) - $script .= " :subject \"" . $this->_escape_string($action['subject']) . "\""; - if ($action['handle']) - $script .= " :handle \"" . $this->_escape_string($action['handle']) . "\""; - if ($action['from']) - $script .= " :from \"" . $this->_escape_string($action['from']) . "\""; - if ($action['mime']) - $script .= " :mime"; - if (strpos($action['reason'], "\n")!==false) - $script .= " text:\n" . $action['reason'] . "\n.\n;\n"; - else - $script .= " \"" . $this->_escape_string($action['reason']) . "\";\n"; - break; - } + // action(s) + foreach ($rule['actions'] as $action) { + switch ($action['type']) { + case 'fileinto': + $extension = 'fileinto'; + $script .= "\tfileinto \"" . $this->_escape_string($action['target']) . "\";\n"; + break; + case 'redirect': + $script .= "\tredirect \"" . $this->_escape_string($action['target']) . "\";\n"; + break; + case 'reject': + case 'ereject': + $extension = $action['type']; + if (strpos($action['target'], "\n")!==false) + $script .= "\t".$action['type']." text:\n" . $action['target'] . "\n.\n;\n"; + else + $script .= "\t".$action['type']." \"" . $this->_escape_string($action['target']) . "\";\n"; + break; + case 'keep': + case 'discard': + case 'stop': + $script .= "\t" . $action['type'] .";\n"; + break; + case 'vacation': + $extension = 'vacation'; + $script .= "\tvacation"; + if ($action['days']) + $script .= " :days " . $action['days']; + if ($action['addresses']) + $script .= " :addresses " . $this->_print_list($action['addresses']); + if ($action['subject']) + $script .= " :subject \"" . $this->_escape_string($action['subject']) . "\""; + if ($action['handle']) + $script .= " :handle \"" . $this->_escape_string($action['handle']) . "\""; + if ($action['from']) + $script .= " :from \"" . $this->_escape_string($action['from']) . "\""; + if ($action['mime']) + $script .= " :mime"; + if (strpos($action['reason'], "\n")!==false) + $script .= " text:\n" . $action['reason'] . "\n.\n;\n"; + else + $script .= " \"" . $this->_escape_string($action['reason']) . "\";\n"; + break; + } - if ($extension && !isset($exts[$extension])) - $exts[$extension] = $extension; - } + if ($extension && !isset($exts[$extension])) + $exts[$extension] = $extension; + } - $script .= "}\n"; - $idx++; + $script .= "}\n"; + $idx++; } - // requires - if (sizeof($exts)) - $script = 'require ["' . implode('","', $exts) . "\"];\n" . $script; + // requires + if (sizeof($exts)) + $script = 'require ["' . implode('","', $exts) . "\"];\n" . $script; - return $script; + return $script; } - /** + /** * Returns script object * */ - public function as_array() + public function as_array() { - return $this->content; + return $this->content; } - /** + /** * Returns array of supported extensions * */ - public function get_extensions() + public function get_extensions() { - return array_values($this->supported); + return array_values($this->supported); } - /** + /** * Converts text script to rules array * * @param string Text script */ - private function _parse_text($script) + private function _parse_text($script) { - $i = 0; - $content = array(); - - // remove C comments - $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)) - { - $content[$i]['name'] = $matches[1]; - } - 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]); - } - } - - return $content; + $i = 0; + $content = array(); + + // remove C comments + $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)) { + $content[$i]['name'] = $matches[1]; + } + 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]); + } + } + } + + return $content; } - /** + /** * Convert text script fragment to rule object * * @param string Text rule */ - private function _tokenize_rule($content) + private function _tokenize_rule($content) { - $result = NULL; + $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])); - $actions = $this->_parse_actions(trim($matches[5])); + 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; - if ($tests && $actions) - $result = array( + 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, + ); } - return $result; + return $result; } - /** + /** * Parse body of actions section * * @param string Text body * @return array Array of parsed action type/target pairs */ - private function _parse_actions($content) + private function _parse_actions($content) { - $result = NULL; - - // supported actions - $patterns[] = '^\s*discard;'; - $patterns[] = '^\s*keep;'; - $patterns[] = '^\s*stop;'; - $patterns[] = '^\s*redirect\s+(.*?[^\\\]);'; - if (in_array('fileinto', $this->supported)) - $patterns[] = '^\s*fileinto\s+(.*?[^\\\]);'; - if (in_array('reject', $this->supported)) { - $patterns[] = '^\s*reject\s+text:(.*)\n\.\n;'; - $patterns[] = '^\s*reject\s+(.*?[^\\\]);'; - $patterns[] = '^\s*ereject\s+text:(.*)\n\.\n;'; - $patterns[] = '^\s*ereject\s+(.*?[^\\\]);'; - } - if (in_array('vacation', $this->supported)) - $patterns[] = '^\s*vacation\s+(.*?[^\\\]);'; - - $pattern = '/(' . implode('$)|(', $patterns) . '$)/ms'; - - // parse actions body - if (preg_match_all($pattern, $content, $mm, PREG_SET_ORDER)) - { - foreach ($mm as $m) - { - $content = trim($m[0]); + $result = NULL; + + // supported actions + $patterns[] = '^\s*discard;'; + $patterns[] = '^\s*keep;'; + $patterns[] = '^\s*stop;'; + $patterns[] = '^\s*redirect\s+(.*?[^\\\]);'; + if (in_array('fileinto', $this->supported)) + $patterns[] = '^\s*fileinto\s+(.*?[^\\\]);'; + if (in_array('reject', $this->supported)) { + $patterns[] = '^\s*reject\s+text:(.*)\n\.\n;'; + $patterns[] = '^\s*reject\s+(.*?[^\\\]);'; + $patterns[] = '^\s*ereject\s+text:(.*)\n\.\n;'; + $patterns[] = '^\s*ereject\s+(.*?[^\\\]);'; + } + if (in_array('vacation', $this->supported)) + $patterns[] = '^\s*vacation\s+(.*?[^\\\]);'; + + $pattern = '/(' . implode('$)|(', $patterns) . '$)/ms'; + + // parse actions body + 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)) - { - $result[] = array('type' => $matches[1]); - } - elseif(preg_match('/^fileinto/', $content)) - { - $result[] = array('type' => 'fileinto', 'target' => $this->_parse_string($m[sizeof($m)-1])); - } - 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)) - { - $result[] = array('type' => $matches[1], 'target' => $this->_parse_string($matches[2])); - } - elseif(preg_match('/^vacation\s+(.*);$/sm', $content, $matches)) - { - $vacation = array('type' => 'vacation'); - - if (preg_match('/:(days)\s+([0-9]+)/', $content, $vm)) { - $vacation['days'] = $vm[2]; - $content = preg_replace('/:(days)\s+([0-9]+)/', '', $content); - } - if (preg_match('/:(subject)\s+(".*?[^\\\]")/', $content, $vm)) { - $vacation['subject'] = $vm[2]; - $content = preg_replace('/:(subject)\s+(".*?[^\\\]")/', '', $content); - } - if (preg_match('/:(addresses)\s+\[(.*?[^\\\])\]/', $content, $vm)) { - $vacation['addresses'] = $this->_parse_list($vm[2]); - $content = preg_replace('/:(addresses)\s+\[(.*?[^\\\])\]/', '', $content); - } - if (preg_match('/:(handle)\s+(".*?[^\\\]")/', $content, $vm)) { - $vacation['handle'] = $vm[2]; - $content = preg_replace('/:(handle)\s+(".*?[^\\\]")/', '', $content); - } - if (preg_match('/:(from)\s+(".*?[^\\\]")/', $content, $vm)) { - $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); - } - - $vacation['reason'] = $this->_parse_string($content); - - $result[] = $vacation; + if(preg_match('/^(discard|keep|stop)/', $content, $matches)) { + $result[] = array('type' => $matches[1]); + } + elseif(preg_match('/^fileinto/', $content)) { + $result[] = array('type' => 'fileinto', 'target' => $this->_parse_string($m[sizeof($m)-1])); + } + 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)) { + $result[] = array('type' => $matches[1], 'target' => $this->_parse_string($matches[2])); + } + elseif(preg_match('/^vacation\s+(.*);$/sm', $content, $matches)) { + $vacation = array('type' => 'vacation'); + + if (preg_match('/:(days)\s+([0-9]+)/', $content, $vm)) { + $vacation['days'] = $vm[2]; + $content = preg_replace('/:(days)\s+([0-9]+)/', '', $content); + } + if (preg_match('/:(subject)\s+(".*?[^\\\]")/', $content, $vm)) { + $vacation['subject'] = $vm[2]; + $content = preg_replace('/:(subject)\s+(".*?[^\\\]")/', '', $content); + } + if (preg_match('/:(addresses)\s+\[(.*?[^\\\])\]/', $content, $vm)) { + $vacation['addresses'] = $this->_parse_list($vm[2]); + $content = preg_replace('/:(addresses)\s+\[(.*?[^\\\])\]/', '', $content); + } + if (preg_match('/:(handle)\s+(".*?[^\\\]")/', $content, $vm)) { + $vacation['handle'] = $vm[2]; + $content = preg_replace('/:(handle)\s+(".*?[^\\\]")/', '', $content); + } + if (preg_match('/:(from)\s+(".*?[^\\\]")/', $content, $vm)) { + $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); + } + + $vacation['reason'] = $this->_parse_string($content); + + $result[] = $vacation; + } } } - } - return $result; + return $result; } - /** + /** * Parse test/conditions section * * @param string Text */ - - private function _parse_tests($content) + private function _parse_tests($content) { - $result = NULL; + $result = NULL; - // lists - if (preg_match('/^(allof|anyof)\s+\((.*)\)$/sm', $content, $matches)) - { - $content = $matches[2]; - $join = $matches[1]=='allof' ? true : false; + // lists + if (preg_match('/^(allof|anyof)\s+\((.*)\)$/sm', $content, $matches)) { + $content = $matches[2]; + $join = $matches[1]=='allof' ? true : false; } - else - $join = false; + else + $join = false; - // supported tests regular expressions - // TODO: comparators, envelope - $patterns[] = '(not\s+)?(exists)\s+\[(.*?[^\\\])\]'; - $patterns[] = '(not\s+)?(exists)\s+(".*?[^\\\]")'; - $patterns[] = '(not\s+)?(true)'; - $patterns[] = '(not\s+)?(size)\s+:(under|over)\s+([0-9]+[KGM]{0,1})'; - $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+\[(.*?[^\\\]")\]'; - $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+(".*?[^\\\]")'; - $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+(".*?[^\\\]")'; - $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+\[(.*?[^\\\]")\]'; + // supported tests regular expressions + // TODO: comparators, envelope + $patterns[] = '(not\s+)?(exists)\s+\[(.*?[^\\\])\]'; + $patterns[] = '(not\s+)?(exists)\s+(".*?[^\\\]")'; + $patterns[] = '(not\s+)?(true)'; + $patterns[] = '(not\s+)?(size)\s+:(under|over)\s+([0-9]+[KGM]{0,1})'; + $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+\[(.*?[^\\\]")\]'; + $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+(".*?[^\\\]")'; + $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+\[(.*?[^\\\]")\]\s+(".*?[^\\\]")'; + $patterns[] = '(not\s+)?(header)\s+:(contains|is|matches)\s+(".*?[^\\\]")\s+\[(.*?[^\\\]")\]'; - // join patterns... - $pattern = '/(' . implode(')|(', $patterns) . ')/'; - - // ...and parse tests list - if (preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) - { - foreach ($matches as $match) - { - $size = sizeof($match); + // join patterns... + $pattern = '/(' . implode(')|(', $patterns) . ')/'; + + // ...and parse tests list + 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])) - { - $result[] = array( - 'test' => 'size', - 'not' => $match[$size-4] ? true : false, - 'type' => $match[$size-2], // under/over - 'arg' => $match[$size-1], // value - ); + if (preg_match('/^(not\s+)?size/', $match[0])) { + $result[] = array( + 'test' => 'size', + 'not' => $match[$size-4] ? true : false, + 'type' => $match[$size-2], // under/over + 'arg' => $match[$size-1], // value + ); } - elseif (preg_match('/^(not\s+)?header/', $match[0])) - { - $result[] = array( - 'test' => 'header', - 'not' => $match[$size-5] ? true : false, - 'type' => $match[$size-3], // is/contains/matches - 'arg1' => $this->_parse_list($match[$size-2]), // header(s) - 'arg2' => $this->_parse_list($match[$size-1]), // string(s) - ); + elseif (preg_match('/^(not\s+)?header/', $match[0])) { + $result[] = array( + 'test' => 'header', + 'not' => $match[$size-5] ? true : false, + 'type' => $match[$size-3], // is/contains/matches + 'arg1' => $this->_parse_list($match[$size-2]), // header(s) + 'arg2' => $this->_parse_list($match[$size-1]), // string(s) + ); } - 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+)?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])) - { - $result[] = array( - 'test' => 'true', - 'not' => $match[$size-2] ? true : false, - ); + elseif (preg_match('/^(not\s+)?true/', $match[0])) { + $result[] = array( + 'test' => 'true', + 'not' => $match[$size-2] ? true : false, + ); } } } - return array($result, $join); + return array($result, $join); } - /** + /** * Parse string value * * @param string Text */ - private function _parse_string($content) + private function _parse_string($content) { - $text = ''; - $content = trim($content); + $text = ''; + $content = trim($content); - if (preg_match('/^text:(.*)\.$/sm', $content, $matches)) - $text = trim($matches[1]); - elseif (preg_match('/^"(.*)"$/', $content, $matches)) - $text = str_replace('\"', '"', $matches[1]); + if (preg_match('/^text:(.*)\.$/sm', $content, $matches)) + $text = trim($matches[1]); + elseif (preg_match('/^"(.*)"$/', $content, $matches)) + $text = str_replace('\"', '"', $matches[1]); - return $text; + return $text; } - /** + /** * Escape special chars in string value * * @param string Text */ - private function _escape_string($content) + private function _escape_string($content) { - $replace['/"/'] = '\\"'; + $replace['/"/'] = '\\"'; - 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]); + 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]); - return $content; + return $content; } - else - return preg_replace(array_keys($replace), array_values($replace), $content); + else + return preg_replace(array_keys($replace), array_values($replace), $content); } - /** + /** * Parse string or list of strings to string or array of strings * * @param string Text */ - private function _parse_list($content) + private function _parse_list($content) { - $result = array(); + $result = array(); - for ($x=0, $len=strlen($content); $x<$len; $x++) - { - switch ($content[$x]) - { - case '\\': - $str .= $content[++$x]; - break; - case '"': - if (isset($str)) - { - $result[] = $str; - unset($str); - } - else - $str = ''; - break; - default: - if(isset($str)) - $str .= $content[$x]; - break; + for ($x=0, $len=strlen($content); $x<$len; $x++) { + switch ($content[$x]) { + case '\\': + $str .= $content[++$x]; + break; + case '"': + if (isset($str)) { + $result[] = $str; + unset($str); + } + else + $str = ''; + break; + default: + if(isset($str)) + $str .= $content[$x]; + break; } } - if (sizeof($result)>1) - return $result; - elseif (sizeof($result) == 1) - return $result[0]; - else - return NULL; + if (sizeof($result)>1) + return $result; + elseif (sizeof($result) == 1) + return $result[0]; + else + return NULL; } - /** + /** * Convert array of elements to list of strings * * @param string Text */ - private function _print_list($list) + private function _print_list($list) { - $list = (array) $list; - foreach($list as $idx => $val) - $list[$idx] = $this->_escape_string($val); + $list = (array) $list; + foreach($list as $idx => $val) + $list[$idx] = $this->_escape_string($val); - return '["' . implode('","', $list) . '"]'; + return '["' . implode('","', $list) . '"]'; } } diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc index a5bb878c6..ac410d3db 100644 --- a/plugins/managesieve/localization/en_US.inc +++ b/plugins/managesieve/localization/en_US.inc @@ -46,6 +46,7 @@ $labels['newfilterset'] = 'New filters set'; $labels['active'] = 'active'; $labels['copyfromset'] = 'Copy filters from set'; $labels['none'] = '- none -'; +$labels['filterdisabled'] = 'Filter disabled'; $messages = array(); $messages['filterunknownerror'] = 'Unknown server error'; diff --git a/plugins/managesieve/localization/pl_PL.inc b/plugins/managesieve/localization/pl_PL.inc index 663b37f05..6d3ca3102 100644 --- a/plugins/managesieve/localization/pl_PL.inc +++ b/plugins/managesieve/localization/pl_PL.inc @@ -47,6 +47,7 @@ $labels['newfilterset'] = 'Nowy zbiór filtrów'; $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'; diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js index 99b820f4e..08954c1c2 100644 --- a/plugins/managesieve/managesieve.js +++ b/plugins/managesieve/managesieve.js @@ -26,9 +26,10 @@ if (window.rcmail) { if (rcmail.env.action == 'plugin.managesieve') { - if (rcmail.gui_objects.sieveform) + if (rcmail.gui_objects.sieveform) { rcmail.enable_command('plugin.managesieve-save', true); - else { + } + else { rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', 'plugin.managesieve-down', false); rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror); @@ -47,6 +48,8 @@ if (window.rcmail) { rcmail.filters_list.focus(); } } + if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled) + $('#disabled').attr('checked', true); }); /*********************************************************/ @@ -91,7 +94,7 @@ if (window.rcmail) { 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 @@ if (window.rcmail) { 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 (window.rcmail) { 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 @@ if (window.rcmail) { 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 (window.rcmail) { 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 (window.rcmail) { 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 @@ if (window.rcmail) { 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; diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php index e88fcff45..4c50616b1 100644 --- a/plugins/managesieve/managesieve.php +++ b/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 @@ class managesieve extends rcube_plugin 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 @@ class managesieve extends rcube_plugin 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 @@ class managesieve extends rcube_plugin 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 @@ class managesieve extends rcube_plugin { // 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,8 +528,12 @@ class managesieve extends rcube_plugin $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'); @@ -722,7 +729,10 @@ class managesieve extends rcube_plugin $out .= "</div>\n"; $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'); diff --git a/plugins/managesieve/skins/default/managesieve.css b/plugins/managesieve/skins/default/managesieve.css index 295c3f61f..9f72e4a62 100644 --- a/plugins/managesieve/skins/default/managesieve.css +++ b/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; @@ -170,12 +173,6 @@ body.iframe padding: 20px 10px 10px 10px; } -#filter-form input, select -{ - font-size: 10pt; - font-family: inherit; -} - fieldset { background-color: white; @@ -248,3 +245,24 @@ span.label 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; +} diff --git a/plugins/managesieve/skins/default/templates/filteredit.html b/plugins/managesieve/skins/default/templates/filteredit.html index 164b91ca8..81c6e6bc6 100644 --- a/plugins/managesieve/skins/default/templates/filteredit.html +++ b/plugins/managesieve/skins/default/templates/filteredit.html @@ -99,9 +99,15 @@ function rule_join_radio(value) <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> diff --git a/plugins/managesieve/skins/default/templates/managesieve.html b/plugins/managesieve/skins/default/templates/managesieve.html index 0913ef5fc..f99466e80 100644 --- a/plugins/managesieve/skins/default/templates/managesieve.html +++ b/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> |