1 <?php
4 /* Sort multidimensional arrays for key 'text' */
5 function sort_list($val1, $val2)
6 {
7 $v1= strtolower($val1['text']);
8 $v2= strtolower($val2['text']);
9 if ($v1 > $v2){
10 return 1;
11 }
12 if ($v1 < $v2){
13 return -1;
14 }
15 return 0;
16 }
19 class ogroup extends plugin
20 {
21 /* Variables */
22 var $cn= "";
23 var $description= "";
24 var $base= "";
25 var $gosaGroupObjects= "";
26 var $department= "";
27 var $objects= array();
28 var $objcache= array();
29 var $memberList= array();
30 var $member= array();
31 var $orig_dn= "";
32 var $group_dialog= FALSE;
33 var $view_logged = FALSE;
35 /* attribute list for save action */
36 var $attributes= array("cn", "description", "gosaGroupObjects","member");
37 var $objectclasses= array("top", "gosaGroupOfNames");
39 function ogroup (&$config, $dn= NULL)
40 {
41 plugin::plugin ($config, $dn);
42 $this->orig_dn= $dn;
44 $this->member = array();
46 /* Load member objects */
47 if (isset($this->attrs['member'])){
48 foreach ($this->attrs['member'] as $key => $value){
49 if ("$key" != "count"){
50 $value= @LDAP::convert($value);
51 $this->member["$value"]= "$value";
52 }
53 }
54 }
55 $this->is_account= TRUE;
57 /* Get global filter config */
58 if (!session::is_set("ogfilter")){
59 $ui= get_userinfo();
60 $base= get_base_from_people($ui->dn);
61 $ogfilter= array( "dselect" => $base,
62 "regex" => "*");
63 session::set("ogfilter", $ogfilter);
64 }
65 $ogfilter= session::get('ogfilter');
67 /* Adjust flags */
68 foreach( array( "U" => "accounts",
69 "G" => "groups",
70 "A" => "applications",
71 "D" => "departments",
72 "S" => "servers",
73 "W" => "workstations",
74 "O" => "winstations",
75 "T" => "terminals",
76 "F" => "phones",
77 "_" => "subtrees",
78 "P" => "printers") as $key => $val){
80 if (preg_match("/$key/", $this->gosaGroupObjects)){
81 $ogfilter[$val]= "checked";
82 } else {
83 $ogfilter[$val]= "";
84 }
85 }
86 session::set("ogfilter", $ogfilter);
88 if(session::is_set('CurrentMainBase')){
89 $this->base = session::get('CurrentMainBase');
90 }
92 /* Set base */
93 if ($this->dn == "new"){
94 $this->base = session::get('CurrentMainBase');
95 } else {
96 $this->base= preg_replace("/^[^,]+,".normalizePreg(get_ou("ogroupou"))."/","",$this->dn);
97 }
99 /* Load member data */
100 $this->reload();
101 }
103 function AddDelMembership($NewMember = false){
105 if($NewMember){
107 /* Add member and force reload */
108 $this->member[$NewMember]= $NewMember;
109 $this->reload();
111 $this->memberList[$NewMember]= $this->objcache[$NewMember];
112 unset ($this->objects[$NewMember]);
113 uasort ($this->memberList, 'sort_list');
114 reset ($this->memberList);
115 }else{
116 /* Delete objects from group */
117 if (isset($_POST['delete_membership']) && isset($_POST['members'])){
118 foreach ($_POST['members'] as $value){
119 $this->objects["$value"]= $this->memberList[$value];
120 unset ($this->memberList["$value"]);
121 unset ($this->member["$value"]);
122 uasort ($this->objects, 'sort_list');
123 reset ($this->objects);
124 }
125 $this->reload();
126 }
128 /* Add objects to group */
129 if (isset($_POST['add_object_finish']) && isset($_POST['objects'])){
131 $tmp = "";
132 foreach($this->memberList as $obj){
133 $tmp .= $obj['type'];
134 }
135 $skipped = FALSE;
136 foreach ($_POST['objects'] as $value){
137 if(preg_match("/T/",$tmp) && $this->objects[$value]['type'] == "W"){
138 $skipped =TRUE;
139 }elseif(preg_match("/W/",$tmp) && $this->objects[$value]['type'] == "T"){
140 $skipped =TRUE;
141 }else{
142 $this->memberList["$value"]= $this->objects[$value];
143 $this->member["$value"]= $value;
144 unset ($this->objects[$value]);
145 uasort ($this->memberList, 'sort_list');
146 reset ($this->memberList);
147 }
148 }
149 if($skipped){
150 print_red(_("You can only have terminals or workstations in one group, but not both."));
151 }
152 $this->reload();
153 }
154 }
155 }
157 function execute()
158 {
159 /* Call parent execute */
160 plugin::execute();
162 if(!$this->view_logged){
163 $this->view_logged = TRUE;
164 new log("view","ogroups/".get_class($this),$this->dn);
165 }
168 /* Do we represent a valid group? */
169 if (!$this->is_account){
170 $display= "<img alt=\"\" src=\"images/stop.png\" align=\"middle\"> <b>".
171 _("This 'dn' is no object group.")."</b>";
172 return ($display);
173 }
175 /* Delete objects from group */
176 if (isset($_POST['delete_membership']) && isset($_POST['members'])){
177 foreach ($_POST['members'] as $value){
178 if(isset($this->memberList[$value])){
179 $this->objects["$value"]= $this->memberList[$value];
180 unset ($this->memberList["$value"]);
181 unset ($this->member["$value"]);
182 uasort ($this->objects, 'sort_list');
183 reset ($this->objects);
184 }
185 }
186 $this->reload();
187 }
189 /* Add objects to group */
190 if (isset($_POST['add_object_finish']) && isset($_POST['objects'])){
191 foreach ($_POST['objects'] as $value){
192 if(isset($this->objects[$value])){
193 $this->memberList["$value"]= $this->objects[$value];
194 $this->member["$value"]= $value;
195 unset ($this->objects[$value]);
196 uasort ($this->memberList, 'sort_list');
197 reset ($this->memberList);
198 }
199 }
200 $this->reload();
201 }
203 /* Load templating engine */
204 $smarty= get_smarty();
206 $tmp = $this->plInfo();
207 foreach($tmp['plProvidedAcls'] as $name => $translation){
208 $smarty->assign($name."ACL",$this->getacl($name));
209 }
211 /* Base select dialog */
212 $once = true;
213 foreach($_POST as $name => $value){
214 if(preg_match("/^chooseBase/",$name) && $once && $this->acl_is_moveable()){
215 $once = false;
216 $this->dialog = new baseSelectDialog($this->config,$this,$this->get_allowed_bases());
217 $this->dialog->setCurrentBase($this->base);
218 }
219 }
221 /* Dialog handling */
222 if(is_object($this->dialog) && $this->acl_is_moveable()){
223 /* Must be called before save_object */
224 $this->dialog->save_object();
226 if($this->dialog->isClosed()){
227 $this->dialog = false;
228 }elseif($this->dialog->isSelected()){
230 /* A new base was selected, check if it is a valid one */
231 $tmp = $this->get_allowed_bases();
232 if(isset($tmp[$this->dialog->isSelected()])){
233 $this->base = $this->dialog->isSelected();
234 }
235 $this->dialog= false;
236 }else{
237 return($this->dialog->execute());
238 }
239 }
241 /* Add objects? */
242 if (isset($_POST["edit_membership"])){
243 $this->group_dialog= TRUE;
244 $this->dialog= TRUE;
245 }
247 /* Add objects finished? */
248 if (isset($_POST["add_object_finish"]) || isset($_POST["add_object_cancel"])){
249 $this->group_dialog= FALSE;
250 $this->dialog= FALSE;
251 }
253 /* Manage object add dialog */
254 if ($this->group_dialog){
256 /* Save data */
257 $ogfilter= session::get("ogfilter");
258 foreach( array("dselect", "regex") as $type){
259 if (isset($_POST[$type])){
260 $ogfilter[$type]= $_POST[$type];
261 }
262 }
263 if (isset($_POST['dselect'])){
264 foreach( array("accounts", "groups", "applications", "departments",
265 "servers", "workstations", "winstations", "terminals", "printers","subtrees",
266 "phones") as $type){
268 if (isset($_POST[$type])) {
269 $ogfilter[$type]= "checked";
270 } else {
271 $ogfilter[$type]= "";
272 }
273 }
274 }
275 if (isset($_GET['search'])){
276 $s= mb_substr($_GET['search'], 0, 1, "UTF8")."*";
277 if ($s == "**"){
278 $s= "*";
279 }
280 $ogfilter['regex']= $s;
281 }
282 session::set("ogfilter", $ogfilter);
283 $this->reload();
285 /* Calculate actual groups */
286 $smarty->assign("objects", $this->convert_list($this->objects));
288 /* Show dialog */
289 $smarty->assign("search_image", get_template_path('images/search.png'));
290 $smarty->assign("launchimage", get_template_path('images/small_filter.png'));
291 $smarty->assign("tree_image", get_template_path('images/tree.png'));
292 $smarty->assign("deplist", $this->config->idepartments);
293 $smarty->assign("alphabet", generate_alphabet());
294 foreach( array("dselect", "regex", "accounts", "groups", "applications",
295 "departments", "servers", "workstations", "winstations", "terminals","subtrees",
296 "printers", "phones") as $type){
297 $smarty->assign("$type", $ogfilter[$type]);
298 }
299 $smarty->assign("hint", print_sizelimit_warning());
300 $smarty->assign("apply", apply_filter());
302 $display= $smarty->fetch (get_template_path('ogroup_objects.tpl', TRUE, dirname(__FILE__)));
303 return ($display);
304 }
306 /* Bases / Departments */
307 if ((isset($_POST['base'])) && ($this->acl_is_moveable())){
308 $this->base= $_POST['base'];
309 }
311 /* Assemble combine string */
312 if ($this->gosaGroupObjects == "[]"){
313 $smarty->assign("combinedObjects", _("none"));
314 } elseif (strlen($this->gosaGroupObjects) > 4){
315 $smarty->assign("combinedObjects", "<font color=red>"._("too many different objects!")."</font>");
316 } else {
317 $conv= array( "U" => _("users"),
318 "G" => _("groups"),
319 "A" => _("applications"),
320 "D" => _("departments"),
321 "S" => _("servers"),
322 "W" => _("workstations"),
323 "O" => _("winstations"),
324 "T" => _("terminals"),
325 "F" => _("phones"),
326 "P" => _("printers"));
328 $type= preg_replace('/[\[\]]/', '', $this->gosaGroupObjects);
329 $p1= $conv[$type[0]];
330 error_reporting(0);
331 if (isset($type[1]) && preg_match('/[UGADSFOWTP]/', $type[1])){
332 $p2= $conv[$type[1]];
333 $smarty->assign("combinedObjects", "$p1 "._("and")." $p2");
334 } else {
335 $smarty->assign("combinedObjects", "$p1");
336 }
337 error_reporting(E_ALL | E_STRICT);
338 }
340 /* Assign variables */
341 $smarty->assign("bases", $this->get_allowed_bases());
342 $smarty->assign("base_select", $this->base);
343 $smarty->assign("department", $this->department);
344 $smarty->assign("members", $this->convert_list($this->memberList));
346 /* Objects have to be tuned... */
347 $smarty->assign("objects", $this->convert_list($this->objects));
349 /* Fields */
350 foreach ($this->attributes as $val){
351 $smarty->assign("$val", $this->$val);
352 }
354 return ($smarty->fetch (get_template_path('generic.tpl', TRUE)));
355 }
358 /* Save data to object */
359 function save_object()
360 {
361 /* Save additional values for possible next step */
362 if (isset($_POST['ogroupedit'])){
364 /* Create a base backup and reset the
365 base directly after calling plugin::save_object();
366 Base will be set seperatly a few lines below */
367 $base_tmp = $this->base;
368 plugin::save_object();
369 $this->base = $base_tmp;
371 /* Save base, since this is no LDAP attribute */
372 $tmp = $this->get_allowed_bases();
373 if(isset($_POST['base'])){
374 if(isset($tmp[$_POST['base']])){
375 $this->base= $_POST['base'];
376 }
377 }
378 }
379 }
382 /* (Re-)Load objects */
383 function reload()
384 {
385 /*###########
386 Variable initialisation
387 ###########*/
389 $this->objects = array();
390 $this->ui = get_userinfo();
391 $filter = "";
392 $objectClasses = array();
394 $ogfilter = session::get("ogfilter");
395 $regex = $ogfilter['regex'];
397 $ldap= $this->config->get_ldap_link();
398 $ldap->cd ($ogfilter['dselect']);
401 /*###########
402 Generate Filter
403 ###########*/
405 $p_f= array("accounts" => array("CLASS"=>"gosaAccount" ,"DN"=> get_people_ou() ,"ACL" => "users"),
406 "groups" => array("CLASS"=>"posixGroup" ,"DN"=> get_groups_ou('ogroupou') ,"ACL" => "groups"),
407 "applications" => array("CLASS"=>"gosaApplication","DN"=> get_ou('applicationou') ,"ACL" => "application"),
408 "departments" => array("CLASS"=>"gosaDepartment" ,"DN"=> "" ,"ACL" => "department"),
409 "servers" => array("CLASS"=>"goServer" ,"DN"=> get_ou('serverou') ,"ACL" => "server"),
410 "workstations" => array("CLASS"=>"gotoWorkstation","DN"=> get_ou('workstationou') ,"ACL" => "workstation"),
411 "winstations" => array("CLASS"=>"opsiClient", "DN"=> get_ou('WINSTATIONS') ,"ACL" => "winstation"),
412 "terminals" => array("CLASS"=>"gotoTerminal" ,"DN"=> get_ou('terminalou') ,"ACL" => "terminal"),
413 "printers" => array("CLASS"=>"gotoPrinter" ,"DN"=> get_ou('printerou') ,"ACL" => "printer"),
414 "phones" => array("CLASS"=>"goFonHardware" ,"DN"=> get_ou('phoneou') ,"ACL" => "phone"));
417 /*###########
418 Perform search for selected objectClasses & regex to fill list with objects
419 ###########*/
421 $Get_list_flags = 0;
422 if($ogfilter['subtrees'] == "checked"){
423 $Get_list_flags |= GL_SUBSEARCH;
424 }
426 foreach($p_f as $post_name => $data){
428 if($ogfilter[$post_name] == "checked"){
430 if($ogfilter['subtrees']){
431 $base = $ogfilter['dselect'];
432 }else{
433 $base = $data['DN'].$ogfilter['dselect'];
434 }
436 $filter = "(&(objectClass=".$data['CLASS'].")(|(uid=$regex)(cn=$regex)(ou=$regex)))";
437 $res = get_list($filter, $data['ACL'] , $base,
438 array("description", "objectClass", "sn", "givenName", "uid","ou","cn"),$Get_list_flags);
440 /* fetch results and append them to the list */
441 foreach($res as $attrs){
443 $type= $this->getObjectType($attrs);
444 $name= $this->getObjectName($attrs);
446 /* Fill array */
447 if (isset($attrs["description"][0])){
448 $this->objects[$attrs["dn"]]= array("text" => "$name [".$attrs["description"][0]."]", "type" => "$type");
449 } elseif (isset($attrs["uid"][0])) {
450 $this->objects[$attrs["dn"]]= array("text" => "$name [".$attrs["uid"][0]."]", "type" => "$type");
451 } else {
452 $this->objects[$attrs["dn"]]= array("text" => "$name", "type" => "$type");
453 }
454 }
455 }
456 }
457 uasort ($this->objects, 'sort_list');
458 reset ($this->objects);
461 /*###########
462 Build member list and try to detect obsolete entries
463 ###########*/
465 $this->memberList = array();
467 /* Walk through all single member entry */
468 foreach($this->member as $dn){
470 /* The dn for the current member can't be resolved
471 it seams that this entry was removed
472 */
473 /* Try to resolv the entry again, if it still fails, display error msg */
474 $ldap->cat($dn, array("cn", "sn", "givenName", "ou", "description", "objectClass"));
476 /* It has failed, add entry with type flag I (Invalid)*/
477 if ($ldap->error != "Success"){
478 $this->memberList[$dn]= array('text' => _("Non existing dn:")." ".@LDAP::fix($dn),"type" => "I");
480 } else {
482 /* Append this entry to our all object list */
484 /* Fetch object */
485 $attrs= $ldap->fetch();
487 $type= $this->getObjectType($attrs);
488 $name= $this->getObjectName($attrs);
490 if (isset($attrs["description"][0])){
491 $this->objcache[$attrs["dn"]]= array("text" => "$name [".$attrs["description"][0]."]", "type" => "$type");
492 } elseif (isset($attrs["uid"][0])) {
493 $this->objcache[$attrs["dn"]]= array("text" => "$name [".$attrs["uid"][0]."]", "type" => "$type");
494 } else {
495 $this->objcache[$attrs["dn"]]= array("text" => "$name", "type" => "$type");
496 }
497 $this->objcache[$attrs["dn"]]['objectClass'] = $attrs['objectClass'];
498 if(isset($attrs['uid'])){
499 $this->objcache[$attrs["dn"]]['uid'] = $attrs['uid'];
500 }
502 /* Fill array */
503 if (isset($attrs["description"][0])){
504 $this->objects[$attrs["dn"]]= array("text" => "$name [".$attrs["description"][0]."]", "type" => "$type");
505 } else {
506 $this->objects[$attrs["dn"]]= array("text" => "$name", "type" => "$type");
507 }
509 $this->memberList[$dn]= $this->objects[$attrs["dn"]];
510 }
511 }
512 uasort ($this->memberList, 'sort_list');
513 reset ($this->memberList);
515 /* Assemble types of currently combined objects */
516 $objectTypes= "";
517 foreach ($this->memberList as $dn => $desc){
519 /* Invalid object? */
520 if ($desc['type'] == 'I'){
521 continue;
522 }
524 /* Fine. Add to list. */
525 if (!preg_match('/'.$desc['type'].'/', $objectTypes)){
526 $objectTypes.= $desc['type'];
527 }
528 }
529 $this->gosaGroupObjects= "[$objectTypes]";
530 }
533 function convert_list($input)
534 {
535 $temp= "";
536 $conv= array( "U" => "select_user.png",
537 "G" => "select_groups.png",
538 "A" => "select_application.png",
539 "D" => "select_department.png",
540 "S" => "select_server.png",
541 "W" => "select_workstation.png",
542 "O" => "select_winstation.png",
543 "T" => "select_terminal.png",
544 "F" => "select_phone.png",
545 "I" => "flag.png",
546 "P" => "select_printer.png");
548 foreach ($input as $key => $value){
549 /* Generate output */
550 $temp.= "<option title='".addslashes( $key)."' value=\"$key\" class=\"select\" style=\"background-image:url('".get_template_path("images/".$conv[$value['type']])."');\">".$value['text']."</option>\n";
551 }
553 return ($temp);
554 }
557 function getObjectType($attrs)
558 {
559 $type= "I";
561 foreach(array( "U" => "gosaAccount",
562 "G" => "posixGroup",
563 "A" => "gosaApplication",
564 "D" => "gosaDepartment",
565 "S" => "goServer",
566 "W" => "gotoWorkstation",
567 "O" => "opsiClient",
568 "T" => "gotoTerminal",
569 "F" => "goFonHardware",
570 "P" => "gotoPrinter") as $index => $class){
571 if (in_array($class, $attrs['objectClass'])){
572 $type= $index;
573 break;
574 }
575 }
577 return ($type);
578 }
581 function getObjectName($attrs)
582 {
583 /* Person? */
584 $name ="";
585 if (in_array('gosaAccount', $attrs['objectClass'])){
586 if(isset($attrs['sn']) && isset($attrs['givenName'])){
587 $name= $attrs['sn'][0].", ".$attrs['givenName'][0];
588 } else {
589 $name= $attrs['uid'][0];
590 }
591 } else {
592 if(isset($attrs["cn"][0])) {
593 $name= $attrs['cn'][0];
594 } else {
595 $name= $attrs['ou'][0];
596 }
597 }
599 return ($name);
600 }
603 function check()
604 {
605 /* Call common method to give check the hook */
606 $message= plugin::check();
608 /* Permissions for that base? */
609 if ($this->base != ""){
610 $new_dn= 'cn='.$this->cn.','.get_ou('ogroupou').$this->base;
611 } else {
612 $new_dn= $this->dn;
613 }
616 $ldap = $this->config->get_ldap_link();
617 if($this->dn != $new_dn){
618 $ldap->cat ($new_dn, array('dn'));
619 }
621 if($ldap->count() !=0){
622 $message[]= _("There is already an object with this cn.");
623 }
625 /* Set new acl base */
626 if($this->dn == "new") {
627 $this->set_acl_base($this->base);
628 }
630 /* must: cn */
631 if ($this->cn == ""){
632 $message[]= "The required field 'Name' is not set.";
633 }
635 /* To many different object types? */
636 if (strlen($this->gosaGroupObjects) > 4){
637 $message[]= _("You can combine two different object types at maximum only!");
638 }
640 return ($message);
641 }
644 /* Save to LDAP */
645 function save()
646 {
647 plugin::save();
649 /* Move members to target array */
650 $this->attrs['member'] =array();
651 foreach ($this->member as $key => $desc){
652 $this->attrs['member'][]= @LDAP::fix($key);
653 }
655 $ldap= $this->config->get_ldap_link();
657 /* New accounts need proper 'dn', propagate it to remaining objects */
658 if ($this->dn == 'new'){
659 $this->dn= 'cn='.$this->cn.','.get_ou('ogroupou').$this->base;
660 }
662 /* Save data. Using 'modify' implies that the entry is already present, use 'add' for
663 new entries. So do a check first... */
664 $ldap->cat ($this->dn, array('dn'));
665 if ($ldap->fetch()){
666 /* Modify needs array() to remove values :-( */
667 if (!count ($this->member)){
668 $this->attrs['member']= array();
669 }
670 $mode= "modify";
672 } else {
673 $mode= "add";
674 $ldap->cd($this->config->current['BASE']);
675 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $this->dn));
676 }
678 /* Write back to ldap */
679 $ldap->cd($this->dn);
680 $this->cleanup();
681 $ldap->$mode($this->attrs);
683 if($mode == "add"){
684 new log("create","ogroups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
685 }else{
686 new log("modify","ogroups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
687 }
689 /* Trigger post signal */
690 $this->handle_post_events($mode);
692 $ret= 0;
693 if (show_ldap_error($ldap->get_error(), sprintf(_("Saving of object group/generic with dn '%s' failed."),$this->dn))){
694 $ret= 1;
695 }
697 return ($ret);
698 }
700 function remove_from_parent()
701 {
702 plugin::remove_from_parent();
704 $ldap= $this->config->get_ldap_link();
705 $ldap->rmdir($this->dn);
706 show_ldap_error($ldap->get_error(), sprintf(_("Removing of object group/generic with dn '%s' failed."),$this->dn));
708 new log("remove","ogroups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
710 /* Trigger remove signal */
711 $this->handle_post_events("remove");
712 }
715 function PrepareForCopyPaste($source)
716 {
717 /* Update available object types */
718 if(isset($source['gosaGroupObjects'][0])){
719 $this->gosaGroupObjects = $source['gosaGroupObjects'][0];
720 }
722 /* Reload tabs */
723 $this->parent->reload($this->gosaGroupObjects );
725 /* Reload plugins */
726 foreach($this->parent->by_object as $name => $class ){
727 if(get_class($this) != $name) {
728 $this->parent->by_object[$name]->PrepareForCopyPaste($source);
729 }
730 }
732 /* Load member objects */
733 if (isset($source['member'])){
734 foreach ($source['member'] as $key => $value){
735 if ("$key" != "count"){
736 $value= @LDAP::convert($value);
737 $this->member["$value"]= "$value";
738 }
739 }
740 }
742 }
745 function getCopyDialog()
746 {
747 $smarty = get_smarty();
748 $smarty->assign("cn", $this->cn);
749 $str = $smarty->fetch(get_template_path("paste_generic.tpl",TRUE,dirname(__FILE__)));
750 $ret = array();
751 $ret['string'] = $str;
752 $ret['status'] = "";
753 return($ret);
754 }
756 function saveCopyDialog()
757 {
758 if(isset($_POST['cn'])){
759 $this->cn = $_POST['cn'];
760 }
761 }
764 static function plInfo()
765 {
766 return (array(
767 "plShortName" => _("Generic"),
768 "plDescription" => _("Object group generic"),
769 "plSelfModify" => FALSE,
770 "plDepends" => array(),
771 "plPriority" => 1,
772 "plSection" => array("administration"),
773 "plCategory" => array("ogroups" => array("description" => _("Object groups"),
774 "objectClass" => "gosaGroupOfNames")),
775 "plProvidedAcls"=> array(
776 "cn" => _("Name"),
777 "base" => _("Base"),
778 "description" => _("Description"),
779 "member" => _("Member"))
780 ));
781 }
782 }
784 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
785 ?>