2e8f884724b6eac13050f3de054cfb45b7068e43
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 $parent_mode = 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);
60 /* Check if we are are part of a windows workstation
61 */
62 $this->parent = $parent;
63 if($parent instanceof wingeneric){
64 $this->parent_mode = FALSE;
65 }
67 /* Get hostId
68 */
69 if($hostId != "new"){
70 if(preg_match("/^opsi:/",$hostId)){
71 $this->hostId = preg_replace("/^opsi:=([^,]*),.*$/","\\1",$hostId);
72 }elseif($this->parent instanceof wingeneric){
73 $this->hostId = $this->parent->cn;
74 $this->hostId = preg_replace('/\$$/',"",$this->hostId);
75 }
76 }
78 /* Try to plugin */
79 $this->init();
80 }
83 /*! \brief Try to load opsi client informations from the
84 gosa support daemon.
85 */
86 private function init()
87 {
88 $err = FALSE;
89 $this->init_failed = FALSE;
90 $this->initially_was_account = FALSE;
92 /* Try to load client infos from the gosa support daemon
93 */
94 if(!empty($this->hostId)){
96 $list = $this->opsi->list_clients($this->hostId);
97 $err |= $this->opsi->is_error();
99 /* Walk through all returned opsi clients and try to detect
100 one that matches our hostId.
101 #FIXME Implement an opsi method which returns infos for only one opsi client, not all.
102 */
103 foreach($list as $entry){
104 if(preg_match("/^".normalizePreg($this->hostId)."$/i",$entry['NAME'][0]['VALUE'])){
105 $this->initially_was_account = TRUE;
106 foreach(array(
107 "is_installed" => "LASTSEEN",
108 "description" => "DESCRIPTION",
109 "mac" => "MAC",
110 "note" => "NOTES") as $des => $src){
111 $des2 = "initial_".$des;
112 $this->$des2 = $this->$des = $entry[$src][0]['VALUE'];
113 }
114 break;
115 }
116 }
117 }
119 /* Read informations about available netboot products.
120 If not already done, before.
121 */
122 if(!$err && !count($this->a_availableNetbootProducts)){
123 $this->a_availableNetbootProducts = $this->opsi->get_netboot_products();
124 ksort($this->a_availableNetbootProducts);
125 $err |= $this->opsi->is_error();
126 }
128 /* Read informations about available netboot products.
129 If not already done, before.
130 */
131 if(!$err && !count($this->a_availableLocalProducts)) {
132 $this->a_availableLocalProducts = $this->opsi->get_local_products();
133 ksort($this->a_availableLocalProducts);
134 $err |= $this->opsi->is_error();
135 }
137 /* Get products selected by this host.
138 */
139 if(!$err && !empty($this->hostId)) {
140 $tmp = array_keys($this->opsi->get_netboot_products($this->hostId));
141 $err |= $this->opsi->is_error();
142 if(count($tmp) && !$err && !isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
143 $this->s_selectedNetbootProduct = $tmp[0];
145 /* Read configuration for "Netboot Products" */
146 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct])){
147 $CFG = $this->opsi->get_product_properties($this->s_selectedNetbootProduct,$this->hostId);
148 $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'] = $CFG;
149 }
150 }
151 $err |= $this->opsi->is_error();
152 }
154 /* Get all selected local products
155 */
156 if(!$err && !empty($this->hostId) && !count($this->a_selectedLocalProducts)) {
157 $tmp = $this->opsi->get_local_products($this->hostId);
158 $err |= $this->opsi->is_error();
159 $this->a_selectedLocalProducts = $tmp;
160 }
162 /* Load product configuration for all already selected products.
163 */
164 if(!$err && !empty($this->hostId)) {
165 foreach($this->a_selectedLocalProducts as $name => $data){
166 if(!$err && !isset($this->a_selectedLocalProducts[$name]['CFG'])){
167 $CFG = $this->opsi->get_product_properties($name,$this->hostId);
168 $err |= $this->opsi->is_error();
169 $this->a_selectedLocalProducts[$name]['CFG'] = $CFG;
170 }
171 }
172 }
174 /* Check if everything went fine else reset everything and display a retry button
175 */
176 if($err){
177 $this->init_failed = TRUE;
179 }else{
181 /* Remember initial settings */
182 $this->is_account = TRUE;
183 $this->a_initial_selectedLocalProducts = $this->a_selectedLocalProducts;
184 $this->s_initial_selectedNetbootProduct = $this->s_selectedNetbootProduct;
185 $this->a_initial_availableNetbootProducts = $this->a_availableNetbootProducts;
187 /* Ensure that a valid netboot is selected product is.
188 */
189 if(empty($this->s_selectedNetbootProduct)){
190 $this->s_selectedNetbootProduct = key($this->a_availableNetbootProducts);
191 }
192 }
193 }
196 /*! \brief Check given data.
197 @return Array Returns an array with all issues.
198 */
199 public function check()
200 {
201 return(array());
202 $messages = plugin::check();
204 if(empty($this->hostId)){
205 $messages[] = msgPool::required(_("Name"));
206 }elseif(!preg_match("/\./",$this->hostId)){
208 /* The hostId must contain a domain part
209 */
210 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
211 _("The field 'Name' must contain a domain part!"));
212 }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
213 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
214 }
216 /* Ensure that the mac address is valid
217 */
218 if(!tests::is_mac($this->mac) || empty($this->mac)){
219 $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
220 }
221 return($messages);
222 }
225 /*! \brief Create the html ui of this plugin
226 @return String HTML content.
227 */
228 public function execute()
229 {
230 $display ="";
232 /* The pluign initialization failed due to communication problems with the gosa daemon.
233 A retry button will be displayed here.
234 */
235 if($this->init_failed){
236 $smarty = get_smarty();
237 $smarty->assign("init_failed",TRUE);
238 $smarty->assign("message",$this->opsi->get_error());
239 return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
240 }
242 /* If we are not a stand alone opsi client, we must be a samba client
243 which has the opsi tab enabled.
244 Check if the opsi is added or removed and display state buttons.
245 */
246 if(!$this->parent_mode){
247 if(isset($_POST['modify_state'])){
248 if($this->is_account){
249 $this->is_account= FALSE;
250 }elseif(!$this->is_account){
251 $this->is_account= TRUE;
252 }
253 }
254 if($this->is_account){
255 $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("OPSI")),
256 msgPool::featuresEnabled(_("OPSI")));
257 }else{
258 $display = $this->show_enable_header(msgPool::addFeaturesButton(_("OPSI")),
259 msgPool::featuresDisabled(_("OPSI")));
260 return($display);
261 }
262 }
264 /* Check if we have a sub dialog opened
265 */
266 if(is_object($this->dialog)){
267 $this->dialog->save_object();
268 return($this->dialog->execute());
269 }
271 /* Create HTML output of this plugin
272 */
273 $smarty = get_smarty();
274 foreach($this->attributes as $attr){
275 $smarty->assign($attr,$this->$attr);
276 }
278 $smarty->assign("parent_mode", $this->parent_mode);
279 $smarty->assign("is_installed", $this->is_installed);
280 $smarty->assign("init_failed",FALSE);
281 $divSLP = new divSelectBox();
282 $divALP = new divSelectBox();
284 /* Create list of available local products
285 */
286 foreach($this->a_availableLocalProducts as $name => $data){
287 if(isset($this->a_selectedLocalProducts[$name])) continue;
289 $add_tab = array("string" => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
290 $name_tab = array("string" => $name);
291 $desc_tab = array("string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
292 "attach" => "title='".$data['DESC']."' style='border-right:0px;'");
293 $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
294 }
296 /* Create list of selected local products
297 */
298 ksort($this->a_selectedLocalProducts);
299 foreach($this->a_selectedLocalProducts as $name => $data){
301 $name_tab = array("string" => $name);
302 $desc_tab = array(
303 "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
304 "attach" => "title='".$data['DESC']."'");
306 /* Only display edit button, if there is something to edit
307 */
308 $edit = "<img src='images/empty.png' alt=' '>";
309 if(count($data['CFG'])){
310 $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
311 }
312 $del = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";
314 $opt_tab = array("string" => $edit.$del,
315 "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
316 $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
317 }
319 /* Check if netboot product is configurable
320 */
321 $cfg_able =FALSE;
322 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
323 $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
324 }
326 $smarty->assign("netboot_configurable",$cfg_able);
327 $smarty->assign("hostId", $this->hostId);
328 $smarty->assign("divSLP", $divSLP->DrawList());
329 $smarty->assign("divALP", $divALP->DrawList());
330 $smarty->assign("SNP", $this->s_selectedNetbootProduct);
331 $smarty->assign("ANP", $this->a_availableNetbootProducts);
332 return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
333 }
336 /*! \brief Save modifications using the gosa support daemon.
337 */
338 public function save()
339 {
341 /* Check if we have to create a new opsi client
342 or just have to save client modifications.
343 */
344 if(!$this->initially_was_account && $this->is_account){
345 $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
346 if($this->opsi->is_error()){
347 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
348 return;
349 }
350 }else{
352 /* Update client modifcations.
353 -Only if necessary
354 */
355 if($this->note != $this->initial_note ||
356 $this->description != $this->initial_description ||
357 $this->mac != $this->initial_mac){
358 $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
359 if($this->opsi->is_error()){
360 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
361 return;
362 }
363 }
364 }
367 /***********
368 Detect local netboot product changes
369 - Check which products were removed.
370 - Check which products were added.
371 ***********/
374 /* Detect which products were removed an which added.
375 */
376 $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
377 $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
379 /* Remove products from client
380 */
381 foreach($del as $name => $data){
382 $this->opsi->del_product_from_client($name,$this->hostId);
383 if($this->opsi->is_error()){
384 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
385 return;
386 }
387 }
389 /* Add products to client
390 And set the product properties.
391 */
392 foreach($add as $name => $data){
393 $this->opsi->add_product_to_client($name,$this->hostId);
394 if($this->opsi->is_error()){
395 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
396 return;
397 }
398 if(!empty($data['CFG'])){
399 $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
400 if($this->opsi->is_error()){
401 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
402 return;
403 }
404 }
405 }
407 /* Save local product properties
408 */
409 foreach($this->a_selectedLocalProducts as $name => $data){
410 if(isset($del[$name]) || isset($add[$name])) continue;
412 /* Update product properties if there are changes
413 */
414 $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
415 if(count($diffs)){
416 $this->opsi->set_product_properties($name,$diffs,$this->hostId);
417 if($this->opsi->is_error()){
418 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
419 return;
420 }
421 }
422 }
424 /*********
425 Detect Netboot product changes
426 - Check if another netboot product was selected.
427 - Check if the product properties were changes.
428 *********/
430 /* Update used netboot product.
431 */
432 if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
433 if(!empty($this->s_initial_selectedNetbootProduct)){
434 $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
435 if($this->opsi->is_error()){
436 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
437 return;
438 }
439 }
440 $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
441 if($this->opsi->is_error()){
442 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
443 return;
444 }
445 }
447 /* Check if we have to update the netboot product properties
448 This is the case, if this product is newly selected.
449 Or if there was at least one configuration attribute modified.
450 */
451 $cfg_1 = $cfg_2 = array();
452 if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
453 $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
454 }
455 if(isset($this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
456 $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
457 }
458 $diffs = $this->get_config_changes($cfg_1,$cfg_2);
459 $to_update = array();
460 if( !$this->initially_was_account ||
461 $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
462 $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
463 }elseif(count($diffs)){
464 $to_update = $diffs;
465 }
467 if(count($to_update)){
468 $name = $this->s_selectedNetbootProduct;
469 $this->opsi->set_product_properties($name,$to_update,$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 }
475 }
478 public function get_config_changes($c1,$c2)
479 {
480 /* Get key which are not present in both entries
481 */
482 $res = array();
483 foreach($c2 as $name => $value){
484 if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
485 $res[$name] = $c2[$name];
486 }
487 }
488 foreach($c1 as $name => $value){
489 if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
490 $res[$name] = $c1[$name];
491 }
492 }
493 return($res);
494 }
497 /*! \brief Removes the opsi client
498 */
499 public function remove_from_parent()
500 {
501 $this->opsi->del_client($this->hostId);
502 if($this->opsi->is_error()){
503 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
504 return;
505 }
506 }
509 /*! \brief Save html posts
510 */
511 public function save_object()
512 {
513 /* Init failed; reinit is triggered here.
514 */
515 if(isset($_POST['reinit']) && $this->init_failed){
516 $this->init();
517 }
519 /* Property are currently edited, close the dialog.
520 */
521 if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
522 $this->dialog = NULL;
523 }
525 /* Save product property changes
526 */
527 if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
528 $this->dialog->save_object();
529 $pro = $this->dialog->get_product();
530 $CFG = $this->dialog->get_cfg();
531 if(isset($this->a_selectedLocalProducts[$pro])){
532 $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
533 $this->dialog = NULL;
534 }elseif($this->s_selectedNetbootProduct == $pro){
535 $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
536 $this->dialog = NULL;
537 }else{
538 trigger_error("Fatal, unknown product was configured.");
539 }
540 }
542 /* Save html post
543 */
544 if(isset($_POST['opsiGeneric_posted'])){
546 plugin::save_object();
548 /* Get hostId
549 */
550 if(isset($_POST['hostId']) && $this->parent_mode){
551 $this->hostId = get_post('hostId');
552 }
554 /* Send actions like 'install' or 'wake' to the si server
555 */
556 if(isset($_POST['opsi_action']) && isset($_POST['opsi_trigger_action']) && $this->parent_mode){
557 $action = $_POST['opsi_action'];
558 if($action == "install"){
559 $this->install_client();
560 }
561 }
563 /* Get selected netboot product.
564 */
565 if(isset($_POST['opsi_netboot_product'])){
566 $SNP = trim($_POST['opsi_netboot_product']);
567 if(isset($this->a_availableNetbootProducts[$SNP])){
569 if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
570 $CFG = $this->opsi->get_product_properties($SNP);
571 $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
572 if($this->opsi->is_error()){
573 $this->init_failed = TRUE;
574 return;
575 }
576 }
577 $this->s_selectedNetbootProduct = $SNP;
578 }
579 }
581 /* Add/remove/edit local products
582 */
583 foreach($_POST as $name => $value){
585 /* Check if netboot product configuration is requested
586 */
587 if(preg_match("/^configure_netboot/",$name)){
588 $pro = $this->s_selectedNetbootProduct;
589 $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
590 $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
591 break;
592 }
594 /* Add product
595 */
596 if(preg_match("/^add_lp_/",$name)){
597 $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
598 if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
599 $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
600 $CFG = $this->opsi->get_product_properties($product);
601 if($this->opsi->is_error()){
602 $this->init_failed = TRUE;
603 return;
604 }
605 $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
606 }
607 break;
608 }
610 /* Delete product
611 */
612 if(preg_match("/^del_lp_/",$name)){
613 $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
614 if(isset($this->a_selectedLocalProducts[$product])){
615 unset($this->a_selectedLocalProducts[$product]);
616 }
617 break;
618 }
620 /* Edit a product
621 */
622 if(preg_match("/^edit_lp_/",$name)){
623 $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
624 $this->dialog = new opsiProperties($this->config,
625 $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
626 break;
627 }
628 }
629 }
630 }
633 /* Triggers client installation
634 */
635 function install_client()
636 {
637 $this->opsi->send_action("install",$this->hostId,$this->mac);
638 if($this->opsi->is_error()){
639 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
640 }
641 }
644 /* Return plugin informations for acl handling */
645 static function plInfo()
646 {
647 return (array(
648 "plShortName" => _("Generic"),
649 "plDescription" => _("OPSI generic"),
650 "plSelfModify" => FALSE,
651 "plDepends" => array(),
652 "plPriority" => 1,
653 "plSection" => array("administration"),
654 "plCategory" => array("opsi" => array("description" => _("OPSI client"),
655 "objectClass" => "dummy_class_opsi")),
656 "plProvidedAcls"=> array()
657 ));
658 }
660 }
663 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
664 ?>