Code

Added some templates && functionality
[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   function execute()
25   {
26     foreach($_POST as $name => $value){
27       if(preg_match("/Add_Test_Object_/",$name)) {
28         $name = preg_replace("/Add_Test_Object_/","",$name);
29         $name = preg_replace("/_(x|y)$/","",$name);
30     
31         $test_types_to_add = array(
32           "address" =>_("Address"),
33           "header"  =>_("Header"),
34           "envelope"=>_("Envelope"),
35           "size"    =>_("Size"),
36           "exists"  =>_("Exists"),
37           "allof"   =>_("All of"),
38           "anyof"   =>_("Any of"),
39           "true"    =>_("True"),
40           "false"   =>_("False"));
41  
42         $smarty = get_smarty();
43         $smarty->assign("ID",$name); 
44         $smarty->assign("test_types_to_add",$test_types_to_add); 
45         $ret = $smarty->fetch(get_template_path("templates/select_test_type.tpl",TRUE,dirname(__FILE__)));
46         return($ret);
47       }
48     }
51     return($this->dump());
52   }
54   /* Create a html interface for the current sieve filter 
55    */
56   function dump()
57   {
58     error_reporting(E_ALL);
60     /**************
61      * Handle new elements 
62      **************/
64     if(isset($_POST['select_new_element_type_cancel'])){
65       $this->add_new = FALSE;
66     }
68     if($this->add_new){
69   
70       $element_types= array(
71           "sieve_keep"      => _("Keep"),
72           "sieve_comment"   => _("Comment"),
73           "sieve_fileinto"  => _("File into"),
74           "sieve_keep"      => _("Keep"),
75           "sieve_discard"   => _("Discard"),
76           "sieve_redirect"  => _("Redirect"),
77           "sieve_reject"    => _("Reject"),
78           "sieve_require"   => _("Require"),
79           "sieve_stop"      => _("Stop"),
80           "sieve_vacation"  => _("Vacation message"),
81           "sieve_if"        => _("If"));
84       /* Element selected */
85       if(isset($_POST['element_type']) && isset($element_types[$_POST['element_type']])){
86         $this->add_element_type = $_POST['element_type'];
87       }
89       /* Create new element and add it at the selected position */
90       if(isset($_POST['select_new_element_type'])){
92         $ele[] = new $this->add_element_type(NULL, preg_replace("/[^0-9]/","",microtime()));
93         if($this->add_element_type == "sieve_if"){
94           $ele[] = new sieve_block_start(NULL,preg_replace("/[^0-9]/","",microtime()));
95           $ele[] = new sieve_block_end(NULL,preg_replace("/[^0-9]/","",microtime()));
96         }
97         $start = $end = array();
98         $found = false;  
100         /* Add above current element*/
101         if($this->add_type == "top"){
102           foreach($this->pap as $key => $obj){
103             if($obj->object_id == $this->add_new_id){
104               $found = true;
105             }
106             if(!$found){
107               $start[] = $obj;
108             }else{
109               $end[] = $obj;
110             }
111           }    
112         }else{
113         /* Add below current element */
114           foreach($this->pap as $key => $obj){
115             if(!$found){
116               $start[] = $obj;
117             }else{
118               $end[] = $obj;
119             }
120             if($obj->object_id == $this->add_new_id){
121               $found = true;
122             }
123           }    
124         }
126         /* Only add, if current element could be located */
127         if($found){
128           $new = array();
129           foreach($start as $obj){
130             $new[] = $obj;
131           }      
132           foreach($ele as $el){
133             $new[] = $el;
134           }
135           foreach($end as $obj){
136             $new[] = $obj;
137           }
138           $this->pap = $new;
139           $this->add_new = FALSE;
140         }else{
141           print_red(_("Something went wrong while adding a new entry."));
142         }
143       }
145     }
147     /* Only display select dialog if it is necessary */
148     if($this->add_new){  
149       $smarty = get_smarty();
150       $smarty->assign("element_types",$element_types );
151       $smarty->assign("element_type",$this->add_element_type);
152       $str = $smarty->fetch(get_template_path("templates/add_element.tpl",TRUE,dirname(__FILE__)));
153       return($str);
154     }
156     /* Only parse the tokens once */
157     if(!count($this->pap)){
158       $this->dump_ = "";
159       $this->mode_stack = array();
160       $this->pap = array();
161       $this->doDump_(0, '', true);
162     }
164     /* Create html results */
165     $smarty = get_smarty();
167     $this -> dump_ = "";
168     foreach($this->pap as $key => $object){
169       if(is_object($object)){
170         $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); 
171       }
172     }
173     
174     /* Create html results */
175     $smarty = get_smarty();
176     $smarty->assign("Contents",$this->dump_);
177     $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
178     return ($ret);
179   }
182   /* This function walks through the object tree generated by the "Parse" class.
183    * All Commands will be resolved and grouped. So the Commands and their 
184    *  parameter are combined. Like "IF" and ":comparator"...
185    */  
186   function doDump_($node_id, $prefix, $last,$num = 1)
187   {
188     /* Indicates that current comman will only be valid for a single line. 
189      *  this command type will be removed from mode_stack after displaying it.
190      */
191     $rewoke_last = FALSE;
193     /* Get node */ 
194     $node = $this->nodes_[$node_id];
196     /* This closes the last mode */
197     if($node['class'] == "block-start"){
198       $tmp = array_pop($this->mode_stack);
199       $this->handle_elements($tmp,$node_id);
200       $this->handle_elements(array("TYPE" => "block_start"),$node_id);
201     }
203     /* This closes the last mode */
204     if($node['class'] == "block-end"){
205       $tmp = array_pop($this->mode_stack);
206       $this->handle_elements($tmp,$node_id);
207       $this->handle_elements(array("TYPE" => "block_end"),$node_id);
208     }
210     /* Semicolon indicates a new command */
211     if($node['class'] == "semicolon"){
212       $tmp =array_pop($this->mode_stack);
213       $this->handle_elements($tmp,$node_id);
214     }
216     /* Handle comments */
217     if($node['class'] == "comment"){
218       $this->mode_stack[] = array("TYPE" => $node['class']);
219       $rewoke_last = TRUE;
220     }
222     /* Handle identifiers */
223     $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
224     if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
225       $this->mode_stack[] = array("TYPE" => $node['text']); 
226     }
228     /* Add current node to current command stack */
229     end($this->mode_stack);
230     $key = key($this->mode_stack);
231     $this->mode_stack[$key]['ELEMENTS'][] = $node;
233     /* Remove last mode from mode stack, cause it was only valid for a single line */
234     if($rewoke_last){
235       $tmp =array_pop($this->mode_stack);
236       $this->handle_elements($tmp,$node_id);
237     }
239     /* If this is a sub element, just call this for all childs */       
240     if(isset($this->childs_[$node_id])){
241       $childs = $this->childs_[$node_id];
242       for ($i=0; $i<count($childs); ++$i)
243       {
244         $c_last = false;
245         if ($i+1 == count($childs))
246         {
247           $c_last = true;
248         }
249         $this->doDump_($childs[$i], "", $num);
250       }
251     }
252   }
255   /* Create a class for each resolved object.
256    * And append this class to a list of objects.
257    */
258   function handle_elements($data,$id)
259   {
260     if(!isset($data['TYPE'])){
261       return;
262     }
263     $type = $data['TYPE'];
264     
265     $class_name= "sieve_".$type ;
266     if(class_exists($class_name)){
267       $this->pap[] = new $class_name($data,$id);
268     }else{
269       echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
270     }
271   }
273   function save_object()
274   {
275     reset($this->pap);
276     foreach($this->pap as $key => $obj){
278       if(in_array(get_class($obj),array("sieve_if",
279                                         "sieve_elsif",
280                                         "sieve_vacation",
281                                         "sieve_comment",
282                                         "sieve_reject",
283                                         "sieve_fileinto",
284                                         "sieve_require",
285                                         "sieve_redirect"))){
288         if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
289           $this->pap[$key]->save_object();
290         }
291       }
293       $once = TRUE;
294       foreach($_POST as $name => $value){
296         if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
297           $once = FALSE;
298           $this->remove_object($key);
299         }
300         if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
301           $this->move_up_down($key,"up");
302           $once = FALSE;
303         }
304         if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
305           $this->move_up_down($key,"down");
306           $once = FALSE;
307         }
308         if(isset($obj->object_id) && preg_match("/^Add_Object_Top_".$obj->object_id."_/",$name) && $once){
309           $this->add_new_object($obj->object_id,"top");
310           $once = FALSE;
311         }
312         if(isset($obj->object_id) && preg_match("/^Add_Object_Bottom_".$obj->object_id."_/",$name) && $once){
313           $this->add_new_object($obj->object_id,"bottom");
314           $once = FALSE;
315         }
316       }
317     }
318   }
321   /* Add a new object at the given position */
322   function add_new_object($id,$top_bottom = "bottom")
323   {
324     $this->add_new    = TRUE;
325     $this->add_new_id = $id;
326     $this->add_type   = $top_bottom;
327   }
330   /* Remove the object at the given position */
331   function remove_object($key_id)
332   {
333     $class = get_class($this->pap[$key_id]);
334     if(in_array($class,array("sieve_if"))){
335       $block_start= $key_id;
336       $block_end  = $this->get_block_end($key_id);
337       for($i = $block_start ; $i <= $block_end ; $i ++ ){
338         unset($this->pap[$i]);
339       }
340     }else{
341       unset($this->pap[$key_id]);
342     }
343     $tmp = array();
344     foreach($this->pap as $element){
345       $tmp[] = $element;
346     }
347     $this->pap = $tmp;
348   }
351   /* This function moves a given element to another position.
352    * Single elements like "keep;" will simply be moved one posisition down/up.
353    * Multiple elements like if-elsif-else will be moved as block. 
354    * 
355    *  $key_id     specified the element that should be moved.
356    *  $direction  specifies to move elements "up" or "down"
357    */
358   function move_up_down($key_id,$direction = "down")
359   {
360      
361     /* Get the current element to decide what to move. */ 
362     $e_class = get_class($this->pap[$key_id]);
363       
364     if(in_array($e_class,array("sieve_if"))){
365       $block_start= $key_id;
366       $block_end  = $this->get_block_end($key_id);
368       /* Depending on the direction move up down */
369       if($direction == "down"){
370         $next_free  = $this->_get_next_free_move_slot($block_end,$direction); 
371       }else{
372         $next_free  = $this->_get_next_free_move_slot($block_start,$direction); 
373       }
375       /* Move the given block */ 
376       $this->move_multiple_elements($block_start,$block_end,$next_free);
377     }
379     if(in_array($e_class,array( "sieve_stop",
380                                 "sieve_keep",
381                                 "sieve_require",
382                                 "sieve_comment",
383                                 "sieve_stop",   
384                                 "sieve_reject", 
385                                 "sieve_fileinto",
386                                 "sieve_redirect", 
387                                 "sieve_discard"))){
388       $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
389     }
390   }
392   
393   /* Move the given block to position */
394   function move_multiple_elements($start,$end,$to)
395   {
396     /* Use class names for testing */
397     $data = $this->pap;
399     /* Get block to move */
400     $block_to_move = array_slice($data,$start, ($end - $start +1));
402     /* We want do move this block up */
403     if($end > $to){
404       
405       /* Get start block */
406       $start_block = array_slice($data,0,$to);
408       /* Get Get all elements between the block to move 
409        *  and next free position 
410        */
411       $block_to_free = array_slice($data,$to ,$start - $to );  
412       $block_to_end = array_slice($data,$end+1);
413       $new = array();
414       foreach($start_block as $block){
415         $new[] = $block;
416       }
417       foreach($block_to_move as $block){
418         $new[] = $block;
419       }
420       foreach($block_to_free as $block){
421         $new[] = $block;
422       }
423       foreach($block_to_end as $block){
424         $new[] = $block;
425       }
426       $old = $this->pap;
427       $this->pap = $new;
428     }
429     
431     /* We want to move this block down. */
432     if($to > $end){
434       /* Get start block */
435       $start_block = array_slice($data,0,$start);
437       /* Get Get all elements between the block to move 
438        *  and next free position 
439        */
440       $block_to_free = array_slice($data,$end +1,($to - $end  ));  
442       /* Get the rest 
443        */
444       $block_to_end = array_slice($data,$to+1);
446       $new = array();
447       foreach($start_block as $block){
448         $new[] = $block;
449       }
450       foreach($block_to_free as $block){
451         $new[] = $block;
452       }
453       foreach($block_to_move as $block){
454         $new[] = $block;
455       }
456       foreach($block_to_end as $block){
457         $new[] = $block;
458       }
459       $old = $this->pap;
460       $this->pap = $new;
461     }
462   }  
464   
465   /* This function returns the id of the element 
466    *  where the current block ends  
467    */
468   function get_block_end($start)
469   {
470     /* Only execute if this is a really a block element. 
471      * Block elements is only sieve_if
472      */
473     if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
475       $class      = get_class($this->pap[$start]);
476       $next_class = get_class($this->pap[$start+1]);
477       $block_depth = 0;
479       $end = FALSE;
481       while(!$end && $start < count($this->pap)){
482  
483         if($class == "sieve_block_start"){
484           $block_depth ++;
485         }
487         if($class == "sieve_block_end"){
488           $block_depth --;
489         }
491         if( $block_depth == 0 && 
492             $class == "sieve_block_end" && 
493             !in_array($next_class,array("sieve_else","sieve_elsif"))){
494           $end = TRUE;
495           $start --;
496         }
497         $start ++;       
498         $class      = get_class($this->pap[$start]);
499         $next_class = get_class($this->pap[$start+1]);
500       }
501     }
502     return($start);
503   }
506   /* This function moves the single element at 
507    *  position $from to position $to.
508    */
509   function move_single_element($from,$to)
510   {
511     if($from == $to) {
512       return;
513     }
515     $ret = array();
516     $tmp = $this->pap;
518     $begin = array();
519     $middle = array();
520     $end = array();
521     $element = $this->pap[$from];
523     if($from > $to ){
525       /* Get all element in fron to element to move */    
526       if($from  != 0){
527         $begin = array_slice($tmp,0,$to);
528       }
530       /* Get all elements between */
531       $middle = array_slice($tmp,$to , ($from - ($to) ));  
532     
533       /* Get the rest */ 
534       $end  = array_slice($tmp,$from+1);
535  
536       foreach($begin as $data){
537         $ret[] = $data;
538       }
539       $ret[] = $element;
540       foreach($middle as $data){
541         $ret[] = $data;
542       }
543       foreach($end as $data){
544         $ret[] = $data;
545       }
546       $this->pap = $ret;
547     }
548     if($from < $to ){
550       /* Get all element in fron to element to move */    
551       if($from  != 0){
552         $begin = array_slice($tmp,0,$from);
553       }
555       /* Get all elements between */
556       $middle = array_slice($tmp,$from+1 , ($to - ($from)));  
557     
558       /* Get the rest */ 
559       $end  = array_slice($tmp,$to+1);
560  
561       foreach($begin as $data){
562         $ret[] = $data;
563       }
564       foreach($middle as $data){
565         $ret[] = $data;
566       }
567       $ret[] = $element;
568       foreach($end as $data){
569         $ret[] = $data;
570       }
571       $this->pap = $ret;
572     }
573   }
576   /* Returns the next free position where we 
577    *  can add a new sinle element 
578    *    $key_id     = Current position
579    *    $direction  = Forward or backward.
580    */
581   function _get_next_free_move_slot($key_id,$direction)
582   {
583     $last_class = "";
584     $current_class ="";
585     $next_class = "";
587     /* After this elements we can add new elements 
588      *  without having any trouble.
589      */
590     $allowed_to_add_after = array("sieve_keep",
591                                   "sieve_require", 
592                                   "sieve_stop", 
593                                   "sieve_reject", 
594                                   "sieve_fileinto", 
595                                   "sieve_redirect", 
596                                   "sieve_discard",
597                                   "sieve_comment",
598                                   "sieve_block_start"
599                                  );
601     /* Before this elements we can add new elements 
602      *  without having any trouble.
603      */
604     $allowed_to_add_before = array("sieve_keep",
605                                   "sieve_require", 
606                                   "sieve_stop", 
607                                   "sieve_reject", 
608                                   "sieve_fileinto", 
609                                   "sieve_comment",
610                                   "sieve_redirect", 
611                                   "sieve_discard",
612                                   "sieve_if", 
613                                   "sieve_block_end"
614                                  );
616     if($direction == "down"){
617     
618       $test = $this->pap;
619       while($key_id < count($test)){
620         if(($key_id+1) == count($test)) {
621           return($key_id);
622         }
623         $key_id ++;
624         $current_class  = get_class($test[$key_id]);
625         if(in_array($current_class, $allowed_to_add_after)){
626           return($key_id);
627         } 
628       } 
629     }else{
630   
631       $test = $this->pap;
632       if($key_id == 0) {
633         return($key_id);
634       }
635       $key_id --;
636       while($key_id >=0 ){
637         $current_class  = get_class($test[$key_id]);
638         if(in_array($current_class, $allowed_to_add_before)){
639           return($key_id);
640         } 
641         $key_id --;
642       }
643       return(0);
644     }
645   }
648   /* Need to be reviewed */
649   function get_sieve_script()
650   {
651     $tmp ="";
652     if(count($this->pap)){
653       $buffer = "";    
654       foreach($this->pap as $part)  {
655         if(get_class($part) == "sieve_block_end"){
656           $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
657         }
658         $tmp2 = $part->get_sieve_script_part();
660         if(get_class($part) == "sieve_reject"){
661           $tmp.=$tmp2;
662         }else{
664           $tmp3 = split("\n",$tmp2);
665           foreach($tmp3 as $str){
666             $str2 = trim($str);
667             if(empty($str2)) continue;
668             $tmp.= $buffer.$str."\n";
669           }
670         }
671         if(get_class($part) == "sieve_block_start"){
672           $buffer .= SIEVE_INDENT_TAB;
673         }
674       }
675     }
676     if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
677       $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
678     }
679     return($tmp);
680   }
682   function Add_Element()
683   {
684     $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
685     $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
686   }
688   function check()
689   {
690                 $msgs = array();
692     /* Some logical checks. 
693      *  like :  only sieve_comment can appear before require.
694      */
695     
696     /* Ensure that there are no command before require 
697      *  - Get id of last require tag
698      *  - Collect object types in from of this tag. 
699      *  - Check if there are tags collected that are not allowed 
700      */
701     $last_found_at = -1; 
702     $objs = array();
703     foreach($this->pap as $key => $obj){
704       if(get_class($obj) == "sieve_require"){
705         $last_found_at = $key;
706       }
707     }
708     foreach($this->pap as $key => $obj){
709       if($key == $last_found_at) break;  
710       if(!in_array(get_class($obj),array("sieve_comment","sieve_require"))){
711         $objs[] = get_class($obj);
712       }
713     }
714     if(count($objs) && $last_found_at != -1){
715       $str = _("Require must be the first command in the script.");  
716       $msgs[] = $str;
717       print_red($str);;
718     }
719     
720                 foreach($this->pap as $obj){
721                         $o_msgs = $obj->check();
722                         foreach($o_msgs as $o_msg){
723                                 $msgs[] = $o_msg;
724                         }
725                 }
726                 return($msgs);
727   }
731 /* Create valid sieve string/string-list 
732  *  out of a given array
733  */
734 function sieve_create_strings($data)
736   $ret = "";
737   if(is_array($data)){
738     if(count($data) == 1){
739       $ret = "\"";
740       foreach($data as $dat){
741         $ret .=$dat;
742       }
743       $ret.="\"";
744     }else{
745       foreach($data as $dat){
746         $ret.= "\"";
747         $ret.=$dat;
748         $ret.="\", ";
749       }
750       $ret = preg_replace("/,$/","",trim($ret));
751       $ret = "[".$ret."]";
752     }
753   }else{
755     $Multiline = preg_match("/\n/",$data);
756     $data = preg_replace("/\r/","",$data);;
758     if($Multiline){
759       $ret = "text: \r\n".$data."\r\n.\r\n";
760     }else{
761       $ret = "\"".$data."\"";
762     }
763   }
764   $ret = preg_replace("/\"\"/","\"",$ret);
765   $ret = preg_replace("/\n/","\r\n",$ret);
766   
767   return($ret);
770 /* This checks if there is a string at the current position 
771  *  in the token array. 
772  * If there is a string list at the current position,
773  *  this function will return a complete list of all
774  *  strings used in this list.
775  * It also returns an offset of the last token position 
776  */
777 function sieve_get_strings($data,$id)
779   $ret = array();
780   if($data[$id]['class'] == "left-bracket"){
781     while($data[$id]['class']  != "right-bracket" && $id < count($data)){
782       
783       if($data[$id]['class'] == "quoted-string"){
784         $ret[] = $data[$id]['text'];
785       }
786       $id ++;
787     }
788   }elseif($data[$id]['class'] == "quoted-string"){
789     $ret[] = $data[$id]['text'];
790   }elseif($data[$id]['class'] == "number"){
791     $ret[] = $data[$id]['text'];
792   }
793   return(array("OFFSET" => $id, "STRINGS" => $ret));
796 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
797 ?>