1 <?php
3 /*! \brief This class allows to manage backend config items and packages.
4 */
5 class ConfigManagement extends plugin
6 {
7 var $initTime;
8 var $plHeadline = "Config management";
9 var $plDescription = "Config management";
10 var $plIcon = "plugins/goto/images/ConfigManagement.png";
12 var $selectedContainer;
14 var $dataModel = NULL;
15 var $listing = NULL;
17 var $cfgItemMap = NULL;
19 var $addableContainerItems = array();
20 var $currentObject = NULL;
21 var $itemsPerMethod = NULL;
23 var $initFailed = TRUE;
24 var $initialized = FALSE;
25 var $errorMessage = "";
28 /*! \brief Initialize the plugin and finally update the data model.
29 */
30 function __construct($config, $dn)
31 {
32 $this->config = &$config;
33 $this->listing = new ConfigManagementListing($this->config, get_userinfo(), $this);
35 // Load the template engine and tell her what template
36 // to use for the HTML it produces.
37 $this->TemplateEngine = new TemplateEngine($config);
38 }
41 function init()
42 {
43 $success = TRUE;
44 $success &= $this->loadInstallationMethods();
45 $success &= $this->updateDataModel();
46 $this->initFailed = !$success;
47 if($success){
48 $this->initialized = TRUE;
49 $this->listing->setListingTypes($this->getListingTypes());
50 $this->setCurrentContainer('/root');
51 }
52 }
55 /*! \brief Intializes this plugin
56 * All available installation methods will be loaded
57 */
58 function loadInstallationMethods()
59 {
60 // Get static installation methods.
61 $this->installationMethods = json_decode(file_get_contents(get_template_path('goto/Config/root.json', TRUE)), TRUE);
63 // Load configuration via rpc.
64 $rpc = $this->config->getRpcHandle();
66 // Load some base install parameters
67 $res = $rpc->getSupportedBaseInstallMethods();
68 $baseInstTypes = $installationTypes = $installationMethods = array();
69 foreach($res as $type => $data){
70 $baseInstTypes[$type] = $type;
71 if(isset($data['repositories'])){
72 foreach($data['repositories'] as $m){
73 $installationTypes[$m] = $m;
74 }
75 }
76 if(isset($data['methods'])){
77 foreach($data['methods'] as $m){
78 $installationMethods[$m] =$m;
79 }
80 }
81 }
83 $res = $rpc->getSupportedInstallMethods();
84 if(!$rpc->success()){
85 $this->errorMessage = $rpc->get_error();;
86 return(FALSE);
87 }
89 // Populate install methods on success.
90 if(!count($res)){
91 $this->errorMessage = _("No selectable install methods returned!");
92 return(FALSE);
93 }else{
95 // Merge result with hard coded methods
96 $this->installationMethods = array_merge($this->installationMethods, $res);
98 // Walk through entries and create useful mappings.
99 $this->cfgItemMap = array();
100 $this->itemConfig = array();
101 $this->itemsPerMethod = array();
102 $rootElements = array('Release');
103 foreach($this->installationMethods as $method => $items){
104 foreach($items['items'] as $itemName => $item){
105 $this->itemsPerMethod[$method][] = $itemName;
106 $this->cfgItemMap[$itemName] = $method;
107 $this->itemConfig[$itemName] = &$this->installationMethods[$method]['items'][$itemName];
109 // This enables us to create the first level of config items when
110 // a release is selected.
111 if($item['name'] == "/" && $itemName != 'root'){
112 $rootElements = array_merge($rootElements, $item['container']);
113 }
114 }
115 }
116 }
118 // Fill replacements
119 $map['%ROOT_CFG_ITEMS'] = &$rootElements;
120 $map['%INSTALLATION_TYPES'] = array('dep' => 'dep', 'rpm' => 'rpm');
121 $map['%INSTALLATION_METHODS'] = array('puppet' => 'puppet');
122 $map['%BASE_INSTALLATION_METHODS'] = array('preseed' => 'preseed');
123 $this->itemConfig = $this->__fillPlaceholder($this->itemConfig, $map);
124 return(TRUE);
125 }
128 /*! \brief Fill in replacements in the itemConfig
129 * Some values are dynamic and cannot be hardcoded, a placeholder is
130 * used in this case in the deinition file root.json.
131 */
132 function __fillPlaceholder($data, $placeholder)
133 {
134 foreach($data as $name => $sData){
135 if(is_array($sData)){
136 $data[$name] = $this->__fillPlaceholder($data[$name], $placeholder);
137 }elseif(is_string($sData) && isset($placeholder[$sData])){
138 $data[$name] = &$placeholder[$sData];
139 }
140 }
141 return($data);
142 }
145 /*! \brief Updates all distributions, releases, packages and items in the dataModel
146 * Load information from the backend.
147 */
148 function updateDataModel()
149 {
150 // Recreate the data model, to have a clean and fresh instance.
151 $this->dataModel = new ConfigManagementDataModel();
154 // Load templates from the backend and append them on the base
155 $rpc = $this->config->getRpcHandle();
156 $res = $rpc->installListTemplates();
157 if(!$rpc->success()){
158 $this->errorMessage = sprintf(_("Failed to load templates: %s"), $rpc->get_error());
159 return(FALSE);
160 }
161 foreach($res as $tName => $tData){
162 $tData['name'] = $tName;
163 $this->dataModel->addItem('Template','/root', $tName, $tData, '-');
164 }
167 // Load distributions
168 $rpc = $this->config->getRpcHandle();
169 $res = $rpc->getDistributions();
170 if(!$rpc->success()){
171 $this->errorMessage = sprintf(_("Failed to load distributions: %s"), $rpc->get_error());
172 return(FALSE);
173 }else{
174 if(is_array($res)){
176 foreach($res as $dist){
178 // Simple strings
179 $values = array();
180 foreach(array('origin','installation_method', 'installation_method','debian_security',
181 'debian_volatile', 'name', 'mirror_sources', 'managed', 'path') as $attr){
182 $values[$attr] = $dist[$attr];
183 }
185 // Boxed strings
186 foreach(array('type') as $attr){
187 $values[$attr] = $dist[$attr]['name'];
188 }
190 // Arrays
191 foreach(array('releases', 'architectures', 'components', 'sections') as $attr){
192 $values[$attr] = array();
193 if(is_array($dist[$attr])){
194 foreach($dist[$attr] as $aEntry){
195 $values[$attr][] = $aEntry['name'];
196 }
197 }
198 }
200 $this->dataModel->addItem('Distribution','/root', $dist['name'], $values);
202 if(isset($dist['releases'])){
204 // Sort releases by name length
205 $sort = array();
206 foreach($dist['releases'] as $id => $release){
207 $sort[strlen($release['name']) . $release['name']] = $id;
208 }
209 uksort($sort, "strnatcasecmp");
211 // Append release tags
212 foreach($sort as $id){
213 $release = $dist['releases'][$id];
214 $rPath = $release['name'];
215 $rPath = "/root/{$dist['name']}/$rPath";
216 $rName = preg_replace("/^.*\//","", $rPath);
217 $rPath = preg_replace("/\/[^\/]*$/","", $rPath);
218 $values = array('name' => $rName);
220 if(!$this->dataModel->itemExistsByPath($rPath)){
221 trigger_error("Invalid release name '{$rName}' in path '{$rPath}' received! Skipping entry!");
222 }else{
223 $id = $this->dataModel->addItem('Release',$rPath, $rName, $values);
224 }
225 }
226 }
227 }
228 }
229 }
230 return(TRUE);
231 }
234 /*! \brief Keep track of posted values and populate those
235 * which are interesting for us.
236 * Inspects the _POST and _GET values.
237 */
238 function save_object()
239 {
240 // Update the listing class, this is necessary to get post
241 // actions from it.
242 $this->listing->save_object();
244 // Get the selected distribution and release from the listing widget.
245 $cont = $this->listing->getSelectedContainer();
246 if(isset($_POST['ROOT'])){
247 $this->setCurrentContainer('/root');
248 }elseif(isset($_POST['BACK'])){
249 $path = $this->selectedContainer;
250 if($this->dataModel->itemExistsByPath($path)){
251 $data = $this->dataModel->getItemByPath($path);
252 if($data['parentPath']){
253 $this->setCurrentContainer($data['parentPath']);
254 }
255 }
256 }else{
257 $this->setCurrentContainer($cont);
258 }
259 }
262 /*! \brief Load extended sub-objecte like 'config items' or 'packages'
263 * for the given release path.
264 * @param String The release path to load sub-objects for.
265 * @return NULL
266 */
267 function updateItemList($path)
268 {
269 // Fist get Item and check if it is an release
270 if($this->dataModel->itemExistsByPath($path)){
271 $data = $this->dataModel->getItemByPath($path);
273 // Only releases can contain config-items.
274 if($data['type'] == 'Release' && $data['status'] != "fetched"){
276 // Request all config items for the selected release via rpc.
277 $rpc = $this->config->getRpcHandle();
278 $releasePath = $this->getReleasePart($path);
279 $res = $rpc->listConfigItems($releasePath);
280 if(!$rpc->success()){
281 msg_dialog::display(_("Error"),sprintf(_("Failed to load distributions: %s"),$rpc->get_error()),ERROR_DIALOG);
282 return;
283 }else{
285 if(!$res) return;
287 // Sort entries by path length
288 $sLen = array();
289 foreach($res as $itemPath => $type){
290 $sLen[strlen($itemPath)."_".$itemPath] = $itemPath;
291 }
292 uksort($sLen, "strnatcasecmp");
294 // Walk through each entry and then try to add it to the model
295 foreach($sLen as $unused => $itemPath){
297 // Do not add the root element '/'
298 if($itemPath == "/") continue;
300 $type = $res[$itemPath];
302 // Append the items-path to the current path to create the
303 // effective item path in the data model.
304 $targetPath = trim($path."/".$itemPath);
306 // Remove trailing and duplicated slashes
307 $targetPath = rtrim($targetPath, '/');
308 $targetPath = preg_replace("/\/\/*/","/", $targetPath);
310 // Extract the items name
311 $name = preg_replace("/^.*\//","", $targetPath);
313 // Cleanup the path and then add the item.
314 $targetPath = preg_replace("/[^\/]*$/","", $targetPath);
315 $targetPath = rtrim($targetPath,'/');
316 $this->dataModel->addItem($type, $targetPath, $name,array(),'-' );
317 }
318 $this->dataModel->setItemStatus($path, 'fetched');
319 }
320 }
321 }
322 }
325 /*! \brief Sets the currently selected container and item path.
326 * @param String The path of the container to set.
327 * @param String The path of the item to set.
328 * @return
329 */
330 function setCurrentContainer($cont)
331 {
332 // Do nothing while the service wasn't initialized.
333 if(!$this->initialized) return;
335 $this->selectedContainer = $cont;
337 // Update list of items within the selected container.
338 $this->updateItemList($this->selectedContainer);
340 // Transfer checked values back to the listing class.
341 $this->listing->setContainers($this->getContainerList());
342 $this->listing->setContainer($cont);
344 // Update the list of addable sub objects
345 $this->addableContainerItems = $this->getAddableContainersPerPath($cont);
346 }
349 function getAddableContainersPerPath($path)
350 {
351 $currentItem = $this->dataModel->getItemByPath($path);
352 $method = $this->getInstallationMethodPerPath($path);
354 // Get allowed items for the currently selected method
355 // merge in root elements, they are allowed everywhere.
356 $allowedItems = $this->itemsPerMethod[$method];
357 $allowedItems = array_merge($allowedItems, $this->itemsPerMethod['root']);
359 // Get addable items
360 $possibleItems = $this->itemConfig[$currentItem['type']]['container'];
361 return(array_unique(array_intersect($allowedItems, $possibleItems)));
362 }
365 function getInstallationMethodPerPath($path)
366 {
367 $path .= '/';
368 while(preg_match("/\//", $path)){
369 $path = preg_replace("/\/[^\/]*$/","",$path);
370 $item = $this->dataModel->getItemByPath($path);
371 if(isset($item['values']['installation_method'])){
372 return($item['values']['installation_method']);
373 }
374 }
375 return('root');
376 }
379 /*! \brief Generate the HTML content for this plugin.
380 * Actually renders the listing widget..
381 */
382 function execute()
383 {
384 // Request an update of the data model
385 if(!$this->initialized){
386 $this->init();
387 }
389 // Act on init fails
390 if($this->initFailed){
391 $smarty = get_smarty();
392 $smarty->assign('error', $this->errorMessage);
393 return($smarty->fetch(get_template_path('rpcError.tpl', TRUE)));
394 }else{
396 // Get the selected release and store it in a session variable
397 // to allow the configFilter to access it and display the
398 // packages and items.
399 $res = $this->listing->execute();
400 $this->listing->setListingTypes($this->getListingTypes());
401 return($res);
402 }
403 }
406 /*! \brief Returns a list of items which will then be displayed
407 * in the management-list.
408 * (The management class calls this method from its execute())
409 * @return Array A list of items/objects for the listing.
410 */
411 function getItemsToBeDisplayed()
412 {
413 $path = $this->selectedContainer;
414 $item = $this->dataModel->getItemByPath($path);
415 return($item);
416 }
419 /*! \brief Returns a simply list of all distributions.
420 * This list will then be used to generate the entries of the
421 * ItemSelectors in the listing class.
422 */
423 function getContainerList()
424 {
425 $data = $this->dataModel->getItemByPath('/root');
426 $res = array();
427 $res["/root"] = array("name" => "/", "desc" => "");
428 $res = array_merge($res,$this->__recurseItem($data));
429 return($res);
430 }
433 /*! \brief Recursivly wlks through an item and collects all path and name info.
434 * The reult can then be used to fill the ItemSelector.
435 * @param Array The Item to recurse.
436 * @param Array The type of of objects to collect.
437 * @param String The parent path prefix which should be removed.
438 * @return Array An array containing Array[path] = name
439 */
440 function __recurseItem($item, $parent = "")
441 {
442 $res = array();
443 $path = preg_replace("/".preg_quote($parent,'/')."/","",$item['path']);
444 $res[$path] = array('name' => $item['name'],'desc'=>$item['type']);
445 if(count($item['children'])){
446 foreach($item['children'] as $child){
447 $res = array_merge($res, $this->__recurseItem($child, $parent));
448 }
449 }
450 return($res);
451 }
454 /*! \brief Returns a info list about all items we can manage,
455 * this used to fill the listings <objectType> settings.
456 * @return Array An array with item info.
457 */
458 function getListingTypes()
459 {
460 $types= array();
461 $types['Distribution']['objectClass'] = 'Distribution';
462 $types['Distribution']['label'] = _('Distribution');
463 $types['Distribution']['image'] = 'images/lists/edit.png';
464 $types['Distribution']['category'] = 'Device';
465 $types['Distribution']['class'] = 'Device';
467 $types['Release']['objectClass'] = 'Release';
468 $types['Release']['label'] = _('Release');
469 $types['Release']['image'] = 'images/lists/delete.png';
470 $types['Release']['category'] = 'Device';
471 $types['Release']['class'] = 'Device';
473 $types['Component']['objectClass'] = 'Component';
474 $types['Component']['label'] = _('Component');
475 $types['Component']['image'] = 'plugins/users/images/select_user.png';
476 $types['Component']['category'] = 'Device';
477 $types['Component']['class'] = 'Device';
479 foreach($this->installationMethods as $method => $items){
480 foreach($items['items'] as $itemName => $item){
481 $types[$itemName]['objectClass'] = $itemName;
482 $types[$itemName]['label'] = $item['name'];
483 $types[$itemName]['image'] = 'plugins/fai/images/fai_script.png';
484 $types[$itemName]['category'] = 'Device';
485 $types[$itemName]['class'] = 'Device';
486 }
487 }
489 return($types);
490 }
493 /*! \brief The plugins ACL and plugin-property definition.
494 * @return
495 */
496 public static function plInfo()
497 {
498 return (array(
499 "plShortName" => _("Config management"),
500 "plDescription" => _("Config management"),
501 "plSelfModify" => FALSE,
502 "plDepends" => array(),
503 "plPriority" => 0,
504 "plSection" => array("administration"),
505 "plCategory" => array(
506 "ConfigManagement" => array("description" => _("Config management"),
507 "objectClass" => "FAKE_OC_ConfigManagement")),
508 "plProvidedAcls"=> array(
509 "cfgItem" => _("Config item")
510 )
511 ));
512 }
515 /*! \brief Acts on open requests.
516 * (This action is received from the ConfigManagementListing class.)
517 * @param Array The items ids. (May contain multiple ids)
518 * @return
519 */
520 function openEntry($ids)
521 {
522 $id = $ids[0];
523 $item = $this->dataModel->getItemByDn($id);
524 $this->setCurrentContainer($item['path']);
525 return;
526 }
530 /*! \brief Removes an entry from the listing.
531 */
532 function removeEntry($ids)
533 {
534 foreach($ids as $id){
535 $item = $this->dataModel->getItemByDn($id);
537 // Is an config item.
538 if($this->cfgItemMap[$item['type']] != 'root'){
539 $release = $this->getReleasePart($item['path']);
540 $path = $this->getItemPath($item['path']);
541 $rpc = $this->config->getRpcHandle();
542 $rpc->removeConfigItem($release, $path);
543 if(!$rpc->success()){
544 msg_dialog::display(_("Error"), sprintf(_("Failed to remove: %s"), $rpc->get_error()),ERROR_DIALOG);
545 return(NULL);
546 }else{
547 $this->dataModel->removeItem($item['path']);
548 }
549 }else{
551 // Remove distribution
552 if($item['type'] == 'Distribution'){
554 $dist = $this->getDistributionPart($item['path']);
555 $rpc = $this->config->getRpcHandle();
556 $rpc->removeDistribution($dist, array('recursive' => TRUE));
557 if(!$rpc->success()){
558 msg_dialog::display(_("Error"), sprintf(_("Failed to remove the distribution: %s. Error was: %s"),
559 $dist, $rpc->get_error()), ERROR_DIALOG);
560 return(NULL);
561 }else{
562 $this->dataModel->removeItem($item['path']);
563 }
564 }elseif($item['type'] == 'Release'){
566 // Remove release
567 $release = preg_replace("/^.*\//","", $this->getReleasePart($item['path']));
568 $rpc = $this->config->getRpcHandle();
569 $rpc->removeRelease($release, array('recursive' => TRUE));
570 if(!$rpc->success()){
571 msg_dialog::display(_("Error"), sprintf(_("Failed to remove the release: %s. Error was: %s"),
572 $release, $rpc->get_error()),ERROR_DIALOG);
573 return(NULL);
574 }else{
575 $this->dataModel->removeItem($item['path']);
576 }
578 }elseif($item['type'] == 'Template'){
580 // Remove Template
581 $rpc = $this->config->getRpcHandle();
582 $rpc->installRemoveTemplate($item['name']);
583 if(!$rpc->success()){
584 msg_dialog::display(_("Error"), sprintf(_("Failed to remove the template: %s. Error was: %s"),
585 $release, $rpc->get_error()),ERROR_DIALOG);
586 return(NULL);
587 }else{
588 $this->dataModel->removeItem($item['path']);
589 }
590 }else{
591 trigger_error($item['type']." - are not handled yet!");
592 }
593 }
594 }
595 }
598 /*! \brief Returns a list of used item names for a given path.
599 */
600 function getUsedNamesForPath($path)
601 {
602 $item = $this->dataModel->getItemByPath($path);
603 $names = array();
604 foreach($item['children'] as $path => $data){
605 $names[] = $data['name'];
606 }
607 return($names);
608 }
611 /*! \brief Edits a selected list item.
612 */
613 function editEntry($ids)
614 {
615 $item = $this->dataModel->getItemByDn($ids[0]);
617 if(!$item) return;
619 $release = $this->getReleasePart($item['path']);
620 $path = $this->getItemPath($item['path']);
621 $method = $this->cfgItemMap[$item['type']];
623 // Load item values on demand
624 if($this->cfgItemMap[$item['type']] != 'root'){
625 if($item['status'] == '-'){
626 $rpc = $this->config->getRpcHandle();
627 $res = $rpc->getConfigItem($release, $path);
628 if(!$rpc->success()){
629 msg_dialog::display(_("Error"), sprintf(_("Failed to load config item details: %s"), $rpc->get_error()),ERROR_DIALOG);
630 return;
631 }else{
632 $item['values'] = $res;
633 $this->dataModel->setItemStatus($item['path'], 'fetched');
634 $this->dataModel->setItemValues($item['path'], $item['values']);
635 }
636 }
637 }elseif($item['type'] == 'Template'){
638 if($item['status'] == '-'){
639 $rpc = $this->config->getRpcHandle();
640 $res = $rpc->installGetTemplate($item['name']);
641 if(!$rpc->success()){
642 msg_dialog::display(_("Error"), sprintf(_("Failed to load template details: %s"), $rpc->get_error()),ERROR_DIALOG);
643 return;
644 }else{
645 $item['values'] = $res;
646 $this->dataModel->setItemStatus($item['path'], 'fetched');
647 $this->dataModel->setItemValues($item['path'], $item['values']);
648 }
649 }
650 }
652 $this->TemplateEngine->load($this->itemConfig);
653 $this->TemplateEngine->setTemplate($method.".tpl");
654 $this->TemplateEngine->editItem($item['type'],$item['values']);
655 $this->listing->setDialogObject($this->TemplateEngine);
656 $this->currentObject = $item;
657 }
660 /*! \brief Initiates the creation of a new item
661 */
662 function newEntry($type)
663 {
664 // We've to add a config item
665 $this->TemplateEngine->load($this->itemConfig);
666 if($this->cfgItemMap[$type] != 'root'){
667 $method = $this->cfgItemMap[$type];
668 $this->TemplateEngine->setTemplate($method.".tpl");
669 $this->TemplateEngine->createItem($type,array());
670 $this->listing->setDialogObject($this->TemplateEngine);
671 $this->currentObject = NULL;
672 }else{
673 $this->TemplateEngine->setTemplate("root.tpl");
674 $this->TemplateEngine->createItem($type,array());
675 $this->listing->setDialogObject($this->TemplateEngine);
676 $this->currentObject = NULL;
677 }
678 }
681 /*! \brief Extracts the item-path out of a path.
682 * e.g. /debian/squeeze/test/module -> /test/module
683 */
684 function getItemPath($fullPath)
685 {
686 $fPath = $fullPath.'/';
687 while(preg_match("/\//", $fPath)){
688 $fPath = preg_replace("/\/[^\/]*$/","", $fPath);
689 $item = $this->dataModel->getItemByPath($fPath);
690 if(in_array($item['type'], array('Release', 'Distribution', 'root'))){
691 return(preg_replace("/".preg_quote($item['path'],'/')."/", "", $fullPath));
692 }
693 }
694 return(NULL);
695 }
698 /*! \brief Extracts the releaes path out of a path.
699 * e.g. /debian/squeeze/test/module -> /debian/squeeze
700 */
701 function getReleasePath($fullPath)
702 {
703 $fullPath.='/';
704 while(preg_match("/\//", $fullPath)){
705 $fullPath = preg_replace("/\/[^\/]*$/","", $fullPath);
706 $item = $this->dataModel->getItemByPath($fullPath);
707 if($item['type'] == 'Release'){
708 return($fullPath);
709 }
710 }
711 return(NULL);
712 }
715 /*! \brief Extracts the distribution path out of a path.
716 * e.g. /root/debian/squeeze/test/module -> /root/debian
717 */
718 function getDistributionPath($fullPath)
719 {
720 $fullPath.='/';
721 while(preg_match("/\//", $fullPath)){
722 $fullPath = preg_replace("/\/[^\/]*$/","", $fullPath);
723 $item = $this->dataModel->getItemByPath($fullPath);
724 if($item['type'] == 'Distribution'){
725 return($fullPath);
726 }
727 }
728 return(NULL);
729 }
732 /*! \brief Extracts the distribution-part out of a path.
733 * e.g. /root/debian/squeeze/test/module -> debian
734 */
735 function getDistributionPart($fullPath)
736 {
737 return(trim(preg_replace("#^/root/#","", $this->getDistributionPath($fullPath)), '/'));
738 }
741 /*! \brief Extracts the release-part out of a path.
742 * e.g. /root/debian/squeeze/test/module -> squeeze/test
743 */
744 function getReleasePart($path)
745 {
746 $rPath = $this->getReleasePath($path);
747 $dPath = $this->getDistributionPath($path);
748 return(preg_replace("/^".preg_quote($dPath, '/')."\/?/", "", $rPath));
749 }
752 function saveItemChanges()
753 {
754 // Save template engine modifications and validate values.
755 $this->TemplateEngine->save_object();
756 $msgs = $this->TemplateEngine->check();
758 // Get values to be saved
759 $values = array();
760 foreach($this->TemplateEngine->getWidgets() as $w){
761 $values[$w->getName()] = $w->getValue();
762 }
764 // No input error were found, now check that we do not use the same name twice
765 // and that it is valid.
766 if(!count($msgs)){
768 // Get used item names for the current path
769 if($this->currentObject){
771 // Get used item names in the parent path.
772 $usedNames = $this->getUsedNamesForPath($this->currentObject['parentPath']);
773 }else{
775 // Get used items for the selected path.
776 $usedNames = $this->getUsedNamesForPath($this->selectedContainer);
777 }
779 // Allow the item to keep its name.
780 if($this->currentObject != NULL && isset($this->currentObject['values']['name'])){
781 $usedNames = array_remove_entries(array($this->currentObject['values']['name']), $usedNames);
782 }
783 if(in_array($values['name'],$usedNames)){
784 $msgs[] = msgPool::duplicated(_("Name"));
785 }
786 }
788 // Display errors
789 if(count($msgs)){
790 msg_dialog::displayChecks($msgs);
791 return;
792 }
794 // Get the item type to be saved
795 $item = $this->currentObject;
796 $type = $this->TemplateEngine->getItemType();
797 if($this->cfgItemMap[$type] == 'root'){
799 // We've to create a new distribution
800 if($type == 'Distribution'){
802 // Distributions cannot be renamed!
803 if(isset($item['name']) && $item['name'] != $values['name']){
804 msg_dialog::displayChecks(array("Distributions cannot be renamed!"));
805 return;
806 }
808 // Create a new distribution
809 if(!$item){
810 $name = $values['name'];
811 $itype = $values['installation_type'];
812 $imethod = $values['installation_method'];
813 $origin = $values['origin'];
815 // Initiate the rpc request.
816 $rpc = $this->config->getRpcHandle();
817 $res = $rpc->createDistribution($name, $itype, array('mirror'=>$origin, 'install_method' => $imethod));
818 if(!$rpc->success()){
819 msg_dialog::display(_("Error"), sprintf(_("Failed to save distributions: %s"),
820 $rpc->get_error()),ERROR_DIALOG);
821 return(NULL);
822 }
823 }
825 // Collect distribution properties
826 $data = array();
827 $data['distribution'] = $values['name'];
828 $data['arch'] = $values['architectures'];
829 $data['component'] = $values['components'];
830 $data['mirror_sources'] = $values['mirror_sources'];
832 // Set distribution properties
833 $rpc = $this->config->getRpcHandle();
834 $rpc->setDistribution($data);
835 if(!$rpc->success()){
836 msg_dialog::display(_("Error"), sprintf(_("Failed to save distribution properties: %s"),
837 $rpc->get_error()),ERROR_DIALOG);
838 return(NULL);
839 }
841 // We've successfully added the item, now add it to the tree.
842 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],$values, '-' );
844 // Finally - close the dialog.
845 $this->listing->clearDialogObject();
847 }elseif($type == 'Release'){
849 // We've to update a release.
850 if($item){
852 // Check if we've to rename the item.
853 $path = $this->getReleasePart($item['parentPath']);
854 $curPath = $this->getReleasePart($item['path']);
855 $newPath = trim($path."/".$values['name'], '/');
856 if($curPath != $newPath){
857 $rpc = $this->config->getRpcHandle();
858 $res = $rpc->renameRelease($curPath, $newPath);
859 if(!$rpc->success()){
860 msg_dialog::display(_("Error"), sprintf(_("Failed to save release: %s"), $rpc->get_error()),ERROR_DIALOG);
861 return(NULL);
862 }else{
863 $nP = $item['parentPath'].'/'.$values['name'];
864 $this->dataModel->moveItem($item['path'], $nP);
865 $this->dataModel->setItemValues($nP, $values);
866 $this->listing->clearDialogObject();
867 }
868 }else{
869 $this->listing->clearDialogObject();
870 }
873 }else{
875 // Build up the new release path.
876 $name = $values['name'];
877 $rPath = $this->getReleasePart($this->selectedContainer);
878 $newPath = trim($rPath."/".$name, '/');
880 // Detect the parent distribution
881 $dist = $this->getDistributionPart($this->selectedContainer);
883 // Initiate the rpc request.
884 $rpc = $this->config->getRpcHandle();
885 $res = $rpc->createRelease($dist, $newPath);
886 if(!$rpc->success()){
887 msg_dialog::display(_("Error"), sprintf(_("Failed to save release: %s"), $rpc->get_error()),ERROR_DIALOG);
888 return(NULL);
889 }else{
891 // We've successfully added/saved the item, now add it to the tree.
892 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],$values, '-' );
893 $this->listing->clearDialogObject();
894 }
895 }
896 }elseif($type == "Template"){
898 // Initiate the rpc request.
899 $rpc = $this->config->getRpcHandle();
900 $res = $rpc->installSetTemplate($values['name'], $values);
901 if(!$rpc->success()){
902 msg_dialog::display(_("Error"), sprintf(_("Failed to save template: %s"), $rpc->get_error()),ERROR_DIALOG);
903 return(NULL);
904 }else{
906 // We've successfully added/saved the item, now add it to the tree.
907 if(!$item){
908 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],$values, '-' );
909 $this->listing->clearDialogObject();
910 }else{
912 // Update the model and clear the dialogs
913 $path = $item['path'];
914 $this->dataModel->setItemValues($path, $values);
915 $this->listing->clearDialogObject();
916 }
917 }
919 }else{
920 echo "{$type} Cannot be saved yet";
921 $this->listing->clearDialogObject();
922 return;
923 }
924 }
926 // Save a CONFIG-ITEM object.
927 if($this->cfgItemMap[$type] != 'root'){
929 // Get paths
930 $release = $this->getReleasePart($this->selectedContainer);
932 if($item){
933 $oldPath = $item['path'];
934 $oldItemPath = $this->getItemPath($item['path']);
935 $newPath = $item['parentPath']."/".$values['name'];
936 $newItemPath = $this->getItemPath($newPath);
937 }else{
938 $newPath = $this->selectedContainer."/".$values['name'];
939 $newItemPath = $this->getItemPath($this->selectedContainer)."/".$values['name'];
940 }
942 // If this is a new item, then create it now.
943 if($item == NULL){
945 // Add the new item
946 $rpc = $this->config->getRpcHandle();
947 $res = $rpc->setConfigItem($release, $newItemPath, $type, $values);
948 if(!$rpc->success()){
949 msg_dialog::display(_("Error"), sprintf(_("Failed to save %s: %s"),$type, $rpc->get_error()),ERROR_DIALOG);
950 return(NULL);
951 }else{
953 // We've successfully added the item, now add it to the tree.
954 $this->dataModel->addItem($type, $this->selectedContainer, $values['name'],array(), '-' );
956 // Finally - close the dialog.
957 $this->listing->clearDialogObject();
958 }
959 }else{
961 // Write the modifications back to the server.
962 $rpc = $this->config->getRpcHandle();
963 $res = $rpc->setConfigItem($release, $oldItemPath, $type, $values);
964 if(!$rpc->success()){
965 msg_dialog::display(_("Error"), sprintf(_("Failed to save config item: %s"), $rpc->get_error()),ERROR_DIALOG);
966 return(NULL);
967 }else{
969 // Update the data model
970 $this->dataModel->setItemValues($oldPath, $values);
971 if($oldPath != $newPath){
972 $this->dataModel->moveItem($oldPath, $newPath);
973 }
974 $this->listing->clearDialogObject();
975 }
976 }
977 }
978 }
979 function remove_lock() {}
980 }
983 ?>