1 <?php
3 /*! \brief This class allows to manage backend config items and packages.
4 */
5 class newConfigManagement extends plugin
6 {
7 var $initTime;
8 var $plHeadline = "Config management";
9 var $plDescription = "Config management";
11 var $selectedContainer;
13 var $dataModel = NULL;
14 var $listing = NULL;
16 var $cfgItemMap = NULL;
18 var $addableContainerItems = array();
19 var $currentObject = NULL;
22 /*! \brief Initialize the plugin and finally update the data model.
23 */
24 function __construct($config, $dn)
25 {
26 $this->config = &$config;
27 $this->listing = new ConfigManagementListing($this->config, get_userinfo(), $this);
29 // Load the template engine and tell her what template
30 // to use for the HTML it produces.
31 $this->TemplateEngine = new TemplateEngine($config);
33 // Preset item config - with Distribution and Release objects.
34 $items = array();
35 $items['root']['container'] = array('Distribution');
36 $items['root']['name'] = '/';
37 $items['root']['description'] = _('Root');
39 $items['Distribution']['container'] = array('Release');
40 $items['Distribution']['name'] = 'Distribution';
41 $items['Distribution']['description'] = _('Distribution');
42 $items['Distribution']['options']['name']['description'] = _("Name");
43 $items['Distribution']['options']['name']['default'] = "";
44 $items['Distribution']['options']['name']['value'] = "";
45 $items['Distribution']['options']['name']['required'] = true;
46 $items['Distribution']['options']['name']['type'] = 'string';
47 $items['Distribution']['options']['name']['display'] = _('Name');
48 $items['Distribution']['options']['type']['description'] = _("Distribution type");
49 $items['Distribution']['options']['type']['default'] = "deb";
50 $items['Distribution']['options']['type']['value'] = "deb";
51 $items['Distribution']['options']['type']['values'] = array("deb" => 'deb', "rpm" => 'rpm');
52 $items['Distribution']['options']['type']['required'] = true;
53 $items['Distribution']['options']['type']['type'] = 'combobox';
54 $items['Distribution']['options']['type']['display'] = _('Distribution type');
56 $items['Release']['container'] = array('Release', '__CFG_ITEMS__');
57 $items['Release']['name'] = 'Release';
58 $items['Release']['description'] = _('Release');
59 $items['Release']['options']['name']['description'] = _("Name");
60 $items['Release']['options']['name']['default'] = "";
61 $items['Release']['options']['name']['value'] = "";
62 $items['Release']['options']['name']['required'] = true;
63 $items['Release']['options']['name']['type'] = 'string';
64 $items['Release']['options']['name']['display'] = _('Name');
66 $this->installationMethods = array();
67 $this->installationMethods['root']['description'] = _('root');
68 $this->installationMethods['root']['name'] = 'root';
69 $this->installationMethods['root']['title'] = _('root');
70 $this->installationMethods['root']['items']['Distribution'] = &$items['Distribution'];
71 $this->installationMethods['root']['items']['Release'] = &$items['Release'];
72 $this->installationMethods['root']['items']['root'] = &$items['root'];
74 // Request an update of the data model
75 $this->loadInstallationMethods();
76 $this->updateDataModel();
77 $this->listing->setListingTypes($this->getListingTypes());
79 $this->setInstallMethod('root');
81 }
84 /*! \brief Sets the installation method to the given method.
85 * Updates the template engine and adds the initial root
86 * object for the selected method.
87 * @param The method to use.
88 * @return TRUE on success else FALSE.
89 */
90 function setInstallMethod($str)
91 {
92 if(!isset($this->installationMethods[$str])){
93 $this->invalidInstallMethod =TRUE;
94 $this->errorMessage = sprintf(_("Invalid installation method %s selected!"), bold($str));
95 msg_dialog::display(_("Setup"), $this->errorMessage, ERROR_DIALOG);
96 return(FALSE);
97 }else{
99 $this->TemplateEngine->setTemplate($str.".tpl");
100 $this->invalidInstallMethod =FALSE;
101 $this->TemplateEngine->load($this->itemConfig);
102 }
103 }
106 /*! \brief Updates all distributions, releases, packages and items in the dataModel
107 * Load information from the backend.
108 */
109 function updateDataModel()
110 {
111 // Recreate the data model, to have a clean and fresh instance.
112 $this->dataModel = new ConfigManagementDataModel();
114 // Load distributions
115 $rpc = $this->config->getRpcHandle();
116 $res = $rpc->getDistributions();
118 if(!$rpc->success()){
119 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
120 return(NULL);
121 }else{
122 foreach($res as $dist){
123 $dist['__removeable'] = TRUE;
124 $this->dataModel->addItem('Distribution','/root', $dist['name'], $dist);
125 if(isset($dist['releases'])){
126 foreach($dist['releases'] as $release){
127 $distPath = "/root/{$dist['name']}";
128 $release['__removeable'] = TRUE;
129 $this->dataModel->addItem('Release',$distPath, $release['name'], $release);
130 }
131 }
132 }
133 }
134 }
137 /*! \brief Keep track of posted values and populate those
138 * which are interesting for us.
139 * Inspects the _POST and _GET values.
140 */
141 function save_object()
142 {
143 // Update the listing class, this is necessary to get post
144 // actions from it.
145 $this->listing->save_object();
147 // Get the selected distribution and release from the listing widget.
148 $cont = $this->listing->getSelectedContainer();
149 if(isset($_POST['ROOT'])){
150 $this->setCurrentContainer('/root');
151 }elseif(isset($_POST['BACK'])){
153 $path = $this->selectedContainer;
154 if($this->dataModel->itemExistsByPath($path)){
155 $data = $this->dataModel->getItemByPath($path);
156 if($data['parentPath']){
157 $this->setCurrentContainer($data['parentPath']);
158 }
159 }
160 }else{
161 $this->setCurrentContainer($cont);
162 }
163 }
166 /*! \brief Load extended sub-objecte like 'config items' or 'packages'
167 * for the given release path.
168 * @param String The release path to load sub-objects for.
169 * @return NULL
170 */
171 function updateItemList($path)
172 {
173 // Fist get Item and check if it is an release
174 if($this->dataModel->itemExistsByPath($path)){
175 $data = $this->dataModel->getItemByPath($path);
177 // Only releases can contain config-items.
178 if($data['type'] == 'Release' && $data['status'] != "fetched"){
180 $rpc = $this->config->getRpcHandle();
181 $res = $rpc->listConfigItems($data['name']);
182 if(!$rpc->success() || !$res){
183 msg_dialog::display(_("Error"),
184 sprintf(_("Failed to load distributions: %s"),
185 $rpc->get_error()),ERROR_DIALOG);
186 }else{
188 // Sort entries by path length
189 $sLen = array();
190 foreach($res as $itemPath => $type){
191 $sLen[strlen($itemPath)."_".$itemPath] = $itemPath;
192 }
193 uksort($sLen, "strnatcasecmp");
195 // Walk through each entry and then try to add it to the model
196 foreach($sLen as $unused => $itemPath){
198 // Do not add the root element '/'
199 if($itemPath == "/") continue;
201 $type = $res[$itemPath];
203 // Root installation objects do not have a name, so we use 'root' here.
204 $targetPath = trim($path."/".$itemPath);
206 // Remove trailing and duplicated slashes
207 $targetPath = rtrim($targetPath, '/');
208 $targetPath = preg_replace("/\/\/*/","/", $targetPath);
210 // Extract the items name
211 $name = preg_replace("/^.*\//","", $targetPath);
213 // Cleanup the path and then add the item.
214 $targetPath = preg_replace("/[^\/]*$/","", $targetPath);
215 $targetPath = rtrim($targetPath,'/');
216 $this->dataModel->addItem($type, $targetPath, $name,
217 array(
218 '__editable' => TRUE,
219 '__removeable' => TRUE,
220 '__path' => $itemPath,
221 '__release' => $path
222 ),'-' );
223 }
224 $this->dataModel->setItemStatus($path, 'fetched');
225 }
226 }
227 }
228 }
231 /*! \brief Sets the currently selected container and item path.
232 * @param String The path of the container to set.
233 * @param String The path of the item to set.
234 * @return
235 */
236 function setCurrentContainer($cont)
237 {
238 $this->selectedContainer = $cont;
240 // Update list of items within the selected container.
241 $this->updateItemList($this->selectedContainer);
243 // Transfer checked values back to the listing class.
244 $this->listing->setContainers($this->getContainerList());
245 $this->listing->setContainer($cont);
247 // Update the list of addable sub objects
248 $item = $this->dataModel->getItemByPath($cont);
249 if(isset($this->itemConfig[$item['type']]['container'])){
250 $this->addableContainerItems = $this->itemConfig[$item['type']]['container'];
251 }else{
252 $this->addableContainerItems = array();
253 }
254 #print_a($this->installationMethods);
255 #print_a($this->itemConfig);
256 #echo $item['type'];
257 #print_a($this->addableContainerItems);
258 }
261 /*! \brief Generate the HTML content for this plugin.
262 * Actually renders the listing widget..
263 */
264 function execute()
265 {
266 // Get the selected release and store it in a session variable
267 // to allow the configFilter to access it and display the
268 // packages and items.
269 $res = $this->listing->execute();
270 $this->listing->setListingTypes($this->getListingTypes());
271 return($res);
272 }
275 /*! \brief Returns a list of items which will then be displayed
276 * in the management-list.
277 * (The management class calls this method from its execute())
278 * @return Array A list of items/objects for the listing.
279 */
280 function getItemsToBeDisplayed()
281 {
282 $path = $this->selectedContainer;
283 $item = $this->dataModel->getItemByPath($path);
284 return($item);
285 }
288 /*! \brief Returns a simply list of all distributions.
289 * This list will then be used to generate the entries of the
290 * ItemSelectors in the listing class.
291 */
292 function getContainerList()
293 {
294 $data = $this->dataModel->getItemByPath('/root');
295 $res = array();
296 $res["/root"] = array("name" => "/", "desc" => "");
297 $res = array_merge($res,$this->__recurseItem($data, array('Distribution','Release')));
298 return($res);
299 }
302 /*! \brief Recursivly wlks through an item and collects all path and name info.
303 * The reult can then be used to fill the ItemSelector.
304 * @param Array The Item to recurse.
305 * @param Array The type of of objects to collect.
306 * @param String The parent path prefix which should be removed.
307 * @return Array An array containing Array[path] = name
308 */
309 function __recurseItem($item, $types, $parent = "")
310 {
311 $res = array();
312 if(1 || in_array($item['type'], $types)){
313 $path = preg_replace("/".preg_quote($parent,'/')."/","",$item['path']);
314 $res[$path] = array('name' => $item['name'],'desc'=>$item['type']);
315 }
316 if(count($item['children'])){
317 foreach($item['children'] as $child){
318 $res = array_merge($res, $this->__recurseItem($child, $types, $parent));
319 }
320 }
321 return($res);
322 }
325 /*! \brief Intializes this plugin
326 * All available installation methods will be loaded
327 */
328 function loadInstallationMethods()
329 {
330 // Reset erros
331 $this->rpcError = $this->initFailed = FALSE;
333 // Load configuration via rpc.
334 $rpc = $this->config->getRpcHandle();
336 // Populate install methods on success.
337 $res = $rpc->getSupportedInstallMethods();
338 if(!$rpc->success()){
339 $this->rpcError = TRUE;
340 $this->errorMessage = $rpc->get_error();;
341 return;
342 }
343 if(!count($res)){
344 $this->errorMessage = _("No selectable install methods returned!");
345 msg_dialog::display(_("Setup"), $this->errorMessage , ERROR_DIALOG);
346 $this->initFailed = TRUE;
347 return;
348 }else{
349 $this->installationMethods = array_merge($this->installationMethods, $res);
350 $this->cfgItemMap = array();
351 $this->itemConfig = array();
352 $rootElements = array();
353 foreach($this->installationMethods as $method => $items){
354 foreach($items['items'] as $itemName => $item){
355 $this->cfgItemMap[$itemName] = $method;
356 $this->itemConfig[$itemName] = &$this->installationMethods[$method]['items'][$itemName];
358 foreach($item['container'] as $cont){
359 if($cont == "__CFG_ITEMS__"){
360 $this->installationMethods[$method]['items'][$itemName]['container'] = &$rootElements;
361 }
362 }
364 // This enables us to create the first level of config items when
365 // a release is selected.
366 if($item['name'] == "/" && $itemName != 'root'){
367 $rootElements = array_merge($rootElements, $item['container']);
368 }
369 }
370 }
371 }
372 }
375 /*! \brief Returns a info list about all items we can manage,
376 * this used to fill the listings <objectType> settings.
377 * @return Array An array with item info.
378 */
379 function getListingTypes()
380 {
381 $types= array();
382 $types['Distribution']['objectClass'] = 'Distribution';
383 $types['Distribution']['label'] = _('Distribution');
384 $types['Distribution']['image'] = 'images/lists/edit.png';
385 $types['Distribution']['category'] = 'Device';
386 $types['Distribution']['class'] = 'Device';
388 $types['Release']['objectClass'] = 'Release';
389 $types['Release']['label'] = _('Release');
390 $types['Release']['image'] = 'images/lists/delete.png';
391 $types['Release']['category'] = 'Device';
392 $types['Release']['class'] = 'Device';
394 $types['Component']['objectClass'] = 'Component';
395 $types['Component']['label'] = _('Component');
396 $types['Component']['image'] = 'plugins/users/images/select_user.png';
397 $types['Component']['category'] = 'Device';
398 $types['Component']['class'] = 'Device';
400 foreach($this->installationMethods as $method => $items){
401 foreach($items['items'] as $itemName => $item){
402 $types[$itemName]['objectClass'] = $itemName;
403 $types[$itemName]['label'] = $item['name'];
404 $types[$itemName]['image'] = 'plugins/fai/images/fai_script.png';
405 $types[$itemName]['category'] = 'Device';
406 $types[$itemName]['class'] = 'Device';
407 }
408 }
410 return($types);
411 }
414 /*! \brief The plugins ACL and plugin-property definition.
415 * @return
416 */
417 public static function plInfo()
418 {
419 return (array(
420 "plShortName" => _("Config management"),
421 "plDescription" => _("Config management"),
422 "plSelfModify" => FALSE,
423 "plDepends" => array(),
424 "plPriority" => 0,
425 "plSection" => array("administration"),
426 "plCategory" => array(
427 "newConfigManagement" => array("description" => _("Config management"),
428 "objectClass" => "FAKE_OC_newConfigManagement")),
429 "plProvidedAcls"=> array()
430 ));
431 }
434 /*! \brief Acts on open requests.
435 * (This action is received from the ConfigManagementListing class.)
436 * @param Array The items ids. (May contain multiple ids)
437 * @return
438 */
439 function openEntry($ids)
440 {
441 $id = $ids[0];
442 $item = $this->dataModel->getItemById($id);
443 $this->setCurrentContainer($item['path']);
444 return;
445 }
449 /*! \brief Removes an entry from the listing.
450 */
451 function removeEntry($ids)
452 {
454 $item = $this->dataModel->getItemById($ids[0]);
456 // Is an config item.
457 if(isset($this->cfgItemMap[$item['type']])){
458 $release = preg_replace("/^.*\//","",$item['values']['__release']);
459 $path = $item['values']['__path'];
460 $rpc = $this->config->getRpcHandle();
461 $rpc->removeConfigItem($release, $path);
462 if(!$rpc->success()){
463 msg_dialog::display(_("Error"), sprintf(_("Failed to remove: %s"), $rpc->get_error()),ERROR_DIALOG);
464 return(NULL);
465 }else{
466 $this->dataModel->removeItem($item['path']);
467 }
468 }else{
469 echo $item['type']." - are not handled yet!";
470 }
471 }
474 /*! \brief Edits a selected list item.
475 */
476 function editEntry($ids)
477 {
478 // Update the template engine to use another type of item and
479 // some other values.
480 $item = $this->dataModel->getItemById($ids[0]);
481 if(isset($this->cfgItemMap[$item['type']])){
482 $release = preg_replace("/^.*\//","",$item['values']['__release']);
483 $path = $item['values']['__path'];
484 $method = $this->cfgItemMap[$item['type']];
486 // Load item values on demand
487 if($item['status'] == '-'){
488 $rpc = $this->config->getRpcHandle();
489 $item['values']['itemValues'] = $rpc->getConfigItem($release, $path);
490 $this->dataModel->setItemStatus($item['path'], 'fetched');
491 $this->dataModel->setItemValues($item['path'], $item['values']);
492 }
494 $this->setInstallMethod($method);
495 $this->TemplateEngine->setValues($item['type'],$item['values']['itemValues']);
496 $this->listing->setDialogObject($this->TemplateEngine);
497 $this->currentObject = $item;
498 }
499 }
502 /*! \brief Initiates the creation of a new item
503 */
504 function newEntry($type)
505 {
506 // We've to add a config item
507 if(isset($this->cfgItemMap[$type])){
508 $method = $this->cfgItemMap[$type];
509 $this->setInstallMethod($method);
510 $this->TemplateEngine->setValues($type,array());
511 $this->listing->setDialogObject($this->TemplateEngine);
512 $this->currentObject = NULL;
513 }elseif($type == 'Distribution'){
514 $this->setInstallMethod('Distribution');
515 $this->TemplateEngine->setValues($type,array());
516 $this->listing->setDialogObject($this->TemplateEngine);
517 $this->currentObject = NULL;
518 }elseif($type == 'Release'){
519 }
520 }
523 /*! \brief Extracts the item-path out of a path.
524 * e.g. /debian/squeeze/test/module -> /test/module
525 */
526 function getItemPath($fullPath)
527 {
528 $fPath = $fullPath.'/';
529 while(preg_match("/\//", $fPath)){
530 $fPath = preg_replace("/\/[^\/]*$/","", $fPath);
531 $item = $this->dataModel->getItemByPath($fPath);
532 if(isset($this->cfgItemMap[$item['type']])){
533 return(preg_replace("/".preg_quote($item['parentPath'],'/')."/", "", $fullPath));
534 }
535 }
536 return(NULL);
537 }
540 /*! \brief Extracts the releaes path out of a path.
541 * e.g. /debian/squeeze/test/module -> /debian/squeeze
542 */
543 function getReleasePath($fullPath)
544 {
545 $fullPath.='/';
546 while(preg_match("/\//", $fullPath)){
547 $fullPath = preg_replace("/\/[^\/]*$/","", $fullPath);
548 $item = $this->dataModel->getItemByPath($fullPath);
549 if($item['type'] == 'Release'){
550 return($fullPath);
551 }
552 }
553 return(NULL);
554 }
557 function saveItemChanges()
558 {
559 $item = $this->currentObject;
561 // Null means a new object has to be added.
562 if($item == NULL){
564 // Save template engine modifications
565 $this->TemplateEngine->save_object();
566 $release = preg_replace("/^.*\//","", $this->getReleasePath($this->selectedContainer));
567 $type = $this->TemplateEngine->getItemType();
569 // Collect modified values
570 $values = array();
571 foreach($this->TemplateEngine->getWidgets() as $w){
572 $values[$w->getName()] = $w->getValue();
573 }
575 // Create the elements target path
576 $path = $this->getItemPath($this->selectedContainer)."/".$values['name'];
578 // Add the new item
579 $rpc = $this->config->getRpcHandle();
580 $res = $rpc->setConfigItem($release, $path, $type, $values);
581 if(!$rpc->success()){
582 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
583 return(NULL);
584 }else{
586 // We've successfully added the item, now add it to the tree.
587 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],
588 array(
589 '__editable' => TRUE,
590 '__removeable' => TRUE,
591 '__path' => $path,
592 '__release' => $this->getReleasePath($this->selectedContainer)
593 ), '-' );
595 // Finally - close the dialog.
596 $this->listing->clearDialogObject();
597 }
598 }else{
600 // Collect modified values.
601 $this->TemplateEngine->save_object();
602 $values = array();
603 foreach($this->TemplateEngine->getWidgets() as $w){
604 $values[$w->getName()] = $w->getValue();
605 }
607 // Get the items release & path info
608 $release = preg_replace("/^.*\//","",$item['values']['__release']);
609 $path = $item['values']['__path'];
611 // Write the modifications back to the server.
612 $rpc = $this->config->getRpcHandle();
613 $res = $rpc->setConfigItem($release, $path, $item['type'], $values);
614 if(!$rpc->success()){
615 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
616 return(NULL);
617 }else{
619 // Update the data model
620 $item['values']['itemValues'] = $values;
621 $this->dataModel->setItemValues($item['path'], $item['values']);
622 $this->listing->clearDialogObject();
623 }
624 }
625 }
626 function remove_lock() {}
627 }
630 ?>