Code

Updated locales
[gosa.git] / gosa-core / plugins / admin / acl / class_aclRole.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id$$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
23 class aclrole extends acl
24 {
25   /* Definitions */
26   var $plHeadline= "Access control roles";
27   var $plDescription= "Edit AC roles";
29   /* attribute list for save action */
30   var $attributes= array('gosaAclTemplate',"cn","description");
31   var $objectclasses= array('top','gosaRole');
33   /* Helpers */
34   var $dialogState= "head";
35   var $aclType= "";
36   var $aclObject= "";
37   var $aclContents= array();
38   var $target= "group";
39   var $aclTypes= array();
40   var $aclObjects= array();
41   var $recipients= array();
42   var $isContainer= TRUE;
43   var $currentIndex= 0;
44   var $wasNewEntry= FALSE;
45   var $ocMapping= array();
46   var $savedAclContents= array();
47   var $myAclObjects = array();
49   /* Role attributes */
50   var $gosaAclTemplate= "";
51   var $cn = "";
52   var $description = "";
53   var $orig_dn;
54   var $base ="";
56   function aclrole (&$config,  $dn= NULL)
57   {
58     /* Include config object */
59     plugin::plugin($config, $dn);
61     if($this->dn == "new"){
62       $this->base  = session::get('CurrentMainBase');
63     }else{
64       $this->base = preg_replace("/^[^,]+,[^,]+,/","",$this->dn);
65       new log("view","aclroles/".get_class($this),$this->dn);
66     }
67    
68     /* Load ACL's */
69     $this->gosaAclTemplate= array();
70     if (isset($this->attrs["gosaAclTemplate"])){
71       for ($i= 0; $i<$this->attrs["gosaAclTemplate"]['count']; $i++){
72         $acl= $this->attrs["gosaAclTemplate"][$i];
73         $this->gosaAclTemplate= array_merge($this->gosaAclTemplate, $this->explodeACL($acl));
74       }
75     }
76     ksort($this->gosaAclTemplate);
78     /* Extract available categories from plugin info list */
79     $tmp= session::get('plist');
80     $plist= $tmp->info;
81     $oc = array();
82     foreach ($plist as $class => $acls){
84       /* Only feed categories */
85       if (isset($acls['plCategory'])){
87         /* Walk through supplied list and feed only translated categories */
88         foreach($acls['plCategory'] as $idx => $data){
90           /* Non numeric index means -> base object containing more informations */
91           if (preg_match('/^[0-9]+$/', $idx)){
92             if (!isset($this->ocMapping[$data])){
93               $this->ocMapping[$data]= array();
94               $this->ocMapping[$data][]= '0';
95             }
96             $this->ocMapping[$data][]= $class;
97           } else {
98             if (!isset($this->ocMapping[$idx])){
99               $this->ocMapping[$idx]= array();
100               $this->ocMapping[$idx][]= '0';
101             }
102             $this->ocMapping[$idx][]= $class;
103             $this->aclObjects[$idx]= $data['description'];
105             /* Additionally filter the classes we're interested in in "self edit" mode */
106             if (is_array($data['objectClass'])){
107               foreach($data['objectClass'] as $objectClass){
108                 if (in_array_ics($objectClass, $oc)){
109                   $this->myAclObjects[$idx.'/'.$class]= $acls['plDescription'];
110                   break;
111                 }
112               }
113             } else {
114               if (in_array_ics($data['objectClass'], $oc)){
115                 $this->myAclObjects[$idx.'/'.$class]= $acls['plDescription'];
116               }
117             }
118           }
120         }
121       }
122     }
123     $this->aclObjects['all']= '*&nbsp;'._("All categories");
124     $this->ocMapping['all']= array('0' => 'all');
126     /* Sort categories */
127     asort($this->aclObjects);
129     /* Fill acl types */
130     $this->aclTypes= array( "reset" => _("Reset ACL"),
131                              "one" => _("One level"),
132                              "base" => _("Current object"),
133                              "sub" => _("Complete subtree"),
134                              "psub" => _("Complete subtree (permanent)"));
135     asort($this->aclTypes);
137     /* Finally - we want to get saved... */
138     $this->is_account= TRUE;
139   }
142   function execute()
143   {
144     /* Call parent execute */
145     plugin::execute();
147     /* Base select dialog */
148     $once = true;
149     foreach($_POST as $name => $value){
150       if((preg_match("/^chooseBase/",$name) && $once) && ($this->acl_is_moveable())){
151         $once = false;
152         $this->dialog = new baseSelectDialog($this->config,$this,$this->get_allowed_bases());
153         $this->dialog->setCurrentBase($this->base);
154       }
155     }
157     /* Dialog handling */
158     if(is_object($this->dialog)){
159       /* Must be called before save_object */
160       $this->dialog->save_object();
162       if($this->dialog->isClosed()){
163         $this->dialog = false;
164       }elseif($this->dialog->isSelected()){
166         /* Check if selected base is valid */
167         $tmp = $this->get_allowed_bases();
168         if(isset($tmp[$this->dialog->isSelected()])){
169           $this->base = $this->dialog->isSelected();
170         }
171         $this->dialog= false;
172       }else{
173         return($this->dialog->execute());
174       }
175     }
177     $tmp= session::get('plist');
178     $plist= $tmp->info;
180     /* Handle posts */
181     if (isset($_POST['new_acl'])){
182       $this->dialogState= 'create';
183       $this->dialog= TRUE;
184       $this->currentIndex= count($this->gosaAclTemplate);
185       $this->loadAclEntry(TRUE);
186     }
188     $new_acl= array();
189     $aclDialog= FALSE;
190     $firstedit= FALSE;
191     foreach($_POST as $name => $post){
193       /* Actions... */
194       if (preg_match('/^acl_edit_.*_x/', $name)){
195         $this->dialogState= 'create';
196         $firstedit= TRUE;
197         $this->dialog= TRUE;
198         $this->currentIndex= preg_replace('/^acl_edit_([0-9]+).*$/', '\1', $name);
199         $this->loadAclEntry();
200         continue;
201       }
202       if (preg_match('/^acl_del_.*_x/', $name)){
203         unset($this->gosaAclTemplate[preg_replace('/^acl_del_([0-9]+).*$/', '\1', $name)]);
204         continue;
205       }
207       if (preg_match('/^cat_edit_.*_x/', $name)){
208         $this->aclObject= preg_replace('/^cat_edit_([^_]+)_.*$/', '\1', $name);
209         $this->dialogState= 'edit';
210         foreach ($this->ocMapping[$this->aclObject] as $oc){
211           if (isset($this->aclContents[$oc])){
212             $this->savedAclContents[$oc]= $this->aclContents[$oc];
213           }
214         }
215         continue;
216       }
217       if (preg_match('/^cat_del_.*_x/', $name)){
218         $idx= preg_replace('/^cat_del_([^_]+)_.*$/', '\1', $name);
219         foreach ($this->ocMapping[$idx] as $key){
220           unset($this->aclContents["$idx/$key"]);
221         }
222         continue;
223       }
225       /* Sorting... */
226       if (preg_match('/^sortup_.*_x/', $name)){
227         $index= preg_replace('/^sortup_([0-9]+).*$/', '\1', $name);
228         if ($index > 0){
229           $tmp= $this->gosaAclTemplate[$index];
230           $this->gosaAclTemplate[$index]= $this->gosaAclTemplate[$index-1];
231           $this->gosaAclTemplate[$index-1]= $tmp;
232         }
233         continue;
234       }
235       if (preg_match('/^sortdown_.*_x/', $name)){
236         $index= preg_replace('/^sortdown_([0-9]+).*$/', '\1', $name);
237         if ($index < count($this->gosaAclTemplate)-1){
238           $tmp= $this->gosaAclTemplate[$index];
239           $this->gosaAclTemplate[$index]= $this->gosaAclTemplate[$index+1];
240           $this->gosaAclTemplate[$index+1]= $tmp;
241         }
242         continue;
243       }
245       /* ACL saving... */
246       if (preg_match('/^acl_.*_[^xy]$/', $name)){
247         $aclDialog= TRUE;
248         list($dummy, $object, $attribute, $value)= split('_', $name);
250         /* Skip for detection entry */
251         if ($object == 'dummy') {
252           continue;
253         }
255         /* Ordinary ACL */
256         if (!isset($new_acl[$object])){
257           $new_acl[$object]= array();
258         }
259         if (isset($new_acl[$object][$attribute])){
260           $new_acl[$object][$attribute].= $value;
261         } else {
262           $new_acl[$object][$attribute]= $value;
263         }
264       }
265     }
266     
267     /* Only be interested in new acl's, if we're in the right _POST place */
268     if ($aclDialog && $this->aclObject != "" && is_array($this->ocMapping[$this->aclObject])){
270       foreach ($this->ocMapping[$this->aclObject] as $oc){
271         unset($this->aclContents[$oc]);
272         unset($this->aclContents[$this->aclObject.'/'.$oc]);
273         if (isset($new_acl[$oc])){
274           $this->aclContents[$oc]= $new_acl[$oc];
275         }
276         if (isset($new_acl[$this->aclObject.'/'.$oc])){
277           $this->aclContents[$this->aclObject.'/'.$oc]= $new_acl[$this->aclObject.'/'.$oc];
278         }
279       }
280     }
282     /* Save new acl in case of base edit mode */
283     if (1 == 0 && $this->aclType == 'base' && !$firstedit){
284       $this->aclContents= $new_acl;
285     }
287     /* Cancel new acl? */
288     if (isset($_POST['cancel_new_acl'])){
289       $this->dialogState= 'head';
290       $this->dialog= FALSE;
291       if ($this->wasNewEntry){
292         unset ($this->gosaAclTemplate[$this->currentIndex]);
293       }
294     }
296     /* Store ACL in main object? */
297     if (isset($_POST['submit_new_acl'])){
298       $this->gosaAclTemplate[$this->currentIndex]['type']= $this->aclType;
299       $this->gosaAclTemplate[$this->currentIndex]['members']= $this->recipients;
300       $this->gosaAclTemplate[$this->currentIndex]['acl']= $this->aclContents;
301       $this->dialogState= 'head';
302       $this->dialog= FALSE;
303     }
305     /* Cancel edit acl? */
306     if (isset($_POST['cancel_edit_acl'])){
307       $this->dialogState= 'create';
308       foreach ($this->ocMapping[$this->aclObject] as $oc){
309         if (isset($this->savedAclContents[$oc])){
310           $this->aclContents[$oc]= $this->savedAclContents[$oc];
311         }
312       }
313     }
315     /* Save edit acl? */
316     if (isset($_POST['submit_edit_acl'])){
317       $this->dialogState= 'create';
318     }
320     /* Add acl? */
321     if (isset($_POST['add_acl']) && $_POST['aclObject'] != ""){
322       $this->dialogState= 'edit';
323       $this->savedAclContents= array();
324       foreach ($this->ocMapping[$this->aclObject] as $oc){
325         if (isset($this->aclContents[$oc])){
326           $this->savedAclContents[$oc]= $this->aclContents[$oc];
327         }
328       }
329     }
331     /* Save common values */
332     foreach (array("aclType", "aclObject", "target") as $key){
333       if (isset($_POST[$key])){
334         $this->$key= validate($_POST[$key]);
335       }
336     }
338     /* Create templating instance */
339     $smarty= get_smarty();
341     $smarty->assign("bases", $this->get_allowed_bases());
342     $smarty->assign("base_select", $this->base);
344     $tmp = $this->plInfo();
345     foreach($tmp['plProvidedAcls'] as $name => $translation){
346       $smarty->assign($name."ACL",$this->getacl($name));
347     }
349     if ($this->dialogState == 'head'){
350       /* Draw list */
351       $aclList= new divSelectBox("aclList");
352       $aclList->SetHeight(350);
353       
354       /* Fill in entries */
355       foreach ($this->gosaAclTemplate as $key => $entry){
356         $field1= array("string" => $this->aclTypes[$entry['type']], "attach" => "style='width:100px'");
357         $field2= array("string" => $this->assembleAclSummary($entry));
358         $action= "<input type='image' name='sortup_$key' alt='up' title='"._("Up")."' src='images/sort_up.png' align='top'>";
359         $action.= "<input type='image' name='sortdown_$key' alt='down' title='"._("Down")."' src='images/sort_down.png'>";
360         $action.= "<input class='center' type='image' src='images/edit.png' alt='"._("Edit")."' name='acl_edit_$key' title='".msgPool::editButton(_("ACL"))."'>";
361         $action.= "<input class='center' type='image' src='images/edittrash.png' alt='"._("Delete")."' name='acl_del_$key' title='".msgPool::delButton(_("ACL"))."'>";
363         $field3= array("string" => $action, "attach" => "style='border-right:0px;width:50px;text-align:right;'");
364         $aclList->AddEntry(array($field1, $field2, $field3));
365       }
367       $smarty->assign("aclList", $aclList->DrawList());
368     }
370     if ($this->dialogState == 'create'){
371       /* Draw list */
372       $aclList= new divSelectBox("aclList");
373       $aclList->SetHeight(450);
375       /* Add settings for all categories to the (permanent) list */
376       foreach ($this->aclObjects as $section => $dsc){
377         $summary= "";
378         foreach($this->ocMapping[$section] as $oc){
379           if (isset($this->aclContents[$oc]) && count($this->aclContents[$oc]) && isset($this->aclContents[$oc][0]) &&
380               $this->aclContents[$oc][0] != ""){
382             $summary.= "$oc, ";
383             continue;
384           }
385           if (isset($this->aclContents["$section/$oc"]) && count($this->aclContents["$section/$oc"]) && isset($this->aclContents["$section/$oc"][0]) &&
386               $this->aclContents["$section/$oc"][0] != ""){
388             $summary.= "$oc, ";
389             continue;
390           }
391           if (isset($this->aclContents[$oc]) && !isset($this->aclContents[$oc][0]) && count($this->aclContents[$oc])){
392             $summary.= "$oc, ";
393           }
394         }
396         /* Set summary... */
397         if ($summary == ""){
398           $summary= '<i>'._("No ACL settings for this category").'</i>';
399         } else {
400           $summary= sprintf(_("ACL for these objects: %s"), preg_replace('/, $/', '', $summary));
401         }
403         $field1= array("string" => $dsc, "attach" => "style='width:140px'");
404         $field2= array("string" => $summary);
405         $action= "<input class='center' type='image' src='images/edit.png' alt='"._("Edit")."' name='cat_edit_$section' title='"._("Edit category ACL")."'>";
406         $action.= "<input class='center' type='image' src='images/edittrash.png' alt='"._("Delete")."' name='cat_del_$section' title='"._("Reset category ACL")."'>";
407         $field3= array("string" => $action, "attach" => "style='border-right:0px;width:50px'");
408         $aclList->AddEntry(array($field1, $field2, $field3));
409       }
411       $smarty->assign("aclList", $aclList->DrawList());
412       $smarty->assign("aclType", $this->aclType);
413       $smarty->assign("aclTypes", $this->aclTypes);
414       $smarty->assign("target", $this->target);
416       if ($this->aclType == 'base'){
417         $smarty->assign('aclSelector', $this->buildAclSelector($this->myAclObjects));
418       }
419     }
421     if ($this->dialogState == 'edit'){
422       $smarty->assign('headline', sprintf(_("Edit ACL for '%s', scope is '%s'"), $this->aclObjects[$this->aclObject], $this->aclTypes[$this->aclType]));
424       /* Collect objects for selected category */
425       foreach ($this->ocMapping[$this->aclObject] as $idx => $class){
426         if ($idx == 0){
427           continue;
428         }
429         $aclObjects[$this->aclObject.'/'.$class]= $plist[$class]['plDescription'];
430       }
431       if ($this->aclObject == 'all'){
432         $aclObjects['all']= _("All objects in current subtree");
433       }
434       $smarty->assign('aclSelector', $this->buildAclSelector($aclObjects));
435     }
437     /* Show main page */
438     $smarty->assign("dialogState", $this->dialogState);
440     /* Assign cn and decription if this is a role */
441     foreach(array("cn","description") as $name){
442       $smarty->assign($name,$this->$name);
443     }
444     return ($smarty->fetch (get_template_path('acl_role.tpl',dirname(__FILE__))));
445   }
447   function sort_by_priority($list)
448   {
449     $tmp= session::get('plist');
450     $plist= $tmp->info;
451     asort($plist);
452     $newSort = array();
454     foreach($list as $name => $translation){
455       $na  =  preg_replace("/^.*\//","",$name);
456       if (!isset($plist[$na]['plPriority'])){
457         $prio= 0;
458       } else {
459         $prio=  $plist[$na]['plPriority'] ;
460       }
462       $newSort[$name] = $prio;
463     }
465     asort($newSort);
467     $ret = array();
468     foreach($newSort as $name => $prio){
469       $ret[$name] = $list[$name];
470     }
471     return($ret);
472   }
474   function loadAclEntry($new= FALSE)
475   {
476     /* New entry gets presets... */
477     if ($new){
478       $this->aclType= 'sub';
479       $this->recipients= array();
480       $this->aclContents= array();
481     } else {
482       $acl= $this->gosaAclTemplate[$this->currentIndex];
483       $this->aclType= $acl['type'];
484       $this->recipients= $acl['members'];
485       $this->aclContents= $acl['acl'];
486     }
488     $this->wasNewEntry= $new;
489   }
492   function aclPostHandler()
493   {
494     if (isset($_POST['save_acl'])){
495       $this->save();
496       return TRUE;
497     }
499     return FALSE;
500   }
503   function save()
504   {
505     /* Assemble ACL's */
506     $tmp_acl= array();
507     foreach ($this->gosaAclTemplate as $prio => $entry){
508       $final= "";
509       $members= "";
510       if (isset($entry['members'])){
511         foreach ($entry['members'] as $key => $dummy){
512           $members.= base64_encode(preg_replace('/^.:/', '', $key)).',';
513         }
514       }
515       $final= $prio.":".$entry['type'].":".preg_replace('/,$/', '', $members);
517       /* ACL's if needed */
518       if ($entry['type'] != "reset" && $entry['type'] != "role"){
519         $acl= ":";
520         if (isset($entry['acl'])){
521           foreach ($entry['acl'] as $object => $contents){
523             /* Only save, if we've some contents in there... */
524             if (count($contents)){
525               $acl.= $object.";";
527               foreach($contents as $attr => $permission){
529                 /* First entry? Its the one for global settings... */
530                 if ($attr == '0'){
531                   $acl.= $permission;
532                 } else {
533                   $acl.= '#'.$attr.';'.$permission;
534                 }
536               }
537               $acl.= ',';
538             }
539             
540           }
541         }
542         $final.= preg_replace('/,$/', '', $acl);
543       }
545       $tmp_acl[]= $final;
546     } 
548     /* Call main method */
549     plugin::save();
551     /* Finally (re-)assign it... */
552     $this->attrs["gosaAclTemplate"]= $tmp_acl;
554     /* Remove acl from this entry if it is empty... */
555     if (!count($tmp_acl)){
556       /* Remove attribute */
557       if ($this->initially_was_account){
558         $this->attrs["gosaAclTempalte"]= array();
559       } else {
560         if (isset($this->attrs["gosaAclTemplate"])){
561           unset($this->attrs["gosaAclTemplate"]);
562         }
563       }
564     }
566     /* Do LDAP modifications */
567     $ldap= $this->config->get_ldap_link();
569     /* Check if object already exists */
570     $ldap->cat($this->dn);
571     if($ldap->count()){
572       $ldap->cd($this->dn);
573       $this->cleanup();
574       $ldap->modify ($this->attrs);
575       new log("modify","aclroles/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
576     }else{
577       $ldap->cd($this->config->current['BASE']);
578       $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$this->dn));
579       $ldap->cd($this->dn);
580       $ldap->add($this->attrs);
581       new log("create","aclroles/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
582     }
584     if (!$ldap->success()){
585       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, "", get_class()));
586     }
588     /* Refresh users ACL */
589     $ui= get_userinfo();
590     $ui->loadACL();
591     session::set('ui',$ui);
592   }
595   function remove_from_parent()
596   {
597     $ldap = $this->config->get_ldap_link();
598     $serach_for = "*:role:".base64_encode($this->dn).":*"; 
599     $ldap->search ("(&(objectClass=gosaACL)(gosaAclEntry=".$serach_for."))",array('dn','cn','sn','givenName','uid'));
600     $all_names = "";
603     $cnt = 3;
604     while(($attrs = $ldap->fetch()) && $cnt){
605       $name = $attrs['dn'];
606       $name = preg_replace("/[ ]/","&nbsp;",$name);
607       $name = "<i>'".$name."'</i>";
608       $all_names .= $name.", ";
609       $cnt --;
610     }
612     if(!empty($all_names)){
613       $all_names = preg_replace("/, $/","",$all_names);
614       if(!$cnt){
615         $all_names .= ", ...";
616       }
617       $all_names = "<span style='text-align:left;'>".$all_names."</span>";
618       msg_dialog::display(_("Object in use"), sprintf(_("This role cannot be removed while it is in use by these objects:")."<br><br>%s", $all_names), WARNING_DIALOG);
619       return;
620     }
621         
622     $ldap->rmDir($this->dn);
623     new log("remove","aclroles/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
624     if (!$ldap->success()){
625       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, "", get_class()));
626     }
628     /* Optionally execute a command after we're done */
629     $this->handle_post_events("remove");
631     /* Delete references to object groups */
632     $ldap->cd ($this->config->current['BASE']);
633     $ldap->search ("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter($this->dn)."))", array("cn"));
634     while ($ldap->fetch()){
635       $og= new ogroup($this->config, $ldap->getDN());
636       unset($og->member[$this->dn]);
637       $og->save ();
638       if (!$ldap->success()){
639         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $og->dn, "", get_class()));
640       }
641     }
642   }
645   function save_object()
646   {
647     plugin::save_object();
648     if(isset($_POST['acl_role_posted'])){
649      
650       /* Get base selection */
651       if(isset($_POST['base'])){
652         $tmp = $this->get_allowed_bases();
653         if(isset($tmp[$_POST['base']])){
654           $this->base = $_POST['base'];
655         }
656       }
657     }
658   }
661   function saveCopyDialog()
662   {
663     if(isset($_POST['cn'])){
664       $this->cn = $_POST['cn'];
665     }
666   }
667   
669   function getCopyDialog()
670   {
671     $smarty = get_smarty();
672     $smarty->assign("cn",$this->cn);
673     $str = $smarty->fetch(get_template_path("paste_role.tpl",TRUE,dirname(__FILE__)));
674     $ret = array();
675     $ret['string'] = $str;
676     $ret['status'] = "";
677     return($ret);
678   }
680   
681   function PrepareForCopyPaste($source)
682   {
683     plugin::PrepareForCopyPaste($source);
684     
685     $source_o = new aclrole($this->config,$source['dn']);
686     $this->gosaAclTemplate = $source_o->gosaAclTemplate;
687   }
688   
690   /* Return plugin informations for acl handling  */
691   static function plInfo()
692   {
693     return (array(
694           "plShortName" => _("Role"),
695           "plDescription" => _("Access control roles"),
696           "plSelfModify"  => FALSE,
697           "plDepends"     => array(),
698           "plPriority"    => 0,
699           "plSection"     => array("admin"),
700           "plCategory"    => array("aclroles" => array("objectClass" => "gosaRole", "description" => _("Access control roles"))),
702           "plProvidedAcls"    => array(
703             "cn"                => _("Name"),
704             "base"              => _("Base"),
705             "description"       => _("Description"))
706         ));
707   }
711 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
712 ?>