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 $cfgTypeMap = NULL;
17 var $cfgItemMap = NULL;
19 var $addableContainerItems = array();
20 var $currentObject = NULL;
23 /*! \brief Initialize the plugin and finally update the data model.
24 */
25 function __construct($config, $dn)
26 {
27 $this->config = &$config;
28 $this->listing = new ConfigManagementListing($this->config, get_userinfo(), $this);
30 // Load the template engine and tell her what template
31 // to use for the HTML it produces.
32 $this->TemplateEngine = new TemplateEngine($config);
34 // Request an update of the data model
35 $this->loadInstallationMethods();
36 $this->updateDataModel();
37 $this->listing->setListingTypes($this->getListingTypes());
38 }
41 /*! \brief Sets the installation method to the given method.
42 * Updates the template engine and adds the initial root
43 * object for the selected method.
44 * @param The method to use.
45 * @return TRUE on success else FALSE.
46 */
47 function setInstallMethod($str)
48 {
49 if(!isset($this->installationMethods[$str])){
50 $this->itemConfig = array();
51 $this->invalidInstallMethod =TRUE;
52 $this->errorMessage = sprintf(_("Invalid installation method %s selected!"), bold($str));
53 msg_dialog::display(_("Setup"), $this->errorMessage, ERROR_DIALOG);
54 return(FALSE);
55 }else{
57 $this->TemplateEngine->setTemplate($str.".tpl");
58 $this->itemConfig = $this->installationMethods[$str]['items'];
59 $this->invalidInstallMethod =FALSE;
60 $this->TemplateEngine->load($this->itemConfig);
62 // Detect root item, its name is /
63 $root = NULL;
64 foreach($this->itemConfig as $key => $item){
65 if($item['name'] == '/') {
66 $root = $key;
67 break;
68 }
69 }
70 if(!$root){
71 $this->errorMessage = sprintf(_("Installation method %s is invalid: no root object found!"), bold($str));
72 msg_dialog::display(_("Setup"), $this->errorMessage , ERROR_DIALOG);
73 $this->initFailed = TRUE;
74 $this->itemConfig = array();
75 return(FALSE);
76 }
77 }
78 }
81 /*! \brief Updates all distributions, releases, packages and items in the dataModel
82 * Load information from the backend.
83 */
84 function updateDataModel()
85 {
86 // Recreate the data model, to have a clean and fresh instance.
87 $this->dataModel = new ConfigManagementDataModel();
89 // Load distributions
90 $rpc = $this->config->getRpcHandle();
91 $res = $rpc->getDistributions();
93 if(!$rpc->success()){
94 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
95 return(NULL);
96 }else{
97 $this->cfgTypeMap = array();
98 foreach($res as $dist){
99 $dist['__removeable'] = TRUE;
100 $this->dataModel->addItem('Distribution','/root', $dist['name'], $dist);
101 $this->cfgTypeMap['/root/'.$dist['name']] = $dist['installation_method'];
102 foreach($dist['releases'] as $release){
103 $distPath = "/root/{$dist['name']}";
104 $release['__removeable'] = TRUE;
105 $this->dataModel->addItem('Release',$distPath, $release['name'], $release);
106 }
107 }
108 }
109 }
112 /*! \brief Keep track of posted values and populate those
113 * which are interesting for us.
114 * Inspects the _POST and _GET values.
115 */
116 function save_object()
117 {
118 // Update the listing class, this is necessary to get post
119 // actions from it.
120 $this->listing->save_object();
122 // Get the selected distribution and release from the listing widget.
123 $cont = $this->listing->getSelectedContainer();
124 if(isset($_POST['ROOT'])){
125 $this->setCurrentContainer('/root');
126 }elseif(isset($_POST['BACK'])){
128 $path = $this->selectedContainer;
129 if($this->dataModel->itemExistsByPath($path)){
130 $data = $this->dataModel->getItemByPath($path);
131 if($data['parentPath']){
132 $this->setCurrentContainer($data['parentPath']);
133 }
134 }
135 }else{
136 $this->setCurrentContainer($cont);
137 }
138 }
141 /*! \brief Load extended sub-objecte like 'config items' or 'packages'
142 * for the given release path.
143 * @param String The release path to load sub-objects for.
144 * @return NULL
145 */
146 function updateItemList($path)
147 {
148 // Fist get Item and check if it is an release
149 if($this->dataModel->itemExistsByPath($path)){
150 $data = $this->dataModel->getItemByPath($path);
152 // Only releases can contain config-items.
153 if($data['type'] == 'Release' && $data['status'] != "fetched"){
155 $rpc = $this->config->getRpcHandle();
156 $res = $rpc->listConfigItems($data['name']);
157 if(!$rpc->success()){
158 msg_dialog::display(_("Error"),
159 sprintf(_("Failed to load distributions: %s"),
160 $rpc->get_error()),ERROR_DIALOG);
161 }else{
163 // Sort entries by path length
164 $sLen = array();
165 foreach($res as $itemPath => $type){
166 $sLen[strlen($itemPath)."_".$itemPath] = $itemPath;
167 }
168 uksort($sLen, "strnatcasecmp");
170 // Walk through each entry and then try to add it to the model
171 foreach($sLen as $unused => $itemPath){
173 // Do not add the root element '/'
174 if($itemPath == "/") continue;
176 $type = $res[$itemPath];
178 // Root installation objects do not have a name, so we use 'root' here.
179 $targetPath = trim($path."/".$itemPath);
181 // Remove trailing and duplicated slashes
182 $targetPath = rtrim($targetPath, '/');
183 $targetPath = preg_replace("/\/\/*/","/", $targetPath);
185 // Extract the items name
186 $name = preg_replace("/^.*\//","", $targetPath);
188 // Cleanup the path and then add the item.
189 $targetPath = preg_replace("/[^\/]*$/","", $targetPath);
190 $targetPath = rtrim($targetPath,'/');
191 $this->dataModel->addItem($type, $targetPath, $name,
192 array(
193 '__editable' => TRUE,
194 '__removeable' => TRUE,
195 '__path' => $itemPath,
196 '__release' => $path
197 ),'-' );
198 }
199 $this->dataModel->setItemStatus($path, 'fetched');
200 }
201 }
202 }
203 }
206 /*! \brief Sets the currently selected container and item path.
207 * @param String The path of the container to set.
208 * @param String The path of the item to set.
209 * @return
210 */
211 function setCurrentContainer($cont)
212 {
213 $this->selectedContainer = $cont;
215 // Update list of items within the selected container.
216 $this->updateItemList($this->selectedContainer);
218 // Transfer checked values back to the listing class.
219 $this->listing->setContainers($this->getContainerList());
220 $this->listing->setContainer($cont);
222 // Set the correct installation method for the selected item
223 if(isset($this->cfgTypeMap[$cont])){
224 $method = $this->cfgTypeMap[$cont];
225 $this->setInstallMethod($method);
226 }
228 // Update the list of addable sub objects
229 $item = $this->dataModel->getItemByPath($cont);
230 if(isset($this->itemConfig[$item['type']]['container'])){
231 $this->addableContainerItems = $this->itemConfig[$item['type']]['container'];
232 }else{
233 $this->addableContainerItems = array();
234 }
235 }
238 /*! \brief Generate the HTML content for this plugin.
239 * Actually renders the listing widget..
240 */
241 function execute()
242 {
243 // Get the selected release and store it in a session variable
244 // to allow the configFilter to access it and display the
245 // packages and items.
246 $res = $this->listing->execute();
247 $this->listing->setListingTypes($this->getListingTypes());
248 return($res);
249 }
252 /*! \brief Returns a list of items which will then be displayed
253 * in the management-list.
254 * (The management class calls this method from its execute())
255 * @return Array A list of items/objects for the listing.
256 */
257 function getItemsToBeDisplayed()
258 {
260 $path = $this->selectedContainer;
261 $item = $this->dataModel->getItemByPath($path);
262 return($item);
263 }
266 /*! \brief Returns a simply list of all distributions.
267 * This list will then be used to generate the entries of the
268 * ItemSelectors in the listing class.
269 */
270 function getContainerList()
271 {
272 $data = $this->dataModel->getItemByPath('/root');
273 $res = array();
274 $res["/root"] = array("name" => "/", "desc" => "");
275 $res = array_merge($res,$this->__recurseItem($data, array('Distribution','Release')));
276 return($res);
277 }
280 /*! \brief Recursivly walks through an item and collects all path and name info.
281 * The reult can then be used to fill the ItemSelector.
282 * @param Array The Item to recurse.
283 * @param Array The type of of objects to collect.
284 * @param String The parent path prefix which should be removed.
285 * @return Array An array containing Array[path] = name
286 */
287 function __recurseItem($item, $types, $parent = "")
288 {
289 $res = array();
290 if(1 || in_array($item['type'], $types)){
291 $path = preg_replace("/".preg_quote($parent,'/')."/","",$item['path']);
292 $res[$path] = array('name' => $item['name'],'desc'=>$item['type']);
293 }
294 if(count($item['children'])){
295 foreach($item['children'] as $child){
296 $res = array_merge($res, $this->__recurseItem($child, $types, $parent));
297 }
298 }
299 return($res);
300 }
303 /*! \brief Intializes this plugin
304 * All available installation methods will be loaded
305 */
306 function loadInstallationMethods()
307 {
308 // Reset erros
309 $this->rpcError = $this->initFailed = FALSE;
311 // Load configuration via rpc.
312 $rpc = $this->config->getRpcHandle();
314 // Populate install methods on success.
315 $res = $rpc->getSupportedInstallMethods();
316 if(!$rpc->success()){
317 $this->rpcError = TRUE;
318 $this->errorMessage = $rpc->get_error();;
319 return;
320 }
321 $this->installationMethods = $res;
323 if(!count($this->installationMethods)){
324 $this->errorMessage = _("No selectable install methods returned!");
325 msg_dialog::display(_("Setup"), $this->errorMessage , ERROR_DIALOG);
326 $this->initFailed = TRUE;
327 return;
328 }else{
329 $this->cfgItemMap = array();
330 foreach($this->installationMethods as $method => $items){
331 foreach($items['items'] as $itemName => $item){
332 $this->cfgItemMap[$itemName] = $method;
334 // This enables us to create the first level of config items when
335 // a release is selected.
336 if($item['name'] == "/"){
337 $this->installationMethods[$method]['items']['Release'] =
338 &$this->installationMethods[$method]['items'][$itemName];
339 }
340 }
341 }
342 }
343 }
346 /*! \brief Returns a info list about all items we can manage,
347 * this used to fill the listings <objectType> settings.
348 * @return Array An array with item info.
349 */
350 function getListingTypes()
351 {
352 $types= array();
353 $types['Distribution']['objectClass'] = 'Distribution';
354 $types['Distribution']['label'] = _('Distribution');
355 $types['Distribution']['image'] = 'images/lists/edit.png';
356 $types['Distribution']['category'] = 'Device';
357 $types['Distribution']['class'] = 'Device';
359 $types['Release']['objectClass'] = 'Release';
360 $types['Release']['label'] = _('Release');
361 $types['Release']['image'] = 'images/lists/delete.png';
362 $types['Release']['category'] = 'Device';
363 $types['Release']['class'] = 'Device';
365 $types['Component']['objectClass'] = 'Component';
366 $types['Component']['label'] = _('Component');
367 $types['Component']['image'] = 'plugins/users/images/select_user.png';
368 $types['Component']['category'] = 'Device';
369 $types['Component']['class'] = 'Device';
371 foreach($this->installationMethods as $method => $items){
372 foreach($items['items'] as $itemName => $item){
373 $types[$itemName]['objectClass'] = $itemName;
374 $types[$itemName]['label'] = $item['name'];
375 $types[$itemName]['image'] = 'plugins/fai/images/fai_script.png';
376 $types[$itemName]['category'] = 'Device';
377 $types[$itemName]['class'] = 'Device';
378 }
379 }
381 return($types);
382 }
385 /*! \brief The plugins ACL and plugin-property definition.
386 * @return
387 */
388 public static function plInfo()
389 {
390 return (array(
391 "plShortName" => _("Config management"),
392 "plDescription" => _("Config management"),
393 "plSelfModify" => FALSE,
394 "plDepends" => array(),
395 "plPriority" => 0,
396 "plSection" => array("administration"),
397 "plCategory" => array(
398 "newConfigManagement" => array("description" => _("Config management"),
399 "objectClass" => "FAKE_OC_newConfigManagement")),
400 "plProvidedAcls"=> array()
401 ));
402 }
405 /*! \brief Acts on open requests.
406 * (This action is received from the ConfigManagementListing class.)
407 * @param Array The items ids. (May contain multiple ids)
408 * @return
409 */
410 function openEntry($ids)
411 {
412 $id = $ids[0];
413 $item = $this->dataModel->getItemById($id);
414 $this->setCurrentContainer($item['path']);
415 return;
416 }
420 /*! \brief Removes an entry from the listing.
421 */
422 function removeEntry($ids)
423 {
425 $item = $this->dataModel->getItemById($ids[0]);
427 // Is an config item.
428 if(isset($this->cfgItemMap[$item['type']])){
429 $release = preg_replace("/^.*\//","",$item['values']['__release']);
430 $path = $item['values']['__path'];
431 $rpc = $this->config->getRpcHandle();
432 $rpc->removeConfigItem($release, $path);
433 if(!$rpc->success()){
434 msg_dialog::display(_("Error"), sprintf(_("Failed to remove: %s"), $rpc->get_error()),ERROR_DIALOG);
435 return(NULL);
436 }else{
437 $this->dataModel->removeItem($item['path']);
438 }
439 }else{
440 echo $item['type']." - are not handled yet!";
441 }
442 }
445 /*! \brief Edits a selected list item.
446 */
447 function editEntry($ids)
448 {
449 // Update the template engine to use another type of item and
450 // some other values.
451 $item = $this->dataModel->getItemById($ids[0]);
452 if(isset($this->cfgItemMap[$item['type']])){
453 $release = preg_replace("/^.*\//","",$item['values']['__release']);
454 $path = $item['values']['__path'];
455 $method = $this->cfgItemMap[$item['type']];
457 // Load item values on demand
458 if($item['status'] == '-'){
459 $rpc = $this->config->getRpcHandle();
460 $item['values']['itemValues'] = $rpc->getConfigItem($release, $path);
461 $this->dataModel->setItemStatus($item['path'], 'fetched');
462 $this->dataModel->setItemValues($item['path'], $item['values']);
463 }
465 $this->setInstallMethod($method);
466 $this->TemplateEngine->setValues($item['type'],$item['values']['itemValues']);
467 $this->listing->setDialogObject($this->TemplateEngine);
468 $this->currentObject = $item;
469 }
470 }
473 /*! \brief Initiates the creation of a new item
474 */
475 function newEntry($type)
476 {
477 $method = $this->cfgItemMap[$type];
478 $this->setInstallMethod($method);
479 $this->TemplateEngine->setValues($type,array());
480 $this->listing->setDialogObject($this->TemplateEngine);
481 $this->currentObject = NULL;
482 }
485 /*! \brief Extracts the item-path out of a path.
486 * e.g. /debian/squeeze/test/module -> /test/module
487 */
488 function getItemPath($fullPath)
489 {
490 $fPath = $fullPath.'/';
491 while(preg_match("/\//", $fPath)){
492 $fPath = preg_replace("/\/[^\/]*$/","", $fPath);
493 $item = $this->dataModel->getItemByPath($fPath);
494 if(isset($this->cfgItemMap[$item['type']])){
495 return(preg_replace("/".preg_quote($item['parentPath'],'/')."/", "", $fullPath));
496 }
497 }
498 return(NULL);
499 }
502 /*! \brief Extracts the releaes path out of a path.
503 * e.g. /debian/squeeze/test/module -> /debian/squeeze
504 */
505 function getReleasePath($fullPath)
506 {
507 $fullPath.='/';
508 while(preg_match("/\//", $fullPath)){
509 $fullPath = preg_replace("/\/[^\/]*$/","", $fullPath);
510 $item = $this->dataModel->getItemByPath($fullPath);
511 if($item['type'] == 'Release'){
512 return($fullPath);
513 }
514 }
515 return(NULL);
516 }
519 function saveItemChanges()
520 {
521 $item = $this->currentObject;
523 // Null means a new object has to be added.
524 if($item == NULL){
526 // Save template engine modifications
527 $this->TemplateEngine->save_object();
528 $release = preg_replace("/^.*\//","", $this->getReleasePath($this->selectedContainer));
529 $type = $this->TemplateEngine->getItemType();
531 // Collect modified values
532 $values = array();
533 foreach($this->TemplateEngine->getWidgets() as $w){
534 $values[$w->getName()] = $w->getValue();
535 }
537 // Create the elements target path
538 $path = $this->getItemPath($this->selectedContainer)."/".$values['name'];
540 // Add the new item
541 $rpc = $this->config->getRpcHandle();
542 $res = $rpc->setConfigItem($release, $path, $type, $values);
543 if(!$rpc->success()){
544 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
545 return(NULL);
546 }else{
548 // We've successfully added the item, now add it to the tree.
549 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],
550 array(
551 '__editable' => TRUE,
552 '__removeable' => TRUE,
553 '__path' => $path,
554 '__release' => $this->getReleasePath($this->selectedContainer)
555 ), '-' );
557 // Finally - close the dialog.
558 $this->listing->clearDialogObject();
559 }
560 }else{
562 // Collect modified values.
563 $this->TemplateEngine->save_object();
564 $values = array();
565 foreach($this->TemplateEngine->getWidgets() as $w){
566 $values[$w->getName()] = $w->getValue();
567 }
569 // Get the items release & path info
570 $release = preg_replace("/^.*\//","",$item['values']['__release']);
571 $path = $item['values']['__path'];
573 // Write the modifications back to the server.
574 $rpc = $this->config->getRpcHandle();
575 $res = $rpc->setConfigItem($release, $path, $item['type'], $values);
576 if(!$rpc->success()){
577 msg_dialog::display(_("Error"), sprintf(_("Failed to load distributions: %s"), $rpc->get_error()),ERROR_DIALOG);
578 return(NULL);
579 }else{
581 // Update the data model
582 $item['values']['itemValues'] = $values;
583 $this->dataModel->setItemValues($item['path'], $item['values']);
584 $this->listing->clearDialogObject();
585 }
586 }
587 }
588 function remove_lock() {}
589 }
592 ?>