1 <?php
3 /*! \brief A GOsa plugin which generates a device configuration dialog
4 */
5 class ConfigManagement extends management
6 {
8 // Used to render the item-configuration dialog
9 private $TemplateEngine = NULL;
11 // A list of all configured items for this device.
12 private $allConfiguredItems = array();
14 // The currently active item
15 // Add add and remove action will be performed on this item
16 private $currentItem = array();
18 // A baseSelector which will be fed with a simulated
19 // department list. All entries will get a fake base
20 // which can then be used to fill the baseSelector widget.
21 private $itemContainerSelector = NULL;
23 // The currently selected base within management list.
24 private $base ;
26 // This array contains a list of all item-types we can add
27 // to the currently selected item (currentItem)
28 private $addableContainerItems = array();
30 // Due to the fakt that we use a fake 'base/dn' for all items
31 // we've to map this 'base/dn' to the items 'id'.
32 private $mappingBaseToID = array();
34 private $rootItemID;
35 private $itemConfig = array();
36 private $lastItemID = 1;
38 private $rpcError = FALSE;
39 private $initFailed = FALSE;
40 private $invalidInstallMethod = FALSE;
41 private $errorMessage = "";
43 // Some plugin related memebers which are not member of
44 // the management class. See class plugin.
45 public $initTime;
46 public $ignore_account = FALSE;
47 public $pl_notify;
48 public $read_only;
50 public $allItemConfigurations = NULL;
52 /*! \brief Constructs the device configuration plugin
53 * @param Config The GOsa configuration object.
54 */
55 function __construct(&$config, $dn)
56 {
57 // Load the template engine and tell her what template
58 // to use for the HTML it produces.
59 $this->TemplateEngine = new TemplateEngine($config);
60 $this->TemplateEngine->setTemplate('puppet.tpl');
61 $this->config = $config;
63 // Set storage points - We do not have any - We just create a fake list which lists all items
64 $this->storagePoints = array("");
66 // Try to initialize
67 $this->init();
68 $this->rebuildListing();
69 }
72 /*! \brief Sets the installation method to the given method.
73 * Updates the template engine and adds the initial root
74 * object for the selected method.
75 * @param The method to use.
76 * @return TRUE on success else FALSE.
77 */
78 function setInstallMethod($str)
79 {
80 if(!isset($this->allItemConfigurations[$str])){
81 $this->itemConfig = array();
82 $this->invalidInstallMethod =TRUE;
83 $this->errorMessage = sprintf(_("Invalid installation method %s selected!"), bold($str));
84 msg_dialog::display(_("Setup"), $this->errorMessage, ERROR_DIALOG);
85 return(FALSE);
86 }else{
88 $this->itemConfig = $this->allItemConfigurations[$str]['items'];
89 $this->invalidInstallMethod =FALSE;
90 $this->TemplateEngine->load($this->itemConfig);
92 // Detect root item, its name is /
93 $root = NULL;
94 foreach($this->itemConfig as $key => $item){
95 if($item['name'] == '/') {
96 $root = $key;
97 break;
98 }
99 }
100 if(!$root){
101 $this->errorMessage = sprintf(_("Installation method %s is invalid: no root object found!"), bold($str));
102 msg_dialog::display(_("Setup"), $this->errorMessage , ERROR_DIALOG);
103 $this->initFailed = TRUE;
104 $this->itemConfig = array();
105 return(FALSE);
106 }
108 // Set current item to 'root', this is the minimum to get things running.
109 $idRoot = $this->addItem($root,'root',array());
110 $this->rootItemID = $idRoot;
111 $this->setCurrentItem($idRoot);
112 $this->setSelectedListItemID($idRoot);
113 $this->rebuildListing();
114 return(TRUE);
115 }
116 }
119 /*! \brief Intializes this plugin
120 * All available installation methods will be loaded
121 * and populated.
122 */
123 function init()
124 {
125 // Reset erros
126 $this->rpcError = $this->initFailed = FALSE;
128 // Load configuration via rpc.
129 $rpc = $this->config->getRpcHandle();
131 // Populate install methods on success.
132 $res = $rpc->getSupportedInstallMethods();
133 if(!$rpc->success()){
134 $this->rpcError = TRUE;
135 $this->errorMessage = $rpc->get_error();;
136 return;
137 }
138 $this->allItemConfigurations = $res;
139 if(!count($this->allItemConfigurations)){
140 $this->errorMessage = _("No selectable install methods available!");
141 msg_dialog::display(_("Setup"), $this->errorMessage , ERROR_DIALOG);
142 $this->initFailed = TRUE;
143 return;
144 }
145 }
148 /*! \brief Add a new child-item to the currently selected one.
149 *
150 * @param String type The 'type' of the new object, eg. 'KickstartTemplate'
151 * @param String name The 'name' of the new object.
152 * @param Array values The initial values for this object.
153 * @return
154 */
155 function addItem($type,$name, $values = array())
156 {
157 if(!isset($this->itemConfig[$type])){
158 echo "Invalid type {$type}, skipping item!<br>";
159 return;
160 }
162 // Add missing values with the item-type defaults.
163 $allValuesSet = TRUE;
164 foreach($this->itemConfig[$type]['options'] as $oName => $oValue){
165 if(!isset($values[$oName])){
166 $values[$oName] = (isset($oValue['default']))?$oValue['default']:"";
167 }
168 }
170 // Get the currently selected entry, its the parent for the newly
171 // added one.
172 $current = &$this->currentItem;
174 // Create a FAKE base to be able to use the management lists
175 // which are currently ldap and thus dn based.
176 $base = (isset($current['base']))? ",".$current['base'] : '';
177 $base = "{$type}={$name}{$base}";
180 if(isset($this->mappingBaseToID[$base])){
181 echo "Cannot add entry, dn already in use! {$base}<br>";
182 return(null);
183 }
185 // Get next free item slot.
186 $id = ($this->lastItemID ++);
187 $new = array(
188 'base' => $base,
189 'children' => array(),
190 'id' => $id,
191 'type' => $type,
192 'name' => $name,
193 'values' => $values);
195 // Append the entry to the list of all items.
196 $this->allConfiguredItems[$id] = $new;
198 // Create a child referenc, this creates some kind of entry tree.
199 $current['children'][$id] = &$this->allConfiguredItems[$id];
201 // Add entries to the list of base and id mappings
202 // this allows us to easily detect the base for an id and vice versa.
203 $this->mappingBaseToID[$id] = $base;
204 $this->mappingBaseToID[$base] = $id;
205 return($id);
206 }
209 /*! \brief Selects an item as active.
210 * All further add and remove actions will be performed
211 * on the obejcts children.
212 * @param String The 'id' of the item we want to select.
213 * @return
214 */
215 function setCurrentItem($item)
216 {
217 if(!isset($this->allConfiguredItems[$item])){
218 echo "Invalid item name {$name}! Skipping selection!";
219 return;
220 }
222 // Set the new item info.
223 $this->currentItem = &$this->allConfiguredItems[$item];
224 $this->currentItemType = $this->currentItem['type'];
225 $this->currentItemDescriptor = $this->itemConfig[$this->currentItem['type']];
226 }
229 /*! \brief Removes a given item ID.
230 * @param String The 'id' of the item we want to remove.
231 * @return
232 */
233 function removeItem($id, &$data = NULL)
234 {
235 if($data === NULL){
236 $data = &$this->allConfiguredItems;
237 }
239 // Remove the item and its children
240 if(isset($data[$id])){
241 foreach($data[$id]['children'] as $cid => $item){
242 $this->removeItem($cid, $data);
243 }
244 unset($data[$id]);
245 }
247 // Remove to current id from sub entries
248 foreach($data as $key => $item){
249 $this->removeItem($id, $data[$key]['children']);
250 }
251 }
254 /*! \brief Initiate item edit.
255 * An action send from the management list.
256 * @param See management::editEntry
257 * @return
258 */
259 function editEntry($action="",$target=array(),$all=array(),
260 $altTabClass ="", $altTabType = "", $altAclCategory="")
261 {
262 $this->setCurrentItem($target[0]);
263 $this->dialogObject = $this->TemplateEngine;
264 $this->skipFooter = TRUE;
265 $this->dialog = TRUE;
267 // Update the template engine to use another type of item and
268 // some other values.
269 $this->TemplateEngine->setValues($this->currentItemType,$this->currentItem['values']);
270 }
273 /*! \brief Save changes for the currently edited item.
274 */
275 function saveItemChanges()
276 {
277 // Save eventually changed values
278 if($this->currentItem){
280 // Check if everything is fine.
281 $msgs = $this->TemplateEngine->save_object();
282 $msgs = $this->TemplateEngine->check();
283 if(count($msgs)){
284 msg_dialog::displayChecks($msgs);
285 }else{
286 foreach($this->TemplateEngine->getWidgets() as $widget){
287 $this->currentItem['values'][$widget->getName()] = $widget->getValue();
288 }
289 $this->closeDialogs();
290 }
291 }
292 }
295 /*! \brief React on open requests from the management list
296 */
297 function openEntry($action="",$target=array(),$all=array(),
298 $altTabClass ="", $altTabType = "", $altAclCategory="")
299 {
300 $this->setSelectedListItemID($target[0]);
301 }
304 /*! \brief Overridden render method of class mangement.
305 * this allows us to add a release selection box.
306 */
307 function renderList()
308 {
309 // In case of an error abort here.
310 if($this->rpcError || $this->initFailed || $this->invalidInstallMethod){
311 $smarty = get_smarty();
312 $smarty->assign('initFailed', $this->initFailed);
313 $smarty->assign('rpcError', $this->rpcError);
314 $smarty->assign('invalidInstallMethod', $this->invalidInstallMethod);
315 $smarty->assign('error', $this->errorMessage);
316 return($smarty->fetch(get_template_path('failed.tpl', TRUE)));
317 }
319 // Collect item container list to be able to render the fake-base selector
320 if(!$this->itemContainerSelector){
321 $this->itemContainerSelector = new releaseSelector(
322 $this->getContainerList(),
323 $this->base,
324 $this->allConfiguredItems[$this->rootItemID]['base']);
325 }else{
326 $this->itemContainerSelector->setBases($this->getContainerList());
327 }
328 $this->itemContainerSelector->update(true);
329 $this->itemContainerSelector->setBase($this->base);
331 session::set('DEVICE_ITEMS', $this->allConfiguredItems);
332 $this->rebuildListing();
333 $filter = $this->getFilter();
334 $headpage = $this->getHeadpage();
336 $headpage->update();
337 $smarty = get_smarty();
338 $smarty->assign("RELEASE", $this->itemContainerSelector->render());
339 $display = $headpage->render();
340 return($this->getHeader().$display);
341 }
344 /*! \brief Build up a list of items useable for the itemSelector.
345 */
346 function getContainerList($array = NULL)
347 {
348 $array = ($array == NULL)?$this->allConfiguredItems[$this->rootItemID]: $array;
349 $ret[$array['base']] = $array['type'];
350 if(count($array['children'])){
351 foreach($array['children'] as $subItem){
352 $ret = array_merge($ret, $this->getContainerList($subItem));
353 }
354 }
355 return($ret);
356 }
359 /*! \brief Update the management class and tell her which
360 * items are available for the itemSelector (baseSelector).
361 */
362 function rebuildListing()
363 {
364 // Build filter
365 if (session::global_is_set(get_class($this)."_filter")){
366 $filter= session::global_get(get_class($this)."_filter");
367 } else {
368 $filter = new filter(get_template_path("DeviceConfig-filter.xml", true));
369 $filter->setObjectStorage($this->storagePoints);
370 }
371 $this->setFilter($filter);
373 // Load service xml file and fill in placeholders
374 $contents =file_get_contents(get_template_path("DeviceConfig-list.xml", true));
376 // Build up device-list configuration
377 $types ="";
378 $images = array();
379 $images[] = 'images/lists/edit.png';
380 $images[] = 'images/caps.png';
381 $images[] = 'images/lists/trash.png';
382 $images[] = 'images/filter.png';
383 $images[] = 'images/find.png';
384 $i = 0;
385 foreach($this->itemConfig as $type => $item){
386 $desc = $item['description'];
387 $img = $images[$i++];
388 $types .=
389 " <objectType>".
390 " <label>{$desc}</label>".
391 " <objectClass>{$type}</objectClass>".
392 " <category>Device</category>".
393 " <class>dummy</class>".
394 " <image>{$img}</image>".
395 " </objectType>";
396 }
397 $contents = preg_replace("/%TYPES%/", $types, $contents);
400 $items = "";
401 $i = 0;
402 foreach($this->addableContainerItems as $item){
403 $desc = $this->itemConfig[$item]['description'];
404 $img = $images[$i++];
405 $items .=
406 "<action>".
407 " <name>add_{$item}</name>".
408 " <type>entry</type>".
409 " <image>{$img}</image>".
410 " <label>{$desc}</label>".
411 "</action>";
412 }
414 if(!empty($items)){
415 $items =
417 "<action>".
418 " <type>sub</type>".
419 " <image>images/lists/element.png[new]</image>".
420 " <label>Create</label>".
421 " {$items}".
422 "</action>";
423 }
425 $contents = preg_replace("/%ITEMS%/", $items, $contents);
427 $headpage = new listing($contents,TRUE);
428 $headpage->setBase($this->base);
429 $headpage->setFilter($filter);
431 parent::__construct($this->config, $this->ui, "services", $headpage);
433 // Register default actions
434 $this->registerAction("new", "newEntry");
435 $this->registerAction("edit", "openEntry"); // !! We forward 'edit' to 'open' to have a department like navigation.
436 $this->registerAction("editEntry", "editEntry");
438 $this->registerAction("saveItemChanges", "saveItemChanges");
439 $this->registerAction("cancelItemEdit", "closeDialogs");
440 $this->registerAction("cancelItemAdd", "closeDialogs");
441 $this->registerAction("saveItemAdd", "saveItemAdd");
442 foreach($this->itemConfig as $name => $item){
443 $this->registerAction("add_{$name}", "newEntry");
444 }
445 }
448 /*! \brief This method intiates the object creation.
449 *
450 * @param String 'action' The name of the action which was the used as trigger.
451 * @param Array 'target' A list of object dns, which should be affected by this method.
452 * @param Array 'all' A combination of both 'action' and 'target'.
453 */
454 function newEntry($action="",$target=array(),$all=array(),
455 $altTabClass ="", $altTabType = "", $altAclCategory="")
456 {
457 $toAdd = preg_replace("/^add_/", "",$action);
458 $itemToAdd = $this->itemConfig[$toAdd];
460 $this->dialogObject = new AddItemDialog($this->config,$toAdd,$itemToAdd);
461 $this->dialog = true;
462 }
465 /*! \brief Saves newly created items and adds them as child to
466 * the currently selected item.
467 */
468 function saveItemAdd()
469 {
470 if(!$this->dialogObject instanceOf AddItemDialog) return;
472 $msgs = $this->dialogObject->save_object();
473 $msgs = $this->dialogObject->check();
474 if(count($msgs)){
475 msg_dialog::displayChecks($msgs);
476 }else{
477 $itemName = $this->dialogObject->getName();
478 $itemCfg = $this->dialogObject->getItemCfg();
479 $itemType = $this->dialogObject->getItemType();
481 $this->setCurrentItem($this->mappingBaseToID[$this->base]);
483 $this->addItem($itemType, $itemName);
484 $this->closeDialogs();
486 # $this->setCurrentItem($itemName);
487 # $this->dialogObject = $this->TemplateEngine;
488 # $this->skipFooter = TRUE;
489 # $this->dialog = TRUE;
491 }
492 }
495 /*! \brief Keep track of posted values, some may be interesting for us.
496 * Tell the template engine to take care of posted values too.
497 * @param String
498 * @return
499 */
500 function save_object()
501 {
502 if(isset($_POST['retryInit'])){
503 $this->init();
504 return;
505 }
507 // Do nothing else in case of an error
508 if($this->rpcError || $this->initFailed) return;
510 // Add sub-module requested.
511 if(isset($_POST['addSubModule']) && isset($_POST['subModule'])){
512 $sub = get_post('subModule');
513 if(in_array($sub, $this->currentItemDescriptor['container'])){
515 // Check if this is a valid item
516 if(!isset($this->itemConfig[$sub])) {
517 echo "Invalid item type '{$sub}'!";
518 $values = array();
519 }else{
520 $values = $this->itemConfig[$sub]['options'];
521 }
522 $name = 'test'.rand(0,99999);
523 $this->addItem($sub,$name,$values);
524 }
525 }
527 // Get the selected item-id from the item list and populate it.
528 if($this->itemContainerSelector){
529 $this->itemContainerSelector->update();
530 $id = $this->mappingBaseToID[$this->itemContainerSelector->getBase()];
531 $this->setSelectedListItemID($id);
532 }
533 }
536 /* \brief Updates the currenlty seleted item in the management list
537 */
538 function setSelectedListItemID($id)
539 {
540 $this->base = $this->mappingBaseToID[$id];
541 $type = $this->allConfiguredItems[$id]['type'];
542 $this->addableContainerItems = $this->itemConfig[$type]['container'];
543 }
546 /*! \brief Forward plugin acls
547 */
548 function set_acl_base($base)
549 {
550 $this->acl_base = $base;
551 }
554 /*! \brief Forward plugin acls
555 */
556 function set_acl_category($category)
557 {
558 $this->acl_category = $category;
559 }
561 function save()
562 {
563 foreach($this->allConfiguredItems as $name => $item){
564 foreach($item['values'] as $oName => $oValue){
565 if(!is_array($oValue)) $oValue = array($oValue);
566 foreach($oValue as $val){
567 echo "<br>{$name} -- <i>{$item['type']}</i>: <b>{$oName}</b>: {$val}";
568 }
569 }
570 }
571 }
574 /*! \brief Initiates the removal for the given entries
575 * and displays a confirmation dialog.
576 *
577 * @param String 'action' The name of the action which was the used as trigger.
578 * @param Array 'target' A list of object dns, which should be affected by this method.
579 * @param Array 'all' A combination of both 'action' and 'target'.
580 */
581 protected function removeEntryRequested($action="",$target=array(),$all=array())
582 {
583 foreach($target as $id){
584 $this->removeItem($id);
585 }
586 }
589 // Inject user actions
590 function detectPostActions()
591 {
592 if($this->rpcError || $this->initFailed) return(array('action' => ''));
594 $action = management::detectPostActions();
595 if(isset($_POST['saveItemEdit'])) $action['action'] = "saveItemChanges";
596 if(isset($_POST['saveItemAdd'])) $action['action'] = "saveItemAdd";
597 if(isset($_POST['cancelItemEdit'])) $action['action'] = "cancelItemEdit";
598 if(isset($_POST['cancelItemAdd'])) $action['action'] = "cancelItemAdd";
599 return($action);
600 }
603 function closeDialogs()
604 {
605 parent::closeDialogs();
606 $this->dialog = false;
607 }
610 function check()
611 {
612 return(array());
613 }
615 function getRootItemId()
616 {
617 return($this->rootItemID);
618 }
621 public static function plInfo()
622 {
623 return (array(
624 "plShortName" => _("Config management"),
625 "plDescription" => _("Config management"),
626 "plSelfModify" => FALSE,
627 "plDepends" => array(),
628 "plPriority" => 0,
629 "plSection" => array("administration"),
630 "plCategory" => array(
631 "ConfigManagement" => array("description" => _("Config management"),
632 "objectClass" => "FAKE_OC_ConfigManagement")),
633 "plProvidedAcls"=> array()
634 ));
635 }
636 }
637 ?>