Code

some changes
[gosa.git] / include / sieve / class_My_Tree.inc
1 <?php
5 /* This class is inherited from the original 'Tree'
6  *  class written by Heiko Hund.
7  * It is partly rewritten to create a useable html interface 
8  *  for each single sieve token. 
9  * This gives us the ability to edit existing sieve filters. 
10  */
11 class My_Tree extends Tree
12 {
13   var $dumpFn_;
14   var $dump_;
16   var $mode_stack = array();
17   var $pap              = array();
19   var $add_new          = FALSE;
20   var $add_new_id       = 0;
21   var $add_type         = "top";
22   var $add_element_type = "";
24   /* Create a html interface for the current sieve filter 
25    */
26   function dump()
27   {
28     error_reporting(E_ALL);
30     /**************
31      * Handle new elements 
32      **************/
34     if($this->add_new){
35   
36       $element_types= array(
37           "sieve_keep"      => _("Keep"),
38           "sieve_comment"   => _("Comment"),
39           "sieve_fileinto"  => _("File into"),
40           "sieve_keep"      => _("Keep"),
41           "sieve_discard"   => _("Discard"),
42           "sieve_redirect"  => _("Redirect"),
43           "sieve_reject"    => _("Reject"),
44           "sieve_require"   => _("Require"),
45           "sieve_stop"      => _("Stop"),
46           "sieve_vacation"  => _("Vacation message"),
47           "sieve_if"        => _("If"));
50       /* Element selected */
51       if(isset($_POST['element_type']) && isset($element_types[$_POST['element_type']])){
52         $this->add_element_type = $_POST['element_type'];
53       }
55       /* Create new element and add it at the selected position */
56       if(isset($_POST['select_new_element_type'])){
58         $ele = new $this->add_element_type(NULL, preg_replace("/[^0-9]/","",microtime()));
59         $start = $end = array();
60         $found = false;  
62         /* Add above current element*/
63         if($this->add_type == "top"){
64           foreach($this->pap as $key => $obj){
65             if($obj->object_id == $this->add_new_id){
66               $found = true;
67             }
68             if(!$found){
69               $start[] = $obj;
70             }else{
71               $end[] = $obj;
72             }
73           }    
74         }else{
75         /* Add below current element */
76           foreach($this->pap as $key => $obj){
77             if(!$found){
78               $start[] = $obj;
79             }else{
80               $end[] = $obj;
81             }
82             if($obj->object_id == $this->add_new_id){
83               $found = true;
84             }
85           }    
86         }
88         /* Only add, if current element could be located */
89         if($found){
90           $new = array();
91           foreach($start as $obj){
92             $new[] = $obj;
93           }      
94           $new[] = $ele;
95           foreach($end as $obj){
96             $new[] = $obj;
97           }
98           $this->pap = $new;
99           $this->add_new = FALSE;
100         }else{
101           print_red(_("Something went wrong while adding a new entry."));
102         }
103       }
105     }
107     /* Only display select dialog if it is necessary */
108     if($this->add_new){  
109       $smarty = get_smarty();
110       $smarty->assign("element_types",$element_types );
111       $smarty->assign("element_type",$this->add_element_type);
112       $str = $smarty->fetch(get_template_path("templates/add_element.tpl",TRUE,dirname(__FILE__)));
113       return($str);
114     }
116     /* Only parse the tokens once */
117     if(!count($this->pap)){
118       $this->dump_ = "";
119       $this->mode_stack = array();
120       $this->pap = array();
121       $this->doDump_(0, '', true);
122     }
124     /* Create html results */
125     $smarty = get_smarty();
127     $this -> dump_ = "";
128     foreach($this->pap as $key => $object){
129       if(is_object($object)){
130         $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); 
131       }
132     }
133     
134     /* Create html results */
135     $smarty = get_smarty();
136     $smarty->assign("Contents",$this->dump_);
137     $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
138     return ($ret);
139   }
142   /* This function walks through the object tree generated by the "Parse" class.
143    * All Commands will be resolved and grouped. So the Commands and their 
144    *  parameter are combined. Like "IF" and ":comparator"...
145    */  
146   function doDump_($node_id, $prefix, $last,$num = 1)
147   {
148     /* Indicates that current comman will only be valid for a single line. 
149      *  this command type will be removed from mode_stack after displaying it.
150      */
151     $rewoke_last = FALSE;
153     /* Get node */ 
154     $node = $this->nodes_[$node_id];
156     /* This closes the last mode */
157     if($node['class'] == "block-start"){
158       $tmp = array_pop($this->mode_stack);
159       $this->handle_elements($tmp,$node_id);
160       $this->handle_elements(array("TYPE" => "block_start"),$node_id);
161     }
163     /* This closes the last mode */
164     if($node['class'] == "block-end"){
165       $tmp = array_pop($this->mode_stack);
166       $this->handle_elements($tmp,$node_id);
167       $this->handle_elements(array("TYPE" => "block_end"),$node_id);
168     }
170     /* Semicolon indicates a new command */
171     if($node['class'] == "semicolon"){
172       $tmp =array_pop($this->mode_stack);
173       $this->handle_elements($tmp,$node_id);
174     }
176     /* Handle comments */
177     if($node['class'] == "comment"){
178       $this->mode_stack[] = array("TYPE" => $node['class']);
179       $rewoke_last = TRUE;
180     }
182     /* Handle identifiers */
183     $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
184     if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
185       $this->mode_stack[] = array("TYPE" => $node['text']); 
186     }
188     /* Add current node to current command stack */
189     end($this->mode_stack);
190     $key = key($this->mode_stack);
191     $this->mode_stack[$key]['ELEMENTS'][] = $node;
193     /* Remove last mode from mode stack, cause it was only valid for a single line */
194     if($rewoke_last){
195       $tmp =array_pop($this->mode_stack);
196       $this->handle_elements($tmp,$node_id);
197     }
199     /* If this is a sub element, just call this for all childs */       
200     if(isset($this->childs_[$node_id])){
201       $childs = $this->childs_[$node_id];
202       for ($i=0; $i<count($childs); ++$i)
203       {
204         $c_last = false;
205         if ($i+1 == count($childs))
206         {
207           $c_last = true;
208         }
209         $this->doDump_($childs[$i], "", $num);
210       }
211     }
212   }
215   /* Create a class for each resolved object.
216    * And append this class to a list of objects.
217    */
218   function handle_elements($data,$id)
219   {
220     if(!isset($data['TYPE'])){
221       return;
222     }
223     $type = $data['TYPE'];
224     
225     $class_name= "sieve_".$type ;
226     if(class_exists($class_name)){
227       $this->pap[] = new $class_name($data,$id);
228     }else{
229       echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
230     }
231   }
233   function save_object()
234   {
235     reset($this->pap);
236     foreach($this->pap as $key => $obj){
238       if(in_array(get_class($obj),array("sieve_if",
239                                         "sieve_elsif",
240                                         "sieve_vacation",
241                                         "sieve_comment",
242                                         "sieve_reject",
243                                         "sieve_fileinto",
244                                         "sieve_require",
245                                         "sieve_redirect"))){
248         if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
249           $this->pap[$key]->save_object();
250         }
251       }
253       $once = TRUE;
254       foreach($_POST as $name => $value){
256         if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
257           $once = FALSE;
258           $this->remove_object($key);
259         }
260         if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
261           $this->move_up_down($key,"up");
262           $once = FALSE;
263         }
264         if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
265           $this->move_up_down($key,"down");
266           $once = FALSE;
267         }
268         if(isset($obj->object_id) && preg_match("/^Add_Object_Top_".$obj->object_id."_/",$name) && $once){
269           $this->add_new_object($obj->object_id,"top");
270           $once = FALSE;
271         }
272         if(isset($obj->object_id) && preg_match("/^Add_Object_Bottom_".$obj->object_id."_/",$name) && $once){
273           $this->add_new_object($obj->object_id,"bottom");
274           $once = FALSE;
275         }
276       }
277     }
278   }
281   /* Add a new object at the given position */
282   function add_new_object($id,$top_bottom = "bottom")
283   {
284     $this->add_new    = TRUE;
285     $this->add_new_id = $id;
286     $this->add_type   = $top_bottom;
287   }
290   /* Remove the object at the given position */
291   function remove_object($key_id)
292   {
293     $class = get_class($this->pap[$key_id]);
294     if(in_array($class,array("sieve_if"))){
295       $block_start= $key_id;
296       $block_end  = $this->get_block_end($key_id);
297       for($i = $block_start ; $i <= $block_end ; $i ++ ){
298         unset($this->pap[$i]);
299       }
300     }else{
301       unset($this->pap[$key_id]);
302     }
303     $tmp = array();
304     foreach($this->pap as $element){
305       $tmp[] = $element;
306     }
307     $this->pap = $tmp;
308   }
311   /* This function moves a given element to another position.
312    * Single elements like "keep;" will simply be moved one posisition down/up.
313    * Multiple elements like if-elsif-else will be moved as block. 
314    * 
315    *  $key_id     specified the element that should be moved.
316    *  $direction  specifies to move elements "up" or "down"
317    */
318   function move_up_down($key_id,$direction = "down")
319   {
320      
321     /* Get the current element to decide what to move. */ 
322     $e_class = get_class($this->pap[$key_id]);
323       
324     if(in_array($e_class,array("sieve_if"))){
325       $block_start= $key_id;
326       $block_end  = $this->get_block_end($key_id);
328       /* Depending on the direction move up down */
329       if($direction == "down"){
330         $next_free  = $this->_get_next_free_move_slot($block_end,$direction); 
331       }else{
332         $next_free  = $this->_get_next_free_move_slot($block_start,$direction); 
333       }
335       /* Move the given block */ 
336       $this->move_multiple_elements($block_start,$block_end,$next_free);
337     }
339     if(in_array($e_class,array( "sieve_stop",
340                                 "sieve_keep",
341                                 "sieve_require",
342                                 "sieve_comment",
343                                 "sieve_stop",   
344                                 "sieve_reject", 
345                                 "sieve_fileinto",
346                                 "sieve_redirect", 
347                                 "sieve_discard"))){
348       $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
349     }
350   }
352   
353   /* Move the given block to position */
354   function move_multiple_elements($start,$end,$to)
355   {
356     /* Use class names for testing */
357     $data = $this->pap;
359     /* Get block to move */
360     $block_to_move = array_slice($data,$start, ($end - $start +1));
362     /* We want do move this block up */
363     if($end > $to){
364       
365       /* Get start block */
366       $start_block = array_slice($data,0,$to);
368       /* Get Get all elements between the block to move 
369        *  and next free position 
370        */
371       $block_to_free = array_slice($data,$to ,$start - $to );  
372       $block_to_end = array_slice($data,$end+1);
373       $new = array();
374       foreach($start_block as $block){
375         $new[] = $block;
376       }
377       foreach($block_to_move as $block){
378         $new[] = $block;
379       }
380       foreach($block_to_free as $block){
381         $new[] = $block;
382       }
383       foreach($block_to_end as $block){
384         $new[] = $block;
385       }
386       $old = $this->pap;
387       $this->pap = $new;
388     }
389     
391     /* We want to move this block down. */
392     if($to > $end){
394       /* Get start block */
395       $start_block = array_slice($data,0,$start);
397       /* Get Get all elements between the block to move 
398        *  and next free position 
399        */
400       $block_to_free = array_slice($data,$end +1,($to - $end  ));  
402       /* Get the rest 
403        */
404       $block_to_end = array_slice($data,$to+1);
406       $new = array();
407       foreach($start_block as $block){
408         $new[] = $block;
409       }
410       foreach($block_to_free as $block){
411         $new[] = $block;
412       }
413       foreach($block_to_move as $block){
414         $new[] = $block;
415       }
416       foreach($block_to_end as $block){
417         $new[] = $block;
418       }
419       $old = $this->pap;
420       $this->pap = $new;
421     }
422   }  
424   
425   /* This function returns the id of the element 
426    *  where the current block ends  
427    */
428   function get_block_end($start)
429   {
430     /* Only execute if this is a really a block element. 
431      * Block elements is only sieve_if
432      */
433     if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
435       $class      = get_class($this->pap[$start]);
436       $next_class = get_class($this->pap[$start+1]);
437       $block_depth = 0;
439       $end = FALSE;
441       while(!$end && $start < count($this->pap)){
442  
443         if($class == "sieve_block_start"){
444           $block_depth ++;
445         }
447         if($class == "sieve_block_end"){
448           $block_depth --;
449         }
451         if( $block_depth == 0 && 
452             $class == "sieve_block_end" && 
453             !in_array($next_class,array("sieve_else","sieve_elsif"))){
454           $end = TRUE;
455           $start --;
456         }
457         $start ++;       
458         $class      = get_class($this->pap[$start]);
459         $next_class = get_class($this->pap[$start+1]);
460       }
461     }
462     return($start);
463   }
466   /* This function moves the single element at 
467    *  position $from to position $to.
468    */
469   function move_single_element($from,$to)
470   {
471     if($from == $to) {
472       return;
473     }
475     $ret = array();
476     $tmp = $this->pap;
478     $begin = array();
479     $middle = array();
480     $end = array();
481     $element = $this->pap[$from];
483     if($from > $to ){
485       /* Get all element in fron to element to move */    
486       if($from  != 0){
487         $begin = array_slice($tmp,0,$to);
488       }
490       /* Get all elements between */
491       $middle = array_slice($tmp,$to , ($from - ($to) ));  
492     
493       /* Get the rest */ 
494       $end  = array_slice($tmp,$from+1);
495  
496       foreach($begin as $data){
497         $ret[] = $data;
498       }
499       $ret[] = $element;
500       foreach($middle as $data){
501         $ret[] = $data;
502       }
503       foreach($end as $data){
504         $ret[] = $data;
505       }
506       $this->pap = $ret;
507     }
508     if($from < $to ){
510       /* Get all element in fron to element to move */    
511       if($from  != 0){
512         $begin = array_slice($tmp,0,$from);
513       }
515       /* Get all elements between */
516       $middle = array_slice($tmp,$from+1 , ($to - ($from)));  
517     
518       /* Get the rest */ 
519       $end  = array_slice($tmp,$to+1);
520  
521       foreach($begin as $data){
522         $ret[] = $data;
523       }
524       foreach($middle as $data){
525         $ret[] = $data;
526       }
527       $ret[] = $element;
528       foreach($end as $data){
529         $ret[] = $data;
530       }
531       $this->pap = $ret;
532     }
533   }
536   /* Returns the next free position where we 
537    *  can add a new sinle element 
538    *    $key_id     = Current position
539    *    $direction  = Forward or backward.
540    */
541   function _get_next_free_move_slot($key_id,$direction)
542   {
543     $last_class = "";
544     $current_class ="";
545     $next_class = "";
547     /* After this elements we can add new elements 
548      *  without having any trouble.
549      */
550     $allowed_to_add_after = array("sieve_keep",
551                                   "sieve_require", 
552                                   "sieve_stop", 
553                                   "sieve_reject", 
554                                   "sieve_fileinto", 
555                                   "sieve_redirect", 
556                                   "sieve_discard",
557                                   "sieve_comment",
558                                   "sieve_block_start"
559                                  );
561     /* Before this elements we can add new elements 
562      *  without having any trouble.
563      */
564     $allowed_to_add_before = array("sieve_keep",
565                                   "sieve_require", 
566                                   "sieve_stop", 
567                                   "sieve_reject", 
568                                   "sieve_fileinto", 
569                                   "sieve_comment",
570                                   "sieve_redirect", 
571                                   "sieve_discard",
572                                   "sieve_if", 
573                                   "sieve_block_end"
574                                  );
576     if($direction == "down"){
577     
578       $test = $this->pap;
579       while($key_id < count($test)){
580         if(($key_id+1) == count($test)) {
581           return($key_id);
582         }
583         $key_id ++;
584         $current_class  = get_class($test[$key_id]);
585         if(in_array($current_class, $allowed_to_add_after)){
586           return($key_id);
587         } 
588       } 
589     }else{
590   
591       $test = $this->pap;
592       if($key_id == 0) {
593         return($key_id);
594       }
595       $key_id --;
596       while($key_id >=0 ){
597         $current_class  = get_class($test[$key_id]);
598         if(in_array($current_class, $allowed_to_add_before)){
599           return($key_id);
600         } 
601         $key_id --;
602       }
603       return(0);
604     }
605   }
608   /* Need to be reviewed */
609   function get_sieve_script()
610   {
611     $tmp ="";
612     if(count($this->pap)){
613       $buffer = "";    
614       foreach($this->pap as $part)  {
615         if(get_class($part) == "sieve_block_end"){
616           $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
617         }
618         $tmp2 = $part->get_sieve_script_part();
620         if(get_class($part) == "sieve_reject"){
621           $tmp.=$tmp2;
622         }else{
624           $tmp3 = split("\n",$tmp2);
625           foreach($tmp3 as $str){
626             $str2 = trim($str);
627             if(empty($str2)) continue;
628             $tmp.= $buffer.$str."\n";
629           }
630         }
631         if(get_class($part) == "sieve_block_start"){
632           $buffer .= SIEVE_INDENT_TAB;
633         }
634       }
635     }
636     if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
637       $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
638     }
639     return($tmp);
640   }
642   function Add_Element()
643   {
644     $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
645     $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
646   }
648   function check()
649   {
650                 $msgs = array();
651                 foreach($this->pap as $obj){
653                         $o_msgs = $obj->check();
654                         foreach($o_msgs as $o_msg){
655                                 $msgs[] = $o_msg;
656                         }
657                 }
658                 return($msgs);
659   }
663 /* Create valid sieve string/string-list 
664  *  out of a given array
665  */
666 function sieve_create_strings($data)
668   $ret = "";
669   if(is_array($data)){
670     if(count($data) == 1){
671       $ret = "\"";
672       foreach($data as $dat){
673         $ret .=$dat;
674       }
675       $ret.="\"";
676     }else{
677       foreach($data as $dat){
678         $ret.= "\"";
679         $ret.=$dat;
680         $ret.="\", ";
681       }
682       $ret = preg_replace("/,$/","",trim($ret));
683       $ret = "[".$ret."]";
684     }
685   }else{
687     $Multiline = preg_match("/\n/",$data);
688     $data = preg_replace("/\r/","",$data);;
690     if($Multiline){
691       $ret = "text: \r\n".$data."\r\n.\r\n";
692     }else{
693       $ret = "\"".$data."\"";
694     }
695   }
696   $ret = preg_replace("/\"\"/","\"",$ret);
697   $ret = preg_replace("/\n/","\r\n",$ret);
698   
699   return($ret);
702 /* This checks if there is a string at the current position 
703  *  in the token array. 
704  * If there is a string list at the current position,
705  *  this function will return a complete list of all
706  *  strings used in this list.
707  * It also returns an offset of the last token position 
708  */
709 function sieve_get_strings($data,$id)
711   $ret = array();
712   if($data[$id]['class'] == "left-bracket"){
713     while($data[$id]['class']  != "right-bracket" && $id < count($data)){
714       
715       if($data[$id]['class'] == "quoted-string"){
716         $ret[] = $data[$id]['text'];
717       }
718       $id ++;
719     }
720   }elseif($data[$id]['class'] == "quoted-string"){
721     $ret[] = $data[$id]['text'];
722   }elseif($data[$id]['class'] == "number"){
723     $ret[] = $data[$id]['text'];
724   }
725   return(array("OFFSET" => $id, "STRINGS" => $ret));
728 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
729 ?>