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 $s_selectedNetbootProduct = "";
19 private $s_initial_selectedNetbootProduct = "";
21 /* Contains a list of all available local products
22 */
23 private $a_availableLocalProducts = array();
24 private $a_selectedLocalProducts = array();
25 private $a_initial_selectedLocalProducts = array();
27 /* Internal veriables
28 */
29 private $opsi; // The opsi handle
30 public $parent = NULL; // The parent object (in case of samba)
32 private $hostId = ""; // The host Id of the currently edit opsi host
33 public $mac = ""; // The hosts mac address
34 public $note = ""; // A note
35 public $description = ""; // The client description
37 public $initial_mac = "";
38 public $initial_note = "";
39 public $initial_description = "";
41 private $init_failed = FALSE; // Is true if the opsi communication failed
42 private $parent_mode = TRUE; // Is true if this is a standlone plugin. (Not samba)
43 private $is_installed= FALSE; // Is true is the hast is already installed.
45 public $attributes = array("mac","note","description");
48 /*! \brief Initialize this class
49 @param Object The GOsa base config.
50 @param String The Id of the host that we want to edit.
51 @param Object The parent object. (in case of samba)
52 */
53 public function __construct($config,$hostId,&$parent = NULL)
54 {
55 /* Create opsi handle
56 */
57 $this->opsi = new opsi($config);
59 /* Check if we are are part of a windows workstation
60 */
61 $this->parent = $parent;
62 if($parent instanceof wingeneric){
63 $this->parent_mode = FALSE;
64 }
66 /* Get hostId
67 */
68 if($hostId != "new"){
69 if(preg_match("/^opsi:/",$hostId)){
70 $this->hostId = preg_replace("/^opsi:=([^,]*),.*$/","\\1",$hostId);
71 }elseif($this->parent instanceof wingeneric){
72 $this->hostId = $this->parent->cn;
73 $this->hostId = preg_replace('/\$$/',"",$this->hostId);
74 }
75 }
77 /* Try to plugin */
78 $this->init();
79 }
82 /*! \brief Try to load opsi client informations from the
83 gosa support daemon.
84 */
85 private function init()
86 {
87 $err = FALSE;
88 $this->init_failed = FALSE;
89 $this->initially_was_account = FALSE;
91 /* Try to load client infos from the gosa support daemon
92 */
93 if($this->hostId != "new"){
94 $list = $this->opsi->list_clients($this->hostId);
95 $err |= $this->opsi->is_error();
97 /* Walk through all returned opsi clients and try to detect
98 one that matches our hostId.
99 #FIXME Implement an opsi method which returns infos for only one opsi client, not all.
100 */
101 foreach($list as $entry){
102 if(preg_match("/^".normalizePreg($this->hostId)."$/i",$entry['NAME'][0]['VALUE'])){
103 $this->initially_was_account = TRUE;
104 foreach(array(
105 "is_installed" => "LASTSEEN",
106 "description" => "DESCRIPTION",
107 "mac" => "MAC",
108 "note" => "NOTES") as $des => $src){
109 $des2 = "initial_".$des;
110 $this->$des2 = $this->$des = $entry[$src][0]['VALUE'];
111 }
112 break;
113 }
114 }
115 }
117 /* Fetch all product infos from support daemon
118 */
119 if(!$err){
120 $this->a_availableNetbootProducts = $this->opsi->get_netboot_products();
121 $err |= $this->opsi->is_error();
122 }
123 if(!$err) {
124 $this->a_availableLocalProducts = $this->opsi->get_local_products();
125 $err |= $this->opsi->is_error();
126 }
128 /* Get products selected by this host.
129 */
130 if(!$err && !empty($this->hostId)) {
131 $tmp = array_keys($this->opsi->get_netboot_products($this->hostId));
132 if(count($tmp)){
133 $this->s_selectedNetbootProduct = $tmp[0];
134 }
135 $err |= $this->opsi->is_error();
136 }
137 if(!$err && !empty($this->hostId)) {
138 $tmp = $this->opsi->get_local_products($this->hostId);
139 $err |= $this->opsi->is_error();
140 $this->a_selectedLocalProducts = $tmp;
141 }
143 /* Load product configuration for all already selected products.
144 */
145 if(!$err && !empty($this->hostId)) {
146 foreach($this->a_selectedLocalProducts as $name => $data){
147 $CFG = $this->opsi->get_product_properties($name,$this->hostId);
148 $err |= $this->opsi->is_error();
149 $this->a_selectedLocalProducts[$name]['CFG'] = $CFG;
150 }
151 }
153 /* Check if everything went fine else reset everything and display a retry button
154 */
155 if($err){
156 $this->a_availableNetbootProducts = array();
157 $this->s_selectedNetbootProduct = "";
158 $this->s_initial_selectedNetbootProduct = "";
159 $this->a_availableLocalProducts = array();
160 $this->a_selectedLocalProducts = array();
161 $this->a_initial_selectedLocalProducts = array();
162 $this->init_failed = TRUE;
163 }else{
165 /* Remember initial settings */
166 $this->is_account = $this->initially_was_account;
167 $this->a_initial_selectedLocalProducts = $this->a_selectedLocalProducts;
168 $this->s_initial_selectedNetbootProduct = $this->s_selectedNetbootProduct;
169 }
170 }
173 /*! \brief Check given data.
174 @return Array Returns an array with all issues.
175 */
176 public function check()
177 {
178 return(array());
179 $messages = plugin::check();
181 if(empty($this->hostId)){
182 $messages[] = msgPool::required(_("Name"));
183 }elseif(!preg_match("/\./",$this->hostId)){
185 /* The hostId must contain a domain part
186 */
187 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"",
188 _("The client name must contain a domain part (e.g. '.company.de')."));
189 }elseif(preg_match("/[^a-z0-9\.\-_]/",$this->hostId)){
190 $messages[] = msgPool::invalid(_("Name"),$this->hostId,"/[a-z0-9\.\-_]/");
191 }
193 /* Ensure that the mac address is valid
194 */
195 if(!tests::is_mac($this->mac) || empty($this->mac)){
196 $messages[] = msgPool::invalid(_("MAC address"),$this->mac,"","00:0C:7F:31:33:F1");
197 }
198 return($messages);
199 }
202 /*! \brief Create the html ui of this plugin
203 @return String HTML content.
204 */
205 public function execute()
206 {
207 $display ="";
209 /* The pluign initialization failed due to communication problems with the gosa daemon.
210 A retry button will be displayed here.
211 */
212 if($this->init_failed){
213 $smarty = get_smarty();
214 $smarty->assign("init_failed",TRUE);
215 $smarty->assign("message",$this->opsi->get_error());
216 return($smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
217 }
219 /* If we are not a stand alone opsi client, we must be a samba client
220 which has the opsi tab enabled.
221 Check if the opsi is added or removed and display state buttons.
222 */
223 if(!$this->parent_mode){
224 if(isset($_POST['modify_state'])){
225 if($this->is_account){
226 $this->is_account= FALSE;
227 }elseif(!$this->is_account){
228 $this->is_account= TRUE;
229 }
230 }
231 if($this->is_account){
232 $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("Opsi")),
233 msgPool::featuresEnabled(_("Opsi")));
234 }else{
235 $display = $this->show_enable_header(msgPool::addFeaturesButton(_("Opsi")),
236 msgPool::featuresDisabled(_("Opsi")));
237 return($display);
238 }
239 }
241 /* Check if we have a sub dialog opened
242 */
243 if(is_object($this->dialog)){
244 $this->dialog->save_object();
245 return($this->dialog->execute());
246 }
248 /* Create HTML output of this plugin
249 */
250 $smarty = get_smarty();
251 foreach($this->attributes as $attr){
252 $smarty->assign($attr,$this->$attr);
253 }
255 $smarty->assign("parent_mode", $this->parent_mode);
256 $smarty->assign("is_installed", $this->is_installed);
257 $smarty->assign("init_failed",FALSE);
258 $divSLP = new divSelectBox();
259 $divALP = new divSelectBox();
261 /* Create list of available local products
262 */
263 ksort($this->a_availableLocalProducts);
264 foreach($this->a_availableLocalProducts as $name => $data){
265 if(isset($this->a_selectedLocalProducts[$name])) continue;
267 $add_tab = array("string" => "<input type='image' src='images/back.png' name='add_lp_".$name."'>");
268 $name_tab = array("string" => $name);
269 $desc_tab = array("string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
270 "attach" => "title='".$data['DESC']."' style='border-right:0px;'");
271 $divALP->AddEntry(array($add_tab,$name_tab,$desc_tab));
272 }
274 /* Create list of selected local products
275 */
276 ksort($this->a_selectedLocalProducts);
277 foreach($this->a_selectedLocalProducts as $name => $data){
279 $name_tab = array("string" => $name);
280 $desc_tab = array(
281 "string" => "<div style='height: 14px;overflow:hidden;'>".$data['DESC']."</div>",
282 "attach" => "title='".$data['DESC']."'");
284 /* Only display edit button, if there is something to edit
285 */
286 $edit = "<img src='images/empty.png' alt=' '>";
287 if(count($data['CFG'])){
288 $edit = "<input type='image' src='images/lists/edit.png' name='edit_lp_".$name."'>";
289 }
290 $del = "<input type='image' src='images/lists/trash.png' name='del_lp_".$name."'>";
292 $opt_tab = array("string" => $edit.$del,
293 "attach" => "style='border-right:0px; width: 40px; text-align:right;'");
294 $divSLP->AddEntry(array($name_tab,$desc_tab,$opt_tab));
295 }
297 ksort($this->a_availableNetbootProducts);
298 $smarty->assign("hostId", $this->hostId);
299 $smarty->assign("divSLP", $divSLP->DrawList());
300 $smarty->assign("divALP", $divALP->DrawList());
301 $smarty->assign("SNP", $this->s_selectedNetbootProduct);
302 $smarty->assign("ANP", $this->a_availableNetbootProducts);
303 return($display.$smarty->fetch(get_template_path("generic.tpl",TRUE,dirname(__FILE__))));
304 }
307 /*! \brief Save modifications using the gosa support daemon.
308 */
309 public function save()
310 {
312 /* Check if we have to create a new opsi client
313 or just have to save client modifications.
314 */
315 if(!$this->initially_was_account && $this->is_account){
316 $res = $this->opsi->add_client($this->hostId,$this->mac,$this->note,$this->description);
317 if($this->opsi->is_error()){
318 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
319 return;
320 }
321 }else{
323 /* Update client modifcations.
324 -Only if necessary
325 */
326 if($this->note != $this->initial_note ||
327 $this->description != $this->initial_description ||
328 $this->mac != $this->initial_mac){
329 $this->opsi->modify_client($this->hostId,$this->mac,$this->note,$this->description);
330 if($this->opsi->is_error()){
331 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
332 return;
333 }
334 }
335 }
337 /* Detect which products were removed an which added.
338 */
339 $add = array_diff_assoc($this->a_selectedLocalProducts,$this->a_initial_selectedLocalProducts);
340 $del = array_diff_assoc($this->a_initial_selectedLocalProducts,$this->a_selectedLocalProducts);
342 /* Remove products from client
343 */
344 foreach($del as $name => $data){
345 $this->opsi->del_product_from_client($name,$this->hostId);
346 if($this->opsi->is_error()){
347 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
348 return;
349 }
350 }
352 /* Add products to client
353 */
354 foreach($add as $name => $data){
355 $this->opsi->add_product_to_client($name,$this->hostId);
356 if($this->opsi->is_error()){
357 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
358 return;
359 }
360 if(!empty($data['CFG'])){
361 $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
362 if($this->opsi->is_error()){
363 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
364 return;
365 }
366 }
367 }
369 /* Save local product properties
370 */
371 foreach($this->a_selectedLocalProducts as $name => $data){
372 if(isset($del[$name]) || isset($add[$name])) continue;
374 /* Update product properties if there are changes
375 */
376 #if(serialize($data['CFG']) != serialize($this->a_initial_selectedLocalProducts[$name]['CFG'])){
377 if($data['CFG'] != $this->a_initial_selectedLocalProducts[$name]['CFG']){
378 $this->opsi->set_product_properties($name,$data['CFG'],$this->hostId);
379 if($this->opsi->is_error()){
380 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
381 return;
382 }
383 }
384 }
386 /* Update used netboot product.
387 */
388 if($this->s_selectedNetbootProduct != $this->s_initial_selectedNetbootProduct){
389 if(!empty($this->s_initial_selectedNetbootProduct)){
390 $this->opsi->del_product_from_client($this->s_initial_selectedNetbootProduct,$this->hostId);
391 if($this->opsi->is_error()){
392 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
393 return;
394 }
395 }
396 $this->opsi->add_product_to_client($this->s_selectedNetbootProduct,$this->hostId);
397 if($this->opsi->is_error()){
398 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
399 return;
400 }
401 }
402 }
405 /*! \brief Removes the opsi client
406 */
407 public function remove_from_parent()
408 {
409 $this->opsi->del_client($this->hostId);
410 if($this->opsi->is_error()){
411 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
412 return;
413 }
414 }
417 /*! \brief Save html posts
418 */
419 public function save_object()
420 {
421 /* Init failed; reinit is triggered here.
422 */
423 if(isset($_POST['reinit']) && $this->init_failed){
424 $this->init();
425 }
427 /* Property are currently edited, close the dialog.
428 */
429 if(isset($_POST['cancel_properties']) && is_object($this->dialog)){
430 $this->dialog = NULL;
431 }
433 /* Save product property changes
434 */
435 if(isset($_POST['save_properties']) && ($this->dialog instanceof opsiProperties)){
436 $this->dialog->save_object();
437 $pro = $this->dialog->get_product();
438 $CFG = $this->dialog->get_cfg();
439 if(isset($this->a_selectedLocalProducts[$pro])){
440 $this->a_selectedLocalProducts[$pro]['CFG'] = $CFG;
441 }
442 $this->dialog = NULL;
443 }
445 /* Save html post
446 */
447 if(isset($_POST['opsiGeneric_posted'])){
449 plugin::save_object();
451 /* Get hostId
452 */
453 if(isset($_POST['hostId']) && $this->parent_mode){
454 $this->hostId = get_post('hostId');
455 }
457 /* Send actions like 'install' or 'wake' to the si server
458 */
459 if(isset($_POST['opsi_action']) && isset($_POST['opsi_trigger_action']) && $this->parent_mode){
460 $action = $_POST['opsi_action'];
461 if($action == "install"){
462 $this->install_client();
463 }
464 }
466 /* Get selected netboot product.
467 */
468 if(isset($_POST['opsi_netboot_product'])){
469 $SNP = trim($_POST['opsi_netboot_product']);
470 if(isset($this->a_availableNetbootProducts[$SNP])){
471 $this->s_selectedNetbootProduct = $SNP;
472 }
473 }
475 /* Add/remove/edit local products
476 */
477 foreach($_POST as $name => $value){
479 /* Add product
480 */
481 if(preg_match("/^add_lp_/",$name)){
482 $product = preg_replace("/^add_lp_(.*)_.$/","\\1",$name);
483 if(isset($this->a_availableLocalProducts[$product]) && !isset($this->a_selectedLocalProducts[$product])){
484 $this->a_selectedLocalProducts[$product] = $this->a_availableLocalProducts[$product];
485 $CFG = $this->opsi->get_product_properties($product);
486 $this->a_selectedLocalProducts[$product]['CFG'] = $CFG;
487 }
488 break;
489 }
491 /* Delete product
492 */
493 if(preg_match("/^del_lp_/",$name)){
494 $product = preg_replace("/^del_lp_(.*)_.$/","\\1",$name);
495 if(isset($this->a_selectedLocalProducts[$product])){
496 unset($this->a_selectedLocalProducts[$product]);
497 }
498 break;
499 }
501 /* Edit a product
502 */
503 if(preg_match("/^edit_lp_/",$name)){
504 $product = preg_replace("/^edit_lp_(.*)_.$/","\\1",$name);
505 $this->dialog = new opsiProperties($this->config,
506 $product,$this->a_selectedLocalProducts[$product]['CFG'],$this->hostId);
507 break;
508 }
509 }
510 }
511 }
514 /* Triggers client installation
515 */
516 function install_client()
517 {
518 $this->opsi->send_action("install",$this->hostId,$this->mac);
519 if($this->opsi->is_error()){
520 msg_dialog::display(_("Error"),msgPool::siError($this->opsi->get_error()),ERROR_DIALOG);
521 }
522 }
525 /* Return plugin informations for acl handling */
526 static function plInfo()
527 {
528 return (array(
529 "plShortName" => _("Generic"),
530 "plDescription" => _("Opsi generic"),
531 "plSelfModify" => FALSE,
532 "plDepends" => array(),
533 "plPriority" => 1,
534 "plSection" => array("administration"),
535 "plCategory" => array("opsi" => array("description" => _("Opsi client"),
536 "objectClass" => "dummy_class_opsi")),
537 "plProvidedAcls"=> array()
538 ));
539 }
541 }
544 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
545 ?>