Code

2e8f884724b6eac13050f3de054cfb45b7068e43
[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(!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];
144       
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     }
173   
174     /* Check if everything went fine else reset everything and display a retry button 
175      */
176     if($err){
177       $this->init_failed = TRUE;
178       
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   {
340     
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     }
388     
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   }
477   
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     }
524   
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         }
593       
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         }
609   
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         }
619       
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   }
663 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
664 ?>