Code

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