ACC SHELL

Path : /usr/share/YaST2/include/add-on/
File Upload :
Current File : //usr/share/YaST2/include/add-on/add-on-workflow.ycp

/**
 * File:
 *      include/add-on/add-on-workflow.ycp
 *
 * Module:
 *      System installation
 *
 * Summary:
 *      Add-on product installation workflow
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *
 *
 */
{

textdomain "add-on";

import "AddOnProduct";
import "WorkflowManager";
import "Linuxrc";
import "Mode";
import "Popup";
import "Report";
import "Sequencer";
import "SourceManager";
import "PackageSystem";
import "SuSEFirewall";
import "Stage";
import "Wizard";
import "Confirm";
import "GetInstArgs";
import "Installation";
import "PackageCallbacks";
import "PackagesUI";

include "packager/inst_source_dialogs.ycp";
include "packager/repositories_include.ycp";
include "add-on/misc.ycp";

boolean going_back_in_workflow = GetInstArgs::going_back();

/**
 * Initialize current inst. sources
 */
symbol Read () {
    Pkg::SourceStartManager(true);
    return `next;
}

/**
 * Write (changed) inst. sources
 */
symbol Write () {
    Pkg::SourceSaveAll();
    return `next;
}

// Used for adding sources
symbol createResult = `again;

/**
 * Checks whether some network is available in the current moment,
 * see the bug #170147 for more information.
 */
boolean IsAnyNetworkAvailable () {
        boolean ret = false;
        
        string command = "TERM=dumb /sbin/ip -o address show | grep inet | grep -v scope.host";
        y2milestone("Running %1", command);
        map cmd_run = (map) SCR::Execute(.target.bash_output, command);
        y2milestone("Command returned: %1", cmd_run);

        // command failed
        if (cmd_run["exit"]:-1 != 0) {
            // some errors were there, we don't know the status, rather return that it's available
            // `grep` also returns non zero exit code when there is nothing to do...
            if (cmd_run["stdout"]:"" != "") {
                y2error("Checking the network failed");
                ret = true;
            }
        // some devices are listed
        } else if (cmd_run["stdout"]:"" != nil && cmd_run["stdout"]:"" != "" && cmd_run["stdout"]:"" != "\n") {
            ret = true;
        }
        
        return ret;    
}

/**
 * Returns begining string for source type
 *
 * @param symbol source_type
 * @return string url begins with...
 */
string GetURLBeginsWith (symbol source_type) {
    string url = "";

    if      ( source_type == `ftp ) url = "ftp://";
    else if ( source_type == `http ) url = "http://";
    else if ( source_type == `https ) url = "https://";
    else if ( source_type == `samba ) url = "smb://";
    else if ( source_type == `nfs ) url = "nfs://";
    else if ( source_type == `cd ) url = "cd:///";
    else if ( source_type == `dvd ) url = "dvd:///";
    else if ( source_type == `local_dir ) url = "dir://";

    return url;
}

// used Add-Ons are stored in AddOnProduct::add_on_products
// bnc #393620
void AddAddOnToStore (integer src_id) {
    if (src_id == nil) {
	y2error ("Wrong src_id: %1", src_id);
	return;
    }

    // BNC #450274
    // Prevent from adding one product twice
    list <map> matching_products = filter (map one_product, AddOnProduct::add_on_products, {
	return (one_product["media"]:-1 == src_id);
    });

    if (size (matching_products) > 0) {
	y2milestone ("Product already added: %1", matching_products);
	return;
    }

    map source_data = Pkg::SourceGeneralData (src_id);

    AddOnProduct::add_on_products = add (AddOnProduct::add_on_products, $[
	"media" : AddOnProduct::src_id,
	// table cell
	"product" : source_data["name"]:source_data["alias"]:_("No product found in the repository"),
	"media_url" : source_data["url"]:"",
	"product_dir" : source_data["product_dir"]:"",
    ]);
}

/**
 * Run dialog for selecting the media
 * @return symbol for wizard sequencer
 */
symbol MediaSelect () {
    map<string,any> aliases = $[
	"type" : ``(TypeDialog()),
	"edit" : ``(EditDialog ()),
	"store" : ``(StoreSource ()),
    ];

    list <integer> sources_before = Pkg::SourceGetCurrent (false);
    y2milestone ("Sources before adding new one: %1", sources_before);

    map sequence = $[
	"ws_start" : "type",
	"type" : $[
	    `next : "edit",
	    // bnc #392083
	    `finish : "store",
	    `abort : `abort,
	],
	"edit" : $[
	    `next : "store",
	    // bnc #392083
	    `finish : "store",
	    `abort : `abort,
	],
	"store" : $[
	    `next : `next,
	    // bnc #392083
	    `finish : `next,
	    `abort : `abort,
	],
    ];

    y2milestone ("Starting repository sequence");
    symbol ret = Sequencer::Run (aliases, sequence);

    list <integer> sources_after = Pkg::SourceGetCurrent (false);
    y2milestone ("Sources with new one added: %1", sources_after);

    // bnc #393011
    // AddOnProduct::src_id must be set to the latest source ID
    boolean src_id_found = false;

    foreach (integer one_source, sources_after, {
	if (! contains (sources_before, one_source)) {
	    AddOnProduct::src_id = one_source;
	    y2milestone ("Added source ID is: %1", AddOnProduct::src_id);
	    src_id_found = true;
	    break;
	}
    });

    if (src_id_found) {
	// used add-ons are stored in a special list
	AddAddOnToStore (AddOnProduct::src_id);
    } else {
	AddOnProduct::src_id = sources_after[size(sources_after) - 1]:0;
	y2warning ("Fallback src_id: %1", AddOnProduct::src_id);
    }

    // BNC #441380
    // Refresh and load the added source, this is needed since the unified
    // functions from packager are used.
    Pkg::SourceRefreshNow (AddOnProduct::src_id);
    Pkg::SourceLoad();

    // BNC #468449
    // It may happen that the add-on control file contains some code that
    // would drop the changes made, so it's better to save the soruces now
    if (Mode::normal()) {
	y2milestone ("Saving all sources");
	Pkg::SourceSaveAll();
    }

    AddOnProduct::last_ret = ret;
    y2milestone ("MediaSelect Dialog ret: %1", ret);
    return ret;
}

string new_addon_name = "";

// bugzilla #304659
/**
 * Sets the Add-On's product name
 *
 * @param integer src_id (source ID)
 */
void SetAddOnProductName (integer src_id) {
    new_addon_name = "";

    if (src_id == nil) {
	y2error ("Cannot set name, no ID!");
	return;
    }

    new_addon_name = SourceDialogs::GetRepoName();

    // no name to change to
    if (new_addon_name == nil || new_addon_name == "") {
	y2milestone ("No special name set");
	return;
    }

    list <map <string,any> > new_addon_set = [
	$[ "SrcId" : src_id, "name" : new_addon_name ]
    ];
    boolean result = Pkg::SourceEditSet (new_addon_set);

    y2milestone ("Adjusting Add-On: %1 returned: %2", new_addon_set, result);

    // do not use it next time
    SourceDialogs::SetRepoName ("");
}

/**
 * Run dialog for selecting the catalog on the media (if more than one present)
 * @return symbol for wizard sequencer
 */
symbol CatalogSelect () {
    list<integer> sources = SourceManager::newSources;
    y2milestone("New sources: %1", sources);

    if (size (sources) == 0)
    {
	// error report
	Report::Error (_("No software repository found on medium."));
	y2milestone ("CatalogSelect Dialog ret: %1", `back);
	return `back;
    }

    if (size (sources) == 1)
    {
	if (AddOnProduct::last_ret != `next)
	{
	    y2milestone ("Deleting source %1", sources[0]:0);
	    Pkg::SourceDelete(sources[0]:0);

	    y2milestone ("CatalogSelect Dialog ret: %1", AddOnProduct::last_ret);
	    return AddOnProduct::last_ret;
	}

	// busy message
	UI::OpenDialog(`Label(`id (`add_on_popup_id), _("Initializing new source...")));
	integer src_id = SourceManager::newSources[0]:0;
	map data = Pkg::SourceGeneralData (src_id);
	y2milestone("Adding product: %1", data);
	string url = data["url"]:"";
	string product_dir = data["product_dir"]:"";
	y2milestone ("Deleting source %1", src_id);
	Pkg::SourceDelete (src_id);
	src_id = Pkg::SourceCreate (url, product_dir);
	SourceManager::newSources = [src_id];

	// a little hack because of packager leaving
	// windows open...
	if (UI::WidgetExists(`add_on_popup_id)) {
	    UI::CloseDialog();
	} else if (UI::WidgetExists(`contents)) {
	    y2warning ("Already in base dialog!");
	} else {
	    y2error ("Error in packager, closing current dialog!");
	    while (! UI::WidgetExists(`contents)) {
		y2milestone ("Calling UI::CloseDialog");
		UI::CloseDialog();
	    }
	}

	if (src_id == -1)
	{
	    // error report
	    Report::Error (_("Failed to initialize the software repository."));
	    AddOnProduct::ClearRegistrationRequest (src_id);

	    y2milestone ("CatalogSelect Dialog ret: %1", AddOnProduct::last_ret);
	    return `finish;
	}

	AddOnProduct::src_id = src_id;
	SourceManager::newSources = [src_id];
	y2milestone ("Only one source available - skipping dialog");

	y2milestone ("CatalogSelect Dialog ret: %1", AddOnProduct::last_ret);
	return AddOnProduct::last_ret;
    }
    y2milestone ("Running catalog select dialog");
    list catalogs = maplist (integer src, sources, {
	map data = Pkg::SourceGeneralData (src);
	// placeholder for unknown directory
	string dir = data["product_dir"]:_("Unknown");
	if (dir == "")
	    dir = "/";
	return `item (`id (src), sformat (_("URL: %1, Directory: %2"),
	    // place holder for unknown URL
	    data["url"]:_("Unknown"), dir));
    });

    // dialog caption
    string title = _("Software Repository Selection");
    // help text
    string help_text = _("<p><big><b>Software Repository Selection</b></big><br>
Multiple repositories were found on the selected medium.
Select the repository to use.</p>
");

    term contents = `HBox (`HSpacing(4), `VBox (
	`VSpacing (2),
	`SelectionBox (`id (`catalogs), _("Repositories &Found"), catalogs),
	`VSpacing (2)
    ), `HSpacing (4));
    Wizard::SetContents (title, contents, help_text, true, true);
    symbol ret = nil;
    integer selected = nil;
    while (ret == nil)
    {
	ret = (symbol)UI::UserInput ();
	if (ret == `abort || ret == `cancel)
	{
	    ret = `abort;
//	    if (Stage::initial())
//	    {
//	        if (Popup::ConfirmAbort (`painless))
//		    break;
//	    }
//	    else
//	    {
		// yes-no popup
		if (Popup::YesNo (_("Really abort add-on product installation?")))
		    break;
//	    }
	    continue;
	}
	else if (ret == `back)
	{
	    break;
	}
	else if (ret == `next)
	{
	    selected = (integer)UI::QueryWidget (`id (`catalogs), `CurrentItem);
	    if (selected == nil)
	    {
		ret = nil;
		// popup message
		Popup::Message (_("Select a repository."));
	    }
	}
    }

    if (ret != `next)
    {
	foreach (integer src, SourceManager::newSources, {
	    y2milestone ("Deleting source %1", src);
	    Pkg::SourceDelete (src);
	});
    }
    else
    {
	foreach (integer src, SourceManager::newSources, {
	    if (src != selected)
	    {
		y2milestone ("Deleting source %1", src);
		Pkg::SourceDelete (src);
	    }
	});
	map data = Pkg::SourceGeneralData (selected);
	string url = data["url"]:"";
	string product_dir = data["product_dir"]:"";
	y2milestone ("Deleting source %1", selected);
	Pkg::SourceDelete (selected);
	selected = Pkg::SourceCreate (url, product_dir);
	SourceManager::newSources = [selected];
	if (selected == -1)
	{
	    // error report
	    Report::Error (_("Failed to initialize the software repository."));
	    AddOnProduct::ClearRegistrationRequest (selected);

	    y2milestone ("CatalogSelect Dialog ret: %1", `finish);
	    return `finish;
	}

	AddOnProduct::src_id = selected;
	SourceManager::newSources = [selected];
    }

    AddOnProduct::last_ret = ret;
    y2milestone ("CatalogSelect Dialog ret: %1", AddOnProduct::last_ret);
    return ret;
}

// BNC #474745
// Installs all the products from just added add-on media
symbol InstallProduct () {
    y2milestone ("AddOnID: %1", AddOnProduct::src_id);

    if (AddOnProduct::src_id == nil || AddOnProduct::src_id < 0) {
	y2error ("No source has been added");
	return `next;
    }

    list <map <string, any> > all_products = Pkg::ResolvableProperties ("", `product, "");

    // `selected and `installed products
    // each map contains "name" and "version"
    list <map> s_a_i_products = [];

    foreach (map <string, any> one_product, all_products, {
	s_a_i_products = add (s_a_i_products,
	    $["name":one_product["name"]:"xyz", "version":one_product["version"]:"abc"]
	);
    });

    foreach (map <string, any> one_product, all_products, {
//	map this_product = $["name":one_product["name"]:"zyx", "version":one_product["version"]:"cba"];

	// Product doesn't match the new source ID
	if (one_product["source"]:-255 != AddOnProduct::src_id) {
	    return;
	}

	// Product is not available (either `installed or `selected or ...)
	if (one_product["status"]:`available != `available) {
	    return;
	}

//	// Available but also already installed or selected
//	if (contains (s_a_i_products, this_product)) {
//	    y2warning ("Product %1 is already installed", this_product);
//	    return;
//	}

	string product_name = one_product["name"]:"-Unknown-Product-";

	y2milestone (
	    "Selecting product '%1' for installation -> %2",
	    product_name,
	    Pkg::ResolvableInstall (product_name, `product)
	);
    });

    return `next;
}

symbol ProductSelect () {
    list<map<string,any> > all_products = Pkg::ResolvableProperties ("", `product, "");

    map <string, string> already_used_urls = $[];
    // getting all source urls and product_dirs
    foreach (map<string,any> p, all_products, {
	integer one_src_id = tointeger(p["source"]:nil);
	if (one_src_id == nil) return;
	// the last source (just added)
	if (one_src_id == AddOnProduct::src_id) return;

	map <string, any> src_general_data = Pkg::SourceGeneralData (one_src_id);
	string source_url = src_general_data["url"]:"";
	if (source_url != "" && source_url != nil) {
	    already_used_urls[source_url] = src_general_data["product_dir"]:"";
	}
    });
    y2milestone ("Already used urls with product_dirs: %1", already_used_urls);
    
    list<map<string,any> > installed_products = filter (map<string,any> p, all_products, {
	return p["status"]:nil == `selected || p["status"]:nil == `installed;
    });
    y2milestone ("Already installed/selected products: %1", installed_products);
    list<map<string,any> > products = filter (map<string,any> p, all_products, {
	return p["source"]:-1 == AddOnProduct::src_id;
    });
    y2milestone ("Products on the media: %1", products);

    // there are no product on the given url
    if (size (products) == 0)
    {
	y2milestone ("No poduct found on the media, but anyway, using it :-)");
	// Display /media.1/info.txt if such file exists
	// Display license and wait for agreement
	// FIXME the same code is below
	boolean license_ret = AddOnProduct::AcceptedLicenseAndInfoFile(AddOnProduct::src_id);
	if (license_ret != true) {
	    y2milestone("Removing the current source ID %1", AddOnProduct::src_id);
	    Pkg::SourceDelete(AddOnProduct::src_id);

	    y2milestone ("ProductSelect Dialog ret: %1", `abort);
	    return `abort;
	}

        map data = Pkg::SourceGeneralData (AddOnProduct::src_id);
        string url = data["url"]:"";
        string product_dir = data["product_dir"]:"";

	// bugzilla #304659
	SetAddOnProductName (AddOnProduct::src_id);

	AddOnProduct::add_on_products = add (AddOnProduct::add_on_products, $[
	    "media" : AddOnProduct::src_id,
	    // table cell
	    "product" : ((new_addon_name != "" && new_addon_name != nil) ? new_addon_name : _("No product found in the repository")),
	    "media_url" : url,
	    "product_dir" : product_dir,
	]);

	if (Mode::config ())
	{
	    AddOnProduct::mode_config_sources
		= add (AddOnProduct::mode_config_sources, AddOnProduct::src_id);
	}

	y2milestone ("ProductSelect Dialog ret: %1", `next);
	return `next;
    }

// disabling functionality (partly bugzilla #227605)
// we know whether this product is already installed
// but we can't know whether the source hasn't been already removed
// 
//    products = filter (map<string,any> prod, products, {
//	boolean installed = false;
//	find (map<string,any> p, installed_products, {
//	    if (p["name"]:"" == prod["name"]:"" && p["version"]:"" == prod["version"]:"")
//	    {
//		installed = true;
//		y2milestone ("Product %1 installed", p);
//		return true;
//	    }
//	    return false;
//	});
//	if (installed)
//	{
//	    y2milestone ("Removing %1 from the list of available products", prod);
//	    return false;
//	}
//	return true;
//    });
//    if (size (products) == 0)
//    {
//	// message popup
//	Popup::Message (_("The product on the media is already installed
//or selected for installation."));
//	y2milestone ("Deleting installatino source %1", AddOnProduct::src_id);
//	Pkg::SourceDelete (AddOnProduct::src_id);
//	AddOnProduct::ClearRegistrationRequest (AddOnProduct::src_id);
//	return `finish;
//    }
    // Display /media.1/info.txt if such file exists
    // Display license and wait for agreement
    // FIXME the same code is above
    boolean license_ret = AddOnProduct::AcceptedLicenseAndInfoFile(AddOnProduct::src_id);
    if (license_ret != true) {
	    y2milestone("Removing the current source ID %1", AddOnProduct::src_id);
	    Pkg::SourceDelete(AddOnProduct::src_id);

	    y2milestone ("ProductSelect Dialog ret: %1", `abort);
	    return `abort;
    }

    // there is only one product on the given url
    if (size (products) == 1)
    {
	// bugzilla #227605
	// this product with this url has been already installed or selected for installation
	map <string, any> src_general_data = Pkg::SourceGeneralData (AddOnProduct::src_id);
	string current_url = src_general_data["url"]:"";
	if (current_url != "" && current_url != nil) {
	    y2milestone ("%1", already_used_urls);
	    if (already_used_urls[current_url]:"" == src_general_data["product_dir"]:"") {
		// error pop-up
		Popup::Message (_("The product on the media is already installed
or selected for installation."));
		y2milestone ("Deleting installation source %1 %2 (%3)",
		    AddOnProduct::src_id, current_url, src_general_data["product_dir"]:"");
		Pkg::SourceDelete (AddOnProduct::src_id);
		AddOnProduct::ClearRegistrationRequest (AddOnProduct::src_id);

		y2milestone ("ProductSelect Dialog ret: %1", `finish);
		return `finish;
	    }
	}

	y2milestone ("Only one product available - skipping dialog");
	map<string,any> prod = products[0]:$[];
	if (! AddOnProduct::CheckProductDependencies ([prod["name"]:""]))
	{
	    Pkg::ResolvableRemove (prod["name"]:"", `product);
	    // message popup
	    Popup::Message (_("Dependencies of the add-on product cannot be fulfilled."));
	    AddOnProduct::last_ret = `back;

	    y2milestone ("ProductSelect Dialog ret: %1", `back);
	    return `back;
	}
	// check whether the product is already available on some media - it is similar as below
	integer found_source = -1;
	foreach (map<string,any> p, all_products, {
	    if (p["name"]:"" == prod["name"]:"" && p["version"]:"" == prod["version"]:""
		&& p["media"]:-2 != prod["media"]:-3)
	    {
		y2milestone ("Product %1 already available on media %2", p, p["media"]:-1);
		found_source = p["media"]:-1;
		break;
	    }
	});
	if (found_source != -1)
	{
	    y2milestone ("Deleting source %1", AddOnProduct::src_id);
	    Pkg::SourceDelete (AddOnProduct::src_id);
	    AddOnProduct::src_id = found_source;
	}
	Pkg::ResolvableInstall (prod["name"]:"", `product);
        map data = Pkg::SourceGeneralData (AddOnProduct::src_id);

        string url = data["url"]:"";
        string product_dir = data["product_dir"]:"";

	// bugzilla #304659
	SetAddOnProductName (AddOnProduct::src_id);

	AddOnProduct::add_on_products = add (AddOnProduct::add_on_products, $[
	    "media" : AddOnProduct::src_id,
	    "product" : ((new_addon_name != "" && new_addon_name != nil) ? new_addon_name : prod["display_name"]:prod["short_name"]:prod["name"]:""),
	    "autoyast_product" : prod["name"]:"",
	    "media_url" : url,
	    "product_dir" : product_dir,
	]);

	if (found_source == -1 && Mode::config ())
	{
	    AddOnProduct::mode_config_sources =
		add (AddOnProduct::mode_config_sources, AddOnProduct::src_id);
	}

	y2milestone ("ProductSelect Dialog ret: %1", `next);
	return `next;
    }

    // there are more than one products on the given url
    y2milestone ("Running product selection dialog");
    symbol ret = nil;
    list items = maplist (map<string,any> product, products, {
	return `item (`id (product["name"]:""), product["name"]:"");
    });
    // dialog caption
    string title = _("Product Selection");
    term contents = `HBox (`HStretch(), `VBox (
	`VStretch (),
	// multi selection list
	`MultiSelectionBox (`id (`products), _("Available Products"), items),
	`VStretch ()
    ), `HStretch ());
    // help text
    string help_text = _("<p><b><big>Product Selection</big></b><br/>
Multiple products were found in the repository. Select the products
to install.</p>
");
    Wizard::SetContents (title, contents, help_text, true, true);
    while (ret == nil)
    {
	ret = (symbol)UI::UserInput();
	if (ret == `cancel || ret == `abort)
	{
	    ret = `abort;
//	    if (Stage::initial())
//	    {
//	        if (Popup::ConfirmAbort (`painless))
//		    break;
//	    }
//	    else
//	    {
		// yes-no popup
		if (Popup::YesNo (_("Really abort add-on product installation?")))
		    break;
//	    }
	    continue;
	}
	if (ret == `next)
	{
	    list<string> selected = (list<string>)UI::QueryWidget (`id (`products), `SelectedItems);
	    // check whether the product is already available on some media - it is similar as above
	    list<map<string,any> > prods = filter (map<string,any> p, products, {
		return contains (selected, p["name"]:"");
	    });
	    boolean all_found = true;
	    map<string,integer> prod2src = $[];
	    foreach (map<string,any> prod, prods, {
	      boolean product_found = false;
	      foreach (map<string,any> p, all_products, {
		if (p["name"]:"" == prod["name"]:"" && p["version"]:"" == prod["version"]:""
		    && p["media"]:-2 != prod["media"]:-3)
		{
		    product_found = true;
		    prod2src[prod["name"]:""] = p["media"]:-3;
		    break;
		}
	      });
	      all_found = all_found && product_found;
	    });
	    if (all_found)
	    {
	        y2milestone ("Deleting source %1", AddOnProduct::src_id);
		Pkg::SourceDelete (AddOnProduct::src_id);
		AddOnProduct::src_id = -1;
	    }
	    foreach (string product, selected, {
		Pkg::ResolvableInstall (product, `product);
	    });
	    if (! AddOnProduct::CheckProductDependencies (selected))
	    {
		foreach (string product, selected, {
		    Pkg::ResolvableRemove (product, `product);
		});
		// message popup
		Popup::Message (_("Dependencies of the selected add-on products cannot be fulfilled."));
		ret = nil;
		continue;
	    }
	    map data = Pkg::SourceGeneralData (AddOnProduct::src_id);
	    string url = data["url"]:"";
	    string product_dir = data["product_dir"]:"";

	    // bugzilla #227605
	    // this product with this url has been already installed or selected for installation
	    if (url != "" && url != nil) {
		if (already_used_urls[url]:"" == product_dir) {
		    // error pop-up
		    Popup::Message (_("The product on the media is already installed
or selected for installation."));
		    y2milestone ("Deleting installation source %1 %2 (%3)",
			AddOnProduct::src_id, url, product_dir);
		    Pkg::SourceDelete (AddOnProduct::src_id);
		    AddOnProduct::ClearRegistrationRequest (AddOnProduct::src_id);

		    y2milestone ("ProductSelect Dialog ret: %1", `finish);
		    return `finish;
		}
	    }

	    foreach (string product, selected, {
		integer src_id = (AddOnProduct::src_id == -1 ? prod2src[product]:-1 : AddOnProduct::src_id);

		// bugzilla #304659
		SetAddOnProductName (AddOnProduct::src_id);
		
		AddOnProduct::add_on_products = add (AddOnProduct::add_on_products, $[
		    "media" : src_id,
		    "product" : ((new_addon_name != "" && new_addon_name != nil) ? new_addon_name : product),
		    "autoyast_prouduct" : product,
		    "media_url" : url,
		    "product_dir" : product_dir,
		]);
	    });
	    if ( AddOnProduct::src_id != -1 && Mode::config ())
	    {
		AddOnProduct::mode_config_sources =
		    add (AddOnProduct::mode_config_sources, AddOnProduct::src_id);
	    }
	}
	else if (ret != `back)
	{
	    ret = nil;
	}
    }

    if (ret == `abort)
    {
	y2milestone ("Deleting source %1", AddOnProduct::src_id);
	Pkg::SourceDelete (AddOnProduct::src_id);
    }

    AddOnProduct::last_ret = ret;
    y2milestone ("ProductSelect Dialog ret: %1", AddOnProduct::last_ret);
    return ret;
}

symbol RunWizard() {
    map aliases = $[
	"media" : ``(MediaSelect ()),
	// "catalog" : ``(CatalogSelect ()),
	// "product" : ``(ProductSelect ()),
	"install_product" : ``(InstallProduct ()),
    ];

    map sequence = $[
	"ws_start" : "media",
	"media" : $[
	    `abort : `abort,
	    `next : "install_product",
	    `finish : "install_product",
	],
//	"catalog" : $[
//	    `abort : `abort,
//	    `next : "product",
//	    `finish : `next,
//	],
//	"product" : $[
//	    `abort : `abort,
//	    `next : `next,
//	    `finish : `next,
//	],
	"install_product" : $[
	    `abort : `abort,
	    `next : `next,
	    `finish : `next,
	],
    ];
    return Sequencer::Run(aliases, sequence);
}

symbol RunAutorunWizard() {
    map aliases = $[
	"catalog" : ``(CatalogSelect ()),
	"product" : ``(ProductSelect ()),
    ];

    map sequence = $[
	"ws_start" : "catalog",
	"catalog" : $[
	    `abort : `abort,
	    `next : "product",
	    `finish : `next,
	],
	"product" : $[
	    `abort : `abort,
	    `next : `next,
	    `finish : `next,
	],
    ];
    return Sequencer::Run(aliases, sequence);

}


void Redraw (boolean enable_back, boolean enable_next, boolean enable_abort,
    string back_button, string next_button, string abort_button) {

    y2milestone ("Called Redraw()");
    // main screen heading
    string title = _("Add-On Product Installation");

    // Help for add-on products
    string help = _("<p><big><b>Add-On Product Installation</b></big></br>
Here see all add-on products that are selected for installation.
To add a new product, click <b>Add</b>. To remove an already added one,
select it and click <b>Delete</b>.</p>");

    y2milestone ("Current products: %1", AddOnProduct::add_on_products);

    integer index = -1;
    list items = maplist (map<string,any> product, AddOnProduct::add_on_products, {
	y2milestone ("%1", product);

	index = index + 1;
	map data = $[];

	// BNC #464162, In AytoYaST, there is no media nr. yet
	if (haskey (product, "media") && product["media"]:-1 > -1) {
	    data = Pkg::SourceGeneralData (product["media"]:-1);
	} else {
	    data = product;
	    if (haskey (data, "media_url")) data["url"] = data["media_url"]:"";
	}

	// placeholder for unknown path
	string dir = data["product_dir"]:_("Unknown");
	if (dir == "")
	    dir = "/";
	// table cell, %1 is URL, %2 is directory name
	string media = sformat (_("%1, Directory: %2"),
	    // placeholder for unknown URL
	    data["url"]:_("Unknown"), dir);
	return `item (
	    `id (index),
	    // sformat (_("Product %1"), product["product"]:"")
	    product["product"]:"",
	    media
	);
    });

    term contents = `VBox (
	`Table (
	    `id (`summary),
	    `header (
		// table header
		_("Product"),
		// table header
		_("Media")
	    ),
	    items
	),
	`Left(
	    `HBox (
		`PushButton (`id (`add), Label::AddButton ()),
		`PushButton (`id (`delete), Label::DeleteButton ()),
		`HStretch ()
	    )
	)
    );

    Wizard::SetContentsButtons (title, contents, help, back_button, next_button);
    Wizard::SetAbortButton (`abort, abort_button);

    // Disable next button according to settings
    if (! enable_next) Wizard::DisableNextButton();

    // If back or abort buttons should not be enabled, hide them
    // -> [Cancel] [OK] dialog
    if (! enable_back) Wizard::HideBackButton();
    if (! enable_abort) Wizard::HideAbortButton();

    Wizard::SetTitleIcon("yast-addon");

    // disable delete button if no items listed
    // bug #203809
    if (size(items) == 0) {
	UI::ChangeWidget(`id (`delete), `Enabled, false);
    }
}

void RemoveSelectedAddOn (integer selected) {
    y2milestone ("Deleting %1 %2", selected, AddOnProduct::add_on_products[selected]:nil);

    // remove whole media if the product is the only one on the media
    integer media = AddOnProduct::add_on_products[selected, "media"]:-1;
    integer med_count = size (filter (map<string,any> prod, AddOnProduct::add_on_products, {
    	    return prod["media"]:-1 == media;
    }));

    if (med_count == 1) {
	y2milestone ("Deleting source %1", media);
            Pkg::SourceDelete (media);
    }

    // remove the selected record
    AddOnProduct::add_on_products[selected] = nil;
    AddOnProduct::add_on_products = filter (map<string,any> prod, AddOnProduct::add_on_products, {
        return prod != nil;
    });

    // Remove product from add-ons
    AddOnProduct::Disintegrate (media);

    // remove product from list of product to register (FATE #301312)
    AddOnProduct::RemoveRegistrationFlag (media);
}

// bugzilla #221377
// the original control file is stored as /control.xml
// the other (added) control files are under the
// /tmp/$yast_tmp/control_files/ directory
// as $srcid.xml files
//
// bugzilla #237297
// in the installation workflow - back/ next buttons
// in the installation proposal - cancel / accept buttons
//
// bugzilla #449773
// added enable_abort, abort_button
//
symbol RunAddOnMainDialog (boolean enable_back, boolean enable_next, boolean enable_abort,
    string back_button, string next_button, string abort_button,
    boolean confirm_abort) {

    symbol ret = nil;

    boolean not_enough_memory = (Stage::initial() && HasInsufficientMemory());
    boolean no_addons = (size (AddOnProduct::add_on_products) == 0);

    // bugzilla #239630
    // It might be dangerous to add more installation sources in installation
    // on machine with less memory
    // Do not report when some add-ons are already in use
    if (not_enough_memory && ! no_addons) {
	if (! ContinueIfInsufficientMemory ()) {
	    // next time, it will be skipped too
	    Installation::add_on_selected = false;
	    Installation::productsources_selected = false;

	    return `next;
	}
    }

    // FATE #301928 - Saving one click
    // Bugzilla #305809 if "going_back", do not save that click!
    if (no_addons && ! going_back_in_workflow) {
	y2milestone ("Skipping to media_select");
	// only once
	going_back_in_workflow = false;
	ret = `first_time;
    }

    // Show Add-Ons table
    Redraw (enable_back, enable_next, enable_abort, back_button, next_button, abort_button);

    // store the initial settings, only once
    WorkflowManager::SetBaseWorkflow (false);

    // added / removed
    boolean some_addon_changed = false;

    repeat {
	// FATE #301928 - Saving one click
	if (ret == `first_time) {
	    ret = `add;
	} else {
    	    ret = (symbol) UI::UserInput();
	}

	// aborting
	if (ret == `abort || ret == `cancel) {
	    // User should confirm that
            if (confirm_abort == true) {
		if (Popup::ConfirmAbort (`incomplete)) {
		    ret = `abort;
		    break;
		} else {
		    ret = nil;
		}
	    // Running system
	    } else {
		break;
	    }

	// removing add-on
        } else if (ret == `delete) {
            integer selected = (integer) UI::QueryWidget (`id (`summary), `CurrentItem);
            if (selected == nil) {
                // message report
                Report::Message (_("Select a product to delete."));
                continue;
            }

	    // bugzilla #305802
	    if (! Confirm::DeleteSelected()) {
		continue;
	    }

	    // TRANSLATORS: busy message
	    UI::OpenDialog (`Label (_("Removing selected add-on...")));

	    RemoveSelectedAddOn (selected);
	    some_addon_changed = true;

	    UI::CloseDialog();

	    Redraw (enable_back, enable_next, enable_abort, back_button, next_button, abort_button);

	// adding new add-on
        } else if (ret == `add) {

	    // bugzilla #293428
	    // Release all sources before adding a new one
	    // because of CD/DVD + url cd://
	    Pkg::SourceReleaseAll();

	    // bugzilla #305788
	    // Use new wizard window for adding new Add-On.
	    // Do not use "Steps" dialog.
	    Wizard::OpenNextBackDialog();
	    Wizard::SetTitleIcon("yast-addon");
            symbol ret = RunWizard ();
	    Wizard::CloseDialog();

	    if (ret == `next) {
		// Add-On product has been added, integrate it (change workflow, use y2update)
		AddOnProduct::Integrate (AddOnProduct::src_id);

		// check whether it requests registration (FATE #301312)
		AddOnProduct::PrepareForRegistration (AddOnProduct::src_id);
		some_addon_changed = true;
	    }

	    Redraw (enable_back, enable_next, enable_abort, back_button, next_button, abort_button);

	    // bugzilla #293428
	    // Release all sources after adding a new one
	    // because of CD/DVD + url cd://
	    Pkg::SourceReleaseAll();
        }

    } until ( ret == `next || ret == `back );

    y2milestone ("Ret: %1, Some Add-on Added/Removed: %2", ret, some_addon_changed);
    y2milestone ("Registration will be requested: %1", AddOnProduct::ProcessRegistration());

    // First stage installation, #247892
    // installation, update or autoinstallation
    if (Stage::initial()) {
	// bugzilla #221377
	if (some_addon_changed) {
	    AddOnProduct::ReIntegrateFromScratch();
	}
    }

    // bugzilla #293428
    // Release all sources after all Add-Ons are added and merged
    y2milestone ("Releasing all sources...");
    Pkg::SourceReleaseAll();

    // bugzilla #305788
    Wizard::RestoreBackButton();
    Wizard::RestoreAbortButton();
    Wizard::RestoreNextButton();
    
    return ret;
}

// AddOnsOverviewDialog -->

void CreateAddOnsOverviewDialog () {
    y2milestone ("Creating OverviewDialog");

    Wizard::SetContents (
	// TRANSLATORS: dialog caption
	_("Installed Add-On Products"),
	`VBox (
	    `Table (
		`id ("list_of_addons"),
		`opt (`notify, `immediate),
		`header (
		    // TRANSLATORS: table header item
		    _("Add-On Product"),
		    // TRANSLATORS: table header item
		    _("URL")
		),
		[]
	    ),
	    `VSquash(
		// Rich text plus border
		`MinHeight (6,
		    `RichText (`id ("product_details"), "")
		)
	    ),
	    `HBox (
		`PushButton (`id (`add), Label::AddButton()),
		`HSpacing (1),
		`PushButton (`id (`delete), Label::DeleteButton()),
		`HStretch(),
		// TRANSLATORS: push button
		`PushButton (`id (`packager), _("Run &Software Manager..."))
	    )
	),
	// TRANSLATORS: dialog help adp/1
	_("<p>Here you can see all of the add-on products which are installed on your system.</p>") +
	// TRANSLATORS: dialog help adp/2
	_("<p>Use the <b>Add</b> button to add a new add-on product, or the <b>Delete</b> button to remove an add-on which is in use.</p>"),
	false,
	true
    );

    Wizard::SetTitleIcon ("yast-addon");

    Wizard::HideBackButton();
    Wizard::SetAbortButton (`abort, Label::CancelButton());
    Wizard::SetNextButton (`next, Label::OKButton());
}

map product_infos = $[];

map ReturnCurrentlySelectedProductInfo () {
    if (! UI::WidgetExists (`id ("list_of_addons"))) {
	y2error ("No such widget: %1", "list_of_addons");
	return nil;
    }

    string item_id = (string) UI::QueryWidget (`id ("list_of_addons"), `CurrentItem);

    // no items
    if (item_id == nil) {
	return nil;
    }

    if (! regexpmatch (item_id, "product_")) {
	y2error ("Wrong product ID '%1'", item_id);
	return nil;
    }

    item_id = substring (item_id, 8);

    return product_infos[item_id]:$[];
}

void AdjustInfoWidget () {
    map pi = ReturnCurrentlySelectedProductInfo();
    if (pi == nil || pi == $[]) {
	UI::ChangeWidget (`id ("product_details"), `Value, "");
	return;
    }

    string rt_description = sformat (
	"<p>%1
%2
%3
%4</p>",

	sformat (
	    _("<b>Vendor:</b> %1<br>"),
	    pi["product","vendor"]:_("Unknown vendor")
	),
	sformat (
	    _("<b>Version:</b> %1<br>"),
	    pi["product","version"]:_("Unknown version")
	),
	sformat (
	    _("<b>Repository URL:</b> %1<br>"),
	    (size (pi["info","URLs"]:[]) > 0 ?
		mergestring (pi["info","URLs"]:[], ",")
		:
		_("Unknown repository URL")
	    )
	),
	(size (pi["info","aliases"]:[]) > 0 ?
	    sformat (_("<b>Repository Alias:</b> %1<br>"), mergestring (pi["info","aliases"]:[], ","))
	    :
	    ""
	)
    );

    UI::ChangeWidget (`id ("product_details"), `Value, rt_description);
}

/**
 * Logs wrong product with 'log_this' error and returns 'return_this'.
 * Added because of bnc #459461
 */
string LogWrongProduct (map <string,any> one_product, string log_this, string return_this) {
    y2error ("Erroneous product: %1: %2", log_this, one_product);

    return return_this;
}

/**
 * Modifies repository info (adds some missing pieces).
 */
void AdjustRepositoryInfo (map <string, list> & info) {
    foreach (integer one_repo, (list <integer>) info["IDs"]:[], {
	if (one_repo == nil || one_repo == -1) {
	    y2warning ("Wrong repo ID: %1", one_repo);
	    return;
	}

	map source_data = Pkg::SourceGeneralData (one_repo);

	if (source_data != nil && haskey (source_data, "base_urls")) {
	    info["URLs"] = source_data["base_urls"]:[];
	} else {
	    y2error ("No URLs for repo ID %1", one_repo);
	}

	if (source_data != nil && haskey (source_data, "alias")) {
	    info["aliases"] = [source_data["alias"]:""];
	}
    });
}

/**
 * @struct $[
 *     "IDs"  : [8, 9, 12],
 *     "URLs" : ["dvd://", "http://some/URL/", "ftp://another/URL/"],
 *     "aliases" : ["alias1", "alias2", "alias3"],
 * ]
 */
map <string, list> GetRepoInfo (map <string,any> & this_product, list <map <string,any> > & all_products) {
    map <string, list> ret = $["IDs" : [], "URLs" : [], "aliases" : []];

    string product_arch		= this_product["arch"]:"";
    string product_name		= this_product["name"]:"";
    string product_version	= this_product["version"]:"";

    foreach (map <string,any> one_product, all_products, {
	// if (one_product["status"]:`unknown != `available)	return;
	if (one_product["arch"]:""	!= product_arch)	return;
	if (one_product["name"]:""	!= product_name)	return;
	if (one_product["version"]:""	!= product_version)	return;

	if (haskey (one_product, "source") && one_product["source"]:-1 != -1)
	    ret["IDs"] = add (ret["IDs"]:[], one_product["source"]:-1);
    });

    AdjustRepositoryInfo (ret);

    return ret;
}

void RedrawAddOnsOverviewTable () {
    list <map <string,any> > all_products = Pkg::ResolvableProperties ("", `product, "");

    all_products = maplist (map <string,any> one_product, all_products, {
	// otherwise it fills the log too much
	foreach (string key, ["license", "description"], {
	    if (haskey (one_product, key)) {
		one_product[key] = substring (one_product[key]:"", 0, 40) + "...";
	    }
	});

	return one_product;
    });

    integer source_nr = nil;
    map repository_info = nil;
    integer counter = -1;

    list <term> products = [];
    product_infos = $[];

    list <map <string,any> > installed_products = filter (map <string,any> one_product, all_products, {
	// Do not list the base product
	if (one_product["category"]:"addon" == "base")
	    return false;
	// BNC #475591: Only those `installed or `selected ones should be actually visible
	return (one_product["status"]:`unknown == `installed || one_product["status"]:`unknown == `selected);
    });

    foreach (map <string,any> one_product, installed_products, {
	// only add-on products should be listed
	if (haskey (one_product, "type") && one_product["type"]:"addon" != "addon") {
	    y2milestone ("Skipping product: %1", one_product["display_name"]:one_product["name"]:"");
	    return;
	}

	counter = counter + 1;

	y2milestone ("Product: %1, Info: %2", one_product, repository_info);
	if (repository_info == nil) {
	    y2warning ("No matching repository found for product listed above");
	}

	repository_info = GetRepoInfo (one_product, all_products);

	product_infos[tostring(counter)] = $[
	    "product" : one_product,
	    "info" : repository_info,
	];

	products = add (products, `item (
	    `id (sformat ("product_%1", counter)),
	    one_product["display_name"]:one_product["name"]:_("Unknown product"),
	    repository_info["URLs", 0]:_("Unknown URL")
	));
    });

    UI::ChangeWidget (`id ("list_of_addons"), `Items, products);
    AdjustInfoWidget();

    // Nothing to do delete when there are no product listed
    UI::ChangeWidget (`id(`delete), `Enabled, (size (installed_products) > 0));
}

boolean RunPackageSelector () {
	boolean solve_ret = Pkg::PkgSolve (false);
	y2milestone ("Calling Solve() returned: %1", solve_ret);

	symbol result = PackagesUI::RunPackageSelector ($["mode":`summaryMode]);

	if (result != `accept) {
	    return false;
	}

	Wizard::OpenNextBackDialog();

	y2milestone ("Calling inst_rpmcopy");
	WFM::call ("inst_rpmcopy");
	y2milestone ("Done");

	Wizard::CloseDialog();

	return true;
}

/**
 * Removes the currently selected Add-On
 *
 * @return boolean whether something has changed its state
 */
boolean RemoveProductWithDependencies () {
    map pi = ReturnCurrentlySelectedProductInfo();
    if (pi == nil || pi == $[]) {
	y2error ("Cannot remove unknown product");
	return nil;
    }

    string product_name = pi["product","display_name"]:pi["product","name"]:_("Unknown product");

    if (! Popup::AnyQuestion (
	Label::WarningMsg(),
	sformat (_("Deleting the add-on product %1 may result in removing all the packages
installed from this add-on.

Are sure you want to delete it?"), product_name),
	Label::DeleteButton (),
	Label::CancelButton (),
	`focus_no
    )) {
	y2milestone ("Deleting '%1' canceled", product_name);
	return nil;
    }

    // TRANSLATORS: busy popup message
    UI::OpenDialog (`Label(_("Removing product dependencies...")));
    // OpenDialog-BusyMessage

    list <integer> src_ids = pi["info","IDs"]:[];

    // Temporary definitions
    boolean pack_ret = false;
    string package_string = "";

    // ["pkg1 version release arch", "pkg2 version release arch", ... ]
    list <string> installed_packages = maplist (string inst_package, Pkg::GetPackages (`installed, false), {
	// ... but we need ["pkg1 version-release arch", "pkg2 version-release arch", ... ]
	return regexpsub (inst_package, "(.*) (.*) (.*) (.*)", "\\1 \\2-\\3 \\4");
    });

    // y2milestone ("Installed packages: %1", installed_packages);

    // All packages from Add-On / Repository
    list <map <string,any> > packages_from_repo = Pkg::ResolvableProperties ("", `package, "");

    packages_from_repo = filter (map <string, any> one_package, packages_from_repo, {
	// Package is not at the repositories to be deleted
	if (! contains (src_ids, one_package["source"]:-1))
	    return false;

	// Package *is* at the repository to delete

	// "name version-release arch", "version" already contains a release
	package_string = sformat ("%1 %2 %3",  one_package["name"]:"", one_package["version"]:"", one_package["arch"]:"");

	// The very same package (which is avaliable at the source) is also installed
	return contains (installed_packages, package_string);
    });

    y2milestone ("%1 packages installed from repository", size (packages_from_repo));

    // Removing selected product, whatever it means
    // It might remove several products when they use the same name
    if ((pi["product","status"]:`unknown == `installed || pi["product","status"]:`unknown == `selected) && pi["product","name"]:"" != "") {
	y2milestone ("Removing product: '%1'", pi["product","name"]:"");
	Pkg::ResolvableRemove (pi["product","name"]:"", `product);
    } else {
	y2milestone ("Product is neither `installed nor `selected");
    }

    // Removing repositories of the selected product
    y2milestone ("Removing repositories: %1, url(s): %2", src_ids, pi["info","URLs"]:[]);
    foreach (integer src_id, src_ids, {
	if (src_id > -1) {
	    y2milestone ("Removing repository ID: %1", src_id);
	    Pkg::SourceDelete (src_id);
	} else {
	    y2milestone ("Product doesn't have any repository in use");
	}
    });

    // The product repository is already removed, checking all installed packages

    // All available packages
    list <string> available_packages = maplist (string inst_package, Pkg::GetPackages (`available, false), {
	// ... but we need ["pkg1 version-release arch", "pkg2 version-release arch", ... ]
	return regexpsub (inst_package, "(.*) (.*) (.*) (.*)", "\\1 \\2-\\3 \\4");
    });

    list <string> available_package_names = Pkg::GetPackages (`available, true);
    y2milestone ("%1 available packages", size (available_package_names));

    boolean status_changed = false;

    // check all packages installed from the just removed repository
    foreach (map <string, any> one_package, packages_from_repo, {
	// "name version-release arch", "version" already contains a release
	package_string = sformat ("%1 %2 %3",  one_package["name"]:"", one_package["version"]:"", one_package["arch"]:"");

	// installed package is not available anymore
	if (! contains (available_packages, package_string)) {
	    status_changed = true;

	    // it must be removed
	    y2milestone ("Removing: %1", package_string);
	    Pkg::ResolvableRemove (one_package["name"]:"~~~", `package);

	    // but if another version is present, select if for installation
	    if (contains (available_package_names, one_package["name"]:"~~~")) {
		y2milestone ("Installing another version of %1", one_package["name"]:"");
		Pkg::ResolvableInstall (one_package["name"]:"", `package);
	    }
	}
    });

    // See OpenDialog-BusyMessage
    UI::CloseDialog();

    if (status_changed) {
	return RunPackageSelector();
    }

    return true;
}

void RunAddProductWorkflow () {
    // feedback heading
    string heading = _("Add-On Product Installation");
    // feedback message
    string message
        = _("Reading packages available at the installation repositories...");

    Popup::ShowFeedback (heading, message);

    y2milestone ("syncing srcid %1 to zmd", AddOnProduct::src_id);
    boolean synced
        = SourceManager::SyncAddedAndDeleted ([AddOnProduct::src_id], []);
    y2milestone ("sync status: %1", synced);

    Popup::ClearFeedback ();

    WFM::CallFunction ("inst_addon_update_sources", []);
    AddOnProduct::DoInstall();
    // Write only when there are some changes
    Write();

    Pkg::SourceReleaseAll();
}

// Cleanup UI - Prepare it for progress callbacks
void SetWizardWindowInProgress () {
    Wizard::SetContents (
	_("Add-On Products"),
	`Label (_("Initializing...")),
	_("<p>Initializing add-on products...</p>"),
	false,
	false
    );

    Wizard::SetTitleIcon ("yast-addon");
}

// BNC #476417: When user cancels removing an add-on, we have to neutralize all
// libzypp resolvables to their inital states
void NeutralizeAllResolvables () {
    foreach (symbol one_type, [`product, `patch, `package, `srcpackage, `pattern], {
	y2milestone ("Neutralizing all: %1", one_type);
	Pkg::ResolvableNeutral ("", one_type, true);
    });
}

// Either there are no repositories now or they
// were changed, neutralized, etc.
void LoadLibzyppNow () {
    y2milestone ("Reloading libzypp");
    SetWizardWindowInProgress();

    // Reinitialize
    Pkg::TargetInitialize (Installation::destdir);
    Pkg::TargetLoad();
    Pkg::SourceStartManager (true);
}

symbol RunAddOnsOverviewDialog () {
    y2milestone ("Overview Dialog");
    symbol ret = `next;

    // to see which products are installed
    Pkg::PkgSolve (true);

    CreateAddOnsOverviewDialog();
    RedrawAddOnsOverviewTable();

    any userret = nil;

    while (true) {
	userret = UI::UserInput();

	// Abort
	if (userret == `abort || userret == `cancel) {
	    y2warning ("Aborting...");
	    ret = `abort;
	    break;

	// Closing
	} else if (userret == `next || userret == `finish) {
	    y2milestone ("Finishing...");
	    ret = `next;
	    break;

	// Addin new product
	} else if (userret == `add) {
	    y2milestone ("Using new Add-On...");

	    if (RunWizard() == `next) {
		RunAddProductWorkflow();
	    }

	    // Something has disabled all the repositories or finished
	    // libzypp, reload it
	    list <integer> current_repos = Pkg::SourceGetCurrent(true);
	    if (current_repos == nil || size (current_repos) == 0) {
		LoadLibzyppNow();
	    }

	    CreateAddOnsOverviewDialog();
	    RedrawAddOnsOverviewTable();

	// Removing product
	} else if (userret == `delete) {
	    y2milestone ("Removing selected product...");

	    boolean rpwd = RemoveProductWithDependencies();
	    y2milestone ("RPWD result was: %1", rpwd);

	    // nil == user decided not to remove the product
	    if (rpwd == nil) {
		y2milestone ("User decided not to remove the selected product");
		continue;

	    // false == user decided not confirm the add-on removal
	    // libzypp has been already changed
	    // BNC #476417: Getting libzypp to the previous state
	    } else if (rpwd == false) {
		y2milestone ("User aborted the package manager");

		SetWizardWindowInProgress();

		// Neutralizing all resolvables (some are usually marked for removal)
		NeutralizeAllResolvables();
		Pkg::SourceFinishAll();

		LoadLibzyppNow();

	    // true == packages and sources have been removed
	    } else {
		// Store sources state
		Pkg::SourceSaveAll();
	    }

	    CreateAddOnsOverviewDialog();
	    RedrawAddOnsOverviewTable();

	// Redrawing info widget
	} else if (userret == "list_of_addons") {
	    AdjustInfoWidget();

	// Calling packager directly
	} else if (userret == `packager) {
	    y2milestone ("Calling packager...");
	    RunPackageSelector();

	    CreateAddOnsOverviewDialog();
	    RedrawAddOnsOverviewTable();

	// Everything else
	} else {
	    y2error ("Uknown ret: %1", userret);
	}
    }

    Wizard::RestoreBackButton();

    return ret;
}

// <-- AddOnsOverviewDialog

} //end of include

ACC SHELL 2018