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