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);
29       $element_types= array(
30           "sieve_keep"      => _("Keep"),
31           "sieve_comment"   => _("Comment"),
32           "sieve_fileinto"  => _("File into"),
33           "sieve_keep"      => _("Keep"),
34           "sieve_discard"   => _("Discard"),
35           "sieve_redirect"  => _("Redirect"),
36           "sieve_reject"    => _("Reject"),
37           "sieve_require"   => _("Require"),
38           "sieve_stop"      => _("Stop"),
39           "sieve_vacation"  => _("Vacation message"),
40           "sieve_if"        => _("If"));
43     if($this->add_new && isset($_POST['element_type']) && isset($element_types[$_POST['element_type']])){
44       $this->add_element_type = $_POST['element_type'];
45     }
48     if($this->add_new && isset($_POST['select_new_element_type'])){
49      
50       $ele = new $this->add_element_type(NULL, preg_replace("/[^0-9]/","",microtime()));
51       $start = $end = array();
52       $found = false;  
53  
54       if($this->add_type == "top"){
55         foreach($this->pap as $key => $obj){
56           if($obj->object_id == $this->add_new_id){
57             $found = true;
58           }
59           if(!$found){
60             $start[] = $obj;
61           }else{
62             $end[] = $obj;
63           }
64         }    
65       }else{
66         foreach($this->pap as $key => $obj){
67           if(!$found){
68             $start[] = $obj;
69           }else{
70             $end[] = $obj;
71           }
72           if($obj->object_id == $this->add_new_id){
73             $found = true;
74           }
75         }    
76       }
78       if($found){
79         $new = array();
80         foreach($start as $obj){
81           $new[] = $obj;
82         }      
83         $new[] = $ele;
84         foreach($end as $obj){
85           $new[] = $obj;
86         }
87         $this->pap = $new;
88         $this->add_new = FALSE;
89       }else{
90         print_red(_("Something went wrong while adding a new entry."));
91       }
92     }
94     if($this->add_new){
96       $smarty = get_smarty();
97       $smarty->assign("element_types",$element_types );
98       $smarty->assign("element_type",$this->add_element_type);
100       $str = $smarty->fetch(get_template_path("templates/add_element.tpl",TRUE,dirname(__FILE__)));
101       return($str);
102     }
104     /* Only parse the tokens once */
105     if(!count($this->pap)){
106       $this->dump_ = "";
107       $this->mode_stack = array();
108       $this->pap = array();
109       $this->doDump_(0, '', true);
110     }
112     /* Create html results */
113     $smarty = get_smarty();
115     $this -> dump_ = "";
116     foreach($this->pap as $key => $object){
117       if(is_object($object)){
118         $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); 
119       }
120     }
121     
122     /* Create html results */
123     $smarty = get_smarty();
124     $smarty->assign("Contents",$this->dump_);
125     $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
126     return ($ret);
127   }
130   /* This function walks through the object tree generated by the "Parse" class.
131    * All Commands will be resolved and grouped. So the Commands and their 
132    *  parameter are combined. Like "IF" and ":comparator"...
133    */  
134   function doDump_($node_id, $prefix, $last,$num = 1)
135   {
136     /* Indicates that current comman will only be valid for a single line. 
137      *  this command type will be removed from mode_stack after displaying it.
138      */
139     $rewoke_last = FALSE;
141     /* Get node */ 
142     $node = $this->nodes_[$node_id];
144     /* This closes the last mode */
145     if($node['class'] == "block-start"){
146       $tmp = array_pop($this->mode_stack);
147       $this->handle_elements($tmp,$node_id);
148       $this->handle_elements(array("TYPE" => "block_start"),$node_id);
149     }
151     /* This closes the last mode */
152     if($node['class'] == "block-end"){
153       $tmp = array_pop($this->mode_stack);
154       $this->handle_elements($tmp,$node_id);
155       $this->handle_elements(array("TYPE" => "block_end"),$node_id);
156     }
158     /* Semicolon indicates a new command */
159     if($node['class'] == "semicolon"){
160       $tmp =array_pop($this->mode_stack);
161       $this->handle_elements($tmp,$node_id);
162     }
164     /* Handle comments */
165     if($node['class'] == "comment"){
166       $this->mode_stack[] = array("TYPE" => $node['class']);
167       $rewoke_last = TRUE;
168     }
170     /* Handle identifiers */
171     $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
172     if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
173       $this->mode_stack[] = array("TYPE" => $node['text']); 
174     }
176     /* Add current node to current command stack */
177     end($this->mode_stack);
178     $key = key($this->mode_stack);
179     $this->mode_stack[$key]['ELEMENTS'][] = $node;
181     /* Remove last mode from mode stack, cause it was only valid for a single line */
182     if($rewoke_last){
183       $tmp =array_pop($this->mode_stack);
184       $this->handle_elements($tmp,$node_id);
185     }
187     /* If this is a sub element, just call this for all childs */       
188     if(isset($this->childs_[$node_id])){
189       $childs = $this->childs_[$node_id];
190       for ($i=0; $i<count($childs); ++$i)
191       {
192         $c_last = false;
193         if ($i+1 == count($childs))
194         {
195           $c_last = true;
196         }
197         $this->doDump_($childs[$i], "", $num);
198       }
199     }
200   }
203   /* Create a class for each resolved object.
204    * And append this class to a list of objects.
205    */
206   function handle_elements($data,$id)
207   {
208     if(!isset($data['TYPE'])){
209       return;
210     }
211     $type = $data['TYPE'];
212     
213     $class_name= "sieve_".$type ;
214     if(class_exists($class_name)){
215       $this->pap[] = new $class_name($data,$id);
216     }else{
217       echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
218     }
219   }
221   function save_object()
222   {
223     reset($this->pap);
224     foreach($this->pap as $key => $obj){
226       if(in_array(get_class($obj),array("sieve_if",
227                                         "sieve_elsif",
228                                         "sieve_vacation",
229                                         "sieve_comment",
230                                         "sieve_reject",
231                                         "sieve_fileinto",
232                                         "sieve_require",
233                                         "sieve_redirect"))){
236         if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
237           $this->pap[$key]->save_object();
238         }
239       }
241       $once = TRUE;
242       foreach($_POST as $name => $value){
244         if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
245           $once = FALSE;
246           $this->remove_object($key);
247         }
248         if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
249           $this->move_up_down($key,"up");
250           $once = FALSE;
251         }
252         if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
253           $this->move_up_down($key,"down");
254           $once = FALSE;
255         }
256         if(isset($obj->object_id) && preg_match("/^Add_Object_Top_".$obj->object_id."_/",$name) && $once){
257           $this->add_new_object($obj->object_id,"top");
258           $once = FALSE;
259         }
260         if(isset($obj->object_id) && preg_match("/^Add_Object_Bottom_".$obj->object_id."_/",$name) && $once){
261           $this->add_new_object($obj->object_id,"bottom");
262           $once = FALSE;
263         }
264       }
265     }
266   }
269   /* Add a new object at the given position */
270   function add_new_object($id,$top_bottom = "bottom")
271   {
272     $this->add_new    = TRUE;
273     $this->add_new_id = $id;
274     $this->add_type   = $top_bottom;
275   }
278   /* Remove the object at the given position */
279   function remove_object($key_id)
280   {
281     $class = get_class($this->pap[$key_id]);
282     if(in_array($class,array("sieve_if"))){
283       $block_start= $key_id;
284       $block_end  = $this->get_block_end($key_id);
285       for($i = $block_start ; $i <= $block_end ; $i ++ ){
286         unset($this->pap[$i]);
287       }
288     }else{
289       unset($this->pap[$key_id]);
290     }
291     $tmp = array();
292     foreach($this->pap as $element){
293       $tmp[] = $element;
294     }
295     $this->pap = $tmp;
296   }
299   /* This function moves a given element to another position.
300    * Single elements like "keep;" will simply be moved one posisition down/up.
301    * Multiple elements like if-elsif-else will be moved as block. 
302    * 
303    *  $key_id     specified the element that should be moved.
304    *  $direction  specifies to move elements "up" or "down"
305    */
306   function move_up_down($key_id,$direction = "down")
307   {
308      
309     /* Get the current element to decide what to move. */ 
310     $e_class = get_class($this->pap[$key_id]);
311       
312     if(in_array($e_class,array("sieve_if"))){
313       $block_start= $key_id;
314       $block_end  = $this->get_block_end($key_id);
316       /* Depending on the direction move up down */
317       if($direction == "down"){
318         $next_free  = $this->_get_next_free_move_slot($block_end,$direction); 
319       }else{
320         $next_free  = $this->_get_next_free_move_slot($block_start,$direction); 
321       }
323       /* Move the given block */ 
324       $this->move_multiple_elements($block_start,$block_end,$next_free);
325     }
327     if(in_array($e_class,array( "sieve_stop",
328                                 "sieve_keep",
329                                 "sieve_require", 
330                                 "sieve_stop",   
331                                 "sieve_reject", 
332                                 "sieve_fileinto",
333                                 "sieve_redirect", 
334                                 "sieve_discard"))){
335       $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
336     }
337   }
339   
340   /* Move the given block to position */
341   function move_multiple_elements($start,$end,$to)
342   {
343     /* Use class names for testing */
344     $data = $this->pap;
346     /* Get block to move */
347     $block_to_move = array_slice($data,$start, ($end - $start +1));
349     /* We want do move this block up */
350     if($end > $to){
351       
352       /* Get start block */
353       $start_block = array_slice($data,0,$to);
355       /* Get Get all elements between the block to move 
356        *  and next free position 
357        */
358       $block_to_free = array_slice($data,$to ,$start - $to );  
359       $block_to_end = array_slice($data,$end+1);
360       $new = array();
361       foreach($start_block as $block){
362         $new[] = $block;
363       }
364       foreach($block_to_move as $block){
365         $new[] = $block;
366       }
367       foreach($block_to_free as $block){
368         $new[] = $block;
369       }
370       foreach($block_to_end as $block){
371         $new[] = $block;
372       }
373       $old = $this->pap;
374       $this->pap = $new;
375     }
376     
378     /* We want to move this block down. */
379     if($to > $end){
381       /* Get start block */
382       $start_block = array_slice($data,0,$start);
384       /* Get Get all elements between the block to move 
385        *  and next free position 
386        */
387       $block_to_free = array_slice($data,$end +1,($to - $end  ));  
389       /* Get the rest 
390        */
391       $block_to_end = array_slice($data,$to+1);
393       $new = array();
394       foreach($start_block as $block){
395         $new[] = $block;
396       }
397       foreach($block_to_free as $block){
398         $new[] = $block;
399       }
400       foreach($block_to_move as $block){
401         $new[] = $block;
402       }
403       foreach($block_to_end as $block){
404         $new[] = $block;
405       }
406       $old = $this->pap;
407       $this->pap = $new;
408     }
409   }  
411   
412   /* This function returns the id of the element 
413    *  where the current block ends  
414    */
415   function get_block_end($start)
416   {
417     /* Only execute if this is a really a block element. 
418      * Block elements is only sieve_if
419      */
420     if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
422       $class      = get_class($this->pap[$start]);
423       $next_class = get_class($this->pap[$start+1]);
424       $block_depth = 0;
426       $end = FALSE;
428       while(!$end && $start < count($this->pap)){
429  
430         if($class == "sieve_block_start"){
431           $block_depth ++;
432         }
434         if($class == "sieve_block_end"){
435           $block_depth --;
436         }
438         if( $block_depth == 0 && 
439             $class == "sieve_block_end" && 
440             !in_array($next_class,array("sieve_else","sieve_elsif"))){
441           $end = TRUE;
442           $start --;
443         }
444         $start ++;       
445         $class      = get_class($this->pap[$start]);
446         $next_class = get_class($this->pap[$start+1]);
447       }
448     }
449     return($start);
450   }
453   /* This function moves the single element at 
454    *  position $from to position $to.
455    */
456   function move_single_element($from,$to)
457   {
458     if($from == $to) {
459       return;
460     }
462     $ret = array();
463     $tmp = $this->pap;
465     $begin = array();
466     $middle = array();
467     $end = array();
468     $element = $this->pap[$from];
470     if($from > $to ){
472       /* Get all element in fron to element to move */    
473       if($from  != 0){
474         $begin = array_slice($tmp,0,$to);
475       }
477       /* Get all elements between */
478       $middle = array_slice($tmp,$to , ($from - ($to) ));  
479     
480       /* Get the rest */ 
481       $end  = array_slice($tmp,$from+1);
482  
483       foreach($begin as $data){
484         $ret[] = $data;
485       }
486       $ret[] = $element;
487       foreach($middle as $data){
488         $ret[] = $data;
489       }
490       foreach($end as $data){
491         $ret[] = $data;
492       }
493       $this->pap = $ret;
494     }
495     if($from < $to ){
497       /* Get all element in fron to element to move */    
498       if($from  != 0){
499         $begin = array_slice($tmp,0,$from);
500       }
502       /* Get all elements between */
503       $middle = array_slice($tmp,$from+1 , ($to - ($from)));  
504     
505       /* Get the rest */ 
506       $end  = array_slice($tmp,$to+1);
507  
508       foreach($begin as $data){
509         $ret[] = $data;
510       }
511       foreach($middle as $data){
512         $ret[] = $data;
513       }
514       $ret[] = $element;
515       foreach($end as $data){
516         $ret[] = $data;
517       }
518       $this->pap = $ret;
519     }
520   }
523   /* Returns the next free position where we 
524    *  can add a new sinle element 
525    *    $key_id     = Current position
526    *    $direction  = Forward or backward.
527    */
528   function _get_next_free_move_slot($key_id,$direction)
529   {
530     $last_class = "";
531     $current_class ="";
532     $next_class = "";
534     /* After this elements we can add new elements 
535      *  without having any trouble.
536      */
537     $allowed_to_add_after = array("sieve_keep",
538                                   "sieve_require", 
539                                   "sieve_stop", 
540                                   "sieve_reject", 
541                                   "sieve_fileinto", 
542                                   "sieve_redirect", 
543                                   "sieve_discard",
544                                   "sieve_comment",
545                                   "sieve_block_start"
546                                  );
548     /* Before this elements we can add new elements 
549      *  without having any trouble.
550      */
551     $allowed_to_add_before = array("sieve_keep",
552                                   "sieve_require", 
553                                   "sieve_stop", 
554                                   "sieve_reject", 
555                                   "sieve_fileinto", 
556                                   "sieve_comment",
557                                   "sieve_redirect", 
558                                   "sieve_discard",
559                                   "sieve_if", 
560                                   "sieve_block_end"
561                                  );
563     if($direction == "down"){
564     
565       $test = $this->pap;
566       while($key_id < count($test)){
567         if(($key_id+1) == count($test)) {
568           return($key_id);
569         }
570         $key_id ++;
571         $current_class  = get_class($test[$key_id]);
572         if(in_array($current_class, $allowed_to_add_after)){
573           return($key_id);
574         } 
575       } 
576     }else{
577   
578       $test = $this->pap;
579       if($key_id == 0) {
580         return($key_id);
581       }
582       $key_id --;
583       while($key_id >=0 ){
584         $current_class  = get_class($test[$key_id]);
585         if(in_array($current_class, $allowed_to_add_before)){
586           return($key_id);
587         } 
588         $key_id --;
589       }
590       return(0);
591     }
592   }
595   /* Need to be reviewed */
596   function get_sieve_script()
597   {
598     $tmp ="";
599     if(count($this->pap)){
600       $buffer = "";    
601       foreach($this->pap as $part)  {
602         if(get_class($part) == "sieve_block_end"){
603           $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
604         }
605         $tmp2 = $part->get_sieve_script_part();
607         if(get_class($part) == "sieve_reject"){
608           $tmp.=$tmp2;
609         }else{
611           $tmp3 = split("\n",$tmp2);
612           foreach($tmp3 as $str){
613             $str2 = trim($str);
614             if(empty($str2)) continue;
615             $tmp.= $buffer.$str."\n";
616           }
617         }
618         if(get_class($part) == "sieve_block_start"){
619           $buffer .= SIEVE_INDENT_TAB;
620         }
621       }
622     }
623     if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
624       $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
625     }
626     return($tmp);
627   }
629   function Add_Element()
630   {
631     $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
632     $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
633   }
635   function check()
636   {
637                 $msgs = array();
638                 foreach($this->pap as $obj){
640                         $o_msgs = $obj->check();
641                         foreach($o_msgs as $o_msg){
642                                 $msgs[] = $o_msg;
643                         }
644                 }
645                 return($msgs);
646   }
650 /* Create valid sieve string/string-list 
651  *  out of a given array
652  */
653 function sieve_create_strings($data)
655   $ret = "";
656   if(is_array($data)){
657     if(count($data) == 1){
658       $ret = "\"";
659       foreach($data as $dat){
660         $ret .=$dat;
661       }
662       $ret.="\"";
663     }else{
664       foreach($data as $dat){
665         $ret.= "\"";
666         $ret.=$dat;
667         $ret.="\", ";
668       }
669       $ret = preg_replace("/,$/","",trim($ret));
670       $ret = "[".$ret."]";
671     }
672   }else{
674     $Multiline = preg_match("/\n/",$data);
675     $data = preg_replace("/\r/","",$data);;
677     if($Multiline){
678       $ret = "text: \r\n".$data."\r\n.\r\n";
679     }else{
680       $ret = "\"".$data."\"";
681     }
682   }
683   $ret = preg_replace("/\"\"/","\"",$ret);
684   $ret = preg_replace("/\n/","\r\n",$ret);
685   
686   return($ret);
689 /* This checks if there is a string at the current position 
690  *  in the token array. 
691  * If there is a string list at the current position,
692  *  this function will return a complete list of all
693  *  strings used in this list.
694  * It also returns an offset of the last token position 
695  */
696 function sieve_get_strings($data,$id)
698   $ret = array();
699   if($data[$id]['class'] == "left-bracket"){
700     while($data[$id]['class']  != "right-bracket" && $id < count($data)){
701       
702       if($data[$id]['class'] == "quoted-string"){
703         $ret[] = $data[$id]['text'];
704       }
705       $id ++;
706     }
707   }elseif($data[$id]['class'] == "quoted-string"){
708     $ret[] = $data[$id]['text'];
709   }elseif($data[$id]['class'] == "number"){
710     $ret[] = $data[$id]['text'];
711   }
712   return(array("OFFSET" => $id, "STRINGS" => $ret));
715 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
716 ?>