Code

Updated texts
[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 = TRUE;
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       }
191     }
192   }
195   /*! \brief  Check given data.
196       @return Array   Returns an array with all issues.
197    */
198   public function check()
199   {
200     return(array());
201     $messages = plugin::check();
203     if(empty($this->hostId)){
204       $messages[] = msgPool::required(_("Name"));
205     }elseif(!preg_match("/\./",$this->hostId)){
207       /* The hostId must contain a domain part 
208        */
209       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
210           _("The field 'Name' must contain a domain part!"));
211     }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
212       $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
213     }
215     /* Ensure that the mac address is valid
216      */
217     if(!tests::is_mac($this->mac) || empty($this->mac)){
218       $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
219     }
220     return($messages);
221   }
224   /*! \brief  Create the html ui of this plugin
225       @return String  HTML content.
226    */
227   public function execute()
228   {
229     $display ="";
231     /* The pluign initialization failed due to communication problems with the gosa daemon. 
232        A retry button will be displayed here.
233      */
234     if($this->init_failed){
235       $smarty = get_smarty();
236       $smarty->assign("init_failed",TRUE);
237       $smarty->assign("message",$this->opsi->get_error());
238       return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
239     }  
241     /* If we are not a stand alone opsi client, we must be a samba client 
242        which has the opsi tab enabled.
243        Check if the opsi is added or removed and display state buttons.
244      */
245     if(!$this->parent_mode){
246       if(isset($_POST['modify_state'])){
247         if($this->is_account){
248           $this->is_account= FALSE;
249         }elseif(!$this->is_account){
250           $this->is_account= TRUE;
251         }
252       }
253       if($this->is_account){
254         $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("OPSI")), 
255             msgPool::featuresEnabled(_("OPSI")));
256       }else{
257         $display = $this->show_enable_header(msgPool::addFeaturesButton(_("OPSI")), 
258             msgPool::featuresDisabled(_("OPSI")));
259         return($display);
260       } 
261     } 
263     /* Check if we have a sub dialog opened
264      */
265     if(is_object($this->dialog)){
266       $this->dialog->save_object();
267       return($this->dialog->execute());
268     }
270     /* Create HTML output of this plugin
271      */
272     $smarty = get_smarty();
273     foreach($this->attributes as $attr){
274       $smarty->assign($attr,$this->$attr);
275     }
277     $smarty->assign("parent_mode", $this->parent_mode);
278     $smarty->assign("is_installed", $this->is_installed);
279     $smarty->assign("init_failed",FALSE);
280     $divSLP = new divSelectBox();
281     $divALP = new divSelectBox();
283     /* Create list of available local products 
284      */
285     foreach($this->a_availableLocalProducts as $name => $data){
286       if(isset($this->a_selectedLocalProducts[$name])) continue;
288       $add_tab  = array("string"   => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
289       $name_tab = array("string"   => $name);
290       $desc_tab = array("string"   => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
291           "attach"   => "title='".$data['DESC']."' style='border-right:0px;'");
292       $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
293     }
295     /* Create list of selected local products 
296      */
297     ksort($this->a_selectedLocalProducts);
298     foreach($this->a_selectedLocalProducts as $name => $data){
300       $name_tab = array("string"   => $name);
301       $desc_tab = array(
302           "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
303           "attach" => "title='".$data['DESC']."'");
305       /* Only display edit button, if there is something to edit 
306        */
307       $edit = "<img src='images/empty.png' alt=' '>";
308       if(count($data['CFG'])){
309         $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
310       }
311       $del  = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";  
313       $opt_tab  = array("string" => $edit.$del,
314           "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
315       $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
316     }
318     /* Check if netboot product is configurable 
319      */
320     $cfg_able =FALSE;
321     if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
322       $cfg_able = count($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG']);
323     }
325     $smarty->assign("netboot_configurable",$cfg_able);
326     $smarty->assign("hostId", $this->hostId);
327     $smarty->assign("divSLP", $divSLP->DrawList());
328     $smarty->assign("divALP", $divALP->DrawList());
329     $smarty->assign("SNP", $this->s_selectedNetbootProduct);
330     $smarty->assign("ANP", $this->a_availableNetbootProducts);
331     return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
332   }
335   /*! \brief  Save modifications using the gosa support daemon.
336    */
337   public function save()
338   {
339     
340     /* Check if we have to create a new opsi client
341         or just have to save client modifications.
342      */
343     if(!$this->initially_was_account && $this->is_account){
344       $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
345       if($this->opsi->is_error()){
346         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
347         return;
348       }
349     }else{
351       /* Update client modifcations.
352           -Only if necessary  
353        */
354       if($this->note != $this->initial_note || 
355           $this->description != $this->initial_description ||
356           $this->mac != $this->initial_mac){
357         $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
358         if($this->opsi->is_error()){
359           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
360           return;
361         }
362       }
363     }
366     /***********
367       Detect local netboot product changes
368        - Check which products were removed.
369        - Check which products were added. 
370      ***********/
373     /* Detect which products were removed an which added.
374      */
375     $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
376     $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
378     /* Remove products from client
379      */
380     foreach($del as $name => $data){
381       $this->opsi->del_product_from_client($name,$this->hostId);
382       if($this->opsi->is_error()){
383         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
384         return;
385       }
386     }
387     
388     /* Add products to client
389        And set the product properties.
390      */
391     foreach($add as $name => $data){
392       $this->opsi->add_product_to_client($name,$this->hostId);
393       if($this->opsi->is_error()){
394         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
395         return;
396       }
397       if(!empty($data['CFG'])){
398         $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
399         if($this->opsi->is_error()){
400           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
401           return;
402         }
403       }
404     }
406     /* Save local product properties 
407      */
408     foreach($this->a_selectedLocalProducts as $name => $data){
409       if(isset($del[$name]) || isset($add[$name])) continue;
411       /* Update product properties if there are changes 
412        */
413       $diffs = $this->get_config_changes($data['CFG'],$this->a_initial_selectedLocalProducts[$name]['CFG']);
414       if(count($diffs)){
415         $this->opsi->set_product_properties($name,$diffs,$this->hostId);
416         if($this->opsi->is_error()){
417           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
418           return;
419         }
420       }
421     }
423     /*********
424       Detect Netboot product changes
425        - Check if another netboot product was selected. 
426        - Check if the product properties were changes.
427      *********/
429     /* Update used netboot product. 
430      */
431     if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
432       if(!empty($this->s_initial_selectedNetbootProduct)){
433         $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
434         if($this->opsi->is_error()){
435           msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
436           return;
437         }
438       }
439       $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
440       if($this->opsi->is_error()){
441         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);    
442         return;
443       }
444     }
446     /* Check if we have to update the netboot product properties 
447         This is the case, if this product is newly selected.
448         Or if there was at least one configuration attribute modified.
449      */
450     $cfg_1 = $cfg_2 = array();
451     if(isset($this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
452       $cfg_1 = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
453     }
454     if(isset($this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'])){
455       $cfg_2 = $this->a_initial_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
456     }
457     $diffs = $this->get_config_changes($cfg_1,$cfg_2);
458     $to_update = array();
459     if( !$this->initially_was_account || 
460         $this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
461       $to_update = $this->a_availableNetbootProducts[$this->s_selectedNetbootProduct]['CFG'];
462     }elseif(count($diffs)){
463       $to_update = $diffs;
464     }
466     if(count($to_update)){
467       $name = $this->s_selectedNetbootProduct;
468       $this->opsi->set_product_properties($name,$to_update,$this->hostId);
469       if($this->opsi->is_error()){
470         msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
471         return;
472       }
473     }
474   }
476   
477   public function get_config_changes($c1,$c2)
478   {
479     /* Get key which are not present in both entries 
480      */
481     $res = array();
482     foreach($c2 as $name => $value){
483       if(!isset($c1[$name]) || $c1[$name]['DEFAULT'] != $c2[$name]['DEFAULT']){
484         $res[$name] = $c2[$name];
485       }
486     }
487     foreach($c1 as $name => $value){
488       if(!isset($c2[$name]) || $c2[$name]['DEFAULT'] != $c1[$name]['DEFAULT']){
489         $res[$name] = $c1[$name];
490       }
491     }
492     return($res);
493   }
496   /*! \brief  Removes the opsi client 
497    */  
498   public function remove_from_parent()
499   {
500     $this->opsi->del_client($this->hostId);
501     if($this->opsi->is_error()){
502       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
503       return;
504     }
505   }
508   /*! \brief  Save html posts 
509    */
510   public function save_object()
511   {
512     /* Init failed; reinit is triggered here.
513      */
514     if(isset($_POST['reinit']) && $this->init_failed){
515       $this->init();
516     }
518     /* Property are currently edited, close the dialog. 
519      */
520     if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
521       $this->dialog = NULL;
522     }
523   
524     /* Save product property changes 
525      */
526     if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
527       $this->dialog->save_object();
528       $pro = $this->dialog->get_product();
529       $CFG = $this->dialog->get_cfg();
530       if(isset($this->a_selectedLocalProducts[$pro])){
531         $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
532         $this->dialog = NULL;
533       }elseif($this->s_selectedNetbootProduct == $pro){
534         $this->a_availableNetbootProducts[$pro]['CFG'] = $CFG;
535         $this->dialog = NULL;
536       }else{
537         trigger_error("Fatal, unknown product was configured.");
538       }
539     }
541     /* Save html post
542      */
543     if(isset($_POST['opsiGeneric_posted'])){
545       plugin::save_object();
547       /* Get hostId 
548        */
549       if(isset($_POST['hostId']) && $this->parent_mode){
550         $this->hostId = get_post('hostId');
551       }
553       /* Send actions like 'install' or 'wake' to the si server 
554        */
555       if(isset($_POST['opsi_action']) && isset($_POST['opsi_trigger_action']) && $this->parent_mode){
556         $action = $_POST['opsi_action'];
557         if($action == "install"){
558           $this->install_client(); 
559         }
560       }
562       /* Get selected netboot product.
563        */
564       if(isset($_POST['opsi_netboot_product'])){
565         $SNP = trim($_POST['opsi_netboot_product']);
566         if(isset($this->a_availableNetbootProducts[$SNP])){
568           if(!isset($this->a_availableNetbootProducts[$SNP]['CFG'])){
569             $CFG = $this->opsi->get_product_properties($SNP);
570             $this->a_availableNetbootProducts[$SNP]['CFG'] = $CFG;
571             if($this->opsi->is_error()){
572               $this->init_failed = TRUE;
573               return;
574             }
575           }
576           $this->s_selectedNetbootProduct = $SNP;
577         }
578       }
580       /* Add/remove/edit local products 
581        */
582       foreach($_POST as $name => $value){
584         /* Check if netboot product configuration is requested 
585          */
586         if(preg_match("/^configure_netboot/",$name)){
587           $pro = $this->s_selectedNetbootProduct;
588           $cfg = $this->a_availableNetbootProducts[$pro]['CFG'];
589           $this->dialog = new opsiProperties($this->config,$pro,$cfg,$this->hostId);
590           break;
591         }
592       
593         /* Add product 
594          */
595         if(preg_match("/^add_lp_/",$name)){
596           $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
597           if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
598             $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
599             $CFG = $this->opsi->get_product_properties($product);
600             if($this->opsi->is_error()){
601               $this->init_failed = TRUE;
602               return;
603             }
604             $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
605           }
606           break;
607         }
608   
609         /* Delete product 
610          */
611         if(preg_match("/^del_lp_/",$name)){
612           $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
613           if(isset($this->a_selectedLocalProducts[$product])){
614             unset($this->a_selectedLocalProducts[$product]);
615           }
616           break;
617         }
618       
619         /* Edit a product  
620          */
621         if(preg_match("/^edit_lp_/",$name)){
622           $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
623           $this->dialog = new opsiProperties($this->config,
624               $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
625           break;
626         }
627       }   
628     }
629   }
632   /* Triggers client installation 
633    */
634   function install_client()
635   {
636     $this->opsi->send_action("install",$this->hostId,$this->mac);
637     if($this->opsi->is_error()){
638       msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
639     }
640   }
643   /* Return plugin informations for acl handling */
644   static function plInfo()
645   {
646     return (array(
647           "plShortName"   => _("Generic"),
648           "plDescription" => _("OPSI generic"),
649           "plSelfModify"  => FALSE,
650           "plDepends"     => array(),
651           "plPriority"    => 1,
652           "plSection"     => array("administration"),
653           "plCategory"    => array("opsi" => array("description"  => _("OPSI client"),
654                                                      "objectClass"  => "dummy_class_opsi")),
655           "plProvidedAcls"=> array()
656           ));
657   }
662 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
663 ?>