Code

Added focus to cn
[gosa.git] / plugins / admin / mimetypes / class_mimetypeGeneric.inc
1 <?php
2 class mimetype extends plugin
3 {
4         /* CLI vars */
5         var $cli_summary                  = "This tab allows you to modify, add or remove mime types in the selected department.";
6         var $cli_description    = "Management dialog to handle mime types";
7         var $ignore_account     = true; // This is always a valid account
8         var $cli_parameters             = array("eins" => "Eins ist toll", "zwei" => "Zwei ist noch besser");
9         var $objectclasses              = array("top","gotoMimeType");
10         var $attributes                   = array("cn","gotoMimeApplication","gotoMimeLeftClickAction","gotoMimeIcon",
11                                                                         "description","gotoMimeFilePattern","gotoMimeEmbeddedApplication",
12                                                                         "gotoMimeGroup");       
14         /* Class vars */
15         var $acl;
16         var $ui;
17         var $cn                                                                               = "";                                     
18         var $gotoMimeLeftClickAction                        = "I";
19         var $gotoMimeLeftClickAction_I              = true;
20         var $gotoMimeLeftClickAction_E              = false;
21         var $gotoMimeLeftClickAction_Q              = false;
22         var $gotoMimeIcon                                                         = "*removed*";
23         var $description                                                          = "";
24         var $gotoMimeFilePattern                                      = array();
25         var $gotoMimeApplication                                      = array();
26         var $gotoMimeEmbeddedApplication            = array();
27         var $gotoMimeGroup                                                      = "";
28         var $iconData                                                               = NULL;     
29         var $base                                                                             = "";
31   /* To prevent errors when using the 'apply' button, we use this variables 
32       to manage array attributes */
33         var $use_gotoMimeIcon                                           = NULL;
34         var $use_gotoMimeFilePattern                        = array();
35         var $use_gotoMimeApplication                        = array();
36         var $use_gotoMimeEmbeddedApplication    = array();
38   /* divLists */
39   var $DivPatterns                                                        = NULL;
40   var $DivApps                                                              = NULL;
41   var $DivEApps                                                             = NULL;
43   /* Mime type release mode */
44   var $isReleaseMimeType                                              = false;
45   var $dialog                                                                       = NULL;
47   /* These vars will be copied too, if you use copy&paste mode */
48   var $CopyPasteVars        = array("use_gotoMimeFilePattern","use_gotoMimeApplication","use_gotoMimeEmbeddedApplication","iconData");
50   /* Select options */
51   var $MimeGroups                             = array("video","audio","system");
53   /* Orig_Dn is used to verify if this object is new or only edited */          
54   var $orig_dn                  = "";
57   function mimetype($config,$dn= NULL)
58   {
59     plugin::plugin ($config, $dn);
61     /* Save original dn */
62     $this->orig_dn = $dn;       
64     /* get gotoMimeLeftClickActions I/E/Q */
65     if(isset($this->gotoMimeLeftClickAction)){
66       $str = $this->gotoMimeLeftClickAction;
67       for($i = 0 ; $i < strlen($str) ; $i ++ ){
68         $varna = "gotoMimeLeftClickAction_". $str[$i];
69         if(isset($this->$varna)){
70           $this->$varna = true;
71         }
72       }
73     }
75     /* If both radio buttons arn't set, set option I */
76     if(!$this->gotoMimeLeftClickAction_I && !$this->gotoMimeLeftClickAction_E){
77       $this->gotoMimeLeftClickAction_I = true;
78     }
80     /* Get list of array attributes */
81     foreach(array("gotoMimeFilePattern") as $attr){
82       $this->$attr = array();
83       if(isset($this->attrs[$attr])){
84         $tmp = array();
85         for($i = 0 ; $i < $this->attrs[$attr]['count'] ; $i ++){
86           $str = $this->attrs[$attr][$i];
87           $tmp[] = $str;
88         }
89         $use_attr = "use_".$attr;
90         $this->$use_attr = $tmp;
91       }
92     }
94     /* Get list of array attributes with priority tag ( Test|32 )*/
95     foreach(array("gotoMimeApplication","gotoMimeEmbeddedApplication") as $attr){
96       $this->$attr = array();
97       if(isset($this->attrs[$attr])){
98         $tmp = array();
99         for($i = 0 ; $i < $this->attrs[$attr]['count'] ; $i ++){
100           $str = $this->attrs[$attr][$i];
101           $tmp2= split("\|",$str);
103           if(count($tmp2) == 2){
104             $name = $tmp2[0];
105             $prio = $tmp2[1];
106             $tmp[$prio] = $name;
107           }
108         }
109         ksort($tmp);
110         $use_attr = "use_".$attr;
111         $this->$use_attr = $tmp;
112       }
113     }
115     /* Check if release Management is enabled */
116     $tmp = search_config($this->config->data,"faiManagement","CLASS");
117     if(!empty($tmp)) {
118       $this->isReleaseMimeType= true;
119     }
121     /* Set base */      
122     if ($this->dn == "new"){
123       if(isset($_SESSION['CurrentMainBase'])){
124         $this->base= $_SESSION['CurrentMainBase'];
125       }else{
126         $ui= get_userinfo();
127         $this->base= dn2base($ui->dn);
128       }
129     } else {
130       $this->base= preg_replace ("/^[^,]+,[^,]+,/", "", $this->dn);
131     }
133     /* Get icon data */
134     if(isset($this->attrs['gotoMimeIcon'])){
135       $ldap = $this->config->get_ldap_link();
136       $this->iconData = $ldap->get_attribute($this->dn,"gotoMimeIcon");
137       $this->saved_attributes['gotoMimeIcon'] = $this->iconData;
138     }
139     if ($this->iconData == ""){
140       $this->set_new_picture("");
141     }
142     $_SESSION['binary']                 = $this->iconData;
143     $_SESSION['binarytype']             = "image/jpeg";
144   }
147   function execute()
148   {
149     $smarty = get_smarty();
151     /* Base select dialog */
152     $once = true;
153     foreach($_POST as $name => $value){
154       if(preg_match("/^chooseBase/",$name) && $once){
155         $once = false;
156         $this->dialog = new baseSelectDialog($this->config,$this);
157         echo $this->base;
158         $this->dialog->setCurrentBase($this->base);
159       }
160     }
162     /* Dialog handling */
163     if(is_object($this->dialog)){
165       /* Must be called before save_object */
166       $this->dialog->save_object();
168       if($this->dialog->isClosed()){
169         $this->dialog = false;
170       }elseif($this->dialog->isSelected()){
171         $this->base = $this->dialog->isSelected();
172         $this->dialog= false;
173       }else{
174         return($this->dialog->execute());
175       }
176     }
178     /* Check Posts */
179     $posts = array(     
180         "/^Pattern_SortUp_/"    => array("Action" => "Pattern_SortUp",  "Func" => "ArrayUp",    "Attr" => "use_gotoMimeFilePattern"),
181         "/^Pattern_SortDown_/"  => array("Action" => "Pattern_SortDown","Func" => "ArrayDown",  "Attr" => "use_gotoMimeFilePattern"),
182         "/^Pattern_Remove_/"    => array("Action" => "Pattern_Remove",  "Func" => "ArrayRemove","Attr" => "use_gotoMimeFilePattern"),
183         "/^Apps_SortUp_/"               => array("Action" => "Apps_SortUp",             "Func" => "ArrayUp",    "Attr" => "use_gotoMimeApplication"),
184         "/^Apps_SortDown_/"     => array("Action" => "Apps_SortDown",   "Func" => "ArrayDown",  "Attr" => "use_gotoMimeApplication"),
185         "/^Apps_Remove_/"               => array("Action" => "Apps_Remove",             "Func" => "ArrayRemove","Attr" => "use_gotoMimeApplication"),
186         "/^EApps_SortUp_/"              => array("Action" => "EApps_SortUp",    "Func" => "ArrayUp",    "Attr" => "use_gotoMimeEmbeddedApplication"),
187         "/^EApps_SortDown_/"    => array("Action" => "EApps_SortDown",  "Func" => "ArrayDown",  "Attr" => "use_gotoMimeEmbeddedApplication"),
188         "/^EApps_Remove_/"              => array("Action" => "EApps_Remove",    "Func" => "ArrayRemove","Attr" => "use_gotoMimeEmbeddedApplication"));          
189     $once = true;
191     /* Walk through posts and try to find some commands for us. */
192     foreach($_POST as $name => $value){
194       /* Walk through possible commands */
195       foreach($posts as $regex => $action){
197         /* Check if there is a command posted */
198         if(preg_match($regex,$name) && $once){
199           $once = false;
201           /* Get action vars */
202           $func                 = $action['Func'];              // Get function name 
203           $attr                 = $action['Attr'];              // Get attribute name
205           /* Get entry id */
206           $s_entry      = preg_replace($regex,"",$name);
207           $s_entry      = preg_replace("/_[xy]$/","",$s_entry); 
209           /* Execute a command with the given attribute and entry 
210              e.g. $this->gotoMimeFilePattern = $this->ArrayUp(3,$this->gotoMimeFilePattern) */
211           if(chkacl($this->acl,$attr) == ""){
212             $this->$attr= $this->$func($s_entry,$this->$attr,true);                                      
213           }
214         }
215       }
216     }
218     /* Set a new icon was requested */
219     if(isset($_POST['update_icon']) && (isset($_FILES['picture_file']['name']))){
220       $this->set_new_picture($_FILES['picture_file']['tmp_name']);              
221     }
223     /* Add gotoMimeFilePattern */
224     if(isset($_POST['AddNewFilePattern']) && isset($_POST['NewFilePattern'])){
225       if(chkacl($this->acl,"gotoMimeFilePattern") == ""){
226         $str = $_POST['NewFilePattern'];
227         if(!empty($str)){
228           $this->use_gotoMimeFilePattern[] = $str;
229         }
230       }
231     }   
233     /* Add gotoMimeFilePattern */
234     if(isset($_POST['AddNewApplication']) && isset($_POST['NewApplication'])){
235       if(chkacl($this->acl,"gotoMimeApplication") == ""){
236         $str = $_POST['NewApplication'];
237         if(!empty($str)){
238           $this->use_gotoMimeApplication[] = $str;
239         }
240       }
241     }   
243     /* Add gotoMimeFilePattern */
244     if(isset($_POST['AddNewEmbeddedApplication']) && isset($_POST['NewEmbeddedApplication'])){
245       if(chkacl($this->acl,"gotoMimeEmbeddedApplication") == ""){
246         $str = $_POST['NewEmbeddedApplication'];
247         if(!empty($str)){
248           $this->use_gotoMimeEmbeddedApplication[] = $str;
249         }
250       }
251     }   
253     /* Create divlists */
254     $DivPatterns        = new DivSelectBox("gotoMimePatterns");
255     $DivApps            = new DivSelectBox("gotoMimeApplications");
256     $DivEApps           = new DivSelectBox("gotoMimeEmbeddedApplications");
257     $DivPatterns        -> SetHeight(100);
258     $DivApps            -> SetHeight(100);
259     $DivEApps           -> SetHeight(100);
262     if(chkacl($this->acl,"gotoMimeFilePattern") == ""){
263       $Pattern_Actions= " <input type='image' src='images/sort_up.png'  class='center'  name='Pattern_SortUp_%s' >&nbsp;
264       <input type='image' src='images/sort_down.png'    class='center'  name='Pattern_SortDown_%s'>&nbsp;
265       <input type='image' src='images/edittrash.png'    class='center'  name='Pattern_Remove_%s'>";
266     }else{
267       $Pattern_Actions= "";     
268     }
270     if(chkacl($this->acl,"gotoMimeApplication") == ""){
271       $Apps_Actions     = " <input type='image' src='images/sort_up.png'        class='center'  name='Apps_SortUp_%s' >&nbsp;
272       <input type='image' src='images/sort_down.png'    class='center'  name='Apps_SortDown_%s'>&nbsp;
273       <input type='image' src='images/edittrash.png'    class='center'  name='Apps_Remove_%s'>";
274     }else{
275       $Apps_Actions= "";        
276     }
278     if(chkacl($this->acl,"gotoMimeEmbeddedApplication") == ""){
279       $EApps_Actions    = " <input type='image' src='images/sort_up.png'        class='center'  name='EApps_SortUp_%s' >&nbsp;
280       <input type='image' src='images/sort_down.png'    class='center'  name='EApps_SortDown_%s'>&nbsp;
281       <input type='image' src='images/edittrash.png'    class='center'  name='EApps_Remove_%s'>";
282     }else{
283       $EApps_Actions= "";       
284     }
286     foreach($this->use_gotoMimeFilePattern as $key => $pattern){
287       $field1 = array("string" => $pattern);
288       $field2 = array("string" => preg_replace("/%s/",$key,$Pattern_Actions),"attach"=>"style='border-right:0px;width:50px;'");
289       $fields = array($field1,$field2);
290       $DivPatterns -> AddEntry($fields);
291     }                   
292     foreach($this->use_gotoMimeApplication as $key => $pattern){
293       $field1 = array("string" => $pattern);
294       $field2 = array("string" => preg_replace("/%s/",$key,$Apps_Actions),"attach"=>"style='border-right:0px;width:50px;'");
295       $fields = array($field1,$field2);
296       $DivApps -> AddEntry($fields);
297     }                   
298     foreach($this->use_gotoMimeEmbeddedApplication as $key => $pattern){
299       $field1 = array("string" => $pattern);
300       $field2 = array("string" => preg_replace("/%s/",$key,$EApps_Actions),"attach"=>"style='border-right:0px;width:50px;'");
301       $fields = array($field1,$field2);
302       $DivEApps -> AddEntry($fields);
303     }
304     $smarty->assign("baseACL",                                          chkacl($this->acl,"base"));     
305     $smarty->assign("bases",                                            $this->config->idepartments);           
306     $smarty->assign("base_select",                                      $this->base);           
307     $smarty->assign("isReleaseMimeType",                        $this->isReleaseMimeType);
308     $smarty->assign("gotoMimeFilePatterns",                     $DivPatterns->DrawList());
309     $smarty->assign("gotoMimeApplications",                     $DivApps->DrawList());
310     $smarty->assign("gotoMimeEmbeddedApplications",     $DivEApps->DrawList());
312     /* Assign class vars to smarty */
313     foreach($this->attributes as $attr){
314       $smarty->assign($attr,$this->$attr);
315       $smarty->assign($attr."ACL",chkacl($this->acl,$attr));
316     }   
318     /* Assign additional vars that are not included in attributes*/
319     foreach(array("gotoMimeLeftClickAction_I","gotoMimeLeftClickAction_E","gotoMimeLeftClickAction_Q") as $attr){
320       $smarty->assign($attr,$this->$attr);
321     }   
323     /* Assign select box options */
324     $smarty->assign("gotoMimeGroups",$this->MimeGroups);
325     $smarty->assign("gotoMimeIcon"      ,$this->get_picture());
326     return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
327   }
330   function save_object()
331   {
332     if(isset($_POST['MimeGeneric'])){
333       plugin::save_object();
335       /* Only save base if we are not in release mode */
336       if(!$this->isReleaseMimeType){
337         if(isset($_POST['base'])){
338           $this->base = $_POST['base'];
339         }
340       }
342       /* Save radio buttons */
343       if(chkacl($this->acl,"gotoMimeLeftClickAction")== ""){
344         if(isset($_POST['gotoMimeLeftClickAction_IE'])){
345           $chr = $_POST['gotoMimeLeftClickAction_IE'];
346           if($chr == "E"){
347             $this->gotoMimeLeftClickAction_E = true;
348             $this->gotoMimeLeftClickAction_I = false;
349           }else{
350             $this->gotoMimeLeftClickAction_E = false;
351             $this->gotoMimeLeftClickAction_I = true;
352           }
353         }
354         if(isset($_POST['gotoMimeLeftClickAction_Q'])){
355           $this->gotoMimeLeftClickAction_Q = true;
356         }else{
357           $this->gotoMimeLeftClickAction_Q = false;
358         }
359       }
360     }
361   }
364   /* save current changes */
365   function save()
366   {
367     /* Create gotoMimeLeftClickAction out of checkboxes and radio buttons */    
368     $arr = array ("E","I","Q");
369     $str = "";
370     foreach ($arr as $Chr){
371       $var = "gotoMimeLeftClickAction_".$Chr;
372       if($this->$var){
373         $str .= $Chr;
374       }
375     }
376     $this->gotoMimeLeftClickAction = $str;
378     /* Create array entries with priority tag ( Test|3 )*/
379     foreach(array("gotoMimeEmbeddedApplication","gotoMimeApplication") as $attr){
380       $i                                = 0;
381       $use_attr                 = "use_".$attr;
382       $tmp                      = array();
383       $this->$attr      = array();
384       foreach($this->$use_attr as $entry){
385         $tmp[] = $entry."|".$i ++;
386       }
387       $this->$attr = $tmp;
388     }
390     /* Create array entries */
391     foreach(array("gotoMimeFilePattern") as $attr){
392       $i                                = 0;
393       $use_attr                 = "use_".$attr;
394       $tmp                      = array();
395       $this->$attr      = array();
396       foreach($this->$use_attr as $entry){
397         $tmp[] = $entry;
398       }
399       $this->$attr = $tmp;
400     }
402     /* Remove Icon if requested  */
403     if($this->use_gotoMimeIcon != "*removed*"){
404       $this->gotoMimeIcon = $this->iconData;
405     }else{
406       $this->gotoMimeIcon = "";
407     }
409     plugin::save();
411     /* If this is a newly created object, skip storing those 
412        attributes that contain an empty array */
413     if($this->orig_dn == "new"){
414       foreach(array("gotoMimeEmbeddedApplication","gotoMimeApplication","gotoMimeFilePattern") as $attr){
415         if(!count($this->$attr)){
416           unset($this->attrs[$attr]);
417         }
418       }
419     }
421     $ldap = $this->config->get_ldap_link();
422     $ldap-> cd ( $this->config->current['BASE']);
423     $ldap->cat($this->dn);
424     if($ldap->count()){
425       $ldap->cd($this->dn);
426       $this->cleanup();
427       $ldap->modify($this->attrs);
428     }else{
429       $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
430       $ldap->cd($this->dn);
431       $ldap->add($this->attrs);
432     }
433     show_ldap_error($ldap->get_error(),_("Saving mime type failed."));
434   }
437   /* Remove current mime type */
438   function remove_from_parent()
439   {
440     $ldap = $this->config->get_ldap_link();
441     $ldap->rmDir($this->dn);
442     show_ldap_error($ldap->get_error(), _("Removing mie type failed"));
444     /* Optionally execute a command after we're done */
445     $this->handle_post_events("remove");
447     /* Delete references to object groups */
448     $ldap->cd ($this->config->current['BASE']);
449     $ldap->search ("(&(objectClass=gosaGroupOfNames)(member=".$this->dn."))", array("cn"));
450     while ($ldap->fetch()){
451       $og= new ogroup($this->config, $ldap->getDN());
452       unset($og->member[$this->dn]);
453       $og->save ();
454       show_ldap_error($ldap->get_error(), sprintf(_("Removing mime type from objectgroup '%s' failed"), $og->dn));
455     }
456   }
459   /* Check given values */
460   function check()
461   {
462     $message = plugin::check();
463     if(empty($this->cn)){
464       $message[] = _("Please specify a valid name for this mime type.");
465     }
466     if(!count($this->use_gotoMimeFilePattern)){
467       $message[] = _("Please specify at least one file pattern.") ;
468     }
470     /* Check if there is already a mime type with this cn */
471     $ldap = $this->config->get_ldap_link();
472     $ldap->cd($this->config->current["BASE"]);
473     if($this->isReleaseMimeType && (isset($_SESSION['mimefilter']['release']))){
474       $baseDn = str_replace($this->config->current['BASE'],$this->base,$_SESSION['mimefilter']['release']);
475       $baseDn = preg_replace("/ou=mime,.*/","ou=mime,".$this->base,$_SESSION['mimefilter']['release']);
476       $ldap->ls("(&(objectClass=gotoMimeType)(cn=".$this->cn."))",$baseDn,array("cn"));
477       if($ldap->count()){
478         $attrs = $ldap->fetch();
479         if($this->dn != $attrs['dn']) {
480           $message[]= _("There's already a mime type with this 'Name'.");
481         }
482       }
483     }else{
484       $ldap->ls("(&(objectClass=gotoMimeType)(cn=".$this->cn."))","ou=mime,".$this->base,array("cn"));
485       if ($ldap->count()){
486         $attrs = $ldap->fetch();
487         if($this->dn != $attrs['dn']) {
488           $message[]= _("There's already an mime with this 'Name'.");
489         }
490       }
491     }
493     /* Check permissions for that base? */
494     if ($this->base != ""){
495       $new_dn= "cn=".$this->cn.",ou=mime,".$this->base;
496     } else {
497       $new_dn= $this->dn;
498     }
499     if (chkacl($this->acl, "create") != ""){
500       $message[]= _("You have no permissions to create a mime type on this 'Base'.");
501     }
503     return($message);
504   }
506   /** Helper functions **/
508   /* Set a new picture */       
509   function set_new_picture($filename)
510   {
511     if (empty($filename)){
512       $filename= "./images/default_icon.png";
513       $this->use_gotoMimeIcon= "*removed*";
514     }
516     if (file_exists($filename)){
517       $fd = fopen ($filename, "rb");
518       $this->iconData= fread ($fd, filesize ($filename));
519       $_SESSION['binary']= $this->iconData;
520       $_SESSION['binarytype']= "image/jpeg";
521       fclose ($fd);
522     }
523   }
525   /* Get picture link */
526   function get_picture()
527   {
528     $_SESSION['binary']= $this->iconData;
529     $_SESSION['binarytype']= "image/jpeg";
530     return("getbin.php");
531   }
533   /* Transports the given Arraykey one position up*/
534   function ArrayUp($atr,$attrs)
535   {
536     $ret = $attrs;
537     $pos = $this->getpos($atr,$attrs) ;
538     $cn = count($attrs);
539     if(!(($pos == -1)||($pos == 1))){
540       $before = array_slice($attrs,0,($pos-2));
541       $mitte  = array_reverse(array_slice($attrs,($pos-2),2));
542       $unten  = array_slice($attrs,$pos);
543       $ret = array();
544       $ret = $this->combineArrays($before,$mitte,$unten);
545     }
546     return($ret);
547   }
550   /* Transports the given Arraykey one position down*/
551   function ArrayDown($atr,$attrs)
552   {
553     $ret = $attrs;
554     $pos = $this->getpos($atr,$attrs) ;
555     $cn = count($attrs);
556     if(!(($pos == -1)||($pos == $cn))){
557       $before = array_slice($attrs,0,($pos-1));
558       $mitte  = array_reverse(array_slice($attrs,($pos-1),2));
559       $unten  = array_slice($attrs,($pos+1));
560       $ret = array();
561       $ret = $this->combineArrays($before,$mitte,$unten);
562     }
563     return($ret);
564   }
567   /* return the position of the element in the array */
568   function getpos($atr,$attrs)
569   {
570     $i = 0;
571     foreach($attrs as $attr => $name)    {
572       $i++;
573       if($attr == $atr){
574         return($i);
575       }
576     }
577     return(-1);
578   }
581   /* Remove this element from array */
582   function ArrayRemove($key,$array,$reorder = false)    
583   {
584     if(isset($array[$key])){
585       unset($array[$key]);
586       if($reorder){
587         $tmp = array();
588         foreach($array as $entry){
589           $tmp[] = $entry;
590         }
591         $array = $tmp;
592       }
593     }   
594     return($array);
595   }
598   /* Combine new array */
599   function combineArrays($ar0,$ar1,$ar2)
600   {
601     $ret = array();
602     if(is_array($ar0))
603       foreach($ar0 as $ar => $a){
604         $ret[]=$a;
605       }
606     if(is_array($ar1))
607       foreach($ar1 as $ar => $a){
608         $ret[]=$a;
609       }
610     if(is_array($ar2))
611       foreach($ar2 as $ar => $a){
612         $ret[]=$a;
613       }
614     return($ret);
615   }
618   /* Return a dialog with all fields that must be changed, 
619      if we want to copy this entry */
620   function getCopyDialog()
621   {
622     $str = "";
624     $smarty = get_smarty();
625     $smarty->assign("cn",               $this->cn);
626     $smarty->assign("description",  $this->description);
627     $str = $smarty->fetch(get_template_path("paste_generic.tpl",TRUE,dirname(__FILE__)));
629     $ret = array();
630     $ret['string'] = $str;
631     $ret['status'] = "";
632     return($ret);
633   }
636   /* Save all */
637   function saveCopyDialog()
638   {
639     $attrs = array("cn","description");
640     foreach($attrs as $attr){
641       if(isset($_POST[$attr])){
642         $this->$attr = $_POST[$attr];
643       }
644     }
645   }
647 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
648 ?>