fc66b2997f10c210e6d2f071eafa842edfe1927f
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 foreach($this->a_selectedLocalProducts as $name => $data){
322 $name_tab = array("string" => $name);
323 $desc_tab = array(
324 "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
325 "attach" => "title='".$data['DESC']."'");
327 /* Only display edit button, if there is something to edit
328 */
329 $edit = "<img src='images/empty.png' alt=' '>";
330 if(count($data['CFG'])){
331 $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
332 }
333 $del = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";
335 $opt_tab = array("string" => $edit.$del,
336 "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
338 if($this->acl_is_writeable("localProduct")){
339 $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
340 }else{
341 $divSLP->AddEntry(array($name_tab,$desc_tab));
342 }
343 }
345 /* Check if netboot product is configurable
346 */
347 $cfg_able =FALSE;
348 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
349 $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
350 $cfg_able &= $this->acl_is_readable("netbootProduct");
351 }
353 $smarty->assign("netboot_configurable",$cfg_able);
354 $smarty->assign("hostId", $this->hostId);
355 $smarty->assign("divSLP", $divSLP->DrawList());
356 $smarty->assign("divALP", $divALP->DrawList());
357 $smarty->assign("SNP", $this->s_selectedNetbootProduct);
358 $smarty->assign("ANP", $this->a_availableNetbootProducts);
359 return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
360 }
363 /*! \brief Save modifications using the gosa support daemon.
364 */
365 public function save()
366 {
368 /* Check if we have to create a new opsi client
369 or just have to save client modifications.
370 */
371 if(!$this->initially_was_account && $this->is_account){
372 $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
373 if($this->opsi->is_error()){
374 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
375 return;
376 }
377 }else{
379 /* Update client modifcations.
380 -Only if necessary
381 */
382 if($this->note != $this->initial_note ||
383 $this->description != $this->initial_description ||
384 $this->mac != $this->initial_mac){
385 $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
386 if($this->opsi->is_error()){
387 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
388 return;
389 }
390 }
391 }
394 /***********
395 Detect local netboot product changes
396 - Check which products were removed.
397 - Check which products were added.
398 ***********/
401 /* Detect which products were removed an which added.
402 */
403 $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
404 $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
406 /* Remove products from client
407 */
408 foreach($del as $name => $data){
409 $this->opsi->del_product_from_client($name,$this->hostId);
410 if($this->opsi->is_error()){
411 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
412 return;
413 }
414 }
416 /* Add products to client
417 And set the product properties.
418 */
419 foreach($add as $name => $data){
420 $this->opsi->add_product_to_client($name,$this->hostId);
421 if($this->opsi->is_error()){
422 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
423 return;
424 }
425 if(!empty($data['CFG'])){
426 $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
427 if($this->opsi->is_error()){
428 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
429 return;
430 }
431 }
432 }
434 /* Save local product properties
435 */
436 foreach($this->a_selectedLocalProducts as $name => $data){
437 if(isset($del[$name]) || isset($add[$name])) continue;
439 /* Update product properties if there are changes
440 */
441 $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
442 if(count($diffs)){
443 $this->opsi->set_product_properties($name,$diffs,$this->hostId);
444 if($this->opsi->is_error()){
445 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
446 return;
447 }
448 }
449 }
451 /*********
452 Detect Netboot product changes
453 - Check if another netboot product was selected.
454 - Check if the product properties were changes.
455 *********/
457 /* Update used netboot product.
458 */
459 if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
460 if(!empty($this->s_initial_selectedNetbootProduct)){
461 $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
462 if($this->opsi->is_error()){
463 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
464 return;
465 }
466 }
467 $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
468 if($this->opsi->is_error()){
469 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
470 return;
471 }
472 }
474 /* Check if we have to update the netboot product properties
475 This is the case, if this product is newly selected.
476 Or if there was at least one configuration attribute modified.
477 */
478 $cfg_1 = $cfg_2 = array();
479 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
480 $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
481 }
482 if(isset($this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
483 $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
484 }
485 $diffs = $this->get_config_changes($cfg_1,$cfg_2);
486 $to_update = array();
487 if( !$this->initially_was_account ||
488 $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
489 $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
490 }elseif(count($diffs)){
491 $to_update = $diffs;
492 }
494 if(count($to_update)){
495 $name = $this->s_selectedNetbootProduct;
496 $this->opsi->set_product_properties($name,$to_update,$this->hostId);
497 if($this->opsi->is_error()){
498 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
499 return;
500 }
501 }
502 }
505 public function get_config_changes($c1,$c2)
506 {
507 /* Get key which are not present in both entries
508 */
509 $res = array();
510 foreach($c2 as $name => $value){
511 if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
512 $res[$name] = $c2[$name];
513 }
514 }
515 foreach($c1 as $name => $value){
516 if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
517 $res[$name] = $c1[$name];
518 }
519 }
520 return($res);
521 }
524 /*! \brief Removes the opsi client
525 */
526 public function remove_from_parent()
527 {
528 $this->opsi->del_client($this->hostId);
529 if($this->opsi->is_error()){
530 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
531 return;
532 }
533 }
536 /*! \brief Save html posts
537 */
538 public function save_object()
539 {
540 /* Init failed; reinit is triggered here.
541 */
542 if(isset($_POST['reinit']) && $this->init_failed){
543 $this->init();
544 }
546 /* Property are currently edited, close the dialog.
547 */
548 if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
549 $this->dialog = NULL;
550 }
552 /* Save product property changes
553 */
554 if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
555 $this->dialog->save_object();
556 $pro = $this->dialog->get_product();
557 $CFG = $this->dialog->get_cfg();
558 if(isset($this->a_selectedLocalProducts[$pro])){
559 $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
560 $this->dialog = NULL;
561 }elseif($this->s_selectedNetbootProduct == $pro){
562 $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
563 $this->dialog = NULL;
564 }else{
565 trigger_error("Fatal, unknown product was configured.");
566 }
567 }
569 /* Save html post
570 */
571 if(isset($_POST['opsiGeneric_posted'])){
573 plugin::save_object();
575 /* Get hostId
576 */
577 if(isset($_POST['hostId']) && $this->standalone && $this->acl_is_writeable("hostId")){
578 $this->hostId = get_post('hostId');
579 }
581 /* Send actions like 'install' or 'wake' to the si server
582 */
583 if($this->acl_is_writeable("triggerAction") &&
584 isset($_POST['opsi_action']) &&
585 isset($_POST['opsi_trigger_action']) &&
586 $this->standalone ){
587 $action = $_POST['opsi_action'];
588 if($action == "install"){
589 $this->install_client();
590 }
591 }
593 /* Get selected netboot product.
594 */
595 if(isset($_POST['opsi_netboot_product']) && $this->acl_is_writeable("netbootProduct")){
596 $SNP = trim($_POST['opsi_netboot_product']);
597 if(isset($this->a_availableNetbootProducts[$SNP])){
598 if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
599 $CFG = $this->opsi->get_product_properties($SNP);
600 $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
601 if($this->opsi->is_error()){
602 $this->init_failed = TRUE;
603 return;
604 }
605 }
606 $this->s_selectedNetbootProduct = $SNP;
607 }
608 }
610 /* Add/remove/edit local products
611 */
612 foreach($_POST as $name => $value){
614 /* Check if netboot product configuration is requested
615 */
616 if(preg_match("/^configure_netboot/",$name) && $this->acl_is_readable("netbootProduct")){
617 $pro = $this->s_selectedNetbootProduct;
618 $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
619 $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
620 break;
621 }
623 /* Add product
624 */
625 if(preg_match("/^add_lp_/",$name) && $this->acl_is_writeable("localProduct")){
626 $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
627 if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
628 $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
629 $CFG = $this->opsi->get_product_properties($product);
630 if($this->opsi->is_error()){
631 $this->init_failed = TRUE;
632 return;
633 }
634 $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
635 }
636 break;
637 }
639 /* Delete product
640 */
641 if(preg_match("/^del_lp_/",$name) && $this->acl_is_writeable("localProduct")){
642 $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
643 if(isset($this->a_selectedLocalProducts[$product])){
644 unset($this->a_selectedLocalProducts[$product]);
645 }
646 break;
647 }
649 /* Edit a product
650 */
651 if(preg_match("/^edit_lp_/",$name) && $this->acl_is_readable("localProduct")){
652 $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
653 $this->dialog = new opsiProperties($this->config,
654 $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
655 break;
656 }
657 }
658 }
659 }
662 /* Triggers client installation
663 */
664 function install_client()
665 {
666 $this->opsi->send_action("install",$this->hostId,$this->mac);
667 if($this->opsi->is_error()){
668 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
669 }
670 }
673 /* Return plugin informations for acl handling */
674 static function plInfo()
675 {
676 return (array(
677 "plShortName" => _("Generic"),
678 "plDescription" => _("OPSI generic"),
679 "plSelfModify" => FALSE,
680 "plDepends" => array(),
681 "plPriority" => 1,
682 "plSection" => array("administration"),
683 "plCategory" => array("opsi" => array("description" => _("Opsi"),
684 "objectClass" => "dummy_class_opsi")),
686 "plProvidedAcls"=> array(
687 "hostId" => _("Name"),
688 "mac" => _("MAC address"),
689 "description" => _("Description"),
690 "note" => _("Note"),
691 "netbootProduct" => _("Netboot product"),
692 "localProduct" => _("Local product"),
693 "triggerAction" => _("Action"))
694 ));
695 }
696 }
699 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
700 ?>