Code

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