1 <?php
4 /*! \brief The opsi client base class.
5 This class can be implemented in tow different ways:
6 * as standalone opsi client
7 * as part of the samba tabs
8 both types will be detected automatically.
10 This class allows to edit the properties of an opsi client
11 and its products.
12 */
13 class opsiGeneric extends plugin
14 {
15 /* Contains a list of all available netboot products
16 */
17 private $a_availableNetbootProducts = array();
18 private $a_initial_availableNetbootProducts = array();
19 private $s_selectedNetbootProduct = "";
20 private $s_initial_selectedNetbootProduct = "";
22 /* Contains a list of all available local products
23 */
24 private $a_availableLocalProducts = array();
25 private $a_selectedLocalProducts = array();
26 private $a_initial_selectedLocalProducts = array();
28 /* Internal veriables
29 */
30 private $opsi; // The opsi handle
31 public $parent = NULL; // The parent object (in case of samba)
33 private $hostId = ""; // The host Id of the currently edit opsi host
34 public $mac = ""; // The hosts mac address
35 public $note = ""; // A note
36 public $description = ""; // The client description
38 public $initial_mac = "";
39 public $initial_note = "";
40 public $initial_description = "";
42 private $init_failed = FALSE; // Is true if the opsi communication failed
43 private $standalone = TRUE; // Is true if this is a standlone plugin. (Not samba)
44 private $is_installed= FALSE; // Is true is the hast is already installed.
46 public $attributes = array("mac","note","description");
49 /*! \brief Initialize this class
50 @param Object The GOsa base config.
51 @param String The Id of the host that we want to edit.
52 @param Object The parent object. (in case of samba)
53 */
54 public function __construct(&$config,$hostId,&$parent = NULL)
55 {
56 /* Create opsi handle
57 */
58 $this->opsi = new opsi($config);
59 $this->config = $config;
61 /* Check if we are are part of a windows workstation
62 */
63 $this->parent = $parent;
64 if($parent instanceof wingeneric){
65 $this->standalone = FALSE;
66 }
68 /* Get hostId
69 */
70 if($hostId != "new"){
71 if(preg_match("/^opsi:/",$hostId)){
72 $this->hostId = preg_replace("/^opsi:=([^,]*),.*$/","\\1",$hostId);
73 }elseif($this->parent instanceof wingeneric){
74 $this->hostId = $this->parent->cn;
75 $this->hostId = preg_replace('/\$$/',"",$this->hostId);
76 }
77 }
79 /* Try to plugin */
80 $this->init();
81 }
84 /*! \brief Try to load opsi client informations from the
85 gosa support daemon.
86 */
87 private function init()
88 {
89 $err = FALSE;
90 $this->init_failed = FALSE;
91 $this->initially_was_account = FALSE;
93 /* We are a standalone plugin.
94 */
95 if($this->standalone ) {
96 $this->is_account = TRUE;
97 }
99 /* Try to load client infos from the gosa support daemon
100 */
101 if(!empty($this->hostId)){
103 $list = $this->opsi->list_clients($this->hostId);
104 $err |= $this->opsi->is_error();
106 /* Walk through all returned opsi clients and try to detect
107 one that matches our hostId.
108 #FIXME Implement an opsi method which returns infos for only one opsi client, not all.
109 */
110 foreach($list as $entry){
111 if(preg_match("/^".normalizePreg($this->hostId)."$/i",$entry['NAME'][0]['VALUE'])){
112 $this->initially_was_account = TRUE;
113 $this->is_account = TRUE;
114 foreach(array(
115 "is_installed" => "LASTSEEN",
116 "description" => "DESCRIPTION",
117 "mac" => "MAC",
118 "note" => "NOTES") as $des => $src){
119 $des2 = "initial_".$des;
120 $this->$des2 = $this->$des = $entry[$src][0]['VALUE'];
121 }
122 break;
123 }
124 }
125 }
127 /* Read informations about available netboot products.
128 If not already done, before.
129 */
130 if(!$err && !count($this->a_availableNetbootProducts)){
131 $this->a_availableNetbootProducts = $this->opsi->get_netboot_products();
132 ksort($this->a_availableNetbootProducts);
133 $err |= $this->opsi->is_error();
134 }
136 /* Read informations about available netboot products.
137 If not already done, before.
138 */
139 if(!$err && !count($this->a_availableLocalProducts)) {
140 $this->a_availableLocalProducts = $this->opsi->get_local_products();
141 ksort($this->a_availableLocalProducts);
142 $err |= $this->opsi->is_error();
143 }
145 /* Get products selected by this host.
146 */
147 if(!$err && !empty($this->hostId)) {
148 $tmp = array_keys($this->opsi->get_netboot_products($this->hostId));
149 $err |= $this->opsi->is_error();
150 if(count($tmp) && !$err && !isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
151 $this->s_selectedNetbootProduct = $tmp[0];
153 /* Read configuration for "Netboot Products" */
154 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct])){
155 $CFG = $this->opsi->get_product_properties($this->s_selectedNetbootProduct,$this->hostId);
156 $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'] = $CFG;
157 }
158 }
159 $err |= $this->opsi->is_error();
160 }
162 /* Get all selected local products
163 */
164 if(!$err && !empty($this->hostId) && !count($this->a_selectedLocalProducts)) {
165 $tmp = $this->opsi->get_local_products($this->hostId);
166 $err |= $this->opsi->is_error();
167 $this->a_selectedLocalProducts = $tmp;
168 }
170 /* Load product configuration for all already selected products.
171 */
172 if(!$err && !empty($this->hostId)) {
173 foreach($this->a_selectedLocalProducts as $name => $data){
174 if(!$err && !isset($this->a_selectedLocalProducts[$name]['CFG'])){
175 $CFG = $this->opsi->get_product_properties($name,$this->hostId);
176 $err |= $this->opsi->is_error();
177 $this->a_selectedLocalProducts[$name]['CFG'] = $CFG;
178 }
179 }
180 }
182 /* Check if everything went fine else reset everything and display a retry button
183 */
184 if($err){
185 $this->init_failed = TRUE;
187 }else{
189 /* Remember initial settings */
190 $this->a_initial_selectedLocalProducts = $this->a_selectedLocalProducts;
191 $this->s_initial_selectedNetbootProduct = $this->s_selectedNetbootProduct;
192 $this->a_initial_availableNetbootProducts = $this->a_availableNetbootProducts;
194 /* Ensure that a valid netboot is selected product is.
195 */
196 if(empty($this->s_selectedNetbootProduct)){
197 $this->s_selectedNetbootProduct = key($this->a_availableNetbootProducts);
198 }
199 }
200 }
203 /*! \brief Check given data.
204 @return Array Returns an array with all issues.
205 */
206 public function check()
207 {
208 $messages = plugin::check();
210 if(empty($this->hostId)){
211 $messages[] = msgPool::required(_("Name"));
212 }elseif(!preg_match("/\./",$this->hostId)){
214 /* The hostId must contain a domain part
215 */
216 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
217 _("The field 'Name' must contain a domain part!"));
218 }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
219 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
220 }
222 /* Ensure that the mac address is valid
223 */
224 if(!tests::is_mac($this->mac) || empty($this->mac)){
225 $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
226 }
227 return($messages);
228 }
231 /*! \brief Create the html ui of this plugin
232 @return String HTML content.
233 */
234 public function execute()
235 {
236 $display ="";
238 /* The pluign initialization failed due to communication problems with the gosa daemon.
239 A retry button will be displayed here.
240 */
241 if($this->init_failed){
242 $smarty = get_smarty();
243 $smarty->assign("standalone ", $this->standalone );
244 $smarty->assign("init_failed",TRUE);
245 $smarty->assign("message",$this->opsi->get_error());
246 return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
247 }
250 /* If we are not a stand alone opsi client, we must be a samba client
251 which has the opsi tab enabled.
252 Check if the opsi is added or removed and display state buttons.
253 */
254 if(!$this->standalone ){
256 /* Do we need to flip is_account state? */
257 if(isset($_POST['modify_state'])){
258 if($this->is_account && $this->acl_is_removeable()){
259 $this->is_account= FALSE;
260 }elseif(!$this->is_account && $this->acl_is_createable()){
261 $this->is_account= TRUE;
262 }
263 }
264 if($this->is_account){
265 $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("OPSI")),
266 msgPool::featuresEnabled(_("OPSI")));
267 }else{
268 $display = $this->show_enable_header(msgPool::addFeaturesButton(_("OPSI")),
269 msgPool::featuresDisabled(_("OPSI")));
270 return($display);
271 }
272 }
274 /* Check if we have a sub dialog opened
275 */
276 if(is_object($this->dialog)){
277 $this->dialog->save_object();
278 return($this->dialog->execute());
279 }
281 /* Create HTML output of this plugin
282 */
283 $smarty = get_smarty();
284 $smarty->assign("standalone", $this->standalone );
285 foreach($this->attributes as $attr){
286 $smarty->assign($attr,$this->$attr);
287 }
289 /* Assign ACLs */
290 $tmp = $this->plInfo();
291 foreach($tmp['plProvidedAcls'] as $name => $translated){
292 $smarty->assign($name."ACL",$this->getacl($name));
293 }
295 $smarty->assign("is_installed", $this->is_installed);
296 $smarty->assign("init_failed",FALSE);
297 $divSLP = new divSelectBox();
298 $divALP = new divSelectBox();
300 /* Create list of available local products
301 */
302 foreach($this->a_availableLocalProducts as $name => $data){
303 if(isset($this->a_selectedLocalProducts[$name])) continue;
305 $add_tab = array("string" => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
306 $name_tab = array("string" => $name);
307 $desc_tab = array("string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
308 "attach" => "title='".$data['DESC']."' style='border-right:0px;'");
310 if($this->acl_is_writeable("localProduct")){
311 $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
312 }else{
313 $divALP->AddEntry(array($name_tab,$desc_tab));
314 }
315 }
317 /* Create list of selected local products
318 */
319 ksort($this->a_selectedLocalProducts);
320 if($this->acl_is_readable("localProduct")){
321 foreach($this->a_selectedLocalProducts as $name => $data){
323 $name_tab = array("string" => $name);
324 $desc_tab = array(
325 "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
326 "attach" => "title='".$data['DESC']."'");
328 /* Only display edit button, if there is something to edit
329 */
330 $edit = "<img src='images/empty.png' alt=' '>";
331 if(count($data['CFG'])){
332 $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
333 }
334 $del = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";
336 $opt_tab = array("string" => $edit.$del,
337 "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
339 if($this->acl_is_writeable("localProduct")){
340 $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
341 }else{
342 $divSLP->AddEntry(array($name_tab,$desc_tab));
343 }
344 }
345 }
347 /* Check if netboot product is configurable
348 */
349 $cfg_able =FALSE;
350 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
351 $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
352 $cfg_able &= $this->acl_is_readable("netbootProduct");
353 }
355 $smarty->assign("netboot_configurable",$cfg_able);
356 $smarty->assign("hostId", $this->hostId);
357 $smarty->assign("divSLP", $divSLP->DrawList());
358 $smarty->assign("divALP", $divALP->DrawList());
359 $smarty->assign("SNP", $this->s_selectedNetbootProduct);
360 $smarty->assign("ANP", $this->a_availableNetbootProducts);
361 return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
362 }
365 /*! \brief Save modifications using the gosa support daemon.
366 */
367 public function save()
368 {
370 /* Check if we have to create a new opsi client
371 or just have to save client modifications.
372 */
373 if(!$this->initially_was_account && $this->is_account){
374 $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
375 if($this->opsi->is_error()){
376 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
377 return;
378 }
379 }else{
381 /* Update client modifcations.
382 -Only if necessary
383 */
384 if($this->note != $this->initial_note ||
385 $this->description != $this->initial_description ||
386 $this->mac != $this->initial_mac){
387 $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
388 if($this->opsi->is_error()){
389 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
390 return;
391 }
392 }
393 }
396 /***********
397 Detect local netboot product changes
398 - Check which products were removed.
399 - Check which products were added.
400 ***********/
403 /* Detect which products were removed an which added.
404 */
405 $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
406 $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
408 /* Remove products from client
409 */
410 foreach($del as $name => $data){
411 $this->opsi->del_product_from_client($name,$this->hostId);
412 if($this->opsi->is_error()){
413 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
414 return;
415 }
416 }
418 /* Add products to client
419 And set the product properties.
420 */
421 foreach($add as $name => $data){
422 $this->opsi->add_product_to_client($name,$this->hostId);
423 if($this->opsi->is_error()){
424 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
425 return;
426 }
427 if(!empty($data['CFG'])){
428 $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
429 if($this->opsi->is_error()){
430 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
431 return;
432 }
433 }
434 }
436 /* Save local product properties
437 */
438 foreach($this->a_selectedLocalProducts as $name => $data){
439 if(isset($del[$name]) || isset($add[$name])) continue;
441 /* Update product properties if there are changes
442 */
443 $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
444 if(count($diffs)){
445 $this->opsi->set_product_properties($name,$diffs,$this->hostId);
446 if($this->opsi->is_error()){
447 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
448 return;
449 }
450 }
451 }
453 /*********
454 Detect Netboot product changes
455 - Check if another netboot product was selected.
456 - Check if the product properties were changes.
457 *********/
459 /* Update used netboot product.
460 */
461 if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
462 if(!empty($this->s_initial_selectedNetbootProduct)){
463 $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
464 if($this->opsi->is_error()){
465 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
466 return;
467 }
468 }
469 $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
470 if($this->opsi->is_error()){
471 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
472 return;
473 }
474 }
476 /* Check if we have to update the netboot product properties
477 This is the case, if this product is newly selected.
478 Or if there was at least one configuration attribute modified.
479 */
480 $cfg_1 = $cfg_2 = array();
481 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
482 $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
483 }
484 if(isset($this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
485 $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
486 }
487 $diffs = $this->get_config_changes($cfg_1,$cfg_2);
488 $to_update = array();
489 if( !$this->initially_was_account ||
490 $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
491 $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
492 }elseif(count($diffs)){
493 $to_update = $diffs;
494 }
496 if(count($to_update)){
497 $name = $this->s_selectedNetbootProduct;
498 $this->opsi->set_product_properties($name,$to_update,$this->hostId);
499 if($this->opsi->is_error()){
500 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
501 return;
502 }
503 }
504 }
507 public function get_config_changes($c1,$c2)
508 {
509 /* Get key which are not present in both entries
510 */
511 $res = array();
512 foreach($c2 as $name => $value){
513 if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
514 $res[$name] = $c2[$name];
515 }
516 }
517 foreach($c1 as $name => $value){
518 if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
519 $res[$name] = $c1[$name];
520 }
521 }
522 return($res);
523 }
526 /*! \brief Removes the opsi client
527 */
528 public function remove_from_parent()
529 {
530 $this->opsi->del_client($this->hostId);
531 if($this->opsi->is_error()){
532 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
533 return;
534 }
535 }
538 /*! \brief Save html posts
539 */
540 public function save_object()
541 {
542 /* Init failed; reinit is triggered here.
543 */
544 if(isset($_POST['reinit']) && $this->init_failed){
545 $this->init();
546 }
548 /* Property are currently edited, close the dialog.
549 */
550 if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
551 $this->dialog = NULL;
552 }
554 /* Save product property changes
555 */
556 if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
557 $this->dialog->save_object();
558 $pro = $this->dialog->get_product();
559 $CFG = $this->dialog->get_cfg();
560 if(isset($this->a_selectedLocalProducts[$pro])){
561 if($this->acl_is_writeable("localProduct")){
562 $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
563 }
564 $this->dialog = NULL;
565 }elseif($this->s_selectedNetbootProduct == $pro){
566 if($this->acl_is_writeable("netbootProduct")){
567 $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
568 }
569 $this->dialog = NULL;
570 }else{
571 trigger_error("Fatal, unknown product was configured.");
572 }
573 }
575 /* Save html post
576 */
577 if(isset($_POST['opsiGeneric_posted'])){
579 plugin::save_object();
581 /* Get hostId
582 */
583 if(isset($_POST['hostId']) && $this->standalone && $this->acl_is_writeable("hostId")){
584 $this->hostId = get_post('hostId');
585 }
587 /* Send actions like 'install' or 'wake' to the si server
588 */
589 if($this->acl_is_writeable("triggerAction") &&
590 isset($_POST['opsi_action']) &&
591 isset($_POST['opsi_trigger_action']) &&
592 $this->standalone ){
593 $action = $_POST['opsi_action'];
594 if($action == "install"){
595 $this->install_client();
596 }
597 if($action == "wake"){
598 $this->wake_client();
599 }
600 }
602 /* Get selected netboot product.
603 */
604 if(isset($_POST['opsi_netboot_product']) && $this->acl_is_writeable("netbootProduct")){
605 $SNP = trim($_POST['opsi_netboot_product']);
606 if(isset($this->a_availableNetbootProducts[$SNP])){
607 if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
608 $CFG = $this->opsi->get_product_properties($SNP);
609 $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
610 if($this->opsi->is_error()){
611 $this->init_failed = TRUE;
612 return;
613 }
614 }
615 $this->s_selectedNetbootProduct = $SNP;
616 }
617 }
619 /* Add/remove/edit local products
620 */
621 foreach($_POST as $name => $value){
623 /* Check if netboot product configuration is requested
624 */
625 if(preg_match("/^configure_netboot/",$name) && $this->acl_is_readable("netbootProduct")){
626 $pro = $this->s_selectedNetbootProduct;
627 $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
628 $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
629 break;
630 }
632 /* Add product
633 */
634 if(preg_match("/^add_lp_/",$name) && $this->acl_is_writeable("localProduct")){
635 $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
636 if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
637 $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
638 $CFG = $this->opsi->get_product_properties($product);
639 if($this->opsi->is_error()){
640 $this->init_failed = TRUE;
641 return;
642 }
643 $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
644 }
645 break;
646 }
648 /* Delete product
649 */
650 if(preg_match("/^del_lp_/",$name) && $this->acl_is_writeable("localProduct")){
651 $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
652 if(isset($this->a_selectedLocalProducts[$product])){
653 unset($this->a_selectedLocalProducts[$product]);
654 }
655 break;
656 }
658 /* Edit a product
659 */
660 if(preg_match("/^edit_lp_/",$name) && $this->acl_is_readable("localProduct")){
661 $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
662 $this->dialog = new opsiProperties($this->config,
663 $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
664 break;
665 }
666 }
667 }
668 }
671 /* Triggers client installation
672 */
673 function install_client()
674 {
675 $this->opsi->send_action("install",$this->hostId,$this->mac);
676 if($this->opsi->is_error()){
677 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
678 }
679 }
682 /* Wake up client
683 */
684 function wake_client()
685 {
686 /* Check if we are able to communicate with the GOsa supprot daemon
687 */
688 if(class_available("gosaSupportDaemon")){
689 $o = new gosaSupportDaemon();
690 if($o->connect() && class_available("DaemonEvent_wakeup")){
691 $evt = new DaemonEvent_wakeup($this->config);
692 $evt->add_targets(array($this->mac));
693 $o->append($evt);
694 }
695 }
696 }
699 /* Return plugin informations for acl handling */
700 static function plInfo()
701 {
702 return (array(
703 "plShortName" => _("Generic"),
704 "plDescription" => _("OPSI generic"),
705 "plSelfModify" => FALSE,
706 "plDepends" => array(),
707 "plPriority" => 1,
708 "plSection" => array("administration"),
709 "plCategory" => array("opsi" => array("description" => _("Opsi"),
710 "objectClass" => "dummy_class_opsi")),
712 "plProvidedAcls"=> array(
713 "hostId" => _("Name"),
714 "mac" => _("MAC address"),
715 "description" => _("Description"),
716 "note" => _("Note"),
717 "netbootProduct" => _("Netboot product"),
718 "localProduct" => _("Local product"),
719 "triggerAction" => _("Action"))
720 ));
721 }
722 }
725 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
726 ?>