Code

get rid of the div stuff
[gosa.git] / gosa-plugins / fai / admin / fai / class_faiDiskEntry.inc
1 <?php
3 class faiDiskEntry extends plugin
4 {
5   /* attribute list for save action */
6   var $ignore_account= TRUE;
7   var $attributes   = array("DISKcn","DISKdescription");
8   var $UsedAttrs            = array("cn","description","FAIpartitionType",
9       "FAIpartitionNr","FAIfsType","FAImountPoint","FAIpartitionSize",
10       "FAIfsTuneOptions", "FAIfsCreateOptions", "FAImountOptions",
11       "FAIfsOptions","FAIpartitionFlags","FAIdiskOption");
13   var $objectclasses= array();
15   var $DISKcn              = "";
16   var $DISKdescription     = "";
17   var $DISKFAIdiskOptions  = "";
18   
19   var $partitions          = array();
20   var $is_edit             = false;
21   var $old_cn              = "";
22   var $fstabkey            = "device";
23   var $disklabel           = "msdos";
24   var $FAIstate            = "";
25   var $FAIdiskType         = "disk";
26   
27   var $lvmDevices          = array();
29   function faiDiskEntry (&$config, $dn= NULL,$parent,$disk,$type)
30   {
31     plugin::plugin ($config, $dn);
32   
33     // Set default attributes 
34     $this->parent = $parent;
35     $this->FAIdiskType = $type;
37     // If disk is not empty, then we are going to edit 
38     //  an existing disk, load disk info now. 
39     if($disk){
41       // Get devices used in volume base disks.
42       if(isset($disk['FAIlvmDevice'])){
43         $this->lvmDevices = $disk['FAIlvmDevice'];
44       }
46       // Load default attributes 
47       $this->DISKcn          = $disk['cn'];
48       $this->DISKdescription = $disk['description'];
49       $this->is_edit         = true;
50       $this->old_cn          = $disk['cn'];
52       // Load partition info 
53       foreach($disk['partitions'] as $values){
55         $name = $values['FAIpartitionNr'];
57         // Load partition attributes  
58         $disk['partitions'][$name]['old_cn']= $disk['partitions'][$name]['cn'];
59         $disk['partitions'][$name]['FAIdiskType']= $this->FAIdiskType;
60         foreach($this->UsedAttrs as $attr){
61           if(!isset($values[$attr])){
62             $disk['partitions'][$name][$attr]="";  
63           }
64         }
66         if (preg_match('/^_/', $disk['partitions'][$name]['FAIfsType'])){
67           $disk['partitions'][$name]['FAIfsType']= 
68             preg_replace('/^_/', '', $disk['partitions'][$name]['FAIfsType']);
69         }
70       }
72       $this->partitions      = $disk['partitions'];
74       /* Load FAIdiskOptions.
75        * Some options are disk related and others are used for partitions.
76        *  - fstabkey    -> disk
77        *  - disklabel   -> disk
78        *  - bootable    -> partition
79        *  - preserve    -> partition
80        *  - resize      -> partition
81        */
82       if (isset($disk['FAIdiskOption'])){
83         foreach($disk['FAIdiskOption'] as $option) {
85           // Get fstab key  
86           if (preg_match("/fstabkey:(device|label|uuid)/", $option)){
87             $this->fstabkey= preg_replace("/^.*fstabkey:(device|label|uuid).*$/", "$1", $option);
88             continue;
89           }
91           // Get disk label
92           if (preg_match("/disklabel:(msdos|gpt)/", $option)){
93             $this->disklabel= preg_replace("/^.*disklabel:(msdos|gpt).*$/", "$1", $option);
94             continue;
95           }
97           // Load bootable flag for partitions 
98           if (preg_match("/^bootable:/", $option)){
99             $bootable = explode(",", trim(preg_replace("/^bootable:/","",$option),","));
100             foreach($bootable as $bootflag){
101               if(isset($this->partitions[$bootflag])){
102                 $this->partitions[$bootflag]['bootable'] = TRUE;  
103               }
104             }
105             continue;
106           }
108           // Load resize flag for partitions 
109           if (preg_match("/^resize:/", $option)){
110             $resize = explode(",", trim(preg_replace("/^resize:/","",$option),","));
111             foreach($resize as $id){
112               if(isset($this->partitions[$id])){
113                 $this->partitions[$id]['resize'] = TRUE;  
114               }
115             }
116             continue;
117           }
119           // Load preserve_always flag for partitions 
120           if (preg_match("/^preserve_always:/", $option)){
121             $preserve = explode(",", trim(preg_replace("/^preserve_always:/","",$option),","));
122             foreach($preserve as $presflag){
123               if(isset($this->partitions[$presflag])){
124                 $this->partitions[$presflag]['preserve'] = TRUE;  
125                 $this->partitions[$presflag]['preserveType'] = 'always';  
126               }
127             }
128             continue;
129           }
131           // Load preserve_reinstall flag for partitions 
132           if (preg_match("/^preserve_reinstall:/", $option)){
133             $preserve = explode(",", trim(preg_replace("/^preserve_reinstall:/","",$option),","));
134             foreach($preserve as $presflag){
135               if(isset($this->partitions[$bootflag])){
136                 $this->partitions[$presflag]['preserve'] = TRUE;  
137                 $this->partitions[$presflag]['preserveType'] = 'reinstall'; 
138               } 
139             }
140             continue;
141           }
142         }
143       } else {
144         $this->fstabkey= "device";
145       }
146     }
148     // Prepare lists
149     $this->diskList = new sortableListing();
150     $this->diskList->setDeleteable(true);
151     $this->diskList->setEditable(true);
152     $this->diskList->setWidth("100%");
153     $this->diskList->setHeight("400px");
154     $this->diskList->setColspecs(array('200px','*'));
155     $this->diskList->setHeader(array("Name",_("Description"),_("Type"),_("Mount point"),_("Options")));
156     $this->diskList->setDefaultSortColumn(1);
157     $this->diskList->setAcl('rwcdm'); // All ACLs, we filter on our own here.
158   }
161   function execute()
162   {
163     /* Call parent execute */
164     plugin::execute();
166     // Fill templating stuff
167     $smarty     = get_smarty();
168     $s_action   = "";
169     $s_entry    = "";
170     $display    = "";
172     // Add partition to lvm compilation.
173     if(isset($_POST['addLvmPartition']) && isset($_POST['lvmPartitionAdd'])){
174       $name = get_post('lvmPartitionAdd');
175       $this->lvmDevices[$name] = $name;
176     }
178     // Remove partition from lvm compilation.
179     if(isset($_POST['delLvmPartition']) && isset($_POST['physicalPartition'])){
180       $names = $_POST['physicalPartition'];
181       foreach($names as $name){
182         if(isset($this->lvmDevices[$name])){
183           unset($this->lvmDevices[$name]);
184         }
185       }
186     }
188     // Get list actions
189     $this->diskList->save_object();
190     $action = $this->diskList->getAction();
191     if($action['action'] == 'edit'){
192         $id = $this->diskList->getKey($action['targets'][0]);
193         if(isset($this->partitions[$id])){
194             $this->dialog = new faiPartition($this->config,$this->partitions[$id], $this,$this->FAIdiskType);
195         }
196     }
197     if($action['action'] == 'delete'){
198         $id = $this->diskList->getKey($action['targets'][0]);
199         if(isset($this->partitions[$id])){
200             $this->removePartition($id);
201         }
202     }
204     /* Create a new partition for this disk.
205      */
206     if(isset($_POST['AddPartition']) && !preg_match("/freeze/i",$this->FAIstate)){
207       $this->dialog = new faiPartition($this->config, array(), $this,$this->FAIdiskType);
208     }
210     /* Handle partition dialogs.
211      */
212     if($this->dialog instanceOf plugin && isset($_POST['PartitionCancel'])){
213       $this->dialog = null;
214     }
215     if($this->dialog instanceOf plugin && isset($_POST['PartitionSave'])){
216       $this->dialog->save_object();
218       // Validate new partition
219       $new_partition = $this->dialog->save(); 
220       $msgs = $this->dialog->check();
221       $msgs = array_merge($msgs,$this->check_disks($new_partition));
223       if(!count($msgs)){
224         $this->updatePartition($new_partition);
225         $this->dialog = null;
226       }else{
227         msg_dialog::displayChecks($msgs);
228       }
229     }
230     if($this->dialog instanceOf plugin){
231       $this->dialog->save_object();
232       return($this->dialog->execute());
233     }
235     // Assign checkbox related values.
236     foreach($this->attributes as $attrs){
237       $smarty->assign($attrs,$this->$attrs);
238       if($this->$attrs){
239         $smarty->assign($attrs."CHK"," ");
240       }else{
241         $smarty->assign($attrs."CHK"," disabled ");
242       }
243     }
245     // Assign disk info to smarty.
246     $smarty->assign("setup", $this->generateParts());
247     $smarty->assign("sub_object_is_createable",$this->acl_is_createable());
248     $smarty->assign("freeze",preg_match("/freeze/i",$this->FAIstate));
249     $smarty->assign("fstabkeys", array("device" => _("Device"), "label" => _("Label"), "uuid" => _("UUID")));
250     $smarty->assign("disklabels", array("msdos" => "MSDOS", "gpt" => "GPT"));
251     $smarty->assign("fstabkey", $this->fstabkey);
252     $smarty->assign("disklabel", $this->disklabel);
253     $smarty->assign("FAIdiskType", $this->FAIdiskType);
254     $smarty->assign("plist", $this->getPartitionList());
255     $smarty->assign("physicalPartitionList", $this->getAvailablePartitions());
257     foreach($this->attributes as $attr){
258       $smarty->assign($attr,$this->$attr);
259     }
261     // Assign partitions
262     $tmp = $this->plInfo();
263     $sacl = "";
264     foreach($tmp['plProvidedAcls'] as $name => $translated){
265       $acl = $this->getacl($name, preg_match("/freeze/i",$this->FAIstate));
266       $smarty->assign($name."ACL",$acl);
267     }
268    
269     $display.= $smarty->fetch(get_template_path('faiDiskEntry.tpl', TRUE));
270     return($display);
271   }
274   /* Creates a human readable list, that contains all physical 
275    *  devices that are used by the volume group.
276    * This list will then be displayed in a html select box.
277    * (lvm)
278    */
279   function getPartitionList()
280   {
281     /* Create a list of all available disks and partitions. 
282      * This list will then be used to display detailed info.
283      */
284     $disks = $this->parent->disks;
285     foreach($disks as $dname => $disk){
287       // Skip currently edited disk 
288       if($disk['cn'] == $this->old_cn) continue;
289    
290       // Add disk 
291       $objs[$dname] = $disk;
293       // Add disk partitions
294       foreach($disk['partitions'] as $id => $part){
295         $part['parentDisk'] = $disk;
296         $objs[$part['cn']] = $part;
297       }
298     }
300     // Attach current disk setup to the details list.
301     $data = $this->save();
302     $objs[$data['cn']] = $data;
303     foreach($data['partitions'] as $part){
304       $part['parentDisk'] = $data;
305       $objs[$part['cn']] = $part;
306     }
308     // Walk through physical partition combinations and build up 
309     //  user friendly list with partition details.
310     $list = array();
311     foreach($this->lvmDevices as $device){
313       // We've a html select box here, add spaces for better readability
314       $str = $device;
315       $str = preg_replace("/ /","&nbsp;",str_pad($str,20," "));
317       // Add disk/partition details.
318       if(isset($objs[$device])){
319         if(isset($objs[$device]['FAIpartitionSize'])){
320           if($objs[$device]['parentDisk']['FAIdiskType'] == "raid"){
321             $str .= _("Disks").":&nbsp;";
322             $str .= preg_replace("/(:spare|:missing)/i","",$objs[$device]['FAIpartitionSize']);
323           }else{
324             $str .= _("Size").":&nbsp;";
325             $str .= $objs[$device]['FAIpartitionSize'];
326           } 
327         }
328       }
329       $list[$device] = $str;
330     }
331     return($list);
332   }
334  
335   /* Returns a list of available partitions that are useable in 
336    *  lvm disk setups.
337    */ 
338   function getAvailablePartitions()
339   {
340     $may = $used = array();
341     foreach($this->parent->disks as $disk){
343       // Skip ourselves
344       if($disk['cn'] == $this->DISKcn) continue;
346       // Add partition from lvm combinations
347       if($disk['FAIdiskType'] == "lvm"){
348         $used = array_merge($used,$disk['FAIlvmDevice']);
349       }
351       foreach($disk['partitions'] as $key => $part){
353         // Add disks of raid arrays, to the used list.
354         if($disk['FAIdiskType'] == "raid"){
355           foreach(explode(",",$part['FAIpartitionSize']) as $rDevice){
356             $used[] = preg_replace("/:.*$/i","",$rDevice);
357           }
358         }
360         // Collect all available partitions
361         if($disk['FAIdiskType'] == "disk" || $disk['FAIdiskType'] == "raid"){
362           $name = $part['cn'];
363           if(!isset($this->lvmDevices[$name])){
364             $may[] = $name;
365           }
366         }
367       }
368     }
370     // Check which of the available disks are unused.
371     $ret = array();
372     foreach($may as $val){
373       if(!in_array($val,$used)){
374         $ret[$val] = $val;
375       }
376     }
377     return($ret);
378   }
381   /* Remove the selected partition and shift the following partitions 
382    *  to fill the gap.
383    * Additionally update the partition numbers correspondingly.
384    *  (Checks if the partition is in use, too)
385    */
386   function removePartition($id)
387   {
388     $start = false;
390     /* Create a list of all partitions that are used in
391      *  lvm or raid compilations.
392      */
393     $list = array();
394     foreach($this->parent->disks as $dname => $disk){
395       if($disk['FAIdiskType'] != "disk"){
396         if($disk['FAIdiskType'] == "lvm"){
397           foreach($disk['FAIlvmDevice'] as $partname){
398             $list[preg_replace("/:.*$/","",$partname)][] = $disk;
399           }
400         }
401         foreach($disk['partitions'] as $partkey => $part){
402           if($disk['FAIdiskType'] == "raid"){
403             foreach(explode(",",$part['FAIpartitionSize']) as $partname){
404               $list[preg_replace("/:.*$/","",$partname)][] = $disk;
405             }
406           }
407         }
408       }
409     }
411     /* Now that we've a list of all partition references, lets check if
412      *  one of the partitions we are going to remove is still in use.
413      */
414     if(isset($list[$this->partitions[$id]['cn']])){
415       $used = array();
416       foreach($list[$this->partitions[$id]['cn']] as $disk){
417         $used[$disk['cn']] = $disk['cn'];
418       }
419       $used = implode(",",$used);
420       msg_dialog::display(_("Error"),
421           sprintf(_("The disk cannot be deleted while it is used in the '%s' disk definition!"),
422             $used), ERROR_DIALOG);
423     }else{
424       unset($this->partitions[$id]);
425     }
426   }
428   
429   function get_free_partition_number()
430   {
431     $used = array();
432     foreach($this->partitions as $key => $part){
433       $used[$key] = $part['FAIpartitionNr'];
434     }
435     $id = 1;
436     while(in_array($id,$used) && $id < 16 ){
437       $id++;
438     }
439     return($id);
440   }
444   /* Add or update a partition 
445    */
446   function updatePartition($part)
447   {
448     if(!isset($part['FAIpartitionNr']) || $part['FAIpartitionNr'] == "undefined"){
449       $part['FAIpartitionNr'] = $this->get_free_partition_number();
450     }
452     /* Update the disk cn -       
453      * Do NOT touch the partition 'cn' in case of lvm or raid devices. 
454      */
455     if($this->FAIdiskType == "disk"){
456       $part['cn'] = $this->DISKcn.$part['FAIpartitionNr'];
457     }
459     /* Check if we've to update partition names of lvm compilations.
460      */ 
461     if($this->FAIdiskType == "lvm"){
462       if(isset($this->partitions[$part['FAIpartitionNr']])){
463         $old_cn = $this->partitions[$part['FAIpartitionNr']]['cn'];
464         $new_cn = $part['cn'];
465         if(isset($this->lvmDevices[$old_cn])){
466           unset($this->lvmDevices[$old_cn]);
467           $this->lvmDevices[$new_cn] = $new_cn;
468         }
469       }
470     }
472     /* Set raid names to md#
473      */ 
474     if($this->FAIdiskType == "raid"){
475       $part['cn'] = 'md'.$part['FAIpartitionNr'];
476     }
478     $this->partitions[$part['FAIpartitionNr']] = $part;
479   }
482   /* This method generates the partition listing with all necessary info,
483    *  depending on the disk type.
484    */
485   function generateParts()
486   {
487     $data = $lData = array();
488     foreach($this->partitions as $key => $part){
489     
490       $cn       = $part['cn'];
491       $desc     = $part['description'];
492       $number   = $part['FAIpartitionNr'];
493       $size     = $part['FAIpartitionSize'];
494       $type     = $part['FAIpartitionType'];
496       // Remove encryption info from the mount point.
497       $mnt = $part['FAImountPoint'];
498       if(preg_match("/:encrypt/", $mnt)){
499         $mnt = preg_replace("/:encrypt/","",$mnt);
500       }
502       // create human readable strings out of the flags.
503       $opt = "";
504       if(isset($part['encrypted']) && $part['encrypted']){
505         $opt.= "&nbsp;"._("encrypted").", ";
506       }      
507       if(isset($part['bootable']) && $part['bootable']){
508         $opt.= "&nbsp;"._("bootable").", ";
509       }      
510       if(isset($part['preserve']) && $part['preserve']){
511         $opt.= "&nbsp;"._("preserve").":&nbsp;".$part['preserveType'].", ";
512       }     
514       // Combine options to a single table col. 
515       $opt = "<i>".preg_replace('/, $/',"",$opt)."&nbsp;</i>";
517       // Build up info table, depending on the disk type. 
518       $data[$key]=$key;
519       if($this->FAIdiskType == "lvm"){
520         $lData[$key]=array('data' => array($cn,$desc,"",$mnt,$opt,$size));
521       }elseif($this->FAIdiskType == "raid"){
522         $raid = $part['FAIpartitionType']." (".$part['FAIpartitionSize'].")";
523         $lData[$key]=array('data' => array($cn,$desc,$raid,$mnt,$opt));
524       }else{
525         $lData[$key]=array('data' => array("",$desc,$type,$mnt,$opt,$size));
526       }
527     }
528     $this->diskList->setListData($data,$lData);
529     $acl = "rwcdm";
530     if(preg_match("/freeze/", $this->FAIstate)) $acl = "r";
531     $this->diskList->setAcl($acl);;
532     $this->diskList->update();
533     return($this->diskList->render());
534   }
537   function save()
538   {
539     $tmp = array();
540     $tmp['cn']          = $this->DISKcn;
542     /* Attach partitions. 
543      * And prepare names and numbers.
544      */
545     foreach($this->partitions as $key=>$val) {
546       $this->partitions[$key]['FAIpartitionNr']=$key;
547       if($this->FAIdiskType == "disk"){
548         $this->partitions[$key]['cn'] = $this->DISKcn.$key;
549       }elseif($this->FAIdiskType == "lvm"){
550         $this->partitions[$key]['FAIpartitionType'] = 'lvm';
551       }
552     }
554     $tmp['description'] = $this->DISKdescription;
555     $tmp['partitions']  = $this->partitions;
556     $tmp['FAIdiskType'] = $this->FAIdiskType;
558     // Add lvm devices if available.
559     $tmp['FAIlvmDevice'] = array();
560     foreach($this->lvmDevices as $dev){
561       $tmp['FAIlvmDevice'][] = $dev;
562     } 
564     /* Assemble flags */
565     $tmp['FAIdiskOption'] = array("fstabkey:".$this->fstabkey, "disklabel:".$this->disklabel);
567     /* If hdd name has changed, tell partitionTable to rename it */
568     if(($this->is_edit)&&($this->old_cn != $this->DISKcn)){
569       $tmp['rename']['from']  = $this->old_cn;
570       $tmp['rename']['to']    = $this->DISKcn;
571     }
573     // Build up disk options 
574     $bootable = "";
575     $resize = "";
576     $preserve_always = "";
577     $preserve_reinstall = "";
579     /* Assemble boot, resize and preserve flags 
580      */
581     foreach($tmp['partitions'] as $id => $part){
582       if(isset($part['bootable']) && $part['bootable']){
583         $bootable .= $id.",";
584       }
585       if(isset($part['resize']) && $part['resize']){
586         $resize .= $id.",";
587       }
588       if(isset($part['preserve']) && $part['preserve']){
589         if($part['preserveType'] == "always"){
590           $preserve_always .= $id.",";
591         }else{
592           $preserve_reinstall .= $id.",";
593         }
594       }
596       // Unset non valid attributes 
597       foreach(array("bootable","encrypted","preserve","preserveType","resize","FAIdiskType") as $attr){
598         if(isset($tmp['partitions'][$id][$attr])){
599           unset($tmp['partitions'][$id][$attr]);
600         }
601       }
602     }    
604     /* Assembe disk flags
605      */
606     if(!empty($bootable)){
607       $tmp['FAIdiskOption'][] = "bootable:".trim($bootable,",");
608     }
609     if(!empty($resize)){
610       $tmp['FAIdiskOption'][] = "resize:".trim($resize,",");
611     }
612     if(!empty($preserve_always)){
613       $tmp['FAIdiskOption'][] = "preserve_always:".trim($preserve_always,",");
614     }
615     if(!empty($preserve_reinstall)){
616       $tmp['FAIdiskOption'][] = "preserve_reinstall:".trim($preserve_reinstall,",");
617     }
619     return($tmp);
620   }
623   /* Save data to object */
624   function save_object()
625   {
626     if((isset($_POST['TableEntryFrameSubmitted'])) && !preg_match("/freeze/", $this->FAIstate) ){
627       plugin::save_object();
629       // Save posted disk label and fstab key
630       if (isset($_POST['disklabel']) && preg_match("/^(msdos|gpt)$/", $_POST['disklabel'])){
631         $this->disklabel= $_POST['disklabel'];
632       }
633       if (isset($_POST['fstabkey']) && preg_match("/^(device|label|uuid)$/", $_POST['fstabkey'])){
634         $this->fstabkey= $_POST['fstabkey'];
635       }
636     }
637   }
640   /* Check supplied data */
641   function check()
642   {
643     /* Call common method to give check the hook */
644     $message= plugin::check();
645   
646     /* Check for an empty disk name */
647     $d = trim($this->DISKcn);
648     if($d == "" ){
649       $message[] = msgPool::required(_("Name"));
650     }
651     if(preg_match("/[^a-z0-9_\-]/i",$d)){
652       $message[] = msgPool::invalid(_("Name"),$d,"/[a-z0-9_\-]/i");
653     }
655     return ($message);
656   }
659   /* Checks the disk combinations.
660    * 
661    */  
662   function check_disks($disk_to_add = array())
663   {
664     $msgs = array();
666     /* Check 'disk' combinations. 
667      *  - There can be four primary partitions.
668      *  - If there is at least one 'logical' partition, then there can be only 
669      *     three 'primary' partitions.
670      */    
671     if($this->FAIdiskType == "disk"){
672      
673       $types = array('logical' => array(), 'primary' => array());
674       $types[$disk_to_add['FAIpartitionType']][$disk_to_add['FAIpartitionNr']] = 1;
675       foreach($this->partitions as $key => $part){
676         $types[$part['FAIpartitionType']][$part['FAIpartitionNr']] = 1;
677       }
679       // There can only be four primary partitions per disk - without any logical partition.
680       if(count($types['logical']) == 0){
681         if(count($types['primary']) > 4){
682           $msgs[] = _("You have more than four primary partition table entries in your configuration, please check your configuration twice.");
683         }
684       }else{
685         if(count($types['primary']) > 3){
686           $msgs[] = _("You cannot have more than three primary partition while using logical partitions, please check your configuration twice.");
687         }
688       }
689     }
691     return($msgs);
692   }  
695   /* Return plugin informations for acl handling */
696   static function plInfo()
697   {
698     return (array(
699           "plShortName" => _("Partition table entry"),
700           "plDescription" => _("FAI partition table entry"),
701           "plSelfModify"  => FALSE,
702           "plDepends"     => array(),
703           "plPriority"    => 27,
704           "plSection"     => array("administration"),
705           "plCategory"    => array("fai"),
706           "plProvidedAcls" => array(
707             "DISKcn"           => _("Name"),
708             "DISKdescription"  => _("Description"),
709             "DISKFAIdiskOption"  => _("Disk options"),
710             "FAIpartitionType"  => _("Partition type"),
711             "FAIpartitionNr"    => _("Partition no."),
712             "FAIfsType"         => _("File system type"),
713             "FAImountPoint"     => _("Mount point"),
714             "FAIpartitionSize"  => _("Partition size"),
715             "FAImountOptions"   => _("Mount options"),
716             "FAIfsOptions"      => _("File system options"),
717             "FAIpartitionFlags" => _("Partition flags"))
718           ));
719   }
720  
723 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
724 ?>